#!/usr/bin/python3 -cimport os, sys; os.execv(os.path.dirname(sys.argv[1]) + "/../common/pywrap", sys.argv)## Copyright (C) 2020 Red Hat, Inc.# SPDX-License-Identifier: LGPL-2.1-or-laterimport osimport reimport subprocessimport unittestimport testlibdirname = os.path.dirname(__file__)run_tests = os.path.join(testlib.TEST_DIR, "common", "run-tests")EXAMPLE_DIR = os.path.join(testlib.TEST_DIR, "example")VERIFY_DIR = os.path.join(testlib.TEST_DIR, "verify")ROOT_DIR = os.path.dirname(testlib.TEST_DIR)class TestRunTestListing(unittest.TestCase):def testBasic(self):# Listing on check-* fileself.assertEqual(subprocess.check_output([os.path.join(EXAMPLE_DIR, "check-example"), "-l", "TestNondestructiveExample"]).strip().decode(),"TestNondestructiveExample.testOne\nTestNondestructiveExample.testTwo")# Filter on classp = subprocess.run([run_tests, "--test-dir", EXAMPLE_DIR, "-l", "TestNondestructiveExample"], capture_output=True, check=True)self.assertIn(b"TestNondestructiveExample.testOne\nTestNondestructiveExample.testTwo", p.stdout.strip())# Filter a specific testself.assertIn("TestNondestructiveExample.testOne",subprocess.check_output([run_tests, "--test-dir", EXAMPLE_DIR, "-l", "TestNondestructiveExample.testOne"]).strip().decode())# Exclude test patternsout = subprocess.check_output([run_tests, "--test-dir", EXAMPLE_DIR, "-l","--exclude", "bogus", "--exclude", "TestNondestructiveExample.testTwo","TestNondestructiveExample"]).strip().decode()self.assertIn("TestNondestructiveExample.testOne", out)self.assertNotIn("testTwo", out)ndtests = subprocess.run([run_tests, "--test-dir", EXAMPLE_DIR, "-n", "-l"], check=True, capture_output=True)self.assertIn(b"TestExample.testNondestructive\n", ndtests.stdout)self.assertIn(b"TestNondestructiveExample.testOne\nTestNondestructiveExample.testTwo", ndtests.stdout)# nondestructive tests are sorted alphabeticallyverify_ndtests = subprocess.run([run_tests, "--test-dir", VERIFY_DIR, "-n", "-l"],check=True, capture_output=True)self.assertRegex(verify_ndtests.stdout,re.compile(rb".*TestAccounts.*TestFirewall.*TestLogin.*TestServices.*TestTerminal.*", re.S))def testNonDestructive(self):self.assertEqual(subprocess.check_output([run_tests, "--test-dir", EXAMPLE_DIR, "--nondestructive", "-l", "TestExample"]).strip().decode(),"TestExample.testNondestructive")# with short option and substringself.assertEqual(subprocess.check_output([run_tests, "--test-dir", EXAMPLE_DIR, "-nl", "TestExamp"]).strip().decode(),"TestExample.testNondestructive")# This can't be @nondestructive, as we run our own @nondestructive test nested inside this test. This will already call the# cleanup handlers, and the outside cleanup would then fail to do that again.@testlib.onlyImage("Only run on the required image", "fedora-coreos")class TestRunTest(testlib.MachineCase):def testExistingMachine(self):env = os.environ.copy()try:del env["TEST_JOBS"]except KeyError:passout = subprocess.check_output([run_tests, "--test-dir", EXAMPLE_DIR, "-vt","--machine", self.machine.ssh_address + ":" + self.machine.ssh_port,"--browser", self.machine.web_address + ":" + self.machine.web_port,"TestNondestructiveExample"], env=env)self.assertRegex(out, b"\nok .* TestNondestructiveExample.testOne")self.assertRegex(out, b"\nok .* TestNondestructiveExample.testTwo")self.assertIn(b"TESTS PASSED", out)# implies --nondestructiveout = subprocess.check_output([run_tests, "--test-dir", EXAMPLE_DIR, "--machine", "1.2.3.4:56", "--browser", "1.2.3.4:67","TestExample.testBasic"], env=env)self.assertIn(b"1..0", out)self.assertNotIn(b"TestExample", out)def testRetry(self):# Don't test this if not in git repo as we use `git diff` for this logicif not os.path.exists(os.path.join(ROOT_DIR, ".git")):returnenv = os.environ.copy()env["TEST_FAILURES"] = "1"# pretend this was a PR against main (set by CI normally, or by the user with --base)env["BASE_BRANCH"] = "main"try:del env["TEST_JOBS"]except KeyError:pass# Check that we retry 3 times failing testsprocess = subprocess.run([run_tests, "--test-dir", EXAMPLE_DIR, "TestExample.testFail", "TestExample.testSkip"],env=env, capture_output=True)stdout = process.stdoutself.assertRegex(stdout, rb"\nnot ok 1 .*test\/example\/check-example TestExample.testFail \(1\.7GB\) # RETRY 1 \(be robust against unstable tests\)\n")self.assertRegex(stdout, rb"\nnot ok 1 .*test\/example\/check-example TestExample.testFail \(1\.7GB\) # RETRY 2 \(be robust against unstable tests\)\n")self.assertRegex(stdout, rb"\nnot ok 1 .*test\/example\/check-example TestExample.testFail \(1\.7GB\)\n")self.assertNotRegex(stdout, b"RETRY 3")self.assertRegex(stdout, rb"\nok 2 .*test\/example\/check-example TestExample.testSkip \(1\.7GB\) # SKIP testSkip \(__main__\.TestExample")self.assertRegex(stdout, rb"# 1 TESTS FAILED \[\d*s on .*, 2 destructive tests, 0 nondestructive tests: \]")# Check retry logic for changed teststest_file = os.path.join(EXAMPLE_DIR, "check-retry")with open(test_file, 'r') as f:original_test = f.read()def write_file(file, content, mode=0o666):# test files need to be executable, respect umaskcontent_bin = content.encode()fd = os.open(file, os.O_CREAT | os.O_TRUNC | os.O_WRONLY, mode=mode)try:written = os.write(fd, content_bin)assert written == len(content_bin)finally:os.close(fd)self.addCleanup(write_file, test_file, original_test)write_file(test_file, original_test.replace("class NoTest", "class Test"))process = subprocess.run([run_tests, "--test-dir", EXAMPLE_DIR, "TestRetryExample.testFail", "TestRetryExample.testBasic","TestRetryExample.testNoRetry"], env=env, capture_output=True)stdout = process.stdout# Changed test need to succeed 3 timesself.assertRegex(stdout, rb"\nok 1 .*test\/example\/check-retry TestRetryExample.testBasic \(1\.7GB\) # RETRY 1 \(test affected tests 3 times\)\n")self.assertRegex(stdout, rb"\nok 1 .*test\/example\/check-retry TestRetryExample.testBasic \(1\.7GB\) # RETRY 2 \(test affected tests 3 times\)\n")self.assertRegex(stdout, rb"\nok 1 .*test\/example\/check-retry TestRetryExample.testBasic \(1\.7GB\)\n")self.assertNotRegex(stdout, b"RETRY 3")# Changed test is never retriedself.assertRegex(stdout, rb"\nnot ok 2 .*test\/example\/check-retry TestRetryExample.testFail \(1\.7GB\)\n")self.assertNotRegex(stdout, b"testFail # RETRY")# Using @no_retry_when_changed prevents this retry logicself.assertRegex(stdout, rb"\nok 3 .*test\/example\/check-retry TestRetryExample.testNoRetry \(1\.7GB\)\n")self.assertNotRegex(stdout, b"testNoRetry # RETRY")# Check retry logic for changed sourceshell = os.path.join("pkg", "shell", "hosts.tsx")self.assertTrue(os.path.exists(shell))with open(shell, 'r') as f:original_source = f.read()self.addCleanup(write_file, shell, original_source)write_file(shell, original_source + "\n")# create affected test for shell changesaffected_testfile = os.path.join(EXAMPLE_DIR, "check-shell-testlib")new_content = original_test.replace("class NoTest", "class NeinTest")write_file(affected_testfile, new_content.replace("class NeinTestRetryExample", "class TestShell"), mode=0o777)self.addCleanup(os.unlink, affected_testfile)process = subprocess.run([run_tests, "--test-dir", EXAMPLE_DIR,"TestShell.testBasic", "TestShell.testNoRetry"],env=env, capture_output=True)stdout = process.stdoutself.assertRegex(stdout, rb"\nok .* TestShell.testBasic \(1\.7GB\) # RETRY 1 \(test affected tests 3 times\)\n")self.assertRegex(stdout, rb"\nok .* TestShell.testBasic \(1\.7GB\) # RETRY 2 \(test affected tests 3 times\)\n")self.assertRegex(stdout, rb"\nok .* TestShell.testBasic \(1\.7GB\)\n")self.assertNotRegex(stdout, b"RETRY 3")self.assertRegex(stdout, b"\nok .* TestShell.testNoRetry \\(1\\.7GB\\)\n")self.assertNotRegex(stdout, b"TestShell.testNoRetry # RETRY")# Check that just .css changes do no affect retry logicsystemd = os.path.join("pkg", "systemd", "overview.scss")self.assertTrue(os.path.exists(systemd))with open(systemd, 'r') as f:original_source = f.read()self.addCleanup(write_file, systemd, original_source)write_file(systemd, original_source + "\n")# create affected test for systemd changesaffected_testfile = os.path.join(EXAMPLE_DIR, "check-system-testlib")write_file(affected_testfile, """#!/usr/bin/env python3import unittestclass TestSystemd(unittest.TestCase):def testBasic(self):self.assertTrue(True)""", mode=0o777)self.addCleanup(os.unlink, affected_testfile)process = subprocess.run([run_tests, "--test-dir", EXAMPLE_DIR, "TestSystemd.testBasic"],env=env, capture_output=True)stdout = process.stdoutself.assertRegex(stdout, rb"\nok .* TestSystemd.testBasic \(1\.7GB\)\n")self.assertNotRegex(stdout, b"RETRY")@testlib.nondestructivedef testTodo(self):env = os.environ.copy()try:del env["TEST_JOBS"]except KeyError:passenv["TEST_TODO"] = "1"process = subprocess.run([run_tests, "--test-dir", EXAMPLE_DIR, "TestTodo"],env=env, capture_output=True)stdout = process.stdout# A @todo test which fails should write 'not ok ... # TODO ...'self.assertRegex(stdout, rb"\nnot ok . .*test/example/check-example TestTodo.testTodoFail \(1\.7GB\) # TODO 2 is not yet sufficiently large\n")# A @todo test which passes should write 'not ok ... # expected failure ...' and hard failself.assertRegex(stdout, rb"\nnot ok . .*test/example/check-example TestTodo.testTodoPass \(1\.7GB\) # expected failure: 2 is not yet sufficiently large\n")# There should have been 2 cases, one failself.assertEqual(process.returncode, 1)@testlib.nondestructiveclass TestTestlib(testlib.MachineCase):def testRestoreAPI(self):m = self.machineself.assertEqual(m.execute("whoami").strip(), "root")# existing filem.execute("echo original > /etc/someconfig")self.restore_file("/etc/someconfig")m.execute("echo changed > /etc/someconfig")# nonexisting fileself.restore_file("/var/lib/cockpittest.txt")m.execute("echo data > /var/lib/cockpittest.txt")# existing dirm.execute("mkdir -p /var/lib/existing_dir; echo hello > /var/lib/existing_dir/original")self.restore_dir("/var/lib/existing_dir")m.execute("rm /var/lib/existing_dir/original; echo pwned > /var/lib/existing_dir/new")# nonexisting dirself.restore_dir("/var/lib/cockpittestnew")m.execute("mkdir -p /var/lib/cockpittestnew; echo hello > /var/lib/cockpittestnew/cruft")# NSS is backed up by defaultm.execute("useradd cockpittest")# now pretend the test ends hereself.doCleanups()# correctly restored existing fileself.assertEqual("original", m.execute("cat /etc/someconfig").strip())m.execute("rm /etc/someconfig")# correctly cleaned up nonexisting filem.execute("test ! -e /var/lib/cockpittest.txt")# correctly restored existing dirself.assertEqual("original", m.execute("ls /var/lib/existing_dir").strip())self.assertEqual("hello\n", m.execute("cat /var/lib/existing_dir/original"))m.execute("rm -r /var/lib/existing_dir")# correctly removed nonexisting dirm.execute("test ! -e /var/lib/cockpittestnew")# NSS/home got restoredself.assertNotIn("cockpittest", self.machine.execute("cat /etc/passwd"))self.machine.execute("test ! -e /home/cockpittest")@testlib.skipBeiboot("no cockpit-system package installed in beiboot mode")def testMiscAPI(self):assert self.system_before(1000)assert not self.system_before(100)if __name__ == '__main__':testlib.test_main()