unitytk 0.2.2__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. {unitytk-0.2.2/unitytk.egg-info → unitytk-0.4.0}/PKG-INFO +2 -2
  2. {unitytk-0.2.2 → unitytk-0.4.0}/pyproject.toml +7 -2
  3. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_articulated_rig_runtime.py +18 -19
  4. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_asset_delivery.py +5 -8
  5. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_audio_events_integration.py +25 -21
  6. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_base_test.py +140 -6
  7. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_blender_render_effects_e2e.py +2 -6
  8. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_blender_shadow_plane_integration.py +4 -8
  9. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_blender_shot_metadata_integration.py +5 -11
  10. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_clip_name_contract_integration.py +6 -10
  11. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_controller_runtime_integration.py +2 -6
  12. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_deploy_templates.py +249 -5
  13. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_embedded_dependencies_integration.py +7 -9
  14. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_emissive_groups_integration.py +2 -6
  15. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_fade_window_preservation.py +2 -7
  16. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_keyed_visibility.py +4 -12
  17. unitytk-0.4.0/test/test_lightmap_metadata_integration.py +2473 -0
  18. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_maya_surface_materials_integration.py +124 -69
  19. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_multi_section_audio_integration.py +2 -15
  20. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_render_effects_e2e.py +4 -4
  21. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_render_opacity_controller.py +2 -7
  22. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_render_opacity_e2e.py +6 -8
  23. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_render_opacity_integration.py +2 -8
  24. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_run_tests.py +17 -0
  25. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_shadow_plane_integration.py +6 -10
  26. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_shadow_plane_runtime_integration.py +51 -16
  27. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_shared_material_standalone.py +4 -12
  28. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_shot_metadata_integration.py +100 -22
  29. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_shots_audio_sidebyside_integration.py +16 -20
  30. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_standalone_enduser.py +5 -12
  31. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_temp_projects.py +143 -8
  32. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/__init__.py +1 -1
  33. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/ArticulatedRigController.cs +81 -249
  34. unitytk-0.4.0/unitytk/templates/ArticulatedRig/ArticulationRecord.g.cs +330 -0
  35. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/AudioEvent/AudioEventController.cs +112 -128
  36. unitytk-0.4.0/unitytk/templates/AudioEvent/AudioRecord.g.cs +260 -0
  37. unitytk-0.4.0/unitytk/templates/AudioEvent/AudioRecord.g.cs.meta +11 -0
  38. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/EmissiveGroup/EmissiveGroupController.cs +28 -56
  39. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/EmissiveGroup/EmissiveGroupsLit.shader +36 -1
  40. unitytk-0.4.0/unitytk/templates/EmissiveGroup/EmissiveGroupsRecord.g.cs +268 -0
  41. unitytk-0.4.0/unitytk/templates/EmissiveGroup/EmissiveGroupsRecord.g.cs.meta +11 -0
  42. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableCubeUV.hlsl +119 -0
  43. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableCubeUV.hlsl.meta +7 -0
  44. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableLit.shader +391 -0
  45. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableLit.shader.meta +9 -0
  46. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverablePmrem.cs +307 -0
  47. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverablePmrem.cs.meta +11 -0
  48. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableShading.cs +570 -0
  49. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableShading.cs.meta +11 -0
  50. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableToneMap.shader +65 -0
  51. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableToneMap.shader.meta +9 -0
  52. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableToneMapFeature.cs +53 -0
  53. unitytk-0.4.0/unitytk/templates/LightmapMetadata/DeliverableToneMapFeature.cs.meta +11 -0
  54. unitytk-0.4.0/unitytk/templates/LightmapMetadata/HandoffBlock.g.cs +303 -0
  55. unitytk-0.4.0/unitytk/templates/LightmapMetadata/HandoffBlock.g.cs.meta +11 -0
  56. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/LightmapMetadata/LightmapMetadataController.cs +678 -257
  57. unitytk-0.4.0/unitytk/templates/LightmapMetadata/LightmapRecord.g.cs +466 -0
  58. unitytk-0.4.0/unitytk/templates/LightmapMetadata/LightmapRecord.g.cs.meta +11 -0
  59. unitytk-0.4.0/unitytk/templates/LightmapMetadata/MapAbsence.g.cs +18 -0
  60. unitytk-0.4.0/unitytk/templates/LightmapMetadata/MapAbsence.g.cs.meta +11 -0
  61. unitytk-0.4.0/unitytk/templates/LightmapMetadata/MaterialRecord.cs +81 -0
  62. unitytk-0.4.0/unitytk/templates/LightmapMetadata/MaterialRecord.cs.meta +11 -0
  63. unitytk-0.4.0/unitytk/templates/LightmapMetadata/MaterialRecord.g.cs +291 -0
  64. unitytk-0.4.0/unitytk/templates/LightmapMetadata/MaterialRecord.g.cs.meta +11 -0
  65. unitytk-0.4.0/unitytk/templates/LightmapMetadata/RenderingPolicy.g.cs +50 -0
  66. unitytk-0.4.0/unitytk/templates/LightmapMetadata/RenderingPolicy.g.cs.meta +11 -0
  67. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/M3trik.Unitytk.asmdef +8 -1
  68. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/MayaSurface/MayaSurfaceMaterialImporter.cs +79 -28
  69. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/README.md +97 -50
  70. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShadowPlane/ShadowPlane.shader +12 -5
  71. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShadowPlane/ShadowPlaneController.cs +128 -127
  72. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShadowPlane/ShadowPlaneHorizon.hlsl +4 -2
  73. unitytk-0.4.0/unitytk/templates/ShadowPlane/ShadowRecord.g.cs +419 -0
  74. unitytk-0.4.0/unitytk/templates/ShadowPlane/ShadowRecord.g.cs.meta +11 -0
  75. unitytk-0.4.0/unitytk/templates/ShotMetadata/AnimationRecord.g.cs +272 -0
  76. unitytk-0.4.0/unitytk/templates/ShotMetadata/AnimationRecord.g.cs.meta +11 -0
  77. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShotMetadata/ShotMetadataController.cs +56 -55
  78. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/UnitytkSettings.cs +411 -31
  79. {unitytk-0.2.2 → unitytk-0.4.0/unitytk.egg-info}/PKG-INFO +2 -2
  80. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk.egg-info/SOURCES.txt +32 -0
  81. unitytk-0.4.0/unitytk.egg-info/requires.txt +1 -0
  82. unitytk-0.2.2/test/test_lightmap_metadata_integration.py +0 -1321
  83. unitytk-0.2.2/unitytk/templates/ArticulatedRig/ArticulationRecord.g.cs +0 -202
  84. unitytk-0.2.2/unitytk.egg-info/requires.txt +0 -1
  85. {unitytk-0.2.2 → unitytk-0.4.0}/LICENSE +0 -0
  86. {unitytk-0.2.2 → unitytk-0.4.0}/MANIFEST.in +0 -0
  87. {unitytk-0.2.2 → unitytk-0.4.0}/docs/README.md +0 -0
  88. {unitytk-0.2.2 → unitytk-0.4.0}/setup.cfg +0 -0
  89. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_bool_to_fade_curve.py +0 -0
  90. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_launcher.py +0 -0
  91. {unitytk-0.2.2 → unitytk-0.4.0}/test/test_scene_builder.py +0 -0
  92. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/asset_delivery.py +0 -0
  93. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/launcher.py +0 -0
  94. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/scene_builder.py +0 -0
  95. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/template_deployer.py +0 -0
  96. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/ArticulatedRigController.cs.meta +0 -0
  97. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/ArticulatedRigPointerGrab.cs +0 -0
  98. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/ArticulatedRigPointerGrab.cs.meta +0 -0
  99. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/ArticulationRecord.g.cs.meta +0 -0
  100. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/XR/ArticulatedRigXRGrab.cs +0 -0
  101. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/XR/ArticulatedRigXRGrab.cs.meta +0 -0
  102. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/XR/ArticulatedRigXRSetup.cs +0 -0
  103. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/XR/ArticulatedRigXRSetup.cs.meta +0 -0
  104. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/XR/M3trik.Unitytk.XR.asmdef +0 -0
  105. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/XR/M3trik.Unitytk.XR.asmdef.meta +0 -0
  106. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig/XR.meta +0 -0
  107. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ArticulatedRig.meta +0 -0
  108. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/AudioEvent/AudioEventController.cs.meta +0 -0
  109. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/AudioEvent.meta +0 -0
  110. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/EmissiveGroup/EmissiveGroupController.cs.meta +0 -0
  111. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/EmissiveGroup/EmissiveGroups.hlsl +0 -0
  112. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/EmissiveGroup/EmissiveGroups.hlsl.meta +0 -0
  113. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/EmissiveGroup/EmissiveGroupsLit.shader.meta +0 -0
  114. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/EmissiveGroup.meta +0 -0
  115. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/LightmapMetadata/LightmapMetadataController.cs.meta +0 -0
  116. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/LightmapMetadata.meta +0 -0
  117. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/M3trik.Unitytk.asmdef.meta +0 -0
  118. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/MayaSurface/MayaSurfaceMaterialImporter.cs.meta +0 -0
  119. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/MayaSurface.meta +0 -0
  120. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/README.md.meta +0 -0
  121. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/RenderEffects/RenderEffectsController.cs +0 -0
  122. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/RenderEffects/RenderEffectsController.cs.meta +0 -0
  123. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/RenderEffects.meta +0 -0
  124. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShadowPlane/ShadowPlane.shader.meta +0 -0
  125. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShadowPlane/ShadowPlaneController.cs.meta +0 -0
  126. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShadowPlane/ShadowPlaneHorizon.hlsl.meta +0 -0
  127. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShadowPlane.meta +0 -0
  128. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShotMetadata/ShotMetadataController.cs.meta +0 -0
  129. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/ShotMetadata.meta +0 -0
  130. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/UnitytkSettings.cs.meta +0 -0
  131. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk/templates/__init__.py +0 -0
  132. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk.egg-info/dependency_links.txt +0 -0
  133. {unitytk-0.2.2 → unitytk-0.4.0}/unitytk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitytk
3
- Version: 0.2.2
3
+ Version: 0.4.0
4
4
  Summary: A modular Python toolkit for Unity interaction.
5
5
  Author-email: Ryan Simpson <m3trik@outlook.com>
6
6
  License: MIT
@@ -13,7 +13,7 @@ Classifier: Operating System :: POSIX :: Linux
13
13
  Requires-Python: >=3.9
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
- Requires-Dist: pythontk>=0.13.1
16
+ Requires-Dist: pythontk>=0.17.0
17
17
  Dynamic: license-file
18
18
 
19
19
  [![Tests](https://img.shields.io/badge/Tests-146%20passed-brightgreen.svg)](../test/)
@@ -17,7 +17,12 @@ classifiers = [
17
17
  ]
18
18
  # The floor is HAND-RATCHETED: unitytk is off-chain from m3trik/push.ps1, so no
19
19
  # release cascade bumps it.
20
- # pythontk 0.13.1, the release this one ships and was tested with: an FBX
20
+ # pythontk 0.17.0, the release this one ships and was tested with: the
21
+ # templates read the scene records by their renamed keys alone (`animation`,
22
+ # `audio`, `lightmaps`, `shadows`, `materials`, `handoff` v3 -- was
23
+ # `shot_metadata`, `audio_manifest`, `lightmap_metadata`, `shadow_metadata`),
24
+ # and only pythontk 0.17.0 writes them; an older one's FBX imports with no
25
+ # shots, audio, lightmaps or shadows. Before it, pythontk 0.13.1: an FBX
21
26
  # deliverable carries the files its records name through
22
27
  # `FbxMedia.embed_dependencies` (new in 0.13.1; on 0.13.0 the copy deliverer
23
28
  # ships the FBX without its maps), and the suite holds the templates to its
@@ -27,7 +32,7 @@ classifiers = [
27
32
  # needs 0.8.84: it scans through `AppLauncher.scan_install_dirs` (new in
28
33
  # 0.8.72) and needs its pattern order to be a priority (0.8.84), so the Hub's
29
34
  # chosen install location wins over the default.
30
- dependencies = ["pythontk>=0.13.1"]
35
+ dependencies = ["pythontk>=0.17.0"]
31
36
 
32
37
  [project.urls]
33
38
  Homepage = "https://github.com/m3trik/unitytk"
@@ -857,7 +857,9 @@ class LiveExport:
857
857
 
858
858
 
859
859
  def _invalid_rigs():
860
- """Rigs the reference refuses (ValueError); the port must refuse them too."""
860
+ """Rigs the reference refuses (ValueError); the port must refuse them too.
861
+ Each is a rig as the record states it -- every key its shape requires --
862
+ so each is refused for its own fault, not a key left out."""
861
863
  base = {"name": "bad", "joints": [], "grab": []}
862
864
 
863
865
  def rig(*joints):
@@ -865,6 +867,9 @@ def _invalid_rigs():
865
867
  out["joints"] = list(joints)
866
868
  return out
867
869
 
870
+ def channel(name, lo=None, hi=None):
871
+ return {"channel": name, "min": lo, "max": hi, "weight": 1.0}
872
+
868
873
  ok = {
869
874
  "name": "a",
870
875
  "parent": None,
@@ -876,10 +881,10 @@ def _invalid_rigs():
876
881
  return [
877
882
  rig(ok, dict(ok, name="b", parent=1)), # parent not earlier
878
883
  rig(dict(ok, rotate_order="xxy")), # unknown order
879
- rig(dict(ok, channels=[{"channel": "sx"}])), # unknown channel
880
- rig(dict(ok, channels=[{"channel": "rx"}, {"channel": "rx"}])), # twice
881
- rig(dict(ok, channels=[{"channel": "rz", "min": 10, "max": -10}])), # reversed
882
- rig(ok, dict(ok, name="b", parent=0, channels=[{"channel": "tx", "min": None, "max": 3}])), # valid
884
+ rig(dict(ok, channels=[channel("sx")])), # unknown channel
885
+ rig(dict(ok, channels=[channel("rx"), channel("rx")])), # twice
886
+ rig(dict(ok, channels=[channel("rz", lo=10, hi=-10)])), # reversed
887
+ rig(ok, dict(ok, name="b", parent=0, channels=[channel("tx", hi=3)])), # valid
883
888
  ] # fmt: skip
884
889
 
885
890
 
@@ -915,7 +920,7 @@ public class ArticulatedRigVerifier
915
920
  static M.Frame[] Frames(object o) => L(o).Select(f => new M.Frame(Vec(L(f)[0]), Qt(L(f)[1]))).ToArray();
916
921
 
917
922
  // ---- JSON out ---------------------------------------------------------
918
- static string N(double v) => ArticulationJson.Number(v);
923
+ static string N(double v) => UnitytkJson.Number(v);
919
924
  static string B(bool b) => b ? "true" : "false";
920
925
  static string S(string s) => "\"" + (s ?? "").Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\r", "") + "\"";
921
926
  static string A(double[] a) => a == null ? "null" : "[" + string.Join(",", a.Select(N)) + "]";
@@ -934,7 +939,7 @@ public class ArticulatedRigVerifier
934
939
  try
935
940
  {
936
941
  AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
937
- inputs = O(ArticulationJson.Parse(File.ReadAllText(INPUTS)));
942
+ inputs = O(UnitytkJson.Parse(File.ReadAllText(INPUTS)));
938
943
  }
939
944
  catch (Exception e) { Put("inputs_error", S(e.ToString())); }
940
945
  if (inputs != null)
@@ -1671,7 +1676,7 @@ def _run_unity(test, temp_dir, execute_method, prepare, timeout=600):
1671
1676
  verifier; returns the parsed results (or skips on the license wall)."""
1672
1677
  editor = os.environ.get("UNITYTK_TEST_EDITOR")
1673
1678
  launcher = UnityLauncher(editor) if editor else UnityLauncher()
1674
- project_path = os.path.join(temp_dir, "UnityProject")
1679
+ project_path = TempProjects.project(temp_dir)
1675
1680
  if not launcher.create_project(project_path, batch_mode=True):
1676
1681
  test.skipTest(UnityEnv.project_creation_reason())
1677
1682
  results_path = os.path.join(temp_dir, "unity_results.json")
@@ -1720,7 +1725,7 @@ class TestArticulatedRigRuntime(unittest.TestCase):
1720
1725
 
1721
1726
  @classmethod
1722
1727
  def setUpClass(cls):
1723
- cls.temp_dir = TempProjects.allocate("articulated_rig_runtime")
1728
+ cls.temp_dir = TempProjects.allocate("articulated_rig_runtime", cls)
1724
1729
  cls.unity = None
1725
1730
  cls.doc = ptk.ArticulationConformance.cases(seed=SEED, per_rig=PER_RIG)
1726
1731
  cls.scenario = ControllerScenario(cls.doc)
@@ -1756,10 +1761,6 @@ class TestArticulatedRigRuntime(unittest.TestCase):
1756
1761
  }
1757
1762
  logger.info(f"Temp dir: {cls.temp_dir}")
1758
1763
 
1759
- @classmethod
1760
- def tearDownClass(cls):
1761
- logger.info(f"Results preserved in: {cls.temp_dir}")
1762
-
1763
1764
  def _results(self):
1764
1765
  unity = self.__class__.unity
1765
1766
  if not unity:
@@ -1865,9 +1866,7 @@ class TestArticulatedRigRuntime(unittest.TestCase):
1865
1866
  for name in _bundle_assets()
1866
1867
  }
1867
1868
  self.assertEqual(got, expected, "Unity named a deployed asset differently")
1868
- package = TemplateDeployer.package_dir(
1869
- os.path.join(self.temp_dir, "UnityProject")
1870
- )
1869
+ package = TemplateDeployer.package_dir(TempProjects.project(self.temp_dir))
1871
1870
  rewritten = []
1872
1871
  for name in _bundle_assets():
1873
1872
  path = os.path.join(package, *name.split("/")) + ".meta"
@@ -2703,7 +2702,7 @@ public class ArticulatedRigXRVerifier
2703
2702
  const string INPUTS = @"__INPUTS_PATH__";
2704
2703
 
2705
2704
  static string B(bool b) => b ? "true" : "false";
2706
- static string N(double v) => ArticulationJson.Number(v);
2705
+ static string N(double v) => UnitytkJson.Number(v);
2707
2706
  static IList<object> L(object o) => (IList<object>)o;
2708
2707
  static float F(object o) => (float)(double)o;
2709
2708
 
@@ -2712,7 +2711,7 @@ public class ArticulatedRigXRVerifier
2712
2711
  var sb = new StringBuilder("{\n");
2713
2712
  try
2714
2713
  {
2715
- var inputs = (IDictionary<string, object>)ArticulationJson.Parse(File.ReadAllText(INPUTS));
2714
+ var inputs = (IDictionary<string, object>)UnitytkJson.Parse(File.ReadAllText(INPUTS));
2716
2715
  var names = L(inputs["names"]);
2717
2716
  var parents = L(inputs["parents"]);
2718
2717
  var locals = L(inputs["locals"]);
@@ -2778,7 +2777,7 @@ class TestArticulatedRigXRI(unittest.TestCase):
2778
2777
 
2779
2778
  @classmethod
2780
2779
  def setUpClass(cls):
2781
- cls.temp_dir = TempProjects.allocate("articulated_rig_xri")
2780
+ cls.temp_dir = TempProjects.allocate("articulated_rig_xri", cls)
2782
2781
  cls.unity = None
2783
2782
 
2784
2783
  @EnvSkip.unless(_unity_available(), "no Unity editor found")
@@ -394,16 +394,13 @@ class SelfContainedDeliveryTest(unittest.TestCase):
394
394
  decision of 2026-10-05: every dependency embedded, zero sidecar files)."""
395
395
 
396
396
  RECORDS = {
397
- "lightmap_metadata": {
398
- "version": 1,
397
+ "lightmaps": {
398
+ "version": 2,
399
+ "unit_scale": 0.01,
399
400
  "objects": [{"name": "Crate", "map": "Crate_Lightmap.exr", "uvIndex": 1}],
400
- "probe": {
401
- "map": "Room_Probe.exr",
402
- "position": [0, 0, 0],
403
- "unit_scale": 0.01,
404
- },
401
+ "probe": {"map": "Room_Probe.exr", "position": [0, 0, 0], "box": None},
405
402
  },
406
- "shadow_metadata": {
403
+ "shadows": {
407
404
  "version": 2,
408
405
  "planes": [
409
406
  {
@@ -6,13 +6,13 @@ Validates the new single-carrier audio events pipeline end-to-end:
6
6
  1. Maya (mayapy): Author audio tracks on the canonical ``data_internal``
7
7
  carrier via :class:`mayatk.audio_utils.AudioUtils`, then call
8
8
  :meth:`mayatk.audio_utils.audio_clips.AudioClips.prepare_for_export`
9
- to bake ``audio_manifest`` as a plain string channel on ``data_export``
9
+ to bake the ``audio`` record as a plain string channel on ``data_export``
10
10
  (the authoring state stays keyed on ``data_internal`` and never exports).
11
11
  Animate a cube so the FBX contains at least one ``AnimationClip``
12
12
  for the audio events to bind to. Export FBX.
13
13
  2. Unity (batchmode): Import FBX with the real
14
14
  ``AudioEventController.cs`` deployed. Verify:
15
- a. the ``audio_manifest`` string arrives on the ``data_export``
15
+ a. the ``audio`` record arrives on the ``data_export``
16
16
  GameObject (raw user-property),
17
17
  b. the importer auto-attaches an ``AudioEventController`` to the
18
18
  prefab **root** (and explicitly NOT to the ``data_export``
@@ -414,17 +414,13 @@ class TestAudioEventsIntegration(unittest.TestCase):
414
414
 
415
415
  @classmethod
416
416
  def setUpClass(cls):
417
- cls.temp_dir = TempProjects.allocate("audio_events_integ")
417
+ cls.temp_dir = TempProjects.allocate("audio_events_integ", cls)
418
418
  cls.fbx_path = None
419
419
  cls.maya_results = None
420
420
  cls.unity_results = None
421
421
  cls.postproc = None
422
422
  logger.info(f"Temp dir: {cls.temp_dir}")
423
423
 
424
- @classmethod
425
- def tearDownClass(cls):
426
- logger.info(f"Results preserved in: {cls.temp_dir}")
427
-
428
424
  # ------------------------------------------------------------------
429
425
  # Step 1: Maya export via AudioClips.prepare_for_export
430
426
  # ------------------------------------------------------------------
@@ -469,7 +465,7 @@ class TestAudioEventsIntegration(unittest.TestCase):
469
465
  self.__class__.fbx_path = r["fbx_path"]
470
466
  self.assertTrue(r["fbx_exists"])
471
467
  self.assertEqual(r["carrier"], "data_internal")
472
- self.assertEqual(r["manifest_attr"], "audio_manifest")
468
+ self.assertEqual(r["manifest_attr"], "audio")
473
469
  # v2 manifest: versioned JSON. No shots in this scene, so events are
474
470
  # unscoped (clip "") with frames RELATIVE to Maya's playback min
475
471
  # (= 1 here) — Unity's clip clock maps Maya frame `min` to time 0.
@@ -499,7 +495,7 @@ class TestAudioEventsIntegration(unittest.TestCase):
499
495
  # as the sibling integration tests.
500
496
  editor = os.environ.get("UNITYTK_TEST_EDITOR")
501
497
  launcher = UnityLauncher(editor) if editor else UnityLauncher()
502
- project_path = os.path.join(self.temp_dir, "UnityProject")
498
+ project_path = TempProjects.project(self.temp_dir)
503
499
 
504
500
  logger.info(f"Creating Unity project: {project_path}")
505
501
  if not launcher.create_project(project_path, batch_mode=True):
@@ -623,11 +619,11 @@ class TestAudioEventsIntegration(unittest.TestCase):
623
619
  f"in postprocessor results: {list(postproc)}",
624
620
  )
625
621
  self.assertIn(
626
- "audio_manifest",
622
+ "audio",
627
623
  props,
628
- f"audio_manifest user-property missing on {name}; saw: {list(props)}",
624
+ f"audio record missing on {name}; saw: {list(props)}",
629
625
  )
630
- manifest = props["audio_manifest"]
626
+ manifest = props["audio"]
631
627
  for label in ("footstep", "jump", "land"):
632
628
  self.assertIn(label, manifest)
633
629
 
@@ -648,7 +644,7 @@ class TestAudioEventsIntegration(unittest.TestCase):
648
644
 
649
645
  # (c) the real AudioEventImporter auto-attached the controller to
650
646
  # the prefab ROOT (where the Animator will live), NOT to the GO
651
- # carrying audio_manifest — this is the runtime-dispatch invariant.
647
+ # carrying the audio record — this is the runtime-dispatch invariant.
652
648
  self.assertTrue(
653
649
  unity.get("controller_on_root"),
654
650
  "AudioEventController should live on the prefab root so "
@@ -739,7 +735,7 @@ class TestAudioEventsIntegration(unittest.TestCase):
739
735
 
740
736
 
741
737
  # ======================================================================
742
- # Carrier precedence: one audio_manifest per model, the scene's own first
738
+ # Carrier precedence: one audio record per model, the scene's own first
743
739
  # ======================================================================
744
740
 
745
741
  CARRIER_VERIFIER_CS = r"""
@@ -784,7 +780,7 @@ public class AudioCarrierVerifier
784
780
 
785
781
 
786
782
  class TestAudioCarrierPrecedence(unittest.TestCase):
787
- """An FBX carrying ``audio_manifest`` on several carriers plays the
783
+ """An FBX carrying the ``audio`` record on several carriers plays the
788
784
  scene's own only.
789
785
 
790
786
  An assembly ships a stray ``data_export1`` (and a module's
@@ -805,23 +801,31 @@ class TestAudioCarrierPrecedence(unittest.TestCase):
805
801
 
806
802
  @classmethod
807
803
  def setUpClass(cls):
808
- cls.temp_dir = TempProjects.allocate("audio_carrier_precedence")
804
+ cls.temp_dir = TempProjects.allocate("carrier_precedence", cls)
809
805
 
810
806
  @BlenderEnv.skip_unless_available()
811
807
  @EnvSkip.unless(_unity_available(), "no Unity editor found")
812
808
  def test_the_scenes_own_carrier_wins(self):
813
- own = {"version": 2, "events": [{"clip": "", "frame": 0, "name": "footstep"}]}
814
- stray = {"version": 2, "events": [{"clip": "", "frame": 3, "name": "door"}]}
809
+ own = {
810
+ "version": 2,
811
+ "fps": 24.0,
812
+ "events": [{"clip": "", "frame": 0, "name": "footstep"}],
813
+ }
814
+ stray = {
815
+ "version": 2,
816
+ "fps": 24.0,
817
+ "events": [{"clip": "", "frame": 3, "name": "door"}],
818
+ }
815
819
  fixture = CarrierFbx.build(
816
820
  os.path.join(self.temp_dir, "assembly.fbx"),
817
821
  {
818
- "data_export": {"audio_manifest": own},
819
- "data_export1": {"audio_manifest": stray},
822
+ "data_export": {"audio": own},
823
+ "data_export1": {"audio": stray},
820
824
  },
821
825
  )
822
826
 
823
827
  launcher = UnityLauncher()
824
- project = os.path.join(self.temp_dir, "UnityProject")
828
+ project = TempProjects.project(self.temp_dir)
825
829
  if not launcher.create_project(project, batch_mode=True):
826
830
  self.skipTest(UnityEnv.project_creation_reason())
827
831
  delivered = os.path.join(project, "Assets", "Delivered")
@@ -42,6 +42,7 @@ from base_test import (
42
42
  MayaEnv,
43
43
  SuiteOptions,
44
44
  TemplateSources,
45
+ TempProjects,
45
46
  TestAssets,
46
47
  UnityEnv,
47
48
  UserPropsProbe,
@@ -486,6 +487,83 @@ class UnityEnvTest(unittest.TestCase):
486
487
  self.assertEqual(result.errors, [])
487
488
  self.assertEqual(result.testsRun, 3)
488
489
 
490
+ def test_make_urp_fails_fast_on_a_project_too_deep_for_the_editor(self):
491
+ """The editor is not long-path aware: past MAX_PATH its package imports
492
+ fail, URP's Autodesk Interactive graph resolves to nothing, and the
493
+ material suite read the fallback pick as a product bug (2026-10-19)."""
494
+ deep = "C:\\" + "d" * (260 - UnityEnv.URP_IMPORT_DEPTH - 3) # one past the limit
495
+
496
+ class Case(unittest.TestCase):
497
+ def test_deep(self):
498
+ UnityEnv.make_urp(self, None, deep, "unused.log")
499
+
500
+ result = _run_case(Case)
501
+ self.assertEqual(result.errors, [])
502
+ self.assertEqual(len(result.failures), 1)
503
+ self.assertIn("UNITYTK_TEMP_PROJECTS", result.failures[0][1])
504
+
505
+ def test_the_depth_gate_covers_every_shader_graph_urp_ships(self):
506
+ """The gate measured the Autodesk Interactive graph; URP's Arnold
507
+ transparent graph sits 25 characters deeper, so a 107-character project
508
+ passed it, that graph failed to import, URP's Arnold mapper left
509
+ aiStandardSurface on plain Lit and the material suite failed on every
510
+ run from the in-repo base (2026-10-19). Measured against each installed
511
+ editor's bundled URP, not a list of the graphs the suites use today."""
512
+ from unitytk import UnityFinder
513
+
514
+ packages = [
515
+ p
516
+ for p in map(UnityEnv.bundled_urp, UnityFinder.find_editors().values())
517
+ if os.path.isdir(p)
518
+ ]
519
+ if not packages:
520
+ self.skipTest(EnvSkip.reason(UnityEnv.NO_BUNDLED_URP))
521
+ deepest = UnityEnv.URP_IMPORT_DEPTH - len(UnityEnv.URP_PACKAGE_CACHE)
522
+ for package in packages:
523
+ for dirpath, dirnames, filenames in os.walk(package):
524
+ # A folder ending "~" (samples, docs) or hidden is never imported.
525
+ dirnames[:] = [d for d in dirnames if not d.endswith("~") and not d.startswith(".")]
526
+ for name in filenames:
527
+ if name.endswith((".shadergraph", ".shadersubgraph")):
528
+ below = os.path.join(dirpath, name)[len(package):]
529
+ self.assertLessEqual(len(below), deepest, below)
530
+
531
+ @unittest.skipUnless(
532
+ os.name == "nt", "MAX_PATH is the Windows editor's limit; no Unity runs here"
533
+ )
534
+ def test_every_suite_fits_a_urp_project_under_the_default_base(self):
535
+ """A plain ``run_tests.py --all`` builds its projects under the in-repo
536
+ base, where ``<run>\\urp\\UnityProject`` put the material suite's URP
537
+ project at 107 characters: past the gate, so every URP suite failed at
538
+ once (2026-10-19). Every suite's prefix, not only today's URP suites:
539
+ any of them may call ``make_urp`` on its project, or add a ``"urp"`` one."""
540
+ prefixes = set()
541
+ # The allocator's own tests allocate into a sandboxed base, retired prefixes included.
542
+ for path in _source_files(".py", exclude=("test_temp_projects.py", "test_run_tests.py")):
543
+ with open(path, encoding="utf-8", errors="replace") as fh:
544
+ tree = ast.parse(fh.read(), filename=path)
545
+ for node in ast.walk(tree):
546
+ if (
547
+ isinstance(node, ast.Call)
548
+ and isinstance(node.func, ast.Attribute)
549
+ and node.func.attr == "allocate"
550
+ and getattr(node.func.value, "id", None) == "TempProjects"
551
+ and node.args
552
+ and isinstance(node.args[0], ast.Constant)
553
+ and isinstance(node.args[0].value, str)
554
+ ):
555
+ prefixes.add(node.args[0].value)
556
+ self.assertGreater(len(prefixes), 10, "the suites' allocations were not found")
557
+ # TempArtifacts' tag: the clock (16 hex digits), the pid in hex (6: Linux's
558
+ # pid_max ceiling; Windows ids run below it) and 4 random digits.
559
+ tag = "f" * (16 + 6 + 4)
560
+ budget = 259 - UnityEnv.URP_IMPORT_DEPTH
561
+ for prefix in sorted(prefixes):
562
+ run = os.path.join(TempProjects.DEFAULT_BASE, f"{prefix}_{tag}")
563
+ for project in (TempProjects.project(run), TempProjects.project(run, "urp")):
564
+ with self.subTest(project=project):
565
+ self.assertLessEqual(len(project), budget, project)
566
+
489
567
 
490
568
  class SuiteOptionsTest(unittest.TestCase):
491
569
  """The opt-in switches are named once, neutrally."""
@@ -578,6 +656,64 @@ class TemplateSourcesTest(unittest.TestCase):
578
656
  with self.assertRaises(FileNotFoundError): # a bare, pre-move name
579
657
  TemplateSources.copy_flat(["RenderEffectsController.cs"], dest)
580
658
 
659
+ def test_a_staged_set_must_carry_the_generated_records_it_reads(self):
660
+ """AudioEventController.cs staged without AudioRecord.g.cs compiled to
661
+ CS0103 in Unity and only the batch editor said so (2026-10-23)."""
662
+ controller = "AudioEvent/AudioEventController.cs"
663
+ record = "AudioEvent/AudioRecord.g.cs"
664
+ self.assertEqual(TemplateSources.missing_records([controller]), [record])
665
+ self.assertEqual(TemplateSources.missing_records([controller, record]), [])
666
+ # A record named only in a comment is no dependency.
667
+ self.assertEqual(TemplateSources.missing_records(["UnitytkSettings.cs"]), [])
668
+
669
+ import pythontk as ptk
670
+
671
+ store = ptk.TempArtifacts("unitytk_template_sources", policy="scoped")
672
+ self.addCleanup(store.cleanup)
673
+ dest = os.path.join(store.dir_path(), "Scripts")
674
+ with self.assertRaises(ValueError) as ctx:
675
+ TemplateSources.copy_flat(["UnitytkSettings.cs", controller], dest)
676
+ self.assertIn(record, str(ctx.exception))
677
+ self.assertFalse(os.path.exists(dest), "a refused set copies nothing")
678
+
679
+ def test_every_suite_stages_a_compile_closed_set(self):
680
+ """Each literal set a suite hands TemplateSources carries the records
681
+ its files read -- checked here, where no Unity is needed, because the
682
+ suites that stage them skip on every machine without one."""
683
+ calls = {"copy_flat", "available"}
684
+ offenders = []
685
+ for path in _source_files(".py", exclude=("base_test.py",)):
686
+ with open(path, encoding="utf-8", errors="replace") as fh:
687
+ tree = ast.parse(fh.read(), path)
688
+ consts = {
689
+ t.id: node.value
690
+ for node in tree.body
691
+ if isinstance(node, ast.Assign)
692
+ for t in node.targets
693
+ if isinstance(t, ast.Name)
694
+ }
695
+ for node in ast.walk(tree):
696
+ if not (
697
+ isinstance(node, ast.Call)
698
+ and isinstance(node.func, ast.Attribute)
699
+ and node.func.attr in calls
700
+ and isinstance(node.func.value, ast.Name)
701
+ and node.func.value.id == "TemplateSources"
702
+ and node.args
703
+ ):
704
+ continue
705
+ arg = node.args[0]
706
+ arg = consts.get(arg.id, arg) if isinstance(arg, ast.Name) else arg
707
+ try:
708
+ names = ast.literal_eval(arg)
709
+ except ValueError:
710
+ continue # built at run time: copy_flat checks it then
711
+ missing = TemplateSources.missing_records(names)
712
+ if missing:
713
+ rel = os.path.relpath(path, REPO_ROOT)
714
+ offenders.append(f"{rel}:{node.lineno} lacks {missing}")
715
+ self.assertEqual(offenders, [], "\n".join(offenders))
716
+
581
717
  def test_no_suite_joins_the_templates_dir_itself(self):
582
718
  """Five suites joined their own templates dir with a bare file name;
583
719
  one of them silently skipped a copy that no longer resolved."""
@@ -601,10 +737,10 @@ class UserPropsProbeTest(unittest.TestCase):
601
737
  self.assertFalse(UserPropsProbe.is_carrier(None))
602
738
 
603
739
  def test_find_carrier_returns_the_matching_entry(self):
604
- postproc = {"root": {}, "data_export 1": {"shot_metadata": "{}"}}
740
+ postproc = {"root": {}, "data_export 1": {"animation": "{}"}}
605
741
  name, props = UserPropsProbe.find_carrier(postproc)
606
742
  self.assertEqual(name, "data_export 1")
607
- self.assertEqual(props, {"shot_metadata": "{}"})
743
+ self.assertEqual(props, {"animation": "{}"})
608
744
 
609
745
  def test_find_carrier_is_none_when_absent(self):
610
746
  self.assertEqual(UserPropsProbe.find_carrier({"root": {}}), (None, None))
@@ -616,10 +752,8 @@ class UserPropsProbeTest(unittest.TestCase):
616
752
  self.assertFalse(UserPropsProbe.is_carrier("root/data_export/child"))
617
753
 
618
754
  def test_carrier_props_returns_just_the_props(self):
619
- postproc = {"root": {}, "root/data_export": {"shot_metadata": "{}"}}
620
- self.assertEqual(
621
- UserPropsProbe.carrier_props(postproc), {"shot_metadata": "{}"}
622
- )
755
+ postproc = {"root": {}, "root/data_export": {"animation": "{}"}}
756
+ self.assertEqual(UserPropsProbe.carrier_props(postproc), {"animation": "{}"})
623
757
 
624
758
  def test_carrier_props_is_none_when_absent(self):
625
759
  self.assertIsNone(UserPropsProbe.carrier_props({"root": {}}))
@@ -306,15 +306,11 @@ class TestBlenderRenderEffectsE2E(unittest.TestCase):
306
306
 
307
307
  @classmethod
308
308
  def setUpClass(cls):
309
- cls.temp_dir = TempProjects.allocate("blender_effects_e2e")
309
+ cls.temp_dir = TempProjects.allocate("blender_effects_e2e", cls)
310
310
  cls.blender_results = None
311
311
  cls.unity_results = None
312
312
  logger.info(f"Temp dir: {cls.temp_dir}")
313
313
 
314
- @classmethod
315
- def tearDownClass(cls):
316
- logger.info(f"Results preserved in: {cls.temp_dir}")
317
-
318
314
  @BlenderEnv.skip_unless_available()
319
315
  def test_01_blender_export(self):
320
316
  """Author both channels and the keyed visibility; write both ways."""
@@ -351,7 +347,7 @@ class TestBlenderRenderEffectsE2E(unittest.TestCase):
351
347
  self.skipTest("Blender results not available -- test_01 must run first")
352
348
  editor = os.environ.get("UNITYTK_TEST_EDITOR")
353
349
  launcher = UnityLauncher(editor) if editor else UnityLauncher()
354
- project_path = os.path.join(self.temp_dir, "UnityProject")
350
+ project_path = TempProjects.project(self.temp_dir)
355
351
  if not launcher.create_project(project_path, batch_mode=True):
356
352
  self.skipTest(UnityEnv.project_creation_reason())
357
353
  assets_path = os.path.join(project_path, "Assets")
@@ -198,7 +198,7 @@ ok = exporter.perform_export(
198
198
  tasks={"export_data_node": True},
199
199
  )
200
200
  fbx_path = os.path.join(output_dir, __FBX_NAME__ + ".fbx")
201
- payload = btk.DataNodes.read(ptk.Scope.DELIVERABLE, ShadowRig.SHADOW_METADATA)
201
+ payload = btk.DataNodes.read(ptk.Scope.DELIVERABLE, ptk.SceneRecords.SHADOWS.key)
202
202
 
203
203
  # The maps ship loose beside the FBX: the records name them by file name.
204
204
  textures = []
@@ -542,15 +542,11 @@ class TestBlenderShadowPlaneIntegration(unittest.TestCase):
542
542
 
543
543
  @classmethod
544
544
  def setUpClass(cls):
545
- cls.temp_dir = TempProjects.allocate("blender_shadow_integ")
545
+ cls.temp_dir = TempProjects.allocate("blender_shadow_integ", cls)
546
546
  cls.blender_results = None
547
547
  cls.unity_results = None
548
548
  logger.info(f"Temp dir: {cls.temp_dir}")
549
549
 
550
- @classmethod
551
- def tearDownClass(cls):
552
- logger.info(f"Results preserved in: {cls.temp_dir}")
553
-
554
550
  def _blender(self):
555
551
  blender = self.__class__.blender_results
556
552
  if not blender:
@@ -590,7 +586,7 @@ class TestBlenderShadowPlaneIntegration(unittest.TestCase):
590
586
  self.assertAlmostEqual(payload.get("unit_scale"), 1.0, places=6)
591
587
  records = {p["name"]: p for p in payload.get("planes", [])}
592
588
  for key, rig in r["rigs"].items():
593
- self.assertIn(rig["plane"], records, f"{key}: no shadow_metadata record")
589
+ self.assertIn(rig["plane"], records, f"{key}: no shadows record")
594
590
  self.assertEqual(records[r["rigs"]["horizon"]["plane"]]["type"], "horizon")
595
591
  self.assertEqual(
596
592
  records[r["rigs"]["sun"]["plane"]]["source_type"], "directional"
@@ -606,7 +602,7 @@ class TestBlenderShadowPlaneIntegration(unittest.TestCase):
606
602
  blender = self._blender()
607
603
  editor = os.environ.get("UNITYTK_TEST_EDITOR")
608
604
  launcher = UnityLauncher(editor) if editor else UnityLauncher()
609
- project_path = os.path.join(self.temp_dir, "UnityProject")
605
+ project_path = TempProjects.project(self.temp_dir)
610
606
  if not launcher.create_project(project_path, batch_mode=True):
611
607
  self.skipTest(UnityEnv.project_creation_reason())
612
608
 
@@ -151,7 +151,7 @@ for name, mode in VARIANTS.items():
151
151
  )
152
152
  path = os.path.join(output_dir, name + ".fbx")
153
153
  exists = bool(ok) and os.path.exists(path)
154
- meta = btk.DataNodes.read(ptk.Scope.DELIVERABLE, btk.DataNodes.SHOT_METADATA)
154
+ meta = btk.DataNodes.read(ptk.Scope.DELIVERABLE, ptk.SceneRecords.SHOTS.key)
155
155
  variants[name] = {
156
156
  "mode": mode,
157
157
  "fbx_exists": exists,
@@ -181,16 +181,12 @@ class TestBlenderShotMetadataIntegration(unittest.TestCase):
181
181
 
182
182
  @classmethod
183
183
  def setUpClass(cls):
184
- cls.temp_dir = TempProjects.allocate("blender_shot_meta_integ")
184
+ cls.temp_dir = TempProjects.allocate("blender_shot_meta_integ", cls)
185
185
  cls.blender_results = None
186
186
  cls.unity_results = None
187
187
  cls.postproc = None
188
188
  logger.info(f"Temp dir: {cls.temp_dir}")
189
189
 
190
- @classmethod
191
- def tearDownClass(cls):
192
- logger.info(f"Results preserved in: {cls.temp_dir}")
193
-
194
190
  # ---- Step 1: Blender export ----------------------------------------
195
191
 
196
192
  @BlenderEnv.skip_unless_available()
@@ -217,7 +213,6 @@ class TestBlenderShotMetadataIntegration(unittest.TestCase):
217
213
  {c: (s["start"], s["end"]) for c, s in shots.items()}, SHOTS, name
218
214
  )
219
215
  self.assertEqual(shots["A01"]["description"], "Open the hangar doors")
220
- self.assertEqual(shots["A01"]["section"], "A")
221
216
  # Shots Only ships the shots; Full Sequence Only ships one take.
222
217
  self.assertEqual(r["variants"]["shot_clips_shots"]["takes"], sorted(SHOTS))
223
218
  self.assertEqual(len(r["variants"]["shot_clips_full"]["takes"]), 1)
@@ -232,7 +227,7 @@ class TestBlenderShotMetadataIntegration(unittest.TestCase):
232
227
  self.skipTest("Blender results not available -- test_01 must run first")
233
228
  editor = os.environ.get("UNITYTK_TEST_EDITOR")
234
229
  launcher = UnityLauncher(editor) if editor else UnityLauncher()
235
- project_path = os.path.join(self.temp_dir, "UnityProject")
230
+ project_path = TempProjects.project(self.temp_dir)
236
231
  if not launcher.create_project(project_path, batch_mode=True):
237
232
  self.skipTest(UnityEnv.project_creation_reason())
238
233
 
@@ -313,8 +308,8 @@ class TestBlenderShotMetadataIntegration(unittest.TestCase):
313
308
  self.assertTrue(unity.get("postprocessor_fired"))
314
309
  carrier = UserPropsProbe.carrier_props(self.__class__.postproc or {})
315
310
  self.assertIsNotNone(carrier, "no data_export carrier arrived")
316
- self.assertIn("shot_metadata", carrier)
317
- self.assertIn("Open the hangar doors", carrier["shot_metadata"])
311
+ self.assertIn("animation", carrier)
312
+ self.assertIn("Open the hangar doors", carrier["animation"])
318
313
 
319
314
  self.assertTrue(unity.get("prefab_loaded"))
320
315
  self.assertTrue(unity.get("data_export_found"))
@@ -326,7 +321,6 @@ class TestBlenderShotMetadataIntegration(unittest.TestCase):
326
321
  self.assertEqual(unity.get("record_count"), 2)
327
322
  recs = {r.split("|")[0]: r.split("|") for r in unity.get("records", [])}
328
323
  self.assertEqual(recs["A01"][1], "Open the hangar doors")
329
- self.assertEqual(recs["A01"][2], "A")
330
324
  self.assertEqual(recs["A02"][1], "Raise the platform")
331
325
  clip_names = set(unity.get("clip_names", []))
332
326
  for clip in recs: # every record's clip resolves to an imported clip