75 lines
2.7 KiB
Python
75 lines
2.7 KiB
Python
import sys
|
|
import pathlib
|
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))
|
|
|
|
import os
|
|
import xml.etree.ElementTree as ET
|
|
|
|
import pytest
|
|
|
|
from enhanced_mcm_generator import EnhancedMCMGenerator
|
|
|
|
|
|
def _canonicalize_xml(path: str) -> bytes:
|
|
"""Return a canonicalised representation of an L5X / XML file.
|
|
|
|
The function removes volatile attributes (e.g. ExportDate) and sorts
|
|
attributes of every element so the resulting byte string is stable
|
|
across Python runs and operating systems.
|
|
"""
|
|
tree = ET.parse(path)
|
|
root = tree.getroot()
|
|
|
|
# Remove volatile attributes that change on every export (present on many elements)
|
|
for elem in root.iter():
|
|
elem.attrib.pop("ExportDate", None)
|
|
|
|
# Recursively sort attributes to obtain a deterministic ordering
|
|
def _sort_attrs(elem: ET.Element):
|
|
if elem.attrib:
|
|
# Convert to list with sorted items to keep ElementTree stable
|
|
sorted_items = sorted(elem.attrib.items())
|
|
elem.attrib.clear()
|
|
elem.attrib.update(sorted_items)
|
|
for child in elem:
|
|
_sort_attrs(child)
|
|
|
|
_sort_attrs(root)
|
|
|
|
# Normalise text nodes: strip leading/trailing whitespace so that
|
|
# cosmetic indentation inside <Comment> elements does not cause false
|
|
# differences.
|
|
for elem in root.iter():
|
|
if elem.text is not None:
|
|
elem.text = elem.text.strip()
|
|
|
|
# ElementTree does not guarantee attribute ordering when converting to
|
|
# string, but because we have manually re-inserted sorted attributes we
|
|
# get a deterministic output here.
|
|
return ET.tostring(root, encoding="utf-8")
|
|
|
|
|
|
@pytest.mark.regression
|
|
def test_generated_project_matches_golden(tmp_path):
|
|
"""Generate the project and compare it against the golden reference.
|
|
|
|
If this test fails, a refactor has changed the *semantic* XML output.
|
|
Check the diff to decide whether the change is intended or not.
|
|
"""
|
|
project_name = "MCM04_Chute_Load"
|
|
|
|
# 1. Run the generator to build a fresh project under the temporary dir
|
|
generator = EnhancedMCMGenerator(project_name, excel_file="Data.xlsx")
|
|
assert generator.load_and_process_data(), "Failed to load/process Excel data"
|
|
|
|
output_path = generator.generate_complete_project()
|
|
|
|
# 2. Compare with the golden file
|
|
golden_path = os.path.join(
|
|
"generated_projects", "MCM04_Chute_Load_To_Compare_Against.L5X"
|
|
)
|
|
assert os.path.exists(golden_path), "Golden file is missing"
|
|
|
|
assert _canonicalize_xml(output_path) == _canonicalize_xml(
|
|
golden_path
|
|
), "Generated project differs from golden reference" |