From 8eeb87acac8d4fa5d3192c9ef358693619e8150f Mon Sep 17 00:00:00 2001 From: J Date: Wed, 21 Jan 2026 08:16:17 +0100 Subject: [PATCH] unit and integration tests, as well as coverage and tox automation --- .gitignore | 11 ++++ coverage-report.pdf | Bin 0 -> 35080 bytes diffusion2d.py | 11 +++- requirements.txt | 5 ++ tests/integration/test_diffusion2d.py | 27 ++++++++++ tests/unit/test_diffusion2d_functions.py | 62 ++++++++++++++++++++++- tox.toml | 19 +++++++ 7 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 coverage-report.pdf create mode 100644 requirements.txt create mode 100644 tox.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..560f82c --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +__pycache__/ + +.venv/ +venv/ + +.pytest_cache/ + +.coverage +htmlcov/ + +.tox/ diff --git a/coverage-report.pdf b/coverage-report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..caccba661e4b505b100ee6232d821f34490dd3d9 GIT binary patch literal 35080 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 literal 0 HcmV?d00001 diff --git a/diffusion2d.py b/diffusion2d.py index 51a07f2..3394fb8 100644 --- a/diffusion2d.py +++ b/diffusion2d.py @@ -38,6 +38,11 @@ def __init__(self): self.dt = None def initialize_domain(self, w=10., h=10., dx=0.1, dy=0.1): + assert isinstance(w, float) + assert isinstance(h, float) + assert isinstance(dx, float) + assert isinstance(dy, float) + self.w = w self.h = h self.dx = dx @@ -45,7 +50,11 @@ def initialize_domain(self, w=10., h=10., dx=0.1, dy=0.1): self.nx = int(w / dx) self.ny = int(h / dy) - def initialize_physical_parameters(self, d=4., T_cold=300, T_hot=700): + def initialize_physical_parameters(self, d=4., T_cold=300.0, T_hot=700.0): + assert isinstance(d, float) + assert isinstance(T_cold, float) + assert isinstance(T_hot, float) + self.D = d self.T_cold = T_cold self.T_hot = T_hot diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4c573bc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +numpy>=1.26.0 +matplotlib>=3.8.0 +pytest>=9.0.0 +coverage>=7.13.1 +tox>=4.23.2 diff --git a/tests/integration/test_diffusion2d.py b/tests/integration/test_diffusion2d.py index fd026b4..8b7e7b6 100644 --- a/tests/integration/test_diffusion2d.py +++ b/tests/integration/test_diffusion2d.py @@ -11,9 +11,36 @@ def test_initialize_physical_parameters(): """ solver = SolveDiffusion2D() + solver.initialize_domain(w=10.0, h=10.0, dx=1.0, dy=1.0) + solver.initialize_physical_parameters(d=2.0, T_cold=300.0, T_hot=700.0) + + expected_dt = 0.125 + + assert solver.dt == expected_dt + + assert solver.D == 2.0 + assert solver.T_cold == 300.0 + assert solver.T_hot == 700.0 + def test_set_initial_condition(): """ Checks function SolveDiffusion2D.get_initial_function """ solver = SolveDiffusion2D() + + solver.initialize_domain(w=10.0, h=10.0, dx=1.0, dy=1.0) + solver.initialize_physical_parameters(d=1.0, T_cold=0.0, T_hot=100.0) + + u = solver.set_initial_condition() + + # shape + assert u.shape == (solver.nx, solver.ny) + + # center index + cx_index = int(5 / solver.dx) + cy_index = int(5 / solver.dy) + + # check center and corner values + assert u[cx_index, cy_index] == solver.T_hot + assert u[0,0] == solver.T_cold \ No newline at end of file diff --git a/tests/unit/test_diffusion2d_functions.py b/tests/unit/test_diffusion2d_functions.py index c4277ff..60e5a07 100644 --- a/tests/unit/test_diffusion2d_functions.py +++ b/tests/unit/test_diffusion2d_functions.py @@ -11,16 +11,74 @@ def test_initialize_domain(): """ solver = SolveDiffusion2D() + # input values + w = 12.0 + h = 8.0 + dx = 0.5 + dy = 0.25 + + # expected results + expected_nx = 24 + expected_ny = 32 + + # call function under test + solver.initialize_domain(w=w, h=h, dx=dx, dy=dy) + + # assertions + assert solver.nx == expected_nx + assert solver.ny == expected_ny def test_initialize_physical_parameters(): """ - Checks function SolveDiffusion2D.initialize_domain + Check function SolveDiffusion2D.initialize_physical_parameters """ solver = SolveDiffusion2D() + solver.dx = 1.0 + solver.dy = 2.0 + + d = 2.0 + T_cold = 300.0 + T_hot = 700.0 + + # dx² = 1 + # dy² = 4 + # + # dt = (1 * 4) / (2 * 2 * (1 + 4)) + # dt = 4 / 20 + # dt = 0.2 + # expected result + expected_dt = 0.2 + + solver.initialize_physical_parameters(d=d, T_cold=T_cold, T_hot=T_hot) + + # assertions + assert solver.D == d + assert solver.T_cold == T_cold + assert solver.T_hot == T_hot + assert solver.dt == expected_dt def test_set_initial_condition(): """ - Checks function SolveDiffusion2D.get_initial_function + Check function SolveDiffusion2D.set_initial_condition """ solver = SolveDiffusion2D() + + solver.nx = 10 + solver.ny = 10 + solver.dx = 1.0 + solver.dy = 1.0 + solver.T_cold = 0.0 + solver.T_hot = 100.0 + + # call function under test + u = solver.set_initial_condition() + + # basic shape check + assert u.shape == (10, 10) + + # center should be hot + assert u[5, 5] == solver.T_hot + + # corner should be cold + assert u[0, 0] == solver.T_cold diff --git a/tox.toml b/tox.toml new file mode 100644 index 0000000..2e640b4 --- /dev/null +++ b/tox.toml @@ -0,0 +1,19 @@ +[tool.tox] +env_list = ["py", "coverage"] +skips_dist = true + +[tool.tox.testenv.py] +description = "Run unit and integration tests" +deps = ["-r requirements.txt"] +commands = [ + "python -m pytest tests/unit", + "python -m pytest tests/integration" +] + +[tool.tox.testenv.coverage] +description = "Generate coverage report" +deps = ["-r requirements.txt", "coverage"] +commands = [ + "coverage run -m pytest", + "coverage html" +]