Skip to content

Commit 3dca5aa

Browse files
authored
Add some unit tests (#306)
* Start adding unit tests to improve coverage * Add in constants for Re and g0 * tmp commit * add unit tests for checkArgs * Move functions only used by checkArgs to checkArgs module and add more tests * add more unit tests to test_checkArgs
1 parent 63ec43c commit 3dca5aa

File tree

10 files changed

+476
-84
lines changed

10 files changed

+476
-84
lines changed

test/test_checkArgs.py

Lines changed: 385 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
1+
import datetime
2+
import os
3+
import pytest
4+
import sys
5+
6+
import multiprocessing as mp
7+
import numpy as np
8+
9+
from argparse import ArgumentParser
10+
from test import TEST_DIR, pushd
11+
12+
import RAiDER.runProgram
13+
14+
from RAiDER.checkArgs import checkArgs, makeDelayFileNames, modelName2Module
15+
from RAiDER.constants import Zenith, _ZREF
16+
17+
18+
SCENARIO_1 = os.path.join(TEST_DIR, "scenario_1")
19+
SCENARIO_2 = os.path.join(TEST_DIR, "scenario_2")
20+
21+
22+
def isWriteable(dirpath):
23+
'''Test whether a directory is writeable'''
24+
try:
25+
filehandle = open(os.path.join(dirpath, 'tmp.txt'), 'w' )
26+
filehandle.close()
27+
return True
28+
except IOError:
29+
return False
30+
31+
@pytest.fixture
32+
def parsed_args(tmp_path):
33+
parser = RAiDER.runProgram.create_parser()
34+
args = parser.parse_args([
35+
'--date', '20200103',
36+
'--time', '23:00:00',
37+
#'--latlon', 'latfile.dat', 'lonfile.dat',
38+
'--bbox', '-1', '1', '-1', '1',
39+
'--model', 'ERA5',
40+
'--outformat', 'hdf5'
41+
])
42+
return args, parser
43+
44+
45+
def test_checkArgs_outfmt_1(parsed_args):
46+
'''Test that passing height levels with hdf5 outformat works'''
47+
args, p = parsed_args
48+
args.outformat = 'hdf5'
49+
args.heightlvs = [10, 100, 1000]
50+
checkArgs(args, p)
51+
assert True
52+
53+
def test_checkArgs_outfmt_2(parsed_args):
54+
'''Test that passing a raster format with height levels throws an error'''
55+
args, p = parsed_args
56+
args.heightlvs = [10, 100, 1000]
57+
args.outformat = 'envi'
58+
with pytest.raises(ValueError):
59+
checkArgs(args, p)
60+
61+
def test_checkArgs_outfmt_3(parsed_args):
62+
'''Test that passing a raster format with height levels throws an error'''
63+
args, p = parsed_args
64+
args.query_area = os.path.join(SCENARIO_2, 'stations.csv')
65+
argDict = checkArgs(args, p)
66+
assert argDict['flag'] == 'station_file'
67+
68+
def test_checkArgs_outfmt_4(parsed_args):
69+
'''Test that passing a raster format with height levels throws an error'''
70+
args, p = parsed_args
71+
args.query_area = [os.path.join(SCENARIO_1, 'geom', 'lat.dat'), os.path.join(SCENARIO_1, 'geom', 'lat.dat')]
72+
argDict = checkArgs(args, p)
73+
assert argDict['flag'] == 'files'
74+
75+
def test_checkArgs_outloc_1(parsed_args):
76+
'''Test that the default output and weather model directories are correct'''
77+
args, p = parsed_args
78+
argDict = checkArgs(args, p)
79+
out = argDict['out']
80+
wmLoc = argDict['wmLoc']
81+
assert os.path.abspath(out) == os.getcwd()
82+
assert os.path.abspath(wmLoc) == os.path.join(os.getcwd(), 'weather_files')
83+
84+
def test_checkArgs_outloc_2(parsed_args, tmp_path):
85+
'''Tests that the correct output location gets assigned when provided'''
86+
with pushd(tmp_path):
87+
args, p = parsed_args
88+
args.out = tmp_path
89+
argDict = checkArgs(args, p)
90+
out = argDict['out']
91+
assert out == tmp_path
92+
93+
def test_checkArgs_outloc_2b(parsed_args, tmp_path):
94+
''' Tests that the weather model directory gets passed through by itself'''
95+
with pushd(tmp_path):
96+
args, p = parsed_args
97+
args.out = tmp_path
98+
args.wmLoc = 'weather_dir'
99+
argDict = checkArgs(args, p)
100+
assert argDict['wmLoc'] == 'weather_dir'
101+
102+
def test_checkArgs_outloc_3(parsed_args):
103+
'''Tests that the weather model directory gets created when needed'''
104+
args, p = parsed_args
105+
argDict = checkArgs(args, p)
106+
assert os.path.isdir(argDict['wmLoc'])
107+
108+
def test_checkArgs_outloc_4(parsed_args):
109+
'''Tests for creating writeable weather model directory'''
110+
args, p = parsed_args
111+
argDict = checkArgs(args, p)
112+
113+
assert isWriteable(argDict['wmLoc'])
114+
115+
def test_ll_bounds_1(parsed_args):
116+
'''Tests that lats out of bounds raises error'''
117+
args, p = parsed_args
118+
args.query_area[0] = -91
119+
with pytest.raises(ValueError):
120+
checkArgs(args, p)
121+
122+
def test_ll_bounds_2(parsed_args):
123+
'''Tests that lats out of bounds raises error'''
124+
args, p = parsed_args
125+
args.query_area[1] = 91
126+
with pytest.raises(ValueError):
127+
checkArgs(args, p)
128+
129+
def test_los_1(parsed_args):
130+
'''Tests that lats out of bounds raises error'''
131+
args, p = parsed_args
132+
args.lineofsight = 'los.rdr'
133+
argDict = checkArgs(args, p)
134+
assert argDict['los'][0] == 'los'
135+
assert argDict['los'][1] == 'los.rdr'
136+
137+
def test_los_2(parsed_args):
138+
'''Tests that lats out of bounds raises error'''
139+
args, p = parsed_args
140+
args.statevectors = 'sv.txt'
141+
argDict = checkArgs(args, p)
142+
assert argDict['los'][0] == 'sv'
143+
assert argDict['los'][1] == 'sv.txt'
144+
145+
def test_los_3(parsed_args):
146+
'''Tests that lats out of bounds raises error'''
147+
args, p = parsed_args
148+
argDict = checkArgs(args, p)
149+
assert argDict['los'] == Zenith
150+
151+
def test_models_1a(parsed_args):
152+
'''Tests that the weather model gets passed through correctly'''
153+
args, p = parsed_args
154+
argDict = checkArgs(args, p)
155+
assert argDict['weather_model']['type'].Model() == 'ERA-5'
156+
assert argDict['weather_model']['name'] == 'era5'
157+
158+
def test_models_1b(parsed_args):
159+
'''Tests that the weather model gets passed through correctly'''
160+
args, p = parsed_args
161+
args.model = 'HRRR'
162+
argDict = checkArgs(args, p)
163+
assert argDict['weather_model']['type'].Model() == 'HRRR'
164+
assert argDict['weather_model']['name'] == 'hrrr'
165+
166+
def test_models_1c(parsed_args):
167+
'''Tests that the weather model gets passed through correctly'''
168+
args, p = parsed_args
169+
args.model = 'NCMR'
170+
argDict = checkArgs(args, p)
171+
assert argDict['weather_model']['type'].Model() == 'NCMR'
172+
assert argDict['weather_model']['name'] == 'ncmr'
173+
174+
def test_models_1d(parsed_args):
175+
'''Tests that the weather model gets passed through correctly'''
176+
args, p = parsed_args
177+
args.model = 'era-5'
178+
argDict = checkArgs(args, p)
179+
assert argDict['weather_model']['type'].Model() == 'ERA-5'
180+
assert argDict['weather_model']['name'] == 'era5'
181+
182+
def test_models_1e(parsed_args):
183+
'''Tests that the weather model gets passed through correctly'''
184+
args, p = parsed_args
185+
args.model = 'ERA-5'
186+
argDict = checkArgs(args, p)
187+
assert argDict['weather_model']['type'].Model() == 'ERA-5'
188+
assert argDict['weather_model']['name'] == 'era5'
189+
190+
def test_models_1f(parsed_args):
191+
'''Tests that the weather model gets passed through correctly'''
192+
args, p = parsed_args
193+
args.model = 'Era-5'
194+
argDict = checkArgs(args, p)
195+
assert argDict['weather_model']['type'].Model() == 'ERA-5'
196+
assert argDict['weather_model']['name'] == 'era5'
197+
198+
def test_models_2(parsed_args):
199+
'''Tests that unknown weather models get rejected'''
200+
args, p = parsed_args
201+
args.model = 'unknown'
202+
with pytest.raises(NotImplementedError):
203+
checkArgs(args, p)
204+
205+
def test_models_3a(parsed_args):
206+
'''Tests that WRF weather models requires files'''
207+
args, p = parsed_args
208+
args.model = 'WRF'
209+
with pytest.raises(RuntimeError):
210+
checkArgs(args, p)
211+
212+
def test_models_3b(parsed_args):
213+
'''Tests that HDF5 weather models requires files'''
214+
args, p = parsed_args
215+
args.model = 'HDF5'
216+
with pytest.raises(RuntimeError):
217+
checkArgs(args, p)
218+
219+
def test_models_3c(parsed_args):
220+
'''Tests that WRF weather models requires files'''
221+
args, p = parsed_args
222+
args.model = 'WRF'
223+
args.files = ['file1.wrf', 'file2.wrf']
224+
argDict = checkArgs(args, p)
225+
assert True
226+
227+
def test_zref_1(parsed_args):
228+
'''tests that default zref gets generated'''
229+
args, p = parsed_args
230+
argDict = checkArgs(args, p)
231+
assert argDict['zref'] == _ZREF
232+
233+
def test_zref_2(parsed_args):
234+
'''tests that default zref gets generated'''
235+
ztest = 20000
236+
args, p = parsed_args
237+
args.zref = ztest
238+
argDict = checkArgs(args, p)
239+
assert argDict['zref'] == ztest
240+
241+
def test_parallel_1(parsed_args):
242+
'''tests that parallel options are handled correctly'''
243+
args, p = parsed_args
244+
argDict = checkArgs(args, p)
245+
assert argDict['parallel'] == 1
246+
247+
def test_parallel_2(parsed_args):
248+
'''tests that parallel options are handled correctly'''
249+
args, p = parsed_args
250+
args.parallel = 'all'
251+
argDict = checkArgs(args, p)
252+
assert argDict['parallel'] == mp.cpu_count()
253+
254+
def test_parallel_3(parsed_args):
255+
'''tests that parallel options are handled correctly'''
256+
args, p = parsed_args
257+
args.parallel = 2
258+
argDict = checkArgs(args, p)
259+
assert argDict['parallel'] == 2
260+
261+
def test_parallel_4(parsed_args):
262+
'''tests that parallel options are handled correctly'''
263+
args, p = parsed_args
264+
args.parallel = 2000
265+
argDict = checkArgs(args, p)
266+
assert argDict['parallel'] == mp.cpu_count()
267+
268+
def test_verbose_1(parsed_args):
269+
'''tests that verbose option is handled correctly'''
270+
args, p = parsed_args
271+
argDict = checkArgs(args, p)
272+
assert not argDict['verbose']
273+
274+
def test_verbose_2(parsed_args):
275+
'''tests that verbose option is handled correctly'''
276+
args, p = parsed_args
277+
args.verbose = True
278+
argDict = checkArgs(args, p)
279+
assert argDict['verbose']
280+
281+
def test_download_only_1(parsed_args):
282+
'''tests that the download-only option is handled correctly'''
283+
args, p = parsed_args
284+
argDict = checkArgs(args, p)
285+
assert not argDict['download_only']
286+
287+
def test_download_only_2(parsed_args):
288+
'''tests that the download-only option is handled correctly'''
289+
args, p = parsed_args
290+
args.download_only = True
291+
argDict = checkArgs(args, p)
292+
assert argDict['download_only']
293+
294+
def test_useWeatherNodes_1(parsed_args):
295+
'''tests that the correct flag gets passed'''
296+
args, p = parsed_args
297+
argDict = checkArgs(args, p)
298+
assert argDict['flag'] == 'bounding_box'# default arguments use a bounding box
299+
300+
def test_filenames_1(parsed_args):
301+
'''tests that the correct filenames are generated'''
302+
args, p = parsed_args
303+
argDict = checkArgs(args, p)
304+
assert 'Delay' not in argDict['wetFilenames'][0]
305+
assert 'wet' in argDict['wetFilenames'][0]
306+
assert 'hydro' in argDict['hydroFilenames'][0]
307+
assert '20200103' in argDict['wetFilenames'][0]
308+
assert '20200103' in argDict['hydroFilenames'][0]
309+
assert len(argDict['hydroFilenames']) == 1
310+
311+
def test_filenames_2(parsed_args):
312+
'''tests that the correct filenames are generated'''
313+
args, p = parsed_args
314+
args.query_area = os.path.join(SCENARIO_2, 'stations.csv')
315+
argDict = checkArgs(args, p)
316+
assert 'Delay' in argDict['wetFilenames'][0]
317+
assert '20200103' in argDict['wetFilenames'][0]
318+
assert len(argDict['wetFilenames']) == 1
319+
320+
def test_makeDelayFileNames_1():
321+
assert makeDelayFileNames(None, None, "h5", "name", "dir") == \
322+
("dir/name_wet_ztd.h5", "dir/name_hydro_ztd.h5")
323+
324+
def test_makeDelayFileNames_2():
325+
assert makeDelayFileNames(None, (), "h5", "name", "dir") == \
326+
("dir/name_wet_std.h5", "dir/name_hydro_std.h5")
327+
328+
def test_makeDelayFileNames_3():
329+
assert makeDelayFileNames(datetime.datetime(2020, 1, 1, 1, 2, 3), None, "h5", "model_name", "dir") == \
330+
(
331+
"dir/model_name_wet_20200101T010203_ztd.h5",
332+
"dir/model_name_hydro_20200101T010203_ztd.h5"
333+
)
334+
335+
def test_makeDelayFileNames_4():
336+
assert makeDelayFileNames(datetime.datetime(1900, 12, 31, 1, 2, 3), "los", "h5", "model_name", "dir") == \
337+
(
338+
"dir/model_name_wet_19001231T010203_std.h5",
339+
"dir/model_name_hydro_19001231T010203_std.h5"
340+
)
341+
342+
def test_model2module():
343+
model_module_name, model_obj = modelName2Module('ERA5')
344+
assert model_obj().Model() == 'ERA-5'
345+
346+
def test_dem_1(parsed_args):
347+
'''Test that passing a raster format with height levels throws an error'''
348+
args, p = parsed_args
349+
argDict = checkArgs(args, p)
350+
assert argDict['heights'][0] == 'skip'
351+
assert argDict['heights'][1] is None
352+
353+
def test_dem_2(parsed_args):
354+
'''Test that passing a raster format with height levels throws an error'''
355+
args, p = parsed_args
356+
args.heightlvs = [10, 100, 1000]
357+
argDict = checkArgs(args, p)
358+
assert argDict['heights'][0] == 'lvs'
359+
assert np.allclose(argDict['heights'][1], [10, 100, 1000])
360+
361+
def test_dem_3(parsed_args):
362+
'''Test that passing a raster format with height levels throws an error'''
363+
args, p = parsed_args
364+
args.heightlvs = [10, 100, 1000]
365+
args.query_area = os.path.join(SCENARIO_2, 'stations.csv')
366+
argDict = checkArgs(args, p)
367+
assert argDict['heights'][0] == 'lvs'
368+
assert np.allclose(argDict['heights'][1], [10, 100, 1000])
369+
370+
def test_dem_4(parsed_args):
371+
'''Test that passing a raster format with height levels throws an error'''
372+
args, p = parsed_args
373+
args.query_area = os.path.join(SCENARIO_2, 'stations.csv')
374+
argDict = checkArgs(args, p)
375+
assert argDict['heights'][0] == 'pandas'
376+
assert argDict['heights'][1][0] == argDict['wetFilenames'][0]
377+
378+
def test_dem_5(parsed_args):
379+
'''Test that passing a raster format with height levels throws an error'''
380+
args, p = parsed_args
381+
args.query_area = [os.path.join(SCENARIO_1, 'geom', 'lat.dat'), os.path.join(SCENARIO_1, 'geom', 'lat.dat')]
382+
argDict = checkArgs(args, p)
383+
assert argDict['heights'][0] == 'download'
384+
assert argDict['heights'][1] == os.path.join(argDict['out'], 'geom', 'warpedDEM.dem')
385+

test/test_scenario_1.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
from RAiDER.constants import Zenith
1212
from RAiDER.delay import tropo_delay
13-
from RAiDER.utilFcns import gdal_open, makeDelayFileNames, modelName2Module
13+
from RAiDER.utilFcns import gdal_open
14+
from RAiDER.checkArgs import makeDelayFileNames, modelName2Module
1415

1516
SCENARIO_DIR = os.path.join(TEST_DIR, "scenario_1")
1617
_RTOL = 1e-4

0 commit comments

Comments
 (0)