diff --git a/.coverage b/.coverage new file mode 100644 index 00000000..238d1cbb Binary files /dev/null and b/.coverage differ diff --git a/README.md b/README.md index da66993c..898d1595 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,367 @@ Please follow the instructions in [python_testing_exercise.md](https://github.co ### pytest log +#### Step 3, initial test +``` +============================= test session starts ============================== +collecting ... collected 4 items + +test_diffusion2d_functions.py::test_initialize_domain PASSED [ 25%] +test_diffusion2d_functions.py::test_initialize_domain_test_2 PASSED [ 50%] +test_diffusion2d_functions.py::test_initialize_physical_parameters PASSED [ 75%]dt = 0.0011428571428571432 + +test_diffusion2d_functions.py::test_set_initial_condition PASSED [100%] + +============================== 4 passed in 0.22s =============================== +``` +#### Step 3, fail on purpose +``` +============================= test session starts ============================== +collecting ... collected 4 items +# +test_diffusion2d_functions.py::test_initialize_domain FAILED [ 25%] +test_diffusion2d_functions.py:7 (test_initialize_domain) +5 != 10 + +Expected :10 +Actual :5 + + +def test_initialize_domain(): +""" +Check function SolveDiffusion2D.initialize_domain +""" +solver = SolveDiffusion2D() +# test values +w = 20. +h = 10. +dx = 2. +dy = 0.2 + + # call function + solver.initialize_domain(w, h, dx, dy) + + # assertions + assert solver.w == w + assert solver.h == h + assert solver.dx == dx + assert solver.dy == dy + + # expected op +> assert solver.nx == 10 +E assert 5 == 10 +E + where 5 = .nx + +test_diffusion2d_functions.py:29: AssertionError + +test_diffusion2d_functions.py::test_initialize_domain_test_2 FAILED [ 50%] +test_diffusion2d_functions.py:31 (test_initialize_domain_test_2) +6 != 5 + +Expected :5 +Actual :6 + + +def test_initialize_domain_test_2(): +""" +Second testcase for function SolveDiffusion2D.initialize_domain +""" +solver = SolveDiffusion2D() +# Arrange test values +# bad values + + w = 15.2 + h = 17.1 + dx = 2.7 + dy = 0.29 + + # Act (= Perform test action) + solver.initialize_domain(w, h, dx, dy) + + # assersions + assert solver.w == w + assert solver.h == h + assert solver.dx == dx + assert solver.dy == dy + + # expected op +> assert solver.nx == 5 +E assert 6 == 5 +E + where 6 = .nx + +test_diffusion2d_functions.py:54: AssertionError + +test_diffusion2d_functions.py::test_initialize_physical_parameters PASSED [ 75%]dt = 0.0011428571428571432 + +test_diffusion2d_functions.py::test_set_initial_condition PASSED [100%] + +========================= 2 failed, 2 passed in 0.30s ========================== + +Process finished with exit code 1 +``` + ### unittest log +#### Step 4: purposely break all unit tests +``` +Launching pytest with arguments /Users/ombahiwal/Desktop/WS24/Courses_WS24/Simulation Software Engineering/Exercises/05_testing_ci/testing-python-exercise-wt2425/tests/unit/test_diffusion2d_functions.py --no-header --no-summary -q in /Users/ombahiwal/Desktop/WS24/Courses_WS24/Simulation Software Engineering/Exercises/05_testing_ci/testing-python-exercise-wt2425/tests/unit + +============================= test session starts ============================== +collecting ... collected 4 items + +test_diffusion2d_functions.py::TestDiffusion2D::test_initialize_domain +test_diffusion2d_functions.py::TestDiffusion2D::test_initialize_domain_test_2 +test_diffusion2d_functions.py::TestDiffusion2D::test_initialize_physical_parameters +test_diffusion2d_functions.py::TestDiffusion2D::test_set_initial_condition + +============================== 4 failed in 0.28s =============================== +FAILED [ 25%] +test_diffusion2d_functions.py:13 (TestDiffusion2D.test_initialize_domain) +5 != 10 + +Expected :10 +Actual :5 + + +self = + + def test_initialize_domain(self): + """ + Check function SolveDiffusion2D.initialize_domain + """ + solver = SolveDiffusion2D() + # test values + w = 20. + h = 10. + dx = 2. + dy = 0.2 + + # call function + solver.initialize_domain(w, h, dx, dy) + + # assertions + assert solver.w == w + assert solver.h == h + assert solver.dx == dx + assert solver.dy == dy + + # expected op +> assert solver.nx == 10 +E assert 5 == 10 +E + where 5 = .nx + +test_diffusion2d_functions.py:35: AssertionError +FAILED [ 50%] +test_diffusion2d_functions.py:37 (TestDiffusion2D.test_initialize_domain_test_2) +6 != 5 + +Expected :5 +Actual :6 + + +self = + + def test_initialize_domain_test_2(self): + """ + Second testcase for function SolveDiffusion2D.initialize_domain + """ + solver = SolveDiffusion2D() + # Arrange test values + # bad values + w = 15.2 + h = 17.1 + dx = 2.7 + dy = 0.29 + + # Act (= Perform test action) + solver.initialize_domain(w, h, dx, dy) + + # assersions + assert solver.w == w + assert solver.h == h + assert solver.dx == dx + assert solver.dy == dy + + # expected op +> assert solver.nx == 5 +E assert 6 == 5 +E + where 6 = .nx + +test_diffusion2d_functions.py:60: AssertionError +FAILED [ 75%] +test_diffusion2d_functions.py:62 (TestDiffusion2D.test_initialize_physical_parameters) +self = + + def test_initialize_physical_parameters(self): + """ + Checks function SolveDiffusion2D.initialize_domain + """ + solver = SolveDiffusion2D() + # Arrange + d = 3.5 + T_cold = 342.4 + T_hot = 723.15 + solver.dx = 0.1 + solver.dy = 0.2 + + # Act +> solver.initialize_physical_parameters(d, T_cold, T_hot) + +test_diffusion2d_functions.py:76: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +self = , d = 3.5 +T_cold = 342.4, T_hot = 723.15 + + 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_cold must be a float" + # Purposely break + # self.D = d + self.T_cold = T_cold + self.T_hot = T_hot + + # Computing a stable time step + dx2, dy2 = self.dx * self.dx, self.dy * self.dy +> self.dt = dx2 * dy2 / (2 * self.D * (dx2 + dy2)) +E TypeError: unsupported operand type(s) for *: 'int' and 'NoneType' + +../../diffusion2d.py:61: TypeError +FAILED [100%] +test_diffusion2d_functions.py:84 (TestDiffusion2D.test_set_initial_condition) +np.float64(100.0) != 600.0 + +Expected :600.0 +Actual :np.float64(100.0) + + +self = + + def test_set_initial_condition(self): + """ + Checks function SolveDiffusion2D.get_initial_function + """ + solver = SolveDiffusion2D() + # arranve values + solver.nx = 100 + solver.ny = 50 + solver.dx = 0.1 + solver.dy = 0.2 + solver.T_cold = 100. + solver.T_hot = 600. + + # call + u: np.ndarray = solver.set_initial_condition() + + # assersion + + # Domain has expected dimensions + assert u.shape[0] == solver.nx + assert u.shape[1] == solver.ny + + # center is hot and border is cold + assert u[0][0] == 100. + assert u[99][0] == 100. + assert u[0][49] == 100. + assert u[99][49] == 100. + +> assert u[49][24] == 600. +E assert np.float64(100.0) == 600.0 + +test_diffusion2d_functions.py:113: AssertionError + +Process finished with exit code 1 + +``` + +### Step 5: Integration pytest +``` +============================= test session starts ============================== +collecting ... collected 2 items + +test_diffusion2d.py::test_initialize_physical_parameters PASSED [ 50%]dt = 0.0016666666666666672 + +test_diffusion2d.py::test_set_initial_condition PASSED [100%]dt = 0.09259259259259259 + + +============================== 2 passed in 0.24s =============================== + + +============================= test session starts ============================== +collecting ... collected 2 items + +test_diffusion2d.py::test_initialize_physical_parameters FAILED [ 50%] +test_diffusion2d.py:7 (test_initialize_physical_parameters) +def test_initialize_physical_parameters(): + """ + Checks function SolveDiffusion2D.initialize_domain + """ + solver = SolveDiffusion2D() + solver.initialize_domain(10., 20., 0.2, 0.1) +> solver.initialize_physical_parameters(2.4, 0., 1.) + +test_diffusion2d.py:14: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +self = , d = 2.4 +T_cold = 0.0, T_hot = 1.0 + + 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_cold must be a float" + # Purposely break + # self.D = d + self.T_cold = T_cold + self.T_hot = T_hot + + # Computing a stable time step + dx2, dy2 = self.dx * self.dx, self.dy * self.dy +> self.dt = dx2 * dy2 / (2 * self.D * (dx2 + dy2)) +E TypeError: unsupported operand type(s) for *: 'int' and 'NoneType' + +../../diffusion2d.py:61: TypeError + +test_diffusion2d.py::test_set_initial_condition FAILED [100%] +test_diffusion2d.py:19 (test_set_initial_condition) +def test_set_initial_condition(): + """ + Checks function SolveDiffusion2D.get_initial_function + """ + solver = SolveDiffusion2D() + solver.initialize_domain(10., 10., 1., 1.) +> solver.initialize_physical_parameters(2.7, 0., 1.) + +test_diffusion2d.py:26: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +self = , d = 2.7 +T_cold = 0.0, T_hot = 1.0 + + 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_cold must be a float" + # Purposely break + # self.D = d + self.T_cold = T_cold + self.T_hot = T_hot + + # Computing a stable time step + dx2, dy2 = self.dx * self.dx, self.dy * self.dy +> self.dt = dx2 * dy2 / (2 * self.D * (dx2 + dy2)) +E TypeError: unsupported operand type(s) for *: 'int' and 'NoneType' + +../../diffusion2d.py:61: TypeError + + +============================== 2 failed in 0.27s =============================== + +Process finished with exit code 1 + +``` + +#### Step 6 : Coverage ## Citing diff --git a/Test Results - .html b/Test Results - .html new file mode 100644 index 00000000..1b53c2c7 --- /dev/null +++ b/Test Results - .html @@ -0,0 +1,661 @@ + + + + + Test Results — + + + + + + + + + +
+ +
+
    +
  • + +
    17 ms
    +
    test_diffusion2d_functions
    +
      +
    • + +
      17 ms
      +
      TestDiffusion2D
      +
        +
      • + +
        0 ms
        +
        passedtest_initialize_domain
        +
          +
        • + PASSED [ 25%]
          +
        • +
        +
      • +
      • + +
        0 ms
        +
        passedtest_initialize_domain_test_2
        +
          +
        • + PASSED [ 50%]
          +
        • +
        +
      • +
      • + +
        0 ms
        +
        failedtest_initialize_physical_parameters
        +
          +
        • + FAILED [ 75%]
          +
        • +
        • + test_diffusion2d_functions.py:62 (TestDiffusion2D.test_initialize_physical_parameters)self =
          def test_initialize_physical_parameters(self):
          """
          Checks function SolveDiffusion2D.initialize_domain
          """
          solver = SolveDiffusion2D()
          # Arrange
          d = 3.5
          T_cold = 342.4
          T_hot = 723.15
          solver.dx = 0.1
          solver.dy = 0.2

          # Act
          > solver.initialize_physical_parameters(d, T_cold, T_hot)
          test_diffusion2d_functions.py:76:
          _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
          self = , d = 3.5
          T_cold = 342.4, T_hot = 723.15
          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_cold must be a float"
          # Purposely break
          # self.D = d
          self.T_cold = T_cold
          self.T_hot = T_hot

          # Computing a stable time step
          dx2, dy2 = self.dx * self.dx, self.dy * self.dy
          > self.dt = dx2 * dy2 / (2 * self.D * (dx2 + dy2))
          E TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'
          ../../diffusion2d.py:61: TypeError
          +
        • +
        +
      • +
      • + +
        17 ms
        +
        failedtest_set_initial_condition
        +
          +
        • + FAILED [100%]
          +
        • +
        • + test_diffusion2d_functions.py:84 (TestDiffusion2D.test_set_initial_condition)
          np.float64(100.0) != 600.0
          self =
          def test_set_initial_condition(self):
          """
          Checks function SolveDiffusion2D.get_initial_function
          """
          solver = SolveDiffusion2D()
          # arranve values
          solver.nx = 100
          solver.ny = 50
          solver.dx = 0.1
          solver.dy = 0.2
          solver.T_cold = 100.
          solver.T_hot = 600.

          # call
          u: np.ndarray = solver.set_initial_condition()

          # assersion

          # Domain has expected dimensions
          assert u.shape[0] == solver.nx
          assert u.shape[1] == solver.ny

          # center is hot and border is cold
          assert u[0][0] == 100.
          assert u[99][0] == 100.
          assert u[0][49] == 100.
          assert u[99][49] == 100.

          > assert u[49][24] == 600.
          E assert np.float64(100.0) == 600.0
          test_diffusion2d_functions.py:113: AssertionError
          +
        • +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + + diff --git a/diffusion2d.py b/diffusion2d.py index 51a07f2d..65c7cf74 100644 --- a/diffusion2d.py +++ b/diffusion2d.py @@ -38,15 +38,21 @@ def __init__(self): self.dt = None def initialize_domain(self, w=10., h=10., dx=0.1, dy=0.1): + assert all(map(lambda v: type(v) == float, [w, h, dx, dy])), "All inputs nums must be floats" self.w = w self.h = h self.dx = dx self.dy = dy + # Purposely break (h) self.nx = int(w / dx) self.ny = int(h / dy) def initialize_physical_parameters(self, d=4., T_cold=300, T_hot=700): - self.D = d + 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_cold must be a float" + # Purposely break + # self.D = d self.T_cold = T_cold self.T_hot = T_hot @@ -61,7 +67,9 @@ def set_initial_condition(self): # Initial conditions - circle of radius r centred at (cx,cy) (mm) r, cx, cy = 2, 5, 5 - r2 = r ** 2 + # purposely break + r2 = 0 + # r2 = r ** 2 for i in range(self.nx): for j in range(self.ny): p2 = (i * self.dx - cx) ** 2 + (j * self.dy - cy) ** 2 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..b529b41d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ +[ pyproject.toml] + +[tool.pytest.ini_options] +pythonpath = "." \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..ac3f2889 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy>=1.15.0 +matplotlib>=3.0.0 \ No newline at end of file diff --git a/tests/integration/test_diffusion2d.py b/tests/integration/test_diffusion2d.py index fd026b40..d3bfdcab 100644 --- a/tests/integration/test_diffusion2d.py +++ b/tests/integration/test_diffusion2d.py @@ -3,17 +3,42 @@ """ from diffusion2d import SolveDiffusion2D - +import pytest def test_initialize_physical_parameters(): """ Checks function SolveDiffusion2D.initialize_domain """ solver = SolveDiffusion2D() - + solver.initialize_domain(10., 20., 0.2, 0.1) + solver.initialize_physical_parameters(2.4, 0., 1.) + dx2 = 0.04 + dy2 = 0.01 + # dt = 0.0004 / (2*2.4*0.05) = 1/600 = 0.0016666 + assert solver.dt == pytest.approx(0.001666, abs=1e-6) def test_set_initial_condition(): """ Checks function SolveDiffusion2D.get_initial_function """ solver = SolveDiffusion2D() + solver.initialize_domain(10., 10., 1., 1.) + solver.initialize_physical_parameters(2.7, 0., 1.) + u = solver.set_initial_condition() + + expected_array = [ + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.], + [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.], + [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + ] + + for x in range(u.shape[0]): + for y in range(u.shape[1]): + assert expected_array[x][y] == u[x, y], f"Values at {x}, {y} do not match" diff --git a/tests/unit/__pycache__/test_diffusion2d_functions.cpython-311-pytest-8.3.3.pyc b/tests/unit/__pycache__/test_diffusion2d_functions.cpython-311-pytest-8.3.3.pyc new file mode 100644 index 00000000..9c5a22ff Binary files /dev/null and b/tests/unit/__pycache__/test_diffusion2d_functions.cpython-311-pytest-8.3.3.pyc differ diff --git a/tests/unit/test_diffusion2d_functions.py b/tests/unit/test_diffusion2d_functions.py index c4277ffd..daad16de 100644 --- a/tests/unit/test_diffusion2d_functions.py +++ b/tests/unit/test_diffusion2d_functions.py @@ -1,26 +1,116 @@ """ Tests for functions in class SolveDiffusion2D """ - +import pytest +import numpy as np from diffusion2d import SolveDiffusion2D +from unittest import TestCase +import unittest + +class TestDiffusion2D(unittest.TestCase): + + def setUp(self): + self.solver = SolveDiffusion2D() + def test_initialize_domain(self): + """ + Check function SolveDiffusion2D.initialize_domain + """ + solver = SolveDiffusion2D() + # test values + w = 20. + h = 10. + dx = 2. + dy = 0.2 + + # call function + solver.initialize_domain(w, h, dx, dy) + + # assertions + assert solver.w == w + assert solver.h == h + assert solver.dx == dx + assert solver.dy == dy + + # expected op + assert solver.nx == 10 + assert solver.ny == 50 + + def test_initialize_domain_test_2(self): + """ + Second testcase for function SolveDiffusion2D.initialize_domain + """ + solver = SolveDiffusion2D() + # Arrange test values + # bad values + w = 15.2 + h = 17.1 + dx = 2.7 + dy = 0.29 + + # Act (= Perform test action) + solver.initialize_domain(w, h, dx, dy) + + # assersions + assert solver.w == w + assert solver.h == h + assert solver.dx == dx + assert solver.dy == dy + + # expected op + assert solver.nx == 5 + assert solver.ny == 58 + + def test_initialize_physical_parameters(self): + """ + Checks function SolveDiffusion2D.initialize_domain + """ + solver = SolveDiffusion2D() + # Arrange + d = 3.5 + T_cold = 342.4 + T_hot = 723.15 + solver.dx = 0.1 + solver.dy = 0.2 + + # Act + solver.initialize_physical_parameters(d, T_cold, T_hot) + + # Assert + assert solver.D == d + assert solver.T_cold == T_cold + assert solver.T_hot == T_hot + assert solver.dt == pytest.approx(0.001142, abs=1e-6) + + def test_set_initial_condition(self): + """ + Checks function SolveDiffusion2D.get_initial_function + """ + solver = SolveDiffusion2D() + # arranve values + solver.nx = 100 + solver.ny = 50 + solver.dx = 0.1 + solver.dy = 0.2 + solver.T_cold = 100. + solver.T_hot = 600. -def test_initialize_domain(): - """ - Check function SolveDiffusion2D.initialize_domain - """ - solver = SolveDiffusion2D() + # call + u: np.ndarray = solver.set_initial_condition() + # assersion -def test_initialize_physical_parameters(): - """ - Checks function SolveDiffusion2D.initialize_domain - """ - solver = SolveDiffusion2D() + # Domain has expected dimensions + assert u.shape[0] == solver.nx + assert u.shape[1] == solver.ny + # center is hot and border is cold + assert u[0][0] == 100. + assert u[99][0] == 100. + assert u[0][49] == 100. + assert u[99][49] == 100. -def test_set_initial_condition(): - """ - Checks function SolveDiffusion2D.get_initial_function - """ - solver = SolveDiffusion2D() + assert u[49][24] == 600. + assert u[50][24] == 600. + assert u[49][25] == 600. + assert u[50][25] == 600. \ No newline at end of file diff --git a/tox.toml b/tox.toml new file mode 100644 index 00000000..874a35bd --- /dev/null +++ b/tox.toml @@ -0,0 +1,12 @@ +requires = ["tox>=4"] +env_list = ["pytest", "unittest"] + +[env.pytest] +description = "Run pytest" +deps = ["pytest>=8", "-r requirements.txt"] +commands = [["pytest", "tests/integration/test_diffusion2d.py"]] + +[env.unittest] +description = "Run unittest" +deps = ["-r requirements.txt"] +commands = [["python3", "-m", "unittest", "tests/unit/test_diffusion2d_functions.py"]] \ No newline at end of file