Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add xdist support #53

Merged
merged 7 commits into from
Sep 25, 2024
Merged

Add xdist support #53

merged 7 commits into from
Sep 25, 2024

Conversation

tfcollins
Copy link
Owner

@tfcollins tfcollins commented Sep 23, 2024

Summary by Sourcery

Add support for pytest-xdist to enable parallel test execution by introducing custom port handling and xdist markers. Update the iio_emu_manager class and logging configuration to accommodate these changes. Include pytest-xdist as a dependency and update the iio-emu installation script. Add tests to verify the new functionality.

New Features:

  • Introduce support for pytest-xdist to enable parallel test execution by adding custom port handling and xdist markers.

Enhancements:

  • Modify the iio_emu_manager class to support custom port configuration for parallel test execution.
  • Update logging configuration to create separate log files for each xdist worker.

Build:

  • Add pytest-xdist to the list of install requirements in setup.py.
  • Update the iio-emu installation script to clone the main branch instead of a specific version.

Tests:

  • Add new tests in tests/test_xdist_support.py to verify the plugin's compatibility with pytest-xdist.

Copy link

sourcery-ai bot commented Sep 23, 2024

Reviewer's Guide by Sourcery

This pull request adds support for pytest-xdist, enabling parallel test execution for the pytest-libiio plugin. The changes include modifications to handle custom ports for IIO emulators, adjustments to logging for parallel execution, and the addition of xdist markers for test distribution.

File-Level Changes

Change Details Files
Add xdist support to the pytest-libiio plugin
  • Introduce custom port handling for IIO emulators
  • Add xdist markers to split tests based on context
  • Modify logging configuration for parallel execution
  • Update _iio_emu fixture to support worker-specific ports
  • Add pytest-xdist as a dependency in setup.py
pytest_libiio/plugin.py
setup.py
Update iio-emu installation script
  • Change iio-emu repository branch from v0.1.0 to main
.github/scripts/install_iioemu.sh
Add new test file for xdist support
  • Create test cases for fmcomms2 and ad9081 hardware with xdist
  • Implement logging and device information printing in tests
tests/test_xdist_support.py

Sequence Diagram

sequenceDiagram
    participant Master as Master Process
    participant Worker1 as Worker 1
    participant Worker2 as Worker 2
    participant IIOEmu as IIO Emulator
    Master->>Worker1: Start with custom port
    Master->>Worker2: Start with custom port
    Worker1->>IIOEmu: Initialize (port 30432)
    Worker2->>IIOEmu: Initialize (port 30433)
    Worker1->>IIOEmu: Run tests
    Worker2->>IIOEmu: Run tests
    Worker1-->>Master: Report results
    Worker2-->>Master: Report results
Loading

Tips
  • Trigger a new Sourcery review by commenting @sourcery-ai review on the pull request.
  • Continue your discussion with Sourcery by replying directly to review comments.
  • You can change your review settings at any time by accessing your dashboard:
    • Enable or disable the Sourcery-generated pull request summary or reviewer's guide;
    • Change the review language;
  • You can always contact us if you have any questions or feedback.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @tfcollins - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 5 issues found
  • 🟢 Security: all looks good
  • 🟡 Testing: 4 issues found
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment to tell me if it was helpful.

@@ -14,17 +15,25 @@
import pytest_libiio.meta as meta
import yaml

IIO_EMU_BASE_PORT = 30431
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Consider a more dynamic port allocation strategy

While using a base port and incrementing it works for a small number of workers, it might not scale well for larger parallel test runs. Consider implementing a more dynamic port allocation strategy to avoid potential conflicts.

Suggested change
IIO_EMU_BASE_PORT = 30431
from contextlib import closing
import socket
def find_free_port():
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
s.bind(('', 0))
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
return s.getsockname()[1]
IIO_EMU_BASE_PORT = find_free_port()

auto: bool = True,
rx_dev: str = None,
tx_dev: str = None,
custom_port: int = None,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Add validation for the custom_port parameter

To ensure robustness, consider adding validation for the custom_port parameter. Check if it's within a valid port range (e.g., 1024-65535) before using it.

        custom_port: Optional[int] = None,

    @staticmethod
    def _validate_port(port: Optional[int]) -> None:
        if port is not None and not 1024 <= port <= 65535:
            raise ValueError("Custom port must be between 1024 and 65535")

    def __post_init__(self):
        self._validate_port(self.custom_port)

)


def pytest_collection_modifyitems(config, items):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Add error handling and logging in pytest_collection_modifyitems

Consider adding error handling and logging in this function, especially for cases where the context table is empty or there are issues with markers. This will make debugging easier if something goes wrong during test collection.

def pytest_collection_modifyitems(config, items):
    try:
        # Get list of contexts and mapped hardware
        class Object(object):
            pass
        context_objs = Object()
    except Exception as e:
        config.warn(f"pytest_libiio", f"Error in test collection: {str(e)}")
        return

worker_id = worker_id.replace("gw", "")
custom_port = IIO_EMU_BASE_PORT + int(worker_id)
logger = logging.getLogger()
logger.info(f"Using custom port {custom_port}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Use debug log level for custom port information

Consider using logger.debug() instead of logger.info() for logging the custom port. This information is more suitable for debug-level logging and will help prevent cluttering the logs during normal operation.

Suggested change
logger.info(f"Using custom port {custom_port}")
logger.debug(f"Using custom port {custom_port}")

@@ -293,14 +338,23 @@


@pytest.fixture(scope="session", autouse=True)
def _iio_emu(request):
def _iio_emu(request, worker_id):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Implement a more generic approach for worker identification

The current implementation is tightly coupled to the specific format of xdist worker IDs. Consider implementing a more generic approach for worker identification that doesn't rely on specific ID formats. This will make the code more flexible and easier to maintain if the worker ID format changes in the future.

def _iio_emu(request, get_worker_id=None):
    worker_id = get_worker_id() if get_worker_id else getattr(request.config, 'workerinput', {}).get('workerid', 'master')

Comment on lines +1 to +5
"""Tests for verifying plugin works with pytest-xdist."""

import pytest
import iio
import logging
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Add more comprehensive docstring

Consider expanding the module docstring to provide more context about what these tests are verifying and how they relate to the xdist support feature.

"""
Tests for verifying plugin compatibility with pytest-xdist.

This module contains tests that ensure the plugin functions correctly
when used with pytest-xdist for parallel test execution. It verifies
that the plugin's features work as expected in a distributed testing
environment.
"""

import pytest
import iio
import logging

Comment on lines +21 to +23
for dev in ctx.devices:
print(f"dev: {dev.name}, {dev.id}")
logging.info(f"dev: {dev.name}, {dev.id}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid loops in tests. (no-loop-in-tests)

ExplanationAvoid complex code, like loops, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +32 to +34
for dev in ctx.devices:
print(f"dev: {dev.name}, {dev.id}")
logging.info(f"dev: {dev.name}, {dev.id}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid loops in tests. (no-loop-in-tests)

ExplanationAvoid complex code, like loops, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines 60 to +63
for dev in self.data_devices:
cmd.append(f"{dev}@data.bin")
if self.custom_port:
cmd.append("-p")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

"""Initialization emulation fixture"""
if request.config.getoption("--emu"):
if worker_id == "master":
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation

Python has a number of builtin variables: functions and constants that
form a part of the language, such as list, getattr, and type
(See https://docs.python.org/3/library/functions.html).
It is valid, in the language, to re-bind such variables:

list = [1, 2, 3]

However, this is considered poor practice.

  • It will confuse other developers.
  • It will confuse syntax highlighters and linters.
  • It means you can no longer use that builtin for its original purpose.

How can you solve this?

Rename the variable something more specific, such as integers.
In a pinch, my_list and similar names are colloquially-recognized
placeholders.

@tfcollins tfcollins merged commit 8c38b15 into master Sep 25, 2024
16 checks passed
@tfcollins tfcollins deleted the tfcollins/xdist-support branch September 25, 2024 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant