Пример #1
0
    def testShader(self):

        shader = self.compileShader(
            os.path.dirname(__file__) + "/shaders/constant.osl")

        node = GafferOSL.OSLLight()
        self.assertIsInstance(node, GafferScene.Light)

        node.loadShader(shader)
        self.assertEqual(node["parameters"].keys(), ["Cs"])

        cs = GafferTest.CapturingSlot(node.plugDirtiedSignal())
        node["parameters"]["Cs"].setValue(imath.Color3f(1, 0, 0))
        self.assertIn(node["out"]["attributes"], {x[0] for x in cs})

        a = node["out"].attributes("/light")
        self.assertIn("osl:light", a)
Пример #2
0
    def testMetadataSignalling(self):

        s = Gaffer.ScriptNode()
        s["r"] = Gaffer.Random()

        b = Gaffer.Box.create(s, Gaffer.StandardSet([s["r"]]))
        p = b.promotePlug(b["r"]["floatRange"])

        cs = GafferTest.CapturingSlot(
            Gaffer.Metadata.plugValueChangedSignal(b))

        Gaffer.Metadata.registerValue(p, "description", "hello")

        self.assertEqual(len(cs), 1)
        self.assertEqual(
            cs[0], (p, "description",
                    Gaffer.Metadata.ValueChangedReason.InstanceRegistration))
Пример #3
0
    def testSetDriver(self):

        semaphore = threading.Semaphore(0)
        imageReceivedConnection = GafferImage.Display.imageReceivedSignal(
        ).connect(
            functools.partial(self.__incrementUpdateCountAndRelease,
                              semaphore=semaphore))

        driversCreated = GafferTest.CapturingSlot(
            GafferImage.Display.driverCreatedSignal())

        server = IECore.DisplayDriverServer()
        cortexWindow = IECore.Box2i(IECore.V2i(0), IECore.V2i(99))
        gafferWindow = IECore.Box2i(IECore.V2i(0), IECore.V2i(100))
        driver = IECore.ClientDisplayDriver(
            cortexWindow, cortexWindow, ["Y"], {
                "displayHost": "localHost",
                "displayPort": str(server.portNumber()),
                "remoteDisplayType": "GafferImage::GafferDisplayDriver",
            })

        display = GafferImage.Display()
        self.assertTrue(display.getDriver() is None)

        self.assertTrue(len(driversCreated), 1)
        display.setDriver(driversCreated[0][0])
        self.assertTrue(display.getDriver().isSame(driversCreated[0][0]))

        self.__sendBucket(
            driver, cortexWindow,
            IECore.FloatVectorData([0.5] * gafferWindow.size().x *
                                   gafferWindow.size().y))

        driver.imageClose()
        semaphore.acquire()

        self.assertEqual(
            display["out"]["format"].getValue().getDisplayWindow(),
            gafferWindow)
        self.assertEqual(display["out"]["dataWindow"].getValue(), gafferWindow)
        self.assertEqual(display["out"]["channelNames"].getValue(),
                         IECore.StringVectorData(["Y"]))
        self.assertEqual(
            display["out"].channelData("Y", IECore.V2i(0)),
            IECore.FloatVectorData([0.5] * GafferImage.ImagePlug.tileSize() *
                                   GafferImage.ImagePlug.tileSize()))
Пример #4
0
	def testErrorSignalledOnIntermediateNodes( self ) :

		nodes = [ GafferTest.BadNode() ]
		for i in range( 0, 10 ) :

			nodes.append( GafferTest.AddNode() )
			nodes[-1]["op1"].setInput(
				nodes[-2]["sum"] if i != 0 else nodes[-2]["out3"]
			)

		slots = [ GafferTest.CapturingSlot( n.errorSignal() ) for n in nodes ]

		self.assertRaises( RuntimeError, nodes[-1]["sum"].getValue )
		for i, slot in enumerate( slots ) :
			self.assertEqual( len( slot ), 1 )
			self.assertTrue( slot[0][0].isSame( nodes[i]["out3"] if i == 0 else nodes[i]["sum"] ) )
			self.assertTrue( slot[0][1].isSame( nodes[0]["out3"] ) )
Пример #5
0
	def testChanged( self ) :

		c = Gaffer.Context()
		c["test"] = IECore.StringVectorData( [ "one" ] )
		h = c.hash()

		cs = GafferTest.CapturingSlot( c.changedSignal() )

		d = c.get( "test", _copy = False ) # dangerous! the context won't know if we make changes
		d.append( "two" )
		self.assertEqual( c.get( "test" ), IECore.StringVectorData( [ "one", "two" ] ) )
		self.assertEqual( len( cs ), 0 )

		c.changed( "test" ) # let the context know what we've been up to
		self.assertEqual( len( cs ), 1 )
		self.assertEqual( cs[0], ( c, "test" ) )
		self.assertNotEqual( c.hash(), h )
Пример #6
0
	def testValuesOutsideRangeAreClamped( self ) :

		s = GafferUI.Slider( value = 0.1, min = 0, max = 2 )

		cs = GafferTest.CapturingSlot( s.valueChangedSignal() )

		s.setValue( 3 )
		self.assertEqual( s.getValue(), 2 )

		self.assertEqual( len( cs ), 1 )

		s.setValue( 3 )
		self.assertEqual( s.getValue(), 2 )

		# second attempt was clamped to same position as before, so shouldn't
		# signal any changes.
		self.assertEqual( len( cs ), 1 )
Пример #7
0
    def testDirtyPropagation(self):

        r = GafferImage.ImageReader()
        r["fileName"].setValue(self.fileName)

        t = GafferImage.ImageTransform()
        t["in"].setInput(r["out"])

        cs = GafferTest.CapturingSlot(t.plugDirtiedSignal())
        t["transform"]["scale"].setValue(IECore.V2f(2., 2.))

        dirtiedPlugs = set([x[0].relativeName(x[0].node()) for x in cs])
        self.assertEqual(len(dirtiedPlugs), 4)
        self.assertTrue("out" in dirtiedPlugs)
        self.assertTrue("out.channelData" in dirtiedPlugs)
        self.assertTrue("out.dataWindow" in dirtiedPlugs)
        self.assertTrue("__scaledFormat" in dirtiedPlugs)
Пример #8
0
	def testAffectsChannelNames( self ) :

		c1 = GafferImage.Constant()
		c2 = GafferImage.Constant()

		copy = GafferImage.CopyChannels()
		copy["in"][0].setInput( c1["out"] )
		copy["in"][1].setInput( c2["out"] )

		cs = GafferTest.CapturingSlot( copy.plugDirtiedSignal() )

		c2["layer"].setValue( "diffuse" )
		self.assertTrue( copy["out"]["channelNames"] in [ x[0] for x in cs ] )

		del cs[:]
		copy["channels"].setValue( "diffuse.R" )
		self.assertTrue( copy["out"]["channelNames"] in [ x[0] for x in cs ] )
Пример #9
0
	def testDirtyPropagation( self ) :

		s = Gaffer.NameSwitch()
		s.setup( Gaffer.IntPlug() )
		s["in"].resize( 2 )

		cs = GafferTest.CapturingSlot( s.plugDirtiedSignal() )
		s["in"][1]["name"].setValue( "x" )
		self.assertIn( s["out"], { x[0] for x in cs } )

		del cs[:]
		s["in"][1]["enabled"].setValue( False )
		self.assertIn( s["out"], { x[0] for x in cs } )

		del cs[:]
		s["in"][1]["value"].setValue( 10 )
		self.assertIn( s["out"], { x[0] for x in cs } )
    def testSelection(self):

        d = {}
        for i in range(0, 10):
            dd = {}
            for j in range(0, 10):
                dd[str(j)] = j
            d[str(i)] = dd

        p = Gaffer.DictPath(d, "/")

        w = GafferUI.PathListingWidget(
            p,
            allowMultipleSelection=True,
            displayMode=GafferUI.PathListingWidget.DisplayMode.Tree)
        _GafferUI._pathListingWidgetAttachTester(
            GafferUI._qtAddress(w._qtWidget()))

        self.assertTrue(w.getSelection().isEmpty())

        # Set selection. This should be immediately reflected in
        # the `selectionChangedSignal` and the result of `getSelection()`.

        cs = GafferTest.CapturingSlot(w.selectionChangedSignal())
        s = IECore.PathMatcher(["/1", "/2", "/9", "/2/5", "/3/1"])

        w.setSelection(s)
        self.assertEqual(w.getSelection(), s)
        self.assertEqual(len(cs), 1)

        # Delete a path that was selected.
        d2 = d["2"]
        del d["2"]
        self.__emitPathChanged(w)
        # We don't expect this to affect the result of `getSelection()` because
        # the selection state is independent of the model contents.
        self.assertEqual(w.getSelection(), s)

        # Now try to set selection twice in succession, so the model doesn't have
        # chance to finish one update before starting the next.

        s1 = IECore.PathMatcher(["/9", "/9/10", "/8/6"])
        s2 = IECore.PathMatcher(["/9", "/9/9", "/5/6", "3"])
        w.setSelection(s1)
        w.setSelection(s2)
        self.assertEqual(w.getSelection(), s2)
Пример #11
0
    def testParentDirtinessSignalledAfterAllChildren(self):

        n = Gaffer.DependencyNode()
        n["i"] = Gaffer.FloatPlug()
        n["o"] = Gaffer.V3fPlug(direction=Gaffer.Plug.Direction.Out)

        for c in n["o"].children():
            c.setInput(n["i"])

        cs = GafferTest.CapturingSlot(n.plugDirtiedSignal())
        n["i"].setValue(10)

        self.assertEqual(len(cs), 5)
        self.assertTrue(cs[0][0].isSame(n["i"]))
        self.assertTrue(cs[1][0].isSame(n["o"]["x"]))
        self.assertTrue(cs[2][0].isSame(n["o"]["y"]))
        self.assertTrue(cs[3][0].isSame(n["o"]["z"]))
        self.assertTrue(cs[4][0].isSame(n["o"]))
Пример #12
0
    def testDirtyPropagation(self):

        read = GafferImage.ImageReader()
        read["fileName"].setValue(
            os.path.join(self.path, "blueWithDataWindow.100x100.exr"))

        reformat = GafferImage.Reformat()
        reformat["in"].setInput(read["out"])

        cs = GafferTest.CapturingSlot(reformat.plugDirtiedSignal())
        reformat["format"].setValue(GafferImage.Format(150, 125, 1.))

        dirtiedPlugs = set([x[0].relativeName(x[0].node()) for x in cs])
        self.assertEqual(len(dirtiedPlugs), 4)
        self.assertTrue("out" in dirtiedPlugs)
        self.assertTrue("out.dataWindow" in dirtiedPlugs)
        self.assertTrue("out.channelData" in dirtiedPlugs)
        self.assertTrue("out.format" in dirtiedPlugs)
Пример #13
0
    def test(self):

        n = GafferTest.AddNode()
        cs = GafferTest.CapturingSlot(n.plugDirtiedSignal())

        n["op1"].setValue(10)
        n["op2"].setValue(20)

        self.assertEqual(len([x[0] for x in cs if x[0].isSame(n["sum"])]), 2)

        del cs[:]

        with Gaffer.DirtyPropagationScope():

            n["op1"].setValue(11)
            n["op2"].setValue(21)

        self.assertEqual(len([x[0] for x in cs if x[0].isSame(n["sum"])]), 1)
Пример #14
0
	def testSetExpressionShortcuts( self ) :

		s = Gaffer.ScriptNode()

		s["n"] = Gaffer.Node()
		s["n"]["user"]["i"] = Gaffer.IntPlug( flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic )

		s["e"] = Gaffer.Expression()
		cs = GafferTest.CapturingSlot( s["e"].expressionChangedSignal() )

		s["e"].setExpression( 'parent["n"]["user"]["i"] = 10' )
		self.assertEqual( len( cs ), 1 )

		s["e"].setExpression( 'parent["n"]["user"]["i"] = 10' )
		self.assertEqual( len( cs ), 1 )

		s["e"].setExpression( 'parent["n"]["user"]["i"] = 20' )
		self.assertEqual( len( cs ), 2 )
Пример #15
0
    def testCameraChangedSignal(self):

        v = GafferUI.ViewportGadget()

        cs = GafferTest.CapturingSlot(v.cameraChangedSignal())

        v.setCamera(v.getCamera())
        self.assertEqual(len(cs), 0)

        c = v.getCamera()
        c.parameters()["perspective:fov"] = IECore.FloatData(10)

        v.setCamera(c)
        self.assertEqual(len(cs), 1)
        self.assertEqual(cs[0], (v, ))

        v.setCamera(v.getCamera())
        self.assertEqual(len(cs), 1)
Пример #16
0
    def testGlobalsDirtyPropagation(self):

        options = GafferScene.StandardOptions()

        attributes = GafferScene.CustomAttributes()
        attributes["in"].setInput(options["out"])
        attributes["global"].setValue(True)

        self.assertEqual(attributes["out"].globals(), IECore.CompoundObject())

        cs = GafferTest.CapturingSlot(attributes.plugDirtiedSignal())
        options["options"]["renderCamera"]["enabled"].setValue(True)

        self.assertIn(attributes["out"]["globals"], {x[0] for x in cs})
        self.assertEqual(
            attributes["out"].globals(),
            IECore.CompoundObject(
                {"option:render:camera": IECore.StringData("")}))
Пример #17
0
    def testObjectAffectsChildNames(self):

        plane = GafferScene.Plane()
        sphere = GafferScene.Sphere()

        instancer = GafferScene.Instancer()
        instancer["in"].setInput(plane["out"])
        instancer["instances"].setInput(sphere["out"])
        instancer["parent"].setValue("/plane")

        cs = GafferTest.CapturingSlot(instancer.plugDirtiedSignal())
        plane["divisions"]["x"].setValue(2)

        dirtiedPlugs = [s[0] for s in cs]

        self.assertTrue(instancer["out"]["childNames"] in dirtiedPlugs)
        self.assertTrue(instancer["out"]["bound"] in dirtiedPlugs)
        self.assertTrue(instancer["out"]["transform"] in dirtiedPlugs)
Пример #18
0
	def testDirtyPropagationThroughNetwork( self ) :

		s = GafferArnold.ArnoldShader()
		s.loadShader( "standard_surface" )

		n1 = GafferArnold.ArnoldShader()
		n1.loadShader( "noise" )

		n2 = GafferArnold.ArnoldShader()
		n2.loadShader( "noise" )

		s["parameters"]["base_color"].setInput( n1["out"] )
		n1["parameters"]["color1"].setInput( n2["out"] )

		cs = GafferTest.CapturingSlot( s.plugDirtiedSignal() )

		n2["parameters"]["amplitude"].setValue( 20 )

		self.assertTrue( "ArnoldShader.out" in [ x[0].fullName() for x in cs ] )
Пример #19
0
    def testHighlighting(self):

        g = GafferUI.Gadget()
        self.assertEqual(g.getHighlighted(), False)

        g.setHighlighted(True)
        self.assertEqual(g.getHighlighted(), True)

        g.setHighlighted(False)
        self.assertEqual(g.getHighlighted(), False)

        cs = GafferTest.CapturingSlot(g.renderRequestSignal())

        g.setHighlighted(False)
        self.assertEqual(len(cs), 0)

        g.setHighlighted(True)
        self.assertEqual(len(cs), 1)
        self.assertTrue(cs[0][0].isSame(g))
Пример #20
0
    def testInactiveInputsDontPropagateDirtiness(self):

        n1 = GafferTest.AddNode()
        n2 = GafferTest.AddNode()

        s = Gaffer.Switch()
        s.setup(n1["sum"])
        s["in"][0].setInput(n1["sum"])
        s["in"][1].setInput(n2["sum"])

        # Because the index is constant, the switch should
        # have a direct pass-through connection.
        self.assertEqual(s["out"].source(), n1["sum"])

        # Which means that the inactive inputs should not
        # affect the output of the switch at all.
        cs = GafferTest.CapturingSlot(s.plugDirtiedSignal())
        n2["op1"].setValue(10)
        self.assertNotIn(s["out"], {x[0] for x in cs})
Пример #21
0
    def testAffects(self):

        c = GafferScene.Cube()

        s = GafferTest.CapturingSlot(c.plugDirtiedSignal())

        c["name"].setValue("box")
        self.assertEqual(len(s), 2)
        self.failUnless(s[0][0].isSame(c["out"]["childNames"]))
        self.failUnless(s[1][0].isSame(c["out"]))

        del s[:]

        c["dimensions"]["x"].setValue(10)
        found = False
        for ss in s:
            if ss[0].isSame(c["out"]):
                found = True
        self.failUnless(found)
Пример #22
0
	def testPlugFlagsChangedSignal( self ) :
	
		n = Gaffer.Node()
		n["p"] = Gaffer.Plug()

		cs = GafferTest.CapturingSlot( n.plugFlagsChangedSignal() )
		self.assertEqual( len( cs ), 0 )
		
		n["p"].setFlags( Gaffer.Plug.Flags.ReadOnly, True )
		self.assertEqual( len( cs ), 1 )
		self.failUnless( cs[0][0].isSame( n["p"] ) )
		
		# second time should have no effect because they're the same
		n["p"].setFlags( Gaffer.Plug.Flags.ReadOnly, True )
		self.assertEqual( len( cs ), 1 )
		
		n["p"].setFlags( Gaffer.Plug.Flags.ReadOnly, False )
		self.assertEqual( len( cs ), 2 )
		self.failUnless( cs[1][0].isSame( n["p"] ) )
Пример #23
0
    def testErrorSignal(self):

        b = GafferTest.BadNode()
        a = GafferTest.AddNode()
        a["op1"].setInput(b["out3"])

        cs = GafferTest.CapturingSlot(b.errorSignal())

        self.assertRaises(RuntimeError, b["out1"].getValue)
        self.assertEqual(len(cs), 1)
        self.assertTrue(cs[0][0].isSame(b["out1"]))
        self.assertTrue(cs[0][1].isSame(b["out1"]))
        self.assertTrue(isinstance(cs[0][2], str))

        self.assertRaises(RuntimeError, a["sum"].getValue)
        self.assertEqual(len(cs), 2)
        self.assertTrue(cs[1][0].isSame(b["out3"]))
        self.assertTrue(cs[1][1].isSame(b["out3"]))
        self.assertTrue(isinstance(cs[1][2], str))
Пример #24
0
	def testNodeMetadata( self ) :

		s = Gaffer.ScriptNode()

		s["b"] = Gaffer.Box()
		self.assertEqual( Gaffer.Metadata.value( s["b"], "description" ), None )

		cs = GafferTest.CapturingSlot( Gaffer.Metadata.nodeValueChangedSignal() )

		Gaffer.Metadata.registerValue( s["b"], "description", "aaa" )
		self.assertEqual( Gaffer.Metadata.value( s["b"], "description" ), "aaa" )

		self.assertEqual( len( cs ), 1 )
		self.assertEqual( cs[0], ( Gaffer.Box.staticTypeId(), "description", s["b"] ) )

		s2 = Gaffer.ScriptNode()
		s2.execute( s.serialise() )

		self.assertEqual( Gaffer.Metadata.value( s["b"], "description" ), "aaa" )
    def testDirtyPropagation(self):

        standardAttributes = GafferScene.StandardAttributes()
        localiseAttributes = GafferScene.LocaliseAttributes()
        localiseAttributes["in"].setInput(standardAttributes["out"])

        cs = GafferTest.CapturingSlot(localiseAttributes.plugDirtiedSignal())

        standardAttributes["attributes"]["visibility"]["enabled"].setValue(
            True)
        self.assertIn(localiseAttributes["out"]["attributes"],
                      {x[0]
                       for x in cs})

        del cs[:]
        localiseAttributes["attributes"].setValue("x")
        self.assertIn(localiseAttributes["out"]["attributes"],
                      {x[0]
                       for x in cs})
Пример #26
0
    def testAffects(self):

        s = GafferScene.Sphere()

        ss = GafferTest.CapturingSlot(s.plugDirtiedSignal())

        s["name"].setValue("ball")
        self.assertEqual(len(ss), 2)
        self.failUnless(ss[0][0].isSame(s["out"]["childNames"]))
        self.failUnless(ss[1][0].isSame(s["out"]))

        del ss[:]

        s["divisions"]["x"].setValue(10)
        found = False
        for sss in ss:
            if sss[0].isSame(s["out"]):
                found = True
        self.failUnless(found)
Пример #27
0
	def testDisconnectedDirty( self ) :

		r = GafferImage.ImageReader()
		r["fileName"].setValue( os.path.expandvars( "$GAFFER_ROOT/python/GafferImageTest/images/colorbars_half_max.exr" ) )
		clamp = GafferImage.Clamp()
		clamp["in"].setInput( r["out"] )

		cs = GafferTest.CapturingSlot( clamp.plugDirtiedSignal() )
		clamp["max"].setValue( IECore.Color4f( .25, 1., 1., 1. ) )

		dirtiedPlugs = set( [ x[0].relativeName( x[0].node() ) for x in cs ] )

		expectedPlugs = [
			'out.channelData',
			'out'
		]

		for plug in expectedPlugs :
			self.assertTrue( plug in dirtiedPlugs )
Пример #28
0
    def testSetInputShortcut(self):

        n1 = Gaffer.Node()
        n1["c"] = Gaffer.Plug()

        n2 = Gaffer.Node()
        n2["c"] = Gaffer.Plug(direction=Gaffer.Plug.Direction.Out)

        cs = GafferTest.CapturingSlot(n1.plugInputChangedSignal())
        self.assertEqual(len(cs), 0)

        n1["c"].setInput(n2["c"])
        # we should get a signal the first time
        self.assertEqual(len(cs), 1)

        n1["c"].setInput(n2["c"])
        # but the second time there should be no signal,
        # because it was the same.
        self.assertEqual(len(cs), 1)
Пример #29
0
	def testSetDirtyPropagation( self ) :

		p = GafferScene.Plane()
		s = GafferScene.Set()
		s["in"].setInput( p["out"] )

		cs = GafferTest.CapturingSlot( s.plugDirtiedSignal() )

		p["sets"].setValue( "test" )
		self.assertTrue( s["out"]["set"] in { x[0] for x in cs } )

		del cs[:]

		s["mode"].setValue( s.Mode.Remove )
		self.assertTrue( s["out"]["set"] in { x[0] for x in cs } )

		del cs[:]

		s["name"].setValue( "test" )
		self.assertTrue( s["out"]["set"] in { x[0] for x in cs } )

		del cs[:]

		s["paths"].setValue( IECore.StringVectorData( [ "/plane" ] ) )
		self.assertTrue( s["out"]["set"] in { x[0] for x in cs } )

		del cs[:]

		s["enabled"].setValue( False )
		self.assertTrue( s["out"]["set"] in { x[0] for x in cs } )
		s["enabled"].setValue( True )

		del cs[:]

		f = GafferScene.PathFilter()
		s["filter"].setInput( f["out"] )
		self.assertTrue( s["out"]["set"] in { x[0] for x in cs } )

		del cs[:]

		f["paths"].setValue( IECore.StringVectorData( [ "/plane" ] ) )
		self.assertTrue( s["out"]["set"] in { x[0] for x in cs } )
Пример #30
0
    def testDirtyPropagation(self):
        a = GafferImage.Constant()
        b = GafferImage.Constant()
        aShuffle = GafferImage.Shuffle()
        aShuffle["in"].setInput(a["out"])
        bShuffle = GafferImage.Shuffle()
        bShuffle["in"].setInput(b["out"])
        holdout = GafferImage.DeepHoldout()
        holdout["in"].setInput(aShuffle["out"])
        holdout["holdout"].setInput(bShuffle["out"])

        cs = GafferTest.CapturingSlot(holdout.plugDirtiedSignal())

        a["color"]["r"].setValue(0.5)
        dirtiedPlugs = {x[0].relativeName(holdout) for x in cs}
        self.assertIn("__intermediateIn.channelData", dirtiedPlugs)
        self.assertIn("__flattened.channelData", dirtiedPlugs)
        self.assertIn("out.channelData", dirtiedPlugs)
        del cs[:]

        b["color"]["a"].setValue(0.5)
        dirtiedPlugs = {x[0].relativeName(holdout) for x in cs}
        self.assertIn("__flattened.channelData", dirtiedPlugs)
        self.assertIn("out.channelData", dirtiedPlugs)
        del cs[:]

        aShuffle["channels"].addChild(bShuffle.ChannelPlug("Z", "__white"))
        dirtiedPlugs = {x[0].relativeName(holdout) for x in cs}
        self.assertIn("__intermediateIn.channelData", dirtiedPlugs)
        self.assertIn("__flattened.channelData", dirtiedPlugs)
        self.assertIn("out.channelData", dirtiedPlugs)
        self.assertIn("__intermediateIn.channelNames", dirtiedPlugs)
        self.assertIn("__flattened.channelNames", dirtiedPlugs)
        self.assertIn("out.channelNames", dirtiedPlugs)
        del cs[:]

        bShuffle["channels"].addChild(bShuffle.ChannelPlug("Z", "__white"))
        dirtiedPlugs = {x[0].relativeName(holdout) for x in cs}
        self.assertIn("__flattened.channelData", dirtiedPlugs)
        self.assertIn("out.channelData", dirtiedPlugs)
        self.assertIn("__flattened.channelNames", dirtiedPlugs)
        del cs[:]