Skip to content

Commit

Permalink
unit tests for png-extraction
Browse files Browse the repository at this point in the history
  • Loading branch information
chinvib66 committed Jun 17, 2021
1 parent 5c95c98 commit ba20542
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ target/
**/*.csv
__pycache__
**/*.pyc
htmlcov
.coverage
coverage.xml
/tests/data/tmp
/tests/data/**/*.dcm
!/tests/data/**/no-img.dcm
13 changes: 13 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
requests
pymongo
schedule
pydicom
pynetdicom
image
numpy
pandas
pillow
pypng
pytest
pytest-mock
pytest-cov
34 changes: 34 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Testing Framework for Niffler Modules

## Setup

Install the requirements from `<repo-home>/requirements-dev.txt`

```
pip install -r requirements-dev.txt
```

Add the test data in `<repo-home>/tests/data/<module-name>/input` for respective tests.

### PNG Extraction Data Setup

Test data in `<repo-home>/tests/data/png-extraction/input`.

For unit tests, add a valid dcm file, with name `test-img.dcm`.

## Running Tests

Initialize the required data, and run tests from `<repo-home>`.

```bash
pytest ./tests
```

For coverage report, run

```bash
pytest ./tests --cov=./modules --cov-report=html
```

and open the `<repo-home>/htmlcov/index.html`

15 changes: 15 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os
import pytest
from pathlib import Path


def create_dirs(*args):
for dir in args:
if not os.path.exists(dir):
os.makedirs(dir)


def pytest_configure():
pytest.data_dir = Path.cwd() / 'tests' / 'data'
pytest.out_dir = Path.cwd() / 'tests' / 'data' / 'tmp' / 'niffler-tests'
pytest.create_dirs = create_dirs
Empty file.
Empty file.
Empty file.
217 changes: 217 additions & 0 deletions tests/unit/test_png_extraction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import glob
import pytest
import sys
import time

from pathlib import Path, PurePath
from pytest_mock import MockerFixture

# Import Niffler Module
niffler_modules_path = Path.cwd() / 'modules'
sys.path.append(str(niffler_modules_path / 'png-extraction'))
import ImageExtractor

import pydicom
import pandas as pd


@pytest.fixture
def mock_pydicom_config_data_element_callback(mocker: MockerFixture):
return mocker.patch.object(pydicom.config, 'data_element_callback')


@pytest.fixture
def mock_pydicom_config_data_element_callback_kwargs(mocker: MockerFixture):
return mocker.patch.object(pydicom.config, 'data_element_callback_kwargs')


@pytest.fixture
def mock_logger(mocker: MockerFixture):
return mocker.patch('ImageExtractor.logging')


class TestGetPath:
dicom_home = "/mock/path/to/dicom/home"

def test_get_path_zero_depth(self):
depth = 0
dcm_path = ImageExtractor.get_path(depth, self.dicom_home)
assert dcm_path == f"{self.dicom_home}/*.dcm"

def test_get_path_some_depth(self):
depth = 3
dcm_path = ImageExtractor.get_path(depth, self.dicom_home)
assert dcm_path == f"{self.dicom_home}{''.join(['/*']*depth)}/*.dcm"


class TestFixMismatch:
with_VRs = ['PN', 'DS', 'IS']

def test_fix_mismatch(self, mock_pydicom_config_data_element_callback, mock_pydicom_config_data_element_callback_kwargs):
ImageExtractor.fix_mismatch(with_VRs=self.with_VRs)
assert pydicom.config.data_element_callback is ImageExtractor.fix_mismatch_callback
assert pydicom.config.data_element_callback_kwargs['with_VRs'] == self.with_VRs


class TestExtractHeaders:
valid_test_dcm_file = 0, str(
pytest.data_dir / 'png-extraction' / 'input' / 'test-img.dcm')
invalid_test_dcm_file = 0, str(
pytest.data_dir / 'png-extraction' / 'input' / 'no-img.dcm')

def test_no_image(self):
headers = ImageExtractor.extract_headers(
self.invalid_test_dcm_file)
assert headers['has_pix_array'] is False

def test_valid_image(self):
headers = ImageExtractor.extract_headers(self.valid_test_dcm_file)
assert headers['has_pix_array'] is True

# TODO large dcm files


class TestGetTuples:
test_dcm_file = str(
pytest.data_dir / 'png-extraction' / 'input' / 'test-img.dcm')
test_valid_plan = pydicom.dcmread(test_dcm_file, force=True)

def test_correct_output(self):
first_key = self.test_valid_plan.dir()[0]
tuple_list = ImageExtractor.get_tuples(self.test_valid_plan)
assert tuple_list[0][0] == first_key

# TODO hasattr error
# TODO large dcm files


class TestExtractImages:

# TODO Write suitable assertions

test_dcm_file = str(
pytest.data_dir / 'png-extraction' / 'input' / 'test-img.dcm')
invalid_test_dcm_file = str(
pytest.data_dir / 'png-extraction' / 'input' / 'no-img.dcm')

def setup_method(self):
header_list = [ImageExtractor.extract_headers(
(0, self.test_dcm_file))]
self.file_data = pd.DataFrame(header_list)
self.index = 0
self.invalid_file_data = pd.DataFrame([
{
'some_col_1': 'Dummy Col1 Value',
'some_col_2': 'Dummy Col2 Value',
'file': self.invalid_test_dcm_file
}
])
out_dir = pytest.out_dir / 'png-extraction/outputs/TestExtractImages'
self.png_destination = f"{str(out_dir)}/extracted-images/"
self.failed = f"{str(out_dir)}/failed-dicom/"
pytest.create_dirs(out_dir, self.png_destination, self.failed)

def test_is16bit(self):
flattened_to_level = "patient"
is16Bit = "True"
out_img = ImageExtractor.extract_images(
self.file_data,
self.index,
self.png_destination,
flattened_to_level,
self.failed,
is16Bit
)
assert out_img[0].startswith(self.test_dcm_file)

def test_not_is16bit(self):
flattened_to_level = "patient"
is16Bit = "False"
out_img = ImageExtractor.extract_images(
self.file_data,
self.index,
self.png_destination,
flattened_to_level,
self.failed,
is16Bit
)
assert out_img[0].startswith(self.test_dcm_file)

def test_level_patient(self):
flattened_to_level = "patient"
is16Bit = "False"
out_img = ImageExtractor.extract_images(
self.file_data,
self.index,
self.png_destination,
flattened_to_level,
self.failed,
is16Bit
)
assert out_img[0].startswith(self.test_dcm_file)

def test_level_study(self):
flattened_to_level = "study"
is16Bit = "False"
out_img = ImageExtractor.extract_images(
self.file_data,
self.index,
self.png_destination,
flattened_to_level,
self.failed,
is16Bit
)
assert out_img[0].startswith(self.test_dcm_file)

def test_level_other(self):
flattened_to_level = "other"
is16Bit = "False"
out_img = ImageExtractor.extract_images(
self.file_data,
self.index,
self.png_destination,
flattened_to_level,
self.failed,
is16Bit
)
assert out_img[0].startswith(self.test_dcm_file)

def test_level_other_no_study_uuid(self):
flattened_to_level = "other"
is16Bit = "False"
out_img = ImageExtractor.extract_images(
self.file_data.drop(['StudyInstanceUID'], axis=1),
self.index,
self.png_destination,
flattened_to_level,
self.failed,
is16Bit
)
assert out_img[0].startswith(self.test_dcm_file)

def test_level_study_no_study_uuid(self):
flattened_to_level = "study"
is16Bit = "False"
out_img = ImageExtractor.extract_images(
self.file_data.drop(['StudyInstanceUID'], axis=1),
self.index,
self.png_destination,
flattened_to_level,
self.failed,
is16Bit
)
assert out_img[0].startswith(self.test_dcm_file)

def test_failed_read_attrerr(self):
flattened_to_level = "study"
is16Bit = "False"
out_img = ImageExtractor.extract_images(
self.invalid_file_data,
self.index,
self.png_destination,
flattened_to_level,
self.failed,
is16Bit
)
assert out_img[1][0] == self.invalid_test_dcm_file
assert out_img[2] is not None

0 comments on commit ba20542

Please sign in to comment.