[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