Skip to content

Commit b033a0c

Browse files
Merge main: combine single-parser testing with schema generation testing
2 parents e2044a2 + 73083b5 commit b033a0c

6 files changed

Lines changed: 58 additions & 43 deletions

File tree

.github/workflows/test.yml

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ permissions:
1212
contents: read
1313

1414
jobs:
15+
# Determine which tests to run based on changed files
1516
detect-changes:
1617
runs-on: ubuntu-latest
1718
outputs:
1819
single-parser: ${{ steps.detect-parser.outputs.parser }}
1920
run-all-tests: ${{ steps.detect-parser.outputs.run-all }}
21+
schema-generation: ${{ steps.filter.outputs.schema-generation }}
2022
steps:
2123
- uses: actions/checkout@v4
2224
- uses: dorny/paths-filter@v3
@@ -33,6 +35,16 @@ jobs:
3335
- 'pyproject.toml'
3436
- 'scripts/**'
3537
- '.github/**'
38+
schema-generation:
39+
- 'src/allotropy/allotrope/schemas/**'
40+
- 'src/allotropy/allotrope/schema_parser/**'
41+
- 'src/allotropy/allotrope/models/**'
42+
- 'src/allotropy/allotrope/schemas.py'
43+
- 'src/allotropy/exceptions.py'
44+
- 'scripts/generate_schemas.py'
45+
- 'scripts/download_schema.py'
46+
- 'tests/allotrope/schema_parser/**'
47+
- 'pyproject.toml'
3648
- name: Detect single parser changes
3749
id: detect-parser
3850
run: |
@@ -93,11 +105,11 @@ jobs:
93105
run: pip install "hatch>=1.13.0"
94106
- name: Install click
95107
run: pip install click!=8.3.0
96-
- name: Run Tests
108+
- name: Run Tests (excluding schema generation)
97109
run: |
98110
if [[ "${{ needs.detect-changes.outputs.run-all }}" == "true" ]]; then
99111
echo "Running all tests"
100-
hatch run test_all.py3.10:pytest -n 2 tests
112+
hatch run test_all.py3.10:pytest -n 2 tests --ignore=tests/allotrope/schema_parser/generate_schemas_test.py
101113
else
102114
echo "Running tests for parser: ${{ needs.detect-changes.outputs.single-parser }}"
103115
hatch run test_all.py3.10:pytest -n 2 tests/parsers/${{ needs.detect-changes.outputs.single-parser }} tests/parser_factory_test.py::test_table_contents
@@ -121,11 +133,11 @@ jobs:
121133
# NOTE: due to bug: https://github.com/pallets/click/issues/3066
122134
- name: Install click
123135
run: pip install click!=8.3.0
124-
- name: Run Tests
136+
- name: Run Tests (excluding schema generation)
125137
run: |
126138
if [[ "${{ needs.detect-changes.outputs.run-all }}" == "true" ]]; then
127139
echo "Running all tests"
128-
hatch run test_all.py3.11:pytest -n 2 tests
140+
hatch run test_all.py3.11:pytest -n 2 tests --ignore=tests/allotrope/schema_parser/generate_schemas_test.py
129141
else
130142
echo "Running tests for parser: ${{ needs.detect-changes.outputs.single-parser }}"
131143
hatch run test_all.py3.11:pytest -n 2 tests/parsers/${{ needs.detect-changes.outputs.single-parser }} tests/parser_factory_test.py::test_table_contents
@@ -148,17 +160,43 @@ jobs:
148160
run: pip install "hatch>=1.13.0"
149161
- name: Install click
150162
run: pip install click!=8.3.0
151-
- name: Run Tests
163+
- name: Run Tests (excluding schema generation)
152164
run: |
153165
if [[ "${{ needs.detect-changes.outputs.run-all }}" == "true" ]]; then
154166
echo "Running all tests"
155-
hatch run test_all.py3.12:pytest -n 2 tests
167+
hatch run test_all.py3.12:pytest -n 2 tests --ignore=tests/allotrope/schema_parser/generate_schemas_test.py
156168
else
157169
echo "Running tests for parser: ${{ needs.detect-changes.outputs.single-parser }}"
158170
hatch run test_all.py3.12:pytest -n 2 tests/parsers/${{ needs.detect-changes.outputs.single-parser }} tests/parser_factory_test.py::test_table_contents
159171
fi
160172
timeout-minutes: 15
161173

174+
# Schema generation test - only runs when relevant files change or on main branch
175+
schema_generation_test:
176+
needs: detect-changes
177+
# Always run on main branch, otherwise only when schema files change
178+
if: ${{ github.ref == 'refs/heads/main' || needs.detect-changes.outputs.schema-generation == 'true' }}
179+
runs-on: ubuntu-latest
180+
name: Schema Generation Test
181+
strategy:
182+
matrix:
183+
python-version: ["3.10"] # Only run on one Python version since output is the same
184+
185+
steps:
186+
- uses: actions/checkout@v4
187+
with:
188+
lfs: true
189+
- uses: actions/setup-python@v5
190+
with:
191+
python-version: ${{ matrix.python-version }}
192+
- name: Install hatch
193+
run: pip install "hatch>=1.13.0"
194+
- name: Install click
195+
run: pip install click!=8.3.0
196+
- name: Run Schema Generation Test
197+
run: hatch run test_all.py${{ matrix.python-version }}:pytest tests/allotrope/schema_parser/generate_schemas_test.py -v
198+
timeout-minutes: 5
199+
162200
lint:
163201
runs-on: ubuntu-latest
164202
name: Quality Checks

src/allotropy/allotrope/schema_parser/backup_manager.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
from itertools import zip_longest
44
from pathlib import Path
55
import shutil
6-
7-
from allotropy.parsers.utils.uuids import random_uuid_str
6+
import uuid
87

98
PathType = Path | str
109

@@ -19,7 +18,7 @@ def _files_equal(path1: PathType, path2: PathType) -> bool:
1918

2019
def _get_backup_path(path: PathType) -> Path:
2120
_path = Path(path)
22-
return Path(_path.parent, f".{_path.stem}.{random_uuid_str()}.bak{_path.suffix}")
21+
return Path(_path.parent, f".{_path.stem}.{uuid.uuid4()!s}.bak{_path.suffix}")
2322

2423

2524
def get_original_path(path: PathType) -> Path:

src/allotropy/allotrope/schema_parser/model_class_editor.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
get_all_schema_components,
1818
get_schema_definitions_mapping,
1919
)
20-
from allotropy.parsers.utils.values import assert_not_none
2120

2221
SCHEMA_DIR_PATH = "src/allotropy/allotrope/schemas"
2322
SHARED_FOLDER_MODULE = "allotropy.allotrope.models.shared"
@@ -196,7 +195,10 @@ def create(contents: str) -> DataclassField:
196195
type_string, default_value = (
197196
content.split("=") if "=" in content else (content, None)
198197
)
199-
types = _parse_field_types(assert_not_none(type_string))
198+
if type_string is None:
199+
msg = f"Expected non-null type string for field {name}."
200+
raise ValueError(msg)
201+
types = _parse_field_types(type_string)
200202

201203
return DataclassField(name, default_value, types)
202204

src/allotropy/allotrope/schema_parser/path_util.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import re
55
from typing import Any
66

7-
from allotropy.exceptions import AllotropeValidationError
8-
97
ALLOTROPE_DIR: Path = Path(__file__).parent.parent
108
ALLOTROPY_DIR: Path = ALLOTROPE_DIR.parent
119
ROOT_DIR: Path = ALLOTROPE_DIR.parent.parent.parent
@@ -74,10 +72,10 @@ def get_schema_path_from_manifest(manifest: str) -> Path:
7472
def get_schema_path_from_asm(asm_dict: Mapping[str, Any]) -> Path:
7573
if "$asm.manifest" not in asm_dict:
7674
msg = "File is not valid ASM - missing $asm.manifest field"
77-
raise AllotropeValidationError(msg)
75+
raise ValueError(msg)
7876
if not isinstance(asm_dict["$asm.manifest"], str):
7977
msg = f"File is not valid ASM - $asm.manifest is not a string: {asm_dict['$asm.manifest']}"
80-
raise AllotropeValidationError(msg)
78+
raise ValueError(msg)
8179
return get_schema_path_from_manifest(asm_dict["$asm.manifest"])
8280

8381

src/allotropy/allotrope/schemas.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
get_schema_path_from_manifest,
1313
SHARED_SCHEMAS_DEFINITIONS_PATH,
1414
)
15-
from allotropy.constants import DEFAULT_ENCODING
1615
from allotropy.exceptions import AllotropeSerializationError, AllotropeValidationError
1716

17+
DEFAULT_ENCODING = "UTF-8"
18+
1819
# Override format checker to remove "uri-reference" check, which ASM schemas fail against.
1920
FORMAT_CHECKER = copy.deepcopy(
2021
jsonschema.validators.Draft202012Validator.FORMAT_CHECKER
Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from pathlib import Path
2-
import re
32

43
import pytest
54

@@ -20,33 +19,11 @@ def _get_schema_paths() -> list[Path]:
2019
]
2120

2221

23-
def _pick_subset(paths: list[Path], count: int = 8) -> list[Path]:
24-
if not paths:
25-
return []
26-
if len(paths) <= count:
27-
return paths
28-
step = max(1, len(paths) // count)
29-
# Evenly sample across the set for broad coverage
30-
return [paths[i] for i in range(0, len(paths), step)][:count]
31-
32-
33-
def test_generate_schemas_smoke_subset() -> None:
34-
"""Fast smoke test over a representative subset of schemas."""
35-
paths = _get_schema_paths()
36-
subset = _pick_subset(paths, count=8)
37-
# Build a regex that matches exactly any of the chosen relative schema paths
38-
escaped = [re.escape(str(p)) for p in subset]
39-
pattern = rf"^({'|'.join(escaped)})$"
40-
models_changed = generate_schemas(dry_run=True, schema_regex=pattern)
41-
assert (
42-
not models_changed
43-
), f"Expected no models files to have changed by generate-schemas script, found changes in: {models_changed}.\nPlease run 'hatch run scripts:generate-schemas' and validate the changes."
44-
45-
4622
@pytest.mark.long
47-
def test_generate_schemas_runs_to_completion() -> None:
48-
"""Full run once across all schemas. Marked long for optional execution."""
49-
models_changed = generate_schemas(dry_run=True)
23+
@pytest.mark.parametrize("schema_path", _get_schema_paths())
24+
def test_generate_schemas_runs_to_completion(schema_path: Path) -> None:
25+
"""Test that schema generation produces no unexpected changes."""
26+
models_changed = generate_schemas(dry_run=True, schema_regex=str(schema_path))
5027
assert (
5128
not models_changed
5229
), f"Expected no models files to have changed by generate-schemas script, found changes in: {models_changed}.\nPlease run 'hatch run scripts:generate-schemas' and validate the changes."

0 commit comments

Comments
 (0)