revitdevtool-pytest 0.1.0__tar.gz → 0.3.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 (69) hide show
  1. revitdevtool_pytest-0.3.0/AGENTS.md +164 -0
  2. revitdevtool_pytest-0.3.0/CHANGELOG.md +151 -0
  3. revitdevtool_pytest-0.3.0/CLAUDE.md +1 -0
  4. revitdevtool_pytest-0.3.0/PKG-INFO +234 -0
  5. revitdevtool_pytest-0.3.0/README.md +207 -0
  6. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/pyproject.toml +12 -8
  7. revitdevtool_pytest-0.3.0/src/revitdevtool_pytest/__init__.py +3 -0
  8. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/src/revitdevtool_pytest/bridge.py +14 -53
  9. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/src/revitdevtool_pytest/connection.py +70 -69
  10. revitdevtool_pytest-0.3.0/src/revitdevtool_pytest/constants.py +168 -0
  11. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/src/revitdevtool_pytest/dialog_resolver.py +10 -11
  12. revitdevtool_pytest-0.3.0/src/revitdevtool_pytest/discovery.py +303 -0
  13. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/src/revitdevtool_pytest/models.py +0 -27
  14. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/src/revitdevtool_pytest/plugin.py +59 -29
  15. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/src/revitdevtool_pytest/reporting.py +6 -6
  16. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/src/revitdevtool_pytest/suite_leasing.py +13 -57
  17. revitdevtool_pytest-0.3.0/tests/Civil3d/conftest.py +70 -0
  18. revitdevtool_pytest-0.3.0/tests/Civil3d/test_active_state.py +56 -0
  19. revitdevtool_pytest-0.3.0/tests/Civil3d/test_civil3d_api.py +149 -0
  20. revitdevtool_pytest-0.3.0/tests/Civil3d/test_document.py +110 -0
  21. revitdevtool_pytest-0.3.0/tests/Civil3d/test_geometry.py +129 -0
  22. revitdevtool_pytest-0.3.0/tests/Civil3d/test_smoke.py +25 -0
  23. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/conftest.py +1 -1
  24. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_active_state.py +2 -2
  25. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/uv.lock +1 -1
  26. revitdevtool_pytest-0.1.0/.claude/skills/gitnexus/gitnexus-cli/SKILL.md +0 -82
  27. revitdevtool_pytest-0.1.0/.claude/skills/gitnexus/gitnexus-debugging/SKILL.md +0 -89
  28. revitdevtool_pytest-0.1.0/.claude/skills/gitnexus/gitnexus-exploring/SKILL.md +0 -78
  29. revitdevtool_pytest-0.1.0/.claude/skills/gitnexus/gitnexus-guide/SKILL.md +0 -64
  30. revitdevtool_pytest-0.1.0/.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md +0 -97
  31. revitdevtool_pytest-0.1.0/.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md +0 -121
  32. revitdevtool_pytest-0.1.0/AGENTS.md +0 -101
  33. revitdevtool_pytest-0.1.0/CHANGELOG.md +0 -87
  34. revitdevtool_pytest-0.1.0/CLAUDE.md +0 -101
  35. revitdevtool_pytest-0.1.0/PKG-INFO +0 -159
  36. revitdevtool_pytest-0.1.0/README.md +0 -132
  37. revitdevtool_pytest-0.1.0/src/revitdevtool_pytest/__init__.py +0 -3
  38. revitdevtool_pytest-0.1.0/src/revitdevtool_pytest/constants.py +0 -43
  39. revitdevtool_pytest-0.1.0/src/revitdevtool_pytest/discovery.py +0 -143
  40. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/.github/workflows/publish.yml +0 -0
  41. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/.gitignore +0 -0
  42. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/.vscode/settings.json +0 -0
  43. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/LICENSE +0 -0
  44. {revitdevtool_pytest-0.1.0 → revitdevtool_pytest-0.3.0}/src/revitdevtool_pytest/suite_lock.py +0 -0
  45. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/_session_probe.py +0 -0
  46. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/__init__.py +0 -0
  47. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/constants.py +0 -0
  48. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/contracts.py +0 -0
  49. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/custom_workflow.py +0 -0
  50. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/field_mapping.py +0 -0
  51. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/header_workflow.py +0 -0
  52. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/model.py +0 -0
  53. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/serializer.py +0 -0
  54. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/table_cell.py +0 -0
  55. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/template_workflow.py +0 -0
  56. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/schedule/test.json +0 -0
  57. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_dependencies.py +0 -0
  58. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_image_export.py +0 -0
  59. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_revit_basic.py +0 -0
  60. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_revit_document.py +0 -0
  61. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_revit_transactions.py +0 -0
  62. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_schedule_custom_copy_revit.py +0 -0
  63. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_schedule_field_mapping_revit.py +0 -0
  64. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_schedule_header_capture_revit.py +0 -0
  65. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_schedule_header_copy_revit.py +0 -0
  66. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_scheduleformatting_revit.py +0 -0
  67. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_session_cleanliness.py +0 -0
  68. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_smoke.py +0 -0
  69. {revitdevtool_pytest-0.1.0/tests → revitdevtool_pytest-0.3.0/tests/Revit}/test_streaming.py +0 -0
@@ -0,0 +1,164 @@
1
+ # AGENTS
2
+
3
+ ## Overview
4
+
5
+ This is the pytest client plugin for RevitDevTool. It bridges local pytest with a live host process via Named Pipe JSON-RPC. Supports Revit, AutoCAD-family, and any host that exposes a `DevToolsPipeServer` pipe.
6
+
7
+ ## Architecture
8
+
9
+ ```
10
+ Local pytest (collect) → Named Pipe → Host process (PytestRunner.py) → Results → Local pytest (report)
11
+ ```
12
+
13
+ ### Module Map
14
+
15
+ | Module | Role |
16
+ |--------|------|
17
+ | `plugin.py` | Hook orchestrator — lifecycle, options, bridge setup |
18
+ | `connection.py` | Bridge lifecycle — discover, connect, lease, launch |
19
+ | `bridge.py` | Wire protocol — Named Pipe framing, request/response |
20
+ | `discovery.py` | Pipe scan, host registry lookup, process launch |
21
+ | `reporting.py` | Map remote CaseResult → pytest TestReport |
22
+ | `suite_leasing.py` | Cross-process host instance allocation (file-based) |
23
+ | `suite_lock.py` | Windows mutex for same-suite concurrency guard |
24
+ | `dialog_resolver.py` | Auto-dismiss host startup dialogs via Win32 |
25
+ | `models.py` | Wire protocol data models (mirrors C# PytestContracts) |
26
+ | `constants.py` | Shared constants, option names, host registry, defaults |
27
+
28
+ ### Host-Side (in RevitDevTool repo)
29
+
30
+ | File | Role |
31
+ |------|------|
32
+ | `PytestRunner.py` | Embedded script — runs `pytest.main()` inside host |
33
+ | `SetupRevit.py` / `SetupAcad.py` | Runtime setup — API refs, I/O redirection |
34
+ | `PytestExecutionService.cs` | C# orchestrator — receives pipe request, invokes Python |
35
+ | `PytestContracts.cs` | C# wire models (mirrors `models.py`) |
36
+
37
+ ## Multi-Host Support
38
+
39
+ The plugin supports any host that registers a DevToolsPipeServer Named Pipe.
40
+
41
+ ### Pre-registered Hosts
42
+
43
+ | Host Name | Pipe Prefix | Executable | Product ID |
44
+ |-----------|------------|------------|------------|
45
+ | `revit` | `Revit` | `Revit.exe` | — |
46
+ | `autocad` | `AutoCad` | `acad.exe` | `01` |
47
+ | `civil3d` | `Civil3D` | `acad.exe` | `00` |
48
+ | `plant3d` | `Plant3D` | `acad.exe` | `17` |
49
+ | `acadarch` | `AcadArch` | `acad.exe` | `04` |
50
+ | `acadmech` | `AcadMech` | `acad.exe` | `05` |
51
+ | `acadmep` | `AcadMep` | `acad.exe` | `06` |
52
+ | `acadelec` | `AcadElec` | `acad.exe` | `07` |
53
+ | `acadmap3d` | `AcadMap3D` | `acad.exe` | `02` |
54
+ | `navisworks` | `Navisworks` | — | — |
55
+ | `rhino` | `Rhino` | — | — |
56
+ | `tekla` | `Tekla` | — | — |
57
+
58
+ Hosts without an `exe_name` (e.g. Rhino, Tekla) can only be connected via pipe auto-discovery or `--host-pipe`. Any unknown host name also gets a fallback config using the host name as pipe prefix.
59
+
60
+ ### Pipe Name Format
61
+
62
+ Pipes follow `{Host}_{Version}_{PID}` — mirrors C# `InstanceManager` pattern `^\w+_[^_]+_\d+$`.
63
+
64
+ Examples:
65
+ - `Revit_2025_12345` (year-based version)
66
+ - `AutoCad_2026_7890` (year-based)
67
+ - `Rhino_8.0_9999` (semver)
68
+ - `Tekla_2024.1_1111` (dotted version)
69
+
70
+ Version is any non-underscore string — not limited to 4-digit years.
71
+
72
+ ### Executable Discovery
73
+
74
+ - **Revit**: `HKLM\SOFTWARE\Autodesk\Revit\Autodesk Revit {version}` → `InstallationLocation`
75
+ - **AutoCAD family**: `HKLM\SOFTWARE\Autodesk\AutoCAD` → enumerate releases → match product ID via `ACAD-XXNN` pattern → `GlobUPILocation` / `AcadLocation`
76
+ - **Generic**: Hosts with `registry_key` + `registry_value` use registry lookup, then filesystem fallback under `Program Files\Autodesk\`
77
+ - **No exe_name**: Discovery returns `None`; connect via existing pipes only
78
+
79
+ ## Key Design Decisions
80
+
81
+ - **Dual pytest model**: Local pytest collects tests; remote pytest executes them. This enables IDE integration (test tree, navigation) while executing in the host's thread.
82
+ - **`--capture=sys`**: Required because fd-level capture (`os.dup2`) doesn't work in embedded Python.NET.
83
+ - **`--disable-plugin-autoload`**: Prevents third-party plugins from interfering with in-host execution.
84
+ - **`sys.__pytest_running__`**: Flag set by PytestRunner to prevent setup scripts from hijacking stdout/stderr during test runs.
85
+ - **Streaming vs batch**: CLI gets real-time progress notifications; IDE adapters get one batch to avoid double-counting.
86
+ - **Suite leasing**: File-based lease store binds suite → host PID across processes. Mutex prevents same-suite parallel runs.
87
+ - **force_launch**: When enabled, spawns new host and waits for its exact PID pipe (ignores existing instances).
88
+ - **Open host registry**: Unknown host names get a fallback `HostConfig(pipe_prefix=host_name)` — any host exposing a DevToolsPipeServer pipe works without pre-registration.
89
+
90
+ ## Running Tests
91
+
92
+ ```bash
93
+ # From project root with .venv activated:
94
+ pytest
95
+
96
+ # Or via uv / pixi:
97
+ uv run pytest
98
+ pixi run pytest
99
+
100
+ # Specific test:
101
+ pytest tests/test_active_state.py::test_active_view_info -v
102
+
103
+ # Target a different host:
104
+ pytest --host autocad --host-version 2026
105
+ pytest --host rhino --host-version 8.0
106
+ ```
107
+
108
+ Plugin auto-enables `-rP` (show captured stdout for passing tests).
109
+
110
+ ## Configuration
111
+
112
+ All options in `pyproject.toml` under `[tool.pytest.ini_options]`:
113
+
114
+ ```toml
115
+ host_name = "revit" # Host to connect to (revit, autocad, civil3d, rhino, etc.)
116
+ host_version = "2025" # Required for launch (accepts any string: year, semver, etc.)
117
+ host_launch = false # true = always spawn new instance
118
+ host_timeout = "60" # Per-test timeout (seconds)
119
+ host_launch_timeout = "180" # Startup wait (seconds)
120
+ host_pipe = "" # Explicit pipe (bypass discovery)
121
+ ```
122
+
123
+ CLI equivalents: `--host`, `--host-version`, `--host-timeout`, `--host-pipe`, `--host-launch`, `--host-launch-timeout`.
124
+
125
+ ## Fixtures Pattern
126
+
127
+ ```python
128
+ # conftest.py (Revit)
129
+ @pytest.fixture(scope="session")
130
+ def revit_uiapp():
131
+ return __revit__ # UIApplication injected by host
132
+
133
+ @pytest.fixture(scope="session")
134
+ def revit_doc(revit_uiapp):
135
+ return revit_uiapp.ActiveUIDocument.Document
136
+
137
+ @pytest.fixture
138
+ def revit_auto_rollback(revit_transaction_service):
139
+ """Start undo tracking, revert after test."""
140
+ revit_transaction_service.StartChanges()
141
+ try:
142
+ yield revit_transaction_service
143
+ finally:
144
+ revit_transaction_service.RevertChanges()
145
+ ```
146
+
147
+ ## Common Traps
148
+
149
+ - Tests execute **inside the host**, not locally. `import` statements for host APIs only work at test runtime.
150
+ - `__revit__` (Revit) or equivalent builtins are injected by host setup scripts — always access via fixtures.
151
+ - Host APIs require main-thread access. All tests run sequentially via FIFO queue (`IHostContextExecutor`).
152
+ - Named Pipe format: `{Host}_{Version}_{PID}` (e.g. `Revit_2025_12345`, `Rhino_8.0_9999`). Version is any non-underscore string.
153
+ - PEP 723 dependencies in `conftest.py` are auto-installed by `PytestDependencyService` before execution.
154
+ - `--host-launch` requires `--host-version` to be set — otherwise exits with config error.
155
+ - Print output inside tests is captured by pytest's `--capture=sys` mechanism and returned via `CaseResult.stdout`.
156
+
157
+ ## Change Rules
158
+
159
+ - Wire protocol changes must update both `models.py` (Python) and `PytestContracts.cs` (C#).
160
+ - Adding CLI options: register in both `pytest_addoption` (CLI) and `parser.addini` (INI).
161
+ - Connection logic is stateless by design — all functions receive parameters, no module-level mutable state except `plugin.py` globals.
162
+ - After modifying connection/discovery logic, manually test with both `host_launch = true` and `false`.
163
+ - Host registry changes: keep `HOST_REGISTRY` in `constants.py` in sync with `HostApp` enum and `AcadPathResolver.ProductIdMap` in the C# codebase.
164
+ - Any host with a DevToolsPipeServer pipe can be reached without pre-registration (fallback config).
@@ -0,0 +1,151 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.3.0] - 2026-05-31
6
+
7
+ ### Added
8
+
9
+ - **Multi-host support:** plugin now supports Revit, AutoCAD, Civil3D, Plant3D, Navisworks, Rhino, Tekla, and any host exposing a DevToolsPipeServer Named Pipe.
10
+ - **`--host` option:** new CLI flag and `host_name` INI key to select the target host application (default: `revit`).
11
+ - **`HostConfig` dataclass:** centralized executable discovery and launch configuration per host product, stored in `HOST_REGISTRY`.
12
+ - **AutoCAD-family executable discovery:** registry-based resolver mirrors C# `AcadPathResolver` — enumerates `HKLM\SOFTWARE\Autodesk\AutoCAD` releases, matches product IDs (`ACAD-XXNN`), resolves install path via `GlobUPILocation` / `AcadLocation`.
13
+ - **Open host registry:** `get_host_config()` returns a fallback `HostConfig(pipe_prefix=host_name)` for unknown host names — any host with a DevToolsPipeServer pipe works without pre-registration.
14
+ - **Multi-host dialog resolver:** `StartupDialogResolver` keywords expanded to cover AutoCAD, Civil, and Plant startup dialogs.
15
+
16
+ ### Changed
17
+
18
+ - **CLI/INI options renamed:** all `--revit-*` / `revit_*` options replaced with `--host-*` / `host_*` equivalents (`--host-version`, `--host-timeout`, `--host-pipe`, `--host-launch`, `--host-launch-timeout`).
19
+ - **Flexible pipe pattern:** `PIPE_PATTERN` changed from `^\w+_\d{4}_\d+$` to `^\w+_[^_]+_\d+$` — version segment accepts any non-underscore string (year, semver, dotted), matching C# `InstanceManager`.
20
+ - **`HostInstance.version` type:** changed from `int` to `str` to support non-year version formats (e.g. `8.0`, `2024.1`, `v3.2.1`).
21
+ - **`--host-version` accepts string:** no longer restricted to 4-digit year integers.
22
+ - **`RevitBridge` → `HostBridge`:** bridge class renamed for host-agnostic semantics.
23
+ - **`RevitInstance` → `HostInstance`:** discovery dataclass renamed.
24
+ - **`find_revit_pipes()` → `find_host_pipes()`:** accepts optional `host_name` filter; returns all matching pipes including unregistered prefixes.
25
+ - **`find_revit_path()` → `find_host_executable()`:** dispatches to Revit registry, AutoCAD-family registry, or returns `None` for hosts without exe discovery.
26
+ - **`start_revit()` → `start_host()`:** uses `HostConfig.launch_args` per host.
27
+ - **`wait_for_revit_pipe()` → `wait_for_host_pipe()`:** host-agnostic pipe polling.
28
+ - **`HostConfig.exe_name` optional:** hosts without a known executable (Rhino, Tekla, etc.) connect via pipe auto-discovery or `--host-pipe` only.
29
+ - **`is_process_alive()`:** handles `exe_name=None` gracefully — checks PID liveness without executable name verification.
30
+
31
+ ### Removed
32
+
33
+ - **`_opt_int()` helper:** version is now parsed as string, no integer conversion needed.
34
+
35
+ ### Notes
36
+
37
+ - `0.3.0` is a breaking change from `0.2.x`: all CLI flags and INI keys are renamed. Existing `revit_*` / `--revit-*` options are no longer recognized.
38
+ - The pipe pattern is now aligned with the C# `InstanceManager.HostPipePattern` regex (`^\w+_[^_]+_\d+$`).
39
+ - Revit users: add `host_name = "revit"` to `pyproject.toml` (or rely on the default).
40
+
41
+ ## [0.2.0] - 2026-05-31
42
+
43
+ ### Added
44
+
45
+ - **Auto print capture:** plugin automatically enables `-rP` behavior — captured `print()` output is shown for both passing and failing tests without any user configuration.
46
+ - **PID-aware pipe discovery:** `wait_for_revit_pipe` can now wait for a specific process ID, ensuring `force_launch` connects to the newly spawned instance only.
47
+
48
+ ### Changed
49
+
50
+ - **`revit_launch` → force_launch semantics:** renamed internal parameter from `prefer_fresh` to `force_launch`. When enabled, the plugin disconnects any existing bridge, spawns a new Revit instance, and waits for that exact PID's pipe — never reuses existing instances.
51
+ - **`_opt_bool` fix:** corrected boolean option parsing so `revit_launch = true` in `pyproject.toml` is properly respected when no CLI flag is explicitly passed.
52
+ - **Type annotations refactor:** resolved false-positive IDE warnings across `bridge.py`, `connection.py`, `suite_leasing.py`, and `dialog_resolver.py`.
53
+ - **`SetupRevit.py` (Revit-side):** I/O hijacking is now conditional on `sys.__pytest_running__` flag — prevents conflict with pytest capture during test execution.
54
+ - **`PytestRunner.py` (Revit-side):** sets `sys.__pytest_running__ = True` during execution; top-level error handling wraps entire `_run()` to always return structured JSON.
55
+
56
+ ### Removed
57
+
58
+ - **`discover_tests()`** method from `bridge.py` (client-side dead code; server endpoint remains).
59
+ - **`DiscoverRequest` / `DiscoverResponse`** classes from `models.py` (unused by client).
60
+ - **`BRIDGE_METHOD_TESTS_DISCOVER`** constant (no longer referenced).
61
+ - **`resolve_existing()` / `get_suite_process_id()` / `_prune_stale()`** from `suite_leasing.py` (unused).
62
+ - **`launch_revit()`** from `discovery.py` (replaced by `start_revit` + PID tracking).
63
+ - **`addopts = "-rP"`** from `pyproject.toml` (now automatic via plugin).
64
+
65
+ ### Fixed
66
+
67
+ - **`revit_launch = true` in INI ignored:** `_opt_bool` now correctly reads INI value when CLI flag is not explicitly set.
68
+
69
+ ## [0.1.0] - 2026-05-03
70
+
71
+ ### Added
72
+
73
+ - **Real-time streaming:** test progress notifications streamed from Revit to the pytest client during execution.
74
+ - **Suite leasing:** Windows Mutex-based exclusive access prevents multiple pytest processes from connecting to the same Revit instance.
75
+ - **Test discovery:** `PytestRunRequest` + `_BridgePlugin` integration enables `--collect-only` discovery without executing tests.
76
+ - **Reporting module:** remote `CaseResult[]` mapping back to pytest `TestReport`, with `run_remote_session` orchestrating discover + run.
77
+ - **Connection module:** centralized `ensure_bridge()` with auto-connect, lease management, and Revit auto-launch flow.
78
+ - **Schedule API tests:** comprehensive test suite for Revit schedule operations (copy, field mapping, header, template, custom workflow).
79
+ - **Image export tests:** `test_image_export.py` covering `RevitImageExporter`.
80
+ - **Document & transaction tests:** `test_revit_document.py`, `test_revit_transactions.py`.
81
+ - **Session cleanliness tests:** `test_session_cleanliness.py`, `test_active_state.py` verifying state isolation between runs.
82
+ - **Streaming tests:** `test_streaming.py` validating progress notification flow.
83
+ - **Dependencies tests:** `test_dependencies.py` verifying package resolution.
84
+ - **GitNexus integration:** AGENTS.md, CLAUDE.md, and CLI skills for graph-based code intelligence.
85
+ - **Project setup guide:** recommended `uv` and `pixi` workflows in README.
86
+
87
+ ### Changed
88
+
89
+ - **Architecture rewrite:** `plugin.py` restructured — execution now intercepted via `pytest_runtestloop` instead of `pytest_pyfunc_call`. Remote session orchestrates discover + run in a single round-trip.
90
+ - **Configuration:** primary config via `[tool.pytest.ini_options]` in `pyproject.toml` — CLI flags remain as overrides.
91
+ - **`bridge.py`:** expanded with `discover_tests()`, `run_tests()`, notification dispatch, frame protocol hardening.
92
+ - **`models.py`:** new `DiscoverRequest`, `RunRequest`, `RunResponse`, `CaseResult`, `CollectionError` contracts.
93
+ - **`constants.py`:** centralized all bridge methods, option names, timeouts, pipe patterns.
94
+ - **`pyproject.toml`:** bumped `revit_launch_timeout` to 180s, added `revit_timeout` default.
95
+
96
+ ### Removed
97
+
98
+ - **`serializer.py`:** deprecated — test code serialization replaced by structured discovery + run flow.
99
+ - **`.vscode/settings.json`:** removed from repo; config lives in `pyproject.toml`.
100
+
101
+ ### Notes
102
+
103
+ - `0.1.0` represents a major architectural shift from single-test relay to session-oriented orchestration. The plugin now discovers tests remotely, runs them in batch, and streams progress back in real-time.
104
+ - Suite leasing ensures safe concurrent usage across multiple test suits targeting the same Revit year.
105
+
106
+ ## [0.0.3] - 2026-04-10
107
+
108
+ ### Fixed
109
+
110
+ - Fixed Revit auto-launch dialog handling by starting the startup dialog resolver immediately after spawning Revit, before waiting for the named pipe.
111
+ - Fixed startup dialog button selection to prefer safe allow-list actions such as `Always Load` and `Load Once`.
112
+ - Prevented accidental clicks on destructive dialog actions such as `Do Not Load`, `Cancel`, and `No`.
113
+
114
+ ### Notes
115
+
116
+ - This release focuses on reliable auto-launch behavior when Revit shows unsigned add-in security dialogs during startup.
117
+
118
+ ## [0.0.2] - 2026-04-10
119
+
120
+ ### Fixed
121
+
122
+ - Fixed pytest plugin auto-loading via the `pytest11` entry point.
123
+ - Fixed the `pytest_unconfigure` hook signature in `revitdevtool_pytest.plugin` so Pluggy accepts the plugin during pytest startup.
124
+ - Restored the expected standalone package behavior so `RevitDevTool.PyTest` can be discovered and activated by a normal Python/pytest environment before connecting to Revit.
125
+
126
+ ### Changed
127
+
128
+ - Aligned the published package metadata name with the recommended normalized package name `revitdevtool_pytest`.
129
+
130
+ ### Notes
131
+
132
+ - This release is intended as a bugfix release for the `0.0.1` package.
133
+ - No user-facing CLI options are changed in this release.
134
+ - This name alignment is intended to remain compatible with existing PyPI installs because Python package indexes and installers normalize `.`, `-`, and `_` in distribution names.
135
+
136
+ ## [0.0.1] - 2026-04-09
137
+
138
+ ### Added
139
+
140
+ - Initial standalone `pytest` plugin package for running Revit API tests through the RevitDevTool Named Pipe bridge.
141
+ - Automatic discovery of running Revit instances through named pipes.
142
+ - Optional Revit auto-launch flow via `--revit-launch` and `--revit-version`.
143
+ - Remote test execution by intercepting `pytest_pyfunc_call` and forwarding serialized test code to Revit.
144
+ - Support for explicit pipe targeting with `--revit-pipe`.
145
+ - Per-test timeout and launch timeout configuration.
146
+ - Revit startup dialog resolver integration.
147
+ - Example test suite and VS Code integration guidance in the project documentation.
148
+
149
+ ### Notes
150
+
151
+ - `0.0.1` introduced the core test bridge workflow, but the published package contains a plugin loading issue fixed in `0.0.2`.
@@ -0,0 +1 @@
1
+ Refer to [AGENTS.md](./AGENTS.md) for project rules, conventions, and workflow.
@@ -0,0 +1,234 @@
1
+ Metadata-Version: 2.4
2
+ Name: revitdevtool_pytest
3
+ Version: 0.3.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 host applications via RevitDevTool Named Pipe bridge.
35
+ Supports Revit, AutoCAD-family, and any host exposing a DevToolsPipeServer pipe.
36
+ Tests run inside a live host process — write standard pytest, execute remotely.
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install revitdevtool_pytest
42
+ ```
43
+
44
+ ## Dependencies
45
+
46
+ | Package | Version |
47
+ |---|---|
48
+ | Python | >= 3.10 |
49
+ | pytest | >= 9.0 |
50
+ | pywin32 | >= 311 |
51
+
52
+ ## Requirements
53
+
54
+ - Windows (Named Pipes)
55
+ - A host application with [RevitDevTool](https://github.com/trgiangv/RevitDevTool) add-in installed and loaded
56
+
57
+ ## Supported Hosts
58
+
59
+ ### Pre-registered
60
+
61
+ | Host Name | Application | Pipe Prefix |
62
+ |---|---|---|
63
+ | `revit` | Autodesk Revit | `Revit` |
64
+ | `autocad` | AutoCAD | `AutoCad` |
65
+ | `civil3d` | Civil 3D | `Civil3D` |
66
+ | `plant3d` | Plant 3D | `Plant3D` |
67
+ | `acadarch` | AutoCAD Architecture | `AcadArch` |
68
+ | `acadmech` | AutoCAD Mechanical | `AcadMech` |
69
+ | `acadmep` | AutoCAD MEP | `AcadMep` |
70
+ | `acadelec` | AutoCAD Electrical | `AcadElec` |
71
+ | `acadmap3d` | AutoCAD Map 3D | `AcadMap3D` |
72
+ | `navisworks` | Navisworks | `Navisworks` |
73
+ | `rhino` | Rhino | `Rhino` |
74
+ | `tekla` | Tekla Structures | `Tekla` |
75
+
76
+ ### Any host
77
+
78
+ Any host name works — unknown names get a fallback config using the name as pipe prefix. Connect via auto-discovery or `--host-pipe`.
79
+
80
+ ### Pipe Name Format
81
+
82
+ Pipes follow `{Host}_{Version}_{PID}` (e.g. `Revit_2025_12345`, `Rhino_8.0_9999`). Version can be any format — year, semver, etc.
83
+
84
+ ## Project Setup
85
+
86
+ **Recommended:** scaffold your project with [uv](https://docs.astral.sh/uv/) or [pixi](https://pixi.sh/):
87
+
88
+ ```bash
89
+ # uv
90
+ uv init my-revit-tests
91
+ cd my-revit-tests
92
+ uv add revitdevtool_pytest
93
+
94
+ # pixi (uses pyproject.toml as manifest)
95
+ pixi init --format pyproject my-revit-tests
96
+ cd my-revit-tests
97
+ pixi add --pypi revitdevtool_pytest
98
+ ```
99
+
100
+ ## Configuration
101
+
102
+ Add plugin settings to `[tool.pytest.ini_options]` in your `pyproject.toml`:
103
+
104
+ ```toml
105
+ [tool.pytest.ini_options]
106
+ host_name = "revit"
107
+ host_version = "2025"
108
+ host_launch = false
109
+ host_launch_timeout = "180"
110
+ ```
111
+
112
+ | Option | Type | Default | Description |
113
+ |---|---|---|---|
114
+ | `host_name` | string | `"revit"` | Host application name (see Supported Hosts above). |
115
+ | `host_version` | string | — | Host version (e.g. `"2025"`, `"8.0"`). Required when `host_launch = true`. |
116
+ | `host_launch` | bool | `false` | Force-launch a **new** host instance (skip reusing existing free instances). |
117
+ | `host_timeout` | string | `"60"` | Per-test execution timeout in seconds. |
118
+ | `host_launch_timeout` | string | `"120"` | Seconds to wait for host to start. |
119
+ | `host_pipe` | string | — | Explicit pipe name (bypasses auto-discovery). |
120
+
121
+ CLI flags override INI settings:
122
+
123
+ ```bash
124
+ # Revit
125
+ pytest --host-launch --host-version=2025 -v
126
+
127
+ # AutoCAD
128
+ pytest --host autocad --host-version=2026 -v
129
+
130
+ # Civil 3D
131
+ pytest --host civil3d --host-version=2026 -v
132
+ ```
133
+
134
+ ### Connection Behavior
135
+
136
+ - **Default** (`host_launch = false`): plugin auto-discovers a running host instance matching `host_version` via Named Pipe scan. If none found, exits with error.
137
+ - **Force launch** (`host_launch = true`): always spawns a new host process, waits for its pipe to appear, then connects. Existing instances are ignored.
138
+
139
+ ### Print Output
140
+
141
+ Captured `print()` output from tests running inside the host is automatically shown in the terminal for passing tests (equivalent to `-rP`). No extra flags needed.
142
+
143
+ ## Usage
144
+
145
+ ### Fixtures
146
+
147
+ Define fixtures in `conftest.py` that provide host API objects:
148
+
149
+ ```python
150
+ import pytest
151
+
152
+ # Revit fixtures
153
+ @pytest.fixture(scope="session")
154
+ def revit_uiapp():
155
+ return __revit__ # UIApplication injected by RevitDevTool
156
+
157
+ @pytest.fixture(scope="session")
158
+ def revit_app(revit_uiapp):
159
+ return revit_uiapp.Application
160
+
161
+ @pytest.fixture(scope="session")
162
+ def revit_doc(revit_uiapp):
163
+ return revit_uiapp.ActiveUIDocument.Document
164
+ ```
165
+
166
+ ### Writing Tests
167
+
168
+ ```python
169
+ def test_active_view(revit_doc):
170
+ view = revit_doc.ActiveView
171
+ print(f"Active View: {view.Name}")
172
+ assert view.Name is not None
173
+
174
+ def test_revit_version(revit_app):
175
+ assert "2025" in revit_app.VersionName
176
+ ```
177
+
178
+ ### PEP 723 Dependencies
179
+
180
+ Declare test-suite dependencies in `conftest.py` using PEP 723 metadata. RevitDevTool auto-installs them before test execution:
181
+
182
+ ```python
183
+ # /// script
184
+ # dependencies = [
185
+ # "numpy>=2.0",
186
+ # "polars>=1.0",
187
+ # ]
188
+ # ///
189
+ ```
190
+
191
+ ### Running Tests
192
+
193
+ ```bash
194
+ # With pyproject.toml configured:
195
+ pytest
196
+
197
+ # Override version for a single run:
198
+ pytest --host-version=2026 -v
199
+
200
+ # Target a different host:
201
+ pytest --host autocad --host-version=2026 -v
202
+
203
+ # Using uv or pixi:
204
+ uv run pytest
205
+ pixi run pytest
206
+ ```
207
+
208
+ ## How It Works
209
+
210
+ 1. pytest discovers and collects tests locally
211
+ 2. Plugin intercepts `pytest_runtestloop`, connects to the host via Named Pipe
212
+ 3. Test nodeids are sent to the host's `PytestRunner.py` (embedded in RevitDevTool)
213
+ 4. Tests execute inside the host's Python.NET environment with full API access
214
+ 5. Results (pass/fail/skip, stdout, stderr, tracebacks) are returned via pipe
215
+ 6. Plugin maps results back to standard pytest reports
216
+
217
+ ## IDE Integration
218
+
219
+ ### VS Code / Cursor
220
+
221
+ ```json
222
+ {
223
+ "python.testing.pytestEnabled": true,
224
+ "python.testing.pytestArgs": ["tests"]
225
+ }
226
+ ```
227
+
228
+ ### PyCharm
229
+
230
+ Enable pytest as the test runner. Plugin auto-detects IDE adapters and disables streaming to avoid duplicate results in the test tree.
231
+
232
+ ## License
233
+
234
+ [MIT](LICENSE)