nx_vcf_test.nx source
↩ module page · 263 lines · 12162 B
1// nx_vcf_test.nx -- KAT for VCF single-line SNV writer.
2//
3// expect_exit: 0
4//
5// license_tier: ORIGINAL
6
7import "nx_syscalls.nx"
8import "nx_const.nx"
9import "nx_vcf.nx"
10
11func main() -> i64 {
12
13 let chrom: *u8 = sys_mmap(32)
14 let out: *u8 = sys_mmap(128)
15
16 // ============================================================
17 // Section A -- canonical SNV line.
18 // (chrom="chr1", pos=12345, REF=A, ALT=G, qual=60, PASS, depth=20)
19 // Expected: "chr1\t12345\t.\tA\tG\t60\tPASS\tDP=20\n"
20 // Byte count: chr1(4)+\t+12345(5)+\t+.(1)+\t+A(1)+\t+G(1)+\t+60(2)
21 // +\t+PASS(4)+\t+DP=20(5)+\n = 4+1+5+1+1+1+1+1+1+1+2+1+4+1+5+1
22 // = 30 content bytes + 1 LF terminator = 31 total
23 // ============================================================
24
25 chrom[0]=0x63; chrom[1]=0x68; chrom[2]=0x72; chrom[3]=0x31 // "chr1"
26
27 // BISECT: test helpers individually first.
28 let nc: i64 = nx_copy_bytes(chrom, 4, out, 0, 128)
29 if nc != 4 { return 210 }
30
31 let nb: i64 = nx_copy_byte(NX_ASCII_TAB, out, 4, 128)
32 if nb != 1 { return 211 }
33
34 let ni: i64 = nx_emit_int(12345, out, 5, 128)
35 if ni != 5 { return 212 }
36
37 // Try trivial vcf_write_snv_line call.
38 let fields_tiny: *i64 = sys_mmap(64) as *i64
39 fields_tiny[NX_VCF_FIELD_POS] = 1
40 fields_tiny[NX_VCF_FIELD_REF] = NX_DNA_A
41 fields_tiny[NX_VCF_FIELD_ALT] = NX_DNA_G
42 fields_tiny[NX_VCF_FIELD_QUAL] = 0
43 fields_tiny[NX_VCF_FIELD_PASS] = 1
44 fields_tiny[NX_VCF_FIELD_DEPTH] = 0
45 let n_tiny: i64 = vcf_write_snv_line(chrom, 4, fields_tiny, out, 128)
46 if n_tiny < 0 { return 200 }
47 if n_tiny == 0 { return 201 }
48 // "chr1\t1\t.\tA\tG\t0\tPASS\tDP=0\n" = 4+1+1+1+1+1+1+1+1+1+1+1+4+1+4+1 = 25
49 if n_tiny != 25 { return n_tiny + 100 }
50
51 let fields1: *i64 = sys_mmap(64) as *i64
52 fields1[NX_VCF_FIELD_POS] = 12345
53 fields1[NX_VCF_FIELD_REF] = NX_DNA_A
54 fields1[NX_VCF_FIELD_ALT] = NX_DNA_G
55 fields1[NX_VCF_FIELD_QUAL] = 60
56 fields1[NX_VCF_FIELD_PASS] = 1
57 fields1[NX_VCF_FIELD_DEPTH] = 20
58 let n1: i64 = vcf_write_snv_line(chrom, 4, fields1, out, 128)
59 if n1 != 31 { return n1 + 100 }
60
61 // Verify byte-for-byte.
62 if (out[0] & 0xff) != 0x63 { return 10 } // c
63 if (out[1] & 0xff) != 0x68 { return 11 } // h
64 if (out[2] & 0xff) != 0x72 { return 12 } // r
65 if (out[3] & 0xff) != 0x31 { return 13 } // 1
66 if (out[4] & 0xff) != 0x09 { return 14 } // \t
67 if (out[5] & 0xff) != 0x31 { return 15 } // 1
68 if (out[6] & 0xff) != 0x32 { return 16 } // 2
69 if (out[7] & 0xff) != 0x33 { return 17 } // 3
70 if (out[8] & 0xff) != 0x34 { return 18 } // 4
71 if (out[9] & 0xff) != 0x35 { return 19 } // 5
72 if (out[10] & 0xff) != 0x09 { return 20 } // \t
73 if (out[11] & 0xff) != 0x2E { return 21 } // .
74 if (out[12] & 0xff) != 0x09 { return 22 } // \t
75 if (out[13] & 0xff) != 0x41 { return 23 } // A (REF)
76 if (out[14] & 0xff) != 0x09 { return 24 } // \t
77 if (out[15] & 0xff) != 0x47 { return 25 } // G (ALT)
78 if (out[16] & 0xff) != 0x09 { return 26 } // \t
79 if (out[17] & 0xff) != 0x36 { return 27 } // 6
80 if (out[18] & 0xff) != 0x30 { return 28 } // 0
81 if (out[19] & 0xff) != 0x09 { return 29 } // \t
82 if (out[20] & 0xff) != 0x50 { return 30 } // P
83 if (out[21] & 0xff) != 0x41 { return 31 } // A
84 if (out[22] & 0xff) != 0x53 { return 32 } // S
85 if (out[23] & 0xff) != 0x53 { return 33 } // S
86 if (out[24] & 0xff) != 0x09 { return 34 } // \t
87 if (out[25] & 0xff) != 0x44 { return 35 } // D
88 if (out[26] & 0xff) != 0x50 { return 36 } // P
89 if (out[27] & 0xff) != 0x3D { return 37 } // =
90 if (out[28] & 0xff) != 0x32 { return 38 } // 2
91 if (out[29] & 0xff) != 0x30 { return 39 } // 0
92 if (out[30] & 0xff) != 0x0A { return 40 } // LF terminator
93
94 // ============================================================
95 // Section B -- FILTER=fail emits '.' instead of "PASS".
96 // (chr1, pos=100, A>T, qual=5, pass_flag=0, depth=3)
97 // Expected: "chr1\t100\t.\tA\tT\t5\t.\tDP=3\n"
98 // Length: 4+1+3+1+1+1+1+1+1+1+1+1+1+1+4+1 = 23
99 // ============================================================
100
101 let fields2: *i64 = sys_mmap(64) as *i64
102 fields2[NX_VCF_FIELD_POS] = 100
103 fields2[NX_VCF_FIELD_REF] = NX_DNA_A
104 fields2[NX_VCF_FIELD_ALT] = NX_DNA_T
105 fields2[NX_VCF_FIELD_QUAL] = 5
106 fields2[NX_VCF_FIELD_PASS] = 0
107 fields2[NX_VCF_FIELD_DEPTH] = 3
108 let n2: i64 = vcf_write_snv_line(chrom, 4, fields2, out, 128)
109 if n2 != 24 { return n2 + 50 }
110 // Layout: chr1(0-3) \t(4) 100(5-7) \t(8) .(9) \t(10) A(11) \t(12)
111 // T(13) \t(14) 5(15) \t(16) .(17 FILTER) \t(18) DP=3(19-22) \n(23)
112 if (out[17] & 0xff) != 0x2E { return 51 } // '.' FILTER
113 if (out[18] & 0xff) != 0x09 { return 52 } // \t before INFO
114 if (out[19] & 0xff) != 0x44 { return 53 } // D
115 if (out[23] & 0xff) != 0x0A { return 54 } // LF
116
117 // ============================================================
118 // Section C -- multi-digit depth & high QUAL.
119 // (chr1, pos=1000000, C>T, qual=99, PASS, depth=1234)
120 // Expected: "chr1\t1000000\t.\tC\tT\t99\tPASS\tDP=1234\n"
121 // ============================================================
122
123 let fields3: *i64 = sys_mmap(64) as *i64
124 fields3[NX_VCF_FIELD_POS] = 1000000
125 fields3[NX_VCF_FIELD_REF] = NX_DNA_C
126 fields3[NX_VCF_FIELD_ALT] = NX_DNA_T
127 fields3[NX_VCF_FIELD_QUAL] = 99
128 fields3[NX_VCF_FIELD_PASS] = 1
129 fields3[NX_VCF_FIELD_DEPTH] = 1234
130 let n3: i64 = vcf_write_snv_line(chrom, 4, fields3, out, 128)
131 // Length: chr1(4) \t 1000000(7) \t .(1) \t C(1) \t T(1) \t 99(2) \t PASS(4) \t DP=1234(7) \n = 35
132 if n3 != 35 { return n3 + 60 }
133 // chr1(0-3) \t(4) 1000000(5-11) \t(12) .(13) \t(14) C(15) \t(16) T(17)
134 if (out[15] & 0xff) != 0x43 { return 90 } // REF 'C'
135 if (out[17] & 0xff) != 0x54 { return 91 } // ALT 'T'
136
137 // ============================================================
138 // Section D -- bad inputs rejected.
139 // ============================================================
140
141 // ============================================================
142 // Section E -- vcf_write_header: byte-exact + length check.
143 // ============================================================
144
145 let hbuf: *u8 = sys_mmap(256)
146 let nh: i64 = vcf_write_header(hbuf, 256)
147 if nh != 175 { return nh + 500 } // debug-as-exit if length wrong
148
149 // Spot-check Line 1: "##fileformat=VCFv4.2\n" first/last bytes.
150 if (hbuf[0] & 0xff) != 0x23 { return 502 } // #
151 if (hbuf[1] & 0xff) != 0x23 { return 503 } // #
152 if (hbuf[2] & 0xff) != 0x66 { return 504 } // f
153 if (hbuf[12] & 0xff) != 0x3D { return 505 } // =
154 if (hbuf[13] & 0xff) != 0x56 { return 506 } // V
155 if (hbuf[19] & 0xff) != 0x32 { return 507 } // 2 (end of VCFv4.2)
156 if (hbuf[20] & 0xff) != 0x0A { return 508 } // \n
157
158 // Line 2 spot-checks: starts at offset 21.
159 if (hbuf[21] & 0xff) != 0x23 { return 510 } // # of ##INFO
160 if (hbuf[27] & 0xff) != 0x3D { return 511 } // = of INFO=
161 // Last byte of line 2 LF at offset 21 + 62 = 83.
162 if (hbuf[83] & 0xff) != 0x0A { return 512 }
163
164 // Line 3 spot-checks: starts at offset 84.
165 if (hbuf[84] & 0xff) != 0x23 { return 520 } // # of ##FILTER
166 if (hbuf[86] & 0xff) != 0x46 { return 521 } // F of FILTER
167 // Last LF at offset 84 + 51 = 135.
168 if (hbuf[135] & 0xff) != 0x0A { return 522 }
169
170 // Line 4 spot-checks: starts at offset 136.
171 if (hbuf[136] & 0xff) != 0x23 { return 530 } // # of #CHROM
172 if (hbuf[137] & 0xff) != 0x43 { return 531 } // C
173 if (hbuf[142] & 0xff) != 0x09 { return 532 } // \t after CHROM
174 if (hbuf[174] & 0xff) != 0x0A { return 533 } // final \n at offset 174
175
176 // Capacity overflow returns -1.
177 if vcf_write_header(hbuf, 174) != -1 { return 540 }
178 if vcf_write_header(hbuf, 0) != -1 { return 541 }
179
180 // ============================================================
181 // Section F -- vcf_write_var_line: INSERTION (REF=A, ALT=AGT).
182 // Expected: "chr1\t100\t.\tA\tAGT\t60\tPASS\tDP=20\n"
183 // Length: 4+1+3+1+1+1+1+1+3+1+2+1+4+1+5+1 = 31 bytes (incl LF)
184 // ============================================================
185
186 let var_fields: *i64 = sys_mmap(64) as *i64
187 var_fields[NX_VCF_VAR_FIELD_POS] = 100
188 var_fields[NX_VCF_VAR_FIELD_QUAL] = 60
189 var_fields[NX_VCF_VAR_FIELD_PASS] = 1
190 var_fields[NX_VCF_VAR_FIELD_DEPTH] = 20
191 var_fields[NX_VCF_VAR_FIELD_REF_LEN] = 1
192 var_fields[NX_VCF_VAR_FIELD_ALT_LEN] = 3
193
194 let ref_alt_ins: *u8 = sys_mmap(16)
195 ref_alt_ins[0] = 0x41 // REF: A
196 ref_alt_ins[1] = 0x41 // ALT[0]: A
197 ref_alt_ins[2] = 0x47 // ALT[1]: G
198 ref_alt_ins[3] = 0x54 // ALT[2]: T
199
200 let var_out: *u8 = sys_mmap(128)
201 let nv1: i64 = vcf_write_var_line(chrom, 4, var_fields, ref_alt_ins, var_out, 128)
202 if nv1 != 31 { return nv1 + 600 }
203 // Layout: chr1(0-3) \t(4) 100(5-7) \t(8) .(9) \t(10) A(11=REF) \t(12)
204 // AGT(13-15) \t(16) 60(17-18) \t(19) PASS(20-23) \t(24) DP=20(25-29) \n(30)
205 if (var_out[11] & 0xff) != 0x41 { return 601 } // REF 'A'
206 if (var_out[13] & 0xff) != 0x41 { return 602 } // ALT[0] 'A'
207 if (var_out[14] & 0xff) != 0x47 { return 603 } // ALT[1] 'G'
208 if (var_out[15] & 0xff) != 0x54 { return 604 } // ALT[2] 'T'
209 if (var_out[30] & 0xff) != 0x0A { return 605 } // final LF at index 30
210
211 // ============================================================
212 // Section G -- vcf_write_var_line: DELETION (REF=AGT, ALT=A).
213 // Expected: "chr1\t100\t.\tAGT\tA\t60\tPASS\tDP=20\n" (also 31 bytes)
214 // ============================================================
215
216 var_fields[NX_VCF_VAR_FIELD_REF_LEN] = 3
217 var_fields[NX_VCF_VAR_FIELD_ALT_LEN] = 1
218
219 let ref_alt_del: *u8 = sys_mmap(16)
220 ref_alt_del[0] = 0x41 // REF[0]: A
221 ref_alt_del[1] = 0x47 // REF[1]: G
222 ref_alt_del[2] = 0x54 // REF[2]: T
223 ref_alt_del[3] = 0x41 // ALT: A
224
225 let nv2: i64 = vcf_write_var_line(chrom, 4, var_fields, ref_alt_del, var_out, 128)
226 if nv2 != 31 { return nv2 + 700 }
227 // chr1(0-3) \t(4) 100(5-7) \t(8) .(9) \t(10) AGT(11-13) \t(14) A(15) \t ...
228 if (var_out[11] & 0xff) != 0x41 { return 701 } // REF[0] 'A'
229 if (var_out[12] & 0xff) != 0x47 { return 702 } // REF[1] 'G'
230 if (var_out[13] & 0xff) != 0x54 { return 703 } // REF[2] 'T'
231 if (var_out[15] & 0xff) != 0x41 { return 704 } // ALT 'A'
232
233 // ============================================================
234 // Section H -- vcf_write_var_line: bad inputs.
235 // ============================================================
236
237 var_fields[NX_VCF_VAR_FIELD_REF_LEN] = 0 // empty REF refused
238 if vcf_write_var_line(chrom, 4, var_fields, ref_alt_del, var_out, 128) != -1 { return 710 }
239 var_fields[NX_VCF_VAR_FIELD_REF_LEN] = 1
240 var_fields[NX_VCF_VAR_FIELD_ALT_LEN] = 0 // empty ALT refused
241 if vcf_write_var_line(chrom, 4, var_fields, ref_alt_del, var_out, 128) != -1 { return 711 }
242
243 // Bad inputs via field tweaks.
244 let fbad: *i64 = sys_mmap(64) as *i64
245 fbad[NX_VCF_FIELD_POS]=1; fbad[NX_VCF_FIELD_REF]=NX_DNA_A
246 fbad[NX_VCF_FIELD_ALT]=NX_DNA_G; fbad[NX_VCF_FIELD_QUAL]=60
247 fbad[NX_VCF_FIELD_PASS]=1; fbad[NX_VCF_FIELD_DEPTH]=20
248
249 fbad[NX_VCF_FIELD_POS] = 0
250 if vcf_write_snv_line(chrom, 4, fbad, out, 128) != -1 { return 70 }
251 fbad[NX_VCF_FIELD_POS] = 1; fbad[NX_VCF_FIELD_REF] = -1
252 if vcf_write_snv_line(chrom, 4, fbad, out, 128) != -1 { return 71 }
253 fbad[NX_VCF_FIELD_REF] = NX_DNA_A; fbad[NX_VCF_FIELD_ALT] = 4
254 if vcf_write_snv_line(chrom, 4, fbad, out, 128) != -1 { return 72 }
255 fbad[NX_VCF_FIELD_ALT] = NX_DNA_G; fbad[NX_VCF_FIELD_QUAL] = -1
256 if vcf_write_snv_line(chrom, 4, fbad, out, 128) != -1 { return 73 }
257 fbad[NX_VCF_FIELD_QUAL] = 60; fbad[NX_VCF_FIELD_DEPTH] = -1
258 if vcf_write_snv_line(chrom, 4, fbad, out, 128) != -1 { return 74 }
259 fbad[NX_VCF_FIELD_DEPTH] = 20
260 if vcf_write_snv_line(chrom, 4, fbad, out, 5) != -1 { return 75 }
261
262 return 0
263}