diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 62abfd4b..fa942693 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,20 @@ jobs: max_attempts: 3 timeout_minutes: 3 command: make clean && make compile -j16 + + build-etl: + runs-on: ubuntu-latest + container: huskysat/found:latest + + steps: + - uses: actions/checkout@v4 + + - name: Build all ETL + uses: nick-fields/retry@v3.0.2 + with: + max_attempts: 3 + timeout_minutes: 3 + command: ./build.sh clean && ./build.sh make FOUND_CONTAINER_BACKEND=ETL compile -j16 test: runs-on: ubuntu-latest @@ -145,6 +159,20 @@ jobs: timeout_minutes: 3 command: ./build.sh clean && ./build.sh cmake "" --parallel 16 + cmake-build-etl: + runs-on: ubuntu-latest + container: huskysat/found:latest + + steps: + - uses: actions/checkout@v4 + + - name: Build all ETL + uses: nick-fields/retry@v3.0.2 + with: + max_attempts: 3 + timeout_minutes: 3 + command: ./build.sh clean && ./build.sh cmake "-DFOUND_CONTAINER_BACKEND=ETL" --target compile --parallel 16 + cmake-float: runs-on: ubuntu-latest container: huskysat/found:latest @@ -177,4 +205,21 @@ jobs: command: | ./build.sh clean ./build.sh cmake -DOMIT_ASAN=ON --target found-test --parallel 16 - valgrind ./build/bin/found-test \ No newline at end of file + valgrind ./build/bin/found-test + + cmake-etl-test: + runs-on: ubuntu-latest + container: huskysat/found:latest + + steps: + - uses: actions/checkout@v4 + + - name: Memory Check (ETL) + uses: nick-fields/retry@v3.0.2 + with: + max_attempts: 3 + timeout_minutes: 3 + command: | + ./build.sh clean + ./build.sh cmake "-DFOUND_CONTAINER_BACKEND=ETL -DOMIT_ASAN=ON" --target found-test --parallel 16 + valgrind ./build/bin/found-test diff --git a/.gitignore b/.gitignore index cf3f8d2a..e5c2243b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ googletest* **.found # Exclude all .Ds_Store files (these are generated by macOS) -/*.DS_Store \ No newline at end of file +/*.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 92c5af13..f38921af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,6 @@ endfunction() set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(CACHE_DIR ${PROJECT_SOURCE_DIR}/.cache) set(BIN_DIR ${BUILD_DIR}/bin) -# set(OBJ_DIR ${BUILD_DIR}/objects) No longer needed set(DOC_DIR ${BUILD_DIR}/documentation) # Source directory @@ -63,7 +62,6 @@ set(SRC_MAIN "${SRC_DIR}/main.cpp") set(TEST_DIR ${PROJECT_SOURCE_DIR}/test) file(GLOB_RECURSE TEST CONFIGURE_DEPENDS ${TEST_DIR}/*.cpp) file(GLOB_RECURSE TEST_H ${TEST_DIR}/*.hpp) -list(APPEND TEST_H ${SRC_DIR}) # Documentation directory set(DOC_COVERAGE_DIR ${DOC_DIR}/coverage) @@ -79,12 +77,46 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) include_directories(${PROJECT_SOURCE_DIR}) include_directories(${SRC_DIR}) +##### ETL ##### +# ETL definitions +set(ETL "etl") +set(ETL_VERSION "20.46.2") +FetchContent_Declare( + ${ETL} + GIT_REPOSITORY https://github.com/ETLCPP/etl.git + GIT_TAG ${ETL_VERSION} +) +FetchContent_MakeAvailable(${ETL}) +set(ETL_INCLUDE_DIR "${etl_SOURCE_DIR}/include") +include_directories(${ETL_INCLUDE_DIR}) + # Logging macros option(DISABLE_LOGGING "Disable logging" OFF) set(LOGGING_LEVEL INFO CACHE STRING "Logging level") +# Container backend config +set(FOUND_CONTAINER_BACKEND + "STL" + CACHE STRING "Container backend to use (STL or ETL)") +set_property(CACHE FOUND_CONTAINER_BACKEND PROPERTY STRINGS STL ETL) + +set(FOUND_SUPPORTED_CONTAINER_BACKENDS STL ETL) +string(TOUPPER "${FOUND_CONTAINER_BACKEND}" FOUND_CONTAINER_BACKEND) +if(NOT FOUND_CONTAINER_BACKEND IN_LIST FOUND_SUPPORTED_CONTAINER_BACKENDS) + message(FATAL_ERROR + "FOUND_CONTAINER_BACKEND must be one of: STL, ETL") +endif() + +set(FOUND_CONTAINER_BACKEND_DEFINITIONS) +if(FOUND_CONTAINER_BACKEND STREQUAL "ETL") + list(APPEND FOUND_CONTAINER_BACKEND_DEFINITIONS + FOUND_USE_ETL_CONTAINERS) +endif() + +message(STATUS "FOUND container backend: ${FOUND_CONTAINER_BACKEND}") + # Floating-point config option(FLOAT_MODE "Enable FOUND_FLOAT_MODE" OFF) if(FLOAT_MODE) @@ -98,7 +130,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -fno-omit-frame-pointer") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") # Global compile options -add_compile_options(-Wall -Wextra -Wno-missing-field-initializers -pedantic) +add_compile_options(-Wall -Wextra -Wno-missing-field-initializers -pedantic -Wdouble-promotion -Werror) ########## Source Libraries ########## @@ -131,6 +163,7 @@ if(NOT EXISTS ${STB_IMAGE_SRC}) file(WRITE ${STB_IMAGE_SRC} "#include \"${STB_IMAGE}/${STB_IMAGE}.h\"\n") endif() add_library(${STB_IMAGE} STATIC ${STB_IMAGE_SRC}) +target_compile_options(${STB_IMAGE} PRIVATE -Wno-error -Wno-double-promotion) target_compile_definitions(${STB_IMAGE} PRIVATE STB_IMAGE_IMPLEMENTATION) target_include_directories(${STB_IMAGE} PUBLIC $) @@ -159,12 +192,15 @@ set(SRC_LIBS ${STB_IMAGE} ${EIGEN}) add_library(found_lib STATIC ${FILTERED_SRC}) target_link_libraries(found_lib PUBLIC ${EIGEN} PRIVATE ${STB_IMAGE}) target_include_directories(found_lib - PUBLIC - $ - $) -target_compile_definitions(found_lib PRIVATE - $<$>:ENABLE_LOGGING> - $<$>:LOGGING_LEVEL=${LOGGING_LEVEL}>) + PUBLIC + $ + $ + $) +target_compile_definitions(found_lib PRIVATE + ${FOUND_CONTAINER_BACKEND_DEFINITIONS} + $<$>:ENABLE_LOGGING> + $<$>:LOGGING_LEVEL=${LOGGING_LEVEL}>) + add_library(found::found_lib ALIAS found_lib) ########## Test Libraries ########## @@ -178,8 +214,6 @@ set(GTEST googletest) set(GTEST_VERSION release-1.12.1) set(GTEST_LIBS gmock gmock_main) -include(FetchContent) - FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/${GTEST}.git @@ -212,30 +246,47 @@ endif() ########## Targets ########## -add_compile_options(-Wdouble-promotion -Werror) - ##### compile ##### file(MAKE_DIRECTORY ${BIN_DIR}) add_executable(found ${SRC_MAIN}) - +target_include_directories(found PRIVATE ${ETL_INCLUDE_DIR}) target_link_libraries(found PRIVATE ${SRC_LIBS} found_lib) -target_compile_definitions(found PRIVATE - $<$>:ENABLE_LOGGING> - $<$>:LOGGING_LEVEL=${LOGGING_LEVEL}>) +target_compile_definitions(found PRIVATE + ${FOUND_CONTAINER_BACKEND_DEFINITIONS} + $<$>:ENABLE_LOGGING> + $<$>:LOGGING_LEVEL=${LOGGING_LEVEL}>) + set_target_properties(found PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${BIN_DIR}) add_custom_target(compile - DEPENDS found - COMMENT "Compiles source code") + DEPENDS found + COMMENT "Compiles source code") -if(ENABLE_WORKFLOW) ##### test ##### - add_executable(found-test EXCLUDE_FROM_ALL ${TEST} ${FILTERED_SRC}) -target_compile_definitions(found-test PRIVATE ENABLE_LOGGING - LOGGING_LEVEL=INFO) + +if(ENABLE_WORKFLOW) + target_link_libraries(found-test PRIVATE + ${TEST_LIBS} + ) +else() + target_link_libraries(found-test PRIVATE + found_lib + ) +endif() + +target_link_options(found-test PRIVATE + --coverage + $<$>:-fsanitize=address> +) + +target_compile_definitions(found-test PRIVATE + ${FOUND_CONTAINER_BACKEND_DEFINITIONS} + ENABLE_LOGGING + LOGGING_LEVEL=INFO) + target_compile_options(found-test PRIVATE -Wdouble-promotion -Werror --coverage @@ -243,18 +294,23 @@ target_compile_options(found-test PRIVATE $<$>:-fsanitize=address> $<$>:-fomit-frame-pointer> ) -target_link_libraries(found-test PRIVATE - --coverage - ${TEST_LIBS} - $<$>:-fsanitize=address> - $<$>:-fomit-frame-pointer>) -set_target_properties(found-test PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${BIN_DIR}) -add_dependencies(found-test ${TEST_LIBS}) + +set_target_properties(found-test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${BIN_DIR}) +if(TEST_LIBS) + add_dependencies(found-test ${TEST_LIBS}) +endif() + add_custom_target(test DEPENDS found-test COMMENT "Compiles test code") +add_custom_target( + integration-test + COMMAND cd ${PROJECT_SOURCE_DIR} && ${BIN_DIR}/found-test --gtest_filter=IntegrationTest.* --gtest_brief=1 + COMMENT "Runs integration tests for the configured container backend" + VERBATIM + DEPENDS found-test) + ##### coverage ##### file(MAKE_DIRECTORY ${DOC_COVERAGE_DIR}) @@ -265,11 +321,16 @@ set(GCOVR_CONFIG ${PROJECT_SOURCE_DIR}/gcovr.cfg) add_custom_target( coverage COMMAND cd ${PROJECT_SOURCE_DIR} && ${BIN_DIR}/found-test --gtest_brief=1 - COMMAND ${GCOVR} -r ${PROJECT_SOURCE_DIR} - # WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND ${GCOVR} + --config ${GCOVR_CONFIG} + --object-directory . + -r ${PROJECT_SOURCE_DIR} + --html-details ${DOC_COVERAGE_DIR}/index.html COMMENT "Running tests and generating coverage report" VERBATIM - DEPENDS found-test) + WORKING_DIRECTORY ${BUILD_DIR} + DEPENDS found-test + ) ##### linting ##### @@ -296,8 +357,6 @@ add_custom_target( DEPENDS ${DOXYGEN_AWESOME_ARTIFACT} ${SRC} COMMENT "Generates Doxygen Documentation over code") -endif() - ##### clean_all ##### add_custom_target(clean_all diff --git a/Makefile b/Makefile index 1973f41f..edd59ef6 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ STB_IMAGE := stb_image STB_IMAGE_URL := https://raw.githubusercontent.com/nothings/stb/master/$(STB_IMAGE).h STB_IMAGE_CACHE_DIR := $(CACHE_DIR)/$(STB_IMAGE) STB_IMAGE_CACHE_ARTIFACT := $(STB_IMAGE_CACHE_DIR)/$(STB_IMAGE).cpp +STB_IMAGE_CACHE_OBJECT := $(STB_IMAGE_CACHE_DIR)/$(STB_IMAGE).o STB_IMAGE_DIR := $(BUILD_LIBRARY_SRC_DIR)/$(STB_IMAGE) # Define the GoogleTest library and build targets @@ -93,10 +94,32 @@ ifdef FLOAT_MODE FOUND_FLOAT_MODE_MACRO := -DFOUND_FLOAT_MODE -Wdouble-promotion endif +# Container backend macros +ifeq ($(FOUND_CONTAINER_BACKEND),ETL) + FOUND_CONTAINER_BACKEND_MACROS := -DFOUND_USE_ETL_CONTAINERS +endif + +CXXFLAGS += $(FOUND_CONTAINER_BACKEND_MACROS) +CXXFLAGS_TEST += $(FOUND_CONTAINER_BACKEND_MACROS) + LOGGING_MACROS_TEST := -DENABLE_LOGGING -DLOGGING_LEVEL=INFO +# Define the ETL library (FetchContent style) +ETL := etl +ETL_VERSION := 20.46.2 +ETL_URL := https://github.com/ETLCPP/etl.git +ETL_CACHE_DIR := $(CACHE_DIR)/$(ETL)-$(ETL_VERSION) +ETL_INCLUDE_DIR := $(ETL_CACHE_DIR)/include +$(ETL_CACHE_DIR): + git clone --branch $(ETL_VERSION) --depth 1 $(ETL_URL) $(ETL_CACHE_DIR) + +ifeq ($(FOUND_CONTAINER_BACKEND),ETL) +ETL_DEPS := $(ETL_CACHE_DIR) +ETL_INCLUDE_LIBS := -I$(ETL_INCLUDE_DIR) +endif + # Compiler flags -LIBS := $(SRC_LIBS) -I$(BUILD_LIBRARY_SRC_DIR) -isystem $(EIGEN_DIR) +LIBS := $(SRC_LIBS) -I$(BUILD_LIBRARY_SRC_DIR) -isystem $(EIGEN_DIR) $(ETL_INCLUDE_LIBS) LIBS_TEST := $(TEST_LIBS) -isystem $(EIGEN_DIR) -I$(GTEST_DIR)/$(GTEST)/include -I$(GTEST_DIR)/googlemock/include -pthread DEBUG_FLAGS := -ggdb -fno-omit-frame-pointer COVERAGE_FLAGS := --coverage @@ -129,14 +152,7 @@ ifdef DEBUG endif PASS_ON_COVERAGE_FAIL := false -# Prints out a Header when each -# target begins -# -# Argument -# - $(1) The name of the target -# -# Prints out a banner for each -# target with the specified name +# Prints out a Header when each target begins define PRINT_TARGET_HEADER @MIDDLE_LINE="Target: $(1)"; \ MIDDLE_LINE_LEN=$$(echo -n "$$MIDDLE_LINE" | wc -m); \ @@ -162,7 +178,7 @@ all: $(COMPILE_SETUP_TARGET) \ $(DOXYGEN_TARGET) \ # The build setup target (sets up appropriate directories) -$(COMPILE_SETUP_TARGET): compile_setup_message $(BUILD_DIR) $(STB_IMAGE_DIR) $(EIGEN_DIR) +$(COMPILE_SETUP_TARGET): compile_setup_message $(BUILD_DIR) $(STB_IMAGE_DIR) $(EIGEN_DIR) $(ETL_DEPS) $(BUILD_DIR): mkdir -p $(BUILD_DIR) mkdir -p $(BUILD_DOCUMENTATION_DIR) @@ -174,12 +190,16 @@ $(BUILD_LIBRARY_SRC_DIR): mkdir -p $(BUILD_LIBRARY_SRC_DIR) compile_setup_message: $(call PRINT_TARGET_HEADER, $(COMPILE_SETUP_TARGET)) -$(STB_IMAGE_DIR): $(STB_IMAGE_CACHE_ARTIFACT) $(BUILD_LIBRARY_SRC_DIR) - cp -r $(STB_IMAGE_CACHE_DIR) $(BUILD_LIBRARY_SRC_DIR) +$(STB_IMAGE_DIR)/$(STB_IMAGE).o: $(STB_IMAGE_CACHE_OBJECT) $(BUILD_LIBRARY_SRC_DIR) + mkdir -p $(STB_IMAGE_DIR) + cp -r $(STB_IMAGE_CACHE_DIR)/* $(STB_IMAGE_DIR)/ +# Update the directory alias to depend on the file +$(STB_IMAGE_DIR): $(STB_IMAGE_DIR)/$(STB_IMAGE).o +$(STB_IMAGE_CACHE_OBJECT): $(STB_IMAGE_CACHE_ARTIFACT) + $(CXX) -I$(CACHE_DIR) -DSTB_IMAGE_IMPLEMENTATION -c $(STB_IMAGE_CACHE_ARTIFACT) -o $(STB_IMAGE_CACHE_OBJECT) # Exclude CXXFLAGS because we know its fine $(STB_IMAGE_CACHE_ARTIFACT): wget $(STB_IMAGE_URL) -P $(STB_IMAGE_CACHE_DIR) - echo '#define STB_IMAGE_IMPLEMENTATION\n#include "stb_image/stb_image.h"' > $(STB_IMAGE_CACHE_ARTIFACT) - $(CXX) -I$(CACHE_DIR) -c $(STB_IMAGE_CACHE_ARTIFACT) -o $(STB_IMAGE_CACHE_DIR)/$(STB_IMAGE).o # Exclude CXXFLAGS because we know its fine + echo '#include "stb_image/stb_image.h"' > $(STB_IMAGE_CACHE_ARTIFACT) # Eigen header-only library (download and extract) $(EIGEN_DIR): $(EIGEN_CACHE_ARTIFACT) $(BUILD_LIBRARY_SRC_DIR) @@ -190,9 +210,9 @@ $(EIGEN_CACHE_ARTIFACT): # The compile target $(COMPILE_TARGET): $(COMPILE_SETUP_TARGET) compile_message $(BIN) -$(BIN): $(SRC_OBJS) $(BIN_DIR) $(STB_IMAGE_DIR) $(EIGEN_DIR) +$(BIN): $(SRC_OBJS) $(BIN_DIR) $(STB_IMAGE_DIR) $(EIGEN_DIR) $(ETL_DEPS) $(CXX) $(OPTIMIZATION) $(CXXFLAGS) -o $(BIN) $(SRC_OBJS) $(LDFLAGS) -$(BUILD_SRC_DIR)/%.o: $(SRC_DIR)/%.cpp $(STB_IMAGE_DIR) $(EIGEN_DIR) +$(BUILD_SRC_DIR)/%.o: $(SRC_DIR)/%.cpp $(STB_IMAGE_DIR) $(EIGEN_DIR) $(ETL_DEPS) mkdir -p $(@D) $(CXX) $(OPTIMIZATION) $(CXXFLAGS) -c $< -o $@ compile_message: @@ -221,12 +241,15 @@ test_setup_message: # The test target $(TEST_TARGET): $(TEST_SETUP_TARGET) test_message $(TEST_BIN) -$(TEST_BIN): $(GTEST_DIR) $(TEST_OBJS) $(BIN_DIR) +$(TEST_BIN): $(GTEST_DIR) $(TEST_OBJS) $(BIN_DIR) $(STB_IMAGE_DIR) $(CXX) $(CXXFLAGS_TEST) $(COVERAGE_FLAGS) -o $(TEST_BIN) $(TEST_OBJS) $(LIBS) $(LDFLAGS_TEST) -$(BUILD_TEST_DIR)/%.o: $(TEST_DIR)/%.cpp $(GTEST_DIR) + + +$(BUILD_TEST_DIR)/%.o: $(TEST_DIR)/%.cpp $(GTEST_DIR) $(ETL_DEPS) mkdir -p $(@D) $(CXX) $(CXXFLAGS_TEST) $(COVERAGE_FLAGS) -c $< -o $@ -$(BUILD_TEST_DIR)/%.o: $(SRC_DIR)/%.cpp $(GTEST_DIR) + +$(BUILD_TEST_DIR)/%.o: $(SRC_DIR)/%.cpp $(GTEST_DIR) $(ETL_DEPS) mkdir -p $(@D) $(CXX) $(CXXFLAGS_TEST) $(COVERAGE_FLAGS) -c $< -o $@ test_message: @@ -236,7 +259,7 @@ test_message: $(COVERAGE_TARGET): $(TEST_SETUP_TARGET) $(TEST_TARGET) $(call PRINT_TARGET_HEADER, $(COVERAGE_TARGET)) ./$(TEST_BIN) --gtest_brief=1 - gcovr || $(PASS_ON_COVERAGE_FAIL) + gcovr --html-details $(BUILD_DOCUMENTATION_COVERAGE_DIR)/index.html || $(PASS_ON_COVERAGE_FAIL) # The stylecheck target for tests $(GOOGLE_STYLECHECK_TEST_TARGET): $(TEST) $(TEST_H) @@ -245,10 +268,12 @@ $(GOOGLE_STYLECHECK_TEST_TARGET): $(TEST) $(TEST_H) # The pre-processed artifacts target (private) $(PRIVATE_TARGET): $(COMPILE_SETUP_TARGET) $(TEST_SETUP_TARGET) private_message $(PRIVATE_SRC) $(PRIVATE_TEST) -$(BUILD_PRIVATE_SRC_DIR)/%.i: $(SRC) + +$(BUILD_PRIVATE_SRC_DIR)/%.i: $(SRC) $(ETL_DEPS) mkdir -p $(@D) $(CXX) $(CXXFLAGS) -E $< -o $@ -$(BUILD_PRIVATE_TEST_DIR)/%.i: $(TEST) + +$(BUILD_PRIVATE_TEST_DIR)/%.i: $(TEST) $(ETL_DEPS) mkdir -p $(@D) $(CXX) $(CXXFLAGS_TEST) -E $< -o $@ private_message: @@ -278,4 +303,4 @@ release: OPTIMIZATION = -O3 release: CXXFLAGS += -DNDEBUG release: compile --include $(SRC_OBJS:.o=.d) $(TEST_OBJS:.o=.d) \ No newline at end of file +-include $(SRC_OBJS:.o=.d) $(TEST_OBJS:.o=.d) diff --git a/build.sh b/build.sh index 5260ed2e..ac5b0101 100755 --- a/build.sh +++ b/build.sh @@ -15,6 +15,10 @@ display_help() { echo " ./build.sh clean" echo " ./build.sh clean_all" echo " ./build.sh --help | -h" + echo "" + echo "ETL build examples (explicit macro required):" + echo " ./build.sh cmake \"-DFOUND_CONTAINER_BACKEND=ETL\" --target compile --parallel 16" + echo " ./build.sh make FOUND_CONTAINER_BACKEND=ETL compile -j16" } # Exit if no arguments were provided @@ -29,7 +33,7 @@ case "$1" in shift mkdir -p build && cd build - CONFIG_OPTS="${1:-}" # Use empty string if not set + CONFIG_OPTS="${1:-}" if [ $# -gt 0 ]; then shift; fi CMD="cmake $CONFIG_OPTS .. && cmake --build . $*" diff --git a/gcovr.cfg b/gcovr.cfg index 8ac0376d..6f922b85 100644 --- a/gcovr.cfg +++ b/gcovr.cfg @@ -2,7 +2,10 @@ exclude = build/* exclude = test/* exclude = .cache/* exclude = src/main.cpp +exclude = src/common/containers.hpp +exclude = src/common/spatial/* exclude-lines-by-pattern = \s*assert\(|.*new\s+ # excludes assert and new statements + exclude-throw-branches = yes exclude-unreachable-branches = yes exclude-noncode-lines = yes @@ -14,6 +17,6 @@ html-medium-threshold = 70 html-high-threshold = 85 html-theme=github.green html-title = FOUND Coverage Report -html = build/documentation/coverage/index.html delete-gcov-files = yes -print-summary = yes \ No newline at end of file +print-summary = yes +merge-mode-functions = merge-use-line-min diff --git a/src/command-line/execution/executors.cpp b/src/command-line/execution/executors.cpp index 0b6032e8..161b6f84 100644 --- a/src/command-line/execution/executors.cpp +++ b/src/command-line/execution/executors.cpp @@ -1,18 +1,22 @@ #include "command-line/execution/executors.hpp" #include +#include #include +#include #include #include "common/logging.hpp" #include "common/time/time.hpp" +#include "common/containers.hpp" namespace found { CalibrationPipelineExecutor::CalibrationPipelineExecutor(CalibrationOptions &&options, - std::unique_ptr calibrationAlgorithm) + cnt::unique_ptr + calibrationAlgorithm) : options_(std::move(options)) { - std::unique_ptr, Quaternion>> calibrationStage( + cnt::unique_ptr, Quaternion>> calibrationStage( std::move(calibrationAlgorithm)); this->pipeline_.Complete(std::move(calibrationStage)); } @@ -42,13 +46,13 @@ DistancePipelineExecutor::~DistancePipelineExecutor() { } DistancePipelineExecutor::DistancePipelineExecutor(DistanceOptions &&options, - std::unique_ptr edgeDetectionAlgorithm, - std::unique_ptr distanceAlgorithm, - std::unique_ptr vectorizationAlgorithm) + cnt::unique_ptr edgeDetectionAlgorithm, + cnt::unique_ptr distanceAlgorithm, + cnt::unique_ptr vectorizationAlgorithm) : options_(std::move(options)) { - std::unique_ptr> edgeDetectionStage(std::move(edgeDetectionAlgorithm)); - std::unique_ptr> distanceStage(std::move(distanceAlgorithm)); - std::unique_ptr> vectorStage( + cnt::unique_ptr> edgeDetectionStage(std::move(edgeDetectionAlgorithm)); + cnt::unique_ptr> distanceStage(std::move(distanceAlgorithm)); + cnt::unique_ptr> vectorStage( std::move(vectorizationAlgorithm)); this->pipeline_.AddStage(std::move(edgeDetectionStage)) .AddStage(std::move(distanceStage)) @@ -57,15 +61,15 @@ DistancePipelineExecutor::DistancePipelineExecutor(DistanceOptions &&options, DistancePipelineExecutor::DistancePipelineExecutor(DistanceOptions &&options, - std::unique_ptr edgeDetectionAlgorithm, - std::unique_ptr filters, - std::unique_ptr distanceAlgorithm, - std::unique_ptr vectorizationAlgorithm) + cnt::unique_ptr edgeDetectionAlgorithm, + cnt::unique_ptr filters, + cnt::unique_ptr distanceAlgorithm, + cnt::unique_ptr vectorizationAlgorithm) : options_(std::move(options)) { - std::unique_ptr> edgeDetectionStage(std::move(edgeDetectionAlgorithm)); - std::unique_ptr> filterStage(std::move(filters)); - std::unique_ptr> distanceStage(std::move(distanceAlgorithm)); - std::unique_ptr> vectorStage( + cnt::unique_ptr> edgeDetectionStage(std::move(edgeDetectionAlgorithm)); + cnt::unique_ptr> filterStage(std::move(filters)); + cnt::unique_ptr> distanceStage(std::move(distanceAlgorithm)); + cnt::unique_ptr> vectorStage( std::move(vectorizationAlgorithm)); this->pipeline_.AddStage(std::move(edgeDetectionStage)) .AddStage(std::move(filterStage)) @@ -92,15 +96,14 @@ void DistancePipelineExecutor::OutputResults() { if (this->options_.calibrationData.header.version != emptyDFVer) { outputDF.header = this->options_.calibrationData.header; outputDF.relative_attitude = this->options_.calibrationData.relative_attitude; - outputDF.positions = std::make_unique(outputDF.header.num_positions + 1); - std::copy(this->options_.calibrationData.positions.get(), - this->options_.calibrationData.positions.get() + outputDF.header.num_positions, - outputDF.positions.get()); + outputDF.positions.resize(outputDF.header.num_positions + 1); + std::copy(this->options_.calibrationData.positions.begin(), + this->options_.calibrationData.positions.begin() + outputDF.header.num_positions, + outputDF.positions.begin()); } else { - outputDF.relative_attitude = this->options_.refAsOrientation - ? Quaternion::Identity() // GCOVR_EXCL_BR_LINE - : SphericalToQuaternion(this->options_.relOrientation); // GCOVR_EXCL_LINE - outputDF.positions = std::make_unique(1); + outputDF.relative_attitude = SphericalToQuaternion(this->options_.relOrientation); + outputDF.header.num_positions = 0; + outputDF.positions.resize(1); } outputDF.positions[outputDF.header.num_positions++] = {static_cast(getUT1Time().epochs), *positionVector}; if (this->options_.outputFile != "") { @@ -113,9 +116,10 @@ void DistancePipelineExecutor::OutputResults() { } OrbitPipelineExecutor::OrbitPipelineExecutor(OrbitOptions &&options, - std::unique_ptr orbitPropagationAlgorithm) + cnt::unique_ptr + orbitPropagationAlgorithm) : options_(std::move(options)) { - std::unique_ptr> orbitStage( + cnt::unique_ptr> orbitStage( std::move(orbitPropagationAlgorithm)); this->pipeline_.Complete(std::move(orbitStage)); } diff --git a/src/command-line/execution/executors.hpp b/src/command-line/execution/executors.hpp index ef91b499..4acd8308 100644 --- a/src/command-line/execution/executors.hpp +++ b/src/command-line/execution/executors.hpp @@ -50,7 +50,7 @@ class CalibrationPipelineExecutor : public PipelineExecutor { * @param calibrationAlgorithm The calibration algorithm to use */ explicit CalibrationPipelineExecutor(CalibrationOptions &&options, - std::unique_ptr calibrationAlgorithm); + cnt::unique_ptr calibrationAlgorithm); void ExecutePipeline() override; void OutputResults() override; @@ -60,6 +60,8 @@ class CalibrationPipelineExecutor : public PipelineExecutor { const CalibrationOptions options_; /// The Calibration pipeline CalibrationPipeline pipeline_; + /// The Calibration Algorithm used + cnt::unique_ptr calibrationAlgorithm; }; /** @@ -86,9 +88,9 @@ class DistancePipelineExecutor : public PipelineExecutor { * @pre Each provided stage is already "ready" (e.g., pipelines passed in were Completed) before transfer. */ explicit DistancePipelineExecutor(DistanceOptions &&options, - std::unique_ptr edgeDetectionAlgorithm, - std::unique_ptr distanceAlgorithm, - std::unique_ptr vectorizationAlgorithm); + cnt::unique_ptr edgeDetectionAlgorithm, + cnt::unique_ptr distanceAlgorithm, + cnt::unique_ptr vectorizationAlgorithm); /** * Constructs a DistancePipelineExecutor with an edge-filtering pipeline @@ -104,10 +106,10 @@ class DistancePipelineExecutor : public PipelineExecutor { * @pre Stage input/output types align with the Distance pipeline: Image -> Points -> Points -> PositionVector. */ explicit DistancePipelineExecutor(DistanceOptions &&options, - std::unique_ptr edgeDetectionAlgorithm, - std::unique_ptr filters, - std::unique_ptr distanceAlgorithm, - std::unique_ptr vectorizationAlgorithm); + cnt::unique_ptr edgeDetectionAlgorithm, + cnt::unique_ptr filters, + cnt::unique_ptr distanceAlgorithm, + cnt::unique_ptr vectorizationAlgorithm); void ExecutePipeline() override; void OutputResults() override; @@ -117,6 +119,12 @@ class DistancePipelineExecutor : public PipelineExecutor { const DistanceOptions options_; /// The Distance pipeline being used DistancePipeline pipeline_; + /// The Edge Detection Algorithm used + cnt::unique_ptr edgeDetectionAlgorithm; + /// The Distance Determination Algorithm being used + cnt::unique_ptr distanceAlgorithm; + /// The Vectorization/Rotation Algorithm being used + cnt::unique_ptr vectorizationAlgorithm; }; /** @@ -132,7 +140,7 @@ class OrbitPipelineExecutor : public PipelineExecutor { * @param orbitPropagationAlgorithm The orbit propagation algorithm to use */ explicit OrbitPipelineExecutor(OrbitOptions &&options, - std::unique_ptr orbitPropagationAlgorithm); + cnt::unique_ptr orbitPropagationAlgorithm); void ExecutePipeline() override; void OutputResults() override; @@ -142,6 +150,8 @@ class OrbitPipelineExecutor : public PipelineExecutor { const OrbitOptions options_; /// The Orbit pipeline OrbitPipeline pipeline_; + /// The Orbit Propagation Algorithm being used + cnt::unique_ptr orbitPropagationAlgorithm; }; } // namespace found diff --git a/src/command-line/found-main.cpp b/src/command-line/found-main.cpp index 340b35bd..a6c50d2e 100644 --- a/src/command-line/found-main.cpp +++ b/src/command-line/found-main.cpp @@ -21,11 +21,16 @@ int main(int argc, char **argv) { } std::string command(argv[1]); - std::unique_ptr executor; if (command == "calibration") { - executor = CreateCalibrationPipelineExecutor(ParseCalibrationOptions(argc, argv)); + cnt::unique_ptr executor = + CreateCalibrationPipelineExecutor(ParseCalibrationOptions(argc, argv)); + executor->ExecutePipeline(); + executor->OutputResults(); } else if (command == "distance") { - executor = CreateDistancePipelineExecutor(ParseDistanceOptions(argc, argv)); + cnt::unique_ptr executor = + CreateDistancePipelineExecutor(ParseDistanceOptions(argc, argv)); + executor->ExecutePipeline(); + executor->OutputResults(); // TODO: Uncomment when orbit stage is implemented // } else if (command == "orbit") { // executor = CreateOrbitPipelineExecutor(ParseOrbitOptions(argc, argv)); @@ -75,9 +80,6 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } - executor->ExecutePipeline(); - executor->OutputResults(); - return EXIT_SUCCESS; } diff --git a/src/common/containers.hpp b/src/common/containers.hpp new file mode 100644 index 00000000..bf13a69f --- /dev/null +++ b/src/common/containers.hpp @@ -0,0 +1,85 @@ +#ifndef SRC_COMMON_CONTAINERS_HPP_ +#define SRC_COMMON_CONTAINERS_HPP_ + +#ifndef FOUND_MAX_IMAGE_WIDTH +#define FOUND_MAX_IMAGE_WIDTH 1024 +#endif +#ifndef FOUND_MAX_IMAGE_HEIGHT +#define FOUND_MAX_IMAGE_HEIGHT 1024 +#endif +#ifndef FOUND_MAX_IMAGE_PIXELS +#define FOUND_MAX_IMAGE_PIXELS (FOUND_MAX_IMAGE_WIDTH * FOUND_MAX_IMAGE_HEIGHT) +#endif +#ifndef FOUND_MAX_POINTS +#define FOUND_MAX_POINTS \ + ((FOUND_MAX_IMAGE_WIDTH > FOUND_MAX_IMAGE_HEIGHT) ? FOUND_MAX_IMAGE_WIDTH : FOUND_MAX_IMAGE_HEIGHT) +#endif +#ifndef FOUND_MAX_COMPONENTS +#define FOUND_MAX_COMPONENTS (((FOUND_MAX_IMAGE_WIDTH + 1) / 2) * ((FOUND_MAX_IMAGE_HEIGHT + 1) / 2)) +#endif +#ifndef FOUND_MAX_EDGES +#define FOUND_MAX_EDGES FOUND_MAX_COMPONENTS +#endif +#ifndef FOUND_MAX_LOCATION_RECORDS +#define FOUND_MAX_LOCATION_RECORDS 4096 +#endif + +#ifdef FOUND_USE_ETL_CONTAINERS +#include +#include +#include + +#include "etl/pool.h" +#include "etl/vector.h" +#else +#include +#include +#include +#include +#endif + +namespace found::cnt { + +#ifdef FOUND_USE_ETL_CONTAINERS +template +using vector = etl::vector; + +template +using pool = etl::pool; +#else +struct dummy_pool { +}; + +template +using vector = std::vector; + +template +using pool = dummy_pool; +#endif + +template +using unique_ptr = std::unique_ptr; + +template +unique_ptr make_unique(Args &&...args) { + return std::make_unique(std::forward(args)...); +} + +template +unique_ptr make_unique_as(Args &&...args) { + return std::make_unique(std::forward(args)...); +} + +template +unique_ptr make_unique([[maybe_unused]] pool &pool_ref, Args &&...args) { + return make_unique(std::forward(args)...); +} + +template +unique_ptr make_unique_as([[maybe_unused]] pool &pool_ref, Args &&...args) { + return make_unique_as(std::forward(args)...); +} + +} // namespace found::cnt + +#endif // SRC_COMMON_CONTAINERS_HPP_ diff --git a/src/common/decimal.hpp b/src/common/decimal.hpp index e0875ffe..1381e68b 100644 --- a/src/common/decimal.hpp +++ b/src/common/decimal.hpp @@ -59,7 +59,7 @@ #define DECIMAL_ROUND(x) (DECIMAL(std::round(x))) #define DECIMAL_CEIL(x) (DECIMAL(std::ceil(x))) #define DECIMAL_FLOOR(x) (DECIMAL(std::floor(x))) -#define DECIMAL_ABS(x) (DECIMAL(std::abs(x))) +#define DECIMAL_ABS(x) (DECIMAL(std::fabs(x))) // Trig Methods wrapped with Decimal typecast) #define DECIMAL_SIN(x) (DECIMAL(std::sin(x))) diff --git a/src/common/pipeline/pipelines.hpp b/src/common/pipeline/pipelines.hpp index 46c6d632..49cced1c 100644 --- a/src/common/pipeline/pipelines.hpp +++ b/src/common/pipeline/pipelines.hpp @@ -8,6 +8,7 @@ #include #include +#include "common/containers.hpp" #include "common/pipeline/stages.hpp" /// The default number of pipeline stages @@ -64,7 +65,7 @@ class Pipeline : public FunctionStage { protected: /// Ownership storage for the stages - std::unique_ptr stages[N]; + cnt::unique_ptr stages[N]; /// The number of stages size_t size = 0; /// Whether we're complete @@ -85,7 +86,7 @@ class Pipeline : public FunctionStage { * @post stage is stored inside this such that stage and this share the same * lifetime. Ownership of stage is transferred to this */ - inline void AddStageHelper(std::unique_ptr &&stage) { + inline void AddStageHelper(cnt::unique_ptr &&stage) { assert(this->size < N); if (this->ready) throw std::invalid_argument("Pipeline is already ready"); this->stages[size++] = std::move(stage); @@ -105,7 +106,7 @@ class Pipeline : public FunctionStage { * @post This is now ready. Ownership of stage is transferred to this. The * lifetime of stage matches this */ - inline void CompleteHelper(std::unique_ptr &&stage) { + inline void CompleteHelper(cnt::unique_ptr &&stage) { assert(this->size < N); if (this->ready) throw std::invalid_argument("Pipeline is already ready"); this->stages[size++] = std::move(stage); @@ -168,7 +169,7 @@ class SequentialPipeline : public Pipeline { * @pre This method is called when the number of registered stages is * less than N - 1 */ - template SequentialPipeline &AddStage(std::unique_ptr> stage) { + template SequentialPipeline &AddStage(cnt::unique_ptr> stage) { FunctionStage *stagePtr = stage.get(); if (this->size == 0) { if (!std::is_same::value) { @@ -201,7 +202,7 @@ class SequentialPipeline : public Pipeline { * * @pre The number of registered stages is less than N */ - template SequentialPipeline &Complete(std::unique_ptr> stage) { + template SequentialPipeline &Complete(cnt::unique_ptr> stage) { assert(this->size < N); if (this->ready) throw std::invalid_argument("Pipeline is already ready"); this->AddStage(std::move(stage)); @@ -275,7 +276,7 @@ class ModifyingPipeline : public Pipeline { * * @return this, with the added stage */ - ModifyingPipeline &AddStage(std::unique_ptr> stage) { + ModifyingPipeline &AddStage(cnt::unique_ptr> stage) { assert(this->size < N - 1); Pipeline::AddStageHelper(std::move(stage)); return *this; @@ -291,7 +292,7 @@ class ModifyingPipeline : public Pipeline { * @pre This method is called when the number of * registered stages is less than N */ - ModifyingPipeline &Complete(std::unique_ptr> stage) { + ModifyingPipeline &Complete(cnt::unique_ptr> stage) { assert(this->size < N); Pipeline::CompleteHelper(std::move(stage)); return *this; diff --git a/src/common/pipeline/stages.hpp b/src/common/pipeline/stages.hpp index cc526895..9fcec1ad 100644 --- a/src/common/pipeline/stages.hpp +++ b/src/common/pipeline/stages.hpp @@ -72,7 +72,7 @@ class FunctionStage : public Stage &, raw_type> { * @pre this->product points to a valid location */ void DoAction() override { - *this->product = this->Run(this->resource); + *this->product = this->Run(this->resource); }; /** diff --git a/src/common/style.hpp b/src/common/style.hpp index 916e007b..30f13aa1 100644 --- a/src/common/style.hpp +++ b/src/common/style.hpp @@ -1,21 +1,21 @@ #ifndef SRC_COMMON_STYLE_HPP_ #define SRC_COMMON_STYLE_HPP_ -#include #include #include #include #include +#include +#include "common/containers.hpp" #include "common/spatial/attitude-utils.hpp" #include "common/decimal.hpp" #include "common/pipeline/pipelines.hpp" namespace found { -/// The output for Edge Detection Algorithms (edge.hpp/cpp). Currently set -/// to a vector of 2D points on the image, according to image coordinate systems -typedef std::vector Points; +/// The output for Edge Detection Algorithms (edge.hpp/cpp). Uses found::cnt::vector to switch backends. +typedef cnt::vector Points; /// The output for Vector Assembly Algorithms (vectorize.hpp). Currently set /// to a 3D Vector that represents the satellite's position relative to Earth's @@ -62,7 +62,7 @@ struct Edge { }; /// A collection of Edges -typedef std::vector Edges; +typedef cnt::vector Edges; /** * Represents a connected component in an image @@ -99,7 +99,7 @@ struct LocationRecord { // so that we don't have to copy the data. /// A collection of Location Records -typedef std::vector LocationRecords; +typedef cnt::vector LocationRecords; /** * OrbitParams defines the orbital diff --git a/src/datafile/datafile.hpp b/src/datafile/datafile.hpp index 878c3b0a..82fd9041 100644 --- a/src/datafile/datafile.hpp +++ b/src/datafile/datafile.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "common/spatial/attitude-utils.hpp" // Includes Vec3 and EulerAngles #include "common/style.hpp" @@ -78,7 +79,7 @@ struct DataFile { /** * @brief Collection of location records in the file. */ - std::unique_ptr positions; + std::vector positions; /** * The path of this DataFile. diff --git a/src/datafile/encoding.hpp b/src/datafile/encoding.hpp index 2d94010b..72ccb87e 100644 --- a/src/datafile/encoding.hpp +++ b/src/datafile/encoding.hpp @@ -1,8 +1,7 @@ #ifndef SRC_DATAFILE_ENCODING_HPP_ #define SRC_DATAFILE_ENCODING_HPP_ -#include -#include +#include #include "common/decimal.hpp" @@ -17,12 +16,12 @@ namespace found { /** * @brief Converts a 16-bit integer from host byte order to network byte order. - * + * * @param v The integer to convert - * + * * @return The integer in network byte order. */ -inline uint16_t htons(uint16_t v) { +inline uint16_t found_htons(uint16_t v) { #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ return (v << 8) | (v >> 8); #else @@ -32,12 +31,12 @@ inline uint16_t htons(uint16_t v) { /** * @brief Converts a 16-bit integer from network byte order to host byte order. - * + * * @param v The integer to convert - * + * * @return The integer in host byte order. */ -inline uint16_t ntohs(uint16_t v) { +inline uint16_t found_ntohs(uint16_t v) { #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ return (v << 8) | (v >> 8); #else @@ -47,82 +46,82 @@ inline uint16_t ntohs(uint16_t v) { /** * @brief Converts a 32-bit integer from host byte order to network byte order. - * + * * @param v The integer to convert - * + * * @return The integer in network byte order. */ -inline uint32_t htonl(uint32_t v) { - #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ - return ((v & 0xFF000000) >> 24) | - ((v & 0x00FF0000) >> 8) | - ((v & 0x0000FF00) << 8) | - ((v & 0x000000FF) << 24); - #else - return v; - #endif +inline uint32_t found_htonl(uint32_t v) { +#if ENDIANESS == __ORDER_LITTLE_ENDIAN__ + return ((v & 0xFF000000) >> 24) | + ((v & 0x00FF0000) >> 8) | + ((v & 0x0000FF00) << 8) | + ((v & 0x000000FF) << 24); +#else + return v; +#endif } /** * @brief Converts a 32-bit integer from network byte order to host byte order. - * + * * @param v The integer to convert - * + * * @return The integer in host byte order. */ -inline uint32_t ntohl(uint32_t v) { - #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ - return ((v & 0xFF000000) >> 24) | - ((v & 0x00FF0000) >> 8) | - ((v & 0x0000FF00) << 8) | - ((v & 0x000000FF) << 24); - #else - return v; - #endif +inline uint32_t found_ntohl(uint32_t v) { +#if ENDIANESS == __ORDER_LITTLE_ENDIAN__ + return ((v & 0xFF000000) >> 24) | + ((v & 0x00FF0000) >> 8) | + ((v & 0x0000FF00) << 8) | + ((v & 0x000000FF) << 24); +#else + return v; +#endif } /** * @brief Converts a 64-bit integer from host byte order to network byte order. - * + * * @param v The integer to convert - * + * * @return The integer in network byte order. */ -inline uint64_t htonl(uint64_t v) { - #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ - return ((v & 0xFF00000000000000ULL) >> 56) | - ((v & 0x00FF000000000000ULL) >> 40) | - ((v & 0x0000FF0000000000ULL) >> 24) | - ((v & 0x000000FF00000000ULL) >> 8) | - ((v & 0x00000000FF000000ULL) << 8) | - ((v & 0x0000000000FF0000ULL) << 24) | - ((v & 0x000000000000FF00ULL) << 40) | - ((v & 0x00000000000000FFULL) << 56); - #else - return v; - #endif +inline uint64_t found_htonll(uint64_t v) { +#if ENDIANESS == __ORDER_LITTLE_ENDIAN__ + return ((v & 0xFF00000000000000ULL) >> 56) | + ((v & 0x00FF000000000000ULL) >> 40) | + ((v & 0x0000FF0000000000ULL) >> 24) | + ((v & 0x000000FF00000000ULL) >> 8) | + ((v & 0x00000000FF000000ULL) << 8) | + ((v & 0x0000000000FF0000ULL) << 24) | + ((v & 0x000000000000FF00ULL) << 40) | + ((v & 0x00000000000000FFULL) << 56); +#else + return v; +#endif } /** * @brief Converts a 64-bit integer from network byte order to host byte order. - * + * * @param v The integer to convert - * + * * @return The integer in host byte order. */ -inline uint64_t ntohl(uint64_t v) { - #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ - return ((v & 0xFF00000000000000ULL) >> 56) | - ((v & 0x00FF000000000000ULL) >> 40) | - ((v & 0x0000FF0000000000ULL) >> 24) | - ((v & 0x000000FF00000000ULL) >> 8) | - ((v & 0x00000000FF000000ULL) << 8) | - ((v & 0x0000000000FF0000ULL) << 24) | - ((v & 0x000000000000FF00ULL) << 40) | - ((v & 0x00000000000000FFULL) << 56); - #else - return v; - #endif +inline uint64_t found_ntohll(uint64_t v) { +#if ENDIANESS == __ORDER_LITTLE_ENDIAN__ + return ((v & 0xFF00000000000000ULL) >> 56) | + ((v & 0x00FF000000000000ULL) >> 40) | + ((v & 0x0000FF0000000000ULL) >> 24) | + ((v & 0x000000FF00000000ULL) >> 8) | + ((v & 0x00000000FF000000ULL) << 8) | + ((v & 0x0000000000FF0000ULL) << 24) | + ((v & 0x000000000000FF00ULL) << 40) | + ((v & 0x00000000000000FFULL) << 56); +#else + return v; +#endif } /** @@ -147,7 +146,7 @@ union _d_u_ { /** * @brief Converts a float from network byte order to host byte order. - * + * * @param v The float value to convert. * @return The converted float value in host byte order. */ @@ -155,7 +154,7 @@ inline float htonf(float v) { #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ _f_u_ t; t.f = v; - t.u = htonl(t.u); + t.u = found_htonl(t.u); return t.f; #else return v; @@ -164,94 +163,94 @@ inline float htonf(float v) { /** * @brief Converts a float from network byte order to host byte order. - * + * * @param v The float value to convert. - * + * * @return The converted float value in host byte order. */ inline float ntohf(float v) { - #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ - _f_u_ t; - t.f = v; - t.u = ntohl(t.u); - return t.f; - #else - return v; - #endif +#if ENDIANESS == __ORDER_LITTLE_ENDIAN__ + _f_u_ t; + t.f = v; + t.u = found_ntohl(t.u); + return t.f; +#else + return v; +#endif } /** * @brief Converts a double from network byte order to host byte order. - * + * * @param v The double value to convert. - * + * * @return The converted double value in host byte order. */ inline double ntohd(double v) { - #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ - _d_u_ t; - t.d = v; - t.u = ntohl(t.u); - return t.d; - #else - return v; - #endif +#if ENDIANESS == __ORDER_LITTLE_ENDIAN__ + _d_u_ t; + t.d = v; + t.u = found_ntohll(t.u); + return t.d; +#else + return v; +#endif } /** * @brief Converts a double from host byte order to network byte order. - * + * * @param v The double value to convert. - * + * * @return The converted double value in network byte order. */ inline double htond(double v) { - #if ENDIANESS == __ORDER_LITTLE_ENDIAN__ - _d_u_ t; - t.d = v; - t.u = htonl(t.u); - return t.d; - #else - return v; - #endif +#if ENDIANESS == __ORDER_LITTLE_ENDIAN__ + _d_u_ t; + t.d = v; + t.u = found_htonll(t.u); + return t.d; +#else + return v; +#endif } /** * @brief Converts a decimal from host byte order to network byte order. - * + * * @param v The decimal value to convert. - * - * @return The converted decimal value in network byte order. + * + * @return The decimal value in network byte order. */ inline decimal htondec(decimal v) { - #ifdef FOUND_FLOAT_MODE - return htonf(v); - #else - return htond(v); - #endif +#ifdef FOUND_FLOAT_MODE + return htonf(v); +#else + return htond(v); +#endif } /** * @brief Converts a decimal from network byte order to host byte order. - * + * * @param v The decimal value to convert. - * - * @return The converted decimal value in host byte order. + * + * @return The decimal value in host byte order. */ inline decimal ntohdec(decimal v) { - #ifdef FOUND_FLOAT_MODE - return htonf(v); - #else - return htond(v); - #endif +#ifdef FOUND_FLOAT_MODE + return htonf(v); +#else + return htond(v); +#endif } /** * @brief Calculates the CRC32 checksum for a given data buffer. - * + * * @param data Pointer to the data buffer. * @param length The size of the data buffer in bytes. - * + * * @return The calculated CRC32 checksum. */ uint32_t calculateCRC32(const void* data, size_t length); diff --git a/src/datafile/serialization.cpp b/src/datafile/serialization.cpp index e57385f6..d08534a4 100644 --- a/src/datafile/serialization.cpp +++ b/src/datafile/serialization.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "common/spatial/attitude-utils.hpp" @@ -12,15 +13,15 @@ namespace found { void hton(DataFileHeader& header) { - header.version = htonl(header.version); - header.num_positions = htonl(header.num_positions); - header.crc = htonl(header.crc); + header.version = found_htonl(header.version); + header.num_positions = found_htonl(header.num_positions); + header.crc = found_htonl(header.crc); } void ntoh(DataFileHeader& header) { - header.version = ntohl(header.version); - header.num_positions = ntohl(header.num_positions); - header.crc = ntohl(header.crc); + header.version = found_ntohl(header.version); + header.num_positions = found_ntohl(header.num_positions); + header.crc = found_ntohl(header.crc); } /** @@ -79,7 +80,7 @@ inline void read(std::istream& stream, decimal& value) { * */ inline void write(std::ostream& stream, const uint64_t& value) { - uint64_t v = htonl(value); + uint64_t v = found_htonll(value); stream.write(reinterpret_cast(&v), sizeof(uint64_t)); } @@ -99,7 +100,7 @@ inline void read(std::istream& stream, uint64_t& value) { if (stream.gcount() != sizeof(uint64_t)) { throw std::ios_base::failure("Failed to read uint64_t value"); } - value = ntohl(value); + value = found_ntohll(value); } /** @@ -113,7 +114,7 @@ inline void read(std::istream& stream, uint64_t& value) { * */ inline void write(std::ostream& stream, const uint32_t& value) { - uint32_t v = htonl(value); + uint32_t v = found_htonl(value); stream.write(reinterpret_cast(&v), sizeof(uint32_t)); } @@ -133,7 +134,7 @@ inline void read(std::istream& stream, uint32_t& value) { if (stream.gcount() != sizeof(uint32_t)) { throw std::ios_base::failure("Failed to read uint32_t value"); } - value = ntohl(value); + value = found_ntohl(value); } /** @@ -252,6 +253,12 @@ uint32_t calculateCRC32(const void* data, size_t length) { } void serializeDataFile(const DataFile& data, std::ostream& stream) { + if (data.header.num_positions > FOUND_MAX_LOCATION_RECORDS) { + throw std::runtime_error("DataFile contains more position records than FOUND_MAX_LOCATION_RECORDS"); + } + if (data.header.num_positions > data.positions.size()) { + throw std::runtime_error("DataFile header.num_positions exceeds stored position count"); + } DataFileHeader header = data.header; header.crc = calculateCRC32(&header, sizeof(header) - sizeof(header.crc)); hton(header); @@ -270,7 +277,10 @@ DataFile deserializeDataFile(std::istream& stream) { read(stream, data.relative_attitude); - data.positions = std::make_unique(data.header.num_positions); + if (data.header.num_positions > FOUND_MAX_LOCATION_RECORDS) { + throw std::runtime_error("DataFile contains more position records than FOUND_MAX_LOCATION_RECORDS"); + } + data.positions.resize(data.header.num_positions); for (uint32_t i = 0; i < data.header.num_positions; ++i) { read(stream, data.positions[i]); } @@ -313,7 +323,7 @@ DataFileHeader readHeader(std::istream& stream) { // Validate CRC uint32_t expected_crc = calculateCRC32(&header, sizeof(header) - sizeof(header.crc)); if (header.crc != expected_crc) { - LOG_ERROR("Expected CRC: " << expected_crc << ", Found CRC: " << ntohl(header.crc)); + LOG_ERROR("Expected CRC: " << expected_crc << ", Found CRC: " << found_ntohl(header.crc)); throw std::ios_base::failure("Header CRC validation failed: Corrupted file"); } diff --git a/src/distance/distance.cpp b/src/distance/distance.cpp index 484831de..5c84de49 100644 --- a/src/distance/distance.cpp +++ b/src/distance/distance.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "common/logging.hpp" #include "common/spatial/attitude-utils.hpp" @@ -123,12 +124,14 @@ PositionVector IterativeSphericalDistanceDeterminationAlgorithm::Run(const Point // Step 1a: Get all unit vector projections of each point and setup logits size_t i = 0; size_t pointsSize = p.size(); - std::unique_ptr projectedPoints(new Vec3[pointsSize]); + cnt::vector projectedPoints; + projectedPoints.resize(pointsSize); for (const Vec2 &point : p) { projectedPoints[i++] = this->cam_.PixelToImageCoordinates(point).normalized(); } i = 0; - std::unique_ptr logits(new uint64_t[pointsSize]); + cnt::vector logits; + logits.resize(pointsSize); // Step 2a: Use the first estimate as a reference PositionVector first(SphericalDistanceDeterminationAlgorithm::Run(p)); @@ -166,7 +169,8 @@ PositionVector IterativeSphericalDistanceDeterminationAlgorithm::Run(const Point decimal IterativeSphericalDistanceDeterminationAlgorithm::GenerateLoss(PositionVector &position, decimal targetDistanceSq, - std::unique_ptr &projectedPoints, + cnt::vector + &projectedPoints, size_t size) { // Generate the loss on point (offset it so it won't be nan, and initialize with distance // error): @@ -187,12 +191,12 @@ decimal IterativeSphericalDistanceDeterminationAlgorithm::GenerateLoss(PositionV } PositionVector IterativeSphericalDistanceDeterminationAlgorithm::ShuffledCall( - std::unique_ptr &source, + cnt::vector &source, size_t n, - std::unique_ptr &logits) { + cnt::vector &logits) { // Step 0: Setup the random number generators - static std::random_device device; // GCOVR_EXCL_LINE - static std::mt19937 gen(device()); // GCOVR_EXCL_LINE + static std::random_device device; // GCOVR_EXCL_BR_LINE + static std::mt19937 gen(device()); // GCOVR_EXCL_BR_LINE // This is okay (being static) since we always override the values // Uniformly generate the first number @@ -207,7 +211,7 @@ PositionVector IterativeSphericalDistanceDeterminationAlgorithm::ShuffledCall( logits[j] = this->Pow(j - index1, this->pdfOrder_); } // Sample for the next number - std::discrete_distribution dist1(logits.get(), logits.get() + n); + std::discrete_distribution dist1(logits.data(), logits.data() + n); size_t index2 = dist1(gen); Vec3 &b = source[index2]; assert(dist1.min() == 0); @@ -220,7 +224,7 @@ PositionVector IterativeSphericalDistanceDeterminationAlgorithm::ShuffledCall( logits[j] *= this->Pow(j - index2, this->pdfOrder_); } // Sample for the last number - std::discrete_distribution dist2(logits.get(), logits.get() + n); + std::discrete_distribution dist2(logits.data(), logits.data() + n); Vec3 &c = source[dist2(gen)]; assert(dist2.min() == 0); assert(dist2.max() == n - 1); diff --git a/src/distance/distance.hpp b/src/distance/distance.hpp index cf37f546..990c3489 100644 --- a/src/distance/distance.hpp +++ b/src/distance/distance.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "common/style.hpp" #include "common/pipeline/stages.hpp" @@ -226,7 +227,7 @@ class IterativeSphericalDistanceDeterminationAlgorithm : public SphericalDistanc */ decimal GenerateLoss(PositionVector &position, decimal targetDistanceSq, - std::unique_ptr &projectedPoints, + cnt::vector &projectedPoints, size_t size); /** @@ -261,7 +262,9 @@ class IterativeSphericalDistanceDeterminationAlgorithm : public SphericalDistanc * terrible change in terms of code, but is more compuationally * complex */ - PositionVector ShuffledCall(std::unique_ptr &source, size_t n, std::unique_ptr &logits); + PositionVector ShuffledCall(cnt::vector &source, + size_t n, + cnt::vector &logits); /** * Performs exponentiation for uint64_t diff --git a/src/distance/edge.cpp b/src/distance/edge.cpp index 2b8e5ee1..ee5d3d71 100644 --- a/src/distance/edge.cpp +++ b/src/distance/edge.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -19,16 +21,22 @@ namespace found { Points SimpleEdgeDetectionAlgorithm::Run(const Image &image) { // Step 0: Define Common Variables uint64_t imageSize = image.width * image.height; + if (imageSize > FOUND_MAX_IMAGE_PIXELS) { + throw std::runtime_error("Image pixel count exceeds FOUND_MAX_IMAGE_PIXELS"); + } // Step 1: Obtain the component that represents space - Components spaces = ConnectedComponentsAlgorithm(image, [&](uint64_t index, const Image &image) { - // Average the pixel, then threshold it + std::function isSpacePixel = [&](uint64_t index, const Image &inputImage) { + // Average the pixel, then threshold it. int sum = 0; - for (int i = 0; i < image.channels; i++) sum += image.image[image.channels * index + i]; - return sum / image.channels < this->threshold_; - }); + for (int i = 0; i < inputImage.channels; i++) { + sum += inputImage.image[inputImage.channels * index + i]; + } + return sum / inputImage.channels < this->threshold_; + }; + Components spaces = ConnectedComponentsAlgorithm(image, isSpacePixel); Component *space = nullptr; - for (auto &component : spaces) { + for (Component &component : spaces) { // Basically, if the component touches the border, and its the biggest one, // we assume it is space if ((component.upperLeft.x() < this->borderLength_ || @@ -247,14 +255,18 @@ inline int NWayEquivalenceAdd(const Image &image, } } } - return minLabel; + return minLabel; // GCOVR_EXCL_BR_LINE } Components ConnectedComponentsAlgorithm(const Image &image, std::function Criteria) { // Step 0: Setup the Problem + const uint64_t imageSize = static_cast(image.width) * image.height; + if (imageSize > FOUND_MAX_IMAGE_PIXELS) { + throw std::runtime_error("Image pixel count exceeds FOUND_MAX_IMAGE_PIXELS"); + } std::unordered_map components; std::unordered_map equivalencies; - std::unique_ptr componentPoints(new int[image.width * image.height]{}); // Faster than using a hashset + std::vector componentPoints(imageSize, 0); int L = 0; int adjacentLabels[4]; @@ -269,7 +281,6 @@ Components ConnectedComponentsAlgorithm(const Image &image, std::function(image.width * image.height); for (uint64_t i = 1; i < imageSize; i++) { // Step 1b: Check if the pixel is an component point if (!Criteria(i, image)) { @@ -279,50 +290,60 @@ Components ConnectedComponentsAlgorithm(const Image &image, std::function= 0; i--) { - auto it = equivalencies.find(i); + std::unordered_map::iterator it = equivalencies.find(i); if (it == equivalencies.end()) continue; // Guarenteed to be the lowest label int lowestLabel = it->second; // Merge the components - auto compIt = components.find(i); + std::unordered_map::iterator compIt = components.find(i); // compIt is guarenteed to exist, so we do not perform a check here - auto &compToMerge = compIt->second; - auto &lowestComp = components[lowestLabel]; + Component &compToMerge = compIt->second; + Component &lowestComp = components[lowestLabel]; lowestComp.points.insert(compToMerge.points.begin(), compToMerge.points.end()); if (compToMerge.upperLeft.x() < lowestComp.upperLeft.x()) lowestComp.upperLeft.x() = compToMerge.upperLeft.x(); @@ -366,9 +387,15 @@ Components ConnectedComponentsAlgorithm(const Image &image, std::function &entry : components) { + const Component &component = entry.second; + if (result.size() >= FOUND_MAX_COMPONENTS) { + throw std::runtime_error("Connected component count exceeds FOUND_MAX_COMPONENTS"); + } + result.push_back(component); + } - return result; + return result; // GCOVR_EXCL_BR_LINE } } // namespace found diff --git a/src/providers/converters.hpp b/src/providers/converters.hpp index 2d7a76a5..a8aeb484 100644 --- a/src/providers/converters.hpp +++ b/src/providers/converters.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "common/logging.hpp" @@ -77,12 +78,12 @@ inline EulerAngles strtoea(const std::string &str) { size_t index = 0; while (index != 2 && end != std::string::npos) { - result[index++] = strtodecimal(str.substr(start, end - start)); + result[index++] = strtodecimal(str.substr(start, end - start)); // GCOVR_EXCL_BR_LINE start = end + 1; end = str.find(delimiter, start); } - result[index++] = strtodecimal(str.substr(start)); + result[index++] = strtodecimal(str.substr(start)); // GCOVR_EXCL_BR_LINE while (index != 3) result[index++] = 0; @@ -116,7 +117,7 @@ inline Image strtoimage(const std::string &str) { Image image; image.image = stbi_load(str.c_str(), &image.width, &image.height, &image.channels, 0); if (!image.image) { - throw std::runtime_error("Could not load image " + str + ": " + stbi_failure_reason()); + throw std::runtime_error("Could not load image " + str + ": " + stbi_failure_reason()); // GCOVR_EXCL_BR_LINE } return image; } @@ -142,10 +143,12 @@ inline DataFile strtodf(const std::string &str) { */ inline LocationRecords strtolr(const std::string &str) { if (str.size() >= 6) { - if (str.substr(str.size() - 6) == ".found") { + if (str.substr(str.size() - 6) == ".found") { // GCOVR_EXCL_BR_LINE LOG_INFO("Getting Position Data from Data File (*.found)"); - DataFile data = strtodf(str); - return LocationRecords(data.positions.get(), data.positions.get() + data.header.num_positions); + DataFile data = strtodf(str); // GCOVR_EXCL_BR_LINE + // GCOVR_EXCL_BR_LINE: excludes stdlib constructor exception-edge branches. + return LocationRecords(data.positions.data(), + data.positions.data() + data.header.num_positions); } } @@ -153,7 +156,7 @@ inline LocationRecords strtolr(const std::string &str) { LocationRecords records; std::ifstream file(str); if (!file.is_open()) { - throw std::runtime_error("Could not open file " + str); + throw std::runtime_error("Could not open file " + str); // GCOVR_EXCL_BR_LINE } std::string line; @@ -162,7 +165,11 @@ inline LocationRecords strtolr(const std::string &str) { LocationRecord record; if (!(iss >> record.timestamp >> record.position.x() >> record.position.y() >> record.position.z())) { file.close(); - throw std::runtime_error("Invalid format for file " + str + ": " + line); + throw std::runtime_error("Invalid format for file " + str + ": " + line); // GCOVR_EXCL_BR_LINE + } + if (records.size() >= FOUND_MAX_LOCATION_RECORDS) { + file.close(); + throw std::runtime_error("Position record count exceeds FOUND_MAX_LOCATION_RECORDS"); } records.push_back(record); } diff --git a/src/providers/factory.hpp b/src/providers/factory.hpp index 251ab046..998567d2 100644 --- a/src/providers/factory.hpp +++ b/src/providers/factory.hpp @@ -18,9 +18,9 @@ namespace found { * * @return A pointer to a CalibrationPipelineExecutor */ -inline std::unique_ptr CreateCalibrationPipelineExecutor(CalibrationOptions &&options) { - return std::make_unique(std::move(options), - ProvideCalibrationAlgorithm(std::forward(options))); +inline cnt::unique_ptr CreateCalibrationPipelineExecutor(CalibrationOptions &&options) { + return cnt::make_unique( + std::move(options), ProvideCalibrationAlgorithm(std::forward(options))); } /** @@ -30,27 +30,22 @@ inline std::unique_ptr CreateCalibrationPipelineExe * * @return A pointer to a DistancePipelineExecutor */ -inline std::unique_ptr CreateDistancePipelineExecutor(DistanceOptions &&options) { - std::unique_ptr edgeAlg = ProvideEdgeDetectionAlgorithm( +inline cnt::unique_ptr CreateDistancePipelineExecutor(DistanceOptions &&options) { + cnt::unique_ptr edgeAlg = ProvideEdgeDetectionAlgorithm( std::forward(options)); - std::unique_ptr filtersOpt = ProvideEdgeFilteringAlgorithm( + cnt::unique_ptr filtersOpt = ProvideEdgeFilteringAlgorithm( std::forward(options)); - std::unique_ptr distAlg = ProvideDistanceDeterminationAlgorithm( + cnt::unique_ptr distAlg = ProvideDistanceDeterminationAlgorithm( std::forward(options)); - std::unique_ptr vecAlg = ProvideVectorGenerationAlgorithm( - std::forward(options)); + cnt::unique_ptr vecAlg = ProvideVectorGenerationAlgorithm( + std::forward(options)); if (filtersOpt) { - return std::make_unique(std::move(options), - std::move(edgeAlg), - std::move(filtersOpt), - std::move(distAlg), - std::move(vecAlg)); + return cnt::make_unique(std::move(options), std::move(edgeAlg), + std::move(filtersOpt), std::move(distAlg), std::move(vecAlg)); } - return std::make_unique(std::move(options), - std::move(edgeAlg), - std::move(distAlg), - std::move(vecAlg)); + return cnt::make_unique(std::move(options), std::move(edgeAlg), + std::move(distAlg), std::move(vecAlg)); } diff --git a/src/providers/stage-providers.hpp b/src/providers/stage-providers.hpp index 86703b8d..a3435d7f 100644 --- a/src/providers/stage-providers.hpp +++ b/src/providers/stage-providers.hpp @@ -32,9 +32,9 @@ namespace found { * * @return A pointer to the CalibrationAlgorithm */ -inline std::unique_ptr ProvideCalibrationAlgorithm( +inline cnt::unique_ptr ProvideCalibrationAlgorithm( [[maybe_unused]] const CalibrationOptions &&options) { - return std::make_unique(); + return cnt::make_unique_as(); } /** @@ -42,12 +42,13 @@ inline std::unique_ptr ProvideCalibrationAlgorithm( * * @param options The options to derive the edge detection algorithm from * - * @return std::unique_ptr The edge detection algorithm + * @return cnt::unique_ptr The edge detection algorithm */ -inline std::unique_ptr ProvideEdgeDetectionAlgorithm(const DistanceOptions &&options) { - return std::make_unique(options.SEDAThreshold, - options.SEDABorderLen, - options.SEDAOffset); +inline cnt::unique_ptr ProvideEdgeDetectionAlgorithm(const DistanceOptions &&options) { + return cnt::make_unique_as( + options.SEDAThreshold, + options.SEDABorderLen, + options.SEDAOffset); } /** @@ -55,25 +56,19 @@ inline std::unique_ptr ProvideEdgeDetectionAlgorithm(con * * @param options The options to derive the distance determination algorithm from * - * @return std::unique_ptr The distance determination algorithm + * @return cnt::unique_ptr The distance determination algorithm */ -inline std::unique_ptr ProvideDistanceDeterminationAlgorithm( +inline cnt::unique_ptr ProvideDistanceDeterminationAlgorithm( const DistanceOptions &&options) { if (options.distanceAlgo == SDDA) { - return std::make_unique(options.radius, Camera(options.focalLength, - options.pixelSize, options.image.width, options.image.height)); + return cnt::make_unique_as( + options.radius, Camera(options.focalLength, options.pixelSize, options.image.width, + options.image.height)); } else if (options.distanceAlgo == ISDDA) { - return std::make_unique(options.radius, - Camera(options.focalLength, - options.pixelSize, - options.image.width, - options.image.height), - options.ISDDAMinIters, - options.ISDDAMaxRefresh, - options.ISDDADistRatio, - options.ISDDADiscimRatio, - options.ISDDAPdfOrd, - options.ISDDARadLossOrd); + return cnt::make_unique_as( + options.radius, Camera(options.focalLength, options.pixelSize, options.image.width, + options.image.height), options.ISDDAMinIters, options.ISDDAMaxRefresh, + options.ISDDADistRatio, options.ISDDADiscimRatio, options.ISDDAPdfOrd, options.ISDDARadLossOrd); } else { LOG_ERROR("Unrecognized distance algorithm: " << options.distanceAlgo); throw std::runtime_error("Unrecognized distance algorithm: " + options.distanceAlgo); @@ -85,21 +80,23 @@ inline std::unique_ptr ProvideDistanceDeterminat * * @param options The options to derive the vector generation algorithm from * - * @return std::unique_ptr The vector generation algorithm + * @return cnt::unique_ptr The vector generation algorithm */ -inline std::unique_ptr ProvideVectorGenerationAlgorithm(const DistanceOptions &&options) { +inline cnt::unique_ptr ProvideVectorGenerationAlgorithm(const DistanceOptions &&options) { Quaternion referenceOrientation = SphericalToQuaternion(options.refOrientation); if (options.calibrationData.header.version != emptyDFVer) { LOG_INFO("Using DataFile for calibration information"); - return std::make_unique(options.calibrationData.relative_attitude, - referenceOrientation); + return cnt::make_unique_as( + options.calibrationData.relative_attitude, referenceOrientation); } else { Quaternion relativeOrientation = SphericalToQuaternion(options.relOrientation); if (options.refAsOrientation) { LOG_INFO("Using provided reference orientation for calibration information"); - return std::make_unique(referenceOrientation); + return cnt::make_unique_as( + referenceOrientation); } - return std::make_unique(relativeOrientation, referenceOrientation); + return cnt::make_unique_as( + relativeOrientation, referenceOrientation); } } @@ -109,18 +106,18 @@ inline std::unique_ptr ProvideVectorGenerationAlgorit * * @param options The options to derive the edge filtering algorithm from * - * @return std::unique_ptr The edge filtering algorithm + * @return cnt::unique_ptr The edge filtering algorithm */ -inline std::unique_ptr ProvideEdgeFilteringAlgorithm(const DistanceOptions &&options) { - std::unique_ptr pipeline = std::make_unique(); +inline cnt::unique_ptr ProvideEdgeFilteringAlgorithm(const DistanceOptions &&options) { + cnt::unique_ptr pipeline = cnt::make_unique(); bool added = false; if (options.enableNoOpEdgeFilter) { - pipeline->Complete(std::make_unique()); + pipeline->Complete(cnt::make_unique_as, NoOpEdgeFilter>()); added = true; } - if (!added) return nullptr; + if (!added) return cnt::unique_ptr(); return pipeline; } diff --git a/test/command-line/execution/executors-test.cpp b/test/command-line/execution/executors-test.cpp index 9d40cfc7..0c6e1071 100644 --- a/test/command-line/execution/executors-test.cpp +++ b/test/command-line/execution/executors-test.cpp @@ -51,7 +51,10 @@ TEST(ExecutorsTest, TestCalibrationPipelineExecutor) { temp_df }; - CalibrationPipelineExecutor executor(std::move(options), std::make_unique()); + static cnt::pool calibrationAlgorithmPool; + CalibrationPipelineExecutor executor( + std::move(options), + cnt::make_unique(calibrationAlgorithmPool)); executor.ExecutePipeline(); testing::internal::CaptureStdout(); // Start capturing stdout @@ -110,32 +113,40 @@ TEST(ExecutorsTest, TestDistancePipelineExecutor) { PositionVector positionVector2{4, 5, 6}; // Setup Mocks - std::unique_ptr mockEdgeDetectionAlgorithm = - std::make_unique(); + static cnt::pool mockEdgeDetectionAlgorithmPool; + cnt::unique_ptr mockEdgeDetectionAlgorithm = + cnt::make_unique(mockEdgeDetectionAlgorithmPool); EXPECT_CALL(*mockEdgeDetectionAlgorithm, Run(ImageMatcher(options.image))) .WillOnce(testing::Return(points)); - std::unique_ptr mockDistanceDeterminationAlgorithm = - std::make_unique(); - EXPECT_CALL(*mockDistanceDeterminationAlgorithm, Run(PointsMatcher(points))) + static cnt::pool mockDistanceDeterminationAlgorithmPool; + cnt::unique_ptr mockDistanceDeterminationAlgorithm = + cnt::make_unique(mockDistanceDeterminationAlgorithmPool); + EXPECT_CALL(*mockDistanceDeterminationAlgorithm, + Run(PointsMatcher(points))) .WillOnce(testing::Return(positionVector1)); - std::unique_ptr mockVectorGenerationAlgorithm = - std::make_unique(); - EXPECT_CALL(*mockVectorGenerationAlgorithm, Run(PositionVectorMatcher(positionVector1))) + static cnt::pool mockVectorGenerationAlgorithmPool; + cnt::unique_ptr mockVectorGenerationAlgorithm = + cnt::make_unique(mockVectorGenerationAlgorithmPool); + EXPECT_CALL(*mockVectorGenerationAlgorithm, + Run(PositionVectorMatcher(positionVector1))) .WillOnce(testing::Return(positionVector2)); - std::unique_ptr + cnt::unique_ptr edgeDetectionAlgorithm(std::move(mockEdgeDetectionAlgorithm)); - std::unique_ptr filters = std::make_unique(); - std::unique_ptr> mockFilter = std::make_unique>(); + static cnt::pool filtersPool; + cnt::unique_ptr filters = cnt::make_unique(filtersPool); + static cnt::pool> mockFilterPool; + cnt::unique_ptr> mockFilter = + cnt::make_unique>(mockFilterPool); EXPECT_CALL(*mockFilter, Run(PointsMatcher(points))) .WillOnce(testing::Invoke([](Points &){ /* no-op for test */ })); filters->Complete(std::move(mockFilter)); - std::unique_ptr + cnt::unique_ptr distanceDeterminationAlgorithm(std::move(mockDistanceDeterminationAlgorithm)); - std::unique_ptr + cnt::unique_ptr vectorGenerationAlgorithm(std::move(mockVectorGenerationAlgorithm)); DistancePipelineExecutor executor(std::move(options), std::move(edgeDetectionAlgorithm), @@ -158,12 +169,8 @@ TEST(ExecutorsTest, TestDistancePipelineExecutor) { ASSERT_THAT(output, testing::MatchesRegex(expectedOutput.str())); - DataFile expected{ - {{'F', 'O', 'U', 'N'}, 1U, 1}, - {}, - std::make_unique(1) - }; - expected.positions[0] = {145295, {4, 5, 6}}; + DataFile expected{{{'F', 'O', 'U', 'N'}, 1U, 1}, {}}; + expected.positions.push_back({145295, {4, 5, 6}}); std::ifstream file(temp_df); DataFile actual = deserializeDataFile(file); @@ -172,6 +179,80 @@ TEST(ExecutorsTest, TestDistancePipelineExecutor) { std::remove(temp_df); } +TEST(ExecutorsTest, TestDistancePipelineExecutorFullCalibrationDataThrows) { + DataFile fullCalibrationData( + {{{'F', 'O', 'U', 'N'}, 1U, FOUND_MAX_LOCATION_RECORDS}, {}}); + fullCalibrationData.positions.resize(FOUND_MAX_LOCATION_RECORDS); + + DistanceOptions options = { + strtoimage("test/common/assets/example_image.jpg"), + std::move(fullCalibrationData), + false, + 0.012, + 20E-6, + {0, 0, 0}, + {0, 0, 0}, + DECIMAL_M_E, + 25, + 1, + 0.0, + "hello", + 92, + 300, + 2.0, + 0, + 10, + 12, + false, + temp_df + }; + Points points = { + {0, 0}, + {1, 1}, + {2, 2} + }; + PositionVector positionVector1{1, 2, 3}; + PositionVector positionVector2{4, 5, 6}; + + static cnt::pool mockEdgeDetectionAlgorithmPool; + cnt::unique_ptr mockEdgeDetectionAlgorithm = + cnt::make_unique(mockEdgeDetectionAlgorithmPool); + EXPECT_CALL(*mockEdgeDetectionAlgorithm, Run(ImageMatcher(options.image))) + .WillOnce(testing::Return(points)); + + static cnt::pool mockDistanceDeterminationAlgorithmPool; + cnt::unique_ptr mockDistanceDeterminationAlgorithm = + cnt::make_unique(mockDistanceDeterminationAlgorithmPool); + EXPECT_CALL(*mockDistanceDeterminationAlgorithm, + Run(PointsMatcher(points))) + .WillOnce(testing::Return(positionVector1)); + + static cnt::pool mockVectorGenerationAlgorithmPool; + cnt::unique_ptr mockVectorGenerationAlgorithm = + cnt::make_unique(mockVectorGenerationAlgorithmPool); + EXPECT_CALL(*mockVectorGenerationAlgorithm, + Run(PositionVectorMatcher(positionVector1))) + .WillOnce(testing::Return(positionVector2)); + + cnt::unique_ptr + edgeDetectionAlgorithm(std::move(mockEdgeDetectionAlgorithm)); + cnt::unique_ptr + distanceDeterminationAlgorithm(std::move(mockDistanceDeterminationAlgorithm)); + cnt::unique_ptr + vectorGenerationAlgorithm(std::move(mockVectorGenerationAlgorithm)); + + DistancePipelineExecutor executor(std::move(options), + std::move(edgeDetectionAlgorithm), + std::move(distanceDeterminationAlgorithm), + std::move(vectorGenerationAlgorithm)); + + executor.ExecutePipeline(); + + ASSERT_THROW(executor.OutputResults(), std::runtime_error); + + std::remove(temp_df); +} + TEST(ExecutorsTest, TestOrbitPipelineExecutor) { OrbitOptions options = { {{1, {1, 1, 1}}, {2, {2, 2, 2}}, {3, {3, 3, 3}}}, @@ -188,12 +269,14 @@ TEST(ExecutorsTest, TestOrbitPipelineExecutor) { {6, {6, 6, 6}} }; - std::unique_ptr mockOrbitPropagationAlgorithm = - std::make_unique(); + static cnt::pool mockOrbitPropagationAlgorithmPool; + cnt::unique_ptr mockOrbitPropagationAlgorithm = + cnt::make_unique(mockOrbitPropagationAlgorithmPool); EXPECT_CALL(*mockOrbitPropagationAlgorithm, Run(testing::_)) .WillOnce(testing::Return(expectedResult)); - OrbitPipelineExecutor executor(std::move(options), std::move(mockOrbitPropagationAlgorithm)); + OrbitPipelineExecutor executor(std::move(options), + std::move(mockOrbitPropagationAlgorithm)); executor.ExecutePipeline(); testing::internal::CaptureStdout(); // Start capturing stdout diff --git a/test/common-test/time/time-test.cpp b/test/common-test/time/time-test.cpp index 4d88b319..96592774 100644 --- a/test/common-test/time/time-test.cpp +++ b/test/common-test/time/time-test.cpp @@ -131,18 +131,13 @@ TEST(TimeTest, TestGetUT1Time) { } TEST(TimeTest, TestGetJulianDateNow) { - DateTime time = getUT1Time(); - #ifdef FOUND_FLOAT_MODE - sleep(1.5); - #endif + DateTime timeBefore = getUT1Time(); decimal julianDate = getCurrentJulianDateTime(); - decimal expectedJulianDate = time.epochs / 86400.0 + 2440587.5; + DateTime timeAfter = getUT1Time(); + decimal minJulianDate = timeBefore.epochs / 86400.0 + 2440587.5; + decimal maxJulianDate = timeAfter.epochs / 86400.0 + 2440587.5; - #ifndef FOUND_FLOAT_MODE - ASSERT_RANGE(julianDate, expectedJulianDate, expectedJulianDate + SECONDS_TOLERANCE); - #else - ASSERT_RANGE(julianDate, expectedJulianDate - SECONDS_TOLERANCE, expectedJulianDate + SECONDS_TOLERANCE); - #endif + ASSERT_RANGE(julianDate, minJulianDate - SECONDS_TOLERANCE, maxJulianDate + SECONDS_TOLERANCE); } TEST(TimeTest, TestGetJulianDateBefore1900) { diff --git a/test/common/common.hpp b/test/common/common.hpp index e1aa8150..7fad525b 100644 --- a/test/common/common.hpp +++ b/test/common/common.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -36,9 +37,9 @@ constexpr auto Vec3Equal = [](const Vec3 &a, const Vec3 &b) { && abs(a.z() - b.z()) < DEFAULT_TOLERANCE; }; -constexpr auto LocationRecordEqual = [](const LocationRecord &a, const LocationRecord &b) { +inline bool LocationRecordEqual(const LocationRecord &a, const LocationRecord &b) { return a.timestamp == b.timestamp && Vec3Equal(a.position, b.position); -}; +} MATCHER_P(LocationRecordsEqual, expected, "") { return std::is_permutation(expected.begin(), expected.end(), diff --git a/test/datafile/serialization-test.cpp b/test/datafile/serialization-test.cpp index 1c3ed33b..df94f9d8 100644 --- a/test/datafile/serialization-test.cpp +++ b/test/datafile/serialization-test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "test/common/common.hpp" @@ -96,6 +97,55 @@ TEST_F(SerializationTest, IncorrectSizeHeader) { /** * @test Serializes and deserializes a DataFile object and verifies round-trip consistency. */ +TEST_F(SerializationTest, SerializeDataFileRejectsHeaderLargerThanPositions) { + DataFile data; + memcpy(data.header.magic, "FOUN", 4); + data.header.version = 1; + data.header.num_positions = 1; + + std::ostringstream out; + + ASSERT_THROW(serializeDataFile(data, out), std::runtime_error); +} + +TEST_F(SerializationTest, SerializeDataFileRejectsTooManyPositions) { + DataFile data; + memcpy(data.header.magic, "FOUN", 4); + data.header.version = 1; + data.header.num_positions = FOUND_MAX_LOCATION_RECORDS + 1; + data.positions.resize(data.header.num_positions); + + std::ostringstream out; + + ASSERT_THROW(serializeDataFile(data, out), std::runtime_error); +} + +TEST_F(SerializationTest, DeserializeDataFileRejectsTooManyPositions) { + std::ostringstream out; + DataFileHeader header; + memcpy(header.magic, "FOUN", 4); + header.version = 1; + header.num_positions = FOUND_MAX_LOCATION_RECORDS + 1; + header.crc = found::calculateCRC32(&header, sizeof(header) - sizeof(header.crc)); + header.version = htonl(header.version); + header.num_positions = htonl(header.num_positions); + header.crc = htonl(header.crc); + out.write(reinterpret_cast(&header), sizeof(header)); + + struct Quat { + double real = 1.0; + double i = 0.0; + double j = 0.0; + double k = 0.0; + } quaternion_field; + out.write(reinterpret_cast(&quaternion_field), sizeof(quaternion_field)); + + std::string buffer = out.str(); + std::istringstream in(buffer); + + ASSERT_THROW(found::deserializeDataFile(in), std::runtime_error); +} + TEST_F(SerializationTest, RoundTripSerialization) { DataFile expected; memcpy(expected.header.magic, "FOUN", 4); @@ -106,9 +156,8 @@ TEST_F(SerializationTest, RoundTripSerialization) { LocationRecord loc1{161803398, {100, 200, 300}}; LocationRecord loc2{271828182, {-100, -200, -300}}; - expected.positions = std::make_unique(2); - expected.positions[0] = loc1; - expected.positions[1] = loc2; + expected.positions.push_back(loc1); + expected.positions.push_back(loc2); std::ostringstream out; serializeDataFile(expected, out); diff --git a/test/distance/edge-test/connected-components-test.cpp b/test/distance/edge-test/connected-components-test.cpp index d83e8bfc..054b4abd 100644 --- a/test/distance/edge-test/connected-components-test.cpp +++ b/test/distance/edge-test/connected-components-test.cpp @@ -776,4 +776,21 @@ TEST(ConnectedComponentsTest, Test4BlobsGeneral) { ASSERT_THAT(actual, testing::UnorderedElementsAreArray(matchers)); } +TEST(ConnectedComponentsTest, TestComponentCapacityGuard) { + const int width = FOUND_MAX_COMPONENTS * 2 + 1; + unsigned char imageData[FOUND_MAX_COMPONENTS * 2 + 1] = {}; + for (int i = 0; i < width; i += 2) { + imageData[i] = 1; + } + + Image image = { + width, + 1, + 1, + imageData, + }; + + ASSERT_THROW(ConnectedComponentsAlgorithm(image, criteria), std::runtime_error); +} + } // namespace found diff --git a/test/distance/edge-test/simple-edge-detection-test.cpp b/test/distance/edge-test/simple-edge-detection-test.cpp index abf2071f..e1dfc317 100644 --- a/test/distance/edge-test/simple-edge-detection-test.cpp +++ b/test/distance/edge-test/simple-edge-detection-test.cpp @@ -524,4 +524,39 @@ TEST(SimpleEdgeDetectionTest, TestVerticalOffset) { ASSERT_THAT(actual, testing::UnorderedElementsAreArray(matchers)); } +TEST(SimpleEdgeDetectionTest, TestImagePixelCapacityGuard) { + unsigned char imageData[1] = {0}; + Image image = {static_cast(FOUND_MAX_IMAGE_PIXELS + 1), 1, 1, imageData}; + + ASSERT_THROW(minimalSEDA.Run(image), std::runtime_error); +} + +TEST(SimpleEdgeDetectionTest, TestVerticalEdgeCapacityGuard) { + const int width = FOUND_MAX_POINTS + 1; + const int height = 2; + std::vector imageData(static_cast(width * height), 0); + for (int x = 0; x < width; ++x) { + imageData[x] = 5; + } + + Image image = {width, height, 1, imageData.data()}; + SimpleEdgeDetectionAlgorithm algorithm(1, 1, 0.0); + + ASSERT_THROW(algorithm.Run(image), std::runtime_error); +} + +TEST(SimpleEdgeDetectionTest, TestHorizontalEdgeCapacityGuard) { + const int width = 2; + const int height = FOUND_MAX_POINTS + 1; + std::vector imageData(static_cast(width * height), 0); + for (int y = 0; y < height; ++y) { + imageData[static_cast(y * width)] = 5; + } + + Image image = {width, height, 1, imageData.data()}; + SimpleEdgeDetectionAlgorithm algorithm(1, 1, 0.0); + + ASSERT_THROW(algorithm.Run(image), std::runtime_error); +} + } // namespace found diff --git a/test/example/example-test.cpp b/test/example/example-test.cpp index 77fc1340..8458c896 100644 --- a/test/example/example-test.cpp +++ b/test/example/example-test.cpp @@ -40,23 +40,12 @@ class ExampleTest : public testing::Test { protected: // This is a common EdgeDetectionAlgorithm // that we'd like to use - MockEdgeDetectionAlgorithm *eda; + MockEdgeDetectionAlgorithm eda; /** * This method always runs before TEST_F(ExampleTest, ...) * test cases, and can be used to setup our mock. * Here, we initialize our field to a Mock EdgeDetectionAlgorithm */ - void SetUp() override { - eda = new MockEdgeDetectionAlgorithm(); - } - /** - * This method always runs after TEST_F(ExampleTest, ...) - * test cases, and can be used to destroy any heap objects. - * Here, we delete our mock. - */ - virtual void TearDown() { - delete eda; - } }; /** @@ -90,12 +79,12 @@ TEST_F(ExampleTest, MySecondTest) { // More information about what's happening can be found within // test/common/mocks/example-mocks.hpp, or at the following website: // https://google.github.io/googletest/gmock_for_dummies.html - EXPECT_CALL(*eda, Run(testing::_)) + EXPECT_CALL(eda, Run(testing::_)) .WillOnce(testing::Return(expectedPoints)); // Here, we run the function we want to test, // which will execute our Mocked behavior - Points p = eda->Run(image); + Points p = eda.Run(image); // Now, we test if the result of our // function matched our expectation diff --git a/test/integration/integration-test.cpp b/test/integration/integration-test.cpp index 9f26ff2f..31a31d6c 100644 --- a/test/integration/integration-test.cpp +++ b/test/integration/integration-test.cpp @@ -4,8 +4,9 @@ #include #include -#include #include +#include +#include #include "test/common/common.hpp" @@ -19,6 +20,18 @@ namespace found { +std::pair RunMainCapturingStdout(int argc, const char* argv[]) { + testing::internal::CaptureStdout(); + try { + const int result = main(argc, const_cast(argv)); + return {result, testing::internal::GetCapturedStdout()}; + } catch (...) { + (void)testing::internal::GetCapturedStdout(); + throw; + } +} + + /// The default arc second tolerance #define DEFAULT_ARC_SEC_TOL DECIMAL(1500) // Equivalent to 5/12 of a degree /// The default magnitude error tolerance @@ -50,21 +63,19 @@ TEST_F(IntegrationTest, TestMainHelp) { int argc = 2; const char *argv[2] = {"found", "-h"}; - testing::internal::CaptureStdout(); // Start capturing stdout - - ASSERT_EQ(EXIT_SUCCESS, main(argc, const_cast(argv))); - - std::string output1 = testing::internal::GetCapturedStdout(); // Stop capturing stdout + const std::pair runOutput1 = RunMainCapturingStdout(argc, argv); + const int result1 = runOutput1.first; + const std::string output1 = runOutput1.second; + ASSERT_EQ(EXIT_SUCCESS, result1); ASSERT_NE(static_cast(0), output1.size()); argv[1] = "--help"; optind = 2; - testing::internal::CaptureStdout(); // Start capturing stdout - - ASSERT_EQ(EXIT_SUCCESS, main(argc, const_cast(argv))); - - std::string output2 = testing::internal::GetCapturedStdout(); // Stop capturing stdout + const std::pair runOutput2 = RunMainCapturingStdout(argc, argv); + const int result2 = runOutput2.first; + const std::string output2 = runOutput2.second; + ASSERT_EQ(EXIT_SUCCESS, result2); ASSERT_NE(static_cast(0), output2.size()); } @@ -81,11 +92,10 @@ TEST_F(IntegrationTest, TestMainCalibrationOptionBlank) { int argc = 2; const char *argv[] = {"found", "calibration"}; - testing::internal::CaptureStdout(); // Start capturing stdout - - ASSERT_EQ(EXIT_SUCCESS, main(argc, const_cast(argv))); - - std::string output = testing::internal::GetCapturedStdout(); // Stop capturing stdout + const std::pair runOutput = RunMainCapturingStdout(argc, argv); + const int result = runOutput.first; + const std::string output = runOutput.second; + ASSERT_EQ(EXIT_SUCCESS, result); std::stringstream expectedOutput; expectedOutput << "\\[INFO\\s[0-9]{4}-[0-9]{2}-[0-9]{2}\\s[0-9]{2}:[0-9]{2}:[0-9]{2}\\s[A-Z]+\\] " @@ -104,7 +114,7 @@ TEST_F(IntegrationTest, TestMainCalibrationGeneral) { testing::internal::CaptureStdout(); // Start capturing stdout - ASSERT_EQ(EXIT_SUCCESS, main(argc, const_cast(argv))); + int result = main(argc, const_cast(argv)); Quaternion ref = SphericalToQuaternion(EulerAngles(DegToRad(1.1), DegToRad(1.2), DegToRad(1.3))).conjugate(); Quaternion loc = SphericalToQuaternion(EulerAngles(DegToRad(1.4), DegToRad(1.5), DegToRad(1.6))).conjugate(); @@ -116,6 +126,7 @@ TEST_F(IntegrationTest, TestMainCalibrationGeneral) { }; std::string output = testing::internal::GetCapturedStdout(); // Stop capturing stdout + ASSERT_EQ(EXIT_SUCCESS, result); std::ifstream file(temp_df); DataFile actual = deserializeDataFile(file); @@ -130,11 +141,10 @@ TEST_F(IntegrationTest, TestMainDistanceWithManualRelOrientationPrint) { "--reference-orientation", "1.1 1.2 1.3", "--relative-orientation", "1.4 1.5 1.6"}; - testing::internal::CaptureStdout(); // Start capturing stdout - - ASSERT_EQ(EXIT_SUCCESS, main(argc, const_cast(argv))); - - std::string output = testing::internal::GetCapturedStdout(); // Stop capturing stdout + const std::pair runOutput = RunMainCapturingStdout(argc, argv); + const int result = runOutput.first; + const std::string output = runOutput.second; + ASSERT_EQ(EXIT_SUCCESS, result); // Current output is just nothing, it outputs the {0, 0, 0} m vector std::stringstream expectedOutput; @@ -153,11 +163,10 @@ TEST_F(IntegrationTest, TestMainDistanceOptionReferenceAsOrientationPrint) { "--image", "test/common/assets/example_image.jpg", "--reference-as-orientation"}; - testing::internal::CaptureStdout(); // Start capturing stdout - - ASSERT_EQ(EXIT_SUCCESS, main(argc, const_cast(argv))); - - std::string output = testing::internal::GetCapturedStdout(); // Stop capturing stdout + const std::pair runOutput = RunMainCapturingStdout(argc, argv); + const int result = runOutput.first; + const std::string output = runOutput.second; + ASSERT_EQ(EXIT_SUCCESS, result); // Current output is just nothing, it outputs the {0, 0, 0} m vector std::stringstream expectedOutput; diff --git a/test/providers/converters-test.cpp b/test/providers/converters-test.cpp index 2bbd3d26..8e37f937 100644 --- a/test/providers/converters-test.cpp +++ b/test/providers/converters-test.cpp @@ -44,6 +44,24 @@ TEST(ConvertersTest, TestEAIncomplete) { ASSERT_DECIMAL_EQ_DEFAULT(DECIMAL(DegToRad(0)), angles.z()); } +TEST(ConvertersTest, TestEAExtraValuesIgnored) { + std::string str = "10,20,30,40"; + EulerAngles angles = strtoea(str); + + ASSERT_DECIMAL_EQ_DEFAULT(DECIMAL(DegToRad(10)), angles.x()); + ASSERT_DECIMAL_EQ_DEFAULT(DECIMAL(DegToRad(20)), angles.y()); + ASSERT_DECIMAL_EQ_DEFAULT(DECIMAL(DegToRad(30)), angles.z()); +} + +TEST(ConvertersTest, TestEASingleValuePadsZeroes) { + std::string str = "5"; + EulerAngles angles = strtoea(str); + + ASSERT_DECIMAL_EQ_DEFAULT(DECIMAL(DegToRad(5)), angles.x()); + ASSERT_DECIMAL_EQ_DEFAULT(DECIMAL(0), angles.y()); + ASSERT_DECIMAL_EQ_DEFAULT(DECIMAL(0), angles.z()); +} + TEST(ConvertersTest, TestBoolFalse) { ASSERT_FALSE(strtobool("")); ASSERT_FALSE(strtobool("0")); @@ -84,17 +102,12 @@ TEST(ConvertersTest, TestDataFileNonExistent) { } TEST(ConvertersTest, TestDataFileNormal) { - DataFile expected{ - {{'F', 'O', 'U', 'N'}, 1U, 5}, - Quaternion(-0.26, 8.5, 0, 9.2), - std::unique_ptr(new LocationRecord[5]{ - {45, {95.21, -62.15, 62.14}}, - {62, {623.25, -6182.9, -361.2}}, - {821, {623.26, 86.18, -105.21}}, - {926, {156.16, -296.29, 682.21}}, - {1062, {61.16, -168.21, -181.21}} - }) - }; + DataFile expected{{{'F', 'O', 'U', 'N'}, 1U, 5}, Quaternion(-0.26, 8.5, 0, 9.2)}; + expected.positions.push_back({45, {95.21, -62.15, 62.14}}); + expected.positions.push_back({62, {623.25, -6182.9, -361.2}}); + expected.positions.push_back({821, {623.26, 86.18, -105.21}}); + expected.positions.push_back({926, {156.16, -296.29, 682.21}}); + expected.positions.push_back({1062, {61.16, -168.21, -181.21}}); std::ofstream file(temp_df); serializeDataFile(expected, file); file.flush(); // Write out all file contents @@ -129,15 +142,10 @@ TEST(ConvertersTest, TestLocationRecordsNormal) { } TEST(ConvertersTest, TestLocationRecordsDataFile) { - DataFile expected{ - {{'F', 'O', 'U', 'N'}, 1U, 3}, - Quaternion(1, 0, 0, 0), - std::unique_ptr(new LocationRecord[3]{ - {45, {95.21, -62.15, 62.14}}, - {62, {623.25, -6182.9, -361.2}}, - {821, {623.26, 86.18, -105.21}} - }) - }; + DataFile expected{{{'F', 'O', 'U', 'N'}, 1U, 3}, Quaternion(1, 0, 0, 0)}; + expected.positions.push_back({45, {95.21, -62.15, 62.14}}); + expected.positions.push_back({62, {623.25, -6182.9, -361.2}}); + expected.positions.push_back({821, {623.26, 86.18, -105.21}}); std::ofstream file(temp_df); serializeDataFile(expected, file); file.flush(); // Write out all file contents @@ -151,4 +159,40 @@ TEST(ConvertersTest, TestLocationRecordsDataFile) { std::remove(temp_df); } +TEST(ConvertersTest, TestLocationRecordsDataFileEmpty) { + DataFile expected{{{'F', 'O', 'U', 'N'}, 1U, 0}, Quaternion(1, 0, 0, 0)}; + std::ofstream file(temp_df); + serializeDataFile(expected, file); + file.flush(); + LocationRecords actual = strtolr(temp_df); + + ASSERT_EQ(static_cast(0), actual.size()); + + std::remove(temp_df); +} + +TEST(ConvertersTest, TestLocationRecordsEmptyTextFile) { + const std::string path = "test/common/assets/temp-empty-pos-data.txt"; + std::ofstream file(path); + file.close(); + + LocationRecords actual = strtolr(path); + ASSERT_TRUE(actual.empty()); + + std::remove(path.c_str()); +} + +TEST(ConvertersTest, TestLocationRecordsTooManyLines) { + const std::string path = "test/common/assets/temp-pos-data.txt"; + std::ofstream file(path); + for (size_t i = 0; i <= FOUND_MAX_LOCATION_RECORDS; ++i) { + file << i << " 1 2 3\n"; + } + file.close(); + + ASSERT_THROW(strtolr(path), std::runtime_error); + + std::remove(path.c_str()); +} + } // namespace found diff --git a/test/providers/stage-providers-test.cpp b/test/providers/stage-providers-test.cpp new file mode 100644 index 00000000..b16966bf --- /dev/null +++ b/test/providers/stage-providers-test.cpp @@ -0,0 +1,117 @@ +#include + +#include +#include +#include + +#include "src/providers/factory.hpp" +#include "src/providers/stage-providers.hpp" + +namespace found { + +TEST(StageProvidersTest, TestProvideCalibrationAlgorithmTwice) { + CalibrationOptions options; + + cnt::unique_ptr first = ProvideCalibrationAlgorithm(std::move(options)); + ASSERT_NE(nullptr, first.get()); + first.reset(); + + CalibrationOptions secondOptions; + cnt::unique_ptr second = ProvideCalibrationAlgorithm(std::move(secondOptions)); + ASSERT_NE(nullptr, second.get()); +} + +TEST(StageProvidersTest, TestProvideEdgeDetectionAlgorithmTwice) { + DistanceOptions firstOptions; + + cnt::unique_ptr first = ProvideEdgeDetectionAlgorithm(std::move(firstOptions)); + ASSERT_NE(nullptr, first.get()); + first.reset(); + + DistanceOptions secondOptions; + cnt::unique_ptr second = ProvideEdgeDetectionAlgorithm(std::move(secondOptions)); + ASSERT_NE(nullptr, second.get()); +} + +TEST(StageProvidersTest, TestProvideDistanceDeterminationAlgorithmBranches) { + DistanceOptions sddaOptions; + sddaOptions.image.width = 2; + sddaOptions.image.height = 2; + sddaOptions.distanceAlgo = SDDA; + cnt::unique_ptr sdda = + ProvideDistanceDeterminationAlgorithm(std::move(sddaOptions)); + ASSERT_NE(nullptr, sdda.get()); + sdda.reset(); + + DistanceOptions isddaOptions; + isddaOptions.image.width = 2; + isddaOptions.image.height = 2; + isddaOptions.distanceAlgo = ISDDA; + cnt::unique_ptr isdda = + ProvideDistanceDeterminationAlgorithm(std::move(isddaOptions)); + ASSERT_NE(nullptr, isdda.get()); + + DistanceOptions unknownOptions; + unknownOptions.image.width = 2; + unknownOptions.image.height = 2; + unknownOptions.distanceAlgo = "UNKNOWN"; + ASSERT_THROW(ProvideDistanceDeterminationAlgorithm(std::move(unknownOptions)), std::runtime_error); +} + +TEST(StageProvidersTest, TestProvideVectorGenerationAlgorithmBranches) { + DistanceOptions relativeOptions; + + cnt::unique_ptr fromRelative = + ProvideVectorGenerationAlgorithm(std::move(relativeOptions)); + ASSERT_NE(nullptr, fromRelative.get()); + fromRelative.reset(); + + DistanceOptions referenceOptions; + referenceOptions.refAsOrientation = true; + cnt::unique_ptr fromReference = + ProvideVectorGenerationAlgorithm(std::move(referenceOptions)); + ASSERT_NE(nullptr, fromReference.get()); + fromReference.reset(); + + DistanceOptions dataFileOptions; + dataFileOptions.calibrationData.header = {{'F', 'O', 'U', 'N'}, 1U, 0}; + dataFileOptions.calibrationData.relative_attitude = Quaternion(1, 0, 0, 0); + cnt::unique_ptr fromDataFile = + ProvideVectorGenerationAlgorithm(std::move(dataFileOptions)); + ASSERT_NE(nullptr, fromDataFile.get()); +} + +TEST(FactoryTest, TestCreateCalibrationPipelineExecutorTwice) { + CalibrationOptions options; + + cnt::unique_ptr first = CreateCalibrationPipelineExecutor(std::move(options)); + ASSERT_NE(nullptr, first.get()); + first.reset(); + + CalibrationOptions secondOptions; + cnt::unique_ptr second = CreateCalibrationPipelineExecutor(std::move(secondOptions)); + ASSERT_NE(nullptr, second.get()); +} + +TEST(FactoryTest, TestCreateDistancePipelineExecutorTwice) { + DistanceOptions firstOptions; + firstOptions.image.width = 2; + firstOptions.image.height = 2; + firstOptions.image.channels = 1; + firstOptions.image.image = nullptr; + + cnt::unique_ptr first = CreateDistancePipelineExecutor(std::move(firstOptions)); + ASSERT_NE(nullptr, first.get()); + first.reset(); + + DistanceOptions secondOptions; + secondOptions.image.width = 2; + secondOptions.image.height = 2; + secondOptions.image.channels = 1; + secondOptions.image.image = nullptr; + + cnt::unique_ptr second = CreateDistancePipelineExecutor(std::move(secondOptions)); + ASSERT_NE(nullptr, second.get()); +} + +} // namespace found