Quoting Brian Paul (2017-10-23 10:37:31)
> On 10/18/2017 06:39 PM, Dylan Baker wrote:
> > Quoting Brian Paul (2017-10-12 21:23:07)
> >> 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
> >
> > I think it makes sense to put TEST_BIN_DIR in core so that framework doesn't
> > depend on test, but I'm content for that to be a follow up patch.
>
> OK, I'll make that change later. R-b on this patch?
>
> -Brianyes, Reviewed-by: Dylan Baker <[email protected]> > > > > > >> + > >> + > >> +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 > >> > >> _______________________________________________ > >> Piglit mailing list > >> [email protected] > >> https://lists.freedesktop.org/mailman/listinfo/piglit >
signature.asc
Description: signature
_______________________________________________ Piglit mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/piglit
