afscgap.test.test_flat_local_filter

Tests for local filtering on returned flat results.

(c) 2025 Regents of University of California / The Eric and Wendy Schmidt Center for Data Science and the Environment at UC Berkeley.

This file is part of afscgap released under the BSD 3-Clause License. See LICENSE.md.

  1"""
  2Tests for local filtering on returned flat results.
  3
  4(c) 2025 Regents of University of California / The Eric and Wendy Schmidt Center
  5for Data Science and the Environment at UC Berkeley.
  6
  7This file is part of afscgap released under the BSD 3-Clause License. See
  8LICENSE.md.
  9"""
 10import unittest
 11import unittest.mock
 12
 13import afscgap.flat_local_filter
 14import afscgap.param
 15
 16
 17class EqualsLocalFilterTests(unittest.TestCase):
 18
 19    def setUp(self):
 20        self._filter = afscgap.flat_local_filter.EqualsLocalFilter(
 21            lambda x: x.get_value(),
 22            1
 23        )
 24
 25    def test_matches_true(self):
 26        self.assertTrue(self._filter.matches(self._make_target(1)))
 27    
 28    def test_matches_false(self):
 29        self.assertFalse(self._filter.matches(self._make_target(1.001)))
 30    
 31    def test_matches_none(self):
 32        self.assertFalse(self._filter.matches(self._make_target(None)))
 33
 34    def _make_target(self, value):
 35        mock = unittest.mock.MagicMock()
 36        mock.get_value = unittest.mock.MagicMock(return_value=value)
 37        return mock
 38
 39
 40class RangeLocalFilterTests(unittest.TestCase):
 41
 42    def setUp(self):
 43        self._filter = afscgap.flat_local_filter.RangeLocalFilter(
 44            lambda x: x.get_value(),
 45            2,
 46            4
 47        )
 48
 49    def test_out_lower(self):
 50        self.assertFalse(self._filter.matches(self._make_target(1)))
 51
 52    def test_at_lower(self):
 53        self.assertTrue(self._filter.matches(self._make_target(2)))
 54
 55    def test_mid(self):
 56        self.assertTrue(self._filter.matches(self._make_target(3)))
 57
 58    def test_at_high(self):
 59        self.assertTrue(self._filter.matches(self._make_target(4)))
 60
 61    def test_above_high(self):
 62        self.assertFalse(self._filter.matches(self._make_target(5)))
 63
 64    def test_none(self):
 65        self.assertFalse(self._filter.matches(self._make_target(None)))
 66
 67    def _make_target(self, value):
 68        mock = unittest.mock.MagicMock()
 69        mock.get_value = unittest.mock.MagicMock(return_value=value)
 70        return mock
 71
 72
 73class LogcalAndLocalFilterTests(unittest.TestCase):
 74
 75    def test_empty(self):
 76        target = self._make_filter([])
 77        self.assertTrue(target.matches(1))
 78    
 79    def test_single_true(self):
 80        target = self._make_filter([True])
 81        self.assertTrue(target.matches(1))
 82    
 83    def test_single_false(self):
 84        target = self._make_filter([False])
 85        self.assertFalse(target.matches(1))
 86    
 87    def test_multiple_true(self):
 88        target = self._make_filter([True, True])
 89        self.assertTrue(target.matches(1))
 90    
 91    def test_multiple_false(self):
 92        target = self._make_filter([True, False])
 93        self.assertFalse(target.matches(1))
 94    
 95    def test_none(self):
 96        target = self._make_filter([True, True])
 97        self.assertTrue(target.matches(None))
 98
 99    def _make_filter(self, values):
100        inner_filters = map(lambda x: self._make_inner_filter(x), values)
101        return afscgap.flat_local_filter.LogicalAndLocalFilter(inner_filters)
102    
103    def _make_inner_filter(self, value):
104        mock = unittest.mock.MagicMock()
105        mock.matches = unittest.mock.MagicMock(return_value=value)
106        return mock
107
108
109class BuildIndividualFilterTests(unittest.TestCase):
110
111    def test_empty(self):
112        param = afscgap.param.EmptyParam()
113        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
114        self.assertIsNone(local_filter)
115
116    def test_equals_true(self):
117        param = afscgap.param.IntEqualsParam(2025)
118        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
119        self.assertTrue(local_filter.matches(self._make_target(2025)))
120    
121    def test_equals_false(self):
122        param = afscgap.param.IntEqualsParam(2025)
123        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
124        self.assertFalse(local_filter.matches(self._make_target(2024)))
125
126    def test_range_true(self):
127        param = afscgap.param.IntRangeParam(2024, 2026)
128        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
129        self.assertTrue(local_filter.matches(self._make_target(2025)))
130
131    def test_range_false(self):
132        param = afscgap.param.IntRangeParam(2024, 2026)
133        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
134        self.assertFalse(local_filter.matches(self._make_target(2023)))
135
136    def test_unsupported_accessor(self):
137        with self.assertRaises(RuntimeError):
138            param = afscgap.param.EmptyParam()
139            afscgap.flat_local_filter.build_individual_filter('unknown', param)
140
141    def test_unsupported_filter(self):
142        with self.assertRaises(RuntimeError):
143            param = unittest.mock.MagicMock()
144            param.get_filter_type = unittest.mock.MagicMock(return_value='unknown')
145            afscgap.flat_local_filter.build_individual_filter('year', param)
146    
147    def _make_target(self, target_value):
148        mock_target = unittest.mock.MagicMock()
149        mock_target.get_year = unittest.mock.MagicMock(return_value=target_value)
150        return mock_target
151
152
153class BuildFilterTests(unittest.TestCase):
154
155    def setUp(self):
156        params = {
157            'year': afscgap.param.IntRangeParam(2024, 2026),
158            'srvy': afscgap.param.StrEqualsParam('GOA'),
159            'count': afscgap.param.EmptyParam()
160        }
161        self._local_filter = afscgap.flat_local_filter.build_filter(params)
162
163    def test_true(self):
164        example = self._build_example(2025, 'GOA', 123)
165        self.assertTrue(self._local_filter.matches(example))
166
167    def test_false_int(self):
168        example = self._build_example(2023, 'GOA', 123)
169        self.assertFalse(self._local_filter.matches(example))
170    
171    def test_false_str(self):
172        example = self._build_example(2025, 'Other', 123)
173        self.assertFalse(self._local_filter.matches(example))
174    
175    def test_ignorable(self):
176        example = self._build_example(2025, 'GOA', None)
177        self.assertTrue(self._local_filter.matches(example))
178
179    def _build_example(self, year, survey, count):
180        mock = unittest.mock.MagicMock()
181        mock.get_year = unittest.mock.MagicMock(return_value=year)
182        mock.get_srvy = unittest.mock.MagicMock(return_value=survey)
183        mock.get_count = unittest.mock.MagicMock(return_value=count)
184        return mock
class EqualsLocalFilterTests(unittest.case.TestCase):
18class EqualsLocalFilterTests(unittest.TestCase):
19
20    def setUp(self):
21        self._filter = afscgap.flat_local_filter.EqualsLocalFilter(
22            lambda x: x.get_value(),
23            1
24        )
25
26    def test_matches_true(self):
27        self.assertTrue(self._filter.matches(self._make_target(1)))
28    
29    def test_matches_false(self):
30        self.assertFalse(self._filter.matches(self._make_target(1.001)))
31    
32    def test_matches_none(self):
33        self.assertFalse(self._filter.matches(self._make_target(None)))
34
35    def _make_target(self, value):
36        mock = unittest.mock.MagicMock()
37        mock.get_value = unittest.mock.MagicMock(return_value=value)
38        return mock

A class whose instances are single test cases.

By default, the test code itself should be placed in a method named 'runTest'.

If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

If it is necessary to override the __init__ method, the base class __init__ method must always be called. It is important that subclasses should not change the signature of their __init__ method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.

When subclassing TestCase, you can set these attributes:

  • failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'.
  • longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed.
  • maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.
def setUp(self):
20    def setUp(self):
21        self._filter = afscgap.flat_local_filter.EqualsLocalFilter(
22            lambda x: x.get_value(),
23            1
24        )

Hook method for setting up the test fixture before exercising it.

def test_matches_true(self):
26    def test_matches_true(self):
27        self.assertTrue(self._filter.matches(self._make_target(1)))
def test_matches_false(self):
29    def test_matches_false(self):
30        self.assertFalse(self._filter.matches(self._make_target(1.001)))
def test_matches_none(self):
32    def test_matches_none(self):
33        self.assertFalse(self._filter.matches(self._make_target(None)))
class RangeLocalFilterTests(unittest.case.TestCase):
41class RangeLocalFilterTests(unittest.TestCase):
42
43    def setUp(self):
44        self._filter = afscgap.flat_local_filter.RangeLocalFilter(
45            lambda x: x.get_value(),
46            2,
47            4
48        )
49
50    def test_out_lower(self):
51        self.assertFalse(self._filter.matches(self._make_target(1)))
52
53    def test_at_lower(self):
54        self.assertTrue(self._filter.matches(self._make_target(2)))
55
56    def test_mid(self):
57        self.assertTrue(self._filter.matches(self._make_target(3)))
58
59    def test_at_high(self):
60        self.assertTrue(self._filter.matches(self._make_target(4)))
61
62    def test_above_high(self):
63        self.assertFalse(self._filter.matches(self._make_target(5)))
64
65    def test_none(self):
66        self.assertFalse(self._filter.matches(self._make_target(None)))
67
68    def _make_target(self, value):
69        mock = unittest.mock.MagicMock()
70        mock.get_value = unittest.mock.MagicMock(return_value=value)
71        return mock

A class whose instances are single test cases.

By default, the test code itself should be placed in a method named 'runTest'.

If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

If it is necessary to override the __init__ method, the base class __init__ method must always be called. It is important that subclasses should not change the signature of their __init__ method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.

When subclassing TestCase, you can set these attributes:

  • failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'.
  • longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed.
  • maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.
def setUp(self):
43    def setUp(self):
44        self._filter = afscgap.flat_local_filter.RangeLocalFilter(
45            lambda x: x.get_value(),
46            2,
47            4
48        )

Hook method for setting up the test fixture before exercising it.

def test_out_lower(self):
50    def test_out_lower(self):
51        self.assertFalse(self._filter.matches(self._make_target(1)))
def test_at_lower(self):
53    def test_at_lower(self):
54        self.assertTrue(self._filter.matches(self._make_target(2)))
def test_mid(self):
56    def test_mid(self):
57        self.assertTrue(self._filter.matches(self._make_target(3)))
def test_at_high(self):
59    def test_at_high(self):
60        self.assertTrue(self._filter.matches(self._make_target(4)))
def test_above_high(self):
62    def test_above_high(self):
63        self.assertFalse(self._filter.matches(self._make_target(5)))
def test_none(self):
65    def test_none(self):
66        self.assertFalse(self._filter.matches(self._make_target(None)))
class LogcalAndLocalFilterTests(unittest.case.TestCase):
 74class LogcalAndLocalFilterTests(unittest.TestCase):
 75
 76    def test_empty(self):
 77        target = self._make_filter([])
 78        self.assertTrue(target.matches(1))
 79    
 80    def test_single_true(self):
 81        target = self._make_filter([True])
 82        self.assertTrue(target.matches(1))
 83    
 84    def test_single_false(self):
 85        target = self._make_filter([False])
 86        self.assertFalse(target.matches(1))
 87    
 88    def test_multiple_true(self):
 89        target = self._make_filter([True, True])
 90        self.assertTrue(target.matches(1))
 91    
 92    def test_multiple_false(self):
 93        target = self._make_filter([True, False])
 94        self.assertFalse(target.matches(1))
 95    
 96    def test_none(self):
 97        target = self._make_filter([True, True])
 98        self.assertTrue(target.matches(None))
 99
100    def _make_filter(self, values):
101        inner_filters = map(lambda x: self._make_inner_filter(x), values)
102        return afscgap.flat_local_filter.LogicalAndLocalFilter(inner_filters)
103    
104    def _make_inner_filter(self, value):
105        mock = unittest.mock.MagicMock()
106        mock.matches = unittest.mock.MagicMock(return_value=value)
107        return mock

A class whose instances are single test cases.

By default, the test code itself should be placed in a method named 'runTest'.

If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

If it is necessary to override the __init__ method, the base class __init__ method must always be called. It is important that subclasses should not change the signature of their __init__ method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.

When subclassing TestCase, you can set these attributes:

  • failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'.
  • longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed.
  • maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.
def test_empty(self):
76    def test_empty(self):
77        target = self._make_filter([])
78        self.assertTrue(target.matches(1))
def test_single_true(self):
80    def test_single_true(self):
81        target = self._make_filter([True])
82        self.assertTrue(target.matches(1))
def test_single_false(self):
84    def test_single_false(self):
85        target = self._make_filter([False])
86        self.assertFalse(target.matches(1))
def test_multiple_true(self):
88    def test_multiple_true(self):
89        target = self._make_filter([True, True])
90        self.assertTrue(target.matches(1))
def test_multiple_false(self):
92    def test_multiple_false(self):
93        target = self._make_filter([True, False])
94        self.assertFalse(target.matches(1))
def test_none(self):
96    def test_none(self):
97        target = self._make_filter([True, True])
98        self.assertTrue(target.matches(None))
class BuildIndividualFilterTests(unittest.case.TestCase):
110class BuildIndividualFilterTests(unittest.TestCase):
111
112    def test_empty(self):
113        param = afscgap.param.EmptyParam()
114        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
115        self.assertIsNone(local_filter)
116
117    def test_equals_true(self):
118        param = afscgap.param.IntEqualsParam(2025)
119        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
120        self.assertTrue(local_filter.matches(self._make_target(2025)))
121    
122    def test_equals_false(self):
123        param = afscgap.param.IntEqualsParam(2025)
124        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
125        self.assertFalse(local_filter.matches(self._make_target(2024)))
126
127    def test_range_true(self):
128        param = afscgap.param.IntRangeParam(2024, 2026)
129        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
130        self.assertTrue(local_filter.matches(self._make_target(2025)))
131
132    def test_range_false(self):
133        param = afscgap.param.IntRangeParam(2024, 2026)
134        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
135        self.assertFalse(local_filter.matches(self._make_target(2023)))
136
137    def test_unsupported_accessor(self):
138        with self.assertRaises(RuntimeError):
139            param = afscgap.param.EmptyParam()
140            afscgap.flat_local_filter.build_individual_filter('unknown', param)
141
142    def test_unsupported_filter(self):
143        with self.assertRaises(RuntimeError):
144            param = unittest.mock.MagicMock()
145            param.get_filter_type = unittest.mock.MagicMock(return_value='unknown')
146            afscgap.flat_local_filter.build_individual_filter('year', param)
147    
148    def _make_target(self, target_value):
149        mock_target = unittest.mock.MagicMock()
150        mock_target.get_year = unittest.mock.MagicMock(return_value=target_value)
151        return mock_target

A class whose instances are single test cases.

By default, the test code itself should be placed in a method named 'runTest'.

If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

If it is necessary to override the __init__ method, the base class __init__ method must always be called. It is important that subclasses should not change the signature of their __init__ method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.

When subclassing TestCase, you can set these attributes:

  • failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'.
  • longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed.
  • maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.
def test_empty(self):
112    def test_empty(self):
113        param = afscgap.param.EmptyParam()
114        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
115        self.assertIsNone(local_filter)
def test_equals_true(self):
117    def test_equals_true(self):
118        param = afscgap.param.IntEqualsParam(2025)
119        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
120        self.assertTrue(local_filter.matches(self._make_target(2025)))
def test_equals_false(self):
122    def test_equals_false(self):
123        param = afscgap.param.IntEqualsParam(2025)
124        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
125        self.assertFalse(local_filter.matches(self._make_target(2024)))
def test_range_true(self):
127    def test_range_true(self):
128        param = afscgap.param.IntRangeParam(2024, 2026)
129        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
130        self.assertTrue(local_filter.matches(self._make_target(2025)))
def test_range_false(self):
132    def test_range_false(self):
133        param = afscgap.param.IntRangeParam(2024, 2026)
134        local_filter = afscgap.flat_local_filter.build_individual_filter('year', param)
135        self.assertFalse(local_filter.matches(self._make_target(2023)))
def test_unsupported_accessor(self):
137    def test_unsupported_accessor(self):
138        with self.assertRaises(RuntimeError):
139            param = afscgap.param.EmptyParam()
140            afscgap.flat_local_filter.build_individual_filter('unknown', param)
def test_unsupported_filter(self):
142    def test_unsupported_filter(self):
143        with self.assertRaises(RuntimeError):
144            param = unittest.mock.MagicMock()
145            param.get_filter_type = unittest.mock.MagicMock(return_value='unknown')
146            afscgap.flat_local_filter.build_individual_filter('year', param)
class BuildFilterTests(unittest.case.TestCase):
154class BuildFilterTests(unittest.TestCase):
155
156    def setUp(self):
157        params = {
158            'year': afscgap.param.IntRangeParam(2024, 2026),
159            'srvy': afscgap.param.StrEqualsParam('GOA'),
160            'count': afscgap.param.EmptyParam()
161        }
162        self._local_filter = afscgap.flat_local_filter.build_filter(params)
163
164    def test_true(self):
165        example = self._build_example(2025, 'GOA', 123)
166        self.assertTrue(self._local_filter.matches(example))
167
168    def test_false_int(self):
169        example = self._build_example(2023, 'GOA', 123)
170        self.assertFalse(self._local_filter.matches(example))
171    
172    def test_false_str(self):
173        example = self._build_example(2025, 'Other', 123)
174        self.assertFalse(self._local_filter.matches(example))
175    
176    def test_ignorable(self):
177        example = self._build_example(2025, 'GOA', None)
178        self.assertTrue(self._local_filter.matches(example))
179
180    def _build_example(self, year, survey, count):
181        mock = unittest.mock.MagicMock()
182        mock.get_year = unittest.mock.MagicMock(return_value=year)
183        mock.get_srvy = unittest.mock.MagicMock(return_value=survey)
184        mock.get_count = unittest.mock.MagicMock(return_value=count)
185        return mock

A class whose instances are single test cases.

By default, the test code itself should be placed in a method named 'runTest'.

If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

If it is necessary to override the __init__ method, the base class __init__ method must always be called. It is important that subclasses should not change the signature of their __init__ method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.

When subclassing TestCase, you can set these attributes:

  • failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'.
  • longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed.
  • maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.
def setUp(self):
156    def setUp(self):
157        params = {
158            'year': afscgap.param.IntRangeParam(2024, 2026),
159            'srvy': afscgap.param.StrEqualsParam('GOA'),
160            'count': afscgap.param.EmptyParam()
161        }
162        self._local_filter = afscgap.flat_local_filter.build_filter(params)

Hook method for setting up the test fixture before exercising it.

def test_true(self):
164    def test_true(self):
165        example = self._build_example(2025, 'GOA', 123)
166        self.assertTrue(self._local_filter.matches(example))
def test_false_int(self):
168    def test_false_int(self):
169        example = self._build_example(2023, 'GOA', 123)
170        self.assertFalse(self._local_filter.matches(example))
def test_false_str(self):
172    def test_false_str(self):
173        example = self._build_example(2025, 'Other', 123)
174        self.assertFalse(self._local_filter.matches(example))
def test_ignorable(self):
176    def test_ignorable(self):
177        example = self._build_example(2025, 'GOA', None)
178        self.assertTrue(self._local_filter.matches(example))