From 375b14e9d4b5566c07afb8fbbddd20a9f4683cd5 Mon Sep 17 00:00:00 2001 From: Tim Pillinger <26465611+wxtim@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:30:53 +0000 Subject: [PATCH] Reinstall picks up installed config. (#345) ensure that vr picks up installed config. * Update pyproject.toml * don't display warnings about ROSE_ORIG_HOST when not relevant * updated lower pin on cylc version * de-flake test, unrelated to PR * updated test for Python 3.7 --------- Co-authored-by: Oliver Sanders --- CHANGES.md | 5 + cylc/rose/entry_points.py | 14 +- cylc/rose/stem.py | 2 +- cylc/rose/utilities.py | 122 ++++++++++++++++-- pyproject.toml | 23 ++++ setup.cfg | 2 +- tests/conftest.py | 119 +++++++++++++++-- .../15_reinstall_using_old_clivars/flow.cylc | 11 ++ .../opt/rose-suite-choc.conf | 2 + .../rose-suite.conf | 0 tests/functional/test_pre_configure.py | 60 +++++++++ tests/functional/test_reinstall_overrides.py | 2 +- tests/unit/test_cli.py | 65 ++++++++++ tests/unit/test_config_node.py | 77 ++++++++++- tests/unit/test_functional_post_install.py | 4 +- 15 files changed, 478 insertions(+), 30 deletions(-) create mode 100644 pyproject.toml create mode 100644 tests/functional/15_reinstall_using_old_clivars/flow.cylc create mode 100644 tests/functional/15_reinstall_using_old_clivars/opt/rose-suite-choc.conf create mode 100644 tests/functional/15_reinstall_using_old_clivars/rose-suite.conf create mode 100644 tests/unit/test_cli.py diff --git a/CHANGES.md b/CHANGES.md index 025b1a7a..0e249a22 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,11 @@ creating a new release entry be sure to copy & paste the span tag with the updated. Only the first match gets replaced, so it's fine to leave the old ones in. --> +## __cylc-rose-1.4.2 (Upcoming)__ + +[#345](https://github.com/cylc/cylc-rose/pull/345) - Fix an issue +where `cylc vr` could report erroneous validation failures. + ## __cylc-rose-1.4.1 (Released 2024-07-23)__ No significant change - Updated to use feature added at Cylc 8.3.3. diff --git a/cylc/rose/entry_points.py b/cylc/rose/entry_points.py index 503382d0..29537551 100644 --- a/cylc/rose/entry_points.py +++ b/cylc/rose/entry_points.py @@ -27,7 +27,9 @@ load_rose_config, process_config, record_cylc_install_options, + retrieve_installed_cli_opts, rose_config_exists, + sanitize_opts, ) if TYPE_CHECKING: @@ -40,7 +42,17 @@ def pre_configure(srcdir: Path, opts: 'Values') -> dict: # nothing to do here return {} - # load the Rose config + opts = sanitize_opts(opts) + + # If we are validating against source, load saved CLI options + # from previous install, as saved in the rose-suite-cylc-install.conf + if ( + getattr(opts, 'against_source', False) + and isinstance(opts.against_source, Path) + ): + opts = retrieve_installed_cli_opts(srcdir, opts) + + # load the source Rose config config_tree = load_rose_config(Path(srcdir), opts=opts) # extract plugin return information from the Rose config diff --git a/cylc/rose/stem.py b/cylc/rose/stem.py index 5972f4cf..22fe7ee5 100644 --- a/cylc/rose/stem.py +++ b/cylc/rose/stem.py @@ -575,7 +575,7 @@ def get_rose_stem_opts(): " the groups into a series of tasks to run." ), action="append", - metavar="PATH/TO/FLOW", + metavar="STRING", default=[], dest="stem_groups") rose_stem_options.add_option( diff --git a/cylc/rose/utilities.py b/cylc/rose/utilities.py index 570138d2..6ed6e93a 100644 --- a/cylc/rose/utilities.py +++ b/cylc/rose/utilities.py @@ -16,6 +16,7 @@ """Cylc support for reading and interpreting ``rose-suite.conf`` files.""" +from contextlib import suppress import itertools import os from pathlib import Path @@ -55,6 +56,11 @@ ) MESSAGE = 'message' ALL_MODES = 'all modes' +STANDARD_VARS = [ + ('ROSE_ORIG_HOST', get_host()), + ('ROSE_VERSION', ROSE_VERSION), + ('CYLC_VERSION', SET_BY_CYLC) +] class NotARoseSuiteException(Exception): @@ -119,9 +125,6 @@ def process_config( if templating not in config_node.value: config_node.set([templating]) - # Get Rose Orig host: - rose_orig_host = get_host() - # For each section process variables and add standard variables. for section in ['env', templating]: @@ -131,17 +134,13 @@ def process_config( # ROSE_ORIG_HOST - If it's the config, replace it, unless it has a # comment marking it as having been saved by ``cylc install``. # In all cases warn users if the value in their config is not used. - for var_name, replace_with in [ - ('ROSE_ORIG_HOST', rose_orig_host), - ('ROSE_VERSION', ROSE_VERSION), - ('CYLC_VERSION', SET_BY_CYLC) - ]: + for var_name, replace_with in STANDARD_VARS: # Warn if we're we're going to override a variable: if override_this_variable(config_node, section, var_name): user_var = config_node[section].value[var_name].value LOG.warning( - f'[{section}]{var_name}={user_var} from rose-suite.conf ' - f'will be ignored: {var_name} will be: {replace_with}' + f'[{section}]{var_name}={user_var}' + f' will be ignored. {var_name} will be: {replace_with}' ) # Handle replacement of stored variable if appropriate: @@ -883,6 +882,7 @@ def record_cylc_install_options( srcdir: Path, rundir: Path, opts: 'Values', + modify: bool = True, ) -> Tuple[ConfigNode, ConfigNode]: """Create/modify files recording Cylc install config options. @@ -909,6 +909,9 @@ def record_cylc_install_options( Equivalent of ``rose suite-run --define KEY=VAL`` - rose_template_vars (list of str): Equivalent of ``rose suite-run --define-suite KEY=VAL`` + modify: + If ``True``, the modified rose-suite-cylc-install.conf will be + written. If ``False``, this will only read files. Returns: Tuple - (cli_config, rose_suite_conf) @@ -957,8 +960,9 @@ def record_cylc_install_options( ROSE_ORIG_HOST_INSTALLED_OVERRIDE_STRING ] - cli_config.comments = [' This file records CLI Options.'] - dumper.dump(cli_config, str(conf_filepath)) + if modify: + cli_config.comments = [' This file records CLI Options.'] + dumper.dump(cli_config, str(conf_filepath)) # Merge the opts section of the rose-suite.conf with those set by CLI: rose_conf_filepath.touch() @@ -968,7 +972,8 @@ def record_cylc_install_options( ) identify_templating_section(rose_suite_conf) - dumper(rose_suite_conf, rose_conf_filepath) + if modify: + dumper(rose_suite_conf, rose_conf_filepath) return cli_config, rose_suite_conf @@ -999,3 +1004,94 @@ def copy_config_file( shutil.copy2(srcdir_rose_conf, rundir_rose_conf) return True + + +def retrieve_installed_cli_opts(srcdir, opts): + """Merge options from previous install, this install and the CLI. + + Allows validation of merged config for pre-configure where the + --against-source argument is used in a Cylc script. + """ + # if opts.against_source is a path then we are validating a source + # directory against installed options + rundir = opts.against_source + + # If the calling script doesn't have this option (Everything except + # Cylc VR) then this defaults to false. If it's true we skip all + # following logic: + if not getattr(opts, 'clear_rose_install_opts', False): + opts.clear_rose_install_opts = False + else: + return opts + + # NOTE: we only need to load the rose-suite-cylc-install for this + cli_config, _ = record_cylc_install_options( + srcdir, rundir, opts, modify=False + ) + + # Set ROSE_ORIG_HOST to ignored, and choose not to ignore it + # if this is a validation against source: + no_ignore = False + if rundir: + no_ignore = True + for keys, node in cli_config.walk(): + if keys[-1] == 'ROSE_ORIG_HOST': + node.state = cli_config.STATE_SYST_IGNORED + + # Get opt_conf_keys stored in rose-suite-cylc-install.conf + opt_conf_keys = cli_config.value.pop('opts').value.split() + if any(opt_conf_keys): + opts.opt_conf_keys = opt_conf_keys + + # Get --suite-defines/-S + # Work out whether user has used "template variables", "jinja2:suite.rc" + # or "empy:suite.rc" (There is an assumption that they aren't mixing + # them that is not guarded against): + for section in SECTIONS: + if cli_config.value.get(section, False): + template_variables = cli_config.value.pop(section) + break + # Create new -S list. + if any(template_variables): + opts.rose_template_vars = [ + f'{keys[1]}={val.value}' + for keys, val in template_variables.walk(no_ignore=no_ignore) + ] + + # Get all other keys (-D): + new_defines = [] + for keys, value in cli_config.walk(no_ignore=no_ignore): + # Filter out section headings: + if not isinstance(value.value, dict): + section, key = keys + # Don't include section for top level items: + section = f"[{section}]" if section else '' + new_defines.append(f'{section}{key}={value.value}') + + opts.defines = new_defines + return opts + + +def sanitize_opts(opts): + """Warn if standard variables which will be overridden + have been added to CLI opts, and remove them. + + n.b. This doesn't remove the need to check the input rose-suite.conf + for these variables. + """ + options = [] + for section in ['rose_template_vars', 'defines']: + for item in getattr(opts, section, []): + options.append((section, item)) + + for (section, item), (var_name, replace) in itertools.product( + options, STANDARD_VARS + ): + if re.match(rf'.*\b{var_name}=', item): + LOG.warning( + f'{section}:{item} from command line args' + f' will be ignored: {var_name} will be: {replace}' + ) + with suppress(ValueError): + getattr(opts, section).remove(item) + return opts diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..6ae39106 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +# Not mandated to use these tools, but if you do: + +[tool.ruff] +line-length = 79 +target-version = "py37" + +[tool.ruff.format] +quote-style = "preserve" + + +[tool.black] +line-length = 79 +target-version = ['py37'] +skip-string-normalization = true + + +[tool.isort] +profile = "black" +line_length = 79 +force_grid_wrap = 2 +lines_after_imports = 2 +combine_as_imports = true +force_sort_within_sections = true diff --git a/setup.cfg b/setup.cfg index d96759f7..f432a125 100644 --- a/setup.cfg +++ b/setup.cfg @@ -56,7 +56,7 @@ python_requires = >=3.7 include_package_data = True install_requires = metomi-rose==2.3.* - cylc-flow>8.3.2,<8.4 + cylc-flow>8.3.5,<8.4 metomi-isodatetime ansimarkup jinja2 diff --git a/tests/conftest.py b/tests/conftest.py index 7c46c046..6483202c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,16 +16,19 @@ import asyncio from functools import partial +import importlib from io import StringIO from pathlib import Path from shlex import split -from shutil import rmtree +from shutil import rmtree, copytree from subprocess import run +import sys from time import sleep from types import SimpleNamespace from uuid import uuid4 import pytest +from pytest import UsageError from cylc.flow import __version__ as CYLC_VERSION from cylc.flow.option_parsers import Options @@ -169,15 +172,20 @@ def _pytest_passed(request: pytest.FixtureRequest) -> bool: )) -def _cylc_validate_cli(capsys, caplog): - """Access the validate CLI""" - async def _inner(srcpath, args=None): - parser = validate_gop() +def _cylc_inspection_cli(capsys, caplog, script, gop): + """Access the CLI for cylc scripts inspecting configurations + """ + async def _inner(srcpath, args=None, n_args=3): + parser = gop() options = Options(parser, args)() output = SimpleNamespace() try: - await cylc_validate(parser, options, str(srcpath)) + if n_args == 3: + await script(parser, options, str(srcpath)) + if n_args == 2: + # Don't include the parser: + await script(options, str(srcpath)) output.ret = 0 output.exc = '' except Exception as exc: @@ -270,12 +278,76 @@ def mod_cylc_reinstall_cli(mod_test_dir): @pytest.fixture def cylc_validate_cli(capsys, caplog): - return _cylc_validate_cli(capsys, caplog) + return _cylc_inspection_cli(capsys, caplog, cylc_validate, validate_gop) @pytest.fixture(scope='module') def mod_cylc_validate_cli(mod_capsys, mod_caplog): - return _cylc_validate_cli(mod_capsys, mod_caplog) + return _cylc_inspection_cli( + mod_capsys, mod_caplog, cylc_validate, validate_gop + ) + + +@pytest.fixture +async def cylc_inspect_scripts(capsys, caplog): + """Run all the common Cylc Test scripts likely to call pre-configure. + + * config + * graph + * list + * validate + * view + + n.b. + * Function adds arg ``--reference`` to supress image being displayed. + """ + + async def _inner(wid, args): + results = {} + + # Handle scripts taking a parser or just the output of the parser: + for script_name, n_args in { + 'config': 3, + 'list': 3, + 'graph': 3, + 'view': 2, + 'validate': 3, + }.items(): + + # Import the script modules: + script_module = importlib.import_module( + f'cylc.flow.scripts.{script_name}' + ) + + # Deal with inconsistent API from Cylc: + if hasattr(script_module, '_main'): + script = script_module._main + elif hasattr(script_module, 'run'): + script = script_module.run + else: + raise UsageError( + f'Script "{script}\'s" module does not contain a ' + '"_main" or "run" function' + ) + + # Supress cylc-graph giving a graphical output: + if script_name == 'graph': + args['reference'] = True + + results[script_name] = await _cylc_inspection_cli( + capsys, + caplog, + script, + script_module.get_option_parser, + )(wid, args, n_args=n_args) + + # Check outputs + assert all(output.ret == 0 for output in results.values()) + + # Return results for more checking if required: + return results + + return _inner @pytest.fixture @@ -442,9 +514,12 @@ def run_ok(): """Run a bash script. Fail if it fails and return its output. """ + def _inner(script: str): result = run(split(script), capture_output=True) - assert result.returncode == 0, f'{script} failed: {result.stderr}' + assert ( + result.returncode == 0 + ), f'{script} failed: {result.stderr.decode}' return result return _inner @@ -457,3 +532,29 @@ def timeout_func(func, message, timeout=5): sleep(1) else: raise TimeoutError(message) + + +@pytest.fixture +def setup_workflow_source_dir(tmp_path): + """Copy a workflow from the codebase to a temp-file-path + and provide that path for use in tests. + """ + + def _inner(code_src): + nonlocal tmp_path + # Set up paths for test: + testpath = tmp_path / 'src' + testpath.mkdir() + + # the files to install are stored in a directory alongside this + # test file: + datapath = Path(__file__).parent / code_src + if sys.version_info.minor > 7: + copytree(datapath, testpath, dirs_exist_ok=True) + else: + # Python 3.7 bodge: + run(f'cp -r {datapath}/* {testpath}', shell=True) + + return datapath, testpath + + yield _inner diff --git a/tests/functional/15_reinstall_using_old_clivars/flow.cylc b/tests/functional/15_reinstall_using_old_clivars/flow.cylc new file mode 100644 index 00000000..a8771156 --- /dev/null +++ b/tests/functional/15_reinstall_using_old_clivars/flow.cylc @@ -0,0 +1,11 @@ +#!jinja2 +{{ assert(SUITE is defined, "1.1 Test --rose-template-variable") }} +{{ assert(DEFINE is defined, "1.2 Test --define") }} +{{ assert(VAL_FROM_OPT is defined, "1.3 Test --opt-conf-keys") }} + +# Just enough Cylc to validate: +[scheduler] + allow implicit tasks = True +[scheduling] + [[graph]] + R1 = foo diff --git a/tests/functional/15_reinstall_using_old_clivars/opt/rose-suite-choc.conf b/tests/functional/15_reinstall_using_old_clivars/opt/rose-suite-choc.conf new file mode 100644 index 00000000..95237984 --- /dev/null +++ b/tests/functional/15_reinstall_using_old_clivars/opt/rose-suite-choc.conf @@ -0,0 +1,2 @@ +[template variables] +VAL_FROM_OPT=3 diff --git a/tests/functional/15_reinstall_using_old_clivars/rose-suite.conf b/tests/functional/15_reinstall_using_old_clivars/rose-suite.conf new file mode 100644 index 00000000..e69de29b diff --git a/tests/functional/test_pre_configure.py b/tests/functional/test_pre_configure.py index 5b597ba3..9da5f770 100644 --- a/tests/functional/test_pre_configure.py +++ b/tests/functional/test_pre_configure.py @@ -27,6 +27,7 @@ import pytest from pytest import param +from cylc.rose.entry_points import pre_configure from cylc.rose.utilities import NotARoseSuiteException, load_rose_config @@ -219,3 +220,62 @@ def test_cylc_script(monkeypatch, option, envvars, cmd, expect): '05_opts_set_from_rose_suite_conf') output = run(split(f'{cmd} {srcpath} {option}'), capture_output=True) assert expect in output.stdout + + +async def test_validate_against_source( + setup_workflow_source_dir, + cylc_validate_cli, + cylc_install_cli, + cylc_inspect_scripts, +): + """Ensure that validation against source picks up + on existing configs saved in rose-suite-cylc-install.conf + """ + _, src = setup_workflow_source_dir( + 'functional/15_reinstall_using_old_clivars' + ) + + opts = { + "rose_template_vars": ["SUITE=1"], + "defines": ["[template variables]DEFINE=2"], + "opt_conf_keys": ['choc'], + } + + # Check that we can validate the source dir with options: + await cylc_inspect_scripts(src, opts) + + # Install our workflow with options. + wid, _ = await cylc_install_cli(src, opts=opts) + + # Check all scripts: + await cylc_inspect_scripts(wid, {"against_source": True}) + + # Reinstall fails if we clear rose install opts: + clear_install_validate = await cylc_validate_cli( + wid, {"against_source": True, 'clear_rose_install_opts': True} + ) + assert clear_install_validate.ret != 0 + assert 'Jinja2 Assertion Error' in str(clear_install_validate.exc.args[0]) + + +def test_invalid_cli_opts(tmp_path, caplog): + """Ensure that CLI options which won't be used because + they are variables set by Rose raise a warning.""" + (tmp_path / 'flow.cylc').touch() + (tmp_path / 'rose-suite.conf').touch() + + opts = SimpleNamespace( + rose_template_vars=['ROSE_ORIG_HOST=42'], + opt_conf_keys=[], + defines=[], + against_source=tmp_path, + ) + + pre_configure(tmp_path, opts) + # Assert we have the warning that we need: + assert ( + "ROSE_ORIG_HOST=42 from command line args will be ignored" + in caplog.messages[0] + ) + # Assert we haven't got any unwanted dupicate warnings: + assert len(caplog.messages) == 1 diff --git a/tests/functional/test_reinstall_overrides.py b/tests/functional/test_reinstall_overrides.py index 4a0ff7f9..28b02baa 100644 --- a/tests/functional/test_reinstall_overrides.py +++ b/tests/functional/test_reinstall_overrides.py @@ -58,7 +58,7 @@ async def test_reinstall_overrides( opts={'rose_template_vars': ['var="CLIinstall"']}) # Play workflow - run_ok(f'cylc play --pause {wid}') + run_ok(f'cylc play --pause {wid} --host localhost') # Reinstall the workflow: await cylc_reinstall_cli( diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py new file mode 100644 index 00000000..f610a05a --- /dev/null +++ b/tests/unit/test_cli.py @@ -0,0 +1,65 @@ +# THIS FILE IS PART OF THE ROSE-CYLC PLUGIN FOR THE CYLC WORKFLOW ENGINE. +# Copyright (C) NIWA & British Crown (Met Office) & Contributors. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""Tests for CLI Specific functionality""" + +import pytest +from types import SimpleNamespace + +from cylc.rose.utilities import sanitize_opts + + +@pytest.mark.parametrize( + "rose_template_vars, defines, expect_warning", + ( + ( + ["ROSE_ORIG_HOST=3"], + [], + "rose_template_vars:ROSE_ORIG_HOST=3" + " from command line args will be ignored:", + ), + ( + [], + ["[env]ROSE_ORIG_HOST=3"], + "defines:[env]ROSE_ORIG_HOST=3" + " from command line args will be ignored:", + ), + ( + ["ROSE_ORIG_HOST=3"], + ["[env]ROSE_ORIG_HOST=3"], + [ + "defines:[env]ROSE_ORIG_HOST=3" + " from command line args will be ignored:", + "rose_template_vars:ROSE_ORIG_HOST=3" + " from command line args will be ignored:", + ], + ), + ([], [], False) + ), +) +def test_sanitzie_opts(caplog, rose_template_vars, defines, expect_warning): + opts = SimpleNamespace( + rose_template_vars=rose_template_vars, + defines=defines, + ) + sanitize_opts(opts) + if expect_warning and isinstance(expect_warning, list): + for warning in expect_warning: + assert any(warning in w for w in caplog.messages) + elif expect_warning: + assert any(expect_warning in w for w in caplog.messages) + else: + assert not caplog.messages diff --git a/tests/unit/test_config_node.py b/tests/unit/test_config_node.py index 8254dc15..6bb908b6 100644 --- a/tests/unit/test_config_node.py +++ b/tests/unit/test_config_node.py @@ -16,6 +16,7 @@ """Tests the plugin with Rose suite configurations via the Python API.""" +from textwrap import dedent from types import SimpleNamespace from metomi.isodatetime.datetimeoper import DateTimeOperator @@ -34,6 +35,7 @@ process_config, id_templating_section, identify_templating_section, + retrieve_installed_cli_opts, ) @@ -293,7 +295,7 @@ def test_ROSE_ORIG_HOST_replacement_behaviour( if ROSE_ORIG_HOST_overridden is True: node = node_with_ROSE_ORIG_HOST() log_str = ( - '[env]ROSE_ORIG_HOST=IMPLAUSIBLE_HOST_NAME from rose-suite.conf' + '[env]ROSE_ORIG_HOST=IMPLAUSIBLE_HOST_NAME' ' will be ignored' ) assert log_str in caplog.records[0].message @@ -345,3 +347,76 @@ def test_deprecation_warnings( assert must_include in records else: assert must_exclude not in records + + +@pytest.mark.parametrize( + 'tv_string', + (('template variables'), ('jinja2:suite.rc'), ('empy:suite.rc')), +) +def test_retrieve_installed_cli_opts(tmp_path, tv_string): + """It merges src, dest and cli. + """ + # Create a source conifg + rose_suite_conf = tmp_path / 'src/rose-suite.conf' + src = rose_suite_conf.parent + src.mkdir(parents=True) + rose_suite_conf.touch() + (src / 'opt').mkdir() + (src / 'opt/rose-suite-option1.conf').touch() + (src / 'opt/rose-suite-option2.conf').touch() + + # Create saved CLI from earlier CLI: + install_conf = tmp_path / 'run/opt/rose-suite-cylc-install.conf' + install_conf.parent.mkdir(parents=True) + install_conf.write_text( + dedent(f''' + opts = option2 + TOP_LEVEL=false + [env] + ENV_LEAVE=true + ENV_OVERRIDE=false + [{tv_string}] + TV_LEAVE=true + TV_OVERRIDE_D=false + TV_OVERRIDE_S=false + ''') + ) + + opts = SimpleNamespace() + opts.against_source = install_conf.parent.parent + opts.defines = [ + f'[{tv_string}]TV_OVERRIDE_D=True', + '[env]ENV_OVERRIDE=true', + 'TOP_LEVEL=true' + ] + opts.rose_template_vars = ['TV_OVERRIDE_S=True'] + opts.opt_conf_keys = ['option1'] + + opts = retrieve_installed_cli_opts( + srcdir=src, + opts=opts, + ) + + assert opts.opt_conf_keys == ['option2', 'option1'] + + rose_template_vars = [ + o for o in opts.rose_template_vars if 'ROSE_ORIG_HOST' not in o + ] + assert rose_template_vars == [ + 'TV_OVERRIDE_S=True', + 'TV_OVERRIDE_D=True', + 'TV_LEAVE=true', + ] + + defines = [d for d in opts.defines if 'ROSE_ORIG_HOST' not in d] + assert defines == [ + '[env]ENV_OVERRIDE=true', + '[env]ENV_LEAVE=true', + 'TOP_LEVEL=true', + ] + + +def test_retrieve_installed_cli_opts_returns_unchanged(): + """...if clear_rose_install_opts is true.""" + opts = SimpleNamespace(clear_rose_install_opts=True, against_source=True) + assert retrieve_installed_cli_opts('Irrelevant', opts) == opts diff --git a/tests/unit/test_functional_post_install.py b/tests/unit/test_functional_post_install.py index 7ff194ed..4a43a80b 100644 --- a/tests/unit/test_functional_post_install.py +++ b/tests/unit/test_functional_post_install.py @@ -48,7 +48,7 @@ def assert_rose_conf_full_equal(left, right, no_ignore=True): for keys_1, node_1 in left.walk(no_ignore=no_ignore): node_2 = right.get(keys_1, no_ignore=no_ignore) assert not ( # noqa: E721 - type(node_1) != type(node_2) or + type(node_1) is not type(node_2) or ( not isinstance(node_1.value, dict) and node_1.value != node_2.value @@ -120,7 +120,6 @@ def test_rose_fileinstall_uses_rose_template_vars(tmp_path): ), 'ref/rose-suite.conf': '!opts=foo (cylc-install)', 'ref/opt/rose-suite-foo.conf': '', - 'ref/rose-suite.conf': '!opts=foo (cylc-install)' }, # ENVIRONMENT VARS {}, @@ -155,7 +154,6 @@ def test_rose_fileinstall_uses_rose_template_vars(tmp_path): 'ref/opt/rose-suite-foo.conf': '', 'ref/opt/rose-suite-bar.conf': '', 'ref/opt/rose-suite-baz.conf': '', - 'ref/rose-suite.conf': '!opts=foo bar baz (cylc-install)' }, # ENVIRONMENT VARS {},