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 pxr import Sdf, Pcp, Tf
26import unittest
27from contextlib import contextmanager
28
29class TestPcpChanges(unittest.TestCase):
30    def test_EmptySublayerChanges(self):
31        subLayer1 = Sdf.Layer.CreateAnonymous()
32        primSpec = Sdf.CreatePrimInLayer(subLayer1, '/Test')
33
34        rootLayer = Sdf.Layer.CreateAnonymous()
35        rootLayer.subLayerPaths.append(subLayer1.identifier)
36
37        layerStackId = Pcp.LayerStackIdentifier(rootLayer)
38        pcp = Pcp.Cache(layerStackId)
39
40        (pi, err) = pcp.ComputePrimIndex('/Test')
41        self.assertFalse(err)
42        self.assertEqual(pi.primStack, [primSpec])
43
44        subLayer2 = Sdf.Layer.CreateAnonymous()
45        self.assertTrue(subLayer2.empty)
46
47        # Adding an empty sublayer should not require recomputing any prim
48        # indexes or change their prim stacks, but it should change the used
49        # layers revision count.
50        revCount = pcp.GetUsedLayersRevision()
51        with Pcp._TestChangeProcessor(pcp):
52            rootLayer.subLayerPaths.insert(0, subLayer2.identifier)
53
54        pi = pcp.FindPrimIndex('/Test')
55        self.assertTrue(pi)
56        self.assertEqual(pi.primStack, [primSpec])
57        self.assertNotEqual(revCount, pcp.GetUsedLayersRevision())
58
59        # Same with deleting an empty sublayer.
60        revCount = pcp.GetUsedLayersRevision()
61        with Pcp._TestChangeProcessor(pcp):
62            del rootLayer.subLayerPaths[0]
63
64        pi = pcp.FindPrimIndex('/Test')
65        self.assertTrue(pi)
66        self.assertEqual(pi.primStack, [primSpec])
67        self.assertNotEqual(revCount, pcp.GetUsedLayersRevision())
68
69    def test_InvalidSublayerAdd(self):
70        invalidSublayerId = "/tmp/testPcpChanges_invalidSublayer.sdf"
71
72        layer = Sdf.Layer.CreateAnonymous()
73        layerStackId = Pcp.LayerStackIdentifier(layer)
74        pcp = Pcp.Cache(layerStackId)
75
76        (layerStack, errs) = pcp.ComputeLayerStack(layerStackId)
77        self.assertEqual(len(errs), 0)
78        self.assertEqual(len(layerStack.localErrors), 0)
79        self.assertTrue(pcp.UsesLayerStack(layerStack))
80
81        with Pcp._TestChangeProcessor(pcp):
82            layer.subLayerPaths.append(invalidSublayerId)
83
84        (layerStack, errs) = pcp.ComputeLayerStack(layerStackId)
85        # This is potentially surprising. Layer stacks are recomputed
86        # immediately during change processing, so any composition
87        # errors generated during that process won't be reported
88        # during the call to ComputeLayerStack. The errors will be
89        # stored in the layer stack's localErrors field, however.
90        self.assertEqual(len(errs), 0)
91        self.assertEqual(len(layerStack.localErrors), 1)
92        self.assertTrue(pcp.UsesLayerStack(layerStack))
93
94    def test_InvalidSublayerRemoval(self):
95        invalidSublayerId = "/tmp/testPcpChanges_invalidSublayer.sdf"
96
97        layer = Sdf.Layer.CreateAnonymous()
98        layer.subLayerPaths.append(invalidSublayerId)
99
100        layerStackId = Pcp.LayerStackIdentifier(layer)
101        pcp = Pcp.Cache(layerStackId)
102
103        (layerStack, errs) = pcp.ComputeLayerStack(layerStackId)
104        self.assertEqual(len(errs), 1)
105        self.assertEqual(len(layerStack.localErrors), 1)
106        self.assertTrue(pcp.IsInvalidSublayerIdentifier(invalidSublayerId))
107
108        with Pcp._TestChangeProcessor(pcp):
109            layer.subLayerPaths.remove(invalidSublayerId)
110
111        (layerStack, errs) = pcp.ComputeLayerStack(layerStackId)
112        self.assertEqual(len(errs), 0)
113        self.assertEqual(len(layerStack.localErrors), 0)
114        self.assertFalse(pcp.IsInvalidSublayerIdentifier(invalidSublayerId))
115        self.assertTrue(pcp.UsesLayerStack(layerStack))
116
117    def test_UnusedVariantChanges(self):
118        layer = Sdf.Layer.CreateAnonymous()
119        parent = Sdf.PrimSpec(layer, 'Root', Sdf.SpecifierDef, 'Scope')
120        vA = Sdf.CreateVariantInLayer(layer, parent.path, 'var', 'A')
121        vB = Sdf.CreateVariantInLayer(layer, parent.path, 'var', 'B')
122        parent.variantSelections['var'] = 'A'
123
124        layerStackId = Pcp.LayerStackIdentifier(layer)
125        pcp = Pcp.Cache(layerStackId)
126
127        (pi, err) = pcp.ComputePrimIndex('/Root')
128        self.assertTrue(pi)
129        self.assertEqual(len(err), 0)
130
131        # Add a new prim spec inside the unused variant and verify that this
132        # does not cause the cached prim index to be blown.
133        with Pcp._TestChangeProcessor(pcp):
134            newPrim = Sdf.PrimSpec(vB.primSpec, 'Child', Sdf.SpecifierDef, 'Scope')
135
136        self.assertTrue(pcp.FindPrimIndex('/Root'))
137
138    def test_SublayerOffsetChanges(self):
139        rootLayerPath = 'TestSublayerOffsetChanges/root.sdf'
140        rootSublayerPath = 'TestSublayerOffsetChanges/root-sublayer.sdf'
141        refLayerPath = 'TestSublayerOffsetChanges/ref.sdf'
142        refSublayerPath = 'TestSublayerOffsetChanges/ref-sublayer.sdf'
143        ref2LayerPath = 'TestSublayerOffsetChanges/ref2.sdf'
144
145        rootLayer = Sdf.Layer.FindOrOpen(rootLayerPath)
146        pcp = Pcp.Cache(Pcp.LayerStackIdentifier(rootLayer))
147
148        (pi, err) = pcp.ComputePrimIndex('/A')
149        self.assertTrue(pi)
150        self.assertEqual(len(err), 0)
151
152        # Verify the expected structure of the test asset. It should simply be
153        # a chain of two references, with layer offsets of 100.0 and 50.0
154        # respectively.
155        refNode = pi.rootNode.children[0]
156        self.assertEqual(refNode.layerStack.layers,
157                    [Sdf.Layer.Find(refLayerPath), Sdf.Layer.Find(refSublayerPath)])
158        self.assertEqual(refNode.arcType, Pcp.ArcTypeReference)
159        self.assertEqual(refNode.mapToRoot.timeOffset, Sdf.LayerOffset(100.0))
160
161        ref2Node = refNode.children[0]
162        self.assertEqual(ref2Node.layerStack.layers, [Sdf.Layer.Find(ref2LayerPath)])
163        self.assertEqual(ref2Node.arcType, Pcp.ArcTypeReference)
164        self.assertEqual(ref2Node.mapToRoot.timeOffset, Sdf.LayerOffset(150.0))
165
166        # Change the layer offset in the local layer stack and verify that
167        # invalidates the prim index and that the updated layer offset is
168        # taken into account after recomputing the index.
169        with Pcp._TestChangeProcessor(pcp):
170            rootLayer.subLayerOffsets[0] = Sdf.LayerOffset(200.0)
171
172        self.assertFalse(pcp.FindPrimIndex('/A'))
173        (pi, err) = pcp.ComputePrimIndex('/A')
174        refNode = pi.rootNode.children[0]
175        ref2Node = refNode.children[0]
176
177        self.assertEqual(refNode.mapToRoot.timeOffset, Sdf.LayerOffset(200.0))
178        self.assertEqual(ref2Node.mapToRoot.timeOffset, Sdf.LayerOffset(250.0))
179
180        # Change the layer offset in a referenced layer stack and again verify
181        # that the prim index is invalidated and the updated layer offset is
182        # taken into account.
183        refLayer = refNode.layerStack.layers[0]
184        with Pcp._TestChangeProcessor(pcp):
185            refLayer.subLayerOffsets[0] = Sdf.LayerOffset(200.0)
186
187            self.assertFalse(pcp.FindPrimIndex('/A'))
188            # Compute the prim index in the change processing block as the
189            # changed refLayer is only being held onto by the changes' lifeboat
190            # as its referencing prim index has been invalidated.
191            (pi, err) = pcp.ComputePrimIndex('/A')
192            refNode = pi.rootNode.children[0]
193            ref2Node = refNode.children[0]
194
195            self.assertEqual(refNode.mapToRoot.timeOffset, Sdf.LayerOffset(200.0))
196            self.assertEqual(ref2Node.mapToRoot.timeOffset, Sdf.LayerOffset(400.0))
197
198    def _RunTcpsChangesForLayer(self, pcpCache, layer, tcpsToExpecteOffsetsMap,
199                                affectedPaths):
200        """
201        Helper function for test_TcpsChanges to run a suite of various TCPS and
202        FPS metadata changes on a particular layer and verifying that the
203        correct change processing is run for every change.
204        """
205
206        # Helper function for a making a TCPS and/or FPS change and verify the
207        # expected change processing, prim index invalidation, and new computed
208        # values.
209        def _ChangeAndVerify(newValDict, expectSignificantChange, expectedTcps):
210
211            # Just verify we have a tcps value and/or fps value to set
212            self.assertTrue('tcps' in newValDict or 'fps' in newValDict)
213            with Pcp._TestChangeProcessor(pcpCache) as cp:
214                # Change block for when we're setting both fps and tcps
215                with Sdf.ChangeBlock():
216                    # Set the tcps value if present (None -> Clear)
217                    if 'tcps' in newValDict:
218                        val = newValDict['tcps']
219                        if val is None:
220                            layer.ClearTimeCodesPerSecond()
221                            self.assertFalse(layer.HasTimeCodesPerSecond())
222                        else:
223                            layer.timeCodesPerSecond = val
224                            self.assertTrue(layer.HasTimeCodesPerSecond())
225
226                    # Set the fps value if present (None -> Clear)
227                    if 'fps' in newValDict:
228                        val = newValDict['fps']
229                        if val is None:
230                            layer.ClearFramesPerSecond()
231                            self.assertFalse(layer.HasFramesPerSecond())
232                        else:
233                            layer.framesPerSecond = val
234                            self.assertTrue(layer.HasFramesPerSecond())
235
236                # Verify whether the change processor logged a significant
237                # change for the expected affected paths or not based on
238                # whether we expect a significant change.
239                if expectSignificantChange:
240                    self.assertEqual(cp.GetSignificantChanges(), affectedPaths)
241                    # A significant change will have invalidated our
242                    # prim's prim index.
243                    self.assertFalse(pcpCache.FindPrimIndex('/A'))
244                    # Recompute the new prim index
245                    (pi, err) = pcpCache.ComputePrimIndex('/A')
246                else:
247                    # No significant should leave our prim's prim index
248                    # valid.
249                    self.assertEqual(cp.GetSignificantChanges(), [])
250                    pi = pcpCache.FindPrimIndex('/A')
251                    self.assertTrue(pi)
252
253                refNode = pi.rootNode.children[0]
254                ref2Node = refNode.children[0]
255
256                # Verify the layer has the expected computed TCPS
257                self.assertEqual(layer.timeCodesPerSecond, expectedTcps)
258                # Verify the ref node offesets match the expected offsets
259                # for the layer's expected computed TCPS.
260                expectedOffsets = tcpsToExpecteOffsetsMap[expectedTcps]
261                self.assertEqual(refNode.mapToRoot.timeOffset,
262                                 expectedOffsets[0])
263                self.assertEqual(ref2Node.mapToRoot.timeOffset,
264                                 expectedOffsets[1])
265
266        # Expect the layer to start with no authored TCPS of FPS. Verify
267        # various changes to authored timeCodesPerSecond
268        self.assertFalse(layer.HasTimeCodesPerSecond())
269        self.assertFalse(layer.HasFramesPerSecond())
270        _ChangeAndVerify({'tcps' : 24.0}, False, 24.0)
271        _ChangeAndVerify({'tcps' : None}, False, 24.0)
272        _ChangeAndVerify({'tcps' : 48.0}, True, 48.0)
273        _ChangeAndVerify({'tcps' : 12.0}, True, 12.0)
274        _ChangeAndVerify({'tcps' : None}, True, 24.0)
275
276        # Expect the layer to start with no authored TCPS of FPS again.
277        # Verify various changes to authored framesPerSecond
278        self.assertFalse(layer.HasTimeCodesPerSecond())
279        self.assertFalse(layer.HasFramesPerSecond())
280        _ChangeAndVerify({'fps' : 24.0}, False, 24.0)
281        _ChangeAndVerify({'fps' : None}, False, 24.0)
282        _ChangeAndVerify({'fps' : 48.0}, True, 48.0)
283        _ChangeAndVerify({'fps' : 12.0}, True, 12.0)
284        _ChangeAndVerify({'fps' : None}, True, 24.0)
285
286        # Change the layer to have an authored non-default framesPerSecond.
287        # Verify various changes to timeCodesPerSecond.
288        _ChangeAndVerify({'fps' : 48.0}, True, 48.0)
289        self.assertFalse(layer.HasTimeCodesPerSecond())
290        self.assertTrue(layer.HasFramesPerSecond())
291        self.assertEqual(layer.timeCodesPerSecond, 48.0)
292        _ChangeAndVerify({'tcps' : 48.0}, False, 48.0)
293        _ChangeAndVerify({'tcps' : None}, False, 48.0)
294        _ChangeAndVerify({'tcps' : 12.0}, True, 12.0)
295        _ChangeAndVerify({'tcps' : 24.0}, True, 24.0)
296        _ChangeAndVerify({'tcps' : None}, True, 48.0)
297
298        # Change the layer to have an authored timeCodesPerSecond.
299        # Verify that various changes to framesPerSecond have no effect.
300        _ChangeAndVerify({'tcps' : 24.0, 'fps' : None}, True, 24.0)
301        self.assertTrue(layer.HasTimeCodesPerSecond())
302        self.assertFalse(layer.HasFramesPerSecond())
303        self.assertEqual(layer.timeCodesPerSecond, 24.0)
304        _ChangeAndVerify({'fps' : 24.0}, False, 24.0)
305        _ChangeAndVerify({'fps' : None}, False, 24.0)
306        _ChangeAndVerify({'fps' : 48.0}, False, 24.0)
307        _ChangeAndVerify({'fps' : 12.0}, False, 24.0)
308        _ChangeAndVerify({'fps' : None}, False, 24.0)
309
310        # Change the layer to start with an unauthored timeCodesPerSecond
311        # and a non-default framesPerSecond
312        # Verify various changes to timeCodesPerSecond and framesPerSecond
313        # at the same time.
314        _ChangeAndVerify({'tcps' : None, 'fps' : 48.0}, True, 48.0)
315        self.assertFalse(layer.HasTimeCodesPerSecond())
316        self.assertTrue(layer.HasFramesPerSecond())
317        self.assertEqual(layer.timeCodesPerSecond, 48.0)
318        _ChangeAndVerify({'tcps' : 48.0, 'fps' : None}, False, 48.0)
319        _ChangeAndVerify({'tcps' : None, 'fps' : 48.0}, False, 48.0)
320        _ChangeAndVerify({'tcps' : 24.0, 'fps' : None}, True, 24.0)
321        _ChangeAndVerify({'tcps' : None, 'fps' : 48.0}, True, 48.0)
322        _ChangeAndVerify({'tcps' : 12.0, 'fps' : None}, True, 12.0)
323        _ChangeAndVerify({'tcps' : 48.0, 'fps' : 12.0}, True, 48.0)
324        _ChangeAndVerify({'tcps' : 12.0, 'fps' : 48.0}, True, 12.0)
325        _ChangeAndVerify({'tcps' : None, 'fps' : 12.0}, False, 12.0)
326        _ChangeAndVerify({'tcps' : 24.0, 'fps' : 24.0}, True, 24.0)
327        _ChangeAndVerify({'tcps' : None, 'fps' : None}, False, 24.0)
328
329    @unittest.skipIf(
330        Tf.GetEnvSetting('PCP_DISABLE_TIME_SCALING_BY_LAYER_TCPS'),
331        "Test requires layer TCPS time scaling enabled")
332    def test_TcpsChanges(self):
333        """
334        Tests change processing for changes that affect the time codes per
335        second of all layers in the layer stacks of a PcpCache.
336        """
337
338        # Use the same layers as the sublayer offset test case.
339        rootLayerPath = 'TestSublayerOffsetChanges/root.sdf'
340        rootSublayerPath = 'TestSublayerOffsetChanges/root-sublayer.sdf'
341        refLayerPath = 'TestSublayerOffsetChanges/ref.sdf'
342        refSublayerPath = 'TestSublayerOffsetChanges/ref-sublayer.sdf'
343        ref2LayerPath = 'TestSublayerOffsetChanges/ref2.sdf'
344
345        rootLayer = Sdf.Layer.FindOrOpen(rootLayerPath)
346        sessionLayer = Sdf.Layer.CreateAnonymous()
347        pcp = Pcp.Cache(Pcp.LayerStackIdentifier(rootLayer, sessionLayer))
348
349        (pi, err) = pcp.ComputePrimIndex('/A')
350        self.assertTrue(pi)
351        self.assertEqual(len(err), 0)
352
353        rootSublayer = Sdf.Layer.Find(rootSublayerPath)
354        refLayer = Sdf.Layer.Find(refLayerPath)
355        refSublayer = Sdf.Layer.Find(refSublayerPath)
356        ref2Layer = Sdf.Layer.Find(ref2LayerPath)
357
358        # Verify the expected structure of the test asset. It should simply be
359        # a chain of two references, with layer offsets of 100.0 and 50.0
360        # respectively.
361        self.assertEqual(pi.rootNode.layerStack.layers,
362                         [sessionLayer, rootLayer, rootSublayer])
363
364        refNode = pi.rootNode.children[0]
365        self.assertEqual(refNode.layerStack.layers, [refLayer, refSublayer])
366        self.assertEqual(refNode.arcType, Pcp.ArcTypeReference)
367        self.assertEqual(refNode.mapToRoot.timeOffset, Sdf.LayerOffset(100.0))
368        for layer in refNode.layerStack.layers:
369            self.assertFalse(layer.HasTimeCodesPerSecond())
370            self.assertEqual(layer.timeCodesPerSecond, 24.0)
371
372        ref2Node = refNode.children[0]
373        self.assertEqual(ref2Node.layerStack.layers, [ref2Layer])
374        self.assertEqual(ref2Node.arcType, Pcp.ArcTypeReference)
375        self.assertEqual(ref2Node.mapToRoot.timeOffset, Sdf.LayerOffset(150.0))
376        for layer in ref2Node.layerStack.layers:
377            self.assertFalse(layer.HasTimeCodesPerSecond())
378            self.assertEqual(layer.timeCodesPerSecond, 24.0)
379
380        # Run the TCPS change suite on the root layer.
381        tcpsToOffsets = {12.0: (Sdf.LayerOffset(100.0, 0.5),
382                                Sdf.LayerOffset(125.0, 0.5)),
383                         24.0: (Sdf.LayerOffset(100.0),
384                                Sdf.LayerOffset(150.0)),
385                         48.0: (Sdf.LayerOffset(100.0, 2.0),
386                                Sdf.LayerOffset(200.0, 2.0))
387                         }
388        self._RunTcpsChangesForLayer(pcp, rootLayer, tcpsToOffsets, ['/'])
389
390        # Run the TCPS change suite on the first reference layer.
391        tcpsToOffsets = {12.0: (Sdf.LayerOffset(100.0, 2.0),
392                                Sdf.LayerOffset(200.0)),
393                         24.0: (Sdf.LayerOffset(100.0),
394                                Sdf.LayerOffset(150.0)),
395                         48.0: (Sdf.LayerOffset(100.0, 0.5),
396                                Sdf.LayerOffset(125.0))
397                         }
398        self._RunTcpsChangesForLayer(pcp, refLayer, tcpsToOffsets, ['/A'])
399
400        # Run the TCPS change suite on the second reference layer.
401        tcpsToOffsets = {12.0: (Sdf.LayerOffset(100.0),
402                                Sdf.LayerOffset(150.0, 2.0)),
403                         24.0: (Sdf.LayerOffset(100.0),
404                                Sdf.LayerOffset(150.0)),
405                         48.0: (Sdf.LayerOffset(100.0),
406                                Sdf.LayerOffset(150.0, 0.5))
407                         }
408        self._RunTcpsChangesForLayer(pcp, ref2Layer, tcpsToOffsets, ['/A'])
409
410        # Run the TCPS change suite on the sublayers of the root and reference
411        # layers. In the particular setup of these layer, TCPS of either
412        # sublayer doesn't change the layer offsets applied to the reference
413        # nodes, but will still cause change management to report significant
414        # changes to prim indexes.
415        tcpsToOffsets = {12.0: (Sdf.LayerOffset(100.0),
416                                Sdf.LayerOffset(150.0)),
417                         24.0: (Sdf.LayerOffset(100.0),
418                                Sdf.LayerOffset(150.0)),
419                         48.0: (Sdf.LayerOffset(100.0),
420                                Sdf.LayerOffset(150.0))
421                         }
422        self._RunTcpsChangesForLayer(pcp, rootSublayer, tcpsToOffsets, ['/'])
423        self._RunTcpsChangesForLayer(pcp, refSublayer, tcpsToOffsets, ['/A'])
424
425        # Run the TCPS change suite on the session layer.
426        tcpsToOffsets = {12.0: (Sdf.LayerOffset(50.0, 0.5),
427                                Sdf.LayerOffset(75.0, 0.5)),
428                         24.0: (Sdf.LayerOffset(100.0),
429                                Sdf.LayerOffset(150.0)),
430                         48.0: (Sdf.LayerOffset(200.0, 2.0),
431                                Sdf.LayerOffset(300.0, 2.0))
432                         }
433        self._RunTcpsChangesForLayer(pcp, sessionLayer, tcpsToOffsets, ['/'])
434
435        # Special cases for the session layer when root layer has a tcps value
436        rootLayer.timeCodesPerSecond = 24.0
437        self.assertTrue(rootLayer.HasTimeCodesPerSecond())
438        self.assertFalse(sessionLayer.HasTimeCodesPerSecond())
439        with Pcp._TestChangeProcessor(pcp) as cp:
440            # Set the session layer's FPS. This will change the session layer's
441            # computed TCPS and is a significant change even though the overall
442            # TCPS of the root layer stack is 24 as it is authored on the root
443            # layer.
444            sessionLayer.framesPerSecond = 48.0
445            self.assertEqual(sessionLayer.timeCodesPerSecond, 48.0)
446            self.assertEqual(cp.GetSignificantChanges(), ['/'])
447            self.assertFalse(pcp.FindPrimIndex('/A'))
448            # Recompute the new prim index
449            (pi, err) = pcp.ComputePrimIndex('/A')
450            refNode = pi.rootNode.children[0]
451            ref2Node = refNode.children[0]
452            # The reference layer offsets are still the same as authored as
453            # the root layer TCPS still matches its layer stack's overall TCPS
454            self.assertEqual(refNode.mapToRoot.timeOffset,
455                             Sdf.LayerOffset(100.0))
456            self.assertEqual(ref2Node.mapToRoot.timeOffset,
457                             Sdf.LayerOffset(150.0))
458
459        # Continuing from the previous case, root layer has TCPS set to 24 and
460        # session layer has FPS set to 48. Now we set the session TCPS to 48.
461        # While this does not cause a TCPS change to session layer taken by
462        # itself, this does mean that the session now overrides the overall
463        # TCPS of the layer stack which used to come the root. We verify here
464        # that this is a significant change and that it scales the layer offsets
465        # to the references.
466        self.assertFalse(sessionLayer.HasTimeCodesPerSecond())
467        with Pcp._TestChangeProcessor(pcp) as cp:
468            sessionLayer.timeCodesPerSecond = 48.0
469            self.assertEqual(sessionLayer.timeCodesPerSecond, 48.0)
470            self.assertEqual(cp.GetSignificantChanges(), ['/'])
471            self.assertFalse(pcp.FindPrimIndex('/A'))
472            # Recompute the new prim index
473            (pi, err) = pcp.ComputePrimIndex('/A')
474            refNode = pi.rootNode.children[0]
475            ref2Node = refNode.children[0]
476            self.assertEqual(refNode.mapToRoot.timeOffset,
477                             Sdf.LayerOffset(200.0, 2.0))
478            self.assertEqual(ref2Node.mapToRoot.timeOffset,
479                             Sdf.LayerOffset(300.0, 2.0))
480
481        # And as a parallel to the previous case, we now clear the session TCPS
482        # again. This is still no effective change to the session layer TCPS
483        # itself, but root layer's TCPS is once again the overall layer stack
484        # TCPS. This is significant changes and the layer offsets return to
485        # their original values.
486        with Pcp._TestChangeProcessor(pcp) as cp:
487            sessionLayer.ClearTimeCodesPerSecond()
488            self.assertEqual(sessionLayer.timeCodesPerSecond, 48.0)
489            self.assertEqual(cp.GetSignificantChanges(), ['/'])
490            self.assertFalse(pcp.FindPrimIndex('/A'))
491            # Recompute the new prim index
492            (pi, err) = pcp.ComputePrimIndex('/A')
493            refNode = pi.rootNode.children[0]
494            ref2Node = refNode.children[0]
495            # The reference layer offsets are still the same as authored as
496            # the root layer TCPS still matches its layer stack's overall TCPS
497            self.assertEqual(refNode.mapToRoot.timeOffset,
498                             Sdf.LayerOffset(100.0))
499            self.assertEqual(ref2Node.mapToRoot.timeOffset,
500                             Sdf.LayerOffset(150.0))
501
502        # One more special case. Neither session or root layers have TCPS set.
503        # Root layer has FPS set to 48, session layer has FPS set to 24. Overall
504        # computed TCPS of the layer stack will be 24, matching session FPS.
505        # We then author a TCPS value of 48 to the root layer, matching its FPS
506        # value. There is no effective change to the root layer's TCPS itself
507        # but we do end up with a significant change as the overall layer stack
508        # TCPS will now compute to 48.
509        sessionLayer.ClearTimeCodesPerSecond()
510        rootLayer.ClearTimeCodesPerSecond()
511        sessionLayer.framesPerSecond = 24.0
512        rootLayer.framesPerSecond = 48.0
513        with Pcp._TestChangeProcessor(pcp) as cp:
514            rootLayer.timeCodesPerSecond = 48.0
515            self.assertEqual(rootLayer.timeCodesPerSecond, 48.0)
516            self.assertEqual(cp.GetSignificantChanges(), ['/'])
517            self.assertFalse(pcp.FindPrimIndex('/A'))
518            # Recompute the new prim index
519            (pi, err) = pcp.ComputePrimIndex('/A')
520            refNode = pi.rootNode.children[0]
521            ref2Node = refNode.children[0]
522            self.assertEqual(refNode.mapToRoot.timeOffset,
523                             Sdf.LayerOffset(100.0, 2.0))
524            self.assertEqual(ref2Node.mapToRoot.timeOffset,
525                             Sdf.LayerOffset(200.0, 2.0))
526
527
528    def test_DefaultReferenceTargetChanges(self):
529        # create a layer, set DefaultPrim, then reference it.
530        targLyr = Sdf.Layer.CreateAnonymous()
531
532        def makePrim(name):
533            primSpec = Sdf.CreatePrimInLayer(targLyr, name)
534            primSpec.specifier = Sdf.SpecifierDef
535
536        makePrim('target1')
537        makePrim('target2')
538
539        targLyr.defaultPrim = 'target1'
540
541        def _Test(referencingLayer):
542            # Make a PcpCache.
543            pcp = Pcp.Cache(Pcp.LayerStackIdentifier(referencingLayer))
544
545            (pi, err) = pcp.ComputePrimIndex('/source')
546
547            # First child node should be the referenced target1.
548            self.assertEqual(pi.rootNode.children[0].path, '/target1')
549
550            # Now clear defaultPrim.  This should issue an error and
551            # fail to pick up the referenced prim.
552            with Pcp._TestChangeProcessor(pcp):
553                targLyr.ClearDefaultPrim()
554
555            (pi, err) = pcp.ComputePrimIndex('/source')
556            self.assertTrue(isinstance(err[0], Pcp.ErrorUnresolvedPrimPath))
557            # If the reference to the defaultPrim is an external reference,
558            # the one child node should be the pseudoroot dependency placeholder.
559            # If the reference is an internal reference, that dependency
560            # placeholder is unneeded.
561            if referencingLayer != targLyr:
562                self.assertEqual(len(pi.rootNode.children), 1)
563                self.assertEqual(pi.rootNode.children[0].path, '/')
564
565            # Change defaultPrim to other target.  This should pick
566            # up the reference again, but to the new prim target2.
567            with Pcp._TestChangeProcessor(pcp):
568                targLyr.defaultPrim = 'target2'
569
570            (pi, err) = pcp.ComputePrimIndex('/source')
571            self.assertEqual(len(err), 0)
572            self.assertEqual(pi.rootNode.children[0].path, '/target2')
573
574            # Reset defaultPrim to original target
575            targLyr.defaultPrim = 'target1'
576
577        # Test external reference case where some other layer references
578        # a layer with defaultPrim specified.
579        srcLyr = Sdf.Layer.CreateAnonymous()
580        srcPrimSpec = Sdf.CreatePrimInLayer(srcLyr, '/source')
581        srcPrimSpec.referenceList.Add(Sdf.Reference(targLyr.identifier))
582        _Test(srcLyr)
583
584        # Test internal reference case where a prim in the layer with defaultPrim
585        # specified references the same layer.
586        srcPrimSpec = Sdf.CreatePrimInLayer(targLyr, '/source')
587        srcPrimSpec.referenceList.Add(Sdf.Reference())
588        _Test(targLyr)
589
590    def test_InternalReferenceChanges(self):
591        rootLayer = Sdf.Layer.CreateAnonymous()
592
593        Sdf.PrimSpec(rootLayer, 'target1', Sdf.SpecifierDef)
594        Sdf.PrimSpec(rootLayer, 'target2', Sdf.SpecifierDef)
595
596        srcPrimSpec = Sdf.PrimSpec(rootLayer, 'source', Sdf.SpecifierDef)
597        srcPrimSpec.referenceList.Add(Sdf.Reference(primPath = '/target1'))
598
599        # Initially, the prim index for /source should contain a single
600        # reference node to /target1 in rootLayer.
601        pcp = Pcp.Cache(Pcp.LayerStackIdentifier(rootLayer))
602        (pi, err) = pcp.ComputePrimIndex('/source')
603
604        self.assertEqual(len(err), 0)
605        self.assertEqual(pi.rootNode.children[0].layerStack.identifier.rootLayer,
606                    rootLayer)
607        self.assertEqual(pi.rootNode.children[0].path, '/target1')
608
609        # Modify the internal reference to point to /target2 and verify the
610        # reference node is updated.
611        with Pcp._TestChangeProcessor(pcp):
612            srcPrimSpec.referenceList.addedItems[0] = \
613                Sdf.Reference(primPath = '/target2')
614
615        (pi, err) = pcp.ComputePrimIndex('/source')
616
617        self.assertEqual(len(err), 0)
618        self.assertEqual(pi.rootNode.children[0].layerStack.identifier.rootLayer,
619                    rootLayer)
620        self.assertEqual(pi.rootNode.children[0].path, '/target2')
621
622        # Clear out all references and verify that the prim index contains no
623        # reference nodes.
624        with Pcp._TestChangeProcessor(pcp):
625            srcPrimSpec.referenceList.ClearEdits()
626
627        (pi, err) = pcp.ComputePrimIndex('/source')
628
629        self.assertEqual(len(err), 0)
630        self.assertEqual(len(pi.rootNode.children), 0)
631
632    def test_VariantChanges(self):
633        rootLayer = Sdf.Layer.CreateAnonymous()
634        modelSpec = Sdf.PrimSpec(rootLayer, 'Variant', Sdf.SpecifierDef)
635
636        pcp = Pcp.Cache(Pcp.LayerStackIdentifier(rootLayer), usd=True)
637
638        # Test changes that are emitted as a variant set and variant
639        # are created.
640
641        pcp.ComputePrimIndex('/Variant')
642        with Pcp._TestChangeProcessor(pcp) as cp:
643            varSetSpec = Sdf.VariantSetSpec(modelSpec, 'test')
644            self.assertEqual(cp.GetSignificantChanges(), ['/Variant'])
645            self.assertEqual(cp.GetSpecChanges(), [])
646            self.assertEqual(cp.GetPrimChanges(), [])
647
648        pcp.ComputePrimIndex('/Variant')
649        with Pcp._TestChangeProcessor(pcp) as cp:
650            modelSpec.variantSelections['test'] = 'A'
651            self.assertEqual(cp.GetSignificantChanges(), ['/Variant'])
652            self.assertEqual(cp.GetSpecChanges(), [])
653            self.assertEqual(cp.GetPrimChanges(), [])
654
655        pcp.ComputePrimIndex('/Variant')
656        with Pcp._TestChangeProcessor(pcp) as cp:
657            modelSpec.variantSetNameList.Add('test')
658            self.assertEqual(cp.GetSignificantChanges(), ['/Variant'])
659            self.assertEqual(cp.GetSpecChanges(), [])
660            self.assertEqual(cp.GetPrimChanges(), [])
661
662        pcp.ComputePrimIndex('/Variant')
663        with Pcp._TestChangeProcessor(pcp) as cp:
664            varSpec = Sdf.VariantSpec(varSetSpec, 'A')
665            self.assertEqual(cp.GetSignificantChanges(), [])
666            self.assertEqual(cp.GetSpecChanges(), ['/Variant'])
667            # Creating the variant spec adds an inert spec to /Variant's prim
668            # stack but does not require rebuilding /Variant's prim index to
669            # account for the new variant node.
670            self.assertEqual(cp.GetPrimChanges(), [])
671
672        pcp.ComputePrimIndex('/Variant')
673        with Pcp._TestChangeProcessor(pcp) as cp:
674            varSpec.primSpec.referenceList.Add(
675                Sdf.Reference('./dummy.sdf', '/Dummy'))
676            self.assertEqual(cp.GetSignificantChanges(), ['/Variant'])
677            self.assertEqual(cp.GetSpecChanges(), [])
678            self.assertEqual(cp.GetPrimChanges(), [])
679
680    def test_InstancingChanges(self):
681        refLayer = Sdf.Layer.CreateAnonymous()
682        refParentSpec = Sdf.PrimSpec(refLayer, 'Parent', Sdf.SpecifierDef)
683        refChildSpec = Sdf.PrimSpec(refParentSpec, 'RefChild', Sdf.SpecifierDef)
684
685        rootLayer = Sdf.Layer.CreateAnonymous()
686        parentSpec = Sdf.PrimSpec(rootLayer, 'Parent', Sdf.SpecifierOver)
687        parentSpec.referenceList.Add(
688            Sdf.Reference(refLayer.identifier, '/Parent'))
689        childSpec = Sdf.PrimSpec(parentSpec, 'DirectChild', Sdf.SpecifierDef)
690
691        # Instancing is only enabled in Usd mode.
692        pcp = Pcp.Cache(Pcp.LayerStackIdentifier(rootLayer), usd=True)
693
694        # /Parent is initially not tagged as an instance, so we should
695        # see both RefChild and DirectChild name children.
696        (pi, err) = pcp.ComputePrimIndex('/Parent')
697        self.assertEqual(len(err), 0)
698        self.assertFalse(pi.IsInstanceable())
699        self.assertEqual(pi.ComputePrimChildNames(), (['RefChild', 'DirectChild'], []))
700
701        with Pcp._TestChangeProcessor(pcp) as cp:
702            parentSpec.instanceable = True
703            self.assertEqual(cp.GetSignificantChanges(), ['/Parent'])
704            self.assertEqual(cp.GetSpecChanges(), [])
705            self.assertEqual(cp.GetPrimChanges(), [])
706
707        # After being made an instance, DirectChild should no longer
708        # be reported as a name child since instances may not introduce
709        # new prims locally.
710        (pi, err) = pcp.ComputePrimIndex('/Parent')
711        self.assertEqual(len(err), 0)
712        self.assertTrue(pi.IsInstanceable())
713        self.assertEqual(pi.ComputePrimChildNames(), (['RefChild'], []))
714
715        with Pcp._TestChangeProcessor(pcp) as cp:
716            parentSpec.instanceable = False
717            self.assertEqual(cp.GetSignificantChanges(), ['/Parent'])
718            self.assertEqual(cp.GetSpecChanges(), [])
719            self.assertEqual(cp.GetPrimChanges(), [])
720
721        # Flipping the instance flag back should restore us to the
722        # original state.
723        (pi, err) = pcp.ComputePrimIndex('/Parent')
724        self.assertEqual(len(err), 0)
725        self.assertFalse(pi.IsInstanceable())
726        self.assertEqual(pi.ComputePrimChildNames(), (['RefChild', 'DirectChild'], []))
727
728    def test_InstancingChangesForSubrootArcs(self):
729        # First layer: Build up the first reference structure
730        refLayer = Sdf.Layer.CreateAnonymous()
731        # Base spec name space hierarchy: /Base/Child/BaseLeaf.
732        baseSpec = Sdf.PrimSpec(refLayer, 'Base', Sdf.SpecifierDef)
733        baseChildSpec = Sdf.PrimSpec(baseSpec, 'Child', Sdf.SpecifierDef)
734        baseLeafSpec = Sdf.PrimSpec(baseChildSpec, 'BaseLeaf', Sdf.SpecifierDef)
735
736        # /A references /Base
737        refASpec = Sdf.PrimSpec(refLayer, 'A', Sdf.SpecifierDef)
738        refASpec.referenceList.Add(Sdf.Reference('', '/Base'))
739
740        # /B references /A
741        refBSpec = Sdf.PrimSpec(refLayer, 'B', Sdf.SpecifierDef)
742        refBSpec.referenceList.Add(Sdf.Reference('', '/A'))
743
744        # Second layer: Same structure as layer with incmented names for clarity
745        # when both layers have prims referenced
746        refLayer2 = Sdf.Layer.CreateAnonymous()
747        base2Spec = Sdf.PrimSpec(refLayer2, 'Base2', Sdf.SpecifierDef)
748        base2ChildSpec = Sdf.PrimSpec(base2Spec, 'Child', Sdf.SpecifierDef)
749        base2LeafSpec = Sdf.PrimSpec(base2ChildSpec, 'Base2Leaf',
750                                     Sdf.SpecifierDef)
751
752        # /A2 references /Base2
753        refA2Spec = Sdf.PrimSpec(refLayer2, 'A2', Sdf.SpecifierDef)
754        refA2Spec.referenceList.Add(Sdf.Reference('', '/Base2'))
755
756        # /B2 references /A2
757        refB2Spec = Sdf.PrimSpec(refLayer2, 'B2', Sdf.SpecifierDef)
758        refB2Spec.referenceList.Add(Sdf.Reference('', '/A2'))
759
760        # Root layer:
761        # Instance spec namespace hierarchy: /Instance/Child/InstanceLeaf
762        # /Instance references /B2.
763        # /Instance/Child subroot references /B/Child
764        #
765        # What this gives us is the following prim index graph for
766        # /Instance/Child:
767        #
768        # /Instance/Child --> /B/Child -a-> /A/Child -a-> /Base/Child
769        #         |
770        #         +-a-> /B2/Child -a-> /A2/Child -a-> /Base2/Child
771        #
772        # All reference arcs are "ancestral" except the direct reference from
773        # /Instance/Child to /B/Child. Ancestral arcs under a direct arc are
774        # treated differently for instancing than normal ancestral arcs.
775        layer = Sdf.Layer.CreateAnonymous()
776        instanceSpec = Sdf.PrimSpec(layer, 'Instance', Sdf.SpecifierDef)
777        instanceSpec.referenceList.Add(
778            Sdf.Reference(refLayer2.identifier, '/B2'))
779        instanceChildSpec = Sdf.PrimSpec(instanceSpec, 'Child',
780                                         Sdf.SpecifierDef)
781        instanceChildSpec.referenceList.Add(
782            Sdf.Reference(refLayer.identifier, '/B/Child'))
783        instanceLeafSpec = Sdf.PrimSpec(instanceChildSpec, 'InstanceLeaf',
784                                        Sdf.SpecifierDef)
785
786        # Instancing is only enabled in Usd mode.
787        pcp = Pcp.Cache(Pcp.LayerStackIdentifier(layer), usd=True)
788
789        # Compute our initial prim index. It is not yet instanceable and
790        # all leaf nodes appear when computing prim child names.
791        (pi, err) = pcp.ComputePrimIndex('/Instance/Child')
792        self.assertEqual(len(err), 0)
793        self.assertFalse(pi.IsInstanceable())
794        self.assertEqual(pi.ComputePrimChildNames(),
795                         (['Base2Leaf', 'BaseLeaf', 'InstanceLeaf'], []))
796
797        # Helper context for verifying the change processing of the changes
798        # affecting /Instance/Child
799        @contextmanager
800        def _VerifyChanges(significant=False, spec=False, prim=False):
801            primIndexPath = '/Instance/Child'
802            # Verify that we have a computed prim index for /Instance/Child
803            # before making the change .
804            self.assertTrue(pcp.FindPrimIndex(primIndexPath))
805            with Pcp._TestChangeProcessor(pcp) as cp:
806                try:
807                    yield cp
808                finally:
809                    self.assertEqual(cp.GetSignificantChanges(),
810                                     [primIndexPath] if significant else [])
811                    self.assertEqual(cp.GetSpecChanges(),
812                                     [primIndexPath] if spec else [])
813                    self.assertEqual(cp.GetPrimChanges(),
814                                     [primIndexPath] if prim else [])
815                    # Significant and prim changes do invalidate the prim index.
816                    if significant or prim:
817                        self.assertFalse(pcp.FindPrimIndex(primIndexPath))
818                    else:
819                        self.assertTrue(pcp.FindPrimIndex(primIndexPath))
820
821        # Add Child spec to /A. This is just a spec change to /Instance/Child
822        # as /Instance/Child is not instanceable yet.
823        with _VerifyChanges(spec=True):
824            Sdf.PrimSpec(refASpec, 'Child', Sdf.SpecifierOver)
825
826        # Add Child spec to /A2. This is just a spec change to /Instance/Child.
827        with _VerifyChanges(spec=True):
828            Sdf.PrimSpec(refA2Spec, 'Child', Sdf.SpecifierOver)
829
830        # Delete both Child specs we just added.
831        with _VerifyChanges(spec=True):
832            del refASpec.nameChildren['Child']
833        with _VerifyChanges(spec=True):
834            del refA2Spec.nameChildren['Child']
835
836        # Now set /Base/Child to instanceable which is always a significant
837        # change.
838        with _VerifyChanges(significant=True):
839            baseChildSpec.instanceable = True
840
841        # /Instance/Child is now instanceable. BaseLeaf is still returned by
842        # ComputePrimChildren because the ancestral node that provides it comes
843        # from a subtree composed for a direct subroot reference arc so it is
844        # part of the instance. Base2Leaf comes from an actual ancestral arc so
845        # it gets skipped when computing children as does the local child
846        # opinion for InstanceLeaf.
847        (pi, err) = pcp.ComputePrimIndex('/Instance/Child')
848        self.assertEqual(len(err), 0)
849        self.assertTrue(pi.IsInstanceable())
850        self.assertEqual(pi.ComputePrimChildNames(), (['BaseLeaf'], []))
851
852        # Add Child spec to /A2 again now that /Instance/Child is instanceable.
853        # This is still just a spec change to /Instance/Child as true ancestral
854        # nodes are ignored by the instance key.
855        with _VerifyChanges(spec=True):
856            Sdf.PrimSpec(refA2Spec, 'Child', Sdf.SpecifierOver)
857
858        # Add Child spec to /A again. This is a significant change as /A/Child
859        # is an ancestral node that's part of a direct arc's subtree and is
860        # part of the instance key.
861        with _VerifyChanges(significant=True):
862            Sdf.PrimSpec(refASpec, 'Child', Sdf.SpecifierOver)
863
864        (pi, err) = pcp.ComputePrimIndex('/Instance/Child')
865        self.assertEqual(len(err), 0)
866        self.assertTrue(pi.IsInstanceable())
867        self.assertEqual(pi.ComputePrimChildNames(), (['BaseLeaf'], []))
868
869    def test_InertPrimChanges(self):
870        refLayer = Sdf.Layer.CreateAnonymous()
871        refParentSpec = Sdf.PrimSpec(refLayer, 'Parent', Sdf.SpecifierDef)
872        refChildSpec = Sdf.PrimSpec(refParentSpec, 'Child', Sdf.SpecifierDef)
873
874        rootLayer = Sdf.Layer.CreateAnonymous()
875        parentSpec = Sdf.PrimSpec(rootLayer, 'Parent', Sdf.SpecifierOver)
876        parentSpec.referenceList.Add(
877            Sdf.Reference(refLayer.identifier, '/Parent'))
878
879        pcp = Pcp.Cache(Pcp.LayerStackIdentifier(rootLayer))
880
881        # Adding an empty over to a prim that already exists (has specs)
882        # is an insignificant change.
883        (pi, err) = pcp.ComputePrimIndex('/Parent/Child')
884        self.assertEqual(err, [])
885        with Pcp._TestChangeProcessor(pcp) as cp:
886            Sdf.CreatePrimInLayer(rootLayer, '/Parent/Child')
887            self.assertEqual(cp.GetSignificantChanges(), [])
888            self.assertEqual(cp.GetSpecChanges(), ['/Parent/Child'])
889            self.assertEqual(cp.GetPrimChanges(), [])
890
891        # Adding an empty over as the first spec for a prim is a
892        # a significant change, even if we haven't computed a prim index
893        # for that path yet.
894        with Pcp._TestChangeProcessor(pcp) as cp:
895            Sdf.CreatePrimInLayer(rootLayer, '/Parent/NewChild')
896            self.assertEqual(cp.GetSignificantChanges(), ['/Parent/NewChild'])
897            self.assertEqual(cp.GetSpecChanges(), [])
898            self.assertEqual(cp.GetPrimChanges(), [])
899
900        (pi, err) = pcp.ComputePrimIndex('/Parent/NewChild2')
901        self.assertEqual(err, [])
902        with Pcp._TestChangeProcessor(pcp) as cp:
903            Sdf.CreatePrimInLayer(rootLayer, '/Parent/NewChild2')
904            self.assertEqual(cp.GetSignificantChanges(), ['/Parent/NewChild2'])
905            self.assertEqual(cp.GetSpecChanges(), [])
906            self.assertEqual(cp.GetPrimChanges(), [])
907
908    def test_InertPrimRemovalChanges(self):
909        subLayer = Sdf.Layer.CreateAnonymous()
910        subParentSpec = Sdf.PrimSpec(subLayer, 'Parent', Sdf.SpecifierDef)
911        subChildSpec = Sdf.PrimSpec(subParentSpec, 'Child', Sdf.SpecifierDef)
912        subAttrSpec = Sdf.AttributeSpec(subChildSpec, 'attr', Sdf.ValueTypeNames.Double)
913        subAttrSpec.default = 1.0
914
915        rootLayer = Sdf.Layer.CreateAnonymous()
916        rootParentSpec = Sdf.PrimSpec(rootLayer, 'Parent', Sdf.SpecifierOver)
917        rootChildSpec = Sdf.PrimSpec(rootParentSpec, 'Child', Sdf.SpecifierOver)
918        rootAttrSpec = Sdf.AttributeSpec(rootChildSpec, 'attr', Sdf.ValueTypeNames.Double)
919        rootLayer.subLayerPaths.append(subLayer.identifier)
920
921        pcp = Pcp.Cache(Pcp.LayerStackIdentifier(rootLayer))
922
923        (pi, err) = pcp.ComputePrimIndex('/Parent')
924        self.assertEqual(err, [])
925        self.assertEqual(pi.primStack, [rootParentSpec, subParentSpec])
926
927        (pi, err) = pcp.ComputePrimIndex('/Parent/Child')
928        self.assertEqual(err, [])
929        self.assertEqual(pi.primStack, [rootChildSpec, subChildSpec])
930
931        (pi, err) = pcp.ComputePropertyIndex('/Parent/Child.attr')
932        self.assertEqual(err, [])
933        self.assertEqual(pi.propertyStack, [rootAttrSpec, subAttrSpec])
934
935        revCount = pcp.GetUsedLayersRevision()
936        with Pcp._TestChangeProcessor(pcp) as cp:
937            del rootLayer.pseudoRoot.nameChildren['Parent']
938            self.assertFalse(rootParentSpec)
939            self.assertFalse(rootChildSpec)
940            self.assertFalse(rootAttrSpec)
941
942            self.assertEqual(cp.GetSignificantChanges(), [])
943            self.assertEqual(cp.GetSpecChanges(),
944                             ['/Parent', '/Parent/Child', '/Parent/Child.attr'])
945            self.assertEqual(cp.GetPrimChanges(), [])
946
947        self.assertEqual(revCount, pcp.GetUsedLayersRevision())
948
949        (pi, err) = pcp.ComputePrimIndex('/Parent')
950        self.assertEqual(err, [])
951        self.assertEqual(pi.primStack, [subParentSpec])
952
953        (pi, err) = pcp.ComputePrimIndex('/Parent/Child')
954        self.assertEqual(err, [])
955        self.assertEqual(pi.primStack, [subChildSpec])
956
957        (pi, err) = pcp.ComputePropertyIndex('/Parent/Child.attr')
958        self.assertEqual(err, [])
959        self.assertEqual(pi.propertyStack, [subAttrSpec])
960
961if __name__ == "__main__":
962    unittest.main()
963