test: add tests/test_validate_network.py for issue #1
This commit is contained in:
1 parent
fbff81d189
commit
7fbe2fb7e5
1 file changed
+119
@@ -0,0 +1,119 @@
|
||||
"""Synthetic fixtures: no company INP or network data is embedded in tests."""
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
spec=importlib.util.spec_from_file_location('validator',Path(__file__).resolve().parents[1]/'tools/validate_network.py')
|
||||
validator=importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(validator)
|
||||
|
||||
INP='''[OPTIONS]
|
||||
FLOW_UNITS CMS
|
||||
LINK_OFFSETS DEPTH
|
||||
[JUNCTIONS]
|
||||
A 10 3 0 0 0
|
||||
B 9 4 0 0 0
|
||||
[CONDUITS]
|
||||
P A B 5 0.015 0.2 0.1 0 0
|
||||
[XSECTIONS]
|
||||
P CIRCULAR 0.6 0 0 0 1
|
||||
[COORDINATES]
|
||||
A 0 0
|
||||
B 3 4
|
||||
'''
|
||||
|
||||
|
||||
def fixture(text=INP):
|
||||
return {'meta':{'sha256':hashlib.sha256(text.encode()).hexdigest(),'flowUnits':'CMS',
|
||||
'offsetMode':'DEPTH','physicalConversion':1},
|
||||
'nodes':[{'id':'A','type':'junction','line':5,'raw':'A 10 3 0 0 0'.split(),
|
||||
'invert':10,'maxDepth':3,'top':13,'xy':[0,0],'connections':['P']},
|
||||
{'id':'B','type':'junction','line':6,'raw':'B 9 4 0 0 0'.split(),
|
||||
'invert':9,'maxDepth':4,'top':13,'xy':[3,4],'connections':['P']}],
|
||||
'links':[{'id':'P','type':'conduit','line':8,'raw':'P A B 5 0.015 0.2 0.1 0 0'.split(),
|
||||
'fromNode':'A','toNode':'B','vertices':[],'pathXY':[[0,0],[3,4]],
|
||||
'planDistance':5,'length':5,'roughness':.015,'inletOffset':.2,'outletOffset':.1,
|
||||
'invertStart':10.2,'invertEnd':9.1,'centerStart':10.5,'centerEnd':9.4,
|
||||
'lengthDelta':0,'lengthDifferenceRatio':0,'slopeInput':.22,'slopePlan':.22,
|
||||
'renderable':True,'section':{'shape':'CIRCULAR','height':.6,'width':.6,
|
||||
'diameter':.6,'barrels':1,'raw':'P CIRCULAR 0.6 0 0 0 1'.split()}}],
|
||||
'summary':{'counts':{'junction':2,'outfall':0,'storage':0,'conduit':1,'pump':0,'weir':0},
|
||||
'coordinateCount':2,'shapes':{'CIRCULAR':1},'renderedConduits':1,'vertexCount':0},'issues':[]}
|
||||
|
||||
|
||||
class ValidationTests(unittest.TestCase):
|
||||
def run_case(self,data=None,text=INP):
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
root=Path(folder)
|
||||
(root/'source.inp').write_bytes(text.encode())
|
||||
(root/'network.json').write_text(json.dumps(data or fixture(text)),encoding='utf8')
|
||||
return validator.validate(root/'source.inp',root/'network.json')
|
||||
|
||||
def test_matching_fixture(self):
|
||||
self.assertEqual(self.run_case()['status'],'passed')
|
||||
|
||||
def test_changed_diameter_is_caught(self):
|
||||
data=fixture();data['links'][0]['section']['diameter']=.9
|
||||
result=self.run_case(data)
|
||||
self.assertEqual(result['status'],'failed')
|
||||
self.assertIn('section.diameter',[e['field'] for e in result['mismatches']])
|
||||
|
||||
def test_changed_connection_is_caught(self):
|
||||
data=fixture();data['links'][0]['toNode']='A'
|
||||
self.assertEqual(self.run_case(data)['status'],'failed')
|
||||
|
||||
def test_missing_facility_is_caught(self):
|
||||
data=fixture();data['nodes'].pop()
|
||||
self.assertEqual(self.run_case(data)['status'],'failed')
|
||||
|
||||
def test_null_is_not_zero(self):
|
||||
data=fixture();data['nodes'][0]['invert']=None
|
||||
self.assertEqual(self.run_case(data)['status'],'failed')
|
||||
|
||||
def test_duplicate_inp_id_is_caught(self):
|
||||
text=INP+'A 5 6\n'
|
||||
self.assertEqual(self.run_case(fixture(text),text)['status'],'failed')
|
||||
|
||||
def test_source_hash_is_checked(self):
|
||||
data=fixture();data['meta']['sha256']='wrong'
|
||||
self.assertEqual(self.run_case(data)['status'],'failed')
|
||||
|
||||
def test_elevation_offsets_are_absolute(self):
|
||||
text=INP.replace('LINK_OFFSETS DEPTH','LINK_OFFSETS ELEVATION')
|
||||
data=fixture(text);data['meta']['offsetMode']='ELEVATION'
|
||||
data['links'][0].update(invertStart=.2,invertEnd=.1,centerStart=.5,centerEnd=.4)
|
||||
data['links'][0].update(slopeInput=.02,slopePlan=.02)
|
||||
self.assertEqual(self.run_case(data,text)['status'],'passed')
|
||||
|
||||
def test_rectangular_width_and_height_are_distinct(self):
|
||||
text=INP.replace('P CIRCULAR 0.6 0 0 0 1','P RECT_CLOSED 0.6 1.2 0 0 1')
|
||||
data=fixture(text)
|
||||
data['links'][0]['section'].update(shape='RECT_CLOSED',width=1.2,diameter=None,
|
||||
raw='P RECT_CLOSED 0.6 1.2 0 0 1'.split())
|
||||
data['summary']['shapes']={'RECT_CLOSED':1}
|
||||
self.assertEqual(self.run_case(data,text)['status'],'passed')
|
||||
data['links'][0]['section']['width']=.6
|
||||
self.assertEqual(self.run_case(data,text)['status'],'failed')
|
||||
|
||||
def test_missing_coordinate_requires_documented_exclusion(self):
|
||||
text=INP.replace('B 3 4\n','')
|
||||
data=fixture(text);data['nodes'][1]['xy']=None
|
||||
data['links'][0].update(pathXY=None,planDistance=None,renderable=False,
|
||||
lengthDelta=None,lengthDifferenceRatio=None,slopePlan=None)
|
||||
data['summary']['coordinateCount']=1
|
||||
data['summary']['renderedConduits']=0
|
||||
self.assertEqual(self.run_case(data,text)['status'],'failed')
|
||||
data['issues']=[{'code':'missing_coordinate','ids':['B']},{'code':'unplaced_link','ids':['P']}]
|
||||
self.assertEqual(self.run_case(data,text)['status'],'passed')
|
||||
|
||||
def test_unsupported_shape_does_not_pass(self):
|
||||
text=INP.replace('CIRCULAR','CUSTOM')
|
||||
with self.assertRaisesRegex(ValueError,'unsupported conduit shape'):
|
||||
self.run_case(fixture(text),text)
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user