From 28dbde16a10ad9b1adf90fa6370305f64a68e374 Mon Sep 17 00:00:00 2001 From: Vincent Rein Date: Thu, 15 Jan 2026 15:16:30 +0100 Subject: [PATCH] Add unittests, regression tests and tox setup --- .coveragerc | 7 ++ coverage-report.pdf | Bin 0 -> 54318 bytes diffusion2d.py | 9 ++- requirements.txt | 5 ++ tests/integration/test_diffusion2d.py | 41 ++++++++++++ tests/unit/test_diffusion2d_functions.py | 81 +++++++++++++++++------ tox.toml | 27 ++++++++ 7 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 .coveragerc create mode 100644 coverage-report.pdf create mode 100644 requirements.txt create mode 100644 tox.toml diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..d24bfde --- /dev/null +++ b/.coveragerc @@ -0,0 +1,7 @@ +[run] +branch = True +source = . +omit = + tests/* + */site-packages/* + */dist-packages/* diff --git a/coverage-report.pdf b/coverage-report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b3db110c48a948114cb0fd1c0a2be15b23efa294 GIT binary patch literal 54318 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 IRT+%`0cDvJ761SM literal 0 HcmV?d00001 diff --git a/diffusion2d.py b/diffusion2d.py index 51a07f2..20870d1 100644 --- a/diffusion2d.py +++ b/diffusion2d.py @@ -38,6 +38,10 @@ def __init__(self): self.dt = None def initialize_domain(self, w=10., h=10., dx=0.1, dy=0.1): + assert type(w) == float, "w must be a float" + assert type(h) == float, "h must be a float" + assert type(dx) == float, "dx must be a float" + assert type(dy) == float, "dy must be a float" self.w = w self.h = h self.dx = dx @@ -45,7 +49,10 @@ 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., T_hot=700.): + assert type(d) == float, "d must be a float" + assert type(T_cold) == float, "T_cold must be a float" + assert type(T_hot) == float, "T_hot must be a 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..f6ac7e2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +numpy +matplotlib +pytest +coverage +unittest \ No newline at end of file diff --git a/tests/integration/test_diffusion2d.py b/tests/integration/test_diffusion2d.py index fd026b4..942e735 100644 --- a/tests/integration/test_diffusion2d.py +++ b/tests/integration/test_diffusion2d.py @@ -3,6 +3,7 @@ """ from diffusion2d import SolveDiffusion2D +import numpy as np def test_initialize_physical_parameters(): @@ -10,6 +11,22 @@ def test_initialize_physical_parameters(): Checks function SolveDiffusion2D.initialize_domain """ solver = SolveDiffusion2D() + w = 12.0 + h = 12.0 + dx = 0.4 + dy = 0.4 + d = 5.0 + T_cold = 300.0 + T_hot = 700.0 + + solver.initialize_domain(w, h, dx, dy) + solver.initialize_physical_parameters(d, T_cold, T_hot) + + dx2 = dx * dx + dy2 = dy * dy + expected_dt = dx2 * dy2 / (2 * d * (dx2 + dy2)) + + assert solver.dt == expected_dt def test_set_initial_condition(): @@ -17,3 +34,27 @@ def test_set_initial_condition(): Checks function SolveDiffusion2D.get_initial_function """ solver = SolveDiffusion2D() + w = 12.0 + h = 12.0 + dx = 1.0 + dy = 1.0 + d = 5.0 + T_cold = 300.0 + T_hot = 700.0 + + solver.initialize_domain(w, h, dx, dy) + solver.initialize_physical_parameters(d, T_cold, T_hot) + + u = solver.set_initial_condition() + + expected_u = T_cold * np.ones((solver.nx, solver.ny)) + r, cx, cy = 2, 5, 5 + r2 = r ** 2 + + for i in range(solver.nx): + for j in range(solver.ny): + p2 = (i * dx - cx) ** 2 + (j * dy - cy) ** 2 + if p2 < r2: + expected_u[i, j] = T_hot + + assert np.allclose(u, expected_u) diff --git a/tests/unit/test_diffusion2d_functions.py b/tests/unit/test_diffusion2d_functions.py index c4277ff..d671341 100644 --- a/tests/unit/test_diffusion2d_functions.py +++ b/tests/unit/test_diffusion2d_functions.py @@ -1,26 +1,65 @@ -""" -Tests for functions in class SolveDiffusion2D -""" - +import unittest +import numpy as np from diffusion2d import SolveDiffusion2D +class TestDiffusion2D(unittest.TestCase): + + def setUp(self): + self.solver = SolveDiffusion2D() -def test_initialize_domain(): - """ - Check function SolveDiffusion2D.initialize_domain - """ - solver = SolveDiffusion2D() - - -def test_initialize_physical_parameters(): - """ - Checks function SolveDiffusion2D.initialize_domain - """ - solver = SolveDiffusion2D() + def test_initialize_domain(self): + """ + Check function SolveDiffusion2D.initialize_domain + """ + w = 20. + h = 10. + dx = 0.2 + dy = 0.5 + + self.solver.initialize_domain(w=w, h=h, dx=dx, dy=dy) + + self.assertEqual(self.solver.nx, int(w / dx)) + self.assertEqual(self.solver.ny, int(h / dy)) + def test_initialize_physical_parameters(self): + """ + Check function SolveDiffusion2D.initialize_physical_parameters + """ + d = 5. + T_cold = 200. + T_hot = 600. + + self.solver.dx = 0.2 + self.solver.dy = 0.5 + + self.solver.initialize_physical_parameters(d=d, T_cold=T_cold, T_hot=T_hot) + + dx2 = self.solver.dx * self.solver.dx + dy2 = self.solver.dy * self.solver.dy + expected_dt = dx2 * dy2 / (2 * d * (dx2 + dy2)) + + self.assertAlmostEqual(self.solver.dt, expected_dt) -def test_set_initial_condition(): - """ - Checks function SolveDiffusion2D.get_initial_function - """ - solver = SolveDiffusion2D() + def test_set_initial_condition(self): + """ + Check function SolveDiffusion2D.set_initial_condition + """ + self.solver.nx = 10 + self.solver.ny = 10 + self.solver.dx = 1.0 + self.solver.dy = 1.0 + self.solver.T_cold = 300. + self.solver.T_hot = 700. + + u = self.solver.set_initial_condition() + + expected_u = self.solver.T_cold * np.ones((self.solver.nx, self.solver.ny)) + r, cx, cy = 2, 5, 5 + r2 = r ** 2 + for i in range(self.solver.nx): + for j in range(self.solver.ny): + p2 = (i * self.solver.dx - cx) ** 2 + (j * self.solver.dy - cy) ** 2 + if p2 < r2: + expected_u[i, j] = self.solver.T_hot + + self.assertTrue(np.array_equal(u, expected_u)) diff --git a/tox.toml b/tox.toml new file mode 100644 index 0000000..664c283 --- /dev/null +++ b/tox.toml @@ -0,0 +1,27 @@ +[tox] +env_list = ["unit", "integration", "report"] +skipsdist = true + +[testenv] +deps = ["-rrequirements.txt"] + +["testenv:unit"] +commands = [ + ["coverage", "run", "-a", "-m", "unittest", "discover", "tests/unit"] +] +set_env = { PYTHONPATH = "{toxinidir}" } + +["testenv:integration"] +commands = [ + ["coverage", "run", "-a", "-m", "pytest", "tests/integration"] +] +set_env = { PYTHONPATH = "{toxinidir}" } + +["testenv:report"] +deps = ["coverage"] +skip_install = true +commands = [ + ["coverage", "report"], + ["coverage", "html"] +] +