[v2,7/26] framework/profile: add a copy method to profile

Submitted by Dylan Baker on Oct. 27, 2016, 9:31 p.m.

Details

Message ID ad6333e8fc1659c4daa52b547a6bf3f24ebc1174.1477603560.git-series.dylan@pnwbakers.com
State New
Headers show

Not browsing as part of any series.

Patch hide | download patch | download mbox

diff --git a/framework/profile.py b/framework/profile.py
index 954bf25..73a8a96 100644
--- a/framework/profile.py
+++ b/framework/profile.py
@@ -31,6 +31,7 @@  from __future__ import (
 )
 import collections
 import contextlib
+import copy
 import importlib
 import itertools
 import multiprocessing
@@ -404,6 +405,21 @@  class TestProfile(object):
         with self.test_list.allow_reassignment:
             yield
 
+    def copy(self):
+        """Create a copy of the TestProfile.
+
+        This method creates a copy with references to the original instance
+        (using copy.copy), except for the test_list attribute, which is copied
+        using copy.deepcopy, which is necessary to ensure that filter_tests
+        only affects the right instance. This allows profiles to be
+        "subclassed" by other profiles, without modifying the original.
+        """
+        new = copy.copy(self)
+        new.test_list = copy.deepcopy(self.test_list)
+        new.forced_test_list = copy.copy(self.forced_test_list)
+        new.filters = copy.copy(self.filters)
+        return new
+
 
 def load_test_profile(filename):
     """Load a python module and return it's profile attribute.
diff --git a/framework/programs/print_commands.py b/framework/programs/print_commands.py
index 033ca87..6e68eb5 100644
--- a/framework/programs/print_commands.py
+++ b/framework/programs/print_commands.py
@@ -95,7 +95,7 @@  def main(input_):
 
     profile_ = profile.load_test_profile(args.testProfile)
 
-    profile_._prepare_test_list()
+    profile_.prepare_test_list()
     for name, test in six.iteritems(profile_.test_list):
         assert isinstance(test, Test)
         print(args.format_string.format(
diff --git a/unittests/framework/test_profile.py b/unittests/framework/test_profile.py
index 6671349..5ef95e4 100644
--- a/unittests/framework/test_profile.py
+++ b/unittests/framework/test_profile.py
@@ -285,6 +285,58 @@  class TestTestProfile(object):
 
             assert grouptools.join('foo', 'abc') in self.profile.test_list
 
+    class TestCopy(object):
+        """Tests for the copy method."""
+
+        @pytest.fixture
+        def fixture(self):
+            orig = profile.TestProfile()
+            orig.test_list['foo'] = utils.Test(['foo'])
+            orig.test_list['bar'] = utils.Test(['bar'])
+            orig.filters = [lambda name, _: name != 'bar']
+            orig.forced_test_list = ['foo']
+            return orig
+
+        def test_filters(self, fixture):
+            """The filters attribute is copied correctly."""
+            new = fixture.copy()
+
+            # Assert that the fixtures are equivalent, but not the same
+            assert fixture.filters == new.filters
+            assert fixture.filters is not new.filters
+
+            # And double check by modifying one of them and asserting that the
+            # other has not changed.
+            new.filters.append(lambda name, _: name != 'oink')
+            assert len(fixture.filters) == 1
+
+        def test_forced_test_list(self, fixture):
+            """The forced_test_list attribute is copied correctly."""
+            new = fixture.copy()
+
+            # Assert that the fixtures are equivalent, but not the same
+            assert fixture.forced_test_list == new.forced_test_list
+            assert fixture.forced_test_list is not new.forced_test_list
+
+            # And double check by modifying one of them and asserting that the
+            # other has not changed.
+            del new.forced_test_list[0]
+            assert fixture.forced_test_list[0] == 'foo'
+
+        def test_test_list(self, fixture):
+            """The test_list attribute is copied correctly."""
+            new = fixture.copy()
+
+            # Assert that the fixtures are equivalent, but not the same
+            assert fixture.test_list == new.test_list
+            assert fixture.test_list is not new.test_list
+
+        def test_prepare_test_list(self, fixture):
+            """The prepare_test_list method doesn't affect both."""
+            new = fixture.copy()
+            new.prepare_test_list()
+            assert new.test_list != fixture.test_list
+
 
 class TestTestDict(object):
     """Tests for the TestDict object."""