diff options
author | Jenkins <jenkins@review.openstack.org> | 2015-06-04 14:36:02 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2015-06-04 14:36:02 +0000 |
commit | 3fe26726c099c488e2ef7d6dd8ca60881fca8c4c (patch) | |
tree | 11f4ed401bf03d50f62c9a5898d641984f9b4b5b | |
parent | 636aa3b9df3a15840556763f898d6776b82ec97c (diff) | |
parent | ab309cf8a73ba63c307096178b390c98f6a53f63 (diff) | |
download | tempest-lib-3fe26726c099c488e2ef7d6dd8ca60881fca8c4c.tar.gz |
Merge "Switch to using ostestr for subunit-trace"
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | setup.cfg | 1 | ||||
-rwxr-xr-x | tempest_lib/cmd/subunit_trace.py | 283 | ||||
-rw-r--r-- | tempest_lib/tests/cmd/__init__.py | 0 | ||||
-rw-r--r-- | tempest_lib/tests/cmd/test_subunit_trace.py | 61 | ||||
-rwxr-xr-x | tools/pretty_tox.sh | 6 | ||||
-rw-r--r-- | tox.ini | 2 |
7 files changed, 2 insertions, 352 deletions
diff --git a/requirements.txt b/requirements.txt index 5279166..889f318 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ httplib2>=0.7.5 paramiko>=1.13.0 six>=1.9.0 oslo.log>=1.0.0 # Apache-2.0 +os-testr>=0.1.0 @@ -25,7 +25,6 @@ packages = [entry_points] console_scripts = - subunit-trace = tempest_lib.cmd.subunit_trace:main skip-tracker = tempest_lib.cmd.skip_tracker:main [build_sphinx] diff --git a/tempest_lib/cmd/subunit_trace.py b/tempest_lib/cmd/subunit_trace.py deleted file mode 100755 index 43bb275..0000000 --- a/tempest_lib/cmd/subunit_trace.py +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2014 Hewlett-Packard Development Company, L.P. -# Copyright 2014 Samsung Electronics -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Trace a subunit stream in reasonable detail and high accuracy.""" - -import argparse -import datetime -import functools -import os -import re -import sys - -import subunit -import testtools - -DAY_SECONDS = 60 * 60 * 24 -FAILS = [] -RESULTS = {} - - -def total_seconds(timedelta): - # NOTE(mtreinish): This method is built-in to the timedelta class in - # python >= 2.7 it is here to enable it's use on older versions - return ((timedelta.days * DAY_SECONDS + timedelta.seconds) * 10 ** 6 + - timedelta.microseconds) / 10 ** 6 - - -def cleanup_test_name(name, strip_tags=True, strip_scenarios=False): - """Clean up the test name for display. - - By default we strip out the tags in the test because they don't help us - in identifying the test that is run to it's result. - - Make it possible to strip out the testscenarios information (not to - be confused with tempest scenarios) however that's often needed to - indentify generated negative tests. - """ - if strip_tags: - tags_start = name.find('[') - tags_end = name.find(']') - if tags_start > 0 and tags_end > tags_start: - newname = name[:tags_start] - newname += name[tags_end + 1:] - name = newname - - if strip_scenarios: - tags_start = name.find('(') - tags_end = name.find(')') - if tags_start > 0 and tags_end > tags_start: - newname = name[:tags_start] - newname += name[tags_end + 1:] - name = newname - - return name - - -def get_duration(timestamps): - start, end = timestamps - if not start or not end: - duration = '' - else: - delta = end - start - duration = '%d.%06ds' % ( - delta.days * DAY_SECONDS + delta.seconds, delta.microseconds) - return duration - - -def find_worker(test): - """Get the worker number. - - If there are no workers because we aren't in a concurrent environment, - assume the worker number is 0. - """ - for tag in test['tags']: - if tag.startswith('worker-'): - return int(tag[7:]) - return 0 - - -# Print out stdout/stderr if it exists, always -def print_attachments(stream, test, all_channels=False): - """Print out subunit attachments. - - Print out subunit attachments that contain content. This - runs in 2 modes, one for successes where we print out just stdout - and stderr, and an override that dumps all the attachments. - """ - channels = ('stdout', 'stderr') - for name, detail in test['details'].items(): - # NOTE(sdague): the subunit names are a little crazy, and actually - # are in the form pythonlogging:'' (with the colon and quotes) - name = name.split(':')[0] - if detail.content_type.type == 'test': - detail.content_type.type = 'text' - if (all_channels or name in channels) and detail.as_text(): - title = "Captured %s:" % name - stream.write("\n%s\n%s\n" % (title, ('~' * len(title)))) - # indent attachment lines 4 spaces to make them visually - # offset - for line in detail.as_text().split('\n'): - stream.write(" %s\n" % line) - - -def show_outcome(stream, test, print_failures=False, failonly=False): - global RESULTS - status = test['status'] - # TODO(sdague): ask lifeless why on this? - if status == 'exists': - return - - worker = find_worker(test) - name = cleanup_test_name(test['id']) - duration = get_duration(test['timestamps']) - - if worker not in RESULTS: - RESULTS[worker] = [] - RESULTS[worker].append(test) - - # don't count the end of the return code as a fail - if name == 'process-returncode': - return - - if status == 'fail': - FAILS.append(test) - stream.write('{%s} %s [%s] ... FAILED\n' % ( - worker, name, duration)) - if not print_failures: - print_attachments(stream, test, all_channels=True) - elif not failonly: - if status == 'success': - stream.write('{%s} %s [%s] ... ok\n' % ( - worker, name, duration)) - print_attachments(stream, test) - elif status == 'skip': - stream.write('{%s} %s ... SKIPPED: %s\n' % ( - worker, name, test['details']['reason'].as_text())) - else: - stream.write('{%s} %s [%s] ... %s\n' % ( - worker, name, duration, test['status'])) - if not print_failures: - print_attachments(stream, test, all_channels=True) - - stream.flush() - - -def print_fails(stream): - """Print summary failure report. - - Currently unused, however there remains debate on inline vs. at end - reporting, so leave the utility function for later use. - """ - if not FAILS: - return - stream.write("\n==============================\n") - stream.write("Failed %s tests - output below:" % len(FAILS)) - stream.write("\n==============================\n") - for f in FAILS: - stream.write("\n%s\n" % f['id']) - stream.write("%s\n" % ('-' * len(f['id']))) - print_attachments(stream, f, all_channels=True) - stream.write('\n') - - -def count_tests(key, value): - count = 0 - for k, v in RESULTS.items(): - for item in v: - if key in item: - if re.search(value, item[key]): - count += 1 - return count - - -def run_time(): - runtime = 0.0 - for k, v in RESULTS.items(): - for test in v: - test_dur = get_duration(test['timestamps']).strip('s') - # NOTE(toabctl): get_duration() can return an empty string - # which leads to a ValueError when casting to float - if test_dur: - runtime += float(test_dur) - return runtime - - -def worker_stats(worker): - tests = RESULTS[worker] - num_tests = len(tests) - delta = tests[-1]['timestamps'][1] - tests[0]['timestamps'][0] - return num_tests, delta - - -def print_summary(stream, elapsed_time): - stream.write("\n======\nTotals\n======\n") - stream.write("Ran: %s tests in %.4f sec.\n" % ( - count_tests('status', '.*'), total_seconds(elapsed_time))) - stream.write(" - Passed: %s\n" % count_tests('status', '^success$')) - stream.write(" - Skipped: %s\n" % count_tests('status', '^skip$')) - stream.write(" - Expected Fail: %s\n" % count_tests('status', '^xfail$')) - stream.write(" - Unexpected Success: %s\n" % count_tests('status', - '^uxsuccess$')) - stream.write(" - Failed: %s\n" % count_tests('status', '^fail$')) - stream.write("Sum of execute time for each test: %.4f sec.\n" % run_time()) - - # we could have no results, especially as we filter out the process-codes - if RESULTS: - stream.write("\n==============\nWorker Balance\n==============\n") - - for w in range(max(RESULTS.keys()) + 1): - if w not in RESULTS: - stream.write( - " - WARNING: missing Worker %s! " - "Race in testr accounting.\n" % w) - else: - num, time = worker_stats(w) - stream.write(" - Worker %s (%s tests) => %ss\n" % - (w, num, time)) - - -def parse_args(): - parser = argparse.ArgumentParser() - parser.add_argument('--no-failure-debug', '-n', action='store_true', - dest='print_failures', help='Disable printing failure ' - 'debug information in realtime') - parser.add_argument('--fails', '-f', action='store_true', - dest='post_fails', help='Print failure debug ' - 'information after the stream is proccesed') - parser.add_argument('--failonly', action='store_true', - dest='failonly', help="Don't print success items", - default=( - os.environ.get('TRACE_FAILONLY', False) - is not False)) - return parser.parse_args() - - -def main(): - args = parse_args() - stream = subunit.ByteStreamToStreamResult( - sys.stdin, non_subunit_name='stdout') - outcomes = testtools.StreamToDict( - functools.partial(show_outcome, sys.stdout, - print_failures=args.print_failures, - failonly=args.failonly)) - summary = testtools.StreamSummary() - result = testtools.CopyStreamResult([outcomes, summary]) - result = testtools.StreamResultRouter(result) - cat = subunit.test_results.CatFiles(sys.stdout) - result.add_rule(cat, 'test_id', test_id=None) - start_time = datetime.datetime.utcnow() - result.startTestRun() - try: - stream.run(result) - finally: - result.stopTestRun() - stop_time = datetime.datetime.utcnow() - elapsed_time = stop_time - start_time - - if count_tests('status', '.*') == 0: - print("The test run didn't actually run any tests") - exit(1) - if args.post_fails: - print_fails(sys.stdout) - print_summary(sys.stdout, elapsed_time) - exit(0 if summary.wasSuccessful() else 1) - - -if __name__ == '__main__': - main() diff --git a/tempest_lib/tests/cmd/__init__.py b/tempest_lib/tests/cmd/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/tempest_lib/tests/cmd/__init__.py +++ /dev/null diff --git a/tempest_lib/tests/cmd/test_subunit_trace.py b/tempest_lib/tests/cmd/test_subunit_trace.py deleted file mode 100644 index c1653d2..0000000 --- a/tempest_lib/tests/cmd/test_subunit_trace.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2015 SUSE Linux GmbH -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from datetime import datetime as dt - -from ddt import data -from ddt import ddt -from ddt import unpack -from mock import patch - -from tempest_lib.cmd import subunit_trace -from tempest_lib.tests import base - - -@ddt -class TestSubunitTrace(base.TestCase): - - @data(([dt(2015, 4, 17, 22, 23, 14, 111111), - dt(2015, 4, 17, 22, 23, 14, 111111)], - "0.000000s"), - ([dt(2015, 4, 17, 22, 23, 14, 111111), - dt(2015, 4, 17, 22, 23, 15, 111111)], - "1.000000s"), - ([dt(2015, 4, 17, 22, 23, 14, 111111), - None], - "")) - @unpack - def test_get_durating(self, timestamps, expected_result): - self.assertEqual(subunit_trace.get_duration(timestamps), - expected_result) - - @data(([dt(2015, 4, 17, 22, 23, 14, 111111), - dt(2015, 4, 17, 22, 23, 14, 111111)], - 0.0), - ([dt(2015, 4, 17, 22, 23, 14, 111111), - dt(2015, 4, 17, 22, 23, 15, 111111)], - 1.0), - ([dt(2015, 4, 17, 22, 23, 14, 111111), - None], - 0.0)) - @unpack - def test_run_time(self, timestamps, expected_result): - patched_res = { - 0: [ - {'timestamps': timestamps} - ] - } - with patch.dict(subunit_trace.RESULTS, patched_res, clear=True): - self.assertEqual(subunit_trace.run_time(), expected_result) diff --git a/tools/pretty_tox.sh b/tools/pretty_tox.sh deleted file mode 100755 index ff554c5..0000000 --- a/tools/pretty_tox.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -o pipefail - -TESTRARGS=$1 -python setup.py testr --slowest --testr-args="--subunit $TESTRARGS" | subunit-trace --no-failure-debug -f @@ -11,7 +11,7 @@ setenv = VIRTUAL_ENV={envdir} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -commands = bash tools/pretty_tox.sh {posargs} +commands = ostestr {posargs} [testenv:pep8] commands = flake8 |