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

Only run Travis tests for plugins either added or modified by PR #36

Merged
merged 49 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
efd5223
add option to specify more than one test file
Jun 21, 2023
700a8bd
run tests of plugins changed by pr
Jun 22, 2023
d415099
Merge branch 'main' of https://github.com/brain-score/core into kvf/r…
Jun 22, 2023
2f3931c
run all test files in plugin dir
Jun 22, 2023
3f448f6
cleanup
Jun 22, 2023
337d30b
add notification that tests are running
Jun 22, 2023
503c52e
get_scoring_info -> get_plugin_info
Jun 22, 2023
146da61
Documentation
kvfairchild Jun 27, 2023
b066cbe
Documentation
kvfairchild Jun 27, 2023
e5f5162
cleanup
Jun 27, 2023
4b684c6
Documentation
kvfairchild Jun 27, 2023
48cee74
Documentation
kvfairchild Jun 27, 2023
e65915b
Refactor
kvfairchild Jun 27, 2023
18f9a49
Refactor
kvfairchild Jun 27, 2023
d22aa18
cleanup
Jun 27, 2023
ef97d2d
specify path for git cmd
Jun 27, 2023
98c0965
add plugin change parsing tests
Jun 29, 2023
f3bc366
travis TEST
Jun 29, 2023
7303912
change test SHAs
Jun 29, 2023
38d0e6a
change test SHAs
Jun 29, 2023
78ddb63
Update brainscore_core/plugin_management/parse_plugin_changes.py
kvfairchild Jul 3, 2023
21be65d
Update brainscore_core/plugin_management/parse_plugin_changes.py
kvfairchild Jul 3, 2023
ddf5c65
Update brainscore_core/plugin_management/parse_plugin_changes.py
kvfairchild Jul 3, 2023
4d44070
Update brainscore_core/plugin_management/parse_plugin_changes.py
kvfairchild Jul 3, 2023
19a04f8
Update brainscore_core/plugin_management/parse_plugin_changes.py
kvfairchild Jul 3, 2023
477209b
Update brainscore_core/plugin_management/parse_plugin_changes.py
kvfairchild Jul 3, 2023
bb0b1ab
Update brainscore_core/plugin_management/parse_plugin_changes.py
kvfairchild Jul 3, 2023
8920368
Update brainscore_core/plugin_management/parse_plugin_changes.py
kvfairchild Jul 3, 2023
dd15a43
Update tests/test_plugin_management/test_parse_plugin_changes.py
kvfairchild Jul 3, 2023
0d7a68b
Update tests/test_plugin_management/test_parse_plugin_changes.py
kvfairchild Jul 3, 2023
f63de37
Update tests/test_plugin_management/test_parse_plugin_changes.py
kvfairchild Jul 3, 2023
ce47a12
Update tests/test_plugin_management/test_parse_plugin_changes__brains…
kvfairchild Jul 3, 2023
abdfe12
Update tests/test_plugin_management/test_parse_plugin_changes__brains…
kvfairchild Jul 3, 2023
3e64802
Update tests/test_plugin_management/test_parse_plugin_changes__brains…
kvfairchild Jul 3, 2023
fefec4d
Update brainscore_core/plugin_management/test_plugins.py
kvfairchild Jul 3, 2023
f601d0e
add comment re: printing output
Jul 6, 2023
31c9ee5
var naming
Jul 6, 2023
bb1a64f
test git access
Jul 6, 2023
986017f
test git access
Jul 6, 2023
13b4635
exclude pr_only tests
Jul 19, 2023
2495b3a
change conditional to type
Aug 4, 2023
dfcbbe3
split jobs
Aug 6, 2023
1bf2229
format
Aug 6, 2023
18af27c
format
Aug 6, 2023
f8c13f4
stage jobs
Aug 6, 2023
95942a7
YAML Additions (#38)
qasim-at-tci Aug 9, 2023
6b0e5e9
mark test_get_all_changed_files pr_only
Aug 10, 2023
d6a20b5
Merge branch 'kvf/reduce-language-test-time' of https://github.com/br…
Aug 10, 2023
1fbc3a5
add pr_only to markers
Aug 10, 2023
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
142 changes: 142 additions & 0 deletions brainscore_core/plugin_management/parse_plugin_changes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import logging
import re
import subprocess
import sys
from pathlib import Path
from typing import List, Tuple, Dict

from .test_plugins import run_args

kvfairchild marked this conversation as resolved.
Show resolved Hide resolved
_logger = logging.getLogger(__name__)

PLUGIN_DIRS = ['models', 'benchmarks', 'data', 'metrics']


def get_all_changed_files(commit_SHA: str, comparison_branch='main') -> List[str]:
"""
:return: a list of file paths, relative to the library root directory, e.g. `['models/mymodel/__init__.py', 'models/mymodel/model.py', 'models/mymodel/test.py']`
"""
core_dir = Path(__file__).parents[2]
cmd = f'git diff --name-only {comparison_branch} {commit_SHA} -C {core_dir}'
files_changed_bytes = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE).stdout.splitlines()
kvfairchild marked this conversation as resolved.
Show resolved Hide resolved
files_changed = [f.decode() for f in files_changed_bytes]

return files_changed


def separate_plugin_files(files: List[str]) -> Tuple[List[str], List[str]]:
"""
:return: one list of files that are located inside a plugin, and one list of files that are located outside of all plugins,
e.g. `['models/mymodel/__init__.py', 'models/mymodel/model.py', 'models/mymodel/test.py'], ['model_helpers/make_model_brainlike.py']`
"""
plugin_files = []
non_plugin_files = []

for f in files:
subdir = f.split('/')[1] if len(f.split('/')) > 1 else None
if not any(plugin_dir == subdir for plugin_dir in PLUGIN_DIRS):
non_plugin_files.append(f)
else:
plugin_files.append(f)

return plugin_files, non_plugin_files


def _plugin_name_from_path(path_relative_to_library: str) -> str:
"""
Returns the name of the plugin from the given path.
E.g. `_plugin_name_from_path("brainscore_vision/models/mymodel")` will return `"mymodel"`.
"""
return path_relative_to_library.split('/')[2]


def get_plugin_paths(plugin_files: List[str], domain_root: str) -> Dict[str, List[str]]:
"""
Returns a dictionary `plugin_type -> plugin names` with the full names of all plugin directories for each plugin_type
"""
plugins = {}
for plugin_type in PLUGIN_DIRS:
plugin_type_path = f'{domain_root}/{plugin_type}/'
plugin_paths = [fpath for fpath in plugin_files if fpath.startswith(plugin_type_path)]
plugins[plugin_type] = list(set([_plugin_name_from_path(fname)
for fname in plugin_paths if f'/{plugin_type}/' in fname]))
return plugins


def get_plugin_ids(plugin_type: str, new_plugin_dirs: List[str], domain_root: str) -> List[str]:
"""
Searches all __init.py__ files in `new_plugin_dirs` of `plugin_type` for registered plugins.
Returns list of identifiers for each registered plugin.
"""
plugin_ids = []

for plugin_dirname in new_plugin_dirs:
init_file = Path(f'{domain_root}/{plugin_type}/{plugin_dirname}/__init__.py')
with open(init_file) as f:
registry_name = plugin_type.strip(
's') + '_registry' # remove plural and determine variable name, e.g. "models" -> "model_registry"
plugin_registrations = [line for line in f if f"{registry_name}["
in line.replace('\"', '\'')]
for line in plugin_registrations:
result = re.search(f'{registry_name}\[.*\]', line)
identifier = result.group(0)[len(registry_name) + 2:-2] # remove brackets and quotes
plugin_ids.append(identifier)

return plugin_ids


def parse_plugin_changes(commit_SHA: str, domain_root: str) -> dict:
"""
Return information about which files changed by the invoking PR (compared against main) belong to plugins

:param commit_SHA: SHA of the invoking PR
:param domain_root: the root package directory of the repo where the PR originates, either 'brainscore' (vision) or 'brainscore_language' (language)
"""
plugin_info_dict = {}
changed_files = get_all_changed_files(commit_SHA)
changed_plugin_files, changed_non_plugin_files = separate_plugin_files(changed_files)

plugin_info_dict["changed_plugins"] = get_plugin_paths(changed_plugin_files, domain_root)
plugin_info_dict["is_automergeable"] = str(num_changed_non_plugin_files > 0)

return plugin_info_dict


def get_plugin_info(commit_SHA: str, domain_root: str):
"""
If any model or benchmark files changed, get plugin ids and set run_score to "True".
Otherwise set to "False".
Print all collected information about plugins.
"""
plugin_info_dict = parse_plugin_changes(commit_SHA, domain_root)

scoring_plugin_types = ("models", "benchmarks")
plugins_to_score = [plugin_info_dict["changed_plugins"][plugin_type] for plugin_type in scoring_plugin_types]

if len(plugins_to_score) > 0:
plugin_info_dict["run_score"] = "True"
for plugin_type in scoring_plugin_types:
scoring_plugin_ids = get_plugin_ids(plugin_type, plugin_info_dict["changed_plugins"][plugin_type], domain_root)
plugin_info_dict[f'new_{plugin_type}'] = ' '.join(scoring_plugin_ids)
else:
plugin_info_dict["run_score"] = "False"

print(plugin_info_dict)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
print(plugin_info_dict)
_logger.debug(f"plugin_info_dict: {plugin_info_dict}")

Copy link
Member

Choose a reason for hiding this comment

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

or delete if not worth debugging, along with the suggested import logging

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This needs to be printed, not logged to debug, as that's how the calling functions access the output.

Copy link
Member

Choose a reason for hiding this comment

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

oh I see -- can you add a comment to the code to explain? I'm worried I'll come across this in a year and again think this should be logged 😄



def run_changed_plugin_tests(commit_SHA: str, domain_root: str):
"""
Initiates run of all tests in each changed plugin directory
"""
plugin_info_dict = parse_plugin_changes(commit_SHA, domain_root)

tests_to_run = []
for plugin_type in plugin_info_dict["changed_plugins"]:
changed_plugins = plugin_info_dict["changed_plugins"][plugin_type]
for plugin_dirname in changed_plugins:
root = Path(f'{domain_root}/{plugin_type}/{plugin_dirname}')
for filepath in root.rglob(r'test*.py'):
tests_to_run.append(str(filepath))

_logger.info("Running tests for new or modified plugins...")
run_args('brainscore_language', tests_to_run)
16 changes: 9 additions & 7 deletions brainscore_core/plugin_management/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import warnings
import yaml
from pathlib import Path
from typing import Dict, Union
from typing import Dict, Union, List

from .environment_manager import EnvironmentManager

Expand Down Expand Up @@ -108,21 +108,23 @@ def run_all_tests(root_directory: Path, results: Dict):
plugin_test_runner()


def run_args(root_directory: Union[Path, str], test_file: Union[None, str] = None, test: Union[None, str] = None):
def run_args(root_directory: Union[Path, str], test_files: Union[None, List[str]] = None, test: Union[None, str] = None):
"""
Run single specified test or all tests for each plugin.

:param root_directory: the directory containing all plugin types, e.g. `/local/brain-score_language/`
:param test_file: path of target test file (optional)
:param test_files: List of paths of target test files. If this is `None`, run all tests
:param test: name of test to run (optional)
"""
results = {}
if not test_file:
if not test_files:
run_all_tests(root_directory=Path(root_directory), results=results)
elif test_file and Path(test_file).exists():
run_specified_tests(root_directory=Path(root_directory), test_file=test_file, results=results, test=test)
else:
warnings.warn("Test file not found.")
for test_file in test_files:
if Path(test_file).exists():
run_specified_tests(root_directory=Path(root_directory), test_file=test_file, results=results, test=test)
else:
warnings.warn(f"Test file {test_file} not found.")
kvfairchild marked this conversation as resolved.
Show resolved Hide resolved

plugins_with_errors = {k: v for k, v in results.items() if (v != 0) and (v != 5)}
num_plugins_failed = len(plugins_with_errors)
Expand Down
51 changes: 51 additions & 0 deletions tests/test_plugin_management/test_parse_plugin_changes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import shutil
import sys
import tempfile
from pathlib import Path

from brainscore_core.plugin_management.parse_plugin_changes import get_all_changed_files, separate_plugin_files, get_plugin_paths, get_plugin_ids

DUMMY_FILES_CHANGED = ['brainscore_core/models/dummy_model/model.py',
'brainscore_core/models/dummy_model/test.py',
'brainscore_core/models/dummy_model/__init__.py',
'brainscore_core/benchmarks/dummy_benchmark/__init__.py',
'brainscore_core/__init__.py',
'brainscore_core/README.md']


def test_get_all_changed_files():

commit_sha = '1ee0923234bd40126cff0d995d56c608a4a803a1'
comparison_branch = 'b55f3f3c5b4f30c0d1963e59f4a65432dfc90c31'
files_changed = get_all_changed_files(commit_sha, comparison_branch)
assert set(['.travis.yml', 'README.md', 'pyproject.toml', 'setup.py']) == set(files_changed)


def test_separate_plugin_files():
plugin_files, non_plugin_files = separate_plugin_files(DUMMY_FILES_CHANGED)
assert set(['brainscore_core/models/dummy_model/model.py',
'brainscore_core/models/dummy_model/test.py',
'brainscore_core/models/dummy_model/__init__.py',
'brainscore_core/benchmarks/dummy_benchmark/__init__.py']) == set(changed_plugin_files)
assert set(['brainscore_core/__init__.py',
'brainscore_core/README.md']) == set(changed_non_plugin_files)


def test_get_plugin_paths():
changed_plugins = get_plugin_paths(DUMMY_FILES_CHANGED, 'brainscore_core')
assert changed_plugins['models'][0] == 'dummy_model'
assert changed_plugins['benchmarks'][0] == 'dummy_benchmark'
assert len(changed_plugins['data']) + len(changed_plugins['metrics']) == 0


def test_get_plugin_ids():

dummy_root = str(Path(__file__).parent / 'test_parse_plugin_changes__brainscore_dummy')
plugin_types = ['models', 'benchmarks']
for plugin_type in plugin_types:
plugin_id = f'dummy_{plugin_type}'.strip('s')
plugin_ids = get_plugin_ids(plugin_type, [plugin_id], dummy_root)
assert plugin_id == plugin_id



Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from brainscore_dummy import benchmark_registry

from .benchmark import DummyBenchmark # doesn't exist, but we only parse and never run this file and never run it
kvfairchild marked this conversation as resolved.
Show resolved Hide resolved

benchmark_registry['dummy-benchmark'] = DummyBenchmark
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from brainscore_dummy import model_registry

from .model import DummyModel # doesn't exist, but we only parse and never run this file

model_registry['dummy-model'] = DummyModel