-
Notifications
You must be signed in to change notification settings - Fork 637
[Backend Tester] Add test name filter #12625
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
Merged
Merged
Changes from 2 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
f120e70
Update
GregoryComer 0fb85e6
Update
GregoryComer ead0616
Update
GregoryComer 0f13676
Update
GregoryComer 06bf03a
Update
GregoryComer 2f8f49b
Update
GregoryComer d21492b
Update
GregoryComer e2c4ea5
Update
GregoryComer b35e7b1
Update
GregoryComer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# Copyright (c) Meta Platforms, Inc. and affiliates. | ||
# All rights reserved. | ||
# | ||
# This source code is licensed under the BSD-style license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
# pyre-unsafe | ||
|
||
import os | ||
import unittest | ||
|
||
from dataclasses import dataclass | ||
from types import ModuleType | ||
from typing import Pattern | ||
|
||
from executorch.backends.test.suite.flow import TestFlow | ||
|
||
# | ||
# This file contains logic related to test discovery and filtering. | ||
# | ||
|
||
|
||
@dataclass | ||
class TestFilter: | ||
"""A set of filters for test discovery.""" | ||
|
||
backends: set[str] | None | ||
""" The set of backends to include. If None, all backends are included. """ | ||
|
||
name_regex: Pattern[str] | None | ||
""" A regular expression to filter test names. If None, all tests are included. """ | ||
|
||
|
||
def discover_tests( | ||
root_module: ModuleType, test_filter: TestFilter | ||
) -> unittest.TestSuite: | ||
# Collect all tests using the unittest discovery mechanism then filter down. | ||
|
||
# Find the file system path corresponding to the root module. | ||
module_file = root_module.__file__ | ||
if module_file is None: | ||
raise RuntimeError(f"Module {root_module} has no __file__ attribute") | ||
|
||
loader = unittest.TestLoader() | ||
module_dir = os.path.dirname(module_file) | ||
suite = loader.discover(module_dir) | ||
|
||
return _filter_tests(suite, test_filter) | ||
|
||
|
||
def _filter_tests( | ||
suite: unittest.TestSuite, test_filter: TestFilter | ||
) -> unittest.TestSuite: | ||
# Recursively traverse the test suite and add them to the filtered set. | ||
filtered_suite = unittest.TestSuite() | ||
|
||
for child in suite: | ||
if isinstance(child, unittest.TestSuite): | ||
filtered_suite.addTest(_filter_tests(child, test_filter)) | ||
elif isinstance(child, unittest.TestCase): | ||
if _is_test_enabled(child, test_filter): | ||
filtered_suite.addTest(child) | ||
else: | ||
raise RuntimeError(f"Unexpected test type: {type(child)}") | ||
|
||
return filtered_suite | ||
|
||
|
||
def _is_test_enabled(test_case: unittest.TestCase, test_filter: TestFilter) -> bool: | ||
test_method = getattr(test_case, test_case._testMethodName) | ||
flow: TestFlow = getattr(test_method, "_flow") | ||
|
||
if test_filter.backends is not None and flow.backend not in test_filter.backends: | ||
return False | ||
|
||
if test_filter.name_regex is not None and not test_filter.name_regex.search( | ||
test_case.id() | ||
): | ||
return False | ||
|
||
return True |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import logging | ||
|
||
from dataclasses import dataclass | ||
from math import log | ||
from typing import Callable, Sequence | ||
|
||
from executorch.backends.test.harness import Tester | ||
|
||
logger = logging.getLogger(__name__) | ||
logger.setLevel(logging.INFO) | ||
|
||
|
||
@dataclass | ||
class TestFlow: | ||
""" | ||
A lowering flow to test. This typically corresponds to a combination of a backend and | ||
a lowering recipe. | ||
""" | ||
|
||
name: str | ||
""" The name of the lowering flow. """ | ||
|
||
backend: str | ||
""" The name of the target backend. """ | ||
|
||
tester_factory: Callable[[], Tester] | ||
""" A factory function that returns a Tester instance for this lowering flow. """ | ||
|
||
|
||
def create_xnnpack_flow() -> TestFlow | None: | ||
try: | ||
from executorch.backends.xnnpack.test.tester import Tester as XnnpackTester | ||
|
||
return TestFlow( | ||
name="xnnpack", | ||
backend="xnnpack", | ||
tester_factory=XnnpackTester, | ||
) | ||
except Exception: | ||
logger.info("Skipping XNNPACK flow registration due to import failure.") | ||
return None | ||
|
||
|
||
def create_coreml_flow() -> TestFlow | None: | ||
try: | ||
from executorch.backends.apple.coreml.test.tester import CoreMLTester | ||
|
||
return TestFlow( | ||
name="coreml", | ||
backend="coreml", | ||
tester_factory=CoreMLTester, | ||
) | ||
except Exception: | ||
logger.info("Skipping Core ML flow registration due to import failure.") | ||
return None | ||
|
||
|
||
def all_flows() -> Sequence[TestFlow]: | ||
flows = [ | ||
create_xnnpack_flow(), | ||
create_coreml_flow(), | ||
] | ||
return [f for f in flows if f is not None] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like we are reimplementing features from unittest or pytest :p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I've tried to avoid this, but unfortunately, it doesn't look like the unittest package structure is very extensible in the way I need. There aren't a lot of hooks to control reporting / filtering / discovery without writing custom driver code. I'm open to suggestions, but this seemed to be the lowest friction path with unittest. Switching to pytest might be an option, but I'm hoping that I don't need to do much more non-differentiated work like this.