Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use hypothesis to implement property based testing. #4703

Open
prady0t opened this issue Dec 21, 2024 · 10 comments · May be fixed by #4724
Open

Use hypothesis to implement property based testing. #4703

prady0t opened this issue Dec 21, 2024 · 10 comments · May be fixed by #4724
Labels
difficulty: medium Will take a few days priority: medium To be resolved if time allows

Comments

@prady0t
Copy link
Contributor

prady0t commented Dec 21, 2024

Pybamm migrated from unittest to pytest successfully in the last GSoC cohort. See : https://pybamm.org/gsoc/2024/#migrate-from-unittest-to-pytest-and-improve-pybamms-testing-infrastructure .
What now remains is implementing hypothesis for property based testing. This will ensure more robust tests and will help us find edge cases and bugs more easily.
Here's an example of how we can write tests using hypothesis : https://hypothesis.works/articles/hypothesis-pytest-fixtures/
The goal here would be to identify all the test function that can benefit from property based testing and later using @given decorator to give that test function set of properties.

Once we have enough tests we can use tools such as oss-fuzz to run it continuously on the cloud. See : https://google.github.io/oss-fuzz/getting-started/new-project-guide/python-lang/#hypothesis

@agriyakhetarpal agriyakhetarpal added difficulty: medium Will take a few days priority: medium To be resolved if time allows labels Dec 22, 2024
@RohitP2005
Copy link
Contributor

Hey it looks like a interesting issue, i am willing to work on it @prady0t @agriyakhetarpal

@prady0t
Copy link
Contributor Author

prady0t commented Dec 22, 2024

Sure! Please go ahead. Let us know if you need any help. I'll try to find out some tests where hypothesis can be used, it will help you get started. Later you can find more such tests on your own.

@RohitP2005
Copy link
Contributor

Thanks i will start my work on it @prady0t

@RohitP2005
Copy link
Contributor

hey @prady0t I thougth maybe I should go by folder inside the /tests/unit to find test that can benefits from the property based testing and integrate them using hypothesis
Can u give a place to start

@prady0t
Copy link
Contributor Author

prady0t commented Dec 27, 2024

Hi @RohitP2005, sorry for the late response. Yes you can start by looking into unit tests first. Here's a file where you can start

This file has tests to calculate averages and input-output can be bounded by properties. For a quick example, here's how you can modify the above test :

Image

This is a very basic example but demonstrates the use case quite well. What I suggest personally is to make a list of all the test files and start going through each one, implementing hypothesis wherever you can. You can keep checking out the boxes for every file you visited. In this way you will end up going through all the tests and others can also keep track of the progress, otherwise it will be a huge mess. I did something similar while migrating from unittest to pytest see #4156.

Here's another example, this class also has a lot of mathematical functions

https://github.com/pybamm-team/PyBaMM/blob/a1f73b6780187e9635c0c4a1164531cbf73fe70c/tests/unit/test_expression_tree/test_unary_operators.py#L16C1-L16C26

Try to find out clever ways you can use hypothesis in these tests, later you can open up a tracking issue and start working on other files.

Here's some reading material that could be helpful :

@RohitP2005
Copy link
Contributor

RohitP2005 commented Dec 27, 2024

Tests within unit folder

  • ├── init.py
  • ├── solvers_matches.csv
  • ├── test_batch_study.py
  • ├── test_callbacks.py
  • ├── test_citations.py
  • ├── test_config.py
  • ├── test_discretisations
  • │   ├── init.py
  • │   └── test_discretisation.py
  • ├── test_doc_utils.py
  • ├── test_experiments
  • │   ├── init.py
  • │   ├── test_base_step.py
  • │   ├── test_experiment.py
  • │   ├── test_experiment_steps.py
  • │   ├── test_experiment_step_termination.py
  • │   └── test_simulation_with_experiment.py
  • ├── test_expression_tree
  • │   ├── init.py
  • │   ├── test_array.py
  • │   ├── test_averages.py
  • │   ├── test_binary_operators.py
  • │   ├── test_broadcasts.py
  • │   ├── test_concatenations.py
  • │   ├── test_coupled_variable.py
  • │   ├── test_d_dt.py
  • │   ├── test_functions.py
  • │   ├── test_independent_variable.py
  • │   ├── test_input_parameter.py
  • │   ├── test_interpolant.py
  • │   ├── test_matrix.py
  • │   ├── test_operations
  • │   │   ├── init.py
  • │   │   ├── test_convert_to_casadi.py
  • │   │   ├── test_copy.py
  • │   │   ├── test_evaluate_python.py
  • │   │   ├── test_jac_2D.py
  • │   │   ├── test_jac.py
  • │   │   ├── test_latexify.py
  • │   │   └── test_unpack_symbols.py
  • │   ├── test_parameter.py
  • │   ├── test_printing
  • │   │   ├── init.py
  • │   │   ├── test_print_name.py
  • │   │   └── test_sympy_overrides.py
  • │   ├── test_scalar.py
  • │   ├── test_state_vector.py
  • │   ├── test_symbolic_diff.py
  • │   ├── test_symbol.py
  • │   ├── test_unary_operators.py
  • │   ├── test_variable.py
  • │   └── test_vector.py
  • ├── test_geometry
  • │   ├── init.py
  • │   └── test_battery_geometry.py
  • ├── test_logger.py
  • ├── test_meshes
  • │   ├── init.py
  • │   ├── test_meshes.py
  • │   ├── test_one_dimensional_submesh.py
  • │   ├── test_scikit_fem_submesh.py
  • │   └── test_zero_dimensional_submesh.py
  • ├── test_models
  • │   ├── init.py
  • │   ├── test_base_model.py
  • │   ├── test_event.py
  • │   ├── test_full_battery_models
  • │   │   ├── init.py
  • │   │   ├── test_base_battery_model.py
  • │   │   ├── test_equivalent_circuit
  • │   │   │   ├── init.py
  • │   │   │   └── test_thevenin.py
  • │   │   ├── test_lead_acid
  • │   │   │   ├── init.py
  • │   │   │   ├── test_base_lead_acid_model.py
  • │   │   │   ├── test_basic_models.py
  • │   │   │   ├── test_full.py
  • │   │   │   └── test_loqs.py
  • │   │   ├── test_lithium_ion
  • │   │   │   ├── base_lithium_ion_half_cell_tests.py
  • │   │   │   ├── base_lithium_ion_tests.py
  • │   │   │   ├── init.py
  • │   │   │   ├── test_base_lithium_ion_model.py
  • │   │   │   ├── test_basic_models.py
  • │   │   │   ├── test_dfn_half_cell.py
  • │   │   │   ├── test_dfn.py
  • │   │   │   ├── test_electrode_soh.py
  • │   │   │   ├── test_mpm_half_cell.py
  • │   │   │   ├── test_mpm.py
  • │   │   │   ├── test_msmr.py
  • │   │   │   ├── test_newman_tobias.py
  • │   │   │   ├── test_splitOCVR.py
  • │   │   │   ├── test_spme_half_cell.py
  • │   │   │   ├── test_spme.py
  • │   │   │   ├── test_spm_half_cell.py
  • │   │   │   ├── test_spm.py
  • │   │   │   └── test_Yang2017.py
  • │   │   └── test_sodium_ion
  • │   │   ├── init.py
  • │   │   └── test_basic_models.py
  • │   ├── test_model_info.py
  • │   └── test_submodels
  • │   ├── init.py
  • │   ├── test_base_submodel.py
  • │   ├── test_effective_current_collector.py
  • │   └── test_particle_polynomial_profile.py
  • ├── test_parameters
  • │   ├── data_for_testing_2D.csv
  • │   ├── data_for_testing_3D.csv
  • │   ├── init.py
  • │   ├── lico2_diffusivity_Dualfoil1998_2D.json
  • │   ├── lico2_ocv_example.csv
  • │   ├── test_base_parameters.py
  • │   ├── test_bpx.py
  • │   ├── test_current_functions.py
  • │   ├── test_ecm_parameters.py
  • │   ├── test_electrical_parameters.py
  • │   ├── test_geometric_parameters.py
  • │   ├── test_lead_acid_parameters.py
  • │   ├── test_lithium_ion_parameters.py
  • │   ├── test_parameter_sets
  • │   │   ├── init.py
  • │   │   ├── test_Ai2020.py
  • │   │   ├── test_Chayambuka2022.py
  • │   │   ├── test_Ecker2015_graphite_halfcell.py
  • │   │   ├── test_Ecker2015.py
  • │   │   ├── test_LCO_Ramadass2004.py
  • │   │   ├── test_LGM50_ORegan2022.py
  • │   │   ├── test_OKane2022_negative_halfcell.py
  • │   │   ├── test_OKane2022.py
  • │   │   └── test_parameters_with_default_models.py
  • │   ├── test_parameter_sets_class.py
  • │   ├── test_parameter_values.py
  • │   ├── test_process_parameter_data.py
  • │   └── test_size_distribution_parameters.py
  • ├── test_plotting
  • │   ├── init.py
  • │   ├── test_plot.py
  • │   ├── test_plot_summary_variables.py
  • │   ├── test_plot_thermal_components.py
  • │   ├── test_plot_voltage_components.py
  • │   └── test_quick_plot.py
  • ├── test_pybamm_data.py
  • ├── test_serialisation
  • │   ├── init.py
  • │   └── test_serialisation.py
  • ├── test_settings.py
  • ├── test_simulation.py
  • ├── test_solvers
  • │   ├── init.py
  • │   ├── test_algebraic_solver.py
  • │   ├── test_base_solver.py
  • │   ├── test_casadi_algebraic_solver.py
  • │   ├── test_casadi_solver.py
  • │   ├── test_dummy_solver.py
  • │   ├── test_idaklu_jax.py
  • │   ├── test_idaklu_solver.py
  • │   ├── test_jax_bdf_solver.py
  • │   ├── test_jax_solver.py
  • │   ├── test_lrudict.py
  • │   ├── test_processed_variable_computed.py
  • │   ├── test_processed_variable.py
  • │   ├── test_scipy_solver.py
  • │   ├── test_solution.py
  • │   └── test_summary_variables.py
  • ├── test_spatial_methods
  • │   ├── init.py
  • │   ├── test_base_spatial_method.py
  • │   ├── test_finite_volume
  • │   │   ├── init.py
  • │   │   ├── test_extrapolation.py
  • │   │   ├── test_finite_volume.py
  • │   │   ├── test_ghost_nodes_and_neumann.py
  • │   │   ├── test_grad_div_shapes.py
  • │   │   └── test_integration.py
  • │   ├── test_scikit_finite_element.py
  • │   ├── test_spectral_volume.py
  • │   └── test_zero_dimensional_method.py
  • ├── test_timer.py
  • └── test_util.py

22 directories, 159 files

@RohitP2005
Copy link
Contributor

@prady0t Is the tree checklist okay ?
Shall make a pull request of my issue branch for each subfolder and make commits for each sub files such that it will be easy for you track the changes

@prady0t
Copy link
Contributor Author

prady0t commented Dec 27, 2024

Before opening a tracking issue I would suggest going through the above mentioned tests and opening a PR covering both files first. We do not want to flood maintainers with a lot of PRs so it would be great if we group a lot of files together and open PRs when we have a lot of changes (just enough so that they are not difficult to review). Also remember not all tests can be tested like this, in fact the once that I mentioned above might not be the best examples.

The goal would be to first identify and list all files that needs such a change, do a lot of changes locally and then open a PR.
The checklist looks fine, just remove the init.py files.

Would love @agriyakhetarpal @arjxn-py @Saransh-cpp thoughts on this.

@RohitP2005
Copy link
Contributor

Before opening a tracking issue I would suggest going through the above mentioned tests and opening a PR covering both files first. We do not want to flood maintainers with a lot of PRs so it would be great if we group a lot of files together and open PRs when we have a lot of changes (just enough so that they are not difficult to review). Also remember not all tests can be tested like this, in fact the once that I mentioned above might not be the best examples.

The goal would be to first identify and list all files that needs such a change, do a lot of changes locally and then open a PR. The checklist looks fine, just remove the init.py files.

Would love @agriyakhetarpal @arjxn-py @Saransh-cpp thoughts on this.

understood , this checklist just shows the available files inside the unit dir, later on reviewing and identifying files which require changes we can remove unecessary files from the checklist .
As u requested i will work on those two files first and open a PR , later we can work on the rest

@agriyakhetarpal
Copy link
Member

One thing to keep in mind is that hypothesis is like a engine for a car, and it takes a very long time to generate its adversarial examples, both when starting the test suite at test collection time, and then when running the tests – and our combination of unit and integration tests already takes thirty minutes in CI. Hypothesis will only bump this number up, so it should be used sparingly and where it is justified to do so, like @prady0t has mentioned. For example, a test file that covers utility files where the functionality to be tested is not numerical in nature will not benefit from property-based testing and we should exclude it, it won't be worth it. Other strategies to reduce our testing time are especially welcome.

@RohitP2005 RohitP2005 linked a pull request Dec 30, 2024 that will close this issue
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty: medium Will take a few days priority: medium To be resolved if time allows
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants