]> FriiDump Source - friidump.git/blob - tests/test_native_report.c
FriiDump 0.5.3.16 candidate: add native reports and Linux Xbox support
[friidump.git] / tests / test_native_report.c
1 #include "native_report.h"
2 #include "nintendo_disc_header.h"
3 #include "xbox_region.h"
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #ifdef WIN32
10 #include <direct.h>
11 #define PATH_SEP "\\"
12 #define TEST_GETCWD _getcwd
13 #define TEST_CHDIR _chdir
14 #else
15 #include <unistd.h>
16 #define PATH_SEP "/"
17 #define TEST_GETCWD getcwd
18 #define TEST_CHDIR chdir
19 #endif
20
21 static int nintendo_disc_header_tests(void) {
22     unsigned char gamecube_header[0x20] = {0};
23     unsigned char wii_header[0x20] = {0};
24     unsigned char unknown_header[0x20] = {0};
25
26     memcpy(gamecube_header, "GALE01", 6);
27     gamecube_header[0x1c] = 0xc2;
28     gamecube_header[0x1d] = 0x33;
29     gamecube_header[0x1e] = 0x9f;
30     gamecube_header[0x1f] = 0x3d;
31
32     memcpy(wii_header, "RSBE01", 6);
33     wii_header[0x18] = 0x5d;
34     wii_header[0x19] = 0x1c;
35     wii_header[0x1a] = 0x9e;
36     wii_header[0x1b] = 0xa3;
37
38     if (nintendo_disc_header_detect(
39             gamecube_header,
40             sizeof(gamecube_header)) != NINTENDO_DISC_HEADER_GAMECUBE) {
41         fprintf(stderr, "GALE01 GameCube header classification failed\n");
42         return 0;
43     }
44
45     if (nintendo_disc_header_detect(
46             wii_header,
47             sizeof(wii_header)) != NINTENDO_DISC_HEADER_WII) {
48         fprintf(stderr, "RSBE01 Wii header classification failed\n");
49         return 0;
50     }
51
52     if (nintendo_disc_header_detect(
53             unknown_header,
54             sizeof(unknown_header)) != NINTENDO_DISC_HEADER_UNKNOWN) {
55         fprintf(stderr, "Unknown Nintendo header classification failed\n");
56         return 0;
57     }
58
59     if (nintendo_disc_header_detect(
60             gamecube_header,
61             0x1f) != NINTENDO_DISC_HEADER_UNKNOWN) {
62         fprintf(stderr, "Truncated Nintendo header classification failed\n");
63         return 0;
64     }
65
66     printf("Nintendo disc-header classification: PASS\n");
67     return 1;
68 }
69
70 static int xbox_region_tests(void) {
71     char region[128];
72
73     if (!xbox_region_format(XB_REGION_US_CANADA, region, sizeof(region)) ||
74         strcmp(region, "North America") != 0) {
75         fprintf(stderr, "Xbox North America region formatting failed: %s\n", region);
76         return 0;
77     }
78
79     if (!xbox_region_format(XB_REGION_JAPAN, region, sizeof(region)) ||
80         strcmp(region, "Japan") != 0) {
81         fprintf(stderr, "Xbox Japan region formatting failed: %s\n", region);
82         return 0;
83     }
84
85     if (!xbox_region_format(XB_REGION_EUROPE_AU_NZ, region, sizeof(region)) ||
86         strcmp(region, "Europe/Australia/New Zealand") != 0) {
87         fprintf(stderr, "Xbox Europe/Australia/New Zealand formatting failed: %s\n", region);
88         return 0;
89     }
90
91     if (!xbox_region_format(
92             XB_REGION_US_CANADA | XB_REGION_JAPAN,
93             region, sizeof(region)) ||
94         strcmp(region, "North America; Japan") != 0) {
95         fprintf(stderr, "Xbox multi-region formatting failed: %s\n", region);
96         return 0;
97     }
98
99     if (!xbox_region_format(0U, region, sizeof(region)) ||
100         strcmp(region, "None (Locked)") != 0) {
101         fprintf(stderr, "Xbox locked-region formatting failed: %s\n", region);
102         return 0;
103     }
104
105     if (!xbox_region_format(0x7fffffffU, region, sizeof(region)) ||
106         strcmp(region, "Region Free") != 0) {
107         fprintf(stderr, "Xbox region-free formatting failed: %s\n", region);
108         return 0;
109     }
110
111     printf("Xbox XBE GameRegion formatting: PASS\n");
112     return 1;
113 }
114
115 static int write_report(friidump_native_report *report,
116                         const char *directory,
117                         const char *filename) {
118     char path[FRIIDUMP_REPORT_PATH_MAX];
119     char written[FRIIDUMP_REPORT_PATH_MAX];
120     char error_text[256];
121
122     snprintf(path, sizeof(path), "%s%s%s", directory, PATH_SEP, filename);
123     if (!friidump_native_report_enable_path(report, path)) {
124         fprintf(stderr, "Could not configure fixture path: %s\n", path);
125         return 0;
126     }
127     if (!friidump_native_report_write(report, written, sizeof(written),
128                                       error_text, sizeof(error_text))) {
129         fprintf(stderr, "Could not write fixture %s: %s\n", path, error_text);
130         return 0;
131     }
132     printf("fixture: %s\n", written);
133     return 1;
134 }
135
136 static void base_drive(friidump_native_report *r,
137                        const char *model,
138                        const char *firmware) {
139     friidump_native_report_set_drive(
140         r, "HL-DT-ST", model, firmware, "ATAPI", "fixture-device");
141     friidump_native_report_set_profile(
142         r, "known_supported_profile_hardening", 0x5e0, 0x90024ff7);
143 }
144
145 static int success_gamecube(const char *dir) {
146     friidump_native_report r;
147     friidump_native_report_init(&r, "0.5.3.16-native-report-test");
148     base_drive(&r, "DVD-ROM GDR8163B", "0M26");
149     friidump_native_report_set_media(
150         &r, "gamecube", "Sonic Mega Collection", "USA", "GUPP8P");
151     friidump_native_report_set_seed(&r, true, "pass", true, 6.0);
152     friidump_native_report_set_dump(
153         &r, true, "pass", true, 712880, true, 1459978240ULL,
154         true, 7241.31, NULL, false, 0, "Sonic Mega Collection.iso");
155     friidump_native_report_set_hashes(
156         &r,
157         "01b52739",
158         "85a525df1481d0ad67d8761f832dca12",
159         "06eb6d15b4d7f90ec0fed9cea9a77db41358d74ed",
160         "2c0da8b5cb1f615d68562ae0f731f28bc82ac59136c70eb57aecbb4fe4b63882");
161     friidump_native_report_add_artifact(
162         &r, "dump_output", "Sonic Mega Collection.iso", true,
163         1459978240ULL,
164         "2c0da8b5cb1f615d68562ae0f731f28bc82ac59136c70eb57aecbb4fe4b63882");
165     friidump_native_report_set_reference(
166         &r, "redump", "match", "Sonic Mega Collection (USA).iso");
167     friidump_native_report_set_outcome(&r, "full_dump", "pass");
168     friidump_native_report_add_note(&r, "Measurement scope: full_optical_payload.");
169     return write_report(&r, dir, "success-gamecube.friidump.json");
170 }
171
172 static int success_xbox(const char *dir) {
173     friidump_native_report r;
174     friidump_native_report_init(&r, "0.5.3.16-native-report-test");
175     base_drive(&r, "DVD-ROM GDR8050L", "0012");
176     friidump_native_report_set_firmware_modified(
177         &r, true, "Modified firmware with HIT 0xE7 memdump support.");
178     friidump_native_report_set_media(
179         &r, "xbox", "Red Faction II", "North America", "TQ00501A");
180     friidump_native_report_set_seed(&r, false, "not_applicable", false, 0.0);
181     friidump_native_report_set_dump(
182         &r, true, "pass", true, 3820880, true, 7825162240ULL,
183         true, 14000.0, NULL, false, 0, "Red Faction II [TQ00501A].iso");
184     friidump_native_report_set_hashes(
185         &r,
186         "12345678",
187         "0123456789abcdef0123456789abcdef",
188         "0123456789abcdef0123456789abcdef01234567",
189         "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
190     friidump_native_report_add_artifact(
191         &r, "dump_output", "Red Faction II [TQ00501A].iso", true,
192         7825162240ULL,
193         "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
194     friidump_native_report_set_reference(&r, "redump", "mismatch", NULL);
195     friidump_native_report_set_outcome(&r, "full_dump", "pass");
196     friidump_native_report_add_note(&r, "Measurement scope: assembled_output.");
197     friidump_native_report_add_note(
198         &r,
199         "Xbox output throughput describes the produced assembled output and is not directly comparable to a full optical-payload read.");
200     return write_report(&r, dir, "success-xbox-assembled.friidump.json");
201 }
202
203 static int partial_gamecube(const char *dir) {
204     friidump_native_report r;
205     friidump_native_report_init(&r, "0.5.3.16-native-report-test");
206     base_drive(&r, "DVD-ROM GDR8163B", "0M26");
207     friidump_native_report_set_media(
208         &r, "gamecube", "Sonic Mega Collection", "USA", "GUPP8P");
209     friidump_native_report_set_seed(&r, true, "pass", true, 6.0);
210     friidump_native_report_set_dump(
211         &r, true, "partial", true, 100000, true, 204800000ULL,
212         true, 1000.0, "raw_sector_read_or_output_write", true, 100000,
213         "Sonic Mega Collection.partial.iso");
214     friidump_native_report_set_hashes(&r, NULL, NULL, NULL, NULL);
215     friidump_native_report_add_artifact(
216         &r, "dump_output", "Sonic Mega Collection.partial.iso", true,
217         204800000ULL, NULL);
218     friidump_native_report_set_reference(&r, NULL, "not_checked", NULL);
219     friidump_native_report_set_outcome(&r, "partial_dump", "partial");
220     friidump_native_report_add_note(&r, "Measurement scope: partial_progress.");
221     return write_report(&r, dir, "partial-gamecube.friidump.json");
222 }
223
224 static int partial_cancelled(const char *dir) {
225     friidump_native_report r;
226     friidump_native_report_init(&r, "0.5.3.16-native-report-test");
227     base_drive(&r, "DVD-ROM GDR8163B", "0M26");
228     friidump_native_report_set_media(
229         &r, "gamecube", "Sonic Mega Collection", "USA", "GUPP8P");
230     friidump_native_report_set_seed(&r, true, "pass", true, 6.0);
231     friidump_native_report_set_dump(
232         &r, true, "partial", true, 25000, true, 51200000ULL,
233         true, 250.0, "user_cancelled", true, 25000,
234         "Sonic Mega Collection.cancelled.iso");
235     friidump_native_report_set_hashes(&r, NULL, NULL, NULL, NULL);
236     friidump_native_report_add_artifact(
237         &r, "dump_output", "Sonic Mega Collection.cancelled.iso", true,
238         51200000ULL, NULL);
239     friidump_native_report_set_reference(&r, NULL, "not_checked", NULL);
240     friidump_native_report_set_outcome(&r, "partial_dump", "partial");
241     friidump_native_report_add_note(&r, "Measurement scope: partial_progress.");
242     friidump_native_report_add_note(
243         &r,
244         "The invocation was cancelled by the user; only completed output bytes are represented.");
245     return write_report(&r, dir, "partial-cancelled.friidump.json");
246 }
247
248 static int partial_xbox_cancelled(const char *dir) {
249     friidump_native_report r;
250     friidump_native_report_init(&r, "0.5.3.16-native-report-test");
251     base_drive(&r, "DVD-ROM GDR8050L", "0012");
252     friidump_native_report_set_firmware_modified(
253         &r, true,
254         "GDR-8163B cross-flashed with modified GDR-8050L firmware; patched to allow 0xE7 memory dumps");
255     friidump_native_report_set_media(
256         &r, "xbox", "Red Faction II", "North America", "TQ00501A");
257     friidump_native_report_set_seed(
258         &r, false, "not_applicable", false, 0.0);
259     friidump_native_report_set_dump(
260         &r, true, "partial", true, 22273, true, 45615104ULL,
261         true, 14.0, "user_cancelled", true, 22273,
262         "Red Faction II [TQ00501A].partial.xiso");
263     friidump_native_report_set_hashes(
264         &r,
265         "89abcdef",
266         "0123456789abcdef0123456789abcdef",
267         "0123456789abcdef0123456789abcdef01234567",
268         "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
269     friidump_native_report_add_artifact(
270         &r, "dump_output",
271         "Red Faction II [TQ00501A].partial.xiso", true,
272         45615104ULL,
273         "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
274     friidump_native_report_set_reference(
275         &r, NULL, "not_checked", NULL);
276     friidump_native_report_set_outcome(
277         &r, "partial_dump", "partial");
278     friidump_native_report_add_note(
279         &r, "Measurement scope: partial_progress.");
280     friidump_native_report_add_note(
281         &r,
282         "The invocation was cancelled by the user; only completed output bytes are represented.");
283     friidump_native_report_add_note(
284         &r, "Xbox XBE GameRegion mask: 0x00000001.");
285     return write_report(
286         &r, dir, "partial-xbox-cancelled.friidump.json");
287 }
288
289 static int seed_failure(const char *dir) {
290     friidump_native_report r;
291     friidump_native_report_init(&r, "0.5.3.16-native-report-test");
292     base_drive(&r, "DVD-ROM GDR8163B", "0M26");
293     friidump_native_report_set_media(&r, "gamecube", NULL, NULL, NULL);
294     friidump_native_report_set_seed(&r, true, "fail", true, 15.0);
295     friidump_native_report_set_dump(
296         &r, false, "not_attempted", false, 0, false, 0,
297         false, 0.0, NULL, false, 0, NULL);
298     friidump_native_report_set_reference(&r, NULL, "not_checked", NULL);
299     friidump_native_report_set_outcome(&r, "seed_only", "fail");
300     friidump_native_report_add_note(&r, "Disc initialization or seed retrieval failed.");
301     return write_report(&r, dir, "seed-failure.friidump.json");
302 }
303
304 static int no_media(const char *dir) {
305     friidump_native_report r;
306     friidump_native_report_init(&r, "0.5.3.16-native-report-test");
307     base_drive(&r, "DVD-ROM GDR8163B", "0M26");
308     friidump_native_report_set_media(&r, "unknown", NULL, NULL, NULL);
309     friidump_native_report_set_seed(&r, false, "not_attempted", false, 0.0);
310     friidump_native_report_set_dump(
311         &r, false, "not_attempted", false, 0, false, 0,
312         false, 0.0, NULL, false, 0, NULL);
313     friidump_native_report_set_reference(&r, NULL, "not_applicable", NULL);
314     friidump_native_report_set_outcome(
315         &r, "diagnostic_no_media", "not_applicable");
316     friidump_native_report_add_note(
317         &r, "Media preflight found no readable disc (sense 02/3A/00).");
318     return write_report(&r, dir, "no-media.friidump.json");
319 }
320
321 static int modified_firmware(const char *dir) {
322     friidump_native_report r;
323     friidump_native_report_init(&r, "0.5.3.16-native-report-test");
324     base_drive(&r, "DVD-ROM GDR8050L", "0L23");
325     friidump_native_report_set_firmware_modified(
326         &r, true, "Cross-flashed firmware with added HIT 0xE7 memdump support.");
327     friidump_native_report_set_media(
328         &r, "gamecube", "Sonic Mega Collection", "USA", "GUPP8P");
329     friidump_native_report_set_seed(&r, true, "pass", true, 6.0);
330     friidump_native_report_set_dump(
331         &r, true, "pass", true, 712880, true, 1459978240ULL,
332         true, 9000.0, NULL, false, 0, "modified-drive-test.iso");
333     friidump_native_report_set_hashes(
334         &r,
335         "01b52739",
336         "85a525df1481d0ad67d8761f832dca12",
337         "06eb6d15b4d7f90ec0fed9cea9a77db41358d74ed",
338         NULL);
339     friidump_native_report_add_artifact(
340         &r, "dump_output", "modified-drive-test.iso", true,
341         1459978240ULL, NULL);
342     friidump_native_report_set_reference(&r, "redump", "match", "fixture");
343     friidump_native_report_set_outcome(&r, "full_dump", "pass");
344     friidump_native_report_add_note(&r, "Measurement scope: full_optical_payload.");
345     friidump_native_report_add_note(
346         &r, "UTF-8 preservation fixture: Pok\xc3\xa9mon.");
347     return write_report(&r, dir, "modified-firmware.friidump.json");
348 }
349
350 static int identity_tests(const char *dir) {
351     friidump_native_report a;
352     friidump_native_report b;
353     char path[FRIIDUMP_REPORT_PATH_MAX];
354     char written[FRIIDUMP_REPORT_PATH_MAX];
355     char error_text[256];
356
357     friidump_native_report_init(&a, "0.5.3.16-native-report-test");
358     friidump_native_report_init(&b, "0.5.3.16-native-report-test");
359     if (strcmp(a.run_id, b.run_id) == 0) {
360         fprintf(stderr, "Retry identity test failed: UUIDs are equal\n");
361         return 0;
362     }
363
364     base_drive(&a, "DVD-ROM GDR8163B", "0M26");
365     friidump_native_report_set_media(&a, "unknown", NULL, NULL, NULL);
366     friidump_native_report_set_outcome(&a, "diagnostic_no_media", "not_applicable");
367     friidump_native_report_set_reference(&a, NULL, "not_applicable", NULL);
368     snprintf(path, sizeof(path), "%s%sidentity-no-overwrite.friidump.json", dir, PATH_SEP);
369     if (!friidump_native_report_enable_path(&a, path))
370         return 0;
371     if (!friidump_native_report_write(&a, written, sizeof(written), error_text, sizeof(error_text)))
372         return 0;
373     if (friidump_native_report_write(&a, written, sizeof(written), error_text, sizeof(error_text))) {
374         fprintf(stderr, "No-overwrite test failed: second publish succeeded\n");
375         return 0;
376     }
377     if (strstr(error_text, "overwrite") == NULL) {
378         fprintf(stderr, "No-overwrite test failed with unexpected error: %s\n", error_text);
379         return 0;
380     }
381     printf("identity: unique retry UUIDs and no-overwrite policy PASS\n");
382     return 1;
383 }
384
385 static int report_directory_test(const char *dir) {
386     friidump_native_report r;
387     char nested[FRIIDUMP_REPORT_PATH_MAX];
388     char written[FRIIDUMP_REPORT_PATH_MAX];
389     char error_text[256];
390     FILE *f;
391
392     snprintf(nested, sizeof(nested), "%s%snested%sreports", dir, PATH_SEP, PATH_SEP);
393     friidump_native_report_init(&r, "0.5.3.16-native-report-test");
394     base_drive(&r, "DVD-ROM GDR8163B", "0M26");
395     friidump_native_report_set_media(
396         &r, "gamecube", "Directory Fixture", "USA", "DIR001");
397     friidump_native_report_set_seed(&r, false, "not_applicable", false, 0.0);
398     friidump_native_report_set_dump(
399         &r, false, "not_attempted", false, 0, false, 0,
400         false, 0.0, NULL, false, 0, NULL);
401     friidump_native_report_set_reference(&r, NULL, "not_applicable", NULL);
402     friidump_native_report_set_outcome(&r, "other", "not_applicable");
403
404     if (!friidump_native_report_enable_default(&r) ||
405         !friidump_native_report_set_name_basis(
406             &r, "logs/directory-fixture.log") ||
407         !friidump_native_report_enable_dir(&r, nested)) {
408         fprintf(stderr, "Could not configure nested report directory\n");
409         return 0;
410     }
411     if (!friidump_native_report_write(
412             &r, written, sizeof(written), error_text, sizeof(error_text))) {
413         fprintf(stderr, "Nested report-directory write failed: %s\n", error_text);
414         return 0;
415     }
416     if (strstr(written, "directory-fixture.friidump.json") == NULL ||
417         strstr(written, r.run_id) != NULL) {
418         fprintf(stderr, "Generated report path did not retain the log-derived name: %s\n", written);
419         return 0;
420     }
421     f = fopen(written, "rb");
422     if (!f) {
423         fprintf(stderr, "Generated report path is not readable: %s\n", written);
424         return 0;
425     }
426     fclose(f);
427     printf("report directory: nested creation and log-derived filename PASS\n");
428     return 1;
429 }
430
431 static int expect_write_failure(friidump_native_report *r,
432                                 const char *dir,
433                                 const char *filename,
434                                 const char *expected_error) {
435     char path[FRIIDUMP_REPORT_PATH_MAX];
436     char written[FRIIDUMP_REPORT_PATH_MAX];
437     char error_text[256];
438     FILE *f;
439
440     snprintf(path, sizeof(path), "%s%s%s", dir, PATH_SEP, filename);
441     remove(path);
442     if (!friidump_native_report_enable_path(r, path))
443         return 0;
444     if (friidump_native_report_write(
445             r, written, sizeof(written), error_text, sizeof(error_text))) {
446         fprintf(stderr, "Invalid report unexpectedly wrote %s\n", path);
447         remove(path);
448         return 0;
449     }
450     if (!strstr(error_text, expected_error)) {
451         fprintf(stderr, "Invalid report error mismatch: expected '%s', got '%s'\n",
452                 expected_error, error_text);
453         return 0;
454     }
455     f = fopen(path, "rb");
456     if (f) {
457         fclose(f);
458         fprintf(stderr, "Invalid report left a published file: %s\n", path);
459         remove(path);
460         return 0;
461     }
462     return 1;
463 }
464
465 static void valid_no_media_report(friidump_native_report *r) {
466     friidump_native_report_init(r, "0.5.3.16-native-report-test");
467     base_drive(r, "DVD-ROM GDR8163B", "0M26");
468     friidump_native_report_set_media(r, "unknown", NULL, NULL, NULL);
469     friidump_native_report_set_seed(r, false, "not_attempted", false, 0.0);
470     friidump_native_report_set_dump(
471         r, false, "not_attempted", false, 0, false, 0,
472         false, 0.0, NULL, false, 0, NULL);
473     friidump_native_report_set_reference(r, NULL, "not_applicable", NULL);
474     friidump_native_report_set_outcome(
475         r, "diagnostic_no_media", "not_applicable");
476 }
477
478 static int path_override_tests(const char *dir) {
479     friidump_native_report first;
480     friidump_native_report second;
481     friidump_native_report working;
482     friidump_native_report combined;
483     char nested[FRIIDUMP_REPORT_PATH_MAX];
484     char explicit_path[FRIIDUMP_REPORT_PATH_MAX];
485     char expected_path[FRIIDUMP_REPORT_PATH_MAX];
486     char written[FRIIDUMP_REPORT_PATH_MAX];
487     char error_text[256];
488     FILE *f;
489
490     snprintf(nested, sizeof(nested), "%s%spath-overrides", dir, PATH_SEP);
491
492     valid_no_media_report(&first);
493     if (!friidump_native_report_enable_default(&first) ||
494         !friidump_native_report_set_name_basis(
495             &first, "logs/default-session.log") ||
496         !friidump_native_report_enable_dir(&first, nested)) {
497         fprintf(stderr, "Could not configure derived path fixture\n");
498         return 0;
499     }
500     if (!friidump_native_report_write(
501             &first, written, sizeof(written), error_text, sizeof(error_text))) {
502         fprintf(stderr, "Could not write derived path fixture: %s\n", error_text);
503         return 0;
504     }
505     snprintf(expected_path, sizeof(expected_path),
506              "%s%sdefault-session.friidump.json", nested, PATH_SEP);
507     if (strcmp(written, expected_path) != 0) {
508         fprintf(stderr, "Derived path mismatch: expected %s, got %s\n",
509                 expected_path, written);
510         return 0;
511     }
512
513     valid_no_media_report(&second);
514     if (!friidump_native_report_enable_default(&second) ||
515         !friidump_native_report_set_name_basis(
516             &second, "logs/default-session.log") ||
517         !friidump_native_report_enable_dir(&second, nested)) {
518         fprintf(stderr, "Could not configure collision fixture\n");
519         return 0;
520     }
521     if (!friidump_native_report_write(
522             &second, written, sizeof(written), error_text, sizeof(error_text))) {
523         fprintf(stderr, "Could not write collision fixture: %s\n", error_text);
524         return 0;
525     }
526     if (strstr(written, second.run_id) == NULL ||
527         strstr(written, "default-session-") == NULL) {
528         fprintf(stderr, "Collision-safe path did not use the run UUID: %s\n", written);
529         return 0;
530     }
531
532     valid_no_media_report(&working);
533     snprintf(explicit_path, sizeof(explicit_path),
534              "%s%sworking-directory.log", nested, PATH_SEP);
535     if (!friidump_native_report_enable_default(&working) ||
536         !friidump_native_report_set_name_basis(&working, explicit_path)) {
537         fprintf(stderr, "Could not configure default beside-log fixture\n");
538         return 0;
539     }
540     if (!friidump_native_report_write(
541             &working, written, sizeof(written), error_text, sizeof(error_text))) {
542         fprintf(stderr, "Could not write default beside-log fixture: %s\n",
543                 error_text);
544         return 0;
545     }
546     snprintf(expected_path, sizeof(expected_path),
547              "%s%sworking-directory.friidump.json", nested, PATH_SEP);
548     if (strcmp(written, expected_path) != 0) {
549         fprintf(stderr, "Beside-log path mismatch: expected %s, got %s\n",
550                 expected_path, written);
551         return 0;
552     }
553     f = fopen(written, "rb");
554     if (!f) {
555         fprintf(stderr, "Default beside-log report is not readable: %s\n", written);
556         return 0;
557     }
558     fclose(f);
559
560     valid_no_media_report(&combined);
561     snprintf(explicit_path, sizeof(explicit_path),
562              "ignored%sparent%scustom-evidence.json", PATH_SEP, PATH_SEP);
563     if (!friidump_native_report_enable_default(&combined) ||
564         !friidump_native_report_enable_path(&combined, explicit_path) ||
565         !friidump_native_report_enable_dir(&combined, nested)) {
566         fprintf(stderr, "Could not configure combined pathname/directory fixture\n");
567         return 0;
568     }
569     if (!friidump_native_report_write(
570             &combined, written, sizeof(written), error_text, sizeof(error_text))) {
571         fprintf(stderr, "Could not write combined override fixture: %s\n", error_text);
572         return 0;
573     }
574     snprintf(expected_path, sizeof(expected_path),
575              "%s%scustom-evidence.json", nested, PATH_SEP);
576     if (strcmp(written, expected_path) != 0) {
577         fprintf(stderr, "Combined override mismatch: expected %s, got %s\n",
578                 expected_path, written);
579         return 0;
580     }
581     f = fopen(written, "rb");
582     if (!f) {
583         fprintf(stderr, "Combined override report is not readable: %s\n", written);
584         return 0;
585     }
586     fclose(f);
587
588     printf("path overrides: default beside-log, derived name, collision handling, and combined overrides PASS\n");
589     return 1;
590 }
591
592 static int invalid_report_tests(const char *dir) {
593     friidump_native_report r;
594
595     valid_no_media_report(&r);
596     friidump_native_report_set_firmware_modified(&r, true, NULL);
597     if (!expect_write_failure(
598             &r, dir, "invalid-modified-without-note.friidump.json",
599             "Modified firmware requires"))
600         return 0;
601
602     valid_no_media_report(&r);
603     snprintf(r.run_id, sizeof(r.run_id),
604              "00000000-0000-0000-0000-000000000000");
605     if (!expect_write_failure(
606             &r, dir, "invalid-zero-uuid.friidump.json",
607             "Run UUID"))
608         return 0;
609
610     valid_no_media_report(&r);
611     friidump_native_report_set_dump(
612         &r, true, "pass", false, 0, false, 0,
613         true, 1.0, NULL, false, 0, "invalid.iso");
614     friidump_native_report_set_outcome(&r, "full_dump", "pass");
615     if (!expect_write_failure(
616             &r, dir, "invalid-success-without-counts.friidump.json",
617             "requires sector and byte counts"))
618         return 0;
619
620     valid_no_media_report(&r);
621     friidump_native_report_set_seed(&r, false, "pass", false, 0.0);
622     if (!expect_write_failure(
623             &r, dir, "invalid-nonattempted-seed-pass.friidump.json",
624             "non-attempted seed stage"))
625         return 0;
626
627     valid_no_media_report(&r);
628     friidump_native_report_set_media(
629         &r, "gamecube", "Artifact Fixture", "USA", "ART001");
630     friidump_native_report_set_seed(&r, true, "pass", true, 1.0);
631     friidump_native_report_set_dump(
632         &r, true, "pass", true, 1, true, 2048,
633         true, 1.0, NULL, false, 0, "missing-artifact.iso");
634     friidump_native_report_set_hashes(
635         &r, NULL, NULL, NULL,
636         "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
637     friidump_native_report_set_outcome(&r, "full_dump", "pass");
638     friidump_native_report_add_note(&r, "Measurement scope: full_optical_payload.");
639     if (!expect_write_failure(
640             &r, dir, "invalid-missing-output-artifact.friidump.json",
641             "exactly one dump_output artifact"))
642         return 0;
643
644     valid_no_media_report(&r);
645     friidump_native_report_set_media(
646         &r, "gamecube", "Artifact Fixture", "USA", "ART002");
647     friidump_native_report_set_seed(&r, true, "pass", true, 1.0);
648     friidump_native_report_set_dump(
649         &r, true, "pass", true, 1, true, 2048,
650         true, 1.0, NULL, false, 0, "artifact-mismatch.iso");
651     friidump_native_report_set_hashes(
652         &r, NULL, NULL, NULL,
653         "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
654     friidump_native_report_add_artifact(
655         &r, "dump_output", "artifact-mismatch.iso", true, 4096,
656         "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
657     friidump_native_report_set_outcome(&r, "full_dump", "pass");
658     friidump_native_report_add_note(&r, "Measurement scope: full_optical_payload.");
659     if (!expect_write_failure(
660             &r, dir, "invalid-output-artifact-mismatch.friidump.json",
661             "does not match"))
662         return 0;
663
664     printf("invalid reports: semantic rejection and no publication PASS\n");
665     return 1;
666 }
667
668 int main(int argc, char **argv) {
669     int ok;
670     if (argc != 2) {
671         fprintf(stderr, "Usage: %s <fixture-directory>\n", argv[0]);
672         return 2;
673     }
674
675     ok = nintendo_disc_header_tests() &&
676          xbox_region_tests() &&
677          success_gamecube(argv[1]) &&
678          success_xbox(argv[1]) &&
679          partial_gamecube(argv[1]) &&
680          partial_cancelled(argv[1]) &&
681          partial_xbox_cancelled(argv[1]) &&
682          seed_failure(argv[1]) &&
683          no_media(argv[1]) &&
684          modified_firmware(argv[1]) &&
685          identity_tests(argv[1]) &&
686          report_directory_test(argv[1]) &&
687          path_override_tests(argv[1]) &&
688          invalid_report_tests(argv[1]);
689
690     return ok ? 0 : 1;
691 }