0
|
1 #!/usr/bin/env python3
|
|
2 import unittest
|
|
3 from subprocess import PIPE, run
|
|
4 import os
|
|
5 import shutil
|
|
6 import sys
|
|
7 import argparse
|
|
8
|
|
9
|
|
10 # This is not best practice but for testing, this is the best I could
|
|
11 # come up with
|
|
12 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
13
|
|
14 # TODO: Species specific aqquired genes only pheno results, not spec specific?
|
|
15
|
|
16 test_names = ["test1", "test2", "test3", "test4"]
|
|
17 test_data = {
|
|
18 # Test published acquired resistance
|
|
19 test_names[0]: "data/test_isolate_01.fa",
|
|
20 test_names[1]: "data/test_isolate_01_1.fq data/test_isolate_01_2.fq",
|
|
21 # Test published point mut resistance
|
|
22 test_names[2]: "data/test_isolate_05.fa",
|
|
23 test_names[3]: "data/test_isolate_05_1.fq data/test_isolate_05_2.fq",
|
|
24 }
|
|
25 run_test_dir = "running_test"
|
|
26 working_dir = os.path.dirname(os.path.realpath(__file__))
|
|
27
|
|
28
|
|
29 class ResFinderRunTest(unittest.TestCase):
|
|
30
|
|
31 @classmethod
|
|
32 def setUpClass(cls):
|
|
33 # Delete "running_test" folder from previous tests if still exists
|
|
34 if os.path.isdir(run_test_dir):
|
|
35 try:
|
|
36 shutil.rmtree(run_test_dir)
|
|
37 # The following error has occured using VirtualBox under Windows 10
|
|
38 # with ResFinder installed in a shared folder:
|
|
39 # OSError [Errno: 26] Text file busy: 'tmp'
|
|
40 except OSError:
|
|
41 procs = run(["rm", "-r", run_test_dir])
|
|
42
|
|
43 # Set absolute path for database folders and external programs
|
|
44 cls.db_path_res = os.path.abspath(args.db_path_res)
|
|
45 cls.blastPath = os.path.abspath(args.blast_path)
|
|
46 cls.kmaPath = os.path.abspath(args.kma_path)
|
|
47 cls.db_path_point = os.path.abspath(args.db_path_point)
|
|
48 cls.dir_res = os.path.join(os.path.dirname(__file__), '../', )
|
|
49 cls.dir_res = os.path.abspath(cls.dir_res)
|
|
50 # Change working dir to test dir
|
|
51 os.chdir(working_dir)
|
|
52 # Does not allow running two tests in parallel
|
|
53 os.makedirs(run_test_dir, exist_ok=False)
|
|
54
|
|
55 @classmethod
|
|
56 def tearDownClass(cls):
|
|
57 try:
|
|
58 shutil.rmtree(run_test_dir)
|
|
59 # The following error has occured using VirtualBox under Windows 10
|
|
60 # with ResFinder installed in a shared folder:
|
|
61 # OSError [Errno: 26] Text file busy: 'tmp'
|
|
62 except OSError:
|
|
63 procs = run(["rm", "-r", run_test_dir])
|
|
64
|
|
65 def test_on_data_with_just_acquired_resgene_using_blast(self):
|
|
66 # Maria has an E. coli isolate, with unknown resistance.
|
|
67 # At first, she just wants to know which acquired resistance genes are
|
|
68 # found in the genome.
|
|
69 # She therefore runs resfinder cmd line.
|
|
70
|
|
71 # First Maria checks out the documentation.
|
|
72 procs = run("python3 ../run_resfinder.py -h", shell=True, stdout=PIPE,
|
|
73 check=True)
|
|
74 output = procs.stdout.decode()
|
|
75 self.assertIn("--help", output)
|
|
76
|
|
77 # Maria goes on to run ResFinder for acquired genes with her E. coli
|
|
78 # isolate.
|
|
79 # First she creates a few directories to store her output.
|
|
80 test1_dir = run_test_dir + "/" + test_names[0]
|
|
81 os.makedirs(test1_dir)
|
|
82 # Then she runs run_resfinder with her first isolate.
|
|
83 cmd_acquired = ("python3 " + self.dir_res + "/run_resfinder.py"
|
|
84 + " -ifa " + test_data[test_names[0]]
|
|
85 + " -o " + test1_dir
|
|
86 + " -s 'Escherichia coli'"
|
|
87 + " --min_cov 0.6"
|
|
88 + " -t 0.8"
|
|
89 + " --acquired"
|
|
90 + " --db_path_res " + self.db_path_res
|
|
91 + " --blastPath " + self.blastPath)
|
|
92
|
|
93 procs = run(cmd_acquired, shell=True, stdout=PIPE, stderr=PIPE,
|
|
94 check=True)
|
|
95
|
|
96 fsa_hit = test1_dir + "/ResFinder_Hit_in_genome_seq.fsa"
|
|
97 fsa_res = test1_dir + "/ResFinder_Resistance_gene_seq.fsa"
|
|
98 res_table = test1_dir + "/ResFinder_results_table.txt"
|
|
99 res_tab = test1_dir + "/ResFinder_results_tab.txt"
|
|
100 results = test1_dir + "/ResFinder_results.txt"
|
|
101
|
|
102 with open(fsa_hit, "r") as fh:
|
|
103 check_result = fh.readline()
|
|
104 self.assertIn("blaB-2_1_AF189300", check_result)
|
|
105
|
|
106 with open(fsa_res, "r") as fh:
|
|
107 check_result = fh.readline()
|
|
108 self.assertIn("blaB-2_AF189300", check_result)
|
|
109
|
|
110 with open(res_table, "r") as fh:
|
|
111 for line in fh:
|
|
112 if(line.startswith("blaB-2")):
|
|
113 check_result = line
|
|
114 break
|
|
115 self.assertIn("blaB-2_1_AF189300", check_result)
|
|
116
|
|
117 with open(res_tab, "r") as fh:
|
|
118 fh.readline()
|
|
119 check_result = fh.readline()
|
|
120 self.assertIn("blaB-2_1_AF189300", check_result)
|
|
121
|
|
122 with open(results, "r") as fh:
|
|
123 fh.readline()
|
|
124 fh.readline()
|
|
125 fh.readline()
|
|
126 fh.readline()
|
|
127 fh.readline()
|
|
128 check_result = fh.readline()
|
|
129 self.assertIn("blaB-2_1_AF189300", check_result)
|
|
130
|
|
131 def test_on_data_with_just_acquired_resgene_using_kma(self):
|
|
132 # Maria has another E. coli isolate, with unknown resistance.
|
|
133 # This time she does not have an assembly, but only raw data.
|
|
134 # She therefore runs resfinder cmd line using KMA.
|
|
135
|
|
136 # First she creates a few directories to store her output.
|
|
137 test2_dir = run_test_dir + "/" + test_names[1]
|
|
138 os.makedirs(test2_dir, exist_ok=False)
|
|
139
|
|
140 # Then she runs run_resfinder with her first isolate.
|
|
141 cmd_acquired = ("python3 " + self.dir_res + "/run_resfinder.py"
|
|
142 + " -ifq " + test_data[test_names[1]]
|
|
143 + " -o " + test2_dir
|
|
144 + " -s 'Escherichia coli'"
|
|
145 + " --min_cov 0.6"
|
|
146 + " -t 0.8"
|
|
147 + " --acquired"
|
|
148 + " --db_path_res " + self.db_path_res
|
|
149 + " --kmaPath " + self.kmaPath)
|
|
150
|
|
151 procs = run(cmd_acquired, shell=True, stdout=PIPE, stderr=PIPE,
|
|
152 check=True)
|
|
153
|
|
154 fsa_hit = test2_dir + "/ResFinder_Hit_in_genome_seq.fsa"
|
|
155 fsa_res = test2_dir + "/ResFinder_Resistance_gene_seq.fsa"
|
|
156 res_table = test2_dir + "/ResFinder_results_table.txt"
|
|
157 res_tab = test2_dir + "/ResFinder_results_tab.txt"
|
|
158 results = test2_dir + "/ResFinder_results.txt"
|
|
159
|
|
160 with open(fsa_hit, "r") as fh:
|
|
161 check_result = fh.readline()
|
|
162 self.assertIn("blaB-2", check_result)
|
|
163
|
|
164 with open(fsa_res, "r") as fh:
|
|
165 check_result = fh.readline()
|
|
166 self.assertIn("blaB-2_AF189300", check_result)
|
|
167
|
|
168 with open(res_table, "r") as fh:
|
|
169 for line in fh:
|
|
170 if(line.startswith("blaB-2")):
|
|
171 check_result = line
|
|
172 break
|
|
173 self.assertIn("blaB-2", check_result)
|
|
174
|
|
175 with open(res_tab, "r") as fh:
|
|
176 fh.readline()
|
|
177 check_result = fh.readline()
|
|
178 self.assertIn("blaB-2", check_result)
|
|
179
|
|
180 with open(results, "r") as fh:
|
|
181 fh.readline()
|
|
182 fh.readline()
|
|
183 fh.readline()
|
|
184 fh.readline()
|
|
185 fh.readline()
|
|
186 check_result = fh.readline()
|
|
187 self.assertIn("blaB-2", check_result)
|
|
188
|
|
189 def test_on_data_with_just_point_mut_using_blast(self):
|
|
190 # Maria also wants to check her assembled E. coli isolate for
|
|
191 # resistance caused by point mutations.
|
|
192
|
|
193 # First she creates a few directories to store her output.
|
|
194 test3_dir = run_test_dir + "/" + test_names[2]
|
|
195 os.makedirs(test3_dir)
|
|
196
|
|
197 # Then she runs run_resfinder with her first isolate.
|
|
198 cmd_point = ("python3 " + self.dir_res + "/run_resfinder.py"
|
|
199 + " -ifa " + test_data[test_names[2]]
|
|
200 + " -o " + test3_dir
|
|
201 + " -s 'Escherichia coli'"
|
|
202 + " --min_cov 0.6"
|
|
203 + " --threshold 0.8"
|
|
204 + " --point"
|
|
205 + " --db_path_point " + self.db_path_point
|
|
206 + " --db_path_res " + self.db_path_res
|
|
207 + " --blastPath " + self.blastPath)
|
|
208
|
|
209 procs = run(cmd_point, shell=True, stdout=PIPE, stderr=PIPE,
|
|
210 check=True)
|
|
211
|
|
212 # Expected output files
|
|
213 pf_pred = test3_dir + "/PointFinder_prediction.txt"
|
|
214 pf_res = test3_dir + "/PointFinder_results.txt"
|
|
215 pf_table = test3_dir + "/PointFinder_table.txt"
|
|
216
|
|
217 with open(pf_res, "r") as fh:
|
|
218 fh.readline()
|
|
219 check_result = fh.readline()
|
|
220 self.assertIn("gyrA", check_result)
|
|
221 self.assertIn("p.S83A", check_result)
|
|
222
|
|
223 point_mut_found = False
|
|
224 with open(pf_table, "r") as fh:
|
|
225 for line in fh:
|
|
226 if(line.startswith("gyrA p.S83A")):
|
|
227 check_result = line
|
|
228 point_mut_found = True
|
|
229 break
|
|
230 self.assertEqual(point_mut_found, True)
|
|
231
|
|
232 def test_on_data_with_just_point_mut_using_kma(self):
|
|
233 # Maria has another E. coli isolate, with unknown resistance.
|
|
234 # This time she does not have an assembly, but only raw data.
|
|
235 # She therefore runs resfinder cmd line using KMA.
|
|
236
|
|
237 # First she creates a few directories to store her output.
|
|
238 test4_dir = run_test_dir + "/" + test_names[3]
|
|
239 os.makedirs(test4_dir, exist_ok=False)
|
|
240
|
|
241 # Then she runs run_resfinder with her first isolate.
|
|
242 cmd_acquired = ("python3 " + self.dir_res + "/run_resfinder.py"
|
|
243 + " -ifq " + test_data[test_names[3]]
|
|
244 + " -o " + test4_dir
|
|
245 + " -s 'Escherichia coli'"
|
|
246 + " --min_cov 0.6"
|
|
247 + " --threshold 0.8"
|
|
248 + " --point"
|
|
249 + " --db_path_point " + self.db_path_point
|
|
250 + " --db_path_res " + self.db_path_res
|
|
251 + " --kmaPath " + self.kmaPath)
|
|
252
|
|
253 procs = run(cmd_acquired, shell=True, stdout=PIPE, stderr=PIPE,
|
|
254 check=True)
|
|
255
|
|
256 # Expected output files
|
|
257 pf_pred = test4_dir + "/PointFinder_prediction.txt"
|
|
258 pf_res = test4_dir + "/PointFinder_results.txt"
|
|
259 pf_table = test4_dir + "/PointFinder_table.txt"
|
|
260
|
|
261 with open(pf_res, "r") as fh:
|
|
262 fh.readline()
|
|
263 check_result = fh.readline()
|
|
264 self.assertIn("gyrA", check_result)
|
|
265 self.assertIn("p.S83A", check_result)
|
|
266
|
|
267 point_mut_found = False
|
|
268 with open(pf_table, "r") as fh:
|
|
269 for line in fh:
|
|
270 if(line.startswith("gyrA p.S83A")):
|
|
271 check_result = line
|
|
272 point_mut_found = True
|
|
273 break
|
|
274 self.assertEqual(point_mut_found, True)
|
|
275
|
|
276
|
|
277 def parse_args():
|
|
278 parser = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
|
|
279 group = parser.add_argument_group("Options")
|
|
280 group.add_argument('-res_help', "--resfinder_help",
|
|
281 action="help")
|
|
282 group.add_argument("-db_res", "--db_path_res",
|
|
283 help="Path to the databases for ResFinder",
|
|
284 default="./db_resfinder")
|
|
285 group.add_argument("-b", "--blastPath",
|
|
286 dest="blast_path",
|
|
287 help="Path to blastn",
|
|
288 default="./cge/blastn")
|
|
289 group.add_argument("-k", "--kmaPath",
|
|
290 dest="kma_path",
|
|
291 help="Path to KMA",
|
|
292 default="./cge/kma/kma")
|
|
293 group.add_argument("-db_point", "--db_path_point",
|
|
294 help="Path to the databases for PointFinder",
|
|
295 default="./db_pointfinder")
|
|
296 ns, args = parser.parse_known_args(namespace=unittest)
|
|
297 return ns, sys.argv[:1] + args
|
|
298
|
|
299 if __name__ == "__main__":
|
|
300 args, argv = parse_args() # run this first
|
|
301 sys.argv[:] = argv # create cleans argv for main()
|
|
302 unittest.main()
|