diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 20677dd..0000000 --- a/.coveragerc +++ /dev/null @@ -1,7 +0,0 @@ -[run] -branch = True -source = os_testr -omit = os_testr/tests/*,os_testr/openstack/* - -[report] -ignore_errors = True diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d6ae633..0000000 --- a/.gitignore +++ /dev/null @@ -1,54 +0,0 @@ -*.py[cod] - -# C extensions -*.so - -# Packages -*.egg* -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg -lib -lib64 - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -cover/ -.coverage* -!.coveragerc -.tox -nosetests.xml -.testrepository -.venv - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -# Complexity -output/*.html -output/*/index.html - -# Sphinx -doc/build - -# pbr generates these -AUTHORS -ChangeLog - -# Editors -*~ -.*.swp -.*sw? diff --git a/.gitreview b/.gitreview deleted file mode 100644 index fb67c90..0000000 --- a/.gitreview +++ /dev/null @@ -1,4 +0,0 @@ -[gerrit] -host=review.openstack.org -port=29418 -project=openstack/os-testr.git diff --git a/.mailmap b/.mailmap deleted file mode 100644 index 516ae6f..0000000 --- a/.mailmap +++ /dev/null @@ -1,3 +0,0 @@ -# Format is: -# -# diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 6d83b3c..0000000 --- a/.testr.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] -test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ - OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ - OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ - ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 863c087..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,16 +0,0 @@ -If you would like to contribute to the development of OpenStack, -you must follow the steps in this page: - - http://docs.openstack.org/infra/manual/developers.html - -Once those steps have been completed, changes to OpenStack -should be submitted for review via the Gerrit tool, following -the workflow documented at: - - http://docs.openstack.org/infra/manual/developers.html#development-workflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/os-testr diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index 921d4d6..0000000 --- a/HACKING.rst +++ /dev/null @@ -1,4 +0,0 @@ -os-testr Style Commandments -=============================================== - -Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 68c771a..0000000 --- a/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c978a52..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include AUTHORS -include ChangeLog -exclude .gitignore -exclude .gitreview - -global-exclude *.pyc diff --git a/README.rst b/README.rst deleted file mode 100644 index 42ff480..0000000 --- a/README.rst +++ /dev/null @@ -1,28 +0,0 @@ -======== -os-testr -======== - -.. image:: https://img.shields.io/pypi/v/os-testr.svg - :target: https://pypi.python.org/pypi/os-testr/ - :alt: Latest Version - -.. image:: https://img.shields.io/pypi/dm/os-testr.svg - :target: https://pypi.python.org/pypi/os-testr/ - :alt: Downloads - -A testr wrapper to provide functionality for OpenStack projects. - -* Free software: Apache license -* Documentation: http://docs.openstack.org/developer/os-testr -* Source: http://git.openstack.org/cgit/openstack/os-testr -* Bugs: http://bugs.launchpad.net/os-testr - -Features --------- - -* ``ostestr``: a testr wrapper that uses subunit-trace for output and builds - some helpful extra functionality around testr -* ``subunit-trace``: an output filter for a subunit stream which provides - useful information about the run -* ``subunit2html``: generates a test results html page from a subunit stream -* ``generate-subunit``: generate a subunit stream for a single test diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..c7943d5 --- /dev/null +++ b/README.txt @@ -0,0 +1,13 @@ +This project is no longer maintained. + +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". + +Use instead the project deb-python-os-testr at +http://git.openstack.org/cgit/openstack/deb-python-os-testr . + +For any further questions, please email +openstack-dev@lists.openstack.org or join #openstack-dev on +Freenode. diff --git a/babel.cfg b/babel.cfg deleted file mode 100644 index 15cd6cb..0000000 --- a/babel.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[python: **.py] - diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100755 index 17a0271..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- coding: utf-8 -*- -# 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. - -import os -import sys - -sys.path.insert(0, os.path.abspath('../..')) -# -- General configuration ---------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [ - 'sphinx.ext.autodoc', - # 'sphinx.ext.intersphinx', - 'oslosphinx' -] - -# autodoc generation is a bit aggressive and a nuisance when doing heavy -# text edit cycles. -# execute "export SPHINX_DEBUG=1" in your terminal to disable - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'os-testr' -copyright = u'2015, Matthew Treinish' - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -add_module_names = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme_path = ["."] -# html_theme = '_theme' -# html_static_path = ['static'] - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -html_use_smartypants = False - -# Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ('index', - '%s.tex' % project, - u'%s Documentation' % project, - u'Matthew Treinish', 'manual'), -] - -man_pages = [('ostestr', 'ostestr', 'tooling to run OpenStack tests', - ['Matthew Treinish'], 1), - ('subunit_trace', 'subunit-trace', 'pretty output filter for ' - 'subunit streams', ['Matthew Treinish'], 1), - ('subunit2html', 'subunit2html', 'generate a html results page ' - 'from a subunit stream', ['Matthew Treinish'], 1)] - -# Example configuration for intersphinx: refer to the Python standard library. -# intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst deleted file mode 100644 index 1728a61..0000000 --- a/doc/source/contributing.rst +++ /dev/null @@ -1,4 +0,0 @@ -============ -Contributing -============ -.. include:: ../../CONTRIBUTING.rst diff --git a/doc/source/generate_subunit.rst b/doc/source/generate_subunit.rst deleted file mode 100644 index 1ede033..0000000 --- a/doc/source/generate_subunit.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. generate_subunit: - -generate-subunit -================ - -generate-subunit is a simple tool to, as its name implies, generate a subunit -stream. It will generate a stream with a single test result to STDOUT. The -subunit protocol lets you concatenate multiple streams together so if you want -to generate a stream with multiple just append the output of multiple executions -of generate-subunit. - -Summary -------- - - generate-subunit timestamp secs [status] [test_id] - -Usage ------ - -generate-subunit has 2 mandatory arguments. These are needed to specify when -the "test" started running and how long it took. The first argument is a POSIX -timestamp (which can returned by the date util using ``date +%s``) for when it -started running. The second argument is the number of seconds it took for the -execution to finish. For example:: - - $ generate-subunit $(date +%s) 42 - -will generate a stream with the test_id 'devstack' successfully running for 42 -secs starting when the command was executed. This leads into the 2 optional -arguments. The first optional argument is for specifying the status. This must -be the 3rd argument when calling generate-subunit. Valid status options can -be found in the `testtools documentation`_. If status is not specified it will -default to success. For example:: - - $ generate-subunit $(date +%s) 42 fail - -will be the same as the previous example except that it marks the test as -failing. - -.. _testtools documentation: http://testtools.readthedocs.io/en/latest/api.html#testtools.StreamResult.status - -The other optional argument is the test_id (aka test name) and is used to -identify the "test" being run. For better or worse this defaults to *devstack*. -(which is an artifact of why this tool was originally created) Note, this must -be the 4th argument when calling generate-subunit. This means you also must -specify a status if you want to set your own test_id. For example:: - - $ generate-subunit %(date +%s) 42 fail my_little_test - -will generate a subunit stream as before except instead the test will be named -my_little_test. diff --git a/doc/source/history.rst b/doc/source/history.rst deleted file mode 100644 index 69ed4fe..0000000 --- a/doc/source/history.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../ChangeLog diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index 1ac9d4d..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. os-testr documentation master file, created by - sphinx-quickstart on Tue Jul 9 22:26:36 2013. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to os-testr's documentation! -======================================================== - -Contents: - -.. toctree:: - :maxdepth: 2 - - readme - installation - usage - contributing - history - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/doc/source/installation.rst b/doc/source/installation.rst deleted file mode 100644 index 8264927..0000000 --- a/doc/source/installation.rst +++ /dev/null @@ -1,12 +0,0 @@ -============ -Installation -============ - -At the command line:: - - $ pip install os-testr - -Or, if you have virtualenvwrapper installed:: - - $ mkvirtualenv os-testr - $ pip install os-testr diff --git a/doc/source/ostestr.rst b/doc/source/ostestr.rst deleted file mode 100644 index 7d8b1cf..0000000 --- a/doc/source/ostestr.rst +++ /dev/null @@ -1,238 +0,0 @@ -.. _ostestr: - -ostestr -======= - -The ostestr command provides a wrapper around the testr command included in -the testrepository package. It's designed to build on the functionality -included in testr and workaround several UI bugs in the short term. By default -it also has output that is much more useful for OpenStack's test suites which -are lengthy in both runtime and number of tests. Please note that the CLI -semantics are still a work in progress as the project is quite young, so -default behavior might change in future version. - -Summary -------- - ostestr [-b|--blacklist_file ] [-r|--regex REGEX] - [-w|--whitelist_file ] - [-p|--pretty] [--no-pretty] [-s|--subunit] [-l|--list] - [-n|--no-discover ] [--slowest] [--no-slowest] - [--pdb ] [--parallel] [--serial] - [-c|--concurrency ] [--until-failure] [--print-exclude] - -Options -------- - - --blacklist_file BLACKLIST_FILE, -b BLACKLIST_FILE - Path to a blacklist file, this file contains a - separate regex exclude on each newline - --whitelist_file WHITELIST_FILE, -w WHITELIST_FILE - Path to a whitelist file, this file contains a - separate regex on each newline - --regex REGEX, -r REGEX - A normal testr selection regex. If a blacklist file is - specified, the regex will be appended to the end of - the generated regex from that file - --pretty, -p - Print pretty output from subunit-trace. This is - mutually exclusive with --subunit - --no-pretty - Disable the pretty output with subunit-trace - --subunit, -s - output the raw subunit v2 from the test run this is - mutually exclusive with --pretty - --list, -l - List all the tests which will be run. - --no-discover TEST_ID, -n TEST_ID - Takes in a single test to bypasses test discover and - just execute the test specified - --slowest - After the test run print the slowest tests - --no-slowest - After the test run don't print the slowest tests - --pdb TEST_ID - Run a single test that has pdb traces added - --parallel - Run tests in parallel (this is the default) - --serial - Run tests serially - --concurrency WORKERS, -c WORKERS - The number of workers to use when running in parallel. - By default this is the number of cpus - --until-failure - Run the tests in a loop until a failure is - encountered. Running with subunit or prettyoutput - enable will force the loop to run testsserially - --print-exclude - If an exclude file is used this option will prints the - comment from the same line and all skipped tests - before the test run - -Running Tests -------------- - -os-testr is primarily for running tests at it's basic level you just invoke -ostestr to run a test suite for a project. (assuming it's setup to run tests -using testr already) For example:: - - $ ostestr - -This will run tests in parallel (with the number of workers matching the number -of CPUs) and with subunit-trace output. If you need to run tests in serial you -can use the serial option:: - - $ ostestr --serial - -Or if you need to adjust the concurrency but still run in parallel you can use --c/--concurrency:: - - $ ostestr --concurrency 2 - -If you only want to run an individual test module or more specific (a single -class, or test) and parallel execution doesn't matter, you can use the --n/--no-discover to skip test discovery and just directly calls subunit.run on -the tests under the covers. Bypassing discovery is desirable when running a -small subset of tests in a larger test suite because the discovery time can -often far exceed the total run time of the tests. - -For example:: - - $ ostestr --no-discover test.test_thing.TestThing.test_thing_method - -Additionally, if you need to run a single test module, class, or single test -with pdb enabled you can use --pdb to directly call testtools.run under the -covers which works with pdb. For example:: - - $ ostestr --pdb tests.test_thing.TestThing.test_thing_method - - -Test Selection --------------- - -ostestr is designed to build on top of the test selection in testr. testr only -exposed a regex option to select tests. This equivalent is functionality is -exposed via the --regex option. For example:: - - $ ostestr --regex 'magic\.regex' - -This will do a straight passthrough of the provided regex to testr. -Additionally, ostestr allows you to specify a blacklist file to define a set -of regexes to exclude. You can specify a blacklist file with the ---blacklist_file/-b option, for example:: - - $ ostestr --blacklist_file $path_to_file - -The format for the file is line separated regex, with '#' used to signify the -start of a comment on a line. For example:: - - # Blacklist File - ^regex1 # Excludes these tests - .*regex2 # exclude those tests - -Will generate a regex to pass to testr which will exclude both any tests -matching '^regex1' and '.*regex2'. If a blacklist file is used in conjunction -with the --regex option the regex specified with --regex will be appended to -the generated output from the --blacklist_file. Also it's worth noting that the -regex test selection options can not be used in conjunction with the ---no-discover or --pdb options described in the previous section. This is -because the regex selection requires using testr under the covers to actually -do the filtering, and those 2 options do not use testr. - -The dual of the blacklist file is the whitelist file which works in the exact -same manner, except that instead of excluding regex matches it includes them. -You can specify the path to the file with --whitelist_file/-w, for example:: - - $ ostestr --whitelist_file $path_to_file - -The format for the file is more or less identical to the blacklist file:: - - # Whitelist File - ^regex1 # Include these tests - .*regex2 # include those tests - -However, instead of excluding the matches it will include them. Note that a -blacklist file can not be used at the same time as a whitelist file, they -are mutually exclusive. - -It's also worth noting that you can use the test list option to dry run any -selection arguments you are using. You just need to use --list/-l with your -selection options to do this, for example:: - - $ ostestr --regex 'regex3.*' --blacklist_file blacklist.txt --list - -This will list all the tests which will be run by ostestr using that combination -of arguments. - -Please not that all of this selection functionality will be expanded on in the -future and a default grammar for selecting multiple tests will be chosen in a -future release. However as of right now all current arguments (which have -guarantees on always remaining in place) are still required to perform any -selection logic while this functionality is still under development. - - -Output Options --------------- - -By default ostestr will use subunit-trace as the output filter on the test -run. It will also print the slowest tests from the run after the run is -concluded. You can disable the printing the slowest tests with the --no-slowest -flag, for example:: - - $ ostestr --no-slowest - -If you'd like to disable the subunit-trace output you can do this using ---no-pretty:: - - $ ostestr --no-pretty - -ostestr also provides the option to just output the raw subunit stream on -STDOUT with --subunit/-s. Note if you want to use this you also have to -specify --no-pretty as the subunit-trace output and the raw subunit output -are mutually exclusive. For example, to get raw subunit output the arguments -would be:: - - $ ostestr --no-pretty --subunit - -An additional option on top of the blacklist file is --print-exclude option. -When this option is specified when using a blacklist file before the tests are -run ostestr will print all the tests it will be excluding from the blacklist -file. If a line in the blacklist file has a comment that will be printed before -listing the tests which will be excluded by that line's regex. If no comment is -present on a line the regex from that line will be used instead. For example, -if you were using the example blacklist file from the previous section the -output before the regular test run output would be:: - - $ ostestr -b blacklist-file blacklist.txt --print-exclude - Excludes these tests - regex1_match - regex1_exclude - - exclude those tests - regex2_match - regex2_exclude - - ... - -Notes for running with tox --------------------------- - -If you use `tox`_ for running your tests and call ostestr as the test command -it's recommended that you set a posargs following ostestr on the commands -stanza. For example:: - - [testenv] - commands = ostestr {posargs} - -.. _tox: https://tox.readthedocs.org/en/latest/ - -this will enable end users to pass args to configure the output, use the -selection logic, or any other options directly from the tox cli. This will let -tox take care of the venv management and the environment separation but enable -direct access to all of the ostestr options to easily customize your test run. -For example, assuming the above posargs usage you would be to do:: - - $ tox -epy34 -- --regex ^regex1 - -or to skip discovery:: - - $ tox -epy34 -- -n test.test_thing.TestThing.test_thing_method diff --git a/doc/source/readme.rst b/doc/source/readme.rst deleted file mode 100644 index a6210d3..0000000 --- a/doc/source/readme.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../README.rst diff --git a/doc/source/subunit2html.rst b/doc/source/subunit2html.rst deleted file mode 100644 index f0ebbc9..0000000 --- a/doc/source/subunit2html.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _subunit2html: - -subunit2html -============ - -subunit2html is a tool that takes in a subunit stream file and will output an -html page - -Summary -------- - - subunit2html subunit_stream [output] - -Usage ------ - -subunit2html takes in 1 mandatory argument. This is used to specify the location -of the subunit stream file. For example:: - - $ subunit2html subunit_stream - -By default subunit2html will store the generated html results file at -results.html file in the current working directory. - -An optional second argument can be provided to set the output path of the html -results file that is generated. If it is provided this will be the output path -for saving the generated file, otherwise results.html in the current working -directory will be used. For example:: - - $ subunit2html subunit_stream test_results.html - -will write the generated html results file to test_results.html in the current -working directory diff --git a/doc/source/subunit_trace.rst b/doc/source/subunit_trace.rst deleted file mode 100644 index 3cf3be0..0000000 --- a/doc/source/subunit_trace.rst +++ /dev/null @@ -1,112 +0,0 @@ -.. _subunit_trace: - -subunit-trace -============= - -subunit-trace is an output filter for subunit streams. It is often used in -conjunction with test runners that emit subunit to enable a consistent and -useful realtime output from a test run. - -Summary -------- - -subunit-trace [--fails|-f] [--failonly] [--perc-diff|-d] [--no-summary] - [--diff-threshold|-t ] [--color] - -Options -------- - - --no-failure-debug, -n - Disable printing failure debug information in realtime - --fails, -f - Print failure debug information after the stream is - processed - --failonly - Don't print success items - --perc-diff, -d - Print percent change in run time on each test - --diff-threshold THRESHOLD, -t THRESHOLD - Threshold to use for displaying percent change from the - avg run time. If one is not specified the percent - change will always be displayed. - --no-summary - Don't print the summary of the test run after completes - --color - Print result with colors - -Usage ------ -subunit-trace will take a subunit stream in via STDIN. This is the only input -into the tool. It will then print on STDOUT the formatted test result output -for the test run information contained in the stream. - -A subunit v2 stream must be passed into subunit-trace. If only a subunit v1 -stream is available you must use the subunit-1to2 utility to convert it before -passing the stream into subunit-trace. For example this can be done by chaining -pipes:: - - $ cat subunit_v1 | subunit-1to2 | subunit-trace - -Adjusting per test output -^^^^^^^^^^^^^^^^^^^^^^^^^ - -subunit-trace provides several options to customize it's output. This allows -users to customize the output from subunit-trace to suit their needs. The output -from subunit-trace basically comes in 2 parts, the per test output, and the -summary at the end. By default subunit-trace will print failure messages during -the per test output, meaning when a test fails it will also print the message -and any traceback and other attachments at that time. However this can be -disabled by using --no-failure-debug, -n. For example:: - - $ testr run --subunit | subunit-trace --no-failure-debug - -Here is also the option to print all failures together at the end of the test -run before the summary view. This is done using the --fails/-f option. For -example:: - - $ testr run --subunit | subunit-trace --fails - -Often the --fails and --no-failure-debug options are used in conjunction to -only print failures at the end of a test run. This is useful for large test -suites where an error message might be lost in the noise. To do this :: - - $ testr run --subunit | subunit-trace --fails --no-failure-debug - -By default subunit-trace will print a line for each test after it completes with -the test status. However, if you only want to see the run time output for -failures and not any other test status you can use the --failonly option. For -example:: - - $ testr run --subunit | subunit-trace --failonly - -The last output option provided by subunit-trace is to disable the summary view -of the test run which is normally displayed at the end of a run. You can do -this using the --no-summary option. For example:: - - $ testr run --subunit | subunit-trace --no-summary - - -Show per test run time percent change -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -subunit-trace provides an option to display the percent change in run time -from the previous run. To do this subunit-trace leverages the testr internals -a bit. It uses the times.dbm database which, the file repository type in -testrepository will create, to get the previous run time for a test. If testr -hasn't ever been used before or for whatever reason subunit-trace is unable to -find the times.dbm file from testr no percentages will be displayed even if it's -enabled. Additionally, if a test is run which does not have an entry in the -times.dbm file will not have a percentage printed for it. - -To enable this feature you use --perc-diff/-d, for example:: - - $ testr run --subunit | subunit-trace --perc-diff - -There is also the option to set a threshold value for this option. If used it -acts as an absolute value and only percentage changes that exceed it will be -printed. Use the --diff-threshold/-t option to set a threshold, for example:: - - $ testr run --subunit | subunit-trace --perc-diff --threshold 45 - -This will only display percent differences when the change in run time is either ->=45% faster or <=45% slower. diff --git a/doc/source/usage.rst b/doc/source/usage.rst deleted file mode 100644 index c7d19bb..0000000 --- a/doc/source/usage.rst +++ /dev/null @@ -1,13 +0,0 @@ -===== -Usage -===== - -This section contains the documentation for each of tools packaged in os-testr - -.. toctree:: - :maxdepth: 2 - - ostestr - subunit_trace - subunit2html - generate_subunit diff --git a/os_testr/__init__.py b/os_testr/__init__.py deleted file mode 100644 index f8a2a30..0000000 --- a/os_testr/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -import pbr.version - - -__version__ = pbr.version.VersionInfo( - 'os_testr').version_string() diff --git a/os_testr/generate_subunit.py b/os_testr/generate_subunit.py deleted file mode 100755 index 15b819f..0000000 --- a/os_testr/generate_subunit.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python2 -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# 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. - - -import datetime -import sys - -import pbr.version -import subunit -from subunit import iso8601 - - -__version__ = pbr.version.VersionInfo('os_testr').version_string() - - -def main(): - if '--version' in sys.argv: - print(__version__) - exit(0) - - start_time = datetime.datetime.fromtimestamp(float(sys.argv[1])).replace( - tzinfo=iso8601.UTC) - elapsed_time = datetime.timedelta(seconds=int(sys.argv[2])) - stop_time = start_time + elapsed_time - - if len(sys.argv) > 3: - status = sys.argv[3] - else: - status = 'success' - - if len(sys.argv) > 4: - test_id = sys.argv[4] - else: - test_id = 'devstack' - - # Write the subunit test - output = subunit.v2.StreamResultToBytes(sys.stdout) - output.startTestRun() - output.status(timestamp=start_time, test_id=test_id) - # Write the end of the test - output.status(test_status=status, timestamp=stop_time, test_id=test_id) - output.stopTestRun() - - -if __name__ == '__main__': - main() diff --git a/os_testr/ostestr.py b/os_testr/ostestr.py deleted file mode 100755 index 17ed1bd..0000000 --- a/os_testr/ostestr.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python2 -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# 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. - -import argparse -import copy -import os -import subprocess -import sys - -import pbr.version -from subunit import run as subunit_run -from testtools import run as testtools_run - -from os_testr import regex_builder as rb - - -__version__ = pbr.version.VersionInfo('os_testr').version_string() - - -def get_parser(args): - parser = argparse.ArgumentParser( - description='Tool to run openstack tests') - parser.add_argument('--version', action='version', - version='%s' % __version__) - list_files = parser.add_mutually_exclusive_group() - list_files.add_argument('--blacklist_file', '-b', - help='Path to a blacklist file, this file ' - 'contains a separate regex exclude on each ' - 'newline') - list_files.add_argument('--whitelist_file', '-w', - help='Path to a whitelist file, this file ' - 'contains a separate regex on each newline.') - group = parser.add_mutually_exclusive_group() - group.add_argument('--regex', '-r', - help='A normal testr selection regex. If a blacklist ' - 'file is specified, the regex will be appended ' - 'to the end of the generated regex from that ' - 'file.') - group.add_argument('--path', metavar='FILE_OR_DIRECTORY', - help='A file name or directory of tests to run.') - group.add_argument('--no-discover', '-n', metavar='TEST_ID', - help="Takes in a single test to bypasses test " - "discover and just execute the test specified. " - "A file name may be used in place of a test " - "name.") - pretty = parser.add_mutually_exclusive_group() - pretty.add_argument('--pretty', '-p', dest='pretty', action='store_true', - help='Print pretty output from subunit-trace. This is ' - 'mutually exclusive with --subunit') - pretty.add_argument('--no-pretty', dest='pretty', action='store_false', - help='Disable the pretty output with subunit-trace') - parser.add_argument('--subunit', '-s', action='store_true', - help='output the raw subunit v2 from the test run ' - 'this is mutually exclusive with --pretty') - parser.add_argument('--list', '-l', action='store_true', - help='List all the tests which will be run.') - parser.add_argument('--color', action='store_true', - help='Use color in the pretty output') - slowest = parser.add_mutually_exclusive_group() - slowest.add_argument('--slowest', dest='slowest', action='store_true', - help="after the test run print the slowest tests") - slowest.add_argument('--no-slowest', dest='slowest', action='store_false', - help="after the test run don't print the slowest " - "tests") - parser.add_argument('--pdb', metavar='TEST_ID', - help='Run a single test that has pdb traces added') - parallel = parser.add_mutually_exclusive_group() - parallel.add_argument('--parallel', dest='parallel', action='store_true', - help='Run tests in parallel (this is the default)') - parallel.add_argument('--serial', dest='parallel', action='store_false', - help='Run tests serially') - parser.add_argument('--concurrency', '-c', type=int, metavar='WORKERS', - help='The number of workers to use when running in ' - 'parallel. By default this is the number of cpus') - parser.add_argument('--until-failure', action='store_true', - help='Run the tests in a loop until a failure is ' - 'encountered. Running with subunit or pretty' - 'output enable will force the loop to run tests' - 'serially') - parser.add_argument('--print-exclude', action='store_true', - help='If an exclude file is used this option will ' - 'prints the comment from the same line and all ' - 'skipped tests before the test run') - parser.set_defaults(pretty=True, slowest=True, parallel=True) - return parser.parse_known_args(args) - - -def call_testr(regex, subunit, pretty, list_tests, slowest, parallel, concur, - until_failure, color, others=None): - others = others or [] - if parallel: - cmd = ['testr', 'run', '--parallel'] - if concur: - cmd.append('--concurrency=%s' % concur) - else: - cmd = ['testr', 'run'] - if list_tests: - cmd = ['testr', 'list-tests'] - elif (subunit or pretty) and not until_failure: - cmd.append('--subunit') - elif not (subunit or pretty) and until_failure: - cmd.append('--until-failure') - cmd.append(regex) - env = copy.deepcopy(os.environ) - - if pretty: - subunit_trace_cmd = ['subunit-trace', '--no-failure-debug', '-f'] - if color: - subunit_trace_cmd.append('--color') - - # This workaround is necessary because of lp bug 1411804 it's super hacky - # and makes tons of unfounded assumptions, but it works for the most part - if (subunit or pretty) and until_failure: - test_list = rb._get_test_list(regex, env) - count = 0 - failed = False - if not test_list: - print("No tests to run") - exit(1) - # If pretty or subunit output is desired manually loop forever over - # test individually and generate the desired output in a linear series - # this avoids 1411804 while retaining most of the desired behavior - while True: - for test in test_list: - if pretty: - cmd = ['python', '-m', 'subunit.run', test] - ps = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE) - subunit_trace_cmd.append('--no-summary') - proc = subprocess.Popen(subunit_trace_cmd, - env=env, - stdin=ps.stdout) - ps.stdout.close() - proc.communicate() - if proc.returncode > 0: - failed = True - break - else: - try: - subunit_run.main([sys.argv[0], test], sys.stdout) - except SystemExit as e: - if e > 0: - print("Ran %s tests without failure" % count) - exit(1) - else: - raise - count = count + 1 - if failed: - print("Ran %s tests without failure" % count) - exit(0) - # If not until-failure special case call testr like normal - elif pretty and not list_tests: - cmd.extend(others) - ps = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE) - proc = subprocess.Popen(subunit_trace_cmd, - env=env, stdin=ps.stdout) - ps.stdout.close() - else: - cmd.extend(others) - proc = subprocess.Popen(cmd, env=env) - proc.communicate() - return_code = proc.returncode - if slowest and not list_tests: - print("\nSlowest Tests:\n") - slow_proc = subprocess.Popen(['testr', 'slowest'], env=env) - slow_proc.communicate() - return return_code - - -def call_subunit_run(test_id, pretty, subunit): - if pretty: - env = copy.deepcopy(os.environ) - cmd = ['python', '-m', 'subunit.run', test_id] - ps = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE) - proc = subprocess.Popen(['subunit-trace', '--no-failure-debug', '-f'], - env=env, stdin=ps.stdout) - ps.stdout.close() - proc.communicate() - return proc.returncode - elif subunit: - subunit_run.main([sys.argv[0], test_id], sys.stdout) - else: - testtools_run.main([sys.argv[0], test_id], sys.stdout) - - -def _select_and_call_runner(opts, exclude_regex, others): - ec = 1 - if not os.path.isdir('.testrepository'): - subprocess.call(['testr', 'init']) - - if not opts.no_discover and not opts.pdb: - ec = call_testr(exclude_regex, opts.subunit, opts.pretty, opts.list, - opts.slowest, opts.parallel, opts.concurrency, - opts.until_failure, opts.color, others) - else: - if others: - print('Unexpected arguments: ' + ' '.join(others)) - return 2 - test_to_run = opts.no_discover or opts.pdb - if test_to_run.find('/') != -1: - test_to_run = rb.path_to_regex(test_to_run) - ec = call_subunit_run(test_to_run, opts.pretty, opts.subunit) - return ec - - -def main(): - opts, others = get_parser(sys.argv[1:]) - if opts.pretty and opts.subunit: - msg = ('Subunit output and pretty output cannot be specified at the ' - 'same time') - print(msg) - exit(2) - if opts.list and opts.no_discover: - msg = ('you can not list tests when you are bypassing discovery to ' - 'run a single test') - print(msg) - exit(3) - if not opts.parallel and opts.concurrency: - msg = "You can't specify a concurrency to use when running serially" - print(msg) - exit(4) - if (opts.pdb or opts.no_discover) and opts.until_failure: - msg = "You can not use until_failure mode with pdb or no-discover" - print(msg) - exit(5) - if opts.path: - regex = rb.path_to_regex(opts.path) - else: - regex = opts.regex - exclude_regex = rb.construct_regex(opts.blacklist_file, - opts.whitelist_file, - regex, - opts.print_exclude) - exit(_select_and_call_runner(opts, exclude_regex, others)) - -if __name__ == '__main__': - main() diff --git a/os_testr/regex_builder.py b/os_testr/regex_builder.py deleted file mode 100644 index 8c9f3ca..0000000 --- a/os_testr/regex_builder.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2016 Hewlett-Packard Development Company, L.P. -# -# 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. - -import copy -import os -import subprocess - - -def _get_test_list(regex, env=None): - env = env or copy.deepcopy(os.environ) - proc = subprocess.Popen(['testr', 'list-tests', regex], env=env, - stdout=subprocess.PIPE) - out = proc.communicate()[0] - raw_test_list = out.split('\n') - bad = False - test_list = [] - exclude_list = ['OS_', 'CAPTURE', 'TEST_TIMEOUT', 'PYTHON', - 'subunit.run discover'] - for line in raw_test_list: - for exclude in exclude_list: - if exclude in line or not line: - bad = True - break - if not bad: - test_list.append(line) - bad = False - return test_list - - -def print_skips(regex, message): - test_list = _get_test_list(regex) - if test_list: - if message: - print(message) - else: - print('Skipped because of regex %s:' % regex) - for test in test_list: - print(test) - # Extra whitespace to separate - print('\n') - - -def path_to_regex(path): - root, _ = os.path.splitext(path) - return root.replace('/', '.') - - -def get_regex_from_whitelist_file(file_path): - lines = [] - for line in open(file_path).read().splitlines(): - split_line = line.strip().split('#') - # Before the # is the regex - line_regex = split_line[0].strip() - if line_regex: - lines.append(line_regex) - return '|'.join(lines) - - -def construct_regex(blacklist_file, whitelist_file, regex, print_exclude): - if not blacklist_file: - exclude_regex = '' - else: - black_file = open(blacklist_file, 'r') - exclude_regex = '' - for line in black_file: - raw_line = line.strip() - split_line = raw_line.split('#') - # Before the # is the regex - line_regex = split_line[0].strip() - if len(split_line) > 1: - # After the # is a comment - comment = split_line[1].strip() - else: - comment = '' - if line_regex: - if print_exclude: - print_skips(line_regex, comment) - if exclude_regex: - exclude_regex = '|'.join([line_regex, exclude_regex]) - else: - exclude_regex = line_regex - if exclude_regex: - exclude_regex = "^((?!" + exclude_regex + ").)*$" - if regex: - exclude_regex += regex - if whitelist_file: - exclude_regex += '%s' % get_regex_from_whitelist_file(whitelist_file) - return exclude_regex diff --git a/os_testr/subunit2html.py b/os_testr/subunit2html.py deleted file mode 100755 index a710b9f..0000000 --- a/os_testr/subunit2html.py +++ /dev/null @@ -1,751 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# -# 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. -""" -Utility to convert a subunit stream to an html results file. -Code is adapted from the pyunit Html test runner at -http://tungwaiyip.info/software/HTMLTestRunner.html - -Takes two arguments. First argument is path to subunit log file, second -argument is path of desired output file. Second argument is optional, -defaults to 'results.html'. - -Original HTMLTestRunner License: ------------------------------------------------------------------------- -Copyright (c) 2004-2007, Wai Yip Tung -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -* Neither the name Wai Yip Tung nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - -import collections -import datetime -import io -import sys -import traceback -from xml.sax import saxutils - -import pbr.version -import subunit -import testtools - - -__version__ = pbr.version.VersionInfo('os_testr').version_string() - - -class TemplateData(object): - """Define a HTML template for report customerization and generation. - - Overall structure of an HTML report - - HTML - +------------------------+ - | | - | | - | | - | STYLESHEET | - | +----------------+ | - | | | | - | +----------------+ | - | | - | | - | | - | | - | | - | HEADING | - | +----------------+ | - | | | | - | +----------------+ | - | | - | REPORT | - | +----------------+ | - | | | | - | +----------------+ | - | | - | ENDING | - | +----------------+ | - | | | | - | +----------------+ | - | | - | | - | | - +------------------------+ - """ - - STATUS = { - 0: 'pass', - 1: 'fail', - 2: 'error', - 3: 'skip', - } - - DEFAULT_TITLE = 'Unit Test Report' - DEFAULT_DESCRIPTION = '' - - # ------------------------------------------------------------------------ - # HTML Template - - HTML_TMPL = r""" - - - - %(title)s - - - %(stylesheet)s - - - - -%(heading)s -%(report)s -%(ending)s - - - -""" - # variables: (title, generator, stylesheet, heading, report, ending) - - # ------------------------------------------------------------------------ - # Stylesheet - # - # alternatively use a for external style sheet, e.g. - # - - STYLESHEET_TMPL = """ - -""" - - # ------------------------------------------------------------------------ - # Heading - # - - HEADING_TMPL = """
-

%(title)s

-%(parameters)s -

%(description)s

-
- -""" # variables: (title, parameters, description) - - HEADING_ATTRIBUTE_TMPL = """ -

%(name)s: %(value)s

-""" # variables: (name, value) - - # ------------------------------------------------------------------------ - # Report - # - - REPORT_TMPL = """ -

Show -Summary -Failed -All -

- ---------- - - - - - - - - - - -%(test_list)s - - - - - - - - - - -
Test Group/Test caseCountPassFailErrorSkipView
Total%(count)s%(Pass)s%(fail)s%(error)s%(skip)s  
-""" # variables: (test_list, count, Pass, fail, error) - - REPORT_CLASS_TMPL = r""" - - %(desc)s - %(count)s - %(Pass)s - %(fail)s - %(error)s - %(skip)s - Detail - - -""" # variables: (style, desc, count, Pass, fail, error, cid) - - REPORT_TEST_WITH_OUTPUT_TMPL = r""" - -
%(desc)s
- - - - - %(status)s - - - - - - -""" # variables: (tid, Class, style, desc, status) - - REPORT_TEST_NO_OUTPUT_TMPL = r""" - -
%(desc)s
- %(status)s - -""" # variables: (tid, Class, style, desc, status) - - REPORT_TEST_OUTPUT_TMPL = r""" -%(id)s: %(output)s -""" # variables: (id, output) - - # ------------------------------------------------------------------------ - # ENDING - # - - ENDING_TMPL = """
 
""" - -# -------------------- The end of the Template class ------------------- - - -class ClassInfoWrapper(object): - def __init__(self, name, mod): - self.name = name - self.mod = mod - - def __repr__(self): - return "%s" % (self.name) - - -class HtmlOutput(testtools.TestResult): - """Output test results in html.""" - - def __init__(self, html_file='result.html'): - super(HtmlOutput, self).__init__() - self.success_count = 0 - self.failure_count = 0 - self.error_count = 0 - self.skip_count = 0 - self.result = [] - self.html_file = html_file - - def addSuccess(self, test): - self.success_count += 1 - output = test.shortDescription() - if output is None: - output = test.id() - self.result.append((0, test, output, '')) - - def addSkip(self, test, err): - output = test.shortDescription() - if output is None: - output = test.id() - self.skip_count += 1 - self.result.append((3, test, output, '')) - - def addError(self, test, err): - output = test.shortDescription() - if output is None: - output = test.id() - # Skipped tests are handled by SkipTest Exceptions. - # if err[0] == SkipTest: - # self.skip_count += 1 - # self.result.append((3, test, output, '')) - else: - self.error_count += 1 - _exc_str = self.formatErr(err) - self.result.append((2, test, output, _exc_str)) - - def addFailure(self, test, err): - print(test) - self.failure_count += 1 - _exc_str = self.formatErr(err) - output = test.shortDescription() - if output is None: - output = test.id() - self.result.append((1, test, output, _exc_str)) - - def formatErr(self, err): - exctype, value, tb = err - return ''.join(traceback.format_exception(exctype, value, tb)) - - def stopTestRun(self): - super(HtmlOutput, self).stopTestRun() - self.stopTime = datetime.datetime.now() - report_attrs = self._getReportAttributes() - generator = 'subunit2html %s' % __version__ - heading = self._generate_heading(report_attrs) - report = self._generate_report() - ending = self._generate_ending() - output = TemplateData.HTML_TMPL % dict( - title=saxutils.escape(TemplateData.DEFAULT_TITLE), - generator=generator, - stylesheet=TemplateData.STYLESHEET_TMPL, - heading=heading, - report=report, - ending=ending, - ) - if self.html_file: - with open(self.html_file, 'wb') as html_file: - html_file.write(output.encode('utf8')) - - def _getReportAttributes(self): - """Return report attributes as a list of (name, value).""" - status = [] - if self.success_count: - status.append('Pass %s' % self.success_count) - if self.failure_count: - status.append('Failure %s' % self.failure_count) - if self.error_count: - status.append('Error %s' % self.error_count) - if self.skip_count: - status.append('Skip %s' % self.skip_count) - if status: - status = ' '.join(status) - else: - status = 'none' - return [ - ('Status', status), - ] - - def _generate_heading(self, report_attrs): - a_lines = [] - for name, value in report_attrs: - line = TemplateData.HEADING_ATTRIBUTE_TMPL % dict( - name=saxutils.escape(name), - value=saxutils.escape(value), - ) - a_lines.append(line) - heading = TemplateData.HEADING_TMPL % dict( - title=saxutils.escape(TemplateData.DEFAULT_TITLE), - parameters=''.join(a_lines), - description=saxutils.escape(TemplateData.DEFAULT_DESCRIPTION), - ) - return heading - - def _generate_report(self): - rows = [] - sortedResult = self._sortResult(self.result) - for cid, (cls, cls_results) in enumerate(sortedResult): - # subtotal for a class - np = nf = ne = ns = 0 - for n, t, o, e in cls_results: - if n == 0: - np += 1 - elif n == 1: - nf += 1 - elif n == 2: - ne += 1 - else: - ns += 1 - - # format class description - if cls.mod == "__main__": - name = cls.name - else: - name = "%s" % (cls.name) - doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" - desc = doc and '%s: %s' % (name, doc) or name - - row = TemplateData.REPORT_CLASS_TMPL % dict( - style=(ne > 0 and 'errorClass' or nf > 0 - and 'failClass' or 'passClass'), - desc = desc, - count = np + nf + ne + ns, - Pass = np, - fail = nf, - error = ne, - skip = ns, - cid = 'c%s' % (cid + 1), - ) - rows.append(row) - - for tid, (n, t, o, e) in enumerate(cls_results): - self._generate_report_test(rows, cid, tid, n, t, o, e) - - report = TemplateData.REPORT_TMPL % dict( - test_list=''.join(rows), - count=str(self.success_count + self.failure_count + - self.error_count + self.skip_count), - Pass=str(self.success_count), - fail=str(self.failure_count), - error=str(self.error_count), - skip=str(self.skip_count), - ) - return report - - def _sortResult(self, result_list): - # unittest does not seems to run in any particular order. - # Here at least we want to group them together by class. - rmap = {} - # Differentiate between classes that have test failures so we can sort - # them at the top of the html page for easier troubleshooting - failclasses = [] - passclasses = [] - for n, t, o, e in result_list: - classes = failclasses if n == 1 or n == 2 else passclasses - if hasattr(t, '_tests'): - for inner_test in t._tests: - self._add_cls(rmap, classes, inner_test, - (n, inner_test, o, e)) - else: - self._add_cls(rmap, classes, t, (n, t, o, e)) - classort = lambda s: str(s) - sortedfailclasses = sorted(failclasses, key=classort) - sortedpassclasses = sorted(passclasses, key=classort) - sortedclasses = sortedfailclasses + sortedpassclasses - r = [(cls, rmap[str(cls)]) for cls in sortedclasses] - return r - - def _add_cls(self, rmap, classes, test, data_tuple): - if hasattr(test, 'test'): - test = test.test - if test.__class__ == subunit.RemotedTestCase: - cl = test._RemotedTestCase__description.rsplit('.', 1)[0] - else: - cl = test.id().rsplit('.', 1)[0] - mod = cl.rsplit('.', 1)[0] - cls = ClassInfoWrapper(cl, mod) - if not str(cls) in rmap: - rmap[str(cls)] = [] - classes.append(cls) - rmap[str(cls)].append(data_tuple) - - def _generate_report_test(self, rows, cid, tid, n, t, o, e): - # e.g. 'pt1.1', 'ft1.1', etc - # ptx.x for passed/skipped tests and ftx.x for failed/errored tests. - has_output = bool(o or e) - tid = ((n == 0 or n == 3) and - 'p' or 'f') + 't%s.%s' % (cid + 1, tid + 1) - name = t.id().split('.')[-1] - # if shortDescription is not the function name, use it - if t.shortDescription().find(name) == -1: - doc = t.shortDescription() - else: - doc = None - desc = doc and ('%s: %s' % (name, doc)) or name - tmpl = (has_output and TemplateData.REPORT_TEST_WITH_OUTPUT_TMPL - or TemplateData.REPORT_TEST_NO_OUTPUT_TMPL) - - script = TemplateData.REPORT_TEST_OUTPUT_TMPL % dict( - id=tid, - output=saxutils.escape(o + e), - ) - - row = tmpl % dict( - tid=tid, - Class=((n == 0 or n == 3) and 'hiddenRow' or 'none'), - style=(n == 2 and 'errorCase' or - (n == 1 and 'failCase' or 'none')), - desc=desc, - script=script, - status=TemplateData.STATUS[n], - ) - rows.append(row) - if not has_output: - return - - def _generate_ending(self): - return TemplateData.ENDING_TMPL - - def startTestRun(self): - super(HtmlOutput, self).startTestRun() - - -class FileAccumulator(testtools.StreamResult): - - def __init__(self): - super(FileAccumulator, self).__init__() - self.route_codes = collections.defaultdict(io.BytesIO) - - def status(self, **kwargs): - if kwargs.get('file_name') != 'stdout': - return - file_bytes = kwargs.get('file_bytes') - if not file_bytes: - return - route_code = kwargs.get('route_code') - stream = self.route_codes[route_code] - stream.write(file_bytes) - - -def main(): - if '--version' in sys.argv: - print(__version__) - exit(0) - - if len(sys.argv) < 2: - print("Need at least one argument: path to subunit log.") - exit(1) - subunit_file = sys.argv[1] - if len(sys.argv) > 2: - html_file = sys.argv[2] - else: - html_file = 'results.html' - - html_result = HtmlOutput(html_file) - stream = open(subunit_file, 'rb') - - # Feed the subunit stream through both a V1 and V2 parser. - # Depends on having the v2 capable libraries installed. - # First V2. - # Non-v2 content and captured non-test output will be presented as file - # segments called stdout. - suite = subunit.ByteStreamToStreamResult(stream, non_subunit_name='stdout') - # The HTML output code is in legacy mode. - result = testtools.StreamToExtendedDecorator(html_result) - # Divert non-test output - accumulator = FileAccumulator() - result = testtools.StreamResultRouter(result) - result.add_rule(accumulator, 'test_id', test_id=None) - result.startTestRun() - suite.run(result) - # Now reprocess any found stdout content as V1 subunit - for bytes_io in accumulator.route_codes.values(): - bytes_io.seek(0) - suite = subunit.ProtocolTestCase(bytes_io) - suite.run(html_result) - result.stopTestRun() - - -if __name__ == '__main__': - main() diff --git a/os_testr/subunit_trace.py b/os_testr/subunit_trace.py deleted file mode 100755 index f4bbc13..0000000 --- a/os_testr/subunit_trace.py +++ /dev/null @@ -1,403 +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.""" -from __future__ import absolute_import - -import argparse -import datetime -import functools -import os -import re -import sys - -import pbr.version -import subunit -import testtools - -from os_testr.utils import colorizer - -# NOTE(mtreinish) on python3 anydbm was renamed dbm and the python2 dbm module -# was renamed to dbm.ndbm, this block takes that into account -try: - import anydbm as dbm -except ImportError: - import dbm - -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'): - line = line.encode('utf8') - stream.write(" %s\n" % line) - - -def find_test_run_time_diff(test_id, run_time): - times_db_path = os.path.join(os.path.join(os.getcwd(), '.testrepository'), - 'times.dbm') - if os.path.isfile(times_db_path): - try: - test_times = dbm.open(times_db_path) - except Exception: - return False - try: - avg_runtime = float(test_times.get(str(test_id), False)) - except Exception: - try: - avg_runtime = float(test_times[str(test_id)]) - except Exception: - avg_runtime = False - - if avg_runtime and avg_runtime > 0: - run_time = float(run_time.rstrip('s')) - perc_diff = ((run_time - avg_runtime) / avg_runtime) * 100 - return perc_diff - return False - - -def show_outcome(stream, test, print_failures=False, failonly=False, - enable_diff=False, threshold='0', abbreviate=False, - enable_color=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 - - for color in [colorizer.AnsiColorizer, colorizer.NullColorizer]: - if not enable_color: - color = colorizer.NullColorizer(stream) - break - if color.supported(): - color = color(stream) - break - - if status == 'fail' or status == 'uxsuccess': - FAILS.append(test) - if abbreviate: - color.write('F', 'red') - else: - stream.write('{%s} %s [%s] ... ' % ( - worker, name, duration)) - color.write('FAILED', 'red') - stream.write('\n') - if not print_failures: - print_attachments(stream, test, all_channels=True) - elif not failonly: - if status == 'success' or status == 'xfail': - if abbreviate: - color.write('.', 'green') - else: - out_string = '{%s} %s [%s' % (worker, name, duration) - perc_diff = find_test_run_time_diff(test['id'], duration) - if enable_diff: - if perc_diff and abs(perc_diff) >= abs(float(threshold)): - if perc_diff > 0: - out_string = out_string + ' +%.2f%%' % perc_diff - else: - out_string = out_string + ' %.2f%%' % perc_diff - stream.write(out_string + '] ... ') - color.write('ok', 'green') - stream.write('\n') - print_attachments(stream, test) - elif status == 'skip': - if abbreviate: - color.write('S', 'blue') - else: - reason = test['details'].get('reason', '') - if reason: - reason = ': ' + reason.as_text() - stream.write('{%s} %s ... ' % ( - worker, name)) - color.write('SKIPPED', 'blue') - stream.write('%s' % (reason)) - stream.write('\n') - else: - if abbreviate: - stream.write('%s' % test['status'][0]) - 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) - stop_time = tests[-1]['timestamps'][1] - start_time = tests[0]['timestamps'][0] - if not start_time or not stop_time: - delta = 'N/A' - else: - delta = stop_time - start_time - return num_tests, str(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) - out_str = " - Worker %s (%s tests) => %s" % (w, num, time) - if time.isdigit(): - out_str += 's' - out_str += '\n' - stream.write(out_str) - - -__version__ = pbr.version.VersionInfo('os_testr').version_string() - - -def parse_args(): - parser = argparse.ArgumentParser() - parser.add_argument('--version', action='version', - version='%s' % __version__) - 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)) - parser.add_argument('--abbreviate', '-a', action='store_true', - dest='abbreviate', help='Print one character status' - 'for each test') - parser.add_argument('--perc-diff', '-d', action='store_true', - dest='enable_diff', - help="Print percent change in run time on each test ") - parser.add_argument('--diff-threshold', '-t', dest='threshold', - help="Threshold to use for displaying percent change " - "from the avg run time. If one is not specified " - "the percent change will always be displayed") - parser.add_argument('--no-summary', action='store_true', - help="Don't print the summary of the test run after " - " completes") - parser.add_argument('--color', action='store_true', - help="Print results with colors") - return parser.parse_args() - - -def trace(stdin, stdout, print_failures=False, failonly=False, - enable_diff=False, abbreviate=False, color=False, post_fails=False, - no_summary=False): - stream = subunit.ByteStreamToStreamResult( - stdin, non_subunit_name='stdout') - outcomes = testtools.StreamToDict( - functools.partial(show_outcome, stdout, - print_failures=print_failures, - failonly=failonly, - enable_diff=enable_diff, - abbreviate=abbreviate, - enable_color=color)) - summary = testtools.StreamSummary() - result = testtools.CopyStreamResult([outcomes, summary]) - result = testtools.StreamResultRouter(result) - cat = subunit.test_results.CatFiles(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") - return 1 - if post_fails: - print_fails(stdout) - if not no_summary: - print_summary(stdout, elapsed_time) - - # NOTE(mtreinish): Ideally this should live in testtools streamSummary - # this is just in place until the behavior lands there (if it ever does) - if count_tests('status', '^success$') == 0: - print("\nNo tests were successful during the run") - return 1 - return 0 if summary.wasSuccessful() else 1 - - -def main(): - args = parse_args() - exit(trace(sys.stdin, sys.stdout, args.print_failures, args.failonly, - args.enable_diff, args.abbreviate, args.color, args.post_fails, - args.no_summary)) - - -if __name__ == '__main__': - main() diff --git a/os_testr/tests/__init__.py b/os_testr/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/os_testr/tests/base.py b/os_testr/tests/base.py deleted file mode 100644 index 1c30cdb..0000000 --- a/os_testr/tests/base.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2010-2011 OpenStack Foundation -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# 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 oslotest import base - - -class TestCase(base.BaseTestCase): - - """Test case base class for all unit tests.""" diff --git a/os_testr/tests/files/__init__.py b/os_testr/tests/files/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/os_testr/tests/files/failing-tests b/os_testr/tests/files/failing-tests deleted file mode 100644 index 78efc93..0000000 --- a/os_testr/tests/files/failing-tests +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - -import testtools - -class FakeTestClass(testtools.TestCase): - def test_pass(self): - self.assertTrue(False) - - def test_pass_list(self): - test_list = ['test', 'a', 'b'] - self.assertIn('fail', test_list) diff --git a/os_testr/tests/files/passing-tests b/os_testr/tests/files/passing-tests deleted file mode 100644 index a55cb1b..0000000 --- a/os_testr/tests/files/passing-tests +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - -import testtools - -class FakeTestClass(testtools.TestCase): - def test_pass(self): - self.assertTrue(True) - - def test_pass_list(self): - test_list = ['test', 'a', 'b'] - self.assertIn('test', test_list) diff --git a/os_testr/tests/files/setup.cfg b/os_testr/tests/files/setup.cfg deleted file mode 100644 index f6f9f73..0000000 --- a/os_testr/tests/files/setup.cfg +++ /dev/null @@ -1,20 +0,0 @@ -[metadata] -name = tempest_unit_tests -version = 1 -summary = Fake Project for testing wrapper scripts -author = OpenStack -author-email = openstack-dev@lists.openstack.org -home-page = http://www.openstack.org/ -classifier = - Intended Audience :: Information Technology - Intended Audience :: System Administrators - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - -[global] -setup-hooks = - pbr.hooks.setup_hook diff --git a/os_testr/tests/files/testr-conf b/os_testr/tests/files/testr-conf deleted file mode 100644 index d5ad083..0000000 --- a/os_testr/tests/files/testr-conf +++ /dev/null @@ -1,5 +0,0 @@ -[DEFAULT] -test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./tests $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list -group_regex=([^\.]*\.)* diff --git a/os_testr/tests/sample_streams/all_skips.subunit b/os_testr/tests/sample_streams/all_skips.subunit deleted file mode 100644 index 54156b5..0000000 Binary files a/os_testr/tests/sample_streams/all_skips.subunit and /dev/null differ diff --git a/os_testr/tests/sample_streams/successful.subunit b/os_testr/tests/sample_streams/successful.subunit deleted file mode 100644 index 6f3b664..0000000 Binary files a/os_testr/tests/sample_streams/successful.subunit and /dev/null differ diff --git a/os_testr/tests/test_ostestr.py b/os_testr/tests/test_ostestr.py deleted file mode 100644 index df76ce6..0000000 --- a/os_testr/tests/test_ostestr.py +++ /dev/null @@ -1,117 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -""" -test_os_testr ----------------------------------- - -Tests for `os_testr` module. -""" - -import mock - -from os_testr import ostestr as os_testr -from os_testr.tests import base - - -class TestGetParser(base.TestCase): - def test_pretty(self): - namespace = os_testr.get_parser(['--pretty']) - self.assertEqual(True, namespace[0].pretty) - namespace = os_testr.get_parser(['--no-pretty']) - self.assertEqual(False, namespace[0].pretty) - self.assertRaises(SystemExit, os_testr.get_parser, - ['--no-pretty', '--pretty']) - - def test_slowest(self): - namespace = os_testr.get_parser(['--slowest']) - self.assertEqual(True, namespace[0].slowest) - namespace = os_testr.get_parser(['--no-slowest']) - self.assertEqual(False, namespace[0].slowest) - self.assertRaises(SystemExit, os_testr.get_parser, - ['--no-slowest', '--slowest']) - - def test_parallel(self): - namespace = os_testr.get_parser(['--parallel']) - self.assertEqual(True, namespace[0].parallel) - namespace = os_testr.get_parser(['--serial']) - self.assertEqual(False, namespace[0].parallel) - self.assertRaises(SystemExit, os_testr.get_parser, - ['--parallel', '--serial']) - - -class TestCallers(base.TestCase): - def test_no_discover(self): - namespace = os_testr.get_parser(['-n', 'project.tests.foo']) - - def _fake_exit(arg): - self.assertTrue(arg) - - def _fake_run(*args, **kwargs): - return 'project.tests.foo' in args - - with mock.patch.object(os_testr, 'exit', side_effect=_fake_exit), \ - mock.patch.object(os_testr, 'get_parser', return_value=namespace), \ - mock.patch.object(os_testr, - 'call_subunit_run', - side_effect=_fake_run): - os_testr.main() - - def test_no_discover_path(self): - namespace = os_testr.get_parser(['-n', 'project/tests/foo']) - - def _fake_exit(arg): - self.assertTrue(arg) - - def _fake_run(*args, **kwargs): - return 'project.tests.foo' in args - - with mock.patch.object(os_testr, 'exit', side_effect=_fake_exit), \ - mock.patch.object(os_testr, 'get_parser', return_value=namespace), \ - mock.patch.object(os_testr, - 'call_subunit_run', - side_effect=_fake_run): - os_testr.main() - - def test_pdb(self): - namespace = os_testr.get_parser(['--pdb', 'project.tests.foo']) - - def _fake_exit(arg): - self.assertTrue(arg) - - def _fake_run(*args, **kwargs): - return 'project.tests.foo' in args - - with mock.patch.object(os_testr, 'exit', side_effect=_fake_exit), \ - mock.patch.object(os_testr, 'get_parser', return_value=namespace), \ - mock.patch.object(os_testr, - 'call_subunit_run', - side_effect=_fake_run): - os_testr.main() - - def test_pdb_path(self): - namespace = os_testr.get_parser(['--pdb', 'project/tests/foo']) - - def _fake_exit(arg): - self.assertTrue(arg) - - def _fake_run(*args, **kwargs): - return 'project.tests.foo' in args - - with mock.patch.object(os_testr, 'exit', side_effect=_fake_exit), \ - mock.patch.object(os_testr, 'get_parser', return_value=namespace), \ - mock.patch.object(os_testr, - 'call_subunit_run', - side_effect=_fake_run): - os_testr.main() diff --git a/os_testr/tests/test_regex_builder.py b/os_testr/tests/test_regex_builder.py deleted file mode 100644 index e284036..0000000 --- a/os_testr/tests/test_regex_builder.py +++ /dev/null @@ -1,170 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -import mock - -import six - -from os_testr import regex_builder as os_testr -from os_testr.tests import base - - -class TestPathToRegex(base.TestCase): - - def test_file_name(self): - result = os_testr.path_to_regex("tests/network/v2/test_net.py") - self.assertEqual("tests.network.v2.test_net", result) - result = os_testr.path_to_regex("openstack/tests/network/v2") - self.assertEqual("openstack.tests.network.v2", result) - - -class TestConstructRegex(base.TestCase): - def test_regex_passthrough(self): - result = os_testr.construct_regex(None, None, 'fake_regex', False) - self.assertEqual(result, 'fake_regex') - - def test_blacklist_regex_with_comments(self): - blacklist_file = six.StringIO() - for i in range(4): - blacklist_file.write('fake_regex_%s # A Comment\n' % i) - blacklist_file.seek(0) - with mock.patch('six.moves.builtins.open', - return_value=blacklist_file): - result = os_testr.construct_regex('fake_path', None, None, False) - self.assertEqual( - result, - "^((?!fake_regex_3|fake_regex_2|fake_regex_1|fake_regex_0).)*$") - - def test_whitelist_regex_with_comments(self): - whitelist_file = six.StringIO() - for i in range(4): - whitelist_file.write('fake_regex_%s # A Comment\n' % i) - whitelist_file.seek(0) - with mock.patch('six.moves.builtins.open', - return_value=whitelist_file): - result = os_testr.construct_regex(None, 'fake_path', None, False) - self.assertEqual( - result, - "fake_regex_0|fake_regex_1|fake_regex_2|fake_regex_3") - - def test_blacklist_regex_without_comments(self): - blacklist_file = six.StringIO() - for i in range(4): - blacklist_file.write('fake_regex_%s\n' % i) - blacklist_file.seek(0) - with mock.patch('six.moves.builtins.open', - return_value=blacklist_file): - result = os_testr.construct_regex('fake_path', None, None, False) - self.assertEqual( - result, - "^((?!fake_regex_3|fake_regex_2|fake_regex_1|fake_regex_0).)*$") - - def test_blacklist_regex_with_comments_and_regex(self): - blacklist_file = six.StringIO() - for i in range(4): - blacklist_file.write('fake_regex_%s # Comments\n' % i) - blacklist_file.seek(0) - with mock.patch('six.moves.builtins.open', - return_value=blacklist_file): - result = os_testr.construct_regex('fake_path', None, - 'fake_regex', False) - - expected_regex = ("^((?!fake_regex_3|fake_regex_2|fake_regex_1|" - "fake_regex_0).)*$fake_regex") - self.assertEqual(result, expected_regex) - - def test_blacklist_regex_without_comments_and_regex(self): - blacklist_file = six.StringIO() - for i in range(4): - blacklist_file.write('fake_regex_%s\n' % i) - blacklist_file.seek(0) - with mock.patch('six.moves.builtins.open', - return_value=blacklist_file): - result = os_testr.construct_regex('fake_path', None, - 'fake_regex', False) - - expected_regex = ("^((?!fake_regex_3|fake_regex_2|fake_regex_1|" - "fake_regex_0).)*$fake_regex") - self.assertEqual(result, expected_regex) - - @mock.patch.object(os_testr, 'print_skips') - def test_blacklist_regex_with_comment_print_skips(self, print_mock): - blacklist_file = six.StringIO() - for i in range(4): - blacklist_file.write('fake_regex_%s # Comment\n' % i) - blacklist_file.seek(0) - with mock.patch('six.moves.builtins.open', - return_value=blacklist_file): - result = os_testr.construct_regex('fake_path', None, - None, True) - - expected_regex = ("^((?!fake_regex_3|fake_regex_2|fake_regex_1|" - "fake_regex_0).)*$") - self.assertEqual(result, expected_regex) - calls = print_mock.mock_calls - self.assertEqual(len(calls), 4) - args = list(map(lambda x: x[1], calls)) - self.assertIn(('fake_regex_0', 'Comment'), args) - self.assertIn(('fake_regex_1', 'Comment'), args) - self.assertIn(('fake_regex_2', 'Comment'), args) - self.assertIn(('fake_regex_3', 'Comment'), args) - - @mock.patch.object(os_testr, 'print_skips') - def test_blacklist_regex_without_comment_print_skips(self, print_mock): - blacklist_file = six.StringIO() - for i in range(4): - blacklist_file.write('fake_regex_%s\n' % i) - blacklist_file.seek(0) - with mock.patch('six.moves.builtins.open', - return_value=blacklist_file): - result = os_testr.construct_regex('fake_path', None, - None, True) - - expected_regex = ("^((?!fake_regex_3|fake_regex_2|" - "fake_regex_1|fake_regex_0).)*$") - self.assertEqual(result, expected_regex) - calls = print_mock.mock_calls - self.assertEqual(len(calls), 4) - args = list(map(lambda x: x[1], calls)) - self.assertIn(('fake_regex_0', ''), args) - self.assertIn(('fake_regex_1', ''), args) - self.assertIn(('fake_regex_2', ''), args) - self.assertIn(('fake_regex_3', ''), args) - - -class TestWhitelistFile(base.TestCase): - def test_read_whitelist_file(self): - file_contents = """regex_a -regex_b""" - whitelist_file = six.StringIO() - whitelist_file.write(file_contents) - whitelist_file.seek(0) - with mock.patch('six.moves.builtins.open', - return_value=whitelist_file): - regex = os_testr.get_regex_from_whitelist_file('/path/to/not_used') - self.assertEqual('regex_a|regex_b', regex) - - def test_whitelist_regex_without_comments_and_regex(self): - file_contents = """regex_a -regex_b""" - whitelist_file = six.StringIO() - whitelist_file.write(file_contents) - whitelist_file.seek(0) - with mock.patch('six.moves.builtins.open', - return_value=whitelist_file): - result = os_testr.construct_regex(None, 'fake_path', - None, False) - - expected_regex = 'regex_a|regex_b' - self.assertEqual(result, expected_regex) diff --git a/os_testr/tests/test_return_codes.py b/os_testr/tests/test_return_codes.py deleted file mode 100644 index 082a74a..0000000 --- a/os_testr/tests/test_return_codes.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# 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. - -import os -import shutil -import subprocess -import tempfile - -import testtools - -from os_testr.tests import base -from six import StringIO - -DEVNULL = open(os.devnull, 'wb') - - -class TestReturnCodes(base.TestCase): - def setUp(self): - super(TestReturnCodes, self).setUp() - # Setup test dirs - self.directory = tempfile.mkdtemp(prefix='ostestr-unit') - self.addCleanup(shutil.rmtree, self.directory) - self.test_dir = os.path.join(self.directory, 'tests') - os.mkdir(self.test_dir) - # Setup Test files - self.testr_conf_file = os.path.join(self.directory, '.testr.conf') - self.setup_cfg_file = os.path.join(self.directory, 'setup.cfg') - self.passing_file = os.path.join(self.test_dir, 'test_passing.py') - self.failing_file = os.path.join(self.test_dir, 'test_failing.py') - self.init_file = os.path.join(self.test_dir, '__init__.py') - self.setup_py = os.path.join(self.directory, 'setup.py') - shutil.copy('os_testr/tests/files/testr-conf', self.testr_conf_file) - shutil.copy('os_testr/tests/files/passing-tests', self.passing_file) - shutil.copy('os_testr/tests/files/failing-tests', self.failing_file) - shutil.copy('setup.py', self.setup_py) - shutil.copy('os_testr/tests/files/setup.cfg', self.setup_cfg_file) - shutil.copy('os_testr/tests/files/__init__.py', self.init_file) - - self.stdout = StringIO() - self.stderr = StringIO() - # Change directory, run wrapper and check result - self.addCleanup(os.chdir, os.path.abspath(os.curdir)) - os.chdir(self.directory) - - def assertRunExit(self, cmd, expected, subunit=False): - p = subprocess.Popen( - "%s" % cmd, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() - - if not subunit: - self.assertEqual( - p.returncode, expected, - "Stdout: %s; Stderr: %s" % (out, err)) - else: - self.assertEqual(p.returncode, expected, - "Expected return code: %s doesn't match actual " - "return code of: %s" % (expected, p.returncode)) - - def test_default_passing(self): - self.assertRunExit('ostestr --regex passing', 0) - - def test_default_fails(self): - self.assertRunExit('ostestr', 1) - - def test_default_passing_no_slowest(self): - self.assertRunExit('ostestr --no-slowest --regex passing', 0) - - def test_default_fails_no_slowest(self): - self.assertRunExit('ostestr --no-slowest', 1) - - def test_default_serial_passing(self): - self.assertRunExit('ostestr --serial --regex passing', 0) - - def test_default_serial_fails(self): - self.assertRunExit('ostestr --serial', 1) - - def test_testr_subunit_passing(self): - self.assertRunExit('ostestr --no-pretty --subunit --regex passing', 0, - subunit=True) - - @testtools.skip('Skipped because of testrepository lp bug #1411804') - def test_testr_subunit_fails(self): - self.assertRunExit('ostestr --no-pretty --subunit', 1, subunit=True) - - def test_testr_no_pretty_passing(self): - self.assertRunExit('ostestr --no-pretty --regex passing', 0) - - def test_testr_no_pretty_fails(self): - self.assertRunExit('ostestr --no-pretty', 1) - - def test_list(self): - self.assertRunExit('ostestr --list', 0) diff --git a/os_testr/tests/test_subunit2html.py b/os_testr/tests/test_subunit2html.py deleted file mode 100644 index 994eed6..0000000 --- a/os_testr/tests/test_subunit2html.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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. - -import sys - -from ddt import data -from ddt import ddt -from subunit import RemotedTestCase -from testtools import PlaceHolder - -from os_testr import subunit2html -from os_testr.tests import base - - -@ddt -class TestSubunit2html(base.TestCase): - @data(RemotedTestCase, PlaceHolder) - def test_class_parsing(self, test_cls): - """Tests that the class paths are parsed for v1 & v2 tests""" - test_ = test_cls("example.path.to.test.method") - obj_ = subunit2html.HtmlOutput() - cls_ = [] - obj_._add_cls({}, cls_, test_, ()) - self.assertEqual("example.path.to.test", cls_[0].name) - - @data(RemotedTestCase, PlaceHolder) - def test_result_sorting(self, test_cls): - tests = [] - for i in range(9): - tests.append(test_cls('example.path.to.test%d.method' % i)) - # addFailure, addError, and addSkip need the real exc_info - try: - raise Exception('fake') - except Exception: - err = sys.exc_info() - obj = subunit2html.HtmlOutput() - obj.addSuccess(tests[3]) - obj.addSuccess(tests[1]) - # example.path.to.test2 has a failure - obj.addFailure(tests[2], err) - obj.addSkip(tests[0], err) - obj.addSuccess(tests[8]) - # example.path.to.test5 has a failure (error) - obj.addError(tests[5], err) - # example.path.to.test4 has a failure - obj.addFailure(tests[4], err) - obj.addSuccess(tests[7]) - # example.path.to.test6 has a failure - obj.addFailure(tests[6], err) - sorted_result = obj._sortResult(obj.result) - # _sortResult returns a list of results of format: - # [(class, [test_result_tuple, ...]), ...] - # sorted by str(class) - # - # Classes with failures (2, 4, 5, and 6) should be sorted separately - # at the top. The rest of the classes should be in sorted order after. - expected_class_order = ['example.path.to.test2', - 'example.path.to.test4', - 'example.path.to.test5', - 'example.path.to.test6', - 'example.path.to.test0', - 'example.path.to.test1', - 'example.path.to.test3', - 'example.path.to.test7', - 'example.path.to.test8'] - for i, r in enumerate(sorted_result): - self.assertEqual(expected_class_order[i], str(r[0])) diff --git a/os_testr/tests/test_subunit_trace.py b/os_testr/tests/test_subunit_trace.py deleted file mode 100644 index 462bceb..0000000 --- a/os_testr/tests/test_subunit_trace.py +++ /dev/null @@ -1,96 +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 -import io -import os -import subprocess -import sys - -from ddt import data -from ddt import ddt -from ddt import unpack -from mock import patch -import six - -from os_testr import subunit_trace -from os_testr.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) - - def test_return_code_all_skips(self): - skips_stream = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'sample_streams/all_skips.subunit') - p = subprocess.Popen(['subunit-trace'], stdin=subprocess.PIPE) - with open(skips_stream, 'rb') as stream: - p.communicate(stream.read()) - self.assertEqual(1, p.returncode) - - def test_return_code_normal_run(self): - regular_stream = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'sample_streams/successful.subunit') - p = subprocess.Popen(['subunit-trace'], stdin=subprocess.PIPE) - with open(regular_stream, 'rb') as stream: - p.communicate(stream.read()) - self.assertEqual(0, p.returncode) - - def test_trace(self): - regular_stream = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'sample_streams/successful.subunit') - bytes_ = io.BytesIO() - with open(regular_stream, 'rb') as stream: - bytes_.write(six.binary_type(stream.read())) - bytes_.seek(0) - stdin = io.TextIOWrapper(io.BufferedReader(bytes_)) - returncode = subunit_trace.trace(stdin, sys.stdout) - self.assertEqual(0, returncode) diff --git a/os_testr/tests/utils/__init__.py b/os_testr/tests/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/os_testr/tests/utils/test_colorizer.py b/os_testr/tests/utils/test_colorizer.py deleted file mode 100644 index fdf96b2..0000000 --- a/os_testr/tests/utils/test_colorizer.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development LP -# 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. - -import six -import sys - -from ddt import data -from ddt import ddt -from ddt import unpack - -from os_testr.tests import base -from os_testr.utils import colorizer - - -@ddt -class TestNullColorizer(base.TestCase): - - @data(None, "foo", sys.stdout, ) - def test_supported_always_true(self, stream): - self.assertTrue(colorizer.NullColorizer.supported(stream)) - - @data(("foo", "red"), ("foo", "bar")) - @unpack - def test_write_string_ignore_color(self, text, color): - output = six.StringIO() - c = colorizer.NullColorizer(output) - c.write(text, color) - self.assertEqual(text, output.getvalue()) - - @data((None, "red"), (None, None)) - @unpack - def test_write_none_exception(self, text, color): - c = colorizer.NullColorizer(sys.stdout) - self.assertRaises(TypeError, c.write, text, color) - - -@ddt -class TestAnsiColorizer(base.TestCase): - - def test_supported_false(self): - # NOTE(masayukig): This returns False because our unittest env isn't - # interactive - self.assertFalse(colorizer.AnsiColorizer.supported(sys.stdout)) - - @data(None, "foo") - def test_supported_error(self, stream): - self.assertRaises(AttributeError, - colorizer.AnsiColorizer.supported, stream) - - @data(("foo", "red", "31"), ("foo", "blue", "34")) - @unpack - def test_write_string_valid_color(self, text, color, color_code): - output = six.StringIO() - c = colorizer.AnsiColorizer(output) - c.write(text, color) - self.assertIn(text, output.getvalue()) - self.assertIn(color_code, output.getvalue()) - - @data(("foo", None), ("foo", "invalid_color")) - @unpack - def test_write_string_invalid_color(self, text, color): - output = six.StringIO() - c = colorizer.AnsiColorizer(output) - self.assertRaises(KeyError, c.write, text, color) diff --git a/os_testr/utils/__init__.py b/os_testr/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/os_testr/utils/colorizer.py b/os_testr/utils/colorizer.py deleted file mode 100644 index 8ecec35..0000000 --- a/os_testr/utils/colorizer.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright 2015 NEC Corporation -# 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. -# -# Colorizer Code is borrowed from Twisted: -# Copyright (c) 2001-2010 Twisted Matrix Laboratories. -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -import sys - - -class AnsiColorizer(object): - """A colorizer is an object that loosely wraps around a stream - - allowing callers to write text to the stream in a particular color. - - Colorizer classes must implement C{supported()} and C{write(text, color)}. - """ - _colors = dict(black=30, red=31, green=32, yellow=33, - blue=34, magenta=35, cyan=36, white=37) - - def __init__(self, stream): - self.stream = stream - - @classmethod - def supported(cls, stream=sys.stdout): - """Check the current platform supports coloring terminal output - - A class method that returns True if the current platform supports - coloring terminal output using this method. Returns False otherwise. - """ - if not stream.isatty(): - return False # auto color only on TTYs - try: - import curses - except ImportError: - return False - else: - try: - try: - return curses.tigetnum("colors") > 2 - except curses.error: - curses.setupterm() - return curses.tigetnum("colors") > 2 - except Exception: - # guess false in case of error - return False - - def write(self, text, color): - """Write the given text to the stream in the given color. - - @param text: Text to be written to the stream. - - @param color: A string label for a color. e.g. 'red', 'white'. - """ - color = self._colors[color] - self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text)) - - -class NullColorizer(object): - """See _AnsiColorizer docstring.""" - def __init__(self, stream): - self.stream = stream - - @classmethod - def supported(cls, stream=sys.stdout): - return True - - def write(self, text, color): - self.stream.write(text) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index eefcfb4..0000000 --- a/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -pbr>=1.6 # Apache-2.0 -Babel>=2.3.4 # BSD -testrepository>=0.0.18 # Apache-2.0/BSD -python-subunit>=0.0.18 # Apache-2.0/BSD -testtools>=1.4.0 # MIT diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 27f9ced..0000000 --- a/setup.cfg +++ /dev/null @@ -1,52 +0,0 @@ -[metadata] -name = os-testr -summary = A testr wrapper to provide functionality for OpenStack projects -description-file = - README.rst -author = OpenStack -author-email = openstack-dev@lists.openstack.org -home-page = http://www.openstack.org/ -classifier = - Environment :: OpenStack - Intended Audience :: Information Technology - Intended Audience :: System Administrators - License :: OSI Approved :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 - -[files] -packages = - os_testr - -[entry_points] -console_scripts = - subunit-trace = os_testr.subunit_trace:main - ostestr = os_testr.ostestr:main - subunit2html = os_testr.subunit2html:main - generate-subunit = os_testr.generate_subunit:main - -[build_sphinx] -source-dir = doc/source -build-dir = doc/build -all_files = 1 - -[upload_sphinx] -upload-dir = doc/build/html - -[compile_catalog] -directory = os_testr/locale -domain = os-testr - -[update_catalog] -domain = os-testr -output_dir = os_testr/locale -input_file = os_testr/locale/os-testr.pot - -[extract_messages] -keywords = _ gettext ngettext l_ lazy_gettext -mapping_file = babel.cfg -output_file = os_testr/locale/os-testr.pot diff --git a/setup.py b/setup.py deleted file mode 100644 index 782bb21..0000000 --- a/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# 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. - -# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT -import setuptools - -# In python < 2.7.4, a lazy loading of package `pbr` will break -# setuptools if some other modules registered functions in `atexit`. -# solution from: http://bugs.python.org/issue15881#msg170215 -try: - import multiprocessing # noqa -except ImportError: - pass - -setuptools.setup( - setup_requires=['pbr>=1.8'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index bd63ab4..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -hacking<0.11,>=0.10.2 # Apache-2.0 - -coverage>=3.6 # Apache-2.0 -discover # BSD -sphinx!=1.3b1,<1.3,>=1.2.1 # BSD -oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 -oslotest>=1.10.0 # Apache-2.0 -testscenarios>=0.4 # Apache-2.0/BSD -ddt>=1.0.1 # MIT -six>=1.9.0 # MIT diff --git a/tox.ini b/tox.ini deleted file mode 100644 index c052dcb..0000000 --- a/tox.ini +++ /dev/null @@ -1,39 +0,0 @@ -[tox] -minversion = 1.6 -envlist = py34,py27,pypy,pep8 -skipsdist = True - -[testenv] -usedevelop = True -install_command = pip install -U {opts} {packages} -setenv = - VIRTUAL_ENV={envdir} -whitelist_externals = find -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -commands = - find . -type f -name "*.pyc" -delete - ostestr {posargs} - -[testenv:pep8] -commands = flake8 - -[testenv:venv] -commands = {posargs} - -[testenv:cover] -commands = python setup.py test --coverage --coverage-package-name='os_testr' --testr-args='{posargs}' - -[testenv:docs] -commands = python setup.py build_sphinx - -[testenv:debug] -commands = oslo_debug_helper {posargs} - -[flake8] -# E123, E125 skipped as they are invalid PEP-8. - -show-source = True -ignore = E123,E125 -builtins = _ -exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build