import importlib.util import os import tempfile import unittest from pathlib import Path from unittest.mock import patch def load_validate_workspace(): module_path = Path(__file__).resolve().parent / "validate_workspace.py" spec = importlib.util.spec_from_file_location("validate_workspace", module_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module class ValidateWorkspaceTests(unittest.TestCase): def test_env_commands_override_cmake_detection(self): validate_workspace = load_validate_workspace() with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) (root / "CMakeLists.txt").write_text("cmake_minimum_required(VERSION 3.20)\n", encoding="utf-8") with patch.dict(os.environ, {"HARNESS_VALIDATION_COMMANDS": "echo first\n echo second \n"}, clear=True): self.assertEqual(validate_workspace.discover_commands(root), ["echo first", "echo second"]) def test_msvc_debug_cmake_commands_are_default_for_cmake_project(self): validate_workspace = load_validate_workspace() with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) (root / "CMakeLists.txt").write_text("cmake_minimum_required(VERSION 3.20)\n", encoding="utf-8") build_dir = root / "build" / "msvc-debug" with patch.dict(os.environ, {}, clear=True): self.assertEqual( validate_workspace.discover_commands(root), [ f'cmake -S "{root}" -B "{build_dir}" -G "Visual Studio 17 2022" -A x64', f'cmake --build "{build_dir}" --config Debug', f'ctest --test-dir "{build_dir}" --output-on-failure -C Debug', ], ) def test_msvc_debug_configure_preset_is_preferred_when_present(self): validate_workspace = load_validate_workspace() with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) (root / "CMakeLists.txt").write_text("cmake_minimum_required(VERSION 3.20)\n", encoding="utf-8") (root / "CMakePresets.json").write_text( """ { "version": 3, "configurePresets": [ { "name": "msvc-debug", "generator": "Visual Studio 17 2022", "binaryDir": "${sourceDir}/out/msvc-debug" } ] } """, encoding="utf-8", ) with patch.dict(os.environ, {}, clear=True): self.assertEqual( validate_workspace.discover_commands(root), [ "cmake --preset msvc-debug", f'cmake --build "{root / "out" / "msvc-debug"}" --config Debug', f'ctest --test-dir "{root / "out" / "msvc-debug"}" --output-on-failure -C Debug', ], ) def test_no_cmake_project_has_no_validation_commands(self): validate_workspace = load_validate_workspace() with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) with patch.dict(os.environ, {}, clear=True): self.assertEqual(validate_workspace.discover_commands(root), []) def test_common_cmake_install_path_is_prepended_when_cmake_is_not_on_path(self): validate_workspace = load_validate_workspace() with tempfile.TemporaryDirectory() as tmp: common_bin = Path(tmp) / "CMake" / "bin" common_bin.mkdir(parents=True) (common_bin / "cmake.exe").write_text("", encoding="utf-8") (common_bin / "ctest.exe").write_text("", encoding="utf-8") with patch.object(validate_workspace, "COMMON_CMAKE_BIN", common_bin): with patch.object(validate_workspace.shutil, "which", return_value=None): env = validate_workspace.validation_environment({"PATH": "C:\\Windows\\System32"}) path_key = "Path" if os.name == "nt" else "PATH" self.assertTrue(env[path_key].startswith(str(common_bin))) def test_common_cmake_install_path_updates_existing_windows_path_key(self): validate_workspace = load_validate_workspace() with tempfile.TemporaryDirectory() as tmp: common_bin = Path(tmp) / "CMake" / "bin" common_bin.mkdir(parents=True) (common_bin / "cmake.exe").write_text("", encoding="utf-8") with patch.object(validate_workspace, "COMMON_CMAKE_BIN", common_bin): with patch.object(validate_workspace.shutil, "which", return_value=None): env = validate_workspace.validation_environment({"Path": "C:\\Windows\\System32"}) self.assertIn("Path", env) self.assertNotIn("PATH", env) self.assertTrue(env["Path"].startswith(str(common_bin))) def test_common_cmake_install_path_normalizes_uppercase_path_on_windows(self): if os.name != "nt": self.skipTest("Windows-specific subprocess environment behavior") validate_workspace = load_validate_workspace() with tempfile.TemporaryDirectory() as tmp: common_bin = Path(tmp) / "CMake" / "bin" common_bin.mkdir(parents=True) (common_bin / "cmake.exe").write_text("", encoding="utf-8") with patch.object(validate_workspace, "COMMON_CMAKE_BIN", common_bin): with patch.object(validate_workspace.shutil, "which", return_value=None): env = validate_workspace.validation_environment({"PATH": "C:\\Windows\\System32"}) self.assertIn("Path", env) self.assertNotIn("PATH", env) self.assertTrue(env["Path"].startswith(str(common_bin))) def test_common_cmake_executable_is_used_when_command_tool_is_not_on_path(self): validate_workspace = load_validate_workspace() with tempfile.TemporaryDirectory() as tmp: common_bin = Path(tmp) / "CMake" / "bin" common_bin.mkdir(parents=True) (common_bin / "cmake.exe").write_text("", encoding="utf-8") with patch.object(validate_workspace, "COMMON_CMAKE_BIN", common_bin): with patch.object(validate_workspace.shutil, "which", return_value=None): command = validate_workspace.resolve_validation_command("cmake --version") self.assertEqual(command, f'"{common_bin / "cmake.exe"}" --version') def test_common_ctest_executable_is_used_when_command_tool_is_not_on_path(self): validate_workspace = load_validate_workspace() with tempfile.TemporaryDirectory() as tmp: common_bin = Path(tmp) / "CMake" / "bin" common_bin.mkdir(parents=True) (common_bin / "ctest.exe").write_text("", encoding="utf-8") with patch.object(validate_workspace, "COMMON_CMAKE_BIN", common_bin): with patch.object(validate_workspace.shutil, "which", return_value=None): command = validate_workspace.resolve_validation_command("ctest --version") self.assertEqual(command, f'"{common_bin / "ctest.exe"}" --version') if __name__ == "__main__": unittest.main()