nx_fastq_test.nx source
↩ module page · 160 lines · 6218 B
1// nx_fastq_test.nx -- KAT for FASTQ single-record stream parser.
2//
3// Hand-built byte buffer fixtures verify exact offset returns +
4// EOF + malformed-input handling.
5//
6// expect_exit: 0
7//
8// license_tier: ORIGINAL
9
10import "nx_syscalls.nx"
11import "nx_const.nx"
12import "nx_fastq.nx"
13
14func main() -> i64 {
15
16 let buf: *u8 = sys_mmap(256)
17 let meta: *i64 = sys_mmap(128) as *i64
18
19 // ============================================================
20 // Section A -- nx_find_lf helper.
21 // ============================================================
22
23 buf[0]=0x41; buf[1]=0x42; buf[2]=0x0A; buf[3]=0x43 // "AB\nC"
24 if nx_find_lf(buf, 0, 4) != 2 { return 1 }
25 if nx_find_lf(buf, 3, 4) != -1 { return 2 } // past the LF
26 if nx_find_lf(buf, 0, 0) != -1 { return 3 } // empty range
27
28 // ============================================================
29 // Section B -- single canonical record.
30 // Buffer layout (17 bytes):
31 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
32 // @ A B C \n A C G T \n + \n I G D B \n
33 // Expected meta: header_start=1 header_len=3
34 // seq_start=5 seq_len=4
35 // qual_start=12 qual_len=4
36 // next_offset=17
37 // ============================================================
38
39 buf[0]=NX_ASCII_AT & 0xff
40 buf[1]=0x41; buf[2]=0x42; buf[3]=0x43 // "ABC"
41 buf[4]=NX_ASCII_LF & 0xff
42 buf[5]=0x41; buf[6]=0x43; buf[7]=0x47; buf[8]=0x54 // "ACGT"
43 buf[9]=NX_ASCII_LF & 0xff
44 buf[10]=NX_ASCII_PLUS & 0xff
45 buf[11]=NX_ASCII_LF & 0xff
46 buf[12]=0x49; buf[13]=0x47; buf[14]=0x44; buf[15]=0x42 // "IGDB"
47 buf[16]=NX_ASCII_LF & 0xff
48
49 if fastq_parse_one_record(buf, 17, 0, meta) != 0 { return 10 }
50 if meta[0] != 1 { return 11 }
51 if meta[1] != 3 { return 12 }
52 if meta[2] != 5 { return 13 }
53 if meta[3] != 4 { return 14 }
54 if meta[4] != 12 { return 15 }
55 if meta[5] != 4 { return 16 }
56 if meta[6] != 17 { return 17 }
57
58 // ============================================================
59 // Section C -- last record without trailing LF (EOF mid-qual-line).
60 // Same as B but drop the final LF -- buffer 16 bytes.
61 // next_offset should equal buf_len = 16.
62 // ============================================================
63
64 if fastq_parse_one_record(buf, 16, 0, meta) != 0 { return 20 }
65 if meta[6] != 16 { return 21 }
66 if meta[3] != 4 { return 22 } // seq_len unchanged
67 if meta[5] != 4 { return 23 } // qual_len unchanged
68
69 // ============================================================
70 // Section D -- EOF: calling at end-of-buffer returns 1.
71 // ============================================================
72
73 if fastq_parse_one_record(buf, 17, 17, meta) != 1 { return 30 }
74 if fastq_parse_one_record(buf, 0, 0, meta) != 1 { return 31 }
75
76 // ============================================================
77 // Section E -- two-record stream.
78 // First record per Section B (offset 0..17).
79 // Append a second record:
80 // 17 @ 18 X 19 \n
81 // 20 T 21 C 22 \n
82 // 23 + 24 \n
83 // 25 ! 26 " 27 \n
84 // Total 28 bytes.
85 // Expected:
86 // first call: returns 0, next_offset=17
87 // second call (offset=17): header_start=18 len=1
88 // seq_start=20 seq_len=2
89 // qual_start=25 qual_len=2
90 // next_offset=28
91 // ============================================================
92
93 buf[17]=NX_ASCII_AT & 0xff
94 buf[18]=0x58 // 'X'
95 buf[19]=NX_ASCII_LF & 0xff
96 buf[20]=0x54; buf[21]=0x43 // "TC"
97 buf[22]=NX_ASCII_LF & 0xff
98 buf[23]=NX_ASCII_PLUS & 0xff
99 buf[24]=NX_ASCII_LF & 0xff
100 buf[25]=0x21; buf[26]=0x22 // "!\"" -- Phred Q=0, Q=1
101 buf[27]=NX_ASCII_LF & 0xff
102
103 if fastq_parse_one_record(buf, 28, 0, meta) != 0 { return 40 }
104 if meta[6] != 17 { return 41 }
105
106 if fastq_parse_one_record(buf, 28, 17, meta) != 0 { return 42 }
107 if meta[0] != 18 { return 43 }
108 if meta[1] != 1 { return 44 }
109 if meta[2] != 20 { return 45 }
110 if meta[3] != 2 { return 46 }
111 if meta[4] != 25 { return 47 }
112 if meta[5] != 2 { return 48 }
113 if meta[6] != 28 { return 49 }
114
115 // Third call at offset 28 should be EOF.
116 if fastq_parse_one_record(buf, 28, 28, meta) != 1 { return 50 }
117
118 // ============================================================
119 // Section F -- malformed: missing '@' at offset.
120 // ============================================================
121
122 buf[0]=0x41 // 'A' instead of '@'
123 if fastq_parse_one_record(buf, 17, 0, meta) != -1 { return 60 }
124
125 // Restore for next sections.
126 buf[0]=NX_ASCII_AT & 0xff
127
128 // ============================================================
129 // Section G -- malformed: missing '+' separator line.
130 // Replace '+' at offset 10 with 'X' -> -1.
131 // ============================================================
132
133 buf[10]=0x58
134 if fastq_parse_one_record(buf, 17, 0, meta) != -1 { return 70 }
135 buf[10]=NX_ASCII_PLUS & 0xff
136
137 // ============================================================
138 // Section H -- malformed: qual line truncated (seq_len > qual_len).
139 // Set buf_len=15 so qual line has only 3 bytes instead of 4.
140 // qual_end = 12 + 4 = 16 > 15 -> -1.
141 // ============================================================
142
143 if fastq_parse_one_record(buf, 15, 0, meta) != -1 { return 80 }
144
145 // ============================================================
146 // Section I -- header-only EOF (truncated).
147 // Just "@ABC\n" then nothing -> EOF mid-record -> -1.
148 // ============================================================
149
150 if fastq_parse_one_record(buf, 5, 0, meta) != -1 { return 90 }
151
152 // ============================================================
153 // Section J -- bad parameters.
154 // ============================================================
155
156 if fastq_parse_one_record(buf, -1, 0, meta) != -1 { return 100 }
157 if fastq_parse_one_record(buf, 17, -1, meta) != -1 { return 101 }
158
159 return 0
160}