github.com/biogo/biogo@v1.0.4/feat/gene/gene_test.go (about) 1 // Copyright ©2015 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 gene 6 7 import ( 8 "github.com/biogo/biogo/feat" 9 10 "testing" 11 12 "gopkg.in/check.v1" 13 ) 14 15 // Assert that interfaces are satisfied 16 var ( 17 _ feat.Feature = (*Gene)(nil) 18 _ feat.Feature = (*NonCodingTranscript)(nil) 19 _ feat.Feature = (*CodingTranscript)(nil) 20 _ feat.Feature = (*Exon)(nil) 21 _ feat.Feature = (*Intron)(nil) 22 _ feat.Feature = (*TranscriptFeature)(nil) 23 24 _ featureOrienter = (*Gene)(nil) 25 _ featureOrienter = (*NonCodingTranscript)(nil) 26 _ featureOrienter = (*CodingTranscript)(nil) 27 _ featureOrienter = (*Exon)(nil) 28 _ featureOrienter = (*Intron)(nil) 29 _ featureOrienter = (*TranscriptFeature)(nil) 30 31 _ Transcript = (*NonCodingTranscript)(nil) 32 _ Transcript = (*CodingTranscript)(nil) 33 34 _ Interface = (*Gene)(nil) 35 ) 36 37 // Hook up gocheck into the "go test" runner. 38 func Test(t *testing.T) { check.TestingT(t) } 39 40 // Create the test suite 41 type S struct{} 42 43 var _ = check.Suite(&S{}) 44 45 // Chr implements feat.Feature and is used as location for the test objects. 46 type Chr string 47 48 func (c Chr) Start() int { return 0 } 49 func (c Chr) End() int { return 0 } 50 func (c Chr) Len() int { return 0 } 51 func (c Chr) Name() string { return string(c) } 52 func (c Chr) Description() string { return "chrom" } 53 func (c Chr) Location() feat.Feature { return nil } 54 55 // ori implements feat.Feature and is used as location for test objects. 56 type ori struct { 57 start, end int 58 name string 59 desc string 60 loc feat.Feature 61 orient feat.Orientation 62 } 63 64 func (o ori) Name() string { return o.name } 65 func (o ori) Description() string { return o.desc } 66 func (o ori) Start() int { return o.start } 67 func (o ori) End() int { return o.end } 68 func (o ori) Len() int { return o.end - o.start } 69 func (o ori) Location() feat.Feature { return o.loc } 70 func (o ori) Orientation() feat.Orientation { return o.orient } 71 72 // Define some test objects that will be used in the actual tests 73 var ( 74 geneA = Gene{ 75 ID: "geneA", 76 Chrom: Chr("Y"), 77 Offset: 100, 78 Orient: feat.Forward, 79 Desc: "forward gene", 80 } 81 geneB = Gene{ 82 ID: "geneB", 83 Chrom: Chr("X"), 84 Offset: 100, 85 Orient: feat.Reverse, 86 Desc: "reverse gene", 87 } 88 geneC = Gene{ 89 ID: "geneC", 90 Chrom: ori{ 91 start: 0, 92 end: 800, 93 orient: feat.Reverse, 94 loc: ori{ 95 start: 0, 96 end: 900, 97 orient: feat.Forward, 98 loc: ori{ 99 start: 0, 100 end: 1000, 101 orient: feat.Reverse, 102 }}}, 103 Offset: 100, 104 Orient: feat.Reverse, 105 Desc: "reverse gene on a contig on a supercontig on an ultra contig.", 106 } 107 codingTranscriptA = CodingTranscript{ 108 ID: "codingTranscriptA", 109 Loc: Chr("Y"), 110 Offset: 100, 111 CDSstart: 100, 112 CDSend: 600, 113 Orient: feat.Forward, 114 Desc: "forward transcript with cds", 115 } 116 codingTranscriptB = CodingTranscript{ 117 ID: "codingTranscriptB", 118 Loc: Chr("X"), 119 Offset: 500, 120 CDSstart: 300, 121 CDSend: 1300, 122 Orient: feat.Reverse, 123 Desc: "reverse transcript with cds", 124 } 125 codingTranscriptC = CodingTranscript{ 126 ID: "codingTranscriptC", 127 Loc: &geneC, 128 Offset: 20, 129 CDSstart: 100, 130 CDSend: 500, 131 Orient: feat.Forward, 132 Desc: "forward transcript with cds on reverse gene", 133 } 134 nonCodingTranscriptA = NonCodingTranscript{ 135 ID: "nonCodingTranscriptA", 136 Loc: Chr("Y"), 137 Offset: 100, 138 Orient: feat.Forward, 139 Desc: "forward non coding transcript", 140 } 141 nonCodingTranscriptB = NonCodingTranscript{ 142 ID: "nonCodingTranscriptB", 143 Loc: Chr("X"), 144 Offset: 500, 145 Orient: feat.Reverse, 146 Desc: "reverse non coding transcript", 147 } 148 ) 149 150 // Tests for Gene 151 var geneTests = []struct { 152 Test string 153 Gene Interface 154 Name string 155 Chrom string 156 Start, End int 157 Len int 158 Orientation feat.Orientation 159 Feats []feat.Feature 160 SetErr string 161 TransCount int 162 }{ 163 { 164 Test: "forward gene with valid feats", 165 Gene: &geneA, 166 Name: "geneA", 167 Chrom: "Y", 168 Start: 100, 169 End: 120, 170 Len: 20, 171 Orientation: feat.Forward, 172 Feats: []feat.Feature{ 173 &NonCodingTranscript{Loc: &geneA, exons: []Exon{{Length: 20}}}, 174 &NonCodingTranscript{Loc: &geneA}, 175 }, 176 TransCount: 2, 177 }, 178 { 179 Test: "reverse gene with valid feats", 180 Gene: &geneB, 181 Name: "geneB", 182 Chrom: "X", 183 Start: 100, 184 End: 110, 185 Len: 10, 186 Orientation: feat.Reverse, 187 Feats: []feat.Feature{ 188 &NonCodingTranscript{Loc: &geneB, exons: []Exon{{Length: 10}}}, 189 &NonCodingTranscript{Loc: &geneB}, 190 }, 191 TransCount: 2, 192 }, 193 { 194 Test: "forward gene with feat on wrong location", 195 Gene: &geneA, 196 Feats: []feat.Feature{&NonCodingTranscript{Loc: &geneB}}, 197 SetErr: "transcript location does not match the gene", 198 }, 199 { 200 Test: "reverse gene with no feat from 0", 201 Gene: &geneB, 202 Feats: []feat.Feature{&NonCodingTranscript{Loc: &geneB, Offset: 5}}, 203 SetErr: "no transcript with 0 start on gene", 204 }, 205 } 206 207 func (s *S) TestGene(c *check.C) { 208 for _, d := range geneTests { 209 g := d.Gene 210 211 // Test SetFeatures 212 if err := g.SetFeatures(d.Feats...); err != nil { 213 c.Assert(err, check.ErrorMatches, d.SetErr) 214 } else { 215 c.Check(g.Name(), check.Equals, d.Name) 216 c.Check(g.Start(), check.Equals, d.Start) 217 c.Check(g.End(), check.Equals, d.End) 218 c.Check(g.Len(), check.Equals, d.Len) 219 c.Check(g.Location().Name(), check.Equals, d.Chrom) 220 c.Check(g.Orientation(), check.Equals, d.Orientation) 221 c.Check(len(TranscriptsOf(g)), check.Equals, d.TransCount) 222 } 223 } 224 } 225 226 // Tests for Transcript 227 var transcriptTests = []struct { 228 Test string 229 Transcript Transcript 230 Name string 231 Loc feat.Feature 232 Start, End int 233 UTR5start, UTR5end int 234 CDSstart, CDSend int 235 UTR3start, UTR3end int 236 Len int 237 Orientation feat.Orientation 238 Exons []Exon 239 AddErr string 240 ExonicLen int 241 }{ 242 { 243 Test: "forward transcript with cds and valid exons", 244 Transcript: &codingTranscriptA, 245 Name: "codingTranscriptA", 246 Loc: Chr("Y"), 247 Orientation: feat.Forward, 248 Exons: []Exon{ 249 {Transcript: &codingTranscriptA, Offset: 0, Length: 300}, 250 {Transcript: &codingTranscriptA, Offset: 600, Length: 200}}, 251 Start: 100, 252 End: 900, 253 UTR5start: 0, 254 UTR5end: 100, 255 CDSstart: 100, 256 CDSend: 600, 257 UTR3start: 600, 258 UTR3end: 800, 259 Len: 800, 260 ExonicLen: 500, 261 }, 262 { 263 Test: "reverse transcript with cds and valid exons", 264 Transcript: &codingTranscriptB, 265 Name: "codingTranscriptB", 266 Loc: Chr("X"), 267 Orientation: feat.Reverse, 268 Exons: []Exon{ 269 {Transcript: &codingTranscriptB, Offset: 0, Length: 600}, 270 {Transcript: &codingTranscriptB, Offset: 900, Length: 600}}, 271 Start: 500, 272 End: 2000, 273 UTR3start: 0, 274 UTR3end: 300, 275 CDSstart: 300, 276 CDSend: 1300, 277 UTR5start: 1300, 278 UTR5end: 1500, 279 Len: 1500, 280 ExonicLen: 1200, 281 }, 282 { 283 Test: "forward transcript with cds and valid exons on reverse gene on a contig on a supercontig on an ultra contig.", 284 Transcript: &codingTranscriptC, 285 Name: "codingTranscriptC", 286 Loc: &geneC, 287 Orientation: feat.Forward, 288 Exons: []Exon{ 289 {Transcript: &codingTranscriptC, Offset: 0, Length: 500}, 290 {Transcript: &codingTranscriptC, Offset: 600, Length: 100}}, 291 Start: 20, 292 End: 720, 293 UTR3start: 0, 294 UTR3end: 100, 295 CDSstart: 100, 296 CDSend: 500, 297 UTR5start: 500, 298 UTR5end: 700, 299 Len: 700, 300 ExonicLen: 600, 301 }, 302 { 303 Test: "forward non-coding transcript with valid exons", 304 Transcript: &nonCodingTranscriptA, 305 Name: "nonCodingTranscriptA", 306 Loc: Chr("Y"), 307 Orientation: feat.Forward, 308 Exons: []Exon{ 309 {Transcript: &nonCodingTranscriptA, Offset: 0, Length: 300}, 310 {Transcript: &nonCodingTranscriptA, Offset: 600, Length: 200}}, 311 Start: 100, 312 End: 900, 313 Len: 800, 314 ExonicLen: 500, 315 }, 316 { 317 Test: "reverse non-coding transcript without exon at 0", 318 Transcript: &nonCodingTranscriptB, 319 Orientation: feat.Reverse, 320 Exons: []Exon{{Transcript: &nonCodingTranscriptB, Offset: 10}}, 321 AddErr: "no exon with a zero start", 322 }, 323 { 324 Test: "reverse non-coding transcript with wrong exon location", 325 Transcript: &nonCodingTranscriptB, 326 Orientation: feat.Reverse, 327 Exons: []Exon{{Offset: 0, Length: 10000}}, 328 AddErr: "exon location is not the transcript", 329 }, 330 } 331 332 func (s *S) TestTranscript(c *check.C) { 333 for _, d := range transcriptTests { 334 t := d.Transcript 335 336 // Test SetExons 337 if err := t.SetExons(d.Exons...); err != nil { 338 c.Assert(err, check.ErrorMatches, d.AddErr) 339 } else { 340 t.Exons()[0].Offset = 1000000 // should have no effect on t 341 342 c.Check(t.Name(), check.Equals, d.Name) 343 c.Check(t.Start(), check.Equals, d.Start) 344 c.Check(t.End(), check.Equals, d.End) 345 c.Check(t.Len(), check.Equals, d.Len) 346 c.Check(t.Location(), check.Equals, d.Loc) 347 c.Check(t.Orientation(), check.Equals, d.Orientation) 348 c.Check(t.Exons().SplicedLen(), check.Equals, d.ExonicLen) 349 350 // Test CodingTranscript specifics 351 if t, ok := t.(*CodingTranscript); ok { 352 utr5, cds, utr3 := t.UTR5(), t.CDS(), t.UTR3() 353 c.Check(utr5.Start(), check.Equals, d.UTR5start) 354 c.Check(utr5.End(), check.Equals, d.UTR5end) 355 c.Check(utr5.Location(), check.Equals, t) 356 c.Check(cds.Start(), check.Equals, d.CDSstart) 357 c.Check(cds.End(), check.Equals, d.CDSend) 358 c.Check(cds.Location(), check.Equals, t) 359 c.Check(utr3.Start(), check.Equals, d.UTR3start) 360 c.Check(utr3.End(), check.Equals, d.UTR3end) 361 c.Check(utr3.Location(), check.Equals, t) 362 363 c.Check(t.CDSstart, check.Equals, d.CDSstart) 364 c.Check(t.CDSend, check.Equals, d.CDSend) 365 c.Check(t.UTR5start(), check.Equals, d.UTR5start) 366 c.Check(t.UTR5end(), check.Equals, d.UTR5end) 367 c.Check(t.UTR3start(), check.Equals, d.UTR3start) 368 c.Check(t.UTR3end(), check.Equals, d.UTR3end) 369 } 370 } 371 } 372 } 373 374 // Tests for Exon and Intron 375 type featureOrienter interface { 376 feat.Orienter 377 feat.Feature 378 } 379 380 var exonIntronTests = []struct { 381 Test string 382 Feat featureOrienter 383 Start, End int 384 Len int 385 Transcript feat.Feature 386 Orientation feat.Orientation 387 }{ 388 { 389 Test: "Exon on transcript", 390 Feat: Exon{Offset: 200, Length: 200}, 391 Start: 200, 392 End: 400, 393 Len: 200, 394 Orientation: feat.Forward, 395 }, 396 { 397 Test: "Intron on transcript", 398 Feat: Intron{Offset: 300, Length: 500}, 399 Start: 300, 400 End: 800, 401 Len: 500, 402 Orientation: feat.Forward, 403 }, 404 } 405 406 func (s *S) TestExonIntron(c *check.C) { 407 for _, d := range exonIntronTests { 408 e := d.Feat 409 410 c.Check(e.Start(), check.Equals, d.Start) 411 c.Check(e.End(), check.Equals, d.End) 412 c.Check(e.Len(), check.Equals, d.Len) 413 c.Check(e.Location(), check.DeepEquals, d.Transcript) 414 c.Check(e.Orientation(), check.Equals, d.Orientation) 415 } 416 } 417 418 // Tests for Exons 419 var exonsTests = []struct { 420 Test string 421 InputExons []Exon 422 Location feat.Feature 423 Start, End, Len, SplicedLen int 424 AddErr string 425 MadeIntrons Introns 426 }{ 427 { 428 Test: "Exons not in order", 429 InputExons: []Exon{ 430 {Offset: 300, Length: 100}, 431 {Offset: 0, Length: 100}, 432 }, 433 Start: 0, 434 End: 400, 435 Len: 2, 436 SplicedLen: 200, 437 MadeIntrons: Introns{ 438 Intron{Offset: 100, Length: 200}, 439 }, 440 }, 441 { 442 Test: "Exons overlap", 443 InputExons: []Exon{ 444 {Offset: 0, Length: 100}, 445 {Offset: 50, Length: 100}, 446 }, 447 AddErr: "exons overlap", 448 }, 449 { 450 Test: "Exons on different transcripts", 451 InputExons: []Exon{ 452 {Transcript: &codingTranscriptA}, 453 {Transcript: &codingTranscriptB}, 454 }, 455 AddErr: "exons location differ", 456 }, 457 } 458 459 func (s *S) TestExons(c *check.C) { 460 for _, d := range exonsTests { 461 var e Exons 462 ie := d.InputExons 463 464 // Test SetExons 465 if e, err := e.Add(ie...); err != nil { 466 c.Assert(err, check.ErrorMatches, d.AddErr) 467 } else { 468 c.Check(e.Location(), check.DeepEquals, d.Location) 469 c.Check(e.Start(), check.Equals, d.Start) 470 c.Check(e.End(), check.Equals, d.End) 471 c.Check(e.Len(), check.Equals, d.Len) 472 c.Check(e.SplicedLen(), check.Equals, d.SplicedLen) 473 c.Check(e.Introns(), check.DeepEquals, d.MadeIntrons) 474 } 475 } 476 }