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