def testContextSanitisation( self ) : plane = GafferScene.Plane() attributeQuery = GafferScene.AttributeQuery() attributeQuery.setup( Gaffer.StringVectorDataPlug( defaultValue = IECore.StringVectorData() ) ) attributeQuery["scene"].setInput( plane["out"] ) attributeQuery["location"].setValue( "/plane" ) attributeQuery["attribute"].setValue( "test" ) attributeQuery["default"].setValue( IECore.StringVectorData( [ "/plane" ] ) ) pathFilter = GafferScene.PathFilter() pathFilter["paths"].setInput( attributeQuery["value"] ) attributes = GafferScene.StandardAttributes() attributes["in"].setInput( plane["out"] ) attributes["filter"].setInput( pathFilter["out"] ) attributes["attributes"]["doubleSided"]["enabled"].setValue( True ) # This exposes a bug whereby the PathFilter leaked the `scene:filter:inputScene` # and `scene:path` context variables when evaluating `paths`. with Gaffer.ContextMonitor( attributeQuery["value"] ) as contextMonitor : self.assertEqual( attributes["out"].attributes( "/plane")["doubleSided"].value, True ) self.assertNotIn( "scene:filter:inputScene", contextMonitor.combinedStatistics().variableNames() ) self.assertNotIn( "scene:path", contextMonitor.combinedStatistics().variableNames() )
def testShaderNetworkGeneratedInGlobalContext(self): constant = GafferImage.Constant() outLayer = GafferOSL.OSLCode() outLayer["out"]["layer"] = GafferOSL.ClosurePlug( direction=Gaffer.Plug.Direction.Out, flags=Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic) outLayer["code"].setValue('layer = outLayer( "", color( 0, 1, 0) )') outImage = GafferOSL.OSLShader() outImage.loadShader("ImageProcessing/OutImage") outImage["parameters"]["in0"].setInput(outLayer["out"]["layer"]) oslImage = GafferOSL.OSLImage() oslImage["in"].setInput(constant["out"]) oslImage["shader"].setInput(outImage["out"]["out"]) with Gaffer.ContextMonitor(oslImage["__oslCode"]) as cm: GafferImageTest.processTiles(oslImage["out"]) cs = cm.combinedStatistics() self.assertEqual(cs.numUniqueContexts(), 1) self.assertNotIn("image:tileOrigin", cs.variableNames()) self.assertNotIn("image:channelName", cs.variableNames())
def testNoUnecessaryHistoryCalls(self): script = Gaffer.ScriptNode() script["camera"] = GafferScene.Camera() view = GafferSceneUI.SceneView() view["in"].setInput(script["camera"]["out"]) view["camera"]["lookThroughEnabled"].setValue(True) view["camera"]["lookThroughCamera"].setValue("/camera") tool = GafferSceneUI.CameraTool(view) tool["active"].setValue(True) # Force CameraTool update, since it is done lazily just prior to render. view.viewportGadget().preRenderSignal()(view.viewportGadget()) with Gaffer.ContextMonitor() as cm: view.viewportGadget().setCameraTransform(imath.M44f().translate( imath.V3f(1, 2, 3))) # Force update view.viewportGadget().preRenderSignal()(view.viewportGadget()) # We do not want the CameraTool to have performed a `SceneAlgo::history` # query during the edit, as they can be expensive and aren't suitable # for repeated use during drags etc. self.assertNotIn(GafferScene.SceneAlgo.historyIDContextName(), cm.combinedStatistics().variableNames()) self.assertEqual(script["camera"]["transform"]["translate"].getValue(), imath.V3f(1, 2, 3))
def testContextLeaks(self): script = Gaffer.ScriptNode() script["plane"] = GafferScene.Plane() script["plane"]["sets"].setValue("A") script["contextVariables"] = Gaffer.ContextVariables() script["contextVariables"].setup(GafferScene.ScenePlug()) script["contextVariables"]["in"].setInput(script["plane"]["out"]) script["contextVariables"]["variables"].addOptionalMember( "a", IECore.StringData("aardvark"), plugName="a") script["expression"] = Gaffer.Expression() script["expression"].setExpression( inspect.cleandoc(""" parent["contextVariables"]["enabled"] = True parent["contextVariables"]["variables"]["a"]["enabled"] = True parent["contextVariables"]["variables"]["a"]["name"] = "b" parent["contextVariables"]["variables"]["a"]["value"] = "b" """)) with Gaffer.ContextMonitor(script["expression"]) as cm: self.assertSceneValid(script["contextVariables"]["out"]) self.assertFalse( set(cm.combinedStatistics().variableNames()).intersection( {"scene:path", "scene:setName", "scene:filter:inputScene"}))
def testRoot(self): a1 = GafferTest.AddNode() a2 = GafferTest.AddNode() with Gaffer.Context() as c: with Gaffer.ContextMonitor(a2) as m: a1["sum"].getValue() a2["sum"].getValue() self.assertFalse(a1["sum"] in m.allStatistics()) self.assertTrue(a2["sum"] in m.allStatistics())
def testNoUnwantedBoundEvaluations(self): reader = GafferScene.SceneReader() reader["fileName"].setValue( "${GAFFER_ROOT}/resources/gafferBot/caches/gafferBot.scc") group = GafferScene.Group() parent = GafferScene.Parent() parent["in"].setInput(reader["out"]) parent["children"][0].setInput(group["out"]) parent["parent"].setValue("/") parent["destination"].setValue("/children") # Computing the root bound should not require more than the bounds # of `/` and `/GAFFERBOT` to be queried from the input scene. with Gaffer.ContextMonitor(reader["out"]["bound"]) as contextMonitor: parent["out"].bound("/") self.assertEqual( contextMonitor.combinedStatistics().numUniqueContexts(), 2) # If we parent to `/GAFFERBOT/children` then computing the bound of `/GAFFERBOT` # should only query `/GAFFERBOT` and `/GAFFERBOT/C_torso_GRP` from the input. Gaffer.ValuePlug.clearCache() Gaffer.ValuePlug.clearHashCache() parent["destination"].setValue("/GAFFERBOT/children") with Gaffer.ContextMonitor(reader["out"]["bound"]) as contextMonitor: parent["out"].bound("/GAFFERBOT") self.assertEqual( contextMonitor.combinedStatistics().numUniqueContexts(), 2) # The bounds for children of `/GAFFERBOT` should be perfect pass throughs. self.assertEqual(parent["out"].boundHash("/GAFFERBOT/C_torso_GRP"), parent["in"].boundHash("/GAFFERBOT/C_torso_GRP"))
def testFileNameContext(self): s = Gaffer.ScriptNode() s["reader"] = GafferImage.OpenImageIOReader() s["expression"] = Gaffer.Expression() s["expression"].setExpression('parent["reader"]["fileName"] = "%s"' % self.fileName) with Gaffer.ContextMonitor(root=s["expression"]) as cm: GafferImage.ImageAlgo.tiles(s["reader"]["out"]) self.assertEqual(set(cm.combinedStatistics().variableNames()), set(['frame', 'framesPerSecond']))
def test(self): c = Gaffer.Context() defaultVariableNames = c.keys() a1 = GafferTest.AddNode() a2 = GafferTest.AddNode() m = Gaffer.ContextMonitor() with c, m: a1["sum"].getValue() a1s = m.plugStatistics(a1["sum"]) self.assertEqual(a1s.numUniqueContexts(), 1) self.assertEqual(set(a1s.variableNames()), set(defaultVariableNames)) for n in defaultVariableNames: self.assertEqual(a1s.numUniqueValues(n), 1) self.assertFalse(a2["sum"] in m.allStatistics()) with c, m: c.setFrame(10) a1["sum"].getValue() a2["sum"].getValue() a1s = m.plugStatistics(a1["sum"]) self.assertEqual(a1s.numUniqueContexts(), 2) self.assertEqual(set(a1s.variableNames()), set(defaultVariableNames)) for n in defaultVariableNames: self.assertEqual(a1s.numUniqueValues(n), 1 if n != "frame" else 2) a2s = m.plugStatistics(a2["sum"]) self.assertEqual(a2s.numUniqueContexts(), 1) self.assertEqual(set(a2s.variableNames()), set(defaultVariableNames)) for n in defaultVariableNames: self.assertEqual(a2s.numUniqueValues(n), 1) with c, m: c["test"] = 10 a1["sum"].getValue() c["test"] = 20 a2["sum"].getValue() cs = m.combinedStatistics() self.assertEqual(cs.numUniqueContexts(), 4) self.assertEqual(set(cs.variableNames()), set(defaultVariableNames + ["test"])) self.assertEqual(cs.numUniqueValues("frame"), 2) self.assertEqual(cs.numUniqueValues("test"), 2)
def __contextMonitor(menu, createIfMissing=False): # We store a monitor per script, so that we don't pollute # other scripts with metrics collected as a side effect # of monitoring this one. script = menu.ancestor(GafferUI.ScriptWindow).scriptNode() monitor = getattr(script, "__contextMonitor", None) if monitor is not None: return monitor if createIfMissing: monitor = Gaffer.ContextMonitor(script.selection()[-1]) monitor.__running = False script.__contextMonitor = monitor return monitor else: return None
def testEnabledEvaluationUsesGlobalContext( self ) : script = Gaffer.ScriptNode() script["plane"] = GafferScene.Plane() script["expression"] = Gaffer.Expression() script["expression"].setExpression( inspect.cleandoc( """ path = context.get("scene:path", None ) assert( path is None ) parent["plane"]["enabled"] = True """ ) ) with Gaffer.ContextMonitor( script["expression"] ) as monitor : self.assertSceneValid( script["plane"]["out"] ) self.assertEqual( monitor.combinedStatistics().numUniqueValues( "scene:path" ), 0 )
def testExtraAttributesOnlyEvaluatedForFilteredLocations( self ) : script = Gaffer.ScriptNode() script["grid"] = GafferScene.Grid() script["filter"] = GafferScene.PathFilter() script["filter"]["paths"].setValue( IECore.StringVectorData( [ "/grid" ] ) ) script["customAttributes"] = GafferScene.CustomAttributes() script["customAttributes"]["in"].setInput( script["grid"]["out"] ) script["customAttributes"]["filter"].setInput( script["filter"]["out"] ) script["expression"] = Gaffer.Expression() script["expression"].setExpression( """parent["customAttributes"]["extraAttributes"] = IECore.CompoundData( { "a" : IECore.StringData( str( context.get( "scene:path" ) ) ) } )""" ) with Gaffer.ContextMonitor( script["expression"] ) as monitor : GafferSceneTest.traverseScene( script["customAttributes"]["out"] ) self.assertEqual( monitor.combinedStatistics().numUniqueValues( "scene:path" ), 1 )
def testSetVariableDoesntLeakToScene(self): sphere = GafferScene.Sphere() sphere["sets"].setValue("testSource") setFilter = GafferScene.SetFilter() setFilter["set"].setValue("${setVariable}Source") setNode = GafferScene.Set() setNode["in"].setInput(sphere["out"]) setNode["filter"].setInput(setFilter["out"]) setNode["name"].setValue("test") setNode["setVariable"].setValue("setVariable") with Gaffer.ContextMonitor(sphere) as monitor: self.assertEqual(setNode["out"].set("test"), sphere["out"].set("testSource")) self.assertNotIn("setVariable", monitor.combinedStatistics().variableNames())
def testNoContextLeakage(self): c = GafferImage.Constant() t1 = GafferImage.ImageTransform() t1["in"].setInput(c["out"]) t2 = GafferImage.ImageTransform() t2["in"].setInput(t1["out"]) with Gaffer.ContextMonitor(root=c) as cm: self.assertImagesEqual(t2["out"], t2["out"]) self.assertEqual( set(cm.combinedStatistics().variableNames()), { "frame", "framesPerSecond", "image:channelName", "image:tileOrigin" }, )
def _run(self, args): if args["cacheMemoryLimit"].value: Gaffer.ValuePlug.setCacheMemoryLimit( 1024 * 1024 * args["cacheMemoryLimit"].value) if args["hashCacheSizeLimit"].value: Gaffer.ValuePlug.setHashCacheSizeLimit( args["hashCacheSizeLimit"].value) self.__timers = collections.OrderedDict() self.__memory = collections.OrderedDict() self.__memory["Application"] = _Memory.maxRSS() script = Gaffer.ScriptNode() script["fileName"].setValue(os.path.abspath(args["script"].value)) with _Timer() as loadingTimer: script.load(continueOnError=True) self.__timers["Loading"] = loadingTimer self.root()["scripts"].addChild(script) self.__memory["Script"] = _Memory.maxRSS( ) - self.__memory["Application"] if args["performanceMonitor"].value: self.__performanceMonitor = Gaffer.PerformanceMonitor() else: self.__performanceMonitor = None if args["contextMonitor"].value: contextMonitorRoot = None if args["contextMonitorRoot"].value: contextMonitorRoot = script.descendant( args["contextMonitorRoot"].value) if contextMonitorRoot is None: IECore.msg( IECore.Msg.Level.Error, "stats", "Context monitor root \"%s\" does not exist" % args["contextMonitorRoot"].value) return 1 self.__contextMonitor = Gaffer.ContextMonitor(contextMonitorRoot) else: self.__contextMonitor = None if args["vtune"].value: try: self.__vtuneMonitor = Gaffer.VTuneMonitor() self.__vtuneMonitor.setActive(True) except AttributeError: IECore.msg(IECore.Msg.Level.Error, "gui", "unable to create requested VTune monitor") self.__output = file(args["outputFile"].value, "w") if args["outputFile"].value else sys.stdout self.__writeVersion(script) self.__output.write("\n") self.__writeArgs(args) self.__output.write("\n") self.__writeSettings(script) self.__output.write("\n") self.__writeVariables(script) self.__output.write("\n") if args["nodeSummary"].value: self.__writeNodes(script) if args["scene"].value: self.__writeScene(script, args) if args["image"].value: self.__writeImage(script, args) if args["task"].value: self.__writeTask(script, args) self.__output.write("\n") self.__writeMemory() self.__output.write("\n") self.__writePerformance(script, args) self.__output.write("\n") self.__writeContext(script, args) self.__output.write("\n") self.__output.close() if args["annotatedScript"].value: if self.__performanceMonitor is not None: Gaffer.MonitorAlgo.annotate( script, self.__performanceMonitor, Gaffer.MonitorAlgo.PerformanceMetric.TotalDuration) Gaffer.MonitorAlgo.annotate( script, self.__performanceMonitor, Gaffer.MonitorAlgo.PerformanceMetric.HashCount) if self.__contextMonitor is not None: Gaffer.MonitorAlgo.annotate(script, self.__contextMonitor) script.serialiseToFile(args["annotatedScript"].value) return 0
def _run(self, args): self.__timers = collections.OrderedDict() self.__memory = collections.OrderedDict() self.__memory["Application"] = _Memory.maxRSS() script = Gaffer.ScriptNode() script["fileName"].setValue(os.path.abspath(args["script"].value)) with _Timer() as loadingTimer: script.load(continueOnError=True) self.__timers["Loading"] = loadingTimer self.__memory["Script"] = _Memory.maxRSS( ) - self.__memory["Application"] if args["performanceMonitor"].value: self.__performanceMonitor = Gaffer.PerformanceMonitor() else: self.__performanceMonitor = None if args["contextMonitor"].value: contextMonitorRoot = None if args["contextMonitorRoot"].value: contextMonitorRoot = script.descendant( args["contextMonitorRoot"].value) if contextMonitorRoot is None: IECore.msg( IECore.Msg.Level.Error, "stats", "Context monitor root \"%s\" does not exist" % args["contextMonitorRoot"].value) return 1 self.__contextMonitor = Gaffer.ContextMonitor(contextMonitorRoot) else: self.__contextMonitor = None if args["vtune"].value: try: self.__vtuneMonitor = Gaffer.VTuneMonitor() self.__vtuneMonitor.setActive(True) except AttributeError: IECore.msg(IECore.Msg.Level.Error, "gui", "unable to create requested VTune monitor") self.__output = file(args["outputFile"].value, "w") if args["outputFile"].value else sys.stdout with Gaffer.Context(script.context()) as context: context.setFrame(args["frame"].value) self.__writeVersion(script) self.__output.write("\n") self.__writeArgs(args) self.__output.write("\n") self.__writeSettings(script) self.__output.write("\n") self.__writeVariables(script) self.__output.write("\n") if args["nodeSummary"].value: self.__writeNodes(script) if args["scene"].value: self.__writeScene(script, args) if args["image"].value: self.__writeImage(script, args) if args["task"].value: self.__writeTask(script, args) self.__output.write("\n") self.__writeMemory() self.__output.write("\n") self.__writePerformance(script, args) self.__output.write("\n") self.__writeContext(script, args) self.__output.write("\n") self.__output.close() return 0
def _run( self, args ) : self.__timers = collections.OrderedDict() self.__memory = collections.OrderedDict() self.__memory["Application"] = _Memory.maxRSS() script = Gaffer.ScriptNode() script["fileName"].setValue( os.path.abspath( args["script"].value ) ) with _Timer() as loadingTimer : script.load( continueOnError = True ) self.__timers["Loading"] = loadingTimer self.__memory["Script"] = _Memory.maxRSS() - self.__memory["Application"] if args["performanceMonitor"].value : self.__performanceMonitor = Gaffer.PerformanceMonitor() else : self.__performanceMonitor = None if args["contextMonitor"].value : contextMonitorRoot = None if args["contextMonitorRoot"].value : contextMonitorRoot = script.descendant( args["contextMonitorRoot"].value ) if contextMonitorRoot is None : IECore.msg( IECore.Msg.Level.Error, "stats", "Context monitor root \"%s\" does not exist" % args["contextMonitorRoot"].value ) return 1 self.__contextMonitor = Gaffer.ContextMonitor( contextMonitorRoot ) else : self.__contextMonitor = None with Gaffer.Context( script.context() ) as context : context.setFrame( args["frame"].value ) self.__printVersion( script ) print "" self.__printArgs( args ) print "" self.__printSettings( script ) print "" self.__printVariables( script ) print "" self.__printNodes( script ) if args["scene"].value : self.__printScene( script, args ) if args["image"].value : self.__printImage( script, args ) print "" self.__printMemory() print "" self.__printPerformance( script, args ) print "" self.__printContext( script, args ) print return 0
def test(self): for useClosure in [False, True]: getRed = GafferOSL.OSLShader() getRed.loadShader("ImageProcessing/InChannel") getRed["parameters"]["channelName"].setValue("R") getGreen = GafferOSL.OSLShader() getGreen.loadShader("ImageProcessing/InChannel") getGreen["parameters"]["channelName"].setValue("G") getBlue = GafferOSL.OSLShader() getBlue.loadShader("ImageProcessing/InChannel") getBlue["parameters"]["channelName"].setValue("B") floatToColor = GafferOSL.OSLShader() floatToColor.loadShader("Conversion/FloatToColor") floatToColor["parameters"]["r"].setInput( getBlue["out"]["channelValue"]) floatToColor["parameters"]["g"].setInput( getGreen["out"]["channelValue"]) floatToColor["parameters"]["b"].setInput( getRed["out"]["channelValue"]) reader = GafferImage.ImageReader() reader["fileName"].setValue( os.path.expandvars( "$GAFFER_ROOT/python/GafferImageTest/images/rgb.100x100.exr" )) shuffle = GafferImage.Shuffle() shuffle["in"].setInput(reader["out"]) shuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("channel")) shuffle["channels"]["channel"]["out"].setValue('unchangedR') shuffle["channels"]["channel"]["in"].setValue('R') image = GafferOSL.OSLImage() image["in"].setInput(shuffle["out"]) # we haven't connected the shader yet, so the node should act as a pass through self.assertEqual(GafferImage.ImageAlgo.image(image["out"]), GafferImage.ImageAlgo.image(shuffle["out"])) # that should all change when we hook up a shader if useClosure: outRGB = GafferOSL.OSLShader() outRGB.loadShader("ImageProcessing/OutLayer") outRGB["parameters"]["layerColor"].setInput( floatToColor["out"]["c"]) imageShader = GafferOSL.OSLShader() imageShader.loadShader("ImageProcessing/OutImage") imageShader["parameters"]["in0"].setInput( outRGB["out"]["layer"]) image["channels"].addChild( Gaffer.NameValuePlug("", GafferOSL.ClosurePlug(), "testClosure")) else: image["channels"].addChild( Gaffer.NameValuePlug("", imath.Color3f(), "testColor")) cs = GafferTest.CapturingSlot(image.plugDirtiedSignal()) def checkDirtiness(expected): self.assertEqual([i[0].fullName() for i in cs], ["OSLImage." + i for i in expected]) del cs[:] if useClosure: image["channels"]["testClosure"]["value"].setInput( imageShader["out"]["out"]) channelsDirtied = [ "channels.testClosure.value", "channels.testClosure" ] else: image["channels"]["testColor"]["value"].setInput( floatToColor["out"]["c"]) channelsDirtied = [ "channels.testColor.value.r", "channels.testColor.value.g", "channels.testColor.value.b", "channels.testColor.value", "channels.testColor" ] checkDirtiness(channelsDirtied + [ "channels", "__shader", "__shading", "__affectedChannels", "out.channelNames", "out.channelData", "out" ]) inputImage = GafferImage.ImageAlgo.image(shuffle["out"]) with Gaffer.ContextMonitor(image["__shading"]) as monitor: self.assertEqual( image["out"].channelNames(), IECore.StringVectorData(["A", "B", "G", "R", "unchangedR"])) # Evaluating channel names only requires evaluating the shading plug if we have a closure self.assertEqual( monitor.combinedStatistics().numUniqueContexts(), 1 if useClosure else 0) # Channels we don't touch should be passed through unaltered for channel, changed in [('B', True), ('G', True), ('R', True), ('A', False), ('unchangedR', False)]: self.assertEqual( image["out"].channelDataHash(channel, imath.V2i( 0, 0)) == shuffle["out"].channelDataHash( channel, imath.V2i(0, 0)), not changed) image["out"].channelData(channel, imath.V2i(0, 0)) # Should only need one shading evaluate for all channels self.assertEqual(monitor.combinedStatistics().numUniqueContexts(), 1) outputImage = GafferImage.ImageAlgo.image(image["out"]) self.assertNotEqual(inputImage, outputImage) self.assertEqual(outputImage["R"], inputImage["B"]) self.assertEqual(outputImage["G"], inputImage["G"]) self.assertEqual(outputImage["B"], inputImage["R"]) # changes in the shader network should signal more dirtiness getGreen["parameters"]["channelName"].setValue("R") checkDirtiness(channelsDirtied + [ "channels", "__shader", "__shading", "__affectedChannels", "out.channelNames", "out.channelData", "out" ]) floatToColor["parameters"]["r"].setInput( getRed["out"]["channelValue"]) checkDirtiness(channelsDirtied + [ "channels", "__shader", "__shading", "__affectedChannels", "out.channelNames", "out.channelData", "out" ]) inputImage = GafferImage.ImageAlgo.image(shuffle["out"]) outputImage = GafferImage.ImageAlgo.image(image["out"]) self.assertEqual(outputImage["R"], inputImage["R"]) self.assertEqual(outputImage["G"], inputImage["R"]) self.assertEqual(outputImage["B"], inputImage["R"]) self.assertEqual(outputImage["A"], inputImage["A"]) self.assertEqual(outputImage["unchangedR"], inputImage["unchangedR"]) image["in"].setInput(None) checkDirtiness([ 'in.format', 'in.dataWindow', 'in.metadata', 'in.deep', 'in.sampleOffsets', 'in.channelNames', 'in.channelData', 'in', '__shading', '__affectedChannels', 'out.channelNames', 'out.channelData', 'out.format', 'out.dataWindow', 'out.metadata', 'out.deep', 'out.sampleOffsets', 'out' ]) image["defaultFormat"]["displayWindow"]["max"]["x"].setValue(200) checkDirtiness([ 'defaultFormat.displayWindow.max.x', 'defaultFormat.displayWindow.max', 'defaultFormat.displayWindow', 'defaultFormat', '__defaultIn.format', '__defaultIn.dataWindow', '__defaultIn', '__shading', '__affectedChannels', 'out.channelNames', 'out.channelData', 'out.format', 'out.dataWindow', 'out' ]) constant = GafferImage.Constant() image["in"].setInput(constant["out"]) checkDirtiness([ 'in.format', 'in.dataWindow', 'in.metadata', 'in.deep', 'in.sampleOffsets', 'in.channelNames', 'in.channelData', 'in', '__shading', '__affectedChannels', 'out.channelNames', 'out.channelData', 'out.format', 'out.dataWindow', 'out.metadata', 'out.deep', 'out.sampleOffsets', 'out' ]) image["in"].setInput(shuffle["out"]) if useClosure: outRGB["parameters"]["layerName"].setValue("newLayer") else: image["channels"][0]["name"].setValue("newLayer") self.assertEqual( image["out"].channelNames(), IECore.StringVectorData([ "A", "B", "G", "R", "newLayer.B", "newLayer.G", "newLayer.R", "unchangedR" ])) for channel in ['B', 'G', 'R', 'A', 'unchangedR']: self.assertEqual( image["out"].channelDataHash(channel, imath.V2i(0, 0)), shuffle["out"].channelDataHash(channel, imath.V2i(0, 0))) self.assertEqual( image["out"].channelData(channel, imath.V2i(0, 0)), shuffle["out"].channelData(channel, imath.V2i(0, 0))) crop = GafferImage.Crop() crop["area"].setValue(imath.Box2i(imath.V2i(0, 0), imath.V2i(0, 0))) crop["in"].setInput(shuffle["out"]) image["in"].setInput(crop["out"]) if useClosure: # When using closures, we can't find out about the new channels being added if the datawindow is # empty self.assertEqual( image["out"].channelNames(), IECore.StringVectorData(["A", "B", "G", "R", "unchangedR"])) else: self.assertEqual( image["out"].channelNames(), IECore.StringVectorData([ "A", "B", "G", "R", "newLayer.B", "newLayer.G", "newLayer.R", "unchangedR" ]))