From f8ac25ef282ddccb0b60e74675f81c1264e16cfa Mon Sep 17 00:00:00 2001 From: Julien Lamy Date: Sun, 12 May 2024 15:16:45 +0200 Subject: [PATCH] Refactor tests --- src/python/dicomifier/bruker/Dataset.cpp | 7 + src/python/dicomifier/bruker/Dataset.h | 2 + src/python/dicomifier/wrappers/Dataset.cpp | 6 + tests/CMakeLists.txt | 29 ---- tests/code/bruker/Dataset.cpp | 108 -------------- tests/code/bruker/Field.cpp | 111 -------------- tests/code/bruker/grammar.cpp | 162 --------------------- tests/code/core/Exception.cpp | 30 ---- tests/python/bruker/test_grammar.py | 79 ++++++++++ 9 files changed, 94 insertions(+), 440 deletions(-) delete mode 100644 tests/code/bruker/Dataset.cpp delete mode 100644 tests/code/bruker/Field.cpp delete mode 100644 tests/code/bruker/grammar.cpp delete mode 100644 tests/code/core/Exception.cpp create mode 100644 tests/python/bruker/test_grammar.py diff --git a/src/python/dicomifier/bruker/Dataset.cpp b/src/python/dicomifier/bruker/Dataset.cpp index 45a3d979..36196a41 100644 --- a/src/python/dicomifier/bruker/Dataset.cpp +++ b/src/python/dicomifier/bruker/Dataset.cpp @@ -96,6 +96,13 @@ ::set_field(Field const & field) this->_fields[field.name] = field; } +std::size_t +Dataset +::size() const +{ + return this->_fields.size(); +} + void Dataset ::_load(std::string::const_iterator begin, std::string::const_iterator end) diff --git a/src/python/dicomifier/bruker/Dataset.h b/src/python/dicomifier/bruker/Dataset.h index 88756f2b..6ace293e 100644 --- a/src/python/dicomifier/bruker/Dataset.h +++ b/src/python/dicomifier/bruker/Dataset.h @@ -44,6 +44,8 @@ class Dataset * (except the PixelData file) */ std::vector const & get_used_files() const; + + std::size_t size() const; typedef std::map::const_iterator const_iterator; const_iterator begin() const { return this->_fields.begin(); } diff --git a/src/python/dicomifier/wrappers/Dataset.cpp b/src/python/dicomifier/wrappers/Dataset.cpp index 34156701..5b0e9ab7 100644 --- a/src/python/dicomifier/wrappers/Dataset.cpp +++ b/src/python/dicomifier/wrappers/Dataset.cpp @@ -44,6 +44,7 @@ void wrap_Dataset(pybind11::module & m) return_value_policy::reference_internal, R"doc( Return a set of files used to create the dataset (except the PixelData file))doc") + .def("__len__", &Dataset::size) .def("__contains__", &Dataset::has_field) .def( "__getitem__", &Dataset::get_field, @@ -53,6 +54,11 @@ void wrap_Dataset(pybind11::module & m) [](Dataset const & d) { return make_key_iterator(d.begin(), d.end()); }, keep_alive<0, 1>()) + .def( + "keys", + [](Dataset const & d) { + return make_key_iterator(d.begin(), d.end()); }, + keep_alive<0, 1>()) .def( "items", [](Dataset const & d) { return make_iterator(d.begin(), d.end()); }, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fa550362..1d47169b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,32 +1,3 @@ -find_package( - Boost 1.58 COMPONENTS filesystem system unit_test_framework REQUIRED) - -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/../src/lib ${Boost_INCLUDE_DIRS}) -add_definitions(-DBOOST_FILESYSTEM_VERSION=3 -DBOOST_TEST_DYN_LINK) -link_directories(${Boost_LIBRARY_DIRS}) - -file(GLOB_RECURSE tests *.cpp) -foreach(test_file ${tests}) - get_filename_component(test ${test_file} NAME_WE) - add_executable(${test} ${test_file}) - target_link_libraries( - ${test} - libdicomifier - ${Boost_LIBRARIES} ${JsonCpp_LIBRARIES}) - - file(READ ${test_file} content) - - set(pattern "BOOST_(AUTO|FIXTURE)_TEST_CASE\\(([^),]+)") - - string(REGEX MATCHALL ${pattern} cases ${content}) - - foreach(case ${cases}) - string(REGEX REPLACE ${pattern} "\\2" case ${case}) - add_test("${test}_${case}" "${test}" "--run_test=${case}") - endforeach() -endforeach() - file(GLOB_RECURSE python_tests "*.py") add_custom_target( PythonTests ${CMAKE_COMMAND} -E echo "Python tests" SOURCES ${python_tests}) diff --git a/tests/code/bruker/Dataset.cpp b/tests/code/bruker/Dataset.cpp deleted file mode 100644 index f5c6735c..00000000 --- a/tests/code/bruker/Dataset.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/************************************************************************* - * Dicomifier - Copyright (C) Universite de Strasbourg - * Distributed under the terms of the CeCILL-B license, as published by - * the CEA-CNRS-INRIA. Refer to the LICENSE file or to - * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html - * for details. - ************************************************************************/ - -#define BOOST_TEST_MODULE Dataset -#include - -#include -#include - -#include "bruker/Dataset.h" -#include "core/Exception.h" - -BOOST_AUTO_TEST_CASE(Constructor) -{ - dicomifier::bruker::Dataset data_set; - BOOST_REQUIRE(!data_set.has_field("SomeField")); -} - -struct LoadFixture -{ - std::string filepath; - - LoadFixture() - : filepath("./test_Dataset_load.txt") - { - std::ofstream myfile; - myfile.open(filepath); - myfile << "##FieldName=Field Value\n"; - myfile << "##$OtherName=( 2, 60 )\n"; - myfile << " \n"; - myfile.close(); - } - - ~LoadFixture() - { - std::remove(filepath.c_str()); - } -}; - -BOOST_FIXTURE_TEST_CASE(Load, LoadFixture) -{ - dicomifier::bruker::Dataset data_set; - data_set.load(filepath); - - BOOST_CHECK(data_set.has_field("FieldName")); - BOOST_CHECK( - data_set.get_field("FieldName").value == - dicomifier::bruker::Field::Value({"Field Value"})); - - BOOST_CHECK(data_set.has_field("OtherName")); - BOOST_CHECK( - data_set.get_field("OtherName").shape == - dicomifier::bruker::Field::Shape({2, 60})); - BOOST_CHECK( - data_set.get_field("OtherName").value == - dicomifier::bruker::Field::Value({"Other", "Values"})); -} - -BOOST_AUTO_TEST_CASE(FieldAccess) -{ - dicomifier::bruker::Field const field("FieldName", {1, 2}, {"foo"}); - dicomifier::bruker::Dataset data_set; - data_set.set_field(field); - BOOST_REQUIRE(data_set.has_field("FieldName")); - BOOST_REQUIRE(data_set.get_field("FieldName").name == "FieldName"); - BOOST_REQUIRE( - data_set.get_field("FieldName").shape == - dicomifier::bruker::Field::Shape({1, 2})); - BOOST_REQUIRE( - data_set.get_field("FieldName").value == - dicomifier::bruker::Field::Value({"foo"})); -} - -BOOST_AUTO_TEST_CASE(InvalidFieldAccess) -{ - dicomifier::bruker::Dataset data_set; - BOOST_REQUIRE_THROW(data_set.get_field("FieldName"), dicomifier::Exception); -} - -BOOST_AUTO_TEST_CASE(Iterators) -{ - std::vector const fields({ - {"Bar", {3, 4}, {"bar"}}, - {"Foo", {1, 2}, {"foo"}}}); - - dicomifier::bruker::Dataset data_set; - data_set.set_field(fields[0]); - data_set.set_field(fields[1]); - - int i=0; - for(auto it=data_set.begin(); it!=data_set.end(); ++it, ++i) - { - BOOST_REQUIRE(ifirst; - BOOST_REQUIRE_EQUAL(name, fields[i].name); - - auto const & field = it->second; - BOOST_REQUIRE_EQUAL(field.name, fields[i].name); - BOOST_REQUIRE(field.shape == fields[i].shape); - BOOST_REQUIRE(field.value == fields[i].value); - } -} diff --git a/tests/code/bruker/Field.cpp b/tests/code/bruker/Field.cpp deleted file mode 100644 index 94ff8eab..00000000 --- a/tests/code/bruker/Field.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/************************************************************************* - * Dicomifier - Copyright (C) Universite de Strasbourg - * Distributed under the terms of the CeCILL-B license, as published by - * the CEA-CNRS-INRIA. Refer to the LICENSE file or to - * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html - * for details. - ************************************************************************/ - -#define BOOST_TEST_MODULE Field -#include - -#include "bruker/Field.h" -#include "core/Exception.h" - -BOOST_AUTO_TEST_CASE(DefaultConstructor) -{ - dicomifier::bruker::Field const field; - BOOST_REQUIRE(field.name.empty()); - BOOST_REQUIRE(field.shape.empty()); - BOOST_REQUIRE(field.value.empty()); -} - -BOOST_AUTO_TEST_CASE(Constructor) -{ - dicomifier::bruker::Field const field("name", {1, 2}, {"foo", 3L}); - BOOST_REQUIRE_EQUAL(field.name, "name"); - BOOST_REQUIRE(field.shape == dicomifier::bruker::Field::Shape({1, 2})); - BOOST_REQUIRE(field.value == dicomifier::bruker::Field::Value({"foo", 3L})); -} - -BOOST_AUTO_TEST_CASE(String) -{ - dicomifier::bruker::Field const field("name", {}, {"foo"}); - BOOST_REQUIRE(field.is_string(0)); - BOOST_REQUIRE_EQUAL(field.get_string(0), "foo"); -} - -BOOST_AUTO_TEST_CASE(BadString) -{ - dicomifier::bruker::Field const field("name", {}, {123L}); - BOOST_REQUIRE_THROW(field.get_string(0), std::exception); -} - -BOOST_AUTO_TEST_CASE(Int) -{ - dicomifier::bruker::Field const field("name", {}, {123L}); - BOOST_REQUIRE(field.is_int(0)); - BOOST_REQUIRE_EQUAL(field.get_int(0), 123); -} - -BOOST_AUTO_TEST_CASE(BadInt) -{ - dicomifier::bruker::Field const field("name", {}, {1.23f}); - BOOST_REQUIRE_THROW(field.get_int(0), std::exception); -} - -BOOST_AUTO_TEST_CASE(Real) -{ - dicomifier::bruker::Field const field("name", {}, {1.23f}); - BOOST_REQUIRE(field.is_real(0)); - BOOST_REQUIRE_CLOSE(field.get_real(0), 1.23, 1e-3); -} - -BOOST_AUTO_TEST_CASE(Struct) -{ - dicomifier::bruker::Field::Value const item({1L, "foo"}); - dicomifier::bruker::Field const field( - "name", {}, dicomifier::bruker::Field::Value({{item}})); - BOOST_REQUIRE(field.is_struct(0)); - BOOST_REQUIRE(field.get_struct(0) == item); -} - -BOOST_AUTO_TEST_CASE(BadStruct) -{ - dicomifier::bruker::Field const field("name", {}, {123L}); - BOOST_REQUIRE_THROW(field.get_struct(0), std::exception); -} - -BOOST_AUTO_TEST_CASE(MixedTypes) -{ - dicomifier::bruker::Field::Value const item({1L, "foo"}); - dicomifier::bruker::Field const field("name", {}, {2L, "bar", -3.45f, item}); - - BOOST_REQUIRE(field.is_int(0)); - BOOST_REQUIRE_EQUAL(field.get_int(0), 2); - - BOOST_REQUIRE(field.is_string(1)); - BOOST_REQUIRE_EQUAL(field.get_string(1), "bar"); - - BOOST_REQUIRE(field.is_real(2)); - BOOST_REQUIRE_CLOSE(field.get_real(2), -3.45, 1e-3); - - BOOST_REQUIRE(field.is_struct(3)); - BOOST_REQUIRE(field.get_struct(3) == item); -} - -BOOST_AUTO_TEST_CASE(Comparison) -{ - dicomifier::bruker::Field const field1("name", {1,2}, {"foo"}); - dicomifier::bruker::Field const field2("name", {1,2}, {"foo"}); - dicomifier::bruker::Field const field3("other_name", {1,2}, {"foo"}); - dicomifier::bruker::Field const field4("name", {3,4,5}, {"foo"}); - dicomifier::bruker::Field const field5("name", {1,2}, {"bar"}); - dicomifier::bruker::Field const field6("name", {1,2}, {42L}); - - BOOST_REQUIRE(field1 == field2); BOOST_REQUIRE(!(field1 != field2)); - BOOST_REQUIRE(field1 != field3); BOOST_REQUIRE(!(field1 == field3)); - BOOST_REQUIRE(field1 != field4); BOOST_REQUIRE(!(field1 == field4)); - BOOST_REQUIRE(field1 != field5); BOOST_REQUIRE(!(field1 == field5)); - BOOST_REQUIRE(field1 != field6); BOOST_REQUIRE(!(field1 == field6)); -} diff --git a/tests/code/bruker/grammar.cpp b/tests/code/bruker/grammar.cpp deleted file mode 100644 index 9232e0b8..00000000 --- a/tests/code/bruker/grammar.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/************************************************************************* - * Dicomifier - Copyright (C) Universite de Strasbourg - * Distributed under the terms of the CeCILL-B license, as published by - * the CEA-CNRS-INRIA. Refer to the LICENSE file or to - * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html - * for details. - ************************************************************************/ - -#define BOOST_TEST_MODULE grammar -#include - -#include - -#include "bruker/grammar.h" -#include "bruker/Field.h" -#include "core/Exception.h" - -std::vector parse(std::string const & value) -{ - auto begin = value.begin(); - auto const end = value.end(); - - std::vector fields; - dicomifier::bruker::grammar g; - auto const parsed = boost::spirit::qi::parse(begin, end, g, fields); - - // Make sure everything was parsed - BOOST_REQUIRE(parsed); - BOOST_REQUIRE(begin == end); - - return fields; -} - -BOOST_AUTO_TEST_CASE(Comment) -{ - std::string const value = "$$Comment"; - auto const fields = parse(value); - - BOOST_REQUIRE(fields.empty()); -} - -BOOST_AUTO_TEST_CASE(UnquotedString) -{ - std::string const value = "##FieldName=A few words"; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE( - fields[0].value == dicomifier::bruker::Field::Value({"A few words"})); -} - -BOOST_AUTO_TEST_CASE(QuotedStrings) -{ - std::string const value = "##FieldName=( 3, 5 )\n "; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE(fields[0].shape == dicomifier::bruker::Field::Shape({3, 5})); - BOOST_REQUIRE( - fields[0].value == dicomifier::bruker::Field::Value({"foo", "bar"})); -} - -BOOST_AUTO_TEST_CASE(Real) -{ - std::string const value = "##FieldName=1.23"; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE_EQUAL(fields[0].value.size(), 1); - BOOST_REQUIRE( - abs((boost::get(fields[0].value[0]) - 1.23f)/1.23f) < 1e-6); -} - -BOOST_AUTO_TEST_CASE(NotReal) -{ - std::string const value = "##FieldName=1.23 foo"; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE( - fields[0].value == dicomifier::bruker::Field::Value{"1.23 foo"}); -} - -BOOST_AUTO_TEST_CASE(Reals) -{ - std::string const value = "##FieldName=( 2 )\n1.23 -4.56"; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE(fields[0].shape == dicomifier::bruker::Field::Shape({2})); - BOOST_REQUIRE_EQUAL(fields[0].value.size(), 2); - BOOST_REQUIRE( - abs((boost::get(fields[0].value[0]) - 1.23f)/1.23f) < 1e-6); - BOOST_REQUIRE( - abs((boost::get(fields[0].value[1]) - -4.56f)/4.56f) < 1e-6); -} - -BOOST_AUTO_TEST_CASE(Integer) -{ - std::string const value = "##FieldName=123"; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE( - fields[0].value == dicomifier::bruker::Field::Value{123L}); -} - -BOOST_AUTO_TEST_CASE(NotInteger) -{ - std::string const value = "##FieldName=123 foo"; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE( - fields[0].value == dicomifier::bruker::Field::Value{"123 foo"}); -} - -BOOST_AUTO_TEST_CASE(Integers) -{ - std::string const value = "##FieldName=( 2 )\n123 -456"; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE(fields[0].shape == dicomifier::bruker::Field::Shape({2})); - BOOST_REQUIRE( - fields[0].value == dicomifier::bruker::Field::Value({123L, -456L})); -} - -BOOST_AUTO_TEST_CASE(Structure) -{ - std::string const value = "##FieldName=( 1 )\n(3, )"; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE(fields[0].shape == dicomifier::bruker::Field::Shape({1})); - - dicomifier::bruker::Field::Value const item({3L, "Foo"}); - BOOST_REQUIRE( - fields[0].value == dicomifier::bruker::Field::Value({{item}})); -} - -BOOST_AUTO_TEST_CASE(RLE) -{ - std::string const value = "##FieldName=( 7 )\n12.34 @3*(12) @2*(3.14) 42"; - auto const fields = parse(value); - - BOOST_REQUIRE_EQUAL(fields.size(), 1); - BOOST_REQUIRE_EQUAL(fields[0].name, "FieldName"); - BOOST_REQUIRE(fields[0].shape == dicomifier::bruker::Field::Shape({7})); - BOOST_REQUIRE( - fields[0].value == dicomifier::bruker::Field::Value({ - 12.34, 12L, 12L, 12L, 3.14, 3.14, 42L})); -} diff --git a/tests/code/core/Exception.cpp b/tests/code/core/Exception.cpp deleted file mode 100644 index f2f371f7..00000000 --- a/tests/code/core/Exception.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/************************************************************************* - * Dicomifier - Copyright (C) Universite de Strasbourg - * Distributed under the terms of the CeCILL-B license, as published by - * the CEA-CNRS-INRIA. Refer to the LICENSE file or to - * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html - * for details. - ************************************************************************/ - -#define BOOST_TEST_MODULE Exception -#include - -#include "core/Exception.h" - -BOOST_AUTO_TEST_CASE(Throw) -{ - BOOST_REQUIRE_THROW( - throw dicomifier::Exception("Error"), dicomifier::Exception); -} - -BOOST_AUTO_TEST_CASE(Check_Message) -{ - try - { - throw dicomifier::Exception("Error"); - } - catch(dicomifier::Exception const & exc) - { - BOOST_CHECK_EQUAL(exc.what(), "Error"); - } -} diff --git a/tests/python/bruker/test_grammar.py b/tests/python/bruker/test_grammar.py new file mode 100644 index 00000000..e2b275d1 --- /dev/null +++ b/tests/python/bruker/test_grammar.py @@ -0,0 +1,79 @@ +import os +import pathlib +import tempfile +import textwrap +import unittest + +import dicomifier + +class TestGrammar(unittest.TestCase): + def test_comment(self): + data_set = __class__._get_dataset("$$Comment") + self.assertEqual(len(data_set), 0) + + def test_unquoted_string(self): + data_set = __class__._get_dataset("##FieldName=A few words") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual(data_set["FieldName"].value, ["A few words"]) + + def test_quoted_strings(self): + data_set = __class__._get_dataset("##FieldName=( 3, 5 )\n ") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual(data_set["FieldName"].value, ["foo", "bar"]) + + def test_real(self): + data_set = __class__._get_dataset("##FieldName=1.23") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual(data_set["FieldName"].value, [1.23]) + + def test_not_real(self): + data_set = __class__._get_dataset("##FieldName=1.23 foo") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual(data_set["FieldName"].value, ["1.23 foo"]) + + def test_reals(self): + data_set = __class__._get_dataset("##FieldName=( 2 )\n1.23 -4.56") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual(data_set["FieldName"].value, [1.23, -4.56]) + + def test_integer(self): + data_set = __class__._get_dataset("##FieldName=123") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual(data_set["FieldName"].value, [123]) + + def test_not_integer(self): + data_set = __class__._get_dataset("##FieldName=123 foo") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual(data_set["FieldName"].value, ["123 foo"]) + + def test_integers(self): + data_set = __class__._get_dataset("##FieldName=( 2 )\n123 -456") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual(data_set["FieldName"].value, [123, -456]) + + def test_structure(self): + data_set = __class__._get_dataset("##FieldName=( 1 )\n(3, )") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual(data_set["FieldName"].value, [[3, "Foo"]]) + + def test_rle(self): + data_set = __class__._get_dataset( + "##FieldName=( 7 )\n12.34 @3*(12) @2*(3.14) 42") + self.assertEqual(list(data_set.keys()), ["FieldName"]) + self.assertEqual( + data_set["FieldName"].value, [12.34, *(3*[12]), *(2*[3.14]), 42]) + + @staticmethod + def _get_dataset(content): + with tempfile.TemporaryDirectory() as directory: + path = os.path.join(directory, "data") + with open(path, "w") as fd: + fd.write(content) + + data_set = dicomifier.bruker.Dataset() + data_set.load(path) + + return data_set + +if __name__ == "__main__": + unittest.main()