1
0
Fork 0
mirror of synced 2024-11-04 17:08:56 -05:00

Changes for black compliance

This commit is contained in:
Tim Byrne 2023-07-10 14:43:17 -05:00
parent e704175201
commit 76ce3defea
No known key found for this signature in database
GPG key ID: 14DB4FC2465A4B12
47 changed files with 1845 additions and 2038 deletions

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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 == ""

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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)

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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

View file

@ -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 == ""

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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 == ""

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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() == ""

View file

@ -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() == ""

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 == ""

View file

@ -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 == ""

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"]

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 == ""

View file

@ -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"

View file

@ -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")

View file

@ -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")