[Piglit] [PATCH 02/13] framework: Split TestProfile and related out of core

Dylan Baker baker.dylan.c at gmail.com
Tue Apr 15 16:12:35 PDT 2014


This solves a dependency loop between core and exectest, laying the
groundwork for future improvements to the exectest module.

Signed-off-by: Dylan Baker <baker.dylan.c at gmail.com>
---
 framework/core.py                | 181 --------------------------------
 framework/profile.py             | 219 +++++++++++++++++++++++++++++++++++++++
 framework/tests/core_tests.py    |   4 +-
 framework/tests/profile_tests.py |  28 +++++
 piglit-resume.py                 |   3 +-
 piglit-run.py                    |   3 +-
 tests/all.py                     |   2 +-
 tests/cl.py                      |   2 +-
 tests/es3conform.py              |   2 +-
 tests/igt.py                     |   3 +-
 tests/oglconform.py              |   2 +-
 tests/sanity.py                  |   2 +-
 12 files changed, 260 insertions(+), 191 deletions(-)
 create mode 100644 framework/profile.py
 create mode 100644 framework/tests/profile_tests.py

diff --git a/framework/core.py b/framework/core.py
index cfe3f78..3979499 100644
--- a/framework/core.py
+++ b/framework/core.py
@@ -399,187 +399,6 @@ class Environment:
         return result
 
 
-class TestProfile(object):
-    def __init__(self):
-        self.tests = {}
-        self.test_list = {}
-        self.filters = []
-        # Sets a default of a Dummy
-        self.dmesg = False
-
-    @property
-    def dmesg(self):
-        """ Return dmesg """
-        return self.__dmesg
-
-    @dmesg.setter
-    def dmesg(self, not_dummy):
-        """ Set dmesg
-
-        Argumnts:
-        not_dummy -- if Truthy dmesg will try to get a PosixDmesg, if Falsy it
-                     will get a DummyDmesg
-
-        """
-        self.__dmesg = get_dmesg(not_dummy)
-
-    def flatten_group_hierarchy(self):
-        '''
-        Convert Piglit's old hierarchical {} structure into a flat
-        dictionary mapping from fully qualified test names to "Test" objects.
-
-        For example,
-        tests['spec']['glsl-1.30']['preprocessor']['compiler']['void.frag']
-        would become:
-        test_list['spec/glsl-1.30/preprocessor/compiler/void.frag']
-        '''
-
-        def f(prefix, group, test_dict):
-            for key in group:
-                fullkey = key if prefix == '' else os.path.join(prefix, key)
-                if isinstance(group[key], dict):
-                    f(fullkey, group[key], test_dict)
-                else:
-                    test_dict[fullkey] = group[key]
-        f('', self.tests, self.test_list)
-        # Clear out the old {}
-        self.tests = {}
-
-    def prepare_test_list(self, env):
-        self.flatten_group_hierarchy()
-
-        def matches_any_regexp(x, re_list):
-            return True in map(lambda r: r.search(x) is not None, re_list)
-
-        def test_matches(path, test):
-            """Filter for user-specified restrictions"""
-            return ((not env.filter or matches_any_regexp(path, env.filter))
-                    and not path in env.exclude_tests and
-                    not matches_any_regexp(path, env.exclude_filter))
-
-        filters = self.filters + [test_matches]
-        def check_all(item):
-            path, test = item
-            for f in filters:
-                if not f(path, test):
-                    return False
-            return True
-
-        # Filter out unwanted tests
-        self.test_list = dict(item for item in self.test_list.iteritems()
-                              if check_all(item))
-
-    def run(self, env, json_writer):
-        '''
-        Schedule all tests in profile for execution.
-
-        See ``Test.schedule`` and ``Test.run``.
-        '''
-
-        self.prepare_test_list(env)
-        log = Log(len(self.test_list), env.verbose)
-
-        def test(pair):
-            """ Function to call test.execute from .map
-
-            adds env and json_writer which are needed by Test.execute()
-
-            """
-            name, test = pair
-            test.execute(env, name, log, json_writer, self.dmesg)
-
-        # Multiprocessing.dummy is a wrapper around Threading that provides a
-        # multiprocessing compatible API
-        #
-        # The default value of pool is the number of virtual processor cores
-        single = multiprocessing.dummy.Pool(1)
-        multi = multiprocessing.dummy.Pool()
-        chunksize = 1
-
-        if env.concurrent == "all":
-            multi.imap(test, self.test_list.iteritems(), chunksize)
-        elif env.concurrent == "none":
-            single.imap(test, self.test_list.iteritems(), chunksize)
-        else:
-            # Filter and return only thread safe tests to the threaded pool
-            multi.imap(test, (x for x in self.test_list.iteritems() if
-                              x[1].run_concurrent), chunksize)
-            # Filter and return the non thread safe tests to the single pool
-            single.imap(test, (x for x in self.test_list.iteritems() if not
-                               x[1].run_concurrent), chunksize)
-
-        # Close and join the pools
-        # If we don't close and the join the pools the script will exit before
-        # the pools finish running
-        multi.close()
-        single.close()
-        multi.join()
-        single.join()
-
-        log.summary()
-
-    def filter_tests(self, function):
-        """Filter out tests that return false from the supplied function
-
-        Arguments:
-        function -- a callable that takes two parameters: path, test and
-                    returns whether the test should be included in the test
-                    run or not.
-        """
-        self.filters.append(function)
-
-    def update(self, *profiles):
-        """ Updates the contents of this TestProfile instance with another
-
-        This method overwrites key:value pairs in self with those in the
-        provided profiles argument. This allows multiple TestProfiles to be
-        called in the same run; which could be used to run piglit and external
-        suites at the same time.
-
-        Arguments:
-        profiles -- one or more TestProfile-like objects to be merged.
-
-        """
-        for profile in profiles:
-            self.tests.update(profile.tests)
-            self.test_list.update(profile.test_list)
-
-
-def loadTestProfile(filename):
-    """ Load a python module and return it's profile attribute
-
-    All of the python test files provide a profile attribute which is a
-    TestProfile instance. This loads that module and returns it or raises an
-    error.
-
-    """
-    mod = importlib.import_module('tests.{0}'.format(
-        os.path.splitext(os.path.basename(filename))[0]))
-
-    try:
-        return mod.profile
-    except AttributeError:
-        print("Error: There is not profile attribute in module {0}."
-              "Did you specify the right file?".format(filename))
-        sys.exit(1)
-
-
-def merge_test_profiles(profiles):
-    """ Helper for loading and merging TestProfile instances
-
-    Takes paths to test profiles as arguments and returns a single merged
-    TestPRofile instance.
-
-    Arguments:
-    profiles -- a list of one or more paths to profile files.
-
-    """
-    profile = loadTestProfile(profiles.pop())
-    for p in profiles:
-        profile.update(loadTestProfile(p))
-    return profile
-
-
 def load_results(filename):
     """ Loader function for TestrunResult class
 
diff --git a/framework/profile.py b/framework/profile.py
new file mode 100644
index 0000000..b76907c
--- /dev/null
+++ b/framework/profile.py
@@ -0,0 +1,219 @@
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# This permission notice shall be included in all copies or
+# substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+# PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHOR(S) BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+""" Provides Profiles for test groups
+
+Each set of tests, both native Piglit profiles and external suite integration,
+are represented by a TestProfile or a TestProfile derived object.
+
+"""
+
+from __future__ import print_function
+import os
+import sys
+import multiprocessing
+import multiprocessing.dummy
+import importlib
+
+from framework.dmesg import get_dmesg
+from framework.log import Log
+
+
+
+class TestProfile(object):
+    def __init__(self):
+        self.tests = {}
+        self.test_list = {}
+        self.filters = []
+        # Sets a default of a Dummy
+        self.dmesg = False
+
+    @property
+    def dmesg(self):
+        """ Return dmesg """
+        return self.__dmesg
+
+    @dmesg.setter
+    def dmesg(self, not_dummy):
+        """ Set dmesg
+
+        Argumnts:
+        not_dummy -- if Truthy dmesg will try to get a PosixDmesg, if Falsy it
+                     will get a DummyDmesg
+
+        """
+        self.__dmesg = get_dmesg(not_dummy)
+
+    def flatten_group_hierarchy(self):
+        '''
+        Convert Piglit's old hierarchical Group() structure into a flat
+        dictionary mapping from fully qualified test names to "Test" objects.
+
+        For example,
+        tests['spec']['glsl-1.30']['preprocessor']['compiler']['void.frag']
+        would become:
+        test_list['spec/glsl-1.30/preprocessor/compiler/void.frag']
+        '''
+
+        def f(prefix, group, test_dict):
+            for key in group:
+                fullkey = key if prefix == '' else os.path.join(prefix, key)
+                if isinstance(group[key], dict):
+                    f(fullkey, group[key], test_dict)
+                else:
+                    test_dict[fullkey] = group[key]
+        f('', self.tests, self.test_list)
+        # Clear out the old Group()
+        self.tests = {}
+
+    def prepare_test_list(self, env):
+        self.flatten_group_hierarchy()
+
+        def matches_any_regexp(x, re_list):
+            return True in map(lambda r: r.search(x) is not None, re_list)
+
+        def test_matches(path, test):
+            """Filter for user-specified restrictions"""
+            return ((not env.filter or matches_any_regexp(path, env.filter))
+                    and not path in env.exclude_tests and
+                    not matches_any_regexp(path, env.exclude_filter))
+
+        filters = self.filters + [test_matches]
+        def check_all(item):
+            path, test = item
+            for f in filters:
+                if not f(path, test):
+                    return False
+            return True
+
+        # Filter out unwanted tests
+        self.test_list = dict(item for item in self.test_list.iteritems()
+                              if check_all(item))
+
+    def run(self, env, json_writer):
+        '''
+        Schedule all tests in profile for execution.
+
+        See ``Test.schedule`` and ``Test.run``.
+        '''
+
+        self.prepare_test_list(env)
+        log = Log(len(self.test_list), env.verbose)
+
+        def test(pair):
+            """ Function to call test.execute from .map
+
+            adds env and json_writer which are needed by Test.execute()
+
+            """
+            name, test = pair
+            test.execute(env, name, log, json_writer, self.dmesg)
+
+        # Multiprocessing.dummy is a wrapper around Threading that provides a
+        # multiprocessing compatible API
+        #
+        # The default value of pool is the number of virtual processor cores
+        single = multiprocessing.dummy.Pool(1)
+        multi = multiprocessing.dummy.Pool()
+        chunksize = 1
+
+        if env.concurrent == "all":
+            multi.imap(test, self.test_list.iteritems(), chunksize)
+        elif env.concurrent == "none":
+            single.imap(test, self.test_list.iteritems(), chunksize)
+        else:
+            # Filter and return only thread safe tests to the threaded pool
+            multi.imap(test, (x for x in self.test_list.iteritems() if
+                              x[1].run_concurrent), chunksize)
+            # Filter and return the non thread safe tests to the single pool
+            single.imap(test, (x for x in self.test_list.iteritems() if not
+                               x[1].run_concurrent), chunksize)
+
+        # Close and join the pools
+        # If we don't close and the join the pools the script will exit before
+        # the pools finish running
+        multi.close()
+        single.close()
+        multi.join()
+        single.join()
+
+        log.summary()
+
+    def filter_tests(self, function):
+        """Filter out tests that return false from the supplied function
+
+        Arguments:
+        function -- a callable that takes two parameters: path, test and
+                    returns whether the test should be included in the test
+                    run or not.
+        """
+        self.filters.append(function)
+
+    def update(self, *profiles):
+        """ Updates the contents of this TestProfile instance with another
+
+        This method overwrites key:value pairs in self with those in the
+        provided profiles argument. This allows multiple TestProfiles to be
+        called in the same run; which could be used to run piglit and external
+        suites at the same time.
+
+        Arguments:
+        profiles -- one or more TestProfile-like objects to be merged.
+
+        """
+        for profile in profiles:
+            self.tests.update(profile.tests)
+            self.test_list.update(profile.test_list)
+
+
+def loadTestProfile(filename):
+    """ Load a python module and return it's profile attribute
+
+    All of the python test files provide a profile attribute which is a
+    TestProfile instance. This loads that module and returns it or raises an
+    error.
+
+    """
+    mod = importlib.import_module('tests.{0}'.format(
+        os.path.splitext(os.path.basename(filename))[0]))
+
+    try:
+        return mod.profile
+    except AttributeError:
+        print("Error: There is not profile attribute in module {0}."
+              "Did you specify the right file?".format(filename))
+        sys.exit(1)
+
+
+def merge_test_profiles(profiles):
+    """ Helper for loading and merging TestProfile instances
+
+    Takes paths to test profiles as arguments and returns a single merged
+    TestPRofile instance.
+
+    Arguments:
+    profiles -- a list of one or more paths to profile files.
+
+    """
+    profile = loadTestProfile(profiles.pop())
+    for p in profiles:
+        profile.update(loadTestProfile(p))
+    return profile
diff --git a/framework/tests/core_tests.py b/framework/tests/core_tests.py
index e2ef23d..44462ce 100644
--- a/framework/tests/core_tests.py
+++ b/framework/tests/core_tests.py
@@ -49,8 +49,8 @@ def test_generate_initialize():
     """
     yieldable = check_initialize
 
-    for target in [core.TestProfile, core.Environment, core.TestrunResult,
-                   core.TestResult, core.PiglitJSONEncoder]:
+    for target in [core.Environment, core.TestrunResult, core.TestResult,
+                   core.PiglitJSONEncoder]:
         yieldable.description = "Test that {} initializes".format(
             target.__name__)
         yield yieldable, target
diff --git a/framework/tests/profile_tests.py b/framework/tests/profile_tests.py
new file mode 100644
index 0000000..c5bdf34
--- /dev/null
+++ b/framework/tests/profile_tests.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2014 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+""" Provides test for the framework.profile modules """
+
+import framework.profile as profile
+
+
+def test_initialize_testprofile():
+    """ TestProfile initializes """
+    profile.TestProfile()
diff --git a/piglit-resume.py b/piglit-resume.py
index 09d0664..590abf0 100755
--- a/piglit-resume.py
+++ b/piglit-resume.py
@@ -28,6 +28,7 @@ import os.path as path
 import argparse
 
 import framework.core as core
+import framework.profile
 
 
 def main():
@@ -76,7 +77,7 @@ def main():
         json_writer.write_dict_item(key, value)
         env.exclude_tests.add(key)
     
-    profile = core.merge_test_profiles(results.options['profile'])
+    profile = framework.profile.merge_test_profiles(results.options['profile'])
     if env.dmesg:
         profile.dmesg = env.dmesg
 
diff --git a/piglit-run.py b/piglit-run.py
index 672c057..b4b00bc 100755
--- a/piglit-run.py
+++ b/piglit-run.py
@@ -31,6 +31,7 @@ import time
 
 sys.path.append(path.dirname(path.realpath(sys.argv[0])))
 import framework.core as core
+import framework.profile
 
 
 def main():
@@ -157,7 +158,7 @@ def main():
     for (key, value) in env.collectData().items():
         json_writer.write_dict_item(key, value)
 
-    profile = core.merge_test_profiles(args.test_profile)
+    profile = framework.profile.merge_test_profiles(args.test_profile)
 
     json_writer.write_dict_key('tests')
     json_writer.open_dict()
diff --git a/tests/all.py b/tests/all.py
index 0945a27..42be8f4 100644
--- a/tests/all.py
+++ b/tests/all.py
@@ -9,7 +9,7 @@ import os.path as path
 import platform
 import shlex
 
-from framework.core import TestProfile
+from framework.profile import TestProfile
 from framework.exectest import PiglitTest
 from framework.gleantest import GleanTest
 from framework.glsl_parser_test import GLSLParserTest, add_glsl_parser_test, import_glsl_parser_tests
diff --git a/tests/cl.py b/tests/cl.py
index 86a76a8..a16fce4 100644
--- a/tests/cl.py
+++ b/tests/cl.py
@@ -9,7 +9,7 @@ import os.path as path
 
 from framework.opencv import add_opencv_tests
 
-from framework.core import TestProfile
+from framework.profile import TestProfile
 from framework.exectest import PiglitTest
 
 ######
diff --git a/tests/es3conform.py b/tests/es3conform.py
index 6549f4e..0aefe58 100644
--- a/tests/es3conform.py
+++ b/tests/es3conform.py
@@ -26,7 +26,7 @@ import sys
 
 from os import path
 from glob import glob
-from framework.core import TestProfile
+from framework.profile import TestProfile
 from framework.exectest import Test, TEST_BIN_DIR
 
 __all__ = ['profile']
diff --git a/tests/igt.py b/tests/igt.py
index c2c522f..1051ec8 100644
--- a/tests/igt.py
+++ b/tests/igt.py
@@ -28,7 +28,8 @@ import sys
 import subprocess
 
 from os import path
-from framework.core import TestProfile, TestResult
+from framework.core import TestResult
+from framework.profile import TestProfile
 from framework.exectest import Test, TEST_BIN_DIR
 
 __all__ = ['profile']
diff --git a/tests/oglconform.py b/tests/oglconform.py
index c303b3e..d62ceff 100644
--- a/tests/oglconform.py
+++ b/tests/oglconform.py
@@ -26,7 +26,7 @@ import re
 import sys
 import subprocess
 
-from framework.core import TestProfile
+from framework.profile import TestProfile
 from framework.exectest import Test, TEST_BIN_DIR
 from os import path
 
diff --git a/tests/sanity.py b/tests/sanity.py
index 059127c..0e0e038 100644
--- a/tests/sanity.py
+++ b/tests/sanity.py
@@ -2,7 +2,7 @@
 # Minimal tests to check whether the installation is working
 #
 
-from framework.core import TestProfile
+from framework.profile import TestProfile
 from framework.gleantest import GleanTest
 
 __all__ = ['profile']
-- 
1.9.2



More information about the Piglit mailing list