[Piglit] [PATCH 1/3] framework: Port framework to python3

Dylan Baker baker.dylan.c at gmail.com
Thu Mar 5 14:04:01 PST 2015


This is a pretty substantial patch, but it would be a lot of work to
port this incrementally with a lot of code churn, much of which wouldn't
receive thorough testing.  And honestly, while it touches every python
file in the tree, it doesn't touch them substantially.

Due to a lot of work put in in the last few months/years the code base
is pretty modern, and most of what needs to be changed are intentional
changes to language form upstream: dicts don't have iter* methods, more
functions return iterators so they need to be wrapped in list() if they
need to be accessed by index or walked multiple times. __future__
imports are removed, and the largest change is the transition from str
-> bytes and unicode -> str.

This also changes the shbang lines in the scripts, and a small updated
to cmake to search for python3 instead of python2.

Signed-off-by: Dylan Baker <dylanx.c.baker at intel.com>
---
 CMakeLists.txt                            |   2 +-
 framework/backends/__init__.py            |   9 +--
 framework/backends/abstract.py            |   5 +-
 framework/backends/json.py                |   4 +-
 framework/backends/junit.py               |  13 ++--
 framework/core.py                         |   8 +--
 framework/dmesg.py                        |   6 +-
 framework/grouptools.py                   |   8 +--
 framework/log.py                          |   9 ++-
 framework/profile.py                      |  25 ++++---
 framework/programs/run.py                 |  10 +--
 framework/programs/summary.py             |   2 +-
 framework/results.py                      |  16 ++---
 framework/status.py                       |  17 ++---
 framework/summary.py                      |  64 +++++++++--------
 framework/test/__init__.py                |   2 +-
 framework/test/base.py                    |  40 ++++-------
 framework/test/gleantest.py               |   2 +-
 framework/test/glsl_parser_test.py        |   4 +-
 framework/test/gtest.py                   |   2 +-
 framework/test/oclconform.py              |   2 +-
 framework/test/opencv.py                  |   2 +-
 framework/test/piglit_test.py             |   2 +-
 framework/tests/backends_tests.py         |   4 +-
 framework/tests/base_tests.py             |   2 +-
 framework/tests/core_tests.py             |  16 ++---
 framework/tests/dmesg_tests.py            |  16 ++---
 framework/tests/gleantest_tests.py        |   2 +-
 framework/tests/glsl_parser_test_tests.py | 115 +++++++++++++++---------------
 framework/tests/grouptools_tests.py       |   6 +-
 framework/tests/gtest_tests.py            |   2 +-
 framework/tests/integration_tests.py      |   8 +--
 framework/tests/json_tests.py             |   2 +-
 framework/tests/log_tests.py              |   4 +-
 framework/tests/opencv_tests.py           |   2 +-
 framework/tests/piglit_test_tests.py      |   2 +-
 framework/tests/results_tests.py          |   4 +-
 framework/tests/results_v0_tests.py       |  14 ++--
 framework/tests/results_v1_tests.py       |   6 +-
 framework/tests/results_v2_tests.py       |   2 +-
 framework/tests/results_v3_tests.py       |   2 +-
 framework/tests/run_parser_tests.py       |   6 +-
 framework/tests/shader_test_tests.py      |  18 ++---
 framework/tests/status_tests.py           |   8 +--
 framework/tests/summary_tests.py          |   8 +--
 framework/tests/test_lists.py             |   2 +-
 framework/tests/utils.py                  |   4 +-
 piglit                                    |   2 +-
 piglit-print-commands.py                  |   4 +-
 piglit-resume.py                          |   2 +-
 piglit-run.py                             |   2 +-
 piglit-summary-html.py                    |   2 +-
 piglit-summary.py                         |   2 +-
 templates/index.mako                      |   2 +-
 templates/testrun_info.mako               |   4 +-
 tests/all.py                              |   2 +-
 tests/cl.py                               |   2 +-
 tests/deqp_gles3.py                       |   8 +--
 tests/igt.py                              |  12 ++--
 tests/xts.py                              |   2 +-
 60 files changed, 267 insertions(+), 288 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index db922d8..69802b6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -189,7 +189,7 @@ IF(PIGLIT_BUILD_GLX_TESTS)
 	pkg_check_modules(GLPROTO REQUIRED glproto)
 ENDIF()
 
-find_package(PythonInterp 2.7 REQUIRED)
+find_package(PythonInterp 3.3 REQUIRED)
 find_package(PythonNumpy 1.6.2 REQUIRED)
 find_package(PythonMako 0.8.0 REQUIRED)
 find_package(PythonSix 1.4.0 REQUIRED)
diff --git a/framework/backends/__init__.py b/framework/backends/__init__.py
index 257bba9..d3b08ed 100644
--- a/framework/backends/__init__.py
+++ b/framework/backends/__init__.py
@@ -25,8 +25,8 @@ from .json import *
 from .junit import *
 
 
-# A list of available backends
-BACKENDS = ['json', 'junit']
+# A set of available backends
+BACKENDS = set(['json', 'junit'])
 
 
 def get_backend(backend):
@@ -37,6 +37,7 @@ def get_backend(backend):
     }
 
     # Be sure that we're exporting the same list of backends that we actually
-    # have available
-    assert backends.keys() == BACKENDS
+    # have available. These must both be sets since lists are ordered and we
+    # don't actually care about ordering, just that they have the same elements
+    assert set(backends.keys()) == BACKENDS
     return backends[backend]
diff --git a/framework/backends/abstract.py b/framework/backends/abstract.py
index 928ba9a..b0c0a2d 100644
--- a/framework/backends/abstract.py
+++ b/framework/backends/abstract.py
@@ -25,13 +25,13 @@ This module provides mixins and base classes for backend modules.
 
 """
 
-from __future__ import print_function, absolute_import
+
 import os
 import abc
 import itertools
 
 
-class Backend(object):
+class Backend(object, metaclass=abc.ABCMeta):
     """ Abstract base class for summary backends
 
     This class provides an abstract ancestor for classes implementing backends,
@@ -45,7 +45,6 @@ class Backend(object):
     be thread safe and not need to be locked during write)
 
     """
-    __metaclass__ = abc.ABCMeta
 
     @abc.abstractmethod
     def __init__(self, dest, metadata, **options):
diff --git a/framework/backends/json.py b/framework/backends/json.py
index d14eeb2..0bf2da5 100644
--- a/framework/backends/json.py
+++ b/framework/backends/json.py
@@ -20,7 +20,7 @@
 
 """ Module providing json backend for piglit """
 
-from __future__ import print_function, absolute_import
+
 import os
 import shutil
 
@@ -141,7 +141,7 @@ class JSONBackend(FileBackend):
     def write_test(self, name, data):
         """ Write a test into the JSON tests dictionary """
         t = os.path.join(self._dest, 'tests',
-                         '{}.json'.format(self._counter.next()))
+                         '{}.json'.format(next(self._counter)))
         with open(t, 'w') as f:
             json.dump({name: data}, f, default=piglit_encoder)
             self._fsync(f)
diff --git a/framework/backends/junit.py b/framework/backends/junit.py
index 53b6086..796ec7c 100644
--- a/framework/backends/junit.py
+++ b/framework/backends/junit.py
@@ -20,7 +20,7 @@
 
 """ Module implementing a JUnitBackend for piglit """
 
-from __future__ import print_function, absolute_import
+
 import os.path
 import re
 import shutil
@@ -96,13 +96,12 @@ class JUnitBackend(FileBackend):
         # This must be bytes or unicode
         piglit.attrib['tests'] = str(len(piglit))
 
-        with open(os.path.join(self._dest, 'results.xml'), 'w') as f:
-            f.write("<?xml version='1.0' encoding='utf-8'?>\n")
+        with open(os.path.join(self._dest, 'results.xml'), 'wb') as f:
             # lxml has a pretty print we want to use
             if etree.__name__ == 'lxml.etree':
-                f.write(etree.tostring(root, pretty_print=True))
+                f.write(etree.tostring(root, encoding='utf-8', pretty_print=True))
             else:
-                f.write(etree.tostring(root))
+                f.write(etree.tostring(root, encoding='utf-8'))
 
         shutil.rmtree(os.path.join(self._dest, 'tests'))
 
@@ -198,7 +197,7 @@ class JUnitBackend(FileBackend):
         calculate_result()
 
         t = os.path.join(self._dest, 'tests',
-                         '{}.xml'.format(self._counter.next()))
-        with open(t, 'w') as f:
+                         '{}.xml'.format(next(self._counter)))
+        with open(t, 'wb') as f:
             f.write(etree.tostring(element))
             self._fsync(f)
diff --git a/framework/core.py b/framework/core.py
index dfc8f6a..0514afb 100644
--- a/framework/core.py
+++ b/framework/core.py
@@ -22,13 +22,13 @@
 
 # Piglit core
 
-from __future__ import print_function, absolute_import
+
 import errno
 import os
 import re
 import subprocess
 import sys
-import ConfigParser
+import configparser
 
 __all__ = [
     'PIGLIT_CONFIG',
@@ -40,7 +40,7 @@ __all__ = [
 
 
 PLATFORMS = ["glx", "x11_egl", "wayland", "gbm", "mixed_glx_egl"]
-PIGLIT_CONFIG = ConfigParser.SafeConfigParser(allow_no_value=True)
+PIGLIT_CONFIG = configparser.SafeConfigParser(allow_no_value=True)
 
 def get_config(arg=None):
     if arg:
@@ -117,7 +117,7 @@ class Options(object):
         }
 
     def __iter__(self):
-        for key, values in self.__dict__.iteritems():
+        for key, values in self.__dict__.items():
             # If the values are regex compiled then yield their pattern
             # attribute, which is the original plaintext they were compiled
             # from, otherwise yield them normally.
diff --git a/framework/dmesg.py b/framework/dmesg.py
index 1a5f629..f1cb976 100644
--- a/framework/dmesg.py
+++ b/framework/dmesg.py
@@ -35,7 +35,7 @@ dmesg implementation for their OS.
 
 """
 
-from __future__ import print_function, absolute_import
+
 import re
 import sys
 import subprocess
@@ -136,7 +136,7 @@ class BaseDmesg(object):
 
             # Replace the results of any subtests
             if 'subtest' in result:
-                for key, value in result['subtest'].iteritems():
+                for key, value in result['subtest'].items():
                     result['subtest'][key] = replace(value)
 
             # Add the dmesg values to the result
@@ -171,7 +171,7 @@ class LinuxDmesg(BaseDmesg):
             warnings.warn("Cannot check dmesg for timestamp support. If you "
                           "do not have timestamps enabled in your kernel you "
                           "get incomplete dmesg captures", RuntimeWarning)
-        elif not re.match(r'\[\s*\d+\.\d+\]', self._last_message):
+        elif not re.match(b'\[\s*\d+\.\d+\]', self._last_message):
             # Do an initial check to ensure that dmesg has timestamps, we need
             # timestamps to work correctly. A proper linux dmesg timestamp
             # looks like this: [    0.00000]
diff --git a/framework/grouptools.py b/framework/grouptools.py
index 3d26bbc..1b942cb 100644
--- a/framework/grouptools.py
+++ b/framework/grouptools.py
@@ -44,7 +44,7 @@ __all__ = [
 
 def _assert_illegal(group):
     """Helper that checks for illegal characters in input."""
-    assert isinstance(group, (str, unicode)), 'Type must be string or unicode'
+    assert isinstance(group, str), 'Type must be string'
     assert '\\' not in group, \
         'Groups are not paths and cannot contain \\.  ({})'.format(group)
     assert not group.startswith('/'), \
@@ -104,8 +104,8 @@ def join(*args):
 
     """
     for group in args:
-        assert isinstance(group, (str, unicode)), \
-            'Type must be string or unicode'
+        assert isinstance(group, str), \
+            'Type must be string'
         assert '\\' not in group, \
             'Groups are not paths and cannot contain \\.  ({})'.format(group)
     assert not args[0].startswith('/'), \
@@ -152,7 +152,7 @@ def from_path(path):
     This safely handles both Windows and Unix style paths.
 
     """
-    assert isinstance(path, (str, unicode)), 'Type must be string or unicode'
+    assert isinstance(path, str), 'Type must be string'
     assert not path.startswith('/'), \
         'Groups cannot start with /. ({})' .format(path)
 
diff --git a/framework/log.py b/framework/log.py
index 759974a..4072326 100644
--- a/framework/log.py
+++ b/framework/log.py
@@ -26,7 +26,7 @@ returning BaseLog derived instances to individual tests.
 
 """
 
-from __future__ import print_function, absolute_import
+
 import sys
 import abc
 import itertools
@@ -36,7 +36,7 @@ import collections
 __all__ = ['LogManager']
 
 
-class BaseLog(object):
+class BaseLog(object, metaclass=abc.ABCMeta):
     """ Abstract base class for Log objects
 
     It provides a lock, which should be used to lock whever the shared state is
@@ -46,7 +46,6 @@ class BaseLog(object):
     state -- the state dict from LogManager
 
     """
-    __metaclass__ = abc.ABCMeta
 
     SUMMARY_KEYS = set([
         'pass', 'fail', 'warn', 'crash', 'skip', 'dmesg-warn', 'dmesg-fail',
@@ -109,7 +108,7 @@ class QuietLog(BaseLog):
         else:
             self._endcode = '\n'
 
-        self.__counter = self._test_counter.next()
+        self.__counter = next(self._test_counter)
         self._state['running'].append(self.__counter)
 
     def start(self, name):
@@ -154,7 +153,7 @@ class QuietLog(BaseLog):
             done=str(self._state['complete']).zfill(self._pad),
             total=str(self._state['total']).zfill(self._pad),
             status=', '.join('{0}: {1}'.format(k, v) for k, v in
-                             sorted(self._state['summary'].iteritems())),
+                             sorted(self._state['summary'].items())),
             running=''.join('|/-\\'[x % 4] for x in self._state['running'])
         )
 
diff --git a/framework/profile.py b/framework/profile.py
index e8e8ba1..037697f 100644
--- a/framework/profile.py
+++ b/framework/profile.py
@@ -26,13 +26,12 @@ are represented by a TestProfile or a TestProfile derived object.
 
 """
 
-from __future__ import print_function, absolute_import
+
 import os
 import sys
 import multiprocessing
 import multiprocessing.dummy
 import importlib
-import types
 import contextlib
 import itertools
 
@@ -67,11 +66,11 @@ class TestDict(dict):  # pylint: disable=too-few-public-methods
         filesystems.
 
         """
-        assert isinstance(key, basestring), \
+        assert isinstance(key, str), \
                "Keys must be strings, but was {}".format(type(key))
         # None is required to make empty assignment work:
         # foo = Tree['a']
-        assert isinstance(value, (Test, types.NoneType)), \
+        assert isinstance(value, (Test, type(None))), \
                "Values must be either a Test, but was {}".format(type(value))
 
         super(TestDict, self).__setitem__(key.lower(), value)
@@ -154,7 +153,7 @@ class TestProfile(object):
             return True
 
         # Filter out unwanted tests
-        self.test_list = dict(item for item in self.test_list.iteritems()
+        self.test_list = dict(item for item in self.test_list.items()
                               if check_all(item))
 
         if not self.test_list:
@@ -231,15 +230,15 @@ class TestProfile(object):
         multi = multiprocessing.dummy.Pool()
 
         if opts.concurrent == "all":
-            run_threads(multi, self.test_list.iteritems())
+            run_threads(multi, iter(self.test_list.items()))
         elif opts.concurrent == "none":
-            run_threads(single, self.test_list.iteritems())
+            run_threads(single, iter(self.test_list.items()))
         else:
             # Filter and return only thread safe tests to the threaded pool
-            run_threads(multi, (x for x in self.test_list.iteritems()
+            run_threads(multi, (x for x in self.test_list.items()
                                 if x[1].run_concurrent))
             # Filter and return the non thread safe tests to the single pool
-            run_threads(single, (x for x in self.test_list.iteritems()
+            run_threads(single, (x for x in self.test_list.items()
                                  if not x[1].run_concurrent))
 
         log.get().summary()
@@ -305,7 +304,7 @@ class TestProfile(object):
         ...     g(['power', 'test'], 'powertest')
 
         """
-        assert isinstance(group, basestring), type(group)
+        assert isinstance(group, str), type(group)
 
         def adder(args, name=None, **kwargs):
             """Helper function that actually adds the tests.
@@ -325,13 +324,13 @@ class TestProfile(object):
             if not name:
                 lgroup = grouptools.join(group, ' '.join(args))
             else:
-                assert isinstance(name, basestring)
+                assert isinstance(name, str)
                 lgroup = grouptools.join(group, name)
 
             self.test_list[lgroup] = test_class(
                 args,
-                **{k:v for k, v in itertools.chain(default_args.iteritems(),
-                                                   kwargs.iteritems())})
+                **{k:v for k, v in itertools.chain(default_args.items(),
+                                                   kwargs.items())})
 
         yield adder
 
diff --git a/framework/programs/run.py b/framework/programs/run.py
index 8be6439..062f0ac 100644
--- a/framework/programs/run.py
+++ b/framework/programs/run.py
@@ -20,13 +20,13 @@
 # DEALINGS IN THE SOFTWARE.
 
 
-from __future__ import print_function
+
 import argparse
 import sys
 import os
 import os.path as path
 import time
-import ConfigParser
+import configparser
 import ctypes
 
 import framework.core as core
@@ -65,7 +65,7 @@ def _default_platform():
                       file=sys.stderr)
                 sys.exit(1)
             return plat
-        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+        except (configparser.NoOptionError, configparser.NoSectionError):
             return 'mixed_glx_egl'
 
 
@@ -84,7 +84,7 @@ def _default_backend():
                   file=sys.stderr)
             sys.exit(1)
         return backend
-    except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+    except (configparser.NoOptionError, configparser.NoSectionError):
         return 'json'
 
 
@@ -337,7 +337,7 @@ def resume(input_):
         file_start_count=len(results.tests) + 1)
     # Specifically do not initialize again, everything initialize does is done.
 
-    for name in results.tests.iterkeys():
+    for name in results.tests.keys():
         opts.exclude_tests.add(name)
 
     profile = framework.profile.merge_test_profiles(results.options['profile'])
diff --git a/framework/programs/summary.py b/framework/programs/summary.py
index a273918..674bfe0 100644
--- a/framework/programs/summary.py
+++ b/framework/programs/summary.py
@@ -153,7 +153,7 @@ def csv(input_):
     testrun = framework.results.load_results(args.testResults)
 
     def write_results(output):
-        for name, result in testrun.tests.iteritems():
+        for name, result in testrun.tests.items():
             output.write("{},{},{},{}\n".format(name, result['time'],
                                                 result['returncode'],
                                                 result['result']))
diff --git a/framework/results.py b/framework/results.py
index 961376f..501bf98 100644
--- a/framework/results.py
+++ b/framework/results.py
@@ -21,7 +21,7 @@
 
 """ Module for results generation """
 
-from __future__ import print_function, absolute_import
+
 import os
 import sys
 
@@ -66,7 +66,7 @@ class TestResult(dict):
 
         """
         def update(d, u, check):
-            for k, v in u.iteritems():
+            for k, v in u.items():
                 if isinstance(v, dict):
                     d[k] = update(d.get(k, {}), v, True)
                 else:
@@ -119,8 +119,8 @@ class TestrunResult(object):
     def write(self, file_):
         """ Write only values of the serialized_keys out to file """
         with open(file_, 'w') as f:
-            json.dump(dict((k, v) for k, v in self.__dict__.iteritems()
-                           if k in self.serialized_keys),
+            json.dump({k: v for k, v in self.__dict__.items()
+                           if k in self.serialized_keys},
                       f, default=piglit_encoder, indent=JSONBackend.INDENT)
 
     @classmethod
@@ -130,7 +130,7 @@ class TestrunResult(object):
         result.results_version = 0
         result.__dict__.update(json.load(results_file))
 
-        for key, value in result.tests.iteritems():
+        for key, value in result.tests.items():
             result.tests[key] = TestResult.load(value)
 
         return result
@@ -172,7 +172,7 @@ class TestrunResult(object):
                     continue
             # XXX: There has to be a better way to get a single key: value out
             # of a dict even when the key name isn't known
-            for key, value in test.iteritems():
+            for key, value in test.items():
                 testrun.tests[key] = TestResult.load(value)
 
         return testrun
@@ -291,7 +291,7 @@ def _update_zero_to_one(results):
     updated_results = {}
     remove = set()
 
-    for name, test in results.tests.iteritems():
+    for name, test in results.tests.items():
         # fix dmesg errors if any
         if isinstance(test.get('dmesg'), list):
             test['dmesg'] = '\n'.join(test['dmesg'])
@@ -340,7 +340,7 @@ def _update_zero_to_one(results):
         #
         # this must be the last thing done in this loop, or there will be pain
         if test.get('subtest'):
-            for sub in test['subtest'].iterkeys():
+            for sub in test['subtest'].keys():
                 # adding the leading / ensures that we get exactly what we
                 # expect, since endswith does a character by chacter match, if
                 # the subtest name is duplicated it wont match, and if there
diff --git a/framework/status.py b/framework/status.py
index 9af2ce6..e6eb7c5 100644
--- a/framework/status.py
+++ b/framework/status.py
@@ -55,7 +55,7 @@ The formula for determining fixes is:
 
 """
 
-from __future__ import print_function, absolute_import
+
 
 __all__ = ['NOTRUN',
            'PASS',
@@ -160,9 +160,6 @@ class Status(object):
     def __str__(self):
         return str(self.name)
 
-    def __unicode__(self):
-        return unicode(self.name)
-
     def __lt__(self, other):
         return not self.__ge__(other)
 
@@ -174,8 +171,8 @@ class Status(object):
         # the __int__ magic method
         if isinstance(other, (int, Status)):
             return int(self) == int(other)
-        elif isinstance(other, (str, unicode)):
-            return unicode(self) == unicode(other)
+        elif isinstance(other, str):
+            return str(self) == str(other)
         raise TypeError("Cannot compare type: {}".format(type(other)))
 
     def __ne__(self, other):
@@ -203,13 +200,13 @@ class NoChangeStatus(Status):
         super(NoChangeStatus, self).__init__(name, value, fraction)
 
     def __eq__(self, other):
-        if isinstance(other, (str, unicode, Status)):
-            return unicode(self) == unicode(other)
+        if isinstance(other, (str, Status)):
+            return str(self) == str(other)
         raise TypeError("Cannot compare type: {}".format(type(other)))
 
     def __ne__(self, other):
-        if isinstance(other, (str, unicode, Status)):
-            return unicode(self) != unicode(other)
+        if isinstance(other, (str, Status)):
+            return str(self) != str(other)
         raise TypeError("Cannot compare type: {}".format(type(other)))
 
 
diff --git a/framework/summary.py b/framework/summary.py
index 619d4f5..0f47e47 100644
--- a/framework/summary.py
+++ b/framework/summary.py
@@ -19,7 +19,7 @@
 # OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
  
-from __future__ import print_function, absolute_import
+
 import os
 import os.path as path
 import itertools
@@ -99,7 +99,7 @@ class HTMLIndex(list):
             if open == close:
                 return [], []
             else:
-                for i, j in itertools.izip_longest(open, close):
+                for i, j in itertools.zip_longest(open, close):
                     if i != j:
                         for k in common:
                             open.remove(k)
@@ -335,7 +335,7 @@ class Summary:
             # loop will cause a RuntimeError
             temp_results = {}
 
-            for key, value in results.tests.iteritems():
+            for key, value in results.tests.items():
                 # if the first character of key is a / then our while loop will
                 # become an infinite loop. Beyond that / should never be the
                 # leading character, if it is then there is a bug in one of the
@@ -346,7 +346,7 @@ class Summary:
                 # subtests' statuses and fractions down to the test, and then
                 # proceed like normal.
                 if 'subtest' in value:
-                    for (subt, subv) in value['subtest'].iteritems():
+                    for (subt, subv) in value['subtest'].items():
                         subt = grouptools.join(key, subt)
                         subv = so.status_lookup(subv)
 
@@ -380,7 +380,7 @@ class Summary:
             # Update the the results.tests dictionary with the subtests so that
             # they are entered into the appropriate pages other than all.
             # Updating it in the loop will raise a RuntimeError
-            for key, value in temp_results.iteritems():
+            for key, value in temp_results.items():
                 results.tests[key] = value
 
         # Create the lists of statuses like problems, regressions, fixes,
@@ -403,7 +403,7 @@ class Summary:
                 self.tests['skipped'].add(test)
 
             # find fixes, regressions, and changes
-            for i in xrange(len(status) - 1):
+            for i in range(len(status) - 1):
                 first = status[i]
                 last = status[i + 1]
                 if first in [so.SKIP, so.NOTRUN] and last not in [so.SKIP, so.NOTRUN]:
@@ -428,7 +428,7 @@ class Summary:
                        'timeout': 0, 'warn': 0, 'dmesg-warn': 0,
                        'dmesg-fail': 0}
 
-        for test in results.tests.itervalues():
+        for test in results.tests.values():
             self.totals[str(test['result'])] += 1
 
     def generate_html(self, destination, exclude):
@@ -473,16 +473,17 @@ class Summary:
             self.__find_totals(each)
 
             with open(path.join(destination, name, "index.html"), 'w') as out:
-                out.write(testindex.render(name=each.name,
-                                           totals=self.totals,
-                                           time=time,
-                                           options=each.options,
-                                           uname=each.uname,
-                                           glxinfo=each.glxinfo,
-                                           lspci=each.lspci))
+                out.write(testindex.render_unicode(
+                    name=each.name,
+                    totals=self.totals,
+                    time=time,
+                    options=each.options,
+                    uname=each.uname,
+                    glxinfo=each.glxinfo,
+                    lspci=each.lspci))
 
             # Then build the individual test results
-            for key, value in each.tests.iteritems():
+            for key, value in each.tests.items():
                 html_path = path.join(destination, name, escape_filename(key + ".html"))
                 temp_path = path.dirname(html_path)
 
@@ -497,7 +498,7 @@ class Summary:
                         value['time'] = datetime.timedelta(0, value['time'])
 
                     with open(html_path, 'w') as out:
-                        out.write(testfile.render(
+                        out.write(testfile.render_unicode(
                             testname=key,
                             value=value,
                             css=path.relpath(result_css, temp_path),
@@ -519,26 +520,29 @@ class Summary:
         # alltests, where the other pages all use the same name. ie,
         # changes.html, self.changes, and page=changes.
         with open(path.join(destination, "index.html"), 'w') as out:
-            out.write(index.render(results=HTMLIndex(self, self.tests['all']),
-                                   page='all',
-                                   pages=pages,
-                                   colnum=len(self.results),
-                                   exclude=exclude))
+            out.write(index.render_unicode(
+                results=HTMLIndex(self, self.tests['all']),
+                page='all',
+                pages=pages,
+                colnum=len(self.results),
+                exclude=exclude))
 
         # Generate the rest of the pages
         for page in pages:
             with open(path.join(destination, page + '.html'), 'w') as out:
             # If there is information to display display it
                 if self.tests[page]:
-                    out.write(index.render(results=HTMLIndex(self,
-                                                             self.tests[page]),
-                                           pages=pages,
-                                           page=page,
-                                           colnum=len(self.results),
-                                           exclude=exclude))
+                    out.write(index.render_unicode(
+                        results=HTMLIndex(self, self.tests[page]),
+                        pages=pages,
+                        page=page,
+                        colnum=len(self.results),
+                        exclude=exclude))
                 # otherwise provide an empty page
                 else:
-                    out.write(empty_status.render(page=page, pages=pages))
+                    out.write(empty_status.render_unicode(
+                        page=page,
+                        pages=pages))
 
     def generate_text(self, diff, summary):
         """ Write summary information to the console """
@@ -571,6 +575,6 @@ class Summary:
             print("    changes: {changes}\n"
                   "      fixes: {fixes}\n"
                   "regressions: {regressions}".format(
-                      **dict((k, len(v)) for k, v in self.tests.iteritems())))
+                      **{k: len(v) for k, v in self.tests.items()}))
 
-        print("      total: {}".format(sum(self.totals.itervalues())))
+        print("      total: {}".format(sum(self.totals.values())))
diff --git a/framework/test/__init__.py b/framework/test/__init__.py
index a9b5f4e..1de27e6 100644
--- a/framework/test/__init__.py
+++ b/framework/test/__init__.py
@@ -24,7 +24,7 @@
 # create a general use API, but allow it to be controlled by setting the
 # __all__ in each module
 
-from __future__ import print_function, absolute_import
+
 from .base import *
 from .piglit_test import *
 from .gleantest import *
diff --git a/framework/test/base.py b/framework/test/base.py
index efc20cb..23fd522 100644
--- a/framework/test/base.py
+++ b/framework/test/base.py
@@ -22,7 +22,7 @@
 
 """ Module provides a base class for Tests """
 
-from __future__ import print_function, absolute_import
+
 import errno
 import os
 import subprocess
@@ -96,7 +96,7 @@ class ProcessTimeout(threading.Thread):
         return self.status
 
 
-class Test(object):
+class Test(object, metaclass=abc.ABCMeta):
     """ Abstract base class for Test classes
 
     This class provides the framework for running tests, with several methods
@@ -116,7 +116,6 @@ class Test(object):
 
     """
     OPTS = Options()
-    __metaclass__ = abc.ABCMeta
     __slots__ = ['run_concurrent', 'env', 'result', 'cwd', '_command',
                  '_test_hook_execute_run', '__proc_timeout']
     timeout = 0
@@ -196,7 +195,7 @@ class Test(object):
         self.result['command'] = ' '.join(self.command)
         self.result['environment'] = " ".join(
             '{0}="{1}"'.format(k, v) for k, v in itertools.chain(
-                self.OPTS.env.iteritems(), self.env.iteritems()))
+                iter(self.OPTS.env.items()), iter(self.env.items())))
 
         if self.is_skip():
             self.result['result'] = 'skip'
@@ -213,7 +212,7 @@ class Test(object):
 
         self.interpret_result()
 
-        if self.result['returncode'] < 0:
+        if self.result['returncode'] is not None and self.result['returncode'] < 0:
             # check if the process was terminated by the timeout
             if self.timeout > 0 and self.__proc_timeout.join() > 0:
                 self.result['result'] = 'timeout'
@@ -273,9 +272,9 @@ class Test(object):
         # requirements.
         #
         fullenv = dict()
-        for key, value in itertools.chain(os.environ.iteritems(),
-                                          self.OPTS.env.iteritems(),
-                                          self.env.iteritems()):
+        for key, value in itertools.chain(iter(os.environ.items()),
+                                          iter(self.OPTS.env.items()),
+                                          iter(self.env.items())):
             fullenv[key] = str(value)
 
         # preexec_fn is not supported on Windows platforms
@@ -317,22 +316,9 @@ class Test(object):
             else:
                 raise e
 
-        # proc.communicate() returns 8-bit strings, but we need
-        # unicode strings.  In Python 2.x, this is because we
-        # will eventually be serializing the strings as JSON,
-        # and the JSON library expects unicode.  In Python 3.x,
-        # this is because all string operations require
-        # unicode.  So translate the strings into unicode,
-        # assuming they are using UTF-8 encoding.
-        #
-        # If the subprocess output wasn't properly UTF-8
-        # encoded, we don't want to raise an exception, so
-        # translate the strings using 'replace' mode, which
-        # replaces erroneous charcters with the Unicode
-        # "replacement character" (a white question mark inside
-        # a black diamond).
-        self.result['out'] = out.decode('utf-8', 'replace')
-        self.result['err'] = err.decode('utf-8', 'replace')
+        # Popen returns bytes, but the rest of the code assumes unicode
+        self.result['out'] = str(out)
+        self.result['err'] = str(err)
         self.result['returncode'] = returncode
 
         return error
@@ -358,7 +344,7 @@ class WindowResizeMixin(object):
         Test.run() to return early.
 
         """
-        for _ in xrange(5):
+        for _ in range(5):
             err = super(WindowResizeMixin, self)._run_command()
             if err:
                 return err
@@ -370,7 +356,7 @@ class WindowResizeMixin(object):
         # add a message about why, and return True so that the test will exit
         # early
         self.result['result'] = 'fail'
-        self.result['err'] = unicode()
-        self.result['out'] = unicode('Got spurious resize more than 5 times')
+        self.result['err'] = str()
+        self.result['out'] = str('Got spurious resize more than 5 times')
         self.result['returncode'] = None
         return True
diff --git a/framework/test/gleantest.py b/framework/test/gleantest.py
index de43857..19776ba 100644
--- a/framework/test/gleantest.py
+++ b/framework/test/gleantest.py
@@ -22,7 +22,7 @@
 
 """ Glean support """
 
-from __future__ import print_function, absolute_import
+
 import os
 from .base import Test
 from .piglit_test import TEST_BIN_DIR
diff --git a/framework/test/glsl_parser_test.py b/framework/test/glsl_parser_test.py
index 0ab3b0e..3248003 100644
--- a/framework/test/glsl_parser_test.py
+++ b/framework/test/glsl_parser_test.py
@@ -21,7 +21,7 @@
 
 """ This module enables the running of GLSL parser tests. """
 
-from __future__ import print_function, absolute_import
+
 import os
 import re
 import sys
@@ -76,7 +76,7 @@ class GLSLParserTest(PiglitBaseTest):
                 command = self.__get_command(self.__parser(testfile, filepath),
                                              filepath)
             except GLSLParserInternalError as e:
-                print(e.message, file=sys.stderr)
+                print(e.args[0], file=sys.stderr)
                 sys.exit(1)
 
         super(GLSLParserTest, self).__init__(command, run_concurrent=True)
diff --git a/framework/test/gtest.py b/framework/test/gtest.py
index cd80871..9cf2390 100644
--- a/framework/test/gtest.py
+++ b/framework/test/gtest.py
@@ -22,7 +22,7 @@
 # Authors: Tom Stellard <thomas.stellard at amd.com>
 #
 
-from __future__ import print_function, absolute_import 
+ 
 import re
 
 from .base import Test
diff --git a/framework/test/oclconform.py b/framework/test/oclconform.py
index 8186f5f..9b391b8 100644
--- a/framework/test/oclconform.py
+++ b/framework/test/oclconform.py
@@ -22,7 +22,7 @@
 # Authors: Tom Stellard <thomas.stellard at amd.com>
 #
 
-from __future__ import print_function, print_function
+
 import re
 import subprocess
 from os.path import join
diff --git a/framework/test/opencv.py b/framework/test/opencv.py
index 157102e..93fcba9 100644
--- a/framework/test/opencv.py
+++ b/framework/test/opencv.py
@@ -22,7 +22,7 @@
 # Authors: Tom Stellard <thomas.stellard at amd.com>
 #
 
-from __future__ import print_function, absolute_import
+
 import re
 import subprocess
 from os import path
diff --git a/framework/test/piglit_test.py b/framework/test/piglit_test.py
index 5dd2de6..95f791d 100644
--- a/framework/test/piglit_test.py
+++ b/framework/test/piglit_test.py
@@ -22,7 +22,7 @@
 
 """ Module provides a base class for Tests """
 
-from __future__ import print_function, absolute_import
+
 import os
 import sys
 import glob
diff --git a/framework/tests/backends_tests.py b/framework/tests/backends_tests.py
index b43302a..82461a3 100644
--- a/framework/tests/backends_tests.py
+++ b/framework/tests/backends_tests.py
@@ -22,7 +22,7 @@
 
 """ Tests for the backend package """
 
-from __future__ import print_function, absolute_import
+
 import os
 
 try:
@@ -75,7 +75,7 @@ def test_get_backend():
 
     check = lambda n, i: nt.assert_is(backends.get_backend(n), i)
 
-    for name, inst in backends_.iteritems():
+    for name, inst in backends_.items():
         check.description = 'get_backend({0}) returns {0} backend'.format(name)
         yield check, name, inst
 
diff --git a/framework/tests/base_tests.py b/framework/tests/base_tests.py
index f4b4ad6..6d99a76 100644
--- a/framework/tests/base_tests.py
+++ b/framework/tests/base_tests.py
@@ -20,7 +20,7 @@
 
 """ Tests for the exectest module """
 
-from __future__ import print_function, absolute_import
+
 
 import nose.tools as nt
 
diff --git a/framework/tests/core_tests.py b/framework/tests/core_tests.py
index 56c7ac3..3f085d4 100644
--- a/framework/tests/core_tests.py
+++ b/framework/tests/core_tests.py
@@ -20,11 +20,11 @@
 
 """ Module providing tests for the core module """
 
-from __future__ import print_function, absolute_import
+
 import os
 import collections
 import shutil
-import ConfigParser
+import configparser
 import textwrap
 
 import nose.tools as nt
@@ -66,7 +66,7 @@ class GetConfigFixture(object):
             if os.path.exists('piglit.conf'):
                 shutil.move('piglit.conf', 'piglit.conf.restore')
                 self.restore = True
-            core.PIGLIT_CONFIG = ConfigParser.SafeConfigParser(
+            core.PIGLIT_CONFIG = configparser.SafeConfigParser(
                 allow_no_value=True)
         except Exception as e:
             raise utils.UtilsError(e)
@@ -82,7 +82,7 @@ class GetConfigFixture(object):
 
             if self.restore:
                 shutil.move('piglit.conf.restore', 'piglit.conf')
-            core.PIGLIT_CONFIG = ConfigParser.SafeConfigParser(
+            core.PIGLIT_CONFIG = configparser.SafeConfigParser(
                 allow_no_value=True)
         except Exception as e:
             raise utils.UtilsError(e)
@@ -90,7 +90,7 @@ class GetConfigFixture(object):
 
 def _reset_piglit_config():
     """ Set core.PIGLIT_CONFIG back to pristine """
-    core.PIGLIT_CONFIG = ConfigParser.SafeConfigParser()
+    core.PIGLIT_CONFIG = configparser.SafeConfigParser()
 
 
 def check_initialize(target):
@@ -126,7 +126,7 @@ def test_parse_listfile_return():
     should return a list of files with no whitespace
 
     """
-    contents = "/tmp/foo\n/tmp/bar\n"
+    contents = b"/tmp/foo\n/tmp/bar\n"
 
     with utils.with_tempfile(contents) as tfile:
         results = core.parse_listfile(tfile)
@@ -141,7 +141,7 @@ def check_whitespace(actual, base, message):
 
 def test_parse_listfile_whitespace():
     """ Test that parse_listfile remove whitespace """
-    contents = "/tmp/foo\n/tmp/foo  \n/tmp/foo\t\n"
+    contents = b"/tmp/foo\n/tmp/foo  \n/tmp/foo\t\n"
 
     with utils.with_tempfile(contents) as tfile:
         results = core.parse_listfile(tfile)
@@ -171,7 +171,7 @@ def test_parse_listfile_tilde():
     *BSD, OSX) and Windows.
 
     """
-    contents = "~/foo\n"
+    contents = b"~/foo\n"
 
     with utils.with_tempfile(contents) as tfile:
         results = core.parse_listfile(tfile)
diff --git a/framework/tests/dmesg_tests.py b/framework/tests/dmesg_tests.py
index 9a7fef9..4425e52 100644
--- a/framework/tests/dmesg_tests.py
+++ b/framework/tests/dmesg_tests.py
@@ -25,7 +25,7 @@ don't want to run them use '-e sudo' with nosetests
 
 """
 
-from __future__ import print_function, absolute_import
+
 import os
 import sys
 import subprocess
@@ -192,9 +192,10 @@ def test_dmesg_wrap_partial():
     test.DMESG_COMMAND = ['echo', 'b\nc\nd\n']
     test.update_dmesg()
 
-    nt.assert_items_equal(test._new_messages, ['d'],
-                          msg=("_new_messages should be equal to ['d'], but is"
-                               " {} instead.".format(test._new_messages)))
+    nt.ok_(
+        test._new_messages == [b'd'],
+        msg=("_new_messages should be equal to ['d'], but is {} instead.".format(
+            test._new_messages)))
 
 
 def test_dmesg_wrap_complete():
@@ -215,10 +216,9 @@ def test_dmesg_wrap_complete():
     test.DMESG_COMMAND = ['echo', '1\n2\n3\n']
     test.update_dmesg()
 
-    nt.assert_items_equal(test._new_messages, ['1', '2', '3'],
-                          msg=("_new_messages should be equal to "
-                               "['1', '2', '3'], but is {} instead".format(
-                                   test._new_messages)))
+    nt.ok_(test._new_messages == [b'1', b'2', b'3'],
+           msg=("_new_messages should be equal to ['1', '2', '3'], "
+                "but is {} instead".format(test._new_messages)))
 
 
 @utils.nose_generator
diff --git a/framework/tests/gleantest_tests.py b/framework/tests/gleantest_tests.py
index 26872b0..3cee914 100644
--- a/framework/tests/gleantest_tests.py
+++ b/framework/tests/gleantest_tests.py
@@ -20,7 +20,7 @@
 
 """ Tests for the glean class. Requires Nose """
 
-from __future__ import print_function, absolute_import
+
 
 from framework.test import GleanTest
 
diff --git a/framework/tests/glsl_parser_test_tests.py b/framework/tests/glsl_parser_test_tests.py
index eb0f9bd..bb0946d 100644
--- a/framework/tests/glsl_parser_test_tests.py
+++ b/framework/tests/glsl_parser_test_tests.py
@@ -20,7 +20,7 @@
 
 """ Provides tests for the shader_test module """
 
-from __future__ import print_function, absolute_import
+
 import sys
 import os
 
@@ -42,24 +42,21 @@ def _check_config(content):
         return glsl.GLSLParserTest(tfile), tfile
 
 
+ at nt.raises(glsl.GLSLParserNoConfigError)
 def test_no_config_start():
     """ GLSLParserTest requires [config] """
-    content = ('// expect_result: pass\n'
-               '// glsl_version: 1.00\n'
-               '// [end config]\n')
+    content = (b'// expect_result: pass\n'
+               b'// glsl_version: 1.00\n'
+               b'// [end config]\n')
     with utils.with_tempfile(content) as tfile:
-        with nt.assert_raises(glsl.GLSLParserNoConfigError) as exc:
-            glsl.GLSLParserTest(tfile)
-            nt.assert_equal(
-                exc.exception, 'No [config] section found!',
-                msg="No config section found, no exception raised")
+        glsl.GLSLParserTest(tfile)
 
 
 def test_find_config_start():
     """ GLSLParserTest finds [config] """
-    content = ('// [config]\n'
-               '// glsl_version: 1.00\n'
-               '//\n')
+    content = (b'// [config]\n'
+               b'// glsl_version: 1.00\n'
+               b'//\n')
     with utils.with_tempfile(content) as tfile:
         with nt.assert_raises(SystemExit) as exc:
             glsl.GLSLParserTest(tfile)
@@ -70,7 +67,7 @@ def test_find_config_start():
 
 def test_no_config_end():
     """ GLSLParserTest requires [end config] """
-    with utils.with_tempfile('// [config]\n') as tfile:
+    with utils.with_tempfile(b'// [config]\n') as tfile:
         with nt.assert_raises(SystemExit) as exc:
             glsl.GLSLParserTest(tfile)
             nt.assert_equal(
@@ -80,9 +77,9 @@ def test_no_config_end():
 
 def test_no_expect_result():
     """ expect_result section is required """
-    content = ('// [config]\n'
-               '// glsl_version: 1.00\n'
-               '//\n')
+    content = (b'// [config]\n'
+               b'// glsl_version: 1.00\n'
+               b'//\n')
     with utils.with_tempfile(content) as tfile:
         with nt.assert_raises(SystemExit) as exc:
             glsl.GLSLParserTest(tfile)
@@ -94,9 +91,9 @@ def test_no_expect_result():
 
 def test_no_glsl_version():
     """ glsl_version section is required """
-    content = ('// [config]\n'
-               '// expect_result: pass\n'
-               '// [end config]\n')
+    content = (b'// [config]\n'
+               b'// expect_result: pass\n'
+               b'// [end config]\n')
     with utils.with_tempfile(content) as tfile:
         with nt.assert_raises(SystemExit) as exc:
             glsl.GLSLParserTest(tfile)
@@ -108,10 +105,10 @@ def test_no_glsl_version():
 
 def test_cpp_comments():
     """ Parses C++ style comments """
-    content = ('// [config]\n'
-               '// expect_result: pass\n'
-               '// glsl_version: 1.00\n'
-               '// [end config]\n')
+    content = (b'// [config]\n'
+               b'// expect_result: pass\n'
+               b'// glsl_version: 1.00\n'
+               b'// [end config]\n')
     test, name = _check_config(content)
 
     nt.assert_equal(
@@ -121,12 +118,12 @@ def test_cpp_comments():
 
 def test_c_comments():
     """ Parses C style comments """
-    content = ('/*\n'
-               ' * [config]\n'
-               ' * expect_result: pass\n'
-               ' * glsl_version: 1.00\n'
-               ' * [end config]\n'
-               ' */\n')
+    content = (b'/*\n'
+               b' * [config]\n'
+               b' * expect_result: pass\n'
+               b' * glsl_version: 1.00\n'
+               b' * [end config]\n'
+               b' */\n')
     test, name = _check_config(content)
 
     nt.assert_equal(test.command, [os.path.join(TEST_BIN_DIR, 'glslparsertest'),
@@ -136,11 +133,11 @@ def test_c_comments():
 
 def test_blank_in_config():
     """ C++ style comments can have uncommented newlines """
-    content = ('// [config]\n'
-               '\n'
-               '// expect_result: pass\n'
-               '// glsl_version: 1.00\n'
-               '// [end config]\n')
+    content = (b'// [config]\n'
+               b'\n'
+               b'// expect_result: pass\n'
+               b'// glsl_version: 1.00\n'
+               b'// [end config]\n')
 
     test, name = _check_config(content)
 
@@ -152,11 +149,11 @@ def test_blank_in_config():
 
 def test_empty_in_config():
     """ C++ sytle comments can have blank commented lines """
-    content = ('// [config]\n'
-               '//\n'
-               '// expect_result: pass\n'
-               '// glsl_version: 1.00\n'
-               '// [end config]\n')
+    content = (b'// [config]\n'
+               b'//\n'
+               b'// expect_result: pass\n'
+               b'// glsl_version: 1.00\n'
+               b'// [end config]\n')
 
     test, name = _check_config(content)
 
@@ -183,19 +180,19 @@ def check_config_to_command(config, result):
 def test_config_to_command():
     """ Generate tests that confirm the config file is correctly parsed """
     content = [
-        ('// [config]\n// expect_result: pass\n// glsl_version: 1.00\n// [end config]\n',
+        (b'// [config]\n// expect_result: pass\n// glsl_version: 1.00\n// [end config]\n',
          [os.path.join(TEST_BIN_DIR, 'glslparsertest'), 'pass', '1.00'],
          'all required options'),
-        ('// [config]\n// expect_result: pass\n// glsl_version: 1.00\n//check_link: true\n// [end config]\n',
+        (b'// [config]\n// expect_result: pass\n// glsl_version: 1.00\n//check_link: true\n// [end config]\n',
          [os.path.join(TEST_BIN_DIR, 'glslparsertest'), 'pass', '1.00', '--check-link'],
          'check_link true'),
-        ('// [config]\n// expect_result: pass\n// glsl_version: 1.00\n//check_link: false\n// [end config]\n',
+        (b'// [config]\n// expect_result: pass\n// glsl_version: 1.00\n//check_link: false\n// [end config]\n',
          [os.path.join(TEST_BIN_DIR, 'glslparsertest'), 'pass', '1.00'],
          'check_link false'),
-        ('// [config]\n// expect_result: pass\n// glsl_version: 1.00\n//require_extensions: ARB_foo\n// [end config]\n',
+        (b'// [config]\n// expect_result: pass\n// glsl_version: 1.00\n//require_extensions: ARB_foo\n// [end config]\n',
          [os.path.join(TEST_BIN_DIR, 'glslparsertest'), 'pass', '1.00', 'ARB_foo'],
          'one required_extension'),
-        ('// [config]\n// expect_result: pass\n// glsl_version: 1.00\n//require_extensions: ARB_foo ARB_bar\n// [end config]\n',
+        (b'// [config]\n// expect_result: pass\n// glsl_version: 1.00\n//require_extensions: ARB_foo ARB_bar\n// [end config]\n',
          [os.path.join(TEST_BIN_DIR, 'glslparsertest'), 'pass', '1.00', 'ARB_foo', 'ARB_bar'],
          'multiple required_extensions'),
     ]
@@ -208,11 +205,11 @@ def test_config_to_command():
 
 def test_bad_section_name():
     """ A section name not in the _CONFIG_KEYS name raises an error """
-    content = ('// [config]\n'
-               '// expect_result: pass\n'
-               '// glsl_version: 1.00\n'
-               '// new_awesome_key: foo\n'
-               '// [end config]\n')
+    content = (b'// [config]\n'
+               b'// expect_result: pass\n'
+               b'// glsl_version: 1.00\n'
+               b'// new_awesome_key: foo\n'
+               b'// [end config]\n')
 
     with utils.with_tempfile(content) as tfile:
         with nt.assert_raises(SystemExit) as e:
@@ -225,12 +222,12 @@ def test_bad_section_name():
 
 def test_good_section_names():
     """ A section name in the _CONFIG_KEYS does not raise an error """
-    content = ('// [config]\n'
-               '// expect_result: pass\n'
-               '// glsl_version: 1.00\n'
-               '// require_extensions: EXT_foo\n'
-               '// check_link: True\n'
-               '// [end config]\n')
+    content = (b'// [config]\n'
+               b'// expect_result: pass\n'
+               b'// glsl_version: 1.00\n'
+               b'// require_extensions: EXT_foo\n'
+               b'// check_link: True\n'
+               b'// [end config]\n')
 
     try:
         _check_config(content)
@@ -261,8 +258,8 @@ def test_duplicate_entries():
     for name, value in content:
         check_no_duplicates.description = \
             "duplicate values of {0} raise an exception".format(name)
-        test = '// [config]\n{0}{1}// [end config]'.format(
-            ''.join(x[1] for x in content), value)
+        test = bytes('// [config]\n{0}{1}// [end config]'.format(
+            ''.join(x[1] for x in content), value), encoding='utf-8')
 
         yield check_no_duplicates, test, name
 
@@ -300,7 +297,7 @@ def glslparser_exetensions_seperators():
                '// [end config]\n')
 
     for name, value in problems:
-        test = content.format(value)
+        test = bytes(content.format(value), encoding='utf-8')
         with utils.with_tempfile(test) as tfile:
             check_bad_character.description = (
                 'require_extensions: {0} should raise an error'.format(name))
@@ -332,7 +329,7 @@ def test_good_extensions():
     ]
 
     for x in options:
-        test = content.format(x)
+        test = bytes(content.format(x), encoding='utf-8')
         check_good_extension.description = \
             'require_extension {} is valid'.format(x)
 
diff --git a/framework/tests/grouptools_tests.py b/framework/tests/grouptools_tests.py
index 94f44af..f55bc08 100644
--- a/framework/tests/grouptools_tests.py
+++ b/framework/tests/grouptools_tests.py
@@ -20,7 +20,7 @@
 
 """Module with tests for grouptools."""
 
-from __future__ import print_function
+
 import inspect
 
 import nose.tools as nt
@@ -104,7 +104,7 @@ class _GroupToolsTest(_SharedFunctionTest):
     @nt.raises(AssertionError)
     def test_assertion_slash(self):
         """Test that a leading / is an error."""
-        if isinstance(self.input, (str, unicode)):
+        if isinstance(self.input, str):
             self.function('/' + self.input)
         elif not self.explode:
             self.function(['/' + i for i in self.input])
@@ -115,7 +115,7 @@ class _GroupToolsTest(_SharedFunctionTest):
     @nt.raises(AssertionError)
     def test_assertion_backslash(self):
         """Test that \\ in a group is an error."""
-        if isinstance(self.input, (str, unicode)):
+        if isinstance(self.input, str):
             self.function(self.input.replace('/', '\\'))
         elif not self.explode:
             self.function(i.replace('/', '\\') for i in self.input)
diff --git a/framework/tests/gtest_tests.py b/framework/tests/gtest_tests.py
index 2a90cf9..13a5500 100644
--- a/framework/tests/gtest_tests.py
+++ b/framework/tests/gtest_tests.py
@@ -20,7 +20,7 @@
 
 """ Module providing tests for gtest """
 
-from __future__ import print_function, absolute_import
+
 
 from framework.test import GTest
 
diff --git a/framework/tests/integration_tests.py b/framework/tests/integration_tests.py
index 0971eb3..50166ad 100644
--- a/framework/tests/integration_tests.py
+++ b/framework/tests/integration_tests.py
@@ -26,9 +26,9 @@ errors and to ensure that the API hasn't changed without fixing these modules
 
 """
 
-from __future__ import print_function, absolute_import
+
 import importlib
-import ConfigParser
+import configparser
 
 from nose.plugins.skip import SkipTest
 
@@ -36,7 +36,7 @@ import framework.core
 
 
 def setup_module():
-    framework.core.PIGLIT_CONFIG = ConfigParser.SafeConfigParser(
+    framework.core.PIGLIT_CONFIG = configparser.SafeConfigParser(
         allow_no_value=True)
     framework.core.get_config()
 
@@ -51,7 +51,7 @@ def _import(name):
     """
     try:
         return importlib.import_module(name)
-    except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+    except (configparser.NoOptionError, configparser.NoSectionError):
         raise SkipTest('No config section for {}'.format(name))
     except SystemExit as e:
         if e.code == 0:
diff --git a/framework/tests/json_tests.py b/framework/tests/json_tests.py
index 70a501a..ba5b51e 100644
--- a/framework/tests/json_tests.py
+++ b/framework/tests/json_tests.py
@@ -26,7 +26,7 @@ tests and they will change with each version of the json output.
 
 """
 
-from __future__ import print_function, absolute_import
+
 import os
 
 import nose.tools as nt
diff --git a/framework/tests/log_tests.py b/framework/tests/log_tests.py
index 3532b6e..42f22bf 100644
--- a/framework/tests/log_tests.py
+++ b/framework/tests/log_tests.py
@@ -20,7 +20,7 @@
 
 """ Module provides tests for log.py module """
 
-from __future__ import print_function, absolute_import
+
 import sys
 import collections
 
@@ -137,7 +137,7 @@ def check_no_output(func, args, file_=sys.stdout):
     file_.truncate()
 
     func(*args)
-    file_.seek(-1)
+    file_.seek(0, 2)
     assert file_.tell() == 0, 'file.tell() is at {}'.format(file_.tell())
 
 
diff --git a/framework/tests/opencv_tests.py b/framework/tests/opencv_tests.py
index 82e0ac5..bb54c5f 100644
--- a/framework/tests/opencv_tests.py
+++ b/framework/tests/opencv_tests.py
@@ -20,7 +20,7 @@
 
 """ Module for testing opencv """
 
-from __future__ import print_function, absolute_import
+
 
 from framework.test import OpenCVTest
 
diff --git a/framework/tests/piglit_test_tests.py b/framework/tests/piglit_test_tests.py
index 764a9c6..dc38325 100644
--- a/framework/tests/piglit_test_tests.py
+++ b/framework/tests/piglit_test_tests.py
@@ -20,7 +20,7 @@
 
 """ Tests for the exectest module """
 
-from __future__ import print_function, absolute_import
+
 
 import nose.tools as nt
 
diff --git a/framework/tests/results_tests.py b/framework/tests/results_tests.py
index 6d90990..af95e53 100644
--- a/framework/tests/results_tests.py
+++ b/framework/tests/results_tests.py
@@ -21,7 +21,7 @@
 """ Module providing tests for the core module """
 
 
-from __future__ import print_function, absolute_import
+
 import os
 import json
 
@@ -156,7 +156,7 @@ def test_update_results_old():
 
 def test_resume_non_folder():
     """ TestrunResult.resume doesn't accept a file """
-    with utils.with_tempfile('') as f:
+    with utils.with_tempfile(b'') as f:
         with nt.assert_raises(AssertionError):
             results.TestrunResult.resume(f)
 
diff --git a/framework/tests/results_v0_tests.py b/framework/tests/results_v0_tests.py
index ff01339..d225ce5 100644
--- a/framework/tests/results_v0_tests.py
+++ b/framework/tests/results_v0_tests.py
@@ -20,7 +20,7 @@
 
 """ Module provides tests for converting version zero results to version 1 """
 
-from __future__ import print_function, absolute_import
+
 import os
 import json
 import copy
@@ -117,7 +117,7 @@ DATA['tests'].update({
     },
 })
 
-with utils.with_tempfile(json.dumps(DATA)) as t:
+with utils.with_tempfile(bytes(json.dumps(DATA), encoding='ascii')) as t:
     with open(t, 'r') as f:
         RESULT = results._update_zero_to_one(results.TestrunResult.load(f))
 
@@ -147,7 +147,7 @@ def test_subtests_test_is_testresult():
 
 def test_info_delete():
     """ Version 1: Remove the info name from results """
-    for value in RESULT.tests.itervalues():
+    for value in RESULT.tests.values():
         assert 'info' not in value
 
 
@@ -206,7 +206,7 @@ def test_info_split():
     data['tests']['sometest']['info'] = \
         'Returncode: 1\n\nErrors:stderr\n\nOutput: stdout\n\nmore\n\nstuff'
 
-    with utils.with_tempfile(json.dumps(data)) as t:
+    with utils.with_tempfile(bytes(json.dumps(data), encoding='ascii')) as t:
         with open(t, 'r') as f:
             results._update_zero_to_one(results.TestrunResult.load(f))
 
@@ -216,13 +216,13 @@ def test_subtests_with_slash():
 
     expected = 'group2/groupA/test/subtest 1'
     nt.assert_not_in(
-        expected, RESULT.tests.iterkeys(),
+        expected, iter(RESULT.tests.keys()),
         msg='{0} found in result, when it should not be'.format(expected))
 
 
 def test_handle_fixed_subtests():
     """ Version 1: Correctly handle new single entry subtests correctly """
-    assert 'group3/groupA/test' in RESULT.tests.iterkeys()
+    assert 'group3/groupA/test' in iter(RESULT.tests.keys())
 
 
 def _load_with_update(data):
@@ -232,7 +232,7 @@ def _load_with_update(data):
 
     """
     try:
-        with utils.with_tempfile(json.dumps(data)) as t:
+        with utils.with_tempfile(bytes(json.dumps(data), encoding='ascii')) as t:
             result = results.load_results(t)
     except OSError as e:
         # There is the potential that the file will be renamed. In that event
diff --git a/framework/tests/results_v1_tests.py b/framework/tests/results_v1_tests.py
index 7ce8b92..49acfbe 100644
--- a/framework/tests/results_v1_tests.py
+++ b/framework/tests/results_v1_tests.py
@@ -20,7 +20,7 @@
 
 """Tests for version 1 to version 2."""
 
-from __future__ import print_function, absolute_import
+
 
 try:
     import simplejson as json
@@ -71,7 +71,7 @@ class TestV2Update(object):
             }
         }
 
-        with utils.with_tempfile(json.dumps(data)) as t:
+        with utils.with_tempfile(bytes(json.dumps(data), encoding='ascii')) as t:
             with open(t, 'r') as f:
                 cls.result = results._update_one_to_two(
                     results.TestrunResult.load(f))
@@ -138,7 +138,7 @@ class TestV2NoUpdate(object):
             }
         }
 
-        with utils.with_tempfile(json.dumps(data)) as t:
+        with utils.with_tempfile(bytes(json.dumps(data), encoding='ascii')) as t:
             with open(t, 'r') as f:
                 cls.result = results._update_one_to_two(
                     results.TestrunResult.load(f))
diff --git a/framework/tests/results_v2_tests.py b/framework/tests/results_v2_tests.py
index 3367ffb..b67ea84 100644
--- a/framework/tests/results_v2_tests.py
+++ b/framework/tests/results_v2_tests.py
@@ -76,7 +76,7 @@ DATA = {
     }
 }
 
-with utils.with_tempfile(json.dumps(DATA)) as t:
+with utils.with_tempfile(bytes(json.dumps(DATA), encoding='ascii')) as t:
     with open(t, 'r') as f:
         # pylint: disable=protected-access
         RESULT = results._update_two_to_three(results.TestrunResult.load(f))
diff --git a/framework/tests/results_v3_tests.py b/framework/tests/results_v3_tests.py
index 21164b8..1795ee3 100644
--- a/framework/tests/results_v3_tests.py
+++ b/framework/tests/results_v3_tests.py
@@ -78,7 +78,7 @@ DATA = {
 
 def make_result(data):
     """Write data to a file and return a result.TestrunResult object."""
-    with utils.with_tempfile(json.dumps(data)) as t:
+    with utils.with_tempfile(bytes(json.dumps(data), encoding='ascii')) as t:
         with open(t, 'r') as f:
             # pylint: disable=protected-access
             return results._update_three_to_four(results.TestrunResult.load(f))
diff --git a/framework/tests/run_parser_tests.py b/framework/tests/run_parser_tests.py
index 77f60bb..73d1a6b 100644
--- a/framework/tests/run_parser_tests.py
+++ b/framework/tests/run_parser_tests.py
@@ -20,11 +20,11 @@
 
 """ Module of tests for the run commandline parser """
 
-from __future__ import print_function, absolute_import
+
 import sys
 import os
 import shutil
-import ConfigParser
+import configparser
 
 import nose.tools as nt
 
@@ -108,7 +108,7 @@ class _Helpers(TestWithEnvClean):
 
     def setup(self):
         # Set core.PIGLIT_CONFIG back to pristine between tests
-        core.PIGLIT_CONFIG = ConfigParser.SafeConfigParser()
+        core.PIGLIT_CONFIG = configparser.SafeConfigParser()
 
 
 class TestBackend(_Helpers):
diff --git a/framework/tests/shader_test_tests.py b/framework/tests/shader_test_tests.py
index 7b06012..8004fe4 100644
--- a/framework/tests/shader_test_tests.py
+++ b/framework/tests/shader_test_tests.py
@@ -20,7 +20,7 @@
 
 """ Provides tests for the shader_test module """
 
-from __future__ import print_function, absolute_import
+
 import os
 
 import nose.tools as nt
@@ -36,8 +36,8 @@ def test_initialize_shader_test():
 
 def test_parse_gl_test_no_decimal():
     """ The GL Parser raises an exception if GL version lacks decimal """
-    data = ('[require]\n'
-            'GL = 2\n')
+    data = (b'[require]\n'
+            b'GL = 2\n')
     with utils.with_tempfile(data) as temp:
         with nt.assert_raises(testm.ShaderTestParserException) as exc:
             testm.ShaderTest(temp)
@@ -49,9 +49,9 @@ def test_parse_gl_test_no_decimal():
 
 def test_parse_gles2_test():
     """ Tests the parser for GLES2 tests """
-    data = ('[require]\n'
-            'GL ES >= 2.0\n'
-            'GLSL ES >= 1.00\n')
+    data = (b'[require]\n'
+            b'GL ES >= 2.0\n'
+            b'GLSL ES >= 1.00\n')
     with utils.with_tempfile(data) as temp:
         test = testm.ShaderTest(temp)
 
@@ -63,9 +63,9 @@ def test_parse_gles2_test():
 
 def test_parse_gles3_test():
     """ Tests the parser for GLES3 tests """
-    data = ('[require]\n'
-            'GL ES >= 3.0\n'
-            'GLSL ES >= 3.00\n')
+    data = (b'[require]\n'
+            b'GL ES >= 3.0\n'
+            b'GLSL ES >= 3.00\n')
     with utils.with_tempfile(data) as temp:
         test = testm.ShaderTest(temp)
 
diff --git a/framework/tests/status_tests.py b/framework/tests/status_tests.py
index 52ad134..531017b 100644
--- a/framework/tests/status_tests.py
+++ b/framework/tests/status_tests.py
@@ -25,7 +25,7 @@ etc
 
 """
 
-from __future__ import print_function, absolute_import
+
 import itertools
 
 import nose.tools as nt
@@ -239,7 +239,6 @@ def test_nochangestatus_magic():
     # generator equality tests
     for comp, type_ in [(obj, 'status.NoChangeStatus'),
                         (stat, 'status.Status'),
-                        (u'Test', 'unicode'),
                         ('Test', 'str')]:
         check_operator_equal.description = (
             'Operator eq works with type: {0} on class '
@@ -262,7 +261,6 @@ def test_status_magic():
 
     for func, name, result in [
             (str, 'str', 'foo'),
-            (unicode, 'unicode', u'foo'),
             (repr, 'repr', 'foo'),
             (int, 'int', 0)]:
         check_operator.description = 'Operator {0} works on class {1}'.format(
@@ -300,11 +298,11 @@ def test_status_eq_raises():
 
 @nt.raises(TypeError)
 def test_nochangestatus_eq_raises():
-    """ NoChangeStatus == !(str, unicode, Status) raises TypeError """
+    """ NoChangeStatus == !(str, Status) raises TypeError """
     status.NOTRUN == dict()
 
 
 @nt.raises(TypeError)
 def test_nochangestatus_ne_raises():
-    """ NoChangeStatus != (str, unicode, Status) raises TypeError """
+    """ NoChangeStatus != (str, Status) raises TypeError """
     status.NOTRUN != dict()
diff --git a/framework/tests/summary_tests.py b/framework/tests/summary_tests.py
index 14fa5c2..1618d23 100644
--- a/framework/tests/summary_tests.py
+++ b/framework/tests/summary_tests.py
@@ -21,7 +21,7 @@
 
 """ Module providing tests for the summary module """
 
-from __future__ import print_function, absolute_import
+
 import copy
 
 try:
@@ -73,8 +73,8 @@ def check_sets(old, ostat, new, nstat, set_):
     old['tests']['sometest']['result'] = ostat
     new['tests']['sometest']['result'] = nstat
 
-    with utils.with_tempfile(json.dumps(old)) as ofile:
-        with utils.with_tempfile(json.dumps(new)) as nfile:
+    with utils.with_tempfile(bytes(json.dumps(old), encoding='ascii')) as ofile:
+        with utils.with_tempfile(bytes(json.dumps(new), encoding='ascii')) as nfile:
             summ = summary.Summary([ofile, nfile])
 
             print(summ.tests)
@@ -95,7 +95,7 @@ def test_subtest_handling():
     data['tests']['is_skip'] = {}
     data['tests']['is_skip']['result'] = 'skip'
 
-    with utils.with_tempfile(json.dumps(data)) as sumfile:
+    with utils.with_tempfile(bytes(json.dumps(data), encoding='ascii')) as sumfile:
         summ = summary.Summary([sumfile])
 
         check_subtests_are_tests.description = \
diff --git a/framework/tests/test_lists.py b/framework/tests/test_lists.py
index c96248b..969e24f 100644
--- a/framework/tests/test_lists.py
+++ b/framework/tests/test_lists.py
@@ -26,7 +26,7 @@ es3conform, etc)
 
 """
 
-from __future__ import print_function, absolute_import
+
 import importlib
 import os.path as path
 
diff --git a/framework/tests/utils.py b/framework/tests/utils.py
index dc8af64..c0e28d2 100644
--- a/framework/tests/utils.py
+++ b/framework/tests/utils.py
@@ -25,7 +25,7 @@ in a single place.
 
 """
 
-from __future__ import print_function, absolute_import
+
 import os
 import sys
 import shutil
@@ -85,7 +85,7 @@ class UtilsError(Exception):
 def resultfile():
     """ Create a stringio with some json in it and pass that as results """
     with tempfile.NamedTemporaryFile(delete=False) as output:
-        json.dump(JSON_DATA, output)
+        output.write(bytes(json.dumps(JSON_DATA), encoding='utf-8'))
 
     yield output
 
diff --git a/piglit b/piglit
index 53a4bb4..bbd9522 100755
--- a/piglit
+++ b/piglit
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 
 # Copyright (c) 2014 Intel Corporation
 
diff --git a/piglit-print-commands.py b/piglit-print-commands.py
index e32ec70..f54e1dc 100755
--- a/piglit-print-commands.py
+++ b/piglit-print-commands.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #
 # Permission is hereby granted, free of charge, to any person
 # obtaining a copy of this software and associated documentation
@@ -22,7 +22,7 @@
 # DEALINGS IN THE SOFTWARE.
 
 
-from __future__ import print_function
+
 import argparse
 import sys
 import os
diff --git a/piglit-resume.py b/piglit-resume.py
index 6b78529..8af6665 100755
--- a/piglit-resume.py
+++ b/piglit-resume.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 
 # Copyright (c) 2014 Intel Corporation
 
diff --git a/piglit-run.py b/piglit-run.py
index 4c0f878..93c0bcc 100755
--- a/piglit-run.py
+++ b/piglit-run.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 
 # Copyright (c) 2014 Intel Corporation
 
diff --git a/piglit-summary-html.py b/piglit-summary-html.py
index 4b5278e..b9bb003 100755
--- a/piglit-summary-html.py
+++ b/piglit-summary-html.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 
 # Copyright (c) 2014 Intel Corporation
 
diff --git a/piglit-summary.py b/piglit-summary.py
index d1294be..5a97806 100755
--- a/piglit-summary.py
+++ b/piglit-summary.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 
 # Copyright (c) 2014 Intel Corporation
 
diff --git a/templates/index.mako b/templates/index.mako
index 1ca46d3..3aca903 100644
--- a/templates/index.mako
+++ b/templates/index.mako
@@ -31,7 +31,7 @@
 
         ## Status columns
         ## Create an additional column for each summary
-        % for _ in xrange(colnum):
+        % for _ in range(colnum):
         <col />
         % endfor
       </colgroup>
diff --git a/templates/testrun_info.mako b/templates/testrun_info.mako
index 9ff5022..30d77dd 100644
--- a/templates/testrun_info.mako
+++ b/templates/testrun_info.mako
@@ -21,10 +21,10 @@
         <td>totals</td>
         <td>
           <table>
-            % for key, value in sorted(totals.iteritems(), key=lambda (k,v): (v,k), reverse=True):
+            % for key, value in sorted(totals.items(), key=lambda t: (t[1], t[0]), reverse=True):
             <tr><td>${key}</td><td>${value}</td></tr>
             % endfor
-            <tr><td>total</td><td>${sum(totals.itervalues())}</td></tr>
+            <tr><td>total</td><td>${sum(totals.values())}</td></tr>
           </table>
         </td>
       </tr>
diff --git a/tests/all.py b/tests/all.py
index dc73c77..0cfcd8e 100644
--- a/tests/all.py
+++ b/tests/all.py
@@ -1959,7 +1959,7 @@ with profile.group_manager(
     for stage, type_, comp, sampler in itertools.product(
             stages, types, comps, samplers):
         for func in ['textureGather'] if 'Cube' in sampler else ['textureGather', 'textureGatherOffset', 'textureGatherOffsets' ]:
-            for cs in xrange(len(comp)):
+            for cs in range(len(comp)):
                 assert cs <= 3
                 address_mode = 'clamp' if sampler == '2DRect' else 'repeat'
                 cmd = ['textureGather', stage,
diff --git a/tests/cl.py b/tests/cl.py
index 6e0eb6e..cc0a4a4 100644
--- a/tests/cl.py
+++ b/tests/cl.py
@@ -7,7 +7,7 @@
 # invalid constant names, they're not really fixable, so just hide them.
 # pylint: disable=invalid-name
 
-from __future__ import division, absolute_import, print_function
+
 
 import os
 
diff --git a/tests/deqp_gles3.py b/tests/deqp_gles3.py
index 9b048f5..659f081 100644
--- a/tests/deqp_gles3.py
+++ b/tests/deqp_gles3.py
@@ -18,7 +18,7 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-import ConfigParser
+import configparser
 import os
 import subprocess
 import xml.etree.cElementTree as ET
@@ -68,9 +68,9 @@ def get_option(env_varname, config_option):
     try:
         opt = framework.core.PIGLIT_CONFIG.get(config_option[0],
                                                config_option[1])
-    except ConfigParser.NoSectionError:
+    except configparser.NoSectionError:
         pass
-    except ConfigParser.NoOptionError:
+    except configparser.NoOptionError:
         pass
 
     return opt
@@ -150,7 +150,7 @@ class DEQPTest(Test):
 
         for line in self.result['out'].split('\n'):
             line = line.lstrip()
-            for k, v in DEQPTest.__RESULT_MAP.iteritems():
+            for k, v in DEQPTest.__RESULT_MAP.items():
                 if line.startswith(k):
                     self.result['result'] = v
                     return
diff --git a/tests/igt.py b/tests/igt.py
index bd4c70e..54ed25d 100644
--- a/tests/igt.py
+++ b/tests/igt.py
@@ -44,10 +44,10 @@ __all__ = ['profile']
 def checkEnvironment():
     debugfs_path = "/sys/kernel/debug/dri"
     if os.getuid() != 0:
-        print "Test Environment check: not root!"
+        print("Test Environment check: not root!")
         return False
     if not os.path.isdir(debugfs_path):
-        print "Test Environment check: debugfs not mounted properly!"
+        print("Test Environment check: debugfs not mounted properly!")
         return False
     for subdir in os.listdir(debugfs_path):
         if not os.path.isdir(os.path.join(debugfs_path, subdir)):
@@ -55,10 +55,10 @@ def checkEnvironment():
         clients = open(os.path.join(debugfs_path, subdir, "clients"), 'r')
         lines = clients.readlines()
         if len(lines) > 2:
-            print "Test Environment check: other drm clients running!"
+            print("Test Environment check: other drm clients running!")
             return False
 
-    print "Test Environment check: Succeeded."
+    print("Test Environment check: Succeeded.")
     return True
 
 if 'IGT_TEST_ROOT' in os.environ:
@@ -71,7 +71,7 @@ else:
 # check for the test lists
 if not (os.path.exists(os.path.join(igtTestRoot, 'single-tests.txt'))
         and os.path.exists(os.path.join(igtTestRoot, 'multi-tests.txt'))):
-    print "intel-gpu-tools test lists not found."
+    print("intel-gpu-tools test lists not found.")
     sys.exit(0)
 
 
@@ -138,7 +138,7 @@ def addSubTestCases(test):
          return
 
     if proc.returncode != 0:
-         print "Error: Could not list subtests for " + test
+         print("Error: Could not list subtests for " + test)
          return
 
     subtests = out.split("\n")
diff --git a/tests/xts.py b/tests/xts.py
index 0e277dc..5cd5161 100644
--- a/tests/xts.py
+++ b/tests/xts.py
@@ -23,7 +23,7 @@
 
 """ Test integreation for the X Test Suite """
 
-from __future__ import print_function, division, absolute_import
+
 import os
 import re
 import sys
-- 
2.3.1



More information about the Piglit mailing list