/** * @file harness.hpp * @brief What every test binary shares: counting, reporting, fixture paths * * Each test binary covers one area of the library and is registered as * its own ctest case, so a failure names the area it happened in. This * header carries what all of them need and nothing specific to any one. * * @copyright Copyright (c) 2026 SILO GROUP LLC * @author Chris Punches * * Part of the Dark Horse Linux Package Manager (DPM) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #pragma once #include #include #include #ifndef TEST_FIXTURE_MODULES #error "TEST_FIXTURE_MODULES must be defined" #endif #ifndef TEST_FIXTURE_CONF #error "TEST_FIXTURE_CONF must be defined" #endif #ifndef TEST_FIXTURE_METADATA #error "TEST_FIXTURE_METADATA must be defined" #endif #ifndef TEST_SCRATCH_ROOT #error "TEST_SCRATCH_ROOT must be defined" #endif /** Assertions run and assertions failed, reported by harness_report(). */ static int g_checks = 0; static int g_failures = 0; /** * @brief Records one assertion and reports it when it fails * * Every failure names the file and line, so a ctest case that fails * points at the assertion rather than at the binary. */ #define CHECK(cond) \ do { \ g_checks++; \ if (!(cond)) { \ g_failures++; \ std::fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, \ #cond); \ } \ } while (0) /** * @brief Reports whether the context's last error carries a substring * * @param ctx The context to read * @param needle The text the reason is expected to contain * @return true when a reason is recorded and contains needle */ static inline bool error_contains(dpm_ctx* ctx, const char* needle) { const char* err = dpm_get_last_error(ctx); return err != nullptr && std::strstr(err, needle) != nullptr; } /** * @brief Prints the tally and yields the process exit status * * @return 0 when every assertion passed, 1 otherwise */ static inline int harness_report(const char* suite) { std::printf("%s: %d checks, %d failures\n", suite, g_checks, g_failures); return g_failures == 0 ? 0 : 1; }