Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: pytest

on:
pull_request:
workflow_dispatch:

jobs:
pytest:
name: "pytest"

strategy:
fail-fast: false
matrix:
include:
- name: "CentOS Stream 9"
image: "quay.io/centos/centos:stream9"
pytest_args: '--deselect test/rhsmlib_test/test_hwprobe.py::HardwareProbeTest::test_networkinfo --deselect test/rhsmlib_test/test_facts.py::TestFactsDBusObject::test_GetFacts'
# The 'test_networkinfo' breaks in CentOS container because it has IPv6 disabled.
# Because of a bug in Python, collecting 'socket.AF_INET6' via 'socket.getaddrinfo()' causes
# segfaults instead of exceptions. This deselect is a workaround until it is fixed or until
# we switch to a different way of collecting network facts.
# 'test_GetFacts' triggers full fact collection and causes the same error.
- name: "CentOS Stream 8"
image: "quay.io/centos/centos:stream8"
pytest_args: ''
- name: "Fedora latest"
image: "fedora:latest"
pytest_args: ''
- name: "Fedora Rawhide"
image: "fedora:rawhide"
pytest_args: ''

runs-on: ubuntu-latest
container:
image: ${{ matrix.image }}

steps:
- name: "Checkout repository"
uses: actions/checkout@v3

- name: "Run container-pre-test.sh"
run: |
bash scripts/container-pre-test.sh

- name: "Run pytest"
env:
SUBMAN_TEST_IN_CONTAINER: "1"
PYTEST_ADDOPTS: "--color=yes --code-highlight=yes --showlocals"
run: |
dbus-run-session \
python3 -m pytest ${{ matrix.pytest_args }}
27 changes: 27 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,33 @@ coverage html
coverage xml
```

## Containers

You can use Podman to run the test suite, ensuring your local setup does not differ from CI.

First, pick which container you'll want to use: CentOS Stream 8 (not supported by the main branch anymore), CentOS Stream 9, Fedora latest, Fedora Rawhide, ...:

```bash
IMAGE="quay.io/centos/centos:stream8"
IMAGE="quay.io/centos/centos:stream9"
IMAGE="registry.fedoraproject.org/fedora:latest"
IMAGE="registry.fedoraproject.org/fedora:rawhide"
```

Enter the container (assuming you are in the project root) and run a pre-test script that will set install the dependencies and compile C extensions:

```bash
podman run -it --rm --name subscription-manager -v .:/subscription-manager --privileged $IMAGE bash
cd /subscription-manager/
bash scripts/container-pre-test.sh
```

Then you can run the test suite. You have to use `dbus-run-session` wrapper, because D-Bus is not running in containers:

```bash
SUBMAN_TEST_IN_CONTAINER=1 dbus-run-session python3 -m pytest
```

---

Following text may not be up to date.
Expand Down
2 changes: 0 additions & 2 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
addopts =
# Non-registered markers raise errors
--strict-markers
# Start with tests that failed last time, --ff
--failed-first
# Show extra summary for (f)ailed, (E)rror, (s)kipped and (w)arnings
-rfEsw
# Mark some tests not to be run
Expand Down
40 changes: 40 additions & 0 deletions scripts/container-pre-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash

source /etc/os-release
# These repositories are required for the 'libdnf-devel' package.
# Fedora has it available out of the box.
# RHEL needs it to be enabled via 'subscription-manager repos'.
if [[ $ID == "centos" ]]; then
dnf --setopt install_weak_deps=False install -y dnf-plugins-core
if [[ $VERSION == "8" ]]; then
dnf config-manager --set-enabled powertools
fi
if [[ $VERSION == "9" ]]; then
dnf config-manager --enable crb
fi
fi

# Install essential packages
dnf --setopt install_weak_deps=False install -y \
git gcc cmake python3 python3-devel python3-pip

# Install system, build and runtime packages
dnf --setopt install_weak_deps=False install -y \
intltool dbus-daemon dbus-devel \
python3-setuptools \
openssl-devel glib2-devel libdnf-devel \
python3-rpm python3-librepo python3-gobject python3-gobject python3-dbus \
python3-dateutil python3-requests python3-iniparse python3-ethtool \
glibc-langpack-en glibc-langpack-de glibc-langpack-ja

# Install branch specific packages
dnf --setopt instal_week_deps=False install -y \
gtk3-devel

# Install test packages
python3 -m pip install --upgrade pip wheel
python3 -m pip install -r test-requirements.txt

# Build the project
python3 setup.py build
python3 setup.py build_ext --inplace
23 changes: 0 additions & 23 deletions src/rhsm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,29 +67,6 @@ class UnsupportedOperationException(Exception):
pass


def which(program):
"""
Function returning path of program (it could be path to executable file or command)
:param program: string with command
:return: Path to command, when some executable exists in system. Otherwise it returns None.
"""

def is_exe(_fpath):
return os.path.isfile(_fpath) and os.access(_fpath, os.X_OK)

fpath, fname = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file

return None


def has_bad_scheme(url):
"""Check a url for an invalid or unuseful schema.

Expand Down
4 changes: 2 additions & 2 deletions src/rhsmlib/facts/kpatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

import logging
import os
import shutil

from rhsmlib.facts import collector
from rhsm.utils import which

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -62,7 +62,7 @@ def _is_kpatch_installed():
Check if kpatch is installed
:return: Return true, when kpatch CLI tool is installed. Otherwise return False
"""
return which('kpatch') is not None
return shutil.which('kpatch') is not None

def _get_installed_live_kernel_patches(self):
"""
Expand Down
6 changes: 3 additions & 3 deletions src/subscription_manager/repolib.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from subscription_manager.repofile import YumRepoFile
from subscription_manager.utils import get_supported_resources

from rhsm.config import get_config_parser, in_container
import rhsm.config
import six
from six.moves import configparser

Expand All @@ -43,7 +43,7 @@

log = logging.getLogger(__name__)

conf = config.Config(get_config_parser())
conf = config.Config(rhsm.config.get_config_parser())

ALLOWED_CONTENT_TYPES = ["yum", "deb"]

Expand Down Expand Up @@ -295,7 +295,7 @@ def get_expansion(self):
# same consumer as the host they run on. They only have the same
# access to content as the host they run on.)
result = None
if not in_container():
if not rhsm.config.in_container():
uep = self.cp_provider.get_consumer_auth_cp()
result = self.release_status_cache.read_status(uep, self.identity.uuid)

Expand Down
2 changes: 1 addition & 1 deletion test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
-e ./syspurpose

flake8<4
pytest
pytest<7
pytest-randomly
pytest-timeout
pytest-forked
Expand Down
9 changes: 8 additions & 1 deletion test/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ def unstub_conf():
subscription_manager.managercli.conf = config.Config(self.mock_cfg_parser)
self.addCleanup(unstub_conf)

facts_host_patcher = patch('rhsmlib.dbus.facts.FactsClient', auto_spec=True)
facts_host_patcher = patch('rhsmlib.dbus.facts.FactsClient', autospec=True)
self.mock_facts_host = facts_host_patcher.start()
self.mock_facts_host.return_value.GetFacts.return_value = self.set_facts()

Expand Down Expand Up @@ -280,6 +280,13 @@ def unstub_conf():

set_up_mock_sp_store(syncedstore_mock)

# Do not read system files. Even if the tests run in container environment,
# report that we are running on bare metal.
self.in_container_patcher = patch("rhsm.config.in_container")
in_container_mock = self.in_container_patcher.start()
in_container_mock.return_value = False
self.addCleanup(self.in_container_patcher.stop)

self.files_to_cleanup = []

def tearDown(self):
Expand Down
4 changes: 2 additions & 2 deletions test/rhsm/functional/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def setUp(self):

@patch.object(Restlib, 'validateResponse')
@patch('rhsm.connection.drift_check', return_value=False)
@patch('httplib.HTTPSConnection', auto_spec=True)
@patch('httplib.HTTPSConnection', autospec=True)
def test_bind_no_args(self, mock_conn, mock_drift, mock_validate):

self.cp.bind(self.consumer_uuid)
Expand All @@ -225,7 +225,7 @@ def test_bind_no_args(self, mock_conn, mock_drift, mock_validate):

@patch.object(Restlib, 'validateResponse')
@patch('rhsm.connection.drift_check', return_value=False)
@patch('httplib.HTTPSConnection', auto_spec=True)
@patch('httplib.HTTPSConnection', autospec=True)
def test_bind_by_pool(self, mock_conn, mock_drift, mock_validate):
# this test is just to verify we make the httplib connection with
# right args, we don't validate the bind here
Expand Down
22 changes: 1 addition & 21 deletions test/rhsm/unit/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
ServerUrlParseErrorEmpty, ServerUrlParseErrorNone, \
ServerUrlParseErrorPort, ServerUrlParseErrorScheme, \
ServerUrlParseErrorJustScheme, has_bad_scheme, has_good_scheme, \
parse_url, cmd_name, which
parse_url, cmd_name
from rhsm.config import DEFAULT_PORT, DEFAULT_PREFIX, DEFAULT_HOSTNAME


Expand Down Expand Up @@ -417,23 +417,3 @@ def test_rhsm_debug(self):
def test_virt_who(self):
argv = ['/usr/share/virt-who/virtwho.py']
self.assertEqual("virtwho.py", cmd_name(argv))


class TestWhich(unittest.TestCase):
def test_which_python(self):
"""Some python command just has to exist :-)"""
cmd_path = which('python')
self.assertIsNotNone(cmd_path)

def test_which_bin_sh(self):
"""Assumed that Linux is used and some /bin/sh exist"""
cmd_path = which('/bin/sh')
self.assertIsNotNone(cmd_path)

def test_which_not_existing_command(self):
cmd_path = which('not-existing-command')
self.assertIsNone(cmd_path)

def test_which_not_existing_command_path(self):
cmd_path = which('/not/existing/command/path/not-existing-command')
self.assertIsNone(cmd_path)
18 changes: 7 additions & 11 deletions test/rhsmlib_test/test_attach.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,32 +164,28 @@ class TestAttachDBusObject(DBusServerStubProvider):
dbus_class = AttachDBusObject
dbus_class_kwargs = {}

@classmethod
def setUpClass(cls) -> None:
def setUp(self) -> None:
is_simple_content_access_patch = mock.patch(
"rhsmlib.dbus.objects.attach.is_simple_content_access",
name="is_simple_content_access",
)
cls.patches["is_simple_content_access"] = is_simple_content_access_patch.start()
cls.addClassCleanup(is_simple_content_access_patch.stop)
self.patches["is_simple_content_access"] = is_simple_content_access_patch.start()
self.addCleanup(is_simple_content_access_patch.stop)

is_registered_patch = mock.patch(
"rhsmlib.dbus.base_object.BaseObject.is_registered",
name="is_registered",
)
cls.patches["is_registered"] = is_registered_patch.start()
cls.addClassCleanup(is_registered_patch.stop)
self.patches["is_registered"] = is_registered_patch.start()
self.addCleanup(is_registered_patch.stop)

update_patch = mock.patch(
"subscription_manager.certlib.BaseActionInvoker.update",
name="update",
)
cls.patches["update"] = update_patch.start()
cls.addClassCleanup(update_patch.stop)

super().setUpClass()
self.patches["update"] = update_patch.start()
self.addCleanup(update_patch.stop)

def setUp(self) -> None:
self.patches["is_simple_content_access"].return_value = False
self.patches["is_registered"].return_value = True
self.patches["update"].return_value = None
Expand Down
3 changes: 1 addition & 2 deletions test/rhsmlib_test/test_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ def test_get_aarch64_uuid_collection(self):
result = firmware_provider_class.get_all()
self.assertTrue(result['dmi.system.uuid'] == '356B6CCC-30C4-11B2-A85C-BBB0CCD29F36')

@mock.patch(OPEN_FUNCTION, mock.MagicMock(side_effect=IOError()))
def test_get_aarch64_uuid_collection_no_file(self):
mock.mock_open(read_data="no file")
mock.mock_open.side_effect = IOError()
firmware_provider_class = firmware_info.get_firmware_collector(arch='aarch64')
firmware_provider_class.arch = 'aarch64'
result = firmware_provider_class.get_all()
Expand Down
9 changes: 4 additions & 5 deletions test/rhsmlib_test/test_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,15 @@ class TestConsumerDbusObject(DBusServerStubProvider):
dbus_class = ConsumerDBusObject
dbus_class_kwargs = {}

@classmethod
def setUpClass(cls) -> None:
def setUp(self) -> None:
get_consumer_uuid_patch = mock.patch(
"rhsmlib.dbus.objects.consumer.Consumer.get_consumer_uuid",
name="get_consumer_uuid",
)
cls.patches["get_consumer_uuid"] = get_consumer_uuid_patch.start()
cls.addClassCleanup(get_consumer_uuid_patch)
self.patches["get_consumer_uuid"] = get_consumer_uuid_patch.start()
self.addCleanup(get_consumer_uuid_patch.stop)

super().setUpClass()
super().setUp()

def test_GetUuid(self):
self.patches["get_consumer_uuid"].return_value = "fake-uuid"
Expand Down
11 changes: 3 additions & 8 deletions test/rhsmlib_test/test_facts.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,14 @@ class TestFactsDBusObject(DBusServerStubProvider):
dbus_class = AllFacts
dbus_class_kwargs = {}

@classmethod
def setUpClass(cls) -> None:
# Do not try to use system virt-what
def setUp(self) -> None:
get_virt_info_patch = mock.patch(
"rhsmlib.facts.virt.VirtWhatCollector.get_virt_info",
name="get_virt_info",
)
cls.patches["get_virt_info"] = get_virt_info_patch.start()
cls.addClassCleanup(get_virt_info_patch.stop)

super().setUpClass()
self.patches["get_virt_info"] = get_virt_info_patch.start()
self.addCleanup(get_virt_info_patch.stop)

def setUp(self) -> None:
self.patches["get_virt_info"].return_value = {"virt.is_guest": "Unknown"}

super().setUp()
Expand Down
Loading