-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #140 from chinvib66/feature/png-unit-tests
PNG Extraction Unit Tests
- Loading branch information
Showing
8 changed files
with
285 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |