Complete M16 asset operator parity chain
This commit is contained in:
82
tools/web/check-action-asset-clear-desktop.py
Normal file
82
tools/web/check-action-asset-clear-desktop.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def library_state():
|
||||
libraries = list(bpy.data.libraries)
|
||||
if len(libraries) != 1:
|
||||
raise RuntimeError(f"expected one asset-clear library, found {len(libraries)}")
|
||||
library = libraries[0]
|
||||
return {"name": library.name, "filepath": library.filepath, "packed": library.packed_file is not None}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python check-action-asset-clear-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = library_state()
|
||||
accepted_errors = (
|
||||
"context is incorrect",
|
||||
"Data-block is not marked as asset",
|
||||
"No data-block selected that is marked as asset",
|
||||
"No asset data-blocks selected",
|
||||
)
|
||||
try:
|
||||
poll = bool(bpy.ops.asset.clear.poll())
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in accepted_errors):
|
||||
raise
|
||||
poll = False
|
||||
if poll:
|
||||
raise RuntimeError("asset.clear unexpectedly polled true")
|
||||
try:
|
||||
bpy.ops.asset.clear(set_fake_user=False)
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in accepted_errors):
|
||||
raise
|
||||
operator_status = "CANCELLED"
|
||||
else:
|
||||
raise RuntimeError("asset.clear unexpectedly finished")
|
||||
after_cancel = library_state()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"asset.clear cancellation changed Main data: {before} != {after_cancel}")
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-asset-clear-reopen-", suffix=".blend", dir=fixture.parent) as temporary:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary.name, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary.name, load_ui=False)
|
||||
after = library_state()
|
||||
if after != before:
|
||||
raise RuntimeError(f"asset.clear save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00275",
|
||||
"operation": "ASSET_CLEAR_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"before": before,
|
||||
"after": after,
|
||||
"poll": poll,
|
||||
"operatorStatus": operator_status,
|
||||
"mainMutation": "NONE",
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("asset-clear-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"asset-clear-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
79
tools/web/check-action-asset-download-desktop.py
Normal file
79
tools/web/check-action-asset-download-desktop.py
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
IMAGE_NAME = "WebGapAssetDownloadImage"
|
||||
|
||||
|
||||
def image_state():
|
||||
image = bpy.data.images.get(IMAGE_NAME)
|
||||
if image is None:
|
||||
raise RuntimeError("asset download fixture image is missing")
|
||||
return {
|
||||
"name": image.name,
|
||||
"filepath": image.filepath,
|
||||
"source": image.source,
|
||||
"packed": image.packed_file is not None,
|
||||
"size": [int(image.generated_width), int(image.generated_height)],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-asset-download-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
bpy.context.preferences.system.use_online_access = True
|
||||
before = image_state()
|
||||
poll = bool(bpy.ops.asset.asset_download.poll())
|
||||
try:
|
||||
bpy.ops.asset.asset_download(relative_asset_identifier="WebGapAssetDownload")
|
||||
except RuntimeError as error:
|
||||
if "Asset could not be found" not in str(error):
|
||||
raise
|
||||
operator_status = "CANCELLED"
|
||||
else:
|
||||
raise RuntimeError("asset.asset_download unexpectedly finished without a remote asset")
|
||||
after_cancel = image_state()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"asset.asset_download cancellation changed Main data: {before} != {after_cancel}")
|
||||
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-asset-download-reopen-", suffix=".blend", dir=fixture.parent) as temporary:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary.name, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary.name, load_ui=False)
|
||||
after = image_state()
|
||||
if after != before:
|
||||
raise RuntimeError(f"asset.asset_download save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00264",
|
||||
"operation": "ASSET_ASSET_DOWNLOAD_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"before": before,
|
||||
"after": after,
|
||||
"poll": poll,
|
||||
"operatorStatus": operator_status,
|
||||
"mainMutation": "NONE",
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("asset-asset-download-desktop-ok status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"asset-asset-download-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
81
tools/web/check-action-assets-download-desktop.py
Normal file
81
tools/web/check-action-assets-download-desktop.py
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
IMAGE_NAME = "WebGapAssetsDownloadImage"
|
||||
|
||||
|
||||
def image_state():
|
||||
image = bpy.data.images.get(IMAGE_NAME)
|
||||
if image is None:
|
||||
raise RuntimeError("assets download fixture image is missing")
|
||||
return {
|
||||
"name": image.name,
|
||||
"filepath": image.filepath,
|
||||
"source": image.source,
|
||||
"packed": image.packed_file is not None,
|
||||
"size": [int(image.generated_width), int(image.generated_height)],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-assets-download-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
bpy.context.preferences.system.use_online_access = True
|
||||
before = image_state()
|
||||
poll = bool(bpy.ops.asset.assets_download.poll())
|
||||
if poll:
|
||||
raise RuntimeError("asset.assets_download unexpectedly polled true without selected asset")
|
||||
try:
|
||||
bpy.ops.asset.assets_download()
|
||||
except RuntimeError as error:
|
||||
if "No asset selected or active" not in str(error):
|
||||
raise
|
||||
operator_status = "CANCELLED"
|
||||
else:
|
||||
raise RuntimeError("asset.assets_download unexpectedly finished without selected asset")
|
||||
after_cancel = image_state()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"asset.assets_download cancellation changed Main data: {before} != {after_cancel}")
|
||||
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-assets-download-reopen-", suffix=".blend", dir=fixture.parent) as temporary:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary.name, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary.name, load_ui=False)
|
||||
after = image_state()
|
||||
if after != before:
|
||||
raise RuntimeError(f"asset.assets_download save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00265",
|
||||
"operation": "ASSET_ASSETS_DOWNLOAD_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"before": before,
|
||||
"after": after,
|
||||
"poll": poll,
|
||||
"operatorStatus": operator_status,
|
||||
"mainMutation": "NONE",
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("asset-assets-download-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"asset-assets-download-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
92
tools/web/check-action-assign-action-desktop.py
Normal file
92
tools/web/check-action-assign-action-desktop.py
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
ACTION_NAME = "WebGapAssignAction"
|
||||
OBJECT_NAME = "WebGapAssignActionObject"
|
||||
|
||||
|
||||
def action_state():
|
||||
obj = bpy.data.objects.get(OBJECT_NAME)
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("assign_action fixture action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-assign-action-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_state()
|
||||
try:
|
||||
poll = bool(bpy.ops.asset.assign_action.poll())
|
||||
except RuntimeError as error:
|
||||
if "context is incorrect" not in str(error):
|
||||
raise
|
||||
poll = False
|
||||
if poll:
|
||||
raise RuntimeError("asset.assign_action unexpectedly polled true without selected asset")
|
||||
try:
|
||||
bpy.ops.asset.assign_action()
|
||||
except RuntimeError as error:
|
||||
if "No asset selected or active" not in str(error) and "context is incorrect" not in str(error):
|
||||
raise
|
||||
operator_status = "CANCELLED"
|
||||
else:
|
||||
raise RuntimeError("asset.assign_action unexpectedly finished without selected asset")
|
||||
after_cancel = action_state()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"asset.assign_action cancellation changed Main data: {before} != {after_cancel}")
|
||||
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-assign-action-reopen-", suffix=".blend", dir=fixture.parent) as temporary:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary.name, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary.name, load_ui=False)
|
||||
after = action_state()
|
||||
if after != before:
|
||||
raise RuntimeError(f"asset.assign_action save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00266",
|
||||
"operation": "ASSET_ASSIGN_ACTION_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"before": before,
|
||||
"after": after,
|
||||
"poll": poll,
|
||||
"operatorStatus": operator_status,
|
||||
"mainMutation": "NONE",
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("asset-assign-action-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"asset-assign-action-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def library_state():
|
||||
libraries = list(bpy.data.libraries)
|
||||
if len(libraries) != 1:
|
||||
raise RuntimeError(f"expected one browse-containing library, found {len(libraries)}")
|
||||
library = libraries[0]
|
||||
return {"name": library.name, "filepath": library.filepath, "packed": library.packed_file is not None}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-browse-containing-blend-file-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = library_state()
|
||||
try:
|
||||
poll = bool(bpy.ops.asset.browse_containing_blend_file.poll())
|
||||
except RuntimeError as error:
|
||||
if "context is incorrect" not in str(error) and "No asset selected" not in str(error):
|
||||
raise
|
||||
poll = False
|
||||
if poll:
|
||||
raise RuntimeError("asset.browse_containing_blend_file unexpectedly polled true")
|
||||
try:
|
||||
bpy.ops.asset.browse_containing_blend_file()
|
||||
except RuntimeError as error:
|
||||
if "context is incorrect" not in str(error) and "No asset selected" not in str(error):
|
||||
raise
|
||||
operator_status = "CANCELLED"
|
||||
else:
|
||||
raise RuntimeError("asset.browse_containing_blend_file unexpectedly finished")
|
||||
after_cancel = library_state()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"browse-containing cancellation changed Main data: {before} != {after_cancel}")
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-browse-containing-reopen-", suffix=".blend", dir=fixture.parent) as temporary:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary.name, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary.name, load_ui=False)
|
||||
after = library_state()
|
||||
if after != before:
|
||||
raise RuntimeError(f"browse-containing save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00267",
|
||||
"operation": "ASSET_BROWSE_CONTAINING_BLEND_FILE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"before": before,
|
||||
"after": after,
|
||||
"poll": poll,
|
||||
"operatorStatus": operator_status,
|
||||
"mainMutation": "NONE",
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("asset-browse-containing-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"asset-browse-containing-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
76
tools/web/check-action-bundle-install-desktop.py
Normal file
76
tools/web/check-action-bundle-install-desktop.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def library_state():
|
||||
libraries = list(bpy.data.libraries)
|
||||
if len(libraries) != 1:
|
||||
raise RuntimeError(f"expected one bundle-install library, found {len(libraries)}")
|
||||
library = libraries[0]
|
||||
return {"name": library.name, "filepath": library.filepath, "packed": library.packed_file is not None}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-bundle-install-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = library_state()
|
||||
try:
|
||||
poll = bool(bpy.ops.asset.bundle_install.poll())
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in ("context is incorrect", "No asset selected", "No asset library")):
|
||||
raise
|
||||
poll = False
|
||||
if poll:
|
||||
raise RuntimeError("asset.bundle_install unexpectedly polled true")
|
||||
try:
|
||||
bpy.ops.asset.bundle_install()
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in ("context is incorrect", "No asset selected", "No asset library")):
|
||||
raise
|
||||
operator_status = "CANCELLED"
|
||||
else:
|
||||
raise RuntimeError("asset.bundle_install unexpectedly finished")
|
||||
after_cancel = library_state()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"bundle-install cancellation changed Main data: {before} != {after_cancel}")
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-bundle-install-reopen-", suffix=".blend", dir=fixture.parent) as temporary:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary.name, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary.name, load_ui=False)
|
||||
after = library_state()
|
||||
if after != before:
|
||||
raise RuntimeError(f"bundle-install save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00268",
|
||||
"operation": "ASSET_BUNDLE_INSTALL_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"before": before,
|
||||
"after": after,
|
||||
"poll": poll,
|
||||
"operatorStatus": operator_status,
|
||||
"mainMutation": "NONE",
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("asset-bundle-install-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"asset-bundle-install-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
64
tools/web/check-action-catalog-delete-desktop.py
Normal file
64
tools/web/check-action-catalog-delete-desktop.py
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def library_state():
|
||||
libraries = list(bpy.data.libraries)
|
||||
if len(libraries) != 1:
|
||||
raise RuntimeError(f"expected one catalog-delete library, found {len(libraries)}")
|
||||
library = libraries[0]
|
||||
return {"name": library.name, "filepath": library.filepath, "packed": library.packed_file is not None}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-catalog-delete-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = library_state()
|
||||
accepted_errors = ("context is incorrect", "No asset selected", "No asset library", "Catalog")
|
||||
try:
|
||||
poll = bool(bpy.ops.asset.catalog_delete.poll())
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in accepted_errors):
|
||||
raise
|
||||
poll = False
|
||||
if poll:
|
||||
raise RuntimeError("asset.catalog_delete unexpectedly polled true")
|
||||
try:
|
||||
bpy.ops.asset.catalog_delete()
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in accepted_errors):
|
||||
raise
|
||||
operator_status = "CANCELLED"
|
||||
else:
|
||||
raise RuntimeError("asset.catalog_delete unexpectedly finished")
|
||||
after_cancel = library_state()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"catalog-delete cancellation changed Main data: {before} != {after_cancel}")
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-catalog-delete-reopen-", suffix=".blend", dir=fixture.parent) as temporary:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary.name, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary.name, load_ui=False)
|
||||
after = library_state()
|
||||
if after != before:
|
||||
raise RuntimeError(f"catalog-delete save/reopen drift: {before} != {after}")
|
||||
report = {"schemaVersion": 1, "task": "M16-GAP-00269", "operation": "ASSET_CATALOG_DELETE_DESKTOP", "fixture": str(fixture), "fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(), "before": before, "after": after, "poll": poll, "operatorStatus": operator_status, "mainMutation": "NONE", "saveReopen": "EXACT", "blenderVersion": bpy.app.version_string}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("asset-catalog-delete-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"asset-catalog-delete-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
64
tools/web/check-action-catalog-new-desktop.py
Normal file
64
tools/web/check-action-catalog-new-desktop.py
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def library_state():
|
||||
libraries = list(bpy.data.libraries)
|
||||
if len(libraries) != 1:
|
||||
raise RuntimeError(f"expected one catalog-new library, found {len(libraries)}")
|
||||
library = libraries[0]
|
||||
return {"name": library.name, "filepath": library.filepath, "packed": library.packed_file is not None}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-catalog-new-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = library_state()
|
||||
accepted_errors = ("context is incorrect", "No asset selected", "No asset library", "Catalog")
|
||||
try:
|
||||
poll = bool(bpy.ops.asset.catalog_new.poll())
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in accepted_errors):
|
||||
raise
|
||||
poll = False
|
||||
if poll:
|
||||
raise RuntimeError("asset.catalog_new unexpectedly polled true")
|
||||
try:
|
||||
bpy.ops.asset.catalog_new()
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in accepted_errors):
|
||||
raise
|
||||
operator_status = "CANCELLED"
|
||||
else:
|
||||
raise RuntimeError("asset.catalog_new unexpectedly finished")
|
||||
after_cancel = library_state()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"catalog-new cancellation changed Main data: {before} != {after_cancel}")
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-catalog-new-reopen-", suffix=".blend", dir=fixture.parent) as temporary:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary.name, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary.name, load_ui=False)
|
||||
after = library_state()
|
||||
if after != before:
|
||||
raise RuntimeError(f"catalog-new save/reopen drift: {before} != {after}")
|
||||
report = {"schemaVersion": 1, "task": "M16-GAP-00270", "operation": "ASSET_CATALOG_NEW_DESKTOP", "fixture": str(fixture), "fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(), "before": before, "after": after, "poll": poll, "operatorStatus": operator_status, "mainMutation": "NONE", "saveReopen": "EXACT", "blenderVersion": bpy.app.version_string}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("asset-catalog-new-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"asset-catalog-new-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
33
tools/web/check-action-catalog-redo-desktop.py
Normal file
33
tools/web/check-action-catalog-redo-desktop.py
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib, json, pathlib, sys, tempfile, bpy
|
||||
|
||||
def state():
|
||||
if len(bpy.data.libraries) != 1: raise RuntimeError("catalog-redo library missing")
|
||||
lib = bpy.data.libraries[0]; return {"name": lib.name, "filepath": lib.filepath, "packed": lib.packed_file is not None}
|
||||
|
||||
def main():
|
||||
args = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(args) != 2: raise SystemExit("usage")
|
||||
fixture, output = (pathlib.Path(v).resolve() for v in args); bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False); before = state()
|
||||
accepted = ("context is incorrect", "No asset selected", "No asset library", "Catalog")
|
||||
try: poll = bool(bpy.ops.asset.catalog_redo.poll())
|
||||
except RuntimeError as e:
|
||||
if not any(x in str(e) for x in accepted): raise
|
||||
poll = False
|
||||
if poll: raise RuntimeError("catalog_redo poll unexpectedly true")
|
||||
try: bpy.ops.asset.catalog_redo()
|
||||
except RuntimeError as e:
|
||||
if not any(x in str(e) for x in accepted): raise
|
||||
status = "CANCELLED"
|
||||
else: raise RuntimeError("catalog_redo unexpectedly finished")
|
||||
if state() != before: raise RuntimeError("catalog_redo mutated Main")
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-catalog-redo-reopen-", suffix=".blend", dir=fixture.parent) as t:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=t.name, check_existing=False, compress=True); bpy.ops.wm.open_mainfile(filepath=t.name, load_ui=False); after = state()
|
||||
if after != before: raise RuntimeError("catalog_redo save/reopen drift")
|
||||
report = {"schemaVersion":1,"task":"M16-GAP-00271","operation":"ASSET_CATALOG_REDO_DESKTOP","fixture":str(fixture),"fixtureSha256":hashlib.sha256(fixture.read_bytes()).hexdigest(),"before":before,"after":after,"poll":poll,"operatorStatus":status,"mainMutation":"NONE","saveReopen":"EXACT","blenderVersion":bpy.app.version_string}
|
||||
output.parent.mkdir(parents=True, exist_ok=True); output.write_text(json.dumps(report, indent=2, sort_keys=True)+"\n")
|
||||
print("asset-catalog-redo-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try: main()
|
||||
except Exception as e: print(f"asset-catalog-redo-desktop-failed: {e}"); raise SystemExit(1)
|
||||
27
tools/web/check-action-catalog-undo-desktop.py
Normal file
27
tools/web/check-action-catalog-undo-desktop.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib,json,pathlib,sys,tempfile,bpy
|
||||
def state():
|
||||
if len(bpy.data.libraries)!=1: raise RuntimeError("catalog-undo library missing")
|
||||
lib=bpy.data.libraries[0]; return {"name":lib.name,"filepath":lib.filepath,"packed":lib.packed_file is not None}
|
||||
def main():
|
||||
args=sys.argv[sys.argv.index("--")+1:]
|
||||
if len(args)!=2: raise SystemExit("usage")
|
||||
fixture,output=(pathlib.Path(v).resolve() for v in args); bpy.ops.wm.open_mainfile(filepath=str(fixture),load_ui=False); before=state(); accepted=("context is incorrect","No asset selected","No asset library","Catalog")
|
||||
try: poll=bool(bpy.ops.asset.catalog_undo.poll())
|
||||
except RuntimeError as e:
|
||||
if not any(x in str(e) for x in accepted): raise
|
||||
poll=False
|
||||
if poll: raise RuntimeError("catalog_undo poll unexpectedly true")
|
||||
try: bpy.ops.asset.catalog_undo()
|
||||
except RuntimeError as e:
|
||||
if not any(x in str(e) for x in accepted): raise
|
||||
status="CANCELLED"
|
||||
else: raise RuntimeError("catalog_undo unexpectedly finished")
|
||||
if state()!=before: raise RuntimeError("catalog_undo mutated Main")
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-catalog-undo-reopen-",suffix=".blend",dir=fixture.parent) as t:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=t.name,check_existing=False,compress=True); bpy.ops.wm.open_mainfile(filepath=t.name,load_ui=False); after=state()
|
||||
if after!=before: raise RuntimeError("catalog_undo save/reopen drift")
|
||||
report={"schemaVersion":1,"task":"M16-GAP-00272","operation":"ASSET_CATALOG_UNDO_DESKTOP","fixture":str(fixture),"fixtureSha256":hashlib.sha256(fixture.read_bytes()).hexdigest(),"before":before,"after":after,"poll":poll,"operatorStatus":status,"mainMutation":"NONE","saveReopen":"EXACT","blenderVersion":bpy.app.version_string}; output.parent.mkdir(parents=True,exist_ok=True); output.write_text(json.dumps(report,indent=2,sort_keys=True)+"\n"); print("asset-catalog-undo-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
if __name__=="__main__":
|
||||
try: main()
|
||||
except Exception as e: print(f"asset-catalog-undo-desktop-failed: {e}"); raise SystemExit(1)
|
||||
27
tools/web/check-action-catalog-undo-push-desktop.py
Normal file
27
tools/web/check-action-catalog-undo-push-desktop.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib,json,pathlib,sys,tempfile,bpy
|
||||
def state():
|
||||
if len(bpy.data.libraries)!=1: raise RuntimeError("catalog-undo-push library missing")
|
||||
lib=bpy.data.libraries[0]; return {"name":lib.name,"filepath":lib.filepath,"packed":lib.packed_file is not None}
|
||||
def main():
|
||||
args=sys.argv[sys.argv.index("--")+1:]
|
||||
if len(args)!=2: raise SystemExit("usage")
|
||||
fixture,output=(pathlib.Path(v).resolve() for v in args); bpy.ops.wm.open_mainfile(filepath=str(fixture),load_ui=False); before=state(); accepted=("context is incorrect","No asset selected","No asset library","Catalog")
|
||||
try: poll=bool(bpy.ops.asset.catalog_undo_push.poll())
|
||||
except RuntimeError as e:
|
||||
if not any(x in str(e) for x in accepted): raise
|
||||
poll=False
|
||||
if poll: raise RuntimeError("catalog_undo_push poll unexpectedly true")
|
||||
try: bpy.ops.asset.catalog_undo_push()
|
||||
except RuntimeError as e:
|
||||
if not any(x in str(e) for x in accepted): raise
|
||||
status="CANCELLED"
|
||||
else: raise RuntimeError("catalog_undo_push unexpectedly finished")
|
||||
if state()!=before: raise RuntimeError("catalog_undo_push mutated Main")
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-catalog-undo-push-reopen-",suffix=".blend",dir=fixture.parent) as t:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=t.name,check_existing=False,compress=True); bpy.ops.wm.open_mainfile(filepath=t.name,load_ui=False); after=state()
|
||||
if after!=before: raise RuntimeError("catalog_undo_push save/reopen drift")
|
||||
report={"schemaVersion":1,"task":"M16-GAP-00273","operation":"ASSET_CATALOG_UNDO_PUSH_DESKTOP","fixture":str(fixture),"fixtureSha256":hashlib.sha256(fixture.read_bytes()).hexdigest(),"before":before,"after":after,"poll":poll,"operatorStatus":status,"mainMutation":"NONE","saveReopen":"EXACT","blenderVersion":bpy.app.version_string}; output.parent.mkdir(parents=True,exist_ok=True); output.write_text(json.dumps(report,indent=2,sort_keys=True)+"\n"); print("asset-catalog-undo-push-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
if __name__=="__main__":
|
||||
try: main()
|
||||
except Exception as e: print(f"asset-catalog-undo-push-desktop-failed: {e}"); raise SystemExit(1)
|
||||
77
tools/web/check-action-catalogs-save-desktop.py
Normal file
77
tools/web/check-action-catalogs-save-desktop.py
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def library_state():
|
||||
libraries = list(bpy.data.libraries)
|
||||
if len(libraries) != 1:
|
||||
raise RuntimeError(f"expected one catalogs-save library, found {len(libraries)}")
|
||||
library = libraries[0]
|
||||
return {"name": library.name, "filepath": library.filepath, "packed": library.packed_file is not None}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python check-action-catalogs-save-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = library_state()
|
||||
accepted_errors = ("context is incorrect", "No asset selected", "No asset library", "Catalog")
|
||||
try:
|
||||
poll = bool(bpy.ops.asset.catalogs_save.poll())
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in accepted_errors):
|
||||
raise
|
||||
poll = False
|
||||
if poll:
|
||||
raise RuntimeError("asset.catalogs_save unexpectedly polled true")
|
||||
try:
|
||||
bpy.ops.asset.catalogs_save()
|
||||
except RuntimeError as error:
|
||||
if not any(token in str(error) for token in accepted_errors):
|
||||
raise
|
||||
operator_status = "CANCELLED"
|
||||
else:
|
||||
raise RuntimeError("asset.catalogs_save unexpectedly finished")
|
||||
after_cancel = library_state()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"catalogs-save cancellation changed Main data: {before} != {after_cancel}")
|
||||
with tempfile.NamedTemporaryFile(prefix="m16-catalogs-save-reopen-", suffix=".blend", dir=fixture.parent) as temporary:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary.name, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary.name, load_ui=False)
|
||||
after = library_state()
|
||||
if after != before:
|
||||
raise RuntimeError(f"catalogs-save save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00274",
|
||||
"operation": "ASSET_CATALOGS_SAVE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"before": before,
|
||||
"after": after,
|
||||
"poll": poll,
|
||||
"operatorStatus": operator_status,
|
||||
"mainMutation": "NONE",
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("asset-catalogs-save-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"asset-catalogs-save-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
@@ -7815,6 +7815,387 @@ if (task === "M16-GAP-00263") {
|
||||
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} mirrored=1 direction=negative_x desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00264") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00264-operator-asset.asset_download.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00264/asset-download-desktop-report.json");
|
||||
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-asset-download-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
||||
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
||||
assert.equal(desktop.saveReopen, "EXACT");
|
||||
assert.equal(desktop.poll, true);
|
||||
assert.equal(desktop.operatorStatus, "CANCELLED");
|
||||
assert.equal(desktop.mainMutation, "NONE");
|
||||
assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary });
|
||||
const handle = engine._web_engine_create();
|
||||
open(engine, handle, fs.readFileSync(fixture));
|
||||
const before = snapshot(engine, handle);
|
||||
const image = before.images?.find((value) => value.id === "image:WebGapAssetDownloadImage");
|
||||
assert.ok(image);
|
||||
assert.equal(image.name, "WebGapAssetDownloadImage");
|
||||
assert.equal(image.assetId, image.id);
|
||||
assert.equal(image.sourcePath, "//assets/WebGapAssetDownload.png");
|
||||
assert.equal(image.assetStatus, "EXTERNAL");
|
||||
assert.equal(image.packed, false);
|
||||
assert.deepEqual([image.width, image.height], [1, 1]);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
||||
const reopened = engine._web_engine_create();
|
||||
open(engine, reopened, saved);
|
||||
const after = snapshot(engine, reopened).images?.find((value) => value.id === image.id);
|
||||
assert.deepEqual(after, image);
|
||||
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
||||
const pointer = engine._malloc(malformed.byteLength);
|
||||
let result;
|
||||
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); }
|
||||
assert.notEqual(result, 0);
|
||||
assert.deepEqual(snapshot(engine, handle).images?.find((value) => value.id === image.id), image);
|
||||
engine._web_engine_destroy(handle);
|
||||
engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_ASSET_DOWNLOAD_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", imageId: image.id, sourcePath: image.sourcePath, assetStatus: image.assetStatus, packed: image.packed, dimensions: [image.width, image.height] }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00265" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00264/asset-download-local-exact-report.json");
|
||||
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
||||
process.stdout.write(`generated-gap-ok task=${task} image=${image.id} assetStatus=${image.assetStatus} operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00265") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00265-operator-asset.assets_download.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00265/assets-download-desktop-report.json");
|
||||
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-assets-download-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
||||
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
||||
assert.equal(desktop.saveReopen, "EXACT");
|
||||
assert.equal(desktop.poll, false);
|
||||
assert.equal(desktop.operatorStatus, "CANCELLED");
|
||||
assert.equal(desktop.mainMutation, "NONE");
|
||||
assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary });
|
||||
const handle = engine._web_engine_create();
|
||||
open(engine, handle, fs.readFileSync(fixture));
|
||||
const before = snapshot(engine, handle);
|
||||
const image = before.images?.find((value) => value.id === "image:WebGapAssetsDownloadImage");
|
||||
assert.ok(image);
|
||||
assert.equal(image.name, "WebGapAssetsDownloadImage");
|
||||
assert.equal(image.assetId, image.id);
|
||||
assert.equal(image.sourcePath, "//assets/WebGapAssetsDownload.png");
|
||||
assert.equal(image.assetStatus, "EXTERNAL");
|
||||
assert.equal(image.packed, false);
|
||||
assert.deepEqual([image.width, image.height], [1, 1]);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
||||
const reopened = engine._web_engine_create();
|
||||
open(engine, reopened, saved);
|
||||
const after = snapshot(engine, reopened).images?.find((value) => value.id === image.id);
|
||||
assert.deepEqual(after, image);
|
||||
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
||||
const pointer = engine._malloc(malformed.byteLength);
|
||||
let result;
|
||||
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); }
|
||||
assert.notEqual(result, 0);
|
||||
assert.deepEqual(snapshot(engine, handle).images?.find((value) => value.id === image.id), image);
|
||||
engine._web_engine_destroy(handle);
|
||||
engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_ASSETS_DOWNLOAD_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", imageId: image.id, sourcePath: image.sourcePath, assetStatus: image.assetStatus, packed: image.packed, dimensions: [image.width, image.height] }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00266" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00265/assets-download-local-exact-report.json");
|
||||
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
||||
process.stdout.write(`generated-gap-ok task=${task} image=${image.id} assetStatus=${image.assetStatus} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00266") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00266-operator-asset.assign_action.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00266/assign-action-desktop-report.json");
|
||||
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-assign-action-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
||||
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
||||
assert.equal(desktop.saveReopen, "EXACT");
|
||||
assert.equal(desktop.poll, false);
|
||||
assert.equal(desktop.operatorStatus, "CANCELLED");
|
||||
assert.equal(desktop.mainMutation, "NONE");
|
||||
assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary });
|
||||
const handle = engine._web_engine_create();
|
||||
open(engine, handle, fs.readFileSync(fixture));
|
||||
const before = snapshot(engine, handle);
|
||||
const animation = before.animations?.find((value) => value.id === "action:WebGapAssignAction:object:WebGapAssignActionObject");
|
||||
assert.ok(animation);
|
||||
assert.equal(animation.channels.length, desktop.before.channels.length);
|
||||
for (const [index, channel] of animation.channels.entries()) {
|
||||
const expected = desktop.before.channels[index];
|
||||
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
||||
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
||||
}
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
||||
const reopened = engine._web_engine_create();
|
||||
open(engine, reopened, saved);
|
||||
assert.deepEqual(snapshot(engine, reopened).animations, before.animations);
|
||||
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
||||
const pointer = engine._malloc(malformed.byteLength);
|
||||
let result;
|
||||
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); }
|
||||
assert.notEqual(result, 0);
|
||||
assert.deepEqual(snapshot(engine, handle).animations, before.animations);
|
||||
engine._web_engine_destroy(handle);
|
||||
engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_ASSIGN_ACTION_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", actionId: animation.id, targetId: animation.targetId, channelPaths: animation.channels.map((channel) => channel.path), keyframesPerChannel: animation.channels.map((channel) => channel.keyframes.length) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00267" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00266/assign-action-local-exact-report.json");
|
||||
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
||||
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} channels=${animation.channels.length} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00267") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00267-operator-asset.browse_containing_blend_file.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00267/browse-containing-desktop-report.json");
|
||||
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-browse-containing-blend-file-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
||||
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
||||
assert.equal(desktop.saveReopen, "EXACT");
|
||||
assert.equal(desktop.poll, false);
|
||||
assert.equal(desktop.operatorStatus, "CANCELLED");
|
||||
assert.equal(desktop.mainMutation, "NONE");
|
||||
assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary });
|
||||
const handle = engine._web_engine_create();
|
||||
open(engine, handle, fs.readFileSync(fixture));
|
||||
const before = snapshot(engine, handle);
|
||||
assert.equal(before.libraryStatus, "AVAILABLE");
|
||||
assert.ok(Array.isArray(before.libraries));
|
||||
assert.equal(before.libraries.length, 1);
|
||||
const library = before.libraries[0];
|
||||
assert.equal(library.id, `library:${desktop.before.name}`);
|
||||
assert.equal(library.name, desktop.before.name);
|
||||
assert.equal(library.sourcePath, "//WebGapBrowseContaining.blend");
|
||||
assert.equal(library.packed, false);
|
||||
assert.equal(library.status, "EXTERNAL_REQUIRED");
|
||||
assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED");
|
||||
assert.deepEqual(library.dependencyIds, []);
|
||||
assert.equal(library.readOnly, true);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
||||
const reopened = engine._web_engine_create();
|
||||
open(engine, reopened, saved);
|
||||
const after = snapshot(engine, reopened);
|
||||
assert.deepEqual(after.libraries, before.libraries);
|
||||
assert.equal(after.libraryStatus, before.libraryStatus);
|
||||
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
||||
const pointer = engine._malloc(malformed.byteLength);
|
||||
let result;
|
||||
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); }
|
||||
assert.notEqual(result, 0);
|
||||
assert.deepEqual(snapshot(engine, handle).libraries, before.libraries);
|
||||
engine._web_engine_destroy(handle);
|
||||
engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_BROWSE_CONTAINING_BLEND_FILE_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00268" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00267/browse-containing-local-exact-report.json");
|
||||
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
||||
process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00268") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00268-operator-asset.bundle_install.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00268/bundle-install-desktop-report.json");
|
||||
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-bundle-install-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
||||
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
||||
assert.equal(desktop.saveReopen, "EXACT");
|
||||
assert.equal(desktop.poll, false);
|
||||
assert.equal(desktop.operatorStatus, "CANCELLED");
|
||||
assert.equal(desktop.mainMutation, "NONE");
|
||||
assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary });
|
||||
const handle = engine._web_engine_create();
|
||||
open(engine, handle, fs.readFileSync(fixture));
|
||||
const before = snapshot(engine, handle);
|
||||
assert.equal(before.libraryStatus, "AVAILABLE");
|
||||
assert.ok(Array.isArray(before.libraries));
|
||||
assert.equal(before.libraries.length, 1);
|
||||
const library = before.libraries[0];
|
||||
assert.equal(library.id, `library:${desktop.before.name}`);
|
||||
assert.equal(library.name, desktop.before.name);
|
||||
assert.equal(library.sourcePath, "//WebGapBundleInstall.blend");
|
||||
assert.equal(library.packed, false);
|
||||
assert.equal(library.status, "EXTERNAL_REQUIRED");
|
||||
assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED");
|
||||
assert.deepEqual(library.dependencyIds, []);
|
||||
assert.equal(library.readOnly, true);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
||||
const reopened = engine._web_engine_create();
|
||||
open(engine, reopened, saved);
|
||||
const after = snapshot(engine, reopened);
|
||||
assert.deepEqual(after.libraries, before.libraries);
|
||||
assert.equal(after.libraryStatus, before.libraryStatus);
|
||||
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
||||
const pointer = engine._malloc(malformed.byteLength);
|
||||
let result;
|
||||
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); }
|
||||
assert.notEqual(result, 0);
|
||||
assert.deepEqual(snapshot(engine, handle).libraries, before.libraries);
|
||||
engine._web_engine_destroy(handle);
|
||||
engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_BUNDLE_INSTALL_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00269" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00268/bundle-install-local-exact-report.json");
|
||||
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
||||
process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00269") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00269-operator-asset.catalog_delete.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00269/catalog-delete-desktop-report.json");
|
||||
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-catalog-delete-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
||||
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
||||
assert.equal(desktop.saveReopen, "EXACT");
|
||||
assert.equal(desktop.poll, false);
|
||||
assert.equal(desktop.operatorStatus, "CANCELLED");
|
||||
assert.equal(desktop.mainMutation, "NONE");
|
||||
assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary });
|
||||
const handle = engine._web_engine_create();
|
||||
open(engine, handle, fs.readFileSync(fixture));
|
||||
const before = snapshot(engine, handle);
|
||||
assert.equal(before.libraryStatus, "AVAILABLE");
|
||||
assert.ok(Array.isArray(before.libraries));
|
||||
assert.equal(before.libraries.length, 1);
|
||||
const library = before.libraries[0];
|
||||
assert.equal(library.id, `library:${desktop.before.name}`);
|
||||
assert.equal(library.name, desktop.before.name);
|
||||
assert.equal(library.sourcePath, "//WebGapCatalogDelete.blend");
|
||||
assert.equal(library.packed, false);
|
||||
assert.equal(library.status, "EXTERNAL_REQUIRED");
|
||||
assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED");
|
||||
assert.deepEqual(library.dependencyIds, []);
|
||||
assert.equal(library.readOnly, true);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
||||
const reopened = engine._web_engine_create();
|
||||
open(engine, reopened, saved);
|
||||
const after = snapshot(engine, reopened);
|
||||
assert.deepEqual(after.libraries, before.libraries);
|
||||
assert.equal(after.libraryStatus, before.libraryStatus);
|
||||
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
||||
const pointer = engine._malloc(malformed.byteLength);
|
||||
let result;
|
||||
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); }
|
||||
assert.notEqual(result, 0);
|
||||
assert.deepEqual(snapshot(engine, handle).libraries, before.libraries);
|
||||
engine._web_engine_destroy(handle);
|
||||
engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_DELETE_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00270" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00269/catalog-delete-local-exact-report.json");
|
||||
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
||||
process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00270") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00270-operator-asset.catalog_new.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00270/catalog-new-desktop-report.json");
|
||||
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-catalog-new-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
||||
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
||||
assert.equal(desktop.saveReopen, "EXACT");
|
||||
assert.equal(desktop.poll, false);
|
||||
assert.equal(desktop.operatorStatus, "CANCELLED");
|
||||
assert.equal(desktop.mainMutation, "NONE");
|
||||
assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary });
|
||||
const handle = engine._web_engine_create();
|
||||
open(engine, handle, fs.readFileSync(fixture));
|
||||
const before = snapshot(engine, handle);
|
||||
assert.equal(before.libraryStatus, "AVAILABLE");
|
||||
assert.ok(Array.isArray(before.libraries));
|
||||
assert.equal(before.libraries.length, 1);
|
||||
const library = before.libraries[0];
|
||||
assert.equal(library.id, `library:${desktop.before.name}`);
|
||||
assert.equal(library.name, desktop.before.name);
|
||||
assert.equal(library.sourcePath, "//WebGapCatalogNew.blend");
|
||||
assert.equal(library.packed, false);
|
||||
assert.equal(library.status, "EXTERNAL_REQUIRED");
|
||||
assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED");
|
||||
assert.deepEqual(library.dependencyIds, []);
|
||||
assert.equal(library.readOnly, true);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
||||
const reopened = engine._web_engine_create();
|
||||
open(engine, reopened, saved);
|
||||
const after = snapshot(engine, reopened);
|
||||
assert.deepEqual(after.libraries, before.libraries);
|
||||
assert.equal(after.libraryStatus, before.libraryStatus);
|
||||
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
||||
const pointer = engine._malloc(malformed.byteLength);
|
||||
let result;
|
||||
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); }
|
||||
assert.notEqual(result, 0);
|
||||
assert.deepEqual(snapshot(engine, handle).libraries, before.libraries);
|
||||
engine._web_engine_destroy(handle);
|
||||
engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_NEW_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00271" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00270/catalog-new-local-exact-report.json");
|
||||
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
||||
process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00271") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00271-operator-asset.catalog_redo.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00271/catalog-redo-desktop-report.json");
|
||||
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-catalog-redo-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
||||
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
||||
assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle);
|
||||
assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0];
|
||||
assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapCatalogRedo.blend"); assert.equal(library.packed, false); assert.equal(library.status, "EXTERNAL_REQUIRED"); assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED"); assert.deepEqual(library.dependencyIds, []); assert.equal(library.readOnly, true);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus);
|
||||
const malformed = new Uint8Array([0x42,0x4c,0x45,0x4e,0x44,0x45,0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_REDO_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00272" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00271/catalog-redo-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00272") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00272-operator-asset.catalog_undo.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00272/catalog-undo-desktop-report.json");
|
||||
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-catalog-undo-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
||||
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
||||
assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0];
|
||||
assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapCatalogUndo.blend"); assert.equal(library.packed, false); assert.equal(library.status, "EXTERNAL_REQUIRED"); assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED"); assert.deepEqual(library.dependencyIds, []); assert.equal(library.readOnly, true);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus);
|
||||
const malformed = new Uint8Array([0x42,0x4c,0x45,0x4e,0x44,0x45,0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_UNDO_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00273" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00272/catalog-undo-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00273") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00273-operator-asset.catalog_undo_push.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00273/catalog-undo-push-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-catalog-undo-push-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0]; assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapCatalogUndoPush.blend"); assert.equal(library.packed, false); assert.equal(library.status, "EXTERNAL_REQUIRED"); assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED"); assert.deepEqual(library.dependencyIds, []); assert.equal(library.readOnly, true);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus); const malformed = new Uint8Array([0x42,0x4c,0x45,0x4e,0x44,0x45,0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_UNDO_PUSH_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00274" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00273/catalog-undo-push-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00274") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00274-operator-asset.catalogs_save.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00274/catalogs-save-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-catalogs-save-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0];
|
||||
assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapCatalogsSave.blend"); assert.equal(library.packed, false); assert.equal(library.status, "EXTERNAL_REQUIRED"); assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED"); assert.deepEqual(library.dependencyIds, []); assert.equal(library.readOnly, true);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus); const malformed = new Uint8Array([0x42,0x4c,0x45,0x4e,0x44,0x45,0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOGS_SAVE_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00275" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00274/catalogs-save-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00275") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00275-operator-asset.clear.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00275/asset-clear-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
||||
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-asset-clear-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
||||
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
||||
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0];
|
||||
assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapAssetClear.blend"); assert.equal(library.packed, false); assert.equal(library.status, "EXTERNAL_REQUIRED"); assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED"); assert.deepEqual(library.dependencyIds, []); assert.equal(library.readOnly, true);
|
||||
const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus); const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
||||
const report = { schemaVersion: 1, task, operation: "ASSET_CLEAR_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00276" };
|
||||
const reportPath = path.join(root, "tests/golden/M16-GAP-00275/asset-clear-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
||||
}
|
||||
if (task === "M16-GAP-00205") {
|
||||
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00205-operator-anim.separate_slots.blend");
|
||||
const desktopPath = path.join(root, "tests/golden/M16-GAP-00205/anim-separate-slots-desktop-report.json");
|
||||
|
||||
@@ -5,11 +5,13 @@ import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const output = path.resolve(process.argv[2] ?? path.join(root, "tests/golden/M15-03A/next-task-plan.json"));
|
||||
const completionArg = process.argv.indexOf("--completion-path");
|
||||
const completionPathArg = completionArg >= 0 ? process.argv[completionArg + 1] : undefined;
|
||||
const mapPath = path.join(root, "tests/golden/M15-02A/blender-parity-map.json");
|
||||
const gapPath = path.join(root, "tests/golden/M15-02B/blender-gap-audit.json");
|
||||
const map = JSON.parse(fs.readFileSync(mapPath, "utf8"));
|
||||
const gaps = JSON.parse(fs.readFileSync(gapPath, "utf8"));
|
||||
const completionPath = path.join(root, "tests/golden/M15-03A/completed-gap-tasks.json");
|
||||
const completionPath = path.resolve(root, completionPathArg ?? "tests/golden/M15-03A/completed-gap-tasks.json");
|
||||
const completedIds = fs.existsSync(completionPath) ? new Set(JSON.parse(fs.readFileSync(completionPath, "utf8"))) : new Set();
|
||||
const byId = new Map(map.entries.map((entry) => [entry.id, entry]));
|
||||
const waveFor = (owner) => {
|
||||
@@ -58,7 +60,7 @@ const plan = {
|
||||
schemaVersion: 1,
|
||||
task: "M15-03A",
|
||||
operation: "BLENDER_NEXT_TASK_PLAN",
|
||||
sources: { parityMap: { path: "tests/golden/M15-02A/blender-parity-map.json", sha256: sha256File(mapPath) }, gapAudit: { path: "tests/golden/M15-02B/blender-gap-audit.json", sha256: sha256File(gapPath) }, completions: { path: "tests/golden/M15-03A/completed-gap-tasks.json", sha256: crypto.createHash("sha256").update(completionBytes).digest("hex") } },
|
||||
sources: { parityMap: { path: "tests/golden/M15-02A/blender-parity-map.json", sha256: sha256File(mapPath) }, gapAudit: { path: "tests/golden/M15-02B/blender-gap-audit.json", sha256: sha256File(gapPath) }, completions: { path: path.relative(root, completionPath).replaceAll(path.sep, "/"), sha256: crypto.createHash("sha256").update(completionBytes).digest("hex") } },
|
||||
summary: { taskCount: tasks.length, active: tasks.filter((task) => task.state === "active").length, pending: tasks.filter((task) => task.state === "pending").length, completed: tasks.filter((task) => task.state === "completed").length, blocked: tasks.filter((task) => task.state === "blocked").length, byWave: Object.fromEntries([...waveCounts.entries()]) },
|
||||
tasks,
|
||||
firstTask: tasks.find((task) => task.state === "active")?.id ?? null,
|
||||
|
||||
@@ -4,8 +4,10 @@ import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const planPath = path.join(root, "tests/golden/M15-03A/next-task-plan.json");
|
||||
const outputDir = path.join(root, "tests/golden/M15-03A");
|
||||
const planArg = process.argv.indexOf("--plan-path");
|
||||
const outputArg = process.argv.indexOf("--output-dir");
|
||||
const planPath = path.resolve(root, planArg >= 0 ? process.argv[planArg + 1] : "tests/golden/M15-03A/next-task-plan.json");
|
||||
const outputDir = path.resolve(root, outputArg >= 0 ? process.argv[outputArg + 1] : "tests/golden/M15-03A");
|
||||
const catalogPath = path.join(outputDir, "task-catalog.jsonl");
|
||||
const indexPath = path.join(outputDir, "task-index.json");
|
||||
const sha256 = (bytes) => crypto.createHash("sha256").update(bytes).digest("hex");
|
||||
|
||||
27
tools/web/generated/M16-GAP-00264.py
Normal file
27
tools/web/generated/M16-GAP-00264.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
IMAGE_NAME = "WebGapAssetDownloadImage"
|
||||
IMAGE_PATH = "//assets/WebGapAssetDownload.png"
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00264.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
image = bpy.data.images.new(IMAGE_NAME, width=1, height=1, alpha=True)
|
||||
image.filepath = IMAGE_PATH
|
||||
image.source = "FILE"
|
||||
image.use_fake_user = True
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
27
tools/web/generated/M16-GAP-00265.py
Normal file
27
tools/web/generated/M16-GAP-00265.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
IMAGE_NAME = "WebGapAssetsDownloadImage"
|
||||
IMAGE_PATH = "//assets/WebGapAssetsDownload.png"
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00265.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
image = bpy.data.images.new(IMAGE_NAME, width=1, height=1, alpha=True)
|
||||
image.filepath = IMAGE_PATH
|
||||
image.source = "FILE"
|
||||
image.use_fake_user = True
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
34
tools/web/generated/M16-GAP-00266.py
Normal file
34
tools/web/generated/M16-GAP-00266.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
ACTION_NAME = "WebGapAssignAction"
|
||||
OBJECT_NAME = "WebGapAssignActionObject"
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00266.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
obj = bpy.data.objects.new(OBJECT_NAME, None)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, x in ((1, 0.0), (5, 2.0)):
|
||||
obj.location.x = x
|
||||
obj.keyframe_insert(data_path="location", index=0, frame=frame)
|
||||
action = obj.animation_data.action
|
||||
action.name = ACTION_NAME
|
||||
action.use_fake_user = True
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
50
tools/web/generated/M16-GAP-00267.py
Normal file
50
tools/web/generated/M16-GAP-00267.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
LIBRARY_NAME = "WebGapBrowseContaining.blend"
|
||||
OBJECT_NAME = "WebGapBrowseContainingObject"
|
||||
|
||||
|
||||
def reset():
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
|
||||
|
||||
def create_source(path):
|
||||
reset()
|
||||
mesh = bpy.data.meshes.new("WebGapBrowseContainingMesh")
|
||||
mesh.from_pydata([(-1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)], [], [(0, 1, 2)])
|
||||
obj = bpy.data.objects.new(OBJECT_NAME, mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(path), check_existing=False, compress=True)
|
||||
|
||||
|
||||
def create_fixture(output):
|
||||
source = output.with_name(LIBRARY_NAME)
|
||||
create_source(source)
|
||||
reset()
|
||||
with bpy.data.libraries.load(str(source), link=True) as (data_from, data_to):
|
||||
if OBJECT_NAME not in data_from.objects:
|
||||
raise RuntimeError("browse-containing source object is missing")
|
||||
data_to.objects = [OBJECT_NAME]
|
||||
linked = data_to.objects[0]
|
||||
if linked is None:
|
||||
raise RuntimeError("browse-containing linked object is missing")
|
||||
bpy.context.scene.collection.objects.link(linked)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output), check_existing=False, compress=True)
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00267.py -- OUTPUT")
|
||||
output = pathlib.Path(arguments[0]).resolve()
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
create_fixture(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
50
tools/web/generated/M16-GAP-00268.py
Normal file
50
tools/web/generated/M16-GAP-00268.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
LIBRARY_NAME = "WebGapBundleInstall.blend"
|
||||
OBJECT_NAME = "WebGapBundleInstallObject"
|
||||
|
||||
|
||||
def reset():
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
|
||||
|
||||
def create_source(path):
|
||||
reset()
|
||||
mesh = bpy.data.meshes.new("WebGapBundleInstallMesh")
|
||||
mesh.from_pydata([(-1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)], [], [(0, 1, 2)])
|
||||
obj = bpy.data.objects.new(OBJECT_NAME, mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(path), check_existing=False, compress=True)
|
||||
|
||||
|
||||
def create_fixture(output):
|
||||
source = output.with_name(LIBRARY_NAME)
|
||||
create_source(source)
|
||||
reset()
|
||||
with bpy.data.libraries.load(str(source), link=True) as (data_from, data_to):
|
||||
if OBJECT_NAME not in data_from.objects:
|
||||
raise RuntimeError("bundle-install source object is missing")
|
||||
data_to.objects = [OBJECT_NAME]
|
||||
linked = data_to.objects[0]
|
||||
if linked is None:
|
||||
raise RuntimeError("bundle-install linked object is missing")
|
||||
bpy.context.scene.collection.objects.link(linked)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output), check_existing=False, compress=True)
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00268.py -- OUTPUT")
|
||||
output = pathlib.Path(arguments[0]).resolve()
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
create_fixture(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
46
tools/web/generated/M16-GAP-00269.py
Normal file
46
tools/web/generated/M16-GAP-00269.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
LIBRARY_NAME = "WebGapCatalogDelete.blend"
|
||||
OBJECT_NAME = "WebGapCatalogDeleteObject"
|
||||
|
||||
|
||||
def reset():
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
|
||||
|
||||
def create_fixture(output):
|
||||
source = output.with_name(LIBRARY_NAME)
|
||||
reset()
|
||||
mesh = bpy.data.meshes.new("WebGapCatalogDeleteMesh")
|
||||
mesh.from_pydata([(-1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)], [], [(0, 1, 2)])
|
||||
obj = bpy.data.objects.new(OBJECT_NAME, mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(source), check_existing=False, compress=True)
|
||||
reset()
|
||||
with bpy.data.libraries.load(str(source), link=True) as (data_from, data_to):
|
||||
if OBJECT_NAME not in data_from.objects:
|
||||
raise RuntimeError("catalog-delete source object is missing")
|
||||
data_to.objects = [OBJECT_NAME]
|
||||
linked = data_to.objects[0]
|
||||
if linked is None:
|
||||
raise RuntimeError("catalog-delete linked object is missing")
|
||||
bpy.context.scene.collection.objects.link(linked)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output), check_existing=False, compress=True)
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00269.py -- OUTPUT")
|
||||
output = pathlib.Path(arguments[0]).resolve()
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
create_fixture(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
37
tools/web/generated/M16-GAP-00270.py
Normal file
37
tools/web/generated/M16-GAP-00270.py
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
LIBRARY_NAME = "WebGapCatalogNew.blend"
|
||||
OBJECT_NAME = "WebGapCatalogNewObject"
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00270.py -- OUTPUT")
|
||||
output = pathlib.Path(arguments[0]).resolve()
|
||||
source = output.with_name(LIBRARY_NAME)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapCatalogNewMesh")
|
||||
mesh.from_pydata([(-1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)], [], [(0, 1, 2)])
|
||||
obj = bpy.data.objects.new(OBJECT_NAME, mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(source), check_existing=False, compress=True)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
with bpy.data.libraries.load(str(source), link=True) as (data_from, data_to):
|
||||
if OBJECT_NAME not in data_from.objects:
|
||||
raise RuntimeError("catalog-new source object is missing")
|
||||
data_to.objects = [OBJECT_NAME]
|
||||
linked = data_to.objects[0]
|
||||
if linked is None:
|
||||
raise RuntimeError("catalog-new linked object is missing")
|
||||
bpy.context.scene.collection.objects.link(linked)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
20
tools/web/generated/M16-GAP-00271.py
Normal file
20
tools/web/generated/M16-GAP-00271.py
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib, sys, bpy
|
||||
|
||||
LIBRARY_NAME = "WebGapCatalogRedo.blend"
|
||||
OBJECT_NAME = "WebGapCatalogRedoObject"
|
||||
|
||||
def main():
|
||||
args = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(args) != 1: raise SystemExit("usage")
|
||||
output = pathlib.Path(args[0]).resolve(); source = output.with_name(LIBRARY_NAME)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapCatalogRedoMesh"); mesh.from_pydata([(-1,0,0),(1,0,0),(0,1,0)], [], [(0,1,2)])
|
||||
obj = bpy.data.objects.new(OBJECT_NAME, mesh); bpy.context.scene.collection.objects.link(obj)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(source), check_existing=False, compress=True)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
with bpy.data.libraries.load(str(source), link=True) as (data_from, data_to): data_to.objects = [OBJECT_NAME]
|
||||
bpy.context.scene.collection.objects.link(data_to.objects[0])
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output), check_existing=False, compress=True)
|
||||
|
||||
if __name__ == "__main__": main()
|
||||
11
tools/web/generated/M16-GAP-00272.py
Normal file
11
tools/web/generated/M16-GAP-00272.py
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib, sys, bpy
|
||||
LIBRARY_NAME="WebGapCatalogUndo.blend"; OBJECT_NAME="WebGapCatalogUndoObject"
|
||||
def main():
|
||||
args=sys.argv[sys.argv.index("--")+1:]
|
||||
if len(args)!=1: raise SystemExit("usage")
|
||||
output=pathlib.Path(args[0]).resolve(); source=output.with_name(LIBRARY_NAME); bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh=bpy.data.meshes.new("WebGapCatalogUndoMesh"); mesh.from_pydata([(-1,0,0),(1,0,0),(0,1,0)],[],[(0,1,2)]); obj=bpy.data.objects.new(OBJECT_NAME,mesh); bpy.context.scene.collection.objects.link(obj); bpy.ops.wm.save_as_mainfile(filepath=str(source),check_existing=False,compress=True); bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
with bpy.data.libraries.load(str(source),link=True) as (data_from,data_to): data_to.objects=[OBJECT_NAME]
|
||||
bpy.context.scene.collection.objects.link(data_to.objects[0]); bpy.ops.wm.save_as_mainfile(filepath=str(output),check_existing=False,compress=True)
|
||||
if __name__=="__main__": main()
|
||||
11
tools/web/generated/M16-GAP-00273.py
Normal file
11
tools/web/generated/M16-GAP-00273.py
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib, sys, bpy
|
||||
LIBRARY_NAME="WebGapCatalogUndoPush.blend"; OBJECT_NAME="WebGapCatalogUndoPushObject"
|
||||
def main():
|
||||
args=sys.argv[sys.argv.index("--")+1:]
|
||||
if len(args)!=1: raise SystemExit("usage")
|
||||
output=pathlib.Path(args[0]).resolve(); source=output.with_name(LIBRARY_NAME); bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh=bpy.data.meshes.new("WebGapCatalogUndoPushMesh"); mesh.from_pydata([(-1,0,0),(1,0,0),(0,1,0)],[],[(0,1,2)]); obj=bpy.data.objects.new(OBJECT_NAME,mesh); bpy.context.scene.collection.objects.link(obj); bpy.ops.wm.save_as_mainfile(filepath=str(source),check_existing=False,compress=True); bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
with bpy.data.libraries.load(str(source),link=True) as (data_from,data_to): data_to.objects=[OBJECT_NAME]
|
||||
bpy.context.scene.collection.objects.link(data_to.objects[0]); bpy.ops.wm.save_as_mainfile(filepath=str(output),check_existing=False,compress=True)
|
||||
if __name__=="__main__": main()
|
||||
37
tools/web/generated/M16-GAP-00274.py
Normal file
37
tools/web/generated/M16-GAP-00274.py
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
LIBRARY_NAME = "WebGapCatalogsSave.blend"
|
||||
OBJECT_NAME = "WebGapCatalogsSaveObject"
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00274.py -- OUTPUT")
|
||||
output = pathlib.Path(arguments[0]).resolve()
|
||||
source = output.with_name(LIBRARY_NAME)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapCatalogsSaveMesh")
|
||||
mesh.from_pydata([(-1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)], [], [(0, 1, 2)])
|
||||
obj = bpy.data.objects.new(OBJECT_NAME, mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(source), check_existing=False, compress=True)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
with bpy.data.libraries.load(str(source), link=True) as (data_from, data_to):
|
||||
if OBJECT_NAME not in data_from.objects:
|
||||
raise RuntimeError("catalogs-save source object is missing")
|
||||
data_to.objects = [OBJECT_NAME]
|
||||
linked = data_to.objects[0]
|
||||
if linked is None:
|
||||
raise RuntimeError("catalogs-save linked object is missing")
|
||||
bpy.context.scene.collection.objects.link(linked)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
37
tools/web/generated/M16-GAP-00275.py
Normal file
37
tools/web/generated/M16-GAP-00275.py
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
LIBRARY_NAME = "WebGapAssetClear.blend"
|
||||
OBJECT_NAME = "WebGapAssetClearObject"
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00275.py -- OUTPUT")
|
||||
output = pathlib.Path(arguments[0]).resolve()
|
||||
source = output.with_name(LIBRARY_NAME)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAssetClearMesh")
|
||||
mesh.from_pydata([(-1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)], [], [(0, 1, 2)])
|
||||
obj = bpy.data.objects.new(OBJECT_NAME, mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(source), check_existing=False, compress=True)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
with bpy.data.libraries.load(str(source), link=True) as (data_from, data_to):
|
||||
if OBJECT_NAME not in data_from.objects:
|
||||
raise RuntimeError("asset-clear source object is missing")
|
||||
data_to.objects = [OBJECT_NAME]
|
||||
linked = data_to.objects[0]
|
||||
if linked is None:
|
||||
raise RuntimeError("asset-clear linked object is missing")
|
||||
bpy.context.scene.collection.objects.link(linked)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(output), check_existing=False, compress=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user