// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2025 ai-ad4 and the FreePDFEditor contributors // // Smoke test for the spike-runner JSON contract. The CI pipeline parses the // single-line JSON that emit_json_report produces; if that format drifts the // whole gate matrix goes dark. This test pins the format without depending on // QPDF, so it runs on every platform even before the spike builds. #include "../spike/common/SpikeRunner.h" #include #include #include namespace { // Capture stdout during emit_json_report by writing to a captured buffer — // emit_json_report writes to real stdout, so we redirect via freopen. This is // crude but avoids pulling a dependency into the scaffold test. std::string capture(freepdfeditor::spike::SpikeResult r) { std::fflush(stdout); FILE* saved = stdout; stdout = std::tmpfile(); if (!stdout) { stdout = saved; return {}; } int rc = freepdfeditor::spike::emit_json_report(r); std::fflush(stdout); std::string buf; std::rewind(stdout); int c; while ((c = std::fgetc(stdout)) != EOF) buf.push_back(static_cast(c)); std::fclose(stdout); stdout = saved; (void)rc; return buf; } } // namespace int main() { using namespace freepdfeditor::spike; SpikeResult r{}; r.spike = "A"; r.name = "test"; r.total = 10; r.passed = 9; r.failed = 1; r.errored = 0; r.metric_name = "byte_identical_rate"; r.metric_value = 0.9; r.target = 0.99; r.gate_met = false; r.failed_samples = {"bad.pdf"}; r.notes = "hello \"world\"\nline2"; std::string out = capture(r); int failures = 0; auto assert_contains = [&](const std::string& needle) { if (out.find(needle) == std::string::npos) { std::fprintf(stderr, "FAIL: output missing %s\n", needle.c_str()); std::fprintf(stderr, "output was: %s\n", out.c_str()); ++failures; } }; assert_contains("\"spike\":\"A\""); assert_contains("\"name\":\"test\""); assert_contains("\"total\":10"); assert_contains("\"passed\":9"); assert_contains("\"failed\":1"); assert_contains("\"metric\":\"byte_identical_rate\""); assert_contains("\"value\":0.900000"); assert_contains("\"target\":0.990000"); assert_contains("\"gate_met\":false"); assert_contains("\"samples\":[\"bad.pdf\"]"); assert_contains("\"notes\":\"hello \\\"world\\\"\\nline2\""); // Single line, terminated by newline. if (out.empty() || out.back() != '\n') { std::fprintf(stderr, "FAIL: output not newline-terminated\n"); ++failures; } if (out.find('\n') != out.size() - 1) { std::fprintf(stderr, "FAIL: output contains embedded newline\n"); ++failures; } // Gate-met returns 0; not-met returns 1. Test the contract directly. r.gate_met = true; if (freepdfeditor::spike::emit_json_report(r) != 0) { std::fprintf(stderr, "FAIL: gate_met=true should return 0\n"); ++failures; } r.gate_met = false; if (freepdfeditor::spike::emit_json_report(r) != 1) { std::fprintf(stderr, "FAIL: gate_met=false should return 1\n"); ++failures; } return failures == 0 ? 0 : 1; }