sysml-windtrader 0.2.2__tar.gz → 0.2.4__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.
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/CHANGELOG.md +28 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/PKG-INFO +37 -6
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/README.md +35 -4
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/pyproject.toml +2 -2
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/src/windtrader/cli.py +4 -1
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/src/windtrader/client.py +40 -2
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/tests/test_cli.py +8 -4
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/tests/test_client.py +62 -4
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/.github/workflows/ci.yml +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/.github/workflows/docstring-badge.yml +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/.github/workflows/publish.yml +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/.github/workflows/release.yml +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/.gitignore +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/.pre-commit-config.yaml +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/LICENSE +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/src/windtrader/__init__.py +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/src/windtrader/_jars.py +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/tests/test_integration_jar.py +0 -0
- {sysml_windtrader-0.2.2 → sysml_windtrader-0.2.4}/tests/test_jars.py +0 -0
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v0.2.4 (2026-10-17)
|
|
6
|
+
|
|
7
|
+
### 🐛 Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Normalize any process exit code outside the documented 0/2/3 contract to **3**
|
|
10
|
+
(runtime error) in `CommandResult` (and thus the CLI). A JVM startup failure on an
|
|
11
|
+
old JRE (e.g. `UnsupportedClassVersionError` hitting the Java-21-compiled
|
|
12
|
+
`windtrader-java` 0.2.0 JAR) previously leaked a foreign code like **1**, breaking
|
|
13
|
+
the contract and making `is_runtime_error` (and CLI exit-code branching) miss the
|
|
14
|
+
case. The underlying JVM detail is preserved on `stderr`.
|
|
15
|
+
- Add a Java-executable override so users with an old default `java` can point the
|
|
16
|
+
wrapper at a Java 21+ JVM: `WINDTRADER_JAVA` (explicit path) → `JAVA_HOME/bin/java`
|
|
17
|
+
→ `java` on PATH.
|
|
18
|
+
|
|
19
|
+
### 📝 Documentation
|
|
20
|
+
|
|
21
|
+
- README: state the **Java 21+** requirement in Installation; document the exit-code
|
|
22
|
+
normalization and the Java-executable resolution order in Exit Code Contract and
|
|
23
|
+
Configuration.
|
|
24
|
+
- pyproject description: note "requires Java 21+".
|
|
25
|
+
|
|
26
|
+
## v0.2.3 (2026-10-17)
|
|
27
|
+
|
|
28
|
+
### 📝 Documentation
|
|
29
|
+
|
|
30
|
+
- README: reflect `sysml-windtrader` distribution name; document
|
|
31
|
+
`validate_across_versions` with its real signature
|
|
32
|
+
|
|
5
33
|
## v0.2.2 (2026-10-17)
|
|
6
34
|
|
|
7
35
|
### 🐛 Bug Fixes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sysml-windtrader
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary: Python wrapper around windtrader-java for SysML v2 parse-only validation.
|
|
3
|
+
Version: 0.2.4
|
|
4
|
+
Summary: Python wrapper around windtrader-java for SysML v2 parse-only validation (requires Java 21+).
|
|
5
5
|
Author: Westfall-io
|
|
6
6
|
License: MIT License
|
|
7
7
|
|
|
@@ -36,9 +36,10 @@ Requires-Dist: python-semantic-release>=10.0.0; extra == 'dev'
|
|
|
36
36
|
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
37
37
|
Description-Content-Type: text/markdown
|
|
38
38
|
|
|
39
|
-
# windtrader
|
|
39
|
+
# sysml-windtrader
|
|
40
40
|
|
|
41
|
-
**windtrader** is a lightweight Python wrapper around the
|
|
41
|
+
**sysml-windtrader** (`windtrader` on import) is a lightweight Python wrapper around the
|
|
42
|
+
`windtrader-java` SysML v2 validator.
|
|
42
43
|
It provides a simple, scriptable way to validate SysML v2 textual syntax from Python while
|
|
43
44
|
delegating all grammar authority to the official Java-based validator.
|
|
44
45
|
|
|
@@ -79,7 +80,11 @@ Or for development:
|
|
|
79
80
|
pip install -e ".[dev]"
|
|
80
81
|
```
|
|
81
82
|
|
|
82
|
-
Java
|
|
83
|
+
**Java requirement:** the default `windtrader-java` release (0.2.x) is compiled for
|
|
84
|
+
**Java 21** (class file version 65). Java **21 or newer** must be available on your
|
|
85
|
+
system (CI uses Temurin JDK 21). If your default `java` is older, point the wrapper at a
|
|
86
|
+
Java 21+ JVM via `WINDTRADER_JAVA` (explicit executable path) or `JAVA_HOME` — see
|
|
87
|
+
[Configuration](#configuration).
|
|
83
88
|
|
|
84
89
|
---
|
|
85
90
|
|
|
@@ -162,6 +167,18 @@ else:
|
|
|
162
167
|
print("Failed:", res.stderr)
|
|
163
168
|
```
|
|
164
169
|
|
|
170
|
+
### Validate across versions
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from windtrader import validate_across_versions
|
|
174
|
+
|
|
175
|
+
results = validate_across_versions("part { attribute mass; }", versions=["0.2.0"])
|
|
176
|
+
|
|
177
|
+
# One CommandResult per version, in the order given.
|
|
178
|
+
for r in results:
|
|
179
|
+
print(r.version, "->", "valid" if r.ok else "invalid")
|
|
180
|
+
```
|
|
181
|
+
|
|
165
182
|
### CommandResult
|
|
166
183
|
|
|
167
184
|
Returned objects include:
|
|
@@ -186,7 +203,14 @@ The Java validator defines:
|
|
|
186
203
|
| 2 | Invalid SysML |
|
|
187
204
|
| 3 | Runtime error |
|
|
188
205
|
|
|
189
|
-
Python preserves these codes exactly.
|
|
206
|
+
Python preserves these codes exactly. Any process exit code **outside** (0, 2, 3) —
|
|
207
|
+
for example the JVM's own startup failure code (1) when an old Java hits a
|
|
208
|
+
Java-21-compiled JAR — is normalized to **3** (runtime error), so the contract never
|
|
209
|
+
leaks a foreign code. The underlying JVM detail is kept on `stderr` (e.g.
|
|
210
|
+
`UnsupportedClassVersionError ... class file version 65.0 ... only recognizes ... 62.0`).
|
|
211
|
+
|
|
212
|
+
In the Python API, `CommandResult.exit_code` is always one of 0/2/3; `is_invalid_syntax`
|
|
213
|
+
is `exit_code == 2` and `is_runtime_error` is `exit_code not in (0, 2)` (i.e. 3).
|
|
190
214
|
|
|
191
215
|
---
|
|
192
216
|
|
|
@@ -197,6 +221,13 @@ Environment variables:
|
|
|
197
221
|
- `WINDTRADER_CACHE_DIR` – override JAR cache directory
|
|
198
222
|
- `WINDTRADER_JAVA_REPO` – override GitHub repo for JAR downloads
|
|
199
223
|
- `WINDTRADER_JAVA_STABLE_ASSET` – explicit asset URL (optional)
|
|
224
|
+
- `WINDTRADER_JAVA` – explicit path to a `java` executable (or a JVM launcher) to use
|
|
225
|
+
instead of `java` on PATH. Handy on machines whose default Java is too old for the
|
|
226
|
+
target JAR — set it to a Java 21+ executable.
|
|
227
|
+
- `JAVA_HOME` – if set (and `WINDTRADER_JAVA` is not), the wrapper uses
|
|
228
|
+
`<JAVA_HOME>/bin/java` as the Java executable.
|
|
229
|
+
|
|
230
|
+
Java executable resolution order: `WINDTRADER_JAVA` → `JAVA_HOME/bin/java` → `java` on PATH.
|
|
200
231
|
|
|
201
232
|
---
|
|
202
233
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
# windtrader
|
|
1
|
+
# sysml-windtrader
|
|
2
2
|
|
|
3
|
-
**windtrader** is a lightweight Python wrapper around the
|
|
3
|
+
**sysml-windtrader** (`windtrader` on import) is a lightweight Python wrapper around the
|
|
4
|
+
`windtrader-java` SysML v2 validator.
|
|
4
5
|
It provides a simple, scriptable way to validate SysML v2 textual syntax from Python while
|
|
5
6
|
delegating all grammar authority to the official Java-based validator.
|
|
6
7
|
|
|
@@ -41,7 +42,11 @@ Or for development:
|
|
|
41
42
|
pip install -e ".[dev]"
|
|
42
43
|
```
|
|
43
44
|
|
|
44
|
-
Java
|
|
45
|
+
**Java requirement:** the default `windtrader-java` release (0.2.x) is compiled for
|
|
46
|
+
**Java 21** (class file version 65). Java **21 or newer** must be available on your
|
|
47
|
+
system (CI uses Temurin JDK 21). If your default `java` is older, point the wrapper at a
|
|
48
|
+
Java 21+ JVM via `WINDTRADER_JAVA` (explicit executable path) or `JAVA_HOME` — see
|
|
49
|
+
[Configuration](#configuration).
|
|
45
50
|
|
|
46
51
|
---
|
|
47
52
|
|
|
@@ -124,6 +129,18 @@ else:
|
|
|
124
129
|
print("Failed:", res.stderr)
|
|
125
130
|
```
|
|
126
131
|
|
|
132
|
+
### Validate across versions
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from windtrader import validate_across_versions
|
|
136
|
+
|
|
137
|
+
results = validate_across_versions("part { attribute mass; }", versions=["0.2.0"])
|
|
138
|
+
|
|
139
|
+
# One CommandResult per version, in the order given.
|
|
140
|
+
for r in results:
|
|
141
|
+
print(r.version, "->", "valid" if r.ok else "invalid")
|
|
142
|
+
```
|
|
143
|
+
|
|
127
144
|
### CommandResult
|
|
128
145
|
|
|
129
146
|
Returned objects include:
|
|
@@ -148,7 +165,14 @@ The Java validator defines:
|
|
|
148
165
|
| 2 | Invalid SysML |
|
|
149
166
|
| 3 | Runtime error |
|
|
150
167
|
|
|
151
|
-
Python preserves these codes exactly.
|
|
168
|
+
Python preserves these codes exactly. Any process exit code **outside** (0, 2, 3) —
|
|
169
|
+
for example the JVM's own startup failure code (1) when an old Java hits a
|
|
170
|
+
Java-21-compiled JAR — is normalized to **3** (runtime error), so the contract never
|
|
171
|
+
leaks a foreign code. The underlying JVM detail is kept on `stderr` (e.g.
|
|
172
|
+
`UnsupportedClassVersionError ... class file version 65.0 ... only recognizes ... 62.0`).
|
|
173
|
+
|
|
174
|
+
In the Python API, `CommandResult.exit_code` is always one of 0/2/3; `is_invalid_syntax`
|
|
175
|
+
is `exit_code == 2` and `is_runtime_error` is `exit_code not in (0, 2)` (i.e. 3).
|
|
152
176
|
|
|
153
177
|
---
|
|
154
178
|
|
|
@@ -159,6 +183,13 @@ Environment variables:
|
|
|
159
183
|
- `WINDTRADER_CACHE_DIR` – override JAR cache directory
|
|
160
184
|
- `WINDTRADER_JAVA_REPO` – override GitHub repo for JAR downloads
|
|
161
185
|
- `WINDTRADER_JAVA_STABLE_ASSET` – explicit asset URL (optional)
|
|
186
|
+
- `WINDTRADER_JAVA` – explicit path to a `java` executable (or a JVM launcher) to use
|
|
187
|
+
instead of `java` on PATH. Handy on machines whose default Java is too old for the
|
|
188
|
+
target JAR — set it to a Java 21+ executable.
|
|
189
|
+
- `JAVA_HOME` – if set (and `WINDTRADER_JAVA` is not), the wrapper uses
|
|
190
|
+
`<JAVA_HOME>/bin/java` as the Java executable.
|
|
191
|
+
|
|
192
|
+
Java executable resolution order: `WINDTRADER_JAVA` → `JAVA_HOME/bin/java` → `java` on PATH.
|
|
162
193
|
|
|
163
194
|
---
|
|
164
195
|
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sysml-windtrader"
|
|
7
|
-
version = "0.2.
|
|
8
|
-
description = "Python wrapper around windtrader-java for SysML v2 parse-only validation."
|
|
7
|
+
version = "0.2.4"
|
|
8
|
+
description = "Python wrapper around windtrader-java for SysML v2 parse-only validation (requires Java 21+)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = { file = "LICENSE" }
|
|
@@ -22,12 +22,15 @@ Exit codes (as defined by windtrader-java)
|
|
|
22
22
|
- 0: Valid SysML (parse succeeded)
|
|
23
23
|
- 2: Invalid SysML (syntax/parse error)
|
|
24
24
|
- 3: Runtime error (tool failure)
|
|
25
|
-
- Any other non-zero:
|
|
25
|
+
- Any other non-zero: mapped to 3 (runtime error)
|
|
26
26
|
|
|
27
27
|
Notes
|
|
28
28
|
-----
|
|
29
29
|
- `--version` prints the Python package version (not the Java backend version).
|
|
30
30
|
- Use `--java-version` to select which `windtrader-java` release asset to download/use.
|
|
31
|
+
- The Java executable is resolved via `WINDTRADER_JAVA` (explicit path) →
|
|
32
|
+
`JAVA_HOME/bin/java` → `java` on PATH. Point `WINDTRADER_JAVA` at a Java 21+ JVM
|
|
33
|
+
if your default `java` is too old to load the default `windtrader-java` JAR.
|
|
31
34
|
- `--timeout` defaults per subcommand: 10s for `check`/`echo`, 120s for `export`
|
|
32
35
|
(export loads the standard library and runs resolve/transform).
|
|
33
36
|
- An unknown subcommand is rejected by argparse with exit 2, the same code used
|
|
@@ -17,14 +17,39 @@ The Python API mirrors these exactly: `validate`/`check`, `echo`, `export`.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
import os
|
|
20
21
|
import subprocess
|
|
21
22
|
import time
|
|
22
23
|
from collections.abc import Sequence
|
|
23
24
|
from dataclasses import dataclass
|
|
25
|
+
from pathlib import Path
|
|
24
26
|
|
|
25
27
|
from ._jars import DEFAULT_VERSION, get_jar_path
|
|
26
28
|
|
|
27
29
|
|
|
30
|
+
def _resolve_java() -> list[str]:
|
|
31
|
+
"""
|
|
32
|
+
Return the argv prefix for the Java executable (allows an override).
|
|
33
|
+
|
|
34
|
+
Resolution order:
|
|
35
|
+
1. ``WINDTRADER_JAVA`` env var — explicit path to a ``java`` (or JVM) executable.
|
|
36
|
+
2. ``JAVA_HOME`` env var — ``<JAVA_HOME>/bin/java``.
|
|
37
|
+
3. ``java`` on PATH (default).
|
|
38
|
+
|
|
39
|
+
This lets users on a machine whose default ``java`` is too old point the wrapper
|
|
40
|
+
at a newer JVM without modifying PATH.
|
|
41
|
+
"""
|
|
42
|
+
explicit = os.environ.get("WINDTRADER_JAVA", "").strip()
|
|
43
|
+
if explicit:
|
|
44
|
+
return [explicit]
|
|
45
|
+
|
|
46
|
+
java_home = os.environ.get("JAVA_HOME", "").strip()
|
|
47
|
+
if java_home:
|
|
48
|
+
return [str(Path(java_home) / "bin" / "java")]
|
|
49
|
+
|
|
50
|
+
return ["java"]
|
|
51
|
+
|
|
52
|
+
|
|
28
53
|
@dataclass(frozen=True)
|
|
29
54
|
class CommandResult:
|
|
30
55
|
"""
|
|
@@ -37,7 +62,12 @@ class CommandResult:
|
|
|
37
62
|
version:
|
|
38
63
|
windtrader-java version string used to resolve/download the jar.
|
|
39
64
|
exit_code:
|
|
40
|
-
Process exit code
|
|
65
|
+
Process exit code, normalized to the documented contract (0 / 2 / 3).
|
|
66
|
+
Any exit code outside (0, 2, 3) is mapped to 3 (runtime error), because a
|
|
67
|
+
JVM-level startup failure (e.g. an old JRE hitting a Java-21 jar ->
|
|
68
|
+
UnsupportedClassVersionError) returns a foreign code like 1 that would
|
|
69
|
+
otherwise break the documented 0/2/3 contract. The underlying detail is
|
|
70
|
+
preserved in `stderr`.
|
|
41
71
|
stdout:
|
|
42
72
|
Captured standard output from the jar.
|
|
43
73
|
stderr:
|
|
@@ -56,6 +86,14 @@ class CommandResult:
|
|
|
56
86
|
jar_path: str
|
|
57
87
|
duration_s: float
|
|
58
88
|
|
|
89
|
+
def __post_init__(self) -> None:
|
|
90
|
+
# Normalize any process exit code outside the documented 0/2/3 contract to
|
|
91
|
+
# 3 (runtime error). See the `exit_code` docstring for why. Recompute `ok`
|
|
92
|
+
# from the normalized code so the two can never disagree.
|
|
93
|
+
if self.exit_code not in (0, 2, 3):
|
|
94
|
+
object.__setattr__(self, "exit_code", 3)
|
|
95
|
+
object.__setattr__(self, "ok", self.exit_code == 0)
|
|
96
|
+
|
|
59
97
|
@property
|
|
60
98
|
def is_invalid_syntax(self) -> bool:
|
|
61
99
|
"""
|
|
@@ -103,7 +141,7 @@ class WindtraderClient:
|
|
|
103
141
|
|
|
104
142
|
t0 = time.time()
|
|
105
143
|
p = subprocess.run(
|
|
106
|
-
[
|
|
144
|
+
[*_resolve_java(), "-jar", str(jar), subcommand],
|
|
107
145
|
input=text,
|
|
108
146
|
text=True,
|
|
109
147
|
capture_output=True,
|
|
@@ -174,13 +174,17 @@ def test_invalid_timeout_flag_exits_with_usage_error(fake_validate, stdin_text):
|
|
|
174
174
|
# ---------------------------------------------------------------------------
|
|
175
175
|
|
|
176
176
|
|
|
177
|
-
@pytest.mark.parametrize("exit_code", [0, 2, 3, 1, 42])
|
|
178
|
-
def test_forwards_jar_exit_code(fake_validate, stdin_text, exit_code):
|
|
179
|
-
"""`main()` returns
|
|
177
|
+
@pytest.mark.parametrize("exit_code,expected", [(0, 0), (2, 2), (3, 3), (1, 3), (42, 3)])
|
|
178
|
+
def test_forwards_jar_exit_code(fake_validate, stdin_text, exit_code, expected):
|
|
179
|
+
"""`main()` returns the jar's exit code, normalized to the 0/2/3 contract.
|
|
180
|
+
|
|
181
|
+
Codes inside the contract (0/2/3) pass through exactly; any foreign code (e.g. 1
|
|
182
|
+
from a JVM startup failure) is mapped to 3 (runtime error).
|
|
183
|
+
"""
|
|
180
184
|
stdin_text("part def P;")
|
|
181
185
|
fake_validate.result = _result(exit_code)
|
|
182
186
|
|
|
183
|
-
assert cli.main([]) ==
|
|
187
|
+
assert cli.main([]) == expected
|
|
184
188
|
|
|
185
189
|
|
|
186
190
|
def test_exit_code_is_an_int(fake_validate, stdin_text):
|
|
@@ -110,13 +110,25 @@ def test_parse_error_result_flags():
|
|
|
110
110
|
assert res.is_runtime_error is False
|
|
111
111
|
|
|
112
112
|
|
|
113
|
-
@pytest.mark.parametrize("exit_code", [1,
|
|
114
|
-
def
|
|
115
|
-
"""Any exit code outside (0, 2)
|
|
113
|
+
@pytest.mark.parametrize("exit_code", [1, 127, -9, 42, 137])
|
|
114
|
+
def test_runtime_error_result_flags_normalized(exit_code):
|
|
115
|
+
"""Any exit code outside (0, 2, 3) surfaces as is_runtime_error AND is normalized to 3."""
|
|
116
116
|
res = _result(exit_code)
|
|
117
117
|
assert res.ok is False
|
|
118
118
|
assert res.is_invalid_syntax is False
|
|
119
119
|
assert res.is_runtime_error is True
|
|
120
|
+
# Normalization keeps the documented 0/2/3 contract (regression: a JVM startup
|
|
121
|
+
# failure like an old JRE hitting a Java-21 jar leaks code 1).
|
|
122
|
+
assert res.exit_code == 3
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_exit_code_3_is_runtime_error_not_normalized():
|
|
126
|
+
"""3 is already inside the contract and stays 3."""
|
|
127
|
+
res = _result(3)
|
|
128
|
+
assert res.exit_code == 3
|
|
129
|
+
assert res.ok is False
|
|
130
|
+
assert res.is_runtime_error is True
|
|
131
|
+
assert res.is_invalid_syntax is False
|
|
120
132
|
|
|
121
133
|
|
|
122
134
|
def test_validation_result_is_frozen():
|
|
@@ -132,12 +144,58 @@ def test_validation_result_is_frozen():
|
|
|
132
144
|
|
|
133
145
|
|
|
134
146
|
def test_validate_text_builds_exact_check_argv(fake_run):
|
|
135
|
-
"""`check` is invoked as exactly [\"java\", \"-jar\", <jar>, \"check\"]."""
|
|
147
|
+
"""`check` is invoked as exactly [\"java\", \"-jar\", <jar>, \"check\"] by default."""
|
|
136
148
|
WindtraderClient().validate_text("part { attribute mass; }")
|
|
137
149
|
|
|
138
150
|
assert fake_run.argv == ["java", "-jar", str(FAKE_JAR), "check"]
|
|
139
151
|
|
|
140
152
|
|
|
153
|
+
def test_java_executable_resolution_defaults_to_java(fake_run, monkeypatch):
|
|
154
|
+
"""With no WINDTRADER_JAVA / JAVA_HOME, the argv prefix is plain `java`."""
|
|
155
|
+
monkeypatch.delenv("WINDTRADER_JAVA", raising=False)
|
|
156
|
+
monkeypatch.delenv("JAVA_HOME", raising=False)
|
|
157
|
+
|
|
158
|
+
WindtraderClient().validate_text("part def P;")
|
|
159
|
+
assert fake_run.argv[0] == "java"
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_java_executable_resolution_uses_java_home(fake_run, monkeypatch):
|
|
163
|
+
"""`JAVA_HOME` is used as `<JAVA_HOME>/bin/java` when WINDTRADER_JAVA is absent."""
|
|
164
|
+
monkeypatch.delenv("WINDTRADER_JAVA", raising=False)
|
|
165
|
+
monkeypatch.setenv("JAVA_HOME", "/opt/jdk21")
|
|
166
|
+
|
|
167
|
+
WindtraderClient().validate_text("part def P;")
|
|
168
|
+
assert fake_run.argv[0] == "/opt/jdk21/bin/java"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_java_executable_resolution_prefers_windtrader_java(fake_run, monkeypatch):
|
|
172
|
+
"""`WINDTRADER_JAVA` (explicit path) wins over JAVA_HOME."""
|
|
173
|
+
monkeypatch.setenv("WINDTRADER_JAVA", "/custom/bin/java21")
|
|
174
|
+
monkeypatch.setenv("JAVA_HOME", "/opt/jdk-not-used")
|
|
175
|
+
|
|
176
|
+
WindtraderClient().validate_text("part def P;")
|
|
177
|
+
assert fake_run.argv[0] == "/custom/bin/java21"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_jvm_startup_failure_normalized_and_stderr_preserved(fake_run):
|
|
181
|
+
"""A JVM startup failure (rc=1, e.g. old Java vs Java-21 jar) -> 3, stderr kept."""
|
|
182
|
+
fake_run.returncode = 1
|
|
183
|
+
fake_run.stdout = ""
|
|
184
|
+
fake_run.stderr = (
|
|
185
|
+
"Error: LinkageError occurred while loading main class "
|
|
186
|
+
"io.westfall.windtrader.RoundTrip\n\tjava.lang.UnsupportedClassVersionError: "
|
|
187
|
+
"class file version 65.0 ... only recognizes class file versions up to 62.0"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
res = WindtraderClient().validate_text("part { attribute mass; }")
|
|
191
|
+
|
|
192
|
+
assert res.exit_code == 3
|
|
193
|
+
assert res.is_runtime_error is True
|
|
194
|
+
assert res.is_invalid_syntax is False
|
|
195
|
+
assert res.ok is False
|
|
196
|
+
assert "UnsupportedClassVersionError" in res.stderr
|
|
197
|
+
|
|
198
|
+
|
|
141
199
|
def test_echo_builds_exact_echo_argv(fake_run):
|
|
142
200
|
"""`echo` is invoked as exactly [\"java\", \"-jar\", <jar>, \"echo\"]."""
|
|
143
201
|
WindtraderClient().echo("part { attribute mass; }")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|