code wiki / (root) / nx_vcf_test.nx

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}