|
a |
|
b/tests/test_features.py |
|
|
1 |
#!/usr/bin/env python3 |
|
|
2 |
# -*- coding: utf-8 -*- |
|
|
3 |
import unittest |
|
|
4 |
import itertools |
|
|
5 |
|
|
|
6 |
from singlecellmultiomics.features import FeatureContainer |
|
|
7 |
|
|
|
8 |
""" |
|
|
9 |
These tests check if the feature container is working correctly |
|
|
10 |
""" |
|
|
11 |
|
|
|
12 |
class TestFeatureContainer(unittest.TestCase): |
|
|
13 |
|
|
|
14 |
|
|
|
15 |
def expect(self, result, desired, presenceTestOnly=False): |
|
|
16 |
if presenceTestOnly: |
|
|
17 |
self.assertTrue( any( r[2] in desired for r in result) ) |
|
|
18 |
if desired is None: |
|
|
19 |
self.assertTrue(len(result)==0) |
|
|
20 |
elif type(desired) is list: |
|
|
21 |
self.assertTrue( len(result)==len(desired) and all( r[2] in desired for r in result)) |
|
|
22 |
else: |
|
|
23 |
self.assertTrue( len(result)==1 and result[0][2]==desired ) |
|
|
24 |
|
|
|
25 |
def test_add(self): |
|
|
26 |
f = FeatureContainer() |
|
|
27 |
f.addFeature('chrY', 1, 3, 'A','+','') |
|
|
28 |
f.addFeature('chrY', 5, 8, 'B','+','') |
|
|
29 |
f.sort() |
|
|
30 |
self.expect( f.findFeaturesAt('chrY',0,'+'), None) |
|
|
31 |
self.expect( f.findFeaturesAt('chrY',1,'+'), 'A') |
|
|
32 |
self.expect( f.findFeaturesAt('chrY',2,'+'), 'A') |
|
|
33 |
self.expect( f.findFeaturesAt('chrY',3,'+'), 'A') |
|
|
34 |
self.expect( f.findFeaturesAt('chrY',4,'+'), None) |
|
|
35 |
self.expect( f.findFeaturesAt('chrY',5,'+'), 'B') |
|
|
36 |
self.expect( f.findFeaturesAt('chrY',6,'+'), 'B') |
|
|
37 |
self.expect( f.findFeaturesAt('chrY',8,'+'), 'B') |
|
|
38 |
|
|
|
39 |
def test_nested(self): |
|
|
40 |
f = FeatureContainer() |
|
|
41 |
f.addFeature('chrX', 10, 1000, 'parentB','+','') |
|
|
42 |
f.addFeature('chrX', 500, 900, 'nestedB','+','') |
|
|
43 |
f.addFeature('chrX', 10000, 12000, 'C','+','') |
|
|
44 |
f.addFeature('chrX', 100000, 120000, 'D','+','') |
|
|
45 |
f.sort() |
|
|
46 |
self.expect( f.findFeaturesAt('chrX',9,'+'), None) |
|
|
47 |
self.expect( f.findFeaturesAt('chrX',12001,'+'), None) |
|
|
48 |
self.expect( f.findFeaturesAt('chrX',12000,'+'), 'C') |
|
|
49 |
self.expect( f.findFeaturesAt('chrX',120000,'+'), 'D') |
|
|
50 |
self.expect( f.findFeaturesAt('chrX',10,'+'), 'parentB') |
|
|
51 |
|
|
|
52 |
def test_len(self): |
|
|
53 |
f = FeatureContainer() |
|
|
54 |
f.addFeature('chr1',100, 200, '1','+','A forward feature from 100 to 200 chr1') |
|
|
55 |
f.addFeature('chr1',110, 200, '2','-', 'A reverse feature from 110 to 200 chr1') |
|
|
56 |
f.addFeature('chr2',100, 200, '3', '+', 'A forward feature from 100 to 200 chr2') |
|
|
57 |
f.addFeature('chr2',100, 110, '4','+', 'A forward feature from 100 to 110 chr2') |
|
|
58 |
f.addFeature('chr2',100, 150, '5', '-','A reverse feature from 100 to 150 chr2') |
|
|
59 |
self.assertEqual( len(f) , 5) |
|
|
60 |
|
|
|
61 |
def test_stranded(self): |
|
|
62 |
f = FeatureContainer() |
|
|
63 |
f.addFeature('chr1',100, 200, '1','+','A forward feature from 100 to 200 chr1') |
|
|
64 |
f.addFeature('chr1',110, 200, '2','-', 'A reverse feature from 110 to 200 chr1') |
|
|
65 |
f.addFeature('chr2',100, 200, '3', '+', 'A forward feature from 100 to 200 chr2') |
|
|
66 |
f.addFeature('chr2',100, 110, '4','+', 'A forward feature from 100 to 110 chr2') |
|
|
67 |
f.addFeature('chr2',100, 150, '5', '-','A reverse feature from 100 to 150 chr2') |
|
|
68 |
|
|
|
69 |
f.addFeature('chr3',100, 150, '6', '-','feature 6') |
|
|
70 |
f.addFeature('chr3',200, 250, '7', '-','feature 7') |
|
|
71 |
f.addFeature('chr3',200, 450, '8', '-','feature 8') |
|
|
72 |
f.addFeature('chr3',10, 15, '9', '-','feature 9') |
|
|
73 |
f.sort() |
|
|
74 |
|
|
|
75 |
#printFormatted("[BRIGHT]Test for reference presence:") |
|
|
76 |
result = f.getReferenceList() |
|
|
77 |
desired = ['chr1','chr2','chr3'] |
|
|
78 |
#printFormatted("self.expecting %s, %s" % (desired, ("[BRIGHT][GREEN] SUCCES [RESET][DIM]%s\n" % result) if len(result)==len(desired) and all( r in desired for r in result) else '[RED]FAIL %s\n' % result )) |
|
|
79 |
|
|
|
80 |
#printFormatted("[BRIGHT]Test on leftmost start:") |
|
|
81 |
self.expect( f.findFeaturesAt('chr1',100,'+'), '1') |
|
|
82 |
|
|
|
83 |
#printFormatted("[BRIGHT]Test on rightmost end:") |
|
|
84 |
self.expect( f.findFeaturesAt('chr1',200,'+'), '1') |
|
|
85 |
|
|
|
86 |
#printFormatted("[BRIGHT]Test on random location within feature:") |
|
|
87 |
self.expect( f.findFeaturesAt('chr1',120,'+'), '1') |
|
|
88 |
|
|
|
89 |
#printFormatted("[BRIGHT]Test on random location within feature:") |
|
|
90 |
self.expect( f.findFeaturesAt('chr2',120,'+'), '3') |
|
|
91 |
|
|
|
92 |
#printFormatted("[BRIGHT]Test on limit location of feature:") |
|
|
93 |
self.expect( f.findFeaturesAt('chr2',200,'+'), '3') |
|
|
94 |
|
|
|
95 |
#printFormatted("[BRIGHT]Test on non matching location (match available on other side, and one base left of coord):") |
|
|
96 |
self.expect( f.findFeaturesAt('chr2',151,'-'), None) |
|
|
97 |
|
|
|
98 |
|
|
|
99 |
def test_double_matching(self): |
|
|
100 |
f = FeatureContainer() |
|
|
101 |
f.addFeature('chr1',100, 200, '1','+','A forward feature from 100 to 200 chr1') |
|
|
102 |
f.addFeature('chr1',110, 200, '2','-', 'A reverse feature from 110 to 200 chr1') |
|
|
103 |
f.addFeature('chr2',100, 200, '3', '+', 'A forward feature from 100 to 200 chr2') |
|
|
104 |
f.addFeature('chr2',100, 110, '4','+', 'A forward feature from 100 to 110 chr2') |
|
|
105 |
f.addFeature('chr2',100, 150, '5', '-','A reverse feature from 100 to 150 chr2') |
|
|
106 |
|
|
|
107 |
f.addFeature('chr3',100, 150, '6', '-','feature 6') |
|
|
108 |
f.addFeature('chr3',200, 250, '7', '-','feature 7') |
|
|
109 |
f.addFeature('chr3',200, 450, '8', '-','feature 8') |
|
|
110 |
f.addFeature('chr3',10, 15, '9', '-','feature 9') |
|
|
111 |
f.sort() |
|
|
112 |
#printFormatted("[BRIGHT]Tests on double matching locations without strand spec") |
|
|
113 |
self.expect( f.findFeaturesAt('chr1',120), ['1','2']) |
|
|
114 |
self.expect( f.findFeaturesAt('chr2',105), ['3','4','5']) |
|
|
115 |
|
|
|
116 |
|
|
|
117 |
def test_ranges(self): |
|
|
118 |
f = FeatureContainer() |
|
|
119 |
f.addFeature('chr1',100, 200, '1','+','A forward feature from 100 to 200 chr1') |
|
|
120 |
f.addFeature('chr1',110, 200, '2','-', 'A reverse feature from 110 to 200 chr1') |
|
|
121 |
f.addFeature('chr2',100, 200, '3', '+', 'A forward feature from 100 to 200 chr2') |
|
|
122 |
f.addFeature('chr2',100, 110, '4','+', 'A forward feature from 100 to 110 chr2') |
|
|
123 |
f.addFeature('chr2',100, 150, '5', '-','A reverse feature from 100 to 150 chr2') |
|
|
124 |
|
|
|
125 |
f.addFeature('chr3',100, 150, '6', '-','feature 6') |
|
|
126 |
f.addFeature('chr3',200, 250, '7', '-','feature 7') |
|
|
127 |
f.addFeature('chr3',200, 450, '8', '-','feature 8') |
|
|
128 |
f.addFeature('chr3',10, 15, '9', '-','feature 9') |
|
|
129 |
f.sort() |
|
|
130 |
|
|
|
131 |
#printFormatted("[BRIGHT] ==== Range tests... ====" ) |
|
|
132 |
#printFormatted("[BRIGHT]Test for matching start and end coordinates overlapping one feature") |
|
|
133 |
self.expect( f.findFeaturesBetween('chr2',102, 200, '+' ), ['3','4']) |
|
|
134 |
|
|
|
135 |
#printFormatted("[BRIGHT]Test for matching all features on chromosome") |
|
|
136 |
self.expect( f.findFeaturesBetween('chr2',0, 20000, None ), ['3','4','5']) |
|
|
137 |
|
|
|
138 |
#printFormatted("[BRIGHT]Test for matching all but one features on chromosome") |
|
|
139 |
self.expect( f.findFeaturesBetween('chr3',151, 20000, None ), ['8','7']) |
|
|
140 |
|
|
|
141 |
#printFormatted("[BRIGHT]Test for matching all but one features on chromosome") |
|
|
142 |
self.expect( f.findFeaturesBetween('chr3',0, 195, None ), ['6','9']) |
|
|
143 |
|
|
|
144 |
#printFormatted("[BRIGHT]Test for range finding non-existent feature") |
|
|
145 |
self.expect( f.findFeaturesBetween('chr2', 2001, 2000, '+' ), None) |
|
|
146 |
|
|
|
147 |
|
|
|
148 |
#printFormatted("[BRIGHT]Test for finding non-existent feature LEFT NEXT to the point") |
|
|
149 |
self.expect( f.findNearestLeftFeature('chr2', 50, '+' ), None) |
|
|
150 |
|
|
|
151 |
#printFormatted("[BRIGHT]Test for finding existent feature LEFT NEXT to the point") |
|
|
152 |
self.expect( f.findNearestLeftFeature('chr2', 250, None ), '3') |
|
|
153 |
|
|
|
154 |
|
|
|
155 |
#printFormatted("[BRIGHT]Test for finding non-existent feature RIGHT NEXT to the point") |
|
|
156 |
self.expect( f.findNearestRightFeature('chr2', 250, '+' ), None) |
|
|
157 |
|
|
|
158 |
#printFormatted("[BRIGHT]Test for finding existent feature RIGHT NEXT to the point") |
|
|
159 |
self.expect( f.findNearestRightFeature('chr2', 0, '-' ), '5') |
|
|
160 |
|
|
|
161 |
|
|
|
162 |
#printFormatted("[BRIGHT]Test for finding closest feature") |
|
|
163 |
self.expect( f.findNearestFeature('chr1', 0, None ), '1') |
|
|
164 |
|
|
|
165 |
|
|
|
166 |
if __name__ == '__main__': |
|
|
167 |
unittest.main() |