-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_smith_waterman.py
More file actions
135 lines (115 loc) · 6.35 KB
/
Copy pathtest_smith_waterman.py
File metadata and controls
135 lines (115 loc) · 6.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import unittest
import smith_waterman_p as sw
import programme_settings
import blosum
# Adapted from oficial Python unittest documentation: https://docs.python.org/3/library/unittest.html
# Python testing guide: https://realpython.com/python-testing/
class SmithWatermanTests(unittest.TestCase):
def setUp(self):
programme_settings.settings["DEFAULT"]["seq_gap"] = "-8"
programme_settings.settings["DEFAULT"]["blosum"] = "62"
programme_settings.settings["BLAST"]["word_size"] = "4"
programme_settings.settings["BLAST"]["tscore"] = "10"
programme_settings.settings["BLAST"]["tscore_sim"] = "10"
programme_settings.write() # Persist the settings for the module
def test_exact_protein_match(self):
'''Smith-Waterman alignment: identical sequences should give maximal scores.'''
print("\nTesting Smith-Waterman alignment: self-alignment... ", end="")
seq = "ACDEFGHIK"
score = sw.perform_smith_waterman(seq, seq)
self.assertEqual(score, 53.0, "Expected exact match score to be 53.0") # Expected score based on observed output when run
print("PASSED")
def test_no_alignment(self):
'''Completely different sequences should result in minimal or zero scores.'''
print("\nTesting Smith-Waterman alignment: completely different sequences... ", end="")
seq1 = "AAAAAAA"
seq2 = "ZZZZZZZ"
score = sw.perform_smith_waterman(seq1, seq2)
self.assertEqual(score, 0, "Dissimilar sequences should yield zero scores.")
def test_nanog_alignment(self):
'''Real-world test: Nanog sequence aligned to itself should yield high score.'''
print("\nTesting Smith-Waterman alignment: Nanog protein self-alignment... ", end="")
query = "PWNAAPLHNFGEDFLQPYVQLQQNFSASDLEVNLEATRESHAHFSTPQALELFLNYSVTP"
result = sw.perform_smith_waterman(query, query)
self.assertGreaterEqual(result, 300, "Nanog should align to itself with high score.")
print(f"PASSED: Score = {result}")
def test_provided_alignment(self):
'''Recreate provided alignment test in code, expect score around 62'''
print("\nTesting Smith-Waterman alignment: two sequences with known score... ", end="")
seq1 = "MSVGLPGPHSLPSSEEASNSGNASSMPAVFHPENYSCLQGSATEMLCTEAASPRPSSEDLPLQGSPDSSTSPKQKLSSPEADKGPEEEENKVLARKQKMRTVFSQAQLCALKDRFQKQKYLSLQQMQELSSILNLSYKQVKTWFQNQRMKCKRWQKNQWLKTSNGLIQGSAPVEYPSIHCSYPQGYLVNASGSLSMWGSQTWTNPTWSSQTWTNPTWNNQTWTNPTWSSQAWTAQSWNGQPWNAAPLHNFGEDFLQPYVQLQQNFSASDLEVNLEATRESHAHFSTPQALELFLNYSVTPPGEI"
seq2 = "EESINILTVVVPGKWDLLVGLVETDLKLLFSTLLYYLNFVSRAKSWDQNPEQSNRNHSEDERQRKQNRREGGAGKEGGKEEDKVEKELQEELKKDEKEECEALSPKSTLASKSLRNSLWEKFKFSKHLTLQDILSMLFRFNKIDKQIIKWLCEKRKKYNKEMPKQKGIKRLKRPVSIRTSKAMSISPSPRGGLYQ"
score = sw.perform_smith_waterman(seq1, seq2)
self.assertEqual(score, 62, "Expected alignment score of 62 from known test pair")
print("PASSED")
def test_partial_alignment(self):
"""Partially matching sequences should have intermediate scores."""
print("\nTesting Smith-Waterman alignment: partially matching sequences... ", end="")
seq1 = "ACDEFGHIK"
seq2 = "ACDXYZHIK"
score = sw.perform_smith_waterman(seq1, seq2)
self.assertGreater(score, 20, "Partially matching sequences must yield intermediate scores.")
self.assertLess(score, 50, "Partially matching sequences must not score as high as identical sequences.")
print("PASSED")
class SmithWatermanInternalTests(unittest.TestCase):
'''Unit-level tests for matrix creation and scoring logic.'''
def setUp(self):
import smith_waterman_p as sw
programme_settings.settings["DEFAULT"]["seq_gap"] = "-8"
programme_settings.settings["DEFAULT"]["blosum"] = "62"
programme_settings.write()
sw.seqgap = -8 # Needed for calc_score()
sw.sequence1 = "A"
sw.sequence2 = "A"
sw.dist = blosum.BLOSUM(62)
def test_create_matrix_dimensions(self):
'''Test create_matrix returns matrix with correct size.'''
print("\nRunning test: create_matrix() dimensions... ", end="")
matrix = sw.create_matrix(3, 4)
self.assertEqual(len(matrix), 4)
self.assertEqual(len(matrix[0]), 5)
print("PASSED: Matrix is 4x5 (includes zero index)")
def test_calc_score_match(self):
'''Test calc_score returns correct score for a match.'''
print("\nRunning test: calc_score() for MATCH... ", end="")
sw.sequence1 = "A"
sw.sequence2 = "A"
matrix = sw.create_matrix(1, 1)
matrix[0][0] = 0
expected = sw.dist["A"]["A"]
result = sw.calc_score(matrix, 1, 1)
self.assertEqual(result, expected)
print(f"PASSED: Match score = {result}")
def test_calc_score_gap(self):
'''Test calc_score applies gap penalty correctly.'''
print("\nRunning test: calc_score() with GAP penalty... ", end="")
sw.sequence1 = "A"
sw.sequence2 = "X"
matrix = sw.create_matrix(1, 1)
matrix[0][1] = 0 # force deletion
matrix[1][0] = 0 # force insertion
result = sw.calc_score(matrix, 1, 1)
self.assertGreaterEqual(result, sw.seqgap)
print(f"PASSED: Gap penalty handled, score = {result}")
print("?" in sw.dist)
print(sw.dist["?"]['A'])
def test_calc_score_illegal_character_penalised(self):
'''Test that illegal characters are handled gracefully.'''
print("\nRunning test: calc_score() with illegal character '?'... ", end="")
sw.sequence1 = "?"
sw.sequence2 = "A"
matrix = sw.create_matrix(1, 1)
score = sw.calc_score(matrix, 1, 1)
self.assertEqual(score, 0, "Illegal character should result in zero score")
print(f"PASSED: Score returned = {score}")
def test_calc_score_unknown_residue(self):
'''Unknown residues like X should not crash and use default score.'''
print("\nRunning test: calc_score() with unknown residue... ", end="")
matrix = [[0] * 3 for _ in range(3)]
sw.sequence1 = "AX"
sw.sequence2 = "GX"
score = sw.calc_score(matrix, 2, 2) # X vs X match
self.assertIsInstance(score, (int, float))
self.assertLessEqual(score, 0) # default scores are usually low or negative
print(f"PASSED: Score computed = {score}")
if __name__ == "__main__":
unittest.main()