Move xce_test's 15 production sources + headers from src/controller/testsuite/ into a new src/harness/ component with its own meson.build, and strip every now-dangling controller-side reference (11 controller_unit_test_src entries, 4 standalone test-binary blocks, 11 controller_test.c extern/RUN_TEST lines) so the controller build is never left broken. Fixes a broken relative-symlink bridge (xce_test_controller_link) added to keep two controller-embedded tests (test_test_cli.c, test_xcetest_process_entry.c) finding the relocated xce_test binary until task 2.1 moves them too. Discovered and documented in design.md: four controller-domain regression tests call functions directly from four of xce_test's own files, requiring controller_unit_test to permanently recompile them from src/harness/ — a real dependency neither the original discovery nor design audit caught. controller_unit_test: 1228 Tests 0 Failures 0 Ignored (verified via the Unity binary's own printed summary line, not just meson's aggregate count). Task 1.1 of xce-harness-component. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
113 lines
3 KiB
C
113 lines
3 KiB
C
#define _GNU_SOURCE
|
|
#include "snapdiff.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#define SNAPDIFF_BUF_SIZE 65536
|
|
|
|
int snapdiff_paths(const char* path1, const char* path2, FILE* out,
|
|
snapdiff_report_t* rep)
|
|
{
|
|
if (!path1 || !path2)
|
|
return -EINVAL;
|
|
|
|
snapdiff_report_t local;
|
|
memset(&local, 0, sizeof(local));
|
|
local.diff_offset = -1;
|
|
|
|
int fd1 = open(path1, O_RDONLY);
|
|
if (fd1 < 0) {
|
|
int saved = errno;
|
|
if (out)
|
|
fprintf(out, "snapdiff: %s: %s\n", path1, strerror(saved));
|
|
return -saved;
|
|
}
|
|
int fd2 = open(path2, O_RDONLY);
|
|
if (fd2 < 0) {
|
|
int saved = errno;
|
|
close(fd1);
|
|
if (out)
|
|
fprintf(out, "snapdiff: %s: %s\n", path2, strerror(saved));
|
|
return -saved;
|
|
}
|
|
|
|
struct stat st1, st2;
|
|
if (fstat(fd1, &st1) != 0 || fstat(fd2, &st2) != 0) {
|
|
int saved = errno;
|
|
close(fd1);
|
|
close(fd2);
|
|
if (out)
|
|
fprintf(out, "snapdiff: fstat failed: %s\n", strerror(saved));
|
|
return -saved;
|
|
}
|
|
local.size1 = st1.st_size;
|
|
local.size2 = st2.st_size;
|
|
|
|
if (st1.st_size != st2.st_size) {
|
|
local.size_mismatch = 1;
|
|
close(fd1);
|
|
close(fd2);
|
|
if (rep)
|
|
*rep = local;
|
|
if (out)
|
|
fprintf(out,
|
|
"snapdiff: %s (%jd bytes) vs %s (%jd bytes): SIZE MISMATCH\n",
|
|
path1, (intmax_t)local.size1, path2, (intmax_t)local.size2);
|
|
return 1;
|
|
}
|
|
|
|
uint8_t buf1[SNAPDIFF_BUF_SIZE];
|
|
uint8_t buf2[SNAPDIFF_BUF_SIZE];
|
|
off_t off = 0;
|
|
int found_diff = 0;
|
|
|
|
for (;;) {
|
|
ssize_t n1 = read(fd1, buf1, sizeof(buf1));
|
|
ssize_t n2 = read(fd2, buf2, sizeof(buf2));
|
|
if (n1 <= 0 || n2 <= 0)
|
|
break;
|
|
ssize_t n = (n1 < n2) ? n1 : n2;
|
|
for (ssize_t i = 0; i < n; i++) {
|
|
if (buf1[i] != buf2[i]) {
|
|
local.diff_offset = off + (off_t)i;
|
|
found_diff = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (found_diff)
|
|
break;
|
|
off += (off_t)n;
|
|
}
|
|
close(fd1);
|
|
close(fd2);
|
|
|
|
if (!found_diff) {
|
|
local.identical = 1;
|
|
if (rep)
|
|
*rep = local;
|
|
if (out)
|
|
fprintf(out, "snapdiff: %s vs %s: IDENTICAL (%jd bytes)\n", path1,
|
|
path2, (intmax_t)local.size1);
|
|
return 0;
|
|
}
|
|
|
|
local.chunk_index = local.diff_offset / SNAPDIFF_CHUNK_SIZE;
|
|
local.at_chunk_boundary = (local.diff_offset % SNAPDIFF_CHUNK_SIZE == 0);
|
|
|
|
if (rep)
|
|
*rep = local;
|
|
if (out)
|
|
fprintf(out,
|
|
"snapdiff: %s vs %s: DIFFER at offset %jd (chunk %jd%s)\n",
|
|
path1, path2, (intmax_t)local.diff_offset,
|
|
(intmax_t)local.chunk_index,
|
|
local.at_chunk_boundary
|
|
? " -- exactly at a chunk boundary, first byte of chunk"
|
|
: " -- NOT at a chunk boundary, mid-chunk");
|
|
return 1;
|
|
}
|