From 320270221bae5cad2d6e434e454565458e359bff Mon Sep 17 00:00:00 2001 From: Megha Date: Fri, 16 Jan 2026 14:55:18 +0100 Subject: [PATCH] Added unit and integration tests with coverage and tox --- coverage-report.pdf | Bin 0 -> 54304 bytes diffusion2d.py | 11 +++- requirements.txt | 4 ++ tests/integration/test_diffusion2d.py | 71 ++++++++++++++++++++++- tests/unit/test_diffusion2d_functions.py | 58 +++++++++++++++++- tox.toml | 21 +++++++ 6 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 coverage-report.pdf create mode 100644 requirements.txt create mode 100644 tox.toml diff --git a/coverage-report.pdf b/coverage-report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cfc3b5b9976c4059ecacaac7db5e8a707f4f5d53 GIT binary patch literal 54304 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%0D#dQst72 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 Ju#5=u{{yt!zGVOa literal 0 HcmV?d00001 diff --git a/diffusion2d.py b/diffusion2d.py index 51a07f2..0d0448e 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., T_hot=700.): + 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..021aae7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +numpy +matplotlib +pytest +coverage diff --git a/tests/integration/test_diffusion2d.py b/tests/integration/test_diffusion2d.py index fd026b4..9e0dc85 100644 --- a/tests/integration/test_diffusion2d.py +++ b/tests/integration/test_diffusion2d.py @@ -1,19 +1,84 @@ """ Tests for functionality checks in class SolveDiffusion2D """ - +import numpy as np from diffusion2d import SolveDiffusion2D def test_initialize_physical_parameters(): """ - Checks function SolveDiffusion2D.initialize_domain + Integration test: initialize_domain + initialize_physical_parameters + Check computed dt matches the expected formula along with expected values from previous funcations. """ solver = SolveDiffusion2D() + w, h = 12.0, 7.5 + dx, dy = 0.3, 0.5 + D = 3.0 + T_cold, T_hot = 250.0, 900.0 + + solver.initialize_domain(w=w, h=h, dx=dx, dy=dy) + solver.initialize_physical_parameters(d=D, T_cold=T_cold, T_hot=T_hot) + + expected_nx = int(w / dx) + expected_ny = int(h / dy) + dx2 = dx * dx + dy2 = dy * dy + expected_dt = dx2 * dy2 / (2.0 * D * (dx2 + dy2)) + + assert solver.w == w + assert solver.h == h + assert solver.dx == dx + assert solver.dy == dy + assert solver.nx == expected_nx + assert solver.ny == expected_ny + assert solver.T_cold == T_cold + assert solver.T_hot == T_hot + assert solver.D == D + assert solver.dt == expected_dt + + def test_set_initial_condition(): """ - Checks function SolveDiffusion2D.get_initial_function + Integration test: initialize_domain + initialize_physical_parameters + set_initial_condition + Compute expected u array and compare along with expected values from previous funcations. """ solver = SolveDiffusion2D() + + w, h = 10.0, 5.0 + dx, dy = 1.0, 0.5 + D = 4.0 + T_cold, T_hot = 300.0, 700.0 + + solver.initialize_domain(w=w, h=h, dx=dx, dy=dy) + solver.initialize_physical_parameters(d=D, T_cold=T_cold, T_hot=T_hot) + u = solver.set_initial_condition() + + expected_nx = int(w / dx) + expected_ny = int(h / dy) + + dx2 = dx * dx + dy2 = dy * dy + expected_dt = dx2 * dy2 / (2.0 * D * (dx2 + dy2)) + expected_u = T_cold * np.ones((expected_nx, expected_ny)) + + r, cx, cy = 2, 5, 5 + r2 = r ** 2 + for i in range(expected_nx): + for j in range(expected_ny): + p2 = (i * dx - cx) ** 2 + (j * dy - cy) ** 2 + if p2 < r2: + expected_u[i, j] = T_hot + + assert solver.w == w + assert solver.h == h + assert solver.dx == dx + assert solver.dy == dy + assert solver.nx == expected_nx + assert solver.ny == expected_ny + assert solver.T_cold == T_cold + assert solver.T_hot == T_hot + assert solver.D == D + assert solver.dt == expected_dt + assert np.array_equal(u, expected_u) \ No newline at end of file diff --git a/tests/unit/test_diffusion2d_functions.py b/tests/unit/test_diffusion2d_functions.py index c4277ff..49ddb43 100644 --- a/tests/unit/test_diffusion2d_functions.py +++ b/tests/unit/test_diffusion2d_functions.py @@ -3,7 +3,7 @@ """ from diffusion2d import SolveDiffusion2D - +import numpy as np def test_initialize_domain(): """ @@ -11,6 +11,22 @@ def test_initialize_domain(): """ solver = SolveDiffusion2D() + w = 12.0 + h = 7.5 + dx = 0.3 + dy = 0.5 + + expected_nx = int(w / dx) + expected_ny = int(h / dy) + + solver.initialize_domain(w=w, h=h, dx=dx, dy=dy) + + assert solver.w == w + assert solver.h == h + assert solver.dx == dx + assert solver.dy == dy + assert solver.nx == expected_nx + assert solver.ny == expected_ny def test_initialize_physical_parameters(): """ @@ -18,9 +34,49 @@ def test_initialize_physical_parameters(): """ solver = SolveDiffusion2D() + solver.dx = 0.2 + solver.dy = 0.5 + + D = 3.0 + T_cold = 250.0 + T_hot = 900.0 + + dx2 = solver.dx * solver.dx + dy2 = solver.dy * solver.dy + expected_dt = dx2 * dy2 / (2.0 * D * (dx2 + dy2)) + + solver.initialize_physical_parameters(d=D, T_cold=T_cold, T_hot=T_hot) + + 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 """ solver = SolveDiffusion2D() + + solver.dx = 1.0 + solver.dy = 1.0 + solver.nx = 11 + solver.ny = 11 + + solver.T_cold = 300.0 + solver.T_hot = 700.0 + + u = solver.set_initial_condition() + + u_expected = solver.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 * solver.dx - cx) ** 2 + (j * solver.dy - cy) ** 2 + if p2 < r2: + u_expected[i, j] = solver.T_hot + + assert np.array_equal(u, u_expected) diff --git a/tox.toml b/tox.toml new file mode 100644 index 0000000..76f6b5f --- /dev/null +++ b/tox.toml @@ -0,0 +1,21 @@ +requires = ["tox>=4"] +env_list = ["unit", "integration", "report"] + +[env_run_base] +deps = ["-rrequirements.txt"] +set_env = { PYTHONPATH = "{tox_root}" } + +[env.unit] +description = "Run unit tests" +commands = [["pytest", "-q", "tests/unit"]] + +[env.integration] +description = "Run integration tests" +commands = [["pytest", "-q", "tests/integration"]] + +[env.report] +description = "Generate coverage HTML report" +commands = [ + ["coverage", "run", "-m", "pytest"], + ["coverage", "html"] +]