From c47269128b52648b08f2ec7ded5bf9b89272158f Mon Sep 17 00:00:00 2001 From: Stan Lagun Date: Tue, 6 Sep 2016 23:47:22 -0700 Subject: [PATCH] Pass receiver to :GC.subscribeDestruction explicitly Makes the subscriber object be an explicit parameter of destruction dependency subscribe/unsibscribe method. It makes possible use of this functions from static methods and to link other objects. Targets-blueprint: dependency-driven-resource-deallocation Change-Id: I2554385bf2ebc6849f09939530a7c090fb745778 --- murano/engine/system/garbage_collector.py | 42 +++++----- murano/tests/unit/dsl/meta/TestGC.yaml | 16 +++- murano/tests/unit/dsl/test_gc.py | 4 +- .../engine/system/test_garbage_collector.py | 81 +++++++++---------- 4 files changed, 76 insertions(+), 67 deletions(-) diff --git a/murano/engine/system/garbage_collector.py b/murano/engine/system/garbage_collector.py index 393d63b22..74ac6d8c4 100644 --- a/murano/engine/system/garbage_collector.py +++ b/murano/engine/system/garbage_collector.py @@ -12,60 +12,56 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_log import log as logging from yaql.language import specs from yaql.language import yaqltypes from murano.dsl import dsl from murano.dsl import helpers -LOG = logging.getLogger(__name__) - @dsl.name('io.murano.system.GC') class GarbageCollector(object): - @staticmethod - @specs.parameter('target', dsl.MuranoObjectParameter()) + @specs.parameter('publisher', dsl.MuranoObjectParameter()) + @specs.parameter('subscriber', dsl.MuranoObjectParameter()) @specs.parameter('handler', yaqltypes.String(nullable=True)) - def subscribe_destruction(target, handler=None): - caller_context = helpers.get_caller_context() - target_this = target.object.real_this - receiver = helpers.get_this(caller_context) + def subscribe_destruction(publisher, subscriber, handler=None): + publisher_this = publisher.object.real_this + subscriber_this = subscriber.object.real_this if handler: - receiver.type.find_single_method(handler) + subscriber.type.find_single_method(handler) dependency = GarbageCollector._find_dependency( - target_this, receiver, handler) + publisher_this, subscriber_this, handler) if not dependency: - dependency = {'subscriber': helpers.weak_ref(receiver), + dependency = {'subscriber': helpers.weak_ref(subscriber_this), 'handler': handler} - target_this.dependencies.setdefault( + publisher_this.dependencies.setdefault( 'onDestruction', []).append(dependency) @staticmethod - @specs.parameter('target', dsl.MuranoObjectParameter()) + @specs.parameter('publisher', dsl.MuranoObjectParameter()) + @specs.parameter('subscriber', dsl.MuranoObjectParameter()) @specs.parameter('handler', yaqltypes.String(nullable=True)) - def unsubscribe_destruction(target, handler=None): - caller_context = helpers.get_caller_context() - target_this = target.object.real_this - receiver = helpers.get_this(caller_context) + def unsubscribe_destruction(publisher, subscriber, handler=None): + publisher_this = publisher.object.real_this + subscriber_this = subscriber.object.real_this if handler: - receiver.type.find_single_method(handler) + subscriber.type.find_single_method(handler) - dds = target_this.dependencies.get('onDestruction', []) + dds = publisher_this.dependencies.get('onDestruction', []) dependency = GarbageCollector._find_dependency( - target_this, receiver, handler) + publisher_this, subscriber_this, handler) if dependency: dds.remove(dependency) @staticmethod - def _find_dependency(target, subscriber, handler): - dds = target.dependencies.get('onDestruction', []) + def _find_dependency(publisher, subscriber, handler): + dds = publisher.dependencies.get('onDestruction', []) for dd in dds: if dd['handler'] != handler: continue diff --git a/murano/tests/unit/dsl/meta/TestGC.yaml b/murano/tests/unit/dsl/meta/TestGC.yaml index 0774ce404..20da0ed5b 100644 --- a/murano/tests/unit/dsl/meta/TestGC.yaml +++ b/murano/tests/unit/dsl/meta/TestGC.yaml @@ -6,6 +6,17 @@ Methods: Body: - $.find(Node) + foo: + Body: + - trace(foo) + + destructionHandler: + Arguments: + - obj: + Contract: $.class(TestGCNode).notNull() + Body: + - trace('Destruction of {0}'.format($obj.value)) + .destroy: Body: - trace($.value) @@ -37,8 +48,9 @@ Methods: :TestGCNode: value: B - $x: new($model) - - sys:GC.subscribeDestruction($x, _handler) - - sys:GC.subscribeDestruction($x.nodes[0], _handler) + - sys:GC.subscribeDestruction($x, $this, _handler) + - sys:GC.subscribeDestruction($x.nodes[0], $this, _handler) + - sys:GC.subscribeDestruction($x.nodes[0], $x, destructionHandler) - $x: null - sys:GC.collect() diff --git a/murano/tests/unit/dsl/test_gc.py b/murano/tests/unit/dsl/test_gc.py index ac473a44d..8a0068dd3 100644 --- a/murano/tests/unit/dsl/test_gc.py +++ b/murano/tests/unit/dsl/test_gc.py @@ -53,4 +53,6 @@ class TestGC(test_case.DslTestCase): def test_collect_with_subscription(self): self.runner.testObjectsCollectWithSubscription() - self.assertEqual(['Destroy A', 'Destroy B', 'B', 'A'], self.traces) + self.assertEqual( + ['Destroy A', 'Destroy B', 'Destruction of B', 'B', 'A'], + self.traces) diff --git a/murano/tests/unit/engine/system/test_garbage_collector.py b/murano/tests/unit/engine/system/test_garbage_collector.py index c5cb2e6a1..1a90b0c6e 100644 --- a/murano/tests/unit/engine/system/test_garbage_collector.py +++ b/murano/tests/unit/engine/system/test_garbage_collector.py @@ -12,11 +12,10 @@ # License for the specific language governing permissions and limitations # under the License. -import _weakref +import weakref import mock from testtools import matchers -import unittest from murano.dsl import dsl from murano.dsl import exceptions @@ -31,57 +30,57 @@ class TestGarbageCollector(base.MuranoTestCase): def setUp(self): super(TestGarbageCollector, self).setUp() - self.mock_subscriber = mock.MagicMock(spec=murano_object.MuranoObject) - self.mock_class = mock.MagicMock(spec=murano_type.MuranoClass) - self.mock_method = mock.MagicMock(spec=murano_method.MuranoMethod) - self.mock_method.name = "mockHandler" - self.mock_class.methods = mock.PropertyMock( - return_value={"mockHandler": self.mock_method}) - self.mock_subscriber.type = self.mock_class - self.mock_subscriber.object_id = "1234" + subscriber = mock.MagicMock(spec=murano_object.MuranoObject) + subscriber.real_this = subscriber - @mock.patch("murano.dsl.helpers.get_caller_context") - @mock.patch("murano.dsl.helpers.get_this") - def test_set_dd(self, this, caller_context): - this.return_value = self.mock_subscriber - target_0 = mock.MagicMock(spec=dsl.MuranoObjectInterface) - target_0.object = self.mock_subscriber - target_0.object.real_this = self.mock_subscriber - target_0.object.dependencies = {} + mock_class = mock.MagicMock(spec=murano_type.MuranoClass) + mock_method = mock.MagicMock(spec=murano_method.MuranoMethod) + mock_method.name = "mockHandler" + mock_class.methods = mock.PropertyMock( + return_value={"mockHandler": mock_method}) + + def find_single_method(name): + if name != 'mockHandler': + raise exceptions.NoMethodFound(name) + + mock_class.find_single_method = find_single_method + subscriber.type = mock_class + self.subscriber = mock.MagicMock(spec=dsl.MuranoObjectInterface) + self.subscriber.object = subscriber + self.subscriber.type = subscriber.type + + publisher = mock.MagicMock(spec=murano_object.MuranoObject) + publisher.real_this = publisher + self.publisher = mock.MagicMock(spec=dsl.MuranoObjectInterface) + self.publisher.object = publisher + + def test_set_dd(self): + self.publisher.object.dependencies = {} garbage_collector.GarbageCollector.subscribe_destruction( - target_0, handler="mockHandler") - dep = self.mock_subscriber.dependencies["onDestruction"] + self.publisher, self.subscriber, handler="mockHandler") + dep = self.publisher.object.dependencies["onDestruction"] self.assertThat(dep, matchers.HasLength(1)) dep = dep[0] self.assertEqual("mockHandler", dep["handler"]) - self.assertEqual(self.mock_subscriber, dep["subscriber"]()) + self.assertEqual(self.subscriber.object, dep["subscriber"]()) - @mock.patch("murano.dsl.helpers.get_caller_context") - @mock.patch("murano.dsl.helpers.get_this") - def test_unset_dd(self, this, caller_context): - this.return_value = self.mock_subscriber - target_1 = mock.MagicMock(spec=dsl.MuranoObjectInterface) - target_1.object = self.mock_subscriber - target_1.object.real_this = self.mock_subscriber - target_1.object.dependencies = ( + def test_unset_dd(self): + self.publisher.object.dependencies = ( {"onDestruction": [{ - "subscriber": _weakref.ref(self.mock_subscriber), + "subscriber": weakref.ref(self.subscriber.object), "handler": "mockHandler" }]}) garbage_collector.GarbageCollector.unsubscribe_destruction( - target_1, handler="mockHandler") + self.publisher, self.subscriber, handler="mockHandler") self.assertEqual( - [], self.mock_subscriber.dependencies["onDestruction"]) + [], self.publisher.object.dependencies["onDestruction"]) - @unittest.skip("WIP") - @mock.patch("murano.dsl.helpers.get_caller_context") - @mock.patch("murano.dsl.helpers.get_this") - def test_set_wrong_handler(self, this, caller_context): - this.return_value = self.mock_subscriber - target_2 = mock.MagicMock(spec=dsl.MuranoObjectInterface) - target_2.object = mock.MagicMock(murano_object.MuranoObject) - target_2.object.dependencies = {} + def test_set_wrong_handler(self): self.assertRaises( exceptions.NoMethodFound, garbage_collector.GarbageCollector.subscribe_destruction, - target_2, handler="wrongHandler") + self.publisher, self.subscriber, handler="invalidHandler") + self.assertRaises( + exceptions.NoMethodFound, + garbage_collector.GarbageCollector.unsubscribe_destruction, + self.publisher, self.subscriber, handler="invalidHandler")