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? > > -Brian
yes, Reviewed-by: Dylan Baker <dy...@pnwbakers.com> > > > > > >> + > >> + > >> +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 > >> Piglit@lists.freedesktop.org > >> https://lists.freedesktop.org/mailman/listinfo/piglit >
signature.asc
Description: signature
_______________________________________________ Piglit mailing list Piglit@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/piglit