Remediate Ε-W3-T1 package-compat-fixtures review findings
Freeze *.rsbank as binary via .gitattributes; add a truncated additive_forward fixture proving the exact-size proof beats TooNew; enumerate the fixture dir to catch orphaned files; make fixture-size checks fatal instead of just logged; pin fixture version asserts as literals, not build-relative.
This commit is contained in:
@@ -67,13 +67,16 @@ static void writeBytes(const std::string& path, const std::vector<std::uint8_t>&
|
||||
static_cast<std::streamsize>(bytes.size()));
|
||||
}
|
||||
|
||||
// Copies a committed fixture to the path the verb will be pointed at. Fails loudly on an
|
||||
// empty read: an unreadable corpus would otherwise let every "must refuse" suite pass.
|
||||
static bool stageFixture(const Scratch& scratch, const char* fixture) {
|
||||
// Copies a committed fixture to the path the verb will be pointed at. Fails loudly on a
|
||||
// size mismatch (empty included): an unreadable OR mangled corpus would otherwise let
|
||||
// every "must refuse" suite pass, since a garbled fixture still refuses, just not for
|
||||
// the reason under test.
|
||||
static bool stageFixture(const Scratch& scratch, const char* fixture,
|
||||
std::size_t expectedSize) {
|
||||
const std::vector<std::uint8_t> bytes = packageFixtureBytes(fixture);
|
||||
if (bytes.empty()) {
|
||||
std::printf("FAIL: fixture %s read as 0 bytes (path: %s)\n", fixture,
|
||||
packageFixturePath(fixture).c_str());
|
||||
if (bytes.size() != expectedSize) {
|
||||
std::printf("FAIL: fixture %s is %zu bytes, expected %zu (path: %s)\n", fixture,
|
||||
bytes.size(), expectedSize, packageFixturePath(fixture).c_str());
|
||||
++g_fail;
|
||||
return false;
|
||||
}
|
||||
@@ -116,7 +119,7 @@ static ImportLanding runImport(const Scratch& scratch, ReaSamplerSession& sessio
|
||||
// importing and re-exporting it closes export -> import -> export over frozen bytes.
|
||||
static void testV1FixtureReExportsByteIdenticalPayloads() {
|
||||
Scratch scratch("roundtrip");
|
||||
if (!stageFixture(scratch, "v1_shipping.rsbank")) return;
|
||||
if (!stageFixture(scratch, "v1_shipping.rsbank", 1207)) return;
|
||||
|
||||
ReaSamplerSession session;
|
||||
int births = 0;
|
||||
@@ -128,7 +131,7 @@ static void testV1FixtureReExportsByteIdenticalPayloads() {
|
||||
|
||||
const std::vector<std::vector<std::uint8_t>> sourcePayloads =
|
||||
payloadsOf(packageFixtureBytes("v1_shipping.rsbank"));
|
||||
CHECK(sourcePayloads.size() == 1);
|
||||
if (sourcePayloads.size() != 1) { CHECK(false); return; }
|
||||
|
||||
// The landed file is the package's payload verbatim — the first half of the claim.
|
||||
const std::string landed = scratch.bankDir() + "/" + landing.plan.entries[0].destFileName;
|
||||
@@ -153,7 +156,7 @@ static void testV1FixtureReExportsByteIdenticalPayloads() {
|
||||
|
||||
static void testRefuseFixtureRefusesTheWholeImportAndNamesTheWriter() {
|
||||
Scratch scratch("refuse");
|
||||
if (!stageFixture(scratch, "refuse_structural.rsbank")) return;
|
||||
if (!stageFixture(scratch, "refuse_structural.rsbank", 1207)) return;
|
||||
|
||||
ReaSamplerSession session;
|
||||
const BankBook before = session.book();
|
||||
@@ -162,8 +165,10 @@ static void testRefuseFixtureRefusesTheWholeImportAndNamesTheWriter() {
|
||||
session.book(), kTag, journal);
|
||||
|
||||
CHECK(landing.outcome == ImportOutcome::TooNew);
|
||||
CHECK(landing.header.formatVersion == package::kPackageFormatVersion + 1);
|
||||
CHECK(landing.header.minReaderVersion == package::kPackageFormatVersion + 1);
|
||||
// Literal, not kPackageFormatVersion-relative: this is the FIXTURE's frozen pair
|
||||
// (2/2), not this build's (see test_package_compat.cpp's file header note).
|
||||
CHECK(landing.header.formatVersion == 2);
|
||||
CHECK(landing.header.minReaderVersion == 2);
|
||||
CHECK(landing.header.writerVersion == "1.9.0");
|
||||
// Nothing planned, nothing on disk, nothing in the index.
|
||||
CHECK(landing.plan.entries.empty());
|
||||
@@ -173,18 +178,27 @@ static void testRefuseFixtureRefusesTheWholeImportAndNamesTheWriter() {
|
||||
|
||||
// The same eight cuts test_package_compat classifies, driven through the verb's
|
||||
// incremental prefix reader — the one caller that can ask requiredPrefixSize for more
|
||||
// bytes than the file holds.
|
||||
static const char* kTruncationFixtures[] = {
|
||||
"trunc_magic.rsbank", "trunc_version_pair.rsbank",
|
||||
"trunc_writer_semver.rsbank", "trunc_manifest_length.rsbank",
|
||||
"trunc_manifest_body.rsbank", "trunc_payload_start.rsbank",
|
||||
"trunc_payload_middle.rsbank", "trunc_one_short.rsbank",
|
||||
// bytes than the file holds. Sizes match test_package_compat.cpp's kTruncations.
|
||||
struct TruncationFixture {
|
||||
const char* file;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
static const TruncationFixture kTruncationFixtures[] = {
|
||||
{"trunc_magic.rsbank", 2},
|
||||
{"trunc_version_pair.rsbank", 10},
|
||||
{"trunc_writer_semver.rsbank", 18},
|
||||
{"trunc_manifest_length.rsbank", 23},
|
||||
{"trunc_manifest_body.rsbank", 466},
|
||||
{"trunc_payload_start.rsbank", 907},
|
||||
{"trunc_payload_middle.rsbank", 1057},
|
||||
{"trunc_one_short.rsbank", 1206},
|
||||
};
|
||||
|
||||
static void testEveryTruncationRefusesTheImportAsMalformed() {
|
||||
for (const char* fixture : kTruncationFixtures) {
|
||||
Scratch scratch(std::string("trunc_") + fixture);
|
||||
if (!stageFixture(scratch, fixture)) continue;
|
||||
for (const TruncationFixture& fixture : kTruncationFixtures) {
|
||||
Scratch scratch(std::string("trunc_") + fixture.file);
|
||||
if (!stageFixture(scratch, fixture.file, fixture.size)) continue;
|
||||
|
||||
ReaSamplerSession session;
|
||||
const BankBook before = session.book();
|
||||
@@ -192,7 +206,7 @@ static void testEveryTruncationRefusesTheImportAsMalformed() {
|
||||
const ImportLanding landing = landPackage(scratch.importPath(), scratch.projectDir(),
|
||||
session.book(), kTag, journal);
|
||||
if (landing.outcome != ImportOutcome::Malformed) {
|
||||
std::printf("FAIL: %s imported as outcome %d, expected Malformed\n", fixture,
|
||||
std::printf("FAIL: %s imported as outcome %d, expected Malformed\n", fixture.file,
|
||||
static_cast<int>(landing.outcome));
|
||||
++g_fail;
|
||||
}
|
||||
@@ -202,19 +216,30 @@ static void testEveryTruncationRefusesTheImportAsMalformed() {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kHostileFixtures[] = {
|
||||
"hostile_name_dotdot.rsbank", "hostile_name_parent_slash.rsbank",
|
||||
"hostile_name_parent_backslash.rsbank", "hostile_name_subdir_slash.rsbank",
|
||||
"hostile_name_drive_absolute.rsbank", "hostile_name_unc_absolute.rsbank",
|
||||
"hostile_path_dotdot_slash.rsbank", "hostile_path_dotdot_backslash.rsbank",
|
||||
"hostile_path_rooted.rsbank", "hostile_path_drive_absolute.rsbank",
|
||||
"hostile_path_unc_absolute.rsbank",
|
||||
// Sizes match test_package_compat.cpp's kHostiles.
|
||||
struct HostileFixtureFile {
|
||||
const char* file;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
static const HostileFixtureFile kHostileFixtures[] = {
|
||||
{"hostile_name_dotdot.rsbank", 624},
|
||||
{"hostile_name_parent_slash.rsbank", 633},
|
||||
{"hostile_name_parent_backslash.rsbank", 634},
|
||||
{"hostile_name_subdir_slash.rsbank", 634},
|
||||
{"hostile_name_drive_absolute.rsbank", 643},
|
||||
{"hostile_name_unc_absolute.rsbank", 646},
|
||||
{"hostile_path_dotdot_slash.rsbank", 641},
|
||||
{"hostile_path_dotdot_backslash.rsbank", 640},
|
||||
{"hostile_path_rooted.rsbank", 635},
|
||||
{"hostile_path_drive_absolute.rsbank", 643},
|
||||
{"hostile_path_unc_absolute.rsbank", 646},
|
||||
};
|
||||
|
||||
static void testEveryHostileNameIsRefusedBeforeThePlannerRuns() {
|
||||
for (const char* fixture : kHostileFixtures) {
|
||||
Scratch scratch(std::string("hostile_") + fixture);
|
||||
if (!stageFixture(scratch, fixture)) continue;
|
||||
for (const HostileFixtureFile& fixture : kHostileFixtures) {
|
||||
Scratch scratch(std::string("hostile_") + fixture.file);
|
||||
if (!stageFixture(scratch, fixture.file, fixture.size)) continue;
|
||||
|
||||
ReaSamplerSession session;
|
||||
const BankBook before = session.book();
|
||||
@@ -222,7 +247,7 @@ static void testEveryHostileNameIsRefusedBeforeThePlannerRuns() {
|
||||
const ImportLanding landing = landPackage(scratch.importPath(), scratch.projectDir(),
|
||||
session.book(), kTag, journal);
|
||||
if (landing.outcome != ImportOutcome::Malformed) {
|
||||
std::printf("FAIL: %s imported as outcome %d, expected Malformed\n", fixture,
|
||||
std::printf("FAIL: %s imported as outcome %d, expected Malformed\n", fixture.file,
|
||||
static_cast<int>(landing.outcome));
|
||||
++g_fail;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user