diff --git a/src/wily/commands/build.py b/src/wily/commands/build.py index a365290f..f7e8aff4 100644 --- a/src/wily/commands/build.py +++ b/src/wily/commands/build.py @@ -7,6 +7,7 @@ import multiprocessing import os import pathlib +from collections.abc import Sequence from sys import exit from typing import Any, Dict, List, Tuple @@ -39,6 +40,7 @@ def run_operator( # Normalize paths for non-seed passes for key in list(data.keys()): if os.path.isabs(key): + # ToDo: Handle paths on different drives in Windows rel = os.path.relpath(key, config.path) data[rel] = data[key] del data[key] @@ -95,17 +97,26 @@ def build(config: WilyConfig, archiver: Archiver, operators: List[Operator]) -> try: with multiprocessing.Pool(processes=len(operators)) as pool: prev_stats: Dict[str, Dict] = {} + assert isinstance(config.targets, Sequence) + resolved_path = pathlib.Path(config.path).resolve() + resolved_targets = [ + pathlib.Path(target).resolve() for target in config.targets + ] for revision in revisions: # Checkout target revision archiver_instance.checkout(revision, config.checkout_options) stats: Dict[str, Dict] = {"operator_data": {}} - # TODO : Check that changed files are children of the targets targets = [ str(pathlib.Path(config.path) / pathlib.Path(file)) for file in revision.added_files + revision.modified_files - # if any([True for target in config.targets if - # target in pathlib.Path(pathlib.Path(config.path) / pathlib.Path(file)).parents]) + if config.targets == ["."] # Add all files if no target is set + # Check that changed files are children of the targets + or any( + True + for target in resolved_targets + if target in (resolved_path / file).parents + ) ] # Run each operator as a separate process diff --git a/test/unit/test_build_unit.py b/test/unit/test_build_unit.py index ddd8fb86..8dc828e6 100644 --- a/test/unit/test_build_unit.py +++ b/test/unit/test_build_unit.py @@ -1,6 +1,7 @@ import os +import pathlib import sys -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pytest @@ -38,7 +39,7 @@ def revisions(self, path, max_revisions): message="None again", tracked_files=["a", "b", "c", "d"], tracked_dirs=["d"], - added_files=["e"], + added_files=["e", "d/h"], modified_files=["f"], deleted_files=["a"], ), @@ -50,7 +51,7 @@ def checkout(self, revision, options): class MockOperatorCls(BaseOperator): name = "test" - data = {"C:\\home\\test1.py" if sys.platform == "win32" else "/home/test1.py": None} + data = {"\\home\\test1.py" if sys.platform == "win32" else "/home/test1.py": None} def __init__(self, *args, **kwargs): pass @@ -72,6 +73,11 @@ def config(): def test_build_simple(config): _test_operators = (MockOperator,) + # Remove drive from config path, as that breaks os.path.relpath on GitHub + # test runner because config path and test directory point to different drives + path = config.path + config.path = str(pathlib.Path().joinpath("/", *pathlib.Path(path).parts[1:])) + with patch("wily.state.resolve_archiver", return_value=MockArchiver), patch( "wily.commands.build.resolve_operator", return_value=MockOperator ): @@ -79,9 +85,47 @@ def test_build_simple(config): assert result is None +def test_build_targets(): + mock_state = MagicMock() + mock_starmap = MagicMock() + mock_pool = MagicMock() + mock_pool.return_value = mock_pool + mock_pool.__enter__.return_value = mock_pool + mock_pool.starmap = mock_starmap + + config = DEFAULT_CONFIG + config.path = "." + _test_operators = (MockOperator,) + d_path = pathlib.Path("d/") + h_path = str(d_path / "h") + + config.targets = ["."] + with patch("wily.state.resolve_archiver", return_value=MockArchiver), patch( + "wily.commands.build.resolve_operator", return_value=MockOperator + ), patch("wily.commands.build.State", mock_state), patch( + "wily.commands.build.multiprocessing.Pool", mock_pool + ): + build.build(config, MockArchiver, _test_operators) # type: ignore + assert len(mock_starmap.mock_calls) == 6 + assert mock_starmap.call_args_list[0][0][1][-1][-1] == ["e", h_path, "f"] + assert mock_starmap.call_args_list[1][0][1][-1][-1] == [] + + config.targets = [str(d_path)] + mock_starmap.reset_mock() + with patch("wily.state.resolve_archiver", return_value=MockArchiver), patch( + "wily.commands.build.resolve_operator", return_value=MockOperator + ), patch("wily.commands.build.State", mock_state), patch( + "wily.commands.build.multiprocessing.Pool", mock_pool + ): + build.build(config, MockArchiver, _test_operators) # type: ignore + assert len(mock_starmap.mock_calls) == 6 + assert mock_starmap.call_args_list[0][0][1][-1][-1] == [h_path] + assert mock_starmap.call_args_list[1][0][1][-1][-1] == [] + + def test_run_operator(config): rev = Revision("123", None, None, 1, "message", [], [], [], [], []) name, data = build.run_operator(MockOperator, rev, config, ["test1.py"]) assert name == "mock" - path = "C:\\home\\test1.py" if sys.platform == "win32" else "/home/test1.py" + path = "\\home\\test1.py" if sys.platform == "win32" else "/home/test1.py" assert data == {os.path.relpath(path, config.path): None}