Skip to content

Commit 5a24d98

Browse files
committed
Testing
1 parent 4306a53 commit 5a24d98

File tree

8 files changed

+1074
-128
lines changed

8 files changed

+1074
-128
lines changed

cppython/plugins/conan/plugin.py

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,59 @@ def information() -> Information:
6969
"""
7070
return Information()
7171

72-
def install(self) -> None:
73-
"""Installs the provider"""
72+
def _install_dependencies(self, *, update: bool = False) -> None:
73+
"""Common implementation for installing/updating dependencies.
74+
75+
Args:
76+
update: If True, check remotes for newer versions/revisions and install those.
77+
If False, use cached versions when available.
78+
"""
7479
resolved_dependencies = [resolve_conan_dependency(req) for req in self.core_data.cppython_data.dependencies]
7580

7681
self.builder.generate_conanfile(self.core_data.project_data.project_root, resolved_dependencies)
7782

7883
self.core_data.cppython_data.build_path.mkdir(parents=True, exist_ok=True)
7984

80-
def update(self) -> None:
81-
"""Updates the provider"""
82-
resolved_dependencies = [resolve_conan_dependency(req) for req in self.core_data.cppython_data.dependencies]
85+
# Install/update dependencies using Conan API
86+
project_root = self.core_data.project_data.project_root
87+
conanfile_path = project_root / 'conanfile.py'
8388

84-
self.builder.generate_conanfile(self.core_data.project_data.project_root, resolved_dependencies)
89+
if conanfile_path.exists():
90+
# Initialize Conan API
91+
conan_api = ConanAPI()
92+
93+
# Get default profiles
94+
profile_host_path = conan_api.profiles.get_default_host()
95+
profile_build_path = conan_api.profiles.get_default_build()
96+
profile_host = conan_api.profiles.get_profile([profile_host_path])
97+
profile_build = conan_api.profiles.get_profile([profile_build_path])
98+
99+
# Build dependency graph for the package
100+
deps_graph = conan_api.graph.load_graph_consumer(
101+
path=str(conanfile_path),
102+
name=None,
103+
version=None,
104+
user=None,
105+
channel=None,
106+
profile_host=profile_host,
107+
profile_build=profile_build,
108+
lockfile=None,
109+
remotes=conan_api.remotes.list(),
110+
update=update,
111+
check_updates=update,
112+
is_build_require=False,
113+
)
114+
115+
# Install dependencies
116+
conan_api.install.install_binaries(deps_graph=deps_graph, remotes=conan_api.remotes.list())
85117

86-
self.core_data.cppython_data.build_path.mkdir(parents=True, exist_ok=True)
118+
def install(self) -> None:
119+
"""Installs the provider"""
120+
self._install_dependencies(update=False)
121+
122+
def update(self) -> None:
123+
"""Updates the provider"""
124+
self._install_dependencies(update=True)
87125

88126
@staticmethod
89127
def supported_sync_type(sync_type: type[SyncData]) -> bool:
@@ -148,7 +186,10 @@ def publish(self) -> None:
148186
)
149187

150188
# Step 2: Get default profiles
151-
profile_host, profile_build = conan_api.profiles.get_profiles_from_args([])
189+
profile_host_path = conan_api.profiles.get_default_host()
190+
profile_build_path = conan_api.profiles.get_default_build()
191+
profile_host = conan_api.profiles.get_profile([profile_host_path])
192+
profile_build = conan_api.profiles.get_profile([profile_build_path])
152193

153194
# Step 3: Build dependency graph for the package
154195
deps_graph = conan_api.graph.load_graph_consumer(

examples/conan_cmake/simple/pdm.lock

Lines changed: 537 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/fixtures/conan.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
"""Shared fixtures for Conan plugin tests"""
2+
3+
from pathlib import Path
4+
from unittest.mock import Mock
5+
6+
import pytest
7+
from packaging.requirements import Requirement
8+
from pytest_mock import MockerFixture
9+
10+
from cppython.plugins.conan.plugin import ConanProvider
11+
from cppython.plugins.conan.schema import ConanDependency
12+
13+
14+
@pytest.fixture(name='conan_mock_api')
15+
def fixture_conan_mock_api(mocker: MockerFixture) -> Mock:
16+
"""Creates a mock ConanAPI instance for install/update operations
17+
18+
Args:
19+
mocker: Pytest mocker fixture
20+
21+
Returns:
22+
Mock ConanAPI instance
23+
"""
24+
mock_api = mocker.Mock()
25+
26+
# Mock graph module
27+
mock_deps_graph = mocker.Mock()
28+
mock_api.graph.load_graph_consumer = mocker.Mock(return_value=mock_deps_graph)
29+
30+
# Mock install module
31+
mock_api.install.install_binaries = mocker.Mock()
32+
33+
# Mock remotes module
34+
mock_remote = mocker.Mock()
35+
mock_remote.name = 'conancenter'
36+
mock_api.remotes.list = mocker.Mock(return_value=[mock_remote])
37+
38+
# Mock profiles module
39+
mock_profile_host = mocker.Mock()
40+
mock_profile_build = mocker.Mock()
41+
mock_api.profiles.get_default_host = mocker.Mock(return_value='/path/to/default/host')
42+
mock_api.profiles.get_default_build = mocker.Mock(return_value='/path/to/default/build')
43+
mock_api.profiles.get_profile = mocker.Mock(
44+
side_effect=lambda paths: mock_profile_host if 'host' in paths[0] else mock_profile_build
45+
)
46+
47+
return mock_api
48+
49+
50+
@pytest.fixture(name='conan_mock_api_publish')
51+
def fixture_conan_mock_api_publish(mocker: MockerFixture) -> Mock:
52+
"""Creates a mock ConanAPI instance for publish operations
53+
54+
Args:
55+
mocker: Pytest mocker fixture
56+
57+
Returns:
58+
Mock ConanAPI instance configured for publish operations
59+
"""
60+
mock_api = mocker.Mock()
61+
62+
# Mock export module - export returns a tuple (ref, conanfile)
63+
mock_ref = mocker.Mock()
64+
mock_ref.name = 'test_package'
65+
mock_conanfile = mocker.Mock()
66+
mock_api.export.export = mocker.Mock(return_value=(mock_ref, mock_conanfile))
67+
68+
# Mock graph module
69+
mock_api.graph.load_graph_consumer = mocker.Mock()
70+
mock_api.graph.analyze_binaries = mocker.Mock()
71+
72+
# Mock install module
73+
mock_api.install.install_binaries = mocker.Mock()
74+
75+
# Mock list module
76+
mock_select_result = mocker.Mock()
77+
mock_select_result.recipes = ['some_package/1.0@user/channel']
78+
mock_api.list.select = mocker.Mock(return_value=mock_select_result)
79+
80+
# Mock remotes module
81+
mock_remote = mocker.Mock()
82+
mock_remote.name = 'origin'
83+
mock_api.remotes.list = mocker.Mock(return_value=[mock_remote])
84+
85+
# Mock upload module
86+
mock_api.upload.upload_full = mocker.Mock()
87+
88+
# Mock profiles module
89+
mock_profile = mocker.Mock()
90+
mock_api.profiles.get_default_host = mocker.Mock(return_value='/path/to/default/host')
91+
mock_api.profiles.get_default_build = mocker.Mock(return_value='/path/to/default/build')
92+
mock_api.profiles.get_profile = mocker.Mock(return_value=mock_profile)
93+
94+
return mock_api
95+
96+
97+
@pytest.fixture(name='conan_temp_conanfile')
98+
def fixture_conan_temp_conanfile(plugin: ConanProvider) -> Path:
99+
"""Creates a temporary conanfile.py for testing
100+
101+
Args:
102+
plugin: The plugin instance
103+
104+
Returns:
105+
Path to the created conanfile.py
106+
"""
107+
project_root = plugin.core_data.project_data.project_root
108+
conanfile_path = project_root / 'conanfile.py'
109+
conanfile_path.write_text(
110+
'from conan import ConanFile\n\nclass TestConan(ConanFile):\n name = "test_package"\n version = "1.0"\n'
111+
)
112+
return conanfile_path
113+
114+
115+
@pytest.fixture(name='conan_mock_dependencies')
116+
def fixture_conan_mock_dependencies() -> list[Requirement]:
117+
"""Creates mock dependencies for testing
118+
119+
Returns:
120+
List of mock requirements
121+
"""
122+
return [
123+
Requirement('boost>=1.70.0'),
124+
Requirement('zlib>=1.2.11'),
125+
]
126+
127+
128+
@pytest.fixture(name='conan_setup_mocks')
129+
def fixture_conan_setup_mocks(
130+
plugin: ConanProvider,
131+
conan_mock_api: Mock,
132+
mocker: MockerFixture,
133+
) -> dict[str, Mock]:
134+
"""Sets up all mocks for testing install/update operations
135+
136+
Args:
137+
plugin: The plugin instance
138+
conan_mock_api: Mock ConanAPI instance
139+
mocker: Pytest mocker fixture
140+
141+
Returns:
142+
Dictionary containing all mocks
143+
"""
144+
# Mock builder
145+
mock_builder = mocker.Mock()
146+
mock_builder.generate_conanfile = mocker.Mock()
147+
# Set the builder attribute on the plugin
148+
plugin.builder = mock_builder # type: ignore[attr-defined]
149+
150+
# Mock ConanAPI constructor
151+
mock_conan_api_constructor = mocker.patch('cppython.plugins.conan.plugin.ConanAPI', return_value=conan_mock_api)
152+
153+
# Mock resolve_conan_dependency
154+
def mock_resolve(requirement: Requirement) -> ConanDependency:
155+
return ConanDependency(name=requirement.name, version_ge=None)
156+
157+
mock_resolve_conan_dependency = mocker.patch(
158+
'cppython.plugins.conan.plugin.resolve_conan_dependency', side_effect=mock_resolve
159+
)
160+
161+
return {
162+
'builder': mock_builder,
163+
'conan_api_constructor': mock_conan_api_constructor,
164+
'resolve_conan_dependency': mock_resolve_conan_dependency,
165+
}

tests/integration/examples/test_conan_cmake.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66

77
import subprocess
88
from pathlib import Path
9+
from tomllib import loads
910

1011
from typer.testing import CliRunner
1112

13+
from cppython.console.schema import ConsoleInterface
14+
from cppython.core.schema import ProjectConfiguration
15+
from cppython.project import Project
16+
1217
pytest_plugins = ['tests.fixtures.example']
1318

1419

@@ -18,10 +23,22 @@ class TestConanCMake:
1823
@staticmethod
1924
def test_simple(example_runner: CliRunner) -> None:
2025
"""Simple project"""
21-
# By nature of running the test, we require PDM to develop the project and so it will be installed
22-
result = subprocess.run(['pdm', 'install'], capture_output=True, text=True, check=False)
26+
# Create project configuration
27+
project_root = Path.cwd()
28+
project_configuration = ProjectConfiguration(project_root=project_root, version=None)
29+
30+
# Create console interface
31+
interface = ConsoleInterface()
32+
33+
# Load pyproject.toml data
34+
pyproject_path = project_root / 'pyproject.toml'
35+
pyproject_data = loads(pyproject_path.read_text(encoding='utf-8'))
36+
37+
# Create and use the project directly
38+
project = Project(project_configuration, interface, pyproject_data)
2339

24-
assert result.returncode == 0, f'PDM install failed: {result.stderr}'
40+
# Call install directly to get structured results
41+
project.install()
2542

2643
# Run the CMake configuration command
2744
result = subprocess.run(['cmake', '--preset=default'], capture_output=True, text=True, check=False)
File renamed without changes.

0 commit comments

Comments
 (0)