freepdfeditor/test/test_spike_runner_contract.cpp

103 lines
3.2 KiB
C++

// 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 <cstdio>
#include <cstring>
#include <string>
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<char>(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;
}