[Piglit] [PATCH 3/6] framework: move WflInfo class into new wflinfo.py module

Brian Paul brianp at vmware.com
Fri Oct 13 04:23:07 UTC 2017


Reduce the clutter in opengl.py
---
 framework/test/opengl.py | 269 +------------------------------------------
 framework/wflinfo.py     | 290 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/all.py             |   4 +-
 3 files changed, 294 insertions(+), 269 deletions(-)
 create mode 100644 framework/wflinfo.py

diff --git a/framework/test/opengl.py b/framework/test/opengl.py
index 20e1c4f..81de933 100644
--- a/framework/test/opengl.py
+++ b/framework/test/opengl.py
@@ -23,17 +23,12 @@
 from __future__ import (
     absolute_import, division, print_function, unicode_literals
 )
-import errno
 import os
-import subprocess
 import warnings
 
-import six
-
-from framework import exceptions, core
+from framework import exceptions, core, wflinfo
 from framework.options import OPTIONS
 from .base import TestIsSkip
-from framework.test import piglit_test
 
 # pylint: disable=too-few-public-methods
 
@@ -47,266 +42,6 @@ __all__ = [
 _DISABLED = bool(os.environ.get('PIGLIT_NO_FAST_SKIP', False))
 
 
-class StopWflinfo(exceptions.PiglitException):
-    """Exception called when wlfinfo getter should stop."""
-    def __init__(self, reason):
-        super(StopWflinfo, self).__init__()
-        self.reason = reason
-
-
-def find_wflinfo():
-    """Find location of the wflinfo executable."""
-    # First check if it's in our piglit bin/ directory
-    wflinfo = os.path.join(piglit_test.TEST_BIN_DIR, "wflinfo.exe")
-    if os.path.exists(wflinfo):
-        return wflinfo
-    else:
-        # Assume it's in $PATH
-        return "wflinfo"
-
-
-class WflInfo(object):
-    """Class representing platform information as provided by wflinfo.
-
-    The design of this is odd to say the least, it's basically a bag with some
-    lazy property evaluators in it, used to avoid calculating the values
-    provided by wflinfo more than once.
-
-    The problems:
-    - Needs to be shared with all subclasses
-    - Needs to evaluate only once
-    - cannot evaluate until user sets OPTIONS.env['PIGLIT_PLATFORM']
-
-    This solves all of that.
-
-    """
-    __shared_state = {}
-    def __new__(cls, *args, **kwargs):
-        # Implement the borg pattern:
-        # https://code.activestate.com/recipes/66531-singleton-we-dont-need-no-stinkin-singleton-the-bo/
-        #
-        # This is something like a singleton, but much easier to implement
-        self = super(WflInfo, cls).__new__(cls, *args, **kwargs)
-        self.__dict__ = cls.__shared_state
-        return self
-
-    @staticmethod
-    def __call_wflinfo(opts):
-        """Helper to call wflinfo and reduce code duplication.
-
-        This catches and handles CalledProcessError and OSError.ernno == 2
-        gracefully: it passes them to allow platforms without a particular
-        gl/gles version or wflinfo (resepctively) to work.
-
-        Arguments:
-        opts -- arguments to pass to wflinfo other than verbose and platform
-
-        """
-        with open(os.devnull, 'w') as d:
-            try:
-                # Get the piglit platform string and, if needed, convert it
-                # to something that wflinfo understands.
-                platform = OPTIONS.env['PIGLIT_PLATFORM']
-                if platform == "mixed_glx_egl":
-                    platform = "glx"
-
-                wflinfo = find_wflinfo()
-
-                raw = subprocess.check_output(
-                    [wflinfo, '--platform', platform] + opts, stderr=d)
-            except subprocess.CalledProcessError:
-                # When we hit this error it usually going to be because we have
-                # an incompatible platform/profile combination
-                raise StopWflinfo('Called')
-            except OSError as e:
-                # If we get a 'no wflinfo' warning then just return
-                print("wflinfo utility not found.")
-                if e.errno == errno.ENOENT:
-                    raise StopWflinfo('OSError')
-                raise
-        return raw.decode('utf-8')
-
-    @staticmethod
-    def __getline(lines, name):
-        """Find a line in a list return it."""
-        for line in lines:
-            if line.startswith(name):
-                return line
-        raise Exception('Unreachable')
-
-    @core.lazy_property
-    def gl_extensions(self):
-        """Call wflinfo to get opengl extensions.
-
-        This provides a very conservative set of extensions, it provides every
-        extension from gles1, 2 and 3 and from GL both core and compat profile
-        as a single set. This may let a few tests execute that will still skip
-        manually, but it helps to ensure that this method never skips when it
-        shouldn't.
-
-        """
-        _trim = len('OpenGL extensions: ')
-        all_ = set()
-
-        def helper(const, vars_):
-            """Helper function to reduce code duplication."""
-            # This is a pretty fragile function but it really does help with
-            # duplication
-            for var in vars_:
-                try:
-                    ret = self.__call_wflinfo(const + [var])
-                except StopWflinfo as e:
-                    # This means tat the particular api or profile is
-                    # unsupported
-                    if e.reason == 'Called':
-                        continue
-                    else:
-                        raise
-                all_.update(set(self.__getline(
-                    ret.split('\n'), 'OpenGL extensions')[_trim:].split()))
-
-        try:
-            helper(['--verbose', '--api'], ['gles1', 'gles2', 'gles3'])
-            helper(['--verbose', '--api', 'gl', '--profile'],
-                   ['core', 'compat', 'none'])
-        except StopWflinfo as e:
-            # Handle wflinfo not being installed by returning an empty set. This
-            # will essentially make FastSkipMixin a no-op.
-            if e.reason == 'OSError':
-                return set()
-            raise
-
-        # Don't return a set with only WFLINFO_GL_ERROR.
-        ret = {e.strip() for e in all_}
-        if ret == {'WFLINFO_GL_ERROR'}:
-            return set()
-        return ret
-
-    @core.lazy_property
-    def gl_version(self):
-        """Calculate the maximum opengl version.
-
-        This will try (in order): core, compat, and finally no profile,
-        stopping when it finds a profile. It assumes that most implementations
-        will have core and compat as equals, or core as superior to compat in
-        terms of support.
-
-        """
-        ret = None
-        for profile in ['core', 'compat', 'none']:
-            try:
-                raw = self.__call_wflinfo(['--api', 'gl', '--profile', profile])
-            except StopWflinfo as e:
-                if e.reason == 'Called':
-                    continue
-                elif e.reason == 'OSError':
-                    break
-                raise
-            else:
-                try:
-                    # Grab the GL version string, trim any release_number values
-                    ret = float(self.__getline(
-                        raw.split('\n'),
-                        'OpenGL version string').split()[3][:3])
-                except (IndexError, ValueError):
-                    # This is caused by wlfinfo returning an error
-                    pass
-                break
-        return ret
-
-    @core.lazy_property
-    def gles_version(self):
-        """Calculate the maximum opengl es version.
-
-        The design of this function isn't 100% correct. GLES1 and GLES2+ behave
-        differently, since 2+ can be silently promoted, but 1 cannot. This
-        means that a driver can implement 2, 3, 3.1, etc, but never have 1
-        support.
-
-        I don't think this is a big deal for a couple of reasons. First, piglit
-        has a very small set of GLES1 tests, so they shouldn't have big impact
-        on runtime, and second, the design of the FastSkipMixin is
-        conservative: it would rather run a few tests that should be skipped
-        than skip a few tests that should be run.
-
-        """
-        ret = None
-        for api in ['gles3', 'gles2', 'gles1']:
-            try:
-                raw = self.__call_wflinfo(['--api', api])
-            except StopWflinfo as e:
-                if e.reason == 'Called':
-                    continue
-                elif e.reason == 'OSError':
-                    break
-                raise
-            else:
-                try:
-                    # Yes, search for "OpenGL version string" in GLES
-                    # GLES doesn't support patch versions.
-                    ret = float(self.__getline(
-                        raw.split('\n'),
-                        'OpenGL version string').split()[5])
-                except (IndexError, ValueError):
-                    # This is caused by wlfinfo returning an error
-                    pass
-                break
-        return ret
-
-    @core.lazy_property
-    def glsl_version(self):
-        """Calculate the maximum OpenGL Shader Language version."""
-        ret = None
-        for profile in ['core', 'compat', 'none']:
-            try:
-                raw = self.__call_wflinfo(
-                    ['--verbose', '--api', 'gl', '--profile', profile])
-            except StopWflinfo as e:
-                if e.reason == 'Called':
-                    continue
-                elif e.reason == 'OSError':
-                    break
-                raise
-            else:
-                try:
-                    # GLSL versions are M.mm formatted
-                    ret = float(self.__getline(
-                        raw.split('\n'),
-                        'OpenGL shading language').split()[-1][:4])
-                except (IndexError, ValueError):
-                    # This is caused by wflinfo returning an error
-                    pass
-                break
-        return ret
-
-    @core.lazy_property
-    def glsl_es_version(self):
-        """Calculate the maximum OpenGL ES Shader Language version."""
-        ret = None
-        for api in ['gles3', 'gles2']:
-            try:
-                raw = self.__call_wflinfo(['--verbose', '--api', api])
-            except StopWflinfo as e:
-                if e.reason == 'Called':
-                    continue
-                elif e.reason == 'OSError':
-                    break
-                raise
-            else:
-                try:
-                    # GLSL ES version numbering is insane.
-                    # For version >= 3 the numbers are 3.00, 3.10, etc.
-                    # For version 2, they are 1.0.xx
-                    ret = float(self.__getline(
-                        raw.split('\n'),
-                        'OpenGL shading language').split()[-1][:3])
-                except (IndexError, ValueError):
-                    # Handle wflinfo internal errors
-                    pass
-                break
-        return ret
-
-
 class FastSkip(object):
     """A class for testing OpenGL requirements.
 
@@ -335,7 +70,7 @@ class FastSkip(object):
     __slots__ = ['gl_required', 'gl_version', 'gles_version', 'glsl_version',
                  'glsl_es_version']
 
-    info = WflInfo()
+    info = wflinfo.WflInfo()
 
     def __init__(self, gl_required=None, gl_version=None, gles_version=None,
                  glsl_version=None, glsl_es_version=None):
diff --git a/framework/wflinfo.py b/framework/wflinfo.py
new file mode 100644
index 0000000..20ef2e1
--- /dev/null
+++ b/framework/wflinfo.py
@@ -0,0 +1,290 @@
+# Copyright (c) 2015-2016 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.
+
+"""Functions for using the wflinfo utility"""
+
+import os
+import subprocess
+
+from framework import exceptions, core
+from framework.options import OPTIONS
+from framework.test import piglit_test
+
+
+class StopWflinfo(exceptions.PiglitException):
+    """Exception called when wlfinfo getter should stop."""
+    def __init__(self, reason):
+        super(StopWflinfo, self).__init__()
+        self.reason = reason
+
+
+def find_wflinfo():
+    """Find location of the wflinfo executable."""
+    # First check if it's in our piglit bin/ directory
+    wflinfo = os.path.join(piglit_test.TEST_BIN_DIR, "wflinfo.exe")
+    if os.path.exists(wflinfo):
+        return wflinfo
+    else:
+        # Assume it's in $PATH
+        return "wflinfo"
+
+
+class WflInfo(object):
+    """Class representing platform information as provided by wflinfo.
+
+    The design of this is odd to say the least, it's basically a bag with some
+    lazy property evaluators in it, used to avoid calculating the values
+    provided by wflinfo more than once.
+
+    The problems:
+    - Needs to be shared with all subclasses
+    - Needs to evaluate only once
+    - cannot evaluate until user sets OPTIONS.env['PIGLIT_PLATFORM']
+
+    This solves all of that.
+
+    """
+    __shared_state = {}
+    def __new__(cls, *args, **kwargs):
+        # Implement the borg pattern:
+        # https://code.activestate.com/recipes/66531-singleton-we-dont-need-no-stinkin-singleton-the-bo/
+        #
+        # This is something like a singleton, but much easier to implement
+        self = super(WflInfo, cls).__new__(cls, *args, **kwargs)
+        self.__dict__ = cls.__shared_state
+        return self
+
+    @staticmethod
+    def __call_wflinfo(opts):
+        """Helper to call wflinfo and reduce code duplication.
+
+        This catches and handles CalledProcessError and OSError.ernno == 2
+        gracefully: it passes them to allow platforms without a particular
+        gl/gles version or wflinfo (resepctively) to work.
+
+        Arguments:
+        opts -- arguments to pass to wflinfo other than verbose and platform
+
+        """
+        with open(os.devnull, 'w') as d:
+            try:
+                # Get the piglit platform string and, if needed, convert it
+                # to something that wflinfo understands.
+                platform = OPTIONS.env['PIGLIT_PLATFORM']
+                if platform == "mixed_glx_egl":
+                    platform = "glx"
+
+                wflinfo = find_wflinfo()
+
+                raw = subprocess.check_output(
+                    [wflinfo, '--platform', platform] + opts, stderr=d)
+            except subprocess.CalledProcessError:
+                # When we hit this error it usually going to be because we have
+                # an incompatible platform/profile combination
+                raise StopWflinfo('Called')
+            except OSError as e:
+                # If we get a 'no wflinfo' warning then just return
+                print("wflinfo utility not found.")
+                if e.errno == errno.ENOENT:
+                    raise StopWflinfo('OSError')
+                raise
+        return raw.decode('utf-8')
+
+    @staticmethod
+    def __getline(lines, name):
+        """Find a line in a list return it."""
+        for line in lines:
+            if line.startswith(name):
+                return line
+        raise Exception('Unreachable')
+
+    @core.lazy_property
+    def gl_extensions(self):
+        """Call wflinfo to get opengl extensions.
+
+        This provides a very conservative set of extensions, it provides every
+        extension from gles1, 2 and 3 and from GL both core and compat profile
+        as a single set. This may let a few tests execute that will still skip
+        manually, but it helps to ensure that this method never skips when it
+        shouldn't.
+
+        """
+        _trim = len('OpenGL extensions: ')
+        all_ = set()
+
+        def helper(const, vars_):
+            """Helper function to reduce code duplication."""
+            # This is a pretty fragile function but it really does help with
+            # duplication
+            for var in vars_:
+                try:
+                    ret = self.__call_wflinfo(const + [var])
+                except StopWflinfo as e:
+                    # This means tat the particular api or profile is
+                    # unsupported
+                    if e.reason == 'Called':
+                        continue
+                    else:
+                        raise
+                all_.update(set(self.__getline(
+                    ret.split('\n'), 'OpenGL extensions')[_trim:].split()))
+
+        try:
+            helper(['--verbose', '--api'], ['gles1', 'gles2', 'gles3'])
+            helper(['--verbose', '--api', 'gl', '--profile'],
+                   ['core', 'compat', 'none'])
+        except StopWflinfo as e:
+            # Handle wflinfo not being installed by returning an empty set. This
+            # will essentially make FastSkipMixin a no-op.
+            if e.reason == 'OSError':
+                return set()
+            raise
+
+        # Don't return a set with only WFLINFO_GL_ERROR.
+        ret = {e.strip() for e in all_}
+        if ret == {'WFLINFO_GL_ERROR'}:
+            return set()
+        return ret
+
+    @core.lazy_property
+    def gl_version(self):
+        """Calculate the maximum opengl version.
+
+        This will try (in order): core, compat, and finally no profile,
+        stopping when it finds a profile. It assumes that most implementations
+        will have core and compat as equals, or core as superior to compat in
+        terms of support.
+
+        """
+        ret = None
+        for profile in ['core', 'compat', 'none']:
+            try:
+                raw = self.__call_wflinfo(['--api', 'gl', '--profile', profile])
+            except StopWflinfo as e:
+                if e.reason == 'Called':
+                    continue
+                elif e.reason == 'OSError':
+                    break
+                raise
+            else:
+                try:
+                    # Grab the GL version string, trim any release_number values
+                    ret = float(self.__getline(
+                        raw.split('\n'),
+                        'OpenGL version string').split()[3][:3])
+                except (IndexError, ValueError):
+                    # This is caused by wlfinfo returning an error
+                    pass
+                break
+        return ret
+
+    @core.lazy_property
+    def gles_version(self):
+        """Calculate the maximum opengl es version.
+
+        The design of this function isn't 100% correct. GLES1 and GLES2+ behave
+        differently, since 2+ can be silently promoted, but 1 cannot. This
+        means that a driver can implement 2, 3, 3.1, etc, but never have 1
+        support.
+
+        I don't think this is a big deal for a couple of reasons. First, piglit
+        has a very small set of GLES1 tests, so they shouldn't have big impact
+        on runtime, and second, the design of the FastSkipMixin is
+        conservative: it would rather run a few tests that should be skipped
+        than skip a few tests that should be run.
+
+        """
+        ret = None
+        for api in ['gles3', 'gles2', 'gles1']:
+            try:
+                raw = self.__call_wflinfo(['--api', api])
+            except StopWflinfo as e:
+                if e.reason == 'Called':
+                    continue
+                elif e.reason == 'OSError':
+                    break
+                raise
+            else:
+                try:
+                    # Yes, search for "OpenGL version string" in GLES
+                    # GLES doesn't support patch versions.
+                    ret = float(self.__getline(
+                        raw.split('\n'),
+                        'OpenGL version string').split()[5])
+                except (IndexError, ValueError):
+                    # This is caused by wlfinfo returning an error
+                    pass
+                break
+        return ret
+
+    @core.lazy_property
+    def glsl_version(self):
+        """Calculate the maximum OpenGL Shader Language version."""
+        ret = None
+        for profile in ['core', 'compat', 'none']:
+            try:
+                raw = self.__call_wflinfo(
+                    ['--verbose', '--api', 'gl', '--profile', profile])
+            except StopWflinfo as e:
+                if e.reason == 'Called':
+                    continue
+                elif e.reason == 'OSError':
+                    break
+                raise
+            else:
+                try:
+                    # GLSL versions are M.mm formatted
+                    ret = float(self.__getline(
+                        raw.split('\n'),
+                        'OpenGL shading language').split()[-1][:4])
+                except (IndexError, ValueError):
+                    # This is caused by wflinfo returning an error
+                    pass
+                break
+        return ret
+
+    @core.lazy_property
+    def glsl_es_version(self):
+        """Calculate the maximum OpenGL ES Shader Language version."""
+        ret = None
+        for api in ['gles3', 'gles2']:
+            try:
+                raw = self.__call_wflinfo(['--verbose', '--api', api])
+            except StopWflinfo as e:
+                if e.reason == 'Called':
+                    continue
+                elif e.reason == 'OSError':
+                    break
+                raise
+            else:
+                try:
+                    # GLSL ES version numbering is insane.
+                    # For version >= 3 the numbers are 3.00, 3.10, etc.
+                    # For version 2, they are 1.0.xx
+                    ret = float(self.__getline(
+                        raw.split('\n'),
+                        'OpenGL shading language').split()[-1][:3])
+                except (IndexError, ValueError):
+                    # Handle wflinfo internal errors
+                    pass
+                break
+        return ret
+
+
diff --git a/tests/all.py b/tests/all.py
index 8c9e33d..7f31a2f 100644
--- a/tests/all.py
+++ b/tests/all.py
@@ -13,7 +13,7 @@ import six
 from six.moves import range
 
 from framework import grouptools
-from framework.test import opengl
+from framework import wflinfo
 from framework import options
 from framework.profile import TestProfile
 from framework.driver_classifier import DriverClassifier
@@ -257,7 +257,7 @@ profile = TestProfile()  # pylint: disable=invalid-name
 
 shader_tests = collections.defaultdict(list)
 
-wfl_info = opengl.WflInfo()
+wfl_info = wflinfo.WflInfo()
 
 
 # Find and add all shader tests.
-- 
1.9.1



More information about the Piglit mailing list