revitdevtool-pytest 0.3.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/.gitignore +1 -0
  2. revitdevtool_pytest-0.4.0/.run/Attach.run.xml +8 -0
  3. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/.vscode/settings.json +1 -1
  4. revitdevtool_pytest-0.4.0/AGENTS.md +202 -0
  5. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/CHANGELOG.md +18 -0
  6. revitdevtool_pytest-0.4.0/PKG-INFO +220 -0
  7. revitdevtool_pytest-0.4.0/README.md +193 -0
  8. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/pyproject.toml +14 -10
  9. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/__init__.py +1 -1
  10. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/bridge.py +26 -1
  11. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/connection.py +11 -16
  12. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/constants.py +14 -5
  13. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/discovery.py +7 -17
  14. revitdevtool_pytest-0.4.0/src/revitdevtool_pytest/ipy_collect.py +91 -0
  15. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/models.py +3 -0
  16. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/plugin.py +92 -54
  17. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/reporting.py +105 -19
  18. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/suite_leasing.py +30 -28
  19. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/suite_lock.py +21 -13
  20. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Civil3d/test_active_state.py +2 -2
  21. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/conftest.py +2 -2
  22. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/contracts.py +1 -1
  23. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/custom_workflow.py +3 -3
  24. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/field_mapping.py +1 -1
  25. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/header_workflow.py +3 -3
  26. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/serializer.py +3 -3
  27. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/template_workflow.py +6 -6
  28. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_schedule_custom_copy_revit.py +9 -9
  29. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_schedule_field_mapping_revit.py +3 -3
  30. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_schedule_header_capture_revit.py +7 -7
  31. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_schedule_header_copy_revit.py +6 -6
  32. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_scheduleformatting_revit.py +16 -16
  33. revitdevtool_pytest-0.4.0/tests/Revit_Ipy/conftest.py +5 -0
  34. revitdevtool_pytest-0.4.0/tests/Revit_Ipy/ipylib/__init__.py +16 -0
  35. revitdevtool_pytest-0.4.0/tests/Revit_Ipy/ipylib/geom.py +14 -0
  36. revitdevtool_pytest-0.4.0/tests/Revit_Ipy/nested/__init__.py +1 -0
  37. revitdevtool_pytest-0.4.0/tests/Revit_Ipy/nested/test_nested_import_ipy.py +11 -0
  38. revitdevtool_pytest-0.4.0/tests/Revit_Ipy/pyproject.toml +5 -0
  39. revitdevtool_pytest-0.4.0/tests/Revit_Ipy/test_document_ipy.py +41 -0
  40. revitdevtool_pytest-0.4.0/tests/Revit_Ipy/test_ipylib_ipy.py +22 -0
  41. revitdevtool_pytest-0.4.0/tests/plugin/conftest.py +9 -0
  42. revitdevtool_pytest-0.4.0/tests/plugin/test_fan_suite_results.py +50 -0
  43. revitdevtool_pytest-0.4.0/tests/plugin/test_ipy_collect.py +122 -0
  44. revitdevtool_pytest-0.4.0/tests/plugin/test_plugin_runtestloop.py +23 -0
  45. revitdevtool_pytest-0.4.0/tests/plugin/test_reporting.py +212 -0
  46. revitdevtool_pytest-0.4.0/tests/plugin/test_suite_leasing.py +25 -0
  47. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/uv.lock +2 -2
  48. revitdevtool_pytest-0.3.0/AGENTS.md +0 -164
  49. revitdevtool_pytest-0.3.0/PKG-INFO +0 -234
  50. revitdevtool_pytest-0.3.0/README.md +0 -207
  51. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/.github/workflows/publish.yml +0 -0
  52. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/CLAUDE.md +0 -0
  53. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/LICENSE +0 -0
  54. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/src/revitdevtool_pytest/dialog_resolver.py +0 -0
  55. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Civil3d/conftest.py +0 -0
  56. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Civil3d/test_civil3d_api.py +0 -0
  57. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Civil3d/test_document.py +0 -0
  58. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Civil3d/test_geometry.py +0 -0
  59. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Civil3d/test_smoke.py +0 -0
  60. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/_session_probe.py +0 -0
  61. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/__init__.py +0 -0
  62. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/constants.py +0 -0
  63. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/model.py +0 -0
  64. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/table_cell.py +0 -0
  65. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/schedule/test.json +0 -0
  66. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_active_state.py +0 -0
  67. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_dependencies.py +0 -0
  68. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_image_export.py +0 -0
  69. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_revit_basic.py +0 -0
  70. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_revit_document.py +0 -0
  71. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_revit_transactions.py +0 -0
  72. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_session_cleanliness.py +0 -0
  73. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_smoke.py +0 -0
  74. {revitdevtool_pytest-0.3.0 → revitdevtool_pytest-0.4.0}/tests/Revit/test_streaming.py +0 -0
@@ -16,3 +16,4 @@ build/
16
16
  venv/
17
17
  .idea/
18
18
  .gitnexus
19
+ .cocoindex_code/
@@ -0,0 +1,8 @@
1
+ <component name="ProjectRunConfigurationManager">
2
+ <configuration default="false" name="Attach Host: Python" type="PythonDapAttachConfiguration">
3
+ <option name="localRoot" value="$PROJECT_DIR$" />
4
+ <option name="remoteAddress" value="localhost:5678" />
5
+ <option name="remoteRoot" value="$PROJECT_DIR$" />
6
+ <method v="2" />
7
+ </configuration>
8
+ </component>
@@ -21,5 +21,5 @@
21
21
  ],
22
22
  "basedpyright.importStrategy": "useBundled",
23
23
  "basedpyright.analysis.typeCheckingMode": "off",
24
- "python.testing.pytestEnabled": true,
24
+ "python.testing.pytestEnabled": true
25
25
  }
@@ -0,0 +1,202 @@
1
+ # AGENTS
2
+
3
+ ## Overview
4
+
5
+ pytest client plugin for [RevitDevTool](https://github.com/trgiangv/RevitDevTool). Collects tests locally, executes them inside a live host via Named Pipe JSON-RPC (`BridgeMessage` framing).
6
+
7
+ **Supported today:** **Revit** and **AutoCAD family** (AutoCAD, Civil 3D, Plant 3D, Architecture / Mechanical / MEP / Electrical / Map 3D). Other hosts may appear in the registry but are **in progress** — not validated end-to-end yet.
8
+
9
+ **Package:** `revitdevtool_pytest` (PyPI) · **Entry point:** `revitdevtool` · **Version:** see `pyproject.toml`
10
+
11
+ ## Architecture
12
+
13
+ ```mermaid
14
+ flowchart LR
15
+ Collect[Local collect] --> Pipe["DevTools_*"]
16
+ Pipe --> CPython[PytestRunner.py]
17
+ Pipe --> IPy[IpyTestDriver.py]
18
+ CPython --> Report[Local report]
19
+ IPy --> Report
20
+ ```
21
+
22
+ Do **not** connect to `DevToolsMcp_*` (MCP SDK). Pytest uses `DevTools_{Host}_{Version}_{PID}` on `DevToolsPipeServer`.
23
+
24
+ ### Client modules (`src/revitdevtool_pytest/`)
25
+
26
+ | Module | Role |
27
+ |--------|------|
28
+ | `plugin.py` | Hook orchestrator — lifecycle, options, bridge setup |
29
+ | `ipy_collect.py` | Pytest collect convention `test_*_ipy.py` → `ipytests/run` (no CPython import) |
30
+ | `connection.py` | Discover, connect, lease, launch, retry |
31
+ | `bridge.py` | Named Pipe framing, `tests/run` / `ipytests/run` RPC |
32
+ | `discovery.py` | Pipe scan, host registry, process launch |
33
+ | `reporting.py` | Remote `CaseResult` → pytest `TestReport` |
34
+ | `suite_leasing.py` | File-based suite → host PID allocation |
35
+ | `suite_lock.py` | Windows mutex — same suite, one pytest process |
36
+ | `dialog_resolver.py` | Auto-dismiss host startup dialogs (Win32) |
37
+ | `models.py` | Wire models (mirror C# `PytestContracts`) |
38
+ | `constants.py` | Options, host registry, pipe pattern |
39
+
40
+ ### Host-side (RevitDevTool repo)
41
+
42
+ | Location | Role |
43
+ |----------|------|
44
+ | `DevTools.Execution/External/DevToolsPipeServer.cs` | Pytest/control pipe server |
45
+ | `DevTools.Execution/External/Handlers/PytestRequestHandler.cs` | Routes `tests/run` |
46
+ | `DevTools.Execution/External/Handlers/IpyTestRequestHandler.cs` | Routes `ipytests/run` |
47
+ | `DevTools.Execution/External/Testing/` | `PytestExecutionService`, `IpyTestExecutionService`, `PytestContracts` |
48
+ | `DevTools.Execution/Resources/scripts/PytestRunner.py` | In-host `pytest.main()` runner |
49
+ | `DevTools.Execution/Resources/scripts/IpyTestDriver.py` | In-host IronPython test driver (2.7 / 3.4) |
50
+
51
+ Wire methods: `tests/run` (CPython pytest), `ipytests/run` (IronPython unittest, pyRevit-first). Progress notifications: `notifications/tests/progress`.
52
+
53
+ **IPy collect convention is `test_*_ipy.py`** (pytest `test_` + `_ipy` marker). That name is only how the plugin routes onto `ipytests/run`; the host runs unittest on the requested paths and does not require it. Dialect: 2.7 / 3.4, `unittest.TestCase`, no pytest fixtures / PEP 723 (CPython `tests/run` still installs `# /// script` via pixi). `*_ipy_script.py` is execution-tree script, not a test file. Library modules use normal names (`ipylib/geom.py`).
54
+
55
+ ## This repo layout
56
+
57
+ Integration tests for the plugin — not shipped in the wheel.
58
+
59
+ ```
60
+ tests/
61
+ plugin/ # local plugin unit tests (no host); sets REVITDEVTOOL_PYTEST_DISABLE
62
+ Revit/ # default testpaths in pyproject.toml
63
+ conftest.py # PEP 723 deps (CPython tests/run only), revit_doc, rollback fixtures
64
+ schedule/ # shared schedule helpers (import as schedule.*)
65
+ test_*.py # CPython pytest → tests/run
66
+ test_*_ipy.py # mixed-tree IronPython unittest → ipytests/run
67
+ Revit_Ipy/ # IPy libs + more test_*_ipy.py (folder is not identity)
68
+ ipylib/ # 2.7/3.4 helpers imported by tests
69
+ test_*_ipy.py
70
+ Civil3d/
71
+ conftest.py # acad_app, acad_doc, transaction fixtures
72
+ test_*.py
73
+ ```
74
+
75
+ Switch suites via `pyproject.toml`:
76
+
77
+ ```toml
78
+ testpaths = ["tests/Revit"] # or tests/Civil3d
79
+ host_name = "revit" # or civil3d, autocad, …
80
+ host_version = "2025"
81
+ ```
82
+
83
+ Schedule helpers live under `tests/Revit/schedule/`. Imports use the `schedule` package name (e.g. `from schedule.constants import …`), with `pythonpath = ["tests/Revit"]` in `pyproject.toml`.
84
+
85
+ `revit_doc` opens `REVIT_TEST_MODEL_PATH` (default `F:\Project1.rvt`) when set.
86
+
87
+ ## Host support
88
+
89
+ ### Supported (tested)
90
+
91
+ | Host Name | Pipe Prefix | Executable | Product ID |
92
+ |-----------|------------|------------|------------|
93
+ | `revit` | `Revit` | `Revit.exe` | — |
94
+ | `autocad` | `AutoCad` | `acad.exe` | `01` |
95
+ | `civil3d` | `Civil3D` | `acad.exe` | `00` |
96
+ | `plant3d` | `Plant3D` | `acad.exe` | `17` |
97
+ | `acadarch` | `AcadArch` | `acad.exe` | `04` |
98
+ | `acadmech` | `AcadMech` | `acad.exe` | `05` |
99
+ | `acadmep` | `AcadMep` | `acad.exe` | `06` |
100
+ | `acadelec` | `AcadElec` | `acad.exe` | `07` |
101
+ | `acadmap3d` | `AcadMap3D` | `acad.exe` | `02` |
102
+
103
+ ### In progress (not supported yet)
104
+
105
+ | Host Name | Pipe Prefix | Notes |
106
+ |-----------|------------|--------|
107
+ | `navisworks` | `Navisworks` | Registry stub only |
108
+ | `rhino` | `Rhino` | Registry stub only |
109
+ | `tekla` | `Tekla` | Registry stub only |
110
+
111
+ Unknown host names still get a fallback `HostConfig(pipe_prefix=host_name)` for experimentation, but are not supported. Do not document or rely on them until promoted to **Supported**.
112
+
113
+ ### Pipe format
114
+
115
+ `DevTools_{Host}_{Version}_{PID}` — regex `^DevTools_\w+_[^_]+_\d+$`
116
+
117
+ Examples: `DevTools_Revit_2025_12345`, `DevTools_AutoCad_2026_7890`
118
+
119
+ ## Key design decisions
120
+
121
+ - **Dual pytest model:** collect locally, execute in-host (IDE tree + host thread).
122
+ - **`--capture=sys`:** required in embedded Python.NET (no fd-level `dup2`).
123
+ - **`sys.__pytest_running__`:** set by `PytestRunner.py`; setup scripts skip I/O hijack during runs.
124
+ - **Streaming vs batch:** CLI streams progress; IDE adapters (`vscode_pytest`, `TEST_RUN_PIPE`) get one batch.
125
+ - **Suite leasing + mutex:** one pytest process per host+version+workspace; CPython and IronPython tests in that workspace reuse the same host PID. One invocation still cannot mix two `conftest.py` trees — run `tests/Revit` then `tests/Revit_Ipy` separately (same Revit). Mixed `test_*.py` + `test_*_ipy.py` under one conftest is fine (`tests/run` then `ipytests/run` on the same pipe).
126
+ - **`force_launch`:** force new host + wait for that PID's pipe; skip reuse.
127
+ - **No matching instance:** after discovery fails, plugin auto-launches when `host_version` is set.
128
+
129
+ ## Running tests
130
+
131
+ Always **`uv run pytest`** from this repo root — do not use system Python or bare `pytest`.
132
+
133
+ ```powershell
134
+ cd c:\Users\truon\source\repos\RevitDevTool.PyTest
135
+ uv run pytest -v
136
+ uv run pytest tests/Revit/test_active_state.py::test_active_view_info -v
137
+ uv run pytest --host-version=2025 -v
138
+ uv run pytest --host autocad --host-version 2026 -v
139
+ uv run pytest --force-launch --host-version=2025 -v
140
+ uv run pytest tests/Revit_Ipy -v --host-version=2025
141
+ ```
142
+
143
+ Plugin auto-enables `-rP` (stdout for passing tests).
144
+
145
+ ## Configuration
146
+
147
+ `[tool.pytest.ini_options]` in `pyproject.toml`:
148
+
149
+ ```toml
150
+ testpaths = ["tests/Revit"]
151
+ pythonpath = ["tests/Revit"]
152
+ host_name = "revit"
153
+ host_version = "2025"
154
+ force_launch = false
155
+ per_test_timeout = "60"
156
+ launch_timeout = "180"
157
+ host_pipe = ""
158
+ ```
159
+
160
+ CLI: `--host`, `--host-version`, `--per-test-timeout`, `--host-pipe`, `--force-launch`, `--launch-timeout`.
161
+
162
+ ## Fixtures pattern
163
+
164
+ ```python
165
+ # conftest.py (Revit)
166
+ @pytest.fixture(scope="session")
167
+ def revit_uiapp():
168
+ return __revit__ # noqa: F821
169
+
170
+ @pytest.fixture(scope="session")
171
+ def revit_doc(revit_uiapp):
172
+ return revit_uiapp.ActiveUIDocument.Document
173
+
174
+ @pytest.fixture
175
+ def revit_auto_rollback(revit_transaction_service):
176
+ revit_transaction_service.StartChanges()
177
+ try:
178
+ yield revit_transaction_service
179
+ finally:
180
+ revit_transaction_service.RevertChanges()
181
+ ```
182
+
183
+ Host API imports belong **inside test bodies** (or fixture bodies), not at module top level.
184
+
185
+ ## Common traps
186
+
187
+ - Tests run **inside the host**, not locally.
188
+ - `__revit__` only exists at runtime — use fixtures.
189
+ - Host APIs need the main thread; execution is sequential via `IHostContextExecutor`.
190
+ - Pytest pipe ≠ MCP pipe (`DevTools_*` vs `DevToolsMcp_*`).
191
+ - PEP 723 deps in CPython `conftest.py` / `test_*.py` are installed by `PytestDependencyService` before `tests/run`. **IronPython `test_*_ipy.py` does not use PEP 723** — `ipytests/run` never calls that service; IPy 2.7/3.4 cannot import CPython wheels.
192
+ - `--force-launch` requires `--host-version`.
193
+ - `print()` captured via `--capture=sys` → `CaseResult.stdout`.
194
+ - IronPython tests: no pytest fixtures; **no PEP 723**; host APIs inside methods; `test_*_ipy.py` is pytest routing only, not a host filename contract.
195
+
196
+ ## Change rules
197
+
198
+ - Wire protocol: sync `models.py` ↔ `PytestContracts.cs` / `PytestBridgeMethods.cs`.
199
+ - New CLI options: `pytest_addoption` + `parser.addini`.
200
+ - `connection.py` stays stateless; only `plugin.py` holds session globals.
201
+ - After connection/discovery changes: test `force_launch = true` and `false`.
202
+ - `HOST_REGISTRY` ↔ C# `HostApp` enum + `AcadPathResolver.ProductIdMap`.
@@ -2,6 +2,24 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.4.0] - 2026-08-31
6
+
7
+ ### Added
8
+
9
+ - IronPython tests: files named `test_*_ipy.py` run on the host via `ipytests/run`
10
+ (`unittest`). CPython `test_*.py` still uses `tests/run`.
11
+ - CPython and IronPython suites in the same workspace reuse one host instance.
12
+
13
+ ### Changed
14
+
15
+ - Pipe name is `DevTools_{Host}_{Version}_{PID}` (was `{Host}_{Version}_{PID}`).
16
+ Pytest does not use the MCP pipe `DevToolsMcp_*`.
17
+ - CLI/INI names match RevitDevTool.TestAdapter (breaking vs 0.3.0):
18
+ `--host-launch` → `--force-launch`, `--host-timeout` → `--per-test-timeout`,
19
+ `--host-launch-timeout` → `--launch-timeout`. `--host` / `--host-version` unchanged.
20
+ - PEP 723 `# /// script` is installed only for CPython `tests/run`. IronPython
21
+ tests cannot use CPython wheels.
22
+
5
23
  ## [0.3.0] - 2026-05-31
6
24
 
7
25
  ### Added
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.5
2
+ Name: revitdevtool_pytest
3
+ Version: 0.4.0
4
+ Summary: pytest plugin for testing CAD/BIM API code inside host applications via RevitDevTool Named Pipe bridge
5
+ Project-URL: Homepage, https://github.com/trgiangv/RevitDevTool.PyTest
6
+ Project-URL: Repository, https://github.com/trgiangv/RevitDevTool.PyTest
7
+ Project-URL: Issues, https://github.com/trgiangv/RevitDevTool.PyTest/issues
8
+ Author: Truong Giang Vu
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: autocad,bim,civil3d,multi-host,pytest,revit,revit-api,revitdevtool,testing
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: Pytest
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Testing
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: pytest>=9.0
25
+ Requires-Dist: pywin32>=311
26
+ Description-Content-Type: text/markdown
27
+
28
+ # RevitDevTool.PyTest
29
+
30
+ [![PyPI version](https://img.shields.io/pypi/v/revitdevtool_pytest)](https://pypi.org/project/revitdevtool-pytest/)
31
+ [![Python](https://img.shields.io/pypi/pyversions/revitdevtool_pytest)](https://pypi.org/project/revitdevtool-pytest/)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
33
+
34
+ pytest plugin for testing CAD/BIM API code inside live host applications via the RevitDevTool Named Pipe bridge.
35
+
36
+ Write tests locally with pytest; execution happens inside the host process. **Currently supported:** **Revit** and **AutoCAD family** only. Navisworks, Rhino, Tekla, and other hosts are **in progress**.
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install revitdevtool_pytest
42
+ # or
43
+ uv add revitdevtool_pytest
44
+ ```
45
+
46
+ | Requirement | Version |
47
+ |---|---|
48
+ | Python | >= 3.10 |
49
+ | pytest | >= 9.0 |
50
+ | pywin32 | >= 311 |
51
+ | OS | Windows (Named Pipes) |
52
+ | Host | [RevitDevTool](https://github.com/trgiangv/RevitDevTool) add-in loaded |
53
+
54
+ ## Supported hosts
55
+
56
+ ### Supported today
57
+
58
+ | `host_name` | Application | Pipe prefix |
59
+ |---|---|---|
60
+ | `revit` | Autodesk Revit | `Revit` |
61
+ | `autocad` | AutoCAD | `AutoCad` |
62
+ | `civil3d` | Civil 3D | `Civil3D` |
63
+ | `plant3d` | Plant 3D | `Plant3D` |
64
+ | `acadarch` | AutoCAD Architecture | `AcadArch` |
65
+ | `acadmech` | AutoCAD Mechanical | `AcadMech` |
66
+ | `acadmep` | AutoCAD MEP | `AcadMep` |
67
+ | `acadelec` | AutoCAD Electrical | `AcadElec` |
68
+ | `acadmap3d` | AutoCAD Map 3D | `AcadMap3D` |
69
+
70
+ ### In progress
71
+
72
+ | `host_name` | Application | Status |
73
+ |---|---|---|
74
+ | `navisworks` | Navisworks | Not validated |
75
+ | `rhino` | Rhino | Not validated |
76
+ | `tekla` | Tekla Structures | Not validated |
77
+
78
+ The plugin registry may list additional names for future work. Treat them as experimental until documented here as **Supported today**.
79
+
80
+ ### Pipe names (pytest only)
81
+
82
+ The plugin connects to the **pytest/control** pipe:
83
+
84
+ `DevTools_{Host}_{Version}_{PID}`
85
+
86
+ Examples: `DevTools_Revit_2025_12345`, `DevTools_AutoCad_2026_7890`
87
+
88
+ MCP clients use a separate pipe: `DevToolsMcp_{Host}_{Version}_{PID}`. Do not point pytest at an MCP pipe.
89
+
90
+ ## Quick start (consumer project)
91
+
92
+ ```bash
93
+ uv init my-host-tests && cd my-host-tests
94
+ uv add revitdevtool_pytest pytest
95
+ ```
96
+
97
+ `pyproject.toml`:
98
+
99
+ ```toml
100
+ [tool.pytest.ini_options]
101
+ host_name = "revit"
102
+ host_version = "2025"
103
+ force_launch = false
104
+ per_test_timeout = "60"
105
+ launch_timeout = "180"
106
+ ```
107
+
108
+ `tests/conftest.py`:
109
+
110
+ ```python
111
+ import pytest
112
+
113
+ @pytest.fixture(scope="session")
114
+ def revit_uiapp():
115
+ return __revit__
116
+
117
+ @pytest.fixture(scope="session")
118
+ def revit_doc(revit_uiapp):
119
+ return revit_uiapp.ActiveUIDocument.Document
120
+ ```
121
+
122
+ `tests/test_smoke.py`:
123
+
124
+ ```python
125
+ def test_active_view(revit_doc):
126
+ assert revit_doc.ActiveView is not None
127
+ ```
128
+
129
+ ```bash
130
+ uv run pytest -v
131
+ ```
132
+
133
+ ## Configuration
134
+
135
+ | Option | Default | Description |
136
+ |---|---|---|
137
+ | `host_name` | `"revit"` | Target host — use **Supported today** names only. |
138
+ | `host_version` | — | Version string (`"2025"`, `"8.0"`, …). Required for launch. |
139
+ | `force_launch` | `false` | Force a **new** host instance (skip reuse). Requires `host_version`. |
140
+ | `per_test_timeout` | `"60"` | Per-test budget (seconds). The `tests/run` pipe wait is this × collected tests. |
141
+ | `launch_timeout` | `"180"` | Wait for host pipe after launch (seconds). |
142
+ | `host_pipe` | — | Explicit pipe name (skip discovery). |
143
+
144
+ CLI flags override INI: `--host`, `--host-version`, `--force-launch`, `--per-test-timeout`, `--launch-timeout`, `--host-pipe`.
145
+
146
+ ### Connection behavior
147
+
148
+ 1. **`force_launch = false` (default):** scan for `DevTools_*` pipes matching `host_name` / `host_version`, reuse a free instance (suite leasing), or reconnect to a leased PID.
149
+ 2. **No matching instance:** auto-launch host when `host_version` is set (unless `--host-pipe` pins an existing pipe).
150
+ 3. **`force_launch = true`:** always spawn a new process and wait for its pipe.
151
+
152
+ ### Print output
153
+
154
+ Captured `print()` from in-host tests is shown for passing tests (same as `-rP`). No extra flags needed.
155
+
156
+ ## Writing tests
157
+
158
+ - Put **host API imports inside functions** — collection runs on your machine, execution in the host.
159
+ - Use fixtures for `__revit__`, documents, and rollback helpers.
160
+ - Declare suite dependencies with **PEP 723** in CPython `conftest.py` (or `test_*.py`); RevitDevTool installs them before `tests/run`. IronPython `test_*_ipy.py` **does not** use PEP 723 — `ipytests/run` never installs packages.
161
+
162
+ ```python
163
+ # /// script
164
+ # dependencies = ["numpy>=2.0", "polars>=1.0"]
165
+ # ///
166
+ ```
167
+
168
+ ### Revit rollback fixture
169
+
170
+ ```python
171
+ @pytest.fixture
172
+ def revit_auto_rollback(revit_transaction_service):
173
+ revit_transaction_service.StartChanges()
174
+ try:
175
+ yield revit_transaction_service
176
+ finally:
177
+ revit_transaction_service.RevertChanges()
178
+ ```
179
+
180
+ ## How it works
181
+
182
+ 1. Local pytest collects tests.
183
+ 2. Plugin connects to `DevTools_{Host}_{Version}_{PID}`.
184
+ 3. Node IDs sent via JSON-RPC method `tests/run` (`BridgeMessage` length-prefixed frames).
185
+ 4. Host runs `PytestRunner.py` embedded in RevitDevTool (`PytestExecutionService` on main thread).
186
+ 5. Results and optional progress notifications return to the client.
187
+ 6. Plugin maps `CaseResult` → standard pytest reports.
188
+
189
+ ## Developing this repository
190
+
191
+ Clone and run integration tests against a live host:
192
+
193
+ ```powershell
194
+ cd RevitDevTool.PyTest
195
+ uv run pytest -v # Revit suite (default)
196
+ uv run pytest tests/Revit/test_smoke.py -v
197
+ uv run pytest --host civil3d --host-version 2026 # after switching testpaths/host in pyproject.toml
198
+ ```
199
+
200
+ ### Layout
201
+
202
+ | Path | Purpose |
203
+ |---|---|
204
+ | `src/revitdevtool_pytest/` | Plugin source (published wheel) |
205
+ | `tests/Revit/` | Revit integration tests + `schedule/` helpers |
206
+ | `tests/Civil3d/` | Civil 3D / AutoCAD API tests |
207
+
208
+ Revit suite settings in `pyproject.toml`: `testpaths = ["tests/Revit"]`, `pythonpath = ["tests/Revit"]` for local `schedule` imports.
209
+
210
+ Optional env: `REVIT_TEST_MODEL_PATH` — path to `.rvt` for `revit_doc` fixture (default `F:\Project1.rvt`).
211
+
212
+ ## IDE integration
213
+
214
+ **VS Code / Cursor** — `python.testing.pytestEnabled: true`. Plugin detects `vscode_pytest` / `TEST_RUN_PIPE` and disables CLI streaming to avoid duplicate tree entries.
215
+
216
+ **PyCharm** — enable pytest as test runner.
217
+
218
+ ## License
219
+
220
+ [MIT](LICENSE)
@@ -0,0 +1,193 @@
1
+ # RevitDevTool.PyTest
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/revitdevtool_pytest)](https://pypi.org/project/revitdevtool-pytest/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/revitdevtool_pytest)](https://pypi.org/project/revitdevtool-pytest/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ pytest plugin for testing CAD/BIM API code inside live host applications via the RevitDevTool Named Pipe bridge.
8
+
9
+ Write tests locally with pytest; execution happens inside the host process. **Currently supported:** **Revit** and **AutoCAD family** only. Navisworks, Rhino, Tekla, and other hosts are **in progress**.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install revitdevtool_pytest
15
+ # or
16
+ uv add revitdevtool_pytest
17
+ ```
18
+
19
+ | Requirement | Version |
20
+ |---|---|
21
+ | Python | >= 3.10 |
22
+ | pytest | >= 9.0 |
23
+ | pywin32 | >= 311 |
24
+ | OS | Windows (Named Pipes) |
25
+ | Host | [RevitDevTool](https://github.com/trgiangv/RevitDevTool) add-in loaded |
26
+
27
+ ## Supported hosts
28
+
29
+ ### Supported today
30
+
31
+ | `host_name` | Application | Pipe prefix |
32
+ |---|---|---|
33
+ | `revit` | Autodesk Revit | `Revit` |
34
+ | `autocad` | AutoCAD | `AutoCad` |
35
+ | `civil3d` | Civil 3D | `Civil3D` |
36
+ | `plant3d` | Plant 3D | `Plant3D` |
37
+ | `acadarch` | AutoCAD Architecture | `AcadArch` |
38
+ | `acadmech` | AutoCAD Mechanical | `AcadMech` |
39
+ | `acadmep` | AutoCAD MEP | `AcadMep` |
40
+ | `acadelec` | AutoCAD Electrical | `AcadElec` |
41
+ | `acadmap3d` | AutoCAD Map 3D | `AcadMap3D` |
42
+
43
+ ### In progress
44
+
45
+ | `host_name` | Application | Status |
46
+ |---|---|---|
47
+ | `navisworks` | Navisworks | Not validated |
48
+ | `rhino` | Rhino | Not validated |
49
+ | `tekla` | Tekla Structures | Not validated |
50
+
51
+ The plugin registry may list additional names for future work. Treat them as experimental until documented here as **Supported today**.
52
+
53
+ ### Pipe names (pytest only)
54
+
55
+ The plugin connects to the **pytest/control** pipe:
56
+
57
+ `DevTools_{Host}_{Version}_{PID}`
58
+
59
+ Examples: `DevTools_Revit_2025_12345`, `DevTools_AutoCad_2026_7890`
60
+
61
+ MCP clients use a separate pipe: `DevToolsMcp_{Host}_{Version}_{PID}`. Do not point pytest at an MCP pipe.
62
+
63
+ ## Quick start (consumer project)
64
+
65
+ ```bash
66
+ uv init my-host-tests && cd my-host-tests
67
+ uv add revitdevtool_pytest pytest
68
+ ```
69
+
70
+ `pyproject.toml`:
71
+
72
+ ```toml
73
+ [tool.pytest.ini_options]
74
+ host_name = "revit"
75
+ host_version = "2025"
76
+ force_launch = false
77
+ per_test_timeout = "60"
78
+ launch_timeout = "180"
79
+ ```
80
+
81
+ `tests/conftest.py`:
82
+
83
+ ```python
84
+ import pytest
85
+
86
+ @pytest.fixture(scope="session")
87
+ def revit_uiapp():
88
+ return __revit__
89
+
90
+ @pytest.fixture(scope="session")
91
+ def revit_doc(revit_uiapp):
92
+ return revit_uiapp.ActiveUIDocument.Document
93
+ ```
94
+
95
+ `tests/test_smoke.py`:
96
+
97
+ ```python
98
+ def test_active_view(revit_doc):
99
+ assert revit_doc.ActiveView is not None
100
+ ```
101
+
102
+ ```bash
103
+ uv run pytest -v
104
+ ```
105
+
106
+ ## Configuration
107
+
108
+ | Option | Default | Description |
109
+ |---|---|---|
110
+ | `host_name` | `"revit"` | Target host — use **Supported today** names only. |
111
+ | `host_version` | — | Version string (`"2025"`, `"8.0"`, …). Required for launch. |
112
+ | `force_launch` | `false` | Force a **new** host instance (skip reuse). Requires `host_version`. |
113
+ | `per_test_timeout` | `"60"` | Per-test budget (seconds). The `tests/run` pipe wait is this × collected tests. |
114
+ | `launch_timeout` | `"180"` | Wait for host pipe after launch (seconds). |
115
+ | `host_pipe` | — | Explicit pipe name (skip discovery). |
116
+
117
+ CLI flags override INI: `--host`, `--host-version`, `--force-launch`, `--per-test-timeout`, `--launch-timeout`, `--host-pipe`.
118
+
119
+ ### Connection behavior
120
+
121
+ 1. **`force_launch = false` (default):** scan for `DevTools_*` pipes matching `host_name` / `host_version`, reuse a free instance (suite leasing), or reconnect to a leased PID.
122
+ 2. **No matching instance:** auto-launch host when `host_version` is set (unless `--host-pipe` pins an existing pipe).
123
+ 3. **`force_launch = true`:** always spawn a new process and wait for its pipe.
124
+
125
+ ### Print output
126
+
127
+ Captured `print()` from in-host tests is shown for passing tests (same as `-rP`). No extra flags needed.
128
+
129
+ ## Writing tests
130
+
131
+ - Put **host API imports inside functions** — collection runs on your machine, execution in the host.
132
+ - Use fixtures for `__revit__`, documents, and rollback helpers.
133
+ - Declare suite dependencies with **PEP 723** in CPython `conftest.py` (or `test_*.py`); RevitDevTool installs them before `tests/run`. IronPython `test_*_ipy.py` **does not** use PEP 723 — `ipytests/run` never installs packages.
134
+
135
+ ```python
136
+ # /// script
137
+ # dependencies = ["numpy>=2.0", "polars>=1.0"]
138
+ # ///
139
+ ```
140
+
141
+ ### Revit rollback fixture
142
+
143
+ ```python
144
+ @pytest.fixture
145
+ def revit_auto_rollback(revit_transaction_service):
146
+ revit_transaction_service.StartChanges()
147
+ try:
148
+ yield revit_transaction_service
149
+ finally:
150
+ revit_transaction_service.RevertChanges()
151
+ ```
152
+
153
+ ## How it works
154
+
155
+ 1. Local pytest collects tests.
156
+ 2. Plugin connects to `DevTools_{Host}_{Version}_{PID}`.
157
+ 3. Node IDs sent via JSON-RPC method `tests/run` (`BridgeMessage` length-prefixed frames).
158
+ 4. Host runs `PytestRunner.py` embedded in RevitDevTool (`PytestExecutionService` on main thread).
159
+ 5. Results and optional progress notifications return to the client.
160
+ 6. Plugin maps `CaseResult` → standard pytest reports.
161
+
162
+ ## Developing this repository
163
+
164
+ Clone and run integration tests against a live host:
165
+
166
+ ```powershell
167
+ cd RevitDevTool.PyTest
168
+ uv run pytest -v # Revit suite (default)
169
+ uv run pytest tests/Revit/test_smoke.py -v
170
+ uv run pytest --host civil3d --host-version 2026 # after switching testpaths/host in pyproject.toml
171
+ ```
172
+
173
+ ### Layout
174
+
175
+ | Path | Purpose |
176
+ |---|---|
177
+ | `src/revitdevtool_pytest/` | Plugin source (published wheel) |
178
+ | `tests/Revit/` | Revit integration tests + `schedule/` helpers |
179
+ | `tests/Civil3d/` | Civil 3D / AutoCAD API tests |
180
+
181
+ Revit suite settings in `pyproject.toml`: `testpaths = ["tests/Revit"]`, `pythonpath = ["tests/Revit"]` for local `schedule` imports.
182
+
183
+ Optional env: `REVIT_TEST_MODEL_PATH` — path to `.rvt` for `revit_doc` fixture (default `F:\Project1.rvt`).
184
+
185
+ ## IDE integration
186
+
187
+ **VS Code / Cursor** — `python.testing.pytestEnabled: true`. Plugin detects `vscode_pytest` / `TEST_RUN_PIPE` and disables CLI streaming to avoid duplicate tree entries.
188
+
189
+ **PyCharm** — enable pytest as test runner.
190
+
191
+ ## License
192
+
193
+ [MIT](LICENSE)