1#!/pxrpythonsubst 2# 3# Copyright 2017 Pixar 4# 5# Licensed under the Apache License, Version 2.0 (the "Apache License") 6# with the following modification; you may not use this file except in 7# compliance with the Apache License and the following modification to it: 8# Section 6. Trademarks. is deleted and replaced with: 9# 10# 6. Trademarks. This License does not grant permission to use the trade 11# names, trademarks, service marks, or product names of the Licensor 12# and its affiliates, except as required to comply with Section 4(c) of 13# the License and to reproduce the content of the NOTICE file. 14# 15# You may obtain a copy of the Apache License at 16# 17# http://www.apache.org/licenses/LICENSE-2.0 18# 19# Unless required by applicable law or agreed to in writing, software 20# distributed under the Apache License with the above modification is 21# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22# KIND, either express or implied. See the Apache License for the specific 23# language governing permissions and limitations under the Apache License. 24 25from __future__ import print_function 26 27import os, sys, tempfile, unittest 28from pxr import Gf, Tf, Sdf, Usd 29 30allFormats = ['usd' + x for x in 'ac'] 31 32class PayloadedScene(object): 33 def __init__(self, fmt, unload=True, loadSet=Usd.Stage.LoadAll, 34 stageCreateFn=Usd.Stage.CreateInMemory): 35 # Construct the following test case: 36 # 37 # stage.fmt payload1.fmt 38 # /Sad ---(P)---> /Sad 39 # | /Sad/Panda 40 # | 41 # | 42 # /Foo payload2.fmt 43 # /Foo/Baz ---(P)---> /Baz payload3.fmt 44 # /Baz/Garply ---(P)---> /Garply 45 # /Garply/Qux 46 47 ext = '.'+fmt 48 49 # Create payload1.fmt 50 self.__payload1 = stageCreateFn("payload1"+ext) 51 p = self.__payload1.DefinePrim("/Sad/Panda", "Scope") 52 53 # Create payload3.usda 54 self.__payload3 = stageCreateFn("payload3"+ext) 55 p = self.__payload3.DefinePrim("/Garply/Qux", "Scope") 56 57 # Create payload2.usda 58 self.__payload2 = stageCreateFn("payload2"+ext) 59 p = self.__payload2.DefinePrim("/Baz/Garply", "Scope") 60 p.GetPayloads().AddPayload( 61 self.__payload3.GetRootLayer().identifier, "/Garply") 62 63 # 64 # Create the scene that references payload1 and payload2 65 # 66 self.stage = stageCreateFn("scene"+ext, loadSet) 67 p = self.stage.DefinePrim("/Sad", "Scope") 68 p.GetPayloads().AddPayload( 69 self.__payload1.GetRootLayer().identifier, "/Sad") 70 71 p = self.stage.DefinePrim("/Foo/Baz", "Scope") 72 p.GetPayloads().AddPayload( 73 self.__payload2.GetRootLayer().identifier, "/Baz") 74 75 # By default, tests expect that the scene starts 76 # with everything unloaded, unless specified otherwise 77 if unload: 78 self.stage.Unload() 79 80 def CleanupOnDiskAssets(self, fmt): 81 import os 82 83 del self.stage 84 del self.__payload1 85 del self.__payload2 86 del self.__payload3 87 88 ext = "." + fmt 89 for i in [1,2,3]: 90 fname = "payload" + str(i) + ext 91 if os.path.exists(fname): 92 os.unlink(fname) 93 fname = "scene"+ ext 94 if os.path.exists(fname): 95 os.unlink(fname) 96 97 def PrintPaths(self, msg=""): 98 print(" Paths: "+msg) 99 for p in self.stage.Traverse(): 100 print(" ", p) 101 print("") 102 103 104class TestUsdLoadUnload(unittest.TestCase): 105 def test_LoadRules(self): 106 """Test the UsdStageLoadRules object.""" 107 ################################################################ 108 # Basics. 109 r = Usd.StageLoadRules() 110 self.assertEqual(r, Usd.StageLoadRules()) 111 self.assertEqual(r, Usd.StageLoadRules.LoadAll()) 112 r.AddRule('/', Usd.StageLoadRules.NoneRule) 113 self.assertEqual(r, Usd.StageLoadRules.LoadNone()) 114 115 r.LoadWithDescendants('/') 116 r.Minimize() 117 self.assertEqual(r, Usd.StageLoadRules()) 118 119 r.Unload('/') 120 self.assertEqual(r, Usd.StageLoadRules.LoadNone()) 121 122 r.LoadWithoutDescendants('/') 123 self.assertEqual(r.GetRules(), 124 [(Sdf.Path('/'), Usd.StageLoadRules.OnlyRule)]) 125 126 r = Usd.StageLoadRules() 127 r.AddRule('/', Usd.StageLoadRules.AllRule) 128 self.assertTrue(r.IsLoaded('/')) 129 self.assertTrue(r.IsLoaded('/foo/bar/baz')) 130 131 r.AddRule('/', Usd.StageLoadRules.NoneRule) 132 self.assertFalse(r.IsLoaded('/')) 133 self.assertFalse(r.IsLoaded('/foo/bar/baz')) 134 135 # None for '/', All for /Foo/Bar/Baz/Garply 136 r.AddRule('/Foo/Bar/Baz/Garply', Usd.StageLoadRules.AllRule) 137 self.assertTrue(r.IsLoaded('/')) 138 self.assertTrue(r.IsLoaded('/Foo')) 139 self.assertTrue(r.IsLoaded('/Foo/Bar')) 140 self.assertTrue(r.IsLoaded('/Foo/Bar/Baz')) 141 self.assertTrue(r.IsLoaded('/Foo/Bar/Baz/Garply')) 142 self.assertTrue(r.IsLoaded('/Foo/Bar/Baz/Garply/Child')) 143 self.assertFalse(r.IsLoaded('/Foo/Bear')) 144 self.assertFalse(r.IsLoaded('/Foo/Bear/Baz')) 145 self.assertFalse(r.IsLoaded('/Foo/Bear/Baz/Garply')) 146 self.assertFalse(r.IsLoaded('/Foo/Bear/Baz/Garply/Child')) 147 148 # This unload creates a redundant rule, but everything should function 149 # as expected wrt IsLoaded queries. 150 r.Unload('/Foo/Bar/Baz') 151 self.assertEqual( 152 r.GetRules(), 153 [(Sdf.Path('/'), Usd.StageLoadRules.NoneRule), 154 (Sdf.Path('/Foo/Bar/Baz'), Usd.StageLoadRules.NoneRule)]) 155 self.assertFalse(r.IsLoaded('/')) 156 self.assertFalse(r.IsLoaded('/Foo')) 157 self.assertFalse(r.IsLoaded('/Foo/Bar')) 158 self.assertFalse(r.IsLoaded('/Foo/Bar/Baz')) 159 self.assertFalse(r.IsLoaded('/Foo/Bar/Baz/Garply')) 160 self.assertFalse(r.IsLoaded('/Foo/Bar/Baz/Garply/Child')) 161 self.assertFalse(r.IsLoaded('/Foo/Bear')) 162 self.assertFalse(r.IsLoaded('/Foo/Bear/Baz')) 163 self.assertFalse(r.IsLoaded('/Foo/Bear/Baz/Garply')) 164 self.assertFalse(r.IsLoaded('/Foo/Bear/Baz/Garply/Child')) 165 # Minimizing removes the redundant rule, all queries behave the same. 166 r.Minimize() 167 self.assertEqual(r.GetRules(), 168 [(Sdf.Path('/'), Usd.StageLoadRules.NoneRule)]) 169 self.assertFalse(r.IsLoaded('/')) 170 self.assertFalse(r.IsLoaded('/Foo')) 171 self.assertFalse(r.IsLoaded('/Foo/Bar')) 172 self.assertFalse(r.IsLoaded('/Foo/Bar/Baz')) 173 self.assertFalse(r.IsLoaded('/Foo/Bar/Baz/Garply')) 174 self.assertFalse(r.IsLoaded('/Foo/Bar/Baz/Garply/Child')) 175 self.assertFalse(r.IsLoaded('/Foo/Bear')) 176 self.assertFalse(r.IsLoaded('/Foo/Bear/Baz')) 177 self.assertFalse(r.IsLoaded('/Foo/Bear/Baz/Garply')) 178 self.assertFalse(r.IsLoaded('/Foo/Bear/Baz/Garply/Child')) 179 180 ################################################################ 181 # LoadAndUnload 182 r = Usd.StageLoadRules() 183 r.LoadAndUnload( 184 loadSet = ['/Load/All', '/Another/Load/All'], 185 unloadSet = ['/Unload/All', 186 '/Another/Unload/All', 187 '/Load/All/UnloadIneffective'], 188 policy = Usd.LoadWithDescendants) 189 r.Minimize() 190 self.assertEqual( 191 r.GetRules(), 192 [(Sdf.Path('/Another/Unload/All'), Usd.StageLoadRules.NoneRule), 193 (Sdf.Path('/Unload/All'), Usd.StageLoadRules.NoneRule)]) 194 195 r = Usd.StageLoadRules() 196 r.LoadAndUnload( 197 loadSet = ['/Load/All', '/Another/Load/All'], 198 unloadSet = ['/Unload/All', 199 '/Another/Unload/All', 200 '/Load/All/UnloadIneffective'], 201 policy = Usd.LoadWithoutDescendants) 202 r.Minimize() 203 self.assertEqual( 204 r.GetRules(), 205 [(Sdf.Path('/Another/Load/All'), Usd.StageLoadRules.OnlyRule), 206 (Sdf.Path('/Another/Unload/All'), Usd.StageLoadRules.NoneRule), 207 (Sdf.Path('/Load/All'), Usd.StageLoadRules.OnlyRule), 208 (Sdf.Path('/Unload/All'), Usd.StageLoadRules.NoneRule)]) 209 210 r2 = Usd.StageLoadRules() 211 r2.SetRules(r.GetRules()) 212 self.assertEqual(r, r2) 213 self.assertEqual(r.GetRules(), r2.GetRules()) 214 215 ################################################################ 216 # GetEffectiveRuleForPath 217 218 r = Usd.StageLoadRules.LoadNone() 219 self.assertEqual(r.GetEffectiveRuleForPath('/any/path'), 220 Usd.StageLoadRules.NoneRule) 221 r.AddRule('/any', Usd.StageLoadRules.OnlyRule) 222 # Root is now included as OnlyRule due to being in the ancestor chain. 223 self.assertEqual(r.GetEffectiveRuleForPath('/'), 224 Usd.StageLoadRules.OnlyRule) 225 self.assertEqual(r.GetEffectiveRuleForPath('/any'), 226 Usd.StageLoadRules.OnlyRule) 227 self.assertEqual(r.GetEffectiveRuleForPath('/any/path'), 228 Usd.StageLoadRules.NoneRule) 229 self.assertEqual(r.GetEffectiveRuleForPath('/outside/path'), 230 Usd.StageLoadRules.NoneRule) 231 232 self.assertTrue(r.IsLoadedWithNoDescendants('/any')) 233 self.assertFalse(r.IsLoadedWithNoDescendants('/any/path')) 234 self.assertFalse(r.IsLoadedWithAllDescendants('/any')) 235 self.assertFalse(r.IsLoadedWithAllDescendants('/any/path')) 236 237 # Root and /other are OnlyRule like above, /other/child and descendants 238 # are AllRule. 239 r.AddRule('/other/child', Usd.StageLoadRules.AllRule) 240 self.assertEqual(r.GetEffectiveRuleForPath('/'), 241 Usd.StageLoadRules.OnlyRule) 242 self.assertEqual(r.GetEffectiveRuleForPath('/other'), 243 Usd.StageLoadRules.OnlyRule) 244 self.assertEqual(r.GetEffectiveRuleForPath('/other/child'), 245 Usd.StageLoadRules.AllRule) 246 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/descndt/path'), 247 Usd.StageLoadRules.AllRule) 248 self.assertEqual(r.GetEffectiveRuleForPath('/outside/path'), 249 Usd.StageLoadRules.NoneRule) 250 251 self.assertTrue(r.IsLoadedWithNoDescendants('/any')) 252 self.assertFalse(r.IsLoadedWithNoDescendants('/any/path')) 253 self.assertTrue(r.IsLoadedWithAllDescendants('/other/child')) 254 self.assertTrue(r.IsLoadedWithAllDescendants( 255 '/other/child/descndt/path')) 256 257 # Now add an Only and a None under /other/child. 258 r.AddRule('/other/child/only', Usd.StageLoadRules.OnlyRule) 259 r.AddRule('/other/child/none', Usd.StageLoadRules.NoneRule) 260 self.assertEqual(r.GetEffectiveRuleForPath('/'), 261 Usd.StageLoadRules.OnlyRule) 262 self.assertEqual(r.GetEffectiveRuleForPath('/other'), 263 Usd.StageLoadRules.OnlyRule) 264 self.assertEqual(r.GetEffectiveRuleForPath('/other/child'), 265 Usd.StageLoadRules.AllRule) 266 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/descndt/path'), 267 Usd.StageLoadRules.AllRule) 268 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/only'), 269 Usd.StageLoadRules.OnlyRule) 270 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/only/child'), 271 Usd.StageLoadRules.NoneRule) 272 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/none'), 273 Usd.StageLoadRules.NoneRule) 274 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/none/child'), 275 Usd.StageLoadRules.NoneRule) 276 self.assertEqual(r.GetEffectiveRuleForPath('/outside/path'), 277 Usd.StageLoadRules.NoneRule) 278 279 # One more level, an All under a nested None. 280 r.AddRule('/other/child/none/child/all', Usd.StageLoadRules.AllRule) 281 self.assertEqual(r.GetEffectiveRuleForPath('/'), 282 Usd.StageLoadRules.OnlyRule) 283 self.assertEqual(r.GetEffectiveRuleForPath('/other'), 284 Usd.StageLoadRules.OnlyRule) 285 self.assertEqual(r.GetEffectiveRuleForPath('/other/child'), 286 Usd.StageLoadRules.AllRule) 287 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/descndt/path'), 288 Usd.StageLoadRules.AllRule) 289 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/none'), 290 Usd.StageLoadRules.OnlyRule) 291 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/none/child'), 292 Usd.StageLoadRules.OnlyRule) 293 self.assertEqual( 294 r.GetEffectiveRuleForPath('/other/child/none/child/all'), 295 Usd.StageLoadRules.AllRule) 296 297 # Minimize, queries should be the same. 298 r.Minimize() 299 self.assertEqual(r.GetEffectiveRuleForPath('/'), 300 Usd.StageLoadRules.OnlyRule) 301 self.assertEqual(r.GetEffectiveRuleForPath('/other'), 302 Usd.StageLoadRules.OnlyRule) 303 self.assertEqual(r.GetEffectiveRuleForPath('/other/child'), 304 Usd.StageLoadRules.AllRule) 305 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/descndt/path'), 306 Usd.StageLoadRules.AllRule) 307 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/none'), 308 Usd.StageLoadRules.OnlyRule) 309 self.assertEqual(r.GetEffectiveRuleForPath('/other/child/none/child'), 310 Usd.StageLoadRules.OnlyRule) 311 self.assertEqual( 312 r.GetEffectiveRuleForPath('/other/child/none/child/all'), 313 Usd.StageLoadRules.AllRule) 314 315 self.assertEqual( 316 r.GetRules(), 317 [(Sdf.Path('/'), Usd.StageLoadRules.NoneRule), 318 (Sdf.Path('/any'), Usd.StageLoadRules.OnlyRule), 319 (Sdf.Path('/other/child'), Usd.StageLoadRules.AllRule), 320 (Sdf.Path('/other/child/none'), Usd.StageLoadRules.NoneRule), 321 (Sdf.Path('/other/child/none/child/all'), 322 Usd.StageLoadRules.AllRule), 323 (Sdf.Path('/other/child/only'), Usd.StageLoadRules.OnlyRule)]) 324 325 ################################################################ 326 # Swap. 327 r1 = Usd.StageLoadRules.LoadNone() 328 r2 = Usd.StageLoadRules.LoadAll() 329 330 r1.swap(r2) 331 self.assertEqual(r1, Usd.StageLoadRules.LoadAll()) 332 self.assertEqual(r2, Usd.StageLoadRules.LoadNone()) 333 334 r1.AddRule('/foo', Usd.StageLoadRules.NoneRule) 335 r2.AddRule('/bar', Usd.StageLoadRules.AllRule) 336 337 r1.swap(r2) 338 self.assertEqual( 339 r1.GetRules(), 340 [(Sdf.Path('/'), Usd.StageLoadRules.NoneRule), 341 (Sdf.Path('/bar'), Usd.StageLoadRules.AllRule)]) 342 self.assertEqual( 343 r2.GetRules(), 344 [(Sdf.Path('/foo'), Usd.StageLoadRules.NoneRule)]) 345 346 ################################################################ 347 # More minimize testing. 348 r = Usd.StageLoadRules() 349 r.Minimize() 350 self.assertEqual(r, Usd.StageLoadRules()) 351 r.AddRule('/', Usd.StageLoadRules.AllRule) 352 r.Minimize() 353 self.assertEqual(r, Usd.StageLoadRules()) 354 355 r = Usd.StageLoadRules() 356 r.AddRule('/Foo/Bar/Only', Usd.StageLoadRules.OnlyRule) 357 r.AddRule('/Foo/Bar', Usd.StageLoadRules.AllRule) 358 r.AddRule('/Foo', Usd.StageLoadRules.AllRule) 359 r.AddRule('/World/anim', Usd.StageLoadRules.NoneRule) 360 r.AddRule('/World/anim/chars/group', Usd.StageLoadRules.OnlyRule) 361 r.AddRule('/World/anim/sim', Usd.StageLoadRules.AllRule) 362 r.AddRule('/World/anim/sim/other/prim', Usd.StageLoadRules.AllRule) 363 r.AddRule('/World/anim/sim/another/prim', Usd.StageLoadRules.OnlyRule) 364 r.Minimize() 365 self.assertEqual( 366 r.GetRules(), 367 [(Sdf.Path('/Foo/Bar/Only'), Usd.StageLoadRules.OnlyRule), 368 (Sdf.Path('/World/anim'), Usd.StageLoadRules.NoneRule), 369 (Sdf.Path('/World/anim/chars/group'), Usd.StageLoadRules.OnlyRule), 370 (Sdf.Path('/World/anim/sim'), Usd.StageLoadRules.AllRule), 371 (Sdf.Path('/World/anim/sim/another/prim'), 372 Usd.StageLoadRules.OnlyRule)]) 373 374 def test_GetSetLoadRules(self): 375 """Test calling GetLoadRules and SetLoadRules on a UsdStage""" 376 r = Usd.StageLoadRules.LoadNone() 377 r.AddRule('/any', Usd.StageLoadRules.OnlyRule) 378 r.AddRule('/other/child', Usd.StageLoadRules.AllRule) 379 r.AddRule('/other/child/only', Usd.StageLoadRules.OnlyRule) 380 r.AddRule('/other/child/none', Usd.StageLoadRules.NoneRule) 381 r.AddRule('/other/child/none/child/all', Usd.StageLoadRules.AllRule) 382 383 s = Usd.Stage.CreateInMemory() 384 s.DefinePrim('/any/child') 385 s.DefinePrim('/other/child/prim') 386 s.DefinePrim('/other/child/only/prim') 387 s.DefinePrim('/other/child/only/loaded/prim') 388 s.DefinePrim('/other/child/none/prim') 389 s.DefinePrim('/other/child/none/unloaded/prim') 390 s.DefinePrim('/other/child/none/child/all/one/prim') 391 s.DefinePrim('/other/child/none/child/all/two/prim') 392 393 payload = s.OverridePrim('/__payload') 394 395 def addPayload(prim): 396 prim.GetPayloads().AddInternalPayload(payload.GetPath()) 397 398 # Add payloads to all prims except leaf 'prim's and '__payload'. 399 for prim in s.TraverseAll(): 400 if prim.GetName() not in ('prim', '__payload'): 401 addPayload(prim) 402 403 # Create a new stage, with nothing loaded. 404 testStage = Usd.Stage.Open(s.GetRootLayer(), load=Usd.Stage.LoadNone) 405 406 self.assertEqual(list(testStage.Traverse()), []) 407 408 # Now set the load rules and assert that they produce the expected set 409 # of loaded prims. 410 testStage.SetLoadRules(r) 411 self.assertEqual( 412 [prim.GetPath() for prim in testStage.Traverse()], 413 ['/any', 414 '/other', 415 '/other/child', 416 '/other/child/prim', 417 '/other/child/only', 418 '/other/child/only/prim', 419 '/other/child/none', 420 '/other/child/none/prim', 421 '/other/child/none/child', 422 '/other/child/none/child/all', 423 '/other/child/none/child/all/one', 424 '/other/child/none/child/all/one/prim', 425 '/other/child/none/child/all/two', 426 '/other/child/none/child/all/two/prim']) 427 428 self.assertEqual(testStage.GetLoadRules(), r) 429 430 431 def test_LoadAndUnload(self): 432 """Test Stage::LoadUnload thoroughly, as all other requests funnel into it. 433 """ 434 435 print(sys._getframe().f_code.co_name) 436 437 for fmt in allFormats: 438 p = PayloadedScene(fmt) 439 440 # 441 # Everything is unloaded 442 # 443 p.PrintPaths("All unloaded") 444 assert not p.stage.GetPrimAtPath("/Sad/Panda") 445 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply") 446 assert len(p.stage.GetLoadSet()) == 0 447 assert len(p.stage.FindLoadable()) == 2 448 assert Sdf.Path("/Sad") in p.stage.FindLoadable() 449 assert Sdf.Path("/Foo/Baz") in p.stage.FindLoadable() 450 assert Sdf.Path("/Foo/Baz/Garply") not in p.stage.FindLoadable() 451 452 # 453 # Load /Foo without descendants, which will pull in nothing new. 454 # 455 p.stage.LoadAndUnload((Sdf.Path("/Foo"),), tuple(), 456 policy=Usd.LoadWithoutDescendants) 457 p.PrintPaths("/Foo loaded without descendants") 458 assert not p.stage.GetPrimAtPath("/Sad/Panda") 459 assert Sdf.Path("/Foo") not in p.stage.GetLoadSet() 460 assert Sdf.Path("/Foo/Baz") not in p.stage.GetLoadSet() 461 assert Sdf.Path("/Foo/Baz/Garply") not in p.stage.GetLoadSet() 462 assert len(p.stage.FindLoadable()) == 2 463 assert Sdf.Path("/Foo/Baz/Garply") not in p.stage.FindLoadable() 464 465 # 466 # Load /Foo/Baz without descendants, which will pull in /Foo/Baz but 467 # not /Foo/Baz/Garply 468 # 469 p.stage.LoadAndUnload((Sdf.Path("/Foo/Baz"),), tuple(), 470 policy=Usd.LoadWithoutDescendants) 471 p.PrintPaths("/Foo/Baz loaded without descendants") 472 assert not p.stage.GetPrimAtPath("/Sad/Panda") 473 assert Sdf.Path("/Foo") not in p.stage.GetLoadSet() 474 assert Sdf.Path("/Foo/Baz") in p.stage.GetLoadSet() 475 assert Sdf.Path("/Foo/Baz/Garply") not in p.stage.GetLoadSet() 476 assert len(p.stage.FindLoadable()) == 3 477 assert Sdf.Path("/Foo/Baz/Garply") in p.stage.FindLoadable() 478 479 # 480 # Load /Foo which will pull in /Foo/Baz and /Foo/Baz/Garply 481 # 482 p.stage.LoadAndUnload((Sdf.Path("/Foo"),), tuple()) 483 p.PrintPaths("/Foo loaded") 484 assert not p.stage.GetPrimAtPath("/Sad/Panda") 485 assert Sdf.Path("/Foo") not in p.stage.GetLoadSet() 486 assert Sdf.Path("/Foo/Baz") in p.stage.GetLoadSet() 487 assert Sdf.Path("/Foo/Baz/Garply") in p.stage.GetLoadSet() 488 assert len(p.stage.FindLoadable()) == 3 489 assert Sdf.Path("/Foo/Baz/Garply") in p.stage.FindLoadable() 490 491 # 492 # Load /Foo/Baz without descendants, which should pull in just 493 # /Foo/Baz. 494 # 495 p.stage.LoadAndUnload((Sdf.Path("/Foo/Baz"),), tuple(), 496 policy=Usd.LoadWithoutDescendants) 497 p.PrintPaths("/Foo/Baz loaded w/o descendants") 498 assert not p.stage.GetPrimAtPath("/Sad/Panda") 499 assert Sdf.Path("/Foo") not in p.stage.GetLoadSet() 500 assert Sdf.Path("/Foo/Baz") in p.stage.GetLoadSet() 501 assert Sdf.Path("/Foo/Baz/Garply") not in p.stage.GetLoadSet() 502 assert len(p.stage.FindLoadable()) == 3 503 assert Sdf.Path("/Foo/Baz/Garply") in p.stage.FindLoadable() 504 505 # 506 # Load /Foo again, which will pull in /Foo/Baz and /Foo/Baz/Garply 507 # 508 p.stage.LoadAndUnload((Sdf.Path("/Foo"),), tuple()) 509 p.PrintPaths("/Foo loaded") 510 assert not p.stage.GetPrimAtPath("/Sad/Panda") 511 assert Sdf.Path("/Foo") not in p.stage.GetLoadSet() 512 assert Sdf.Path("/Foo/Baz") in p.stage.GetLoadSet() 513 assert Sdf.Path("/Foo/Baz/Garply") in p.stage.GetLoadSet() 514 assert len(p.stage.FindLoadable()) == 3 515 assert Sdf.Path("/Foo/Baz/Garply") in p.stage.FindLoadable() 516 517 # 518 # Unload /Foo/Baz/Garply and load /Foo/Baz without descendants, 519 # which should pull in just /Foo/Baz. 520 # 521 p.stage.LoadAndUnload((Sdf.Path("/Foo/Baz"),), 522 (Sdf.Path("/Foo/Baz/Garply"),), 523 policy=Usd.LoadWithoutDescendants) 524 p.PrintPaths("/Foo/Baz/Garply unloaded, " 525 "/Foo/Baz loaded w/o descendants") 526 assert not p.stage.GetPrimAtPath("/Sad/Panda") 527 assert Sdf.Path("/Foo") not in p.stage.GetLoadSet() 528 assert Sdf.Path("/Foo/Baz") in p.stage.GetLoadSet() 529 assert Sdf.Path("/Foo/Baz/Garply") not in p.stage.GetLoadSet() 530 assert len(p.stage.FindLoadable()) == 3 531 assert Sdf.Path("/Foo/Baz/Garply") in p.stage.FindLoadable() 532 533 # 534 # Unload /Foo, unloading everything 535 # 536 p.stage.LoadAndUnload(tuple(), (Sdf.Path("/Foo"),)) 537 p.PrintPaths("/Foo unloaded") 538 assert not p.stage.GetPrimAtPath("/Sad/Panda") 539 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply") 540 assert len(p.stage.GetLoadSet()) == 0 541 # /Foo/Baz/Garply's payload is no longer visible, loadable count = 2 542 assert len(p.stage.FindLoadable()) == 2 543 544 # 545 # Explicitly load /Foo/Baz, which will implicitly pull in 546 # /Foo/Baz/Garply 547 # 548 p.stage.LoadAndUnload((Sdf.Path("/Foo/Baz"),), tuple()) 549 p.PrintPaths("/Foo/Baz loaded") 550 assert not p.stage.GetPrimAtPath("/Sad/Panda") 551 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply") 552 assert len(p.stage.GetLoadSet()) == 2 553 554 # 555 # Unload /Foo, which unloads everything recursively 556 # 557 p.stage.LoadAndUnload(tuple(), (Sdf.Path("/Foo"),)) 558 p.PrintPaths("/Foo unloaded") 559 assert not p.stage.GetPrimAtPath("/Sad/Panda") 560 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply") 561 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 562 assert len(p.stage.GetLoadSet()) == 0, str(p.stage.GetLoadSet()) 563 564 # 565 # Load /Foo, but unload /Foo/Baz. Verify that the loading of /Foo 566 # overrides the unloading of /Foo/Baz/Garply. 567 # 568 p.PrintPaths("/Foo loaded, /Foo/Baz/Garply unloaded") 569 p.stage.LoadAndUnload((Sdf.Path("/Foo"),), 570 (Sdf.Path("/Foo/Baz/Garply"),)) 571 p.PrintPaths() 572 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply") 573 assert len(p.stage.GetLoadSet()) == 2 574 p.stage.Unload("/") 575 assert len(p.stage.GetLoadSet()) == 0 576 577 # 578 # Load only /Foo/Baz/Garply, which will load /Foo, but not /Sad/Panda 579 # 580 p.PrintPaths("/Foo/Baz/Garply loaded") 581 p.stage.LoadAndUnload((Sdf.Path("/Foo/Baz/Garply"),), tuple()) 582 p.PrintPaths() 583 assert not p.stage.GetPrimAtPath("/Sad/Panda") 584 assert p.stage.GetPrimAtPath("/Foo/Baz") 585 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply") 586 assert len(p.stage.GetLoadSet()) == 2 587 588 589 def test_Load(self): 590 """Tests UsdStage::Load/Unload. 591 """ 592 print(sys._getframe().f_code.co_name) 593 for fmt in allFormats: 594 p = PayloadedScene(fmt) 595 p.PrintPaths() 596 assert not p.stage.GetPrimAtPath("/Sad/Panda") 597 598 p.stage.Load("/Foo") 599 p.PrintPaths() 600 assert not p.stage.GetPrimAtPath("/Sad/Panda") 601 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 602 assert (set(p.stage.GetLoadSet()) == 603 set([Sdf.Path("/Foo/Baz"), 604 Sdf.Path("/Foo/Baz/Garply")])) 605 606 p.stage.Load("/Sad") 607 p.PrintPaths() 608 assert p.stage.GetPrimAtPath("/Sad/Panda") 609 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 610 assert (set(p.stage.GetLoadSet()) == 611 set([Sdf.Path("/Sad"), 612 Sdf.Path("/Foo/Baz"), 613 Sdf.Path("/Foo/Baz/Garply")])) 614 615 p.stage.Unload("/Sad") 616 p.PrintPaths() 617 assert not p.stage.GetPrimAtPath("/Sad/Panda") 618 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 619 self.assertEqual(set(p.stage.GetLoadSet()), 620 set([Sdf.Path("/Foo/Baz"), 621 Sdf.Path("/Foo/Baz/Garply")])) 622 623 p.stage.Unload("/") 624 p.PrintPaths() 625 assert not p.stage.GetPrimAtPath("/Sad/Panda") 626 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 627 self.assertEqual(set(p.stage.GetLoadSet()), set([])) 628 629 p.stage.Load("/") 630 p.PrintPaths() 631 assert p.stage.GetPrimAtPath("/Sad/Panda") 632 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 633 assert (set(p.stage.GetLoadSet()) == 634 set([Sdf.Path("/Sad"), 635 Sdf.Path("/Foo/Baz"), 636 Sdf.Path("/Foo/Baz/Garply")])) 637 638 # If a loaded prim is deactivated, it will no longer have 639 # any child prims (just like any other deactivated prim), 640 # but it is still considered part of the stage's load set 641 # since its payload is still loaded. 642 p.stage.GetPrimAtPath("/Sad").SetActive(False) 643 p.PrintPaths() 644 assert not p.stage.GetPrimAtPath("/Sad/Panda") 645 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 646 assert (set(p.stage.GetLoadSet()) == 647 set([Sdf.Path("/Sad"), 648 Sdf.Path("/Foo/Baz"), 649 Sdf.Path("/Foo/Baz/Garply")])) 650 651 # If an ancestor of a loaded prim is deactivated, the 652 # loaded prim will no longer exist on the stage (since 653 # inactive prims have no descendents), but will still 654 # be considered part of the stage's load set since 655 # its payload is still loaded. 656 p.stage.GetPrimAtPath("/Foo").SetActive(False) 657 p.PrintPaths() 658 assert not p.stage.GetPrimAtPath("/Sad/Panda") 659 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 660 assert (set(p.stage.GetLoadSet()) == 661 set([Sdf.Path("/Sad"), 662 Sdf.Path("/Foo/Baz"), 663 Sdf.Path("/Foo/Baz/Garply")])) 664 665 def test_Create(self): 666 """Test the behavior of UsdStage::Create WRT load behavior""" 667 print(sys._getframe().f_code.co_name) 668 669 # Exercise creating an in memory stage 670 for fmt in allFormats: 671 # Try loading none 672 p = PayloadedScene(fmt, unload=False, loadSet=Usd.Stage.LoadNone) 673 674 p.PrintPaths() 675 assert not p.stage.GetPrimAtPath("/Sad/Panda") 676 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply") 677 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 678 assert len(p.stage.GetLoadSet()) == 0 679 assert len(p.stage.FindLoadable()) == 2 680 681 # Try loading all 682 p = PayloadedScene(fmt, unload=False, loadSet=Usd.Stage.LoadAll) 683 p.PrintPaths() 684 assert p.stage.GetPrimAtPath("/Sad/Panda") 685 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply") 686 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 687 assert len(p.stage.GetLoadSet()) == 3, str(p.stage.GetLoadSet()) 688 assert len(p.stage.FindLoadable()) == 3 689 690 # Exercise creating an on-disk stage 691 for fmt in allFormats: 692 # Try loading none 693 p = PayloadedScene(fmt, unload=False, loadSet=Usd.Stage.LoadNone, 694 stageCreateFn=Usd.Stage.CreateNew) 695 696 p.PrintPaths() 697 assert not p.stage.GetPrimAtPath("/Sad/Panda") 698 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply") 699 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 700 assert len(p.stage.GetLoadSet()) == 0 701 assert len(p.stage.FindLoadable()) == 2 702 703 p.CleanupOnDiskAssets(fmt) 704 705 # Try loading all 706 p = PayloadedScene(fmt, unload=False, loadSet=Usd.Stage.LoadAll, 707 stageCreateFn=Usd.Stage.CreateNew) 708 p.PrintPaths() 709 assert p.stage.GetPrimAtPath("/Sad/Panda") 710 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply") 711 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 712 assert len(p.stage.GetLoadSet()) == 3, str(p.stage.GetLoadSet()) 713 assert len(p.stage.FindLoadable()) == 3 714 715 p.CleanupOnDiskAssets(fmt) 716 717 def test_Open(self): 718 """Test the behavior of UsdStage::Open WRT load behavior. 719 """ 720 print(sys._getframe().f_code.co_name) 721 722 for fmt in allFormats: 723 p = PayloadedScene(fmt) 724 725 # reopen the stage with nothing loaded 726 p.stage = Usd.Stage.Open(p.stage.GetRootLayer().identifier, 727 load=Usd.Stage.LoadNone) 728 p.PrintPaths() 729 assert not p.stage.GetPrimAtPath("/Sad/Panda") 730 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply") 731 assert not p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 732 assert len(p.stage.GetLoadSet()) == 0 733 assert len(p.stage.FindLoadable()) == 2 734 735 # reopen the stage with everything loaded 736 p.stage = Usd.Stage.Open(p.stage.GetRootLayer().identifier, 737 load=Usd.Stage.LoadAll) 738 p.PrintPaths() 739 assert p.stage.GetPrimAtPath("/Sad/Panda") 740 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply") 741 assert p.stage.GetPrimAtPath("/Foo/Baz/Garply/Qux") 742 assert len(p.stage.GetLoadSet()) == 3, str(p.stage.GetLoadSet()) 743 assert len(p.stage.FindLoadable()) == 3 744 745 746 def test_Errors(self): 747 """Tests error conditions. 748 """ 749 750 # TODO: test loading / unloading of inactive prims once we have 751 # correct active composition semantics. 752 753 # TODO: assert that inactive paths do not show up in loaded or 754 # loadable sets. 755 756 print(sys._getframe().f_code.co_name) 757 for fmt in allFormats: 758 p = PayloadedScene(fmt) 759 p.PrintPaths() 760 761 with self.assertRaises(Tf.ErrorException): 762 p.stage.Load("/Non/Existent/Prim") 763 764 # It is explicitly okay to unload nonexistent prim paths. 765 p.stage.Unload("/Non/Existent/Prim") 766 767 set1 = (Sdf.Path("/Non/Existent/Prim"),) 768 set2 = (Sdf.Path("/Other/Existent/Prim"),) 769 with self.assertRaises(Tf.ErrorException): 770 p.stage.LoadAndUnload(set1, set()) 771 with self.assertRaises(Tf.ErrorException): 772 p.stage.LoadAndUnload(set1, set2) 773 774 # In the past, when it was illegal to unload a deactivated prim we'd 775 # get an error for this. Now it is legal. 776 p.stage.Load('/Foo/Baz') 777 foo = p.stage.GetPrimAtPath('/Foo') 778 baz = p.stage.GetPrimAtPath('/Foo/Baz') 779 assert foo 780 assert baz 781 foo.SetActive(False) 782 assert not baz 783 p.stage.Unload('/Foo/Baz') 784 with self.assertRaises(Tf.ErrorException): 785 p.stage.Load('/Foo/Baz') 786 foo.SetActive(True) 787 baz = p.stage.GetPrimAtPath('/Foo/Baz') 788 assert not baz.IsLoaded() 789 p.PrintPaths() 790 791 792 def test_RedundantLoads(self): 793 """Ensure that calling load or unload redundantly is not an error 794 """ 795 print(sys._getframe().f_code.co_name) 796 for fmt in allFormats: 797 p = PayloadedScene(fmt) 798 p.PrintPaths() 799 800 p.stage.Load("/Sad") 801 p.PrintPaths() 802 p.stage.Load("/Sad") 803 p.PrintPaths() 804 805 p.stage.Load("/Foo") 806 p.PrintPaths() 807 p.stage.Load("/Foo") 808 p.PrintPaths() 809 810 p.stage.Unload("/Foo") 811 p.PrintPaths() 812 p.stage.Unload("/Foo") 813 p.PrintPaths() 814 815 816 def test_StageCreateNew(self): 817 """Tests the behavior of Usd.Stage.Create 818 """ 819 for fmt in allFormats: 820 layerName = "testLayer." + fmt 821 if os.path.exists(layerName): 822 os.unlink(layerName) 823 824 s = Usd.Stage.CreateNew(layerName) 825 assert s 826 assert os.path.exists(layerName) 827 lyr = s.GetRootLayer() 828 s.OverridePrim("/Foo") 829 lyr.Save() 830 # Test the layer expiration behavior. Because we only have a weak ptr, 831 # we expect it to expire when the stage goes away. 832 assert lyr 833 del s 834 assert not lyr 835 836 assert os.path.exists(layerName) 837 # No error here, we expect to clobber the existing file. 838 s1 = Usd.Stage.CreateNew(layerName) 839 assert not s1.GetPrimAtPath("/Foo") 840 # We expect this to error, because the layer is already in memory. 841 with self.assertRaises(Tf.ErrorException): 842 Usd.Stage.CreateNew(layerName) 843 assert s1.GetRootLayer() 844 del s1 845 846 # Make sure session layers work 847 session = Usd.Stage.CreateInMemory() 848 session.OverridePrim("/Foo") 849 s = Usd.Stage.CreateNew(layerName, session.GetRootLayer()) 850 assert s.GetPrimAtPath("/Foo") 851 del s, session 852 853 assert os.path.exists(layerName) 854 os.unlink(layerName) 855 856 def test_StageReload(self): 857 """Tests Usd.Stage.Reload 858 """ 859 # Reloading anonymous layers clears content. 860 s = Usd.Stage.CreateInMemory() 861 s.OverridePrim('/foo') 862 s.Reload() 863 assert not s.GetPrimAtPath('/foo') 864 865 # Try with a real file -- saved changes preserved, unsaved changes get 866 # discarded. 867 def _TestStageReload(fmt): 868 with tempfile.NamedTemporaryFile(suffix='.%s' % fmt) as f: 869 f.close() 870 871 s = Usd.Stage.CreateNew(f.name) 872 s.OverridePrim('/foo') 873 s.GetRootLayer().Save() 874 s.OverridePrim('/bar') 875 876 assert s.GetPrimAtPath('/foo') 877 assert s.GetPrimAtPath('/bar') 878 s.Reload() 879 assert s.GetPrimAtPath('/foo') 880 assert not s.GetPrimAtPath('/bar') 881 882 # NOTE: f will want to delete the underlying file on 883 # __exit__ from the context manager. But stage s 884 # may have the file open. If so the deletion will 885 # fail on Windows. Explicitly release our reference 886 # to the stage to close the file. 887 del s 888 889 for fmt in allFormats: 890 # XXX: This verifies current behavior, however this is probably 891 # not the behavior we ultimately want -- see bug 102444. 892 _TestStageReload(fmt) 893 894 def test_StageLayerReload(self): 895 """Test that Usd.Stage responds correctly when one of its 896 layer is directly reloaded.""" 897 import shutil 898 899 def _TestLayerReload(fmt): 900 # First, test case where the reloaded layer is in the 901 # stage's root LayerStack. 902 with tempfile.NamedTemporaryFile(suffix='.%s' % fmt) as l1name, \ 903 tempfile.NamedTemporaryFile(suffix='.%s' % fmt) as l2name: 904 l1name.close() 905 l2name.close() 906 907 l1 = Sdf.Layer.CreateAnonymous() 908 Sdf.CreatePrimInLayer(l1, '/foo') 909 l1.Export(l1name.name) 910 911 l2 = Sdf.Layer.CreateAnonymous() 912 Sdf.CreatePrimInLayer(l2, '/bar') 913 l2.Export(l2name.name) 914 915 s = Usd.Stage.Open(l1name.name) 916 assert s.GetPrimAtPath('/foo') 917 assert not s.GetPrimAtPath('/bar') 918 919 shutil.copyfile(l2name.name, l1name.name) 920 # Force layer reload to avoid issues where mtime does not 921 # change after file copy due to low resolution. 922 s.GetRootLayer().Reload(force = True) 923 924 assert not s.GetPrimAtPath('/foo') 925 assert s.GetPrimAtPath('/bar') 926 927 # NOTE: l1name will want to delete the underlying file 928 # on __exit__ from the context manager. But stage s 929 # may have the file open. If so the deletion will 930 # fail on Windows. Explicitly release our reference 931 # to the stage to close the file. 932 del s 933 934 # Now test the case where the reloaded layer is in a referenced 935 # LayerStack. 936 with tempfile.NamedTemporaryFile(suffix='.%s' % fmt) as rootLayerName, \ 937 tempfile.NamedTemporaryFile(suffix='.%s' % fmt) as refLayerName: 938 rootLayerName.close() 939 refLayerName.close() 940 941 refLayer = Sdf.Layer.CreateAnonymous() 942 Sdf.CreatePrimInLayer(refLayer, '/foo/bar') 943 refLayer.Export(refLayerName.name) 944 945 rootLayer = Sdf.Layer.CreateAnonymous() 946 rootPrim = Sdf.CreatePrimInLayer(rootLayer, '/foo') 947 # The resolver wants references with forward slashes. 948 rootPrim.referenceList.Add( 949 Sdf.Reference(refLayerName.name.replace('\\', '/'), '/foo')) 950 rootLayer.Export(rootLayerName.name) 951 952 s = Usd.Stage.Open(rootLayerName.name) 953 assert s.GetPrimAtPath('/foo/bar') 954 955 del refLayer.GetPrimAtPath('/foo').nameChildren['bar'] 956 refLayer.Export(refLayerName.name) 957 Sdf.Layer.Find(refLayerName.name).Reload(force = True) 958 assert s.GetPrimAtPath('/foo') 959 assert not s.GetPrimAtPath('/foo/bar') 960 961 # NOTE: rootLayerName will want to delete the underlying file 962 # on __exit__ from the context manager. But stage s 963 # may have the file open. If so the deletion will 964 # fail on Windows. Explicitly release our reference 965 # to the stage to close the file. 966 del s 967 968 import platform 969 for fmt in allFormats: 970 # XXX: This verifies current behavior, however this is probably 971 # not the behavior we ultimately want -- see bug 102444. 972 # Can't test Reload() for usdc on Windows because the system 973 # won't allow us to modify the memory-mapped file. 974 if not (platform.system() == 'Windows' and fmt == 'usdc'): 975 _TestLayerReload(fmt) 976 977if __name__ == "__main__": 978 unittest.main() 979