diff --git a/wlauto/tests/test_exec_control.py b/wlauto/tests/test_exec_control.py new file mode 100644 index 00000000..490239d2 --- /dev/null +++ b/wlauto/tests/test_exec_control.py @@ -0,0 +1,269 @@ +# Copyright 2013-2015 ARM Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +# pylint: disable=W0231,W0613,E0611,W0603,R0201 +from unittest import TestCase + +from nose.tools import assert_equal, assert_raises + +from wlauto.utils.exec_control import (init_environment, reset_environment, + activate_environment, once, + once_per_class, once_per_instance) + +class TestClass(object): + + def __init__(self): + self.count = 0 + + @once + def initilize_once(self): + self.count += 1 + + @once_per_class + def initilize_once_per_class(self): + self.count += 1 + + @once_per_instance + def initilize_once_per_instance(self): + self.count += 1 + + +class SubClass(TestClass): + + def __init__(self): + super(SubClass, self).__init__() + + +class SubSubClass(SubClass): + + def __init__(self): + super(SubSubClass, self).__init__() + + +class AnotherClass(object): + + def __init__(self): + self.count = 0 + + @once + def initilize_once(self): + self.count += 1 + + @once_per_class + def initilize_once_per_class(self): + self.count += 1 + + @once_per_instance + def initilize_once_per_instance(self): + self.count += 1 + + +class EnvironmentManagementTest(TestCase): + + def test_duplicate_environment(self): + init_environment('ENVIRONMENT') + assert_raises(ValueError, init_environment, 'ENVIRONMENT') + + def test_reset_missing_environment(self): + assert_raises(ValueError, reset_environment, 'MISSING') + + def test_reset_current_environment(self): + activate_environment('CURRENT_ENVIRONMENT') + t1 = TestClass() + t1.initilize_once() + assert_equal(t1.count, 1) + + reset_environment() + t1.initilize_once() + assert_equal(t1.count, 2) + + def test_switch_environment(self): + activate_environment('ENVIRONMENT1') + t1 = TestClass() + t1.initilize_once() + assert_equal(t1.count, 1) + + activate_environment('ENVIRONMENT2') + t1.initilize_once() + assert_equal(t1.count, 2) + + activate_environment('ENVIRONMENT1') + t1.initilize_once() + assert_equal(t1.count, 2) + + def test_reset_environment_name(self): + activate_environment('ENVIRONMENT') + t1 = TestClass() + t1.initilize_once() + assert_equal(t1.count, 1) + + reset_environment('ENVIRONMENT') + t1.initilize_once() + assert_equal(t1.count, 2) + + +class OnlyOnceEnvironmentTest(TestCase): + + def setUp(self): + activate_environment('TEST_ENVIRONMENT') + + def tearDown(self): + reset_environment('TEST_ENVIRONMENT') + + def test_single_instance(self): + t1 = TestClass() + ac = AnotherClass() + + t1.initilize_once() + assert_equal(t1.count, 1) + + t1.initilize_once() + assert_equal(t1.count, 1) + + ac.initilize_once() + assert_equal(ac.count, 1) + + + def test_mulitple_instances(self): + t1 = TestClass() + t2 = TestClass() + + t1.initilize_once() + assert_equal(t1.count, 1) + + t2.initilize_once() + assert_equal(t2.count, 0) + + + def test_sub_classes(self): + t1 = TestClass() + sc = SubClass() + ss = SubSubClass() + + t1.initilize_once() + assert_equal(t1.count, 1) + + sc.initilize_once() + sc.initilize_once() + assert_equal(sc.count, 0) + + ss.initilize_once() + ss.initilize_once() + assert_equal(ss.count, 0) + + +class OncePerClassEnvironmentTest(TestCase): + + def setUp(self): + activate_environment('TEST_ENVIRONMENT') + + def tearDown(self): + reset_environment('TEST_ENVIRONMENT') + + def test_single_instance(self): + t1 = TestClass() + ac = AnotherClass() + + t1.initilize_once_per_class() + assert_equal(t1.count, 1) + + t1.initilize_once_per_class() + assert_equal(t1.count, 1) + + ac.initilize_once_per_class() + assert_equal(ac.count, 1) + + + def test_mulitple_instances(self): + t1 = TestClass() + t2 = TestClass() + + t1.initilize_once_per_class() + assert_equal(t1.count, 1) + + t2.initilize_once_per_class() + assert_equal(t2.count, 0) + + + def test_sub_classes(self): + t1 = TestClass() + sc1 = SubClass() + sc2 = SubClass() + ss1 = SubSubClass() + ss2 = SubSubClass() + + t1.initilize_once_per_class() + assert_equal(t1.count, 1) + + sc1.initilize_once_per_class() + sc2.initilize_once_per_class() + assert_equal(sc1.count, 1) + assert_equal(sc2.count, 0) + + ss1.initilize_once_per_class() + ss2.initilize_once_per_class() + assert_equal(ss1.count, 1) + assert_equal(ss2.count, 0) + + +class OncePerInstanceEnvironmentTest(TestCase): + + def setUp(self): + activate_environment('TEST_ENVIRONMENT') + + def tearDown(self): + reset_environment('TEST_ENVIRONMENT') + + def test_single_instance(self): + t1 = TestClass() + ac = AnotherClass() + + t1.initilize_once_per_instance() + assert_equal(t1.count, 1) + + t1.initilize_once_per_instance() + assert_equal(t1.count, 1) + + ac.initilize_once_per_instance() + assert_equal(ac.count, 1) + + + def test_mulitple_instances(self): + t1 = TestClass() + t2 = TestClass() + + t1.initilize_once_per_instance() + assert_equal(t1.count, 1) + + t2.initilize_once_per_instance() + assert_equal(t2.count, 1) + + + def test_sub_classes(self): + t1 = TestClass() + sc = SubClass() + ss = SubSubClass() + + t1.initilize_once_per_instance() + assert_equal(t1.count, 1) + + sc.initilize_once_per_instance() + sc.initilize_once_per_instance() + assert_equal(sc.count, 1) + + ss.initilize_once_per_instance() + ss.initilize_once_per_instance() + assert_equal(ss.count, 1) diff --git a/wlauto/utils/exec_control.py b/wlauto/utils/exec_control.py new file mode 100644 index 00000000..01025ee0 --- /dev/null +++ b/wlauto/utils/exec_control.py @@ -0,0 +1,117 @@ +from inspect import getmro + +# "environment" management: +__environments = {} +__active_environment = None + + +def activate_environment(name): + """ + Sets the current tracking environment to ``name``. If an + environment with that name does not already exist, it will be + created. + """ + #pylint: disable=W0603 + global __active_environment + + if name not in __environments.keys(): + init_environment(name) + __active_environment = name + + +def init_environment(name): + """ + Create a new environment called ``name``, but do not set it as the + current environment. + + :raises: ``ValueError`` if an environment with name ``name`` + already exists. + """ + if name in __environments.keys(): + msg = "Environment {} already exists".format(name) + raise ValueError(msg) + __environments[name] = [] + + +def reset_environment(name=None): + """ + Reset method call tracking for environment ``name``. If ``name`` is + not specified or is ``None``, reset the current active environment. + + :raises: ``ValueError`` if an environment with name ``name`` + does not exist. + """ + + if name is not None: + if name not in __environments.keys(): + msg = "Environment {} does not exist".format(name) + raise ValueError(msg) + __environments[name] = [] + else: + if __active_environment is None: + raise ValueError("No Environment Active") + __environments[__active_environment] = [] + +# The decorators: + + +def once_per_instance(method): + """ + The specified method will be invoked only once for every bound + instance within the environment. + """ + def wrapper(*args, **kwargs): + if __active_environment is None: + raise ValueError("No Environment Active") + func_id = repr(args[0]) + if func_id in __environments[__active_environment]: + return + else: + __environments[__active_environment].append(func_id) + return method(*args, **kwargs) + + return wrapper + + +def once_per_class(method): + """ + The specified method will be invoked only once for all instances + of a class within the environment. + """ + def wrapper(*args, **kwargs): + if __active_environment is None: + raise ValueError("No Environment Active") + + func_id = repr(method.func_name) + repr(args[0].__class__) + + if func_id in __environments[__active_environment]: + return + else: + __environments[__active_environment].append(func_id) + return method(*args, **kwargs) + + return wrapper + + +def once(method): + + """ + The specified method will be invoked only once within the + environment. + """ + def wrapper(*args, **kwargs): + if __active_environment is None: + raise ValueError("No Environment Active") + + func_id = repr(method.func_name) + # Store the least derived class, which isn't object, to account + # for subclasses. + func_id += repr(getmro(args[0].__class__)[-2]) + + if func_id in __environments[__active_environment]: + return + else: + __environments[__active_environment].append(func_id) + return method(*args, **kwargs) + + return wrapper