// 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(); }