From d8e338ef1cdbfc8ab2e3f1c5b62b1d668249c662 Mon Sep 17 00:00:00 2001 From: Mahdi Ben Jelloul Date: Tue, 18 May 2021 16:02:06 +0200 Subject: [PATCH 1/4] Alloz variable neutralization in YAML test files Fix text for comparison with country-template parameter Bump Update CHANGELOG.md Co-authored-by: sandcha Test neutralized_variables new attribute for YAML tests Update CHANGELOG.md Co-authored-by: sandcha Add tbs with neutralized variables to yaml tbs cache fixup! Add tbs with neutralized variables to yaml tbs cache Add test on yaml runner on tbs cache with neutralized variable fixup! Add test on yaml runner on tbs cache with neutralized variable --- CHANGELOG.md | 7 ++++ openfisca_core/tools/test_runner.py | 33 ++++++++++++++-- tests/core/test_yaml.py | 4 ++ .../tools/test_runner/test_yaml_runner.py | 38 +++++++++++++------ .../test_with_neutralized_variables.yaml | 17 +++++++++ 5 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 tests/core/yaml_tests/test_with_neutralized_variables.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index a74ddad455..38f4dd8f3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 35.12.0 [#1021](https://github.com/openfisca/openfisca-core/pull/1021) + +#### New features + +- Introduce `neutralize_variables` option in YAML test files + - A neutralized variable in a YAML test will return its default value when computed. + ## 35.11.0 [#1149](https://github.com/openfisca/openfisca-core/pull/1149) #### New Features diff --git a/openfisca_core/tools/test_runner.py b/openfisca_core/tools/test_runner.py index 0820750806..c751f34d82 100644 --- a/openfisca_core/tools/test_runner.py +++ b/openfisca_core/tools/test_runner.py @@ -31,7 +31,22 @@ def import_yaml(): return yaml, Loader -TEST_KEYWORDS = {'absolute_error_margin', 'description', 'extensions', 'ignore_variables', 'input', 'keywords', 'max_spiral_loops', 'name', 'only_variables', 'output', 'period', 'reforms', 'relative_error_margin'} +TEST_KEYWORDS = { + 'absolute_error_margin', + 'description', + 'extensions', + 'ignore_variables', + 'input', + 'keywords', + 'max_spiral_loops', + 'name', + 'neutralized_variables', + 'only_variables', + 'output', + 'period', + 'reforms', + 'relative_error_margin' + } yaml, Loader = import_yaml() @@ -137,7 +152,12 @@ def runtest(self): unexpected_keys = set(self.test.keys()).difference(TEST_KEYWORDS) raise ValueError("Unexpected keys {} in test '{}' in file '{}'".format(unexpected_keys, self.name, self.fspath)) - self.tax_benefit_system = _get_tax_benefit_system(self.baseline_tax_benefit_system, self.test.get('reforms', []), self.test.get('extensions', [])) + self.tax_benefit_system = _get_tax_benefit_system( + self.baseline_tax_benefit_system, + self.test.get('reforms', []), + self.test.get('extensions', []), + self.test.get('neutralized_variables', []) + ) builder = SimulationBuilder() input = self.test.get('input', {}) @@ -283,14 +303,16 @@ def pytest_collect_file(self, parent, path): options = self.options) -def _get_tax_benefit_system(baseline, reforms, extensions): +def _get_tax_benefit_system(baseline, reforms, extensions, neutralized_variables): if not isinstance(reforms, list): reforms = [reforms] if not isinstance(extensions, list): extensions = [extensions] + if not isinstance(neutralized_variables, list): + neutralized_variables = [neutralized_variables] # keep reforms order in cache, ignore extensions order - key = hash((id(baseline), ':'.join(reforms), frozenset(extensions))) + key = hash((id(baseline), ':'.join(reforms), frozenset(extensions), ':'.join(neutralized_variables))) if _tax_benefit_system_cache.get(key): return _tax_benefit_system_cache.get(key) @@ -303,6 +325,9 @@ def _get_tax_benefit_system(baseline, reforms, extensions): current_tax_benefit_system = current_tax_benefit_system.clone() current_tax_benefit_system.load_extension(extension) + for neutralized_variable in neutralized_variables: + current_tax_benefit_system.neutralize_variable(neutralized_variable) + _tax_benefit_system_cache[key] = current_tax_benefit_system return current_tax_benefit_system diff --git a/tests/core/test_yaml.py b/tests/core/test_yaml.py index f63e37ff39..23e24554df 100644 --- a/tests/core/test_yaml.py +++ b/tests/core/test_yaml.py @@ -64,6 +64,10 @@ def test_with_anchors(tax_benefit_system): assert run_yaml_test(tax_benefit_system, 'test_with_anchors.yaml') == EXIT_OK +def test_with_neutralized_variables(tax_benefit_system): + assert run_yaml_test(tax_benefit_system, 'test_with_neutralized_variables.yaml') == EXIT_OK + + def test_run_tests_from_directory_fail(tax_benefit_system): assert run_yaml_test(tax_benefit_system, yaml_tests_dir) == EXIT_TESTSFAILED diff --git a/tests/core/tools/test_runner/test_yaml_runner.py b/tests/core/tools/test_runner/test_yaml_runner.py index 782f7f60b3..71ef79def3 100644 --- a/tests/core/tools/test_runner/test_yaml_runner.py +++ b/tests/core/tools/test_runner/test_yaml_runner.py @@ -27,6 +27,9 @@ def apply_reform(self, path): def load_extension(self, extension): pass + def neutralize_variable(self, variable_name): + pass + def entities_by_singular(self): return {} @@ -95,51 +98,62 @@ def test_variable_not_found(): def test_tax_benefit_systems_with_reform_cache(): baseline = TaxBenefitSystem() - ab_tax_benefit_system = _get_tax_benefit_system(baseline, 'ab', []) - ba_tax_benefit_system = _get_tax_benefit_system(baseline, 'ba', []) + ab_tax_benefit_system = _get_tax_benefit_system(baseline, 'ab', [], []) + ba_tax_benefit_system = _get_tax_benefit_system(baseline, 'ba', [], []) assert ab_tax_benefit_system != ba_tax_benefit_system def test_reforms_formats(): baseline = TaxBenefitSystem() - lonely_reform_tbs = _get_tax_benefit_system(baseline, 'lonely_reform', []) - list_lonely_reform_tbs = _get_tax_benefit_system(baseline, ['lonely_reform'], []) + lonely_reform_tbs = _get_tax_benefit_system(baseline, 'lonely_reform', [], []) + list_lonely_reform_tbs = _get_tax_benefit_system(baseline, ['lonely_reform'], [], []) assert lonely_reform_tbs == list_lonely_reform_tbs def test_reforms_order(): baseline = TaxBenefitSystem() - abba_tax_benefit_system = _get_tax_benefit_system(baseline, ['ab', 'ba'], []) - baab_tax_benefit_system = _get_tax_benefit_system(baseline, ['ba', 'ab'], []) + abba_tax_benefit_system = _get_tax_benefit_system(baseline, ['ab', 'ba'], [], []) + baab_tax_benefit_system = _get_tax_benefit_system(baseline, ['ba', 'ab'], [], []) assert abba_tax_benefit_system != baab_tax_benefit_system # keep reforms order in cache def test_tax_benefit_systems_with_extensions_cache(): baseline = TaxBenefitSystem() - xy_tax_benefit_system = _get_tax_benefit_system(baseline, [], 'xy') - yx_tax_benefit_system = _get_tax_benefit_system(baseline, [], 'yx') + xy_tax_benefit_system = _get_tax_benefit_system(baseline, [], 'xy', []) + yx_tax_benefit_system = _get_tax_benefit_system(baseline, [], 'yx', []) assert xy_tax_benefit_system != yx_tax_benefit_system def test_extensions_formats(): baseline = TaxBenefitSystem() - lonely_extension_tbs = _get_tax_benefit_system(baseline, [], 'lonely_extension') - list_lonely_extension_tbs = _get_tax_benefit_system(baseline, [], ['lonely_extension']) + lonely_extension_tbs = _get_tax_benefit_system(baseline, [], 'lonely_extension', []) + list_lonely_extension_tbs = _get_tax_benefit_system(baseline, [], ['lonely_extension'], []) assert lonely_extension_tbs == list_lonely_extension_tbs def test_extensions_order(): baseline = TaxBenefitSystem() - xy_tax_benefit_system = _get_tax_benefit_system(baseline, [], ['x', 'y']) - yx_tax_benefit_system = _get_tax_benefit_system(baseline, [], ['y', 'x']) + xy_tax_benefit_system = _get_tax_benefit_system(baseline, [], ['x', 'y'], []) + yx_tax_benefit_system = _get_tax_benefit_system(baseline, [], ['y', 'x'], []) assert xy_tax_benefit_system == yx_tax_benefit_system # extensions order is ignored in cache +def test_tax_benefit_systems_with_neutralized_variables(): + baseline = TaxBenefitSystem() + + re_tax_benefit_system = _get_tax_benefit_system(baseline, 'r', ['e'], []) + ren_tax_benefit_system = _get_tax_benefit_system(baseline, 'r', ['e'], ['n']) + n_tax_benefit_system = _get_tax_benefit_system(baseline, [], [], ['n']) + + assert re_tax_benefit_system != ren_tax_benefit_system + assert ren_tax_benefit_system != n_tax_benefit_system + + def test_performance_graph_option_output(): test = {'input': {'salary': {'2017-01': 2000}}, 'output': {'salary': {'2017-01': 2000}}} test_item = TestItem(test) diff --git a/tests/core/yaml_tests/test_with_neutralized_variables.yaml b/tests/core/yaml_tests/test_with_neutralized_variables.yaml new file mode 100644 index 0000000000..58110c3d58 --- /dev/null +++ b/tests/core/yaml_tests/test_with_neutralized_variables.yaml @@ -0,0 +1,17 @@ +- name: "Result outside neutralized variables" + period: 2021-01 + neutralized_variables: + - housing_allowance + input: + age: 30 + output: + basic_income: 600 + +- name: "Result within neutralized variables" + period: 2021-01 + neutralized_variables: + - basic_income + input: + age: 30 + output: + basic_income: 0 From e479959b165b2cb54beea879bc142cd50a51c9d1 Mon Sep 17 00:00:00 2001 From: Mahdi Ben Jelloul Date: Tue, 31 Aug 2021 14:07:40 +0200 Subject: [PATCH 2/4] More comment --- openfisca_core/tools/test_runner.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/openfisca_core/tools/test_runner.py b/openfisca_core/tools/test_runner.py index c751f34d82..be2295ffa6 100644 --- a/openfisca_core/tools/test_runner.py +++ b/openfisca_core/tools/test_runner.py @@ -31,6 +31,14 @@ def import_yaml(): return yaml, Loader +#: These keys the syntax keys for writing YAML tests. +#: +#: .. seealso:: +#: For more information on common keys and examples, please take a look at +#: the official documentation on `Writing YAML tests`_. +#: +#: .. _Writing YAML tests: https://openfisca.org/doc/coding-the-legislation/writing_yaml_tests.html +#: TEST_KEYWORDS = { 'absolute_error_margin', 'description', @@ -40,7 +48,7 @@ def import_yaml(): 'keywords', 'max_spiral_loops', 'name', - 'neutralized_variables', + 'neutralize_variables', 'only_variables', 'output', 'period', @@ -156,7 +164,7 @@ def runtest(self): self.baseline_tax_benefit_system, self.test.get('reforms', []), self.test.get('extensions', []), - self.test.get('neutralized_variables', []) + self.test.get('neutralize_variables', []) ) builder = SimulationBuilder() @@ -303,16 +311,16 @@ def pytest_collect_file(self, parent, path): options = self.options) -def _get_tax_benefit_system(baseline, reforms, extensions, neutralized_variables): +def _get_tax_benefit_system(baseline, reforms, extensions, neutralize_variables): if not isinstance(reforms, list): reforms = [reforms] if not isinstance(extensions, list): extensions = [extensions] - if not isinstance(neutralized_variables, list): - neutralized_variables = [neutralized_variables] + if not isinstance(neutralize_variables, list): + neutralize_variables = [neutralize_variables] # keep reforms order in cache, ignore extensions order - key = hash((id(baseline), ':'.join(reforms), frozenset(extensions), ':'.join(neutralized_variables))) + key = hash((id(baseline), ':'.join(reforms), frozenset(extensions), ':'.join(neutralize_variables))) if _tax_benefit_system_cache.get(key): return _tax_benefit_system_cache.get(key) @@ -325,7 +333,7 @@ def _get_tax_benefit_system(baseline, reforms, extensions, neutralized_variables current_tax_benefit_system = current_tax_benefit_system.clone() current_tax_benefit_system.load_extension(extension) - for neutralized_variable in neutralized_variables: + for neutralized_variable in neutralize_variables: current_tax_benefit_system.neutralize_variable(neutralized_variable) _tax_benefit_system_cache[key] = current_tax_benefit_system From aede92a35a9de5054e5515653a39e07fbf9a7d32 Mon Sep 17 00:00:00 2001 From: Mahdi Ben Jelloul Date: Thu, 9 Sep 2021 16:45:51 +0200 Subject: [PATCH 3/4] Bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ad2c48f5c7..c3d91a3a3a 100644 --- a/setup.py +++ b/setup.py @@ -48,7 +48,7 @@ setup( name = 'OpenFisca-Core', - version = '35.11.0', + version = '35.12.0', author = 'OpenFisca Team', author_email = 'contact@openfisca.org', classifiers = [ From e07cac4927c321688300b7fe77facb1529019c34 Mon Sep 17 00:00:00 2001 From: Mahdi Ben Jelloul Date: Tue, 14 Dec 2021 23:00:20 +0100 Subject: [PATCH 4/4] Adapt to changes --- .../yaml_tests/test_with_neutralized_variables.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename tests/{core => fixtures}/yaml_tests/test_with_neutralized_variables.yaml (84%) diff --git a/tests/core/yaml_tests/test_with_neutralized_variables.yaml b/tests/fixtures/yaml_tests/test_with_neutralized_variables.yaml similarity index 84% rename from tests/core/yaml_tests/test_with_neutralized_variables.yaml rename to tests/fixtures/yaml_tests/test_with_neutralized_variables.yaml index 58110c3d58..9e7e586cab 100644 --- a/tests/core/yaml_tests/test_with_neutralized_variables.yaml +++ b/tests/fixtures/yaml_tests/test_with_neutralized_variables.yaml @@ -1,6 +1,6 @@ - name: "Result outside neutralized variables" period: 2021-01 - neutralized_variables: + neutralize_variables: - housing_allowance input: age: 30 @@ -9,7 +9,7 @@ - name: "Result within neutralized variables" period: 2021-01 - neutralized_variables: + neutralize_variables: - basic_income input: age: 30