github.com/Schaudge/hts@v0.0.0-20240223063651-737b4d69d68c/sam/sam_test.go (about) 1 // Copyright ©2013 The bíogo Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sam 6 7 import ( 8 "bytes" 9 "errors" 10 "flag" 11 "fmt" 12 "io" 13 "reflect" 14 "sort" 15 "strings" 16 "testing" 17 "time" 18 19 "gopkg.in/check.v1" 20 ) 21 22 var ( 23 bam = flag.Bool("bam", false, "output failing bam data for inspection") 24 allbam = flag.Bool("allbam", false, "output all bam data for inspection") 25 ) 26 27 type failure bool 28 29 func (f failure) String() string { 30 if f { 31 return "fail" 32 } 33 return "ok" 34 } 35 36 func Test(t *testing.T) { check.TestingT(t) } 37 38 type S struct{} 39 40 var _ = check.Suite(&S{}) 41 42 func (s *S) TestCloneHeader(c *check.C) { 43 for _, h := range []*Header{ 44 headerHG00096_1000, 45 } { 46 c.Check(h, check.DeepEquals, h.Clone()) 47 } 48 } 49 50 func (s *S) TestSpecExamples(c *check.C) { 51 sr, err := NewReader(bytes.NewReader(specExamples.data)) 52 c.Assert(err, check.Equals, nil) 53 h := sr.Header() 54 c.Check(h.Version, check.Equals, specExamples.header.Version) 55 c.Check(h.SortOrder, check.Equals, specExamples.header.SortOrder) 56 c.Check(h.GroupOrder, check.Equals, specExamples.header.GroupOrder) 57 c.Check(h.Comments, check.DeepEquals, specExamples.header.Comments) 58 59 var buf bytes.Buffer 60 sw, err := NewWriter(&buf, h, FlagDecimal) 61 c.Assert(err, check.Equals, nil) 62 for i, expect := range specExamples.records { 63 r, err := sr.Read() 64 if err != nil { 65 c.Errorf("Unexpected early error: %v", err) 66 continue 67 } 68 c.Check(r.Name, check.Equals, expect.Name) 69 c.Check(r.Pos, check.Equals, expect.Pos) // Zero-based here. 70 c.Check(r.Flags, check.Equals, expect.Flags) 71 if r.Flags&Unmapped == 0 { 72 c.Check(r.Ref, check.Not(check.Equals), nil) 73 if r.Ref != nil { 74 c.Check(r.Ref.Name(), check.Equals, h.Refs()[0].Name()) 75 } 76 } else { 77 c.Check(r.Ref, check.Equals, nil) 78 } 79 c.Check(r.MatePos, check.Equals, expect.MatePos) // Zero-based here. 80 c.Check(r.Cigar, check.DeepEquals, expect.Cigar) 81 c.Check(r.Cigar.IsValid(r.Seq.Length), check.Equals, true) 82 c.Check(r.TempLen, check.Equals, expect.TempLen) 83 c.Check(r.Seq, check.DeepEquals, expect.Seq, check.Commentf("got:%q expected:%q", r.Seq.Expand(), expect.Seq.Expand())) 84 c.Check(r.Qual, check.DeepEquals, expect.Qual) // No valid qualities here. 85 c.Check(r.End(), check.Equals, specExamples.readEnds[i], check.Commentf("unexpected end position for %q at %v, got:%d expected:%d", r.Name, r.Pos, r.End(), specExamples.readEnds[i])) 86 c.Check(r.AuxFields, check.DeepEquals, expect.AuxFields) 87 88 parsedCigar, err := ParseCigar([]byte(specExamples.cigars[i])) 89 c.Check(err, check.Equals, nil) 90 c.Check(parsedCigar, check.DeepEquals, expect.Cigar) 91 92 // In all the examples the last base of the read and the last 93 // base of the ref are valid, so we can check this. 94 expSeq := r.Seq.Expand() 95 c.Check(specExamples.ref[r.End()-1], check.Equals, expSeq[len(expSeq)-1]) 96 97 // Test round trip. 98 err = sw.Write(r) 99 c.Check(err, check.Equals, nil) 100 b, err := r.MarshalText() 101 c.Check(err, check.Equals, nil) 102 var nr Record 103 c.Check(nr.UnmarshalSAM(sr.Header(), b), check.Equals, nil) 104 c.Check(&nr, check.DeepEquals, r) 105 } 106 c.Check(buf.String(), check.DeepEquals, string(specExamples.data)) 107 } 108 109 func mustAux(a Aux, err error) Aux { 110 if err != nil { 111 panic(err) 112 } 113 return a 114 } 115 116 var specExamples = struct { 117 ref string 118 data []byte 119 header Header 120 records []*Record 121 cigars []string 122 readEnds []int 123 }{ 124 ref: "AGCATGTTAGATAAGATAGCTGTGCTAGTAGGCAGTCAGCGCCAT", 125 data: []byte(`@HD VN:1.5 SO:coordinate 126 @SQ SN:ref LN:45 127 @CO -------------------------------------------------------- 128 @CO Coor 12345678901234 5678901234567890123456789012345 129 @CO ref AGCATGTTAGATAA**GATAGCTGTGCTAGTAGGCAGTCAGCGCCAT 130 @CO -------------------------------------------------------- 131 @CO +r001/1 TTAGATAAAGGATA*CTG 132 @CO +r002 aaaAGATAA*GGATA 133 @CO +r003 gcctaAGCTAA 134 @CO +r004 ATAGCT..............TCAGC 135 @CO -r003 ttagctTAGGC 136 @CO -r001/2 CAGCGGCAT 137 @CO -------------------------------------------------------- 138 r001 99 ref 7 30 8M2I4M1D3M = 37 39 TTAGATAAAGGATACTG * 139 r002 0 ref 9 30 3S6M1P1I4M * 0 0 AAAAGATAAGGATA * 140 r003 0 ref 9 30 5S6M * 0 0 GCCTAAGCTAA * SA:Z:ref,29,-,6H5M,17,0; 141 r004 0 ref 16 30 6M14N5M * 0 0 ATAGCTTCAGC * 142 r003 2064 ref 29 17 6H5M * 0 0 TAGGC * SA:Z:ref,9,+,5S6M,30,1; 143 r001 147 ref 37 30 9M = 7 -39 CAGCGGCAT * NM:i:1 144 `), 145 header: Header{ 146 Version: "1.5", 147 SortOrder: Coordinate, 148 GroupOrder: GroupUnspecified, 149 Comments: []string{ 150 "--------------------------------------------------------", 151 "Coor 12345678901234 5678901234567890123456789012345", 152 "ref AGCATGTTAGATAA**GATAGCTGTGCTAGTAGGCAGTCAGCGCCAT", 153 "--------------------------------------------------------", 154 "+r001/1 TTAGATAAAGGATA*CTG", 155 "+r002 aaaAGATAA*GGATA", 156 "+r003 gcctaAGCTAA", 157 "+r004 ATAGCT..............TCAGC", 158 "-r003 ttagctTAGGC", 159 "-r001/2 CAGCGGCAT", 160 "--------------------------------------------------------", 161 }, 162 }, 163 records: []*Record{ 164 { 165 Name: "r001", 166 Pos: 6, 167 MapQ: 30, 168 Cigar: Cigar{ 169 NewCigarOp(CigarMatch, 8), 170 NewCigarOp(CigarInsertion, 2), 171 NewCigarOp(CigarMatch, 4), 172 NewCigarOp(CigarDeletion, 1), 173 NewCigarOp(CigarMatch, 3), 174 }, 175 Flags: Paired | ProperPair | MateReverse | Read1, 176 MatePos: 36, 177 TempLen: 39, 178 Seq: NewSeq([]byte("TTAGATAAAGGATACTG")), 179 Qual: []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 180 }, 181 { 182 Name: "r002", 183 Pos: 8, 184 MapQ: 30, 185 Cigar: Cigar{ 186 NewCigarOp(CigarSoftClipped, 3), 187 NewCigarOp(CigarMatch, 6), 188 NewCigarOp(CigarPadded, 1), 189 NewCigarOp(CigarInsertion, 1), 190 NewCigarOp(CigarMatch, 4), 191 }, 192 MatePos: -1, 193 TempLen: 0, 194 Seq: NewSeq([]byte("AAAAGATAAGGATA")), 195 Qual: []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 196 }, 197 { 198 Name: "r003", 199 Pos: 8, 200 MapQ: 30, 201 Cigar: Cigar{ 202 NewCigarOp(CigarSoftClipped, 5), 203 NewCigarOp(CigarMatch, 6), 204 }, 205 MatePos: -1, 206 TempLen: 0, 207 Seq: NewSeq([]byte("GCCTAAGCTAA")), 208 Qual: []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 209 AuxFields: []Aux{ 210 mustAux(NewAux(NewTag("SA"), "ref,29,-,6H5M,17,0;")), 211 }, 212 }, 213 { 214 Name: "r004", 215 Pos: 15, 216 MapQ: 30, 217 Cigar: Cigar{ 218 NewCigarOp(CigarMatch, 6), 219 NewCigarOp(CigarSkipped, 14), 220 NewCigarOp(CigarMatch, 5), 221 }, 222 MatePos: -1, 223 TempLen: 0, 224 Seq: NewSeq([]byte("ATAGCTTCAGC")), 225 Qual: []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 226 }, 227 { 228 Name: "r003", 229 Pos: 28, 230 MapQ: 17, 231 Cigar: Cigar{ 232 NewCigarOp(CigarHardClipped, 6), 233 NewCigarOp(CigarMatch, 5), 234 }, 235 Flags: Reverse | Supplementary, 236 MatePos: -1, 237 TempLen: 0, 238 Seq: NewSeq([]byte("TAGGC")), 239 Qual: []uint8{0xff, 0xff, 0xff, 0xff, 0xff}, 240 AuxFields: []Aux{ 241 mustAux(NewAux(NewTag("SA"), "ref,9,+,5S6M,30,1;")), 242 }, 243 }, 244 { 245 Name: "r001", 246 Pos: 36, 247 MapQ: 30, 248 Cigar: Cigar{ 249 NewCigarOp(CigarMatch, 9), 250 }, 251 Flags: Paired | ProperPair | Reverse | Read2, 252 MatePos: 6, 253 TempLen: -39, 254 Seq: NewSeq([]byte("CAGCGGCAT")), 255 Qual: []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 256 AuxFields: []Aux{ 257 mustAux(NewAux(NewTag("NM"), uint(1))), 258 }, 259 }, 260 }, 261 cigars: []string{ 262 "8M2I4M1D3M", 263 "3S6M1P1I4M", 264 "5S6M", 265 "6M14N5M", 266 "6H5M", 267 "9M", 268 }, 269 // These coordinates are all open (and zero-based) so that 270 // a slice of the reference doesn't need any alteration. 271 readEnds: []int{ 272 22, 273 18, 274 14, 275 40, 276 33, 277 45, 278 }, 279 } 280 281 var endTests = []struct { 282 cigar Cigar 283 end int 284 }{ 285 { 286 cigar: Cigar{ 287 NewCigarOp(CigarMatch, 20), 288 NewCigarOp(CigarBack, 5), 289 NewCigarOp(CigarMatch, 20), 290 }, 291 end: 35, 292 }, 293 { 294 cigar: Cigar{ 295 NewCigarOp(CigarMatch, 10), 296 NewCigarOp(CigarBack, 3), 297 NewCigarOp(CigarMatch, 11), 298 }, 299 end: 18, 300 }, 301 { 302 cigar: Cigar{ 303 NewCigarOp(CigarHardClipped, 10), 304 NewCigarOp(CigarBack, 3), 305 }, 306 end: 0, 307 }, 308 { 309 cigar: Cigar{ 310 NewCigarOp(CigarMatch, 3), 311 NewCigarOp(CigarHardClipped, 10), 312 }, 313 end: 3, 314 }, 315 { 316 cigar: Cigar{ 317 NewCigarOp(CigarMatch, 3), 318 NewCigarOp(CigarSkipped, 10), 319 }, 320 end: 13, 321 }, 322 { 323 cigar: Cigar{ 324 NewCigarOp(CigarSkipped, 10), 325 NewCigarOp(CigarMatch, 3), 326 }, 327 end: 13, 328 }, 329 { 330 cigar: Cigar{ 331 NewCigarOp(CigarMatch, 3), 332 NewCigarOp(CigarSoftClipped, 10), 333 NewCigarOp(CigarHardClipped, 10), 334 }, 335 end: 3, 336 }, 337 { 338 cigar: Cigar{ 339 NewCigarOp(CigarBack, 10), 340 NewCigarOp(CigarSkipped, 10), 341 NewCigarOp(CigarBack, 10), 342 NewCigarOp(CigarSkipped, 10), 343 NewCigarOp(CigarMatch, 3), 344 }, 345 end: 3, 346 }, 347 { 348 cigar: Cigar{ 349 NewCigarOp(CigarBack, 10), 350 NewCigarOp(CigarSkipped, 10), 351 NewCigarOp(CigarBack, 5), 352 NewCigarOp(CigarSkipped, 10), 353 NewCigarOp(CigarMatch, 3), 354 }, 355 end: 8, 356 }, 357 } 358 359 func (s *S) TestEnd(c *check.C) { 360 for _, test := range endTests { 361 c.Check((&Record{Cigar: test.cigar}).End(), check.Equals, test.end) 362 } 363 } 364 365 var cigarTests = []struct { 366 cigar Cigar 367 length int 368 valid bool 369 }{ 370 { 371 cigar: nil, 372 length: 0, 373 valid: true, 374 }, 375 376 // One thought is that if B is really intended only to provide the ability 377 // to store CG and similar data where the read "skips" back a few bases now 378 // and again vs. the reference one thing that would make this much easier 379 // on those parsing SAM/BAM would be to limit the use of the B operator so 380 // that it cannot skip backwards past the beginning of the read. 381 // 382 // So something like 20M5B20M would be valid, but 50M5000B20M would not be. 383 // 384 // http://sourceforge.net/p/samtools/mailman/message/28466477/ 385 { // 20M5B20M 386 cigar: Cigar{ 387 NewCigarOp(CigarMatch, 20), 388 NewCigarOp(CigarBack, 5), 389 NewCigarOp(CigarMatch, 20), 390 }, 391 length: 40, 392 valid: true, 393 }, 394 { // 50M5000B20M 395 cigar: Cigar{ 396 NewCigarOp(CigarMatch, 50), 397 NewCigarOp(CigarBack, 5000), 398 NewCigarOp(CigarMatch, 20), 399 }, 400 length: 70, 401 valid: false, 402 }, 403 404 // LH's example at http://sourceforge.net/p/samtools/mailman/message/28463294/ 405 { // 10M3B11M 406 // REF:: GCATACGATCGACTAGTCACGT 407 // READ: --ATACGATCGA---------- 408 // READ: ---------CGACTAGTCAC-- 409 cigar: Cigar{ 410 NewCigarOp(CigarMatch, 10), 411 NewCigarOp(CigarBack, 3), 412 NewCigarOp(CigarMatch, 11), 413 }, 414 length: 21, 415 valid: true, 416 }, 417 418 { 419 cigar: Cigar{ 420 NewCigarOp(CigarHardClipped, 10), 421 NewCigarOp(CigarBack, 3), 422 NewCigarOp(CigarMatch, 11), 423 }, 424 length: 11, 425 valid: false, 426 }, 427 { 428 cigar: Cigar{ 429 NewCigarOp(CigarHardClipped, 10), 430 NewCigarOp(CigarBack, 3), 431 }, 432 length: 0, 433 valid: true, 434 }, 435 { 436 cigar: Cigar{ 437 NewCigarOp(CigarMatch, 3), 438 NewCigarOp(CigarHardClipped, 10), 439 }, 440 length: 3, 441 valid: true, 442 }, 443 { 444 cigar: Cigar{ 445 NewCigarOp(CigarMatch, 3), 446 NewCigarOp(CigarHardClipped, 10), 447 NewCigarOp(CigarHardClipped, 10), 448 }, 449 length: 3, 450 valid: false, 451 }, 452 { 453 cigar: Cigar{ 454 NewCigarOp(CigarMatch, 3), 455 NewCigarOp(CigarHardClipped, 10), 456 NewCigarOp(CigarSoftClipped, 10), 457 }, 458 length: 13, 459 valid: false, 460 }, 461 { 462 cigar: Cigar{ 463 NewCigarOp(CigarMatch, 3), 464 NewCigarOp(CigarSoftClipped, 10), 465 NewCigarOp(CigarHardClipped, 10), 466 }, 467 length: 13, 468 valid: true, 469 }, 470 471 // Stupid, but not reason not to be valid. We only care if the 472 // there is a base from the query being used left of the start. 473 { 474 cigar: Cigar{ 475 NewCigarOp(CigarBack, 10), 476 NewCigarOp(CigarSkipped, 10), 477 NewCigarOp(CigarBack, 10), 478 NewCigarOp(CigarSkipped, 10), 479 NewCigarOp(CigarMatch, 3), 480 }, 481 length: 3, 482 valid: true, 483 }, 484 } 485 486 func (s *S) TestCigarIsValid(c *check.C) { 487 for _, test := range cigarTests { 488 c.Check(test.cigar.IsValid(test.length), check.Equals, test.valid) 489 } 490 } 491 492 func (s *S) TestNoHeader(c *check.C) { 493 sam := []byte(`r001 99 ref 7 30 8M2I4M1D3M = 37 39 TTAGATAAAGGATACTG * 494 r002 0 ref 9 30 3S6M1P1I4M * 0 0 AAAAGATAAGGATA * 495 r003 0 ref 9 30 5S6M * 0 0 GCCTAAGCTAA * SA:Z:ref,29,-,6H5M,17,0; 496 r004 0 ref 16 30 6M14N5M * 0 0 ATAGCTTCAGC * 497 r003 2064 ref 29 17 6H5M * 0 0 TAGGC * SA:Z:ref,9,+,5S6M,30,1; 498 r001 147 ref 37 30 9M = 7 -39 CAGCGGCAT * NM:i:1 499 `) 500 501 sr, err := NewReader(bytes.NewReader(sam)) 502 c.Assert(err, check.Equals, nil) 503 h := sr.Header() 504 c.Check(h.Version, check.Equals, "") 505 c.Check(h.SortOrder, check.Equals, UnknownOrder) 506 c.Check(h.GroupOrder, check.Equals, GroupUnspecified) 507 c.Check(h.Comments, check.DeepEquals, []string(nil)) 508 for { 509 _, err := sr.Read() 510 if err != nil { 511 break 512 } 513 } 514 refs := sr.Header().Refs() 515 c.Assert(len(refs), check.Equals, 1) 516 c.Check(refs[0].String(), check.Equals, "@SQ\tSN:ref\tLN:0") 517 } 518 519 func (s *S) TestIterator(c *check.C) { 520 sam := [][]byte{ 521 []byte(`r001 99 ref 7 30 8M2I4M1D3M = 37 39 TTAGATAAAGGATACTG * 522 r002 0 ref 9 30 3S6M1P1I4M * 0 0 AAAAGATAAGGATA * 523 r003 0 ref 9 30 5S6M * 0 0 GCCTAAGCTAA * SA:Z:ref,29,-,6H5M,17,0; 524 r004 0 ref 16 30 6M14N5M * 0 0 ATAGCTTCAGC * 525 r003 2064 ref 29 17 6H5M * 0 0 TAGGC * SA:Z:ref,9,+,5S6M,30,1; 526 r001 147 ref 37 30 9M = 7 -39 CAGCGGCAT * NM:i:1 527 r005 4 ref 37 0 * = 0 0 CAGCGGCAT * 528 `), 529 []byte(`@HD VN:1.5 SO:coordinate 530 @SQ SN:ref LN:45 531 @CO -------------------------------------------------------- 532 @CO Coor 12345678901234 5678901234567890123456789012345 533 @CO ref AGCATGTTAGATAA**GATAGCTGTGCTAGTAGGCAGTCAGCGCCAT 534 @CO -------------------------------------------------------- 535 @CO +r001/1 TTAGATAAAGGATA*CTG 536 @CO +r002 aaaAGATAA*GGATA 537 @CO +r003 gcctaAGCTAA 538 @CO +r004 ATAGCT..............TCAGC 539 @CO -r003 ttagctTAGGC 540 @CO -r001/2 CAGCGGCAT 541 @CO -r005 <unmapped> 542 @CO -------------------------------------------------------- 543 r001 99 ref 7 30 8M2I4M1D3M = 37 39 TTAGATAAAGGATACTG * 544 r002 0 ref 9 30 3S6M1P1I4M * 0 0 AAAAGATAAGGATA * 545 r003 0 ref 9 30 5S6M * 0 0 GCCTAAGCTAA * SA:Z:ref,29,-,6H5M,17,0; 546 r004 0 ref 16 30 6M14N5M * 0 0 ATAGCTTCAGC * 547 r003 2064 ref 29 17 6H5M * 0 0 TAGGC * SA:Z:ref,9,+,5S6M,30,1; 548 r001 147 ref 37 30 9M = 7 -39 CAGCGGCAT * NM:i:1 549 r005 4 ref 37 0 * = 0 0 CAGCGGCAT * 550 `), 551 } 552 553 for _, s := range sam { 554 sr, err := NewReader(bytes.NewReader(s)) 555 c.Assert(err, check.Equals, nil) 556 i := NewIterator(sr) 557 var n int 558 for i.Next() { 559 n++ 560 } 561 c.Check(i.Error(), check.Equals, nil) 562 c.Check(n, check.Equals, 7) 563 } 564 } 565 566 var auxTests = []struct { 567 sam string 568 569 want []*Record 570 }{ 571 { 572 sam: `1f001i8gk#GGCG#AA 0 * 0 0 * * 0 0 * * NH:i:2 HI:i:1 AS:i:13 nM:i:4 NM:i:4 MD:Z:2C0T2T1C13 jM:B:c,-1 jI:B:i,-1 573 1f001i8gk#GGCG#AA 0 * 0 0 * * 0 0 * * NH:i:2 HI:i:2 AS:i:12 nM:i:0 NM:i:0 MD:Z:22 jM:B:c,0 jI:B:i,629,1095 fT:f:3.14 574 1f001i8gk#GGCG#AA 0 * 0 0 * * 0 0 * * NE:i:-100 MN:i:-1000 575 `, 576 want: []*Record{ 577 { 578 Name: "1f001i8gk#GGCG#AA", 579 Pos: -1, 580 MatePos: -1, 581 AuxFields: AuxFields{ 582 { 583 0x4e, 0x48, 0x43, 0x02, // |NHC.| 584 }, 585 { 586 0x48, 0x49, 0x43, 0x01, // |HIC.| 587 }, 588 { 589 0x41, 0x53, 0x43, 0x0d, // |ASC.| 590 }, 591 { 592 0x6e, 0x4d, 0x43, 0x04, // |nMC.| 593 }, 594 { 595 0x4e, 0x4d, 0x43, 0x04, // |NMC.| 596 }, 597 { 598 0x4d, 0x44, 0x5a, 0x32, 0x43, 0x30, 0x54, 0x32, 0x54, 0x31, 0x43, 0x31, 0x33, // |MDZ2C0T2T1C13| 599 }, 600 { 601 0x6a, 0x4d, 0x42, 0x63, 0x01, 0x00, 0x00, 0x00, 0xff, // |jMBc.....| 602 }, 603 { 604 0x6a, 0x49, 0x42, 0x69, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, // |jIBi........| 605 }, 606 }, 607 }, 608 { 609 Name: "1f001i8gk#GGCG#AA", 610 Pos: -1, 611 MatePos: -1, 612 AuxFields: AuxFields{ 613 { 614 0x4e, 0x48, 0x43, 0x02, // |NHC.| 615 }, 616 { 617 0x48, 0x49, 0x43, 0x02, // |HIC.| 618 }, 619 { 620 0x41, 0x53, 0x43, 0x0c, // |ASC.| 621 }, 622 { 623 0x6e, 0x4d, 0x43, 0x00, // |nMC.| 624 }, 625 { 626 0x4e, 0x4d, 0x43, 0x00, // |NMC.| 627 }, 628 { 629 0x4d, 0x44, 0x5a, 0x32, 0x32, // |MDZ22| 630 }, 631 { 632 0x6a, 0x4d, 0x42, 0x63, 0x01, 0x00, 0x00, 0x00, 0x00, // |jMBc.....| 633 }, 634 { 635 0x6a, 0x49, 0x42, 0x69, 0x02, 0x00, 0x00, 0x00, 0x75, 0x02, 0x00, 0x00, 0x47, 0x04, 0x00, 0x00, // |jIBi....u...G...| 636 }, 637 { 638 0x66, 0x54, 0x66, 0xc3, 0xf5, 0x48, 0x40, // |fTf..H@| 639 }, 640 }, 641 }, 642 { 643 Name: "1f001i8gk#GGCG#AA", 644 Pos: -1, 645 MatePos: -1, 646 AuxFields: AuxFields{ 647 { 648 0x4e, 0x45, 0x63, 0x9c, // |NEc.| 649 }, 650 { 651 0x4d, 0x4e, 0x73, 0x18, 0xfc, // |MNs..| 652 }, 653 }, 654 }, 655 }, 656 }, 657 } 658 659 func (s *S) TestAux(c *check.C) { 660 for _, test := range auxTests { 661 sr, err := NewReader(strings.NewReader(test.sam)) 662 c.Assert(err, check.Equals, nil) 663 var recs []*Record 664 for { 665 r, err := sr.Read() 666 if err != nil { 667 c.Assert(err, check.Equals, io.EOF) 668 break 669 } 670 recs = append(recs, r) 671 } 672 c.Check(recs, check.DeepEquals, test.want) 673 } 674 } 675 676 func (s *S) TestIssue26(c *check.C) { 677 fuTag := NewTag("fu") 678 679 var issue26 = struct { 680 data []byte 681 header Header 682 ref Reference 683 rg ReadGroup 684 prog Program 685 }{ 686 // This is a Pacific Biosciences header line. The SO is invalid. 687 data: []byte(`@HD VN:1.5 SO:UNKNOWN pb:3.0b7 688 @SQ SN:ref LN:45 fu:bar 689 @RG ID:group fu:bar 690 @PG ID:program fu:bar 691 `), 692 header: Header{ 693 Version: "1.5", 694 SortOrder: UnknownOrder, 695 GroupOrder: GroupUnspecified, 696 }, 697 ref: Reference{ 698 id: -1, 699 name: "ref", 700 lRef: 45, 701 otherTags: []tagPair{{tag: fuTag, value: "bar"}}, 702 }, 703 rg: ReadGroup{ 704 id: -1, 705 name: "group", 706 otherTags: []tagPair{{tag: fuTag, value: "bar"}}, 707 }, 708 prog: Program{ 709 id: -1, 710 uid: "program", 711 otherTags: []tagPair{{tag: fuTag, value: "bar"}}, 712 }, 713 } 714 715 sr, err := NewReader(bytes.NewReader(issue26.data)) 716 c.Assert(err, check.Equals, nil) 717 h := sr.Header() 718 719 c.Check(h.Version, check.Equals, issue26.header.Version) 720 c.Check(h.SortOrder, check.Equals, issue26.header.SortOrder) 721 c.Check(h.GroupOrder, check.Equals, issue26.header.GroupOrder) 722 c.Assert(len(h.Refs()), check.Equals, 1) 723 ref := h.Refs()[0].Clone() 724 c.Check(equalRefs(ref, &issue26.ref), check.Equals, true) 725 c.Check(ref.Get(refNameTag), check.Equals, "ref") 726 c.Check(ref.Get(refLengthTag), check.Equals, "45") 727 c.Check(ref.Get(fuTag), check.Equals, "bar") 728 c.Assert(len(h.RGs()), check.Equals, 1) 729 rg := h.RGs()[0].Clone() 730 c.Check(*rg, check.DeepEquals, issue26.rg) 731 c.Check(rg.Get(idTag), check.Equals, "group") 732 c.Check(rg.Get(fuTag), check.Equals, "bar") 733 c.Assert(len(h.Progs()), check.Equals, 1) 734 prog := h.Progs()[0].Clone() 735 c.Check(*prog, check.DeepEquals, issue26.prog) 736 c.Check(prog.Get(idTag), check.Equals, "program") 737 c.Check(prog.Get(fuTag), check.Equals, "bar") 738 } 739 740 func (s *S) TestParseISO8601(c *check.C) { 741 for _, test := range []struct { 742 value string 743 want time.Time 744 }{ 745 {value: "2017-05-10", want: time.Date(2017, 05, 10, 0, 0, 0, 0, time.Local)}, 746 {value: "2017-05-10T21:02:29", want: time.Date(2017, 05, 10, 21, 02, 29, 0, time.Local)}, 747 {value: "2017-05-10T21:02:29Z", want: time.Date(2017, 05, 10, 21, 02, 29, 0, time.UTC)}, 748 {value: "2017-05-10T21:02:29+0900", want: time.Date(2017, 05, 10, 21, 02, 29, 0, time.FixedZone("0900", 9*3600))}, 749 {value: "2010-10-19T00:00:00.000+00:00", want: time.Date(2010, 10, 19, 0, 0, 0, 0, time.FixedZone("0000", 0))}, 750 } { 751 date, err := parseISO8601(test.value) 752 c.Check(err, check.Equals, nil) 753 c.Check(date.Equal(test.want), check.Equals, true) 754 } 755 } 756 757 var cigTests = []struct { 758 cig []byte 759 ref int 760 read int 761 }{ 762 {[]byte("151M"), 151, 151}, 763 {[]byte("10S10M"), 10, 20}, 764 {[]byte("11H11M"), 11, 11}, 765 {[]byte("11H1D11M"), 12, 11}, 766 {[]byte("5M21N5M"), 31, 10}, 767 {[]byte("21N"), 21, 0}, 768 {[]byte("0M1I1D"), 1, 1}, 769 {[]byte("1000000000M"), 1e9, 1e9}, 770 {[]byte("1000000000000M"), 1e12, 1e12}, 771 } 772 773 func (s *S) TestLengths(c *check.C) { 774 for _, ct := range cigTests { 775 cig, err := ParseCigar(ct.cig) 776 c.Check(err, check.IsNil) 777 ref, read := cig.Lengths() 778 c.Check(ref, check.Equals, ct.ref) 779 c.Check(read, check.Equals, ct.read) 780 } 781 } 782 783 func (s *S) TestIssue32(c *check.C) { 784 sam := []byte(`@HD VN:1.5 SO:coordinate 785 @SQ SN:name LN:1 786 @RG ID:name 787 @PG ID:name 788 `) 789 790 r, err := NewReader(bytes.NewReader(sam)) 791 c.Check(err, check.Equals, nil) 792 h := r.Header() 793 c.Assert(len(h.Refs()), check.Equals, 1) 794 c.Check(h.Refs()[0].Name(), check.Equals, "name") 795 c.Assert(len(h.RGs()), check.Equals, 1) 796 c.Check(h.RGs()[0].Name(), check.Equals, "name") 797 c.Assert(len(h.Progs()), check.Equals, 1) 798 c.Check(h.Progs()[0].UID(), check.Equals, "name") 799 } 800 801 func (s *S) TestEqualRefs(c *check.C) { 802 a, err := NewReference("aaa", "assem", "species", 1234, nil, nil) 803 c.Assert(err, check.IsNil) 804 b := a.Clone() 805 c.Assert(b.Set(Tag{'T', 'A'}, "xxx"), check.IsNil) 806 807 c.Assert(equalRefs(a, b), check.Equals, false) 808 c.Assert(equalRefs(b, a), check.Equals, false) 809 } 810 811 func (s *S) TestAddClonedRef(c *check.C) { 812 sr, err := NewReader(bytes.NewReader(specExamples.data)) 813 c.Assert(err, check.Equals, nil) 814 h := sr.Header() 815 ref := h.Refs()[0].Clone() 816 c.Check(h.AddReference(ref), check.Equals, nil) 817 } 818 819 func (s *S) TestRenames(c *check.C) { 820 sam := []byte(`@HD VN:1.5 SO:coordinate 821 @SQ SN:name LN:1 822 @SQ SN:taken LN:1 823 @RG ID:name 824 @RG ID:taken 825 @PG ID:name 826 @PG ID:taken 827 `) 828 829 r, err := NewReader(bytes.NewReader(sam)) 830 c.Check(err, check.Equals, nil) 831 h := r.Header() 832 c.Assert(len(h.Refs()), check.Equals, 2) 833 c.Assert(h.Refs()[0].SetName("reference"), check.Equals, nil) 834 c.Check(h.Refs()[0].Name(), check.Equals, "reference") 835 c.Check(h.Refs()[0].SetName("taken"), check.DeepEquals, errors.New("sam: name exists")) 836 837 c.Assert(len(h.RGs()), check.Equals, 2) 838 c.Assert(h.RGs()[0].SetName("read group"), check.Equals, nil) 839 c.Check(h.RGs()[0].Name(), check.Equals, "read group") 840 c.Check(h.RGs()[0].SetName("taken"), check.DeepEquals, errors.New("sam: name exists")) 841 842 c.Assert(len(h.Progs()), check.Equals, 2) 843 c.Assert(h.Progs()[0].SetUID("program"), check.Equals, nil) 844 c.Check(h.Progs()[0].UID(), check.Equals, "program") 845 c.Check(h.Progs()[0].SetUID("taken"), check.DeepEquals, errors.New("sam: uid exists")) 846 847 ref, err := NewReference("ref", "", "", 1, nil, nil) 848 c.Assert(err, check.Equals, nil) 849 c.Assert(ref.SetName("new ref"), check.Equals, nil) 850 851 rg, err := NewReadGroup("rg", "", "", "", "", "", "", "", "", "", time.Time{}, 0) 852 c.Assert(err, check.Equals, nil) 853 c.Assert(rg.SetName("new rg"), check.Equals, nil) 854 855 prog := NewProgram("prog", "", "", "", "") 856 c.Assert(prog.SetUID("new prog"), check.Equals, nil) 857 } 858 859 func (s *S) TestSort(c *check.C) { 860 sr, err := NewReader(bytes.NewReader(specExamples.data)) 861 c.Assert(err, check.Equals, nil) 862 i := NewIterator(sr) 863 var recs []*Record 864 for i.Next() { 865 recs = append(recs, i.Record()) 866 } 867 c.Assert(i.Error(), check.Equals, nil) 868 recs = append(recs, &Record{Name: "000", Ref: nil, Pos: -1}) 869 870 wantPos := []int{6, 8, 8, 15, 28, 36, -1} 871 sort.Sort(byCoordinate(recs)) 872 for i, r := range recs { 873 c.Check(r.Pos, check.Equals, wantPos[i]) 874 } 875 876 wantName := []string{"000", "r001", "r001", "r002", "r003", "r003", "r004"} 877 sort.Sort(byName(recs)) 878 for i, r := range recs { 879 c.Check(r.Name, check.Equals, wantName[i]) 880 } 881 } 882 883 type byName []*Record 884 885 func (r byName) Len() int { return len(r) } 886 func (r byName) Less(i, j int) bool { return r[i].LessByName(r[j]) } 887 func (r byName) Swap(i, j int) { r[i], r[j] = r[j], r[i] } 888 889 type byCoordinate []*Record 890 891 func (r byCoordinate) Len() int { return len(r) } 892 func (r byCoordinate) Less(i, j int) bool { return r[i].LessByCoordinate(r[j]) } 893 func (r byCoordinate) Swap(i, j int) { r[i], r[j] = r[j], r[i] } 894 895 func (s *S) TestRemoveReference(c *check.C) { 896 h := headerHG00096_1000.Clone() 897 h.RemoveReference(h.Refs()[2]) 898 c.Check(len(h.Refs()), check.Equals, len(headerHG00096_1000.Refs())-1) 899 c.Check(fmt.Sprint(h.Refs()[1]), check.Equals, fmt.Sprint(headerHG00096_1000.Refs()[1])) 900 c.Check(fmt.Sprint(h.Refs()[2]), check.Equals, fmt.Sprint(headerHG00096_1000.Refs()[3])) 901 r := headerHG00096_1000.Refs()[2].Clone() 902 c.Check(h.AddReference(r), check.Equals, nil) 903 c.Check(len(h.Refs()), check.Equals, len(headerHG00096_1000.Refs())) 904 } 905 906 func (s *S) TestRemoveReadGroup(c *check.C) { 907 h := headerHG00096_1000.Clone() 908 h.RemoveReadGroup(h.RGs()[1]) 909 c.Check(len(h.RGs()), check.Equals, len(headerHG00096_1000.RGs())-1) 910 c.Check(fmt.Sprint(h.RGs()[0]), check.Equals, fmt.Sprint(headerHG00096_1000.RGs()[0])) 911 c.Check(fmt.Sprint(h.RGs()[1]), check.Equals, fmt.Sprint(headerHG00096_1000.RGs()[2])) 912 r := headerHG00096_1000.RGs()[1].Clone() 913 c.Check(h.AddReadGroup(r), check.Equals, nil) 914 c.Check(len(h.RGs()), check.Equals, len(headerHG00096_1000.RGs())) 915 } 916 917 func (s *S) TestRemoveProgram(c *check.C) { 918 h := headerHG00096_1000.Clone() 919 h.RemoveProgram(h.Progs()[2]) 920 c.Check(len(h.Progs()), check.Equals, len(headerHG00096_1000.Progs())-1) 921 c.Check(fmt.Sprint(h.Progs()[1]), check.Equals, fmt.Sprint(headerHG00096_1000.Progs()[1])) 922 c.Check(fmt.Sprint(h.Progs()[2]), check.Equals, fmt.Sprint(headerHG00096_1000.Progs()[3])) 923 p := headerHG00096_1000.Progs()[2].Clone() 924 c.Check(h.AddProgram(p), check.Equals, nil) 925 c.Check(len(h.Progs()), check.Equals, len(headerHG00096_1000.Progs())) 926 } 927 928 func (s *S) TestMergeHeaders(c *check.C) { 929 tests := []struct { 930 src []*Header 931 want *Header 932 links [][]*Reference 933 err error 934 }{ 935 {}, 936 { 937 src: []*Header{{}}, 938 links: nil, 939 want: &Header{}, 940 err: nil, 941 }, 942 { 943 src: []*Header{ 944 {refs: []*Reference{{id: 0, name: "ref", lRef: 45}}, seenRefs: set{"ref": 0}, seenGroups: set{}, seenProgs: set{}}, 945 {refs: []*Reference{{id: 0, name: "ref", lRef: 45}}, seenRefs: set{"ref": 0}, seenGroups: set{}, seenProgs: set{}}, 946 }, 947 links: [][]*Reference{ 948 {{id: 0, name: "ref", lRef: 45}}, 949 {{id: 0, name: "ref", lRef: 45}}, 950 }, 951 want: &Header{ 952 refs: []*Reference{{id: 0, name: "ref", lRef: 45}}, 953 seenRefs: set{"ref": 0}, 954 seenGroups: set{}, 955 seenProgs: set{}, 956 }, 957 err: nil, 958 }, 959 { 960 src: []*Header{ 961 {refs: []*Reference{{id: 0, name: "refa", lRef: 45}}, seenRefs: set{"refa": 0}, seenGroups: set{}, seenProgs: set{}}, 962 {refs: []*Reference{{id: 0, name: "refb", lRef: 45}}, seenRefs: set{"refb": 0}, seenGroups: set{}, seenProgs: set{}}, 963 }, 964 links: [][]*Reference{ 965 {{id: 0, name: "refa", lRef: 45}}, 966 {{id: 1, name: "refb", lRef: 45}}, 967 }, 968 want: &Header{ 969 refs: []*Reference{ 970 {id: 0, name: "refa", lRef: 45}, 971 {id: 1, name: "refb", lRef: 45}, 972 }, 973 seenRefs: set{"refa": 0, "refb": 1}, 974 seenGroups: set{}, 975 seenProgs: set{}, 976 }, 977 err: nil, 978 }, 979 { 980 src: []*Header{ 981 {refs: []*Reference{{id: 0, name: "ref", lRef: 45}}, seenRefs: set{"ref": 0}, seenGroups: set{}, seenProgs: set{}}, 982 {refs: []*Reference{{id: 0, name: "ref", lRef: 44}}, seenRefs: set{"ref": 0}, seenGroups: set{}, seenProgs: set{}}, 983 }, 984 links: nil, 985 want: nil, 986 err: errors.New("sam: duplicate reference name"), 987 }, 988 } 989 for _, test := range tests[3:] { 990 // Prepare the internal links that cannot be expressed statically. 991 if len(test.src) != 0 { 992 for _, r := range test.src[0].refs { 993 r.owner = test.src[0] 994 } 995 } 996 for _, in := range test.links { 997 for _, ref := range in { 998 ref.owner = test.want 999 } 1000 } 1001 if test.want != nil { 1002 for _, r := range test.want.refs { 1003 r.owner = test.want 1004 } 1005 } 1006 1007 // Set up for identical input case. 1008 var identical bool 1009 if len(test.src) == 2 { 1010 identical = reflect.DeepEqual(test.src[0], test.src[1]) 1011 } 1012 1013 got, links, err := MergeHeaders(test.src) 1014 c.Check(err, check.DeepEquals, test.err) 1015 if err != nil { 1016 continue 1017 } 1018 c.Check(got, check.DeepEquals, test.want) 1019 c.Check(links, check.DeepEquals, test.links) 1020 if identical { 1021 c.Check(links[0][0], check.Equals, links[1][0]) 1022 } 1023 } 1024 } 1025 1026 func newAux(tag Tag, value interface{}) Aux { 1027 aux, err := NewAux(tag, value) 1028 if err != nil { 1029 panic(err) 1030 } 1031 return aux 1032 } 1033 1034 func (s *S) TestBagID(c *check.C) { 1035 for _, test := range []struct { 1036 r *Record 1037 id int64 1038 expectErr bool 1039 }{ 1040 { 1041 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagIDTag, 33)}}, 1042 33, 1043 false, 1044 }, 1045 { 1046 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagIDTag, "34")}}, 1047 34, 1048 false, 1049 }, 1050 { 1051 &Record{Name: "foo", Ref: nil, Pos: 123}, 1052 -1, 1053 false, 1054 }, 1055 { 1056 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagIDTag, -4)}}, 1057 -1, 1058 true, 1059 }, 1060 { 1061 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagIDTag, "abc")}}, 1062 -1, 1063 true, 1064 }, 1065 { 1066 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagIDTag, 33), newAux(bagIDTag, 34)}}, 1067 -1, 1068 true, 1069 }, 1070 } { 1071 1072 id, err := test.r.BagID() 1073 c.Check(id, check.Equals, test.id) 1074 if test.expectErr { 1075 c.Check(err, check.Not(check.Equals), nil) 1076 } else { 1077 c.Check(err, check.Equals, nil) 1078 } 1079 } 1080 } 1081 1082 func (s *S) TestBagSize(c *check.C) { 1083 for _, test := range []struct { 1084 r *Record 1085 id int 1086 expectErr bool 1087 }{ 1088 { 1089 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagSizeTag, 33)}}, 1090 33, 1091 false, 1092 }, 1093 { 1094 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagSizeTag, "34")}}, 1095 -1, 1096 true, 1097 }, 1098 { 1099 &Record{Name: "foo", Ref: nil, Pos: 123}, 1100 -1, 1101 false, 1102 }, 1103 { 1104 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagSizeTag, -12)}}, 1105 -1, 1106 true, 1107 }, 1108 { 1109 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagSizeTag, "abc")}}, 1110 -1, 1111 true, 1112 }, 1113 { 1114 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(bagSizeTag, 33), newAux(bagSizeTag, 34)}}, 1115 -1, 1116 true, 1117 }, 1118 } { 1119 1120 id, err := test.r.BagSize() 1121 c.Check(id, check.Equals, test.id) 1122 if test.expectErr { 1123 c.Check(err, check.Not(check.Equals), nil) 1124 } else { 1125 c.Check(err, check.Equals, nil) 1126 } 1127 } 1128 } 1129 1130 func (s *S) TestDupType(c *check.C) { 1131 for _, test := range []struct { 1132 r *Record 1133 dup DupType 1134 expectErr bool 1135 }{ 1136 { 1137 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(dupTypeTag, "SQ")}}, 1138 DupTypeSQ, 1139 false, 1140 }, 1141 { 1142 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(dupTypeTag, "LB")}}, 1143 DupTypeLB, 1144 false, 1145 }, 1146 { 1147 &Record{Name: "foo", Ref: nil, Pos: 123}, 1148 DupTypeNone, 1149 false, 1150 }, 1151 { 1152 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(dupTypeTag, "abc")}}, 1153 DupTypeNone, 1154 true, 1155 }, 1156 { 1157 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(dupTypeTag, "SQ"), newAux(dupTypeTag, "SQ")}}, 1158 DupTypeNone, 1159 true, 1160 }, 1161 } { 1162 1163 dup, err := test.r.DupType() 1164 c.Check(dup, check.Equals, test.dup) 1165 if test.expectErr { 1166 c.Check(err, check.Not(check.Equals), nil) 1167 } else { 1168 c.Check(err, check.Equals, nil) 1169 } 1170 } 1171 } 1172 1173 func (s *S) TestLibraryBagSize(c *check.C) { 1174 for _, test := range []struct { 1175 r *Record 1176 expected int 1177 expectErr bool 1178 }{ 1179 { 1180 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(libraryBagSizeTag, 3)}}, 1181 3, 1182 false, 1183 }, 1184 { 1185 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(libraryBagSizeTag, 1)}}, 1186 1, 1187 false, 1188 }, 1189 { 1190 &Record{Name: "foo", Ref: nil, Pos: 123}, 1191 -1, 1192 false, 1193 }, 1194 { 1195 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(libraryBagSizeTag, "abc")}}, 1196 -1, 1197 true, 1198 }, 1199 { 1200 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(libraryBagSizeTag, -4)}}, 1201 -1, 1202 true, 1203 }, 1204 } { 1205 1206 libraryBagSize, err := test.r.LibraryBagSize() 1207 c.Check(libraryBagSize, check.Equals, test.expected) 1208 if test.expectErr { 1209 c.Check(err, check.Not(check.Equals), nil) 1210 } else { 1211 c.Check(err, check.Equals, nil) 1212 } 1213 } 1214 } 1215 1216 func (s *S) TestLinearDup(c *check.C) { 1217 for _, test := range []struct { 1218 r *Record 1219 linearDup LinearDupState 1220 expectErr bool 1221 }{ 1222 { 1223 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearDupTag, "primary")}}, 1224 LinearPrimary, 1225 false, 1226 }, 1227 { 1228 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearDupTag, "duplicate")}}, 1229 LinearDuplicate, 1230 false, 1231 }, 1232 { 1233 &Record{Name: "foo", Ref: nil, Pos: 123}, 1234 LinearNone, 1235 false, 1236 }, 1237 { 1238 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearDupTag, "abc")}}, 1239 LinearNone, 1240 true, 1241 }, 1242 { 1243 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{ 1244 newAux(linearDupTag, "primary"), 1245 newAux(linearDupTag, "duplicate"), 1246 }}, 1247 LinearNone, 1248 true, 1249 }, 1250 } { 1251 1252 linearDup, err := test.r.LinearDup() 1253 c.Check(linearDup, check.Equals, test.linearDup) 1254 if test.expectErr { 1255 c.Check(err, check.Not(check.Equals), nil) 1256 } else { 1257 c.Check(err, check.Equals, nil) 1258 } 1259 } 1260 } 1261 1262 func (s *S) TestLinearBagID(c *check.C) { 1263 for _, test := range []struct { 1264 r *Record 1265 id int64 1266 expectErr bool 1267 }{ 1268 { 1269 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagIDTag, 33)}}, 1270 33, 1271 false, 1272 }, 1273 { 1274 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagIDTag, "34")}}, 1275 34, 1276 false, 1277 }, 1278 { 1279 &Record{Name: "foo", Ref: nil, Pos: 123}, 1280 -1, 1281 false, 1282 }, 1283 { 1284 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagIDTag, -4)}}, 1285 -1, 1286 true, 1287 }, 1288 { 1289 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagIDTag, "abc")}}, 1290 -1, 1291 true, 1292 }, 1293 { 1294 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagIDTag, 33), newAux(linearBagIDTag, 34)}}, 1295 -1, 1296 true, 1297 }, 1298 } { 1299 1300 id, err := test.r.LinearBagID() 1301 c.Check(id, check.Equals, test.id) 1302 if test.expectErr { 1303 c.Check(err, check.Not(check.Equals), nil) 1304 } else { 1305 c.Check(err, check.Equals, nil) 1306 } 1307 } 1308 } 1309 1310 func (s *S) TestLinearBagSize(c *check.C) { 1311 for _, test := range []struct { 1312 r *Record 1313 id int 1314 expectErr bool 1315 }{ 1316 { 1317 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagSizeTag, 33)}}, 1318 33, 1319 false, 1320 }, 1321 { 1322 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagSizeTag, "34")}}, 1323 -1, 1324 true, 1325 }, 1326 { 1327 &Record{Name: "foo", Ref: nil, Pos: 123}, 1328 -1, 1329 false, 1330 }, 1331 { 1332 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagSizeTag, -12)}}, 1333 -1, 1334 true, 1335 }, 1336 { 1337 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagSizeTag, "abc")}}, 1338 -1, 1339 true, 1340 }, 1341 { 1342 &Record{Name: "foo", Ref: nil, Pos: 123, AuxFields: AuxFields{newAux(linearBagSizeTag, 33), newAux(linearBagSizeTag, 34)}}, 1343 -1, 1344 true, 1345 }, 1346 } { 1347 1348 id, err := test.r.LinearBagSize() 1349 c.Check(id, check.Equals, test.id) 1350 if test.expectErr { 1351 c.Check(err, check.Not(check.Equals), nil) 1352 } else { 1353 c.Check(err, check.Equals, nil) 1354 } 1355 } 1356 } 1357 1358 func BenchmarkParseCigar(b *testing.B) { 1359 cig := []byte("69S17M5I30M1D45M1D23M5I14M2I4M1I10M2D7M1D6M14I33M1D6M1I7M1I18M1I8M1D4M1D4M2D57M1D21M1D6M1I14M1I7M1I3M1I9M1D3M1D7M1D37M1D9M1I5M1I15M4I12M1D10M1I10M1D8M1D26M7I12M1D20M1I36M1I22M3D8M1I23M1I13M2D10M1D12M1I15M6D4M1D4M1D1M2D5M1D3M17D1M1D13M3D7M1I29M2I9M1D2M4D7M2D8M5D3M1D1M1D23M1D10M6D19M3I24M1D8M1I11M6D14M1I5M8I12M1D8M2D5M2D2M1D23M1D11M4I35M2I19M1I4M1D13M7I33M1D21M3D2M1D9M4I19M1I14M1D7M1I41M1D23M3I18M1I6M1I13M1D9M1D1M1D20M1D23M5D8M1I13M2I11M1D78M2I18M10D9M2D10M1D10M2I6M1D3M1D21M2I7M1D7M2I12M1D20M2D18M1I12M1D8M4D18M1D6M1D20M1D14M1I1M2I23M1I10M1D7M1I15M1D4M1I9M1D11M1D12M1I8M1D21M1I13M2I59M1D12M1D18M1D13M1D22M1D13M1I19M1D13M1D19M1I11M2I27M2D10M1D17M6D13M2D17M1D13M1D19M1I3M1D13M2I33M1I26M2D9M2I21M2D10M1D36M1D32M5I23M1D13M2D17M1I14M2I24M1I5M2I8M2I24M2I9M1D7M1D2M1D15M3I19M1I2M1D3M1I7M1D5M2D24M5I1M4I33M1I13M3I34M1I2M1I23M1D3M2I8M1I5M5S") 1360 for i := 0; i < b.N; i++ { 1361 _, err := ParseCigar(cig) 1362 if err != nil { 1363 panic(err) 1364 } 1365 } 1366 } 1367 1368 func benchmarkAux(b *testing.B, aux []byte) { 1369 for i := 0; i < b.N; i++ { 1370 _, err := ParseAux(aux) 1371 if err != nil { 1372 b.Fatal(err) 1373 } 1374 } 1375 } 1376 1377 func BenchmarkParseAuxInt(b *testing.B) { benchmarkAux(b, []byte("NM:i:1")) } 1378 func BenchmarkParseAuxZ(b *testing.B) { benchmarkAux(b, []byte("SA:Z:ref,29,-,6H5M,17,0;")) } 1379 func BenchmarkParseAuxFloat(b *testing.B) { benchmarkAux(b, []byte("FL:f:100042.42")) } 1380 func BenchmarkParseAuxArray(b *testing.B) { benchmarkAux(b, []byte("BB:B:i,629,1095")) }