nx_text_ingest_test.nx source
↩ module page · 151 lines · 6305 B
1// nx_text_ingest_test.nx -- end-to-end smoke for the Tier 0+1 stack.
2//
3// Exercises:
4// nx_media_pool : alloc / push / type+src lookup
5// nx_storybeat : affect-pack/unpack i64 lane roundtrip
6// nx_media_sniff : PNG magic / JPEG magic / text fallback / mime
7// nx_text_ingest : minimal prose corpus -> beats / scenes / chapters
8//
9// Exit code:
10// 0 all assertions pass
11// N assertion N failed (each test block uses a unique N)
12//
13// Follows the canonical fail-loud smoke convention; runs through
14// nxc2.exe -> RV64 asm -> riscv64-as+ld -> qemu via
15// bench/nx_text_ingest_smoke.sh.
16
17import "syscalls.nx"
18import "nx_media_pool.nx"
19import "nx_storybeat.nx"
20import "nx_storydb.nx"
21import "nx_media_sniff.nx"
22import "nx_text_ingest.nx"
23
24func main() -> i64 {
25
26 // ---------- Block 10: affect pack / unpack roundtrip ----------
27 let packed: i64 = nx_beat_pack_affect(10, -20, 30, -40)
28 if nx_beat_affect_arousal(packed) != 10 { return 10 }
29 if nx_beat_affect_tenderness(packed) != -20 { return 11 }
30 if nx_beat_affect_urgency(packed) != 30 { return 12 }
31 if nx_beat_affect_intimacy(packed) != -40 { return 13 }
32
33 // Zero default beat: every field 0.
34 let packed_zero: i64 = nx_beat_pack_affect(0, 0, 0, 0)
35 if packed_zero != 0 { return 14 }
36
37 // ---------- Block 20: media pool basic ----------
38 let pool: *MediaPool = nx_media_pool_alloc(8)
39 if pool.cap != 8 { return 20 }
40 if pool.n_items != 0 { return 21 }
41
42 let txt_bytes: *u8 = "hello world"
43 let path1: *u8 = "test.txt"
44 let mid1: i64 = nx_media_pool_add(pool, NX_MEDIA_TYPE_TEXT,
45 NX_MEDIA_SRC_USER_FILE,
46 txt_bytes, 11,
47 0 as *u8, 0,
48 path1, 8,
49 1700000000)
50 if mid1 != 0 { return 22 }
51 if pool.n_items != 1 { return 23 }
52 if nx_media_pool_type(pool, 0) != NX_MEDIA_TYPE_TEXT { return 24 }
53 if nx_media_pool_src(pool, 0) != NX_MEDIA_SRC_USER_FILE { return 25 }
54
55 var got_bytes: *u8 = 0 as *u8
56 var got_len: i64 = 0
57 if nx_media_pool_get(pool, 0, &got_bytes, &got_len) != 0 { return 26 }
58 if got_len != 11 { return 27 }
59
60 // Out-of-range lookups return UNKNOWN.
61 if nx_media_pool_type(pool, 99) != NX_MEDIA_TYPE_UNKNOWN { return 28 }
62 if nx_media_pool_src(pool, -1) != NX_MEDIA_SRC_UNKNOWN { return 29 }
63
64 // ---------- Block 30: sniffer magic-bytes ----------
65 let png_buf: *u8 = sys_mmap(8)
66 png_buf[0] = 0x89; png_buf[1] = 0x50; png_buf[2] = 0x4E; png_buf[3] = 0x47
67 png_buf[4] = 0x0D; png_buf[5] = 0x0A; png_buf[6] = 0x1A; png_buf[7] = 0x0A
68 if nx_sniff_is_png(png_buf, 8) != 1 { return 30 }
69 if nx_sniff_bytes(png_buf, 8) != NX_MEDIA_TYPE_IMAGE { return 31 }
70
71 let jpeg_buf: *u8 = sys_mmap(4)
72 jpeg_buf[0] = 0xFF; jpeg_buf[1] = 0xD8; jpeg_buf[2] = 0xFF; jpeg_buf[3] = 0xE0
73 if nx_sniff_is_jpeg(jpeg_buf, 4) != 1 { return 32 }
74 if nx_sniff_bytes(jpeg_buf, 4) != NX_MEDIA_TYPE_IMAGE { return 33 }
75
76 let gif_buf: *u8 = sys_mmap(6)
77 gif_buf[0] = 0x47; gif_buf[1] = 0x49; gif_buf[2] = 0x46
78 gif_buf[3] = 0x38; gif_buf[4] = 0x39; gif_buf[5] = 0x61
79 if nx_sniff_is_gif(gif_buf, 6) != 1 { return 34 }
80 if nx_sniff_bytes(gif_buf, 6) != NX_MEDIA_TYPE_GIF { return 35 }
81
82 // Text fallback.
83 let txt2: *u8 = "the quick brown fox jumps over the lazy dog"
84 if nx_sniff_bytes(txt2, 43) != NX_MEDIA_TYPE_TEXT { return 36 }
85
86 // ---------- Block 40: sniffer MIME tier ----------
87 let mime_html: *u8 = "text/html; charset=utf-8"
88 if nx_sniff_mime(mime_html, 24) != NX_MEDIA_TYPE_HTML { return 40 }
89 let mime_jpg: *u8 = "image/jpeg"
90 if nx_sniff_mime(mime_jpg, 10) != NX_MEDIA_TYPE_IMAGE { return 41 }
91 let mime_mp3: *u8 = "audio/mpeg"
92 if nx_sniff_mime(mime_mp3, 10) != NX_MEDIA_TYPE_AUDIO { return 42 }
93
94 // ---------- Block 50: sniffer extension tier ----------
95 let p_png: *u8 = "scene.png"
96 if nx_sniff_extension(p_png, 9) != NX_MEDIA_TYPE_IMAGE { return 50 }
97 let p_md: *u8 = "chapter1.md"
98 if nx_sniff_extension(p_md, 11) != NX_MEDIA_TYPE_TEXT { return 51 }
99 let p_mp4: *u8 = "clip.mp4"
100 if nx_sniff_extension(p_mp4, 8) != NX_MEDIA_TYPE_VIDEO { return 52 }
101 let p_no_ext: *u8 = "README"
102 if nx_sniff_extension(p_no_ext, 6) != NX_MEDIA_TYPE_UNKNOWN { return 53 }
103
104 // ---------- Block 60: StoryDb push beat ----------
105 let db: *StoryDb = nx_storydb_alloc()
106 if db.n_beats != 0 { return 60 }
107 if db.n_scenes != 0 { return 61 }
108 if db.n_chapters != 0 { return 62 }
109
110 let b0: i64 = nx_storydb_push_beat(db, NX_BEAT_NARRATION,
111 NX_MEDIA_TYPE_TEXT, 0, 0, 100)
112 if b0 != 0 { return 63 }
113 if db.n_beats != 1 { return 64 }
114 let beat0: *StoryBeat = nx_storydb_beat_at(db, 0)
115 if beat0.beat_kind != NX_BEAT_NARRATION { return 65 }
116 if beat0.span_start != 0 { return 66 }
117 if beat0.span_end != 100 { return 67 }
118
119 // ---------- Block 70: text ingester end-to-end ----------
120 let corpus_buf: *u8 = sys_mmap(512)
121 var k: i64 = 0
122 while k < 512 { corpus_buf[k] = 0; k = k + 1 }
123 let corpus: *u8 = "# Chapter One\n\nIt was a dark and stormy night.\n\nShe opened the door.\n\n***\n\nMorning came slowly.\n"
124 var ci: i64 = 0
125 while corpus[ci] != 0 {
126 corpus_buf[ci] = corpus[ci]
127 ci = ci + 1
128 }
129 let corpus_len: i64 = ci
130
131 let db2: *StoryDb = nx_storydb_alloc()
132 let mid_corpus: i64 = nx_media_pool_add(db2.media_pool, NX_MEDIA_TYPE_TEXT,
133 NX_MEDIA_SRC_USER_PASTE,
134 corpus_buf, corpus_len,
135 0 as *u8, 0,
136 0 as *u8, 0,
137 1700000000)
138 if mid_corpus < 0 { return 70 }
139
140 let rc: i64 = nx_text_ingest(db2, mid_corpus)
141 if rc != NX_TEXT_INGEST_OK { return 71 }
142
143 // Expected: 1 heading + 2 paragraphs + 1 transition + 1 paragraph = 5 beats.
144 if db2.n_beats < 4 { return 72 }
145 // At least 1 chapter (from the heading).
146 if db2.n_chapters < 1 { return 73 }
147 // At least 1 scene.
148 if db2.n_scenes < 1 { return 74 }
149
150 return 0
151}