Skip to content

Commit 86ba2c3

Browse files
Feature/test cases (#89)
* Added custom tsv parser utility * Feature added Test cases for acceptance testing * Update CONTRIBUTION Documentation * Fix type declaration * Optimised undesired school and center assignment * skip sus test. move tests to separate module. --------- Co-authored-by: Samrat Pant <Samrat Pant> Co-authored-by: sumanashrestha <[email protected]>
1 parent cd9ff04 commit 86ba2c3

File tree

6 files changed

+215
-4
lines changed

6 files changed

+215
-4
lines changed

.coveragerc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[run]
2+
omit = app.py, test/utils/*

CONTRIBUTION.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ Thank you for considering contributing to our project! We welcome contributions
2323
4. Install dependencies using `pip install -r requirements.txt`
2424
5. Install pre-commit hook using `pre-commit install`
2525
6. Run tests using `pytest tests/test_file_name.py ` or specific test name like `pytest tests/test_file_name.py::test_function_name`
26-
7. Do not push changes without the tests and coverage passing
27-
8. Commit your changes with **proper** commit messages in imperative form like `Add my best feature`, `Fix issues casusing whatever`, `Update docs` etc: [Good reference here](https://cbea.ms/git-commit/)
28-
9. Make changes and push to your forked repository
29-
10. Create PR to the forked repository as mentioned below
26+
7. Ensure the features is passing the acceptance criteria by `pytest test_results.py ` or `python test_results.py `
27+
8. Do not push changes without the tests and coverage passing
28+
9. Commit your changes with **proper** commit messages in imperative form like `Add my best feature`, `Fix issues casusing whatever`, `Update docs` etc: [Good reference here](https://cbea.ms/git-commit/)
29+
10. Make changes and push to your forked repository
30+
11. Create PR to the forked repository as mentioned below
3031

3132

3233
### Pull Requests (PRs):

test/__init__.py

Whitespace-only changes.

test/test_results.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import unittest
2+
import sys
3+
import os
4+
import subprocess
5+
import warnings
6+
7+
# Add the parent directory to the Python path
8+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9+
10+
from test.utils.custom_tsv_parser import ParseTSVFile
11+
12+
PREF_CUTOFF = -4
13+
14+
15+
def get_scode_cscode_id(data):
16+
# Create an id with scode and center with sorted values of scode and center
17+
school_centers = []
18+
for row in data:
19+
scode = int(row["scode"])
20+
center = int(row["cscode"])
21+
sccode_center_id = sorted((scode, center))
22+
sccode_center_id = "_".join(map(str, sccode_center_id))
23+
school_centers.append(sccode_center_id)
24+
return school_centers
25+
26+
27+
class TestSchoolCenter(unittest.TestCase):
28+
"""_Tests to validate the outcome of the output are matching
29+
as per the requirements
30+
31+
Needs the ouptut from the results/school-center.tsv and
32+
results/school-center-distance.tsv files to be present
33+
34+
"""
35+
36+
def setUp(self):
37+
self.school_center_file = "results/school-center.tsv"
38+
self.school_center_distance_file = "results/school-center-distance.tsv"
39+
self.school_center_pref_file = "sample_data/prefs.tsv"
40+
schools_tsv = "sample_data/schools_grade12_2081.tsv"
41+
centers_tsv = "sample_data/centers_grade12_2081.tsv"
42+
cmd = f"python school_center.py {schools_tsv} {centers_tsv} {self.school_center_pref_file}"
43+
subprocess.run(cmd, shell=True)
44+
45+
def tearDown(self):
46+
os.remove(self.school_center_file)
47+
os.remove(self.school_center_distance_file)
48+
49+
def test_results_exists(self):
50+
"""_Test if the application in running which output the results in the
51+
results filder_
52+
53+
Returns:
54+
Pass: If the file exists in the results folder
55+
Fail: If the file doesnot exists in the results folder
56+
"""
57+
self.assertTrue(os.path.exists(self.school_center_file))
58+
self.assertTrue(os.path.exists(self.school_center_distance_file))
59+
self.assertTrue(os.path.exists(self.school_center_pref_file))
60+
61+
def test_scode_student_count_not_more_than_200(self):
62+
"""_Test if the student count is not more than 200_
63+
Test case ID 001:- एक विद्यालयको परिक्षार्थी संख्या हेरी सकभर १००, २०० भन्दा बढी
64+
परीक्षार्थी एकै केन्द्रमा नपर्ने गरी बाँढ्न पर्ने
65+
66+
Returns:
67+
Pass: If the student count is not more than 100
68+
Fail: If the student count is more than 100
69+
"""
70+
ptf = ParseTSVFile(self.school_center_file)
71+
data = ptf.get_rows()
72+
for row in data:
73+
student_count = row["allocation"]
74+
if int(student_count) > 200:
75+
warnings.warn(f"student count is more than 200 for the school {row}")
76+
77+
78+
def test_scode_cscode_not_same(self):
79+
"""_Test if the output of scode is not equal to cscode_
80+
Test case ID :- आफ्नै विद्यालयमा केन्द्र पार्न नहुने
81+
82+
Returns:
83+
Pass: If the scode is not same as cscode
84+
Fail: If the scode is same as cscode
85+
"""
86+
scf = ParseTSVFile(self.school_center_file)
87+
data = scf.get_rows()
88+
failures = []
89+
for row in data:
90+
scode = row["scode"]
91+
cscode = row["cscode"]
92+
if scode == cscode:
93+
failures.append(f"scode and cscode are same for row {row} {scode}")
94+
assert len(failures) == 0, f'{len(failures)} rows failed. {chr(10).join(failures)}'
95+
96+
def test_no_mutual_centers(self):
97+
"""_Test if the scode's center is not same as cscode's
98+
centre and vice versa_
99+
Test case ID :- दुई विद्यालयका परीक्षार्थीको केन्द्र एक अर्कामा पर्न नहुने, अर्थात् कुनै विद्यालयका परीक्षार्थीको केन्द्र परेको विद्यालयका परीक्षार्थीहरूको केन्द्र अघिल्लो विद्यालयमा पार्न नहुने ।
100+
101+
Returns:
102+
Pass: If the scode's center is not same as cscode's center
103+
Fail: If the scode's center is same as cscode's center
104+
"""
105+
scf = ParseTSVFile(self.school_center_file)
106+
data = scf.get_rows()
107+
scodes_centers = []
108+
109+
scodes_centers = get_scode_cscode_id(data)
110+
111+
# Check if there are any duplicates id if duplicate then there is a collision between school and center
112+
duplicates = [
113+
item for item in set(scodes_centers) if scodes_centers.count(item) > 1
114+
]
115+
116+
self.assertFalse(
117+
duplicates,
118+
f"Duplicate values found in scode_center_code: {', '.join(duplicates)}",
119+
)
120+
121+
@unittest.skip ("needs review")
122+
def test_undesired_cscode_scode_pair(self):
123+
"""_Test if the schools and the centers are not matched based on the
124+
cost preferences defined in the prefs.tsv file_
125+
Test case ID :-
126+
1 एकै स्वामित्व / व्यवस्थापनको भनी पहिचान भएका केन्द्रमा पार्न नहुने
127+
2 विगतमा कुनै विद्यालयको कुनै केन्द्रमा पार्दा समस्या देखिएकोमा केन्द्र दोहोऱ्याउन नहुने
128+
129+
Returns:
130+
Pass: If the schools with undesired scodes are are not paired with its cscodes
131+
Fail: If the schools with same management are each other's center
132+
"""
133+
134+
scf = ParseTSVFile(self.school_center_file)
135+
cpf = ParseTSVFile(self.school_center_pref_file)
136+
data_scf = scf.get_rows()
137+
data_cpf = cpf.get_rows()
138+
for cpf_data in data_cpf:
139+
if int(cpf_data["pref"]) < PREF_CUTOFF:
140+
data_cpf.remove(cpf_data)
141+
142+
failures = []
143+
144+
scodes_centers = get_scode_cscode_id(data_scf)
145+
146+
undesired_csodes_centers = get_scode_cscode_id(data_cpf)
147+
for undesired_cscodes_center in undesired_csodes_centers:
148+
if undesired_cscodes_center in scodes_centers:
149+
failures.append(
150+
f"Schools with undesired centers {undesired_cscodes_center}"
151+
)
152+
153+
assert len(failures) == 0, f'{len(failures)} rows failed. {chr(10).join(failures)}'
154+
155+
156+
157+
if __name__ == "__main__":
158+
unittest.main()

test/utils/__init__.py

Whitespace-only changes.

test/utils/custom_tsv_parser.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import csv
2+
3+
4+
class ParseTSVFile:
5+
def __init__(self, file_path):
6+
self.file_path = file_path
7+
8+
def read_file(self):
9+
with open(self.file_path, "r", newline="", encoding="utf-8") as file:
10+
reader = csv.DictReader(file, delimiter="\t")
11+
return reader
12+
13+
def get_columns(self):
14+
with open(self.file_path, "r", newline="", encoding="utf-8") as file:
15+
reader = csv.DictReader(file, delimiter="\t")
16+
return reader.fieldnames
17+
18+
def get_row_count(self):
19+
with open(self.file_path, "r", newline="", encoding="utf-8") as file:
20+
reader = csv.DictReader(file, delimiter="\t")
21+
return len(list(reader))
22+
23+
def get_column_count(self):
24+
with open(self.file_path, "r", newline="", encoding="utf-8") as file:
25+
reader = csv.DictReader(file, delimiter="\t")
26+
return len(reader.fieldnames)
27+
28+
def get_rows(self):
29+
with open(self.file_path, "r", newline="", encoding="utf-8") as file:
30+
reader = csv.DictReader(file, delimiter="\t")
31+
data = []
32+
for row in reader:
33+
data.append(row)
34+
return data
35+
36+
def get_column_data(self, column_name):
37+
with open(self.file_path, "r", newline="", encoding="utf-8") as file:
38+
reader = csv.DictReader(file, delimiter="\t")
39+
data = []
40+
for row in reader:
41+
data.append(row[column_name])
42+
return data
43+
44+
def get_row_data(self, row_number):
45+
with open(self.file_path, "r", newline="", encoding="utf-8") as file:
46+
reader = csv.DictReader(file, delimiter="\t")
47+
data = []
48+
for row in reader:
49+
data.append(row)
50+
return data[row_number]

0 commit comments

Comments
 (0)