From fea7518ab79b87fe1461c8e38cce821983db5d6a Mon Sep 17 00:00:00 2001 From: "rodrigo.rezino" Date: Mon, 31 Jan 2022 14:57:39 -0300 Subject: [PATCH 1/3] Add windows on unit test --- .github/workflows/python-test.yml | 11 ++++++++--- tests/integration_tests/infrastructure/test_cli.py | 3 +++ .../use_cases/test_generate_diagram_usecase.py | 3 +++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 5d85c92..402049b 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -14,12 +14,17 @@ jobs: fail-fast: false matrix: python-version: [ "3.8", "3.9"] - os: [ ubuntu-latest ] + os: [ ubuntu-latest, windows-latest ] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - name: Set up Ubuntu - run: sudo apt-get install -y graphviz + - name: Set up Graphviz + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + sudo apt-get install -y graphviz + elif [ "$RUNNER_OS" == "Windows" ]; then + echo "Graphviz tests will now run on Windows" + fi - name: Set up Python uses: actions/setup-python@v2 with: diff --git a/tests/integration_tests/infrastructure/test_cli.py b/tests/integration_tests/infrastructure/test_cli.py index cb91ca5..65cf0f3 100644 --- a/tests/integration_tests/infrastructure/test_cli.py +++ b/tests/integration_tests/infrastructure/test_cli.py @@ -1,3 +1,5 @@ +import sys +import unittest from unittest import TestCase from click.testing import CliRunner @@ -76,6 +78,7 @@ def test_cli_run_check_should_return_errors_for_wrong_hexa_project(self): self.assertIn('A file from a directory group is pointing to a file in another directory group in same layer.', result.output) + @unittest.skipIf(sys.platform.startswith('win'), 'No Windows Support') def test_cli_run_diagrams_should_return_no_errors_for_correct_hexa_project(self): # This tests check the consistency of this on project runner = CliRunner() diff --git a/tests/integration_tests/use_cases/test_generate_diagram_usecase.py b/tests/integration_tests/use_cases/test_generate_diagram_usecase.py index 687343a..cd3c147 100644 --- a/tests/integration_tests/use_cases/test_generate_diagram_usecase.py +++ b/tests/integration_tests/use_cases/test_generate_diagram_usecase.py @@ -1,4 +1,6 @@ import os.path +import sys +import unittest from tempfile import TemporaryDirectory from time import sleep from unittest import TestCase @@ -10,6 +12,7 @@ class TestGenerateDiagramUseCase(TestCase): + @unittest.skipIf(sys.platform.startswith('win'), 'No Windows Support') def test_generate_diagram(self): temp_dir = TemporaryDirectory() diagram_path = temp_dir.name + '/outputfile' From 6aab47396ef5ff25366b61ea4fb3654b7b6a2080 Mon Sep 17 00:00:00 2001 From: "rodrigo.rezino" Date: Mon, 31 Jan 2022 15:16:18 -0300 Subject: [PATCH 2/3] Add windows on unit test --- .github/workflows/python-test.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 402049b..e640224 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -20,11 +20,13 @@ jobs: - uses: actions/checkout@v2 - name: Set up Graphviz run: | - if [ "$RUNNER_OS" == "Linux" ]; then + echo $RUNNER_OS + if [ "$RUNNER_OS" == "LINUX" ]; then sudo apt-get install -y graphviz - elif [ "$RUNNER_OS" == "Windows" ]; then - echo "Graphviz tests will now run on Windows" + else + echo "Graphviz tests will only run for ubuntu-latest" fi + shell: bash - name: Set up Python uses: actions/setup-python@v2 with: From 570d93131ac2e1278a5394a96210a3e836b66b08 Mon Sep 17 00:00:00 2001 From: "rodrigo.rezino" Date: Mon, 31 Jan 2022 15:33:57 -0300 Subject: [PATCH 3/3] Add windows on unit test --- .github/workflows/python-test.yml | 15 ++++++++++---- pyproject.toml | 2 +- src/hexagonal/domain/hexagonal_layer.py | 4 +++- .../hexagonal_project_layer.py | 3 ++- src/hexagonal/domain/raw_python_file.py | 7 +++++++ .../hexagonal_dependency_flow_checker.py | 4 ++-- .../services/python_file_imports_resolver.py | 17 ++++++++-------- .../services/raw_python_file_builder.py | 7 ++----- .../services/raw_python_project_importer.py | 2 +- .../infrastructure/test_cli.py | 4 ++-- .../test_python_file_imports_resolver.py | 13 ++++++------ .../services/test_raw_python_file_builder.py | 20 +++++++------------ tests/integration_tests/utils/utils.py | 14 +++++++------ tests/utils.py | 12 +++++++++-- 14 files changed, 71 insertions(+), 53 deletions(-) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index e640224..6bf9457 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -14,14 +14,14 @@ jobs: fail-fast: false matrix: python-version: [ "3.8", "3.9"] - os: [ ubuntu-latest, windows-latest ] + os: [ ubuntu-latest ] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Set up Graphviz run: | echo $RUNNER_OS - if [ "$RUNNER_OS" == "LINUX" ]; then + if [ "$RUNNER_OS" == "Linux" ]; then sudo apt-get install -y graphviz else echo "Graphviz tests will only run for ubuntu-latest" @@ -39,7 +39,14 @@ jobs: poetry install - name: Run MyPy Checks run: poetry run mypy src - - name: Run Nose Tests - run: poetry run nosetests -v ./ + - name: Run Tests + run: | + echo $RUNNER_OS + if [ "$RUNNER_OS" == "Linux" ]; then + poetry run python -m nose -v -w ./tests + else + poetry run python -m unittest -v + fi + shell: bash diff --git a/pyproject.toml b/pyproject.toml index 30262dc..8914075 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "hexagonal-py" -version = "0.1.4" +version = "0.1.5" description = """Hexagonal Coherence Check""" readme = 'README.md' authors = ["rfrezino "] diff --git a/src/hexagonal/domain/hexagonal_layer.py b/src/hexagonal/domain/hexagonal_layer.py index 80604f0..db506c5 100644 --- a/src/hexagonal/domain/hexagonal_layer.py +++ b/src/hexagonal/domain/hexagonal_layer.py @@ -1,3 +1,4 @@ +import os from dataclasses import dataclass from typing import List @@ -17,4 +18,5 @@ def _valid_dirs(self): raise Exception(f'Hexagonal Layer directory "{self.name}" must start with /. Example: "/domain"') if dir.endswith('/'): - raise Exception(f'Hexagonal Layer directory "{self.name}" must not finish with /. Example: "/domain"') + raise Exception( + f'Hexagonal Layer directory "{self.name}" must not finish with /. Example: "/domain"') diff --git a/src/hexagonal/domain/hexagonal_project/hexagonal_project_layer.py b/src/hexagonal/domain/hexagonal_project/hexagonal_project_layer.py index ff1833f..9dcbad8 100644 --- a/src/hexagonal/domain/hexagonal_project/hexagonal_project_layer.py +++ b/src/hexagonal/domain/hexagonal_project/hexagonal_project_layer.py @@ -1,3 +1,4 @@ +import os from dataclasses import dataclass from typing import List, Optional @@ -23,7 +24,7 @@ def get_directory_group_index_for_file(self, relative_folder_path_from_project_f for dir_group_index, dir_group in enumerate(self.directories_groups): for dir_name in dir_group: - if dir_name == relative_folder_path_from_project_folder: + if os.path.normcase(os.path.normpath(dir_name)) == relative_folder_path_from_project_folder: return dir_group_index + 1 return 0 diff --git a/src/hexagonal/domain/raw_python_file.py b/src/hexagonal/domain/raw_python_file.py index 123b537..4ecfe94 100644 --- a/src/hexagonal/domain/raw_python_file.py +++ b/src/hexagonal/domain/raw_python_file.py @@ -1,3 +1,4 @@ +import os.path from dataclasses import dataclass @@ -12,3 +13,9 @@ class RawPythonFile: relative_folder_path_from_project_folder: str project_folder_full_path: str + def __post_init__(self): + self.file_full_path = os.path.normcase(os.path.normpath(self.file_full_path)) + self.file_folder_full_path = os.path.normcase(os.path.normpath(self.file_folder_full_path)) + self.relative_folder_path_from_project_folder = os.path.normcase( + os.path.normpath(self.relative_folder_path_from_project_folder)) + self.project_folder_full_path = os.path.normcase(os.path.normpath(self.project_folder_full_path)) diff --git a/src/hexagonal/services/hexagonal_dependency_flow_checker.py b/src/hexagonal/services/hexagonal_dependency_flow_checker.py index 040bfb2..800de46 100644 --- a/src/hexagonal/services/hexagonal_dependency_flow_checker.py +++ b/src/hexagonal/services/hexagonal_dependency_flow_checker.py @@ -15,7 +15,6 @@ class DependencyFlowError: group_inter_dependency: bool - @dataclass class DependencyFlowResponse: errors: List[DependencyFlowError] @@ -44,7 +43,8 @@ def _is_inner_layer_is_point_to_outer_layer(inner_layer: HexagonalProjectLayer, outer_layer: HexagonalProjectLayer) -> bool: return inner_layer.index < outer_layer.index - def _are_files_in_same_layer(self, source_layer: HexagonalProjectLayer, + @staticmethod + def _are_files_in_same_layer(source_layer: HexagonalProjectLayer, module_layer: HexagonalProjectLayer) -> bool: return source_layer.index == module_layer.index diff --git a/src/hexagonal/services/python_file_imports_resolver.py b/src/hexagonal/services/python_file_imports_resolver.py index 2e87568..ddf0b53 100644 --- a/src/hexagonal/services/python_file_imports_resolver.py +++ b/src/hexagonal/services/python_file_imports_resolver.py @@ -34,10 +34,9 @@ def _get_all_modules_source_paths(self) -> List[str]: all_modules.append(self.__get_module_file_name(module)) all_modules.extend( - self._convert_bad_modules_into_paths(base_file_full_path=self._raw_python_file.file_full_path, - bad_modules=list(finder.badmodules.keys()))) + self._convert_bad_modules_into_paths(bad_modules=list(finder.badmodules.keys()))) - return [value for value in all_modules if value is not None] + return [os.path.normcase(os.path.normpath(value)) for value in all_modules if value is not None] @staticmethod def __get_module_file_name(module: Module) -> Optional[str]: @@ -52,19 +51,19 @@ def __get_module_file_name(module: Module) -> Optional[str]: except Exception: return None - def _convert_bad_modules_into_paths(self, base_file_full_path: str, bad_modules: List[str]) -> List[str]: + def _convert_bad_modules_into_paths(self, *, bad_modules: List[str]) -> List[str]: result = [] for bad_module in bad_modules: - bad_module_file = bad_module.replace('.', '/') + '.py' + bad_module_file = bad_module.replace('.', os.sep) + '.py' - while '/' in bad_module_file: - full_bad_module_file = self._raw_python_file.project_folder_full_path + '/' + bad_module_file + while os.sep in bad_module_file: + full_bad_module_file = self._raw_python_file.project_folder_full_path + os.sep + bad_module_file if os.path.isfile(full_bad_module_file): - result.append(full_bad_module_file) + result.append(os.path.normpath(full_bad_module_file)) break - bad_module_file = bad_module_file.split('/', 1)[1] + bad_module_file = bad_module_file.split(os.sep, 1)[1] return result diff --git a/src/hexagonal/services/raw_python_file_builder.py b/src/hexagonal/services/raw_python_file_builder.py index ada2db8..0e80934 100644 --- a/src/hexagonal/services/raw_python_file_builder.py +++ b/src/hexagonal/services/raw_python_file_builder.py @@ -17,17 +17,14 @@ def __init__(self, file_full_path: str, project_source_folder_full_path: str): if not file_full_path.startswith(project_source_folder_full_path): raise Exception('File path and project path do not match.') - if not file_full_path.startswith('/'): - raise Exception("The param file_full_path must have the file's full path.") - if not file_full_path.endswith('.py'): raise Exception('File must have .py extension.') - self._file_full_path = file_full_path + self._file_full_path = os.path.normcase(os.path.normpath(file_full_path)) self._project_source_folder_full_path = os.path.abspath(project_source_folder_full_path) def _get_file_name_from_full_file_path(self) -> str: - return self._file_full_path.split('/')[-1] + return self._file_full_path.split(os.sep)[-1] def _get_file_folder_path(self) -> str: return os.path.abspath(self.file_full_path.replace(self._file_name, '')) diff --git a/src/hexagonal/services/raw_python_project_importer.py b/src/hexagonal/services/raw_python_project_importer.py index b6cd5f2..dfce96b 100644 --- a/src/hexagonal/services/raw_python_project_importer.py +++ b/src/hexagonal/services/raw_python_project_importer.py @@ -45,7 +45,7 @@ def _get_all_python_files_paths_from_source_folder(self) -> List[str]: for file in all_files: include_file = True - if '/.' in file: + if f'{os.sep}.' in file: continue for excluded_dir in self._excluded_folders: diff --git a/tests/integration_tests/infrastructure/test_cli.py b/tests/integration_tests/infrastructure/test_cli.py index 65cf0f3..f7a5daf 100644 --- a/tests/integration_tests/infrastructure/test_cli.py +++ b/tests/integration_tests/infrastructure/test_cli.py @@ -50,8 +50,8 @@ def test_cli_run_check_should_return_errors_for_wrong_clean_project(self): ] ) - self.assertEqual(result.exit_code, 1) self.assertIn('Wrong dependency flow. An inner layer is pointing to an outer layer.', result.output) + self.assertEqual(result.exit_code, 1) def test_cli_run_check_should_return_no_errors_for_correct_hexa_project(self): # This tests check the consistency of this on project @@ -74,9 +74,9 @@ def test_cli_run_check_should_return_errors_for_wrong_hexa_project(self): ['--project_path', get_sample_wrong_test_hexa_arch_project_path(), '--source_path', get_sample_wrong_test_hexa_arch_project_path()]) - self.assertEqual(result.exit_code, 1) self.assertIn('A file from a directory group is pointing to a file in another directory group in same layer.', result.output) + self.assertEqual(result.exit_code, 1) @unittest.skipIf(sys.platform.startswith('win'), 'No Windows Support') def test_cli_run_diagrams_should_return_no_errors_for_correct_hexa_project(self): diff --git a/tests/integration_tests/services/test_python_file_imports_resolver.py b/tests/integration_tests/services/test_python_file_imports_resolver.py index fdc58d7..884c2ca 100644 --- a/tests/integration_tests/services/test_python_file_imports_resolver.py +++ b/tests/integration_tests/services/test_python_file_imports_resolver.py @@ -1,3 +1,4 @@ +import os.path from typing import List from unittest import TestCase @@ -14,19 +15,19 @@ def assertContainsItemWithSuffix(expected_suffix: str, item_list: List[str]): def test_resolve_imported_modules_when_file_is_valid_return_list_of_imported_modules(self): project_path = get_sample_correct_test_clean_arch_project_path() - file_path = project_path + '/usecases/create_person_usecase.py' + file_path = os.path.normcase(os.path.normpath(project_path + '/usecases/create_person_usecase.py')) raw_python_file = RawPythonFile( file_full_path=file_path, file_name='create_person_usecase.py', - file_folder_full_path=project_path + '/usecases', - relative_folder_path_from_project_folder='/usecases', + file_folder_full_path=os.path.normpath(project_path + '/usecases'), + relative_folder_path_from_project_folder=os.path.normpath('/usecases'), project_folder_full_path=project_path, ) imports_resolver = PythonFileImportsResolver(raw_python_file=raw_python_file) imported_modules = imports_resolver.resolve_imported_modules() - self.assertEqual(len(imported_modules), 2) - self.assertContainsItemWithSuffix('/domain/person.py', imported_modules) - self.assertContainsItemWithSuffix('services/person_repository.py', imported_modules) + self.assertEqual(len(imported_modules), 2, imported_modules) + self.assertContainsItemWithSuffix('person.py', imported_modules) + self.assertContainsItemWithSuffix('person_repository.py', imported_modules) diff --git a/tests/integration_tests/services/test_raw_python_file_builder.py b/tests/integration_tests/services/test_raw_python_file_builder.py index 10be816..8c4d11c 100644 --- a/tests/integration_tests/services/test_raw_python_file_builder.py +++ b/tests/integration_tests/services/test_raw_python_file_builder.py @@ -2,20 +2,14 @@ from unittest import TestCase from hexagonal.services.raw_python_file_builder import RawPythonFileBuilder +from tests.utils import fix_path class TestRawPythonFileBuilder(TestCase): - def test_init_should_raise_exception_when_param_file_full_path_is_relative_path(self): - invalid_source_path = '~/test.py' - - with self.assertRaises(Exception) as error: - builder = RawPythonFileBuilder(file_full_path=invalid_source_path, project_source_folder_full_path='~/') - self.assertEqual("The param file_full_path must have the file's full path.", str(error.exception)) - def test_init_should_return_valid_object_when_params_are_correct(self): - valid_source_path = os.path.abspath(__file__) - file_folder = os.path.abspath(__file__.replace('test_raw_python_file_builder.py', '')) + valid_source_path = fix_path(os.path.abspath(__file__)) + file_folder = fix_path(os.path.abspath(__file__.replace('test_raw_python_file_builder.py', ''))) builder = RawPythonFileBuilder(file_full_path=valid_source_path, project_source_folder_full_path=file_folder) @@ -29,9 +23,9 @@ def test_init_should_raise_exception_when_file_and_project_paths_does_not_match( self.assertEqual('File path and project path do not match.', str(error.exception)) def test_build_should_return_valid_object_when_params_are_correct(self): - valid_source_path = os.path.abspath(__file__) - file_folder = os.path.abspath(__file__.replace('test_raw_python_file_builder.py', '')) - project_folder = file_folder.split('/services')[0] + valid_source_path = fix_path(os.path.abspath(__file__)) + file_folder = fix_path(os.path.abspath(__file__.replace('test_raw_python_file_builder.py', ''))) + project_folder = file_folder.split(f'{os.sep}services')[0] builder = RawPythonFileBuilder(file_full_path=valid_source_path, project_source_folder_full_path=project_folder) raw_python_file = builder.build() @@ -39,4 +33,4 @@ def test_build_should_return_valid_object_when_params_are_correct(self): self.assertEqual(valid_source_path, raw_python_file.file_full_path) self.assertEqual('test_raw_python_file_builder.py', raw_python_file.file_name) self.assertEqual(file_folder, raw_python_file.file_folder_full_path) - self.assertEqual('/services', raw_python_file.relative_folder_path_from_project_folder) + self.assertEqual(f'{os.sep}services', raw_python_file.relative_folder_path_from_project_folder) diff --git a/tests/integration_tests/utils/utils.py b/tests/integration_tests/utils/utils.py index 742287d..2cc8327 100644 --- a/tests/integration_tests/utils/utils.py +++ b/tests/integration_tests/utils/utils.py @@ -1,25 +1,27 @@ +import os + from tests.utils import get_tests_path def get_integration_tests_path(): - return get_tests_path() + '/integration_tests' + return os.path.join(get_tests_path(), 'integration_tests') def get_sample_wrong_test_clean_arch_project_path(): - return get_integration_tests_path() + '/utils/sample_projects/wrong_clean_arch_project' + return os.path.join(get_integration_tests_path(), 'utils', 'sample_projects', 'wrong_clean_arch_project') def get_sample_correct_test_clean_arch_project_path(): - return get_integration_tests_path() + '/utils/sample_projects/correct_clean_arch_project' + return os.path.join(get_integration_tests_path(), 'utils', 'sample_projects', 'correct_clean_arch_project') def get_sample_wrong_test_hexa_arch_project_path(): - return get_integration_tests_path() + '/utils/sample_projects/wrong_hexa_arch_project' + return os.path.join(get_integration_tests_path(), 'utils', 'sample_projects', 'wrong_hexa_arch_project') def get_sample_correct_test_hexa_arch_project_path(): - return get_integration_tests_path() + '/utils/sample_projects/correct_hexa_arch_project' + return os.path.join(get_integration_tests_path(), 'utils', 'sample_projects', 'correct_hexa_arch_project') def get_sample_correct_test_hexa_arch_project_toml_path(): - return get_integration_tests_path() + '/utils/sample_projects/correct_hexa_arch_project_toml' + return os.path.join(get_integration_tests_path(), 'utils', 'sample_projects', 'correct_hexa_arch_project_toml') diff --git a/tests/utils.py b/tests/utils.py index 71b10aa..448ce71 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,6 +1,14 @@ +import os.path +import pathlib + + def get_project_path() -> str: - return __file__.split('/tests')[0] + return str(pathlib.Path(__file__).resolve().parent.parent) def get_tests_path() -> str: - return get_project_path() + '/tests' + return os.path.join(get_project_path(), 'tests') + + +def fix_path(path: str) -> str: + return os.path.normcase(os.path.normpath(path))