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  }