diff --git a/.github/workflows/c-coverage.yml b/.github/workflows/c-coverage.yml index 19bfe5d..ccc9a64 100644 --- a/.github/workflows/c-coverage.yml +++ b/.github/workflows/c-coverage.yml @@ -12,7 +12,7 @@ on: # yamllint disable-line rule:truthy jobs: coverage: name: C/C++ CMake CI Coverage - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - name: Checkout repository diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 960ec55..1bf4fc8 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -12,7 +12,7 @@ on: # yamllint disable-line rule:truthy jobs: lint: name: clang-format style check - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - name: Checkout repository @@ -21,12 +21,15 @@ jobs: - name: Install dependencies # yamllint disable rule:line-length run: | - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy main" + sudo mkdir -p /etc/apt/keyrings + curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key \ + | sudo gpg --dearmor -o /etc/apt/keyrings/llvm-snapshot.gpg + echo "deb [signed-by=/etc/apt/keyrings/llvm-snapshot.gpg] http://apt.llvm.org/resolute/ llvm-toolchain-resolute-22 main" \ + | sudo tee /etc/apt/sources.list.d/llvm.list && \ sudo apt-get -y update - sudo apt-get -y install --no-install-recommends --no-install-suggests clang-format-19 - sudo update-alternatives --install /usr/bin/clang-format clang-format $(which clang-format-19) 100 - sudo update-alternatives --set clang-format $(update-alternatives --list clang-format | grep clang-format-19) + sudo apt-get -y install --no-install-recommends --no-install-suggests clang-format-22 + sudo update-alternatives --install /usr/bin/clang-format clang-format $(which clang-format-22) 100 + sudo update-alternatives --set clang-format $(update-alternatives --list clang-format | grep clang-format-22) # yamllint enable rule:line-length - name: Check Tools diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml index 2085706..7179d05 100644 --- a/.github/workflows/cppcheck.yml +++ b/.github/workflows/cppcheck.yml @@ -13,7 +13,7 @@ on: # yamllint disable-line rule:truthy jobs: lint: name: CppCheck Lint - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - name: Checkout repository diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index add32e8..f56d36c 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -18,7 +18,7 @@ jobs: build: name: "Build Docker images" - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 @@ -73,7 +73,7 @@ jobs: lint: name: "Run in docker: LINT" - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 needs: build steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 @@ -98,7 +98,7 @@ jobs: test: name: "Run in docker: TEST" - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 needs: build steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 @@ -124,7 +124,7 @@ jobs: # yamllint disable rule:line-length # security: # name: "Snyk Container" - # runs-on: ubuntu-24.04 + # runs-on: ubuntu-26.04 # needs: build # permissions: # actions: read @@ -173,7 +173,7 @@ jobs: scan: name: "Trivy" - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 needs: build permissions: actions: read diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml index fe87491..48a3da5 100644 --- a/.github/workflows/gitleaks.yml +++ b/.github/workflows/gitleaks.yml @@ -19,7 +19,7 @@ on: # yamllint disable-line rule:truthy jobs: scan: name: gitleaks - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: diff --git a/.github/workflows/make-coverage-html.yml b/.github/workflows/make-coverage-html.yml index 319da88..12bf923 100644 --- a/.github/workflows/make-coverage-html.yml +++ b/.github/workflows/make-coverage-html.yml @@ -15,7 +15,7 @@ on: # yamllint disable-line rule:truthy jobs: make-coverage-html: name: "Run make coverage/html and verify coverage HTML" - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - name: Checkout repository uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 diff --git a/.github/workflows/markdown-lint.yml b/.github/workflows/markdown-lint.yml index a80dc93..4cc5157 100644 --- a/.github/workflows/markdown-lint.yml +++ b/.github/workflows/markdown-lint.yml @@ -15,7 +15,7 @@ permissions: jobs: markdownlint: name: Markdown Lint - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 strategy: matrix: diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 203e947..852a2f4 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -11,7 +11,7 @@ on: # yamllint disable-line rule:truthy jobs: build: name: SonarCloud build and run sonar-scanner - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 env: # Directory where build-wrapper output will be placed BUILD_WRAPPER_OUT_DIR: build diff --git a/.github/workflows/yamllint.yml b/.github/workflows/yamllint.yml index bad6388..ff60197 100644 --- a/.github/workflows/yamllint.yml +++ b/.github/workflows/yamllint.yml @@ -13,7 +13,7 @@ on: # yamllint disable-line rule:truthy jobs: lint: name: YAML lint - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - name: Checkout repository uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 diff --git a/Dockerfile b/Dockerfile index 9398284..d01fdf1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE_VERSION=ubuntu:noble-20260509.1 +ARG BASE_IMAGE_VERSION=ubuntu:26.04 FROM ${BASE_IMAGE_VERSION} AS init ENV WORKDIR=/app @@ -34,7 +34,7 @@ RUN apt-get -y update && \ ca-certificates curl git ninja-build unzip zip && \ rm -rf /var/lib/apt/lists/* && \ mkdir /opt/vcpkg && \ - git clone --branch ${VCPKG_VERSION} https://github.com/microsoft/vcpkg "${VCPKG_ROOT}" && \ + git clone --branch "${VCPKG_VERSION}" https://github.com/microsoft/vcpkg "${VCPKG_ROOT}" && \ /opt/vcpkg/bootstrap-vcpkg.sh && \ ln -s /opt/vcpkg/vcpkg /usr/local/bin/vcpkg && \ rm -rf /var/lib/apt/lists/* && \ @@ -69,12 +69,15 @@ FROM builder AS lint RUN apt-get update && \ apt-get -y install --no-install-recommends --no-install-suggests gnupg software-properties-common && \ rm -rf /var/lib/apt/lists/* -ADD https://apt.llvm.org/llvm-snapshot.gpg.key llvm-snapshot.gpg.key -RUN apt-key add llvm-snapshot.gpg.key && \ - apt-add-repository -y "deb https://apt.llvm.org/plucky/ llvm-toolchain-plucky-20 main" && \ + +RUN mkdir -p /etc/apt/keyrings && \ + curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key \ + | gpg --dearmor -o /etc/apt/keyrings/llvm-snapshot.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/llvm-snapshot.gpg] http://apt.llvm.org/resolute/ llvm-toolchain-resolute-22 main" \ + | tee /etc/apt/sources.list.d/llvm.list && \ apt-get -y update && \ - apt-get -y install --no-install-recommends --no-install-suggests clang-format-19 && \ - update-alternatives --install /usr/bin/clang-format clang-format $(which clang-format-19) 100 && \ + apt-get -y install --no-install-recommends --no-install-suggests clang-format-22 && \ + update-alternatives --install /usr/bin/clang-format clang-format $(which clang-format-22) 100 && \ rm -rf /var/lib/apt/lists/* ADD https://deb.nodesource.com/setup_22.x nodesource_setup.sh @@ -83,10 +86,16 @@ RUN bash nodesource_setup.sh && \ npm install -g --ignore-scripts markdownlint-cli@0.47.0 && \ apt-get -y install --no-install-recommends --no-install-suggests python3-minimal python3-pip && \ rm /usr/lib/python3.*/EXTERNALLY-MANAGED && \ - pip install --no-cache-dir yamllint && \ + apt-get -y install --no-install-recommends --no-install-suggests yamllint && \ apt-get -y install --no-install-recommends --no-install-suggests cppcheck && \ rm -rf /var/lib/apt/lists/* +# Tooling test +RUN clang-format --version && \ + markdownlint --version && \ + yamllint --version && \ + cppcheck --version + # Code source COPY ./src ${WORKDIR}/src COPY ./vcpkg.json ${WORKDIR}/vcpkg.json diff --git a/src/lib/exercises/src/hackerrank/warmup/compare_triplets.c b/src/lib/exercises/src/hackerrank/warmup/compare_triplets.c index 3cbfb53..0abb5c0 100644 --- a/src/lib/exercises/src/hackerrank/warmup/compare_triplets.c +++ b/src/lib/exercises/src/hackerrank/warmup/compare_triplets.c @@ -12,6 +12,9 @@ int *HACKERRANK_WARMUP_compareTriplets(int a_count, const int *a, int b_count, *result_count = 2; int *awards = malloc(sizeof(int) * *result_count); + if (awards == NULL) { + return NULL; + } awards[0] = 0; awards[1] = 0; diff --git a/src/lib/exercises/src/hackerrank/warmup/mini_max_sum.c b/src/lib/exercises/src/hackerrank/warmup/mini_max_sum.c index 5e6613c..9eed7a7 100644 --- a/src/lib/exercises/src/hackerrank/warmup/mini_max_sum.c +++ b/src/lib/exercises/src/hackerrank/warmup/mini_max_sum.c @@ -27,6 +27,9 @@ char *HACKERRANK_WARMUP_miniMaxSumCalculate(int arr_count, const int *arr) { const int BUFFER_MAX_SIZE = 100; char *buffer = malloc(BUFFER_MAX_SIZE * sizeof(char)); + if (buffer == NULL) { + return NULL; + } snprintf(buffer, BUFFER_MAX_SIZE, "%lld %lld", tsum - tmax, tsum - tmin); return buffer; @@ -34,6 +37,10 @@ char *HACKERRANK_WARMUP_miniMaxSumCalculate(int arr_count, const int *arr) { void HACKERRANK_WARMUP_miniMaxSum(int arr_count, const int *arr) { char *result = HACKERRANK_WARMUP_miniMaxSumCalculate(arr_count, arr); + if (result == NULL) { + return; + } + printf("%s\n", result); free(result); } diff --git a/src/lib/exercises/src/hackerrank/warmup/plus_minus.c b/src/lib/exercises/src/hackerrank/warmup/plus_minus.c index c2f322d..f98d906 100644 --- a/src/lib/exercises/src/hackerrank/warmup/plus_minus.c +++ b/src/lib/exercises/src/hackerrank/warmup/plus_minus.c @@ -11,6 +11,9 @@ const int BUFFER_MAX_SIZE = 10; char *format_result(double number) { char *buffer = malloc(BUFFER_MAX_SIZE * sizeof(char)); + if (buffer == NULL) { + return NULL; + } snprintf(buffer, BUFFER_MAX_SIZE, "%0.6lf", number); return buffer; @@ -45,9 +48,16 @@ char **HACKERRANK_WARMUP_plusMinusCalculate(int arr_count, const int *arr) { int n = 3; // Número de strings (puede ser arbitrario) char **answer = malloc(n * sizeof(char *)); // Array de punteros + if (answer == NULL) { + return NULL; + } for (i = 0; i < n; i++) { char *formatted = format_result(results[i]); + if (formatted == NULL) { + HACKERRANK_WARMUP_freePlusMinus(answer, i); + return NULL; + } answer[i] = formatted; } @@ -63,6 +73,9 @@ void HACKERRANK_WARMUP_freePlusMinus(char **plusMinusAnswer, int n) { void HACKERRANK_WARMUP_plusMinus(int arr_count, const int *arr) { char **output = HACKERRANK_WARMUP_plusMinusCalculate(arr_count, arr); + if (output == NULL) { + return; + } for (int i = 0; i < HACKERRANK_WARMUP_PLUSMINUS_LIMIT_ANSWERS; i++) { printf("%s", output[i]); diff --git a/src/lib/exercises/src/hackerrank/warmup/staircase.c b/src/lib/exercises/src/hackerrank/warmup/staircase.c index 4473eec..12a305b 100644 --- a/src/lib/exercises/src/hackerrank/warmup/staircase.c +++ b/src/lib/exercises/src/hackerrank/warmup/staircase.c @@ -10,9 +10,16 @@ char **HACKERRANK_WARMUP_staircaseCalculate(int n) { char **answer = malloc(n * sizeof(char *)); // Array of char pointers + if (answer == NULL) { + return NULL; + } for (int i = 0; i < n; i++) { char *line = malloc((n + 1) * sizeof(char)); // Array of char values + if (line == NULL) { + HACKERRANK_WARMUP_freeStaircase(answer, i); + return NULL; + } for (int j = 0; j < n; j++) { if (j < n - i - 1) { @@ -38,6 +45,9 @@ void HACKERRANK_WARMUP_freeStaircase(char **staircase, int n) { void HACKERRANK_WARMUP_staircase(int n) { char **output = HACKERRANK_WARMUP_staircaseCalculate(n); + if (output == NULL) { + return; + } for (int i = 0; i < n; i++) { printf("%s\n", output[i]); diff --git a/src/lib/exercises/src/hackerrank/warmup/time_conversion.c b/src/lib/exercises/src/hackerrank/warmup/time_conversion.c index 638b71c..7b4de53 100644 --- a/src/lib/exercises/src/hackerrank/warmup/time_conversion.c +++ b/src/lib/exercises/src/hackerrank/warmup/time_conversion.c @@ -54,6 +54,10 @@ char *HACKERRANK_WARMUP_timeConversion(const char *s) { char *conversion = malloc((HACKERRANK_WARMUP_SHORT_TIME_FORMAT_SIZE + 1) * sizeof(char)); + if (conversion == NULL) { + return NULL; + } + for (int i = 0; i < HACKERRANK_WARMUP_SHORT_TIME_FORMAT_SIZE; i++) { conversion[i] = s[i]; } diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 09bc14b..d04fe75 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -1,6 +1,10 @@ find_package(Catch2 3 REQUIRED) find_path(CATCH_INCLUDE_DIR NAMES catch.hpp PATH_SUFFIXES catch2) +# # Create a static library for the memory injector utility +file(GLOB_RECURSE TOOLS_SOURCES "tools/*.cpp") +add_library(memory_injector STATIC ${TOOLS_SOURCES}) + file(GLOB_RECURSE SOURCES "unit/lib/*.cpp") add_executable(unit-tests_combined ${SOURCES}) @@ -12,6 +16,14 @@ target_link_libraries(unit-tests_combined PRIVATE Catch2::Catch2WithMain) # Functionality to test. target_link_libraries(unit-tests_combined PRIVATE exercises) +target_link_libraries(unit-tests_combined PRIVATE memory_injector) + +# GNU ld (Linux) needs --wrap=malloc to redirect calls to __wrap_malloc. +# macOS uses dyld interposition instead, which requires no linker flag. +if(NOT APPLE) + target_link_options(memory_injector INTERFACE "-Wl,--wrap=malloc") +endif() + list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) include(CTest) include(Catch) diff --git a/src/tests/tools/memory_injector.cpp b/src/tests/tools/memory_injector.cpp new file mode 100644 index 0000000..9e08c2a --- /dev/null +++ b/src/tests/tools/memory_injector.cpp @@ -0,0 +1,91 @@ +#include "memory_injector.hpp" +#include + +// Hidden state variables local to this translation unit +static bool force_immediate_error = false; +static int allocation_counter = 0; +static int target_fault_allocation = -1; + +namespace MemoryInjector { +// cppcheck-suppress unusedFunction +void enable_oom_fault() { + force_immediate_error = true; + target_fault_allocation = -1; +} + +// cppcheck-suppress unusedFunction +void disable_fault() { + force_immediate_error = false; + allocation_counter = 0; + target_fault_allocation = -1; +} + +// cppcheck-suppress unusedFunction +void fail_on_allocation_number(int n) { + force_immediate_error = false; + allocation_counter = 0; + target_fault_allocation = n; +} +} // namespace MemoryInjector + +// ── Platform-specific interception ────────────────────────────────────────── + +#if defined(__APPLE__) +// macOS: use dyld interposition. No extra linker flags are required. +// The DYLD_INTERPOSE macro creates an entry in the __DATA,__interpose section +// that dyld uses to redirect calls to `malloc` at load time. + +#include + +static void *my_malloc(size_t size) { + // Case 1: Forced immediate out-of-memory error + if (force_immediate_error) { + return nullptr; + } + + // Case 2: Error on the Nth allocation attempt + if (target_fault_allocation != -1) { + allocation_counter++; + if (allocation_counter == target_fault_allocation) { + return nullptr; + } + } + + // Fall through to the real malloc (resolved via the interpose table) + return malloc(size); +} + +#define DYLD_INTERPOSE(_replacement, _replacee) \ + __attribute__((used)) static struct { \ + const void *replacement; \ + const void *replacee; \ + } _interpose_##_replacee __attribute__((section("__DATA,__interpose"))) = { \ + (const void *)(_replacement), (const void *)(_replacee)} + +DYLD_INTERPOSE(my_malloc, malloc); + +#else +// Linux / GNU ld: use the --wrap=malloc linker flag. +// The linker renames every call to `malloc` → `__wrap_malloc` and makes the +// original symbol available as `__real_malloc`. + +extern "C" void *__real_malloc(size_t); + +extern "C" void *__wrap_malloc(size_t size) { + // Case 1: Forced immediate out-of-memory error + if (force_immediate_error) { + return nullptr; + } + + // Case 2: Error on the Nth allocation attempt + if (target_fault_allocation != -1) { + allocation_counter++; // NOLINT(cppcoreguidelines-avoid-magic-numbers) + if (allocation_counter == target_fault_allocation) { + return nullptr; + } + } + + // If it shouldn't fail, call the real system malloc + return __real_malloc(size); +} +#endif diff --git a/src/tests/tools/memory_injector.hpp b/src/tests/tools/memory_injector.hpp new file mode 100644 index 0000000..ec411b5 --- /dev/null +++ b/src/tests/tools/memory_injector.hpp @@ -0,0 +1,13 @@ +#pragma once +#include + +namespace MemoryInjector { +// Triggers an immediate malloc failure +void enable_oom_fault(); + +// Disables the simulator (malloc goes back to normal behavior) +void disable_fault(); + +// Advanced: Makes malloc succeed N times and fail on the next allocation +void fail_on_allocation_number(int n); +} // namespace MemoryInjector diff --git a/src/tests/unit/lib/hackerrank/warmup/a_very_big_sum.test.cpp b/src/tests/unit/lib/hackerrank/warmup/a_very_big_sum.test.cpp index 63a9464..fd35059 100644 --- a/src/tests/unit/lib/hackerrank/warmup/a_very_big_sum.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/a_very_big_sum.test.cpp @@ -1,12 +1,15 @@ +// 3rd party libs #include +#include +// local libs #include -#include -#include +// std libs #include #include -#include +#include + using json = nlohmann::json; TEST_CASE("aVeryBigSum JSON Test Cases", diff --git a/src/tests/unit/lib/hackerrank/warmup/birthday_cake_candles.test.cpp b/src/tests/unit/lib/hackerrank/warmup/birthday_cake_candles.test.cpp index e03311d..18b88b0 100644 --- a/src/tests/unit/lib/hackerrank/warmup/birthday_cake_candles.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/birthday_cake_candles.test.cpp @@ -1,12 +1,15 @@ +// 3rd party libs #include +#include +// local libs #include -#include -#include +// std libs #include #include -#include +#include + using json = nlohmann::json; TEST_CASE("birthdayCakeCandles JSON Test Cases", diff --git a/src/tests/unit/lib/hackerrank/warmup/compare_triplets.test.cpp b/src/tests/unit/lib/hackerrank/warmup/compare_triplets.test.cpp index 4174ad5..38cc6b5 100644 --- a/src/tests/unit/lib/hackerrank/warmup/compare_triplets.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/compare_triplets.test.cpp @@ -1,44 +1,74 @@ +// 3rd party libs #include +#include +// local libs +#ifdef __linux__ +#include "../../../../tools/memory_injector.hpp" +#endif #include -#include -#include +// std libs #include #include -#include +#include + using json = nlohmann::json; TEST_CASE("compareTriplets JSON Test Cases", "[hackerrank] [jsontestcase] [warmup]") { - std::filesystem::path cwd = std::filesystem::current_path(); - std::string path = - cwd.string() + - "/unit/lib/hackerrank/warmup/compare_triplets.testcases.json"; - - INFO("compareTriplets JSON test cases FILE: " << path); +#ifdef __linux__ + SECTION("compareTriplets: Failure handling during Out of Memory (OOM)") { + MemoryInjector::enable_oom_fault(); - std::ifstream f(path); - json data = json::parse(f); - - for (auto testcase : data) { - auto a_size = static_cast(testcase["a"].size()); - std::vector a_input_vector = testcase["a"]; + std::vector a_input_vector = {1, 2, 3, 4}; + auto a_size = static_cast(a_input_vector.size()); const int *a_input_array = a_input_vector.data(); - auto b_size = static_cast(testcase["b"].size()); - std::vector b_input_vector = testcase["b"]; + std::vector b_input_vector = {4, 3, 2, 1}; + auto b_size = static_cast(b_input_vector.size()); const int *b_input_array = b_input_vector.data(); int result_count; - int *result = HACKERRANK_WARMUP_compareTriplets( - a_size, a_input_array, b_size, b_input_array, &result_count); + REQUIRE(HACKERRANK_WARMUP_compareTriplets(a_size, a_input_array, b_size, + b_input_array, + &result_count) == nullptr); + + // Clean up the state after finishing the test section + MemoryInjector::disable_fault(); + } +#endif + + SECTION("compareTriplets: Normal memory allocation succeeds") { + std::filesystem::path cwd = std::filesystem::current_path(); + std::string path = + cwd.string() + + "/unit/lib/hackerrank/warmup/compare_triplets.testcases.json"; + + INFO("compareTriplets JSON test cases FILE: " << path); + + std::ifstream f(path); + json data = json::parse(f); + + for (auto testcase : data) { + auto a_size = static_cast(testcase["a"].size()); + std::vector a_input_vector = testcase["a"]; + const int *a_input_array = a_input_vector.data(); + + auto b_size = static_cast(testcase["b"].size()); + std::vector b_input_vector = testcase["b"]; + const int *b_input_array = b_input_vector.data(); + int result_count; + + int *result = HACKERRANK_WARMUP_compareTriplets( + a_size, a_input_array, b_size, b_input_array, &result_count); - // Crear un vector a partir del array en C - std::vector result_as_vector(result, result + result_count); + // Crear un vector a partir del array en C + std::vector result_as_vector(result, result + result_count); - CHECK(result_as_vector == testcase["expected"]); + CHECK(result_as_vector == testcase["expected"]); - free(result); + free(result); + } } } diff --git a/src/tests/unit/lib/hackerrank/warmup/diagonal_difference.test.cpp b/src/tests/unit/lib/hackerrank/warmup/diagonal_difference.test.cpp index 93e4c58..74f40e9 100644 --- a/src/tests/unit/lib/hackerrank/warmup/diagonal_difference.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/diagonal_difference.test.cpp @@ -1,12 +1,14 @@ +// 3rd party libs #include +#include +// local libs #include -#include -#include +// std libs #include #include -#include + using json = nlohmann::json; int **jsonToMatrix(const nlohmann::json &matrixJson, int &rows, int &cols) { diff --git a/src/tests/unit/lib/hackerrank/warmup/mini_max_sum.test.cpp b/src/tests/unit/lib/hackerrank/warmup/mini_max_sum.test.cpp index 6d5add7..9934d58 100644 --- a/src/tests/unit/lib/hackerrank/warmup/mini_max_sum.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/mini_max_sum.test.cpp @@ -1,51 +1,70 @@ +// 3rd party libs #include +#include +// local libs +#ifdef __linux__ +#include "../../../../tools/memory_injector.hpp" +#endif #include -#include -#include -#include -#include +// std libs #include #include -#include - -#include // malloc +#include +#include using json = nlohmann::json; TEST_CASE("miniMaxSum JSON Test Cases", "[hackerrank] [jsontestcase] [warmup]") { - std::filesystem::path cwd = std::filesystem::current_path(); - std::string path = - cwd.string() + "/unit/lib/hackerrank/warmup/mini_max_sum.testcases.json"; +#ifdef __linux__ + SECTION("miniMaxSum: Failure handling during Out of Memory (OOM)") { + MemoryInjector::enable_oom_fault(); + + int input_size = 3; + int input_array[] = {1, -2, 0}; - INFO("miniMaxSum JSON test cases FILE: " << path); + HACKERRANK_WARMUP_miniMaxSum(input_size, input_array); + + // Clean up the state after finishing the test section + MemoryInjector::disable_fault(); + } +#endif - std::ifstream f(path); - json data = json::parse(f); + SECTION("miniMaxSum: Normal memory allocation succeeds") { + std::filesystem::path cwd = std::filesystem::current_path(); + std::string path = + cwd.string() + + "/unit/lib/hackerrank/warmup/mini_max_sum.testcases.json"; - for (auto testcase : data) { - auto input_size = static_cast(testcase["input"].size()); - std::vector input_vector = testcase["input"]; - const int *input_array = input_vector.data(); + INFO("miniMaxSum JSON test cases FILE: " << path); - char *result = - HACKERRANK_WARMUP_miniMaxSumCalculate(input_size, input_array); - HACKERRANK_WARMUP_miniMaxSum(input_size, input_array); + std::ifstream f(path); + json data = json::parse(f); - std::string result_as_string(result); + for (auto testcase : data) { + auto input_size = static_cast(testcase["input"].size()); + std::vector input_vector = testcase["input"]; + const int *input_array = input_vector.data(); - free(result); + char *result = + HACKERRANK_WARMUP_miniMaxSumCalculate(input_size, input_array); + HACKERRANK_WARMUP_miniMaxSum(input_size, input_array); - CHECK(result_as_string == testcase["expected"]); + std::string result_as_string(result); + + free(result); + + CHECK(result_as_string == testcase["expected"]); + } } -} -TEST_CASE("miniMaxSum Edge Cases", "[hackerrank] [warmup]") { - std::vector empty({}); + SECTION("miniMaxSum Edge Cases", "[hackerrank] [warmup]") { + std::vector empty({}); - const char *result = HACKERRANK_WARMUP_miniMaxSumCalculate(0, empty.data()); + const char *result = HACKERRANK_WARMUP_miniMaxSumCalculate(0, empty.data()); - CHECK(result == nullptr); + CHECK(result == nullptr); + } } diff --git a/src/tests/unit/lib/hackerrank/warmup/plus_minus.test.cpp b/src/tests/unit/lib/hackerrank/warmup/plus_minus.test.cpp index 3aaa3dd..4620f43 100644 --- a/src/tests/unit/lib/hackerrank/warmup/plus_minus.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/plus_minus.test.cpp @@ -1,44 +1,82 @@ +// 3rd party libs #include +#include +// local libs +#ifdef __linux__ +#include "../../../../tools/memory_injector.hpp" +#endif #include -#include -#include -#include +// std libs #include #include -#include +#include + using json = nlohmann::json; TEST_CASE("plusMinus JSON Test Cases", "[hackerrank] [jsontestcase] [warmup]") { - std::filesystem::path cwd = std::filesystem::current_path(); - std::string path = - cwd.string() + "/unit/lib/hackerrank/warmup/plus_minus.testcases.json"; +#ifdef __linux__ + SECTION("Failure handling during Out of Memory (OOM)") { + // MemoryInjector::enable_oom_fault(); - INFO("plusMinus JSON test cases FILE: " << path); + int input_size = 3; + int input_array[] = {1, -2, 0}; - std::ifstream f(path); - json data = json::parse(f); + MemoryInjector::fail_on_allocation_number(1); + REQUIRE(HACKERRANK_WARMUP_plusMinusCalculate(input_size, input_array) == + nullptr); - for (auto testcase : data) { - auto input_size = static_cast(testcase["input"].size()); - std::vector input_vector = testcase["input"]; - const int *input_array = input_vector.data(); + MemoryInjector::fail_on_allocation_number(2); + REQUIRE(HACKERRANK_WARMUP_plusMinusCalculate(input_size, input_array) == + nullptr); - char **result = - HACKERRANK_WARMUP_plusMinusCalculate(input_size, input_array); + MemoryInjector::fail_on_allocation_number(3); + REQUIRE(HACKERRANK_WARMUP_plusMinusCalculate(input_size, input_array) == + nullptr); - std::vector result_as_vector; + MemoryInjector::disable_fault(); + MemoryInjector::enable_oom_fault(); + REQUIRE_NOTHROW(HACKERRANK_WARMUP_plusMinus(input_size, input_array)); - for (int i = 0; i < HACKERRANK_WARMUP_PLUSMINUS_LIMIT_ANSWERS; i++) { - result_as_vector.emplace_back(result[i]); - } - HACKERRANK_WARMUP_freePlusMinus(result, - HACKERRANK_WARMUP_PLUSMINUS_LIMIT_ANSWERS); + // Clean up the state after finishing the test section + MemoryInjector::disable_fault(); + } +#endif + + SECTION("Normal memory allocation succeeds") { + // Reset the injector state before every section running + // MemoryInjector::disable_fault(); - CHECK(result_as_vector == testcase["expected"]); + std::filesystem::path cwd = std::filesystem::current_path(); + std::string path = + cwd.string() + "/unit/lib/hackerrank/warmup/plus_minus.testcases.json"; - // Just call void function, to collect coverage - HACKERRANK_WARMUP_plusMinus(input_size, input_array); + INFO("plusMinus JSON test cases FILE: " << path); + + std::ifstream f(path); + json data = json::parse(f); + + for (auto testcase : data) { + auto input_size = static_cast(testcase["input"].size()); + std::vector input_vector = testcase["input"]; + const int *input_array = input_vector.data(); + + char **result = + HACKERRANK_WARMUP_plusMinusCalculate(input_size, input_array); + + std::vector result_as_vector; + + for (int i = 0; i < HACKERRANK_WARMUP_PLUSMINUS_LIMIT_ANSWERS; i++) { + result_as_vector.emplace_back(result[i]); + } + HACKERRANK_WARMUP_freePlusMinus( + result, HACKERRANK_WARMUP_PLUSMINUS_LIMIT_ANSWERS); + + CHECK(result_as_vector == testcase["expected"]); + + // Just call void function, to collect coverage + HACKERRANK_WARMUP_plusMinus(input_size, input_array); + } } } diff --git a/src/tests/unit/lib/hackerrank/warmup/simple_array_sum.test.cpp b/src/tests/unit/lib/hackerrank/warmup/simple_array_sum.test.cpp index 3d4d493..432c21f 100644 --- a/src/tests/unit/lib/hackerrank/warmup/simple_array_sum.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/simple_array_sum.test.cpp @@ -1,9 +1,12 @@ +// 3rd party libs #include +#include +// local libs #include + #include #include -#include #include using json = nlohmann::json; diff --git a/src/tests/unit/lib/hackerrank/warmup/solve_me_first.test.cpp b/src/tests/unit/lib/hackerrank/warmup/solve_me_first.test.cpp index b9ca3a7..e61dd3c 100644 --- a/src/tests/unit/lib/hackerrank/warmup/solve_me_first.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/solve_me_first.test.cpp @@ -1,10 +1,13 @@ +// 3rd party libs #include +#include +// local libs #include + +// std libs #include #include -#include -#include using json = nlohmann::json; diff --git a/src/tests/unit/lib/hackerrank/warmup/staircase.test.cpp b/src/tests/unit/lib/hackerrank/warmup/staircase.test.cpp index a08f557..d0ec0b2 100644 --- a/src/tests/unit/lib/hackerrank/warmup/staircase.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/staircase.test.cpp @@ -1,39 +1,66 @@ +// 3rd party libs #include +#include +// local libs +#if defined(__linux__) +#include "../../../../tools/memory_injector.hpp" +#endif #include + +// std libs #include #include -#include #include using json = nlohmann::json; -TEST_CASE("staircase", "[warmup]") { - std::filesystem::path cwd = std::filesystem::current_path(); - std::string path = - cwd.string() + "/unit/lib/hackerrank/warmup/staircase.testcases.json"; +TEST_CASE("staircase JSON Test Cases", "[hackerrank] [jsontestcase] [warmup]") { +#if defined(__linux__) + SECTION("staircase: Failure handling during Out of Memory (OOM)") { + int input = 4; - INFO("staircase JSON test cases FILE: " << path); + MemoryInjector::enable_oom_fault(); + REQUIRE(HACKERRANK_WARMUP_staircaseCalculate(input) == nullptr); + MemoryInjector::disable_fault(); - std::ifstream f(path); - json data = json::parse(f); + MemoryInjector::fail_on_allocation_number(2); + REQUIRE(HACKERRANK_WARMUP_staircaseCalculate(input) == nullptr); + MemoryInjector::disable_fault(); - for (auto testcase : data) { - auto input = static_cast(testcase["input"]); + MemoryInjector::enable_oom_fault(); + REQUIRE_NOTHROW(HACKERRANK_WARMUP_staircase(input)); + MemoryInjector::disable_fault(); + } +#endif - char **result = HACKERRANK_WARMUP_staircaseCalculate(input); + SECTION("plusMinus: Normal memory allocation succeeds") { + std::filesystem::path cwd = std::filesystem::current_path(); + std::string path = + cwd.string() + "/unit/lib/hackerrank/warmup/staircase.testcases.json"; - std::vector result_as_vector; + INFO("staircase JSON test cases FILE: " << path); - for (int i = 0; i < input; i++) { - result_as_vector.emplace_back(result[i]); - } + std::ifstream f(path); + json data = json::parse(f); - HACKERRANK_WARMUP_freeStaircase(result, input); + for (auto testcase : data) { + auto input = static_cast(testcase["input"]); - CHECK(result_as_vector == testcase["expected"]); + char **result = HACKERRANK_WARMUP_staircaseCalculate(input); - // Just call void function, to collect coverage - HACKERRANK_WARMUP_staircase(input); + std::vector result_as_vector; + + for (int i = 0; i < input; i++) { + result_as_vector.emplace_back(result[i]); + } + + HACKERRANK_WARMUP_freeStaircase(result, input); + + CHECK(result_as_vector == testcase["expected"]); + + // Just call void function, to collect coverage + HACKERRANK_WARMUP_staircase(input); + } } } diff --git a/src/tests/unit/lib/hackerrank/warmup/time_conversion.test.cpp b/src/tests/unit/lib/hackerrank/warmup/time_conversion.test.cpp index ac61f23..a685320 100644 --- a/src/tests/unit/lib/hackerrank/warmup/time_conversion.test.cpp +++ b/src/tests/unit/lib/hackerrank/warmup/time_conversion.test.cpp @@ -1,41 +1,62 @@ +// 3rd party libs #include +#include +// local libs +#ifdef __linux__ +#include "../../../../tools/memory_injector.hpp" +#endif #include + +// std libs #include #include -#include -#include using json = nlohmann::json; TEST_CASE("time_conversion JSON Test Cases", "[hackerrank] [jsontestcase] [warmup]") { - std::filesystem::path cwd = std::filesystem::current_path(); - std::string path = - cwd.string() + - "/unit/lib/hackerrank/warmup/time_conversion.testcases.json"; +#ifdef __linux__ + SECTION("time_conversion Failure handling during Out of Memory (OOM)") { + MemoryInjector::enable_oom_fault(); + + const char *input = "12:01:00PM"; + + REQUIRE(HACKERRANK_WARMUP_timeConversion(input) == nullptr); - INFO("time_conversion JSON test cases FILE: " << path); + // Clean up the state after finishing the test section + MemoryInjector::disable_fault(); + } +#endif + + SECTION("Normal memory allocation succeeds") { + std::filesystem::path cwd = std::filesystem::current_path(); + std::string path = + cwd.string() + + "/unit/lib/hackerrank/warmup/time_conversion.testcases.json"; + + INFO("time_conversion JSON test cases FILE: " << path); - std::ifstream f(path); - json data = json::parse(f); + std::ifstream f(path); + json data = json::parse(f); - for (auto testcase : data) { - char *result = HACKERRANK_WARMUP_timeConversion( - testcase["input"].get().c_str()); + for (auto testcase : data) { + char *result = HACKERRANK_WARMUP_timeConversion( + testcase["input"].get().c_str()); - std::string result_as_string(result); + std::string result_as_string(result); - free(result); + free(result); - CHECK(result_as_string == testcase["expected"]); + CHECK(result_as_string == testcase["expected"]); + } } -} -TEST_CASE("time_conversion edge cases", "[hackerrank] [helper] [warmup]") { - CHECK(HACKERRANK_WARMUP_timeConversion(nullptr) == nullptr); + SECTION("time_conversion edge cases", "[hackerrank] [helper] [warmup]") { + CHECK(HACKERRANK_WARMUP_timeConversion(nullptr) == nullptr); - CHECK(HACKERRANK_WARMUP_timeConversion("") == nullptr); + CHECK(HACKERRANK_WARMUP_timeConversion("") == nullptr); - CHECK(HACKERRANK_WARMUP_timeConversion("aa:bb:ccXM") == nullptr); + CHECK(HACKERRANK_WARMUP_timeConversion("aa:bb:ccXM") == nullptr); + } }