Skip to content

Conversation

@Louciole
Copy link
Member

No description provided.

Copilot finished reviewing on behalf of Louciole November 13, 2025 09:14
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the reftests plugin from a monolithic single-file structure into a well-organized modular architecture, improving code maintainability and separation of concerns.

Key Changes:

  • Extracts a 523-line monolithic reftest.py into separate modules with clear responsibilities
  • Separates HTML/CSS/JavaScript from Python code into dedicated static asset files
  • Introduces a WebReport class to encapsulate report generation logic
  • Creates utility functions for file fetching and message retrieval

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
meta/plugins/reftest.py Deleted - replaced by modular structure in reftests/ directory
meta/plugins/__init__.py Updated import to use new reftests module instead of old reftest
meta/plugins/reftests/__init__.py New module initialization file
meta/plugins/reftests/reftest.py Main reftest logic extracted and cleaned up from original file
meta/plugins/reftests/utils.py Utility functions for file/message fetching extracted from main module
meta/plugins/reftests/WebReport.py Web report generation logic encapsulated in dedicated class
meta/plugins/reftests/report.js JavaScript code extracted from inline HTML string to separate file
meta/plugins/reftests/report.css CSS styles extracted from inline HTML string to separate file

print(f"Running {file.relative_to(TESTS_DIR)}...")

def getContainer(test_content: str) -> str | None:
searchContainer = re.search(r"""<container>([\w\W]+?)</container>""", test)
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

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

Variable test_content used instead of test in function call. The function parameter is named test_content, but the function body references a variable test on line 239 that doesn't exist in this scope, which will cause a runtime error.

Suggested change
searchContainer = re.search(r"""<container>([\w\W]+?)</container>""", test)
searchContainer = re.search(r"""<container>([\w\W]+?)</container>""", test_content)

Copilot uses AI. Check for mistakes.
Copy link
Member

@sleepy-monax sleepy-monax left a comment

Choose a reason for hiding this comment

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

This is going in a good direction

Comment on lines +14 to +29
def __init__(self, SOURCE_DIR: Path, TEST_REPORT: Path):
self.TEST_REPORT: Path = TEST_REPORT
self.html = f"""
<!DOCTYPE html>
<html>
<head>
<title>Reftest</title>
<script src="{SOURCE_DIR}/report.js"></script>
<link rel="stylesheet" href="{SOURCE_DIR}/report.css" />
</head>
<body>
<header>
Reftest report
</header>
"""
self.testHtml = ""
Copy link
Member

Choose a reason for hiding this comment

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

SOURCE_DIR and TEST_REPORT should not be constant case

print()
print("Failed tests details:")
print(context.testFailed)
raise RuntimeError("Some tests failed")
Copy link
Member

Choose a reason for hiding this comment

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

This should be the responsability of the test runner

Copy link
Member

Choose a reason for hiding this comment

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

(raising the error I mean)

"""
self.testHtml = ""

def addTestCase(self, testId: int, passed: bool, test: TestCase):
Copy link
Member

Choose a reason for hiding this comment

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

TestId should be a member of TestCase

def addSkippedCase(self):
pass

def addTestCategory(self, testId: int, props, file: Path, passCount: int, failCount: int, skippedCount: int):
Copy link
Member

Choose a reason for hiding this comment

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

Should pass TestResults instead of a bunch of primitives

def addSkipped(self) -> None:
self.skipped += 1

def merge(self, other: 'TestResults') -> None:
Copy link
Member

Choose a reason for hiding this comment

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

"merge" is a bit too generic, maybe something like "sumWith"

Comment on lines +93 to +99
def shouldRunSkipped(self) -> bool:
"""Check if skipped tests should be run."""
return self.args.runSkipped

def shouldStopOnFailure(self) -> bool:
"""Check if execution should stop on first failure."""
return self.args.fast
Copy link
Member

Choose a reason for hiding this comment

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

technically these could be marked as @Property, but fine as is

Copy link
Member

Choose a reason for hiding this comment

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

lol pinged a random guy

Comment on lines +137 to +167
class ReportManager:
"""Manages all test reporting operations."""

def __init__(self, reporters: list[Reporter]) -> None:
self._reporters: list[Reporter] = reporters

def reportTestCase(self, testId: int, passed: bool, test: TestCase) -> None:
"""Report a single test case result."""
for reporter in self._reporters:
reporter.addTestCase(testId, passed, test)

def reportSkippedCase(self) -> None:
"""Report a skipped test case."""
for reporter in self._reporters:
reporter.addSkippedCase()

def reportSkippedFile(self, testId: int, props: dict[str, str]) -> None:
"""Report a skipped test file."""
for reporter in self._reporters:
reporter.addSkippedFile(testId, props)

def reportTestCategory(self, testId: int, props: dict[str, str], file: Path,
passCount: int, failCount: int, skippedCount: int) -> None:
"""Report results for a test category."""
for reporter in self._reporters:
reporter.addTestCategory(testId, props, file, passCount, failCount, skippedCount)

def finish(self, manifests: model.Registry, results: TestResults, context: TestRunnerContext) -> None:
"""Finish reporting and display final results."""
for reporter in self._reporters:
reporter.finish(manifests, results.failed, results.passed, results.skipped, context)
Copy link
Member

Choose a reason for hiding this comment

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

Since it doesn't really manage anything but instead dispatches to other reporter implementations, I think a better name would be ReportRouter, ReportDispatcher, ReportMux, ReportMultiplexer, etc.

Also, since it has the same interface as Reporter, let's be honest and have it implement the Reporter abstract base class.

Comment on lines +173 to +176
def __init__(self, context: TestRunnerContext, reportManager: ReportManager) -> None:
self._context: TestRunnerContext = context
self._reportManager: ReportManager = reportManager
self._parser: TestParser = TestParser()
Copy link
Member

Choose a reason for hiding this comment

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

And here, take the reporter ABC, instead of the dispatch specialisation

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.

2 participants