diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml new file mode 100644 index 0000000..355c5a8 --- /dev/null +++ b/.github/workflows/unit_tests.yaml @@ -0,0 +1,25 @@ +name: Run unit tests + +on: + pull_request: + branches: + - '**' + +jobs: + pytest: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Python Setup + uses: actions/setup-python@v6 + with: + python-version: 3.13 + cache: 'pip' + cache-dependency-path: ./setup.cfg + + - name: Install libraries + run: make python-install-development + + - name: Run pytest + run: pytest diff --git a/src/python/app/io/bmp.py b/src/python/app/io/bmp.py index 71e0973..b813bcf 100644 --- a/src/python/app/io/bmp.py +++ b/src/python/app/io/bmp.py @@ -77,7 +77,7 @@ class BMPReader(IFormatReader): # pylint: disable=too-few-public-methods @override def read_format(self, file: BinaryIO) -> Image: - header = BitmapFileHeader.from_bytes(data=file.read(BitmapFileHeader.HEADER_LENGTH)) + # header = BitmapFileHeader.from_bytes(data=file.read(BitmapFileHeader.HEADER_LENGTH)) dib_header_size = file.read(4) dib_header_size, = struct.unpack('I', dib_header_size) diff --git a/src/python/app/operation/flip.py b/src/python/app/operation/flip.py index 5158db8..b7363a6 100644 --- a/src/python/app/operation/flip.py +++ b/src/python/app/operation/flip.py @@ -34,9 +34,11 @@ def parser(cls, parser: ArgumentParser) -> None: @override def __call__(self, args: Namespace, input_image: Image) -> Image: if args.horizontal: - return Image(np.flip(input_image.data, axis=1)) + input_image = Image(np.flip(input_image.data, axis=1)) if args.vertical: - return Image(np.flip(input_image.data, axis=0)) + input_image = Image(np.flip(input_image.data, axis=0)) - assert False, 'unreachable' + if not args.horizontal and not args.vertical: + assert False, 'unreachable' + return input_image diff --git a/src/python/app/operation/grayscale.py b/src/python/app/operation/grayscale.py index fef8846..574a737 100644 --- a/src/python/app/operation/grayscale.py +++ b/src/python/app/operation/grayscale.py @@ -30,9 +30,10 @@ def __call__(self, args: Namespace, input_image: Image) -> Image: if input_image.data.shape[-1] == 1: return input_image - result_image = 0.2126 * input_image.data[:, :, 0] \ - + 0.7152 * input_image.data[:, :, 1] \ - + 0.0722 * input_image.data[:, :, 2] + result_image = \ + 0.2126 * input_image.data[:, :, 0] \ + + 0.7152 * input_image.data[:, :, 1] \ + + 0.0722 * input_image.data[:, :, 2] return Image(data=np.repeat(a=np.expand_dims(a=np.clip(a=result_image, a_min=0., diff --git a/tests/cpp/test_fast.py b/tests/cpp/test_fast.py new file mode 100644 index 0000000..58f5cfa --- /dev/null +++ b/tests/cpp/test_fast.py @@ -0,0 +1,23 @@ +import pytest +import numpy as np + +from app.fast import system # type: ignore +from app.fast import numpy_add # type: ignore + + +def test_system(): + assert 0 == system('ls -la') + assert 0 != system('false') + + +numpy_add_values = [ + (np.array([2, 1, 4], dtype=np.double), np.sum([2, 1, 4])), + (np.array([3, 1, 2], dtype=np.double), np.sum([3, 1, 2])), + (np.array([10, 24, 11], dtype=np.double), np.sum([10, 24, 11])), + (np.array([99, 1, 22], dtype=np.double), np.sum([99, 1, 22])) +] + + +@pytest.mark.parametrize("array1,expected_result", numpy_add_values) +def test_numpy_add(array1, expected_result): + assert numpy_add(array1) == expected_result diff --git a/tests/python/test_operations.py b/tests/python/test_operations.py new file mode 100644 index 0000000..12c6bc9 --- /dev/null +++ b/tests/python/test_operations.py @@ -0,0 +1,83 @@ +from argparse import Namespace +import numpy as np + +from app.image.image import Image +from app.operation.bgr2rgb import BGR2RGB +from app.operation.flip import Flip +from app.operation.grayscale import Grayscale +from app.operation.rotate90 import Rotate90 + + +def test_bgr2rgb(): + input_image = Image(np.array([ + [[10, 20, 30], [15, 25, 35], [16, 26, 36]], + [[20, 30, 40], [21, 31, 41], [22, 32, 42]], + [[30, 40, 50], [31, 41, 51], [32, 42, 52]] + ])) + + expected_image = Image(np.array([ + [[30, 20, 10], [35, 25, 15], [36, 26, 16]], + [[40, 30, 20], [41, 31, 21], [42, 32, 22]], + [[50, 40, 30], [51, 41, 31], [52, 42, 32]] + ])) + + output_image = BGR2RGB()(args=None, input_image=input_image) + + assert (output_image.data == expected_image.data).all() + + +def test_flip(): + input_image = Image(np.array([ + [[10, 20, 30], [15, 25, 35], [16, 26, 36]], + [[20, 30, 40], [21, 31, 41], [22, 32, 42]], + [[30, 40, 50], [31, 41, 51], [32, 42, 52]] + ])) + + expected_image = Image(np.array([ + [[32, 42, 52], [31, 41, 51], [30, 40, 50]], + [[22, 32, 42], [21, 31, 41], [20, 30, 40]], + [[16, 26, 36], [15, 25, 35], [10, 20, 30]], + ])) + + output_image = Flip()(args=Namespace(horizontal=True, vertical=True), input_image=input_image) + + assert (expected_image.data == output_image.data).all() + + +def test_grayscale(): + # Try for already grayscale image + input_image = Image(np.array([ + [[5], [123], [123]], + [[12], [12], [12]], + [[12], [12], [12]] + ])) + expected_output = input_image + + output_image = Grayscale()(args=None, input_image=input_image) + + assert (expected_output.data == output_image.data).all() + + # Try for coloured image + input_image = Image(np.array([ + [[15, 10, 12], [120, 33, 20]], + [[30, 0, 2], [10, 45, 22]] + ])) + + output_image = Grayscale()(args=None, input_image=input_image) + + assert output_image.data.shape[-1] == 3 + for row in output_image.data: + for pixel in row: + assert np.all(pixel == pixel[0]) + + +def test_rotate90(): + input_image = Image(np.array([ + [[15, 10, 12], [120, 33, 20]], + [[30, 0, 2], [10, 45, 22]] + ])) + + for rotation in range(-5, 5): + expected_output = np.rot90(input_image.data, rotation) + output_image = Rotate90()(args=Namespace(rotations=rotation), input_image=input_image) + assert (expected_output == output_image.data).all()