From 2253e52ab7bda332f404c3109fbd36a520dbfd09 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 14:25:26 -0500 Subject: [PATCH 01/14] Update test Docker image * Update base image (which uses a newer python) * Update linters * Update other dependencies --- Makefile | 2 +- test/Dockerfile | 10 +++++----- test/conftest.py | 2 +- test/requirements.txt | 10 ++++++---- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 69fa003..5da0918 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTESTS = $(wildcard test/test_*.py) -IMAGE = docker.io/yadm/testbed:2022-01-07 +IMAGE = docker.io/yadm/testbed:2023-07-12 OCI = docker .PHONY: all diff --git a/test/Dockerfile b/test/Dockerfile index e6a0a97..9fb2b3a 100644 --- a/test/Dockerfile +++ b/test/Dockerfile @@ -1,9 +1,9 @@ -FROM ubuntu:18.04 +FROM ubuntu:23.04 MAINTAINER Tim Byrne # Shellcheck and esh versions -ARG SC_VER=0.8.0 -ARG ESH_VER=0.3.1 +ARG SC_VER=0.9.0 +ARG ESH_VER=0.3.2 # Install prerequisites and configure UTF-8 locale RUN \ @@ -41,8 +41,8 @@ RUN cd /opt \ # Upgrade pip3 and install requirements COPY test/requirements.txt /tmp/requirements.txt -RUN python3 -m pip install --upgrade pip setuptools \ - && python3 -m pip install --upgrade -r /tmp/requirements.txt \ +RUN python3 -m pip install --break-system-packages --upgrade pip setuptools \ + && python3 -m pip install --break-system-packages --upgrade -r /tmp/requirements.txt \ && rm -f /tmp/requirements # Install esh diff --git a/test/conftest.py b/test/conftest.py index d3bbb76..f3ce22c 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -43,7 +43,7 @@ def flake8_version(): @pytest.fixture(scope='session') def yamllint_version(): """Version of yamllint supported""" - return '1.25.0' + return '1.30.0' @pytest.fixture(scope='session') diff --git a/test/requirements.txt b/test/requirements.txt index 30da6ae..e71b349 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -1,6 +1,8 @@ +black==23.1.0 envtpl -flake8==3.8.4 +flake8==6.0.0 +isort==5.12.0 j2cli -pylint==2.6.0 -pytest==6.2.1 -yamllint==1.25.0 +pylint==2.17.0 +pytest==7.2.2 +yamllint==1.30.0 From 82ba16db34f1b8375cf3e70c490819e04e959e26 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 14:28:00 -0500 Subject: [PATCH 02/14] Support testing with newer version of Git --- test/conftest.py | 3 +++ test/test_init.py | 2 ++ test/test_upgrade.py | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/test/conftest.py b/test/conftest.py index f3ce22c..db15e4a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -267,6 +267,9 @@ def runner(): @pytest.fixture(scope='session') def config_git(): """Configure global git configuration, if missing""" + os.system( + 'git config init.defaultBranch || ' + 'git config --global init.defaultBranch master') os.system( 'git config user.name || ' 'git config --global user.name "test"') diff --git a/test/test_init.py b/test/test_init.py index 5542332..3d149d6 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -56,6 +56,8 @@ def test_init( args.append('-f') # run init + runner(['git', 'config', '--global', 'init.defaultBranch', 'master'], + env={'HOME': home}, cwd=cwd) run = runner(yadm_cmd(*args), env={'HOME': home}, cwd=cwd) if repo_present and not force: diff --git a/test/test_upgrade.py b/test/test_upgrade.py index 2d4d697..f1a5076 100644 --- a/test/test_upgrade.py +++ b/test/test_upgrade.py @@ -22,6 +22,10 @@ def test_upgrade(tmpdir, runner, versions, submodule): # pylint: disable=too-many-statements home = tmpdir.mkdir('HOME') env = {'HOME': str(home)} + runner(['git', 'config', '--global', 'init.defaultBranch', 'master'], + env=env) + runner(['git', 'config', '--global', 'protocol.file.allow', 'always'], + env=env) if submodule: ext_repo = tmpdir.mkdir('ext_repo') From 84437894b51e4c28dac6e0e05cb2e4dbaba9b479 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 14:46:46 -0500 Subject: [PATCH 03/14] Use man.REAL --- test/test_syntax.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_syntax.py b/test/test_syntax.py index 72f4ccc..17f66ca 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -59,7 +59,7 @@ def test_yamllint(pytestconfig, runner, yamllint_version): def test_man(runner): """Check for warnings from man""" run = runner( - command=['man', '--warnings', './yadm.1']) + command=['man.REAL', '--warnings', './yadm.1']) assert run.success assert run.err == '' assert 'yadm - Yet Another Dotfiles Manager' in run.out From 9752d801ae9d4d8c0e08a4b0b98acb3ea8660e56 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 15:31:14 -0500 Subject: [PATCH 04/14] Replace deprecated "pipes" with "shlex" --- test/test_encryption.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/test_encryption.py b/test/test_encryption.py index 73f4c35..4868eb3 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -1,7 +1,7 @@ """Test encryption""" import os -import pipes +import shlex import time import pytest @@ -19,12 +19,12 @@ def add_asymmetric_key(runner, gnupg): env = os.environ.copy() env['GNUPGHOME'] = gnupg.home runner( - ['gpg', '--import', pipes.quote(KEY_FILE)], + ['gpg', '--import', shlex.quote(KEY_FILE)], env=env, shell=True, ) runner( - ['gpg', '--import-ownertrust', '<', pipes.quote(KEY_TRUST)], + ['gpg', '--import-ownertrust', '<', shlex.quote(KEY_TRUST)], env=env, shell=True, ) @@ -37,7 +37,7 @@ def remove_asymmetric_key(runner, gnupg): runner( [ 'gpg', '--batch', '--yes', - '--delete-secret-keys', pipes.quote(KEY_FINGERPRINT) + '--delete-secret-keys', shlex.quote(KEY_FINGERPRINT) ], env=env, shell=True, @@ -45,7 +45,7 @@ def remove_asymmetric_key(runner, gnupg): runner( [ 'gpg', '--batch', '--yes', - '--delete-key', pipes.quote(KEY_FINGERPRINT) + '--delete-key', shlex.quote(KEY_FINGERPRINT) ], env=env, shell=True, @@ -149,7 +149,7 @@ def decrypt_targets(tmpdir_factory, runner, gnupg): ['tar', 'cvf', '-'] + expected + ['|', 'gpg', '--batch', '--yes', '-c'] + - ['--output', pipes.quote(str(symmetric))], + ['--output', shlex.quote(str(symmetric))], cwd=tmpdir, env=env, shell=True) @@ -161,8 +161,8 @@ def decrypt_targets(tmpdir_factory, runner, gnupg): ['tar', 'cvf', '-'] + expected + ['|', 'gpg', '--batch', '--yes', '-e'] + - ['-r', pipes.quote(KEY_NAME)] + - ['--output', pipes.quote(str(asymmetric))], + ['-r', shlex.quote(KEY_NAME)] + + ['--output', shlex.quote(str(asymmetric))], cwd=tmpdir, env=env, shell=True) @@ -465,7 +465,7 @@ def encrypted_data_valid(runner, gnupg, encrypted, expected): env['GNUPGHOME'] = gnupg.home run = runner([ 'gpg', - '-d', pipes.quote(str(encrypted)), + '-d', shlex.quote(str(encrypted)), '2>/dev/null', '|', 'tar', 't'], env=env, shell=True, report=False) file_count = 0 From c8acf77e04e10437d3eb22ed7fc90f4a0ae07e00 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sat, 8 Jul 2023 15:52:41 -0500 Subject: [PATCH 05/14] Adjust pinentry mock The new test Docker image has a newer gnupg which does not behave the same way, handling invalid passwords. This type of error is simulated using an ICP error in the pinentry protocol. --- test/pinentry-mock | 18 +++++++++++++----- test/test_encryption.py | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/test/pinentry-mock b/test/pinentry-mock index d40033b..39da043 100755 --- a/test/pinentry-mock +++ b/test/pinentry-mock @@ -1,12 +1,20 @@ #!/bin/bash # This program is a custom mock pinentry program -# It always uses whatever password is found in the /tmp directory -password="$(cat /tmp/mock-password 2>/dev/null)" +# It uses whatever password is found in the /tmp directory +# If the password is empty, replies CANCEL causing an error to similate invalid +# credentials echo "OK Pleased to meet you" while read -r line; do if [[ $line =~ GETPIN ]]; then - echo -n "D " - echo "$password" + password="$(cat /tmp/mock-password 2>/dev/null)" + if [ -n "$password" ]; then + echo -n "D " + echo "$password" + echo "OK"; + else + echo "CANCEL"; + fi + else + echo "OK"; fi - echo "OK"; done diff --git a/test/test_encryption.py b/test/test_encryption.py index 4868eb3..829ca1b 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -214,7 +214,7 @@ def test_symmetric_encrypt( if missing_encrypt: assert 'does not exist' in run.err elif bad_phrase: - assert 'Invalid passphrase' in run.err + assert 'Invalid IPC' in run.err else: assert encrypted_data_valid( runner, gnupg, paths.archive, encrypt_targets) From f9e03683858a91c94271756c7f0e5973eae841a8 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sun, 9 Jul 2023 10:32:52 -0500 Subject: [PATCH 06/14] Changes for new shellcheck compliance --- test/conftest.py | 2 +- yadm | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index db15e4a..1490d55 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -25,7 +25,7 @@ def pytest_addoption(parser): @pytest.fixture(scope='session') def shellcheck_version(): """Version of shellcheck supported""" - return '0.8.0' + return '0.9.0' @pytest.fixture(scope='session') diff --git a/yadm b/yadm index 003d954..09da278 100755 --- a/yadm +++ b/yadm @@ -289,18 +289,18 @@ function record_score() { # increase the index of any existing alt_template_cmds new_cmds=() for cmd_index in "${!alt_template_cmds[@]}"; do - new_cmds[$((cmd_index+1))]="${alt_template_cmds[$cmd_index]}" + new_cmds[cmd_index+1]="${alt_template_cmds[$cmd_index]}" done alt_template_cmds=() for cmd_index in "${!new_cmds[@]}"; do - alt_template_cmds[$cmd_index]="${new_cmds[$cmd_index]}" + alt_template_cmds[cmd_index]="${new_cmds[$cmd_index]}" done else alt_targets+=("$tgt") # set index to the last index (newly created one) for index in "${!alt_targets[@]}"; do :; done # and set its initial score to zero - alt_scores[$index]=0 + alt_scores[index]=0 fi fi @@ -309,8 +309,8 @@ function record_score() { # record higher scoring sources if [ "$score" -gt "${alt_scores[$index]}" ]; then - alt_scores[$index]="$score" - alt_sources[$index]="$src" + alt_scores[index]="$score" + alt_sources[index]="$src" fi } @@ -336,8 +336,8 @@ function record_template() { fi # record the template command, last one wins - alt_template_cmds[$index]="$cmd" - alt_sources[$index]="$src" + alt_template_cmds[index]="$cmd" + alt_sources[index]="$src" } From 8a87f4a30ffaa983562cefe64c8b79cb8f8a8002 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sun, 9 Jul 2023 11:13:13 -0500 Subject: [PATCH 07/14] Changes for new pylint compliance --- test/conftest.py | 42 +++++++++++++++++++----------------- test/test_syntax.py | 2 +- test/test_unit_score_file.py | 12 +++++------ test/test_upgrade.py | 2 +- test/test_version.py | 5 ++--- test/utils.py | 2 +- 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 1490d55..7ccd9be 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -3,10 +3,10 @@ import collections import contextlib import copy -import distutils.dir_util # pylint: disable=no-name-in-module,import-error import os import platform import pwd +import shutil from subprocess import Popen, PIPE import py import pytest @@ -31,7 +31,7 @@ def shellcheck_version(): @pytest.fixture(scope='session') def pylint_version(): """Version of pylint supported""" - return '2.6.0' + return '2.17.0' @pytest.fixture(scope='session') @@ -204,7 +204,7 @@ class Runner(): merged_env.update(env) self.inp = inp self.wrap(expect) - process = Popen( + with Popen( self.command, stdin=PIPE, stdout=PIPE, @@ -212,14 +212,14 @@ class Runner(): shell=shell, cwd=cwd, env=merged_env, - ) - input_bytes = self.inp - if self.inp: - input_bytes = self.inp.encode() - (out_bstream, err_bstream) = process.communicate(input=input_bytes) - self.out = out_bstream.decode() - self.err = err_bstream.decode() - self.code = process.wait() + ) as process: + input_bytes = self.inp + if self.inp: + input_bytes = self.inp.encode() + (out_bstream, err_bstream) = process.communicate(input=input_bytes) + self.out = out_bstream.decode() + self.err = err_bstream.decode() + self.code = process.wait() self.success = self.code == 0 self.failure = self.code != 0 if report: @@ -365,6 +365,10 @@ def yadm_cmd(paths): return command_list +class NoRelativePath(Exception): + """Exception when finding relative paths""" + + class DataFile(): """Datafile object""" @@ -384,7 +388,7 @@ class DataFile(): """Relative path property""" if self.__parent: return self.__parent.join(self.path) - raise BaseException('Unable to provide relative path, no parent') + raise NoRelativePath('Unable to provide relative path, no parent') @property def tracked(self): @@ -405,10 +409,10 @@ class DataSet(): """Dataset object""" def __init__(self): - self.__files = list() - self.__dirs = list() - self.__tracked_dirs = list() - self.__private_dirs = list() + self.__files = [] + self.__dirs = [] + self.__tracked_dirs = [] + self.__private_dirs = [] self.__relpath = None def __repr__(self): @@ -562,15 +566,13 @@ def ds1_data(tmpdir_factory, config_git, ds1_dset, runner): @pytest.fixture() def ds1_work_copy(ds1_data, paths): """Function scoped copy of ds1_data.work""" - distutils.dir_util.copy_tree( # pylint: disable=no-member - str(ds1_data.work), str(paths.work)) + shutil.copytree(str(ds1_data.work), str(paths.work), dirs_exist_ok=True) @pytest.fixture() def ds1_repo_copy(runner, ds1_data, paths): """Function scoped copy of ds1_data.repo""" - distutils.dir_util.copy_tree( # pylint: disable=no-member - str(ds1_data.repo), str(paths.repo)) + shutil.copytree(str(ds1_data.repo), str(paths.repo), dirs_exist_ok=True) env = os.environ.copy() env['GIT_DIR'] = str(paths.repo) runner( diff --git a/test/test_syntax.py b/test/test_syntax.py index 17f66ca..1a422b3 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -26,7 +26,7 @@ def test_pylint(pytestconfig, runner, pylint_version): run = runner(command=['pylint', '--version'], report=False) if f'pylint {pylint_version}' not in run.out: pytest.skip('Unsupported pylint version') - pyfiles = list() + pyfiles = [] for tfile in os.listdir('test'): if tfile.endswith('.py'): pyfiles.append(f'test/{tfile}') diff --git a/test/test_unit_score_file.py b/test/test_unit_score_file.py index e59c128..dd2f0b7 100644 --- a/test/test_unit_score_file.py +++ b/test/test_unit_score_file.py @@ -213,14 +213,14 @@ def test_score_values( local_user={local_user} """ expected = '' - for filename in filenames: + for filename, score in filenames.items(): script += f""" score_file "{filename}" echo "{filename}" echo "$score" """ expected += filename + '\n' - expected += str(filenames[filename]) + '\n' + expected += str(score) + '\n' run = runner(command=['bash'], inp=script) assert run.success assert run.err == '' @@ -278,14 +278,14 @@ def test_score_values_templates(runner, yadm): local_user={local_user} """ expected = '' - for filename in filenames: + for filename, score in filenames.items(): script += f""" score_file "{filename}" echo "{filename}" echo "$score" """ expected += filename + '\n' - expected += str(filenames[filename]) + '\n' + expected += str(score) + '\n' run = runner(command=['bash'], inp=script) assert run.success assert run.err == '' @@ -337,14 +337,14 @@ def test_underscores_in_distro_and_family(runner, yadm): local_distro_family="{local_distro_family}" """ expected = '' - for filename in filenames: + for filename, score in filenames.items(): script += f""" score_file "{filename}" echo "{filename}" echo "$score" """ expected += filename + '\n' - expected += str(filenames[filename]) + '\n' + expected += str(score) + '\n' run = runner(command=['bash'], inp=script) assert run.success assert run.err == '' diff --git a/test/test_upgrade.py b/test/test_upgrade.py index f1a5076..77b7e4a 100644 --- a/test/test_upgrade.py +++ b/test/test_upgrade.py @@ -39,7 +39,7 @@ def test_upgrade(tmpdir, runner, versions, submodule): os.environ.pop('XDG_DATA_HOME', None) def run_version(version, *args, check_stderr=True): - yadm = 'yadm-%s' % version if version else '/yadm/yadm' + yadm = f'yadm-{version}' if version else '/yadm/yadm' run = runner([yadm, *args], shell=True, cwd=str(home), env=env) assert run.success if check_stderr: diff --git a/test/test_version.py b/test/test_version.py index d440d3b..5655e47 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -10,9 +10,8 @@ def expected_version(yadm): Expected semantic version number. This is taken directly out of yadm, searching for the VERSION= string. """ - yadm_version = re.findall( - r'VERSION=([^\n]+)', - open(yadm).read()) + with open(yadm, encoding='utf-8') as source_file: + yadm_version = re.findall(r'VERSION=([^\n]+)', source_file.read()) if yadm_version: return yadm_version[0] pytest.fail(f'version not found in {yadm}') diff --git a/test/utils.py b/test/utils.py index 67a9e53..6cbe066 100644 --- a/test/utils.py +++ b/test/utils.py @@ -81,7 +81,7 @@ def parse_alt_output(output, linked=True): regex = r'Creating (.+) from template (.+)$' if linked: regex = r'Linking (.+) to (.+)$' - parsed_list = dict() + parsed_list = {} for line in output.splitlines(): match = re.match(regex, line) if match: From bbc1ca17f09963fe5f5241834c6c3b930736125c Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Sun, 9 Jul 2023 11:15:38 -0500 Subject: [PATCH 08/14] Changes for new flake8 compliance --- test/conftest.py | 2 +- test/test_config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 7ccd9be..5e366b4 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -37,7 +37,7 @@ def pylint_version(): @pytest.fixture(scope='session') def flake8_version(): """Version of flake8 supported""" - return '3.8.4' + return '6.0.0' @pytest.fixture(scope='session') diff --git a/test/test_config.py b/test/test_config.py index d364128..19b0d02 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -140,7 +140,7 @@ def test_config_local_write(runner, yadm_cmd, paths, supported_local_configs): def test_config_without_parent_directory(runner, yadm_cmd, paths): - """Write and read attribute to/from config file with non-existent parent dir + """Write/read attribute to/from config file with non-existent parent dir Update configuration file Display value From 7af2af52aec33d7309aa5f1c70a82749d524e7fb Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 10 Jul 2023 09:14:33 -0500 Subject: [PATCH 09/14] Lint using isort --- test/conftest.py | 9 ++++++++- test/test_alt.py | 1 + test/test_alt_copy.py | 1 + test/test_assert_private_dirs.py | 1 + test/test_clone.py | 1 + test/test_config.py | 1 + test/test_encryption.py | 1 + test/test_enter.py | 1 + test/test_git.py | 1 + test/test_list.py | 1 + test/test_perms.py | 1 + test/test_syntax.py | 11 +++++++++++ test/test_unit_copy_perms.py | 1 + test/test_unit_remove_stale_links.py | 1 + test/test_unit_template_j2.py | 1 + test/test_unit_x_program.py | 1 + test/test_upgrade.py | 1 + test/test_version.py | 1 + test/utils.py | 2 +- 19 files changed, 36 insertions(+), 2 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 5e366b4..af68477 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -7,7 +7,8 @@ import os import platform import pwd import shutil -from subprocess import Popen, PIPE +from subprocess import PIPE, Popen + import py import pytest @@ -34,6 +35,12 @@ def pylint_version(): return '2.17.0' +@pytest.fixture(scope='session') +def isort_version(): + """Version of isort supported""" + return '5.12.0' + + @pytest.fixture(scope='session') def flake8_version(): """Version of flake8 supported""" diff --git a/test/test_alt.py b/test/test_alt.py index ddaf374..e57caee 100644 --- a/test/test_alt.py +++ b/test/test_alt.py @@ -1,6 +1,7 @@ """Test alt""" import os import string + import py import pytest import utils diff --git a/test/test_alt_copy.py b/test/test_alt_copy.py index fa8e09c..eb19e3d 100644 --- a/test/test_alt_copy.py +++ b/test/test_alt_copy.py @@ -1,6 +1,7 @@ """Test yadm.alt-copy""" import os + import pytest diff --git a/test/test_assert_private_dirs.py b/test/test_assert_private_dirs.py index bfd55ac..440446b 100644 --- a/test/test_assert_private_dirs.py +++ b/test/test_assert_private_dirs.py @@ -2,6 +2,7 @@ import os import re + import pytest pytestmark = pytest.mark.usefixtures('ds1_copy') diff --git a/test/test_clone.py b/test/test_clone.py index d5da7d9..9268965 100644 --- a/test/test_clone.py +++ b/test/test_clone.py @@ -2,6 +2,7 @@ import os import re + import pytest BOOTSTRAP_CODE = 123 diff --git a/test/test_config.py b/test/test_config.py index 19b0d02..d6a5e33 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -1,6 +1,7 @@ """Test config""" import os + import pytest TEST_SECTION = 'test' diff --git a/test/test_encryption.py b/test/test_encryption.py index 829ca1b..78bbf3a 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -3,6 +3,7 @@ import os import shlex import time + import pytest KEY_FILE = 'test/test_key' diff --git a/test/test_enter.py b/test/test_enter.py index f5ea2d8..5148e23 100644 --- a/test/test_enter.py +++ b/test/test_enter.py @@ -1,6 +1,7 @@ """Test enter""" import os + import pytest diff --git a/test/test_git.py b/test/test_git.py index 76eccab..41b102c 100644 --- a/test/test_git.py +++ b/test/test_git.py @@ -1,6 +1,7 @@ """Test git""" import re + import pytest diff --git a/test/test_list.py b/test/test_list.py index dcfe500..d7d09a6 100644 --- a/test/test_list.py +++ b/test/test_list.py @@ -1,6 +1,7 @@ """Test list""" import os + import pytest diff --git a/test/test_perms.py b/test/test_perms.py index 4f052bd..a49d897 100644 --- a/test/test_perms.py +++ b/test/test_perms.py @@ -1,6 +1,7 @@ """Test perms""" import os + import pytest diff --git a/test/test_syntax.py b/test/test_syntax.py index 1a422b3..8fae79e 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -1,6 +1,7 @@ """Syntax checks""" import os + import pytest @@ -34,6 +35,16 @@ def test_pylint(pytestconfig, runner, pylint_version): assert run.success +def test_isort(pytestconfig, runner, isort_version): + """Passes isort""" + if not pytestconfig.getoption("--force-linters"): + run = runner(command=['isort', '--version'], report=False) + if isort_version not in run.out: + pytest.skip('Unsupported isort version') + run = runner(command=['isort', '-c', 'test']) + assert run.success + + def test_flake8(pytestconfig, runner, flake8_version): """Passes flake8""" if not pytestconfig.getoption("--force-linters"): diff --git a/test/test_unit_copy_perms.py b/test/test_unit_copy_perms.py index 3c79768..b043878 100644 --- a/test/test_unit_copy_perms.py +++ b/test/test_unit_copy_perms.py @@ -1,5 +1,6 @@ """Unit tests: copy_perms""" import os + import pytest OCTAL = '7654' diff --git a/test/test_unit_remove_stale_links.py b/test/test_unit_remove_stale_links.py index 0bd960b..4fcf1a1 100644 --- a/test/test_unit_remove_stale_links.py +++ b/test/test_unit_remove_stale_links.py @@ -1,5 +1,6 @@ """Unit tests: remove_stale_links""" import os + import pytest diff --git a/test/test_unit_template_j2.py b/test/test_unit_template_j2.py index 4042a2d..84afc2d 100644 --- a/test/test_unit_template_j2.py +++ b/test/test_unit_template_j2.py @@ -1,5 +1,6 @@ """Unit tests: template_j2cli & template_envtpl""" import os + import pytest FILE_MODE = 0o754 diff --git a/test/test_unit_x_program.py b/test/test_unit_x_program.py index 8302f3c..883c9af 100644 --- a/test/test_unit_x_program.py +++ b/test/test_unit_x_program.py @@ -1,6 +1,7 @@ """Unit tests: yadm.[git,gpg]-program""" import os + import pytest diff --git a/test/test_upgrade.py b/test/test_upgrade.py index 77b7e4a..1805882 100644 --- a/test/test_upgrade.py +++ b/test/test_upgrade.py @@ -1,6 +1,7 @@ """Test upgrade""" import os + import pytest diff --git a/test/test_version.py b/test/test_version.py index 5655e47..aee6f33 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -1,6 +1,7 @@ """Test version""" import re + import pytest diff --git a/test/utils.py b/test/utils.py index 6cbe066..f71a758 100644 --- a/test/utils.py +++ b/test/utils.py @@ -3,8 +3,8 @@ This module holds values/functions common to multiple tests. """ -import re import os +import re ALT_FILE1 = 'test_alt' ALT_FILE2 = 'test alt/test alt' From 1b36bf2bb64539cf0c74af94e6cfcf5c087610fd Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 10 Jul 2023 13:58:02 -0500 Subject: [PATCH 10/14] Move all test/linting configs into pyproject.toml Except for flake8 which doesn't support pyproject.toml yet. --- .flake8 | 2 ++ pylintrc | 17 ----------------- pyproject.toml | 31 +++++++++++++++++++++++++++++++ pytest.ini | 5 ----- test/pylintrc | 1 - 5 files changed, 33 insertions(+), 23 deletions(-) create mode 100644 .flake8 delete mode 100644 pylintrc create mode 100644 pyproject.toml delete mode 100644 pytest.ini delete mode 120000 test/pylintrc diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..6deafc2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 120 diff --git a/pylintrc b/pylintrc deleted file mode 100644 index ba41b74..0000000 --- a/pylintrc +++ /dev/null @@ -1,17 +0,0 @@ -[BASIC] -good-names=pytestmark - -[DESIGN] -max-args=14 -max-locals=28 -max-attributes=8 -max-statements=65 - -[SIMILARITIES] -min-similarity-lines=8 - -[MESSAGES CONTROL] -disable=redefined-outer-name - -[TYPECHECK] -ignored-modules=py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1e51d44 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[tool.pytest.ini_options] +cache_dir = "/tmp" +addopts = "-ra" +markers = [ + "deprecated", # marks tests for deprecated features (deselect with '-m "not deprecated"') +] + +[tool.pylint.design] +max-args = 14 +max-locals = 28 +max-attributes = 8 +max-statements = 65 + +[tool.pylint.format] +max-line-length = 120 + +[tool.pylint."messages control"] +disable = [ + "redefined-outer-name", +] + +[tool.pylint.similarities] +ignore-imports = "yes" +min-similarity-lines = 8 + +[tool.black] +line-length = 120 + +[tool.isort] +line_length = 120 +profile = "black" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index d032ea5..0000000 --- a/pytest.ini +++ /dev/null @@ -1,5 +0,0 @@ -[pytest] -cache_dir = /tmp -addopts = -ra -markers = - deprecated: marks tests for deprecated features (deselect with '-m "not deprecated"') diff --git a/test/pylintrc b/test/pylintrc deleted file mode 120000 index 05334af..0000000 --- a/test/pylintrc +++ /dev/null @@ -1 +0,0 @@ -../pylintrc \ No newline at end of file From e70417520124970b65bb469836180cd8be6f57d8 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 10 Jul 2023 09:18:36 -0500 Subject: [PATCH 11/14] Add black to linting --- test/conftest.py | 6 ++++++ test/test_syntax.py | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/test/conftest.py b/test/conftest.py index af68477..e05d5dc 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -47,6 +47,12 @@ def flake8_version(): return '6.0.0' +@pytest.fixture(scope='session') +def black_version(): + """Version of black supported""" + return '23.1.0' + + @pytest.fixture(scope='session') def yamllint_version(): """Version of yamllint supported""" diff --git a/test/test_syntax.py b/test/test_syntax.py index 8fae79e..aadafde 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -55,6 +55,16 @@ def test_flake8(pytestconfig, runner, flake8_version): assert run.success +def test_black(pytestconfig, runner, black_version): + """Passes black""" + if not pytestconfig.getoption("--force-linters"): + run = runner(command=['black', '--version'], report=False) + if black_version not in run.out: + pytest.skip('Unsupported black version') + run = runner(command=['black', '--check', 'test']) + assert run.success + + def test_yamllint(pytestconfig, runner, yamllint_version): """Passes yamllint""" if not pytestconfig.getoption("--force-linters"): From 76ce3defea0e69c3e8130e7cfdcd22b64322aea2 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Mon, 10 Jul 2023 14:43:17 -0500 Subject: [PATCH 12/14] Changes for black compliance --- test/conftest.py | 406 +++++++++----------- test/test_alt.py | 226 +++++------ test/test_alt_copy.py | 44 +-- test/test_assert_private_dirs.py | 46 +-- test/test_bootstrap.py | 38 +- test/test_clean.py | 6 +- test/test_clone.py | 282 +++++++------- test/test_config.py | 81 ++-- test/test_encryption.py | 373 ++++++++---------- test/test_enter.py | 140 +++---- test/test_ext_crypt.py | 36 +- test/test_git.py | 40 +- test/test_help.py | 10 +- test/test_hooks.py | 129 +++---- test/test_init.py | 66 ++-- test/test_introspect.py | 40 +- test/test_list.py | 38 +- test/test_perms.py | 86 ++--- test/test_syntax.py | 57 ++- test/test_unit_bootstrap_available.py | 10 +- test/test_unit_choose_template_cmd.py | 48 +-- test/test_unit_configure_paths.py | 94 ++--- test/test_unit_copy_perms.py | 34 +- test/test_unit_encryption.py | 80 ++-- test/test_unit_exclude_encrypted.py | 60 ++- test/test_unit_issue_legacy_path_warning.py | 33 +- test/test_unit_parse_encrypt.py | 177 +++++---- test/test_unit_private_dirs.py | 20 +- test/test_unit_query_distro.py | 28 +- test/test_unit_query_distro_family.py | 22 +- test/test_unit_record_score.py | 86 ++--- test/test_unit_record_template.py | 24 +- test/test_unit_relative_path.py | 6 +- test/test_unit_remove_stale_links.py | 28 +- test/test_unit_report_invalid_alts.py | 20 +- test/test_unit_score_file.py | 321 +++++++--------- test/test_unit_set_local_alt_values.py | 62 +-- test/test_unit_set_os.py | 38 +- test/test_unit_set_yadm_dir.py | 55 ++- test/test_unit_template_default.py | 74 ++-- test/test_unit_template_esh.py | 29 +- test/test_unit_template_j2.py | 33 +- test/test_unit_upgrade.py | 103 +++-- test/test_unit_x_program.py | 35 +- test/test_upgrade.py | 137 ++++--- test/test_version.py | 26 +- test/utils.py | 56 +-- 47 files changed, 1845 insertions(+), 2038 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index e05d5dc..b18cae4 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -23,169 +23,168 @@ def pytest_addoption(parser): ) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def shellcheck_version(): """Version of shellcheck supported""" - return '0.9.0' + return "0.9.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def pylint_version(): """Version of pylint supported""" - return '2.17.0' + return "2.17.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def isort_version(): """Version of isort supported""" - return '5.12.0' + return "5.12.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def flake8_version(): """Version of flake8 supported""" - return '6.0.0' + return "6.0.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def black_version(): """Version of black supported""" - return '23.1.0' + return "23.1.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def yamllint_version(): """Version of yamllint supported""" - return '1.30.0' + return "1.30.0" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_user(): """Test session's user id""" return pwd.getpwuid(os.getuid()).pw_name -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_host(): """Test session's short hostname value""" - return platform.node().split('.')[0] + return platform.node().split(".")[0] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_distro(runner): """Test session's distro""" - distro = '' + distro = "" with contextlib.suppress(Exception): - run = runner(command=['lsb_release', '-si'], report=False) + run = runner(command=["lsb_release", "-si"], report=False) distro = run.out.strip() return distro -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_distro_family(runner): """Test session's distro_family""" - family = '' + family = "" with contextlib.suppress(Exception): - run = runner(command=[ - 'grep', '-oP', r'ID_LIKE=\K.+', '/etc/os-release'], report=False) + run = runner(command=["grep", "-oP", r"ID_LIKE=\K.+", "/etc/os-release"], report=False) family = run.out.strip() return family -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_sys(): """Test session's uname value""" return platform.system() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def tst_arch(): """Test session's uname value""" return platform.machine() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def supported_commands(): """List of supported commands This list should be updated every time yadm learns a new command. """ return [ - 'alt', - 'bootstrap', - 'clean', - 'clone', - 'config', - 'decrypt', - 'encrypt', - 'enter', - 'git-crypt', - 'gitconfig', - 'help', - 'init', - 'introspect', - 'list', - 'perms', - 'transcrypt', - 'upgrade', - 'version', - ] + "alt", + "bootstrap", + "clean", + "clone", + "config", + "decrypt", + "encrypt", + "enter", + "git-crypt", + "gitconfig", + "help", + "init", + "introspect", + "list", + "perms", + "transcrypt", + "upgrade", + "version", + ] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def supported_configs(): """List of supported config options This list should be updated every time yadm learns a new config. """ return [ - 'local.arch', - 'local.class', - 'local.hostname', - 'local.os', - 'local.user', - 'yadm.alt-copy', - 'yadm.auto-alt', - 'yadm.auto-exclude', - 'yadm.auto-perms', - 'yadm.auto-private-dirs', - 'yadm.cipher', - 'yadm.git-program', - 'yadm.gpg-perms', - 'yadm.gpg-program', - 'yadm.gpg-recipient', - 'yadm.openssl-ciphername', - 'yadm.openssl-old', - 'yadm.openssl-program', - 'yadm.ssh-perms', - ] + "local.arch", + "local.class", + "local.hostname", + "local.os", + "local.user", + "yadm.alt-copy", + "yadm.auto-alt", + "yadm.auto-exclude", + "yadm.auto-perms", + "yadm.auto-private-dirs", + "yadm.cipher", + "yadm.git-program", + "yadm.gpg-perms", + "yadm.gpg-program", + "yadm.gpg-recipient", + "yadm.openssl-ciphername", + "yadm.openssl-old", + "yadm.openssl-program", + "yadm.ssh-perms", + ] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def supported_switches(): """List of supported switches This list should be updated every time yadm learns a new switch. """ return [ - '--yadm-archive', - '--yadm-bootstrap', - '--yadm-config', - '--yadm-data', - '--yadm-dir', - '--yadm-encrypt', - '--yadm-repo', - '-Y', - ] + "--yadm-archive", + "--yadm-bootstrap", + "--yadm-config", + "--yadm-data", + "--yadm-dir", + "--yadm-encrypt", + "--yadm-repo", + "-Y", + ] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def supported_local_configs(supported_configs): """List of supported local config options""" - return [c for c in supported_configs if c.startswith('local.')] + return [c for c in supported_configs if c.startswith("local.")] -class Runner(): +class Runner: """Class for running commands Within yadm tests, this object should be used when running commands that @@ -198,17 +197,9 @@ class Runner(): Other instances of simply running commands should use os.system(). """ - def __init__( - self, - command, - inp=None, - shell=False, - cwd=None, - env=None, - expect=None, - report=True): + def __init__(self, command, inp=None, shell=False, cwd=None, env=None, expect=None, report=True): if shell: - self.command = ' '.join([str(cmd) for cmd in command]) + self.command = " ".join([str(cmd) for cmd in command]) else: self.command = command if env is None: @@ -239,56 +230,43 @@ class Runner(): self.report() def __repr__(self): - return f'Runner({self.command})' + return f"Runner({self.command})" def report(self): """Print code/stdout/stderr""" - print(f'{self}') - print(f' RUN: code:{self.code}') + print(f"{self}") + print(f" RUN: code:{self.code}") if self.inp: - print(f' RUN: input:\n{self.inp}') - print(f' RUN: stdout:\n{self.out}') - print(f' RUN: stderr:\n{self.err}') + print(f" RUN: input:\n{self.inp}") + print(f" RUN: stdout:\n{self.out}") + print(f" RUN: stderr:\n{self.err}") def wrap(self, expect): """Wrap command with expect""" if not expect: return - cmdline = ' '.join([f'"{w}"' for w in self.command]) - expect_script = f'set timeout 2\nspawn {cmdline}\n' + cmdline = " ".join([f'"{w}"' for w in self.command]) + expect_script = f"set timeout 2\nspawn {cmdline}\n" for question, answer in expect: - expect_script += ( - 'expect {\n' - f'"{question}" {{send "{answer}\\r"}}\n' - 'timeout {close;exit 128}\n' - '}\n') - expect_script += ( - 'expect eof\n' - 'foreach {pid spawnid os_error_flag value} [wait] break\n' - 'exit $value') + expect_script += "expect {\n" f'"{question}" {{send "{answer}\\r"}}\n' "timeout {close;exit 128}\n" "}\n" + expect_script += "expect eof\n" "foreach {pid spawnid os_error_flag value} [wait] break\n" "exit $value" self.inp = expect_script - print(f'EXPECT:{expect_script}') - self.command = ['expect'] + print(f"EXPECT:{expect_script}") + self.command = ["expect"] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def runner(): """Class for running commands""" return Runner -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def config_git(): """Configure global git configuration, if missing""" - os.system( - 'git config init.defaultBranch || ' - 'git config --global init.defaultBranch master') - os.system( - 'git config user.name || ' - 'git config --global user.name "test"') - os.system( - 'git config user.email || ' - 'git config --global user.email "test@test.test"') + os.system("git config init.defaultBranch || git config --global init.defaultBranch master") + os.system('git config user.name || git config --global user.name "test"') + os.system('git config user.email || git config --global user.email "test@test.test"') @pytest.fixture() @@ -298,19 +276,19 @@ def repo_config(runner, paths): def query_func(key): """Query a yadm repo configuration value""" run = runner( - command=('git', 'config', '--local', key), - env={'GIT_DIR': paths.repo}, + command=("git", "config", "--local", key), + env={"GIT_DIR": paths.repo}, report=False, - ) + ) return run.out.rstrip() return query_func -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def yadm(): """Path to yadm program to be tested""" - full_path = os.path.realpath('yadm') + full_path = os.path.realpath("yadm") assert os.path.isfile(full_path), "yadm program file isn't present" return full_path @@ -319,38 +297,40 @@ def yadm(): def paths(tmpdir, yadm): """Function scoped test paths""" - dir_root = tmpdir.mkdir('root') - dir_remote = dir_root.mkdir('remote') - dir_work = dir_root.mkdir('work') - dir_xdg_data = dir_root.mkdir('xdg_data') - dir_xdg_home = dir_root.mkdir('xdg_home') - dir_data = dir_xdg_data.mkdir('yadm') - dir_yadm = dir_xdg_home.mkdir('yadm') - dir_hooks = dir_yadm.mkdir('hooks') - dir_repo = dir_data.mkdir('repo.git') - file_archive = dir_data.join('archive') - file_bootstrap = dir_yadm.join('bootstrap') - file_config = dir_yadm.join('config') - file_encrypt = dir_yadm.join('encrypt') + dir_root = tmpdir.mkdir("root") + dir_remote = dir_root.mkdir("remote") + dir_work = dir_root.mkdir("work") + dir_xdg_data = dir_root.mkdir("xdg_data") + dir_xdg_home = dir_root.mkdir("xdg_home") + dir_data = dir_xdg_data.mkdir("yadm") + dir_yadm = dir_xdg_home.mkdir("yadm") + dir_hooks = dir_yadm.mkdir("hooks") + dir_repo = dir_data.mkdir("repo.git") + file_archive = dir_data.join("archive") + file_bootstrap = dir_yadm.join("bootstrap") + file_config = dir_yadm.join("config") + file_encrypt = dir_yadm.join("encrypt") paths = collections.namedtuple( - 'Paths', [ - 'pgm', - 'root', - 'remote', - 'work', - 'xdg_data', - 'xdg_home', - 'data', - 'yadm', - 'hooks', - 'repo', - 'archive', - 'bootstrap', - 'config', - 'encrypt', - ]) - os.environ['XDG_CONFIG_HOME'] = str(dir_xdg_home) - os.environ['XDG_DATA_HOME'] = str(dir_xdg_data) + "Paths", + [ + "pgm", + "root", + "remote", + "work", + "xdg_data", + "xdg_home", + "data", + "yadm", + "hooks", + "repo", + "archive", + "bootstrap", + "config", + "encrypt", + ], + ) + os.environ["XDG_CONFIG_HOME"] = str(dir_xdg_home) + os.environ["XDG_DATA_HOME"] = str(dir_xdg_data) return paths( yadm, dir_root, @@ -366,15 +346,17 @@ def paths(tmpdir, yadm): file_bootstrap, file_config, file_encrypt, - ) + ) @pytest.fixture() def yadm_cmd(paths): """Generate custom command_list function""" + def command_list(*args): """Produce params for running yadm with -Y""" return [paths.pgm] + list(args) + return command_list @@ -382,7 +364,7 @@ class NoRelativePath(Exception): """Exception when finding relative paths""" -class DataFile(): +class DataFile: """Datafile object""" def __init__(self, path, tracked=True, private=False): @@ -401,7 +383,7 @@ class DataFile(): """Relative path property""" if self.__parent: return self.__parent.join(self.path) - raise NoRelativePath('Unable to provide relative path, no parent') + raise NoRelativePath("Unable to provide relative path, no parent") @property def tracked(self): @@ -418,7 +400,7 @@ class DataFile(): self.__parent = parent -class DataSet(): +class DataSet: """Dataset object""" def __init__(self): @@ -429,11 +411,7 @@ class DataSet(): self.__relpath = None def __repr__(self): - return ( - f'[DS with {len(self)} files; ' - f'{len(self.tracked)} tracked, ' - f'{len(self.private)} private]' - ) + return f"[DS with {len(self)} files; " f"{len(self.tracked)} tracked, " f"{len(self.private)} private]" def __iter__(self): return iter(self.__files) @@ -471,17 +449,17 @@ class DataSet(): @property def plain_dirs(self): """List of directories in DataSet not starting with '.'""" - return [d for d in self.dirs if not d.startswith('.')] + return [d for d in self.dirs if not d.startswith(".")] @property def hidden_dirs(self): """List of directories in DataSet starting with '.'""" - return [d for d in self.dirs if d.startswith('.')] + return [d for d in self.dirs if d.startswith(".")] @property def tracked_dirs(self): """List of directories in DataSet not starting with '.'""" - return [d for d in self.__tracked_dirs if not d.startswith('.')] + return [d for d in self.__tracked_dirs if not d.startswith(".")] @property def private_dirs(self): @@ -511,23 +489,23 @@ class DataSet(): datafile.relative_to(self.__relpath) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def ds1_dset(tst_sys): """Meta-data for dataset one files""" dset = DataSet() - dset.add_file('t1') - dset.add_file('d1/t2') - dset.add_file(f'test_alt_copy##os.{tst_sys}') - dset.add_file('u1', tracked=False) - dset.add_file('d2/u2', tracked=False) - dset.add_file('.ssh/p1', tracked=False, private=True) - dset.add_file('.ssh/.p2', tracked=False, private=True) - dset.add_file('.gnupg/p3', tracked=False, private=True) - dset.add_file('.gnupg/.p4', tracked=False, private=True) + dset.add_file("t1") + dset.add_file("d1/t2") + dset.add_file(f"test_alt_copy##os.{tst_sys}") + dset.add_file("u1", tracked=False) + dset.add_file("d2/u2", tracked=False) + dset.add_file(".ssh/p1", tracked=False, private=True) + dset.add_file(".ssh/.p2", tracked=False, private=True) + dset.add_file(".gnupg/p3", tracked=False, private=True) + dset.add_file(".gnupg/.p4", tracked=False, private=True) return dset -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def ds1_data(tmpdir_factory, config_git, ds1_dset, runner): """A set of test data, worktree & repo""" # pylint: disable=unused-argument @@ -535,44 +513,24 @@ def ds1_data(tmpdir_factory, config_git, ds1_dset, runner): # @pytest.mark.usefixtures('config_git') # cannot be applied to another fixture. - data = tmpdir_factory.mktemp('ds1') + data = tmpdir_factory.mktemp("ds1") - work = data.mkdir('work') + work = data.mkdir("work") for datafile in ds1_dset: work.join(datafile.path).write(datafile.path, ensure=True) - repo = data.mkdir('repo.git') + repo = data.mkdir("repo.git") env = os.environ.copy() - env['GIT_DIR'] = str(repo) - runner( - command=['git', 'init', '--shared=0600', '--bare', str(repo)], - report=False) - runner( - command=['git', 'config', 'core.bare', 'false'], - env=env, - report=False) - runner( - command=['git', 'config', 'status.showUntrackedFiles', 'no'], - env=env, - report=False) - runner( - command=['git', 'config', 'yadm.managed', 'true'], - env=env, - report=False) - runner( - command=['git', 'config', 'core.worktree', str(work)], - env=env, - report=False) - runner( - command=['git', 'add'] + - [str(work.join(f.path)) for f in ds1_dset if f.tracked], - env=env) - runner( - command=['git', 'commit', '--allow-empty', '-m', 'Initial commit'], - env=env, - report=False) + env["GIT_DIR"] = str(repo) + runner(command=["git", "init", "--shared=0600", "--bare", str(repo)], report=False) + runner(command=["git", "config", "core.bare", "false"], env=env, report=False) + runner(command=["git", "config", "status.showUntrackedFiles", "no"], env=env, report=False) + runner(command=["git", "config", "yadm.managed", "true"], env=env, report=False) + runner(command=["git", "config", "core.worktree", str(work)], env=env, report=False) + runner(command=["git", "add"] + [str(work.join(f.path)) for f in ds1_dset if f.tracked], env=env) + runner(command=["git", "commit", "--allow-empty", "-m", "Initial commit"], env=env, report=False) - data = collections.namedtuple('Data', ['work', 'repo']) + data = collections.namedtuple("Data", ["work", "repo"]) return data(work, repo) @@ -587,11 +545,8 @@ def ds1_repo_copy(runner, ds1_data, paths): """Function scoped copy of ds1_data.repo""" shutil.copytree(str(ds1_data.repo), str(paths.repo), dirs_exist_ok=True) env = os.environ.copy() - env['GIT_DIR'] = str(paths.repo) - runner( - command=['git', 'config', 'core.worktree', str(paths.work)], - env=env, - report=False) + env["GIT_DIR"] = str(paths.repo) + runner(command=["git", "config", "core.worktree", str(paths.work)], env=env, report=False) @pytest.fixture() @@ -616,30 +571,27 @@ def ds1(ds1_work_copy, paths, ds1_dset): return dscopy -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def gnupg(tmpdir_factory, runner): """Location of GNUPGHOME""" def register_gpg_password(password): """Publish a new GPG mock password""" - py.path.local('/tmp/mock-password').write(password) + py.path.local("/tmp/mock-password").write(password) - home = tmpdir_factory.mktemp('gnupghome') + home = tmpdir_factory.mktemp("gnupghome") home.chmod(0o700) - conf = home.join('gpg.conf') - conf.write('no-secmem-warning\n') + conf = home.join("gpg.conf") + conf.write("no-secmem-warning\n") conf.chmod(0o600) - agentconf = home.join('gpg-agent.conf') - agentconf.write( - f'pinentry-program {os.path.abspath("test/pinentry-mock")}\n' - 'max-cache-ttl 0\n' - ) + agentconf = home.join("gpg-agent.conf") + agentconf.write(f'pinentry-program {os.path.abspath("test/pinentry-mock")}\n' "max-cache-ttl 0\n") agentconf.chmod(0o600) - data = collections.namedtuple('GNUPG', ['home', 'pw']) + data = collections.namedtuple("GNUPG", ["home", "pw"]) env = os.environ.copy() - env['GNUPGHOME'] = home + env["GNUPGHOME"] = home # this pre-populates std files in the GNUPGHOME - runner(['gpg', '-k'], env=env) + runner(["gpg", "-k"], env=env) return data(home, register_gpg_password) diff --git a/test/test_alt.py b/test/test_alt.py index e57caee..c429ad4 100644 --- a/test/test_alt.py +++ b/test/test_alt.py @@ -9,34 +9,34 @@ import utils TEST_PATHS = [utils.ALT_FILE1, utils.ALT_FILE2, utils.ALT_DIR] -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('yadm_alt', [True, False], ids=['alt', 'worktree']) +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("yadm_alt", [True, False], ids=["alt", "worktree"]) @pytest.mark.parametrize( - 'tracked,encrypt,exclude', [ + "tracked,encrypt,exclude", + [ (False, False, False), (True, False, False), (False, True, False), (False, True, True), - ], ids=['untracked', 'tracked', 'encrypted', 'excluded']) -def test_alt_source( - runner, paths, - tracked, encrypt, exclude, - yadm_alt): + ], + ids=["untracked", "tracked", "encrypted", "excluded"], +) +def test_alt_source(runner, paths, tracked, encrypt, exclude, yadm_alt): """Test yadm alt operates on all expected sources of alternates""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) utils.create_alt_files( - paths, '##default', tracked=tracked, encrypt=encrypt, exclude=exclude, - yadm_alt=yadm_alt, yadm_dir=yadm_dir) - run = runner([paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'alt']) + paths, "##default", tracked=tracked, encrypt=encrypt, exclude=exclude, yadm_alt=yadm_alt, yadm_dir=yadm_dir + ) + run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"]) assert run.success - assert run.err == '' + assert run.err == "" linked = utils.parse_alt_output(run.out) - basepath = yadm_dir.join('alt') if yadm_alt else paths.work + basepath = yadm_dir.join("alt") if yadm_alt else paths.work for link_path in TEST_PATHS: - source_file_content = link_path + '##default' + source_file_content = link_path + "##default" source_file = basepath.join(source_file_content) link_file = paths.work.join(link_path) if tracked or (encrypt and not exclude): @@ -46,63 +46,69 @@ def test_alt_source( assert link_file.read() == source_file_content assert str(source_file) in linked else: - assert link_file.join( - utils.CONTAINED).read() == source_file_content + assert link_file.join(utils.CONTAINED).read() == source_file_content assert str(source_file) in linked else: assert not link_file.exists() assert str(source_file) not in linked -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('yadm_alt', [True, False], ids=['alt', 'worktree']) +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("yadm_alt", [True, False], ids=["alt", "worktree"]) def test_relative_link(runner, paths, yadm_alt): """Confirm links created are relative""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) utils.create_alt_files( - paths, '##default', tracked=True, encrypt=False, exclude=False, - yadm_alt=yadm_alt, yadm_dir=yadm_dir) - run = runner([paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'alt']) + paths, "##default", tracked=True, encrypt=False, exclude=False, yadm_alt=yadm_alt, yadm_dir=yadm_dir + ) + run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"]) assert run.success - assert run.err == '' + assert run.err == "" - basepath = yadm_dir.join('alt') if yadm_alt else paths.work + basepath = yadm_dir.join("alt") if yadm_alt else paths.work for link_path in TEST_PATHS: - source_file_content = link_path + '##default' + source_file_content = link_path + "##default" source_file = basepath.join(source_file_content) link_file = paths.work.join(link_path) link = link_file.readlink() - relpath = os.path.relpath( - source_file, start=os.path.dirname(link_file)) + relpath = os.path.relpath(source_file, start=os.path.dirname(link_file)) assert link == relpath -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('suffix', [ - '##default', - '##default,e.txt', '##default,extension.txt', - '##a.$tst_arch', '##arch.$tst_arch', - '##o.$tst_sys', '##os.$tst_sys', - '##d.$tst_distro', '##distro.$tst_distro', - '##f.$tst_distro_family', '##distro_family.$tst_distro_family', - '##c.$tst_class', '##class.$tst_class', - '##h.$tst_host', '##hostname.$tst_host', - '##u.$tst_user', '##user.$tst_user', - ]) -def test_alt_conditions( - runner, paths, - tst_arch, tst_sys, tst_distro, tst_distro_family, tst_host, tst_user, - suffix): +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize( + "suffix", + [ + "##default", + "##default,e.txt", + "##default,extension.txt", + "##a.$tst_arch", + "##arch.$tst_arch", + "##o.$tst_sys", + "##os.$tst_sys", + "##d.$tst_distro", + "##distro.$tst_distro", + "##f.$tst_distro_family", + "##distro_family.$tst_distro_family", + "##c.$tst_class", + "##class.$tst_class", + "##h.$tst_host", + "##hostname.$tst_host", + "##u.$tst_user", + "##user.$tst_user", + ], +) +def test_alt_conditions(runner, paths, tst_arch, tst_sys, tst_distro, tst_distro_family, tst_host, tst_user, suffix): """Test conditions supported by yadm alt""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) # set the class - tst_class = 'testclass' - utils.set_local(paths, 'class', tst_class + ".before") - utils.set_local(paths, 'class', tst_class, add=True) - utils.set_local(paths, 'class', tst_class + ".after", add=True) + tst_class = "testclass" + utils.set_local(paths, "class", tst_class + ".before") + utils.set_local(paths, "class", tst_class, add=True) + utils.set_local(paths, "class", tst_class + ".after", add=True) suffix = string.Template(suffix).substitute( tst_arch=tst_arch, @@ -115,9 +121,9 @@ def test_alt_conditions( ) utils.create_alt_files(paths, suffix) - run = runner([paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'alt']) + run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"]) assert run.success - assert run.err == '' + assert run.err == "" linked = utils.parse_alt_output(run.out) for link_path in TEST_PATHS: @@ -128,27 +134,31 @@ def test_alt_conditions( assert paths.work.join(link_path).read() == source_file assert str(paths.work.join(source_file)) in linked else: - assert paths.work.join(link_path).join( - utils.CONTAINED).read() == source_file + assert paths.work.join(link_path).join(utils.CONTAINED).read() == source_file assert str(paths.work.join(source_file)) in linked -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("kind", ["default", "", None, "envtpl", "j2cli", "j2", "esh"]) @pytest.mark.parametrize( - 'kind', ['default', '', None, 'envtpl', 'j2cli', 'j2', 'esh']) -@pytest.mark.parametrize('label', ['t', 'template', 'yadm', ]) -def test_alt_templates( - runner, paths, kind, label): + "label", + [ + "t", + "template", + "yadm", + ], +) +def test_alt_templates(runner, paths, kind, label): """Test templates supported by yadm alt""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) - suffix = f'##{label}.{kind}' + suffix = f"##{label}.{kind}" if kind is None: - suffix = f'##{label}' + suffix = f"##{label}" utils.create_alt_files(paths, suffix) - run = runner([paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'alt']) + run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "alt"]) assert run.success - assert run.err == '' + assert run.err == "" created = utils.parse_alt_output(run.out, linked=False) for created_path in TEST_PATHS: @@ -159,41 +169,39 @@ def test_alt_templates( assert str(paths.work.join(source_file)) in created -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('autoalt', [None, 'true', 'false']) +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("autoalt", [None, "true", "false"]) def test_auto_alt(runner, yadm_cmd, paths, autoalt): """Test auto alt""" # set the value of auto-alt if autoalt: - os.system(' '.join(yadm_cmd('config', 'yadm.auto-alt', autoalt))) + os.system(" ".join(yadm_cmd("config", "yadm.auto-alt", autoalt))) - utils.create_alt_files(paths, '##default') - run = runner(yadm_cmd('status')) + utils.create_alt_files(paths, "##default") + run = runner(yadm_cmd("status")) assert run.success - assert run.err == '' + assert run.err == "" linked = utils.parse_alt_output(run.out) for link_path in TEST_PATHS: - source_file = link_path + '##default' - if autoalt == 'false': + source_file = link_path + "##default" + if autoalt == "false": assert not paths.work.join(link_path).exists() else: assert paths.work.join(link_path).islink() - target = py.path.local( - os.path.realpath(paths.work.join(link_path))) + target = py.path.local(os.path.realpath(paths.work.join(link_path))) if target.isfile(): assert paths.work.join(link_path).read() == source_file # no linking output when run via auto-alt assert str(paths.work.join(source_file)) not in linked else: - assert paths.work.join(link_path).join( - utils.CONTAINED).read() == source_file + assert paths.work.join(link_path).join(utils.CONTAINED).read() == source_file # no linking output when run via auto-alt assert str(paths.work.join(source_file)) not in linked -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.usefixtures("ds1_copy") def test_stale_link_removal(runner, yadm_cmd, paths): """Stale links to alternative files are removed @@ -202,48 +210,47 @@ def test_stale_link_removal(runner, yadm_cmd, paths): """ # set the class - tst_class = 'testclass' - utils.set_local(paths, 'class', tst_class) + tst_class = "testclass" + utils.set_local(paths, "class", tst_class) # create files which match the test class - utils.create_alt_files(paths, f'##class.{tst_class}') + utils.create_alt_files(paths, f"##class.{tst_class}") # run alt to trigger linking - run = runner(yadm_cmd('alt')) + run = runner(yadm_cmd("alt")) assert run.success - assert run.err == '' + assert run.err == "" linked = utils.parse_alt_output(run.out) # assert the proper linking has occurred for stale_path in TEST_PATHS: - source_file = stale_path + '##class.' + tst_class + source_file = stale_path + "##class." + tst_class assert paths.work.join(stale_path).islink() target = py.path.local(os.path.realpath(paths.work.join(stale_path))) if target.isfile(): assert paths.work.join(stale_path).read() == source_file assert str(paths.work.join(source_file)) in linked else: - assert paths.work.join(stale_path).join( - utils.CONTAINED).read() == source_file + assert paths.work.join(stale_path).join(utils.CONTAINED).read() == source_file assert str(paths.work.join(source_file)) in linked # change the class so there are no valid alternates - utils.set_local(paths, 'class', 'changedclass') + utils.set_local(paths, "class", "changedclass") # run alt to trigger linking - run = runner(yadm_cmd('alt')) + run = runner(yadm_cmd("alt")) assert run.success - assert run.err == '' + assert run.err == "" linked = utils.parse_alt_output(run.out) # assert the linking is removed for stale_path in TEST_PATHS: - source_file = stale_path + '##class.' + tst_class + source_file = stale_path + "##class." + tst_class assert not paths.work.join(stale_path).exists() assert str(paths.work.join(source_file)) not in linked -@pytest.mark.usefixtures('ds1_repo_copy') +@pytest.mark.usefixtures("ds1_repo_copy") def test_template_overwrite_symlink(runner, yadm_cmd, paths, tst_sys): """Remove symlinks before processing a template @@ -252,45 +259,44 @@ def test_template_overwrite_symlink(runner, yadm_cmd, paths, tst_sys): be removed just before processing a template. """ - target = paths.work.join(f'test_link##os.{tst_sys}') - target.write('target') + target = paths.work.join(f"test_link##os.{tst_sys}") + target.write("target") - link = paths.work.join('test_link') + link = paths.work.join("test_link") link.mksymlinkto(target, absolute=1) - template = paths.work.join('test_link##template.default') - template.write('test-data') + template = paths.work.join("test_link##template.default") + template.write("test-data") - run = runner(yadm_cmd('add', target, template)) + run = runner(yadm_cmd("add", target, template)) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" assert not link.islink() - assert target.read().strip() == 'target' - assert link.read().strip() == 'test-data' + assert target.read().strip() == "target" + assert link.read().strip() == "test-data" -@pytest.mark.usefixtures('ds1_copy') -@pytest.mark.parametrize('style', ['symlink', 'template']) +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("style", ["symlink", "template"]) def test_ensure_alt_path(runner, paths, style): """Test that directories are created before making alternates""" yadm_dir, yadm_data = setup_standard_yadm_dir(paths) - suffix = 'default' if style == 'symlink' else 'template' - filename = 'a/b/c/file' - source = yadm_dir.join(f'alt/{filename}##{suffix}') - source.write('test-data', ensure=True) - run = runner([ - paths.pgm, '-Y', yadm_dir, '--yadm-data', yadm_data, 'add', source]) + suffix = "default" if style == "symlink" else "template" + filename = "a/b/c/file" + source = yadm_dir.join(f"alt/{filename}##{suffix}") + source.write("test-data", ensure=True) + run = runner([paths.pgm, "-Y", yadm_dir, "--yadm-data", yadm_data, "add", source]) assert run.success - assert run.err == '' - assert run.out == '' - assert paths.work.join(filename).read().strip() == 'test-data' + assert run.err == "" + assert run.out == "" + assert paths.work.join(filename).read().strip() == "test-data" def setup_standard_yadm_dir(paths): """Configure a yadm home within the work tree""" - std_yadm_dir = paths.work.mkdir('.config').mkdir('yadm') - std_yadm_data = paths.work.mkdir('.local').mkdir('share').mkdir('yadm') - std_yadm_data.join('repo.git').mksymlinkto(paths.repo, absolute=1) - std_yadm_dir.join('encrypt').mksymlinkto(paths.encrypt, absolute=1) + std_yadm_dir = paths.work.mkdir(".config").mkdir("yadm") + std_yadm_data = paths.work.mkdir(".local").mkdir("share").mkdir("yadm") + std_yadm_data.join("repo.git").mksymlinkto(paths.repo, absolute=1) + std_yadm_dir.join("encrypt").mksymlinkto(paths.encrypt, absolute=1) return std_yadm_dir, std_yadm_data diff --git a/test/test_alt_copy.py b/test/test_alt_copy.py index eb19e3d..e1beece 100644 --- a/test/test_alt_copy.py +++ b/test/test_alt_copy.py @@ -6,41 +6,41 @@ import pytest @pytest.mark.parametrize( - 'setting, expect_link, pre_existing', [ + "setting, expect_link, pre_existing", + [ (None, True, None), (True, False, None), (False, True, None), - (True, False, 'link'), - (True, False, 'file'), + (True, False, "link"), + (True, False, "file"), ], ids=[ - 'unset', - 'true', - 'false', - 'pre-existing symlink', - 'pre-existing file', - ]) -@pytest.mark.usefixtures('ds1_copy') -def test_alt_copy( - runner, yadm_cmd, paths, tst_sys, - setting, expect_link, pre_existing): + "unset", + "true", + "false", + "pre-existing symlink", + "pre-existing file", + ], +) +@pytest.mark.usefixtures("ds1_copy") +def test_alt_copy(runner, yadm_cmd, paths, tst_sys, setting, expect_link, pre_existing): """Test yadm.alt-copy""" if setting is not None: - os.system(' '.join(yadm_cmd('config', 'yadm.alt-copy', str(setting)))) + os.system(" ".join(yadm_cmd("config", "yadm.alt-copy", str(setting)))) - expected_content = f'test_alt_copy##os.{tst_sys}' + expected_content = f"test_alt_copy##os.{tst_sys}" - alt_path = paths.work.join('test_alt_copy') - if pre_existing == 'symlink': + alt_path = paths.work.join("test_alt_copy") + if pre_existing == "symlink": alt_path.mklinkto(expected_content) - elif pre_existing == 'file': - alt_path.write('wrong content') + elif pre_existing == "file": + alt_path.write("wrong content") - run = runner(yadm_cmd('alt')) + run = runner(yadm_cmd("alt")) assert run.success - assert run.err == '' - assert 'Linking' in run.out + assert run.err == "" + assert "Linking" in run.out assert alt_path.read() == expected_content assert alt_path.islink() == expect_link diff --git a/test/test_assert_private_dirs.py b/test/test_assert_private_dirs.py index 440446b..0cba1f6 100644 --- a/test/test_assert_private_dirs.py +++ b/test/test_assert_private_dirs.py @@ -5,11 +5,11 @@ import re import pytest -pytestmark = pytest.mark.usefixtures('ds1_copy') -PRIVATE_DIRS = ['.gnupg', '.ssh'] +pytestmark = pytest.mark.usefixtures("ds1_copy") +PRIVATE_DIRS = [".gnupg", ".ssh"] -@pytest.mark.parametrize('home', [True, False], ids=['home', 'not-home']) +@pytest.mark.parametrize("home", [True, False], ids=["home", "not-home"]) def test_pdirs_missing(runner, yadm_cmd, paths, home): """Private dirs (private dirs missing) @@ -25,15 +25,15 @@ def test_pdirs_missing(runner, yadm_cmd, paths, home): path.remove() assert not path.exists() - env = {'DEBUG': 'yes'} + env = {"DEBUG": "yes"} if home: - env['HOME'] = paths.work + env["HOME"] = paths.work # run status - run = runner(command=yadm_cmd('status'), env=env) + run = runner(command=yadm_cmd("status"), env=env) assert run.success - assert run.err == '' - assert 'On branch master' in run.out + assert run.err == "" + assert "On branch master" in run.out # confirm directories are created # and are protected @@ -41,17 +41,15 @@ def test_pdirs_missing(runner, yadm_cmd, paths, home): path = paths.work.join(pdir) if home: assert path.exists() - assert oct(path.stat().mode).endswith('00'), ('Directory is ' - 'not secured') + assert oct(path.stat().mode).endswith("00"), "Directory is " "not secured" else: assert not path.exists() # confirm directories are created before command is run: if home: assert re.search( - (r'Creating.+\.(gnupg|ssh).+Creating.+\.(gnupg|ssh).+' - r'Running git command git status'), - run.out, re.DOTALL), 'directories created before command is run' + r"Creating.+\.(gnupg|ssh).+Creating.+\.(gnupg|ssh).+Running git command git status", run.out, re.DOTALL + ), "directories created before command is run" def test_pdirs_missing_apd_false(runner, yadm_cmd, paths): @@ -71,14 +69,13 @@ def test_pdirs_missing_apd_false(runner, yadm_cmd, paths): assert not path.exists() # set configuration - os.system(' '.join(yadm_cmd( - 'config', '--bool', 'yadm.auto-private-dirs', 'false'))) + os.system(" ".join(yadm_cmd("config", "--bool", "yadm.auto-private-dirs", "false"))) # run status - run = runner(command=yadm_cmd('status')) + run = runner(command=yadm_cmd("status")) assert run.success - assert run.err == '' - assert 'On branch master' in run.out + assert run.err == "" + assert "On branch master" in run.out # confirm directories are STILL missing for pdir in PRIVATE_DIRS: @@ -100,19 +97,18 @@ def test_pdirs_exist_apd_false(runner, yadm_cmd, paths): if not path.isdir(): path.mkdir() path.chmod(0o777) - assert oct(path.stat().mode).endswith('77'), 'Directory is secure.' + assert oct(path.stat().mode).endswith("77"), "Directory is secure." # set configuration - os.system(' '.join(yadm_cmd( - 'config', '--bool', 'yadm.auto-perms', 'false'))) + os.system(" ".join(yadm_cmd("config", "--bool", "yadm.auto-perms", "false"))) # run status - run = runner(command=yadm_cmd('status')) + run = runner(command=yadm_cmd("status")) assert run.success - assert run.err == '' - assert 'On branch master' in run.out + assert run.err == "" + assert "On branch master" in run.out # created directories are STILL permissive for pdir in PRIVATE_DIRS: path = paths.work.join(pdir) - assert oct(path.stat().mode).endswith('77'), 'Directory is secure' + assert oct(path.stat().mode).endswith("77"), "Directory is secure" diff --git a/test/test_bootstrap.py b/test/test_bootstrap.py index 4865ece..099dde8 100644 --- a/test/test_bootstrap.py +++ b/test/test_bootstrap.py @@ -4,32 +4,30 @@ import pytest @pytest.mark.parametrize( - 'exists, executable, code, expect', [ - (False, False, 1, 'Cannot execute bootstrap'), - (True, False, 1, 'is not an executable program'), - (True, True, 123, 'Bootstrap successful'), - ], ids=[ - 'missing', - 'not executable', - 'executable', - ]) -def test_bootstrap( - runner, yadm_cmd, paths, exists, executable, code, expect): + "exists, executable, code, expect", + [ + (False, False, 1, "Cannot execute bootstrap"), + (True, False, 1, "is not an executable program"), + (True, True, 123, "Bootstrap successful"), + ], + ids=[ + "missing", + "not executable", + "executable", + ], +) +def test_bootstrap(runner, yadm_cmd, paths, exists, executable, code, expect): """Test bootstrap command""" if exists: - paths.bootstrap.write('') + paths.bootstrap.write("") if executable: - paths.bootstrap.write( - '#!/bin/bash\n' - f'echo {expect}\n' - f'exit {code}\n' - ) + paths.bootstrap.write("#!/bin/bash\n" f"echo {expect}\n" f"exit {code}\n") paths.bootstrap.chmod(0o775) - run = runner(command=yadm_cmd('bootstrap')) + run = runner(command=yadm_cmd("bootstrap")) assert run.code == code if exists and executable: - assert run.err == '' + assert run.err == "" assert expect in run.out else: assert expect in run.err - assert run.out == '' + assert run.out == "" diff --git a/test/test_clean.py b/test/test_clean.py index 39e7e54..4eaf1e4 100644 --- a/test/test_clean.py +++ b/test/test_clean.py @@ -3,9 +3,9 @@ def test_clean_command(runner, yadm_cmd): """Run with clean command""" - run = runner(command=yadm_cmd('clean')) + run = runner(command=yadm_cmd("clean")) # do nothing, this is a dangerous Git command when managing dot files # report the command as disabled and exit as a failure assert run.failure - assert run.out == '' - assert 'disabled' in run.err + assert run.out == "" + assert "disabled" in run.err diff --git a/test/test_clone.py b/test/test_clone.py index 9268965..1cae929 100644 --- a/test/test_clone.py +++ b/test/test_clone.py @@ -6,27 +6,28 @@ import re import pytest BOOTSTRAP_CODE = 123 -BOOTSTRAP_MSG = 'Bootstrap successful' +BOOTSTRAP_MSG = "Bootstrap successful" -@pytest.mark.usefixtures('remote') +@pytest.mark.usefixtures("remote") @pytest.mark.parametrize( - 'good_remote, repo_exists, force, conflicts', [ + "good_remote, repo_exists, force, conflicts", + [ (False, False, False, False), (True, False, False, False), (True, True, False, False), (True, True, True, False), (True, False, False, True), - ], ids=[ - 'bad remote', - 'simple', - 'existing repo', - '-f', - 'conflicts', - ]) -def test_clone( - runner, paths, yadm_cmd, repo_config, ds1, - good_remote, repo_exists, force, conflicts): + ], + ids=[ + "bad remote", + "simple", + "existing repo", + "-f", + "conflicts", + ], +) +def test_clone(runner, paths, yadm_cmd, repo_config, ds1, good_remote, repo_exists, force, conflicts): """Test basic clone operation""" # clear out the work path @@ -34,74 +35,70 @@ def test_clone( paths.work.mkdir() # determine remote url - remote_url = f'file://{paths.remote}' + remote_url = f"file://{paths.remote}" if not good_remote: - remote_url = 'file://bad_remote' + remote_url = "file://bad_remote" old_repo = None if repo_exists: # put a repo in the way paths.repo.mkdir() - old_repo = paths.repo.join('old_repo') - old_repo.write('old_repo') + old_repo = paths.repo.join("old_repo") + old_repo.write("old_repo") if conflicts: - ds1.tracked[0].relative.write('conflict') + ds1.tracked[0].relative.write("conflict") assert ds1.tracked[0].relative.exists() # run the clone command - args = ['clone', '-w', paths.work] + args = ["clone", "-w", paths.work] if force: - args += ['-f'] + args += ["-f"] args += [remote_url] run = runner(command=yadm_cmd(*args)) if not good_remote: # clone should fail assert run.failure - assert run.out == '' - assert 'Unable to clone the repository' in run.err + assert run.out == "" + assert "Unable to clone the repository" in run.err assert not paths.repo.exists() elif repo_exists and not force: # can't overwrite data assert run.failure - assert run.out == '' - assert 'Git repo already exists' in run.err + assert run.out == "" + assert "Git repo already exists" in run.err else: # clone should succeed, and repo should be configured properly assert successful_clone(run, paths, repo_config) # these clones should have master as HEAD - verify_head(paths, 'master') + verify_head(paths, "master") # ensure conflicts are handled properly if conflicts: - assert 'NOTE' in run.out - assert 'Local files with content that differs' in run.out + assert "NOTE" in run.out + assert "Local files with content that differs" in run.out # confirm correct Git origin - run = runner( - command=('git', 'remote', '-v', 'show'), - env={'GIT_DIR': paths.repo}) + run = runner(command=("git", "remote", "-v", "show"), env={"GIT_DIR": paths.repo}) assert run.success - assert run.err == '' - assert f'origin\t{remote_url}' in run.out + assert run.err == "" + assert f"origin\t{remote_url}" in run.out # ensure conflicts are really preserved if conflicts: # test that the conflicts are preserved in the work tree - run = runner( - command=yadm_cmd('status', '-uno', '--porcelain'), - cwd=paths.work) + run = runner(command=yadm_cmd("status", "-uno", "--porcelain"), cwd=paths.work) assert run.success - assert run.err == '' + assert run.err == "" assert str(ds1.tracked[0].path) in run.out # verify content of the conflicts - run = runner(command=yadm_cmd('diff'), cwd=paths.work) + run = runner(command=yadm_cmd("diff"), cwd=paths.work) assert run.success - assert run.err == '' - assert '\n+conflict' in run.out, 'conflict overwritten' + assert run.err == "" + assert "\n+conflict" in run.out, "conflict overwritten" # another force-related assertion if old_repo: @@ -111,54 +108,56 @@ def test_clone( assert old_repo.exists() -@pytest.mark.usefixtures('remote') +@pytest.mark.usefixtures("remote") @pytest.mark.parametrize( - 'bs_exists, bs_param, answer', [ - (False, '--bootstrap', None), - (True, '--bootstrap', None), - (True, '--no-bootstrap', None), - (True, None, 'n'), - (True, None, 'y'), - ], ids=[ - 'force, missing', - 'force, existing', - 'prevent', - 'existing, answer n', - 'existing, answer y', - ]) -def test_clone_bootstrap( - runner, paths, yadm_cmd, repo_config, bs_exists, bs_param, answer): + "bs_exists, bs_param, answer", + [ + (False, "--bootstrap", None), + (True, "--bootstrap", None), + (True, "--no-bootstrap", None), + (True, None, "n"), + (True, None, "y"), + ], + ids=[ + "force, missing", + "force, existing", + "prevent", + "existing, answer n", + "existing, answer y", + ], +) +def test_clone_bootstrap(runner, paths, yadm_cmd, repo_config, bs_exists, bs_param, answer): """Test bootstrap clone features""" # establish a bootstrap create_bootstrap(paths, bs_exists) # run the clone command - args = ['clone', '-w', paths.work] + args = ["clone", "-w", paths.work] if bs_param: args += [bs_param] - args += [f'file://{paths.remote}'] + args += [f"file://{paths.remote}"] expect = [] if answer: - expect.append(('Would you like to execute it now', answer)) + expect.append(("Would you like to execute it now", answer)) run = runner(command=yadm_cmd(*args), expect=expect) if answer: - assert 'Would you like to execute it now' in run.out + assert "Would you like to execute it now" in run.out expected_code = 0 - if bs_exists and bs_param != '--no-bootstrap': + if bs_exists and bs_param != "--no-bootstrap": expected_code = BOOTSTRAP_CODE - if answer == 'y': + if answer == "y": expected_code = BOOTSTRAP_CODE assert BOOTSTRAP_MSG in run.out - elif answer == 'n': + elif answer == "n": expected_code = 0 assert BOOTSTRAP_MSG not in run.out assert successful_clone(run, paths, repo_config, expected_code) - verify_head(paths, 'master') + verify_head(paths, "master") if not bs_exists: assert BOOTSTRAP_MSG not in run.out @@ -167,108 +166,90 @@ def test_clone_bootstrap( def create_bootstrap(paths, exists): """Create bootstrap file for test""" if exists: - paths.bootstrap.write( - '#!/bin/sh\n' - f'echo {BOOTSTRAP_MSG}\n' - f'exit {BOOTSTRAP_CODE}\n') + paths.bootstrap.write("#!/bin/sh\n" f"echo {BOOTSTRAP_MSG}\n" f"exit {BOOTSTRAP_CODE}\n") paths.bootstrap.chmod(0o775) assert paths.bootstrap.exists() else: assert not paths.bootstrap.exists() -@pytest.mark.usefixtures('remote') +@pytest.mark.usefixtures("remote") @pytest.mark.parametrize( - 'private_type, in_repo, in_work', [ - ('ssh', False, True), - ('gnupg', False, True), - ('ssh', True, True), - ('gnupg', True, True), - ('ssh', True, False), - ('gnupg', True, False), - ], ids=[ - 'open ssh, not tracked', - 'open gnupg, not tracked', - 'open ssh, tracked', - 'open gnupg, tracked', - 'missing ssh, tracked', - 'missing gnupg, tracked', - ]) -def test_clone_perms( - runner, yadm_cmd, paths, repo_config, - private_type, in_repo, in_work): + "private_type, in_repo, in_work", + [ + ("ssh", False, True), + ("gnupg", False, True), + ("ssh", True, True), + ("gnupg", True, True), + ("ssh", True, False), + ("gnupg", True, False), + ], + ids=[ + "open ssh, not tracked", + "open gnupg, not tracked", + "open ssh, tracked", + "open gnupg, tracked", + "missing ssh, tracked", + "missing gnupg, tracked", + ], +) +def test_clone_perms(runner, yadm_cmd, paths, repo_config, private_type, in_repo, in_work): """Test clone permission-related functions""" # update remote repo to include private data if in_repo: - rpath = paths.work.mkdir(f'.{private_type}').join('related') - rpath.write('related') + rpath = paths.work.mkdir(f".{private_type}").join("related") + rpath.write("related") os.system(f'GIT_DIR="{paths.remote}" git add {rpath}') os.system(f'GIT_DIR="{paths.remote}" git commit -m "{rpath}"') rpath.remove() # ensure local private data is insecure at the start if in_work: - pdir = paths.work.join(f'.{private_type}') + pdir = paths.work.join(f".{private_type}") if not pdir.exists(): pdir.mkdir() - pfile = pdir.join('existing') - pfile.write('existing') + pfile = pdir.join("existing") + pfile.write("existing") pdir.chmod(0o777) pfile.chmod(0o777) else: paths.work.remove() paths.work.mkdir() - env = {'HOME': paths.work} - run = runner( - yadm_cmd('clone', '-d', '-w', paths.work, f'file://{paths.remote}'), - env=env - ) + env = {"HOME": paths.work} + run = runner(yadm_cmd("clone", "-d", "-w", paths.work, f"file://{paths.remote}"), env=env) assert successful_clone(run, paths, repo_config) - verify_head(paths, 'master') + verify_head(paths, "master") if in_work: # private directories which already exist, should be left as they are, # which in this test is "insecure". - assert re.search( - f'initial private dir perms drwxrwxrwx.+.{private_type}', - run.out) - assert re.search( - f'pre-checkout private dir perms drwxrwxrwx.+.{private_type}', - run.out) - assert re.search( - f'post-checkout private dir perms drwxrwxrwx.+.{private_type}', - run.out) + assert re.search(f"initial private dir perms drwxrwxrwx.+.{private_type}", run.out) + assert re.search(f"pre-checkout private dir perms drwxrwxrwx.+.{private_type}", run.out) + assert re.search(f"post-checkout private dir perms drwxrwxrwx.+.{private_type}", run.out) else: # private directories which are created, should be done prior to # checkout, and with secure permissions. - assert 'initial private dir perms' not in run.out - assert re.search( - f'pre-checkout private dir perms drwx------.+.{private_type}', - run.out) - assert re.search( - f'post-checkout private dir perms drwx------.+.{private_type}', - run.out) + assert "initial private dir perms" not in run.out + assert re.search(f"pre-checkout private dir perms drwx------.+.{private_type}", run.out) + assert re.search(f"post-checkout private dir perms drwx------.+.{private_type}", run.out) # standard perms still apply afterwards unless disabled with auto.perms - assert oct( - paths.work.join(f'.{private_type}').stat().mode).endswith('00'), ( - f'.{private_type} has not been secured by auto.perms') + assert oct(paths.work.join(f".{private_type}").stat().mode).endswith( + "00" + ), f".{private_type} has not been secured by auto.perms" -@pytest.mark.usefixtures('remote') -@pytest.mark.parametrize( - 'branch', ['master', 'default', 'valid', 'invalid']) +@pytest.mark.usefixtures("remote") +@pytest.mark.parametrize("branch", ["master", "default", "valid", "invalid"]) def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch): """Test cloning a branch other than master""" # add a "valid" branch to the remote os.system(f'GIT_DIR="{paths.remote}" git checkout -b valid') - os.system( - f'GIT_DIR="{paths.remote}" git commit ' - f'--allow-empty -m "This branch is valid"') - if branch != 'default': + os.system(f'GIT_DIR="{paths.remote}" git commit ' f'--allow-empty -m "This branch is valid"') + if branch != "default": # When branch == 'default', the "default" branch of the remote repo # will remain "valid" to validate identification the correct default # branch by inspecting the repo. Otherwise it will be set back to @@ -279,45 +260,43 @@ def test_alternate_branch(runner, paths, yadm_cmd, repo_config, branch): paths.work.remove() paths.work.mkdir() - remote_url = f'file://{paths.remote}' + remote_url = f"file://{paths.remote}" # run the clone command - args = ['clone', '-w', paths.work] - if branch not in ['master', 'default']: - args += ['-b', branch] + args = ["clone", "-w", paths.work] + if branch not in ["master", "default"]: + args += ["-b", branch] args += [remote_url] run = runner(command=yadm_cmd(*args)) - if branch == 'invalid': + if branch == "invalid": assert run.failure - assert 'ERROR: Unable to clone the repository' in run.err + assert "ERROR: Unable to clone the repository" in run.err assert f"Remote branch {branch} not found in upstream" in run.err else: assert successful_clone(run, paths, repo_config) # confirm correct Git origin - run = runner( - command=('git', 'remote', '-v', 'show'), - env={'GIT_DIR': paths.repo}) + run = runner(command=("git", "remote", "-v", "show"), env={"GIT_DIR": paths.repo}) assert run.success - assert run.err == '' - assert f'origin\t{remote_url}' in run.out - run = runner(command=yadm_cmd('show')) - if branch == 'master': - assert 'Initial commit' in run.out - verify_head(paths, 'master') + assert run.err == "" + assert f"origin\t{remote_url}" in run.out + run = runner(command=yadm_cmd("show")) + if branch == "master": + assert "Initial commit" in run.out + verify_head(paths, "master") else: - assert 'This branch is valid' in run.out - verify_head(paths, 'valid') + assert "This branch is valid" in run.out + verify_head(paths, "valid") def successful_clone(run, paths, repo_config, expected_code=0): """Assert clone is successful""" assert run.code == expected_code - assert oct(paths.repo.stat().mode).endswith('00'), 'Repo is not secured' - assert repo_config('core.bare') == 'false' - assert repo_config('status.showUntrackedFiles') == 'no' - assert repo_config('yadm.managed') == 'true' + assert oct(paths.repo.stat().mode).endswith("00"), "Repo is not secured" + assert repo_config("core.bare") == "false" + assert repo_config("status.showUntrackedFiles") == "no" + assert repo_config("yadm.managed") == "true" return True @@ -332,15 +311,18 @@ def remote(paths, ds1_repo_copy): paths.repo.move(paths.remote) -def test_no_repo(runner, yadm_cmd, ): +def test_no_repo( + runner, + yadm_cmd, +): """Test cloning without specifying a repo""" - run = runner(command=yadm_cmd('clone', '-f')) + run = runner(command=yadm_cmd("clone", "-f")) assert run.failure - assert run.out == '' - assert 'ERROR: Unable to clone the repository' in run.err - assert 'repository \'repo.git\' does not exist' in run.err + assert run.out == "" + assert "ERROR: Unable to clone the repository" in run.err + assert "repository 'repo.git' does not exist" in run.err def verify_head(paths, branch): """Assert the local repo has the correct head branch""" - assert paths.repo.join('HEAD').read() == f'ref: refs/heads/{branch}\n' + assert paths.repo.join("HEAD").read() == f"ref: refs/heads/{branch}\n" diff --git a/test/test_config.py b/test/test_config.py index d6a5e33..c43d81c 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -4,11 +4,11 @@ import os import pytest -TEST_SECTION = 'test' -TEST_ATTRIBUTE = 'attribute' -TEST_KEY = f'{TEST_SECTION}.{TEST_ATTRIBUTE}' -TEST_VALUE = 'testvalue' -TEST_FILE = f'[{TEST_SECTION}]\n\t{TEST_ATTRIBUTE} = {TEST_VALUE}' +TEST_SECTION = "test" +TEST_ATTRIBUTE = "attribute" +TEST_KEY = f"{TEST_SECTION}.{TEST_ATTRIBUTE}" +TEST_VALUE = "testvalue" +TEST_FILE = f"[{TEST_SECTION}]\n\t{TEST_ATTRIBUTE} = {TEST_VALUE}" def test_config_no_params(runner, yadm_cmd, supported_configs): @@ -19,11 +19,11 @@ def test_config_no_params(runner, yadm_cmd, supported_configs): Exit with 0 """ - run = runner(yadm_cmd('config')) + run = runner(yadm_cmd("config")) assert run.success - assert run.err == '' - assert 'Please read the CONFIGURATION section' in run.out + assert run.err == "" + assert "Please read the CONFIGURATION section" in run.out for config in supported_configs: assert config in run.out @@ -35,11 +35,11 @@ def test_config_read_missing(runner, yadm_cmd): Exit with 0 """ - run = runner(yadm_cmd('config', TEST_KEY)) + run = runner(yadm_cmd("config", TEST_KEY)) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" def test_config_write(runner, yadm_cmd, paths): @@ -50,11 +50,11 @@ def test_config_write(runner, yadm_cmd, paths): Exit with 0 """ - run = runner(yadm_cmd('config', TEST_KEY, TEST_VALUE)) + run = runner(yadm_cmd("config", TEST_KEY, TEST_VALUE)) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" assert paths.config.read().strip() == TEST_FILE @@ -66,10 +66,10 @@ def test_config_read(runner, yadm_cmd, paths): """ paths.config.write(TEST_FILE) - run = runner(yadm_cmd('config', TEST_KEY)) + run = runner(yadm_cmd("config", TEST_KEY)) assert run.success - assert run.err == '' + assert run.err == "" assert run.out.strip() == TEST_VALUE @@ -83,16 +83,16 @@ def test_config_update(runner, yadm_cmd, paths): paths.config.write(TEST_FILE) - run = runner(yadm_cmd('config', TEST_KEY, TEST_VALUE + 'extra')) + run = runner(yadm_cmd("config", TEST_KEY, TEST_VALUE + "extra")) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" - assert paths.config.read().strip() == TEST_FILE + 'extra' + assert paths.config.read().strip() == TEST_FILE + "extra" -@pytest.mark.usefixtures('ds1_repo_copy') +@pytest.mark.usefixtures("ds1_repo_copy") def test_config_local_read(runner, yadm_cmd, paths, supported_local_configs): """Read local attribute @@ -102,19 +102,17 @@ def test_config_local_read(runner, yadm_cmd, paths, supported_local_configs): # populate test values for config in supported_local_configs: - os.system( - f'GIT_DIR="{paths.repo}" ' - f'git config --local "{config}" "value_of_{config}"') + os.system(f'GIT_DIR="{paths.repo}" ' f'git config --local "{config}" "value_of_{config}"') # run yadm config for config in supported_local_configs: - run = runner(yadm_cmd('config', config)) + run = runner(yadm_cmd("config", config)) assert run.success - assert run.err == '' - assert run.out.strip() == f'value_of_{config}' + assert run.err == "" + assert run.out.strip() == f"value_of_{config}" -@pytest.mark.usefixtures('ds1_repo_copy') +@pytest.mark.usefixtures("ds1_repo_copy") def test_config_local_write(runner, yadm_cmd, paths, supported_local_configs): """Write local attribute @@ -125,19 +123,17 @@ def test_config_local_write(runner, yadm_cmd, paths, supported_local_configs): # run yadm config for config in supported_local_configs: - run = runner(yadm_cmd('config', config, f'value_of_{config}')) + run = runner(yadm_cmd("config", config, f"value_of_{config}")) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" # verify test values for config in supported_local_configs: - run = runner( - command=('git', 'config', config), - env={'GIT_DIR': paths.repo}) + run = runner(command=("git", "config", config), env={"GIT_DIR": paths.repo}) assert run.success - assert run.err == '' - assert run.out.strip() == f'value_of_{config}' + assert run.err == "" + assert run.out.strip() == f"value_of_{config}" def test_config_without_parent_directory(runner, yadm_cmd, paths): @@ -148,17 +144,16 @@ def test_config_without_parent_directory(runner, yadm_cmd, paths): Exit with 0 """ - config_file = paths.root + '/folder/does/not/exist/config' + config_file = paths.root + "/folder/does/not/exist/config" - run = runner( - yadm_cmd('--yadm-config', config_file, 'config', TEST_KEY, TEST_VALUE)) + run = runner(yadm_cmd("--yadm-config", config_file, "config", TEST_KEY, TEST_VALUE)) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" - run = runner(yadm_cmd('--yadm-config', config_file, 'config', TEST_KEY)) + run = runner(yadm_cmd("--yadm-config", config_file, "config", TEST_KEY)) assert run.success - assert run.err == '' + assert run.err == "" assert run.out.strip() == TEST_VALUE diff --git a/test/test_encryption.py b/test/test_encryption.py index 78bbf3a..8c64222 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -6,26 +6,26 @@ import time import pytest -KEY_FILE = 'test/test_key' -KEY_FINGERPRINT = 'F8BBFC746C58945442349BCEBA54FFD04C599B1A' -KEY_NAME = 'yadm-test1' -KEY_TRUST = 'test/ownertrust.txt' -PASSPHRASE = 'ExamplePassword' +KEY_FILE = "test/test_key" +KEY_FINGERPRINT = "F8BBFC746C58945442349BCEBA54FFD04C599B1A" +KEY_NAME = "yadm-test1" +KEY_TRUST = "test/ownertrust.txt" +PASSPHRASE = "ExamplePassword" -pytestmark = pytest.mark.usefixtures('config_git') +pytestmark = pytest.mark.usefixtures("config_git") def add_asymmetric_key(runner, gnupg): """Add asymmetric key""" env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home runner( - ['gpg', '--import', shlex.quote(KEY_FILE)], + ["gpg", "--import", shlex.quote(KEY_FILE)], env=env, shell=True, ) runner( - ['gpg', '--import-ownertrust', '<', shlex.quote(KEY_TRUST)], + ["gpg", "--import-ownertrust", "<", shlex.quote(KEY_TRUST)], env=env, shell=True, ) @@ -34,20 +34,14 @@ def add_asymmetric_key(runner, gnupg): def remove_asymmetric_key(runner, gnupg): """Remove asymmetric key""" env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home runner( - [ - 'gpg', '--batch', '--yes', - '--delete-secret-keys', shlex.quote(KEY_FINGERPRINT) - ], + ["gpg", "--batch", "--yes", "--delete-secret-keys", shlex.quote(KEY_FINGERPRINT)], env=env, shell=True, ) runner( - [ - 'gpg', '--batch', '--yes', - '--delete-key', shlex.quote(KEY_FINGERPRINT) - ], + ["gpg", "--batch", "--yes", "--delete-key", shlex.quote(KEY_FINGERPRINT)], env=env, shell=True, ) @@ -79,48 +73,48 @@ def encrypt_targets(yadm_cmd, paths): """ # init empty yadm repo - os.system(' '.join(yadm_cmd('init', '-w', str(paths.work), '-f'))) + os.system(" ".join(yadm_cmd("init", "-w", str(paths.work), "-f"))) expected = [] # standard files w/ dirs & spaces - paths.work.join('inc file1').write('inc file1') - expected.append('inc file1') - paths.encrypt.write('inc file1\n') - paths.work.join('inc dir').mkdir() - paths.work.join('inc dir/inc file2').write('inc file2') - expected.append('inc dir/inc file2') - paths.encrypt.write('inc dir/inc file2\n', mode='a') + paths.work.join("inc file1").write("inc file1") + expected.append("inc file1") + paths.encrypt.write("inc file1\n") + paths.work.join("inc dir").mkdir() + paths.work.join("inc dir/inc file2").write("inc file2") + expected.append("inc dir/inc file2") + paths.encrypt.write("inc dir/inc file2\n", mode="a") # standard globs w/ dirs & spaces - paths.work.join('globs file1').write('globs file1') - expected.append('globs file1') - paths.work.join('globs dir').mkdir() - paths.work.join('globs dir/globs file2').write('globs file2') - expected.append('globs dir/globs file2') - paths.encrypt.write('globs*\n', mode='a') + paths.work.join("globs file1").write("globs file1") + expected.append("globs file1") + paths.work.join("globs dir").mkdir() + paths.work.join("globs dir/globs file2").write("globs file2") + expected.append("globs dir/globs file2") + paths.encrypt.write("globs*\n", mode="a") # blank lines - paths.encrypt.write('\n \n\t\n', mode='a') + paths.encrypt.write("\n \n\t\n", mode="a") # comments - paths.work.join('commentfile1').write('commentfile1') - paths.encrypt.write('#commentfile1\n', mode='a') - paths.encrypt.write(' #commentfile1\n', mode='a') + paths.work.join("commentfile1").write("commentfile1") + paths.encrypt.write("#commentfile1\n", mode="a") + paths.encrypt.write(" #commentfile1\n", mode="a") # exclusions - paths.work.join('extest').mkdir() - paths.encrypt.write('extest/*\n', mode='a') # include within extest - paths.work.join('extest/inglob1').write('inglob1') - paths.work.join('extest/exglob1').write('exglob1') - paths.work.join('extest/exglob2').write('exglob2') - paths.encrypt.write('!extest/ex*\n', mode='a') # exclude the ex* - expected.append('extest/inglob1') # should be left with only in* + paths.work.join("extest").mkdir() + paths.encrypt.write("extest/*\n", mode="a") # include within extest + paths.work.join("extest/inglob1").write("inglob1") + paths.work.join("extest/exglob1").write("exglob1") + paths.work.join("extest/exglob2").write("exglob2") + paths.encrypt.write("!extest/ex*\n", mode="a") # exclude the ex* + expected.append("extest/inglob1") # should be left with only in* return expected -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def decrypt_targets(tmpdir_factory, runner, gnupg): """Fixture for setting data to decrypt @@ -129,234 +123,203 @@ def decrypt_targets(tmpdir_factory, runner, gnupg): * creates a list of expected decrypted files """ - tmpdir = tmpdir_factory.mktemp('decrypt_targets') - symmetric = tmpdir.join('symmetric.tar.gz.gpg') - asymmetric = tmpdir.join('asymmetric.tar.gz.gpg') + tmpdir = tmpdir_factory.mktemp("decrypt_targets") + symmetric = tmpdir.join("symmetric.tar.gz.gpg") + asymmetric = tmpdir.join("asymmetric.tar.gz.gpg") expected = [] - tmpdir.join('decrypt1').write('decrypt1') - expected.append('decrypt1') - tmpdir.join('decrypt2').write('decrypt2') - expected.append('decrypt2') - tmpdir.join('subdir').mkdir() - tmpdir.join('subdir/decrypt3').write('subdir/decrypt3') - expected.append('subdir/decrypt3') + tmpdir.join("decrypt1").write("decrypt1") + expected.append("decrypt1") + tmpdir.join("decrypt2").write("decrypt2") + expected.append("decrypt2") + tmpdir.join("subdir").mkdir() + tmpdir.join("subdir/decrypt3").write("subdir/decrypt3") + expected.append("subdir/decrypt3") gnupg.pw(PASSPHRASE) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home run = runner( - ['tar', 'cvf', '-'] + - expected + - ['|', 'gpg', '--batch', '--yes', '-c'] + - ['--output', shlex.quote(str(symmetric))], + ["tar", "cvf", "-"] + + expected + + ["|", "gpg", "--batch", "--yes", "-c"] + + ["--output", shlex.quote(str(symmetric))], cwd=tmpdir, env=env, - shell=True) + shell=True, + ) assert run.success - gnupg.pw('') + gnupg.pw("") add_asymmetric_key(runner, gnupg) run = runner( - ['tar', 'cvf', '-'] + - expected + - ['|', 'gpg', '--batch', '--yes', '-e'] + - ['-r', shlex.quote(KEY_NAME)] + - ['--output', shlex.quote(str(asymmetric))], + ["tar", "cvf", "-"] + + expected + + ["|", "gpg", "--batch", "--yes", "-e"] + + ["-r", shlex.quote(KEY_NAME)] + + ["--output", shlex.quote(str(asymmetric))], cwd=tmpdir, env=env, - shell=True) + shell=True, + ) assert run.success remove_asymmetric_key(runner, gnupg) return { - 'asymmetric': asymmetric, - 'expected': expected, - 'symmetric': symmetric, + "asymmetric": asymmetric, + "expected": expected, + "symmetric": symmetric, } -@pytest.mark.parametrize( - 'bad_phrase', [False, True], - ids=['good_phrase', 'bad_phrase']) -@pytest.mark.parametrize( - 'missing_encrypt', [False, True], - ids=['encrypt_exists', 'encrypt_missing']) -@pytest.mark.parametrize( - 'overwrite', [False, True], - ids=['clean', 'overwrite']) -def test_symmetric_encrypt( - runner, yadm_cmd, paths, encrypt_targets, - gnupg, bad_phrase, overwrite, missing_encrypt): +@pytest.mark.parametrize("bad_phrase", [False, True], ids=["good_phrase", "bad_phrase"]) +@pytest.mark.parametrize("missing_encrypt", [False, True], ids=["encrypt_exists", "encrypt_missing"]) +@pytest.mark.parametrize("overwrite", [False, True], ids=["clean", "overwrite"]) +def test_symmetric_encrypt(runner, yadm_cmd, paths, encrypt_targets, gnupg, bad_phrase, overwrite, missing_encrypt): """Test symmetric encryption""" if missing_encrypt: paths.encrypt.remove() if bad_phrase: - gnupg.pw('') + gnupg.pw("") else: gnupg.pw(PASSPHRASE) if overwrite: - paths.archive.write('existing archive') + paths.archive.write("existing archive") env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home - run = runner(yadm_cmd('encrypt'), env=env) + env["GNUPGHOME"] = gnupg.home + run = runner(yadm_cmd("encrypt"), env=env) if missing_encrypt or bad_phrase: assert run.failure else: assert run.success - assert run.err == '' + assert run.err == "" if missing_encrypt: - assert 'does not exist' in run.err + assert "does not exist" in run.err elif bad_phrase: - assert 'Invalid IPC' in run.err + assert "Invalid IPC" in run.err else: - assert encrypted_data_valid( - runner, gnupg, paths.archive, encrypt_targets) + assert encrypted_data_valid(runner, gnupg, paths.archive, encrypt_targets) -@pytest.mark.parametrize( - 'bad_phrase', [False, True], - ids=['good_phrase', 'bad_phrase']) -@pytest.mark.parametrize( - 'archive_exists', [True, False], - ids=['archive_exists', 'archive_missing']) -@pytest.mark.parametrize( - 'dolist', [False, True], - ids=['decrypt', 'list']) -def test_symmetric_decrypt( - runner, yadm_cmd, paths, decrypt_targets, gnupg, - dolist, archive_exists, bad_phrase): +@pytest.mark.parametrize("bad_phrase", [False, True], ids=["good_phrase", "bad_phrase"]) +@pytest.mark.parametrize("archive_exists", [True, False], ids=["archive_exists", "archive_missing"]) +@pytest.mark.parametrize("dolist", [False, True], ids=["decrypt", "list"]) +def test_symmetric_decrypt(runner, yadm_cmd, paths, decrypt_targets, gnupg, dolist, archive_exists, bad_phrase): """Test decryption""" # init empty yadm repo - os.system(' '.join(yadm_cmd('init', '-w', str(paths.work), '-f'))) + os.system(" ".join(yadm_cmd("init", "-w", str(paths.work), "-f"))) if bad_phrase: - gnupg.pw('') + gnupg.pw("") time.sleep(1) # allow gpg-agent cache to expire else: gnupg.pw(PASSPHRASE) if archive_exists: - decrypt_targets['symmetric'].copy(paths.archive) + decrypt_targets["symmetric"].copy(paths.archive) # to test overwriting - paths.work.join('decrypt1').write('pre-existing file') + paths.work.join("decrypt1").write("pre-existing file") env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home args = [] if dolist: - args.append('-l') - run = runner(yadm_cmd('decrypt') + args, env=env) + args.append("-l") + run = runner(yadm_cmd("decrypt") + args, env=env) if archive_exists and not bad_phrase: assert run.success - assert 'encrypted with 1 passphrase' in run.err + assert "encrypted with 1 passphrase" in run.err if dolist: - for filename in decrypt_targets['expected']: - if filename != 'decrypt1': # this one should exist + for filename in decrypt_targets["expected"]: + if filename != "decrypt1": # this one should exist assert not paths.work.join(filename).exists() assert filename in run.out else: - for filename in decrypt_targets['expected']: + for filename in decrypt_targets["expected"]: assert paths.work.join(filename).read() == filename else: assert run.failure -@pytest.mark.usefixtures('asymmetric_key') -@pytest.mark.parametrize( - 'ask', [False, True], - ids=['no_ask', 'ask']) -@pytest.mark.parametrize( - 'key_exists', [True, False], - ids=['key_exists', 'key_missing']) -@pytest.mark.parametrize( - 'overwrite', [False, True], - ids=['clean', 'overwrite']) -def test_asymmetric_encrypt( - runner, yadm_cmd, paths, encrypt_targets, gnupg, - overwrite, key_exists, ask): +@pytest.mark.usefixtures("asymmetric_key") +@pytest.mark.parametrize("ask", [False, True], ids=["no_ask", "ask"]) +@pytest.mark.parametrize("key_exists", [True, False], ids=["key_exists", "key_missing"]) +@pytest.mark.parametrize("overwrite", [False, True], ids=["clean", "overwrite"]) +def test_asymmetric_encrypt(runner, yadm_cmd, paths, encrypt_targets, gnupg, overwrite, key_exists, ask): """Test asymmetric encryption""" # specify encryption recipient if ask: - os.system(' '.join(yadm_cmd('config', 'yadm.gpg-recipient', 'ASK'))) - expect = [('Enter the user ID', KEY_NAME), ('Enter the user ID', '')] + os.system(" ".join(yadm_cmd("config", "yadm.gpg-recipient", "ASK"))) + expect = [("Enter the user ID", KEY_NAME), ("Enter the user ID", "")] else: - os.system(' '.join(yadm_cmd('config', 'yadm.gpg-recipient', KEY_NAME))) + os.system(" ".join(yadm_cmd("config", "yadm.gpg-recipient", KEY_NAME))) expect = [] if overwrite: - paths.archive.write('existing archive') + paths.archive.write("existing archive") if not key_exists: remove_asymmetric_key(runner, gnupg) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home - run = runner(yadm_cmd('encrypt'), env=env, expect=expect) + run = runner(yadm_cmd("encrypt"), env=env, expect=expect) if key_exists: assert run.success - assert encrypted_data_valid( - runner, gnupg, paths.archive, encrypt_targets) + assert encrypted_data_valid(runner, gnupg, paths.archive, encrypt_targets) else: assert run.failure - assert 'Unable to write' in run.out if expect else run.err + assert "Unable to write" in run.out if expect else run.err if ask: - assert 'Enter the user ID' in run.out + assert "Enter the user ID" in run.out -@pytest.mark.usefixtures('asymmetric_key') -@pytest.mark.usefixtures('encrypt_targets') +@pytest.mark.usefixtures("asymmetric_key") +@pytest.mark.usefixtures("encrypt_targets") def test_multi_key(runner, yadm_cmd, gnupg): """Test multiple recipients""" # specify two encryption recipient - os.system(' '.join(yadm_cmd( - 'config', 'yadm.gpg-recipient', f'"second-key {KEY_NAME}"'))) + os.system(" ".join(yadm_cmd("config", "yadm.gpg-recipient", f'"second-key {KEY_NAME}"'))) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home - run = runner(yadm_cmd('encrypt'), env=env) + run = runner(yadm_cmd("encrypt"), env=env) assert run.failure - assert 'second-key: skipped: No public key' in run.err + assert "second-key: skipped: No public key" in run.err -@pytest.mark.usefixtures('asymmetric_key') -@pytest.mark.parametrize( - 'key_exists', [True, False], - ids=['key_exists', 'key_missing']) -@pytest.mark.parametrize( - 'dolist', [False, True], - ids=['decrypt', 'list']) -def test_asymmetric_decrypt( - runner, yadm_cmd, paths, decrypt_targets, gnupg, - dolist, key_exists): +@pytest.mark.usefixtures("asymmetric_key") +@pytest.mark.parametrize("key_exists", [True, False], ids=["key_exists", "key_missing"]) +@pytest.mark.parametrize("dolist", [False, True], ids=["decrypt", "list"]) +def test_asymmetric_decrypt(runner, yadm_cmd, paths, decrypt_targets, gnupg, dolist, key_exists): """Test decryption""" # init empty yadm repo - os.system(' '.join(yadm_cmd('init', '-w', str(paths.work), '-f'))) + os.system(" ".join(yadm_cmd("init", "-w", str(paths.work), "-f"))) - decrypt_targets['asymmetric'].copy(paths.archive) + decrypt_targets["asymmetric"].copy(paths.archive) # to test overwriting - paths.work.join('decrypt1').write('pre-existing file') + paths.work.join("decrypt1").write("pre-existing file") if not key_exists: remove_asymmetric_key(runner, gnupg) @@ -364,32 +327,28 @@ def test_asymmetric_decrypt( args = [] if dolist: - args.append('-l') + args.append("-l") env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home - run = runner(yadm_cmd('decrypt') + args, env=env) + env["GNUPGHOME"] = gnupg.home + run = runner(yadm_cmd("decrypt") + args, env=env) if key_exists: assert run.success if dolist: - for filename in decrypt_targets['expected']: - if filename != 'decrypt1': # this one should exist + for filename in decrypt_targets["expected"]: + if filename != "decrypt1": # this one should exist assert not paths.work.join(filename).exists() assert filename in run.out else: - for filename in decrypt_targets['expected']: + for filename in decrypt_targets["expected"]: assert paths.work.join(filename).read() == filename else: assert run.failure - assert 'Unable to extract encrypted files' in run.err + assert "Unable to extract encrypted files" in run.err -@pytest.mark.parametrize( - 'untracked', - [False, 'y', 'n'], - ids=['tracked', 'untracked_answer_y', 'untracked_answer_n']) -def test_offer_to_add( - runner, yadm_cmd, paths, encrypt_targets, gnupg, untracked): +@pytest.mark.parametrize("untracked", [False, "y", "n"], ids=["tracked", "untracked_answer_y", "untracked_answer_n"]) +def test_offer_to_add(runner, yadm_cmd, paths, encrypt_targets, gnupg, untracked): """Test offer to add encrypted archive All the other encryption tests use an archive outside of the work tree. @@ -397,85 +356,75 @@ def test_offer_to_add( should be an offer to add it to the repo if it is not tracked. """ - worktree_archive = paths.work.join('worktree-archive.tar.gpg') + worktree_archive = paths.work.join("worktree-archive.tar.gpg") expect = [] gnupg.pw(PASSPHRASE) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home if untracked: - expect.append(('add it now', untracked)) + expect.append(("add it now", untracked)) else: - worktree_archive.write('exists') - os.system(' '.join(yadm_cmd('add', str(worktree_archive)))) + worktree_archive.write("exists") + os.system(" ".join(yadm_cmd("add", str(worktree_archive)))) - run = runner( - yadm_cmd('encrypt', '--yadm-archive', str(worktree_archive)), - env=env, - expect=expect - ) + run = runner(yadm_cmd("encrypt", "--yadm-archive", str(worktree_archive)), env=env, expect=expect) assert run.success - assert run.err == '' - assert encrypted_data_valid( - runner, gnupg, worktree_archive, encrypt_targets) + assert run.err == "" + assert encrypted_data_valid(runner, gnupg, worktree_archive, encrypt_targets) - run = runner( - yadm_cmd('status', '--porcelain', '-uall', str(worktree_archive))) + run = runner(yadm_cmd("status", "--porcelain", "-uall", str(worktree_archive))) assert run.success - assert run.err == '' + assert run.err == "" - if untracked == 'y': + if untracked == "y": # should be added to the index - assert f'A {worktree_archive.basename}' in run.out - elif untracked == 'n': + assert f"A {worktree_archive.basename}" in run.out + elif untracked == "n": # should NOT be added to the index - assert f'?? {worktree_archive.basename}' in run.out + assert f"?? {worktree_archive.basename}" in run.out else: # should appear modified in the index - assert f'AM {worktree_archive.basename}' in run.out + assert f"AM {worktree_archive.basename}" in run.out -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.usefixtures("ds1_copy") def test_encrypt_added_to_exclude(runner, yadm_cmd, paths, gnupg): """Confirm that .config/yadm/encrypt is added to exclude""" gnupg.pw(PASSPHRASE) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home + env["GNUPGHOME"] = gnupg.home - exclude_file = paths.repo.join('info/exclude') - paths.encrypt.write('test-encrypt-data\n') - paths.work.join('test-encrypt-data').write('') - exclude_file.write('original-data', ensure=True) + exclude_file = paths.repo.join("info/exclude") + paths.encrypt.write("test-encrypt-data\n") + paths.work.join("test-encrypt-data").write("") + exclude_file.write("original-data", ensure=True) - run = runner(yadm_cmd('encrypt'), env=env) + run = runner(yadm_cmd("encrypt"), env=env) - assert 'test-encrypt-data' in paths.repo.join('info/exclude').read() - assert 'original-data' in paths.repo.join('info/exclude').read() + assert "test-encrypt-data" in paths.repo.join("info/exclude").read() + assert "original-data" in paths.repo.join("info/exclude").read() assert run.success - assert run.err == '' + assert run.err == "" def encrypted_data_valid(runner, gnupg, encrypted, expected): """Verify encrypted data matches expectations""" gnupg.pw(PASSPHRASE) env = os.environ.copy() - env['GNUPGHOME'] = gnupg.home - run = runner([ - 'gpg', - '-d', shlex.quote(str(encrypted)), - '2>/dev/null', - '|', 'tar', 't'], env=env, shell=True, report=False) + env["GNUPGHOME"] = gnupg.home + run = runner( + ["gpg", "-d", shlex.quote(str(encrypted)), "2>/dev/null", "|", "tar", "t"], env=env, shell=True, report=False + ) file_count = 0 for filename in run.out.splitlines(): - if filename.endswith('/'): + if filename.endswith("/"): continue file_count += 1 - assert filename in expected, ( - f'Unexpected file in archive: {filename}') - assert file_count == len(expected), ( - 'Number of files in archive does not match expected') + assert filename in expected, f"Unexpected file in archive: {filename}" + assert file_count == len(expected), "Number of files in archive does not match expected" return True diff --git a/test/test_enter.py b/test/test_enter.py index 5148e23..b0626ae 100644 --- a/test/test_enter.py +++ b/test/test_enter.py @@ -6,106 +6,112 @@ import pytest @pytest.mark.parametrize( - 'shell, success', [ - ('delete', True), # if there is no shell variable, bash creates it - ('', False), - ('/usr/bin/env', True), - ('noexec', False), - ], ids=[ - 'shell-missing', - 'shell-empty', - 'shell-env', - 'shell-noexec', - ]) -@pytest.mark.usefixtures('ds1_copy') + "shell, success", + [ + ("delete", True), # if there is no shell variable, bash creates it + ("", False), + ("/usr/bin/env", True), + ("noexec", False), + ], + ids=[ + "shell-missing", + "shell-empty", + "shell-env", + "shell-noexec", + ], +) +@pytest.mark.usefixtures("ds1_copy") def test_enter(runner, yadm_cmd, paths, shell, success): """Enter tests""" env = os.environ.copy() - if shell == 'delete': + if shell == "delete": # remove shell - if 'SHELL' in env: - del env['SHELL'] - elif shell == 'noexec': + if "SHELL" in env: + del env["SHELL"] + elif shell == "noexec": # specify a non-executable path - noexec = paths.root.join('noexec') - noexec.write('') + noexec = paths.root.join("noexec") + noexec.write("") noexec.chmod(0o664) - env['SHELL'] = str(noexec) + env["SHELL"] = str(noexec) else: - env['SHELL'] = shell + env["SHELL"] = shell - run = runner(command=yadm_cmd('enter'), env=env) + run = runner(command=yadm_cmd("enter"), env=env) assert run.success == success - prompt = f'yadm shell ({paths.repo})' + prompt = f"yadm shell ({paths.repo})" if success: - assert run.out.startswith('Entering yadm repo') - assert run.out.rstrip().endswith('Leaving yadm repo') - assert run.err == '' + assert run.out.startswith("Entering yadm repo") + assert run.out.rstrip().endswith("Leaving yadm repo") + assert run.err == "" else: - assert 'does not refer to an executable' in run.err - if 'env' in shell: - assert f'GIT_DIR={paths.repo}' in run.out - assert f'GIT_WORK_TREE={paths.work}' in run.out - assert f'PROMPT={prompt}' in run.out - assert f'PS1={prompt}' in run.out + assert "does not refer to an executable" in run.err + if "env" in shell: + assert f"GIT_DIR={paths.repo}" in run.out + assert f"GIT_WORK_TREE={paths.work}" in run.out + assert f"PROMPT={prompt}" in run.out + assert f"PS1={prompt}" in run.out @pytest.mark.parametrize( - 'shell, opts, path', [ - ('bash', '--norc', '\\w'), - ('csh', '-f', '%~'), - ('zsh', '-f', '%~'), - ], ids=[ - 'bash', - 'csh', - 'zsh', - ]) + "shell, opts, path", + [ + ("bash", "--norc", "\\w"), + ("csh", "-f", "%~"), + ("zsh", "-f", "%~"), + ], + ids=[ + "bash", + "csh", + "zsh", + ], +) @pytest.mark.parametrize( - 'cmd', - [False, 'cmd', 'cmd-bad-exit'], - ids=['no-cmd', 'cmd', 'cmd-bad-exit']) + "cmd", + [False, "cmd", "cmd-bad-exit"], + ids=["no-cmd", "cmd", "cmd-bad-exit"], +) @pytest.mark.parametrize( - 'term', ['', 'dumb'], - ids=['term-empty', 'term-dumb']) -@pytest.mark.usefixtures('ds1_copy') -def test_enter_shell_ops(runner, yadm_cmd, paths, shell, - opts, path, cmd, term): + "term", + ["", "dumb"], + ids=["term-empty", "term-dumb"], +) +@pytest.mark.usefixtures("ds1_copy") +def test_enter_shell_ops(runner, yadm_cmd, paths, shell, opts, path, cmd, term): """Enter tests for specific shell options""" - change_exit = '\nfalse' if cmd == 'cmd-bad-exit' else '' + change_exit = "\nfalse" if cmd == "cmd-bad-exit" else "" # Create custom shell to detect options passed custom_shell = paths.root.join(shell) - custom_shell.write( - f'#!/bin/sh\necho OPTS=$*\necho PROMPT=$PROMPT{change_exit}' - ) + custom_shell.write(f"#!/bin/sh\necho OPTS=$*\necho PROMPT=$PROMPT{change_exit}") custom_shell.chmod(0o775) - test_cmd = ['test1', 'test2', 'test3'] + test_cmd = ["test1", "test2", "test3"] - enter_cmd = ['enter'] + enter_cmd = ["enter"] if cmd: enter_cmd += test_cmd env = os.environ.copy() - env['TERM'] = term - env['SHELL'] = custom_shell + env["TERM"] = term + env["SHELL"] = custom_shell - if shell == 'zsh' and term == 'dumb': - opts += ' --no-zle' + if shell == "zsh" and term == "dumb": + opts += " --no-zle" run = runner(command=yadm_cmd(*enter_cmd), env=env) - if cmd == 'cmd-bad-exit': + if cmd == "cmd-bad-exit": assert run.failure else: assert run.success - assert run.err == '' - assert f'OPTS={opts}' in run.out - assert f'PROMPT=yadm shell ({paths.repo}) {path} >' in run.out + assert run.err == "" + assert f"OPTS={opts}" in run.out + assert f"PROMPT=yadm shell ({paths.repo}) {path} >" in run.out if cmd: - assert '-c ' + ' '.join(test_cmd) in run.out - assert 'Entering yadm repo' not in run.out - assert 'Leaving yadm repo' not in run.out + assert "-c " + " ".join(test_cmd) in run.out + assert "Entering yadm repo" not in run.out + assert "Leaving yadm repo" not in run.out else: - assert 'Entering yadm repo' in run.out - assert 'Leaving yadm repo' in run.out + assert "Entering yadm repo" in run.out + assert "Leaving yadm repo" in run.out diff --git a/test/test_ext_crypt.py b/test/test_ext_crypt.py index cb74afc..92f703f 100644 --- a/test/test_ext_crypt.py +++ b/test/test_ext_crypt.py @@ -4,28 +4,30 @@ import pytest @pytest.mark.parametrize( - 'crypt', - [False, 'installed', 'installed-but-failed'], - ids=['not-installed', 'installed', 'installed-but-failed'] + "crypt", + [False, "installed", "installed-but-failed"], + ids=["not-installed", "installed", "installed-but-failed"], ) @pytest.mark.parametrize( - 'cmd,var', [ - ['git_crypt', 'GIT_CRYPT_PROGRAM'], - ['transcrypt', 'TRANSCRYPT_PROGRAM'], - ], - ids=['git-crypt', 'transcrypt']) + "cmd,var", + [ + ["git_crypt", "GIT_CRYPT_PROGRAM"], + ["transcrypt", "TRANSCRYPT_PROGRAM"], + ], + ids=["git-crypt", "transcrypt"], +) def test_ext_encryption(runner, yadm, paths, tmpdir, crypt, cmd, var): """External encryption tests""" paths.repo.ensure(dir=True) - bindir = tmpdir.mkdir('bin') - pgm = bindir.join('test-ext-crypt') + bindir = tmpdir.mkdir("bin") + pgm = bindir.join("test-ext-crypt") if crypt: - pgm.write('#!/bin/sh\necho ext-crypt ran\n') + pgm.write("#!/bin/sh\necho ext-crypt ran\n") pgm.chmod(0o775) - if crypt == 'installed-but-failed': - pgm.write('false\n', mode='a') + if crypt == "installed-but-failed": + pgm.write("false\n", mode="a") script = f""" YADM_TEST=1 source {yadm} @@ -34,15 +36,15 @@ def test_ext_encryption(runner, yadm, paths, tmpdir, crypt, cmd, var): {cmd} "param1" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) if crypt: - if crypt == 'installed-but-failed': + if crypt == "installed-but-failed": assert run.failure else: assert run.success - assert run.out.strip() == 'ext-crypt ran' - assert run.err == '' + assert run.out.strip() == "ext-crypt ran" + assert run.err == "" else: assert run.failure assert f"command '{pgm}' cannot be located" in run.err diff --git a/test/test_git.py b/test/test_git.py index 41b102c..dea538d 100644 --- a/test/test_git.py +++ b/test/test_git.py @@ -5,7 +5,7 @@ import re import pytest -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.usefixtures("ds1_copy") def test_git(runner, yadm_cmd, paths): """Test series of passthrough git commands @@ -18,42 +18,42 @@ def test_git(runner, yadm_cmd, paths): """ # passthru unknown commands to Git - run = runner(command=yadm_cmd('bogus')) + run = runner(command=yadm_cmd("bogus")) assert run.failure assert "git: 'bogus' is not a git command." in run.err assert "See 'git --help'" in run.err - assert run.out == '' + assert run.out == "" # git command 'add' - badfile - run = runner(command=yadm_cmd('add', '-v', 'does_not_exist')) + run = runner(command=yadm_cmd("add", "-v", "does_not_exist")) assert run.code == 128 assert "pathspec 'does_not_exist' did not match any files" in run.err - assert run.out == '' + assert run.out == "" # git command 'add' - newfile = paths.work.join('test_git') - newfile.write('test_git') - run = runner(command=yadm_cmd('add', '-v', str(newfile))) + newfile = paths.work.join("test_git") + newfile.write("test_git") + run = runner(command=yadm_cmd("add", "-v", str(newfile))) assert run.success - assert run.err == '' + assert run.err == "" assert "add 'test_git'" in run.out # git command 'status' - run = runner(command=yadm_cmd('status')) + run = runner(command=yadm_cmd("status")) assert run.success - assert run.err == '' - assert re.search(r'new file:\s+test_git', run.out) + assert run.err == "" + assert re.search(r"new file:\s+test_git", run.out) # git command 'commit' - run = runner(command=yadm_cmd('commit', '-m', 'Add test_git')) + run = runner(command=yadm_cmd("commit", "-m", "Add test_git")) assert run.success - assert run.err == '' - assert '1 file changed' in run.out - assert '1 insertion' in run.out - assert re.search(r'create mode .+ test_git', run.out) + assert run.err == "" + assert "1 file changed" in run.out + assert "1 insertion" in run.out + assert re.search(r"create mode .+ test_git", run.out) # git command 'log' - run = runner(command=yadm_cmd('log', '--oneline')) + run = runner(command=yadm_cmd("log", "--oneline")) assert run.success - assert run.err == '' - assert 'Add test_git' in run.out + assert run.err == "" + assert "Add test_git" in run.out diff --git a/test/test_help.py b/test/test_help.py index 0d8f2c3..cbfabcc 100644 --- a/test/test_help.py +++ b/test/test_help.py @@ -6,14 +6,14 @@ def test_missing_command(runner, yadm_cmd): """Run without any command""" run = runner(command=yadm_cmd()) assert run.failure - assert run.err == '' - assert run.out.startswith('Usage: yadm') + assert run.err == "" + assert run.out.startswith("Usage: yadm") -@pytest.mark.parametrize('cmd', ['--help', 'help']) +@pytest.mark.parametrize("cmd", ["--help", "help"]) def test_help_command(runner, yadm_cmd, cmd): """Run with help command""" run = runner(command=yadm_cmd(cmd)) assert run.failure - assert run.err == '' - assert run.out.startswith('Usage: yadm') + assert run.err == "" + assert run.out.startswith("Usage: yadm") diff --git a/test/test_hooks.py b/test/test_hooks.py index 704636a..aff3daf 100644 --- a/test/test_hooks.py +++ b/test/test_hooks.py @@ -4,7 +4,8 @@ import pytest @pytest.mark.parametrize( - 'pre, pre_code, post, post_code', [ + "pre, pre_code, post, post_code", + [ (False, 0, False, 0), (True, 0, False, 0), (True, 5, False, 0), @@ -12,83 +13,83 @@ import pytest (False, 0, True, 5), (True, 0, True, 0), (True, 5, True, 5), - ], ids=[ - 'no-hooks', - 'pre-success', - 'pre-fail', - 'post-success', - 'post-fail', - 'pre-post-success', - 'pre-post-fail', - ]) -@pytest.mark.parametrize('cmd', ['--version', 'version']) -def test_hooks( - runner, yadm_cmd, paths, cmd, - pre, pre_code, post, post_code): + ], + ids=[ + "no-hooks", + "pre-success", + "pre-fail", + "post-success", + "post-fail", + "pre-post-success", + "pre-post-fail", + ], +) +@pytest.mark.parametrize("cmd", ["--version", "version"]) +def test_hooks(runner, yadm_cmd, paths, cmd, pre, pre_code, post, post_code): """Test pre/post hook""" # generate hooks if pre: - create_hook(paths, 'pre_version', pre_code) + create_hook(paths, "pre_version", pre_code) if post: - create_hook(paths, 'post_version', post_code) + create_hook(paths, "post_version", post_code) # run yadm run = runner(yadm_cmd(cmd)) # when a pre hook fails, yadm should exit with the hook's code assert run.code == pre_code - assert run.err == '' + assert run.err == "" if pre: - assert 'HOOK:pre_version' in run.out + assert "HOOK:pre_version" in run.out # if pre hook is missing or successful, yadm itself should exit 0 if run.success: if post: - assert 'HOOK:post_version' in run.out + assert "HOOK:post_version" in run.out else: # when a pre hook fails, yadm should not run the command - assert 'version will not be run' in run.out + assert "version will not be run" in run.out # when a pre hook fails, yadm should not run the post hook - assert 'HOOK:post_version' not in run.out + assert "HOOK:post_version" not in run.out # repo fixture is needed to test the population of YADM_HOOK_WORK -@pytest.mark.usefixtures('ds1_repo_copy') +@pytest.mark.usefixtures("ds1_repo_copy") def test_hook_env(runner, yadm_cmd, paths): """Test hook environment""" # test will be done with a non existent "git" passthru command # which should exit with a failing code - cmd = 'passthrucmd' + cmd = "passthrucmd" # write the hook - hook = paths.hooks.join(f'post_{cmd}') - hook.write('#!/bin/bash\nenv\ndeclare\n') + hook = paths.hooks.join(f"post_{cmd}") + hook.write("#!/bin/bash\nenv\ndeclare\n") hook.chmod(0o755) - run = runner(yadm_cmd(cmd, 'extra_args')) + run = runner(yadm_cmd(cmd, "extra_args")) # expect passthru to fail assert run.failure assert f"'{cmd}' is not a git command" in run.err # verify hook environment - assert 'YADM_HOOK_EXIT=1\n' in run.out - assert f'YADM_HOOK_COMMAND={cmd}\n' in run.out - assert f'YADM_HOOK_DIR={paths.yadm}\n' in run.out - assert f'YADM_HOOK_FULL_COMMAND={cmd} extra_args\n' in run.out - assert f'YADM_HOOK_REPO={paths.repo}\n' in run.out - assert f'YADM_HOOK_WORK={paths.work}\n' in run.out - assert 'YADM_ENCRYPT_INCLUDE_FILES=\n' in run.out + assert "YADM_HOOK_EXIT=1\n" in run.out + assert f"YADM_HOOK_COMMAND={cmd}\n" in run.out + assert f"YADM_HOOK_DIR={paths.yadm}\n" in run.out + assert f"YADM_HOOK_FULL_COMMAND={cmd} extra_args\n" in run.out + assert f"YADM_HOOK_REPO={paths.repo}\n" in run.out + assert f"YADM_HOOK_WORK={paths.work}\n" in run.out + assert "YADM_ENCRYPT_INCLUDE_FILES=\n" in run.out # verify the hook environment contains certain exported functions for func in [ - 'builtin_dirname', - 'relative_path', - 'unix_path', - 'mixed_path', + "builtin_dirname", + "relative_path", + "unix_path", + "mixed_path", ]: - assert f'BASH_FUNC_{func}' in run.out + assert f"BASH_FUNC_{func}" in run.out # verify the hook environment contains the list of encrypted files script = f""" @@ -98,10 +99,10 @@ def test_hook_env(runner, yadm_cmd, paths): ENCRYPT_INCLUDE_FILES=(a b c) invoke_hook "post" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'YADM_ENCRYPT_INCLUDE_FILES=a\nb\nc\n' in run.out + assert run.err == "" + assert "YADM_ENCRYPT_INCLUDE_FILES=a\nb\nc\n" in run.out def test_escaped(runner, yadm_cmd, paths): @@ -109,35 +110,33 @@ def test_escaped(runner, yadm_cmd, paths): # test will be done with a non existent "git" passthru command # which should exit with a failing code - cmd = 'passthrucmd' + cmd = "passthrucmd" # write the hook - hook = paths.hooks.join(f'post_{cmd}') - hook.write('#!/bin/bash\nenv\n') + hook = paths.hooks.join(f"post_{cmd}") + hook.write("#!/bin/bash\nenv\n") hook.chmod(0o755) - run = runner(yadm_cmd(cmd, 'a b', 'c\td', 'e\\f')) + run = runner(yadm_cmd(cmd, "a b", "c\td", "e\\f")) # expect passthru to fail assert run.failure # verify escaped values - assert ( - f'YADM_HOOK_FULL_COMMAND={cmd} ' - 'a\\ b c\\\td e\\\\f\n') in run.out + assert f"YADM_HOOK_FULL_COMMAND={cmd} a\\ b c\\\td e\\\\f\n" in run.out -@pytest.mark.parametrize('condition', ['exec', 'no-exec', 'mingw']) +@pytest.mark.parametrize("condition", ["exec", "no-exec", "mingw"]) def test_executable(runner, paths, condition): """Verify hook must be exectuable""" - cmd = 'version' - hook = paths.hooks.join(f'pre_{cmd}') - hook.write('#!/bin/sh\necho HOOK\n') + cmd = "version" + hook = paths.hooks.join(f"pre_{cmd}") + hook.write("#!/bin/sh\necho HOOK\n") hook.chmod(0o644) - if condition == 'exec': + if condition == "exec": hook.chmod(0o755) - mingw = 'OPERATING_SYSTEM="MINGWx"' if condition == 'mingw' else '' + mingw = 'OPERATING_SYSTEM="MINGWx"' if condition == "mingw" else "" script = f""" YADM_TEST=1 source {paths.pgm} YADM_HOOKS="{paths.hooks}" @@ -145,27 +144,23 @@ def test_executable(runner, paths, condition): {mingw} invoke_hook "pre" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) - if condition != 'mingw': + if condition != "mingw": assert run.success - assert run.err == '' + assert run.err == "" else: assert run.failure - assert 'Permission denied' in run.err + assert "Permission denied" in run.err - if condition == 'exec': - assert 'HOOK' in run.out - elif condition == 'no-exec': - assert 'HOOK' not in run.out + if condition == "exec": + assert "HOOK" in run.out + elif condition == "no-exec": + assert "HOOK" not in run.out def create_hook(paths, name, code): """Create hook""" hook = paths.hooks.join(name) - hook.write( - '#!/bin/sh\n' - f'echo HOOK:{name}\n' - f'exit {code}\n' - ) + hook.write("#!/bin/sh\n" f"echo HOOK:{name}\n" f"exit {code}\n") hook.chmod(0o755) diff --git a/test/test_init.py b/test/test_init.py index 3d149d6..a8ba493 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -4,22 +4,24 @@ import pytest @pytest.mark.parametrize( - 'alt_work, repo_present, force', [ + "alt_work, repo_present, force", + [ (False, False, False), (True, False, False), (False, True, False), (False, True, True), (True, True, True), - ], ids=[ - 'simple', - '-w', - 'existing repo', - '-f', - '-w & -f', - ]) -@pytest.mark.usefixtures('ds1_work_copy') -def test_init( - runner, yadm_cmd, paths, repo_config, alt_work, repo_present, force): + ], + ids=[ + "simple", + "-w", + "existing repo", + "-f", + "-w & -f", + ], +) +@pytest.mark.usefixtures("ds1_work_copy") +def test_init(runner, yadm_cmd, paths, repo_config, alt_work, repo_present, force): """Test init Repos should have attribs: @@ -31,56 +33,54 @@ def test_init( """ # these tests will assume this for $HOME - home = str(paths.root.mkdir('HOME')) + home = str(paths.root.mkdir("HOME")) # ds1_work_copy comes WITH an empty repo dir present. - old_repo = paths.repo.join('old_repo') + old_repo = paths.repo.join("old_repo") if repo_present: # Let's put some data in it, so we can confirm that data is gone when # forced to be overwritten. - old_repo.write('old repo data') + old_repo.write("old repo data") assert old_repo.isfile() else: paths.repo.remove() # command args - args = ['init'] + args = ["init"] cwd = None if alt_work: if force: cwd = paths.work.dirname - args.extend(['-w', paths.work.basename]) + args.extend(["-w", paths.work.basename]) else: - args.extend(['-w', paths.work]) + args.extend(["-w", paths.work]) if force: - args.append('-f') + args.append("-f") # run init - runner(['git', 'config', '--global', 'init.defaultBranch', 'master'], - env={'HOME': home}, cwd=cwd) - run = runner(yadm_cmd(*args), env={'HOME': home}, cwd=cwd) + runner(["git", "config", "--global", "init.defaultBranch", "master"], env={"HOME": home}, cwd=cwd) + run = runner(yadm_cmd(*args), env={"HOME": home}, cwd=cwd) if repo_present and not force: assert run.failure - assert 'repo already exists' in run.err - assert old_repo.isfile(), 'Missing original repo' + assert "repo already exists" in run.err + assert old_repo.isfile(), "Missing original repo" else: assert run.success - assert 'Initialized empty shared Git repository' in run.out + assert "Initialized empty shared Git repository" in run.out if repo_present: - assert not old_repo.isfile(), 'Original repo still exists' + assert not old_repo.isfile(), "Original repo still exists" else: - assert run.err == '' + assert run.err == "" if alt_work: - assert repo_config('core.worktree') == paths.work + assert repo_config("core.worktree") == paths.work else: - assert repo_config('core.worktree') == home + assert repo_config("core.worktree") == home # uniform repo assertions - assert oct(paths.repo.stat().mode).endswith('00'), ( - 'Repo is not secure') - assert repo_config('core.bare') == 'false' - assert repo_config('status.showUntrackedFiles') == 'no' - assert repo_config('yadm.managed') == 'true' + assert oct(paths.repo.stat().mode).endswith("00"), "Repo is not secure" + assert repo_config("core.bare") == "false" + assert repo_config("status.showUntrackedFiles") == "no" + assert repo_config("yadm.managed") == "true" diff --git a/test/test_introspect.py b/test/test_introspect.py index b292bd4..3a4fb46 100644 --- a/test/test_introspect.py +++ b/test/test_introspect.py @@ -4,38 +4,38 @@ import pytest @pytest.mark.parametrize( - 'name', [ - '', - 'invalid', - 'commands', - 'configs', - 'repo', - 'switches', - ]) -def test_introspect_category( - runner, yadm_cmd, paths, name, - supported_commands, supported_configs, supported_switches): + "name", + [ + "", + "invalid", + "commands", + "configs", + "repo", + "switches", + ], +) +def test_introspect_category(runner, yadm_cmd, paths, name, supported_commands, supported_configs, supported_switches): """Validate introspection category""" if name: - run = runner(command=yadm_cmd('introspect', name)) + run = runner(command=yadm_cmd("introspect", name)) else: - run = runner(command=yadm_cmd('introspect')) + run = runner(command=yadm_cmd("introspect")) assert run.success - assert run.err == '' + assert run.err == "" expected = [] - if name == 'commands': + if name == "commands": expected = supported_commands - elif name == 'configs': + elif name == "configs": expected = supported_configs - elif name == 'switches': + elif name == "switches": expected = supported_switches # assert values - if name in ('', 'invalid'): - assert run.out == '' - if name == 'repo': + if name in ("", "invalid"): + assert run.out == "" + if name == "repo": assert run.out.rstrip() == paths.repo # make sure every expected value is present diff --git a/test/test_list.py b/test/test_list.py index d7d09a6..afcea6f 100644 --- a/test/test_list.py +++ b/test/test_list.py @@ -6,27 +6,29 @@ import pytest @pytest.mark.parametrize( - 'location', [ - 'work', - 'outside', - 'subdir', - ]) -@pytest.mark.usefixtures('ds1_copy') + "location", + [ + "work", + "outside", + "subdir", + ], +) +@pytest.mark.usefixtures("ds1_copy") def test_list(runner, yadm_cmd, paths, ds1, location): """List tests""" - if location == 'work': + if location == "work": run_dir = paths.work - elif location == 'outside': - run_dir = paths.work.join('..') - elif location == 'subdir': + elif location == "outside": + run_dir = paths.work.join("..") + elif location == "subdir": # first directory with tracked data run_dir = paths.work.join(ds1.tracked_dirs[0]) with run_dir.as_cwd(): # test with '-a' # should get all tracked files, relative to the work path - run = runner(command=yadm_cmd('list', '-a')) + run = runner(command=yadm_cmd("list", "-a")) assert run.success - assert run.err == '' + assert run.err == "" returned_files = set(run.out.splitlines()) expected_files = {e.path for e in ds1 if e.tracked} assert returned_files == expected_files @@ -34,16 +36,14 @@ def test_list(runner, yadm_cmd, paths, ds1, location): # should get all tracked files, relative to the work path unless in a # subdir, then those should be a limited set of files, relative to the # subdir - run = runner(command=yadm_cmd('list')) + run = runner(command=yadm_cmd("list")) assert run.success - assert run.err == '' + assert run.err == "" returned_files = set(run.out.splitlines()) - if location == 'subdir': + if location == "subdir": basepath = os.path.basename(os.getcwd()) # only expect files within the subdir # names should be relative to subdir - expected_files = { - e.path[len(basepath)+1:] - for e in ds1 if e.tracked and e.path.startswith(basepath) - } + index = len(basepath) + 1 + expected_files = {e.path[index:] for e in ds1 if e.tracked and e.path.startswith(basepath)} assert returned_files == expected_files diff --git a/test/test_perms.py b/test/test_perms.py index a49d897..1491c16 100644 --- a/test/test_perms.py +++ b/test/test_perms.py @@ -5,18 +5,17 @@ import os import pytest -@pytest.mark.parametrize('autoperms', ['notest', 'unset', 'true', 'false']) -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.parametrize("autoperms", ["notest", "unset", "true", "false"]) +@pytest.mark.usefixtures("ds1_copy") def test_perms(runner, yadm_cmd, paths, ds1, autoperms): """Test perms""" # set the value of auto-perms - if autoperms != 'notest': - if autoperms != 'unset': - os.system(' '.join( - yadm_cmd('config', 'yadm.auto-perms', autoperms))) + if autoperms != "notest": + if autoperms != "unset": + os.system(" ".join(yadm_cmd("config", "yadm.auto-perms", autoperms))) # privatepaths will hold all paths that should become secured - privatepaths = [paths.work.join('.ssh'), paths.work.join('.gnupg')] + privatepaths = [paths.work.join(".ssh"), paths.work.join(".gnupg")] privatepaths += [paths.work.join(private.path) for private in ds1.private] # create an archive file @@ -24,82 +23,71 @@ def test_perms(runner, yadm_cmd, paths, ds1, autoperms): privatepaths.append(paths.archive) # create encrypted file test data - efile1 = paths.work.join('efile1') - efile1.write('efile1') - efile2 = paths.work.join('efile2') - efile2.write('efile2') - paths.encrypt.write('efile1\nefile2\n!efile1\n') + efile1 = paths.work.join("efile1") + efile1.write("efile1") + efile2 = paths.work.join("efile2") + efile2.write("efile2") + paths.encrypt.write("efile1\nefile2\n!efile1\n") insecurepaths = [efile1] privatepaths.append(efile2) # assert these paths begin unsecured for private in privatepaths + insecurepaths: - assert not oct(private.stat().mode).endswith('00'), ( - 'Path started secured') + assert not oct(private.stat().mode).endswith("00"), "Path started secured" - cmd = 'perms' - if autoperms != 'notest': - cmd = 'status' - run = runner(yadm_cmd(cmd), env={'HOME': paths.work}) + cmd = "perms" + if autoperms != "notest": + cmd = "status" + run = runner(yadm_cmd(cmd), env={"HOME": paths.work}) assert run.success - assert run.err == '' - if cmd == 'perms': - assert run.out == '' + assert run.err == "" + if cmd == "perms": + assert run.out == "" # these paths should be secured if processing perms for private in privatepaths: - if autoperms == 'false': - assert not oct(private.stat().mode).endswith('00'), ( - 'Path should not be secured') + if autoperms == "false": + assert not oct(private.stat().mode).endswith("00"), "Path should not be secured" else: - assert oct(private.stat().mode).endswith('00'), ( - 'Path has not been secured') + assert oct(private.stat().mode).endswith("00"), "Path has not been secured" # these paths should never be secured for private in insecurepaths: - assert not oct(private.stat().mode).endswith('00'), ( - 'Path should not be secured') + assert not oct(private.stat().mode).endswith("00"), "Path should not be secured" -@pytest.mark.parametrize('sshperms', [None, 'true', 'false']) -@pytest.mark.parametrize('gpgperms', [None, 'true', 'false']) -@pytest.mark.usefixtures('ds1_copy') +@pytest.mark.parametrize("sshperms", [None, "true", "false"]) +@pytest.mark.parametrize("gpgperms", [None, "true", "false"]) +@pytest.mark.usefixtures("ds1_copy") def test_perms_control(runner, yadm_cmd, paths, ds1, sshperms, gpgperms): """Test fine control of perms""" # set the value of ssh-perms if sshperms: - os.system(' '.join(yadm_cmd('config', 'yadm.ssh-perms', sshperms))) + os.system(" ".join(yadm_cmd("config", "yadm.ssh-perms", sshperms))) # set the value of gpg-perms if gpgperms: - os.system(' '.join(yadm_cmd('config', 'yadm.gpg-perms', gpgperms))) + os.system(" ".join(yadm_cmd("config", "yadm.gpg-perms", gpgperms))) # privatepaths will hold all paths that should become secured - privatepaths = [paths.work.join('.ssh'), paths.work.join('.gnupg')] + privatepaths = [paths.work.join(".ssh"), paths.work.join(".gnupg")] privatepaths += [paths.work.join(private.path) for private in ds1.private] # assert these paths begin unsecured for private in privatepaths: - assert not oct(private.stat().mode).endswith('00'), ( - 'Path started secured') + assert not oct(private.stat().mode).endswith("00"), "Path started secured" - run = runner(yadm_cmd('perms'), env={'HOME': paths.work}) + run = runner(yadm_cmd("perms"), env={"HOME": paths.work}) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" # these paths should be secured if processing perms for private in privatepaths: - if ( - (sshperms == 'false' and 'ssh' in str(private)) - or - (gpgperms == 'false' and 'gnupg' in str(private)) - ): - assert not oct(private.stat().mode).endswith('00'), ( - 'Path should not be secured') + if (sshperms == "false" and "ssh" in str(private)) or (gpgperms == "false" and "gnupg" in str(private)): + assert not oct(private.stat().mode).endswith("00"), "Path should not be secured" else: - assert oct(private.stat().mode).endswith('00'), ( - 'Path has not been secured') + assert oct(private.stat().mode).endswith("00"), "Path has not been secured" # verify permissions aren't changed for the worktree - assert oct(paths.work.stat().mode).endswith('0755') + assert oct(paths.work.stat().mode).endswith("0755") diff --git a/test/test_syntax.py b/test/test_syntax.py index aadafde..6549822 100644 --- a/test/test_syntax.py +++ b/test/test_syntax.py @@ -7,80 +7,77 @@ import pytest def test_yadm_syntax(runner, yadm): """Is syntactically valid""" - run = runner(command=['bash', '-n', yadm]) + run = runner(command=["bash", "-n", yadm]) assert run.success def test_shellcheck(pytestconfig, runner, yadm, shellcheck_version): """Passes shellcheck""" if not pytestconfig.getoption("--force-linters"): - run = runner(command=['shellcheck', '-V'], report=False) - if f'version: {shellcheck_version}' not in run.out: - pytest.skip('Unsupported shellcheck version') - run = runner(command=['shellcheck', '-s', 'bash', yadm]) + run = runner(command=["shellcheck", "-V"], report=False) + if f"version: {shellcheck_version}" not in run.out: + pytest.skip("Unsupported shellcheck version") + run = runner(command=["shellcheck", "-s", "bash", yadm]) assert run.success def test_pylint(pytestconfig, runner, pylint_version): """Passes pylint""" if not pytestconfig.getoption("--force-linters"): - run = runner(command=['pylint', '--version'], report=False) - if f'pylint {pylint_version}' not in run.out: - pytest.skip('Unsupported pylint version') + run = runner(command=["pylint", "--version"], report=False) + if f"pylint {pylint_version}" not in run.out: + pytest.skip("Unsupported pylint version") pyfiles = [] - for tfile in os.listdir('test'): - if tfile.endswith('.py'): - pyfiles.append(f'test/{tfile}') - run = runner(command=['pylint'] + pyfiles) + for tfile in os.listdir("test"): + if tfile.endswith(".py"): + pyfiles.append(f"test/{tfile}") + run = runner(command=["pylint"] + pyfiles) assert run.success def test_isort(pytestconfig, runner, isort_version): """Passes isort""" if not pytestconfig.getoption("--force-linters"): - run = runner(command=['isort', '--version'], report=False) + run = runner(command=["isort", "--version"], report=False) if isort_version not in run.out: - pytest.skip('Unsupported isort version') - run = runner(command=['isort', '-c', 'test']) + pytest.skip("Unsupported isort version") + run = runner(command=["isort", "-c", "test"]) assert run.success def test_flake8(pytestconfig, runner, flake8_version): """Passes flake8""" if not pytestconfig.getoption("--force-linters"): - run = runner(command=['flake8', '--version'], report=False) + run = runner(command=["flake8", "--version"], report=False) if not run.out.startswith(flake8_version): - pytest.skip('Unsupported flake8 version') - run = runner(command=['flake8', 'test']) + pytest.skip("Unsupported flake8 version") + run = runner(command=["flake8", "test"]) assert run.success def test_black(pytestconfig, runner, black_version): """Passes black""" if not pytestconfig.getoption("--force-linters"): - run = runner(command=['black', '--version'], report=False) + run = runner(command=["black", "--version"], report=False) if black_version not in run.out: - pytest.skip('Unsupported black version') - run = runner(command=['black', '--check', 'test']) + pytest.skip("Unsupported black version") + run = runner(command=["black", "--check", "test"]) assert run.success def test_yamllint(pytestconfig, runner, yamllint_version): """Passes yamllint""" if not pytestconfig.getoption("--force-linters"): - run = runner(command=['yamllint', '--version'], report=False) + run = runner(command=["yamllint", "--version"], report=False) if not run.out.strip().endswith(yamllint_version): - pytest.skip('Unsupported yamllint version') - run = runner( - command=['yamllint', '-s', '$(find . -name \\*.yml)'], - shell=True) + pytest.skip("Unsupported yamllint version") + run = runner(command=["yamllint", "-s", "$(find . -name \\*.yml)"], shell=True) assert run.success def test_man(runner): """Check for warnings from man""" - run = runner( - command=['man.REAL', '--warnings', './yadm.1']) + run = runner(command=["man.REAL", "--warnings", "./yadm.1"]) assert run.success - assert run.err == '' - assert 'yadm - Yet Another Dotfiles Manager' in run.out + assert run.err == "" + assert "yadm - Yet Another Dotfiles Manager" in run.out diff --git a/test/test_unit_bootstrap_available.py b/test/test_unit_bootstrap_available.py index f37ac08..6ab8df8 100644 --- a/test/test_unit_bootstrap_available.py +++ b/test/test_unit_bootstrap_available.py @@ -8,14 +8,14 @@ def test_bootstrap_missing(runner, paths): def test_bootstrap_no_exec(runner, paths): """Test result of bootstrap_available, when bootstrap not executable""" - paths.bootstrap.write('') + paths.bootstrap.write("") paths.bootstrap.chmod(0o644) run_test(runner, paths, False) def test_bootstrap_exec(runner, paths): """Test result of bootstrap_available, when bootstrap executable""" - paths.bootstrap.write('') + paths.bootstrap.write("") paths.bootstrap.chmod(0o775) run_test(runner, paths, True) @@ -27,7 +27,7 @@ def run_test(runner, paths, success): YADM_BOOTSTRAP='{paths.bootstrap}' bootstrap_available """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success == success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" diff --git a/test/test_unit_choose_template_cmd.py b/test/test_unit_choose_template_cmd.py index 536735d..cf7600b 100644 --- a/test/test_unit_choose_template_cmd.py +++ b/test/test_unit_choose_template_cmd.py @@ -2,20 +2,20 @@ import pytest -@pytest.mark.parametrize('label', ['', 'default', 'other']) -@pytest.mark.parametrize('awk', [True, False], ids=['awk', 'no-awk']) +@pytest.mark.parametrize("label", ["", "default", "other"]) +@pytest.mark.parametrize("awk", [True, False], ids=["awk", "no-awk"]) def test_kind_default(runner, yadm, awk, label): """Test kind: default""" - expected = 'template_default' - awk_avail = 'true' + expected = "template_default" + awk_avail = "true" if not awk: - awk_avail = 'false' - expected = '' + awk_avail = "false" + expected = "" - if label == 'other': - expected = '' + if label == "other": + expected = "" script = f""" YADM_TEST=1 source {yadm} @@ -23,30 +23,30 @@ def test_kind_default(runner, yadm, awk, label): template="$(choose_template_cmd "{label}")" echo "TEMPLATE:$template" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert f'TEMPLATE:{expected}\n' in run.out + assert run.err == "" + assert f"TEMPLATE:{expected}\n" in run.out -@pytest.mark.parametrize('label', ['envtpl', 'j2cli', 'j2', 'other']) -@pytest.mark.parametrize('envtpl', [True, False], ids=['envtpl', 'no-envtpl']) -@pytest.mark.parametrize('j2cli', [True, False], ids=['j2cli', 'no-j2cli']) +@pytest.mark.parametrize("label", ["envtpl", "j2cli", "j2", "other"]) +@pytest.mark.parametrize("envtpl", [True, False], ids=["envtpl", "no-envtpl"]) +@pytest.mark.parametrize("j2cli", [True, False], ids=["j2cli", "no-j2cli"]) def test_kind_j2cli_envtpl(runner, yadm, envtpl, j2cli, label): """Test kind: j2 (both j2cli & envtpl) j2cli is preferred over envtpl if available. """ - envtpl_avail = 'true' if envtpl else 'false' - j2cli_avail = 'true' if j2cli else 'false' + envtpl_avail = "true" if envtpl else "false" + j2cli_avail = "true" if j2cli else "false" - if label in ('j2cli', 'j2') and j2cli: - expected = 'template_j2cli' - elif label in ('envtpl', 'j2') and envtpl: - expected = 'template_envtpl' + if label in ("j2cli", "j2") and j2cli: + expected = "template_j2cli" + elif label in ("envtpl", "j2") and envtpl: + expected = "template_envtpl" else: - expected = '' + expected = "" script = f""" YADM_TEST=1 source {yadm} @@ -55,7 +55,7 @@ def test_kind_j2cli_envtpl(runner, yadm, envtpl, j2cli, label): template="$(choose_template_cmd "{label}")" echo "TEMPLATE:$template" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert f'TEMPLATE:{expected}\n' in run.out + assert run.err == "" + assert f"TEMPLATE:{expected}\n" in run.out diff --git a/test/test_unit_configure_paths.py b/test/test_unit_configure_paths.py index 8ecd0ea..d2a680e 100644 --- a/test/test_unit_configure_paths.py +++ b/test/test_unit_configure_paths.py @@ -2,52 +2,56 @@ import pytest -ARCHIVE = 'archive' -BOOTSTRAP = 'bootstrap' -CONFIG = 'config' -ENCRYPT = 'encrypt' -HOME = '/testhome' -REPO = 'repo.git' -YDIR = '.config/yadm' -YDATA = '.local/share/yadm' +ARCHIVE = "archive" +BOOTSTRAP = "bootstrap" +CONFIG = "config" +ENCRYPT = "encrypt" +HOME = "/testhome" +REPO = "repo.git" +YDIR = ".config/yadm" +YDATA = ".local/share/yadm" @pytest.mark.parametrize( - 'override, expect', [ + "override, expect", + [ (None, {}), - ('-Y', {'yadm': 'YADM_DIR'}), - ('--yadm-data', {'data': 'YADM_DATA'}), - ('--yadm-repo', {'repo': 'YADM_REPO', 'git': 'GIT_DIR'}), - ('--yadm-config', {'config': 'YADM_CONFIG'}), - ('--yadm-encrypt', {'encrypt': 'YADM_ENCRYPT'}), - ('--yadm-archive', {'archive': 'YADM_ARCHIVE'}), - ('--yadm-bootstrap', {'bootstrap': 'YADM_BOOTSTRAP'}), - ], ids=[ - 'default', - 'override yadm dir', - 'override yadm data', - 'override repo', - 'override config', - 'override encrypt', - 'override archive', - 'override bootstrap', - ]) + ("-Y", {"yadm": "YADM_DIR"}), + ("--yadm-data", {"data": "YADM_DATA"}), + ("--yadm-repo", {"repo": "YADM_REPO", "git": "GIT_DIR"}), + ("--yadm-config", {"config": "YADM_CONFIG"}), + ("--yadm-encrypt", {"encrypt": "YADM_ENCRYPT"}), + ("--yadm-archive", {"archive": "YADM_ARCHIVE"}), + ("--yadm-bootstrap", {"bootstrap": "YADM_BOOTSTRAP"}), + ], + ids=[ + "default", + "override yadm dir", + "override yadm data", + "override repo", + "override config", + "override encrypt", + "override archive", + "override bootstrap", + ], +) @pytest.mark.parametrize( - 'path', ['.', './override', 'override', '.override', '/override'], ids=[ - 'cwd', './relative', 'relative', 'hidden relative', 'absolute' - ]) + "path", + [".", "./override", "override", ".override", "/override"], + ids=["cwd", "./relative", "relative", "hidden relative", "absolute"], +) def test_config(runner, paths, override, expect, path): """Test configure_paths""" - if path.startswith('/'): + if path.startswith("/"): expected_path = path else: expected_path = str(paths.root.join(path)) args = [override, path] if override else [] - if override == '-Y': + if override == "-Y": matches = match_map(expected_path) - elif override == '--yadm-data': + elif override == "--yadm-data": matches = match_map(None, expected_path) else: matches = match_map() @@ -61,23 +65,23 @@ def test_config(runner, paths, override, expect, path): def match_map(yadm_dir=None, yadm_data=None): """Create a dictionary of matches, relative to yadm_dir""" if not yadm_dir: - yadm_dir = '/'.join([HOME, YDIR]) + yadm_dir = "/".join([HOME, YDIR]) if not yadm_data: - yadm_data = '/'.join([HOME, YDATA]) + yadm_data = "/".join([HOME, YDATA]) return { - 'yadm': f'YADM_DIR="{yadm_dir}"', - 'repo': f'YADM_REPO="{yadm_data}/{REPO}"', - 'config': f'YADM_CONFIG="{yadm_dir}/{CONFIG}"', - 'encrypt': f'YADM_ENCRYPT="{yadm_dir}/{ENCRYPT}"', - 'archive': f'YADM_ARCHIVE="{yadm_data}/{ARCHIVE}"', - 'bootstrap': f'YADM_BOOTSTRAP="{yadm_dir}/{BOOTSTRAP}"', - 'git': f'GIT_DIR="{yadm_data}/{REPO}"', - } + "yadm": f'YADM_DIR="{yadm_dir}"', + "repo": f'YADM_REPO="{yadm_data}/{REPO}"', + "config": f'YADM_CONFIG="{yadm_dir}/{CONFIG}"', + "encrypt": f'YADM_ENCRYPT="{yadm_dir}/{ENCRYPT}"', + "archive": f'YADM_ARCHIVE="{yadm_data}/{ARCHIVE}"', + "bootstrap": f'YADM_BOOTSTRAP="{yadm_dir}/{BOOTSTRAP}"', + "git": f'GIT_DIR="{yadm_data}/{REPO}"', + } def run_test(runner, paths, args, expected_matches, cwd=None): """Run proces global args, and run configure_paths""" - argstring = ' '.join(['"'+a+'"' for a in args]) + argstring = " ".join(['"' + a + '"' for a in args]) script = f""" YADM_TEST=1 HOME="{HOME}" source {paths.pgm} process_global_args {argstring} @@ -87,8 +91,8 @@ def run_test(runner, paths, args, expected_matches, cwd=None): configure_paths declare -p | grep -E '(YADM|GIT)_' """ - run = runner(command=['bash'], inp=script, cwd=cwd) + run = runner(command=["bash"], inp=script, cwd=cwd) assert run.success - assert run.err == '' + assert run.err == "" for match in expected_matches: assert match in run.out diff --git a/test/test_unit_copy_perms.py b/test/test_unit_copy_perms.py index b043878..c0ea04f 100644 --- a/test/test_unit_copy_perms.py +++ b/test/test_unit_copy_perms.py @@ -3,42 +3,40 @@ import os import pytest -OCTAL = '7654' -NON_OCTAL = '9876' +OCTAL = "7654" +NON_OCTAL = "9876" -@pytest.mark.parametrize( - 'stat_broken', [True, False], ids=['normal', 'stat broken']) +@pytest.mark.parametrize("stat_broken", [True, False], ids=["normal", "stat broken"]) def test_copy_perms(runner, yadm, tmpdir, stat_broken): """Test function copy_perms""" src_mode = 0o754 dst_mode = 0o644 - source = tmpdir.join('source') - source.write('test', ensure=True) + source = tmpdir.join("source") + source.write("test", ensure=True) source.chmod(src_mode) - dest = tmpdir.join('dest') - dest.write('test', ensure=True) + dest = tmpdir.join("dest") + dest.write("test", ensure=True) dest.chmod(dst_mode) - override_stat = '' + override_stat = "" if stat_broken: - override_stat = 'function stat() { echo broken; }' + override_stat = "function stat() { echo broken; }" script = f""" YADM_TEST=1 source {yadm} {override_stat} copy_perms "{source}" "{dest}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert run.out == '' + assert run.err == "" + assert run.out == "" expected = dst_mode if stat_broken else src_mode assert oct(os.stat(dest).st_mode)[-3:] == oct(expected)[-3:] -@pytest.mark.parametrize( - 'stat_output', [OCTAL, NON_OCTAL], ids=['octal', 'non-octal']) +@pytest.mark.parametrize("stat_output", [OCTAL, NON_OCTAL], ids=["octal", "non-octal"]) def test_get_mode(runner, yadm, stat_output): """Test function get_mode""" script = f""" @@ -47,8 +45,8 @@ def test_get_mode(runner, yadm, stat_output): mode=$(get_mode abc) echo "MODE:$mode" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" expected = OCTAL if stat_output == OCTAL else "" - assert f'MODE:{expected}\n' in run.out + assert f"MODE:{expected}\n" in run.out diff --git a/test/test_unit_encryption.py b/test/test_unit_encryption.py index ab03c62..098d5f6 100644 --- a/test/test_unit_encryption.py +++ b/test/test_unit_encryption.py @@ -3,12 +3,12 @@ import pytest -@pytest.mark.parametrize('condition', ['default', 'override']) +@pytest.mark.parametrize("condition", ["default", "override"]) def test_get_cipher(runner, paths, condition): """Test _get_cipher()""" - if condition == 'override': - paths.config.write('[yadm]\n\tcipher = override-cipher') + if condition == "override": + paths.config.write("[yadm]\n\tcipher = override-cipher") script = f""" YADM_TEST=1 source {paths.pgm} @@ -19,18 +19,18 @@ def test_get_cipher(runner, paths, condition): echo "output_archive:$output_archive" echo "yadm_cipher:$yadm_cipher" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'output_archive:test-archive' in run.out - if condition == 'override': - assert 'yadm_cipher:override-cipher' in run.out + assert run.err == "" + assert "output_archive:test-archive" in run.out + if condition == "override": + assert "yadm_cipher:override-cipher" in run.out else: - assert 'yadm_cipher:gpg' in run.out + assert "yadm_cipher:gpg" in run.out -@pytest.mark.parametrize('cipher', ['gpg', 'openssl', 'bad']) -@pytest.mark.parametrize('mode', ['_encrypt_to', '_decrypt_from']) +@pytest.mark.parametrize("cipher", ["gpg", "openssl", "bad"]) +@pytest.mark.parametrize("mode", ["_encrypt_to", "_decrypt_from"]) def test_encrypt_decrypt(runner, paths, cipher, mode): """Test _encrypt_to() & _decrypt_from""" @@ -49,24 +49,24 @@ def test_encrypt_decrypt(runner, paths, cipher, mode): GPG_PROGRAM=mock_gpg {mode} {paths.archive} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) - if cipher != 'bad': + if cipher != "bad": assert run.success assert run.out.startswith(cipher) assert str(paths.archive) in run.out - assert run.err == '' + assert run.err == "" else: assert run.failure - assert 'Unknown cipher' in run.err + assert "Unknown cipher" in run.err -@pytest.mark.parametrize('condition', ['default', 'override']) +@pytest.mark.parametrize("condition", ["default", "override"]) def test_get_openssl_ciphername(runner, paths, condition): """Test _get_openssl_ciphername()""" - if condition == 'override': - paths.config.write('[yadm]\n\topenssl-ciphername = override-cipher') + if condition == "override": + paths.config.write("[yadm]\n\topenssl-ciphername = override-cipher") script = f""" YADM_TEST=1 source {paths.pgm} @@ -76,21 +76,21 @@ def test_get_openssl_ciphername(runner, paths, condition): result=$(_get_openssl_ciphername) echo "result:$result" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'override': - assert run.out.strip() == 'result:override-cipher' + assert run.err == "" + if condition == "override": + assert run.out.strip() == "result:override-cipher" else: - assert run.out.strip() == 'result:aes-256-cbc' + assert run.out.strip() == "result:aes-256-cbc" -@pytest.mark.parametrize('condition', ['old', 'not-old']) +@pytest.mark.parametrize("condition", ["old", "not-old"]) def test_set_openssl_options(runner, paths, condition): """Test _set_openssl_options()""" - if condition == 'old': - paths.config.write('[yadm]\n\topenssl-old = true') + if condition == "old": + paths.config.write("[yadm]\n\topenssl-old = true") script = f""" YADM_TEST=1 source {paths.pgm} @@ -101,20 +101,20 @@ def test_set_openssl_options(runner, paths, condition): _set_openssl_options echo "result:${{OPENSSL_OPTS[@]}}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'old': - assert '-testcipher -salt -md md5' in run.out + assert run.err == "" + if condition == "old": + assert "-testcipher -salt -md md5" in run.out else: - assert '-testcipher -salt -pbkdf2 -iter 100000 -md sha512' in run.out + assert "-testcipher -salt -pbkdf2 -iter 100000 -md sha512" in run.out -@pytest.mark.parametrize('recipient', ['ASK', 'present', '']) +@pytest.mark.parametrize("recipient", ["ASK", "present", ""]) def test_set_gpg_options(runner, paths, recipient): """Test _set_gpg_options()""" - paths.config.write(f'[yadm]\n\tgpg-recipient = {recipient}') + paths.config.write(f"[yadm]\n\tgpg-recipient = {recipient}") script = f""" YADM_TEST=1 source {paths.pgm} @@ -124,12 +124,12 @@ def test_set_gpg_options(runner, paths, recipient): _set_gpg_options echo "result:${{GPG_OPTS[@]}}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if recipient == 'ASK': - assert run.out.strip() == 'result:--no-default-recipient -e' - elif recipient != '': - assert run.out.strip() == f'result:-e -r {recipient}' + assert run.err == "" + if recipient == "ASK": + assert run.out.strip() == "result:--no-default-recipient -e" + elif recipient != "": + assert run.out.strip() == f"result:-e -r {recipient}" else: - assert run.out.strip() == 'result:-c' + assert run.out.strip() == "result:-c" diff --git a/test/test_unit_exclude_encrypted.py b/test/test_unit_exclude_encrypted.py index 9d9a074..8937a8b 100644 --- a/test/test_unit_exclude_encrypted.py +++ b/test/test_unit_exclude_encrypted.py @@ -2,38 +2,28 @@ import pytest -@pytest.mark.parametrize( - 'exclude', ['missing', 'outdated', 'up-to-date']) -@pytest.mark.parametrize( - 'encrypt_exists', [True, False], ids=['encrypt', 'no-encrypt']) -@pytest.mark.parametrize( - 'auto_exclude', [True, False], ids=['enabled', 'disabled']) -def test_exclude_encrypted( - runner, tmpdir, yadm, encrypt_exists, auto_exclude, exclude): +@pytest.mark.parametrize("exclude", ["missing", "outdated", "up-to-date"]) +@pytest.mark.parametrize("encrypt_exists", [True, False], ids=["encrypt", "no-encrypt"]) +@pytest.mark.parametrize("auto_exclude", [True, False], ids=["enabled", "disabled"]) +def test_exclude_encrypted(runner, tmpdir, yadm, encrypt_exists, auto_exclude, exclude): """Test exclude_encrypted()""" - header = ( - "# yadm-auto-excludes\n" - "# This section is managed by yadm.\n" - "# Any edits below will be lost.\n" - ) + header = "# yadm-auto-excludes\n# This section is managed by yadm.\n# Any edits below will be lost.\n" config_function = 'function config() { echo "false";}' if auto_exclude: - config_function = 'function config() { return; }' + config_function = "function config() { return; }" - encrypt_file = tmpdir.join('encrypt_file') - repo_dir = tmpdir.join('repodir') - exclude_file = repo_dir.join('info/exclude') + encrypt_file = tmpdir.join("encrypt_file") + repo_dir = tmpdir.join("repodir") + exclude_file = repo_dir.join("info/exclude") if encrypt_exists: - encrypt_file.write('test-encrypt-data\n', ensure=True) - if exclude == 'outdated': - exclude_file.write( - f'original-exclude\n{header}outdated\n', ensure=True) - elif exclude == 'up-to-date': - exclude_file.write( - f'original-exclude\n{header}test-encrypt-data\n', ensure=True) + encrypt_file.write("test-encrypt-data\n", ensure=True) + if exclude == "outdated": + exclude_file.write(f"original-exclude\n{header}outdated\n", ensure=True) + elif exclude == "up-to-date": + exclude_file.write(f"original-exclude\n{header}test-encrypt-data\n", ensure=True) script = f""" YADM_TEST=1 source {yadm} @@ -43,24 +33,22 @@ def test_exclude_encrypted( YADM_REPO="{repo_dir}" exclude_encrypted """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" if auto_exclude: if encrypt_exists: assert exclude_file.exists() - if exclude == 'missing': - assert exclude_file.read() == f'{header}test-encrypt-data\n' + if exclude == "missing": + assert exclude_file.read() == f"{header}test-encrypt-data\n" else: - assert exclude_file.read() == ( - 'original-exclude\n' - f'{header}test-encrypt-data\n') - if exclude != 'up-to-date': - assert f'Updating {exclude_file}' in run.out + assert exclude_file.read() == ("original-exclude\n" f"{header}test-encrypt-data\n") + if exclude != "up-to-date": + assert f"Updating {exclude_file}" in run.out else: - assert run.out == '' + assert run.out == "" else: - assert run.out == '' + assert run.out == "" else: - assert run.out == '' + assert run.out == "" diff --git a/test/test_unit_issue_legacy_path_warning.py b/test/test_unit_issue_legacy_path_warning.py index e43228b..faae7fa 100644 --- a/test/test_unit_issue_legacy_path_warning.py +++ b/test/test_unit_issue_legacy_path_warning.py @@ -3,25 +3,24 @@ import pytest @pytest.mark.parametrize( - 'legacy_path', [ + "legacy_path", + [ None, - 'repo.git', - 'files.gpg', - ], - ) -@pytest.mark.parametrize( - 'override', [True, False], ids=['override', 'no-override']) -@pytest.mark.parametrize( - 'upgrade', [True, False], ids=['upgrade', 'no-upgrade']) + "repo.git", + "files.gpg", + ], +) +@pytest.mark.parametrize("override", [True, False], ids=["override", "no-override"]) +@pytest.mark.parametrize("upgrade", [True, False], ids=["upgrade", "no-upgrade"]) def test_legacy_warning(tmpdir, runner, yadm, upgrade, override, legacy_path): """Use issue_legacy_path_warning""" - home = tmpdir.mkdir('home') + home = tmpdir.mkdir("home") if legacy_path: - home.ensure(f'.config/yadm/{str(legacy_path)}') + home.ensure(f".config/yadm/{str(legacy_path)}") - override = 'YADM_OVERRIDE_REPO=override' if override else '' - main_args = 'MAIN_ARGS=("upgrade")' if upgrade else '' + override = "YADM_OVERRIDE_REPO=override" if override else "" + main_args = 'MAIN_ARGS=("upgrade")' if upgrade else "" script = f""" XDG_CONFIG_HOME= XDG_DATA_HOME= @@ -32,10 +31,10 @@ def test_legacy_warning(tmpdir, runner, yadm, upgrade, override, legacy_path): set_yadm_dirs issue_legacy_path_warning """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.out == '' + assert run.out == "" if legacy_path and (not upgrade) and (not override): - assert 'Legacy paths have been detected' in run.err + assert "Legacy paths have been detected" in run.err else: - assert 'Legacy paths have been detected' not in run.err + assert "Legacy paths have been detected" not in run.err diff --git a/test/test_unit_parse_encrypt.py b/test/test_unit_parse_encrypt.py index ec3a6ee..6a5c23b 100644 --- a/test/test_unit_parse_encrypt.py +++ b/test/test_unit_parse_encrypt.py @@ -7,136 +7,137 @@ def test_not_called(runner, paths): """Test parse_encrypt (not called)""" run = run_parse_encrypt(runner, paths, skip_parse=True) assert run.success - assert run.err == '' - assert 'EIF:unparsed' in run.out, 'EIF should be unparsed' - assert 'EIF_COUNT:1' in run.out, 'Only value of EIF should be unparsed' + assert run.err == "" + assert "EIF:unparsed" in run.out, "EIF should be unparsed" + assert "EIF_COUNT:1" in run.out, "Only value of EIF should be unparsed" def test_short_circuit(runner, paths): """Test parse_encrypt (short-circuit)""" run = run_parse_encrypt(runner, paths, twice=True) assert run.success - assert run.err == '' - assert 'PARSE_ENCRYPT_SHORT=parse_encrypt() not reprocessed' in run.out, ( - 'parse_encrypt() should short-circuit') + assert run.err == "" + assert "PARSE_ENCRYPT_SHORT=parse_encrypt() not reprocessed" in run.out, "parse_encrypt() should short-circuit" @pytest.mark.parametrize( - 'encrypt', [ - ('missing'), - ('empty'), - ]) + "encrypt", + [ + ("missing"), + ("empty"), + ], +) def test_empty(runner, paths, encrypt): """Test parse_encrypt (file missing/empty)""" # write encrypt file - if encrypt == 'missing': - assert not paths.encrypt.exists(), 'Encrypt should be missing' + if encrypt == "missing": + assert not paths.encrypt.exists(), "Encrypt should be missing" else: - paths.encrypt.write('') - assert paths.encrypt.exists(), 'Encrypt should exist' - assert paths.encrypt.size() == 0, 'Encrypt should be empty' + paths.encrypt.write("") + assert paths.encrypt.exists(), "Encrypt should exist" + assert paths.encrypt.size() == 0, "Encrypt should be empty" # run parse_encrypt run = run_parse_encrypt(runner, paths) assert run.success - assert run.err == '' + assert run.err == "" # validate parsing result - assert 'EIF_COUNT:0' in run.out, 'EIF should be empty' + assert "EIF_COUNT:0" in run.out, "EIF should be empty" def create_test_encrypt_data(paths): """Generate test data for testing encrypt""" - edata = '' + edata = "" expected = set() # empty line - edata += '\n' + edata += "\n" # simple comments - edata += '# a simple comment\n' - edata += ' # a comment with leading space\n' + edata += "# a simple comment\n" + edata += " # a comment with leading space\n" # unreferenced directory - paths.work.join('unreferenced').mkdir() + paths.work.join("unreferenced").mkdir() # simple files - edata += 'simple_file\n' - edata += 'simple.file\n' - paths.work.join('simple_file').write('') - paths.work.join('simple.file').write('') - paths.work.join('simple_file2').write('') - paths.work.join('simple.file2').write('') - expected.add('simple_file') - expected.add('simple.file') + edata += "simple_file\n" + edata += "simple.file\n" + paths.work.join("simple_file").write("") + paths.work.join("simple.file").write("") + paths.work.join("simple_file2").write("") + paths.work.join("simple.file2").write("") + expected.add("simple_file") + expected.add("simple.file") # simple files in directories - edata += 'simple_dir/simple_file\n' - paths.work.join('simple_dir/simple_file').write('', ensure=True) - paths.work.join('simple_dir/simple_file2').write('', ensure=True) - expected.add('simple_dir/simple_file') + edata += "simple_dir/simple_file\n" + paths.work.join("simple_dir/simple_file").write("", ensure=True) + paths.work.join("simple_dir/simple_file2").write("", ensure=True) + expected.add("simple_dir/simple_file") # paths with spaces - edata += 'with space/with space\n' - paths.work.join('with space/with space').write('', ensure=True) - paths.work.join('with space/with space2').write('', ensure=True) - expected.add('with space/with space') + edata += "with space/with space\n" + paths.work.join("with space/with space").write("", ensure=True) + paths.work.join("with space/with space2").write("", ensure=True) + expected.add("with space/with space") # hidden files - edata += '.hidden\n' - paths.work.join('.hidden').write('') - expected.add('.hidden') + edata += ".hidden\n" + paths.work.join(".hidden").write("") + expected.add(".hidden") # hidden files in directories - edata += '.hidden_dir/.hidden_file\n' - paths.work.join('.hidden_dir/.hidden_file').write('', ensure=True) - expected.add('.hidden_dir/.hidden_file') + edata += ".hidden_dir/.hidden_file\n" + paths.work.join(".hidden_dir/.hidden_file").write("", ensure=True) + expected.add(".hidden_dir/.hidden_file") # wildcards - edata += 'wild*\n' - paths.work.join('wildcard1').write('', ensure=True) - paths.work.join('wildcard2').write('', ensure=True) - expected.add('wildcard1') - expected.add('wildcard2') + edata += "wild*\n" + paths.work.join("wildcard1").write("", ensure=True) + paths.work.join("wildcard2").write("", ensure=True) + expected.add("wildcard1") + expected.add("wildcard2") - edata += 'dirwild*\n' - paths.work.join('dirwildcard/file1').write('', ensure=True) - paths.work.join('dirwildcard/file2').write('', ensure=True) - expected.add('dirwildcard') + edata += "dirwild*\n" + paths.work.join("dirwildcard/file1").write("", ensure=True) + paths.work.join("dirwildcard/file2").write("", ensure=True) + expected.add("dirwildcard") # excludes - edata += 'exclude*\n' - edata += 'ex ex/*\n' - paths.work.join('exclude_file1').write('') - paths.work.join('exclude_file2.ex').write('') - paths.work.join('exclude_file3.ex3').write('') - expected.add('exclude_file1') - expected.add('exclude_file3.ex3') - edata += '!*.ex\n' - edata += '!ex ex/*.txt\n' - paths.work.join('ex ex/file4').write('', ensure=True) - paths.work.join('ex ex/file5.txt').write('', ensure=True) - paths.work.join('ex ex/file6.text').write('', ensure=True) - expected.add('ex ex/file4') - expected.add('ex ex/file6.text') + edata += "exclude*\n" + edata += "ex ex/*\n" + paths.work.join("exclude_file1").write("") + paths.work.join("exclude_file2.ex").write("") + paths.work.join("exclude_file3.ex3").write("") + expected.add("exclude_file1") + expected.add("exclude_file3.ex3") + edata += "!*.ex\n" + edata += "!ex ex/*.txt\n" + paths.work.join("ex ex/file4").write("", ensure=True) + paths.work.join("ex ex/file5.txt").write("", ensure=True) + paths.work.join("ex ex/file6.text").write("", ensure=True) + expected.add("ex ex/file4") + expected.add("ex ex/file6.text") # double star - edata += 'doublestar/**/file*\n' - edata += '!**/file3\n' - paths.work.join('doublestar/a/b/file1').write('', ensure=True) - paths.work.join('doublestar/c/d/file2').write('', ensure=True) - paths.work.join('doublestar/e/f/file3').write('', ensure=True) - paths.work.join('doublestar/g/h/nomatch').write('', ensure=True) - expected.add('doublestar/a/b/file1') - expected.add('doublestar/c/d/file2') + edata += "doublestar/**/file*\n" + edata += "!**/file3\n" + paths.work.join("doublestar/a/b/file1").write("", ensure=True) + paths.work.join("doublestar/c/d/file2").write("", ensure=True) + paths.work.join("doublestar/e/f/file3").write("", ensure=True) + paths.work.join("doublestar/g/h/nomatch").write("", ensure=True) + expected.add("doublestar/a/b/file1") + expected.add("doublestar/c/d/file2") # doublestar/e/f/file3 is excluded return edata, expected -@pytest.mark.usefixtures('ds1_repo_copy') +@pytest.mark.usefixtures("ds1_repo_copy") def test_file_parse_encrypt(runner, paths): """Test parse_encrypt @@ -147,39 +148,35 @@ def test_file_parse_encrypt(runner, paths): edata, expected = create_test_encrypt_data(paths) # write encrypt file - print(f'ENCRYPT:\n---\n{edata}---\n') + print(f"ENCRYPT:\n---\n{edata}---\n") paths.encrypt.write(edata) assert paths.encrypt.isfile() # run parse_encrypt run = run_parse_encrypt(runner, paths) assert run.success - assert run.err == '' + assert run.err == "" - assert f'EIF_COUNT:{len(expected)}' in run.out, 'EIF count wrong' + assert f"EIF_COUNT:{len(expected)}" in run.out, "EIF count wrong" for expected_file in expected: - assert f'EIF:{expected_file}\n' in run.out + assert f"EIF:{expected_file}\n" in run.out - sorted_expectations = '\n'.join( - [f'EIF:{exp}' for exp in sorted(expected)]) + sorted_expectations = "\n".join([f"EIF:{exp}" for exp in sorted(expected)]) assert sorted_expectations in run.out -def run_parse_encrypt( - runner, paths, - skip_parse=False, - twice=False): +def run_parse_encrypt(runner, paths, skip_parse=False, twice=False): """Run parse_encrypt A count of ENCRYPT_INCLUDE_FILES will be reported as EIF_COUNT:X. All values of ENCRYPT_INCLUDE_FILES will be reported as individual EIF:value lines. """ - parse_cmd = 'parse_encrypt' + parse_cmd = "parse_encrypt" if skip_parse: - parse_cmd = '' + parse_cmd = "" if twice: - parse_cmd = 'parse_encrypt; parse_encrypt' + parse_cmd = "parse_encrypt; parse_encrypt" script = f""" YADM_TEST=1 source {paths.pgm} YADM_ENCRYPT={paths.encrypt} @@ -197,5 +194,5 @@ def run_parse_encrypt( echo "EIF:$value" done """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) return run diff --git a/test/test_unit_private_dirs.py b/test/test_unit_private_dirs.py index 4f182da..58bd39c 100644 --- a/test/test_unit_private_dirs.py +++ b/test/test_unit_private_dirs.py @@ -3,15 +3,15 @@ import pytest @pytest.mark.parametrize( - 'gnupghome', + "gnupghome", [True, False], - ids=['gnupghome-set', 'gnupghome-unset'], + ids=["gnupghome-set", "gnupghome-unset"], ) -@pytest.mark.parametrize('param', ['all', 'gnupg']) +@pytest.mark.parametrize("param", ["all", "gnupg"]) def test_relative_path(runner, paths, gnupghome, param): """Test translate_to_relative""" - alt_gnupghome = 'alt/gnupghome' + alt_gnupghome = "alt/gnupghome" env_gnupghome = paths.work.join(alt_gnupghome) script = f""" @@ -22,13 +22,13 @@ def test_relative_path(runner, paths, gnupghome, param): env = {} if gnupghome: - env['GNUPGHOME'] = env_gnupghome + env["GNUPGHOME"] = env_gnupghome - expected = alt_gnupghome if gnupghome else '.gnupg' - if param == 'all': - expected = f'.ssh {expected}' + expected = alt_gnupghome if gnupghome else ".gnupg" + if param == "all": + expected = f".ssh {expected}" - run = runner(command=['bash'], inp=script, env=env) + run = runner(command=["bash"], inp=script, env=env) assert run.success - assert run.err == '' + assert run.err == "" assert run.out.strip() == expected diff --git a/test/test_unit_query_distro.py b/test/test_unit_query_distro.py index 4f46501..c32760b 100644 --- a/test/test_unit_query_distro.py +++ b/test/test_unit_query_distro.py @@ -2,18 +2,16 @@ import pytest -@pytest.mark.parametrize( - 'condition', ['lsb_release', 'os-release', 'os-release-quotes', 'missing']) +@pytest.mark.parametrize("condition", ["lsb_release", "os-release", "os-release-quotes", "missing"]) def test_query_distro(runner, yadm, tst_distro, tmp_path, condition): """Match lsb_release -si when present""" - test_release = 'testrelease' - lsb_release = '' - os_release = tmp_path.joinpath('os-release') - if 'os-release' in condition: - quotes = '"' if 'quotes' in condition else '' - os_release.write_text( - f"testing\nID={quotes}{test_release}{quotes}\nrelease") - if condition != 'lsb_release': + test_release = "testrelease" + lsb_release = "" + os_release = tmp_path.joinpath("os-release") + if "os-release" in condition: + quotes = '"' if "quotes" in condition else "" + os_release.write_text(f"testing\nID={quotes}{test_release}{quotes}\nrelease") + if condition != "lsb_release": lsb_release = 'LSB_RELEASE_PROGRAM="missing_lsb_release"' script = f""" YADM_TEST=1 source {yadm} @@ -21,12 +19,12 @@ def test_query_distro(runner, yadm, tst_distro, tmp_path, condition): OS_RELEASE="{os_release}" query_distro """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'lsb_release': + assert run.err == "" + if condition == "lsb_release": assert run.out.rstrip() == tst_distro - elif 'os-release' in condition: + elif "os-release" in condition: assert run.out.rstrip() == test_release else: - assert run.out.rstrip() == '' + assert run.out.rstrip() == "" diff --git a/test/test_unit_query_distro_family.py b/test/test_unit_query_distro_family.py index bf68319..1935bf6 100644 --- a/test/test_unit_query_distro_family.py +++ b/test/test_unit_query_distro_family.py @@ -2,25 +2,23 @@ import pytest -@pytest.mark.parametrize( - 'condition', ['os-release', 'os-release-quotes', 'missing']) +@pytest.mark.parametrize("condition", ["os-release", "os-release-quotes", "missing"]) def test_query_distro_family(runner, yadm, tmp_path, condition): """Match ID_LIKE when present""" - test_family = 'testfamily' - os_release = tmp_path.joinpath('os-release') - if 'os-release' in condition: - quotes = '"' if 'quotes' in condition else '' - os_release.write_text( - f"testing\nID_LIKE={quotes}{test_family}{quotes}\nfamily") + test_family = "testfamily" + os_release = tmp_path.joinpath("os-release") + if "os-release" in condition: + quotes = '"' if "quotes" in condition else "" + os_release.write_text(f"testing\nID_LIKE={quotes}{test_family}{quotes}\nfamily") script = f""" YADM_TEST=1 source {yadm} OS_RELEASE="{os_release}" query_distro_family """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if 'os-release' in condition: + assert run.err == "" + if "os-release" in condition: assert run.out.rstrip() == test_family else: - assert run.out.rstrip() == '' + assert run.out.rstrip() == "" diff --git a/test/test_unit_record_score.py b/test/test_unit_record_score.py index 78596e1..a82046c 100644 --- a/test/test_unit_record_score.py +++ b/test/test_unit_record_score.py @@ -30,13 +30,13 @@ def test_dont_record_zeros(runner, yadm): record_score "0" "testtgt" "testsrc" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:0\n' in run.out - assert 'SCORES:\n' in run.out - assert 'TARGETS:\n' in run.out - assert 'SOURCES:\n' in run.out + assert run.err == "" + assert "SIZE:0\n" in run.out + assert "SCORES:\n" in run.out + assert "TARGETS:\n" in run.out + assert "SOURCES:\n" in run.out def test_new_scores(runner, yadm): @@ -50,29 +50,29 @@ def test_new_scores(runner, yadm): record_score "4" "tgt_three" "src_three" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:3\n' in run.out - assert 'SCORES:1 2 4\n' in run.out - assert 'TARGETS:tgt_one tgt_two tgt_three\n' in run.out - assert 'SOURCES:src_one src_two src_three\n' in run.out + assert run.err == "" + assert "SIZE:3\n" in run.out + assert "SCORES:1 2 4\n" in run.out + assert "TARGETS:tgt_one tgt_two tgt_three\n" in run.out + assert "SOURCES:src_one src_two src_three\n" in run.out -@pytest.mark.parametrize('difference', ['lower', 'equal', 'higher']) +@pytest.mark.parametrize("difference", ["lower", "equal", "higher"]) def test_existing_scores(runner, yadm, difference): """Test existing scores""" - expected_score = '2' - expected_src = 'existing_src' - if difference == 'lower': - score = '1' - elif difference == 'equal': - score = '2' + expected_score = "2" + expected_src = "existing_src" + if difference == "lower": + score = "1" + elif difference == "equal": + score = "2" else: - score = '4' - expected_score = '4' - expected_src = 'new_src' + score = "4" + expected_score = "4" + expected_src = "new_src" script = f""" YADM_TEST=1 source {yadm} @@ -83,13 +83,13 @@ def test_existing_scores(runner, yadm, difference): record_score "{score}" "testtgt" "new_src" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:1\n' in run.out - assert f'SCORES:{expected_score}\n' in run.out - assert 'TARGETS:testtgt\n' in run.out - assert f'SOURCES:{expected_src}\n' in run.out + assert run.err == "" + assert "SIZE:1\n" in run.out + assert f"SCORES:{expected_score}\n" in run.out + assert "TARGETS:testtgt\n" in run.out + assert f"SOURCES:{expected_src}\n" in run.out def test_existing_template(runner, yadm): @@ -105,19 +105,19 @@ def test_existing_template(runner, yadm): record_score "2" "testtgt" "new_src" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:1\n' in run.out - assert 'SCORES:1\n' in run.out - assert 'TARGETS:testtgt\n' in run.out - assert 'SOURCES:\n' in run.out + assert run.err == "" + assert "SIZE:1\n" in run.out + assert "SCORES:1\n" in run.out + assert "TARGETS:testtgt\n" in run.out + assert "SOURCES:\n" in run.out def test_config_first(runner, yadm): """Verify YADM_CONFIG is always processed first""" - config = 'yadm_config_file' + config = "yadm_config_file" script = f""" YADM_TEST=1 source {yadm} {INIT_VARS} @@ -130,12 +130,12 @@ def test_config_first(runner, yadm): echo "CMD_VALUE:${{alt_template_cmds[@]}}" echo "CMD_INDEX:${{!alt_template_cmds[@]}}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:3\n' in run.out - assert 'SCORES:2 1 3\n' in run.out - assert f'TARGETS:{config} tgt_before tgt_tmp tgt_after\n' in run.out - assert 'SOURCES:src_config src_before src_tmp src_after\n' in run.out - assert 'CMD_VALUE:cmd_tmp\n' in run.out - assert 'CMD_INDEX:2\n' in run.out + assert run.err == "" + assert "SIZE:3\n" in run.out + assert "SCORES:2 1 3\n" in run.out + assert f"TARGETS:{config} tgt_before tgt_tmp tgt_after\n" in run.out + assert "SOURCES:src_config src_before src_tmp src_after\n" in run.out + assert "CMD_VALUE:cmd_tmp\n" in run.out + assert "CMD_INDEX:2\n" in run.out diff --git a/test/test_unit_record_template.py b/test/test_unit_record_template.py index 6bfd012..4f3c3e8 100644 --- a/test/test_unit_record_template.py +++ b/test/test_unit_record_template.py @@ -25,13 +25,13 @@ def test_new_template(runner, yadm): record_template "tgt_three" "cmd_three" "src_three" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:3\n' in run.out - assert 'TARGETS:tgt_one tgt_two tgt_three\n' in run.out - assert 'CMDS:cmd_one cmd_two cmd_three\n' in run.out - assert 'SOURCES:src_one src_two src_three\n' in run.out + assert run.err == "" + assert "SIZE:3\n" in run.out + assert "TARGETS:tgt_one tgt_two tgt_three\n" in run.out + assert "CMDS:cmd_one cmd_two cmd_three\n" in run.out + assert "SOURCES:src_one src_two src_three\n" in run.out def test_existing_template(runner, yadm): @@ -46,10 +46,10 @@ def test_existing_template(runner, yadm): record_template "testtgt" "new_cmd" "new_src" {REPORT_RESULTS} """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert 'SIZE:1\n' in run.out - assert 'TARGETS:testtgt\n' in run.out - assert 'CMDS:new_cmd\n' in run.out - assert 'SOURCES:new_src\n' in run.out + assert run.err == "" + assert "SIZE:1\n" in run.out + assert "TARGETS:testtgt\n" in run.out + assert "CMDS:new_cmd\n" in run.out + assert "SOURCES:new_src\n" in run.out diff --git a/test/test_unit_relative_path.py b/test/test_unit_relative_path.py index f723c84..e0b32f5 100644 --- a/test/test_unit_relative_path.py +++ b/test/test_unit_relative_path.py @@ -3,7 +3,7 @@ import pytest @pytest.mark.parametrize( - 'base,full_path,expected', + "base,full_path,expected", [ ("/A/B/C", "/A", "../.."), ("/A/B/C", "/A/B", ".."), @@ -25,7 +25,7 @@ def test_relative_path(runner, paths, base, full_path, expected): relative_path "{base}" "{full_path}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out.strip() == expected diff --git a/test/test_unit_remove_stale_links.py b/test/test_unit_remove_stale_links.py index 4fcf1a1..f389ed8 100644 --- a/test/test_unit_remove_stale_links.py +++ b/test/test_unit_remove_stale_links.py @@ -4,21 +4,21 @@ import os import pytest -@pytest.mark.parametrize('linked', [True, False]) -@pytest.mark.parametrize('kind', ['file', 'symlink']) +@pytest.mark.parametrize("linked", [True, False]) +@pytest.mark.parametrize("kind", ["file", "symlink"]) def test_remove_stale_links(runner, yadm, tmpdir, kind, linked): """Test remove_stale_links()""" - source_file = tmpdir.join('source_file') - source_file.write('source file', ensure=True) - link = tmpdir.join('link') + source_file = tmpdir.join("source_file") + source_file.write("source file", ensure=True) + link = tmpdir.join("link") - if kind == 'file': - link.write('link file', ensure=True) + if kind == "file": + link.write("link file", ensure=True) else: - os.system(f'ln -s {source_file} {link}') + os.system(f"ln -s {source_file} {link}") - alt_linked = '' + alt_linked = "" if linked: alt_linked = source_file @@ -30,9 +30,9 @@ def test_remove_stale_links(runner, yadm, tmpdir, kind, linked): remove_stale_links """ - run = runner(command=['bash'], inp=script) - assert run.err == '' - if kind == 'symlink' and not linked: - assert f'rm -f {link}' in run.out + run = runner(command=["bash"], inp=script) + assert run.err == "" + if kind == "symlink" and not linked: + assert f"rm -f {link}" in run.out else: - assert run.out == '' + assert run.out == "" diff --git a/test/test_unit_report_invalid_alts.py b/test/test_unit_report_invalid_alts.py index 8730d61..996b1ef 100644 --- a/test/test_unit_report_invalid_alts.py +++ b/test/test_unit_report_invalid_alts.py @@ -2,15 +2,15 @@ import pytest -@pytest.mark.parametrize('valid', [True, False], ids=['valid', 'no_valid']) -@pytest.mark.parametrize('previous', [True, False], ids=['prev', 'no_prev']) +@pytest.mark.parametrize("valid", [True, False], ids=["valid", "no_valid"]) +@pytest.mark.parametrize("previous", [True, False], ids=["prev", "no_prev"]) def test_report_invalid_alts(runner, yadm, valid, previous): """Use report_invalid_alts""" - lwi = '' - alts = 'INVALID_ALT=()' + lwi = "" + alts = "INVALID_ALT=()" if previous: - lwi = 'LEGACY_WARNING_ISSUED=1' + lwi = "LEGACY_WARNING_ISSUED=1" if not valid: alts = 'INVALID_ALT=("file##invalid")' @@ -20,11 +20,11 @@ def test_report_invalid_alts(runner, yadm, valid, previous): {alts} report_invalid_alts """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.out == '' + assert run.out == "" if not valid and not previous: - assert 'WARNING' in run.err - assert 'file##invalid' in run.err + assert "WARNING" in run.err + assert "file##invalid" in run.err else: - assert run.err == '' + assert run.err == "" diff --git a/test/test_unit_score_file.py b/test/test_unit_score_file.py index dd2f0b7..c84fb1e 100644 --- a/test/test_unit_score_file.py +++ b/test/test_unit_score_file.py @@ -2,40 +2,40 @@ import pytest CONDITION = { - 'default': { - 'labels': ['default'], - 'modifier': 0, - }, - 'arch': { - 'labels': ['a', 'arch'], - 'modifier': 1, - }, - 'system': { - 'labels': ['o', 'os'], - 'modifier': 2, - }, - 'distro': { - 'labels': ['d', 'distro'], - 'modifier': 4, - }, - 'distro_family': { - 'labels': ['f', 'distro_family'], - 'modifier': 8, - }, - 'class': { - 'labels': ['c', 'class'], - 'modifier': 16, - }, - 'hostname': { - 'labels': ['h', 'hostname'], - 'modifier': 32, - }, - 'user': { - 'labels': ['u', 'user'], - 'modifier': 64, - }, - } -TEMPLATE_LABELS = ['t', 'template', 'yadm'] + "default": { + "labels": ["default"], + "modifier": 0, + }, + "arch": { + "labels": ["a", "arch"], + "modifier": 1, + }, + "system": { + "labels": ["o", "os"], + "modifier": 2, + }, + "distro": { + "labels": ["d", "distro"], + "modifier": 4, + }, + "distro_family": { + "labels": ["f", "distro_family"], + "modifier": 8, + }, + "class": { + "labels": ["c", "class"], + "modifier": 16, + }, + "hostname": { + "labels": ["h", "hostname"], + "modifier": 32, + }, + "user": { + "labels": ["u", "user"], + "modifier": 64, + }, +} +TEMPLATE_LABELS = ["t", "template", "yadm"] def calculate_score(filename): @@ -43,48 +43,48 @@ def calculate_score(filename): # pylint: disable=too-many-branches score = 0 - _, conditions = filename.split('##', 1) + _, conditions = filename.split("##", 1) - for condition in conditions.split(','): + for condition in conditions.split(","): label = condition value = None - if '.' in condition: - label, value = condition.split('.', 1) - if label in CONDITION['default']['labels']: + if "." in condition: + label, value = condition.split(".", 1) + if label in CONDITION["default"]["labels"]: score += 1000 - elif label in CONDITION['arch']['labels']: - if value == 'testarch': - score += 1000 + CONDITION['arch']['modifier'] + elif label in CONDITION["arch"]["labels"]: + if value == "testarch": + score += 1000 + CONDITION["arch"]["modifier"] else: score = 0 break - elif label in CONDITION['system']['labels']: - if value == 'testsystem': - score += 1000 + CONDITION['system']['modifier'] + elif label in CONDITION["system"]["labels"]: + if value == "testsystem": + score += 1000 + CONDITION["system"]["modifier"] else: score = 0 break - elif label in CONDITION['distro']['labels']: - if value == 'testdistro': - score += 1000 + CONDITION['distro']['modifier'] + elif label in CONDITION["distro"]["labels"]: + if value == "testdistro": + score += 1000 + CONDITION["distro"]["modifier"] else: score = 0 break - elif label in CONDITION['class']['labels']: - if value == 'testclass': - score += 1000 + CONDITION['class']['modifier'] + elif label in CONDITION["class"]["labels"]: + if value == "testclass": + score += 1000 + CONDITION["class"]["modifier"] else: score = 0 break - elif label in CONDITION['hostname']['labels']: - if value == 'testhost': - score += 1000 + CONDITION['hostname']['modifier'] + elif label in CONDITION["hostname"]["labels"]: + if value == "testhost": + score += 1000 + CONDITION["hostname"]["modifier"] else: score = 0 break - elif label in CONDITION['user']['labels']: - if value == 'testuser': - score += 1000 + CONDITION['user']['modifier'] + elif label in CONDITION["user"]["labels"]: + if value == "testuser": + score += 1000 + CONDITION["user"]["modifier"] else: score = 0 break @@ -94,111 +94,85 @@ def calculate_score(filename): return score -@pytest.mark.parametrize( - 'default', ['default', None], ids=['default', 'no-default']) -@pytest.mark.parametrize( - 'arch', ['arch', None], ids=['arch', 'no-arch']) -@pytest.mark.parametrize( - 'system', ['system', None], ids=['system', 'no-system']) -@pytest.mark.parametrize( - 'distro', ['distro', None], ids=['distro', 'no-distro']) -@pytest.mark.parametrize( - 'cla', ['class', None], ids=['class', 'no-class']) -@pytest.mark.parametrize( - 'host', ['hostname', None], ids=['hostname', 'no-host']) -@pytest.mark.parametrize( - 'user', ['user', None], ids=['user', 'no-user']) -def test_score_values( - runner, yadm, default, arch, system, distro, cla, host, user): +@pytest.mark.parametrize("default", ["default", None], ids=["default", "no-default"]) +@pytest.mark.parametrize("arch", ["arch", None], ids=["arch", "no-arch"]) +@pytest.mark.parametrize("system", ["system", None], ids=["system", "no-system"]) +@pytest.mark.parametrize("distro", ["distro", None], ids=["distro", "no-distro"]) +@pytest.mark.parametrize("cla", ["class", None], ids=["class", "no-class"]) +@pytest.mark.parametrize("host", ["hostname", None], ids=["hostname", "no-host"]) +@pytest.mark.parametrize("user", ["user", None], ids=["user", "no-user"]) +def test_score_values(runner, yadm, default, arch, system, distro, cla, host, user): """Test score results""" # pylint: disable=too-many-branches - local_class = 'testclass' - local_arch = 'testarch' - local_system = 'testsystem' - local_distro = 'testdistro' - local_host = 'testhost' - local_user = 'testuser' - filenames = {'filename##': 0} + local_class = "testclass" + local_arch = "testarch" + local_system = "testsystem" + local_distro = "testdistro" + local_host = "testhost" + local_user = "testuser" + filenames = {"filename##": 0} if default: for filename in list(filenames): - for label in CONDITION[default]['labels']: + for label in CONDITION[default]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' + if not newfile.endswith("##"): + newfile += "," newfile += label filenames[newfile] = calculate_score(newfile) if arch: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[arch]['labels']: + for label in CONDITION[arch]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_arch if match else 'badarch' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_arch if match else "badarch"]) filenames[newfile] = calculate_score(newfile) if system: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[system]['labels']: + for label in CONDITION[system]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_system if match else 'badsys' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_system if match else "badsys"]) filenames[newfile] = calculate_score(newfile) if distro: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[distro]['labels']: + for label in CONDITION[distro]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_distro if match else 'baddistro' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_distro if match else "baddistro"]) filenames[newfile] = calculate_score(newfile) if cla: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[cla]['labels']: + for label in CONDITION[cla]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_class if match else 'badclass' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_class if match else "badclass"]) filenames[newfile] = calculate_score(newfile) if host: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[host]['labels']: + for label in CONDITION[host]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_host if match else 'badhost' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_host if match else "badhost"]) filenames[newfile] = calculate_score(newfile) if user: for filename in list(filenames): for match in [True, False]: - for label in CONDITION[user]['labels']: + for label in CONDITION[user]["labels"]: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([ - label, - local_user if match else 'baduser' - ]) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, local_user if match else "baduser"]) filenames[newfile] = calculate_score(newfile) script = f""" @@ -212,29 +186,29 @@ def test_score_values( local_host={local_host} local_user={local_user} """ - expected = '' + expected = "" for filename, score in filenames.items(): script += f""" score_file "{filename}" echo "{filename}" echo "$score" """ - expected += filename + '\n' - expected += str(score) + '\n' - run = runner(command=['bash'], inp=script) + expected += filename + "\n" + expected += str(score) + "\n" + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out == expected -@pytest.mark.parametrize('ext', [None, 'e', 'extension']) +@pytest.mark.parametrize("ext", [None, "e", "extension"]) def test_extensions(runner, yadm, ext): """Verify extensions do not effect scores""" - local_user = 'testuser' - filename = f'filename##u.{local_user}' + local_user = "testuser" + filename = f"filename##u.{local_user}" if ext: - filename += f',{ext}.xyz' - expected = '' + filename += f",{ext}.xyz" + expected = "" script = f""" YADM_TEST=1 source {yadm} score=0 @@ -243,28 +217,28 @@ def test_extensions(runner, yadm, ext): echo "$score" """ expected = f'{1000 + CONDITION["user"]["modifier"]}\n' - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out == expected def test_score_values_templates(runner, yadm): """Test score results""" - local_class = 'testclass' - local_arch = 'arch' - local_system = 'testsystem' - local_distro = 'testdistro' - local_host = 'testhost' - local_user = 'testuser' - filenames = {'filename##': 0} + local_class = "testclass" + local_arch = "arch" + local_system = "testsystem" + local_distro = "testdistro" + local_host = "testhost" + local_user = "testuser" + filenames = {"filename##": 0} for filename in list(filenames): for label in TEMPLATE_LABELS: newfile = filename - if not newfile.endswith('##'): - newfile += ',' - newfile += '.'.join([label, 'testtemplate']) + if not newfile.endswith("##"): + newfile += "," + newfile += ".".join([label, "testtemplate"]) filenames[newfile] = calculate_score(newfile) script = f""" @@ -277,33 +251,30 @@ def test_score_values_templates(runner, yadm): local_host={local_host} local_user={local_user} """ - expected = '' + expected = "" for filename, score in filenames.items(): script += f""" score_file "{filename}" echo "{filename}" echo "$score" """ - expected += filename + '\n' - expected += str(score) + '\n' - run = runner(command=['bash'], inp=script) + expected += filename + "\n" + expected += str(score) + "\n" + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out == expected -@pytest.mark.parametrize( - 'cmd_generated', - [True, False], - ids=['supported-template', 'unsupported-template']) +@pytest.mark.parametrize("cmd_generated", [True, False], ids=["supported-template", "unsupported-template"]) def test_template_recording(runner, yadm, cmd_generated): """Template should be recorded if choose_template_cmd outputs a command""" - mock = 'function choose_template_cmd() { return; }' - expected = '' + mock = "function choose_template_cmd() { return; }" + expected = "" if cmd_generated: mock = 'function choose_template_cmd() { echo "test_cmd"; }' - expected = 'template recorded' + expected = "template recorded" script = f""" YADM_TEST=1 source {yadm} @@ -311,24 +282,24 @@ def test_template_recording(runner, yadm, cmd_generated): {mock} score_file "testfile##template.kind" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out.rstrip() == expected def test_underscores_in_distro_and_family(runner, yadm): """Test replacing spaces in distro / distro_family with underscores""" - local_distro = 'test distro' - local_distro_family = 'test family' + local_distro = "test distro" + local_distro_family = "test family" filenames = { - 'filename##distro.test distro': 1004, - 'filename##distro.test-distro': 0, - 'filename##distro.test_distro': 1004, - 'filename##distro_family.test family': 1008, - 'filename##distro_family.test-family': 0, - 'filename##distro_family.test_family': 1008, - } + "filename##distro.test distro": 1004, + "filename##distro.test-distro": 0, + "filename##distro.test_distro": 1004, + "filename##distro_family.test family": 1008, + "filename##distro_family.test-family": 0, + "filename##distro_family.test_family": 1008, + } script = f""" YADM_TEST=1 source {yadm} @@ -336,16 +307,16 @@ def test_underscores_in_distro_and_family(runner, yadm): local_distro="{local_distro}" local_distro_family="{local_distro_family}" """ - expected = '' + expected = "" for filename, score in filenames.items(): script += f""" score_file "{filename}" echo "{filename}" echo "$score" """ - expected += filename + '\n' - expected += str(score) + '\n' - run = runner(command=['bash'], inp=script) + expected += filename + "\n" + expected += str(score) + "\n" + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert run.out == expected diff --git a/test/test_unit_set_local_alt_values.py b/test/test_unit_set_local_alt_values.py index 449850c..fa5749d 100644 --- a/test/test_unit_set_local_alt_values.py +++ b/test/test_unit_set_local_alt_values.py @@ -4,26 +4,26 @@ import utils @pytest.mark.parametrize( - 'override', [ + "override", + [ False, - 'class', - 'arch', - 'os', - 'hostname', - 'user', - ], + "class", + "arch", + "os", + "hostname", + "user", + ], ids=[ - 'no-override', - 'override-class', - 'override-arch', - 'override-os', - 'override-hostname', - 'override-user', - ] - ) -@pytest.mark.usefixtures('ds1_copy') -def test_set_local_alt_values( - runner, yadm, paths, tst_arch, tst_sys, tst_host, tst_user, override): + "no-override", + "override-class", + "override-arch", + "override-os", + "override-hostname", + "override-user", + ], +) +@pytest.mark.usefixtures("ds1_copy") +def test_set_local_alt_values(runner, yadm, paths, tst_arch, tst_sys, tst_host, tst_user, override): """Use issue_legacy_path_warning""" script = f""" YADM_TEST=1 source {yadm} && @@ -37,37 +37,37 @@ def test_set_local_alt_values( echo "user='$local_user'" """ - if override == 'class': - utils.set_local(paths, override, 'first') - utils.set_local(paths, override, 'override', add=True) + if override == "class": + utils.set_local(paths, override, "first") + utils.set_local(paths, override, "override", add=True) elif override: - utils.set_local(paths, override, 'override') + utils.set_local(paths, override, "override") - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" - if override == 'class': + if override == "class": assert "class='override'" in run.out else: assert "class=''" in run.out - if override == 'arch': + if override == "arch": assert "arch='override'" in run.out else: assert f"arch='{tst_arch}'" in run.out - if override == 'os': + if override == "os": assert "os='override'" in run.out else: assert f"os='{tst_sys}'" in run.out - if override == 'hostname': + if override == "hostname": assert "host='override'" in run.out else: assert f"host='{tst_host}'" in run.out - if override == 'user': + if override == "user": assert "user='override'" in run.out else: assert f"user='{tst_user}'" in run.out @@ -85,8 +85,8 @@ def test_distro_and_family(runner, yadm): echo "distro='$local_distro'" echo "distro_family='$local_distro_family'" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert "distro='testdistro'" in run.out assert "distro_family='testfamily'" in run.out diff --git a/test/test_unit_set_os.py b/test/test_unit_set_os.py index 936986f..ac61de2 100644 --- a/test/test_unit_set_os.py +++ b/test/test_unit_set_os.py @@ -4,25 +4,27 @@ import pytest @pytest.mark.parametrize( - 'proc_value, expected_os', [ - ('missing', 'uname'), - ('has microsoft inside', 'WSL'), # case insensitive - ('has Microsoft inside', 'WSL'), # case insensitive - ('another value', 'uname'), - ], ids=[ - '/proc/version missing', - '/proc/version includes ms', - '/proc/version excludes Ms', - 'another value', - ]) -def test_set_operating_system( - runner, paths, tst_sys, proc_value, expected_os): + "proc_value, expected_os", + [ + ("missing", "uname"), + ("has microsoft inside", "WSL"), # case insensitive + ("has Microsoft inside", "WSL"), # case insensitive + ("another value", "uname"), + ], + ids=[ + "/proc/version missing", + "/proc/version includes ms", + "/proc/version excludes Ms", + "another value", + ], +) +def test_set_operating_system(runner, paths, tst_sys, proc_value, expected_os): """Run set_operating_system and test result""" # Normally /proc/version (set in PROC_VERSION) is inspected to identify # WSL. During testing, we will override that value. - proc_version = paths.root.join('proc_version') - if proc_value != 'missing': + proc_version = paths.root.join("proc_version") + if proc_value != "missing": proc_version.write(proc_value) script = f""" YADM_TEST=1 source {paths.pgm} @@ -30,9 +32,9 @@ def test_set_operating_system( set_operating_system echo $OPERATING_SYSTEM """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if expected_os == 'uname': + assert run.err == "" + if expected_os == "uname": expected_os = tst_sys assert run.out.rstrip() == expected_os diff --git a/test/test_unit_set_yadm_dir.py b/test/test_unit_set_yadm_dir.py index 32af8bf..b56c98d 100644 --- a/test/test_unit_set_yadm_dir.py +++ b/test/test_unit_set_yadm_dir.py @@ -3,25 +3,20 @@ import pytest @pytest.mark.parametrize( - 'condition', [ - 'basic', - 'override', - 'override_data', - 'xdg_config_home', - 'xdg_data_home' - ], - ) + "condition", + ["basic", "override", "override_data", "xdg_config_home", "xdg_data_home"], +) def test_set_yadm_dirs(runner, yadm, condition): """Test set_yadm_dirs""" - setup = '' - if condition == 'override': - setup = 'YADM_DIR=/override' - elif condition == 'override_data': - setup = 'YADM_DATA=/override' - elif condition == 'xdg_config_home': - setup = 'XDG_CONFIG_HOME=/xdg' - elif condition == 'xdg_data_home': - setup = 'XDG_DATA_HOME=/xdg' + setup = "" + if condition == "override": + setup = "YADM_DIR=/override" + elif condition == "override_data": + setup = "YADM_DATA=/override" + elif condition == "xdg_config_home": + setup = "XDG_CONFIG_HOME=/xdg" + elif condition == "xdg_data_home": + setup = "XDG_DATA_HOME=/xdg" script = f""" HOME=/testhome YADM_TEST=1 source {yadm} @@ -32,17 +27,17 @@ def test_set_yadm_dirs(runner, yadm, condition): echo "YADM_DIR=$YADM_DIR" echo "YADM_DATA=$YADM_DATA" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'basic': - assert 'YADM_DIR=/testhome/.config/yadm' in run.out - assert 'YADM_DATA=/testhome/.local/share/yadm' in run.out - elif condition == 'override': - assert 'YADM_DIR=/override' in run.out - elif condition == 'override_data': - assert 'YADM_DATA=/override' in run.out - elif condition == 'xdg_config_home': - assert 'YADM_DIR=/xdg/yadm' in run.out - elif condition == 'xdg_data_home': - assert 'YADM_DATA=/xdg/yadm' in run.out + assert run.err == "" + if condition == "basic": + assert "YADM_DIR=/testhome/.config/yadm" in run.out + assert "YADM_DATA=/testhome/.local/share/yadm" in run.out + elif condition == "override": + assert "YADM_DIR=/override" in run.out + elif condition == "override_data": + assert "YADM_DATA=/override" in run.out + elif condition == "xdg_config_home": + assert "YADM_DIR=/xdg/yadm" in run.out + elif condition == "xdg_data_home": + assert "YADM_DATA=/xdg/yadm" in run.out diff --git a/test/test_unit_template_default.py b/test/test_unit_template_default.py index 729784e..7243877 100644 --- a/test/test_unit_template_default.py +++ b/test/test_unit_template_default.py @@ -12,7 +12,7 @@ LOCAL_HOST = "default_Test+@-!^Host" LOCAL_USER = "default_Test+@-!^User" LOCAL_DISTRO = "default_Test+@-!^Distro" LOCAL_DISTRO_FAMILY = "default_Test+@-!^Family" -TEMPLATE = f''' +TEMPLATE = f""" start of template default class = >{{{{yadm.class}}}}< default arch = >{{{{yadm.arch}}}}< @@ -98,8 +98,8 @@ Included section for distro_family = \ wrong family 2 {{% endif %}} end of template -''' -EXPECTED = f''' +""" +EXPECTED = f""" start of template default class = >{LOCAL_CLASS}< default arch = >{LOCAL_ARCH}< @@ -122,17 +122,17 @@ Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again) Included section for distro_family = \ {LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again) end of template -''' +""" -INCLUDE_BASIC = 'basic\n' -INCLUDE_VARIABLES = '''\ +INCLUDE_BASIC = "basic\n" +INCLUDE_VARIABLES = """\ included <{{ yadm.class }}> file empty line above -''' -INCLUDE_NESTED = 'no newline at the end' +""" +INCLUDE_NESTED = "no newline at the end" -TEMPLATE_INCLUDE = '''\ +TEMPLATE_INCLUDE = """\ The first line {% include empty %} An empty file removes the line above @@ -141,8 +141,8 @@ An empty file removes the line above {% include dir/nested %} Include basic again: {% include basic %} -''' -EXPECTED_INCLUDE = f'''\ +""" +EXPECTED_INCLUDE = f"""\ The first line An empty file removes the line above basic @@ -152,21 +152,21 @@ empty line above no newline at the end Include basic again: basic -''' +""" def test_template_default(runner, yadm, tmpdir): """Test template_default""" - input_file = tmpdir.join('input') + input_file = tmpdir.join("input") input_file.write(TEMPLATE, ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") # ensure overwrite works when file exists as read-only (there is some # special processing when this is encountered because some environments do # not properly overwrite read-only files) - output_file.write('existing') + output_file.write("existing") output_file.chmod(0o400) script = f""" @@ -182,9 +182,9 @@ def test_template_default(runner, yadm, tmpdir): local_distro_family="{LOCAL_DISTRO_FAMILY}" template_default "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read() == EXPECTED assert os.stat(output_file).st_mode == os.stat(input_file).st_mode @@ -192,19 +192,19 @@ def test_template_default(runner, yadm, tmpdir): def test_source(runner, yadm, tmpdir): """Test yadm.source""" - input_file = tmpdir.join('input') - input_file.write('{{yadm.source}}', ensure=True) + input_file = tmpdir.join("input") + input_file.write("{{yadm.source}}", ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") script = f""" YADM_TEST=1 source {yadm} set_awk template_default "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read().strip() == str(input_file) assert os.stat(output_file).st_mode == os.stat(input_file).st_mode @@ -212,22 +212,22 @@ def test_source(runner, yadm, tmpdir): def test_include(runner, yadm, tmpdir): """Test include""" - empty_file = tmpdir.join('empty') - empty_file.write('', ensure=True) + empty_file = tmpdir.join("empty") + empty_file.write("", ensure=True) - basic_file = tmpdir.join('basic') + basic_file = tmpdir.join("basic") basic_file.write(INCLUDE_BASIC) - variables_file = tmpdir.join(f'variables.{LOCAL_SYSTEM}') + variables_file = tmpdir.join(f"variables.{LOCAL_SYSTEM}") variables_file.write(INCLUDE_VARIABLES) - nested_file = tmpdir.join('dir').join('nested') + nested_file = tmpdir.join("dir").join("nested") nested_file.write(INCLUDE_NESTED, ensure=True) - input_file = tmpdir.join('input') + input_file = tmpdir.join("input") input_file.write(TEMPLATE_INCLUDE) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") script = f""" YADM_TEST=1 source {yadm} @@ -236,9 +236,9 @@ def test_include(runner, yadm, tmpdir): local_system="{LOCAL_SYSTEM}" template_default "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read() == EXPECTED_INCLUDE assert os.stat(output_file).st_mode == os.stat(input_file).st_mode @@ -246,17 +246,17 @@ def test_include(runner, yadm, tmpdir): def test_env(runner, yadm, tmpdir): """Test env""" - input_file = tmpdir.join('input') - input_file.write('{{env.PWD}}', ensure=True) + input_file = tmpdir.join("input") + input_file.write("{{env.PWD}}", ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") script = f""" YADM_TEST=1 source {yadm} set_awk template_default "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - assert output_file.read().strip() == os.environ['PWD'] + assert run.err == "" + assert output_file.read().strip() == os.environ["PWD"] diff --git a/test/test_unit_template_esh.py b/test/test_unit_template_esh.py index 7f2f2b9..2c91c20 100644 --- a/test/test_unit_template_esh.py +++ b/test/test_unit_template_esh.py @@ -11,7 +11,7 @@ LOCAL_HOST = "esh_Test+@-!^Host" LOCAL_USER = "esh_Test+@-!^User" LOCAL_DISTRO = "esh_Test+@-!^Distro" LOCAL_DISTRO_FAMILY = "esh_Test+@-!^Family" -TEMPLATE = f''' +TEMPLATE = f""" start of template esh class = ><%=$YADM_CLASS%>< esh arch = ><%=$YADM_ARCH%>< @@ -90,8 +90,8 @@ Included esh section for distro_family = \ wrong family 2 <% fi -%> end of template -''' -EXPECTED = f''' +""" +EXPECTED = f""" start of template esh class = >{LOCAL_CLASS}< esh arch = >{LOCAL_ARCH}< @@ -111,21 +111,22 @@ Included esh section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again) Included esh section for distro_family = \ {LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again) end of template -''' +""" def test_template_esh(runner, yadm, tmpdir): """Test processing by esh""" + # pylint: disable=duplicate-code - input_file = tmpdir.join('input') + input_file = tmpdir.join("input") input_file.write(TEMPLATE, ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") # ensure overwrite works when file exists as read-only (there is some # special processing when this is encountered because some environments do # not properly overwrite read-only files) - output_file.write('existing') + output_file.write("existing") output_file.chmod(0o400) script = f""" @@ -140,9 +141,9 @@ def test_template_esh(runner, yadm, tmpdir): local_distro_family="{LOCAL_DISTRO_FAMILY}" template_esh "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read().strip() == str(EXPECTED).strip() assert os.stat(output_file).st_mode == os.stat(input_file).st_mode @@ -150,17 +151,17 @@ def test_template_esh(runner, yadm, tmpdir): def test_source(runner, yadm, tmpdir): """Test YADM_SOURCE""" - input_file = tmpdir.join('input') - input_file.write('<%= $YADM_SOURCE %>', ensure=True) + input_file = tmpdir.join("input") + input_file.write("<%= $YADM_SOURCE %>", ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") script = f""" YADM_TEST=1 source {yadm} template_esh "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read().strip() == str(input_file) assert os.stat(output_file).st_mode == os.stat(input_file).st_mode diff --git a/test/test_unit_template_j2.py b/test/test_unit_template_j2.py index 84afc2d..750ee8c 100644 --- a/test/test_unit_template_j2.py +++ b/test/test_unit_template_j2.py @@ -13,7 +13,7 @@ LOCAL_HOST = "j2_Test+@-!^Host" LOCAL_USER = "j2_Test+@-!^User" LOCAL_DISTRO = "j2_Test+@-!^Distro" LOCAL_DISTRO_FAMILY = "j2_Test+@-!^Family" -TEMPLATE = f''' +TEMPLATE = f""" start of template j2 class = >{{{{YADM_CLASS}}}}< j2 arch = >{{{{YADM_ARCH}}}}< @@ -94,8 +94,8 @@ Included j2 section for distro_family = \ wrong family 2 {{%- endif %}} end of template -''' -EXPECTED = f''' +""" +EXPECTED = f""" start of template j2 class = >{LOCAL_CLASS}< j2 arch = >{LOCAL_ARCH}< @@ -116,22 +116,23 @@ Included j2 section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again) Included j2 section for distro_family = \ {LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again) end of template -''' +""" -@pytest.mark.parametrize('processor', ('j2cli', 'envtpl')) +@pytest.mark.parametrize("processor", ("j2cli", "envtpl")) def test_template_j2(runner, yadm, tmpdir, processor): """Test processing by j2cli & envtpl""" + # pylint: disable=duplicate-code - input_file = tmpdir.join('input') + input_file = tmpdir.join("input") input_file.write(TEMPLATE, ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") # ensure overwrite works when file exists as read-only (there is some # special processing when this is encountered because some environments do # not properly overwrite read-only files) - output_file.write('existing') + output_file.write("existing") output_file.chmod(0o400) script = f""" @@ -146,28 +147,28 @@ def test_template_j2(runner, yadm, tmpdir, processor): local_distro_family="{LOCAL_DISTRO_FAMILY}" template_{processor} "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read() == EXPECTED assert os.stat(output_file).st_mode == os.stat(input_file).st_mode -@pytest.mark.parametrize('processor', ('j2cli', 'envtpl')) +@pytest.mark.parametrize("processor", ("j2cli", "envtpl")) def test_source(runner, yadm, tmpdir, processor): """Test YADM_SOURCE""" - input_file = tmpdir.join('input') - input_file.write('{{YADM_SOURCE}}', ensure=True) + input_file = tmpdir.join("input") + input_file.write("{{YADM_SOURCE}}", ensure=True) input_file.chmod(FILE_MODE) - output_file = tmpdir.join('output') + output_file = tmpdir.join("output") script = f""" YADM_TEST=1 source {yadm} template_{processor} "{input_file}" "{output_file}" """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' + assert run.err == "" assert output_file.read().strip() == str(input_file) assert os.stat(output_file).st_mode == os.stat(input_file).st_mode diff --git a/test/test_unit_upgrade.py b/test/test_unit_upgrade.py index 3463740..cf4f6f4 100644 --- a/test/test_unit_upgrade.py +++ b/test/test_unit_upgrade.py @@ -2,21 +2,21 @@ import pytest -@pytest.mark.parametrize('condition', ['override', 'equal', 'existing_repo']) +@pytest.mark.parametrize("condition", ["override", "equal", "existing_repo"]) def test_upgrade_errors(tmpdir, runner, yadm, condition): """Test upgrade() error conditions""" - home = tmpdir.mkdir('home') - yadm_dir = home.join('.config/yadm') - yadm_data = home.join('.local/share/yadm') - override = '' - if condition == 'override': - override = 'override' - if condition == 'equal': + home = tmpdir.mkdir("home") + yadm_dir = home.join(".config/yadm") + yadm_data = home.join(".local/share/yadm") + override = "" + if condition == "override": + override = "override" + if condition == "equal": yadm_data = yadm_dir - if condition == 'existing_repo': - yadm_dir.ensure_dir('repo.git') - yadm_data.ensure_dir('repo.git') + if condition == "existing_repo": + yadm_dir.ensure_dir("repo.git") + yadm_data.ensure_dir("repo.git") script = f""" YADM_TEST=1 source {yadm} @@ -27,17 +27,16 @@ def test_upgrade_errors(tmpdir, runner, yadm, condition): YADM_OVERRIDE_REPO="{override}" upgrade """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.failure - assert 'Unable to upgrade' in run.err - if condition in ['override', 'equal']: - assert 'Paths have been overridden' in run.err - elif condition == 'existing_repo': - assert 'already exists' in run.err + assert "Unable to upgrade" in run.err + if condition in ["override", "equal"]: + assert "Paths have been overridden" in run.err + elif condition == "existing_repo": + assert "already exists" in run.err -@pytest.mark.parametrize( - 'condition', ['no-paths', 'untracked', 'tracked', 'submodules']) +@pytest.mark.parametrize("condition", ["no-paths", "untracked", "tracked", "submodules"]) def test_upgrade(tmpdir, runner, yadm, condition): """Test upgrade() @@ -45,21 +44,21 @@ def test_upgrade(tmpdir, runner, yadm, condition): mock for git. echo will return true, simulating a positive result from "git ls-files". Also echo will report the parameters for "git mv". """ - legacy_paths = ('config', 'encrypt', 'bootstrap', 'hooks/pre_cmd') - home = tmpdir.mkdir('home') - yadm_dir = home.join('.config/yadm') - yadm_data = home.join('.local/share/yadm') - yadm_legacy = home.join('.yadm') + legacy_paths = ("config", "encrypt", "bootstrap", "hooks/pre_cmd") + home = tmpdir.mkdir("home") + yadm_dir = home.join(".config/yadm") + yadm_data = home.join(".local/share/yadm") + yadm_legacy = home.join(".yadm") - if condition != 'no-paths': - yadm_dir.join('repo.git/config').write('test-repo', ensure=True) - yadm_dir.join('files.gpg').write('files.gpg', ensure=True) + if condition != "no-paths": + yadm_dir.join("repo.git/config").write("test-repo", ensure=True) + yadm_dir.join("files.gpg").write("files.gpg", ensure=True) for path in legacy_paths: yadm_legacy.join(path).write(path, ensure=True) mock_git = "" - if condition != 'no-paths': - mock_git = f''' + if condition != "no-paths": + mock_git = f""" function git() {{ echo "$@" if [[ "$*" = *"submodule status" ]]; then @@ -71,7 +70,7 @@ def test_upgrade(tmpdir, runner, yadm, condition): fi return 0 }} - ''' + """ script = f""" YADM_TEST=1 source {yadm} @@ -85,38 +84,30 @@ def test_upgrade(tmpdir, runner, yadm, condition): function cd {{ echo "$@";}} upgrade """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success - assert run.err == '' - if condition == 'no-paths': - assert 'Upgrade is not necessary' in run.out + assert run.err == "" + if condition == "no-paths": + assert "Upgrade is not necessary" in run.out else: - for (lpath, npath) in [ - ('repo.git', 'repo.git'), ('files.gpg', 'archive')]: - expected = ( - f'Moving {yadm_dir.join(lpath)} ' - f'to {yadm_data.join(npath)}') + for lpath, npath in [("repo.git", "repo.git"), ("files.gpg", "archive")]: + expected = f"Moving {yadm_dir.join(lpath)} " f"to {yadm_data.join(npath)}" assert expected in run.out for path in legacy_paths: - expected = ( - f'Moving {yadm_legacy.join(path)} ' - f'to {yadm_dir.join(path)}') + expected = f"Moving {yadm_legacy.join(path)} " f"to {yadm_dir.join(path)}" assert expected in run.out - if condition == 'untracked': - assert 'test-repo' in yadm_data.join('repo.git/config').read() - assert 'files.gpg' in yadm_data.join('archive').read() + if condition == "untracked": + assert "test-repo" in yadm_data.join("repo.git/config").read() + assert "files.gpg" in yadm_data.join("archive").read() for path in legacy_paths: assert path in yadm_dir.join(path).read() - elif condition in ['tracked', 'submodules']: - expected = ( - f'mv {yadm_dir.join("files.gpg")} ' - f'{yadm_data.join("archive")}') + elif condition in ["tracked", "submodules"]: + expected = f'mv {yadm_dir.join("files.gpg")} ' f'{yadm_data.join("archive")}' assert expected in run.out - assert 'files tracked by yadm have been renamed' in run.out - if condition == 'submodules': - assert 'submodule deinit -- mymodule' in run.out - assert 'submodule update --init --recursive -- mymodule' \ - in run.out + assert "files tracked by yadm have been renamed" in run.out + if condition == "submodules": + assert "submodule deinit -- mymodule" in run.out + assert "submodule update --init --recursive -- mymodule" in run.out else: - assert 'submodule deinit' not in run.out - assert 'submodule update --init --recursive' not in run.out + assert "submodule deinit" not in run.out + assert "submodule update --init --recursive" not in run.out diff --git a/test/test_unit_x_program.py b/test/test_unit_x_program.py index 883c9af..33b8473 100644 --- a/test/test_unit_x_program.py +++ b/test/test_unit_x_program.py @@ -6,24 +6,25 @@ import pytest @pytest.mark.parametrize( - 'executable, success, value, match', [ - (None, True, 'program', None), - ('cat', True, 'cat', None), - ('badprogram', False, None, 'badprogram'), - ], ids=[ - 'executable missing', - 'valid alternative', - 'invalid alternative', - ]) -@pytest.mark.parametrize('program', ['git', 'gpg']) -def test_x_program( - runner, yadm_cmd, paths, program, executable, success, value, match): + "executable, success, value, match", + [ + (None, True, "program", None), + ("cat", True, "cat", None), + ("badprogram", False, None, "badprogram"), + ], + ids=[ + "executable missing", + "valid alternative", + "invalid alternative", + ], +) +@pytest.mark.parametrize("program", ["git", "gpg"]) +def test_x_program(runner, yadm_cmd, paths, program, executable, success, value, match): """Set yadm.X-program, and test result of require_X""" # set configuration if executable: - os.system(' '.join(yadm_cmd( - 'config', f'yadm.{program}-program', executable))) + os.system(" ".join(yadm_cmd("config", f"yadm.{program}-program", executable))) # test require_[git,gpg] script = f""" @@ -33,11 +34,11 @@ def test_x_program( require_{program} echo ${program.upper()}_PROGRAM """ - run = runner(command=['bash'], inp=script) + run = runner(command=["bash"], inp=script) assert run.success == success # [GIT,GPG]_PROGRAM set correctly - if value == 'program': + if value == "program": assert run.out.rstrip() == program elif value: assert run.out.rstrip() == value @@ -46,4 +47,4 @@ def test_x_program( if match: assert match in run.err else: - assert run.err == '' + assert run.err == "" diff --git a/test/test_upgrade.py b/test/test_upgrade.py index 1805882..8ab1e94 100644 --- a/test/test_upgrade.py +++ b/test/test_upgrade.py @@ -6,129 +6,126 @@ import pytest @pytest.mark.parametrize( - 'versions', [ - ('1.12.0', '2.5.0'), - ('1.12.0',), - ('2.5.0',), - ], ids=[ - '1.12.0 -> 2.5.0 -> latest', - '1.12.0 -> latest', - '2.5.0 -> latest', - ]) -@pytest.mark.parametrize( - 'submodule', [False, True], - ids=['no submodule', 'with submodules']) + "versions", + [ + ("1.12.0", "2.5.0"), + ("1.12.0",), + ("2.5.0",), + ], + ids=[ + "1.12.0 -> 2.5.0 -> latest", + "1.12.0 -> latest", + "2.5.0 -> latest", + ], +) +@pytest.mark.parametrize("submodule", [False, True], ids=["no submodule", "with submodules"]) def test_upgrade(tmpdir, runner, versions, submodule): """Upgrade tests""" # pylint: disable=too-many-statements - home = tmpdir.mkdir('HOME') - env = {'HOME': str(home)} - runner(['git', 'config', '--global', 'init.defaultBranch', 'master'], - env=env) - runner(['git', 'config', '--global', 'protocol.file.allow', 'always'], - env=env) + home = tmpdir.mkdir("HOME") + env = {"HOME": str(home)} + runner(["git", "config", "--global", "init.defaultBranch", "master"], env=env) + runner(["git", "config", "--global", "protocol.file.allow", "always"], env=env) if submodule: - ext_repo = tmpdir.mkdir('ext_repo') - ext_repo.join('afile').write('some data') + ext_repo = tmpdir.mkdir("ext_repo") + ext_repo.join("afile").write("some data") - for cmd in (('init',), ('add', 'afile'), ('commit', '-m', 'test')): - run = runner(['git', '-C', str(ext_repo), *cmd]) + for cmd in (("init",), ("add", "afile"), ("commit", "-m", "test")): + run = runner(["git", "-C", str(ext_repo), *cmd]) assert run.success - os.environ.pop('XDG_CONFIG_HOME', None) - os.environ.pop('XDG_DATA_HOME', None) + os.environ.pop("XDG_CONFIG_HOME", None) + os.environ.pop("XDG_DATA_HOME", None) def run_version(version, *args, check_stderr=True): - yadm = f'yadm-{version}' if version else '/yadm/yadm' + yadm = f"yadm-{version}" if version else "/yadm/yadm" run = runner([yadm, *args], shell=True, cwd=str(home), env=env) assert run.success if check_stderr: - assert run.err == '' + assert run.err == "" return run # Initialize the repo with the first version first = versions[0] - run_version(first, 'init') + run_version(first, "init") - home.join('file').write('some data') - run_version(first, 'add', 'file') - run_version(first, 'commit', '-m', '"First commit"') + home.join("file").write("some data") + run_version(first, "add", "file") + run_version(first, "commit", "-m", '"First commit"') if submodule: # When upgrading via 2.5.0 we can't have a submodule that's been added # after being cloned as 2.5.0 fails the upgrade in that case. - can_upgrade_cloned_submodule = '2.5.0' not in versions[1:] + can_upgrade_cloned_submodule = "2.5.0" not in versions[1:] if can_upgrade_cloned_submodule: # Check out a repo and then add it as a submodule - run = runner(['git', '-C', str(home), 'clone', str(ext_repo), 'b']) + run = runner(["git", "-C", str(home), "clone", str(ext_repo), "b"]) assert run.success - run_version(first, 'submodule', 'add', str(ext_repo), 'b') + run_version(first, "submodule", "add", str(ext_repo), "b") # Add submodule without first checking it out - run_version(first, 'submodule', 'add', str(ext_repo), 'a', - check_stderr=False) - run_version(first, 'submodule', 'add', str(ext_repo), 'c', - check_stderr=False) + run_version(first, "submodule", "add", str(ext_repo), "a", check_stderr=False) + run_version(first, "submodule", "add", str(ext_repo), "c", check_stderr=False) - run_version(first, 'commit', '-m', '"Add submodules"') + run_version(first, "commit", "-m", '"Add submodules"') - for path in ('.yadm', '.config/yadm'): + for path in (".yadm", ".config/yadm"): yadm_dir = home.join(path) if yadm_dir.exists(): break - yadm_dir.join('bootstrap').write('init stuff') - run_version(first, 'add', yadm_dir.join('bootstrap')) - run_version(first, 'commit', '-m', 'bootstrap') + yadm_dir.join("bootstrap").write("init stuff") + run_version(first, "add", yadm_dir.join("bootstrap")) + run_version(first, "commit", "-m", "bootstrap") - yadm_dir.join('encrypt').write('secret') + yadm_dir.join("encrypt").write("secret") - hooks_dir = yadm_dir.mkdir('hooks') - hooks_dir.join('pre_status').write('status') - hooks_dir.join('post_commit').write('commit') + hooks_dir = yadm_dir.mkdir("hooks") + hooks_dir.join("pre_status").write("status") + hooks_dir.join("post_commit").write("commit") - run_version(first, 'config', 'local.class', 'test') - run_version(first, 'config', 'foo.bar', 'true') + run_version(first, "config", "local.class", "test") + run_version(first, "config", "foo.bar", "true") # Run upgrade with intermediate versions and latest latest = None for version in versions[1:] + (latest,): - run = run_version(version, 'upgrade', check_stderr=not submodule) + run = run_version(version, "upgrade", check_stderr=not submodule) if submodule: lines = run.err.splitlines() if can_upgrade_cloned_submodule: - assert 'Migrating git directory of' in lines[0] - assert str(home.join('b/.git')) in lines[1] - assert str(yadm_dir.join('repo.git/modules/b')) in lines[2] + assert "Migrating git directory of" in lines[0] + assert str(home.join("b/.git")) in lines[1] + assert str(yadm_dir.join("repo.git/modules/b")) in lines[2] del lines[:3] for line in lines: - assert line.startswith('Submodule') - assert 'registered for path' in line + assert line.startswith("Submodule") + assert "registered for path" in line # Verify result for the final upgrade - run_version(latest, 'status') + run_version(latest, "status") - run = run_version(latest, 'show', 'HEAD:file') - assert run.out == 'some data' + run = run_version(latest, "show", "HEAD:file") + assert run.out == "some data" if submodule: if can_upgrade_cloned_submodule: - assert home.join('b/afile').read() == 'some data' - assert home.join('a/afile').read() == 'some data' - assert home.join('c/afile').read() == 'some data' + assert home.join("b/afile").read() == "some data" + assert home.join("a/afile").read() == "some data" + assert home.join("c/afile").read() == "some data" - yadm_dir = home.join('.config/yadm') + yadm_dir = home.join(".config/yadm") - assert yadm_dir.join('bootstrap').read() == 'init stuff' - assert yadm_dir.join('encrypt').read() == 'secret' + assert yadm_dir.join("bootstrap").read() == "init stuff" + assert yadm_dir.join("encrypt").read() == "secret" - hooks_dir = yadm_dir.join('hooks') - assert hooks_dir.join('pre_status').read() == 'status' - assert hooks_dir.join('post_commit').read() == 'commit' + hooks_dir = yadm_dir.join("hooks") + assert hooks_dir.join("pre_status").read() == "status" + assert hooks_dir.join("post_commit").read() == "commit" - run = run_version(latest, 'config', 'local.class') - assert run.out.rstrip() == 'test' + run = run_version(latest, "config", "local.class") + assert run.out.rstrip() == "test" - run = run_version(latest, 'config', 'foo.bar') - assert run.out.rstrip() == 'true' + run = run_version(latest, "config", "foo.bar") + assert run.out.rstrip() == "true" diff --git a/test/test_version.py b/test/test_version.py index aee6f33..52b18ab 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -5,34 +5,32 @@ import re import pytest -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def expected_version(yadm): """ Expected semantic version number. This is taken directly out of yadm, searching for the VERSION= string. """ - with open(yadm, encoding='utf-8') as source_file: - yadm_version = re.findall(r'VERSION=([^\n]+)', source_file.read()) + with open(yadm, encoding="utf-8") as source_file: + yadm_version = re.findall(r"VERSION=([^\n]+)", source_file.read()) if yadm_version: return yadm_version[0] - pytest.fail(f'version not found in {yadm}') - return 'not found' + pytest.fail(f"version not found in {yadm}") + return "not found" def test_semantic_version(expected_version): """Version is semantic""" # semantic version conforms to MAJOR.MINOR.PATCH - assert re.search(r'^\d+\.\d+\.\d+$', expected_version), ( - 'does not conform to MAJOR.MINOR.PATCH') + assert re.search(r"^\d+\.\d+\.\d+$", expected_version), "does not conform to MAJOR.MINOR.PATCH" -@pytest.mark.parametrize('cmd', ['--version', 'version']) -def test_reported_version( - runner, yadm_cmd, cmd, expected_version): +@pytest.mark.parametrize("cmd", ["--version", "version"]) +def test_reported_version(runner, yadm_cmd, cmd, expected_version): """Report correct version and bash/git versions""" run = runner(command=yadm_cmd(cmd)) assert run.success - assert run.err == '' - assert 'bash version' in run.out - assert 'git version' in run.out - assert run.out.endswith(f'\nyadm version {expected_version}\n') + assert run.err == "" + assert "bash version" in run.out + assert "git version" in run.out + assert run.out.endswith(f"\nyadm version {expected_version}\n") diff --git a/test/utils.py b/test/utils.py index f71a758..c36ecac 100644 --- a/test/utils.py +++ b/test/utils.py @@ -6,35 +6,39 @@ This module holds values/functions common to multiple tests. import os import re -ALT_FILE1 = 'test_alt' -ALT_FILE2 = 'test alt/test alt' -ALT_DIR = 'test alt/test alt dir' +ALT_FILE1 = "test_alt" +ALT_FILE2 = "test alt/test alt" +ALT_DIR = "test alt/test alt dir" # Directory based alternates must have a tracked contained file. # This will be the test contained file name -CONTAINED = 'contained_file' +CONTAINED = "contained_file" # These variables are used for making include files which will be processed # within jinja templates -INCLUDE_FILE = 'inc_file' -INCLUDE_DIRS = ['', 'test alt'] -INCLUDE_CONTENT = '8780846c02e34c930d0afd127906668f' +INCLUDE_FILE = "inc_file" +INCLUDE_DIRS = ["", "test alt"] +INCLUDE_CONTENT = "8780846c02e34c930d0afd127906668f" def set_local(paths, variable, value, add=False): """Set local override""" add = "--add" if add else "" - os.system( - f'GIT_DIR={str(paths.repo)} ' - f'git config --local {add} "local.{variable}" "{value}"' - ) + os.system(f"GIT_DIR={str(paths.repo)} " f'git config --local {add} "local.{variable}" "{value}"') -def create_alt_files(paths, suffix, - preserve=False, tracked=True, - encrypt=False, exclude=False, - content=None, includefile=False, - yadm_alt=False, yadm_dir=None): +def create_alt_files( + paths, + suffix, + preserve=False, + tracked=True, + encrypt=False, + exclude=False, + content=None, + includefile=False, + yadm_alt=False, + yadm_dir=None, +): """Create new files, and add to the repo This is used for testing alternate files. In each case, a suffix is @@ -42,7 +46,7 @@ def create_alt_files(paths, suffix, repo handling are dependent upon the function arguments. """ - basepath = yadm_dir.join('alt') if yadm_alt else paths.work + basepath = yadm_dir.join("alt") if yadm_alt else paths.work if not preserve: for remove_path in (ALT_FILE1, ALT_FILE2, ALT_DIR): @@ -60,27 +64,27 @@ def create_alt_files(paths, suffix, # Do not test directory support for jinja alternates test_paths = [new_file1, new_file2] test_names = [ALT_FILE1, ALT_FILE2] - if not re.match(r'##(t$|t\.|template|yadm)', suffix): + if not re.match(r"##(t$|t\.|template|yadm)", suffix): test_paths += [new_dir] test_names += [ALT_DIR] for test_path in test_paths: if content: - test_path.write('\n' + content, mode='a', ensure=True) + test_path.write("\n" + content, mode="a", ensure=True) assert test_path.exists() _create_includefiles(includefile, test_paths, basepath) _create_tracked(tracked, test_paths, paths) - prefix = '.config/yadm/alt/' if yadm_alt else '' + prefix = ".config/yadm/alt/" if yadm_alt else "" _create_encrypt(encrypt, test_names, suffix, paths, exclude, prefix) def parse_alt_output(output, linked=True): """Parse output of 'alt', and return list of linked files""" - regex = r'Creating (.+) from template (.+)$' + regex = r"Creating (.+) from template (.+)$" if linked: - regex = r'Linking (.+) to (.+)$' + regex = r"Linking (.+) to (.+)$" parsed_list = {} for line in output.splitlines(): match = re.match(regex, line) @@ -95,7 +99,7 @@ def parse_alt_output(output, linked=True): def _create_includefiles(includefile, test_paths, basepath): if includefile: for dpath in INCLUDE_DIRS: - incfile = basepath.join(dpath + '/' + INCLUDE_FILE) + incfile = basepath.join(dpath + "/" + INCLUDE_FILE) incfile.write(INCLUDE_CONTENT, ensure=True) test_paths += [incfile] @@ -110,8 +114,6 @@ def _create_tracked(tracked, test_paths, paths): def _create_encrypt(encrypt, test_names, suffix, paths, exclude, prefix): if encrypt: for encrypt_name in test_names: - paths.encrypt.write( - f'{prefix + encrypt_name + suffix}\n', mode='a') + paths.encrypt.write(f"{prefix + encrypt_name + suffix}\n", mode="a") if exclude: - paths.encrypt.write( - f'!{prefix + encrypt_name + suffix}\n', mode='a') + paths.encrypt.write(f"!{prefix + encrypt_name + suffix}\n", mode="a") From 8ba9823407d4c177497f765379bd76f3d83f74ae Mon Sep 17 00:00:00 2001 From: Erik Flodin Date: Sun, 27 Oct 2024 13:38:12 +0100 Subject: [PATCH 13/14] Rewrite default template to handle nested ifs, != and env vars in if The awk script now performs all processing in the BEGIN block using an implementation that is capable of handling if statements which contain nested if statments (fixes #436). To make nested ifs look better, if, else and endif lines can now have optional whitespace before {%. Includes are now handled in the same way as the main file which means that included files can both include other files and have if statements in addition to variables (fixes #406). Include lines can now also have optional whitespace before {%. All variables are handled in the same way now so it's now possible to use env variables in if statements (fixes #488). Also add support for != in addition to == (fixes #358). Thus it's now e.g. possible to check if a variable is set (#477) by doing: {% if yadm.class != ""%} Class is set to {{ yadm.class }} {% endif %} A non-existing yadm or env variable is now replaced with the empty string. --- test/test_unit_template_default.py | 75 ++++++++++++- yadm | 169 ++++++++++++++++------------- 2 files changed, 168 insertions(+), 76 deletions(-) diff --git a/test/test_unit_template_default.py b/test/test_unit_template_default.py index 7243877..aaae0fe 100644 --- a/test/test_unit_template_default.py +++ b/test/test_unit_template_default.py @@ -1,4 +1,5 @@ """Unit tests: template_default""" + import os FILE_MODE = 0o754 @@ -12,6 +13,7 @@ LOCAL_HOST = "default_Test+@-!^Host" LOCAL_USER = "default_Test+@-!^User" LOCAL_DISTRO = "default_Test+@-!^Distro" LOCAL_DISTRO_FAMILY = "default_Test+@-!^Family" +ENV_VAR = "default_Test+@-!^Env" TEMPLATE = f""" start of template default class = >{{{{yadm.class}}}}< @@ -30,6 +32,9 @@ Included section from else {{% if yadm.class == "wrongclass1" %}} wrong class 1 {{% endif %}} +{{% if yadm.class != "wronglcass" %}} +Included section from != +{{% endif\t\t %}} {{% if yadm.class == "{LOCAL_CLASS}" %}} Included section for class = {{{{yadm.class}}}} ({{{{yadm.class}}}} repeated) Multiple lines @@ -97,6 +102,13 @@ Included section for distro_family = \ {{% if yadm.distro_family == "wrongfamily2" %}} wrong family 2 {{% endif %}} +{{% if env.VAR == "{ENV_VAR}" %}} +Included section for env.VAR = {{{{env.VAR}}}} ({{{{env.VAR}}}} again) +{{% endif %}} +{{% if env.VAR == "wrongenvvar" %}} +wrong env.VAR +{{% endif %}} +yadm.no_such_var="{{{{ yadm.no_such_var }}}}" and env.NO_SUCH_VAR="{{{{ env.NO_SUCH_VAR }}}}" end of template """ EXPECTED = f""" @@ -111,6 +123,7 @@ default distro_family = >{LOCAL_DISTRO_FAMILY}< classes = >{LOCAL_CLASS2} {LOCAL_CLASS}< Included section from else +Included section from != Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated) Multiple lines Included section for second class @@ -121,6 +134,8 @@ Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated) Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again) Included section for distro_family = \ {LOCAL_DISTRO_FAMILY} ({LOCAL_DISTRO_FAMILY} again) +Included section for env.VAR = {ENV_VAR} ({ENV_VAR} again) +yadm.no_such_var="" and env.NO_SUCH_VAR="" end of template """ @@ -138,7 +153,7 @@ The first line An empty file removes the line above {%include basic%} {% include "./variables.{{ yadm.os }}" %} -{% include dir/nested %} + {% include dir/nested %} Include basic again: {% include basic %} """ @@ -154,6 +169,42 @@ Include basic again: basic """ +TEMPLATE_NESTED_IFS = """\ +{% if yadm.user == "me" %} + print1 + {% if yadm.user == "me" %} + print2 + {% else %} + no print1 + {% endif %} +{% else %} + {% if yadm.user == "me" %} + no print2 + {% else %} + no print3 + {% endif %} +{% endif %} +{% if yadm.user != "me" %} + no print4 + {% if yadm.user == "me" %} + no print5 + {% else %} + no print6 + {% endif %} +{% else %} + {% if yadm.user == "me" %} + print3 + {% else %} + no print7 + {% endif %} +{% endif %} +""" +EXPECTED_NESTED_IFS = """\ + print1 + print2 + print3 +""" + def test_template_default(runner, yadm, tmpdir): """Test template_default""" @@ -182,7 +233,7 @@ def test_template_default(runner, yadm, tmpdir): local_distro_family="{LOCAL_DISTRO_FAMILY}" template_default "{input_file}" "{output_file}" """ - run = runner(command=["bash"], inp=script) + run = runner(command=["bash"], inp=script, env={"VAR": ENV_VAR}) assert run.success assert run.err == "" assert output_file.read() == EXPECTED @@ -243,12 +294,30 @@ def test_include(runner, yadm, tmpdir): assert os.stat(output_file).st_mode == os.stat(input_file).st_mode +def test_nested_ifs(runner, yadm, tmpdir): + """Test nested if statements""" + + input_file = tmpdir.join("input") + input_file.write(TEMPLATE_NESTED_IFS, ensure=True) + output_file = tmpdir.join("output") + + script = f""" + YADM_TEST=1 source {yadm} + set_awk + local_user="me" + template_default "{input_file}" "{output_file}" + """ + run = runner(command=["bash"], inp=script) + assert run.success + assert run.err == "" + assert output_file.read() == EXPECTED_NESTED_IFS + + def test_env(runner, yadm, tmpdir): """Test env""" input_file = tmpdir.join("input") input_file.write("{{env.PWD}}", ensure=True) - input_file.chmod(FILE_MODE) output_file = tmpdir.join("output") script = f""" diff --git a/yadm b/yadm index 09da278..9dc9240 100755 --- a/yadm +++ b/yadm @@ -368,87 +368,110 @@ function template_default() { # the explicit "space + tab" character class used below is used because not # all versions of awk seem to support the POSIX character classes [[:blank:]] read -r -d '' awk_pgm << "EOF" -# built-in default template processor BEGIN { - blank = "[ ]" - c["class"] = class - c["classes"] = classes - c["arch"] = arch - c["os"] = os - c["hostname"] = host - c["user"] = user - c["distro"] = distro - c["distro_family"] = distro_family - c["source"] = source - ifs = "^{%" blank "*if" - els = "^{%" blank "*else" blank "*%}$" - end = "^{%" blank "*endif" blank "*%}$" - skp = "^{%" blank "*(if|else|endif)" - vld = conditions() - inc_start = "^{%" blank "*include" blank "+\"?" - inc_end = "\"?" blank "*%}$" - inc = inc_start ".+" inc_end - prt = 1 - err = 0 -} -END { exit err } -{ replace_vars() } # variable replacements -$0 ~ vld, $0 ~ end { - if ($0 ~ vld || $0 ~ end) prt=1; - if ($0 ~ els) prt=0; - if ($0 ~ skp) next; -} -($0 ~ ifs && $0 !~ vld), $0 ~ end { - if ($0 ~ ifs && $0 !~ vld) prt=0; - if ($0 ~ els || $0 ~ end) prt=1; - if ($0 ~ skp) next; -} -{ if (!prt) next } -$0 ~ inc { - file = $0 - sub(inc_start, "", file) - sub(inc_end, "", file) - sub(/^[^\/].*$/, source_dir "/&", file) + yadm["class"] = class + yadm["classes"] = classes + yadm["arch"] = arch + yadm["os"] = os + yadm["hostname"] = host + yadm["user"] = user + yadm["distro"] = distro + yadm["distro_family"] = distro_family + yadm["source"] = source - while ((res = getline 0) { - replace_vars() - print + VARIABLE = "(env|yadm)\\.[a-zA-Z0-9_]+" + + current = 0 + filename[current] = ARGV[1] + line[current] = 0 + + level = 0 + skip[level] = 0 + + for (; current >= 0; --current) { + while ((res = getline 0) { + ++line[current] + if ($0 ~ "^[ \t]*\\{%[ \t]*if[ \t]+" VARIABLE "[ \t]*[!=]=[ \t]*\".*\"[ \t]*%\\}$") { + if (skip[level]) { skip[++level] = 1; continue } + + match($0, VARIABLE) + lhs = substr($0, RSTART, RLENGTH) + match($0, /[!=]=/) + op = substr($0, RSTART, RLENGTH) + match($0, /".*"/) + rhs = replace_vars(substr($0, RSTART + 1, RLENGTH - 2)) + + if (lhs == "yadm.class") { + lhs = "not" rhs + split(classes, cls_array, "\n") + for (idx in cls_array) { + if (rhs == cls_array[idx]) { lhs = rhs; break } + } + } + else { + lhs = replace_vars("{{" lhs "}}") + } + + if (op == "==") { skip[++level] = lhs != rhs } + else { skip[++level] = lhs == rhs } + } + else if (/^[ \t]*\{%[ \t]*else[ \t]*%\}$/) { + if (level == 0 || skip[level] < 0) { error("else without matching if") } + skip[level] = skip[level] ? skip[level - 1] : -1 + } + else if (/^[ \t]*\{%[ \t]*endif[ \t]*%\}$/) { + if (--level < 0) { error("endif without matching if") } + } + else if (!skip[level]) { + $0 = replace_vars($0) + if (match($0, /^[ \t]*\{%[ \t]*include[ \t]+("[^"]+"|[^"]+)[ \t]*%\}$/)) { + include = $0 + sub(/^[ \t]*\{%[ \t]*include[ \t]+"?/, "", include) + sub(/"?[ \t]*%\}$/, "", include) + if (index(include, "/") != 1) { + include = source_dir "/" include + } + filename[++current] = include + line[current] = 0 + } + else { print } + } + } + if (res >= 0) { close(filename[current]) } + else if (current == 0) { error("could not read input file") } + else { --current; error("could not read include file '" filename[current + 1] "'") } } - if (res < 0) { - printf "%s:%d: error: could not read '%s'\n", FILENAME, NR, file | "cat 1>&2" - err = 1 + if (level > 0) { + current = 0 + error("unterminated if") } - close(file) - next + exit 0 } -{ print } -function replace_vars() { - for (label in c) { - gsub(("{{" blank "*yadm\\." label blank "*}}"), c[label]) - } - for (label in ENVIRON) { - gsub(("{{" blank "*env\\." label blank "*}}"), ENVIRON[label]) - } +function error(text) { + printf "%s:%d: error: %s\n", + filename[current], line[current], text > "/dev/stderr" + exit 1 } -function condition_helper(label, value) { - gsub(/[\\.^$(){}\[\]|*+?]/, "\\\\&", value) - return sprintf("yadm\\.%s" blank "*==" blank "*\"%s\"", label, value) -} -function conditions() { - pattern = ifs blank "+(" - for (label in c) { - if (label != "class") { - value = c[label] - pattern = sprintf("%s%s|", pattern, condition_helper(label, value)); +function replace_vars(input) { + output = "" + while (match(input, "\\{\\{[ \t]*" VARIABLE "[ \t]*\\}\\}")) { + if (RSTART > 1) { + output = output substr(input, 0, RSTART - 1) + } + data = substr(input, RSTART + 2, RLENGTH - 4) + input = substr(input, RSTART + RLENGTH) + + gsub(/[ \t]+/, "", data) + split(data, fields, /\./) + + if (fields[1] == "env") { + output = output ENVIRON[fields[2]] + } + else { + output = output yadm[fields[2]] } } - split(classes, cls_array, "\n") - for (idx in cls_array) { - value = cls_array[idx] - pattern = sprintf("%s%s|", pattern, condition_helper("class", value)); - } - sub(/\|$/, ")" blank "*%}$", pattern) - return pattern + return output input } EOF From 8e5d4b1578d4a93d45972575a10daa49d4f07eea Mon Sep 17 00:00:00 2001 From: Erik Flodin Date: Fri, 8 Nov 2024 19:51:25 +0100 Subject: [PATCH 14/14] Pass classes as separate arguments to template_default To work around problem with passing newlines in variable with awk on darwin. This fixes #437. --- yadm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/yadm b/yadm index 9dc9240..2afbe65 100755 --- a/yadm +++ b/yadm @@ -369,6 +369,10 @@ function template_default() { # all versions of awk seem to support the POSIX character classes [[:blank:]] read -r -d '' awk_pgm << "EOF" BEGIN { + classes = ARGV[2] + for (i = 3; i < ARGC; ++i) { + classes = classes "\n" ARGV[i] + } yadm["class"] = class yadm["classes"] = classes yadm["arch"] = arch @@ -485,9 +489,8 @@ EOF -v distro_family="$local_distro_family" \ -v source="$input" \ -v source_dir="$(dirname "$input")" \ - -v classes="$(join_string $'\n' "${local_classes[@]}")" \ "$awk_pgm" \ - "$input" > "$temp_file" || rm -f "$temp_file" + "$input" "${local_classes[@]}" > "$temp_file" || rm -f "$temp_file" move_file "$input" "$output" "$temp_file" }