Integration Tests

Integration tests are implemented in the top level application (main.py).

To perorm a test, run main with options that specify which type of test to run and where to find the data for the test.

See the documentation for main.py for details and examples.

Unit Tests

If a module has unit tests they are included in the source file. Tests are defined at the end of the file, in a class that has a name that starts with Test. For example, project.py defines a class named Project, and at the end of the file is another class named TestProject.

A test class defines a series of static methods that have names beginning with test. These methods are run in order.

To run tests for a module, open a terminal window and cd to the top level folder. Then type a command that runs pytest, including the name of the module to test:

$ pytest src/tidegates/project.py 

It's also possible to run all the tests with a single shell command:

$ pytest src/tidegates/*.py


TestProject

Source code in src/tidegates/project.py
 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
class TestProject:

    @staticmethod
    def test_load():
        '''
        Load the test data frame, expect to find 6 rows, with single letter
        barrier IDs
        '''
        p = Project('static/test_wb.csv', DataSet.OPM)
        assert isinstance(p.data, pd.DataFrame)
        assert len(p.data) == 6
        assert list(p.data.BARID) == list('ABCDEF')

    @staticmethod
    def test_regions():
        '''
        The list of region names should be sorted from north to south
        '''
        p = Project('static/workbook.csv', DataSet.TNC_OR)
        assert len(p.regions) == 15
        assert p.regions[0] == 'Columbia'
        assert p.regions[-1] == 'Coquille'

    @staticmethod
    def test_map_info():
        '''
        The frame with map information should have 5 columns
        '''
        p = Project('static/workbook.csv', DataSet.TNC_OR)
        assert isinstance(p.map_info, pd.DataFrame)
        assert len(p.map_info) == len(p.data)
        assert list(p.map_info.columns) == ['id','region','type','x','y']

    @staticmethod
    def test_targets():
        '''
        There should be two sets of targets, each with 10 entries,
        and one entry for each target in the map.
        '''
        p = Project('static/workbook.csv', DataSet.TNC_OR)

        assert len(p.targets) == 2
        assert len(p.targets['Current']) == 10
        assert len(p.targets['Future']) == 10

        t = p.targets['Current']['CO']
        assert t.short == 'Coho'
        assert t.long == 'Coho Streams'
        assert t.habitat == 'sCO' 
        assert t.prepass == 'PREPASS_CO'
        assert t.postpass == 'POSTPASS'

        assert len(p.target_map) == 10
        assert p.target_map['Coho Streams'] == 'CO'

test_load() staticmethod

Load the test data frame, expect to find 6 rows, with single letter barrier IDs

src/tidegates/project.py
84
85
86
87
88
89
90
91
92
93
@staticmethod
def test_load():
    '''
    Load the test data frame, expect to find 6 rows, with single letter
    barrier IDs
    '''
    p = Project('static/test_wb.csv', DataSet.OPM)
    assert isinstance(p.data, pd.DataFrame)
    assert len(p.data) == 6
    assert list(p.data.BARID) == list('ABCDEF')

test_map_info() staticmethod

The frame with map information should have 5 columns

src/tidegates/project.py
105
106
107
108
109
110
111
112
113
@staticmethod
def test_map_info():
    '''
    The frame with map information should have 5 columns
    '''
    p = Project('static/workbook.csv', DataSet.TNC_OR)
    assert isinstance(p.map_info, pd.DataFrame)
    assert len(p.map_info) == len(p.data)
    assert list(p.map_info.columns) == ['id','region','type','x','y']

test_regions() staticmethod

The list of region names should be sorted from north to south

src/tidegates/project.py
 95
 96
 97
 98
 99
100
101
102
103
@staticmethod
def test_regions():
    '''
    The list of region names should be sorted from north to south
    '''
    p = Project('static/workbook.csv', DataSet.TNC_OR)
    assert len(p.regions) == 15
    assert p.regions[0] == 'Columbia'
    assert p.regions[-1] == 'Coquille'

test_targets() staticmethod

There should be two sets of targets, each with 10 entries, and one entry for each target in the map.

src/tidegates/project.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
@staticmethod
def test_targets():
    '''
    There should be two sets of targets, each with 10 entries,
    and one entry for each target in the map.
    '''
    p = Project('static/workbook.csv', DataSet.TNC_OR)

    assert len(p.targets) == 2
    assert len(p.targets['Current']) == 10
    assert len(p.targets['Future']) == 10

    t = p.targets['Current']['CO']
    assert t.short == 'Coho'
    assert t.long == 'Coho Streams'
    assert t.habitat == 'sCO' 
    assert t.prepass == 'PREPASS_CO'
    assert t.postpass == 'POSTPASS'

    assert len(p.target_map) == 10
    assert p.target_map['Coho Streams'] == 'CO'

TestTargets

Source code in src/tidegates/targets.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
class TestTargets:

    @staticmethod
    def test_TNC_columns():
        '''
        Make sure the column names in the Target objects are the same as
        the column names in the data file
        '''
        with open('static/workbook.csv') as f:
            cols = f.readline().strip().split(',')
        targets = make_targets(DataSet.TNC_OR)
        for s in ['Current', 'Future']:
            for t in targets[s].values():
                assert t.habitat in cols
                assert t.prepass in cols
                assert t.postpass in cols
                assert t.unscaled in cols

    @staticmethod
    def test_OPM_columns():
        with open('static/test_wb.csv') as f:
            cols = f.readline().strip().split(',')
        targets = make_targets(DataSet.OPM)
        for s in ['1','2']:
            name = 'T' + s
            assert name in targets
            t = targets[name]
            assert t.habitat in cols
            assert t.prepass in cols
            assert t.postpass in cols

test_TNC_columns() staticmethod

Make sure the column names in the Target objects are the same as the column names in the data file

src/tidegates/targets.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
@staticmethod
def test_TNC_columns():
    '''
    Make sure the column names in the Target objects are the same as
    the column names in the data file
    '''
    with open('static/workbook.csv') as f:
        cols = f.readline().strip().split(',')
    targets = make_targets(DataSet.TNC_OR)
    for s in ['Current', 'Future']:
        for t in targets[s].values():
            assert t.habitat in cols
            assert t.prepass in cols
            assert t.postpass in cols
            assert t.unscaled in cols

TestOP

Source code in src/tidegates/optipass.py
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
class TestOP:

    @staticmethod
    def test_instantiate_object():
        '''
        Test the OP constructor.
        '''
        p = Project('static/workbook.csv', DataSet.TNC_OR)
        op = OP(p, ['Coos'], ['CO','CH'], ['1','1'], 'Current')
        assert op.project == p
        assert op.regions == ['Coos']
        assert op.climate == 'Current'
        assert op.weights == [1,1]
        assert len(op.targets) == 2
        t = op.targets[0]
        assert t.abbrev == 'CO'
        assert t.short == 'Coho'
        assert t.long == 'Coho Streams'

    @staticmethod
    def test_generate_frame():
        '''
        Test the structure of a frame that will be printed as a
        'barrier file' for input to OptiPass
        '''

        p = Project('static/workbook.csv', DataSet.TNC_OR)
        op = OP(p, ['Coos'], ['CO','CH'], ['1','1'], 'Current')
        op.generate_input_frame()
        tf = op.input_frame

        assert list(tf.columns) == ['ID','REG', 'FOCUS', 'DSID', 'HAB_CO', 'HAB_CH', 'PRE_CO', 'PRE_CH', 'NPROJ', 'ACTION', 'COST', 'POST_CO', 'POST_CH']

    # NOTE:  the OPM project does not have habitat in unscaled ("target") units
    # so the calls to collect_results in these tests need to specify scaled = True

    @staticmethod
    def test_example_1():
        '''
        Test the OPResults class by collecting results for Example 1 from the 
        OptiPass User Manual.
        '''
        op = OP(Project('static/test_wb.csv', DataSet.OPM), ['OPM'], ['T1'], ['1'], None)
        op.input_frame = pd.read_csv('static/Example_1/Example1.txt', sep='\t')
        op.outputs = sorted(glob('static/Example_1/example_*.txt'))
        op.collect_results(scaled=True)

        assert len(op.targets) == 1
        assert op.targets[0].abbrev == 'T1'
        # assert len(op.weights) == 1 and round(op.weights[0]) == 1

        assert type(op.summary) == pd.DataFrame
        assert len(op.summary) == 6
        assert round(op.summary.budget.sum()) == 1500
        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
        assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['A',b]] == [400,500]
        assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['D',b]] == [ ]
        assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['E',b]] == [100,300]

        assert op.paths['E'] == ['E','D','A']
        assert op.paths['A'] == ['A']

    @staticmethod
    def test_example_4():
        '''
        Same as test_example_1, but using Example 4, which has two restoration targets.
        '''
        op = OP(Project('static/test_wb.csv', DataSet.OPM), ['OPM'], ['T1','T2'], ['3','1'], None)
        op.input_frame = pd.read_csv('static/Example_4/Example4.txt', sep='\t')
        op.outputs = sorted(glob('static/Example_4/example_*.txt'))
        op.collect_results(scaled=True)

        assert len(op.targets) == 2
        assert op.targets[0].abbrev == 'T1' and op.targets[1].abbrev == 'T2'
        assert len(op.weights) == 2 and round(sum(op.weights)) == 4

        assert type(op.summary) == pd.DataFrame
        assert len(op.summary) == 6
        assert round(op.summary.budget.sum()) == 1500
        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 b != 'count' and op.matrix.loc['A',b]] == [400,500]
        assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['D',b]] == [ ]
        assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['E',b]] == [100,300]

    @staticmethod
    def test_potential_habitat_1():
        '''
        Test the method that computes potential habitat, using the results 
        genearated for Example 1 in the OptiPass manual.
        '''
        op = OP(Project('static/test_wb.csv', DataSet.OPM), ['OPM'], ['T1'], ['1'], None)
        op.input_frame = pd.read_csv('static/Example_1/Example1.txt', sep='\t')
        op.outputs = sorted(glob('static/Example_1/example_*.txt'))
        op.collect_results(scaled=True)

        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
        assert round(m.wph[5],3) == 8.520

    @staticmethod
    def test_potential_habitat_4():
        '''
        Same as test_potential_habitat_1, but using Example 4, with two restoration targets
        '''
        op = OP(Project('static/test_wb.csv', DataSet.OPM), ['OPM'], ['T1','T2'], ['3','1'], None)
        op.input_frame = pd.read_csv('static/Example_4/Example4.txt', sep='\t')
        op.outputs = sorted(glob('static/Example_4/example_*.txt'))
        op.collect_results(scaled=True)

        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    # the value shown in the OP manual

    @staticmethod
    def test_budget_formats():
        '''
        Test the function that generates budget labels.
        '''
        assert list(OP.format_budgets([n*1000000 for n in range(1,6)]).values()) == ['$1M', '$2M', '$3M', '$4M', '$5M']
        assert list(OP.format_budgets([n*100000 for n in range(1,6)]).values()) == ['$100K', '$200K', '$300K', '$400K', '$500K']
        assert list(OP.format_budgets([n*500000 for n in range(1,6)]).values()) == ['$500K', '$1M', '$1.5M', '$2M', '$2.5M']

test_budget_formats() staticmethod

Test the function that generates budget labels.

src/tidegates/optipass.py
586
587
588
589
590
591
592
593
@staticmethod
def test_budget_formats():
    '''
    Test the function that generates budget labels.
    '''
    assert list(OP.format_budgets([n*1000000 for n in range(1,6)]).values()) == ['$1M', '$2M', '$3M', '$4M', '$5M']
    assert list(OP.format_budgets([n*100000 for n in range(1,6)]).values()) == ['$100K', '$200K', '$300K', '$400K', '$500K']
    assert list(OP.format_budgets([n*500000 for n in range(1,6)]).values()) == ['$500K', '$1M', '$1.5M', '$2M', '$2.5M']

test_example_1() staticmethod

Test the OPResults class by collecting results for Example 1 from the OptiPass User Manual.

src/tidegates/optipass.py
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
@staticmethod
def test_example_1():
    '''
    Test the OPResults class by collecting results for Example 1 from the 
    OptiPass User Manual.
    '''
    op = OP(Project('static/test_wb.csv', DataSet.OPM), ['OPM'], ['T1'], ['1'], None)
    op.input_frame = pd.read_csv('static/Example_1/Example1.txt', sep='\t')
    op.outputs = sorted(glob('static/Example_1/example_*.txt'))
    op.collect_results(scaled=True)

    assert len(op.targets) == 1
    assert op.targets[0].abbrev == 'T1'
    # assert len(op.weights) == 1 and round(op.weights[0]) == 1

    assert type(op.summary) == pd.DataFrame
    assert len(op.summary) == 6
    assert round(op.summary.budget.sum()) == 1500
    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
    assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['A',b]] == [400,500]
    assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['D',b]] == [ ]
    assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['E',b]] == [100,300]

    assert op.paths['E'] == ['E','D','A']
    assert op.paths['A'] == ['A']

test_example_4() staticmethod

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

src/tidegates/optipass.py
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
@staticmethod
def test_example_4():
    '''
    Same as test_example_1, but using Example 4, which has two restoration targets.
    '''
    op = OP(Project('static/test_wb.csv', DataSet.OPM), ['OPM'], ['T1','T2'], ['3','1'], None)
    op.input_frame = pd.read_csv('static/Example_4/Example4.txt', sep='\t')
    op.outputs = sorted(glob('static/Example_4/example_*.txt'))
    op.collect_results(scaled=True)

    assert len(op.targets) == 2
    assert op.targets[0].abbrev == 'T1' and op.targets[1].abbrev == 'T2'
    assert len(op.weights) == 2 and round(sum(op.weights)) == 4

    assert type(op.summary) == pd.DataFrame
    assert len(op.summary) == 6
    assert round(op.summary.budget.sum()) == 1500
    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 b != 'count' and op.matrix.loc['A',b]] == [400,500]
    assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['D',b]] == [ ]
    assert [b for b in op.matrix.columns if b != 'count' and op.matrix.loc['E',b]] == [100,300]

test_generate_frame() staticmethod

Test the structure of a frame that will be printed as a 'barrier file' for input to OptiPass

src/tidegates/optipass.py
481
482
483
484
485
486
487
488
489
490
491
492
493
@staticmethod
def test_generate_frame():
    '''
    Test the structure of a frame that will be printed as a
    'barrier file' for input to OptiPass
    '''

    p = Project('static/workbook.csv', DataSet.TNC_OR)
    op = OP(p, ['Coos'], ['CO','CH'], ['1','1'], 'Current')
    op.generate_input_frame()
    tf = op.input_frame

    assert list(tf.columns) == ['ID','REG', 'FOCUS', 'DSID', 'HAB_CO', 'HAB_CH', 'PRE_CO', 'PRE_CH', 'NPROJ', 'ACTION', 'COST', 'POST_CO', 'POST_CH']

test_instantiate_object() staticmethod

Test the OP constructor.

src/tidegates/optipass.py
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
@staticmethod
def test_instantiate_object():
    '''
    Test the OP constructor.
    '''
    p = Project('static/workbook.csv', DataSet.TNC_OR)
    op = OP(p, ['Coos'], ['CO','CH'], ['1','1'], 'Current')
    assert op.project == p
    assert op.regions == ['Coos']
    assert op.climate == 'Current'
    assert op.weights == [1,1]
    assert len(op.targets) == 2
    t = op.targets[0]
    assert t.abbrev == 'CO'
    assert t.short == 'Coho'
    assert t.long == 'Coho Streams'

test_potential_habitat_1() staticmethod

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

src/tidegates/optipass.py
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
@staticmethod
def test_potential_habitat_1():
    '''
    Test the method that computes potential habitat, using the results 
    genearated for Example 1 in the OptiPass manual.
    '''
    op = OP(Project('static/test_wb.csv', DataSet.OPM), ['OPM'], ['T1'], ['1'], None)
    op.input_frame = pd.read_csv('static/Example_1/Example1.txt', sep='\t')
    op.outputs = sorted(glob('static/Example_1/example_*.txt'))
    op.collect_results(scaled=True)

    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
    assert round(m.wph[5],3) == 8.520

test_potential_habitat_4() staticmethod

Same as test_potential_habitat_1, but using Example 4, with two restoration targets

src/tidegates/optipass.py
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
@staticmethod
def test_potential_habitat_4():
    '''
    Same as test_potential_habitat_1, but using Example 4, with two restoration targets
    '''
    op = OP(Project('static/test_wb.csv', DataSet.OPM), ['OPM'], ['T1','T2'], ['3','1'], None)
    op.input_frame = pd.read_csv('static/Example_4/Example4.txt', sep='\t')
    op.outputs = sorted(glob('static/Example_4/example_*.txt'))
    op.collect_results(scaled=True)

    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    # the value shown in the OP manual