From 50f346b07542b770a3b7a723ddccf906864a8cbc Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 15 Jan 2026 14:42:58 +0100 Subject: [PATCH 1/2] Add unit and integration tests, tox config and coverage report --- .gitignore | 11 +++ coverage-report.pdf | Bin 0 -> 51601 bytes diffusion2d.py | 11 ++- requirements.txt | 4 + tests/integration/test_diffusion2d.py | 45 ++++++++++-- tests/unit/test_diffusion2d_functions.py | 90 +++++++++++++++++++---- tox.toml | 12 +++ 7 files changed, 150 insertions(+), 23 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..297b916 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.pyc + +# OS files +.DS_Store + +# Testing artifacts +.coverage +htmlcov/ +.tox/ \ No newline at end of file diff --git a/coverage-report.pdf b/coverage-report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7408f11fe2fa253759cb9a8adafd4071fa5a44d7 GIT binary patch literal 51601 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�_vf+ElPmw*$?< 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

GGn*1o}yG*(3Wa15F`#o8FK@^G<%49LW-X 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 literal 0 HcmV?d00001 diff --git a/diffusion2d.py b/diffusion2d.py index 51a07f2..7db10be 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), "w must be a float" + assert isinstance(h, float), "h must be a float" + assert isinstance(dx, float), "dx must be a float" + assert isinstance(dy, float), "dy must be a 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), "d must be a float" + assert isinstance(T_cold, float), "T_cold must be a float" + assert isinstance(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..8f87fb4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +numpy +matplotlib +pytest +coverage \ No newline at end of file diff --git a/tests/integration/test_diffusion2d.py b/tests/integration/test_diffusion2d.py index fd026b4..2eae7b2 100644 --- a/tests/integration/test_diffusion2d.py +++ b/tests/integration/test_diffusion2d.py @@ -2,18 +2,49 @@ Tests for functionality checks in class SolveDiffusion2D """ +""" +Integration tests for SolveDiffusion2D +""" + +import pytest +import numpy as np +import numpy.testing as npt + from diffusion2d import SolveDiffusion2D def test_initialize_physical_parameters(): - """ - Checks function SolveDiffusion2D.initialize_domain - """ + """Initialize domain and physical parameters, then verify dt.""" solver = SolveDiffusion2D() + solver.initialize_domain(w=10.0, h=10.0, dx=0.5, dy=0.5) + solver.initialize_physical_parameters(d=4.0, T_cold=100.0, T_hot=500.0) + + dx2 = solver.dx * solver.dx + dy2 = solver.dy * solver.dy + expected = (dx2 * dy2) / (2 * solver.D * (dx2 + dy2)) + + assert solver.dt == pytest.approx(expected) -def test_set_initial_condition(): - """ - Checks function SolveDiffusion2D.get_initial_function - """ +def test_set_initial_conditions(): + """Initialize domain and physical parameters, then verify initial condition array.""" solver = SolveDiffusion2D() + solver.initialize_domain(w=10.0, h=10.0, dx=0.5, dy=0.5) + solver.initialize_physical_parameters(d=4.0, T_cold=100.0, T_hot=500.0) + + # call method and store result + u = solver.set_initial_condition() + + # build expected array the same way as in set_initial_condition + expected_u = 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: + expected_u[i, j] = solver.T_hot + + npt.assert_array_equal(u, expected_u) + + diff --git a/tests/unit/test_diffusion2d_functions.py b/tests/unit/test_diffusion2d_functions.py index c4277ff..cfa9873 100644 --- a/tests/unit/test_diffusion2d_functions.py +++ b/tests/unit/test_diffusion2d_functions.py @@ -2,25 +2,85 @@ Tests for functions in class SolveDiffusion2D """ +import pytest +import unittest + from diffusion2d import SolveDiffusion2D -def test_initialize_domain(): - """ - Check function SolveDiffusion2D.initialize_domain - """ - solver = SolveDiffusion2D() +# Original pytest-style tests (commented out): +# +# def test_initialize_domain(): +# """ +# Check function SolveDiffusion2D.initialize_domain +# """ +# solver = SolveDiffusion2D() +# solver.initialize_domain(w=20.0, h=10.0, dx=0.5, dy=0.5) +# assert solver.nx == 40 +# assert solver.ny == 20 +# +# +# def test_initialize_physical_parameters(): +# """ +# Checks function SolveDiffusion2D.initialize_domain +# """ +# solver = SolveDiffusion2D() +# solver.dx = 0.5 +# solver.dy = 0.5 +# solver.initialize_physical_parameters(d=4.0, T_cold=100.0, T_hot=500.0) +# +# expected = (solver.dx * solver.dx) * (solver.dy * solver.dy) / ( +# 2 * 4.0 * (solver.dx * solver.dx + solver.dy * solver.dy) +# ) +# +# assert solver.dt == pytest.approx(expected) +# +# +# def test_set_initial_condition(): +# """ +# Checks function SolveDiffusion2D.get_initial_function +# """ +# solver = SolveDiffusion2D() +# solver.nx = 40 +# solver.ny = 20 +# solver.dx = 0.5 +# solver.dy = 0.5 +# solver.T_cold = 100.0 +# solver.T_hot = 500.0 +# solver.u = solver.set_initial_condition() +# +# assert hasattr(solver, "u") +# assert solver.u is not None + + +class TestDiffusion2D(unittest.TestCase): + def setUp(self): + self.solver = SolveDiffusion2D() + + def test_initialize_domain(self): + self.solver.initialize_domain(w=20.0, h=10.0, dx=0.5, dy=0.5) + self.assertEqual(self.solver.nx, 40) + self.assertEqual(self.solver.ny, 20) + + def test_initialize_physical_parameters(self): + self.solver.dx = 0.5 + self.solver.dy = 0.5 + self.solver.initialize_physical_parameters(d=4.0, T_cold=100.0, T_hot=500.0) + expected = ( + (self.solver.dx * self.solver.dx) * (self.solver.dy * self.solver.dy) + / (2 * 4.0 * (self.solver.dx * self.solver.dx + self.solver.dy * self.solver.dy)) + ) -def test_initialize_physical_parameters(): - """ - Checks function SolveDiffusion2D.initialize_domain - """ - solver = SolveDiffusion2D() + self.assertAlmostEqual(self.solver.dt, expected, places=7) + def test_set_initial_condition(self): + self.solver.nx = 40 + self.solver.ny = 20 + self.solver.dx = 0.5 + self.solver.dy = 0.5 + self.solver.T_cold = 100.0 + self.solver.T_hot = 500.0 + self.solver.u = self.solver.set_initial_condition() -def test_set_initial_condition(): - """ - Checks function SolveDiffusion2D.get_initial_function - """ - solver = SolveDiffusion2D() + self.assertIsNotNone(self.solver.u) diff --git a/tox.toml b/tox.toml new file mode 100644 index 0000000..e2a6895 --- /dev/null +++ b/tox.toml @@ -0,0 +1,12 @@ + +[tox] +env_list = ["py312"] +skipsdist = true + +[testenv] +deps = ["-rrequirements.txt"] +commands = [ + "coverage run -m pytest", + "coverage report", +] + From c88b6f9b3037c06d41b9f2bceeed356a34d5b20a Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 15 Jan 2026 15:15:25 +0100 Subject: [PATCH 2/2] new tox.toml --- tests/unit/test_diffusion2d_functions.py | 150 +++++++++++------------ tox.toml | 22 +++- 2 files changed, 92 insertions(+), 80 deletions(-) diff --git a/tests/unit/test_diffusion2d_functions.py b/tests/unit/test_diffusion2d_functions.py index cfa9873..8e8509a 100644 --- a/tests/unit/test_diffusion2d_functions.py +++ b/tests/unit/test_diffusion2d_functions.py @@ -8,79 +8,77 @@ from diffusion2d import SolveDiffusion2D -# Original pytest-style tests (commented out): -# -# def test_initialize_domain(): -# """ -# Check function SolveDiffusion2D.initialize_domain -# """ -# solver = SolveDiffusion2D() -# solver.initialize_domain(w=20.0, h=10.0, dx=0.5, dy=0.5) -# assert solver.nx == 40 -# assert solver.ny == 20 -# -# -# def test_initialize_physical_parameters(): -# """ -# Checks function SolveDiffusion2D.initialize_domain -# """ -# solver = SolveDiffusion2D() -# solver.dx = 0.5 -# solver.dy = 0.5 -# solver.initialize_physical_parameters(d=4.0, T_cold=100.0, T_hot=500.0) -# -# expected = (solver.dx * solver.dx) * (solver.dy * solver.dy) / ( -# 2 * 4.0 * (solver.dx * solver.dx + solver.dy * solver.dy) -# ) -# -# assert solver.dt == pytest.approx(expected) -# -# -# def test_set_initial_condition(): -# """ -# Checks function SolveDiffusion2D.get_initial_function -# """ -# solver = SolveDiffusion2D() -# solver.nx = 40 -# solver.ny = 20 -# solver.dx = 0.5 -# solver.dy = 0.5 -# solver.T_cold = 100.0 -# solver.T_hot = 500.0 -# solver.u = solver.set_initial_condition() -# -# assert hasattr(solver, "u") -# assert solver.u is not None - - -class TestDiffusion2D(unittest.TestCase): - def setUp(self): - self.solver = SolveDiffusion2D() - - def test_initialize_domain(self): - self.solver.initialize_domain(w=20.0, h=10.0, dx=0.5, dy=0.5) - self.assertEqual(self.solver.nx, 40) - self.assertEqual(self.solver.ny, 20) - - def test_initialize_physical_parameters(self): - self.solver.dx = 0.5 - self.solver.dy = 0.5 - self.solver.initialize_physical_parameters(d=4.0, T_cold=100.0, T_hot=500.0) - - expected = ( - (self.solver.dx * self.solver.dx) * (self.solver.dy * self.solver.dy) - / (2 * 4.0 * (self.solver.dx * self.solver.dx + self.solver.dy * self.solver.dy)) - ) - - self.assertAlmostEqual(self.solver.dt, expected, places=7) - - def test_set_initial_condition(self): - self.solver.nx = 40 - self.solver.ny = 20 - self.solver.dx = 0.5 - self.solver.dy = 0.5 - self.solver.T_cold = 100.0 - self.solver.T_hot = 500.0 - self.solver.u = self.solver.set_initial_condition() - - self.assertIsNotNone(self.solver.u) +def test_initialize_domain(): + """ + Check function SolveDiffusion2D.initialize_domain + """ + solver = SolveDiffusion2D() + solver.initialize_domain(w=20.0, h=10.0, dx=0.5, dy=0.5) + assert solver.nx == 40 + assert solver.ny == 20 + + +def test_initialize_physical_parameters(): + """ + Checks function SolveDiffusion2D.initialize_domain + """ + solver = SolveDiffusion2D() + solver.dx = 0.5 + solver.dy = 0.5 + solver.initialize_physical_parameters(d=4.0, T_cold=100.0, T_hot=500.0) + + expected = (solver.dx * solver.dx) * (solver.dy * solver.dy) / ( + 2 * 4.0 * (solver.dx * solver.dx + solver.dy * solver.dy) + ) + + assert solver.dt == pytest.approx(expected) + + +def test_set_initial_condition(): + """ + Checks function SolveDiffusion2D.get_initial_function + """ + solver = SolveDiffusion2D() + solver.nx = 40 + solver.ny = 20 + solver.dx = 0.5 + solver.dy = 0.5 + solver.T_cold = 100.0 + solver.T_hot = 500.0 + solver.u = solver.set_initial_condition() + + assert hasattr(solver, "u") + assert solver.u is not None + + +# class TestDiffusion2D(unittest.TestCase): +# def setUp(self): +# self.solver = SolveDiffusion2D() + +# def test_initialize_domain(self): +# self.solver.initialize_domain(w=20.0, h=10.0, dx=0.5, dy=0.5) +# self.assertEqual(self.solver.nx, 40) +# self.assertEqual(self.solver.ny, 20) + +# def test_initialize_physical_parameters(self): +# self.solver.dx = 0.5 +# self.solver.dy = 0.5 +# self.solver.initialize_physical_parameters(d=4.0, T_cold=100.0, T_hot=500.0) + +# expected = ( +# (self.solver.dx * self.solver.dx) * (self.solver.dy * self.solver.dy) +# / (2 * 4.0 * (self.solver.dx * self.solver.dx + self.solver.dy * self.solver.dy)) +# ) + +# self.assertAlmostEqual(self.solver.dt, expected, places=7) + +# def test_set_initial_condition(self): +# self.solver.nx = 40 +# self.solver.ny = 20 +# self.solver.dx = 0.5 +# self.solver.dy = 0.5 +# self.solver.T_cold = 100.0 +# self.solver.T_hot = 500.0 +# self.solver.u = self.solver.set_initial_condition() + +# self.assertIsNotNone(self.solver.u) diff --git a/tox.toml b/tox.toml index e2a6895..2f08654 100644 --- a/tox.toml +++ b/tox.toml @@ -1,12 +1,26 @@ - [tox] -env_list = ["py312"] +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 -m pytest", - "coverage report", + ["coverage", "run", "-a", "-m", "pytest", "tests/integration"] ] +set_env = { PYTHONPATH = "{toxinidir}" } +["testenv:report"] +deps = ["coverage"] +skip_install = true +commands = [ + ["coverage", "report"], + ["coverage", "html"] +] \ No newline at end of file