Skip to content

Commit

Permalink
CI: Add test_usb_reset.py: Test setup_cgroup() of scripts/usb_reset.py
Browse files Browse the repository at this point in the history
Signed-off-by: Bernhard Kaindl <[email protected]>
  • Loading branch information
bernhardkaindl committed Jan 15, 2024
1 parent fed3403 commit 85d7f82
Show file tree
Hide file tree
Showing 5 changed files with 509 additions and 16 deletions.
115 changes: 99 additions & 16 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,113 @@ on:
# run daily, this refreshes the cache
- cron: '13 2 * * *'

# Cancel a currently running workflow from the same PR, branch or tag
# when a new workflow is triggered:
# https://stackoverflow.com/questions/66335225/how-to-cancel-previous-runs-in-the-pr-when-you-push-new-commitsupdate-the-curre
concurrency:
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}

jobs:
python-test:
name: Python tests
python2-test:
name: Python2 tests
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
docker-image: # See https://hub.docker.com/_/python/tags for images
- python:2.7.18-alpine3.11
- python:3.11.4-alpine3.18
container: ${{ matrix.docker-image }}
container: python:2.7.18-alpine3.11
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Install python 2 dependencies
if: ${{ startsWith(matrix.docker-image, 'python:2.7.18') }}
run: pip install enum
- name: Install Python2 dependencies
run: pip install enum mock pytest-coverage pytest-mock

- name: Install dependencies
run: pip install mock pytest
- name: Run Python2 tests
run: >
pytest --cov scripts scripts/ -vv -rA
--junitxml=.git/pytest27.xml
--cov-report term-missing
--cov-report html:.git/coverage27.html
--cov-report xml:.git/coverage27.xml
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
directory: .git
files: coverage27.xml
env_vars: OS,PYTHON
fail_ci_if_error: false
flags: python2.7
name: coverage27
verbose: true

- name: Add Pytest coverage comment (if write permission is available)
if: ${{ ! github.event.pull_request.head.repo.fork }}
uses: MishaKav/pytest-coverage-comment@main
continue-on-error: true
with:
junitxml-path: .git/pytest.xml
pytest-xml-coverage-path: .git/coverage27.xml
unique-id-for-comment: pre-commit-coverage-python27
title: >
Python2 coverage comment from
https://github.com/marketplace/actions/pytest-coverage-comment
python3-test:
name: Python3 tests
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v3

# https://www.python4data.science/en/latest/productive/git/advanced/hooks/ci.html
- uses: actions/setup-python@v4
id: python
with:
python-version: '3.10'
cache: 'pip'

- uses: actions/cache@v3
name: Setup cache for running pre-commit fast
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}

- run: echo "::add-matcher::.github/workflows/Python-problemMatcher-xen-api.json"

- uses: pre-commit/[email protected]
name: Run pre-commit checks
with:
# Show the output of the commands for the problem matcher, see above.
extra_args: --all-files --verbose
env:
PYTHONDEVMODE: yes # Enable Python3 checks. See the comment above.
# Skip the no-commit-to-branch check inside of GitHub CI (for CI on merge to master)
SKIP: no-commit-to-branch

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
directory: .git
env_vars: OS,PYTHON
# Whether the job should fail if Codecov runs into an error during upload.
# Not failing the job in this case is ok because the pre-commit checks
# also contain a diff-cover job which would fail on missing changed lines:
fail_ci_if_error: false
flags: ${{ format('python{0}', steps.python.outputs.python-version ) }}
name: coverage
verbose: true

- name: Add Pytest coverage comment (if write permission is available)
if: ${{ ! github.event.pull_request.head.repo.fork }}
uses: MishaKav/pytest-coverage-comment@main
continue-on-error: true
with:
junitxml-path: .git/pytest.xml
pytest-xml-coverage-path: .git/coverage.xml
unique-id-for-comment: pre-commit-coverage
title: >
Python3 coverage comment from
https://github.com/marketplace/actions/pytest-coverage-comment
- name: Run python tests
run: pytest scripts
ocaml-test:
name: Ocaml tests
Expand Down
61 changes: 61 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
# On the initial run, pre-commit downloads its checkers, subsequent runs are fast:
#
# $ pre-commit run # automatically checks which checks are needed.
# $ pre-commit run -a # runs all fixes and checks, even when not needed
#
# When this works, you can enable it as the pre-commit hook for this git clone:
# $ pre-commit install
# $ pre-commit install --hook-type pre-push
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: no-commit-to-branch
name: "Check against about accidental commits to the master branch"
args: [--branch, master]
always_run: true
- id: trailing-whitespace
files: ^(tests/unit_test|.github)
- id: end-of-file-fixer
files: ^(tests/unit_test|.github)
- id: check-yaml
- id: check-added-large-files


- repo: local
hooks:
- id: pytest
name: pytest
entry: env PYTHONDEVMODE=yes python3 -m pytest
--cov scripts scripts
--junitxml=.git/pytest.xml
--cov-report term-missing
--cov-report html:.git/coverage.html
--cov-report xml:.git/coverage.xml
require_serial: true
pass_filenames: false
language: python
types: [python]
additional_dependencies:
- mock
- pytest-coverage
- pytest-mock


- repo: https://github.com/pycqa/pylint
rev: v3.0.3
hooks:
- id: pylint
files: ^scripts/unit_tests/
log_file: ".git/pre-commit-pylint.log"
additional_dependencies: [mock, pytest]


- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
files: ^scripts/unit_tests/
additional_dependencies: [pytest-mock, types-mock]
58 changes: 58 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
[MAIN]

# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use, and will cap the count on Windows to
# avoid hangs.
jobs=2

# Pickle collected data for later comparisons.
persistent=yes

# Discover python modules and packages in the file system subtree.
recursive=yes

# Add paths to the list of the source roots. Supports globbing patterns. The
# source root is an absolute path or a path relative to the current working
# directory used to determine a package namespace for modules located under the
# source root.
source-roots=
ocaml/xapi-storage/python,
scripts,
scripts/examples/python,

# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes

# In verbose mode, extra non-checker-related info will be displayed.
verbose=yes

[MESSAGES CONTROL]

# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once). You can also use "--disable=all" to
# disable everything first and then re-enable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
disable=
bad-option-value, # old pylint for py2: ignore newer (unknown) pylint options
bad-continuation, # old pylint warns about some modern black formatting
useless-option-value, # new pylint has abaondoned these old options

[BASIC]

# Good variable names which should always be accepted, separated by a comma.
good-names=a,
b,
c,
f,
i,
j,
k,
ex,
Run,
_
66 changes: 66 additions & 0 deletions scripts/unit_tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Pytest conftest module with functions and (in the future) pytest fixtures"""
import logging
import os
import sys

import mock
import pytest


# Add an attribute when running under pytest to make it possible for a module
# to check to know if unittest is used (then skip) or pytest (then run test):


def pytest_configure(config): # pylint: disable=unused-argument
"""Configure pytest test cases
This function is called by pytest during its configuration phase. It takes a
single argument 'config', which is an instance of the pytest Config object.
The purpose of this function is to set a flag in the sys module to indicate
that it is being called from pytest. This flag can be used by other parts
of the code to determine if they are running under pytest or not.
:param config: The pytest configuration object.
"""
sys._called_from_pytest = True # pylint: disable=protected-access


def pytest_unconfigure(config): # pylint: disable=unused-argument
"""Perform cleanup or finalization steps after pytest has finished running.
This function is called by pytest after all tests have been executed and
pytest is about to exit. It can be used to perform any necessary cleanup
or finalization steps.
:param config: The pytest configuration object.
"""
del sys._called_from_pytest


@pytest.fixture(scope="function")
def usb_reset():
"""
This function is a pytest fixture that sets up the necessary environment
for testing the 'usb_reset' script.
It mocks the 'xcp' and 'xcp.logger' modules, and adds the directory of the
unit test to the sys.path so that the tested script can be imported.
The imported 'usb_reset.log' is redirected to the 'logging' module.
:returns: The imported 'usb_reset' module.
"""
sys.modules["xcp"] = mock.Mock()
sys.modules["xcp.logger"] = mock.Mock()

# Prepend the directory of the unit test (to import the tested script) to sys.path,
# so we can do an absolute import and absolute patch without adding __init__.py:
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))

# Then, import usb_reset.py from the __file__'s directory (found first there):
import usb_reset as usb_reset_module # pylint: disable=import-outside-toplevel

# Redirect usb_reset.log (is a mock xcp.logger for import) to logging
usb_reset_module.log = logging
return usb_reset_module
Loading

0 comments on commit 85d7f82

Please sign in to comment.