\o/ Reviewed-by: Jordan Justen <jordan.l.jus...@intel.com>
On Thu, Jan 9, 2014 at 12:02 PM, Dylan Baker <baker.dyla...@gmail.com> wrote: > This replaces the console spewer with a simpler console reporting > mechanism inspired by the output of ninja. This reduces code, remove all > singleton instances and speeds up piglit runs. There is a drawback, the > output is much more terse than the previous implementation, giving only > the following output: > [<finished>/<total>] Running Test(s): <test number> > [16008/16011] Running Test(s): 16007 16008 16009 16010 > > This means that one can't look at the output to readily see that all > tests are skipping or failing. However, since these problems are usually > related to environment setup, and not to piglit doing something wrong, > these should be moved to some kind of prerun check instead of relying on > user attentiveness. Since the test numbers update only when they are > completed and hung test can be spotted. > > Performance examples: > Master: > ./piglit-run.py -c tets/quick.tests foo 1065.64s user 373.48s system > 295% cpu 8:06.35 total > > With this patch: > ./piglit-run.py -c tests/quick.tests foo 1045.33s user 353.20s system > 315% cpu 7:23.56 total > > Signed-off-by: Dylan Baker <baker.dyla...@gmail.com> > --- > framework/core.py | 23 +++++++------ > framework/log.py | 78 +++++++++++++++++++++++++++++--------------- > framework/patterns.py | 90 > --------------------------------------------------- > 3 files changed, 63 insertions(+), 128 deletions(-) > delete mode 100644 framework/patterns.py > > diff --git a/framework/core.py b/framework/core.py > index 8bcda5b..aa89584 100644 > --- a/framework/core.py > +++ b/framework/core.py > @@ -32,7 +32,7 @@ import string > import sys > import time > import traceback > -from log import log > +from log import Log > from cStringIO import StringIO > from textwrap import dedent > from threads import synchronized_self > @@ -464,7 +464,7 @@ class Test: > def run(self): > raise NotImplementedError > > - def execute(self, env, path, json_writer): > + def execute(self, env, path, log, json_writer): > ''' > Run the test. > > @@ -472,13 +472,12 @@ class Test: > Fully qualified test name as a string. For example, > ``spec/glsl-1.30/preprocessor/compiler/keywords/void.frag``. > ''' > - def status(msg): > - log(msg=msg, channel=path) > > + log_current = log.get_current() > # Run the test > if env.execute: > try: > - status("running") > + log.log() > time_start = time.time() > result = self.run(env) > time_end = time.time() > @@ -499,8 +498,6 @@ class Test: > result['traceback'] = \ > "".join(traceback.format_tb(sys.exc_info()[2])) > > - status(result['result']) > - > if 'subtest' in result and len(result['subtest'].keys()) > 1: > for test in result['subtest'].keys(): > result['result'] = result['subtest'][test] > @@ -508,7 +505,8 @@ class Test: > else: > json_writer.write_dict_item(path, result) > else: > - status("dry-run") > + log.log() > + log.mark_complete(log_current) > > > class Group(dict): > @@ -565,6 +563,7 @@ class TestProfile: > ''' > > self.prepare_test_list(env) > + log = Log(len(self.test_list)) > > # If concurrency is set to 'all' run all tests out of a concurrent > # threadpool, if it's none, then run evey test serially. otherwise > mix > @@ -572,24 +571,24 @@ class TestProfile: > if env.concurrent == "all": > pool = ThreadPool(multiprocessing.cpu_count()) > for (path, test) in self.test_list.items(): > - pool.add(test.execute, (env, path, json_writer)) > + pool.add(test.execute, (env, path, log, json_writer)) > pool.join() > elif env.concurrent == "none": > pool = ThreadPool(1) > for (path, test) in self.test_list.items(): > - pool.add(test.execute, (env, path, json_writer)) > + pool.add(test.execute, (env, path, log, json_writer)) > pool.join() > else: > pool = ThreadPool(multiprocessing.cpu_count()) > for (path, test) in self.test_list.items(): > if test.runConcurrent: > - pool.add(test.execute, (env, path, json_writer)) > + pool.add(test.execute, (env, path, log, json_writer)) > pool.join() > > pool = ThreadPool(1) > for (path, test) in self.test_list.items(): > if not test.runConcurrent: > - pool.add(test.execute, (env, path, json_writer)) > + pool.add(test.execute, (env, path, log, json_writer)) > pool.join() > > def remove_test(self, test_path): > diff --git a/framework/log.py b/framework/log.py > index 310c552..167e4ea 100644 > --- a/framework/log.py > +++ b/framework/log.py > @@ -1,5 +1,4 @@ > -# > -# Copyright (c) 2010 Intel Corporation > +# Copyright (c) 2013 Intel Corporation > # > # Permission is hereby granted, free of charge, to any person obtaining a > # copy of this software and associated documentation files (the "Software"), > @@ -21,33 +20,60 @@ > # IN THE SOFTWARE. > # > > -import logging > - > +import sys > from threads import synchronized_self > -from patterns import Singleton > > > -class Logger(Singleton): > +class Log(object): > + """ Print Progress to stdout > + > + Arguments: > + total -- The total number of tests to run. > + > + """ > + def __init__(self, total): > + self.__total = total > + self.__complete = 1 > + self.__running = [] > + self.__generator = (x for x in xrange(self.__total)) > + self.__pad = len(str(self.__total)) > + > + def _running(self): > + return "Running Test(s): {}".format( > + " ".join([str(x).zfill(self.__pad) for x in self.__running])) > + > + def _percent(self): > + return "[{0}/{1}]".format(str(self.__complete).zfill(self.__pad), > + str(self.__total).zfill(self.__pad)) > + > + @synchronized_self > + def mark_complete(self, value): > + """ Used to mark a test as complete in the log > + > + Arguments: > + value -- the test number to mark complete > + > + """ > + # Mark running complete > + assert value in self.__running > + self.__running.remove(value) > + > + # increment the number of completed tests > + self.__complete += 1 > + > @synchronized_self > - def __logMessage(self, logfunc, message, **kwargs): > - [logfunc(line, **kwargs) for line in message.split('\n')] > + def log(self): > + """ Print to the screen > + > + Works by moving the cursor back to the front of the line and printing > + over it. > + > + """ > + sys.stdout.write("{0} {1} \r".format(self._percent(), > self._running())) > > @synchronized_self > - def getLogger(self, channel=None): > - if 0 == len(logging.root.handlers): > - logging.basicConfig(format="[%(asctime)s] :: %(message)+8s " > - ":: %(name)s", > - datefmt="%c", > - level=logging.INFO) > - if channel is None: > - channel = "base" > - logger = logging.getLogger(channel) > - return logger > - > - def log(self, type=logging.INFO, msg="", channel=None): > - self.__logMessage(lambda m, > - **kwargs: self.getLogger(channel).log(type, > - m, > - **kwargs), > msg) > - > -log = Logger().log > + def get_current(self): > + """ Returns a new number to know what processes are running """ > + x = self.__generator.next() > + self.__running.append(x) > + return x > diff --git a/framework/patterns.py b/framework/patterns.py > deleted file mode 100644 > index bcf4e7e..0000000 > --- a/framework/patterns.py > +++ /dev/null > @@ -1,90 +0,0 @@ > -# > -# Copyright (c) 2010 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 (including the next > -# paragraph) 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. > -# > - > -import threading > - > - > -class Singleton(object): > - ''' > - Modeled after > - http://www.python.org/download/releases/2.2.3/descrintro/*__new__ > - > - A thread-safe (mostly -- see NOTE) Singleton class pattern. > - > - NOTE: deleting a singleton instance (i.e. Singleton::delInstance) does > not > - guarantee that nothing else is currently using it. To reduce this risk, a > - program should not hold a reference to the instance. Rather, use the > - create/construct syntax (see example below) to access the instance. Yet, > - this still does not guarantee that this type of usage will result in a > - desired effect in a multithreaded program. > - You've been warned so use the singleton pattern wisely! > - > - Example: > - > - class MySingletonClass(Singleton): > - def init(self): > - print "in MySingletonClass::init()", self > - > - def foo(self): > - print "in MySingletonClass::foo()", self > - > - MySingletonClass().foo() > - MySingletonClass().foo() > - MySingletonClass().foo() > - > - ---> output will look something like this: > - in MySingletonClass::init() <__main__.MySingletonClass object at > 0x7ff5b322f3d0> > - in MySingletonClass::foo() <__main__.MySingletonClass object at > 0x7ff5b322f3d0> > - in MySingletonClass::foo() <__main__.MySingletonClass object at > 0x7ff5b322f3d0> > - in MySingletonClass::foo() <__main__.MySingletonClass object at > 0x7ff5b322f3d0> > - ''' > - > - lock = threading.RLock() > - > - def __new__(cls, *args, **kwargs): > - try: > - cls.lock.acquire() > - it = cls.__dict__.get('__it__') > - if it is not None: > - return it > - cls.__it__ = it = object.__new__(cls) > - it.init(*args, **kwargs) > - return it > - finally: > - cls.lock.release() > - > - def init(self, *args, **kwargs): > - ''' > - Derived classes should override this method to do its initializations > - The derived class should not implement a '__init__' method. > - ''' > - pass > - > - @classmethod > - def delInstance(cls): > - cls.lock.acquire() > - try: > - if cls.__dict__.get('__it__') is not None: > - del cls.__it__ > - finally: > - cls.lock.release() > -- > 1.8.5.2 > > _______________________________________________ > Piglit mailing list > Piglit@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/piglit _______________________________________________ Piglit mailing list Piglit@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/piglit