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. > + > + > +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