From 0262e0d97e06ee36786b4491efa178608afb5de5 Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Sat, 12 Dec 2020 07:25:50 -0800 Subject: [PATCH] Added tests for configoptions.py --- README.md | 1 + osxphotos/configoptions.py | 5 ++- setup.py | 1 + tests/test_cli.py | 54 +++++++++++++++++++++++ tests/test_configoptions.py | 86 +++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 tests/test_configoptions.py diff --git a/README.md b/README.md index 94deb39b..64f0a86b 100644 --- a/README.md +++ b/README.md @@ -2247,6 +2247,7 @@ For additional details about how osxphotos is implemented or if you would like t - [bpylist2](https://pypi.org/project/bpylist2/) - [pathvalidate](https://pypi.org/project/pathvalidate/) - [wurlitzer](https://pypi.org/project/wurlitzer/) +- [toml](https://github.com/uiri/toml) ## Acknowledgements diff --git a/osxphotos/configoptions.py b/osxphotos/configoptions.py index 7bacbf7b..679c6b59 100644 --- a/osxphotos/configoptions.py +++ b/osxphotos/configoptions.py @@ -27,6 +27,7 @@ class ConfigOptions: Args: name: name for these options, will be used for section heading in TOML file when saving/loading from file attrs: dict with name and default value for all allowed attributes + ignore: optional list of strings of keys to ignore from attrs dict """ self._name = name self._attrs = attrs.copy() @@ -113,7 +114,7 @@ class ConfigOptions: strb = ", ".join(prefix + x.replace("_", "-") for x in b) else: stra = a - strb = ", ".join(x for x in b) + strb = ", ".join(b) raise ConfigOptionsInvalidError( f"{stra} must be used with at least one of: {strb}." ) @@ -166,7 +167,7 @@ class ConfigOptions: if type(self._attrs[attr]) == tuple: val = tuple(val) setattr(self, attr, val) - return self, None + return self def asdict(self): return {attr: getattr(self, attr) for attr in sorted(self._attrs.keys())} diff --git a/setup.py b/setup.py index 20b10ffd..631f8907 100755 --- a/setup.py +++ b/setup.py @@ -80,6 +80,7 @@ setup( "dataclasses==0.7;python_version<'3.7'", "wurlitzer>=2.0.1", "photoscript>=0.1.0", + "toml>=0.10.0", ], entry_points={"console_scripts": ["osxphotos=osxphotos.__main__:cli"]}, include_package_data=True, diff --git a/tests/test_cli.py b/tests/test_cli.py index df5b4ee2..4c9469e6 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3974,6 +3974,7 @@ def test_save_load_config(): cwd = os.getcwd() # pylint: disable=not-context-manager with runner.isolated_filesystem(): + # test save config file result = runner.invoke( export, [ @@ -3993,6 +3994,7 @@ def test_save_load_config(): files = glob.glob("*") assert "config.toml" in files + # test load config file result = runner.invoke( export, [ @@ -4007,3 +4009,55 @@ def test_save_load_config(): assert "Loaded options from file" in result.output assert "Skipped up to date XMP sidecar" in result.output + # test overwrite existing config file + result = runner.invoke( + export, + [ + os.path.join(cwd, CLI_PHOTOS_DB), + ".", + "-V", + "--sidecar", + "XMP", + "--touch-file", + "--not-live", + "--update", + "--save-config", + "config.toml", + ], + ) + assert result.exit_code == 0 + assert "Saving options to file" in result.output + files = glob.glob("*") + assert "config.toml" in files + + # test load config file with incompat command line option + result = runner.invoke( + export, + [ + os.path.join(cwd, CLI_PHOTOS_DB), + ".", + "-V", + "--load-config", + "config.toml", + "--live", + ], + ) + assert result.exit_code != 0 + assert "Incompatible export options" in result.output + + # test load config file with command line override + result = runner.invoke( + export, + [ + os.path.join(cwd, CLI_PHOTOS_DB), + ".", + "-V", + "--load-config", + "config.toml", + "--sidecar", + "json", + ], + ) + assert result.exit_code == 0 + assert "Writing exiftool JSON sidecar" in result.output + assert "Writing XMP sidecar" not in result.output diff --git a/tests/test_configoptions.py b/tests/test_configoptions.py new file mode 100644 index 00000000..3ec8a59c --- /dev/null +++ b/tests/test_configoptions.py @@ -0,0 +1,86 @@ +""" test ConfigOptions class """ + +import pathlib +import pytest +import toml + +from osxphotos.configoptions import ( + ConfigOptions, + ConfigOptionsInvalidError, + ConfigOptionsLoadError, +) + +VARS = {"foo": "bar", "bar": False, "test1": (), "test2": None, "test2_setting": False} + + +def test_init(): + cfg = ConfigOptions("test", VARS) + assert isinstance(cfg, ConfigOptions) + assert cfg.foo is "bar" + assert cfg.bar == False + assert type(cfg.test1) == tuple + + +def test_init_with_ignore(): + cfg = ConfigOptions("test", VARS, ignore=["test2"]) + assert isinstance(cfg, ConfigOptions) + assert hasattr(cfg, "test1") + assert not hasattr(cfg, "test2") + + +def test_write_to_file_load_from_file(tmpdir): + cfg = ConfigOptions("test", VARS) + cfg.bar = True + cfg_file = pathlib.Path(str(tmpdir)) / "test.toml" + cfg.write_to_file(str(cfg_file)) + assert cfg_file.is_file() + + cfg_dict = toml.load(str(cfg_file)) + assert cfg_dict["test"]["foo"] == "bar" + + cfg2 = ConfigOptions("test", VARS).load_from_file(str(cfg_file)) + assert cfg2.foo == "bar" + assert cfg2.bar + + +def test_load_from_file_error(tmpdir): + cfg_file = pathlib.Path(str(tmpdir)) / "test.toml" + cfg = ConfigOptions("test", VARS) + cfg.write_to_file(str(cfg_file)) + # try to load with a section that doesn't exist in the TOML file + with pytest.raises(ConfigOptionsLoadError): + cfg2 = ConfigOptions("FOO", VARS).load_from_file(str(cfg_file)) + + +def test_asdict(): + cfg = ConfigOptions("test", VARS) + cfg_dict = cfg.asdict() + assert cfg_dict["foo"] == "bar" + assert cfg_dict["bar"] == False + assert cfg_dict["test1"] == () + + +def test_validate(): + cfg = ConfigOptions("test", VARS) + + # test exclusive + assert cfg.validate(exclusive=[("foo", "bar")]) + cfg.bar = True + with pytest.raises(ConfigOptionsInvalidError): + assert cfg.validate(exclusive=[("foo", "bar")]) + + # test dependent + cfg.test2 = True + cfg.test2_setting = 1.0 + assert cfg.validate(dependent=[("test2_setting", ("test2"))]) + cfg.test2 = False + with pytest.raises(ConfigOptionsInvalidError): + assert cfg.validate(dependent=[("test2_setting", ("test2"))]) + + # test inclusive + cfg.foo = "foo" + cfg.bar = True + assert cfg.validate(inclusive=[("foo", "bar")]) + cfg.foo = None + with pytest.raises(ConfigOptionsInvalidError): + assert cfg.validate(inclusive=[("foo", "bar")])