Closed kgizdov closed 4 years ago
First, thank you for your packaging efforts. I appreciate it, and I'm sure Arch users will appreciate it even more.
I've gone through pains to make yadm run on a large number of systems, and gracefully downgrade when optional dependencies are not available. Indeed many aspects of it are written to the lowest common denominator. So for yadm, the systems supported make the coding very constrained. However, for the tests, I want to be as expressive as necessary, and less constrained. But that does translate to a constrained target the tests are designed to run within. This is how I settled upon Docker/Compose as the requirement for full testing. Docker is a reasonable testing dependency for most development machines.
I realize that Docker is NOT, a common dependency for automated packaging requirements. However, I also don't see the entire catalog of tests as necessary for packaging. After all, yadm is not compiled, but is a single Bash program.
I would like to help work through the testing troubles you've had. Perhaps some compromise can be reached.
The version requirements for the various linters are necessary, as the version determines what standards are enforced. I do not want any contributors surprised because their tests succeed, but my own do not (due to version differences). The tests are gracefully skipped if the supported version is missing.
Perhaps, one change could be to make gpg-based tests, gracefully skipped if the supported version is not present. Tests requiring "expect" could also be skipped if it isn't present. Based on the output above, that might allow you to run a large portion of the tests, and not have any failures.
Another possibility would be to write an appropriate test for the build environment. For example, the Homebrew formula implements a very brief test which creates a repo, and adds some content to that repo.
I'm not familiar with packaging for Arch, or what requirements might be present for the official Arch repos. Would skipping the tests that lacked dependencies solve these problems?
Cheers for detailed explanation. It will be probably nice to give a bit of detail myself.
Arch Linux packaging is simple, but comes with high standards for reproducibility and quality. What that means is that, we basically spawn a kind of a complete "new system" with only the required packages for each build. So we start from scratch, add only the dependencies for the package and the build itself and run the build. The tests run in that bare system. This essentially proves that the package as defined can pass its own tests in a complete Arch Linux system containing only the package and its dependent software. Simply put, we verify every Arch user can run the build script and verify the tests pass for them as well and this know the package has correct functionality on their system.
If we put Docker in there, we are not able to satisfactory prove that the package will pass its tests (work correctly) in an Arch Linux system, since the whole point of Docker is to do the opposite. Substitute any other distro/OS for Arch Linux and the same argument stands. Docker tests only run in Docker container and do not prove a package will behave correctly outside.
Thus, I run your tests by providing yadm a whole bare system with all required dependencies to run everything. Most tests do run and pass fine, but a few do fail. This issue is for those.
Basically, main issue I have is that the tests depend on gpg v1
. However, I bet almost everyone caring for security actually runs gpg v2
. So those tests while succeeding with gpg v1
don't actually test the correct thing.
Secondary to this, the linter versions being fixed are a bit weird as well. I understand your argument, however, on the flip side there are a couple of issues. How would anyone reasonably acquire these specific versions every time they run your tests and then move to their distribution versions after the fact? Seems like a lot of hassle. Also, newer version of linters mean better/improved standards in most cases, even better catching/parsing of weird stuff. Blocking newer versions kinda looks like blocking code improvements (not really, but you get my argument). Moreover, you can easily specify which standard you care and don't care and have any version of the linter only test for those.
Finally, I am a bit confused with the /yadm
test directory. Why do you chose a directory which is both hard coded and directly under root (/
)? It would be better if one could specify that directory from the environment (e.g. "${TESTPREFIX}/yadm"). Not all users will have root permissions on the machine they work with in order to provide with such a path.
I'm open to discuss these further and get something working. :)
I'm also sure we can reach a solution.
gpg-v1 implements the same interface as gpg-v2, which allows yadm to support either version.
The reason gpg-v1 is currently used in the test harness is because when I created it, I was unable to get tty-based credentials working with gpg-v2. I need to script the interaction (using expect) and the curses interface of gpg-agent (which is a requirement for gpg-v2) will not work in that way. However, I just did a bit of investigation, and I've successfully used gpg-agent + pinentry-tty within my container, so I should be able to adopt that. Assuming GnuPG2 + gpg-agent + pinentry-tty is available in Arch Linux, this should solve that dependency issue. 👍 It seems like this is the biggest issue right now.
I think it would be easy to provide an argument which removed any version checking for linters. But be aware, the tests may fail if you have different versions which enforce different constraints. In my opinion, the linting is about code quality/maintainability, and not about testing functionality. But I'm happy to create such an argument.
I don't believe a /yadm
directory is required for the tests to work. pytest creates temporary directories for tests and fixtures and that is what it uses. I think you may be looking at the Makefile, which runs the tests via Docker/Compose. The compose configuration exposes the current directory as a read-only mount at /yadm
. That directory could easily be anywhere else. If it isn't being run within a container, you can just run pytest directly, which looks to be what you've mostly had success with already.
I think I can work on these adjustments to testing after the next release (which hopefully isn't very far off).
Let me know if you think this will address the major packaging problems you're experiencing.
On other note for when I make these changes. It seems like pinentry-tty isn't necessary. It is good enough to start gpg-agent with --allow-loopback-pinentry
.
@TheLocehiliosan ok, please let me know how it goes, so I can try it out as well. Thanks.
I did a bunch of experimentation with this tonight. It turns out with pinentry-tty or loopback, it still doesn't work with "expect" (it's difficult to mock). The passphrase need to come directly from the tty, it isn't part of any file handle of the gpg process.
The good news is I think I can accomplish this with a custom pinentry program which mocks the input I need for testing. I have a proof-of-concept working, but I need to flesh it out more.
Have you tried --passphrase-fd 0
per https://wiki.archlinux.org/index.php/GnuPG#Unattended_passphrase ?
@kgizdov I've published some changes to branch wip/test-portability-179
. Can you test the GPG2 tests with that? I have not yet changed the linter version requirements, but that will be easy. I want to know if this works just with gpg2 installed. I believe it will.
Have you tried
--passphrase-fd 0
per https://wiki.archlinux.org/index.php/GnuPG#Unattended_passphrase ?
@rasa I'm familiar with ways to get gpg to use different sources for passphrase, but I don't want to have have conditional code for testing within yadm. I just want it to use gpg-agent like it will in the wild. The new method of using a custom pinentry program works pretty well.
@kgizdov I've updated wip/test-portability-179
again. I've also added a command line option to pylint, --force-linters
. When you provide this option, linters are run regardless of the version installed.
Please let me know how this branch works out for your package building.
@TheLocehiliosan cool, thanks, I'll test and come to you
@TheLocehiliosan so almost everything now succeeds, except for some gpg: Warning: using insecure memory!
and other messages going into stderr
. What I'm saying is, these are not errors per say, but do end up in the error output, which breaks the return code and tests don't pass. There should be some GPG option to suppress these warnings or at least convert them into warnings, not errors. Here's the log.
==> Starting check()...
============================= test session starts ==============================
platform linux -- Python 3.8.0, pytest-5.3.1, py-1.8.0, pluggy-0.13.1
cachedir: /tmp
rootdir: /build/yadm/src/yadm-wip-test-portability-179, inifile: pytest.ini
plugins: pylint-0.14.1, flake8-1.0.4
collected 563 items / 211 deselected / 352 selected
test/test_alt.py ........................................... [ 12%]
test/test_alt_copy.py .......... [ 15%]
test/test_assert_private_dirs.py ... [ 15%]
test/test_bootstrap.py ... [ 16%]
test/test_clean.py . [ 17%]
test/test_clone.py ................... [ 22%]
test/test_config.py ....... [ 24%]
test/test_encryption.py F...F.......................F..F [ 33%]
test/test_enter.py ....... [ 35%]
test/test_git.py . [ 35%]
test/test_help.py .. [ 36%]
test/test_hooks.py ........ [ 38%]
test/test_init.py ..... [ 40%]
test/test_introspect.py ...... [ 41%]
test/test_list.py ... [ 42%]
test/test_perms.py ............. [ 46%]
test/test_syntax.py ..... [ 47%]
test/test_unit_bootstrap_available.py ... [ 48%]
test/test_unit_choose_template_cmd.py ...... [ 50%]
test/test_unit_configure_paths.py ....... [ 52%]
test/test_unit_exclude_encrypted.py ............ [ 55%]
test/test_unit_is_valid_branch_name.py ........... [ 58%]
test/test_unit_issue_legacy_path_warning.py ................ [ 63%]
test/test_unit_parse_encrypt.py ..... [ 64%]
test/test_unit_query_distro.py ... [ 65%]
test/test_unit_record_score.py ...... [ 67%]
test/test_unit_record_template.py .. [ 67%]
test/test_unit_relative_path.py .......... [ 70%]
test/test_unit_remove_stale_links.py .... [ 71%]
test/test_unit_score_file.py ........................................... [ 84%]
........................ [ 90%]
test/test_unit_set_local_alt_values.py ...... [ 92%]
test/test_unit_set_os.py ... [ 93%]
test/test_unit_set_yadm_dir.py .... [ 94%]
test/test_unit_template_default.py .. [ 95%]
test/test_unit_template_j2.py .. [ 95%]
test/test_unit_upgrade.py ....... [ 97%]
test/test_unit_x_program.py ...... [ 99%]
test/test_version.py .. [100%]
=================================== FAILURES ===================================
___________ test_symmetric_encrypt[clean-encrypt_exists-good_phrase] ___________
runner = <class 'conftest.Runner'>
yadm_y = <function yadm_y.<locals>.command_list at 0x7fba25b4f550>
paths = Paths(pgm='/build/yadm/src/yadm-wip-test-portability-179/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_sym...ot/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/encrypt'))
encrypt_targets = ['inc file1', 'inc dir/inc file2', 'globs file1', 'globs dir/globs file2', 'extest/inglob1']
gnupg = GNUPG(home=local('/tmp/pytest-of-builduser/pytest-0/gnupghome0'), pw=<function gnupg.<locals>.register_gpg_password at 0x7fba25b35ee0>)
bad_phrase = False, overwrite = False, missing_encrypt = False
@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_y, paths, encrypt_targets,
gnupg, bad_phrase, overwrite, missing_encrypt):
"""Test symmetric encryption"""
if missing_encrypt:
paths.encrypt.remove()
if bad_phrase:
gnupg.pw('')
else:
gnupg.pw(PASSPHRASE)
if overwrite:
paths.archive.write('existing archive')
env = os.environ.copy()
env['GNUPGHOME'] = gnupg.home
run = runner(yadm_y('encrypt'), env=env)
if missing_encrypt or bad_phrase:
assert run.failure
else:
assert run.success
> assert run.err == ''
E AssertionError: assert 'gpg: Warning...ure memory!\n' == ''
E - gpg: Warning: using insecure memory!
test/test_encryption.py:212: AssertionError
---------------------------- Captured stdout setup -----------------------------
Runner(['gpg', '-k'])
RUN: code:0
RUN: stdout:
RUN: stderr:
gpg: Warning: using insecure memory!
gpg: keybox '/tmp/pytest-of-builduser/pytest-0/gnupghome0/pubring.kbx' created
gpg: /tmp/pytest-of-builduser/pytest-0/gnupghome0/trustdb.gpg: trustdb created
Initialized empty shared Git repository in /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/repo.git/
----------------------------- Captured stdout call -----------------------------
Runner(['/build/yadm/src/yadm-wip-test-portability-179/yadm', '-Y', '/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm', 'encrypt'])
RUN: code:0
RUN: stdout:
Encrypting the following files:
extest/inglob1
globs dir
globs file1
inc dir/inc file2
inc file1
Wrote new file: /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/files.gpg
RUN: stderr:
gpg: Warning: using insecure memory!
_________ test_symmetric_encrypt[overwrite-encrypt_exists-good_phrase] _________
runner = <class 'conftest.Runner'>
yadm_y = <function yadm_y.<locals>.command_list at 0x7fba25bc1ee0>
paths = Paths(pgm='/build/yadm/src/yadm-wip-test-portability-179/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_sym...ot/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_overwri0/root/yadm/encrypt'))
encrypt_targets = ['inc file1', 'inc dir/inc file2', 'globs file1', 'globs dir/globs file2', 'extest/inglob1']
gnupg = GNUPG(home=local('/tmp/pytest-of-builduser/pytest-0/gnupghome0'), pw=<function gnupg.<locals>.register_gpg_password at 0x7fba25b35ee0>)
bad_phrase = False, overwrite = True, missing_encrypt = False
@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_y, paths, encrypt_targets,
gnupg, bad_phrase, overwrite, missing_encrypt):
"""Test symmetric encryption"""
if missing_encrypt:
paths.encrypt.remove()
if bad_phrase:
gnupg.pw('')
else:
gnupg.pw(PASSPHRASE)
if overwrite:
paths.archive.write('existing archive')
env = os.environ.copy()
env['GNUPGHOME'] = gnupg.home
run = runner(yadm_y('encrypt'), env=env)
if missing_encrypt or bad_phrase:
assert run.failure
else:
assert run.success
> assert run.err == ''
E AssertionError: assert 'gpg: Warning...ure memory!\n' == ''
E - gpg: Warning: using insecure memory!
test/test_encryption.py:212: AssertionError
---------------------------- Captured stdout setup -----------------------------
Initialized empty shared Git repository in /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_overwri0/root/yadm/repo.git/
----------------------------- Captured stdout call -----------------------------
Runner(['/build/yadm/src/yadm-wip-test-portability-179/yadm', '-Y', '/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_overwri0/root/yadm', 'encrypt'])
RUN: code:0
RUN: stdout:
Encrypting the following files:
extest/inglob1
globs dir
globs file1
inc dir/inc file2
inc file1
Wrote new file: /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_overwri0/root/yadm/files.gpg
RUN: stderr:
gpg: Warning: using insecure memory!
__________________________ test_offer_to_add[tracked] __________________________
runner = <class 'conftest.Runner'>
yadm_y = <function yadm_y.<locals>.command_list at 0x7fba25c12280>
paths = Paths(pgm='/build/yadm/src/yadm-wip-test-portability-179/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_off...0/root/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/yadm/encrypt'))
encrypt_targets = ['inc file1', 'inc dir/inc file2', 'globs file1', 'globs dir/globs file2', 'extest/inglob1']
gnupg = GNUPG(home=local('/tmp/pytest-of-builduser/pytest-0/gnupghome0'), pw=<function gnupg.<locals>.register_gpg_password at 0x7fba25b35ee0>)
untracked = False
@pytest.mark.parametrize(
'untracked',
[False, 'y', 'n'],
ids=['tracked', 'untracked_answer_y', 'untracked_answer_n'])
def test_offer_to_add(
runner, yadm_y, paths, encrypt_targets, gnupg, untracked):
"""Test offer to add encrypted archive
All the other encryption tests use an archive outside of the work tree.
However, the archive is often inside the work tree, and if it is, there
should be an offer to add it to the repo if it is not tracked.
"""
worktree_archive = paths.work.join('worktree-archive.tar.gpg')
expect = []
gnupg.pw(PASSPHRASE)
env = os.environ.copy()
env['GNUPGHOME'] = gnupg.home
if untracked:
expect.append(('add it now', untracked))
else:
worktree_archive.write('exists')
os.system(' '.join(yadm_y('add', str(worktree_archive))))
run = runner(
yadm_y('encrypt', '--yadm-archive', str(worktree_archive)),
env=env,
expect=expect
)
assert run.success
> assert run.err == ''
E AssertionError: assert 'gpg: Warning...ure memory!\n' == ''
E - gpg: Warning: using insecure memory!
test/test_encryption.py:402: AssertionError
---------------------------- Captured stdout setup -----------------------------
Initialized empty shared Git repository in /tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/yadm/repo.git/
----------------------------- Captured stdout call -----------------------------
Runner(['/build/yadm/src/yadm-wip-test-portability-179/yadm', '-Y', '/tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/yadm', 'encrypt', '--yadm-archive', '/tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/work/worktree-archive.tar.gpg'])
RUN: code:0
RUN: stdout:
Encrypting the following files:
extest/inglob1
globs dir
globs file1
inc dir/inc file2
inc file1
Wrote new file: /tmp/pytest-of-builduser/pytest-0/test_offer_to_add_tracked_0/root/work/worktree-archive.tar.gpg
RUN: stderr:
gpg: Warning: using insecure memory!
________________________ test_encrypt_added_to_exclude _________________________
runner = <class 'conftest.Runner'>
yadm_y = <function yadm_y.<locals>.command_list at 0x7fba25adc790>
paths = Paths(pgm='/build/yadm/src/yadm-wip-test-portability-179/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_enc...oot/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_encrypt_added_to_exclude0/root/yadm/encrypt'))
gnupg = GNUPG(home=local('/tmp/pytest-of-builduser/pytest-0/gnupghome0'), pw=<function gnupg.<locals>.register_gpg_password at 0x7fba25b35ee0>)
@pytest.mark.usefixtures('ds1_copy')
def test_encrypt_added_to_exclude(runner, yadm_y, paths, gnupg):
"""Confirm that .config/yadm/encrypt is added to exclude"""
gnupg.pw(PASSPHRASE)
env = os.environ.copy()
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)
run = runner(yadm_y('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 run.success
> assert run.err == ''
E AssertionError: assert 'gpg: Warning...ure memory!\n' == ''
E - gpg: Warning: using insecure memory!
test/test_encryption.py:440: AssertionError
----------------------------- Captured stdout call -----------------------------
Runner(['/build/yadm/src/yadm-wip-test-portability-179/yadm', '-Y', '/tmp/pytest-of-builduser/pytest-0/test_encrypt_added_to_exclude0/root/yadm', 'encrypt'])
RUN: code:0
RUN: stdout:
Encrypting the following files:
test-encrypt-data
Wrote new file: /tmp/pytest-of-builduser/pytest-0/test_encrypt_added_to_exclude0/root/yadm/files.gpg
RUN: stderr:
gpg: Warning: using insecure memory!
=========================== short test summary info ============================
FAILED test/test_encryption.py::test_symmetric_encrypt[clean-encrypt_exists-good_phrase]
FAILED test/test_encryption.py::test_symmetric_encrypt[overwrite-encrypt_exists-good_phrase]
FAILED test/test_encryption.py::test_offer_to_add[tracked] - AssertionError: ...
FAILED test/test_encryption.py::test_encrypt_added_to_exclude - AssertionErro...
=========== 4 failed, 348 passed, 211 deselected in 60.03s (0:01:00) ===========
==> ERROR: A failure occurred in check().
@kgizdov OK, we're close... I'm curious, what version of libgcrypt
is installed on the build host?
I assume you mean libgcrypt 1.8.5
@kgizdov OK, just checking as I read about a bug with an older version. I'm not sure what the situation is with the build environment, but that's a warning that should be suppressible during tests. I just pushed another update. Can you try that?
@kgizdov Any luck with my changes that suppress the insecure memory warnings?
@TheLocehiliosan just tested and everything passed fine. Good work!
@kgizdov Fantastic! I'll merge these changes into develop
, and likely release 2.2.0 tomorrow. Thanks for your patience. I'm much happier with mocking the gpg-agent credentials this way also.
Describe the bug
The current testing environment for
yadm
presents some issues for crossplatform testing and deployment. Specifically, it requires a custom testing environment such as:/yadm
pathshellcheck
,pylint
,flake8
,yamllint
gpg-agent
To reproduce
Steps to reproduce the behavior:
check
function during build:runner = <class 'conftest.Runner'> yadm_y = <function yadm_y..command_list at 0x7fea48e023a0>
paths = Paths(pgm='/build/yadm/src/yadm-2.0.1/yadm', root=local('/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clea...ot/yadm/config'), encrypt=local('/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/encrypt'))
encrypt_targets = ['inc file1', 'inc dir/inc file2', 'globs file1', 'globs dir/globs file2', 'extest/inglob1']
overwrite = False, missing_encrypt = False, mismatched_phrase = False
test/test_encryption.py:181: AssertionError ---------------------------- Captured stdout setup ----------------------------- Initialized empty shared Git repository in /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/repo.git/ ----------------------------- Captured stdout call ----------------------------- EXPECT:set timeout 2 spawn "/build/yadm/src/yadm-2.0.1/yadm" "-Y" "/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm" "encrypt" expect { "passphrase:" {send "ExamplePassword\r"} timeout {close;exit 128} } expect { "passphrase:" {send "ExamplePassword\r"} timeout {close;exit 128} } expect eof foreach {pid spawnid os_error_flag value} [wait] break exit $value Runner(['expect']) RUN: code:1 RUN: input: set timeout 2 spawn "/build/yadm/src/yadm-2.0.1/yadm" "-Y" "/tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm" "encrypt" expect { "passphrase:" {send "ExamplePassword\r"} timeout {close;exit 128} } expect { "passphrase:" {send "ExamplePassword\r"} timeout {close;exit 128} } expect eof foreach {pid spawnid os_error_flag value} [wait] break exit $value RUN: stdout: spawn /build/yadm/src/yadm-2.0.1/yadm -Y /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm encrypt Encrypting the following files: extest/inglob1 globs dir globs file1 inc dir/inc file2 inc file1
gpg: WARNING: unsafe permissions on homedir '/build/yadm/src/yadm-2.0.1/work/yadm/.gnupg' gpg: keybox '/build/yadm/src/yadm-2.0.1/work/yadm/.gnupg/pubring.kbx' created gpg: problem with the agent: Permission denied gpg: error creating passphrase: Operation cancelled gpg: symmetric encryption of '[stdin]' failed: Operation cancelled ExamplePassword ERROR: Unable to write /tmp/pytest-of-builduser/pytest-0/test_symmetric_encrypt_clean_e0/root/yadm/files.gpg
RUN: stderr: expect: spawn id exp4 not open while executing "expect eof" ... =========================== short test summary info ============================ SKIPPED [1] /build/yadm/src/yadm-2.0.1/test/test_syntax.py:18: Unsupported shellcheck version SKIPPED [1] /build/yadm/src/yadm-2.0.1/test/test_syntax.py:27: Unsupported pylint version SKIPPED [1] /build/yadm/src/yadm-2.0.1/test/test_syntax.py:40: Unsupported flake8 version SKIPPED [1] /build/yadm/src/yadm-2.0.1/test/test_syntax.py:49: Unsupported yamllint version FAILED test/test_encryption.py::test_symmetric_encrypt[clean-encrypt_exists-matching_phrase] FAILED test/test_encryption.py::test_symmetric_encrypt[clean-encrypt_exists-mismatched_phrase] FAILED test/test_encryption.py::test_symmetric_encrypt[overwrite-encrypt_exists-matching_phrase] FAILED test/test_encryption.py::test_symmetric_encrypt[overwrite-encrypt_exists-mismatched_phrase] FAILED test/test_encryption.py::test_symmetric_decrypt[decrypt-archive_exists-correct_phrase] FAILED test/test_encryption.py::test_symmetric_decrypt[list-archive_exists-correct_phrase] FAILED test/test_encryption.py::test_offer_to_add[tracked] - AssertionError: ... FAILED test/test_encryption.py::test_offer_to_add[untracked_answer_y] - Asser... FAILED test/test_encryption.py::test_offer_to_add[untracked_answer_n] - Asser... FAILED test/test_encryption.py::test_encrypt_added_to_exclude - AssertionErro... ========== 10 failed, 325 passed, 4 skipped, 211 deselected in 54.69s ==========