aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/__init__.py (renamed from tests/path/to/leaf)0
-rw-r--r--tests/report_tests/__init__.py1
-rw-r--r--tests/report_tests/basic_report_tests.py80
-rw-r--r--tests/report_tests/plugin_tests/__init__.py (renamed from tests/ziptest)0
-rw-r--r--tests/report_tests/plugin_tests/networking.py31
-rw-r--r--tests/report_tests/report_with_mask.py69
-rwxr-xr-xtests/simple.sh272
-rw-r--r--tests/sos_tests.py490
-rw-r--r--tests/unittests/__init__.py0
-rw-r--r--tests/unittests/archive_tests.py (renamed from tests/archive_tests.py)0
-rw-r--r--tests/unittests/cleaner_tests.py (renamed from tests/cleaner_tests.py)0
-rw-r--r--tests/unittests/importer_tests.py (renamed from tests/importer_tests.py)0
-rw-r--r--tests/unittests/option_tests.py (renamed from tests/option_tests.py)0
-rw-r--r--tests/unittests/path/to/leaf0
-rw-r--r--tests/unittests/plugin_tests.py (renamed from tests/plugin_tests.py)0
-rw-r--r--tests/unittests/policy_tests.py (renamed from tests/policy_tests.py)0
-rw-r--r--tests/unittests/report_tests.py (renamed from tests/report_tests.py)0
-rw-r--r--tests/unittests/sosreport_pexpect.py (renamed from tests/sosreport_pexpect.py)0
-rw-r--r--tests/unittests/tail_test.txt (renamed from tests/tail_test.txt)0
-rw-r--r--tests/unittests/test.txt (renamed from tests/test.txt)0
-rw-r--r--tests/unittests/utilities_tests.py (renamed from tests/utilities_tests.py)0
-rw-r--r--tests/unittests/ziptest0
l---------tests/unittests/ziptest_link (renamed from tests/ziptest_link)0
-rw-r--r--tests/vendor_tests/__init__.py0
-rw-r--r--tests/vendor_tests/redhat/__init__.py0
-rw-r--r--tests/vendor_tests/redhat/rhbz1928628.py44
26 files changed, 715 insertions, 272 deletions
diff --git a/tests/path/to/leaf b/tests/__init__.py
index e69de29b..e69de29b 100644
--- a/tests/path/to/leaf
+++ b/tests/__init__.py
diff --git a/tests/report_tests/__init__.py b/tests/report_tests/__init__.py
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/tests/report_tests/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tests/report_tests/basic_report_tests.py b/tests/report_tests/basic_report_tests.py
new file mode 100644
index 00000000..0cf5bd4e
--- /dev/null
+++ b/tests/report_tests/basic_report_tests.py
@@ -0,0 +1,80 @@
+# This file is part of the sos project: https://github.com/sosreport/sos
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# version 2 of the GNU General Public License.
+#
+# See the LICENSE file in the source distribution for further information.
+
+
+from sos_tests import StageOneReportTest
+
+
+class NormalSoSReport(StageOneReportTest):
+ """
+ :avocado: tags=stageone
+ """
+
+ sos_cmd = '-vvv --label thisismylabel'
+
+ def test_debug_in_logs_verbose(self):
+ self.assertSosLogContains('DEBUG')
+
+ def test_postproc_called(self):
+ self.assertSosLogContains('substituting scrpath')
+
+ def test_label_applied_to_archive(self):
+ self.assertTrue('thisismylabel' in self.archive)
+
+ def test_free_symlink_created(self):
+ self.assertFileCollected('free')
+
+
+class RestrictedSoSReport(StageOneReportTest):
+ """
+ :avocado: tags=stageone
+ """
+
+ sos_cmd = '-o kernel,host,sudo,hardware,dbus,x11 --no-env-var --no-report -t1 --no-postproc'
+
+ def test_no_env_vars_collected(self):
+ self.assertFileNotCollected('environment')
+
+ def test_no_reports_generated(self):
+ self.assertFileNotCollected('sos_reports/sos.html')
+ self.assertFileNotCollected('sos_reports/sos.json')
+ self.assertFileNotCollected('sos_reports/sos.txt')
+
+ def test_was_single_threaded_run(self):
+ self.assertOutputNotContains('Finishing plugins')
+
+ def test_postproc_not_called(self):
+ self.assertOutputNotContains('substituting')
+
+ def test_only_selected_plugins_run(self):
+ self.assertOnlyPluginsIncluded(['kernel', 'host', 'sudo', 'hardware', 'dbus', 'x11'])
+
+
+class DisabledCollectionsReport(StageOneReportTest):
+ """
+ :avocado: tags=stageone
+ """
+
+ sos_cmd = "-n networking,system,logs --skip-files=/etc/fstab --skip-commands='journalctl*'"
+
+ def test_plugins_disabled(self):
+ self.assertPluginNotIncluded('networking')
+ self.assertPluginNotIncluded('system')
+ self.assertPluginNotIncluded('logs')
+
+ def test_skipped_plugins_have_no_dir(self):
+ self.assertFileNotCollected('sos_commands/networking/')
+ self.assertFileNotCollected('sos_commands/system/')
+ self.assertFileNotCollected('sos_commands/logs/')
+
+ def test_skip_files_working(self):
+ self.assertFileNotCollected('/etc/fstab')
+
+ def test_skip_commands_working(self):
+ self.assertFileGlobNotInArchive('sos_commands/*/journalctl*')
+
diff --git a/tests/ziptest b/tests/report_tests/plugin_tests/__init__.py
index e69de29b..e69de29b 100644
--- a/tests/ziptest
+++ b/tests/report_tests/plugin_tests/__init__.py
diff --git a/tests/report_tests/plugin_tests/networking.py b/tests/report_tests/plugin_tests/networking.py
new file mode 100644
index 00000000..f6cfa2d8
--- /dev/null
+++ b/tests/report_tests/plugin_tests/networking.py
@@ -0,0 +1,31 @@
+# This file is part of the sos project: https://github.com/sosreport/sos
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# version 2 of the GNU General Public License.
+#
+# See the LICENSE file in the source distribution for further information.
+
+
+from sos_tests import StageOneReportTest
+
+
+class NetworkingPluginTest(StageOneReportTest):
+ """
+ Basic tests to ensure proper collection from the networking plugins
+
+ :avocado: tags=stageone
+ """
+
+ sos_cmd = '-o networking'
+
+ def test_common_files_collected(self):
+ self.assertFileCollected('/etc/resolv.conf')
+ self.assertFileCollected('/etc/hosts')
+
+ def test_ip_addr_symlink_created(self):
+ self.assertFileCollected('ip_addr')
+
+ def test_forbidden_globs_skipped(self):
+ self.assertFileGlobNotInArchive('/proc/net/rpc/*/channel')
+ self.assertFileGlobNotInArchive('/proc/net/rpc/*/flush')
diff --git a/tests/report_tests/report_with_mask.py b/tests/report_tests/report_with_mask.py
new file mode 100644
index 00000000..a62888ae
--- /dev/null
+++ b/tests/report_tests/report_with_mask.py
@@ -0,0 +1,69 @@
+# This file is part of the sos project: https://github.com/sosreport/sos
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# version 2 of the GNU General Public License.
+#
+# See the LICENSE file in the source distribution for further information.
+
+from sos_tests import StageOneReportTest
+
+import re
+
+
+class ReportWithMask(StageOneReportTest):
+ """Testing around basic --clean/--mask usage and expectations
+
+ :avocado: tags=stageone
+ """
+
+ sos_cmd = '--mask -o host,networking'
+
+ def test_mask_was_run(self):
+ self.assertOutputContains('Beginning obfuscation')
+ self.assertOutputContains('Obfuscation completed')
+
+ def test_private_map_was_generated(self):
+ self.assertOutputContains('A mapping of obfuscated elements is available at')
+ map_file = re.findall('/.*sosreport-.*-private_map', self.cmd_output.stdout)[-1]
+ self.assertFileExists(map_file)
+
+ def test_tarball_named_obfuscated(self):
+ self.assertTrue('obfuscated' in self.archive)
+
+ def test_localhost_was_obfuscated(self):
+ self.assertFileHasContent('/etc/hostname', 'host0')
+
+ def test_ip_address_was_obfuscated(self):
+ # Note: do not test for starting with the 100.* block here, as test
+ # machines may have /32 addresses. Instead, test that the actual
+ # IP address is not present
+ self.assertFileNotHasContent('ip_addr', self.sysinfo['pre']['networking']['ip_addr'])
+
+ def test_loopback_was_not_obfuscated(self):
+ self.assertFileHasContent('ip_addr', '127.0.0.1/8')
+
+ def test_mac_addrs_were_obfuscated(self):
+ content = self.get_file_content('sos_commands/networking/ip_maddr_show')
+ for line in content.splitlines():
+ if line.strip().startswith('link'):
+ mac = line.strip().split()[1]
+ assert mac.startswith('53:4f:53'), "Found unobfuscated mac addr %s" % mac
+
+
+class ReportWithCleanedKeywords(StageOneReportTest):
+ """Testing for obfuscated keywords provided by the user
+
+ :avocado: tags=stageone
+ """
+
+ sos_cmd = '--clean -o filesys,kernel --keywords=fstab,Linux'
+
+ # Ok, sort of cheesy here but this does actually test filename changes on
+ # a file common to all distros
+ def test_filename_obfuscated(self):
+ self.assertFileNotCollected('/etc/fstab')
+ self.assertFileGlobInArchive('/etc/obfuscatedword*')
+
+ def test_keyword_obfuscated_in_file(self):
+ self.assertFileNotHasContent('sos_commands/kernel/uname_-a', 'Linux')
diff --git a/tests/simple.sh b/tests/simple.sh
deleted file mode 100755
index 3eb123c1..00000000
--- a/tests/simple.sh
+++ /dev/null
@@ -1,272 +0,0 @@
-#!/bin/bash
-# This file is part of the sos project: https://github.com/sosreport/sos
-#
-# This copyrighted material is made available to anyone wishing to use,
-# modify, copy, or redistribute it subject to the terms and conditions of
-# version 2 of the GNU General Public License.
-#
-# See the LICENSE file in the source distribution for further information.
-# A quick port of the travis tests to bash, requires root
-# TODO
-# * look into using a framework..
-# * why --dry-run fails?
-# * why --experimental fails?
-# * make it better validate archives and contents
-
-PYTHON=${1:-/usr/bin/python3}
-SOSPATH=${2:-./bin/sos report --batch --tmp-dir=/var/tmp }
-
-NUMOFFAILURES=0
-summary="\nSummary\n"
-FAIL_LIST=""
-
-run_expecting_success () {
- #$1 - is command options
- #$2 - kind of check to do, so far only extract
- FAIL=false
- # Make sure clean
- rm -f /dev/shm/stderr /dev/shm/stdout /var/tmp/sosreport*.tar.*
- rm -rf /var/tmp/sosreport_test/
-
- start=`date +%s`
- echo "######### RUNNING $1 #########"
- $PYTHON $SOSPATH $1 2> /dev/shm/stderr 1> /dev/shm/stdout
-
- if [ $? -eq 0 ]; then
- echo "### Success"
- else
- echo "!!! FAILED !!!"
- add_failure "$1 failed during execution"
- fi
-
- end=`date +%s`
- runtime=$((end-start))
- echo "#### Sos Total time (seconds):" $runtime
-
- if [ -s /dev/shm/stderr ]; then
- add_failure "test generated stderr output, see above"
- echo "### start stderr"
- cat /dev/shm/stderr
- echo "### end stderr"
- fi
-
- echo "### start stdout"
- cat /dev/shm/stdout
- echo "### end stdout"
-
- if [ "extract" = "$2" ]; then
- echo "### start extraction"
- rm -f /var/tmp/sosreport*sha256
- mkdir /var/tmp/sosreport_test/
- tar xfa /var/tmp/sosreport*.tar* -C /var/tmp/sosreport_test --strip-components=1
- if [ -s /var/tmp/sosreport_test/sos_logs/*errors.txt ]; then
- FAIL=true
- echo "!!! FAILED !!!"
- add_failure "Test $1 generated errors"
- echo "#### *errors.txt output"
- ls -alh /var/tmp/sosreport_test/sos_logs/
- cat /var/tmp/sosreport_test/sos_logs/*errors.txt
- fi
- echo "### stop extraction"
- fi
-
- echo "######### DONE WITH $1 #########"
-
- if $FAIL; then
- NUMOFFAILURES=$(($NUMOFFAILURES + 1))
- return 1
- else
- return 0
- fi
-}
-
-update_summary () {
- size="$(grep Size /dev/shm/stdout)"
- size="$(echo "${size:-"Size 0.00MiB"}")"
- summary="${summary} \n failures ${FAIL} \t time ${runtime} \t ${size} \t ${1} "
-}
-
-update_failures () {
- if $FAIL; then
- NUMOFFAILURES=$(($NUMOFFAILURES + 1))
- fi
-}
-
-add_failure () {
- FAIL=true
- echo "!!! TEST FAILED: $1 !!!"
- FAIL_LIST="${FAIL_LIST}\n \t ${FUNCNAME[1]}: \t\t ${1}"
-}
-
-# Test a no frills run with verbosity and make sure the expected items exist
-test_normal_report () {
- cmd="-vvv"
- # get a list of initial kmods loaded
- kmods=( $(lsmod | cut -f1 -d ' ' | sort) )
- run_expecting_success "$cmd" extract
- if [ $? -eq 0 ]; then
- if [ ! -f /var/tmp/sosreport_test/sos_reports/sos.html ]; then
- add_failure "did not generate html reports"
- fi
- if [ ! -f /var/tmp/sosreport_test/sos_reports/manifest.json ]; then
- add_failure "did not generate manifest.json"
- fi
- if [ ! -f /var/tmp/sosreport_test/free ]; then
- add_failure "did not create free symlink in archive root"
- fi
- if [ ! "$(grep "DEBUG" /var/tmp/sosreport_test/sos_logs/sos.log)" ]; then
- add_failure "did not find debug logging when using -vvv"
- fi
- # new list, see if we added any
- new_kmods=( $(lsmod | cut -f1 -d ' ' | sort) )
- if [ "$(printf '%s\n' "${kmods[@]}" "${new_kmods[@]}" | sort | uniq -u)" ]; then
- add_failure "new kernel modules loaded during execution"
- echo "$(printf '%s\n' "${kmods[@]}" "${new_kmods[@]}" | sort | uniq -u)"
- fi
- update_failures
- update_summary "$cmd"
- fi
-}
-
-# Test for correctly skipping html generation, and label setting
-test_noreport_label_only () {
- cmd="--no-report --label TEST -o hardware"
- run_expecting_success "$cmd" extract
- if [ $? -eq 0 ]; then
- if [ -f /var/tmp/sosreport_test/sos_reports/sos.html ]; then
- add_failure "html report generated when --no-report used"
- fi
- if [ ! $(grep /var/tmp/sosreport-*TEST* /dev/shm/stdout) ]; then
- add_failure "no label set on archive"
- fi
- count=$(find /var/tmp/sosreport_test/sos_commands/* -type d | wc -l)
- if [[ "$count" -gt 1 ]]; then
- add_failure "more than one plugin ran when using -o hardware"
- fi
- update_failures
- fi
- update_summary "$cmd"
-}
-
-# test using mask
-test_mask () {
- cmd="--mask"
- run_expecting_success "$cmd" extract
- if [ $? -eq 0 ]; then
- if [ ! $(grep host0 /var/tmp/sosreport_test/hostname) ]; then
- add_failure "hostname not obfuscated with --mask"
- fi
- # we don't yet support binary obfuscation, so skip binary matches
- if [ "$(grep -rI `hostname` /var/tmp/sosreport_test/*)" ]; then
- add_failure "hostname not obfuscated in all places"
- echo "$(grep -rI `hostname` /var/tmp/sosreport_test/*)"
- fi
- # only tests first interface
- mac_addr=$(cat /sys/class/net/$(ip route show default | awk '/default/ {print $5}')/address)
- if [ "$(grep -rI $mac_addr /var/tmp/sosreport_test/*)" ]; then
- add_failure "MAC address not obfuscated in all places"
- echo "$(grep -rI $mac_addr /var/tmp/sosreport_test/*)"
- fi
- # only tests first interface
- ip_addr=$(ip route show default | awk '/default/ {print $3}')
- if [ "$(grep -rI $ip_addr /var/tmp/sosreport_test/*)" ]; then
- add_failure "IP address not obfuscated in all places"
- echo "$(grep -rI $ip_addr /var/tmp/sosreport_test/*)"
- fi
- update_failures
- fi
- update_summary "$cmd"
-}
-
-# test log-size, env vars, and compression type
-test_logsize_env_gzip () {
- cmd="--log-size 0 --no-env-vars -z gzip"
- run_expecting_success "$cmd" extract
- if [ $? -eq 0 ]; then
- if [ -f /var/tmp/sosreport_test/environment ]; then
- add_failure "env vars captured when using --no-env-vars"
- fi
- if [ ! $(grep /var/tmp/sosreport*.gz /dev/shm/stdout) ]; then
- add_failure "archive was not gzip compressed using -z gzip"
- fi
- update_failures
- fi
- update_summary "$cmd"
-}
-
-# test plugin enablement, plugopts and at the same time ensure our list option parsing is working
-test_enable_opts_postproc () {
- cmd="-e opencl -v -k kernel.with-timer,libraries.ldconfigv --no-postproc"
- run_expecting_success "$cmd" extract
- if [ $? -eq 0 ]; then
- if [ ! "$(grep "opencl" /dev/shm/stdout)" ]; then
- add_failure "force enabled plugin opencl did not run"
- fi
- if [ ! -f /var/tmp/sosreport_test/proc/timer* ]; then
- add_failure "/proc/timer* not captured when using -k kernel.with-timer"
- fi
- if [ ! -f /var/tmp/sosreport_test/sos_commands/libraries/ldconfig_-v* ]; then
- add_failure "ldconfig -v not captured when using -k libraries.ldconfigv"
- fi
- if [ "$(grep "substituting" /var/tmp/sosreport_test/sos_logs/sos.log)" ]; then
- add_failure "post-processing ran while using --no-post-proc"
- fi
-
- update_failures
- update_summary "$cmd"
- fi
-}
-
-# test if --build and --threads work properly
-test_build_threads () {
- cmd="--build -t1 -o host,kernel,filesys,hardware,date,logs"
- run_expecting_success "$cmd"
- if [ $? -eq 0 ]; then
- if [ ! "$(grep "Your sosreport build tree" /dev/shm/stdout)" ]; then
- add_failure "did not save the build tree"
- fi
- if [ $(grep "Finishing plugins" /dev/shm/stdout) ]; then
- add_failure "did not limit threads when using --threads 1"
- fi
- update_failures
- update_summary "$cmd"
- fi
-}
-
-# If /etc/sos/sos.conf doesn't exist let's just make it
-if [ -f /etc/sos/sos.conf ]; then
- echo "/etc/sos/sos.conf already exists"
-else
- echo "Creating /etc/sos.conf"
- mkdir /etc/sos
- touch /etc/sos/sos.conf
-fi
-
-
-# Runs not generating sosreports
-run_expecting_success " -l"; update_summary "List plugins"
-run_expecting_success " --list-presets"; update_summary "List presets"
-run_expecting_success " --list-profiles"; update_summary "List profiles"
-
-# Runs generating sosreports
-# TODO:
-# - find a way to test if --since is working
-test_build_threads
-test_normal_report
-test_enable_opts_postproc
-test_noreport_label_only
-test_logsize_env_gzip
-test_mask
-
-echo -e $summary
-
-if [ $NUMOFFAILURES -gt 0 ]; then
- echo -e "\nTests Failed: $NUMOFFAILURES\nFailures within each test:"
- echo -e $FAIL_LIST
- exit 1
-else
- echo "Everything worked!"
- exit 0
-fi
-
-# vim: set et ts=4 sw=4 :
diff --git a/tests/sos_tests.py b/tests/sos_tests.py
new file mode 100644
index 00000000..8da0195d
--- /dev/null
+++ b/tests/sos_tests.py
@@ -0,0 +1,490 @@
+# This file is part of the sos project: https://github.com/sosreport/sos
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# version 2 of the GNU General Public License.
+#
+# See the LICENSE file in the source distribution for further information.
+
+
+from avocado.core.exceptions import TestSkipError
+from avocado import Test
+from avocado.utils import archive, process
+from fnmatch import fnmatch
+
+import glob
+import json
+import os
+import pickle
+import socket
+import re
+
+SOS_TEST_DIR = os.path.dirname(os.path.realpath(__file__))
+SOS_BIN = os.path.realpath(os.path.join(SOS_TEST_DIR, '../bin/sos'))
+
+
+def skipIf(cond, message=None):
+ def decorator(function):
+ def wrapper(self, *args, **kwargs):
+ if callable(cond):
+ if cond(self):
+ raise TestSkipError(message)
+ elif cond:
+ raise TestSkipError(message)
+ return wrapper
+ return decorator
+
+
+class BaseSoSTest(Test):
+ """Base class for all our test classes to build off of.
+
+ Subclasses avocado.Test and then adds wrappers and helper methods that are
+ needed across sos components. Component specific test classes should in
+ turn subclass ``BaseSoSTest`` rather than ``avocado.Test`` directly
+ """
+
+ _klass_name = None
+ _tmpdir = None
+ sos_cmd = ''
+
+ @property
+ def klass_name(self):
+ if not self._klass_name:
+ self._klass_name = os.path.basename(__file__) + '.' + self.__class__.__name__
+ return self._klass_name
+
+ @property
+ def tmpdir(self):
+ if not self._tmpdir:
+ self._tmpdir = os.getenv('AVOCADO_TESTS_COMMON_TMPDIR') + self.klass_name
+ return self._tmpdir
+
+ def generate_sysinfo(self):
+ """Collects some basic information about the system for later reference
+ in individual tests
+ """
+ sysinfo = {}
+
+ # get kernel modules
+ mods = []
+ _out = process.run('lsmod').stdout.decode()
+ for line in _out.splitlines()[1:]:
+ mods.append(line.split()[0])
+ # this particular kmod is both innocuous and unpredictable in terms of
+ # pre-loading even within the same distribution. For now, turn a blind
+ # eye to it with regards to the "no new kmods loaded" perspective
+ if 'binfmt_misc' in mods:
+ mods.remove('binfmt_misc')
+ sysinfo['modules'] = sorted(mods, key=str.lower)
+
+ # get networking info
+ hostname = socket.gethostname()
+ ip_addr = socket.gethostbyname(hostname)
+ sysinfo['networking'] = {}
+ sysinfo['networking']['hostname'] = hostname
+ sysinfo['networking']['ip_addr'] = ip_addr
+
+ return sysinfo
+
+ def _write_file_to_tmpdir(self, fname, content):
+ """Write the given content to fname within the test's tmpdir
+ """
+ fname = os.path.join(self.tmpdir, fname)
+ if isinstance(content, bytes):
+ content = content.decode()
+ with open(fname, 'w') as wfile:
+ wfile.write(content)
+
+ def read_file_from_tmpdir(self, fname):
+ fname = os.path.join(self.tmpdir, fname)
+ with open(fname, 'r') as tfile:
+ return tfile.read()
+ return ''
+
+ def _write_sysinfo(self, fname):
+ """Get the current state of sysinfo and write it into our shared
+ tempdir so it can be loaded in setUp() later
+
+ :param fname: The name of the file to be written in the tempdir
+ :type fname: ``str``
+ """
+ sysinfo = self.generate_sysinfo()
+ self._write_file_to_tmpdir(fname, json.dumps(sysinfo))
+
+ def _read_sysinfo(self, fname):
+ sysinfo = {}
+ content = self.read_file_from_tmpdir(fname)
+ if content:
+ sysinfo = json.loads(content)
+ return sysinfo
+
+ def set_pre_sysinfo(self):
+ self._write_sysinfo('pre_sysinfo')
+
+ def get_pre_sysinfo(self):
+ return self._read_sysinfo('pre_sysinfo')
+
+ def set_post_sysinfo(self):
+ self._write_sysinfo('post_sysinfo')
+
+ def get_post_sysinfo(self):
+ return self._read_sysinfo('post_sysinfo')
+
+ def get_sysinfo(self):
+ sinfo = {
+ 'pre': self.get_pre_sysinfo(),
+ 'post': self.get_post_sysinfo()
+ }
+ return sinfo
+
+ def assertFileExists(self, fname):
+ """Asserts that fname exists on the filesystem"""
+ assert os.path.exists(fname), "%s does not exist" % fname
+
+ def assertFileNotExists(self, fname):
+ """Asserts that fname does not exist on the filesystem"""
+ assert not os.path.exists(fname), "%s exists" % fname
+
+
+class BaseSoSReportTest(BaseSoSTest):
+ """This is the class to use for building sos report tests with.
+
+ An instance of this test is expected to set at minimum a ``sos_cmd`` class
+ attribute that represets the options handed to a specific execution of an
+ sos command. This should be anything following ``sos report --batch``.
+
+ """
+
+ archive = None
+ _manifest = None
+
+
+ @property
+ def manifest(self):
+ if self._manifest is None:
+ try:
+ content = self.read_file_from_tmpdir(self.get_name_in_archive('sos_reports/manifest.json'))
+ self._manifest = json.loads(content)
+ except Exception:
+ self._manifest = ''
+ self.warn('Could not load manifest for test')
+ return self._manifest
+
+ def _extract_archive(self, arc_path):
+ """Extract an archive to the temp directory
+ """
+ _extract_path = self._get_extracted_tarball_path()
+ try:
+ archive.extract(arc_path, _extract_path)
+ self.archive_path = self._get_archive_path()
+ except Exception as err:
+ self.cancel("Could not extract archive: %s" % err)
+
+ def _get_extracted_tarball_path(self):
+ """Based on the klass id setup earlier, provide a name to extract the
+ archive to within the tmpdir
+ """
+ return os.path.join(self.tmpdir, "sosreport-%s" % self.__class__.__name__)
+
+
+ def _execute_sos_cmd(self):
+ """Run the sos command for this test case, and extract it
+ """
+ _cmd = '%s report --batch --tmp-dir %s %s'
+ exec_cmd = _cmd % (SOS_BIN, self.tmpdir, self.sos_cmd)
+ self.cmd_output = process.run(exec_cmd, timeout=300)
+ with open(os.path.join(self.tmpdir, 'output'), 'wb') as pfile:
+ pickle.dump(self.cmd_output, pfile)
+ self.cmd_output.stdout = self.cmd_output.stdout.decode()
+ self.cmd_output.stderr = self.cmd_output.stderr.decode()
+ self.archive = re.findall('/.*sosreport-.*tar.*', self.cmd_output.stdout)[-1]
+ if self.archive:
+ self._extract_archive(self.archive)
+
+
+ def _setup_tmpdir(self):
+ if not os.path.isdir(self.tmpdir):
+ os.mkdir(self.tmpdir)
+
+ def _get_archive_path(self):
+ return glob.glob(self._get_extracted_tarball_path() + '/sosreport*')[0]
+
+ def setup_mocking(self):
+ """Since we need to use setUp() in our overrides of avocado.Test,
+ provide an alternate method for test cases that subclass BaseSoSTest
+ to use.
+ """
+ pass
+
+ def setUp(self):
+ """Execute and extract the sos report to our temporary location, then
+ call sos_setup() for individual test case setup and/or mocking.
+ """
+ # check to prevent multiple setUp() runs
+ if not os.path.isdir(self.tmpdir):
+ # setup our class-shared tmpdir
+ self._setup_tmpdir()
+
+ # do our mocking called for in sos_setup
+ self.setup_mocking()
+
+ # gather some pre-execution information
+ self.set_pre_sysinfo()
+
+ # run the sos command for this test case
+ self._execute_sos_cmd()
+ self.set_post_sysinfo()
+ else:
+ with open(os.path.join(self.tmpdir, 'output'), 'rb') as pfile:
+ self.cmd_output = pickle.load(pfile)
+ if isinstance(self.cmd_output.stdout, bytes):
+ self.cmd_output.stdout = self.cmd_output.stdout.decode()
+ self.cmd_output.stderr = self.cmd_output.stderr.decode()
+ for f in os.listdir(self.tmpdir):
+ if fnmatch(f, 'sosreport*.tar.??'):
+ self.archive = os.path.join(self.tmpdir, f)
+ break
+ self.sysinfo = self.get_sysinfo()
+ self.archive_path = self._get_archive_path()
+
+ def get_name_in_archive(self, fname):
+ """Get the full path to fname as it (would) exist in the archive
+ """
+ return os.path.join(self.archive_path, fname.lstrip('/'))
+
+ def get_file_content(self, fname):
+ """Reads the content of fname from within the archive and returns it
+
+ :param fname: The name of the file
+ :type fname: ``str``
+
+ :returns: Content of fname
+ :rtype: ``str``
+ """
+ content = ''
+ with open(self.get_name_in_archive(fname), 'r') as gfile:
+ content = gfile.read()
+ return content
+
+ def assertFileCollected(self, fname):
+ """Ensure that a given fname is in the extracted archive if it exists
+ on the host system
+
+ :param fname: The name of the file within the archive
+ :type fname: ``str``
+ """
+ if os.path.exists(fname):
+ self.assertFileExists(self.get_name_in_archive(fname))
+ assert True
+
+ def assertFileNotCollected(self, fname):
+ """Ensure that a given fname is NOT in the extracted archive
+
+ :param fname: The name of the file within the archive
+ :type fname: ``str``
+ """
+ self.assertFileNotExists(self.get_name_in_archive(fname))
+
+ def assertFileGlobInArchive(self, fname):
+ """Ensure that at least one file in the archive matches a given fname
+ glob
+
+ :param fname: The glob to match filenames of
+ :type fname: ``str``
+ """
+ files = glob.glob(os.path.join(self.archive_path, fname.lstrip('/')))
+ assert files, "No files matching %s found" % fname
+
+ def assertFileGlobNotInArchive(self, fname):
+ """Ensure that there are NO files in the archive matching a given fname
+ glob
+
+ :param fname: The glob to match filename(s) of
+ :type fname: ``str``
+ """
+ files = glob.glob(os.path.join(self.tmpdir, fname.lstrip('/')))
+ self.log.debug(files)
+ assert not files, "Found files in archive matching %s: %s" % (fname, files)
+
+ def assertFileHasContent(self, fname, content):
+ """Ensure that the given file fname contains the given content
+
+ :param fname: The name of the file
+ :type fname: ``str``
+
+ :param content: The content to match
+ :type content: ``str``
+ """
+ matched = False
+ fname = self.get_name_in_archive(fname)
+ self.assertFileExists(fname)
+ with open(fname, 'r') as lfile:
+ _contents = lfile.read()
+ for line in _contents.splitlines():
+ if re.match(".*%s.*" % content, line, re.I):
+ matched = True
+ break
+ assert matched, "Content '%s' does not appear in %s\n%s" % (content, fname, _contents)
+
+ def assertFileNotHasContent(self, fname, content):
+ """Ensure that the file file fname does NOT contain the given content
+
+ :param fname: The name of the file
+ :type fname: ``str``
+
+ :param content: The content to (not) match
+ :type content: ``str``
+ """
+ matched = False
+ fname = self.get_name_in_archive(fname)
+ with open(fname, 'r') as mfile:
+ for line in mfile.read().splitlines():
+ if re.match(".*%s.*" % content, line, re.I):
+ matched = True
+ break
+ assert not matched, "Content '%s' appears in file %s" % (content, fname)
+
+ def assertSosLogContains(self, content):
+ """Ensure that the given content string exists in sos.log
+ """
+ self.assertFileHasContent('sos_logs/sos.log', content)
+
+ def assertSosLogNotContains(self, content):
+ """Ensure that the given content string does NOT exist in sos.log
+ """
+ self.assertFileNotHasContent('sos_logs/sos.log', content)
+
+ def assertOutputContains(self, content):
+ """Ensure that stdout did contain the given content string
+
+ :param content: The string that should not be in stdout
+ :type content: ``str``
+ """
+ assert content in self.cmd_output.stdout, 'Content string not in output'
+
+ def assertOutputNotContains(self, content):
+ """Ensure that stdout did NOT contain the given content string
+
+ :param content: The string that should not be in stdout
+ :type content: ``str``
+ """
+ assert not re.match(".*%s.*" % content, self.cmd_output.stdout), "String '%s' present in stdout" % content
+
+ def assertPluginIncluded(self, plugin):
+ """Ensure that the specified plugin did run for the sos execution
+
+ Note that this relies on manifest.json being successfully created
+
+ :param plugin: The name of the plugin
+ :type plugin: `` str``
+ """
+ if not self.manifest:
+ self.error("No manifest found, cannot check for %s execution" % plugin)
+ assert plugin in self.manifest['components']['report']['plugins'].keys(), 'Plugin not recorded in manifest'
+
+ def assertPluginNotIncluded(self, plugin):
+ """Ensure that the specified plugin did NOT run for the sos execution
+ Note that this relies on manifest.json being successfully created
+
+ :param plugin: The name of the plugin
+ :type plugin: `` str``
+ """
+ if not self.manifest:
+ self.error("No manifest found, cannot check for %s execution" % plugin)
+ assert plugin not in self.manifest['components']['report']['plugins'].keys(), 'Plugin is recorded in manifest'
+
+ def assertOnlyPluginsIncluded(self, plugins):
+ """Ensure that only the specified plugins are in the manifest
+
+ :param plugins: The plugin names
+ :type plugins: ``str`` or ``list`` of strings
+ """
+ if not self.manifest:
+ self.error("No manifest found, cannot check for %s execution" % plugins)
+ if isinstance(plugins, str):
+ plugins = [plugins]
+ _executed = self.manifest['components']['report']['plugins'].keys()
+
+ # test that all requested plugins did run
+ for i in plugins:
+ assert i in _executed, "Requested plugin '%s' did not run" % i
+
+ # test that no unrequested plugins ran
+ for j in _executed:
+ assert j in plugins, "Unrequested plugin '%s' ran as well" % j
+
+class StageOneReportTest(BaseSoSReportTest):
+ """This is the test class to subclass for all Stage One (no mocking) tests
+ within the sos test suite.
+
+ In addition to any test_* methods defined in the test cases that subclass
+ this, the methods defined here will ALSO run, to ensure basic consistency
+ across test cases
+
+ NOTE: You MUST replace the following line in the docstring of your own
+ test cases, as otherwise the test will be disabled. This line is here to
+ prevent this base class from being treated as a valid test case. Also, if
+ you add any tests to this base class, make sure to add a line such as
+ ':avocado: tags=stageone' to ensure the base tests run with new test cases
+
+ :avocado: disable
+ :avocado: tags=stageone
+ """
+
+ sos_cmd = ''
+
+ def test_archive_created(self):
+ """Ensure that the archive tarball was created and has the right owner
+
+ :avocado: tags=stageone
+ """
+ self.assertFileExists(self.archive)
+ self.assertTrue(os.stat(self.archive).st_uid == 0)
+
+ def test_no_new_kmods_loaded(self):
+ """Ensure that no additional kernel modules have been loaded during an
+ execution of a test
+
+ :avocado: tags=stageone
+ """
+ self.assertCountEqual(self.sysinfo['pre']['modules'],
+ self.sysinfo['post']['modules'])
+
+ def test_archive_has_sos_dirs(self):
+ """Ensure that we have the expected directory layout with in the
+ archive
+
+ :avocado: tags=stageone
+ """
+ self.assertFileCollected('sos_commands')
+ self.assertFileCollected('sos_logs')
+
+ def test_manifest_created(self):
+ """
+ :avocado: tags=stageone
+ """
+ self.assertFileCollected('sos_reports/manifest.json')
+
+ @skipIf(lambda x: '--no-report' in x.sos_cmd, '--no-report used in command')
+ def test_html_reports_created(self):
+ """
+ :avocado: tags=stageone
+ """
+ self.assertFileCollected('sos_reports/sos.html')
+
+ def test_no_exceptions_during_execution(self):
+ """
+ :avocado: tags=stageone
+ """
+ self.assertSosLogNotContains('caught exception in plugin')
+ self.assertFileGlobNotInArchive('sos_logs/*-plugin-errors.txt')
+
+ def test_no_ip_changes(self):
+ """
+ :avocado: tags=stageone
+ """
+ # I.E. make sure we didn't cause any NIC flaps that for some reason
+ # resulted in a new primary IP address. TODO: build this out to make
+ # sure this IP is still bound to the same NIC
+ self.assertEqual(self.sysinfo['pre']['networking']['ip_addr'],
+ self.sysinfo['post']['networking']['ip_addr'])
diff --git a/tests/unittests/__init__.py b/tests/unittests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/unittests/__init__.py
diff --git a/tests/archive_tests.py b/tests/unittests/archive_tests.py
index 320006d0..320006d0 100644
--- a/tests/archive_tests.py
+++ b/tests/unittests/archive_tests.py
diff --git a/tests/cleaner_tests.py b/tests/unittests/cleaner_tests.py
index 5510dd80..5510dd80 100644
--- a/tests/cleaner_tests.py
+++ b/tests/unittests/cleaner_tests.py
diff --git a/tests/importer_tests.py b/tests/unittests/importer_tests.py
index a2dddaba..a2dddaba 100644
--- a/tests/importer_tests.py
+++ b/tests/unittests/importer_tests.py
diff --git a/tests/option_tests.py b/tests/unittests/option_tests.py
index 58f54e94..58f54e94 100644
--- a/tests/option_tests.py
+++ b/tests/unittests/option_tests.py
diff --git a/tests/unittests/path/to/leaf b/tests/unittests/path/to/leaf
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/unittests/path/to/leaf
diff --git a/tests/plugin_tests.py b/tests/unittests/plugin_tests.py
index 2f362c94..2f362c94 100644
--- a/tests/plugin_tests.py
+++ b/tests/unittests/plugin_tests.py
diff --git a/tests/policy_tests.py b/tests/unittests/policy_tests.py
index 6d0c42b9..6d0c42b9 100644
--- a/tests/policy_tests.py
+++ b/tests/unittests/policy_tests.py
diff --git a/tests/report_tests.py b/tests/unittests/report_tests.py
index bb059012..bb059012 100644
--- a/tests/report_tests.py
+++ b/tests/unittests/report_tests.py
diff --git a/tests/sosreport_pexpect.py b/tests/unittests/sosreport_pexpect.py
index 3614fa5b..3614fa5b 100644
--- a/tests/sosreport_pexpect.py
+++ b/tests/unittests/sosreport_pexpect.py
diff --git a/tests/tail_test.txt b/tests/unittests/tail_test.txt
index 8def0f72..8def0f72 100644
--- a/tests/tail_test.txt
+++ b/tests/unittests/tail_test.txt
diff --git a/tests/test.txt b/tests/unittests/test.txt
index d95f3ad1..d95f3ad1 100644
--- a/tests/test.txt
+++ b/tests/unittests/test.txt
diff --git a/tests/utilities_tests.py b/tests/unittests/utilities_tests.py
index 64be9f1e..64be9f1e 100644
--- a/tests/utilities_tests.py
+++ b/tests/unittests/utilities_tests.py
diff --git a/tests/unittests/ziptest b/tests/unittests/ziptest
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/unittests/ziptest
diff --git a/tests/ziptest_link b/tests/unittests/ziptest_link
index e99bb13c..e99bb13c 120000
--- a/tests/ziptest_link
+++ b/tests/unittests/ziptest_link
diff --git a/tests/vendor_tests/__init__.py b/tests/vendor_tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/vendor_tests/__init__.py
diff --git a/tests/vendor_tests/redhat/__init__.py b/tests/vendor_tests/redhat/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/vendor_tests/redhat/__init__.py
diff --git a/tests/vendor_tests/redhat/rhbz1928628.py b/tests/vendor_tests/redhat/rhbz1928628.py
new file mode 100644
index 00000000..00746658
--- /dev/null
+++ b/tests/vendor_tests/redhat/rhbz1928628.py
@@ -0,0 +1,44 @@
+# This file is part of the sos project: https://github.com/sosreport/sos
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# version 2 of the GNU General Public License.
+#
+# See the LICENSE file in the source distribution for further information.
+
+
+from report_tests.plugin_tests.networking import NetworkingPluginTest
+
+
+class rhbz1928628(NetworkingPluginTest):
+ """
+ Only collect an eeprom dump when requested, as otherwise this can cause
+ NIC flaps.
+
+ https://bugzilla.redhat.com/show_bug.cgi?id=1928628
+
+ :avocado: enable
+ :avocado: tags=stageone
+ """
+
+ sos_cmd = '-o networking'
+
+ def test_eeprom_dump_not_collected(self):
+ self.assertFileGlobNotInArchive('sos_commands/networking/ethtool_-e*')
+
+
+class rhbz1928628Enabled(NetworkingPluginTest):
+ """
+ Enable the option to perform eeprom collection.
+
+ WARNING: it has been noted (via this rhbz) that certain NICs may pause
+ during this collection
+
+ :avocado: enable
+ :avocado: tags=stageone
+ """
+
+ sos_cmd = '-o networking -k networking.eepromdump=on'
+
+ def test_eeprom_dump_collected(self):
+ self.assertFileGlobInArchive('sos_commands/networking/ethtool_-e*')