Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow variable neutralization in YAML test files #1021

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
41 changes: 37 additions & 4 deletions openfisca_core/tools/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,30 @@ 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'}
#: 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 = {
benjello marked this conversation as resolved.
Show resolved Hide resolved
'absolute_error_margin',
'description',
'extensions',
'ignore_variables',
'input',
'keywords',
'max_spiral_loops',
'name',
'neutralize_variables',
'only_variables',
'output',
'period',
'reforms',
'relative_error_margin'
}

yaml, Loader = import_yaml()

Expand Down Expand Up @@ -137,7 +160,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('neutralize_variables', [])
)

builder = SimulationBuilder()
input = self.test.get('input', {})
Expand Down Expand Up @@ -283,14 +311,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, neutralize_variables):
if not isinstance(reforms, list):
reforms = [reforms]
if not isinstance(extensions, list):
extensions = [extensions]
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)))
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)

Expand All @@ -303,6 +333,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 neutralize_variables:
current_tax_benefit_system.neutralize_variable(neutralized_variable)

_tax_benefit_system_cache[key] = current_tax_benefit_system

return current_tax_benefit_system
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
4 changes: 4 additions & 0 deletions tests/core/test_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
38 changes: 26 additions & 12 deletions tests/core/tools/test_runner/test_yaml_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}

Expand Down Expand Up @@ -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)
Expand Down
17 changes: 17 additions & 0 deletions tests/fixtures/yaml_tests/test_with_neutralized_variables.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
- name: "Result outside neutralized variables"
period: 2021-01
neutralize_variables:
- housing_allowance
input:
age: 30
output:
basic_income: 600

- name: "Result within neutralized variables"
period: 2021-01
neutralize_variables:
- basic_income
input:
age: 30
output:
basic_income: 0