feat: align FreeCAD property status semantics
This commit is contained in:
145
scripts/freecad-property-status-oracle.py
Normal file
145
scripts/freecad-property-status-oracle.py
Normal file
@@ -0,0 +1,145 @@
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
import FreeCAD as App
|
||||
|
||||
|
||||
FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
|
||||
|
||||
|
||||
def object_state(obj):
|
||||
return {
|
||||
"state": [str(value) for value in obj.State],
|
||||
"mustExecute": bool(obj.MustExecute),
|
||||
"documentTouched": bool(obj.Document.isTouched()),
|
||||
}
|
||||
|
||||
|
||||
def change_case(case_id, attr=0, runtime_status=None):
|
||||
document = App.newDocument("PropertyStatus_" + case_id)
|
||||
try:
|
||||
obj = document.addObject("App::FeatureTest", "Feature")
|
||||
obj.addProperty("App::PropertyInteger", "Value", "Oracle", "Status probe", attr)
|
||||
if runtime_status:
|
||||
obj.setPropertyStatus("Value", runtime_status)
|
||||
document.recompute()
|
||||
document.purgeTouched()
|
||||
before = object_state(obj)
|
||||
obj.Value = 7
|
||||
changed = object_state(obj)
|
||||
recomputed = int(document.recompute())
|
||||
after_recompute = object_state(obj)
|
||||
return {
|
||||
"id": case_id,
|
||||
"attr": int(attr),
|
||||
"runtimeStatus": runtime_status,
|
||||
"reportedStatus": [str(value) for value in obj.getPropertyStatus("Value")],
|
||||
"reportedType": [str(value) for value in obj.getTypeOfProperty("Value")],
|
||||
"before": before,
|
||||
"changed": changed,
|
||||
"recomputeCount": recomputed,
|
||||
"afterRecompute": after_recompute,
|
||||
}
|
||||
finally:
|
||||
App.closeDocument(document.Name)
|
||||
|
||||
|
||||
def locked_dynamic_case():
|
||||
document = App.newDocument("PropertyStatus_LockDynamic")
|
||||
try:
|
||||
obj = document.addObject("App::FeaturePython", "Feature")
|
||||
obj.addProperty("App::PropertyInteger", "LockedValue", "Oracle")
|
||||
obj.setPropertyStatus("LockedValue", "LockDynamic")
|
||||
failures = {}
|
||||
for operation, callback in {
|
||||
"remove": lambda: obj.removeProperty("LockedValue"),
|
||||
"rename": lambda: obj.renameProperty("LockedValue", "RenamedValue"),
|
||||
}.items():
|
||||
try:
|
||||
callback()
|
||||
failures[operation] = None
|
||||
except Exception as error:
|
||||
failures[operation] = {"type": type(error).__name__, "message": str(error)}
|
||||
return {
|
||||
"reportedStatus": [str(value) for value in obj.getPropertyStatus("LockedValue")],
|
||||
"propertyStillPresent": "LockedValue" in obj.PropertiesList,
|
||||
"renamedPropertyPresent": "RenamedValue" in obj.PropertiesList,
|
||||
"failures": failures,
|
||||
}
|
||||
finally:
|
||||
App.closeDocument(document.Name)
|
||||
|
||||
|
||||
def archive_case():
|
||||
with tempfile.TemporaryDirectory(prefix="freecad-property-status-") as temp_dir:
|
||||
path = os.path.join(temp_dir, "PropertyStatus.FCStd")
|
||||
document = App.newDocument("PropertyStatusArchive")
|
||||
try:
|
||||
obj = document.addObject("App::FeaturePython", "Feature")
|
||||
obj.addProperty("App::PropertyInteger", "Persisted", "Oracle")
|
||||
obj.Persisted = 11
|
||||
obj.setPropertyStatus("Persisted", "Output")
|
||||
obj.addProperty("App::PropertyInteger", "RuntimeTransient", "Oracle")
|
||||
obj.RuntimeTransient = 12
|
||||
obj.setPropertyStatus("RuntimeTransient", "Transient")
|
||||
obj.addProperty("App::PropertyInteger", "TypeTransient", "Oracle", "", int(App.PropertyType.Prop_Transient))
|
||||
obj.TypeTransient = 13
|
||||
obj.addProperty("App::PropertyInteger", "NoPersist", "Oracle", "", int(App.PropertyType.Prop_NoPersist))
|
||||
obj.NoPersist = 14
|
||||
document.recompute()
|
||||
document.saveAs(path)
|
||||
finally:
|
||||
App.closeDocument(document.Name)
|
||||
|
||||
with zipfile.ZipFile(path, "r") as archive:
|
||||
document_xml = archive.read("Document.xml").decode("utf-8")
|
||||
|
||||
reopened = App.openDocument(path)
|
||||
try:
|
||||
obj = reopened.getObject("Feature")
|
||||
return {
|
||||
"xml": {
|
||||
"persistedProperty": '<Property name="Persisted"' in document_xml,
|
||||
"runtimeTransientProperty": '<Property name="RuntimeTransient"' in document_xml,
|
||||
"typeTransientProperty": '<Property name="TypeTransient"' in document_xml,
|
||||
"noPersistProperty": '<Property name="NoPersist"' in document_xml,
|
||||
"runtimeTransientHasValue": '<Integer value="12"' in document_xml,
|
||||
"typeTransientHasValue": '<Integer value="13"' in document_xml,
|
||||
},
|
||||
"reopened": {
|
||||
"properties": sorted(str(value) for value in obj.PropertiesList if value in {"Persisted", "RuntimeTransient", "TypeTransient", "NoPersist"}),
|
||||
"values": {
|
||||
"Persisted": int(obj.Persisted),
|
||||
"RuntimeTransient": int(obj.RuntimeTransient),
|
||||
"TypeTransient": int(obj.TypeTransient),
|
||||
},
|
||||
"statuses": {
|
||||
name: [str(value) for value in obj.getPropertyStatus(name)]
|
||||
for name in ["Persisted", "RuntimeTransient", "TypeTransient"]
|
||||
},
|
||||
},
|
||||
}
|
||||
finally:
|
||||
App.closeDocument(reopened.Name)
|
||||
|
||||
|
||||
cases = [
|
||||
change_case("ordinary"),
|
||||
change_case("runtime-output", runtime_status="Output"),
|
||||
change_case("type-output", attr=int(App.PropertyType.Prop_Output)),
|
||||
change_case("runtime-no-recompute", runtime_status="NoRecompute"),
|
||||
change_case("type-no-recompute", attr=int(App.PropertyType.Prop_NoRecompute)),
|
||||
]
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"baselineId": "freecad-1.1.1-property-status-oracle",
|
||||
"freecadVersion": ".".join(str(value) for value in App.Version()[:3]),
|
||||
"gitCommit": FREECAD_COMMIT,
|
||||
"status": "pass",
|
||||
"cases": cases,
|
||||
"lockDynamic": locked_dynamic_case(),
|
||||
"fcstd": archive_case(),
|
||||
}
|
||||
print("FREECAD_PROPERTY_STATUS_ORACLE_RESULT=" + json.dumps(report, sort_keys=True, separators=(",", ":")))
|
||||
Reference in New Issue
Block a user