Unit Tests

Unit testing is done with pytest. To run all the tests, simply cd to the top level directory and type this shell command:

$ pytest

The tests are all in the tests directory:

  • test_main.py has functions that test each of the paths defined in main.py
  • test_optipass.py has functions that test the interface to OptiPass

You can run one set of tests by including the file name in the shell command, e.g.

$ pytest test/test_optipass.py

Tests for main.py

test_projects()

Make sure the demo project is one of the projectes.

Source code in test/test_main.py
14
15
16
17
18
19
20
def test_projects():
    '''
    Make sure the demo project is one of the projectes.
    '''
    resp = client.get('/projects')
    lst = resp.json()
    assert 'demo' in lst

test_html_demo()

Fetch the welcome message for the demo project, look for key words

Source code in test/test_main.py
22
23
24
25
26
27
28
29
30
def test_html_demo():
    '''
    Fetch the welcome message for the demo project, look for key words
    '''
    resp = client.get('/html/demo/welcome.html')
    s = resp.json()
    assert s.count('<p>') == 11
    assert s.count('OptiPass') == 9
    assert s.count('FastAPI') == 1

test_barriers_demo()

Test the barriers entry point

Source code in test/test_main.py
32
33
34
35
36
37
38
39
40
41
42
43
44
def test_barriers_demo():
    '''
    Test the barriers entry point
    '''
    resp = client.get('/barriers/demo')
    dct = resp.json()
    assert dct['project'] == 'demo'
    contents = dct['barriers'].split('\n')
    assert len(contents) == 7
    header = contents[0].split(',')
    assert header[0] == 'ID' and header[-1] == 'comment'
    gates = { line.split(',')[0] for line in contents[1:] }
    assert gates == {'A','B','C','D','E','F'}

test_mapinfo_demo()

Test the mapinfo entry point

Source code in test/test_main.py
46
47
48
49
50
51
52
53
54
55
def test_mapinfo_demo():
    '''
    Test the mapinfo entry point
    '''
    resp = client.get('/mapinfo/demo')
    dct = resp.json()
    info = json.loads(dct['mapinfo'])
    assert type(info) == dict
    assert info['map_type'] == 'StaticMap'
    assert info['map_file'] == 'Riverlands.png'

test_targets_demo()

Test the targets entry point with the demo project

Source code in test/test_main.py
57
58
59
60
61
62
63
64
65
66
67
68
69
def test_targets_demo():
    '''Test the targets entry point with the demo project'''
    resp = client.get('/targets/demo')
    dct = resp.json()
    assert dct['project'] == 'demo'
    contents = dct['targets'].split('\n')
    assert len(contents) == 3
    header = contents[0].split(',')
    assert header[0] == 'abbrev' and header[-1] == 'infra'
    targets = { line.split(',')[0] for line in contents[1:] }
    assert targets == {'T1','T2'}
    layout = dct['layout']
    assert layout == 'T1 T2'

test_unknown_project()

Each of the paths should check for an unknown project name

Source code in test/test_main.py
79
80
81
82
83
84
85
86
87
88
89
90
91
92
def test_unknown_project():
    '''
    Each of the paths should check for an unknown project name
    '''
    paths = [
        '/html/foo/welcome.html',
        '/barriers/foo',
        '/mapinfo/foo',
        '/targets/foo',
        '/colnames/foo',
    ]
    for p in paths:
        resp = client.get(p)
        assert resp.status_code == 404

test_unknown_files()

Paths that fetch file should return 404 not found responses

Source code in test/test_main.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def test_unknown_files():
    '''
    Paths that fetch file should return 404 not found responses 
    '''
    paths = [
        '/html/demo/xxx.html',
        '/map/demo/xxx.png',
    ]
    for p in paths:
        resp = client.get(p)
        dct = resp.json()
        assert resp.status_code == 404
        assert 'not found' in dct['detail']

Tests for optipass,py

test_OP(barriers, targets, colnames)

Test the OP constructor. Make an instance, make sure all instance vars set to defaults.

Source code in test/test_optipass.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def test_OP(barriers, targets, colnames):
    '''
    Test the OP constructor. Make an instance, make sure all instance vars set
    to defaults.
    '''
    op = OptiPass(barriers, targets, colnames, [], [])
    assert len(op.barriers) == 0
    assert len(op.passability) == 0
    assert len(op.targets) == 0
    assert len(op.mapping) == 0
    assert op.weighted == False
    assert op.input_frame is None
    assert op.paths is None
    assert op.summary is None
    assert op.matrix is None
    assert op.tmpdir is None

test_OP_with_existing_data(barriers, targets, colnames)

Test the OP constructor when a path to existing data is passed

Source code in test/test_optipass.py
51
52
53
54
55
56
57
def test_OP_with_existing_data(barriers, targets, colnames):
    '''
    Test the OP constructor when a path to existing data is passed
    '''
    p = Path(os.path.dirname(__file__)) / 'fixtures' / 'Example_1'
    op = OptiPass(barriers, targets, colnames, [], [], tmpdir =p)
    assert op.tmpdir == p

test_region_and_target_names(barriers, targets, colnames)

Verify region and target names

Source code in test/test_optipass.py
59
60
61
62
63
64
65
66
67
68
def test_region_and_target_names(barriers, targets, colnames):
    '''
    Verify region and target names
    '''
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1','T2'])
    assert len(op.barriers) == 6
    assert len(op.passability) == 6
    assert len(op.targets) == 2
    assert len(op.mapping) == 2
    assert list(op.mapping.index) == list(op.targets.index)

test_one_region_and_one_target(barriers, targets, colnames)

Check barrier frame when only one region and one target is used

Source code in test/test_optipass.py
70
71
72
73
74
75
76
77
78
79
def test_one_region_and_one_target(barriers, targets, colnames):
    '''
    Check barrier frame when only one region and one target is used
    '''
    op = OptiPass(barriers, targets, colnames, ['Trident'], ['T1'])
    assert len(op.barriers) == 4
    assert len(op.passability) == 4
    assert len(op.targets) == 1
    assert len(op.mapping) == 1
    assert list(op.mapping.index) == list(op.targets.index)

test_one_target_input(barriers, targets, colnames)

Make an input file for single target.

Source code in test/test_optipass.py
81
82
83
84
85
86
87
88
89
90
91
92
def test_one_target_input(barriers, targets, colnames):
    '''
    Make an input file for single target.
    '''
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1'])
    op.create_input_frame()
    df = op.input_frame
    assert len(df) == 6
    assert list(df.columns) == ['ID', 'REG', 'FOCUS', 'DSID', 'HAB_T1', 'PRE_T1', 'NPROJ', 'ACTION', 'COST', 'POST_T1']
    assert df.FOCUS.sum() == 6
    assert df.ACTION.sum() == 0
    assert df.COST.sum() == 590000

test_two_target_input(barriers, targets, colnames)

Make an input file for two targets

Source code in test/test_optipass.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def test_two_target_input(barriers, targets, colnames):
    '''
    Make an input file for two targets
   '''
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1','T2'])
    op.create_input_frame()
    df = op.input_frame
    assert len(df) == 6
    assert list(df.columns) == ['ID', 'REG', 'FOCUS', 'DSID', 'HAB_T1', 'HAB_T2', 'PRE_T1', 'PRE_T2', 'NPROJ', 'ACTION', 'COST', 'POST_T1', 'POST_T2']
    assert df.FOCUS.sum() == 6
    assert df.ACTION.sum() == 0
    assert df.COST.sum() == 590000

test_unweighted(barriers, targets, colnames)

Make sure weights are initialized to 1s, one per target

Source code in test/test_optipass.py
107
108
109
110
111
112
def test_unweighted(barriers, targets, colnames):
    '''
    Make sure weights are initialized to 1s, one per target
    '''
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1','T2'])
    assert op.weights == [1,1]

test_weighted(barriers, targets, colnames)

Make sure weights are initialized with specified settings

Source code in test/test_optipass.py
114
115
116
117
118
119
def test_weighted(barriers, targets, colnames):
    '''
    Make sure weights are initialized with specified settings
   '''
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1','T2'], weights=[1,2])
    assert op.weights == [1,2]

test_paths(barriers, targets, colnames)

Verify the paths downstream from gates

Source code in test/test_optipass.py
121
122
123
124
125
126
127
128
129
130
131
def test_paths(barriers, targets, colnames):
    '''
    Verify the paths downstream from gates
    '''
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1'])
    op.create_input_frame()
    op.create_paths()
    assert op.paths['F'] == ['F','D','A']
    assert op.paths['C'] == ['C','B','A']
    assert op.paths['B'] == ['B','A']
    assert op.paths['A'] == ['A']

test_output_parser_one_target(barriers, targets, colnames)

Parse an output file with only one target

Source code in test/test_optipass.py
133
134
135
136
137
138
139
140
141
142
143
def test_output_parser_one_target(barriers, targets, colnames):
    '''
    Parse an output file with only one target
    '''
    p = Path(os.path.dirname(__file__)) / 'fixtures' / 'Example_1' / 'output_5.txt'
    cols = { x: [] for x in ['budget', 'habitat', 'gates']}
    op = OptiPass(barriers, targets, colnames, ['Test1'], ['T1'])
    op.parse_output(p, cols)
    assert len(cols['budget']) == 1 and round(cols['budget'][0]) == 500000
    assert len(cols['habitat']) == 1 and round(cols['habitat'][0], 2) == 8.52
    assert len(cols['gates']) == 1 and cols['gates'][0] == ['A', 'B', 'C', 'F']

test_output_parser_two_targets(barriers, targets, colnames)

Parse an output file with two targets

Source code in test/test_optipass.py
145
146
147
148
149
150
151
152
153
154
155
def test_output_parser_two_targets(barriers, targets, colnames):
    '''
    Parse an output file with two targets
   '''
    p = Path(os.path.dirname(__file__)) / 'fixtures' / 'Example_4' / 'output_5.txt'
    cols = { x: [] for x in ['budget', 'habitat', 'gates']}
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1','T2'])
    op.parse_output(p, cols)
    assert len(cols['budget']) == 1 and round(cols['budget'][0]) == 500000
    assert len(cols['habitat']) == 1 and round(cols['habitat'][0], 3) == 32.936
    assert len(cols['gates']) == 1 and cols['gates'][0] == ['A', 'B', 'C', 'F']

test_example_1(barriers, targets, colnames)

Collect all the results for Example 1 from the OptiPass User Manual.

Source code in test/test_optipass.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
def test_example_1(barriers, targets, colnames):
    '''
    Collect all the results for Example 1 from the OptiPass User Manual.
    '''
    p = Path(os.path.dirname(__file__)) / 'fixtures' / 'Example_1'
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1'], tmpdir=p)
    op.create_input_frame()
    op.create_paths()
    op.collect_results()

    assert type(op.summary) == pd.DataFrame
    assert len(op.summary) == 6
    assert round(op.summary.budget.sum()) == 1500000
    assert round(op.summary.habitat.sum(),2) == 23.30

    budget_cols = [col for col in op.matrix.columns if isinstance(col,int)]
    assert budget_cols == list(op.summary.budget)

    # these comprehensions make lists of budgets where a specified gate was selected,
    # e.g. gate A is in the $400K and $500K budgets and D is never selected.
    assert [b for b in op.matrix.columns if isinstance(b, int) and op.matrix.loc['A',b]] == [400000,500000]
    assert [b for b in op.matrix.columns if isinstance(b, int) and op.matrix.loc['D',b]] == [ ]
    assert [b for b in op.matrix.columns if isinstance(b, int) and op.matrix.loc['E',b]] == [100000,300000]

    assert list(op.matrix['count']) == [2,4,3,0,2,1]   # number of times each gate is part of a solution

test_example_4(barriers, targets, colnames)

Same as test_example_1, but using Example 4, which has two restoration targets.

Source code in test/test_optipass.py
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def test_example_4(barriers, targets, colnames):
    '''
    Same as test_example_1, but using Example 4, which has two restoration targets.
    '''
    p = Path(os.path.dirname(__file__)) / 'fixtures' / 'Example_4'
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1','T2'], tmpdir=p)
    op.create_input_frame()
    op.create_paths()
    op.collect_results()

    assert type(op.summary) == pd.DataFrame
    assert len(op.summary) == 6
    assert round(op.summary.budget.sum()) == 1500000
    assert round(op.summary.habitat.sum(),2) == 95.21

    # using two targets does not change the gate selections
    assert [b for b in op.matrix.columns if isinstance(b, int) and op.matrix.loc['A',b]] == [400000,500000]
    assert [b for b in op.matrix.columns if isinstance(b, int) and op.matrix.loc['D',b]] == [ ]
    assert [b for b in op.matrix.columns if isinstance(b, int) and op.matrix.loc['E',b]] == [100000,300000]

    assert list(op.matrix['count']) == [2,4,3,0,2,1]   # number of times each gate is part of a solution

test_potential_habitat_1(barriers, targets, colnames)

Test the method that computes potential habitat, using the results genearated for Example 1 in the OptiPass manual (Box 9).

Source code in test/test_optipass.py
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def test_potential_habitat_1(barriers, targets, colnames):
    '''
    Test the method that computes potential habitat, using the results 
    genearated for Example 1 in the OptiPass manual (Box 9).
    '''
    p = Path(os.path.dirname(__file__)) / 'fixtures' / 'Example_1'
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1'], tmpdir=p)
    op.create_input_frame()
    op.create_paths()
    op.collect_results()

    m = op.summary
    assert len(m) == 6
    assert 'T1' in m.columns and 'wph' in m.columns
    assert round(m.wph[0],3) == 1.238      # PTNL_HABITAT at $0
    assert round(m.wph[5],3) == 8.520      # PTNL_HABITAT at $500K

test_potential_habitat_4(barriers, targets, colnames)

Same as test_potential_habitat_1, but using Example 4, with two restoration targets weighted differently (Box 11)

Source code in test/test_optipass.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
def test_potential_habitat_4(barriers, targets, colnames):
    '''
    Same as test_potential_habitat_1, but using Example 4, with two restoration targets
    weighted differently (Box 11)
   '''
    p = Path(os.path.dirname(__file__)) / 'fixtures' / 'Example_4'
    op = OptiPass(barriers, targets, colnames, ['Trident', 'Red Fork'], ['T1','T2'], weights=[3,1], tmpdir=p)
    op.create_input_frame()
    op.create_paths()
    op.collect_results()

    m = op.summary
    assert len(m) == 6
    assert 'T1' in m.columns and 'T2' in m.columns and 'wph' in m.columns
    assert round(m.wph[0],3) == 5.491
    assert round(m.wph[4],3) == 21.084    # PTNL_HABITAT at $400K