summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorDaniel Dunbar <daniel@zuster.org>2013-08-29 00:54:15 +0000
committerDaniel Dunbar <daniel@zuster.org>2013-08-29 00:54:15 +0000
commit07f0f16bfd58c3e44babae65ccd88b9621d15f48 (patch)
treea2f89b7944cb254e0077c50be018ad61b41f53b1 /utils
parentb11b690d3f36d552091423293489f845090efb4f (diff)
downloadllvm-07f0f16bfd58c3e44babae65ccd88b9621d15f48.tar.gz
llvm-07f0f16bfd58c3e44babae65ccd88b9621d15f48.tar.bz2
llvm-07f0f16bfd58c3e44babae65ccd88b9621d15f48.tar.xz
[lit] Refactor test execution logic into lit.run.Run.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@189554 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'utils')
-rwxr-xr-xutils/lit/lit/main.py124
-rw-r--r--utils/lit/lit/run.py157
2 files changed, 158 insertions, 123 deletions
diff --git a/utils/lit/lit/main.py b/utils/lit/lit/main.py
index 29275ac34a..5c40f1ca53 100755
--- a/utils/lit/lit/main.py
+++ b/utils/lit/lit/main.py
@@ -7,17 +7,16 @@ See lit.pod for more information.
"""
from __future__ import absolute_import
-import math, os, platform, random, re, sys, time, threading, traceback
+import math, os, platform, random, re, sys, time
import lit.ProgressBar
import lit.LitConfig
import lit.Test
import lit.run
import lit.util
-
import lit.discovery
-class TestingProgressDisplay:
+class TestingProgressDisplay(object):
def __init__(self, opts, numTests, progressBar=None):
self.opts = opts
self.numTests = numTests
@@ -57,107 +56,6 @@ class TestingProgressDisplay:
sys.stdout.flush()
-class TestProvider:
- def __init__(self, tests, maxTime):
- self.maxTime = maxTime
- self.iter = iter(range(len(tests)))
- self.lock = threading.Lock()
- self.startTime = time.time()
- self.canceled = False
-
- def cancel(self):
- self.lock.acquire()
- self.canceled = True
- self.lock.release()
-
- def get(self):
- # Check if we have run out of time.
- if self.maxTime is not None:
- if time.time() - self.startTime > self.maxTime:
- return None
-
- # Otherwise take the next test.
- self.lock.acquire()
- if self.canceled:
- self.lock.release()
- return None
- for item in self.iter:
- break
- else:
- item = None
- self.lock.release()
- return item
-
-class Tester(object):
- def __init__(self, run_instance, provider, consumer):
- self.run_instance = run_instance
- self.provider = provider
- self.consumer = consumer
-
- def run(self):
- while 1:
- item = self.provider.get()
- if item is None:
- break
- self.runTest(item)
- self.consumer.taskFinished()
-
- def runTest(self, test_index):
- test = self.run_instance.tests[test_index]
- try:
- self.run_instance.execute_test(test)
- except KeyboardInterrupt:
- # This is a sad hack. Unfortunately subprocess goes
- # bonkers with ctrl-c and we start forking merrily.
- print('\nCtrl-C detected, goodbye.')
- os.kill(0,9)
- self.consumer.update(test_index, test)
-
-class ThreadResultsConsumer(object):
- def __init__(self, display):
- self.display = display
- self.lock = threading.Lock()
-
- def update(self, test_index, test):
- self.lock.acquire()
- try:
- self.display.update(test)
- finally:
- self.lock.release()
-
- def taskFinished(self):
- pass
-
- def handleResults(self):
- pass
-
-def run_one_tester(run, provider, display):
- tester = Tester(run, provider, display)
- tester.run()
-
-def runTests(numThreads, run, provider, display):
- consumer = ThreadResultsConsumer(display)
-
- # If only using one testing thread, don't use tasks at all; this lets us
- # profile, among other things.
- if numThreads == 1:
- run_one_tester(run, provider, consumer)
- return
-
- # Start all of the tasks.
- tasks = [threading.Thread(target=run_one_tester,
- args=(run, provider, consumer))
- for i in range(numThreads)]
- for t in tasks:
- t.start()
-
- # Allow the consumer to handle results, if necessary.
- consumer.handleResults()
-
- # Wait for all the tasks to complete.
- for t in tasks:
- t.join()
-
def main(builtinParameters = {}):
# Bump the GIL check interval, its more important to get any one thread to a
# blocking operation (hopefully exec) than to try and unblock other threads.
@@ -365,19 +263,8 @@ def main(builtinParameters = {}):
startTime = time.time()
display = TestingProgressDisplay(opts, len(run.tests), progressBar)
- provider = TestProvider(run.tests, opts.maxTime)
-
try:
- import win32api
- except ImportError:
- pass
- else:
- def console_ctrl_handler(type):
- provider.cancel()
- return True
- win32api.SetConsoleCtrlHandler(console_ctrl_handler, True)
- try:
- runTests(opts.numThreads, run, provider, display)
+ run.execute_tests(display, opts.numThreads, opts.maxTime)
except KeyboardInterrupt:
sys.exit(2)
display.finish()
@@ -385,11 +272,6 @@ def main(builtinParameters = {}):
if not opts.quiet:
print('Testing Time: %.2fs'%(time.time() - startTime))
- # Update results for any tests which weren't run.
- for test in run.tests:
- if test.result is None:
- test.setResult(lit.Test.Result(lit.Test.UNRESOLVED, '', 0.0))
-
# List test results organized by kind.
hasFailures = False
byCode = {}
diff --git a/utils/lit/lit/run.py b/utils/lit/lit/run.py
index 44666679a6..2ebce855ff 100644
--- a/utils/lit/lit/run.py
+++ b/utils/lit/lit/run.py
@@ -1,8 +1,98 @@
+import os
+import threading
import time
import traceback
+try:
+ import win32api
+except ImportError:
+ win32api = None
+
import lit.Test
+###
+# Test Execution Implementation
+
+class TestProvider(object):
+ def __init__(self, tests, max_time):
+ self.max_time = max_time
+ self.iter = iter(range(len(tests)))
+ self.lock = threading.Lock()
+ self.start_time = time.time()
+ self.canceled = False
+
+ def cancel(self):
+ self.lock.acquire()
+ self.canceled = True
+ self.lock.release()
+
+ def get(self):
+ # Check if we have run out of time.
+ if self.max_time is not None:
+ if time.time() - self.start_time > self.max_time:
+ return None
+
+ # Otherwise take the next test.
+ self.lock.acquire()
+ if self.canceled:
+ self.lock.release()
+ return None
+ for item in self.iter:
+ break
+ else:
+ item = None
+ self.lock.release()
+ return item
+
+class Tester(object):
+ def __init__(self, run_instance, provider, consumer):
+ self.run_instance = run_instance
+ self.provider = provider
+ self.consumer = consumer
+
+ def run(self):
+ while 1:
+ item = self.provider.get()
+ if item is None:
+ break
+ self.run_test(item)
+ self.consumer.task_finished()
+
+ def run_test(self, test_index):
+ test = self.run_instance.tests[test_index]
+ try:
+ self.run_instance.execute_test(test)
+ except KeyboardInterrupt:
+ # This is a sad hack. Unfortunately subprocess goes
+ # bonkers with ctrl-c and we start forking merrily.
+ print('\nCtrl-C detected, goodbye.')
+ os.kill(0,9)
+ self.consumer.update(test_index, test)
+
+class ThreadResultsConsumer(object):
+ def __init__(self, display):
+ self.display = display
+ self.lock = threading.Lock()
+
+ def update(self, test_index, test):
+ self.lock.acquire()
+ try:
+ self.display.update(test)
+ finally:
+ self.lock.release()
+
+ def task_finished(self):
+ pass
+
+ def handle_results(self):
+ pass
+
+def run_one_tester(run, provider, display):
+ tester = Tester(run, provider, display)
+ tester.run()
+
+###
+
class Run(object):
"""
This class represents a concrete, configured testing run.
@@ -14,7 +104,7 @@ class Run(object):
def execute_test(self, test):
result = None
- startTime = time.time()
+ start_time = time.time()
try:
result = test.config.test_format.execute(test, self.lit_config)
@@ -34,6 +124,69 @@ class Run(object):
output += traceback.format_exc()
output += '\n'
result = lit.Test.Result(lit.Test.UNRESOLVED, output)
- result.elapsed = time.time() - startTime
+ result.elapsed = time.time() - start_time
test.setResult(result)
+
+ def execute_tests(self, display, jobs, max_time=None):
+ """
+ execute_tests(display, jobs, [max_time])
+
+ Execute each of the tests in the run, using up to jobs number of
+ parallel tasks, and inform the display of each individual result. The
+ provided tests should be a subset of the tests available in this run
+ object.
+
+ If max_time is non-None, it should be a time in seconds after which to
+ stop executing tests.
+
+ The display object will have its update method called with each test as
+ it is completed. The calls are guaranteed to be locked with respect to
+ one another, but are *not* guaranteed to be called on the same thread as
+ this method was invoked on.
+
+ Upon completion, each test in the run will have its result
+ computed. Tests which were not actually executed (for any reason) will
+ be given an UNRESOLVED result.
+ """
+
+ # Create the test provider object.
+ provider = TestProvider(self.tests, max_time)
+
+ # Install a console-control signal handler on Windows.
+ if win32api is not None:
+ def console_ctrl_handler(type):
+ provider.cancel()
+ return True
+ win32api.SetConsoleCtrlHandler(console_ctrl_handler, True)
+
+ # Actually execute the tests.
+ self._execute_tests_with_provider(provider, display, jobs)
+
+ # Update results for any tests which weren't run.
+ for test in self.tests:
+ if test.result is None:
+ test.setResult(lit.Test.Result(lit.Test.UNRESOLVED, '', 0.0))
+
+ def _execute_tests_with_provider(self, provider, display, jobs):
+ consumer = ThreadResultsConsumer(display)
+
+ # If only using one testing thread, don't use tasks at all; this lets us
+ # profile, among other things.
+ if jobs == 1:
+ run_one_tester(self, provider, consumer)
+ return
+
+ # Start all of the tasks.
+ tasks = [threading.Thread(target=run_one_tester,
+ args=(self, provider, consumer))
+ for i in range(jobs)]
+ for t in tasks:
+ t.start()
+
+ # Allow the consumer to handle results, if necessary.
+ consumer.handle_results()
+
+ # Wait for all the tasks to complete.
+ for t in tasks:
+ t.join()