diff --git a/CMakeLists.txt b/CMakeLists.txt index 08136e67a..38e7900d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,6 +173,10 @@ option(MXLIB_USE_OPENMP "Whether or not to use OpenMP in mxlib and other librari option(MXLIB_USE_CUDA "Whether or not to use CUDA library for mxlib" ON) option(MXLIB_USE_ISIO "Whether or not to use the ImageStreamIO library for mxlib" ON) +option(MXLIB_BUILD_TESTS "Whether or not to build mxlib tests" OFF) +option(MXLIB_BUILD_COVERAGE "Whether or not to enable coverage instrumentation for C/C++ builds" OFF) +set(MXLIB_COVERAGE_TEST_TIMEOUT 300 CACHE STRING "CTest timeout (seconds) for coverage target") +option(MXLIB_BUILD_DOCS "Whether or not to build Doxygen docs as part of the default build" OFF) set(MXLIB_USE_FFT_FROM "fftw" CACHE STRING "Which library to use for the FFT interface in mxlib") @@ -360,6 +364,18 @@ set(CMAKE_CXX_FLAGS "${MXLIB_CXXVERSION} ${MXLIB_OPTIMIZE} ${MXLIB_CXXFLAGS}") set(MXLIB_PC_CFLAGS "${MXLIB_PC_CFLAGS} ${CMAKE_CXX_FLAGS} ${MXLIB_DEFINES}") +if(MXLIB_BUILD_COVERAGE) + # Match the legacy Make coverage behavior: --coverage and -O0 for instrumented builds. + add_compile_definitions(MXLIB_BUILD_COVERAGE=1) + add_compile_options( + $<$:--coverage> + $<$:-O0> + $<$:--coverage> + $<$:-O0> + ) + add_link_options(--coverage) +endif() + ############################################ # OpenMP setup ############################################ @@ -785,4 +801,93 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc install( PROGRAMS gengithead.sh DESTINATION bin) +############################################ +# Tests +############################################ + +if(MXLIB_BUILD_TESTS) + include(CTest) + add_subdirectory(tests) +else() + add_subdirectory(tests EXCLUDE_FROM_ALL) +endif() + +add_custom_target(tests + DEPENDS mxlibTests +) + +set(MXLIB_COVERAGE_BUILD_DIR ${CMAKE_BINARY_DIR}/coverage-build) +set(MXLIB_COVERAGE_INFO ${CMAKE_BINARY_DIR}/coverage.info) +set(MXLIB_COVERAGE_FILTERED_INFO ${CMAKE_BINARY_DIR}/coverage_filtered.info) +set(MXLIB_DOC_OUTPUT_DIR ${CMAKE_BINARY_DIR}/doc) +set(MXLIB_DOC_HTML_DIR ${MXLIB_DOC_OUTPUT_DIR}/html) +set(MXLIB_COVERAGE_REPORT_DIR ${MXLIB_DOC_HTML_DIR}/coverage) + +find_package(Doxygen QUIET) + +if(Doxygen_FOUND) + set(MXLIB_DOXYGEN_CONFIG_IN ${CMAKE_SOURCE_DIR}/doc/mxlib_doxygen.in) + set(MXLIB_DOXYGEN_CONFIG ${CMAKE_BINARY_DIR}/mxlib_doxygen) + set(MXLIB_DOXYGEN_OUTPUT_DIRECTORY ${MXLIB_DOC_OUTPUT_DIR}) + + configure_file(${MXLIB_DOXYGEN_CONFIG_IN} ${MXLIB_DOXYGEN_CONFIG} @ONLY) + + if(MXLIB_BUILD_DOCS) + set(_mxlib_docs_all ALL) + else() + set(_mxlib_docs_all) + endif() + + add_custom_target(docs ${_mxlib_docs_all} + COMMAND ${CMAKE_COMMAND} -E make_directory ${MXLIB_DOC_OUTPUT_DIR} + COMMAND ${DOXYGEN_EXECUTABLE} ${MXLIB_DOXYGEN_CONFIG} + COMMAND ${CMAKE_COMMAND} -DCOVERAGE_REPORT_DIR=${MXLIB_COVERAGE_REPORT_DIR} -P ${CMAKE_SOURCE_DIR}/cmake/ensure_coverage_placeholder.cmake + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/doc + DEPENDS ${MXLIB_DOXYGEN_CONFIG} + ${CMAKE_SOURCE_DIR}/doc/mxlib_header.html + ${CMAKE_SOURCE_DIR}/doc/mxlibDoxygenLayout.xml + ${CMAKE_SOURCE_DIR}/doc/main.dox + ${CMAKE_SOURCE_DIR}/doc/coverage_report.dox + ${CMAKE_SOURCE_DIR}/cmake/ensure_coverage_placeholder.cmake + USES_TERMINAL + ) +else() + message(WARNING "doxygen not found. The docs target will not be available.") +endif() + +find_program(MXLIB_LCOV_EXECUTABLE lcov) +find_program(MXLIB_GENHTML_EXECUTABLE genhtml) + +if(MXLIB_LCOV_EXECUTABLE AND MXLIB_GENHTML_EXECUTABLE) + add_custom_target(coverage + COMMAND ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${MXLIB_COVERAGE_BUILD_DIR} -DMXLIB_BUILD_TESTS=ON -DMXLIB_BUILD_COVERAGE=ON -DMXLIB_USE_CUDA=OFF -DMXLIB_USE_OPENMP=${MXLIB_USE_OPENMP} -DMXLIB_USE_ISIO=${MXLIB_USE_ISIO} -DMXLIB_USE_FFT_FROM=${MXLIB_USE_FFT_FROM} -DMXLIB_USE_BLAS_FROM=${MXLIB_USE_BLAS_FROM} + COMMAND ${CMAKE_COMMAND} --build ${MXLIB_COVERAGE_BUILD_DIR} --target mxlibTests -j + COMMAND ${MXLIB_LCOV_EXECUTABLE} --directory ${MXLIB_COVERAGE_BUILD_DIR} --zerocounters + COMMAND ${CMAKE_CTEST_COMMAND} --test-dir ${MXLIB_COVERAGE_BUILD_DIR} --output-on-failure --timeout ${MXLIB_COVERAGE_TEST_TIMEOUT} + COMMAND ${MXLIB_LCOV_EXECUTABLE} --directory ${MXLIB_COVERAGE_BUILD_DIR} --capture --output-file ${MXLIB_COVERAGE_INFO} + COMMAND ${MXLIB_LCOV_EXECUTABLE} --remove ${MXLIB_COVERAGE_INFO} ${CMAKE_SOURCE_DIR}/tests/* /usr/* /sys/* /tty/* --ignore-errors unused,unused --ignore-errors inconsistent,inconsistent --output-file ${MXLIB_COVERAGE_FILTERED_INFO} + COMMAND ${CMAKE_COMMAND} -E make_directory ${MXLIB_COVERAGE_REPORT_DIR} + COMMAND ${MXLIB_GENHTML_EXECUTABLE} ${MXLIB_COVERAGE_FILTERED_INFO} --output-directory ${MXLIB_COVERAGE_REPORT_DIR} --title mxlib --hierarchical --merge-aliases --suppress-aliases --filter function --demangle-cpp --css-file ${CMAKE_SOURCE_DIR}/tests/coverage/gcov.css + USES_TERMINAL + ) + if(TARGET docs) + add_dependencies(coverage docs) + endif() +else() + message(WARNING "lcov/genhtml not found. The coverage target will not be available.") +endif() + +add_custom_target(coverage_clean + COMMAND ${CMAKE_COMMAND} -E rm -f ${MXLIB_COVERAGE_INFO} + COMMAND ${CMAKE_COMMAND} -E rm -f ${MXLIB_COVERAGE_FILTERED_INFO} + COMMAND ${CMAKE_COMMAND} -E rm -rf ${MXLIB_COVERAGE_REPORT_DIR} + COMMAND ${CMAKE_COMMAND} -E rm -rf ${MXLIB_COVERAGE_BUILD_DIR} +) + +if(TARGET docs) + add_custom_target(docs_clean + COMMAND ${CMAKE_COMMAND} -E rm -rf ${MXLIB_DOC_OUTPUT_DIR} + ) +endif() + #dump_cmake_variables(".") diff --git a/README.md b/README.md index a9c142876..e08129bcc 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,5 @@ The documentation is located here: https://jaredmales.github.io/mxlib-doc/ See the [User's Guide](https://jaredmales.github.io/mxlib-doc/modules.html) for [installation instructions](https://jaredmales.github.io/mxlib-doc/group__installation.html) +See the [Testing](https://jaredmales.github.io/mxlib-doc/group__mxlib__testing.html) +section for test build, test execution, and coverage instructions. diff --git a/agent_context.md b/agent_context.md new file mode 100644 index 000000000..e249807fc --- /dev/null +++ b/agent_context.md @@ -0,0 +1,65 @@ +Follow these code style and documentation rules exactly. + +1) File-Level Documentation +- Each header/source should have a top Doxygen file block: + - \file + - \brief + - \author (if project uses it) + +2) Include Guards and Includes +- Match existing project include-guard naming convention. +- Keep include ordering consistent with project style. +- Do not introduce new include style unless project already uses it. + +3) Function Declaration Documentation +- Every function declaration must have a brief `///` summary. +- Add a short `/** ... */` details block only when needed. +- Document every parameter inline at declaration site using: + - `type name /**< [in] description */` +- Apply to normal methods, constructors, slots, and signals. +- Keep return-value docs where project uses them. + +4) Member Variable Documentation +- Document non-trivial class members with `///`. +- Describe role/ownership/state, not just type. + +5) Naming and Structure +- Use project member naming convention (e.g. `m_` prefix). +- Keep declaration ordering/grouping stable: + - public/protected/private + - slots/signals grouped consistently. +- Leave a blank line between declarations for readability. + +6) Header vs Source Placement +- Keep non-trivial definitions out of headers. +- Move implementations to `.cpp` unless intentionally inline. + +7) Editing Discipline +- Preserve existing behavior unless explicitly requested. +- When renaming members/APIs, update all dependent call sites. +- Keep changes minimal and scoped. + +8) Formatting and Verification +- Run `clang-format` on touched files. +- Ensure docs and naming are consistent after formatting. +- Report any places where project style is ambiguous before making assumptions. + +9) Doxygen Named Section Ordering +- For classes that expose configuration via member data + accessors, keep named sections split into: + - `... - Data` for protected/private member state + - `...` (without `- Data`) for public access functions +- Place the `... - Data` section before the corresponding public accessor section. + +10) Header Declaration Parameter Docs +- In headers, prefer inline parameter documentation on declarations (`type name /**< ... */`) rather than separate `\param` lists, unless there is a specific reason to deviate. + +11) PR Prompt Attribution +- At the top of PR descriptions, include an explicit attribution line when work was performed with Codex. +- Preferred format: + - `This work was performed by GPT-5.3-Codex in response to the prompt: "...".` +- Include the primary user prompt verbatim (or a faithful condensed version if it is extremely long). + +When you finish: +- Summarize what changed. +- List affected files. +- Note any follow-up items or potential edge cases. diff --git a/cmake/ensure_coverage_placeholder.cmake b/cmake/ensure_coverage_placeholder.cmake new file mode 100644 index 000000000..d384e895f --- /dev/null +++ b/cmake/ensure_coverage_placeholder.cmake @@ -0,0 +1,47 @@ +if(NOT DEFINED COVERAGE_REPORT_DIR OR COVERAGE_REPORT_DIR STREQUAL "") + message(FATAL_ERROR "COVERAGE_REPORT_DIR is required") +endif() + +set(_coverage_index "${COVERAGE_REPORT_DIR}/index.html") + +# Never overwrite a real coverage report. +if(EXISTS "${_coverage_index}") + message(STATUS "Coverage index already exists at ${_coverage_index}; leaving it unchanged.") + return() +endif() + +file(MAKE_DIRECTORY "${COVERAGE_REPORT_DIR}") + +file(WRITE "${_coverage_index}" " + + + + + mxlib coverage report + + + + + + + +

Coverage report has not been generated.

+

Run cmake --build _build --target coverage to generate it.

+ + +") + +message(STATUS "Wrote coverage placeholder at ${_coverage_index}") diff --git a/doc/building_tests.dox b/doc/building_tests.dox new file mode 100644 index 000000000..22a505b33 --- /dev/null +++ b/doc/building_tests.dox @@ -0,0 +1,100 @@ +/** \addtogroup testing_building + * @{ + * + * Build tests on demand (always available, even if `MXLIB_BUILD_TESTS=OFF`): + * + * \code{.sh} + * cmake -S . -B _build + * cmake --build _build --target tests -j + * \endcode + * + * Configure with tests enabled: + * + * \code{.sh} + * cmake -S . -B _build -DMXLIB_BUILD_TESTS=ON + * \endcode + * + * With `MXLIB_BUILD_TESTS=ON`, test executables are part of the default build. + * With `MXLIB_BUILD_TESTS=OFF`, they are skipped by default and built only via + * `tests`/`mxlibTests` targets. + * + * Build all test executables: + * + * \code{.sh} + * cmake --build _build --target mxlibTests -j + * \endcode + * + * Run tests: + * + * \code{.sh} + * ctest --test-dir _build --output-on-failure + * \endcode + * + * Run the CTest test suite directly: + * + * \code{.sh} + * cmake --build _build --target mxlibTestRun + * \endcode + * + * Build and run a single test source (Makefile.one equivalent): + * + * \code{.sh} + * cmake -S . -B _build -DMXLIB_BUILD_TESTS=ON -DMXLIB_ONE_TEST=include/math/geo_test.cpp + * cmake --build _build --target mxlibTestOne -j + * cmake --build _build --target mxlibTestOneRun + * \endcode + * + * Coverage generation is integrated into CMake and modeled after the MagAOX flow. + * + * Prerequisites: + * + * \code{.sh} + * lcov --version + * genhtml --version + * \endcode + * + * Generate an HTML coverage report: + * + * \code{.sh} + * cmake -S . -B _build + * cmake --build _build --target coverage + * \endcode + * + * Build Doxygen docs directly from CMake: + * + * \code{.sh} + * cmake --build _build --target docs + * \endcode + * + * Optionally include docs in the default build: + * + * \code{.sh} + * cmake -S . -B _build -DMXLIB_BUILD_DOCS=ON + * \endcode + * + * Optional: tune coverage test timeout (default `300` seconds): + * + * \code{.sh} + * cmake -S . -B _build -DMXLIB_COVERAGE_TEST_TIMEOUT=600 + * \endcode + * + * Coverage artifacts are written under `_build/`: + * + * - `_build/coverage.info` + * - `_build/coverage_filtered.info` + * - `_build/doc/html/coverage/index.html` + * - `_build/doc/html/index.html` + * + * Clean coverage artifacts: + * + * \code{.sh} + * cmake --build _build --target coverage_clean + * \endcode + * + * Convenience scripts are also available: + * + * - `tests/coverage/make_coverage` + * - `tests/coverage/update_coverage` + * + * @} + */ diff --git a/doc/coverage_report.dox b/doc/coverage_report.dox new file mode 100644 index 000000000..83a2a148e --- /dev/null +++ b/doc/coverage_report.dox @@ -0,0 +1,26 @@ +/** \addtogroup testing_coverage + * @{ + * + * \htmlonly + * + * + * \endhtmlonly + * + * @} + */ diff --git a/doc/groupdefs.dox b/doc/groupdefs.dox index 079b07417..0a5b686e9 100644 --- a/doc/groupdefs.dox +++ b/doc/groupdefs.dox @@ -32,7 +32,6 @@ * Various miscellaneous utilities */ - //------------------ error_handling ----------------------- /** \defgroup error_handling_codes Error Codes @@ -564,6 +563,14 @@ /** \defgroup mxlib_testing Testing */ + /** \defgroup testing_building Building Tests + * \ingroup mxlib_testing + */ + + /** \defgroup testing_coverage Coverage Report + * \ingroup mxlib_testing + */ + /** \defgroup unit_tests Unit Tests * \ingroup mxlib_testing */ diff --git a/doc/mxlib_doxygen.in b/doc/mxlib_doxygen.in index 40cb033e8..76ec8f654 100644 --- a/doc/mxlib_doxygen.in +++ b/doc/mxlib_doxygen.in @@ -68,7 +68,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = +OUTPUT_DIRECTORY = @MXLIB_DOXYGEN_OUTPUT_DIRECTORY@ # If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format @@ -935,6 +935,9 @@ WARN_LOGFILE = INPUT = ./groupdefs.dox \ ./main.dox \ + ./testing.dox \ + ./building_tests.dox \ + ./coverage_report.dox \ ./install.dox \ ./sofa.dox \ ./sofa_constants.h \ @@ -1047,7 +1050,9 @@ RECURSIVE = NO # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = ../source/vendor +EXCLUDE = ../source/vendor \ + ../agent_context.md \ + ../README.md # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/doc/testing.dox b/doc/testing.dox new file mode 100644 index 000000000..9a668ef08 --- /dev/null +++ b/doc/testing.dox @@ -0,0 +1,7 @@ +/** \addtogroup mxlib_testing + * @{ + * + * This section documents how to build and run the mxlib tests, and how to generate coverage reports. + * + * @} + */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..1c3238a4c --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,110 @@ +################################################################# +# cmake configuration for mxlib tests +################################################################# + +set(MXLIB_TEST_MAIN_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/testMain.cpp) + +set(MXLIB_TEST_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/include/ao/analysis/aoAtmosphere_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ao/analysis/aoSystem_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ao/analysis/aoPSDs_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/astro/astroDynamics_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ioutils/fileUtils_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ioutils/fits/fitsHeaderCard_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ioutils/fits/fitsFile_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/math/geo_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/math/func/moffat_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/math/templateBLAS_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/math/templateLapack_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/math/randomT_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/sigproc/psdUtils_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/sigproc/psdFilter_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/sigproc/zernike_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/sigproc/signalWindows_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/improc/imageTransforms_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/improc/imageUtils_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/sys/timeUtils_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/improc/imageXCorrFFT_test.cpp +) + +if(MXLIB_USE_CUDA) + list(APPEND MXLIB_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/include/math/cuda/templateCublas_test.cpp) +endif() + +set(MXLIB_TEST_TARGETS) + +function(mxlib_add_test_executable TEST_SOURCE) + file(RELATIVE_PATH _mxlib_test_rel "${CMAKE_CURRENT_SOURCE_DIR}" "${TEST_SOURCE}") + string(REPLACE "/" "_" _mxlib_test_name "${_mxlib_test_rel}") + string(REPLACE "." "_" _mxlib_test_name "${_mxlib_test_name}") + set(_mxlib_test_target "mxlibTest_${_mxlib_test_name}") + + add_executable(${_mxlib_test_target} ${MXLIB_TEST_MAIN_SOURCE} ${TEST_SOURCE}) + target_link_libraries(${_mxlib_test_target} PRIVATE mxlib-shared) + + if(NOT MXLIB_BUILD_TESTS) + set_target_properties(${_mxlib_test_target} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) + endif() + + if(MXLIB_BUILD_TESTS AND BUILD_TESTING) + add_test(NAME ${_mxlib_test_target} COMMAND ${_mxlib_test_target}) + endif() + + set(MXLIB_TEST_TARGETS ${MXLIB_TEST_TARGETS} ${_mxlib_test_target} PARENT_SCOPE) +endfunction() + +foreach(_mxlib_test_source IN LISTS MXLIB_TEST_SOURCES) + mxlib_add_test_executable("${_mxlib_test_source}") +endforeach() + +add_custom_target(mxlibTests DEPENDS ${MXLIB_TEST_TARGETS}) + +if(MXLIB_BUILD_TESTS AND BUILD_TESTING) + add_custom_target(mxlibTestRun + COMMAND ${CMAKE_CTEST_COMMAND} --test-dir ${CMAKE_BINARY_DIR} --output-on-failure + DEPENDS mxlibTests + USES_TERMINAL + ) +endif() + +set(MXLIB_ONE_TEST + "" + CACHE STRING + "Single test source to build, relative to tests/ (e.g. include/math/geo_test.cpp), similar to tests/Makefile.one" +) + +if(MXLIB_ONE_TEST) + if(IS_ABSOLUTE "${MXLIB_ONE_TEST}") + set(MXLIB_ONE_TEST_SOURCE "${MXLIB_ONE_TEST}") + else() + set(MXLIB_ONE_TEST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${MXLIB_ONE_TEST}") + endif() + + if(MXLIB_ONE_TEST_SOURCE MATCHES "\\.o$") + string(REGEX REPLACE "\\.o$" ".cpp" MXLIB_ONE_TEST_SOURCE "${MXLIB_ONE_TEST_SOURCE}") + elseif(MXLIB_ONE_TEST_SOURCE MATCHES "\\.hpp$") + string(REGEX REPLACE "\\.hpp$" ".cpp" MXLIB_ONE_TEST_SOURCE "${MXLIB_ONE_TEST_SOURCE}") + elseif(NOT MXLIB_ONE_TEST_SOURCE MATCHES "\\.cpp$") + string(APPEND MXLIB_ONE_TEST_SOURCE ".cpp") + endif() + + if(NOT EXISTS "${MXLIB_ONE_TEST_SOURCE}") + message(FATAL_ERROR "MXLIB_ONE_TEST source does not exist: ${MXLIB_ONE_TEST_SOURCE}") + endif() + + add_executable(mxlibTestOne ${MXLIB_TEST_MAIN_SOURCE} ${MXLIB_ONE_TEST_SOURCE}) + target_link_libraries(mxlibTestOne PRIVATE mxlib-shared) + if(NOT MXLIB_BUILD_TESTS) + set_target_properties(mxlibTestOne PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) + endif() + + add_custom_target(mxlibTestOneRun + COMMAND $ + DEPENDS mxlibTestOne + USES_TERMINAL + ) + + if(MXLIB_BUILD_TESTS AND BUILD_TESTING) + add_test(NAME mxlibTestOne COMMAND mxlibTestOne) + endif() +endif() diff --git a/tests/coverage/coverage_epilog.html b/tests/coverage/coverage_epilog.html new file mode 100644 index 000000000..04f5b8449 --- /dev/null +++ b/tests/coverage/coverage_epilog.html @@ -0,0 +1 @@ + diff --git a/tests/coverage/coverage_prolog.html b/tests/coverage/coverage_prolog.html new file mode 100644 index 000000000..3bdb46a9b --- /dev/null +++ b/tests/coverage/coverage_prolog.html @@ -0,0 +1,41 @@ +
+
+
Documentation
+ +
+
+
+ diff --git a/tests/coverage/gcov.css b/tests/coverage/gcov.css new file mode 100644 index 000000000..bcbb7d0a2 --- /dev/null +++ b/tests/coverage/gcov.css @@ -0,0 +1,63 @@ +/* Match coverage pages to mxlib Doxygen theme. */ +@import url("../doxygen.css"); +@import url("../tabs.css"); +@import url("../doxygen-awesome.css"); +@import url("../doxygen-awesome-sidebar-only.css"); +@import url("../doxygen-awesome-sidebar-only-darkmode-toggle.css"); + +body { + margin: 0; + padding: 1rem; +} + +body > table:first-child, +body > center > table { + width: min(1400px, 100%); + margin: 0 auto; +} + +th, +td { + padding: 0.3rem 0.45rem; +} + +/* Keep lcov source readability aligned with Doxygen mono typography. */ +.source, +.source a { + font-family: var(--font-family-monospace, monospace); + font-size: 0.92rem; +} + +/* Core line hit/miss coloring used by genhtml. + Newer lcov/genhtml uses tla* classes on source rows/spans. */ +td.lineCov, +span.lineCov, +td.tlaGNC, +span.tlaGNC, +a.tlaGNC { + background-color: #1a4f78 !important; +} + +td.lineNoCov, +span.lineNoCov, +td.tlaUNC, +span.tlaUNC, +a.tlaUNC { + background-color: #5a2222 !important; +} + +/* Ensure source line numbers/counters remain readable over highlight colors. */ +.source, +.source * { + color: var(--page-foreground-color, #d2dbde) !important; +} + +.lineNum { + color: var(--page-secondary-foreground-color, #9aa6ad) !important; +} + +/* Keep bar cells neutral; doxygen tables handle borders/background. */ +td.coverBar > table { + background: transparent; + border: 0; +} diff --git a/tests/coverage/make_coverage b/tests/coverage/make_coverage new file mode 100755 index 000000000..73f23b824 --- /dev/null +++ b/tests/coverage/make_coverage @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -eo pipefail + +# Configure default build tree, then run the CMake coverage workflow. +cmake -S . -B _build +cmake --build _build --target coverage diff --git a/tests/coverage/update_coverage b/tests/coverage/update_coverage new file mode 100755 index 000000000..35c9ddfd2 --- /dev/null +++ b/tests/coverage/update_coverage @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -eo pipefail + +# Re-run coverage workflow in the configured build tree. +cmake --build _build --target coverage diff --git a/tests/include/ao/sim/pyramidSensor_test.cpp b/tests/include/ao/sim/pyramidSensor_test.cpp index c713c1592..7477a62cf 100644 --- a/tests/include/ao/sim/pyramidSensor_test.cpp +++ b/tests/include/ao/sim/pyramidSensor_test.cpp @@ -4,25 +4,18 @@ #define MX_NO_ERROR_REPORTS -//#define DEBUG - - -#include "../../../../include/ioutils/fits/fitsFile.hpp" -using namespace mx::fits; +// #define DEBUG #include "../../../../include/ao/sim/pyramidSensor.hpp" #include "../../../../include/ao/sim/ccdDetector.hpp" using namespace mx::AO::sim; - #include "../../../../include/improc/eigenImage.hpp" #include "../../../../include/improc/imageMasks.hpp" -//#include -//#include +// #include +// #include using namespace mx::improc; - - /// Simulate a pyramid sensor on CPU /** * \ingroup ao_sim_pyramidSensor_tests @@ -37,60 +30,53 @@ TEST_CASE( "Simulate a pyramid sensor on CPU", "[ao::sim]" ) pyramidSensor> pwfs; eigenImage pupil, fm; - pupil.resize(pupSz, pupSz); - pupil.setConstant(0); - maskCircle(pupil, 0.5 * (1.0 * pupSz - 1), 1); + pupil.resize( pupSz, pupSz ); + pupil.setConstant( 0 ); + maskCircle( pupil, 0.5 * ( 1.0 * pupSz - 1 ), 1 ); wavefront wf; realT nphperpix = 1e10 / pupil.sum(); - wf.setAmplitude(pupil * sqrt(nphperpix)); + wf.setAmplitude( pupil * sqrt( nphperpix ) ); - fm.resize(pupSz, pupSz); + fm.resize( pupSz, pupSz ); - pwfs.D(6.5); - pwfs.pupilSz(pupSz); - pwfs.wfSz(wfSz); - pwfs.pupilSep(1 + 4.1 / 56.); + pwfs.D( 6.5 ); + pwfs.pupilSz( pupSz ); + pwfs.wfSz( wfSz ); + pwfs.pupilSep( 1 + 4.1 / 56. ); - pwfs.perStep(0.5); - pwfs.modRadius(3); + pwfs.perStep( 0.5 ); + pwfs.modRadius( 3 ); - pwfs.detSize(120, 120); - pwfs.detector.noNoise(true); - pwfs.detector.expTime(1); + pwfs.detSize( 120, 120 ); + pwfs.detector.noNoise( true ); + pwfs.detector.expTime( 1 ); - pwfs.lambda(850e-9); + pwfs.lambda( 850e-9 ); - wf.setPhase(fm * 0); + wf.setPhase( fm * 0 ); - pwfs.senseWavefrontCal(wf); + pwfs.senseWavefrontCal( wf ); size_t N = 10; double t0 = mx::sys::get_curr_time(); - for(size_t n = 0; n < N; ++n) + for( size_t n = 0; n < N; ++n ) { - pwfs.senseWavefrontCal(wf); + pwfs.senseWavefrontCal( wf ); } double t1 = mx::sys::get_curr_time(); - std::cerr << "\nCPU: " << 1.0*N / (t1-t0) << " fps\n\n"; + std::cerr << "\nCPU: " << 1.0 * N / ( t1 - t0 ) << " fps\n\n"; eigenImage ref = pwfs.detectorImage.image(); - fitsFile ff; - ff.write("ref.fits", ref); - ff.write("tip.fits", pwfs.detectorImage.tipImage()); - realT s = ref.sum(); std::cout << s << '\n'; - REQUIRE(s > 9.9e9); - + REQUIRE( s > 9.9e9 ); } - - /// Simulate a pyramid sensor on GPU /** * \ingroup ao_sim_pyramidSensor_tests @@ -102,61 +88,54 @@ TEST_CASE( "Simulate a pyramid sensor on GPU", "[ao::sim]" ) uint32_t pupSz = 56.0; uint32_t wfSz = 256.0; - omp_set_num_threads(4); + omp_set_num_threads( 4 ); pyramidSensor, 1> pwfs; eigenImage pupil, fm; - pupil.resize(pupSz, pupSz); - pupil.setConstant(0); - maskCircle(pupil, 0.5 * (1.0 * pupSz - 1), 1); + pupil.resize( pupSz, pupSz ); + pupil.setConstant( 0 ); + maskCircle( pupil, 0.5 * ( 1.0 * pupSz - 1 ), 1 ); wavefront wf; realT nphperpix = 1e10 / pupil.sum(); - wf.setAmplitude(pupil * sqrt(nphperpix)); + wf.setAmplitude( pupil * sqrt( nphperpix ) ); - fm.resize(pupSz, pupSz); + fm.resize( pupSz, pupSz ); - pwfs.D(6.5); - pwfs.pupilSz(pupSz); - pwfs.wfSz(wfSz); - pwfs.pupilSep(1 + 4.1 / 56.); + pwfs.D( 6.5 ); + pwfs.pupilSz( pupSz ); + pwfs.wfSz( wfSz ); + pwfs.pupilSep( 1 + 4.1 / 56. ); - pwfs.perStep(0.5); - pwfs.modRadius(3); + pwfs.perStep( 0.5 ); + pwfs.modRadius( 3 ); - pwfs.detSize(120, 120); - pwfs.detector.noNoise(true); - pwfs.detector.expTime(1); + pwfs.detSize( 120, 120 ); + pwfs.detector.noNoise( true ); + pwfs.detector.expTime( 1 ); - pwfs.lambda(850e-9); + pwfs.lambda( 850e-9 ); - wf.setPhase(fm * 0); + wf.setPhase( fm * 0 ); - pwfs.senseWavefrontCal(wf); + pwfs.senseWavefrontCal( wf ); size_t N = 10; double t0 = mx::sys::get_curr_time(); - for(size_t n = 0; n < N; ++n) + for( size_t n = 0; n < N; ++n ) { - pwfs.senseWavefrontCal(wf); + pwfs.senseWavefrontCal( wf ); } double t1 = mx::sys::get_curr_time(); - std::cerr << "\nGPU: " << 1.0*N / (t1-t0) << " fps\n\n"; + std::cerr << "\nGPU: " << 1.0 * N / ( t1 - t0 ) << " fps\n\n"; eigenImage ref = pwfs.detectorImage.image(); - fitsFile ff; - ff.write("refgpu.fits", ref); - ff.write("tipgpu.fits", pwfs.detectorImage.tipImage()); - realT s = ref.sum(); std::cout << s << '\n'; - REQUIRE(s > 9.9e9); - + REQUIRE( s > 9.9e9 ); } - - diff --git a/tests/include/improc/imageTransforms_test.cpp b/tests/include/improc/imageTransforms_test.cpp index 5e3638e1f..bc50e8777 100644 --- a/tests/include/improc/imageTransforms_test.cpp +++ b/tests/include/improc/imageTransforms_test.cpp @@ -14,7 +14,13 @@ #include "../../../include/improc/imageTransforms.hpp" -#include "../../../include/math/fit/fitGaussian.hpp" +namespace +{ +double imageMSE( const mx::improc::eigenImage &a, const mx::improc::eigenImage &b ) +{ + return ( a - b ).square().mean(); +} +} // namespace /** Scenario: Verify direction and accuracy of various image shifts * @@ -28,48 +34,38 @@ SCENARIO( "Verify direction and accuracy of various image shifts", "[improc::ima { WHEN( "shifting" ) { - mx::improc::eigenImage im, shift; - mx::math::fit::fitGaussian2Dsym fit; - double x, y; + mx::improc::eigenImage im, shift, ref; im.resize( 256, 256 ); shift.resize( im.rows(), im.cols() ); - - fit.setArray( shift.data(), shift.rows(), shift.cols() ); + ref.resize( im.rows(), im.cols() ); // Use sigma = 8 to get a well oversampled image, making shifts more accurate mx::math::func::gaussian2D( im.data(), im.rows(), im.cols(), 0., 1.0, 127.5, 127.5, 8 ); - fit.setGuess( 0.0, 1.0, 127.5, 127.5, 8.0 ); mx::improc::imageShift( shift, im, -0.5, -0.5, mx::improc::cubicConvolTransform() ); - fit.fit(); - REQUIRE( fabs( fit.x0() - 127.0 ) < 1e-4 ); // should be much better than this, but this is a test - REQUIRE( fabs( fit.y0() - 127.0 ) < 1e-4 ); + mx::math::func::gaussian2D( ref.data(), ref.rows(), ref.cols(), 0., 1.0, 127.0, 127.0, 8 ); + REQUIRE_THAT( imageMSE( shift, ref ), Catch::Matchers::WithinAbs( 0.0, 1e-5 ) ); mx::improc::imageShift( shift, im, +0.5, +0.5, mx::improc::cubicConvolTransform() ); - fit.fit(); - REQUIRE( fabs( fit.x0() - 128.0 ) < 1e-4 ); - REQUIRE( fabs( fit.y0() - 128.0 ) < 1e-4 ); + mx::math::func::gaussian2D( ref.data(), ref.rows(), ref.cols(), 0., 1.0, 128.0, 128.0, 8 ); + REQUIRE_THAT( imageMSE( shift, ref ), Catch::Matchers::WithinAbs( 0.0, 1e-5 ) ); mx::improc::imageShift( shift, im, +1.0, +1.0, mx::improc::cubicConvolTransform() ); - fit.fit(); - REQUIRE( fabs( fit.x0() - 128.5 ) < 1e-4 ); - REQUIRE( fabs( fit.y0() - 128.5 ) < 1e-4 ); + mx::math::func::gaussian2D( ref.data(), ref.rows(), ref.cols(), 0., 1.0, 128.5, 128.5, 8 ); + REQUIRE_THAT( imageMSE( shift, ref ), Catch::Matchers::WithinAbs( 0.0, 1e-5 ) ); mx::improc::imageShift( shift, im, +0.5, -0.5, mx::improc::cubicConvolTransform() ); - fit.fit(); - REQUIRE( fabs( fit.x0() - 128.0 ) < 1e-4 ); - REQUIRE( fabs( fit.y0() - 127.0 ) < 1e-4 ); + mx::math::func::gaussian2D( ref.data(), ref.rows(), ref.cols(), 0., 1.0, 128.0, 127.0, 8 ); + REQUIRE_THAT( imageMSE( shift, ref ), Catch::Matchers::WithinAbs( 0.0, 1e-5 ) ); mx::improc::imageShift( shift, im, -0.3, +0.7, mx::improc::cubicConvolTransform() ); - fit.fit(); - REQUIRE( fabs( fit.x0() - 127.2 ) < 1e-3 ); // non-0.5 pixel shifts are harder - REQUIRE( fabs( fit.y0() - 128.2 ) < 1e-3 ); + mx::math::func::gaussian2D( ref.data(), ref.rows(), ref.cols(), 0., 1.0, 127.2, 128.2, 8 ); + REQUIRE_THAT( imageMSE( shift, ref ), Catch::Matchers::WithinAbs( 0.0, 1e-5 ) ); mx::improc::imageShift( shift, im, 1.3, -0.7, mx::improc::cubicConvolTransform() ); - fit.fit(); - REQUIRE( fabs( fit.x0() - 128.8 ) < 1e-3 ); - REQUIRE( fabs( fit.y0() - 126.8 ) < 1e-3 ); + mx::math::func::gaussian2D( ref.data(), ref.rows(), ref.cols(), 0., 1.0, 128.8, 126.8, 8 ); + REQUIRE_THAT( imageMSE( shift, ref ), Catch::Matchers::WithinAbs( 0.0, 1e-5 ) ); } } } diff --git a/tests/include/improc/imageUtils_test.cpp b/tests/include/improc/imageUtils_test.cpp index 455a43bac..134f35f02 100644 --- a/tests/include/improc/imageUtils_test.cpp +++ b/tests/include/improc/imageUtils_test.cpp @@ -31,8 +31,8 @@ SCENARIO( "Verify center of light calculation", "[improc::imageCenterOfLight]" ) double x, y; mx::improc::imageCenterOfLight( x, y, im ); - REQUIRE( fabs( x - 31.5 ) < 1e-8 ); - REQUIRE( fabs( y - 31.5 ) < 1e-8 ); + REQUIRE_THAT( x, Catch::Matchers::WithinAbs( 31.5, 1e-8 ) ); + REQUIRE_THAT( y, Catch::Matchers::WithinAbs( 31.5, 1e-8 ) ); } WHEN( "geometric quarter" ) { @@ -44,8 +44,8 @@ SCENARIO( "Verify center of light calculation", "[improc::imageCenterOfLight]" ) double x, y; mx::improc::imageCenterOfLight( x, y, im ); - REQUIRE( fabs( x - 15.5 ) < 1e-8 ); - REQUIRE( fabs( y - 15.5 ) < 1e-8 ); + REQUIRE_THAT( x, Catch::Matchers::WithinAbs( 15.5, 1e-8 ) ); + REQUIRE_THAT( y, Catch::Matchers::WithinAbs( 15.5, 1e-8 ) ); } } } diff --git a/tests/include/improc/imageXCorrDiscrete_test.cpp b/tests/include/improc/imageXCorrDiscrete_test.cpp index 877499a81..5b3534cf0 100644 --- a/tests/include/improc/imageXCorrDiscrete_test.cpp +++ b/tests/include/improc/imageXCorrDiscrete_test.cpp @@ -10,7 +10,6 @@ #include "../../../include/math/func/gaussian.hpp" #include "../../../include/improc/imageXCorrDiscrete.hpp" #include "../../../include/improc/eigenCube.hpp" -#include "../../../include/ioutils/fits/fitsFile.hpp" /** Scenario: centroiding Gaussians with center of light * @@ -40,14 +39,10 @@ SCENARIO( "Verify X-Corr with center of light calculation", "[improc::imageXCorr xcf( x, y, im2 ); - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.m_refIm ); - ff.write( "ccIm.fits", xcf.m_ccIm ); - std::cerr << x << " " << y << "\n"; - REQUIRE( fabs( x - 2 ) < 1e-8 ); - REQUIRE( fabs( y - 2 ) < 1e-8 ); + REQUIRE_THAT( x, Catch::Matchers::WithinAbs( 2, 1e-8 ) ); + REQUIRE_THAT( y, Catch::Matchers::WithinAbs( 2, 1e-8 ) ); } WHEN( "geometric quarter" ) { @@ -59,8 +54,8 @@ SCENARIO( "Verify X-Corr with center of light calculation", "[improc::imageXCorr double x, y; mx::improc::imageCenterOfLight( x, y, im ); - REQUIRE( fabs( x - 15.5 ) < 1e-8 ); - REQUIRE( fabs( y - 15.5 ) < 1e-8 ); + REQUIRE_THAT( x, Catch::Matchers::WithinAbs( 15.5, 1e-8 ) ); + REQUIRE_THAT( y, Catch::Matchers::WithinAbs( 15.5, 1e-8 ) ); } } } diff --git a/tests/include/improc/imageXCorrFFT_test.cpp b/tests/include/improc/imageXCorrFFT_test.cpp index 98463fcf6..9850e500c 100644 --- a/tests/include/improc/imageXCorrFFT_test.cpp +++ b/tests/include/improc/imageXCorrFFT_test.cpp @@ -10,7 +10,6 @@ #include "../../../include/math/func/gaussian.hpp" #include "../../../include/improc/imageXCorrFFT.hpp" #include "../../../include/improc/eigenCube.hpp" -#include "../../../include/ioutils/fits/fitsFile.hpp" /** Scenario: centroiding Gaussians with center of light * @@ -37,24 +36,27 @@ SCENARIO( "Image cross-correlation with FFT using center of light", "[improc::im double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); xcf.peakMethod( mx::improc::xcorrPeakMethod::centerOfLight ); xcf.maxLag( 20 ); xcf.refIm( refIm ); - xcf( x, y, im2 ); - - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } WHEN( "ref at geometric center, equal odd sizes, shift=(+4,+4)" ) { @@ -71,24 +73,29 @@ SCENARIO( "Image cross-correlation with FFT using center of light", "[improc::im double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; + xcf.peakMethod( mx::improc::xcorrPeakMethod::centerOfLight ); + xcf.maxLag( 20 ); mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); xcf.refIm( refIm ); - xcf.peakMethod( mx::improc::xcorrPeakMethod::centerOfLight ); - xcf.maxLag( 20 ); - xcf( x, y, im2 ); - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } WHEN( "ref at geometric center, equal even sizes, shift=(-3.6,+2.25)" ) { @@ -105,24 +112,28 @@ SCENARIO( "Image cross-correlation with FFT using center of light", "[improc::im double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; + xcf.peakMethod( mx::improc::xcorrPeakMethod::centerOfLight ); + xcf.maxLag( 20 ); mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); xcf.refIm( refIm ); - xcf.peakMethod( mx::improc::xcorrPeakMethod::centerOfLight ); - xcf.maxLag( 20 ); - xcf( x, y, im2 ); - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } WHEN( "ref at geometric center, equal odd sizes, shift=(+1.3,-0.6)" ) { @@ -139,24 +150,28 @@ SCENARIO( "Image cross-correlation with FFT using center of light", "[improc::im double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; + xcf.peakMethod( mx::improc::xcorrPeakMethod::centerOfLight ); + xcf.maxLag( 20 ); mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); xcf.refIm( refIm ); - xcf.peakMethod( mx::improc::xcorrPeakMethod::centerOfLight ); - xcf.maxLag( 20 ); - xcf( x, y, im2 ); - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } } } @@ -186,24 +201,28 @@ SCENARIO( "Image cross-correlation with FFT using magnification peak finding", " double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; - - mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); xcf.peakMethod( mx::improc::xcorrPeakMethod::interpPeak ); xcf.maxLag( 5 ); - xcf.refIm( refIm ); - xcf( x, y, im2 ); - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); + + xcf.refIm( refIm ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } WHEN( "ref at geometric center, equal odd sizes, shift=(+4,+4)" ) { @@ -220,10 +239,16 @@ SCENARIO( "Image cross-correlation with FFT using magnification peak finding", " double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); @@ -232,14 +257,11 @@ SCENARIO( "Image cross-correlation with FFT using magnification peak finding", " xcf.maxLag( 5 ); xcf.refIm( refIm ); - xcf( x, y, im2 ); - - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } WHEN( "ref at geometric center, equal even sizes, shift=(-3.6,+2.3)" ) { @@ -256,24 +278,28 @@ SCENARIO( "Image cross-correlation with FFT using magnification peak finding", " double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; + xcf.peakMethod( mx::improc::xcorrPeakMethod::interpPeak ); + xcf.tol( 0.1 ); mx::improc::eigenImage refIm = im0; xcf.refIm( refIm ); - xcf.peakMethod( mx::improc::xcorrPeakMethod::interpPeak ); - xcf.tol( 0.1 ); - xcf( x, y, im2 ); - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } WHEN( "ref at geometric center, equal odd sizes, shift=(+1.3,-0.6)" ) { @@ -290,24 +316,28 @@ SCENARIO( "Image cross-correlation with FFT using magnification peak finding", " double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; + xcf.peakMethod( mx::improc::xcorrPeakMethod::interpPeak ); + xcf.maxLag( 5 ); mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); xcf.refIm( refIm ); - xcf.peakMethod( mx::improc::xcorrPeakMethod::interpPeak ); - xcf.maxLag( 5 ); - xcf( x, y, im2 ); - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } } } @@ -331,24 +361,27 @@ SCENARIO( "Image cross-correlation with FFT using Gaussian peak fit", "[improc:: double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; mx::improc::eigenImage refIm = im0; xcf.peakMethod( mx::improc::xcorrPeakMethod::gaussFit ); xcf.maxLag( 32 ); xcf.refIm( refIm ); - xcf( x, y, im2 ); - - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } WHEN( "ref at geometric center, equal odd sizes, shift=(+4,+4)" ) { @@ -365,24 +398,28 @@ SCENARIO( "Image cross-correlation with FFT using Gaussian peak fit", "[improc:: double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; + xcf.peakMethod( mx::improc::xcorrPeakMethod::gaussFit ); + xcf.maxLag( 32 ); mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); xcf.refIm( refIm ); - xcf.peakMethod( mx::improc::xcorrPeakMethod::gaussFit ); - xcf.maxLag( 32 ); - xcf( x, y, im2 ); - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } WHEN( "ref at geometric center, equal even sizes, shift=(-3.6,+2.25)" ) { @@ -399,10 +436,16 @@ SCENARIO( "Image cross-correlation with FFT using Gaussian peak fit", "[improc:: double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); @@ -410,14 +453,11 @@ SCENARIO( "Image cross-correlation with FFT using Gaussian peak fit", "[improc:: xcf.maxLag( 32 ); xcf.refIm( refIm ); - xcf( x, y, im2 ); - - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } WHEN( "ref at geometric center, equal odd sizes, shift=(+1.3,-0.6)" ) { @@ -434,24 +474,28 @@ SCENARIO( "Image cross-correlation with FFT using Gaussian peak fit", "[improc:: double ycen = 0.5 * ( im0.cols() - 1 ); mx::math::func::gaussian2D( im0.data(), im0.rows(), im0.cols(), 0., 1.0, xcen, ycen, 2 ); - mx::math::func::gaussian2D( - im2.data(), im2.rows(), im2.cols(), 0., 1.0, xcen + xshift, ycen + yshift, 2 ); - - double x, y; + mx::math::func::gaussian2D( im2.data(), + im2.rows(), + im2.cols(), + 0., + 1.0, + xcen + xshift, + ycen + yshift, + 2 ); + + double x, y, peak; mx::improc::imageXCorrFFT> xcf; + xcf.peakMethod( mx::improc::xcorrPeakMethod::gaussFit ); + xcf.maxLag( 32 ); mx::improc::eigenImage refIm = im0; //.block(10,10, im0.rows()-11, im0.cols()-11); xcf.refIm( refIm ); - xcf.peakMethod( mx::improc::xcorrPeakMethod::gaussFit ); - xcf.maxLag( 32 ); - xcf( x, y, im2 ); - mx::fits::fitsFile ff; - ff.write( "refIm.fits", xcf.refIm() ); - ff.write( "ccIm.fits", xcf.ccIm() ); + xcf( x, y, peak, im2 ); REQUIRE( x == Approx( xshift ) ); REQUIRE( y == Approx( yshift ) ); + REQUIRE( peak > 0 ); } } } diff --git a/tests/include/ioutils/stringUtils_test.cpp b/tests/include/ioutils/stringUtils_test.cpp index 2a5c7c4fc..de19c03de 100644 --- a/tests/include/ioutils/stringUtils_test.cpp +++ b/tests/include/ioutils/stringUtils_test.cpp @@ -583,16 +583,14 @@ TEST_CASE( "Converting strings to numbers", "[ioutils::stringUtils]" ) SECTION( "string valid, positive, no error check" ) { long double val = stoT( "22.2567" ); - REQUIRE( fabs( val - static_cast( 22.2567 ) ) < - fabs( 1e-9 * static_cast( 22.2567 ) ) ); + REQUIRE_THAT( static_cast( val ), Catch::Matchers::WithinRel( 22.2567, 1e-9 ) ); } SECTION( "string valid, negative, w/ error check" ) { mx::error_t errc; long double val = stoT( "-2300000.897987", &errc ); - REQUIRE( fabs( val - static_cast( -2300000.897987 ) ) < - fabs( 1e-9 * static_cast( -2300000.897987 ) ) ); + REQUIRE_THAT( static_cast( val ), Catch::Matchers::WithinRel( -2300000.897987, 1e-9 ) ); REQUIRE( errc == mx::error_t::noerror ); } diff --git a/tests/include/math/ft/fftT_test.cpp b/tests/include/math/ft/fftT_test.cpp index d53fcb64a..250bcebdb 100644 --- a/tests/include/math/ft/fftT_test.cpp +++ b/tests/include/math/ft/fftT_test.cpp @@ -32,7 +32,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) float sout = out.abs2().sum() / out.rows(); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "out-of-place, forward, default constructed, eigen interface" ) @@ -50,7 +50,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) float sout = out.abs2().sum() / out.rows(); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "out-of-place, forward, plan constructor" ) @@ -66,7 +66,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) float sout = out.abs2().sum() / out.rows(); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "out-of-place, backward" ) @@ -82,7 +82,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) float sout = out.abs2().sum(); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "in-place, forward" ) @@ -98,13 +98,13 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) float sout = in.abs2().sum() / in.rows(); - float rmsdiff = (in-incheck).abs2().sum(); + float rmsdiff = ( in - incheck ).abs2().sum(); - //Make sure it isn't ident - REQUIRE(rmsdiff > 0); + // Make sure it isn't ident + REQUIRE( rmsdiff > 0 ); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "in-place, backward" ) @@ -120,15 +120,15 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) fft( in, in ); - float sout = in.abs2().sum()/in.rows(); + float sout = in.abs2().sum() / in.rows(); - float rmsdiff = (in-incheck).abs2().sum(); + float rmsdiff = ( in - incheck ).abs2().sum(); - //Make sure it isn't ident - REQUIRE(rmsdiff > 0); + // Make sure it isn't ident + REQUIRE( rmsdiff > 0 ); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } } @@ -151,11 +151,11 @@ TEST_CASE( "2D c2c FFT with FFTW, float", "[math::ft]" ) fft( out.data(), in.data() ); - float sin = in.abs2().sum() * (out.rows()*out.cols()) ; + float sin = in.abs2().sum() * ( out.rows() * out.cols() ); float sout = out.abs2().sum(); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } SECTION( "out-of-place, forward, default constructed, eigen interface" ) @@ -170,26 +170,27 @@ TEST_CASE( "2D c2c FFT with FFTW, float", "[math::ft]" ) fft( out, in ); float sin = in.abs2().sum(); - float sout = out.abs2().sum() / (out.rows()*out.cols()); + float sout = out.abs2().sum() / ( out.rows() * out.cols() ); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } SECTION( "out-of-place, forward, plan constructor" ) { mx::math::ft::fftT, std::complex, 2> fft( 128, 128 ); - mx::improc::eigenImage> in( 128, 128 ), out( 128, 128 );; + mx::improc::eigenImage> in( 128, 128 ), out( 128, 128 ); + ; in.setRandom(); fft( out, in ); float sin = in.abs2().sum(); - float sout = out.abs2().sum() / (in.rows()*out.cols()); + float sout = out.abs2().sum() / ( in.rows() * out.cols() ); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } SECTION( "out-of-place, backward" ) @@ -201,16 +202,19 @@ TEST_CASE( "2D c2c FFT with FFTW, float", "[math::ft]" ) fft( in, out ); - float sin = in.abs2().sum() / (in.rows()*in.cols()); + float sin = in.abs2().sum() / ( in.rows() * in.cols() ); float sout = out.abs2().sum(); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } SECTION( "in-place, forward" ) { - mx::math::ft::fftT, std::complex, 2> fft( 128, 128, mx::math::ft::dir::forward, true ); + mx::math::ft::fftT, std::complex, 2> fft( 128, + 128, + mx::math::ft::dir::forward, + true ); mx::improc::eigenImage> in( 128, 128 ); in.setRandom(); @@ -219,20 +223,23 @@ TEST_CASE( "2D c2c FFT with FFTW, float", "[math::ft]" ) fft( in, in ); - float sout = in.abs2().sum() / (in.rows()*in.cols()); + float sout = in.abs2().sum() / ( in.rows() * in.cols() ); - float rmsdiff = (in-incheck).abs2().sum(); + float rmsdiff = ( in - incheck ).abs2().sum(); - //Make sure it isn't ident - REQUIRE(rmsdiff > 0); + // Make sure it isn't ident + REQUIRE( rmsdiff > 0 ); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } SECTION( "in-place, backward" ) { - mx::math::ft::fftT, std::complex, 2> fft( 128, 128, mx::math::ft::dir::backward, true ); + mx::math::ft::fftT, std::complex, 2> fft( 128, + 128, + mx::math::ft::dir::backward, + true ); mx::improc::eigenImage> in( 128, 128 ); in.setRandom(); @@ -243,15 +250,15 @@ TEST_CASE( "2D c2c FFT with FFTW, float", "[math::ft]" ) fft( in, in ); - float sout = in.abs2().sum()/(in.rows()*in.cols()); + float sout = in.abs2().sum() / ( in.rows() * in.cols() ); - float rmsdiff = (in-incheck).abs2().sum(); + float rmsdiff = ( in - incheck ).abs2().sum(); - //Make sure it isn't ident - REQUIRE(rmsdiff > 0); + // Make sure it isn't ident + REQUIRE( rmsdiff > 0 ); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } } #ifdef HASQUAD diff --git a/tests/include/math/ft/fftTcuda_test.cpp b/tests/include/math/ft/fftTcuda_test.cpp index 9b627a389..de6f3f2da 100644 --- a/tests/include/math/ft/fftTcuda_test.cpp +++ b/tests/include/math/ft/fftTcuda_test.cpp @@ -6,7 +6,6 @@ #include - #include "../../../../include/math/ft/fftT.hpp" #include "../../../../include/improc/eigenImage.hpp" @@ -35,7 +34,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) float sout = out.abs2().sum() / out.rows(); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "out-of-place, forward, default constructed, eigen interface" ) @@ -53,7 +52,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) float sout = out.abs2().sum() / out.rows(); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "out-of-place, forward, plan constructor" ) @@ -69,7 +68,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) float sout = out.abs2().sum() / out.rows(); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "out-of-place, backward" ) @@ -85,7 +84,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) float sout = out.abs2().sum(); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "in-place, forward" ) @@ -107,7 +106,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) REQUIRE(rmsdiff > 0); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } SECTION( "in-place, backward" ) @@ -131,7 +130,7 @@ TEST_CASE( "1D c2c FFT with FFTW, float", "[math::ft]" ) REQUIRE(rmsdiff > 0); // Test by Parsevals - REQUIRE( fabs( sin - sout ) < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, 1e-3 ) ); } } #endif @@ -155,20 +154,20 @@ TEST_CASE( "2D c2c FFT with cuFFT, float", "[math::ft]" ) out.setZero(); mx::cuda::cudaPtr> devIn, devOut; - devIn.upload(in.data(), in.rows(), in.cols()); - devOut.resize(out.rows(), out.cols()); + devIn.upload( in.data(), in.rows(), in.cols() ); + devOut.resize( out.rows(), out.cols() ); cufftResult rv = fft( devOut.data(), devIn.data() ); - REQUIRE(rv == CUFFT_SUCCESS); + REQUIRE( rv == CUFFT_SUCCESS ); - devOut.download(out.data()); + devOut.download( out.data() ); - float sin = in.abs2().sum() * (out.rows()*out.cols()) ; + float sin = in.abs2().sum() * ( out.rows() * out.cols() ); float sout = out.abs2().sum(); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } SECTION( "out-of-place, forward, default constructed, eigen interface" ) @@ -182,48 +181,47 @@ TEST_CASE( "2D c2c FFT with cuFFT, float", "[math::ft]" ) out.setZero(); mx::cuda::cudaPtr> devIn, devOut; - devIn.upload(in.data(), in.rows(), in.cols()); - devOut.resize(out.rows(), out.cols()); + devIn.upload( in.data(), in.rows(), in.cols() ); + devOut.resize( out.rows(), out.cols() ); cufftResult rv = fft( devOut, devIn ); - REQUIRE(rv == CUFFT_SUCCESS); - - devOut.download(out.data()); + REQUIRE( rv == CUFFT_SUCCESS ); + devOut.download( out.data() ); float sin = in.abs2().sum(); - float sout = out.abs2().sum() / (out.rows()*out.cols()); + float sout = out.abs2().sum() / ( out.rows() * out.cols() ); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } SECTION( "out-of-place, forward, plan constructor" ) { mx::math::ft::fftT, std::complex, 2, 1> fft( 128, 128 ); - mx::improc::eigenImage> in( 128, 128 ), out( 128, 128 );; + mx::improc::eigenImage> in( 128, 128 ), out( 128, 128 ); + ; out.setZero(); mx::cuda::cudaPtr> devIn, devOut; - devIn.upload(in.data(), in.rows(), in.cols()); - devOut.resize(out.rows(), out.cols()); + devIn.upload( in.data(), in.rows(), in.cols() ); + devOut.resize( out.rows(), out.cols() ); cufftResult rv = fft( devOut, devIn ); - REQUIRE(rv == CUFFT_SUCCESS); + REQUIRE( rv == CUFFT_SUCCESS ); - devOut.download(out.data()); + devOut.download( out.data() ); float sin = in.abs2().sum(); - float sout = out.abs2().sum() / (in.rows()*out.cols()); + float sout = out.abs2().sum() / ( in.rows() * out.cols() ); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } - SECTION( "out-of-place, backward" ) { mx::math::ft::fftT, std::complex, 2, 1> fft( 128, 128, mx::math::ft::dir::backward ); @@ -233,68 +231,70 @@ TEST_CASE( "2D c2c FFT with cuFFT, float", "[math::ft]" ) in.setZero(); mx::cuda::cudaPtr> devIn, devOut; - devIn.upload(in.data(), in.rows(), in.cols()); - devOut.resize(out.rows(), out.cols()); + devIn.upload( in.data(), in.rows(), in.cols() ); + devOut.resize( out.rows(), out.cols() ); cufftResult rv = fft( devOut, devIn ); - REQUIRE(rv == CUFFT_SUCCESS); + REQUIRE( rv == CUFFT_SUCCESS ); - devOut.download(out.data()); + devOut.download( out.data() ); - float sin = in.abs2().sum() / (in.rows()*in.cols()); + float sin = in.abs2().sum() / ( in.rows() * in.cols() ); float sout = out.abs2().sum(); // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, ( sin ) * ( 1e-3 ) ) ); } -/* - SECTION( "in-place, forward" ) - { - mx::math::ft::fftT, std::complex, 2> fft( 128, 128, mx::math::ft::dir::forward, true ); + /* + SECTION( "in-place, forward" ) + { + mx::math::ft::fftT, std::complex, 2> fft( 128, 128, mx::math::ft::dir::forward, + true ); - mx::improc::eigenImage> in( 128, 128 ); - in.setRandom(); - mx::improc::eigenImage> incheck = in; - float sin = in.abs2().sum(); + mx::improc::eigenImage> in( 128, 128 ); + in.setRandom(); + mx::improc::eigenImage> incheck = in; + float sin = in.abs2().sum(); - fft( in, in ); + fft( in, in ); - float sout = in.abs2().sum() / (in.rows()*in.cols()); + float sout = in.abs2().sum() / (in.rows()*in.cols()); - float rmsdiff = (in-incheck).abs2().sum(); + float rmsdiff = (in-incheck).abs2().sum(); - //Make sure it isn't ident - REQUIRE(rmsdiff > 0); + //Make sure it isn't ident + REQUIRE(rmsdiff > 0); - // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); - } + // Test by Parsevals + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, (sin) * (1e-3) ) ); + } - SECTION( "in-place, backward" ) - { - mx::math::ft::fftT, std::complex, 2> fft( 128, 128, mx::math::ft::dir::backward, true ); + SECTION( "in-place, backward" ) + { + mx::math::ft::fftT, std::complex, 2> fft( 128, 128, mx::math::ft::dir::backward, + true ); - mx::improc::eigenImage> in( 128, 128 ); - in.setRandom(); + mx::improc::eigenImage> in( 128, 128 ); + in.setRandom(); - mx::improc::eigenImage> incheck = in; + mx::improc::eigenImage> incheck = in; - float sin = in.abs2().sum(); + float sin = in.abs2().sum(); - fft( in, in ); + fft( in, in ); - float sout = in.abs2().sum()/(in.rows()*in.cols()); + float sout = in.abs2().sum()/(in.rows()*in.cols()); - float rmsdiff = (in-incheck).abs2().sum(); + float rmsdiff = (in-incheck).abs2().sum(); - //Make sure it isn't ident - REQUIRE(rmsdiff > 0); + //Make sure it isn't ident + REQUIRE(rmsdiff > 0); - // Test by Parsevals - REQUIRE( fabs( sin - sout )/sin < 1e-3 ); - } - */ + // Test by Parsevals + REQUIRE_THAT( sin, Catch::Matchers::WithinAbs( sout, (sin) * (1e-3) ) ); + } + */ } #ifdef HASQUAD diff --git a/tests/include/math/func/moffat_test.cpp b/tests/include/math/func/moffat_test.cpp index 9923dd903..29a8d4f77 100644 --- a/tests/include/math/func/moffat_test.cpp +++ b/tests/include/math/func/moffat_test.cpp @@ -20,7 +20,7 @@ SCENARIO( "compiling 1D Moffat function", "[math::func::moffat]" ) WHEN( "compiling" ) { double mv = mx::math::func::moffat( 0., 0., 1., 0., 1., 1. ); - REQUIRE( mv == 1.0 ); + REQUIRE_THAT( mv, Catch::Matchers::WithinAbs( 1.0, 1e-12 ) ); } } } @@ -37,7 +37,7 @@ SCENARIO( "compiling 2D Moffat function", "[math::func::moffat]" ) WHEN( "compiling" ) { double mv = mx::math::func::moffat2D( 0., 0., 0., 1., 0., 0., 1., 1. ); - REQUIRE( mv == 1.0 ); + REQUIRE_THAT( mv, Catch::Matchers::WithinAbs( 1.0, 1e-12 ) ); } } } @@ -54,7 +54,7 @@ SCENARIO( "compiling Moffat FWHM", "[math::func::moffat]" ) WHEN( "compiling" ) { double mv = mx::math::func::moffatFWHM( 1., 1. ); - REQUIRE( mv == 2.0 ); + REQUIRE_THAT( mv, Catch::Matchers::WithinAbs( 2.0, 1e-12 ) ); } } } diff --git a/tests/include/math/templateBLAS_test.cpp b/tests/include/math/templateBLAS_test.cpp index 0f5d0918b..bd0dcf7f4 100644 --- a/tests/include/math/templateBLAS_test.cpp +++ b/tests/include/math/templateBLAS_test.cpp @@ -14,8 +14,8 @@ SCENARIO( "testing scal", "[templateBLAS]" ) float x[] = { 1., 2. }; int incX = 1; mx::math::scal( N, alpha, x, incX ); - REQUIRE( x[0] == 2.0 ); - REQUIRE( x[1] == 4.0 ); + REQUIRE_THAT( x[0], Catch::Matchers::WithinAbs( 2.0, 1e-6 ) ); + REQUIRE_THAT( x[1], Catch::Matchers::WithinAbs( 4.0, 1e-6 ) ); } WHEN( "precision is double" ) @@ -25,8 +25,8 @@ SCENARIO( "testing scal", "[templateBLAS]" ) double x[] = { 1., 2. }; int incX = 1; mx::math::scal( N, alpha, x, incX ); - REQUIRE( x[0] == 2.0 ); - REQUIRE( x[1] == 4.0 ); + REQUIRE_THAT( x[0], Catch::Matchers::WithinAbs( 2.0, 1e-12 ) ); + REQUIRE_THAT( x[1], Catch::Matchers::WithinAbs( 4.0, 1e-12 ) ); } } } diff --git a/tests/include/sigproc/psdFilter_test.cpp b/tests/include/sigproc/psdFilter_test.cpp index 40da34b0a..3ed4b087b 100644 --- a/tests/include/sigproc/psdFilter_test.cpp +++ b/tests/include/sigproc/psdFilter_test.cpp @@ -13,6 +13,14 @@ #include "../../../include/math/randomT.hpp" #include "../../../include/math/vectorUtils.hpp" +#ifdef MXLIB_BUILD_COVERAGE +constexpr int psdFilterTrials = 100; +constexpr double psdFilterTol = 0.09; +#else +constexpr int psdFilterTrials = 10000; +constexpr double psdFilterTol = 0.02; +#endif + /** Scenario: compiling psdFilter * * Verify compilation and initilization of the 3 ranks for psdFilter. @@ -252,7 +260,7 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) double avgRms = 0; - for( int k = 0; k < 10000; ++k ) + for( int k = 0; k < psdFilterTrials; ++k ) { for( size_t n = 0; n < noise.size(); ++n ) noise[n] = normVar; @@ -260,9 +268,9 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) avgRms += ( mx::math::vectorVariance( noise, 0.0 ) ); } - avgRms = sqrt( avgRms / 10000 ); + avgRms = sqrt( avgRms / psdFilterTrials ); - REQUIRE( fabs( avgRms - 1.0 ) < 0.02 ); + REQUIRE_THAT( avgRms, Catch::Matchers::WithinAbs( 1.0, psdFilterTol ) ); } WHEN( "alpha=-1.5, df arbitrary, var = 2.2" ) { @@ -297,7 +305,7 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) double avgRms = 0; - for( int k = 0; k < 10000; ++k ) + for( int k = 0; k < psdFilterTrials; ++k ) { for( size_t n = 0; n < noise.size(); ++n ) noise[n] = normVar; @@ -305,9 +313,9 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) avgRms += ( mx::math::vectorVariance( noise, 0.0 ) ); } - avgRms = sqrt( avgRms / 10000 ); + avgRms = sqrt( avgRms / psdFilterTrials ); - REQUIRE( fabs( avgRms - sqrt( 2.2 ) ) < 0.02 * sqrt( 2.2 ) ); + REQUIRE_THAT( avgRms, Catch::Matchers::WithinAbs( sqrt( 2.2 ), psdFilterTol * sqrt( 2.2 ) ) ); } } GIVEN( "a rank 2 psd" ) @@ -350,7 +358,7 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) double avgRms = 0; - for( int k = 0; k < 10000; ++k ) + for( int k = 0; k < psdFilterTrials; ++k ) { for( int cc = 0; cc < psd.cols(); ++cc ) { @@ -364,9 +372,9 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) avgRms += noise.square().sum(); //(mx::math::vectorVariance(noise,0.0)); } - avgRms = sqrt( avgRms / ( psd.rows() * psd.cols() ) / 10000 ); + avgRms = sqrt( avgRms / ( psd.rows() * psd.cols() ) / psdFilterTrials ); - REQUIRE( fabs( avgRms - 1.0 ) < 0.02 ); + REQUIRE_THAT( avgRms, Catch::Matchers::WithinAbs( 1.0, psdFilterTol ) ); } WHEN( "alpha=-1.5, dk arb, var=2.2" ) { @@ -406,7 +414,7 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) double avgRms = 0; - for( int k = 0; k < 10000; ++k ) + for( int k = 0; k < psdFilterTrials; ++k ) { for( int cc = 0; cc < psd.cols(); ++cc ) { @@ -420,9 +428,9 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) avgRms += noise.square().sum(); //(mx::math::vectorVariance(noise,0.0)); } - avgRms = sqrt( avgRms / ( psd.rows() * psd.cols() ) / 10000 ); + avgRms = sqrt( avgRms / ( psd.rows() * psd.cols() ) / psdFilterTrials ); - REQUIRE( fabs( avgRms - sqrt( 2.2 ) ) < 0.02 * sqrt( 2.2 ) ); + REQUIRE_THAT( avgRms, Catch::Matchers::WithinAbs( sqrt( 2.2 ), psdFilterTol * sqrt( 2.2 ) ) ); } } GIVEN( "a rank 3 psd" ) @@ -496,7 +504,7 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) double avgRms = 0; - for( int k = 0; k < 10000; ++k ) + for( int k = 0; k < psdFilterTrials; ++k ) { for( int pp = 0; pp < psd.planes(); ++pp ) { @@ -514,9 +522,9 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) avgRms += noise.image( pp ).square().sum(); } - avgRms = sqrt( avgRms / ( psd.rows() * psd.cols() * psd.planes() ) / 10000 ); + avgRms = sqrt( avgRms / ( psd.rows() * psd.cols() * psd.planes() ) / psdFilterTrials ); - REQUIRE( fabs( avgRms - 1.0 ) < 0.02 ); + REQUIRE_THAT( avgRms, Catch::Matchers::WithinAbs( 1.0, psdFilterTol ) ); } WHEN( "k-alpha=-3.5, f-alph=-1.5, dk arb, df arb, var=2" ) { @@ -587,7 +595,7 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) double avgRms = 0; - for( int k = 0; k < 10000; ++k ) + for( int k = 0; k < psdFilterTrials; ++k ) { for( int pp = 0; pp < psd.planes(); ++pp ) { @@ -605,9 +613,9 @@ SCENARIO( "filtering with psdFilter", "[sigproc::psdFilter]" ) avgRms += noise.image( pp ).square().sum(); } - avgRms = sqrt( avgRms / ( psd.rows() * psd.cols() * psd.planes() ) / 10000 ); + avgRms = sqrt( avgRms / ( psd.rows() * psd.cols() * psd.planes() ) / psdFilterTrials ); - REQUIRE( fabs( avgRms - sqrt( 2.0 ) ) < 0.02 * sqrt( 2 ) ); + REQUIRE_THAT( avgRms, Catch::Matchers::WithinAbs( sqrt( 2.0 ), psdFilterTol * sqrt( 2 ) ) ); } } } diff --git a/tests/include/sigproc/psdUtils_test.cpp b/tests/include/sigproc/psdUtils_test.cpp index 154faf53c..15d80ef2c 100644 --- a/tests/include/sigproc/psdUtils_test.cpp +++ b/tests/include/sigproc/psdUtils_test.cpp @@ -108,7 +108,8 @@ SCENARIO( "augmenting a 1 sided PSD", "[sigproc::psdUtils]" ) mx::sigproc::normPSD( psd, f, 1.0 ); // Now have a 1/f^2 PSD with total 1-sided variance of 1.0. - REQUIRE( fabs( mx::sigproc::psdVar( f, psd, 1.0 ) - 1.0 ) < 1e-10 ); // handles epsilon + REQUIRE_THAT( mx::sigproc::psdVar( f, psd, 1.0 ), + Catch::Matchers::WithinAbs( 1.0, 1e-10 ) ); // handles epsilon // proceed to augment: std::vector f2s, psd2s; @@ -128,17 +129,17 @@ SCENARIO( "augmenting a 1 sided PSD", "[sigproc::psdUtils]" ) REQUIRE( f2s[7] == -1 ); // Should now have 1.0 in bin 0, 0.5 in all other bins. - REQUIRE( psd2s[0] == psd[0] ); - REQUIRE( psd2s[1] == 0.5 * psd[1] ); - REQUIRE( psd2s[2] == 0.5 * psd[2] ); - REQUIRE( psd2s[3] == 0.5 * psd[3] ); - REQUIRE( psd2s[4] == psd[4] ); - REQUIRE( psd2s[5] == 0.5 * psd[3] ); - REQUIRE( psd2s[6] == 0.5 * psd[2] ); - REQUIRE( psd2s[7] == 0.5 * psd[1] ); + REQUIRE_THAT( psd2s[0], Catch::Matchers::WithinAbs( psd[0], 1e-12 ) ); + REQUIRE_THAT( psd2s[1], Catch::Matchers::WithinAbs( 0.5 * psd[1], 1e-12 ) ); + REQUIRE_THAT( psd2s[2], Catch::Matchers::WithinAbs( 0.5 * psd[2], 1e-12 ) ); + REQUIRE_THAT( psd2s[3], Catch::Matchers::WithinAbs( 0.5 * psd[3], 1e-12 ) ); + REQUIRE_THAT( psd2s[4], Catch::Matchers::WithinAbs( psd[4], 1e-12 ) ); + REQUIRE_THAT( psd2s[5], Catch::Matchers::WithinAbs( 0.5 * psd[3], 1e-12 ) ); + REQUIRE_THAT( psd2s[6], Catch::Matchers::WithinAbs( 0.5 * psd[2], 1e-12 ) ); + REQUIRE_THAT( psd2s[7], Catch::Matchers::WithinAbs( 0.5 * psd[1], 1e-12 ) ); // handle machine precision - REQUIRE( fabs( mx::sigproc::psdVar( f2s, psd2s, 1.0 ) - 1.0 ) < 1e-10 ); + REQUIRE_THAT( mx::sigproc::psdVar( f2s, psd2s, 1.0 ), Catch::Matchers::WithinAbs( 1.0, 1e-10 ) ); } } } @@ -157,16 +158,16 @@ SCENARIO( "creating a 1D frequency grid", "[sigproc::psdUtils]" ) REQUIRE( mx::sigproc::frequencyGrid( f, 1.0 ) == 0 ); - REQUIRE( fabs( f[0] - 0 ) < 1e-10 ); - REQUIRE( fabs( f[1] - 0.1 ) < 1e-10 ); - REQUIRE( fabs( f[2] - 0.2 ) < 1e-10 ); - REQUIRE( fabs( f[3] - 0.3 ) < 1e-10 ); - REQUIRE( fabs( f[4] - 0.4 ) < 1e-10 ); - REQUIRE( fabs( f[5] - 0.5 ) < 1e-10 ); - REQUIRE( fabs( f[6] - -0.4 ) < 1e-10 ); - REQUIRE( fabs( f[7] - -0.3 ) < 1e-10 ); - REQUIRE( fabs( f[8] - -0.2 ) < 1e-10 ); - REQUIRE( fabs( f[9] - -0.1 ) < 1e-10 ); + REQUIRE_THAT( f[0], Catch::Matchers::WithinAbs( 0, 1e-10 ) ); + REQUIRE_THAT( f[1], Catch::Matchers::WithinAbs( 0.1, 1e-10 ) ); + REQUIRE_THAT( f[2], Catch::Matchers::WithinAbs( 0.2, 1e-10 ) ); + REQUIRE_THAT( f[3], Catch::Matchers::WithinAbs( 0.3, 1e-10 ) ); + REQUIRE_THAT( f[4], Catch::Matchers::WithinAbs( 0.4, 1e-10 ) ); + REQUIRE_THAT( f[5], Catch::Matchers::WithinAbs( 0.5, 1e-10 ) ); + REQUIRE_THAT( f[6], Catch::Matchers::WithinAbs( -0.4, 1e-10 ) ); + REQUIRE_THAT( f[7], Catch::Matchers::WithinAbs( -0.3, 1e-10 ) ); + REQUIRE_THAT( f[8], Catch::Matchers::WithinAbs( -0.2, 1e-10 ) ); + REQUIRE_THAT( f[9], Catch::Matchers::WithinAbs( -0.1, 1e-10 ) ); } WHEN( "dt = 2" ) @@ -175,16 +176,16 @@ SCENARIO( "creating a 1D frequency grid", "[sigproc::psdUtils]" ) REQUIRE( mx::sigproc::frequencyGrid( f, 2.5 ) == 0 ); - REQUIRE( fabs( f[0] - 0 ) < 1e-10 ); - REQUIRE( fabs( f[1] - 0.04 ) < 1e-10 ); - REQUIRE( fabs( f[2] - 0.08 ) < 1e-10 ); - REQUIRE( fabs( f[3] - 0.12 ) < 1e-10 ); - REQUIRE( fabs( f[4] - 0.16 ) < 1e-10 ); - REQUIRE( fabs( f[5] - 0.2 ) < 1e-10 ); - REQUIRE( fabs( f[6] - -0.16 ) < 1e-10 ); - REQUIRE( fabs( f[7] - -0.12 ) < 1e-10 ); - REQUIRE( fabs( f[8] - -0.08 ) < 1e-10 ); - REQUIRE( fabs( f[9] - -0.04 ) < 1e-10 ); + REQUIRE_THAT( f[0], Catch::Matchers::WithinAbs( 0, 1e-10 ) ); + REQUIRE_THAT( f[1], Catch::Matchers::WithinAbs( 0.04, 1e-10 ) ); + REQUIRE_THAT( f[2], Catch::Matchers::WithinAbs( 0.08, 1e-10 ) ); + REQUIRE_THAT( f[3], Catch::Matchers::WithinAbs( 0.12, 1e-10 ) ); + REQUIRE_THAT( f[4], Catch::Matchers::WithinAbs( 0.16, 1e-10 ) ); + REQUIRE_THAT( f[5], Catch::Matchers::WithinAbs( 0.2, 1e-10 ) ); + REQUIRE_THAT( f[6], Catch::Matchers::WithinAbs( -0.16, 1e-10 ) ); + REQUIRE_THAT( f[7], Catch::Matchers::WithinAbs( -0.12, 1e-10 ) ); + REQUIRE_THAT( f[8], Catch::Matchers::WithinAbs( -0.08, 1e-10 ) ); + REQUIRE_THAT( f[9], Catch::Matchers::WithinAbs( -0.04, 1e-10 ) ); } } @@ -196,11 +197,11 @@ SCENARIO( "creating a 1D frequency grid", "[sigproc::psdUtils]" ) REQUIRE( mx::sigproc::frequencyGrid( f, 1.0, false ) == 0 ); - REQUIRE( fabs( f[0] - 0.1 ) < 1e-10 ); - REQUIRE( fabs( f[1] - 0.2 ) < 1e-10 ); - REQUIRE( fabs( f[2] - 0.3 ) < 1e-10 ); - REQUIRE( fabs( f[3] - 0.4 ) < 1e-10 ); - REQUIRE( fabs( f[4] - 0.5 ) < 1e-10 ); + REQUIRE_THAT( f[0], Catch::Matchers::WithinAbs( 0.1, 1e-10 ) ); + REQUIRE_THAT( f[1], Catch::Matchers::WithinAbs( 0.2, 1e-10 ) ); + REQUIRE_THAT( f[2], Catch::Matchers::WithinAbs( 0.3, 1e-10 ) ); + REQUIRE_THAT( f[3], Catch::Matchers::WithinAbs( 0.4, 1e-10 ) ); + REQUIRE_THAT( f[4], Catch::Matchers::WithinAbs( 0.5, 1e-10 ) ); } WHEN( "dt = 1, even size" ) @@ -209,12 +210,12 @@ SCENARIO( "creating a 1D frequency grid", "[sigproc::psdUtils]" ) REQUIRE( mx::sigproc::frequencyGrid( f, 1.0, false ) == 0 ); - REQUIRE( fabs( f[0] - 0.0 ) < 1e-10 ); - REQUIRE( fabs( f[1] - 0.1 ) < 1e-10 ); - REQUIRE( fabs( f[2] - 0.2 ) < 1e-10 ); - REQUIRE( fabs( f[3] - 0.3 ) < 1e-10 ); - REQUIRE( fabs( f[4] - 0.4 ) < 1e-10 ); - REQUIRE( fabs( f[5] - 0.5 ) < 1e-10 ); + REQUIRE_THAT( f[0], Catch::Matchers::WithinAbs( 0.0, 1e-10 ) ); + REQUIRE_THAT( f[1], Catch::Matchers::WithinAbs( 0.1, 1e-10 ) ); + REQUIRE_THAT( f[2], Catch::Matchers::WithinAbs( 0.2, 1e-10 ) ); + REQUIRE_THAT( f[3], Catch::Matchers::WithinAbs( 0.3, 1e-10 ) ); + REQUIRE_THAT( f[4], Catch::Matchers::WithinAbs( 0.4, 1e-10 ) ); + REQUIRE_THAT( f[5], Catch::Matchers::WithinAbs( 0.5, 1e-10 ) ); } } } diff --git a/tests/include/sigproc/signalWindows_test.cpp b/tests/include/sigproc/signalWindows_test.cpp index 118b4bac7..45c8faadd 100644 --- a/tests/include/sigproc/signalWindows_test.cpp +++ b/tests/include/sigproc/signalWindows_test.cpp @@ -9,7 +9,6 @@ #include "../../../include/sigproc/signalWindows.hpp" #include "../../../include/improc/eigenImage.hpp" -#include "../../../include/improc/milkImage.hpp" /** Scenario: creating 2D Rectangular Tukey Windows * @@ -57,8 +56,8 @@ SCENARIO( "creating 2D Rectangular Tukey Windows", "[sigproc::signalWindows::tuk std::vector win1( 256 ); mx::sigproc::window::tukey( win1, 1.0 ); - REQUIRE( win( 0, 0 ) == win1[0] * win1[0] ); - REQUIRE( win( 10, 15 ) == win1[10] * win1[15] ); + REQUIRE_THAT( win( 0, 0 ), Catch::Matchers::WithinAbs( win1[0] * win1[0], 1e-6 ) ); + REQUIRE_THAT( win( 10, 15 ), Catch::Matchers::WithinAbs( win1[10] * win1[15], 1e-6 ) ); } WHEN( "256x256, alpha=0.5" ) { @@ -78,11 +77,8 @@ SCENARIO( "creating 2D Rectangular Tukey Windows", "[sigproc::signalWindows::tuk std::vector win1( 256 ); mx::sigproc::window::tukey( win1, 0.5 ); - mx::improc::milkImage mwin; - mwin.create( "win", win ); - - REQUIRE( win( 0, 0 ) == win1[0] * win1[0] ); - REQUIRE( win( 10, 15 ) == win1[10] * win1[15] ); + REQUIRE_THAT( win( 0, 0 ), Catch::Matchers::WithinAbs( win1[0] * win1[0], 1e-6 ) ); + REQUIRE_THAT( win( 10, 15 ), Catch::Matchers::WithinAbs( win1[10] * win1[15], 1e-6 ) ); } } } diff --git a/tests/include/sys/timeUtils_test.cpp b/tests/include/sys/timeUtils_test.cpp index b806b343a..3b9cedee8 100644 --- a/tests/include/sys/timeUtils_test.cpp +++ b/tests/include/sys/timeUtils_test.cpp @@ -218,7 +218,7 @@ SCENARIO( "Verify parsing of a formatted time string", "[timeutils]" ) REQUIRE( hr == 1 ); REQUIRE( mn == 2 ); - REQUIRE( fabs( sec - 3.23 ) < 1e-7 ); + REQUIRE_THAT( sec, Catch::Matchers::WithinAbs( 3.23, 1e-7 ) ); } WHEN( "negative hour" ) @@ -231,7 +231,7 @@ SCENARIO( "Verify parsing of a formatted time string", "[timeutils]" ) REQUIRE( hr == -1 ); REQUIRE( mn == -2 ); - REQUIRE( fabs( sec - -3.23 ) < 1e-7 ); + REQUIRE_THAT( sec, Catch::Matchers::WithinAbs( -3.23, 1e-7 ) ); } WHEN( "0 pads" ) @@ -244,7 +244,7 @@ SCENARIO( "Verify parsing of a formatted time string", "[timeutils]" ) REQUIRE( hr == 1 ); REQUIRE( mn == 2 ); - REQUIRE( fabs( sec - 3.23 ) < 1e-7 ); + REQUIRE_THAT( sec, Catch::Matchers::WithinAbs( 3.23, 1e-7 ) ); } } } @@ -269,7 +269,7 @@ SCENARIO( "Verify calculation of MJD", "[timeutils]" ) WHEN( "floating seconds" ) { double mjd = mx::sys::Cal2mjd( 2020, 12, 31, 0, 0, 10.2357 ); - REQUIRE( fabs( mjd - 59214.00011846875 ) < 1e-14 ); + REQUIRE_THAT( mjd, Catch::Matchers::WithinAbs( 59214.00011846875, 1e-14 ) ); } } } @@ -311,7 +311,7 @@ SCENARIO( "Verify parsing of an ISO 8601 time string", "[timeutils]" ) REQUIRE( day == 31 ); REQUIRE( hr == 0 ); REQUIRE( min == 0 ); - REQUIRE( fabs( sec - 10.2357 ) < 1e-14 ); + REQUIRE_THAT( sec, Catch::Matchers::WithinAbs( 10.2357, 1e-14 ) ); } } @@ -348,7 +348,7 @@ SCENARIO( "Verify conversion of an ISO 8601 time string to MJD", "[timeutils]" ) { double mjd = mx::sys::ISO8601date2mjd( "2020-12-31T00:00:10.2357" ); - REQUIRE( fabs( mjd - 59214.00011846875 ) < 1e-14 ); + REQUIRE_THAT( mjd, Catch::Matchers::WithinAbs( 59214.00011846875, 1e-14 ) ); } } } diff --git a/tests/include/wfp/fraunhoferPropagator_test.cpp b/tests/include/wfp/fraunhoferPropagator_test.cpp index 008fb5ba6..f0a6d6cd1 100644 --- a/tests/include/wfp/fraunhoferPropagator_test.cpp +++ b/tests/include/wfp/fraunhoferPropagator_test.cpp @@ -6,7 +6,7 @@ #include -//#define DEBUG +// #define DEBUG #include "../../../include/wfp/fraunhoferPropagator.hpp" #include "../../../include/improc/eigenImage.hpp" @@ -23,7 +23,6 @@ TEST_CASE( "Make an Airy pattern and go back to pupil on CPU", "[wfp]" ) typedef std::complex complexT; typedef mx::improc::eigenImage> complexFieldT; - int wfSz = 512; int pupSz = 128; mx::improc::eigenImage pupil; @@ -37,7 +36,7 @@ TEST_CASE( "Make an Airy pattern and go back to pupil on CPU", "[wfp]" ) complexPupil.resize( wfSz, wfSz ); complexFocal.resize( wfSz, wfSz ); realFocal.resize( wfSz, wfSz ); - realPupil.resize(wfSz, wfSz); + realPupil.resize( wfSz, wfSz ); mx::wfp::fraunhoferPropagator fi; mx::wfp::makeComplexPupil( complexPupil, pupil, wfSz ); @@ -49,14 +48,14 @@ TEST_CASE( "Make an Airy pattern and go back to pupil on CPU", "[wfp]" ) realT expwr = pupil.square().sum(); realT pwr = realFocal.sum(); - REQUIRE( fabs(expwr - pwr)/pwr < 1e-4 ); + REQUIRE_THAT( expwr, Catch::Matchers::WithinAbs( pwr, ( pwr ) * ( 1e-4 ) ) ); complexPupil.setZero(); - fi.propagateFocalToPupil( complexPupil, complexFocal); + fi.propagateFocalToPupil( complexPupil, complexFocal ); mx::wfp::extractIntensityImage( realPupil, 0, complexPupil.rows(), 0, complexPupil.cols(), complexPupil, 0, 0 ); - REQUIRE(realPupil.sum() == pupil.sum()); + REQUIRE_THAT( realPupil.sum(), Catch::Matchers::WithinAbs( pupil.sum(), pupil.sum() * 1e-4 ) ); } /// Make an Airy pattern and go back to pupil on GPU @@ -69,7 +68,6 @@ TEST_CASE( "Make an Airy pattern and go back to pupil on GPU", "[wfp]" ) typedef std::complex complexT; typedef mx::improc::eigenImage> complexFieldT; - int wfSz = 512; int pupSz = 128; mx::improc::eigenImage pupil; @@ -83,15 +81,15 @@ TEST_CASE( "Make an Airy pattern and go back to pupil on GPU", "[wfp]" ) complexPupil.resize( wfSz, wfSz ); complexFocal.resize( wfSz, wfSz ); realFocal.resize( wfSz, wfSz ); - realPupil.resize(wfSz, wfSz); + realPupil.resize( wfSz, wfSz ); mx::wfp::fraunhoferPropagator fi; mx::wfp::makeComplexPupil( complexPupil, pupil, wfSz ); - mx::cuda::cudaPtr dev_complexPupil,dev_complexFocal; + mx::cuda::cudaPtr dev_complexPupil, dev_complexFocal; - dev_complexPupil.upload(complexPupil.data(), complexPupil.rows(), complexPupil.cols()); - dev_complexFocal.resize(complexPupil.rows(), complexPupil.cols()); + dev_complexPupil.upload( complexPupil.data(), complexPupil.rows(), complexPupil.cols() ); + dev_complexFocal.resize( complexPupil.rows(), complexPupil.cols() ); BREAD_CRUMB; @@ -99,7 +97,7 @@ TEST_CASE( "Make an Airy pattern and go back to pupil on GPU", "[wfp]" ) BREAD_CRUMB; - dev_complexFocal.download(complexFocal.data()); + dev_complexFocal.download( complexFocal.data() ); BREAD_CRUMB; @@ -110,18 +108,18 @@ TEST_CASE( "Make an Airy pattern and go back to pupil on GPU", "[wfp]" ) realT expwr = pupil.square().sum(); realT pwr = realFocal.sum(); - REQUIRE( fabs(expwr - pwr)/pwr < 1e-4 ); + REQUIRE_THAT( expwr, Catch::Matchers::WithinAbs( pwr, ( pwr ) * ( 1e-4 ) ) ); BREAD_CRUMB; - fi.propagateFocalToPupil( dev_complexPupil, dev_complexFocal); + fi.propagateFocalToPupil( dev_complexPupil, dev_complexFocal ); BREAD_CRUMB; - complexPupil.setZero(); //make sure - dev_complexPupil.download(complexPupil.data()); + complexPupil.setZero(); // make sure + dev_complexPupil.download( complexPupil.data() ); mx::wfp::extractIntensityImage( realPupil, 0, complexPupil.rows(), 0, complexPupil.cols(), complexPupil, 0, 0 ); - REQUIRE(realPupil.sum() == pupil.sum()); + REQUIRE_THAT( realPupil.sum(), Catch::Matchers::WithinAbs( pupil.sum(), pupil.sum() * 1e-4 ) ); }