mcap/src/test/import_tests.jai
Musa Mahmood 72f2754226 [0.1.8] Video capture phase 5: sound, from the computer and the microphone, in sync
- modules/Screen_Record/audio_source.jai: WASAPI, in loopback from the default output device
  (what the computer plays) and from the default input device (the microphone), asked for as
  48 kHz stereo float, which Windows converts any device to; a device that will not convert is
  taken as it is when it gives 48 kHz float. Packets carry the time they were heard. A device
  lost (unplugged, default changed) is opened again from the new default once a second.
- An audio thread mixes them onto the video's own timeline: the same time 0, less the same
  pauses (record_pause now keeps one account of time paused for both threads). A packet within
  30 ms of where its source left off follows on exactly; a source that sends nothing is silence,
  passed 200 ms late. Each 1024 samples every source has covered become an AAC frame.
- encoder.jai: an AAC stream (FFmpeg's aac, 160 kb/s) beside the video, written from the audio
  thread; both threads' packets meet in the file under a lock. The remux keeps it.
- [video] system_audio (true) and microphone (false). capture.json: Audio, AudioSources. A toast
  when a source asked for could not be opened.
- record-probe: -audio system|mic|both, the sound read back (length, level), and -sync-test,
  which flashes a white square and beeps together every 2 s, then finds both in the file.
- Measured: 20 s, beeps 0-21 ms from their flashes, no drift; both sources mixed, 5-18 ms;
  a 6 s pause cuts the same 6 s from sound and video; 3 min, 85 of 90 beeps within 3-24 ms and
  -14 ms from first to last, with 5 outliers (53-147 ms) where two beeps went missing. Tests:
  an MP4 with sound keeps its AAC stream through the remux into the library (30 checks).
2026-10-10 08:08:47 +01:00

876 lines
45 KiB
Plaintext

// The importer's pieces on their own (app/import.jai, modules/Image_Decode): what kind of file bytes
// are, EXIF, turning an image by its orientation, the dates found in EXIF and in file names, and the
// folder: search; videos and animations, and the .mcap file read and written with its original left on disk.
#scope_file
checks_run := 0;
checks_failed := 0;
MAX_GROUPS :: 16;
group_names : [MAX_GROUPS] string;
group_checks : [MAX_GROUPS] int;
group_fails : [MAX_GROUPS] int;
group_count := 0;
output_builder: *String_Builder;
emit :: (format_string: string, args: ..Any) {
if output_builder print_to_builder(output_builder, format_string, ..args);
else print(format_string, ..args);
} @PrintLike
group :: (name: string) {
assert(group_count < MAX_GROUPS, "raise MAX_GROUPS");
group_names[group_count] = name;
group_count += 1;
emit("\n-- % --\n", name);
}
expect :: (condition: bool, format: string, args: ..Any, loc := #caller_location) {
checks_run += 1;
if group_count group_checks[group_count-1] += 1;
if condition return;
checks_failed += 1;
if group_count group_fails[group_count-1] += 1;
emit(" FAIL [%:%] %\n", group_names[max(group_count-1, 0)], loc.line_number, tprint(format, ..args));
}
report :: () -> bool {
emit("\n");
for 0..group_count-1 {
if group_fails[it] emit(" % -- % checks, % FAILED\n", group_names[it], group_checks[it], group_fails[it]);
else emit(" % -- % checks\n", group_names[it], group_checks[it]);
}
if checks_failed emit("\n% OF % IMPORT CHECKS FAILED\n", checks_failed, checks_run);
else emit("\nALL IMPORT TESTS PASSED (% checks over % groups)\n", checks_run, group_count);
return checks_failed == 0;
}
kind_tests :: () {
group("kind");
kind :: (bytes: string) -> Image_Kind { return image_kind(cast([] u8) bytes); }
expect(kind("\x89PNG\r\n\x1A\n....") == .PNG, "PNG");
expect(kind("\xFF\xD8\xFF\xE1") == .JPEG, "JPEG");
expect(kind("GIF89a......") == .GIF, "GIF");
expect(kind("RIFF\0\0\0\0WEBPVP8 ") == .WEBP, "WebP");
expect(kind("II*\0\x08\0\0\0") == .TIFF, "TIFF, little-endian");
expect(kind("MM\0*\0\0\0\x08") == .TIFF, "TIFF, big-endian");
expect(kind("\0\0\0\x18ftypheic\0\0\0\0mif1heic") == .HEIF, "HEIC");
expect(kind("\0\0\0\x1Cftypmif1\0\0\0\0mif1avifmiaf") == .AVIF, "AVIF under the general mif1 brand");
expect(kind("\0\0\0\x18ftypisom\0\0\0\0isommp41") == .VIDEO, "MP4");
expect(kind("\0\0\0\x14ftypqt \0\0\0\0qt ") == .VIDEO, "QuickTime");
expect(kind("\0\0\0\x18ftypcrx \0\0\0\x01crx isom") == .RAW, "Canon CR3");
expect(kind("\x1A\x45\xDF\xA3\x01\0\0\0") == .VIDEO, "Matroska / WebM");
expect(kind("RIFF\0\0\0\0AVI LIST") == .VIDEO, "AVI");
expect(kind("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9") == .VIDEO, "WMV");
expect(kind("\0\0\x01\xBA\x44\0\x04") == .VIDEO, "MPEG program stream");
expect(kind("\0\0\0\x08wide\0\0\0\0mdat") == .VIDEO, "old QuickTime");
expect(video_format_name(cast([] u8) "\0\0\0\x14ftypqt \0\0\0\0qt ", "IMG_7011.MOV") == "QuickTime", "MOV's format");
expect(video_format_name(cast([] u8) "\0\0\0\x18ftypisom\0\0\0\0isommp41", "a.mp4") == "MP4", "MP4's format");
expect(video_format_name(cast([] u8) "\x1A\x45\xDF\xA3", "clip.webm") == "WebM", "WebM's format, by its name");
expect(video_format_name(cast([] u8) "\x1A\x45\xDF\xA3", "clip.mkv") == "Matroska", "Matroska's format");
expect(kind("hello") == .UNKNOWN, "text");
expect(kind("") == .UNKNOWN, "nothing");
expect(image_kind_from_name(.TIFF, "IMG_0001.DNG") == .RAW, "a DNG is RAW");
expect(image_kind_from_name(.TIFF, "scan.tif") == .TIFF, "a .tif stays TIFF");
expect(image_kind_from_name(.UNKNOWN, "x.tga") == .TGA, "TGA by its name");
}
// A TIFF block with Orientation, Make, Model, an Exif directory with DateTimeOriginal, and GPS.
make_tiff :: (little: bool) -> [] u8 {
out: [..] u8;
out.allocator = temp;
u16_ :: (out: *[..] u8, v: u16, little: bool) {
if little { array_add(out, cast(u8) (v & 0xFF)); array_add(out, cast(u8) (v >> 8)); }
else { array_add(out, cast(u8) (v >> 8)); array_add(out, cast(u8) (v & 0xFF)); }
}
u32_ :: (out: *[..] u8, v: u32, little: bool) {
if little { u16_(out, cast(u16) (v & 0xFFFF), little); u16_(out, cast(u16) (v >> 16), little); }
else { u16_(out, cast(u16) (v >> 16), little); u16_(out, cast(u16) (v & 0xFFFF), little); }
}
entry :: (out: *[..] u8, tag: u16, type: u16, count: u32, value: u32, little: bool) {
u16_(out, tag, little); u16_(out, type, little); u32_(out, count, little);
if type == 3 && count == 1 { u16_(out, cast(u16) value, little); u16_(out, 0, little); }
else u32_(out, value, little);
}
// Layout: header 8; IFD0 at 8 (5 entries: 2 + 60 + 4 = 66) ends at 74; strings at 74 ("Pixel\0" 6, "Pixel 8 Pro\0" 12);
// Exif IFD at 92 (1 entry, 18) ends at 110; date at 110 (20); GPS IFD at 130 (4 entries, 54) ends at 184; rationals at 184 (48).
array_add(*out, ..cast([] u8) ifx little then "II" else "MM");
u16_(*out, 42, little);
u32_(*out, 8, little);
u16_(*out, 5, little);
entry(*out, 0x010F, 2, 6, 74, little);
entry(*out, 0x0110, 2, 12, 80, little);
entry(*out, 0x0112, 3, 1, 6, little);
entry(*out, 0x8769, 4, 1, 92, little);
entry(*out, 0x8825, 4, 1, 130, little);
u32_(*out, 0, little);
array_add(*out, ..cast([] u8) "Pixel\0");
array_add(*out, ..cast([] u8) "Pixel 8 Pro\0");
u16_(*out, 1, little);
entry(*out, 0x9003, 2, 20, 110, little);
u32_(*out, 0, little);
array_add(*out, ..cast([] u8) "2024:05:12 10:15:30\0");
u16_(*out, 4, little);
entry(*out, 1, 2, 2, ifx little then cast(u32) #char "S" else (cast(u32) #char "S") << 24, little);
entry(*out, 2, 5, 3, 184, little);
entry(*out, 3, 2, 2, ifx little then cast(u32) #char "W" else (cast(u32) #char "W") << 24, little);
entry(*out, 4, 5, 3, 208, little);
u32_(*out, 0, little);
for u32.[33, 1, 52, 1, 3000, 100, 151, 1, 12, 1, 0, 1] u32_(*out, it, little);
return out;
}
exif_tests :: () {
group("exif");
for little: bool.[true, false] {
tiff := make_tiff(little);
exif := exif_parse_tiff(tiff);
order := ifx little then "little-endian" else "big-endian";
expect(exif.found, "% TIFF found", order);
expect(exif.orientation == 6, "% orientation: %", order, exif.orientation);
expect(exif.make == "Pixel" && exif.model == "Pixel 8 Pro", "% make and model: \"%\" \"%\"", order, exif.make, exif.model);
expect(exif.date_original == "2024:05:12 10:15:30", "% DateTimeOriginal: \"%\"", order, exif.date_original);
expect(exif.has_gps && abs(exif.latitude - (-(33 + 52.0 / 60 + 30.0 / 3600))) < 0.000000001 && abs(exif.longitude - (-(151 + 12.0 / 60))) < 0.000000001,
"% GPS, south and west negative: %, %", order, exif.latitude, exif.longitude);
// The same block inside a JPEG's APP1 segment.
jpeg: [..] u8;
jpeg.allocator = temp;
array_add(*jpeg, 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x04, 0x00, 0x00); // an APP0 first
length := tiff.count + 8;
array_add(*jpeg, 0xFF, 0xE1, cast(u8) (length >> 8), cast(u8) (length & 0xFF));
array_add(*jpeg, ..cast([] u8) "Exif\0\0");
array_add(*jpeg, ..tiff);
array_add(*jpeg, 0xFF, 0xDA);
from_jpeg := exif_read(jpeg);
expect(from_jpeg.orientation == 6 && from_jpeg.model == "Pixel 8 Pro", "% EXIF from a JPEG's APP1", order);
}
expect(!exif_parse_tiff(cast([] u8) "not a tiff").found, "garbage is no EXIF");
expect(exif_read(cast([] u8) "\xFF\xD8\xFF\xDA").orientation == 1, "a JPEG with none is upright");
}
orientation_tests :: () {
group("orientation");
// 3 x 2, pixels numbered by where they are: 0 1 2 / 3 4 5. Each orientation's result, row by row.
expected :: u32.[
0, 1, 2, 3, 4, 5, // 1
2, 1, 0, 5, 4, 3, // 2 mirrored
5, 4, 3, 2, 1, 0, // 3 half a turn
3, 4, 5, 0, 1, 2, // 4 flipped
0, 3, 1, 4, 2, 5, // 5 transposed (2 x 3)
3, 0, 4, 1, 5, 2, // 6 a quarter turn clockwise
5, 2, 4, 1, 3, 0, // 7 transversed
2, 5, 1, 4, 0, 3, // 8 a quarter turn anticlockwise
];
for orientation: 1..8 {
pixels := cast(*u32) alloc(6 * 4,, context.default_allocator);
for 0..5 pixels[it] = cast(u32) it;
image := Image.{pixels = cast(*u8) pixels, width = 3, height = 2};
image_orient(*image, orientation);
swapped := orientation >= 5;
expect(image.width == (ifx swapped then 2 else 3) && image.height == (ifx swapped then 3 else 2), "orientation %: % x %", orientation, image.width, image.height);
got: [6] u32;
for 0..5 got[it] = (cast(*u32) image.pixels)[it];
wanted := array_view(expected, (orientation - 1) * 6, 6);
same := true;
for 0..5 if got[it] != wanted[it] same = false;
expect(same, "orientation %: % (wanted %)", orientation, got, wanted);
image_free(*image);
}
}
date_tests :: () {
group("dates");
expect(import_exif_date("2024:05:12 10:15:30") == "2024-05-12 10:15:30", "EXIF's date");
expect(import_exif_date("0000:00:00 00:00:00") == "", "a camera with no clock set");
expect(import_exif_date(" : : : : ") == "", "blanks");
expect(import_exif_date("") == "", "nothing");
Name_Date :: struct { name, date: string; }
names :: Name_Date.[
.{"IMG_20240512_101530.jpg", "2024-05-12 10:15:30"},
.{"PXL_20240512_101530123.jpg", "2024-05-12 10:15:30"},
.{"Screenshot_20240512-101530.png", "2024-05-12 10:15:30"},
.{"IMG-20240512-WA0001.jpg", "2024-05-12 00:00:00"},
.{"Screenshot 2024-05-12 at 10.15.30.png", "2024-05-12 10:15:30"},
.{"Screenshot_2024-05-12-10-15-30.png", "2024-05-12 10:15:30"},
.{"2024-05-12_10-15-30.snagx", "2024-05-12 10:15:30"},
.{"Screenshot 2024-05-12 101530.png", "2024-05-12 10:15:30"},
.{"IMG_0001.JPG", ""},
.{"scan_12345678.png", ""},
.{"DSC_20241399.jpg", ""},
];
for names {
got := import_name_date(it.name);
expect(got == it.date, "% -> \"%\" (wanted \"%\")", it.name, got, it.date);
}
expect(import_name_is_valid("SnagitLibraryWork") && import_name_is_valid("Phone 2024"), "folder names");
expect(!import_name_is_valid("a/b") && !import_name_is_valid("..") && !import_name_is_valid(".hidden") && !import_name_is_valid("x:") && !import_name_is_valid("trailing."), "not folder names");
expect(import_text("Canon\0\0") == "Canon", "EXIF text loses its trailing nulls");
expect(import_text("caf\xE9") == "caf?", "Latin-1 bytes are not UTF-8");
}
folder_tests :: () {
group("folder search");
terms := search_parse("folder:SnagitLibraryWork kicad");
expect(terms.count == 2 && terms[0].field == .FOLDER && terms[0].text == "snagitlibrarywork", "folder: prefix: %", terms);
entry: Catalog_Entry;
entry.find_folder = "snagitlibrarywork";
entry.find_title = "kicad";
expect(search_matches(*entry, terms), "in the folder");
expect(search_matches(*entry, search_parse("folder:work")), "part of the folder's name");
expect(!search_matches(*entry, search_parse("folder:personal")), "another folder");
expect(!search_matches(*entry, search_parse("snagitlibrarywork")), "a plain word does not match the folder");
}
video_tests :: () {
group("video and animation");
// Rotation from a display matrix (16.16 fixed point): an iPhone held upright stores its frames a
// quarter turn anticlockwise, so they are turned 90 degrees clockwise.
ONE :: 0x10000;
expect(display_matrix_rotation(.[ONE, 0, 0, 0, ONE, 0, 0, 0, 0x40000000]) == 0, "no turn");
expect(display_matrix_rotation(.[0, ONE, 0, -ONE, 0, 0, 0, 0, 0x40000000]) == 90, "portrait: 90 clockwise");
expect(display_matrix_rotation(.[-ONE, 0, 0, 0, -ONE, 0, 0, 0, 0x40000000]) == 180, "upside down");
expect(display_matrix_rotation(.[0, -ONE, 0, ONE, 0, 0, 0, 0, 0x40000000]) == 270, "270 clockwise");
expect(display_matrix_rotation(.[0, 0, 0, 0, 0, 0, 0, 0, 0]) == 0, "an empty matrix");
latitude, longitude, altitude, has_altitude, ok := iso6709_parse("+51.5072-000.1276+011.000/");
expect(ok && abs(latitude - 51.5072) < 0.000001 && abs(longitude + 0.1276) < 0.000001 && has_altitude && abs(altitude - 11) < 0.000001, "Apple's location: % % %", latitude, longitude, altitude);
latitude, longitude, altitude, has_altitude, ok = iso6709_parse("+37.7749-122.4194/");
expect(ok && abs(latitude - 37.7749) < 0.000001 && abs(longitude + 122.4194) < 0.000001 && !has_altitude, "Android's location, with no altitude");
_, _, _, _, ok = iso6709_parse("");
expect(!ok, "no location");
_, _, _, _, ok = iso6709_parse("+95.0+10.0/");
expect(!ok, "a latitude past the pole");
date, offset := import_video_local_date("2024-05-12T10:15:30+0100");
expect(date == "2024-05-12 10:15:30" && offset == "+01:00", "Apple's creationdate: % %", date, offset);
date, offset = import_video_local_date("2024-05-12T10:15:30-07:00");
expect(date == "2024-05-12 10:15:30" && offset == "-07:00", "with a colon in its offset: % %", date, offset);
date, offset = import_video_local_date("nonsense");
expect(date == "" && offset == "", "not a date");
expect(import_video_utc_date("1904-01-01T00:00:00.000000Z") == "", "a camera with no clock");
utc := import_video_utc_date("2024-05-12T09:15:30.000000Z");
expect(utc.count == 19 && begins_with(utc, "2024-05-"), "creation_time as local time: %", utc);
// A GIF of three frames, 20, 0 (shown for 100 ms) and 50 hundredths of a second, with no colour tables.
gif: [..] u8;
gif.allocator = temp;
add :: (gif: *[..] u8, bytes: string) { array_add(gif, ..cast([] u8) bytes); }
add(*gif, "GIF89a\x02\0\x02\0\0\0\0");
for delay: u8.[20, 0, 50] {
add(*gif, "\x21\xF9\x04\0");
array_add(*gif, delay, 0, 0, 0);
add(*gif, "\x2C\0\0\0\0\x02\0\x02\0\0"); // a frame, 2 x 2
add(*gif, "\x02\x02\x44\x01\0"); // LZW code size, one data block, the end
}
add(*gif, "\x3B");
frames, duration_ms, gif_ok := gif_info(gif);
expect(gif_ok && frames == 3 && duration_ms == 200 + 100 + 500, "an animated GIF: % frames, % ms", frames, duration_ms);
frames, duration_ms, gif_ok = gif_info(array_view(gif, 0, 13 + 8 + 10 + 5));
expect(gif_ok && frames == 1, "one cut short counts its whole frames: %", frames);
_, _, gif_ok = gif_info(cast([] u8) "\x89PNG\r\n\x1A\n");
expect(!gif_ok, "not a GIF");
}
// The .mcap file, version 4, and reading and writing it with its original left on disk.
mcap_tests :: () {
group("mcap version 4");
#if OS == .WINDOWS folder := tprint("%/mcap-tests", windows_expand_environment("%TEMP%"));
else folder := "/tmp/mcap-tests";
make_directory_if_it_does_not_exist(folder, recursive = true);
m: Mcap;
m.text = "[[file \"metadata.json\"]]\nWindowName: \"clip.mp4\"\n";
m.preview = .{width = 2, height = 1, png = cast([] u8) "preview"};
m.original_name = "clip.mp4";
original := NewArray(3 * 1024 * 1024 + 17, u8,, temp); // across more than one read of the copy
for * original it.* = cast,trunc(u8) (it_index * 7 + 3);
m.original = original;
m.rendered_kind = .OWN;
m.rendered = cast([] u8) "a frame";
m.back_chunks = .[.{tag = mcap_tag("mask"), key = "model", data = cast([] u8) "large"}];
bytes, serialized := mcap_serialize(m,, temp);
expect(serialized, "serialized");
back, parsed, error := mcap_parse(bytes);
expect(parsed && back.version == 4, "parses as version 4: %", error);
expect(cast(string) back.original == cast(string) original && cast(string) back.rendered == "a frame" && back.back_chunks.count == 1, "every part back");
// Version 3: the original's size a u32.
v3: [..] u8;
v3.allocator = temp;
array_add(*v3, ..array_view(bytes, 0, 8));
array_add(*v3, 3, 0, 0, 0);
head_end := bytes.count - original.count - 8 - (1 + 4 + 7 + mcap_chunks_size_for_test(back.back_chunks));
array_add(*v3, ..array_view(bytes, 12, head_end - 12));
size := cast(u32) original.count;
array_add(*v3, ..cast([] u8) string.{4, cast(*u8) *size});
array_add(*v3, ..array_view(bytes, head_end + 8));
old, old_parsed, old_error := mcap_parse(v3);
expect(old_parsed && old.version == 3 && cast(string) old.original == cast(string) original, "version 3 still reads: %", old_error);
// Streamed: the same bytes as serialized, with the original copied from a file.
source := tprint("%/source.bin", folder);
write_entire_file(source, original.data, original.count);
path := tprint("%/streamed.mcap", folder);
without := m;
without.original = .[];
written, write_error := mcap_write_streamed(path, without, .{source, 0, original.count});
data, read_ok := read_entire_file(path,, temp);
expect(written && read_ok && data == cast(string) bytes, "streamed is byte for byte serialized: %", write_error);
// Read without the original: the ref finds it.
m2, ref, ok, read_error := mcap_read_without_original(path,, temp);
expect(ok && m2.original_name == "clip.mp4" && ref.size == original.count && cast(string) m2.rendered == "a frame" && m2.back_chunks.count == 1, "read without the original: %", read_error);
read_back, range_ok := mcap_read_original(ref,, temp);
expect(range_ok && cast(string) read_back == cast(string) original, "its original where the ref says");
_, shown, which, shown_ok := mcap_read_shown_image(path,, temp);
expect(shown_ok && which == "rendered" && cast(string) shown == "a frame", "shown: the rendered image");
// Storing a scan's chunk rewrites the front and copies the original across unchanged.
stored, store_error := mcap_store(path, front = .[.{tag = mcap_tag("labl"), key = "model", data = cast([] u8) "labels"}]);
after, after_parsed := mcap_parse(cast([] u8) read_entire_file(path,, temp));
expect(stored && after_parsed && cast(string) after.original == cast(string) original && after.front_chunks.count == 1 && after.back_chunks.count == 1, "stored, the original kept: %", store_error);
erased, erase_error, changed := mcap_erase(path, tags = .["labl"]);
expect(erased && changed, "erased: %", erase_error);
// An image with no rendered one is shown as its original.
image := m;
image.rendered_kind = .NONE;
image.original = cast([] u8) "an image";
image_bytes := mcap_serialize(image,, temp);
image_path := tprint("%/image.mcap", folder);
mcap_write(image_path, image_bytes);
_, shown, which, shown_ok = mcap_read_shown_image(image_path,, temp);
expect(shown_ok && which == "original" && cast(string) shown == "an image", "shown: the original, when there is no rendered image");
// Extracted, as the Capture window does to open it.
extracted := tprint("%/extracted.mp4", folder);
extract_ok := mcap_extract_original(ref, extracted);
extracted_data := read_entire_file(extracted,, temp);
expect(extract_ok && extracted_data == cast(string) original, "extracted as it was");
// import_hash_file is import_hash, read a piece at a time.
hash, hashed := import_hash_file(.{source, 0, original.count});
expect(hashed && hash == import_hash(original), "a file's hash is its bytes' hash");
for string.[source, path, image_path, extracted] file_delete(it);
}
// The segmentation chunks (modules/Capture_File/segment_chunks.jai): segs text, the mask's run-length
// encoding, and storing both in one rewrite.
segment_chunk_tests :: () {
group("segment chunks");
s: Mcap_Segments;
s.engine = "onnxruntime 1.30.0, CUDA";
s.date = "2026-10-12T09:30:00Z";
s.image = "rendered";
s.image_width, s.image_height = 4032, 3024;
s.classes = "5c1e9a0d77b2f413";
s.segments.allocator = temp;
array_add(*s.segments, .{1, 0.981, 0.2140, 1210, 380, 900, 2210, "person"});
array_add(*s.segments, .{2, 0.5, 0.0001, 0, 0, 1, 1, "a \"quoted\" class"});
text := mcap_segments_text(s);
back, ok, error := mcap_segments_parse(text,, temp);
expect(ok && back.engine == s.engine && back.date == s.date && back.image == "rendered" && back.image_width == 4032 && back.image_height == 3024 && back.classes == s.classes, "segs header round trip: %", error);
expect(back.segments.count == 2, "both segments back: %", back.segments.count);
if back.segments.count == 2 {
a := back.segments[0];
expect(a.id == 1 && abs(a.score - 0.981) < 0.0005 && abs(a.area - 0.214) < 0.00005 && a.x == 1210 && a.y == 380 && a.width == 900 && a.height == 2210 && a.class == "person", "a segment's values: %", a);
expect(back.segments[1].class == "a \"quoted\" class", "a class with quotes: %", back.segments[1].class);
}
_, newer_ok := mcap_segments_parse("@version 2\nsegment: 1 0.9 0.1 0 0 1 1 \"x\"\n",, temp);
expect(!newer_ok, "a later version is refused");
junk, junk_ok := mcap_segments_parse("@version 1\nsegment: 1 0.9 \"too few numbers\"\nsegment: 0 0.9 0.1 0 0 1 1 \"id 0\"\nsegment: 3 0.9 0.1 0 0 1 1 \"fine\"\nfuture: kept quiet\n",, temp);
expect(junk_ok && junk.segments.count == 1 && junk.segments[0].class == "fine", "lines that cannot be read are skipped");
// Masks: the edge cases of the runs.
round_trip :: (w: s32, h: s32, map: [] u8, what: string, loc := #caller_location) -> s64 {
bytes := mcap_mask_bytes(w, h, map);
mask, ok, error := mcap_mask_parse(bytes,, temp);
expect(ok && mask.width == w && mask.height == h && cast(string) mask.map == cast(string) map, "%: %", what, error, loc = loc);
return bytes.count;
}
W, H :: 1024, 768;
everything := NewArray(W * H, u8,, temp);
for * everything it.* = 7;
size := round_trip(W, H, everything, "one segment over everything");
expect(size == 6 + 1 + 3, "one run of 786432 is 10 bytes, its length in 3: %", size);
nothing := NewArray(W * H, u8,, temp);
round_trip(W, H, nothing, "no segments");
stripes := NewArray(W * H, u8,, temp); // runs that cross rows: three rows and a bit each
for * stripes it.* = cast(u8) ((it_index / (W * 3 + 5)) % 256);
round_trip(W, H, stripes, "runs wrapping across rows, every id to 255");
noise := NewArray(37 * 11, u8,, temp); // runs of 1, and a map with odd sides
for * noise it.* = cast,trunc(u8) (it_index * 2654435761 >> 7);
round_trip(37, 11, noise, "runs of one");
one := u8.[255];
round_trip(1, 1, one, "one pixel, the largest id");
long := NewArray(127 + 128 + 16384 + 1, u8,, temp);
for * long it.* = ifx it_index < 127 then cast(u8) 1 else ifx it_index < 255 then cast(u8) 2 else ifx it_index < 255 + 16384 then cast(u8) 3 else cast(u8) 4;
round_trip(cast(s32) long.count, 1, long, "runs of 127, 128 and 16384");
// Bad masks are refused, not misread.
good := mcap_mask_bytes(37, 11, noise);
bad := array_copy(good,, temp);
bad[0] = 2;
_, ok = mcap_mask_parse(bad,, temp);
expect(!ok, "an unknown format");
_, ok = mcap_mask_parse(array_view(good, 0, good.count - 1),, temp);
expect(!ok, "cut short");
short := array_copy(good,, temp);
short[1] = 38; // a wider map than the runs cover
_, ok = mcap_mask_parse(short,, temp);
expect(!ok, "runs that do not cover it");
over := array_copy(good,, temp);
over[1] = 36; // narrower: the runs overflow it
_, ok = mcap_mask_parse(over,, temp);
expect(!ok, "runs that overflow it");
// Stored together with mcap_store, in one rewrite, and erased together.
#if OS == .WINDOWS folder := tprint("%/mcap-tests", windows_expand_environment("%TEMP%"));
else folder := "/tmp/mcap-tests";
make_directory_if_it_does_not_exist(folder, recursive = true);
path := tprint("%/segments.mcap", folder);
m: Mcap;
m.text = "[[file \"metadata.json\"]]\nWindowName: \"photo.jpg\"\n";
m.preview = .{width = 2, height = 1, png = cast([] u8) "preview"};
m.original_name = "photo.jpg";
m.original = cast([] u8) "the photo's bytes, as they were";
m.front_chunks = .[.{tag = mcap_tag("labl"), key = "siglip", data = cast([] u8) "labels"}];
m.back_chunks = .[.{tag = mcap_tag("zzzz"), key = "later", data = cast([] u8) "a chunk this build does not know"}];
bytes := mcap_serialize(m,, temp);
mcap_write(path, bytes);
MODEL :: "mask2former-swin-base-coco-panoptic";
map := NewArray(37 * 11, u8,, temp);
for * map it.* = cast(u8) (it_index % 3);
stored, store_error := mcap_store(path, front = .[mcap_segments_chunk(MODEL, s)], back = .[mcap_mask_chunk(MODEL, 37, 11, map)]);
after, parsed := mcap_parse(cast([] u8) read_entire_file(path,, temp));
expect(stored && parsed, "stored: %", store_error);
segs_chunk := mcap_find_chunk(after.front_chunks, mcap_tag(MCAP_SEGMENTS_TAG), MODEL);
mask_chunk := mcap_find_chunk(after.back_chunks, mcap_tag(MCAP_MASK_TAG), MODEL);
expect(segs_chunk && mask_chunk, "segs in front and mask in back");
expect(cast(string) after.original == cast(string) m.original && after.front_chunks.count == 2 && after.back_chunks.count == 2, "the original and every other chunk kept");
expect(mcap_find_chunk(after.back_chunks, mcap_tag("zzzz"), "later") != null, "a chunk this build does not know is kept");
if mask_chunk {
mask, mask_ok := mcap_mask_parse(mask_chunk.data,, temp);
expect(mask_ok && cast(string) mask.map == cast(string) map, "the stored mask reads back");
}
// Again: replaced, not added.
mcap_store(path, front = .[mcap_segments_chunk(MODEL, s)], back = .[mcap_mask_chunk(MODEL, 37, 11, map)]);
again, _ := mcap_parse(cast([] u8) read_entire_file(path,, temp));
expect(again.front_chunks.count == 2 && again.back_chunks.count == 2, "storing again replaces them");
erased, erase_error, changed := mcap_erase(path, tags = .[MCAP_SEGMENTS_TAG, MCAP_MASK_TAG]);
gone, _ := mcap_parse(cast([] u8) read_entire_file(path,, temp));
expect(erased && changed && gone.front_chunks.count == 1 && gone.back_chunks.count == 1, "erased together, the rest kept: %", erase_error);
file_delete(path);
}
// .library-folders (app/library_folders.jai): each folder's options, read and written back with what
// this build does not know kept.
library_folders_tests :: () {
group("library folders");
TEXT :: #string END
@version 1
# a comment
[folder "Photos 2014"]
segment: true
ocr: false
[folder "My ""quoted"" folder"]
segment: true
[folder "Screens"]
segment: false
[folder ""]
segment: true
END;
library_folders_parse(TEXT, "test");
expect(library_folder_segments("Photos 2014"), "a folder set to segment");
expect(library_folder_segments("My \"quoted\" folder"), "a quoted name");
expect(!library_folder_segments("Screens"), "a folder set not to");
expect(library_folder_segments(""), "the library's top");
expect(!library_folder_segments("Not listed"), "a folder not listed is off");
// Written back: the unknown key kept, under its folder; a folder with nothing on left out.
text := library_folders_text();
expect(contains(text, "[folder \"My \"\"quoted\"\" folder\"]"), "the quoted name written back quoted");
expect(contains(text, "[folder \"Photos 2014\"]\nsegment: true\nocr: false\n"), "a key this build does not know kept: %", text);
expect(!contains(text, "Screens"), "a folder with no option on is left out");
library_folders_parse(text, "test");
expect(library_folder_segments("Photos 2014") && library_folder_segments("My \"quoted\" folder") && library_folder_segments(""), "it reads back the same");
expect(library_folders_text() == text, "and writes the same again");
library_folders_parse("@version 2\n[folder \"Photos\"]\nsegment: true\n", "test");
expect(!library_folder_segments("Photos"), "a later version is not read: every option off");
library_folders_parse("[folder \"Photos\"]\nsegment: true\n", "test");
expect(!library_folder_segments("Photos"), "no @version: every option off");
library_folders_parse("@version 1\nsegment: true\n[folder \"Photos\"]\nsegment: maybe\n", "test");
expect(!library_folder_segments("Photos"), "a key outside a folder, or a value not true or false, sets nothing");
library_folders_parse("", "test");
expect(!library_folders.folders.count, "no file: no options");
}
// The Capture window's segments overlay (app/segment_ui.jai): the pixels it draws, and reading a file's
// segments for it.
segment_overlay_tests :: () {
group("segment overlay");
// A 4 x 3 mask: two people (ids 1 and 2) side by side on the left, a dog (3) on the right, a gap.
map := u8.[1, 1, 2, 3,
1, 1, 2, 3,
0, 0, 0, 3];
overlay: Segment_Overlay;
overlay.width, overlay.height = 4, 3;
overlay.map = map;
segments := Mcap_Segment.[.{id = 1, class = "person"}, .{id = 2, class = "person"}, .{id = 3, class = "dog"}];
overlay.segments = segments;
pixel :: (pixels: [] u8, x: s64, y: s64) -> [4] u8 {
p: [4] u8;
memcpy(p.data, pixels.data + (y * 4 + x) * 4, 4);
return p;
}
pixels := segment_overlay_pixels(*overlay, .[], 0.5);
expect(pixels.count == 4 * 3 * 4, "RGBA at the mask's size");
expect(pixel(pixels, 0, 2)[3] == 0, "no segment: clear");
a, b := pixel(pixels, 0, 0), pixel(pixels, 2, 0);
expect(a[3] > 0 && b[3] > 0, "segments drawn");
expect(a[0] != b[0] || a[1] != b[1] || a[2] != b[2], "two people side by side differ in shade");
inner, rim := pixel(pixels, 0, 0), pixel(pixels, 1, 0); // the first person's left column, and its edge with the second
expect(rim[3] > inner[3] && rim[0] <= inner[0], "edges stronger and darker than the inside: % and %", rim, inner);
same :: (a: [3] u8, b: [3] u8) -> bool { return a[0] == b[0] && a[1] == b[1] && a[2] == b[2]; }
dog := segment_class_colour("dog", 0);
expect(same(segment_class_colour("dog", 0), dog) && !same(segment_class_colour("person", 0), dog), "a class's colour is its own, the same each time");
// A colour chosen in the legend: the class's base, its instances shades of it; then back to its own.
own := segment_class_colour("dog", 0);
segment_colour_set("dog", .[200, 100, 50], save = false);
expect(same(segment_class_colour("dog", 0), .[200, 100, 50]), "a chosen colour is the class's");
second := segment_class_colour("dog", 1);
expect(second[0] < 200 && second[0] > 100 && second[1] < 100, "its next instance a darker shade of it: %", second);
expect(same(segment_class_colour("person", 0), segment_class_default_colour("person")), "other classes keep their own");
segment_colour_set("dog", .[0, 0, 0], default = true, save = false);
expect(same(segment_class_colour("dog", 0), own), "back to its own colour");
// Only dog's choice is gone: the colours chosen in the app's own data folder are loaded too.
dog_chosen := false;
for segment_ui.overlay_state.colours if it.class == "dog" dog_chosen = true;
expect(!dog_chosen, "and its choice is gone");
hidden := segment_overlay_pixels(*overlay, .["person"], 0.5);
expect(pixel(hidden, 0, 0)[3] == 0 && pixel(hidden, 2, 1)[3] == 0, "a hidden class is clear");
expect(pixel(hidden, 3, 0)[3] > 0, "the others still drawn");
faint := segment_overlay_pixels(*overlay, .[], 0.1);
expect(pixel(faint, 0, 0)[3] < inner[3], "opacity sets the alpha");
// Read from a file as a viewer worker reads it.
#if OS == .WINDOWS folder := tprint("%/mcap-tests", windows_expand_environment("%TEMP%"));
else folder := "/tmp/mcap-tests";
make_directory_if_it_does_not_exist(folder, recursive = true);
path := tprint("%/overlay.mcap", folder);
m: Mcap;
m.text = "[[file \"metadata.json\"]]\nWindowName: \"photo.jpg\"\n";
m.preview = .{width = 2, height = 1, png = cast([] u8) "preview"};
m.original_name = "photo.jpg";
m.original = cast([] u8) "the photo";
mcap_write(path, mcap_serialize(m,, temp));
s: Mcap_Segments;
s.image = "original";
s.image_width, s.image_height = 8, 6;
s.segments.allocator = temp;
array_add(*s.segments, .{id = 3, score = 0.9, area = 0.25, class = "dog"});
array_add(*s.segments, .{id = 1, score = 0.8, area = 0.2, class = "person"});
mcap_store(path, front = .[mcap_segments_chunk(SEGMENT_MODEL, s)], back = .[mcap_mask_chunk(SEGMENT_MODEL, 4, 3, map)]);
read := segment_overlay_read(path);
expect(!read.error && read.width == 4 && read.height == 3 && cast(string) read.map == cast(string) cast([] u8) map, "the mask read back: %", read.error);
expect(read.segments.count == 2 && read.by_id[3] == 0 && read.by_id[1] == 1 && read.by_id[2] == -1, "segments found by id");
segment_overlay_free(read);
bare := tprint("%/bare.mcap", folder);
mcap_write(bare, mcap_serialize(m,, temp));
none := segment_overlay_read(bare);
expect(none.error.count > 0, "a file not segmented says so: %", none.error);
segment_overlay_free(none);
file_delete(path);
file_delete(bare);
}
// A screen capture written into the library (app/screen_capture.jai): a PNG of its pixels, a preview,
// and the window it came from where search reads it.
screen_capture_tests :: () {
group("screen capture");
#if OS == .WINDOWS library := tprint("%/mcap-tests/library", windows_expand_environment("%TEMP%"));
else library := "/tmp/mcap-tests/library";
saved_library := app.library_folder;
app.library_folder = library;
defer app.library_folder = saved_library;
WIDTH :: 700;
HEIGHT :: 300;
shot: Screen_Shot;
shot.width, shot.height = WIDTH, HEIGHT;
shot.left, shot.top = -1920, 40; // on a screen left of the primary one
shot.pixels = NewArray(WIDTH * HEIGHT * 4, u8,, temp);
for y: 0..HEIGHT - 1 for x: 0..WIDTH - 1 {
p := shot.pixels.data + (y * WIDTH + x) * 4;
p[0], p[1], p[2], p[3] = cast,trunc(u8) x, cast,trunc(u8) y, 200, 255;
}
shot.window_title = "Assign Footprints \x01- KiCad"; // a control character, as titles can hold
shot.app_name = "KiCad";
shot.app_path = "C:/Program Files/KiCad/bin/kicad.exe";
path, error := screen_capture_write(shot, "2026-10-10 14:05:09");
expect(!error, "written: %", error);
expect(begins_with(path, tprint("%/%/2026-10-10_14-05-09_", library, SCREEN_CAPTURE_FOLDER)), "named by its date, in its folder: %", path);
again, again_error := screen_capture_write(shot, "2026-10-10 14:05:09");
expect(!again_error && again != path && ends_with(again, "_2.mcap"), "the same again gets a name of its own: %", again);
m, parsed := mcap_parse(cast([] u8) read_entire_file(path,, temp));
expect(parsed, "it reads back");
if parsed {
expect(m.rendered_kind == .NONE && ends_with(m.original_name, ".png"), "the original is a PNG: %", m.original_name);
image, decoded := image_decode(m.original);
expect(decoded && image.width == WIDTH && image.height == HEIGHT, "at its own size");
if decoded {
expect(memcmp(image.pixels, shot.pixels.data, shot.pixels.count) == 0, "every pixel as it was");
image_free(*image);
}
expect(max(m.preview.width, m.preview.height) == MCAP_PREVIEW_MAX_SIDE, "a preview: % x %", m.preview.width, m.preview.height);
text :: (object: Capture_Value, key: string) -> string {
value := capture_object_find(*object, key);
return ifx value && value.kind == .STRING then value.text else "";
}
number :: (object: Capture_Value, key: string) -> float64 {
value := capture_object_find(*object, key);
return ifx value && value.kind == .NUMBER then value.number else 0;
}
doc: Capture_Document;
capture_document_init(*doc, temp);
expect(capture_text_parse(*doc, path, m.text), "its Capture Text reads");
metadata := capture_find_file(*doc, "metadata.json");
capture := capture_find_file(*doc, "capture.json");
expect(metadata && capture, "metadata.json and capture.json");
if metadata {
expect(text(metadata.root, "WindowName") == "Assign Footprints - KiCad", "WindowName: %", text(metadata.root, "WindowName"));
expect(text(metadata.root, "AppName") == "KiCad", "AppName");
expect(text(metadata.root, "CaptureDate") == "2026-10-10 14:05:09", "CaptureDate");
}
if capture {
expect(number(capture.root, "Left") == -1920 && number(capture.root, "Top") == 40, "where on the desktop");
expect(text(capture.root, "Region") == "selection", "a selection");
}
}
file_delete(path);
file_delete(again);
}
// A screen recording written into the library (app/screen_record.jai): the MP4 as its original, a
// frame from it as its rendered image, and capture.json saying it is a video, as the catalog reads it.
// The MP4 is made here with modules/Screen_Record's encoder, from frames of a changing colour.
screen_record_tests :: () {
group("screen recording");
#if OS == .WINDOWS {
expect(record_size_allowed(240, 180), "240 x 180 may be recorded");
expect(!record_size_allowed(239, 180) && !record_size_allowed(240, 179), "smaller may not");
expect(record_size_allowed(3840, 2160), "a 4K screen may");
folder := tprint("%/mcap-tests", windows_expand_environment("%TEMP%"));
library := tprint("%/library", folder);
saved_library := app.library_folder;
app.library_folder = library;
defer app.library_folder = saved_library;
make_directory_if_it_does_not_exist(folder, recursive = true);
WIDTH :: 320;
HEIGHT :: 240;
FRAMES :: 30;
mp4 := tprint("%/recording-test.mp4", folder);
encoder: Encoder;
opened, open_error := encoder_open(*encoder, mp4, WIDTH, HEIGHT, 30, "");
expect(opened, "an encoder opened: %", open_error);
if !opened return;
pixels := NewArray(WIDTH * HEIGHT * 4, u8,, temp);
for frame: 0..FRAMES - 1 {
for i: 0..WIDTH * HEIGHT - 1 {
pixels[i * 4 + 0] = cast,trunc(u8) (frame * 8);
pixels[i * 4 + 1] = cast,trunc(u8) (i % WIDTH);
pixels[i * 4 + 2] = 128;
pixels[i * 4 + 3] = 255;
}
encoder_write(*encoder, pixels.data, frame * 3000); // 30 a second, in 90 kHz ticks
}
encoder_name := copy_string(encoder.name,, temp);
closed, close_error := encoder_close(*encoder);
expect(closed, "the MP4 finished: %", close_error);
_, mp4_size, _ := file_modtime_and_size(mp4);
job: Screen_Capture_Job;
job.video = true;
job.recording = mp4;
job.date = "2026-10-10 15:20:00";
job.encoder = encoder_name;
job.source = "DXGI DuplicateOutput1 on a test";
job.shot.width, job.shot.height = WIDTH, HEIGHT;
job.shot.left, job.shot.top = 100, 200;
job.shot.window_title = "Recording test - KiCad";
job.shot.app_name = "KiCad";
path, error := screen_record_write(*job);
expect(!error, "written: %", error);
if error return;
expect(begins_with(path, tprint("%/%/2026-10-10_15-20-00_", library, SCREEN_CAPTURE_FOLDER)), "named by its date, in its folder: %", path);
expect(!file_exists(mp4), "the MP4 is deleted once it is in the library");
m, parsed := mcap_parse(cast([] u8) read_entire_file(path,, temp));
expect(parsed, "it reads back");
if parsed {
// Recorded fragmented, it goes in a regular MP4: the same frames, its header first.
original := cast(string) m.original;
expect(ends_with(m.original_name, ".mp4") && slice(original, 4, 4) == "ftyp" && find_index_from_left(original, "moof") < 0 && find_index_from_left(original, "moov") < find_index_from_left(original, "mdat"),
"the MP4, made regular, is its original: % (% bytes, % recorded)", m.original_name, m.original.count, mp4_size);
expect(m.rendered_kind == .OWN, "a frame of it is its rendered image");
poster, decoded := image_decode(m.rendered);
expect(decoded && poster.width == WIDTH && poster.height == HEIGHT, "at the recording's size");
if decoded image_free(*poster);
}
entry, entry_error := catalog_load(path);
expect(entry != null, "catalogued: %", entry_error);
if entry {
expect(entry.media == .VIDEO, "a video, from capture.json");
expect(entry.video_codec == "h264", "H.264: %", entry.video_codec);
expect(entry.duration > 0.9 && entry.duration < 1.1, "about a second: %", entry.duration);
expect(entry.title == "Recording test - KiCad" && entry.app == "KiCad", "the window it was recorded from");
catalog_entry_free(entry);
}
file_delete(path);
// A recording a crash left behind: its date from its name, and no window, size or encoder known.
expect(screen_record_leftover_date("C:/Temp/mcap/recording-2026-10-10_07-12-43.mp4") == "2026-10-10 07:12:43", "a leftover's date from its name");
expect(screen_record_leftover_date("C:/Temp/mcap/recording-2026-10-10_07-12-43_2.mp4") == "2026-10-10 07:12:43", "and from a second one's");
encoder = .{};
opened, open_error = encoder_open(*encoder, mp4, WIDTH, HEIGHT, 30, "");
for frame: 0..9 encoder_write(*encoder, pixels.data, frame * 3000);
encoder_close(*encoder);
leftover: Screen_Capture_Job;
leftover.video = true;
leftover.recording = mp4;
leftover.date = "2026-10-10 07:12:43";
leftover_path, leftover_error := screen_record_write(*leftover);
expect(!leftover_error, "a leftover is written: %", leftover_error);
if !leftover_error {
leftover_entry := catalog_load(leftover_path);
expect(leftover_entry && leftover_entry.media == .VIDEO, "as a video");
if leftover_entry catalog_entry_free(leftover_entry);
m2, parsed2 := mcap_parse(cast([] u8) read_entire_file(leftover_path,, temp));
if parsed2 {
doc2: Capture_Document;
capture_document_init(*doc2, temp);
capture_text_parse(*doc2, leftover_path, m2.text);
info2 := capture_find_file(*doc2, "capture.json");
width2 := ifx info2 then capture_object_find(*info2.root, "Width") else null;
encoder2 := ifx info2 then capture_object_find(*info2.root, "Encoder") else null;
expect(width2 && width2.kind == .NUMBER && width2.number == WIDTH && !encoder2, "its size from its frames, and no encoder claimed");
}
file_delete(leftover_path);
}
// With sound: an AAC stream beside the video, kept through the remux, and capture.json says so.
encoder = .{};
opened, open_error = encoder_open(*encoder, mp4, WIDTH, HEIGHT, 30, "", audio = true);
expect(opened && encoder.audio != null, "an encoder with sound opened: %", open_error);
if opened {
tone := NewArray(encoder.audio_frame_size * 2, float,, temp);
for frame: 0..29 encoder_write(*encoder, pixels.data, frame * 3000);
for block: 0..46 { // about a second of 440 Hz
for i: 0..encoder.audio_frame_size - 1 {
v := sin(cast(float) (block * encoder.audio_frame_size + i) * 2 * PI * 440 / 48000) * 0.5;
tone[i * 2], tone[i * 2 + 1] = v, v;
}
encoder_write_audio(*encoder, tone.data, block * encoder.audio_frame_size);
}
encoder_close(*encoder);
sounding: Screen_Capture_Job;
sounding.video = true;
sounding.recording = mp4;
sounding.date = "2026-10-10 08:30:00";
sounding.audio = "Speakers (test)";
sounding.shot.width, sounding.shot.height = WIDTH, HEIGHT;
sounding_path, sounding_error := screen_record_write(*sounding);
expect(!sounding_error, "a recording with sound is written: %", sounding_error);
if !sounding_error {
m4, parsed4 := mcap_parse(cast([] u8) read_entire_file(sounding_path,, temp));
expect(parsed4 && find_index_from_left(cast(string) m4.original, "mp4a") >= 0, "its MP4 has the AAC stream (mp4a)");
expect(parsed4 && contains(m4.text, "Speakers (test)"), "capture.json names the sound's source");
file_delete(sounding_path);
}
}
// One a crash cut short: a frame every 100 ms for 5 s makes fragments at 0, 2 and 4 s (a
// keyframe each 2 s, however few the frames); cut at the last, as a crash would, it goes into the
// library as a regular MP4 (header first, no fragments) of the 4 s before the cut.
encoder = .{};
encoder_open(*encoder, mp4, WIDTH, HEIGHT, 30, "");
for frame: 0..49 encoder_write(*encoder, pixels.data, frame * 9000);
encoder_close(*encoder);
whole := read_entire_file(mp4,, temp);
last_fragment := -1;
for 4..whole.count - 4 if whole[it] == #char "m" && slice(whole, it, 4) == "moof" last_fragment = it - 4;
expect(last_fragment > 0, "the recording is fragmented");
write_entire_file(mp4, slice(whole, 0, last_fragment));
crashed: Screen_Capture_Job;
crashed.video = true;
crashed.recording = mp4;
crashed.date = "2026-10-10 07:48:52";
crashed_path, crashed_error := screen_record_write(*crashed);
expect(!crashed_error, "a cut recording is written: %", crashed_error);
if !crashed_error {
m3, parsed3 := mcap_parse(cast([] u8) read_entire_file(crashed_path,, temp));
original := cast(string) m3.original;
moov := find_index_from_left(original, "moov");
mdat := find_index_from_left(original, "mdat");
expect(parsed3 && moov > 0 && moov < mdat && find_index_from_left(original, "moof") < 0, "a regular MP4, its header first");
crashed_entry := catalog_load(crashed_path);
expect(crashed_entry && crashed_entry.duration > 3.5 && crashed_entry.duration < 4.5, "the 4 s before the cut: %", ifx crashed_entry then crashed_entry.duration else -1);
if crashed_entry catalog_entry_free(crashed_entry);
file_delete(crashed_path);
}
}
}
// What mcap_chunks_size counts, here where it cannot be seen.
mcap_chunks_size_for_test :: (chunks: [] Mcap_Chunk) -> s64 {
size: s64 = 4;
for chunks size += 4 + 4 + 2 + it.key.count + it.data.count;
return size;
}
#scope_export
run_import_tests :: (builder: *String_Builder = null) -> bool {
output_builder = builder;
emit("=== import test suite ===\n");
push_allocator(context.default_allocator);
kind_tests();
exif_tests();
orientation_tests();
date_tests();
folder_tests();
video_tests();
mcap_tests();
segment_chunk_tests();
library_folders_tests();
segment_overlay_tests();
screen_capture_tests();
screen_record_tests();
return report();
}