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})
    def test(self):

        # Hierarchy             Attributes
        # ---------             ----------
        #
        # /outerGroup                            b : outerGroupB, c : outerGroupC
        #    /innerGroup        a : innerGroupA, b : innerGroupB
        #        /plane         a : planeA
        #

        plane = GafferScene.Plane()

        innerGroup = GafferScene.Group()
        innerGroup["name"].setValue("innerGroup")
        innerGroup["in"][0].setInput(plane["out"])

        outerGroup = GafferScene.Group()
        outerGroup["name"].setValue("outerGroup")
        outerGroup["in"][0].setInput(innerGroup["out"])

        planeFilter = GafferScene.PathFilter()
        planeFilter["paths"].setValue(
            IECore.StringVectorData(["/outerGroup/innerGroup/plane"]))

        innerGroupFilter = GafferScene.PathFilter()
        innerGroupFilter["paths"].setValue(
            IECore.StringVectorData(["/outerGroup/innerGroup"]))

        outerGroupFilter = GafferScene.PathFilter()
        outerGroupFilter["paths"].setValue(
            IECore.StringVectorData(["/outerGroup"]))

        planeAttributes = GafferScene.CustomAttributes()
        planeAttributes["in"].setInput(outerGroup["out"])
        planeAttributes["filter"].setInput(planeFilter["out"])
        planeAttributes["attributes"].addChild(
            Gaffer.NameValuePlug("a", "planeA"))

        innerGroupAttributes = GafferScene.CustomAttributes()
        innerGroupAttributes["in"].setInput(planeAttributes["out"])
        innerGroupAttributes["filter"].setInput(innerGroupFilter["out"])
        innerGroupAttributes["attributes"].addChild(
            Gaffer.NameValuePlug("a", "innerGroupA"))
        innerGroupAttributes["attributes"].addChild(
            Gaffer.NameValuePlug("b", "innerGroupB"))

        outerGroupAttributes = GafferScene.CustomAttributes()
        outerGroupAttributes["in"].setInput(innerGroupAttributes["out"])
        outerGroupAttributes["filter"].setInput(outerGroupFilter["out"])
        outerGroupAttributes["attributes"].addChild(
            Gaffer.NameValuePlug("b", "outerGroupB"))
        outerGroupAttributes["attributes"].addChild(
            Gaffer.NameValuePlug("c", "outerGroupC"))

        # No filter, shouldn't do anything.

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

        self.assertScenesEqual(localiseAttributes["out"],
                               localiseAttributes["in"])
        self.assertSceneHashesEqual(localiseAttributes["out"],
                                    localiseAttributes["in"])

        # Filter, but no attributes specified. Still no effect on scenes.

        localiseAttributes["filter"].setInput(planeFilter["out"])
        localiseAttributes["attributes"].setValue("")
        self.assertScenesEqual(localiseAttributes["out"],
                               localiseAttributes["in"])

        # Localise everything, but only for the plane.

        localiseAttributes["attributes"].setValue("*")
        self.assertEqual(localiseAttributes["out"].attributes("/outerGroup"),
                         localiseAttributes["in"].attributes("/outerGroup"))
        self.assertEqual(
            localiseAttributes["out"].attributes("/outerGroup/innerGroup"),
            localiseAttributes["in"].attributes("/outerGroup/innerGroup"))
        self.assertEqual(
            localiseAttributes["out"].attributes(
                "/outerGroup/innerGroup/plane"),
            localiseAttributes["in"].fullAttributes(
                "/outerGroup/innerGroup/plane"))

        # Localise a subset of the attributes.

        localiseAttributes["attributes"].setValue("b")
        self.assertEqual(
            localiseAttributes["out"].attributes(
                "/outerGroup/innerGroup/plane"),
            IECore.CompoundObject({
                "a": IECore.StringData("planeA"),
                "b": IECore.StringData("innerGroupB"),
            }))

        # Localise a different subset of the attributes.

        localiseAttributes["attributes"].setValue("c")
        self.assertEqual(
            localiseAttributes["out"].attributes(
                "/outerGroup/innerGroup/plane"),
            IECore.CompoundObject({
                "a": IECore.StringData("planeA"),
                "c": IECore.StringData("outerGroupC"),
            }))