Bug 1928709 - Remove 'cram' test harness from the tree, r=jmaher,mach-reviewers,ahochheiden
This is no longer being used and the upstream project is unmaintained. Differential Revision: https://phabricator.services.mozilla.com/D227869
This commit is contained in:
@@ -85,7 +85,6 @@ MACH_COMMANDS = {
|
||||
),
|
||||
"configure": MachCommandReference("python/mozbuild/mozbuild/build_commands.py"),
|
||||
"cppunittest": MachCommandReference("testing/mach_commands.py"),
|
||||
"cramtest": MachCommandReference("testing/mach_commands.py"),
|
||||
"crashtest": MachCommandReference("layout/tools/reftest/mach_commands.py"),
|
||||
"data-review": MachCommandReference(
|
||||
"toolkit/components/glean/build_scripts/mach_commands.py"
|
||||
|
||||
@@ -2247,12 +2247,6 @@ VARIABLES = {
|
||||
"""List of manifest files defining MozPerftest performance tests.
|
||||
""",
|
||||
),
|
||||
"CRAMTEST_MANIFESTS": (
|
||||
ManifestparserManifestList,
|
||||
list,
|
||||
"""List of manifest files defining cram unit tests.
|
||||
""",
|
||||
),
|
||||
"TELEMETRY_TESTS_CLIENT_MANIFESTS": (
|
||||
ManifestparserManifestList,
|
||||
list,
|
||||
|
||||
@@ -41,7 +41,6 @@ TEST_MANIFESTS = dict(
|
||||
FIREFOX_UI_FUNCTIONAL=("firefox-ui-functional", "firefox-ui", ".", False),
|
||||
FIREFOX_UI_UPDATE=("firefox-ui-update", "firefox-ui", ".", False),
|
||||
PYTHON_UNITTEST=("python", "python", ".", False),
|
||||
CRAMTEST=("cram", "cram", ".", False),
|
||||
TELEMETRY_TESTS_CLIENT=(
|
||||
"telemetry-tests-client",
|
||||
"toolkit/components/telemetry/tests/marionette/",
|
||||
|
||||
@@ -20,7 +20,6 @@ vendored:third_party/python/charset_normalizer
|
||||
vendored:third_party/python/compare_locales
|
||||
vendored:third_party/python/cookiecutter
|
||||
vendored:third_party/python/cookies
|
||||
vendored:third_party/python/cram
|
||||
vendored:third_party/python/diskcache
|
||||
vendored:third_party/python/dlmanager
|
||||
vendored:third_party/python/ecdsa
|
||||
|
||||
@@ -28,7 +28,6 @@ vendored:third_party/python/charset_normalizer
|
||||
vendored:third_party/python/compare_locales
|
||||
vendored:third_party/python/cookiecutter
|
||||
vendored:third_party/python/cookies
|
||||
vendored:third_party/python/cram
|
||||
vendored:third_party/python/diskcache
|
||||
vendored:third_party/python/dlmanager
|
||||
vendored:third_party/python/ecdsa
|
||||
|
||||
@@ -20,7 +20,6 @@ vendored:third_party/python/charset_normalizer
|
||||
vendored:third_party/python/compare_locales
|
||||
vendored:third_party/python/cookiecutter
|
||||
vendored:third_party/python/cookies
|
||||
vendored:third_party/python/cram
|
||||
vendored:third_party/python/diskcache
|
||||
vendored:third_party/python/dlmanager
|
||||
vendored:third_party/python/ecdsa
|
||||
|
||||
@@ -7,7 +7,6 @@ project-repo-param-prefix: ''
|
||||
product-dir: 'browser'
|
||||
treeherder:
|
||||
group-names:
|
||||
'cram': 'Cram tests'
|
||||
'js-bench-sm': 'JavaScript shell benchmarks with Spidermonkey'
|
||||
'js-bench-v8': 'JavaScript shell benchmarks with Google V8'
|
||||
'node': 'Node tests'
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import date, timedelta
|
||||
|
||||
import requests
|
||||
from mach.decorators import Command, CommandArgument, SubCommand
|
||||
@@ -716,57 +716,6 @@ def run_jsshelltests(command_context, **kwargs):
|
||||
return benchmark.run(**kwargs)
|
||||
|
||||
|
||||
@Command(
|
||||
"cramtest",
|
||||
category="testing",
|
||||
description="Mercurial style .t tests for command line applications.",
|
||||
)
|
||||
@CommandArgument(
|
||||
"test_paths",
|
||||
nargs="*",
|
||||
metavar="N",
|
||||
help="Test paths to run. Each path can be a test file or directory. "
|
||||
"If omitted, the entire suite will be run.",
|
||||
)
|
||||
@CommandArgument(
|
||||
"cram_args",
|
||||
nargs=argparse.REMAINDER,
|
||||
help="Extra arguments to pass down to the cram binary. See "
|
||||
"'./mach python -m cram -- -h' for a list of available options.",
|
||||
)
|
||||
def cramtest(command_context, cram_args=None, test_paths=None, test_objects=None):
|
||||
command_context.activate_virtualenv()
|
||||
import mozinfo
|
||||
from manifestparser import TestManifest
|
||||
|
||||
if test_objects is None:
|
||||
from moztest.resolve import TestResolver
|
||||
|
||||
resolver = command_context._spawn(TestResolver)
|
||||
if test_paths:
|
||||
# If we were given test paths, try to find tests matching them.
|
||||
test_objects = resolver.resolve_tests(paths=test_paths, flavor="cram")
|
||||
else:
|
||||
# Otherwise just run everything in CRAMTEST_MANIFESTS
|
||||
test_objects = resolver.resolve_tests(flavor="cram")
|
||||
|
||||
if not test_objects:
|
||||
message = "No tests were collected, check spelling of the test paths."
|
||||
command_context.log(logging.WARN, "cramtest", {}, message)
|
||||
return 1
|
||||
|
||||
mp = TestManifest()
|
||||
mp.tests.extend(test_objects)
|
||||
tests = mp.active_tests(disabled=False, **mozinfo.info)
|
||||
|
||||
python = command_context.virtualenv_manager.python_path
|
||||
cmd = [python, "-m", "cram"] + cram_args + [t["relpath"] for t in tests]
|
||||
return subprocess.call(cmd, cwd=command_context.topsrcdir)
|
||||
|
||||
|
||||
from datetime import date, timedelta
|
||||
|
||||
|
||||
@Command(
|
||||
"test-info", category="testing", description="Display historical test results."
|
||||
)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!python
|
||||
import sys
|
||||
|
||||
import cram
|
||||
|
||||
try:
|
||||
sys.exit(cram.main(sys.argv[1:]))
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
@@ -1,227 +0,0 @@
|
||||
======================
|
||||
Cram: It's test time
|
||||
======================
|
||||
|
||||
Cram is a functional testing framework for command line applications.
|
||||
Cram tests look like snippets of interactive shell sessions. Cram runs
|
||||
each command and compares the command output in the test with the
|
||||
command's actual output.
|
||||
|
||||
Here's a snippet from `Cram's own test suite`_::
|
||||
|
||||
The $PYTHON environment variable should be set when running this test
|
||||
from Python.
|
||||
|
||||
$ [ -n "$PYTHON" ] || PYTHON="`which python`"
|
||||
$ [ -n "$PYTHONPATH" ] || PYTHONPATH="$TESTDIR/.." && export PYTHONPATH
|
||||
$ if [ -n "$COVERAGE" ]; then
|
||||
> coverage erase
|
||||
> alias cram="`which coverage` run --branch -a $TESTDIR/../scripts/cram"
|
||||
> else
|
||||
> alias cram="$PYTHON $TESTDIR/../scripts/cram"
|
||||
> fi
|
||||
$ command -v md5 > /dev/null || alias md5=md5sum
|
||||
|
||||
Usage:
|
||||
|
||||
$ cram -h
|
||||
[Uu]sage: cram \[OPTIONS\] TESTS\.\.\. (re)
|
||||
|
||||
[Oo]ptions: (re)
|
||||
-h, --help show this help message and exit
|
||||
-V, --version show version information and exit
|
||||
-q, --quiet don't print diffs
|
||||
-v, --verbose show filenames and test status
|
||||
-i, --interactive interactively merge changed test output
|
||||
-d, --debug write script output directly to the terminal
|
||||
-y, --yes answer yes to all questions
|
||||
-n, --no answer no to all questions
|
||||
-E, --preserve-env don't reset common environment variables
|
||||
--keep-tmpdir keep temporary directories
|
||||
--shell=PATH shell to use for running tests (default: /bin/sh)
|
||||
--shell-opts=OPTS arguments to invoke shell with
|
||||
--indent=NUM number of spaces to use for indentation (default: 2)
|
||||
--xunit-file=PATH path to write xUnit XML output
|
||||
|
||||
The format in a nutshell:
|
||||
|
||||
* Cram tests use the ``.t`` file extension.
|
||||
|
||||
* Lines beginning with two spaces, a dollar sign, and a space are run
|
||||
in the shell.
|
||||
|
||||
* Lines beginning with two spaces, a greater than sign, and a space
|
||||
allow multi-line commands.
|
||||
|
||||
* All other lines beginning with two spaces are considered command
|
||||
output.
|
||||
|
||||
* Output lines ending with a space and the keyword ``(re)`` are
|
||||
matched as `Perl-compatible regular expressions`_.
|
||||
|
||||
* Lines ending with a space and the keyword ``(glob)`` are matched
|
||||
with a glob-like syntax. The only special characters supported are
|
||||
``*`` and ``?``. Both characters can be escaped using ``\``, and the
|
||||
backslash can be escaped itself.
|
||||
|
||||
* Output lines ending with either of the above keywords are always
|
||||
first matched literally with actual command output.
|
||||
|
||||
* Lines ending with a space and the keyword ``(no-eol)`` will match
|
||||
actual output that doesn't end in a newline.
|
||||
|
||||
* Actual output lines containing unprintable characters are escaped
|
||||
and suffixed with a space and the keyword ``(esc)``. Lines matching
|
||||
unprintable output must also contain the keyword.
|
||||
|
||||
* Anything else is a comment.
|
||||
|
||||
.. _Cram's own test suite: https://bitbucket.org/brodie/cram/src/default/tests/cram.t
|
||||
.. _Perl-compatible regular expressions: https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions
|
||||
|
||||
|
||||
Download
|
||||
--------
|
||||
|
||||
* `cram-0.7.tar.gz`_ (32 KB, requires Python 2.4-2.7 or Python 3.1 or newer)
|
||||
|
||||
.. _cram-0.7.tar.gz: https://bitheap.org/cram/cram-0.7.tar.gz
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install Cram using make::
|
||||
|
||||
$ wget https://bitheap.org/cram/cram-0.7.tar.gz
|
||||
$ tar zxvf cram-0.7.tar.gz
|
||||
$ cd cram-0.7
|
||||
$ make install
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Cram will print a dot for each passing test. If a test fails, a
|
||||
`unified context diff`_ is printed showing the test's expected output
|
||||
and the actual output. Skipped tests (empty tests and tests that exit
|
||||
with return code ``80``) are marked with ``s`` instead of a dot.
|
||||
|
||||
For example, if we run Cram on `its own example tests`_::
|
||||
|
||||
.s.!
|
||||
--- examples/fail.t
|
||||
+++ examples/fail.t.err
|
||||
@@ -3,21 +3,22 @@
|
||||
$ echo 1
|
||||
1
|
||||
$ echo 1
|
||||
- 2
|
||||
+ 1
|
||||
$ echo 1
|
||||
1
|
||||
|
||||
Invalid regex:
|
||||
|
||||
$ echo 1
|
||||
- +++ (re)
|
||||
+ 1
|
||||
|
||||
Offset regular expression:
|
||||
|
||||
$ printf 'foo\nbar\nbaz\n\n1\nA\n@\n'
|
||||
foo
|
||||
+ bar
|
||||
baz
|
||||
|
||||
\d (re)
|
||||
[A-Z] (re)
|
||||
- #
|
||||
+ @
|
||||
s.
|
||||
# Ran 6 tests, 2 skipped, 1 failed.
|
||||
|
||||
Cram will also write the test with its actual output to
|
||||
``examples/fail.t.err``, allowing you to use other diff tools. This
|
||||
file is automatically removed the next time the test passes.
|
||||
|
||||
When you're first writing a test, you might just write the commands
|
||||
and run the test to see what happens. If you run Cram with ``-i`` or
|
||||
``--interactive``, you'll be prompted to merge the actual output back
|
||||
into the test. This makes it easy to quickly prototype new tests.
|
||||
|
||||
You can specify a default set of options by creating a ``.cramrc``
|
||||
file. For example::
|
||||
|
||||
[cram]
|
||||
verbose = True
|
||||
indent = 4
|
||||
|
||||
Is the same as invoking Cram with ``--verbose`` and ``--indent=4``.
|
||||
|
||||
To change what configuration file Cram loads, you can set the
|
||||
``CRAMRC`` environment variable. You can also specify command line
|
||||
options in the ``CRAM`` environment variable.
|
||||
|
||||
Note that the following environment variables are reset before tests
|
||||
are run:
|
||||
|
||||
* ``TMPDIR``, ``TEMP``, and ``TMP`` are set to the test runner's
|
||||
``tmp`` directory.
|
||||
|
||||
* ``LANG``, ``LC_ALL``, and ``LANGUAGE`` are set to ``C``.
|
||||
|
||||
* ``TZ`` is set to ``GMT``.
|
||||
|
||||
* ``COLUMNS`` is set to ``80``. (Note: When using ``--shell=zsh``,
|
||||
this cannot be reset. It will reflect the actual terminal's width.)
|
||||
|
||||
* ``CDPATH`` and ``GREP_OPTIONS`` are set to an empty string.
|
||||
|
||||
Cram also provides the following environment variables to tests:
|
||||
|
||||
* ``CRAMTMP``, set to the test runner's temporary directory.
|
||||
|
||||
* ``TESTDIR``, set to the directory containing the test file.
|
||||
|
||||
* ``TESTFILE``, set to the basename of the current test file.
|
||||
|
||||
* ``TESTSHELL``, set to the value specified by ``--shell``.
|
||||
|
||||
Also note that care should be taken with commands that close the test
|
||||
shell's ``stdin``. For example, if you're trying to invoke ``ssh`` in
|
||||
a test, try adding the ``-n`` option to prevent it from closing
|
||||
``stdin``. Similarly, if you invoke a daemon process that inherits
|
||||
``stdout`` and fails to close it, it may cause Cram to hang while
|
||||
waiting for the test shell's ``stdout`` to be fully closed.
|
||||
|
||||
.. _unified context diff: https://en.wikipedia.org/wiki/Diff#Unified_format
|
||||
.. _its own example tests: https://bitbucket.org/brodie/cram/src/default/examples/
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
Download the official development repository using Mercurial_::
|
||||
|
||||
hg clone https://bitbucket.org/brodie/cram
|
||||
|
||||
Or Git_::
|
||||
|
||||
git clone https://github.com/brodie/cram.git
|
||||
|
||||
Test Cram using Cram::
|
||||
|
||||
pip install -r requirements.txt
|
||||
make test
|
||||
|
||||
Visit Bitbucket_ or GitHub_ if you'd like to fork the project, watch
|
||||
for new changes, or report issues.
|
||||
|
||||
.. _Mercurial: http://mercurial.selenic.com/
|
||||
.. _Git: http://git-scm.com/
|
||||
.. _coverage.py: http://nedbatchelder.com/code/coverage/
|
||||
.. _Bitbucket: https://bitbucket.org/brodie/cram
|
||||
.. _GitHub: https://github.com/brodie/cram
|
||||
|
||||
|
||||
250
third_party/python/cram/cram-0.7.dist-info/METADATA
vendored
250
third_party/python/cram/cram-0.7.dist-info/METADATA
vendored
@@ -1,250 +0,0 @@
|
||||
Metadata-Version: 2.0
|
||||
Name: cram
|
||||
Version: 0.7
|
||||
Summary: A simple testing framework for command line applications
|
||||
Home-page: https://bitheap.org/cram/
|
||||
Author: Brodie Rao
|
||||
Author-email: brodie@bitheap.org
|
||||
License: GNU GPLv2 or any later version
|
||||
Download-URL: https://bitheap.org/cram/cram-0.7.tar.gz
|
||||
Keywords: automatic functional test framework
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Console
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: GNU General Public License (GPL)
|
||||
Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
|
||||
Classifier: Natural Language :: English
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Unix Shell
|
||||
Classifier: Topic :: Software Development :: Testing
|
||||
|
||||
======================
|
||||
Cram: It's test time
|
||||
======================
|
||||
|
||||
Cram is a functional testing framework for command line applications.
|
||||
Cram tests look like snippets of interactive shell sessions. Cram runs
|
||||
each command and compares the command output in the test with the
|
||||
command's actual output.
|
||||
|
||||
Here's a snippet from `Cram's own test suite`_::
|
||||
|
||||
The $PYTHON environment variable should be set when running this test
|
||||
from Python.
|
||||
|
||||
$ [ -n "$PYTHON" ] || PYTHON="`which python`"
|
||||
$ [ -n "$PYTHONPATH" ] || PYTHONPATH="$TESTDIR/.." && export PYTHONPATH
|
||||
$ if [ -n "$COVERAGE" ]; then
|
||||
> coverage erase
|
||||
> alias cram="`which coverage` run --branch -a $TESTDIR/../scripts/cram"
|
||||
> else
|
||||
> alias cram="$PYTHON $TESTDIR/../scripts/cram"
|
||||
> fi
|
||||
$ command -v md5 > /dev/null || alias md5=md5sum
|
||||
|
||||
Usage:
|
||||
|
||||
$ cram -h
|
||||
[Uu]sage: cram \[OPTIONS\] TESTS\.\.\. (re)
|
||||
|
||||
[Oo]ptions: (re)
|
||||
-h, --help show this help message and exit
|
||||
-V, --version show version information and exit
|
||||
-q, --quiet don't print diffs
|
||||
-v, --verbose show filenames and test status
|
||||
-i, --interactive interactively merge changed test output
|
||||
-d, --debug write script output directly to the terminal
|
||||
-y, --yes answer yes to all questions
|
||||
-n, --no answer no to all questions
|
||||
-E, --preserve-env don't reset common environment variables
|
||||
--keep-tmpdir keep temporary directories
|
||||
--shell=PATH shell to use for running tests (default: /bin/sh)
|
||||
--shell-opts=OPTS arguments to invoke shell with
|
||||
--indent=NUM number of spaces to use for indentation (default: 2)
|
||||
--xunit-file=PATH path to write xUnit XML output
|
||||
|
||||
The format in a nutshell:
|
||||
|
||||
* Cram tests use the ``.t`` file extension.
|
||||
|
||||
* Lines beginning with two spaces, a dollar sign, and a space are run
|
||||
in the shell.
|
||||
|
||||
* Lines beginning with two spaces, a greater than sign, and a space
|
||||
allow multi-line commands.
|
||||
|
||||
* All other lines beginning with two spaces are considered command
|
||||
output.
|
||||
|
||||
* Output lines ending with a space and the keyword ``(re)`` are
|
||||
matched as `Perl-compatible regular expressions`_.
|
||||
|
||||
* Lines ending with a space and the keyword ``(glob)`` are matched
|
||||
with a glob-like syntax. The only special characters supported are
|
||||
``*`` and ``?``. Both characters can be escaped using ``\``, and the
|
||||
backslash can be escaped itself.
|
||||
|
||||
* Output lines ending with either of the above keywords are always
|
||||
first matched literally with actual command output.
|
||||
|
||||
* Lines ending with a space and the keyword ``(no-eol)`` will match
|
||||
actual output that doesn't end in a newline.
|
||||
|
||||
* Actual output lines containing unprintable characters are escaped
|
||||
and suffixed with a space and the keyword ``(esc)``. Lines matching
|
||||
unprintable output must also contain the keyword.
|
||||
|
||||
* Anything else is a comment.
|
||||
|
||||
.. _Cram's own test suite: https://bitbucket.org/brodie/cram/src/default/tests/cram.t
|
||||
.. _Perl-compatible regular expressions: https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions
|
||||
|
||||
|
||||
Download
|
||||
--------
|
||||
|
||||
* `cram-0.7.tar.gz`_ (32 KB, requires Python 2.4-2.7 or Python 3.1 or newer)
|
||||
|
||||
.. _cram-0.7.tar.gz: https://bitheap.org/cram/cram-0.7.tar.gz
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install Cram using make::
|
||||
|
||||
$ wget https://bitheap.org/cram/cram-0.7.tar.gz
|
||||
$ tar zxvf cram-0.7.tar.gz
|
||||
$ cd cram-0.7
|
||||
$ make install
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Cram will print a dot for each passing test. If a test fails, a
|
||||
`unified context diff`_ is printed showing the test's expected output
|
||||
and the actual output. Skipped tests (empty tests and tests that exit
|
||||
with return code ``80``) are marked with ``s`` instead of a dot.
|
||||
|
||||
For example, if we run Cram on `its own example tests`_::
|
||||
|
||||
.s.!
|
||||
--- examples/fail.t
|
||||
+++ examples/fail.t.err
|
||||
@@ -3,21 +3,22 @@
|
||||
$ echo 1
|
||||
1
|
||||
$ echo 1
|
||||
- 2
|
||||
+ 1
|
||||
$ echo 1
|
||||
1
|
||||
|
||||
Invalid regex:
|
||||
|
||||
$ echo 1
|
||||
- +++ (re)
|
||||
+ 1
|
||||
|
||||
Offset regular expression:
|
||||
|
||||
$ printf 'foo\nbar\nbaz\n\n1\nA\n@\n'
|
||||
foo
|
||||
+ bar
|
||||
baz
|
||||
|
||||
\d (re)
|
||||
[A-Z] (re)
|
||||
- #
|
||||
+ @
|
||||
s.
|
||||
# Ran 6 tests, 2 skipped, 1 failed.
|
||||
|
||||
Cram will also write the test with its actual output to
|
||||
``examples/fail.t.err``, allowing you to use other diff tools. This
|
||||
file is automatically removed the next time the test passes.
|
||||
|
||||
When you're first writing a test, you might just write the commands
|
||||
and run the test to see what happens. If you run Cram with ``-i`` or
|
||||
``--interactive``, you'll be prompted to merge the actual output back
|
||||
into the test. This makes it easy to quickly prototype new tests.
|
||||
|
||||
You can specify a default set of options by creating a ``.cramrc``
|
||||
file. For example::
|
||||
|
||||
[cram]
|
||||
verbose = True
|
||||
indent = 4
|
||||
|
||||
Is the same as invoking Cram with ``--verbose`` and ``--indent=4``.
|
||||
|
||||
To change what configuration file Cram loads, you can set the
|
||||
``CRAMRC`` environment variable. You can also specify command line
|
||||
options in the ``CRAM`` environment variable.
|
||||
|
||||
Note that the following environment variables are reset before tests
|
||||
are run:
|
||||
|
||||
* ``TMPDIR``, ``TEMP``, and ``TMP`` are set to the test runner's
|
||||
``tmp`` directory.
|
||||
|
||||
* ``LANG``, ``LC_ALL``, and ``LANGUAGE`` are set to ``C``.
|
||||
|
||||
* ``TZ`` is set to ``GMT``.
|
||||
|
||||
* ``COLUMNS`` is set to ``80``. (Note: When using ``--shell=zsh``,
|
||||
this cannot be reset. It will reflect the actual terminal's width.)
|
||||
|
||||
* ``CDPATH`` and ``GREP_OPTIONS`` are set to an empty string.
|
||||
|
||||
Cram also provides the following environment variables to tests:
|
||||
|
||||
* ``CRAMTMP``, set to the test runner's temporary directory.
|
||||
|
||||
* ``TESTDIR``, set to the directory containing the test file.
|
||||
|
||||
* ``TESTFILE``, set to the basename of the current test file.
|
||||
|
||||
* ``TESTSHELL``, set to the value specified by ``--shell``.
|
||||
|
||||
Also note that care should be taken with commands that close the test
|
||||
shell's ``stdin``. For example, if you're trying to invoke ``ssh`` in
|
||||
a test, try adding the ``-n`` option to prevent it from closing
|
||||
``stdin``. Similarly, if you invoke a daemon process that inherits
|
||||
``stdout`` and fails to close it, it may cause Cram to hang while
|
||||
waiting for the test shell's ``stdout`` to be fully closed.
|
||||
|
||||
.. _unified context diff: https://en.wikipedia.org/wiki/Diff#Unified_format
|
||||
.. _its own example tests: https://bitbucket.org/brodie/cram/src/default/examples/
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
Download the official development repository using Mercurial_::
|
||||
|
||||
hg clone https://bitbucket.org/brodie/cram
|
||||
|
||||
Or Git_::
|
||||
|
||||
git clone https://github.com/brodie/cram.git
|
||||
|
||||
Test Cram using Cram::
|
||||
|
||||
pip install -r requirements.txt
|
||||
make test
|
||||
|
||||
Visit Bitbucket_ or GitHub_ if you'd like to fork the project, watch
|
||||
for new changes, or report issues.
|
||||
|
||||
.. _Mercurial: http://mercurial.selenic.com/
|
||||
.. _Git: http://git-scm.com/
|
||||
.. _coverage.py: http://nedbatchelder.com/code/coverage/
|
||||
.. _Bitbucket: https://bitbucket.org/brodie/cram
|
||||
.. _GitHub: https://github.com/brodie/cram
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
cram/__init__.py,sha256=80M3WLqeS6MAACoIZW89KZR4bOmFm7UcpoRPF6S-8jc,172
|
||||
cram/__main__.py,sha256=AUlczSWsDtiA6srk4dsmdsz8cZXb1QXMdPkobAR-Ex0,152
|
||||
cram/_cli.py,sha256=aIJE2BY0djuOqgtCHe9IVUIl7Vvvk-awsksdmMd1RNc,4345
|
||||
cram/_diff.py,sha256=pXLlKb1UgQX17ayJpPQsGoMHW7bKLcACe9KEZlnMkx0,5630
|
||||
cram/_encoding.py,sha256=PSPdcjenMvC0wabbPhWPkCxeUcohcQ6o3Rk58AC97Uo,2990
|
||||
cram/_main.py,sha256=5gwaBNSyKCq9bwkRLKqNXcsB5Okf0sfxDpousd51CO4,7728
|
||||
cram/_process.py,sha256=2JV6sRl_9p3DYu1IYN5_D-isln9vAh5ua6bAxAy8ytA,1805
|
||||
cram/_run.py,sha256=X5fOy7TKxMdBcis0JczYZkNUoQdJ5wUqlDCM2sRJDm0,2292
|
||||
cram/_test.py,sha256=9QYuf3DRuLs9O1QVP3MfoJlISBRfnC5ONhCL4uXGYG8,7904
|
||||
cram/_xunit.py,sha256=KUAUokY3HhkgPYp0IjSl2m7KvztYdbwW7p1aqdaUJgA,6247
|
||||
cram-0.7.data/scripts/cram,sha256=S3wCw9Ks2J4dtVftWZ8DU0eNtpb1ekf8Bz73Di3PvUs,112
|
||||
cram-0.7.dist-info/DESCRIPTION.rst,sha256=ejwfPio_dRLrZ2PhWnsGbLW6lPyiDTjUAejg5MPG-kg,7080
|
||||
cram-0.7.dist-info/METADATA,sha256=ExruW_6HNwqu-mVqvcCSUtund4CHxt5hb3019a3jLeo,8018
|
||||
cram-0.7.dist-info/RECORD,,
|
||||
cram-0.7.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
|
||||
cram-0.7.dist-info/metadata.json,sha256=cRTULRj1eXU8xWOtqLK8DMhu0vWJELulW_PI8O4ytPU,1063
|
||||
@@ -1,6 +0,0 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.29.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License (GPL)", "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Programming Language :: Unix Shell", "Topic :: Software Development :: Testing"], "download_url": "https://bitheap.org/cram/cram-0.7.tar.gz", "extensions": {"python.details": {"contacts": [{"email": "brodie@bitheap.org", "name": "Brodie Rao", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://bitheap.org/cram/"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["automatic", "functional", "test", "framework"], "license": "GNU GPLv2 or any later version", "metadata_version": "2.0", "name": "cram", "summary": "A simple testing framework for command line applications", "version": "0.7"}
|
||||
6
third_party/python/cram/cram/__init__.py
vendored
6
third_party/python/cram/cram/__init__.py
vendored
@@ -1,6 +0,0 @@
|
||||
"""Functional testing framework for command line applications"""
|
||||
|
||||
from cram._main import main
|
||||
from cram._test import test, testfile
|
||||
|
||||
__all__ = ['main', 'test', 'testfile']
|
||||
10
third_party/python/cram/cram/__main__.py
vendored
10
third_party/python/cram/cram/__main__.py
vendored
@@ -1,10 +0,0 @@
|
||||
"""Main module (invoked by "python -m cram")"""
|
||||
|
||||
import sys
|
||||
|
||||
import cram
|
||||
|
||||
try:
|
||||
sys.exit(cram.main(sys.argv[1:]))
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
134
third_party/python/cram/cram/_cli.py
vendored
134
third_party/python/cram/cram/_cli.py
vendored
@@ -1,134 +0,0 @@
|
||||
"""The command line interface implementation"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from cram._encoding import b, bytestype, stdoutb
|
||||
from cram._process import execute
|
||||
|
||||
__all__ = ['runcli']
|
||||
|
||||
def _prompt(question, answers, auto=None):
|
||||
"""Write a prompt to stdout and ask for answer in stdin.
|
||||
|
||||
answers should be a string, with each character a single
|
||||
answer. An uppercase letter is considered the default answer.
|
||||
|
||||
If an invalid answer is given, this asks again until it gets a
|
||||
valid one.
|
||||
|
||||
If auto is set, the question is answered automatically with the
|
||||
specified value.
|
||||
"""
|
||||
default = [c for c in answers if c.isupper()]
|
||||
while True:
|
||||
sys.stdout.write('%s [%s] ' % (question, answers))
|
||||
sys.stdout.flush()
|
||||
if auto is not None:
|
||||
sys.stdout.write(auto + '\n')
|
||||
sys.stdout.flush()
|
||||
return auto
|
||||
|
||||
answer = sys.stdin.readline().strip().lower()
|
||||
if not answer and default:
|
||||
return default[0]
|
||||
elif answer and answer in answers.lower():
|
||||
return answer
|
||||
|
||||
def _log(msg=None, verbosemsg=None, verbose=False):
|
||||
"""Write msg to standard out and flush.
|
||||
|
||||
If verbose is True, write verbosemsg instead.
|
||||
"""
|
||||
if verbose:
|
||||
msg = verbosemsg
|
||||
if msg:
|
||||
if isinstance(msg, bytestype):
|
||||
stdoutb.write(msg)
|
||||
else: # pragma: nocover
|
||||
sys.stdout.write(msg)
|
||||
sys.stdout.flush()
|
||||
|
||||
def _patch(cmd, diff):
|
||||
"""Run echo [lines from diff] | cmd -p0"""
|
||||
out, retcode = execute([cmd, '-p0'], stdin=b('').join(diff))
|
||||
return retcode == 0
|
||||
|
||||
def runcli(tests, quiet=False, verbose=False, patchcmd=None, answer=None):
|
||||
"""Run tests with command line interface input/output.
|
||||
|
||||
tests should be a sequence of 2-tuples containing the following:
|
||||
|
||||
(test path, test function)
|
||||
|
||||
This function yields a new sequence where each test function is wrapped
|
||||
with a function that handles CLI input/output.
|
||||
|
||||
If quiet is True, diffs aren't printed. If verbose is True,
|
||||
filenames and status information are printed.
|
||||
|
||||
If patchcmd is set, a prompt is written to stdout asking if
|
||||
changed output should be merged back into the original test. The
|
||||
answer is read from stdin. If 'y', the test is patched using patch
|
||||
based on the changed output.
|
||||
"""
|
||||
total, skipped, failed = [0], [0], [0]
|
||||
|
||||
for path, test in tests:
|
||||
def testwrapper():
|
||||
"""Test function that adds CLI output"""
|
||||
total[0] += 1
|
||||
_log(None, path + b(': '), verbose)
|
||||
|
||||
refout, postout, diff = test()
|
||||
if refout is None:
|
||||
skipped[0] += 1
|
||||
_log('s', 'empty\n', verbose)
|
||||
return refout, postout, diff
|
||||
|
||||
abspath = os.path.abspath(path)
|
||||
errpath = abspath + b('.err')
|
||||
|
||||
if postout is None:
|
||||
skipped[0] += 1
|
||||
_log('s', 'skipped\n', verbose)
|
||||
elif not diff:
|
||||
_log('.', 'passed\n', verbose)
|
||||
if os.path.exists(errpath):
|
||||
os.remove(errpath)
|
||||
else:
|
||||
failed[0] += 1
|
||||
_log('!', 'failed\n', verbose)
|
||||
if not quiet:
|
||||
_log('\n', None, verbose)
|
||||
|
||||
errfile = open(errpath, 'wb')
|
||||
try:
|
||||
for line in postout:
|
||||
errfile.write(line)
|
||||
finally:
|
||||
errfile.close()
|
||||
|
||||
if not quiet:
|
||||
origdiff = diff
|
||||
diff = []
|
||||
for line in origdiff:
|
||||
stdoutb.write(line)
|
||||
diff.append(line)
|
||||
|
||||
if (patchcmd and
|
||||
_prompt('Accept this change?', 'yN', answer) == 'y'):
|
||||
if _patch(patchcmd, diff):
|
||||
_log(None, path + b(': merged output\n'), verbose)
|
||||
os.remove(errpath)
|
||||
else:
|
||||
_log(path + b(': merge failed\n'))
|
||||
|
||||
return refout, postout, diff
|
||||
|
||||
yield (path, testwrapper)
|
||||
|
||||
if total[0] > 0:
|
||||
_log('\n', None, verbose)
|
||||
_log('# Ran %s tests, %s skipped, %s failed.\n'
|
||||
% (total[0], skipped[0], failed[0]))
|
||||
158
third_party/python/cram/cram/_diff.py
vendored
158
third_party/python/cram/cram/_diff.py
vendored
@@ -1,158 +0,0 @@
|
||||
"""Utilities for diffing test files and their output"""
|
||||
|
||||
import codecs
|
||||
import difflib
|
||||
import re
|
||||
|
||||
from cram._encoding import b
|
||||
|
||||
__all__ = ['esc', 'glob', 'regex', 'unified_diff']
|
||||
|
||||
def _regex(pattern, s):
|
||||
"""Match a regular expression or return False if invalid.
|
||||
|
||||
>>> from cram._encoding import b
|
||||
>>> [bool(_regex(r, b('foobar'))) for r in (b('foo.*'), b('***'))]
|
||||
[True, False]
|
||||
"""
|
||||
try:
|
||||
return re.match(pattern + b(r'\Z'), s)
|
||||
except re.error:
|
||||
return False
|
||||
|
||||
def _glob(el, l):
|
||||
r"""Match a glob-like pattern.
|
||||
|
||||
The only supported special characters are * and ?. Escaping is
|
||||
supported.
|
||||
|
||||
>>> from cram._encoding import b
|
||||
>>> bool(_glob(b(r'\* \\ \? fo?b*'), b('* \\ ? foobar')))
|
||||
True
|
||||
"""
|
||||
i, n = 0, len(el)
|
||||
res = b('')
|
||||
while i < n:
|
||||
c = el[i:i + 1]
|
||||
i += 1
|
||||
if c == b('\\') and el[i] in b('*?\\'):
|
||||
res += el[i - 1:i + 1]
|
||||
i += 1
|
||||
elif c == b('*'):
|
||||
res += b('.*')
|
||||
elif c == b('?'):
|
||||
res += b('.')
|
||||
else:
|
||||
res += re.escape(c)
|
||||
return _regex(res, l)
|
||||
|
||||
def _matchannotation(keyword, matchfunc, el, l):
|
||||
"""Apply match function based on annotation keyword"""
|
||||
ann = b(' (%s)\n' % keyword)
|
||||
return el.endswith(ann) and matchfunc(el[:-len(ann)], l[:-1])
|
||||
|
||||
def regex(el, l):
|
||||
"""Apply a regular expression match to a line annotated with '(re)'"""
|
||||
return _matchannotation('re', _regex, el, l)
|
||||
|
||||
def glob(el, l):
|
||||
"""Apply a glob match to a line annotated with '(glob)'"""
|
||||
return _matchannotation('glob', _glob, el, l)
|
||||
|
||||
def esc(el, l):
|
||||
"""Apply an escape match to a line annotated with '(esc)'"""
|
||||
ann = b(' (esc)\n')
|
||||
|
||||
if el.endswith(ann):
|
||||
el = codecs.escape_decode(el[:-len(ann)])[0] + b('\n')
|
||||
if el == l:
|
||||
return True
|
||||
|
||||
if l.endswith(ann):
|
||||
l = codecs.escape_decode(l[:-len(ann)])[0] + b('\n')
|
||||
return el == l
|
||||
|
||||
class _SequenceMatcher(difflib.SequenceMatcher, object):
|
||||
"""Like difflib.SequenceMatcher, but supports custom match functions"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._matchers = kwargs.pop('matchers', [])
|
||||
super(_SequenceMatcher, self).__init__(*args, **kwargs)
|
||||
|
||||
def _match(self, el, l):
|
||||
"""Tests for matching lines using custom matchers"""
|
||||
for matcher in self._matchers:
|
||||
if matcher(el, l):
|
||||
return True
|
||||
return False
|
||||
|
||||
def find_longest_match(self, alo, ahi, blo, bhi):
|
||||
"""Find longest matching block in a[alo:ahi] and b[blo:bhi]"""
|
||||
# SequenceMatcher uses find_longest_match() to slowly whittle down
|
||||
# the differences between a and b until it has each matching block.
|
||||
# Because of this, we can end up doing the same matches many times.
|
||||
matches = []
|
||||
for n, (el, line) in enumerate(zip(self.a[alo:ahi], self.b[blo:bhi])):
|
||||
if el != line and self._match(el, line):
|
||||
# This fools the superclass's method into thinking that the
|
||||
# regex/glob in a is identical to b by replacing a's line (the
|
||||
# expected output) with b's line (the actual output).
|
||||
self.a[alo + n] = line
|
||||
matches.append((n, el))
|
||||
ret = super(_SequenceMatcher, self).find_longest_match(alo, ahi,
|
||||
blo, bhi)
|
||||
# Restore the lines replaced above. Otherwise, the diff output
|
||||
# would seem to imply that the tests never had any regexes/globs.
|
||||
for n, el in matches:
|
||||
self.a[alo + n] = el
|
||||
return ret
|
||||
|
||||
def unified_diff(l1, l2, fromfile=b(''), tofile=b(''), fromfiledate=b(''),
|
||||
tofiledate=b(''), n=3, lineterm=b('\n'), matchers=None):
|
||||
r"""Compare two sequences of lines; generate the delta as a unified diff.
|
||||
|
||||
This is like difflib.unified_diff(), but allows custom matchers.
|
||||
|
||||
>>> from cram._encoding import b
|
||||
>>> l1 = [b('a\n'), b('? (glob)\n')]
|
||||
>>> l2 = [b('a\n'), b('b\n')]
|
||||
>>> (list(unified_diff(l1, l2, b('f1'), b('f2'), b('1970-01-01'),
|
||||
... b('1970-01-02'))) ==
|
||||
... [b('--- f1\t1970-01-01\n'), b('+++ f2\t1970-01-02\n'),
|
||||
... b('@@ -1,2 +1,2 @@\n'), b(' a\n'), b('-? (glob)\n'), b('+b\n')])
|
||||
True
|
||||
|
||||
>>> from cram._diff import glob
|
||||
>>> list(unified_diff(l1, l2, matchers=[glob]))
|
||||
[]
|
||||
"""
|
||||
if matchers is None:
|
||||
matchers = []
|
||||
started = False
|
||||
matcher = _SequenceMatcher(None, l1, l2, matchers=matchers)
|
||||
for group in matcher.get_grouped_opcodes(n):
|
||||
if not started:
|
||||
if fromfiledate:
|
||||
fromdate = b('\t') + fromfiledate
|
||||
else:
|
||||
fromdate = b('')
|
||||
if tofiledate:
|
||||
todate = b('\t') + tofiledate
|
||||
else:
|
||||
todate = b('')
|
||||
yield b('--- ') + fromfile + fromdate + lineterm
|
||||
yield b('+++ ') + tofile + todate + lineterm
|
||||
started = True
|
||||
i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4]
|
||||
yield (b("@@ -%d,%d +%d,%d @@" % (i1 + 1, i2 - i1, j1 + 1, j2 - j1)) +
|
||||
lineterm)
|
||||
for tag, i1, i2, j1, j2 in group:
|
||||
if tag == 'equal':
|
||||
for line in l1[i1:i2]:
|
||||
yield b(' ') + line
|
||||
continue
|
||||
if tag == 'replace' or tag == 'delete':
|
||||
for line in l1[i1:i2]:
|
||||
yield b('-') + line
|
||||
if tag == 'replace' or tag == 'insert':
|
||||
for line in l2[j1:j2]:
|
||||
yield b('+') + line
|
||||
106
third_party/python/cram/cram/_encoding.py
vendored
106
third_party/python/cram/cram/_encoding.py
vendored
@@ -1,106 +0,0 @@
|
||||
"""Encoding utilities"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import builtins
|
||||
except ImportError:
|
||||
import __builtin__ as builtins
|
||||
|
||||
__all__ = ['b', 'bchr', 'bytestype', 'envencode', 'fsdecode', 'fsencode',
|
||||
'stdoutb', 'stderrb', 'u', 'ul', 'unicodetype']
|
||||
|
||||
bytestype = getattr(builtins, 'bytes', str)
|
||||
unicodetype = getattr(builtins, 'unicode', str)
|
||||
|
||||
if getattr(os, 'fsdecode', None) is not None:
|
||||
fsdecode = os.fsdecode
|
||||
fsencode = os.fsencode
|
||||
elif bytestype is not str:
|
||||
if sys.platform == 'win32':
|
||||
def fsdecode(s):
|
||||
"""Decode a filename from the filesystem encoding"""
|
||||
if isinstance(s, unicodetype):
|
||||
return s
|
||||
encoding = sys.getfilesystemencoding()
|
||||
if encoding == 'mbcs':
|
||||
return s.decode(encoding)
|
||||
else:
|
||||
return s.decode(encoding, 'surrogateescape')
|
||||
|
||||
def fsencode(s):
|
||||
"""Encode a filename to the filesystem encoding"""
|
||||
if isinstance(s, bytestype):
|
||||
return s
|
||||
encoding = sys.getfilesystemencoding()
|
||||
if encoding == 'mbcs':
|
||||
return s.encode(encoding)
|
||||
else:
|
||||
return s.encode(encoding, 'surrogateescape')
|
||||
else:
|
||||
def fsdecode(s):
|
||||
"""Decode a filename from the filesystem encoding"""
|
||||
if isinstance(s, unicodetype):
|
||||
return s
|
||||
return s.decode(sys.getfilesystemencoding(), 'surrogateescape')
|
||||
|
||||
def fsencode(s):
|
||||
"""Encode a filename to the filesystem encoding"""
|
||||
if isinstance(s, bytestype):
|
||||
return s
|
||||
return s.encode(sys.getfilesystemencoding(), 'surrogateescape')
|
||||
else:
|
||||
def fsdecode(s):
|
||||
"""Decode a filename from the filesystem encoding"""
|
||||
return s
|
||||
|
||||
def fsencode(s):
|
||||
"""Encode a filename to the filesystem encoding"""
|
||||
return s
|
||||
|
||||
if bytestype is str:
|
||||
def envencode(s):
|
||||
"""Encode a byte string to the os.environ encoding"""
|
||||
return s
|
||||
else:
|
||||
envencode = fsdecode
|
||||
|
||||
if getattr(sys.stdout, 'buffer', None) is not None:
|
||||
stdoutb = sys.stdout.buffer
|
||||
stderrb = sys.stderr.buffer
|
||||
else:
|
||||
stdoutb = sys.stdout
|
||||
stderrb = sys.stderr
|
||||
|
||||
if bytestype is str:
|
||||
def b(s):
|
||||
"""Convert an ASCII string literal into a bytes object"""
|
||||
return s
|
||||
|
||||
bchr = chr
|
||||
|
||||
def u(s):
|
||||
"""Convert an ASCII string literal into a unicode object"""
|
||||
return s.decode('ascii')
|
||||
else:
|
||||
def b(s):
|
||||
"""Convert an ASCII string literal into a bytes object"""
|
||||
return s.encode('ascii')
|
||||
|
||||
def bchr(i):
|
||||
"""Return a bytes character for a given integer value"""
|
||||
return bytestype([i])
|
||||
|
||||
def u(s):
|
||||
"""Convert an ASCII string literal into a unicode object"""
|
||||
return s
|
||||
|
||||
try:
|
||||
eval(r'u""')
|
||||
except SyntaxError:
|
||||
ul = eval
|
||||
else:
|
||||
def ul(e):
|
||||
"""Evaluate e as a unicode string literal"""
|
||||
return eval('u' + e)
|
||||
211
third_party/python/cram/cram/_main.py
vendored
211
third_party/python/cram/cram/_main.py
vendored
@@ -1,211 +0,0 @@
|
||||
"""Main entry point"""
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
try:
|
||||
import configparser
|
||||
except ImportError: # pragma: nocover
|
||||
import ConfigParser as configparser
|
||||
|
||||
from cram._cli import runcli
|
||||
from cram._encoding import b, fsencode, stderrb, stdoutb
|
||||
from cram._run import runtests
|
||||
from cram._xunit import runxunit
|
||||
|
||||
def _which(cmd):
|
||||
"""Return the path to cmd or None if not found"""
|
||||
cmd = fsencode(cmd)
|
||||
for p in os.environ['PATH'].split(os.pathsep):
|
||||
path = os.path.join(fsencode(p), cmd)
|
||||
if os.path.isfile(path) and os.access(path, os.X_OK):
|
||||
return os.path.abspath(path)
|
||||
return None
|
||||
|
||||
def _expandpath(path):
|
||||
"""Expands ~ and environment variables in path"""
|
||||
return os.path.expanduser(os.path.expandvars(path))
|
||||
|
||||
class _OptionParser(optparse.OptionParser):
|
||||
"""Like optparse.OptionParser, but supports setting values through
|
||||
CRAM= and .cramrc."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._config_opts = {}
|
||||
optparse.OptionParser.__init__(self, *args, **kwargs)
|
||||
|
||||
def add_option(self, *args, **kwargs):
|
||||
option = optparse.OptionParser.add_option(self, *args, **kwargs)
|
||||
if option.dest and option.dest != 'version':
|
||||
key = option.dest.replace('_', '-')
|
||||
self._config_opts[key] = option.action == 'store_true'
|
||||
return option
|
||||
|
||||
def parse_args(self, args=None, values=None):
|
||||
config = configparser.RawConfigParser()
|
||||
config.read(_expandpath(os.environ.get('CRAMRC', '.cramrc')))
|
||||
defaults = {}
|
||||
for key, isbool in self._config_opts.items():
|
||||
try:
|
||||
if isbool:
|
||||
try:
|
||||
value = config.getboolean('cram', key)
|
||||
except ValueError:
|
||||
value = config.get('cram', key)
|
||||
self.error('--%s: invalid boolean value: %r'
|
||||
% (key, value))
|
||||
else:
|
||||
value = config.get('cram', key)
|
||||
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||
pass
|
||||
else:
|
||||
defaults[key] = value
|
||||
self.set_defaults(**defaults)
|
||||
|
||||
eargs = os.environ.get('CRAM', '').strip()
|
||||
if eargs:
|
||||
args = args or []
|
||||
args += shlex.split(eargs)
|
||||
|
||||
try:
|
||||
return optparse.OptionParser.parse_args(self, args, values)
|
||||
except optparse.OptionValueError:
|
||||
self.error(str(sys.exc_info()[1]))
|
||||
|
||||
def _parseopts(args):
|
||||
"""Parse command line arguments"""
|
||||
p = _OptionParser(usage='cram [OPTIONS] TESTS...', prog='cram')
|
||||
p.add_option('-V', '--version', action='store_true',
|
||||
help='show version information and exit')
|
||||
p.add_option('-q', '--quiet', action='store_true',
|
||||
help="don't print diffs")
|
||||
p.add_option('-v', '--verbose', action='store_true',
|
||||
help='show filenames and test status')
|
||||
p.add_option('-i', '--interactive', action='store_true',
|
||||
help='interactively merge changed test output')
|
||||
p.add_option('-d', '--debug', action='store_true',
|
||||
help='write script output directly to the terminal')
|
||||
p.add_option('-y', '--yes', action='store_true',
|
||||
help='answer yes to all questions')
|
||||
p.add_option('-n', '--no', action='store_true',
|
||||
help='answer no to all questions')
|
||||
p.add_option('-E', '--preserve-env', action='store_true',
|
||||
help="don't reset common environment variables")
|
||||
p.add_option('--keep-tmpdir', action='store_true',
|
||||
help='keep temporary directories')
|
||||
p.add_option('--shell', action='store', default='/bin/sh', metavar='PATH',
|
||||
help='shell to use for running tests (default: %default)')
|
||||
p.add_option('--shell-opts', action='store', metavar='OPTS',
|
||||
help='arguments to invoke shell with')
|
||||
p.add_option('--indent', action='store', default=2, metavar='NUM',
|
||||
type='int', help=('number of spaces to use for indentation '
|
||||
'(default: %default)'))
|
||||
p.add_option('--xunit-file', action='store', metavar='PATH',
|
||||
help='path to write xUnit XML output')
|
||||
opts, paths = p.parse_args(args)
|
||||
paths = [fsencode(path) for path in paths]
|
||||
return opts, paths, p.get_usage
|
||||
|
||||
def main(args):
|
||||
"""Main entry point.
|
||||
|
||||
If you're thinking of using Cram in other Python code (e.g., unit tests),
|
||||
consider using the test() or testfile() functions instead.
|
||||
|
||||
:param args: Script arguments (excluding script name)
|
||||
:type args: str
|
||||
:return: Exit code (non-zero on failure)
|
||||
:rtype: int
|
||||
"""
|
||||
opts, paths, getusage = _parseopts(args)
|
||||
if opts.version:
|
||||
sys.stdout.write("""Cram CLI testing framework (version 0.7)
|
||||
|
||||
Copyright (C) 2010-2016 Brodie Rao <brodie@bitheap.org> and others
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
""")
|
||||
return
|
||||
|
||||
conflicts = [('--yes', opts.yes, '--no', opts.no),
|
||||
('--quiet', opts.quiet, '--interactive', opts.interactive),
|
||||
('--debug', opts.debug, '--quiet', opts.quiet),
|
||||
('--debug', opts.debug, '--interactive', opts.interactive),
|
||||
('--debug', opts.debug, '--verbose', opts.verbose),
|
||||
('--debug', opts.debug, '--xunit-file', opts.xunit_file)]
|
||||
for s1, o1, s2, o2 in conflicts:
|
||||
if o1 and o2:
|
||||
sys.stderr.write('options %s and %s are mutually exclusive\n'
|
||||
% (s1, s2))
|
||||
return 2
|
||||
|
||||
shellcmd = _which(opts.shell)
|
||||
if not shellcmd:
|
||||
stderrb.write(b('shell not found: ') + fsencode(opts.shell) + b('\n'))
|
||||
return 2
|
||||
shell = [shellcmd]
|
||||
if opts.shell_opts:
|
||||
shell += shlex.split(opts.shell_opts)
|
||||
|
||||
patchcmd = None
|
||||
if opts.interactive:
|
||||
patchcmd = _which('patch')
|
||||
if not patchcmd:
|
||||
sys.stderr.write('patch(1) required for -i\n')
|
||||
return 2
|
||||
|
||||
if not paths:
|
||||
sys.stdout.write(getusage())
|
||||
return 2
|
||||
|
||||
badpaths = [path for path in paths if not os.path.exists(path)]
|
||||
if badpaths:
|
||||
stderrb.write(b('no such file: ') + badpaths[0] + b('\n'))
|
||||
return 2
|
||||
|
||||
if opts.yes:
|
||||
answer = 'y'
|
||||
elif opts.no:
|
||||
answer = 'n'
|
||||
else:
|
||||
answer = None
|
||||
|
||||
tmpdir = os.environ['CRAMTMP'] = tempfile.mkdtemp('', 'cramtests-')
|
||||
tmpdirb = fsencode(tmpdir)
|
||||
proctmp = os.path.join(tmpdir, 'tmp')
|
||||
for s in ('TMPDIR', 'TEMP', 'TMP'):
|
||||
os.environ[s] = proctmp
|
||||
|
||||
os.mkdir(proctmp)
|
||||
try:
|
||||
tests = runtests(paths, tmpdirb, shell, indent=opts.indent,
|
||||
cleanenv=not opts.preserve_env, debug=opts.debug)
|
||||
if not opts.debug:
|
||||
tests = runcli(tests, quiet=opts.quiet, verbose=opts.verbose,
|
||||
patchcmd=patchcmd, answer=answer)
|
||||
if opts.xunit_file is not None:
|
||||
tests = runxunit(tests, opts.xunit_file)
|
||||
|
||||
hastests = False
|
||||
failed = False
|
||||
for path, test in tests:
|
||||
hastests = True
|
||||
refout, postout, diff = test()
|
||||
if diff:
|
||||
failed = True
|
||||
|
||||
if not hastests:
|
||||
sys.stderr.write('no tests found\n')
|
||||
return 2
|
||||
|
||||
return int(failed)
|
||||
finally:
|
||||
if opts.keep_tmpdir:
|
||||
stdoutb.write(b('# Kept temporary directory: ') + tmpdirb +
|
||||
b('\n'))
|
||||
else:
|
||||
shutil.rmtree(tmpdir)
|
||||
54
third_party/python/cram/cram/_process.py
vendored
54
third_party/python/cram/cram/_process.py
vendored
@@ -1,54 +0,0 @@
|
||||
"""Utilities for running subprocesses"""
|
||||
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from cram._encoding import fsdecode
|
||||
|
||||
__all__ = ['PIPE', 'STDOUT', 'execute']
|
||||
|
||||
PIPE = subprocess.PIPE
|
||||
STDOUT = subprocess.STDOUT
|
||||
|
||||
def _makeresetsigpipe():
|
||||
"""Make a function to reset SIGPIPE to SIG_DFL (for use in subprocesses).
|
||||
|
||||
Doing subprocess.Popen(..., preexec_fn=makeresetsigpipe()) will prevent
|
||||
Python's SIGPIPE handler (SIG_IGN) from being inherited by the
|
||||
child process.
|
||||
"""
|
||||
if (sys.platform == 'win32' or
|
||||
getattr(signal, 'SIGPIPE', None) is None): # pragma: nocover
|
||||
return None
|
||||
return lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
|
||||
def execute(args, stdin=None, stdout=None, stderr=None, cwd=None, env=None):
|
||||
"""Run a process and return its output and return code.
|
||||
|
||||
stdin may either be None or a string to send to the process.
|
||||
|
||||
stdout may either be None or PIPE. If set to PIPE, the process's output
|
||||
is returned as a string.
|
||||
|
||||
stderr may either be None or STDOUT. If stdout is set to PIPE and stderr
|
||||
is set to STDOUT, the process's stderr output will be interleaved with
|
||||
stdout and returned as a string.
|
||||
|
||||
cwd sets the process's current working directory.
|
||||
|
||||
env can be set to a dictionary to override the process's environment
|
||||
variables.
|
||||
|
||||
This function returns a 2-tuple of (output, returncode).
|
||||
"""
|
||||
if sys.platform == 'win32': # pragma: nocover
|
||||
args = [fsdecode(arg) for arg in args]
|
||||
|
||||
p = subprocess.Popen(args, stdin=PIPE, stdout=stdout, stderr=stderr,
|
||||
cwd=cwd, env=env, bufsize=-1,
|
||||
preexec_fn=_makeresetsigpipe(),
|
||||
close_fds=os.name == 'posix')
|
||||
out, err = p.communicate(stdin)
|
||||
return out, p.returncode
|
||||
77
third_party/python/cram/cram/_run.py
vendored
77
third_party/python/cram/cram/_run.py
vendored
@@ -1,77 +0,0 @@
|
||||
"""The test runner"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from cram._encoding import b, fsdecode, fsencode
|
||||
from cram._test import testfile
|
||||
|
||||
__all__ = ['runtests']
|
||||
|
||||
if sys.platform == 'win32': # pragma: nocover
|
||||
def _walk(top):
|
||||
top = fsdecode(top)
|
||||
for root, dirs, files in os.walk(top):
|
||||
yield (fsencode(root),
|
||||
[fsencode(p) for p in dirs],
|
||||
[fsencode(p) for p in files])
|
||||
else:
|
||||
_walk = os.walk
|
||||
|
||||
def _findtests(paths):
|
||||
"""Yield tests in paths in sorted order"""
|
||||
for p in paths:
|
||||
if os.path.isdir(p):
|
||||
for root, dirs, files in _walk(p):
|
||||
if os.path.basename(root).startswith(b('.')):
|
||||
continue
|
||||
for f in sorted(files):
|
||||
if not f.startswith(b('.')) and f.endswith(b('.t')):
|
||||
yield os.path.normpath(os.path.join(root, f))
|
||||
else:
|
||||
yield os.path.normpath(p)
|
||||
|
||||
def runtests(paths, tmpdir, shell, indent=2, cleanenv=True, debug=False):
|
||||
"""Run tests and yield results.
|
||||
|
||||
This yields a sequence of 2-tuples containing the following:
|
||||
|
||||
(test path, test function)
|
||||
|
||||
The test function, when called, runs the test in a temporary directory
|
||||
and returns a 3-tuple:
|
||||
|
||||
(list of lines in the test, same list with actual output, diff)
|
||||
"""
|
||||
cwd = os.getcwd()
|
||||
seen = set()
|
||||
basenames = set()
|
||||
for i, path in enumerate(_findtests(paths)):
|
||||
abspath = os.path.abspath(path)
|
||||
if abspath in seen:
|
||||
continue
|
||||
seen.add(abspath)
|
||||
|
||||
if not os.stat(path).st_size:
|
||||
yield (path, lambda: (None, None, None))
|
||||
continue
|
||||
|
||||
basename = os.path.basename(path)
|
||||
if basename in basenames:
|
||||
basename = basename + b('-%s' % i)
|
||||
else:
|
||||
basenames.add(basename)
|
||||
|
||||
def test():
|
||||
"""Run test file"""
|
||||
testdir = os.path.join(tmpdir, basename)
|
||||
os.mkdir(testdir)
|
||||
try:
|
||||
os.chdir(testdir)
|
||||
return testfile(abspath, shell, indent=indent,
|
||||
cleanenv=cleanenv, debug=debug,
|
||||
testname=path)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
yield (path, test)
|
||||
230
third_party/python/cram/cram/_test.py
vendored
230
third_party/python/cram/cram/_test.py
vendored
@@ -1,230 +0,0 @@
|
||||
"""Utilities for running individual tests"""
|
||||
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
from cram._encoding import b, bchr, bytestype, envencode, unicodetype
|
||||
from cram._diff import esc, glob, regex, unified_diff
|
||||
from cram._process import PIPE, STDOUT, execute
|
||||
|
||||
__all__ = ['test', 'testfile']
|
||||
|
||||
_needescape = re.compile(b(r'[\x00-\x09\x0b-\x1f\x7f-\xff]')).search
|
||||
_escapesub = re.compile(b(r'[\x00-\x09\x0b-\x1f\\\x7f-\xff]')).sub
|
||||
_escapemap = dict((bchr(i), b(r'\x%02x' % i)) for i in range(256))
|
||||
_escapemap.update({b('\\'): b('\\\\'), b('\r'): b(r'\r'), b('\t'): b(r'\t')})
|
||||
|
||||
def _escape(s):
|
||||
"""Like the string-escape codec, but doesn't escape quotes"""
|
||||
return (_escapesub(lambda m: _escapemap[m.group(0)], s[:-1]) +
|
||||
b(' (esc)\n'))
|
||||
|
||||
def test(lines, shell='/bin/sh', indent=2, testname=None, env=None,
|
||||
cleanenv=True, debug=False):
|
||||
r"""Run test lines and return input, output, and diff.
|
||||
|
||||
This returns a 3-tuple containing the following:
|
||||
|
||||
(list of lines in test, same list with actual output, diff)
|
||||
|
||||
diff is a generator that yields the diff between the two lists.
|
||||
|
||||
If a test exits with return code 80, the actual output is set to
|
||||
None and diff is set to [].
|
||||
|
||||
Note that the TESTSHELL environment variable is available in the
|
||||
test (set to the specified shell). However, the TESTDIR and
|
||||
TESTFILE environment variables are not available. To run actual
|
||||
test files, see testfile().
|
||||
|
||||
Example usage:
|
||||
|
||||
>>> from cram._encoding import b
|
||||
>>> refout, postout, diff = test([b(' $ echo hi\n'),
|
||||
... b(' [a-z]{2} (re)\n')])
|
||||
>>> refout == [b(' $ echo hi\n'), b(' [a-z]{2} (re)\n')]
|
||||
True
|
||||
>>> postout == [b(' $ echo hi\n'), b(' hi\n')]
|
||||
True
|
||||
>>> bool(diff)
|
||||
False
|
||||
|
||||
lines may also be a single bytes string:
|
||||
|
||||
>>> refout, postout, diff = test(b(' $ echo hi\n bye\n'))
|
||||
>>> refout == [b(' $ echo hi\n'), b(' bye\n')]
|
||||
True
|
||||
>>> postout == [b(' $ echo hi\n'), b(' hi\n')]
|
||||
True
|
||||
>>> bool(diff)
|
||||
True
|
||||
>>> (b('').join(diff) ==
|
||||
... b('--- \n+++ \n@@ -1,2 +1,2 @@\n $ echo hi\n- bye\n+ hi\n'))
|
||||
True
|
||||
|
||||
Note that the b() function is internal to Cram. If you're using Python 2,
|
||||
use normal string literals instead. If you're using Python 3, use bytes
|
||||
literals.
|
||||
|
||||
:param lines: Test input
|
||||
:type lines: bytes or collections.Iterable[bytes]
|
||||
:param shell: Shell to run test in
|
||||
:type shell: bytes or str or list[bytes] or list[str]
|
||||
:param indent: Amount of indentation to use for shell commands
|
||||
:type indent: int
|
||||
:param testname: Optional test file name (used in diff output)
|
||||
:type testname: bytes or None
|
||||
:param env: Optional environment variables for the test shell
|
||||
:type env: dict or None
|
||||
:param cleanenv: Whether or not to sanitize the environment
|
||||
:type cleanenv: bool
|
||||
:param debug: Whether or not to run in debug mode (don't capture stdout)
|
||||
:type debug: bool
|
||||
:return: Input, output, and diff iterables
|
||||
:rtype: (list[bytes], list[bytes], collections.Iterable[bytes])
|
||||
"""
|
||||
indent = b(' ') * indent
|
||||
cmdline = indent + b('$ ')
|
||||
conline = indent + b('> ')
|
||||
usalt = 'CRAM%s' % time.time()
|
||||
salt = b(usalt)
|
||||
|
||||
if env is None:
|
||||
env = os.environ.copy()
|
||||
|
||||
if cleanenv:
|
||||
for s in ('LANG', 'LC_ALL', 'LANGUAGE'):
|
||||
env[s] = 'C'
|
||||
env['TZ'] = 'GMT'
|
||||
env['CDPATH'] = ''
|
||||
env['COLUMNS'] = '80'
|
||||
env['GREP_OPTIONS'] = ''
|
||||
|
||||
if isinstance(lines, bytestype):
|
||||
lines = lines.splitlines(True)
|
||||
|
||||
if isinstance(shell, (bytestype, unicodetype)):
|
||||
shell = [shell]
|
||||
env['TESTSHELL'] = shell[0]
|
||||
|
||||
if debug:
|
||||
stdin = []
|
||||
for line in lines:
|
||||
if not line.endswith(b('\n')):
|
||||
line += b('\n')
|
||||
if line.startswith(cmdline):
|
||||
stdin.append(line[len(cmdline):])
|
||||
elif line.startswith(conline):
|
||||
stdin.append(line[len(conline):])
|
||||
|
||||
execute(shell + ['-'], stdin=b('').join(stdin), env=env)
|
||||
return ([], [], [])
|
||||
|
||||
after = {}
|
||||
refout, postout = [], []
|
||||
i = pos = prepos = -1
|
||||
stdin = []
|
||||
for i, line in enumerate(lines):
|
||||
if not line.endswith(b('\n')):
|
||||
line += b('\n')
|
||||
refout.append(line)
|
||||
if line.startswith(cmdline):
|
||||
after.setdefault(pos, []).append(line)
|
||||
prepos = pos
|
||||
pos = i
|
||||
stdin.append(b('echo %s %s $?\n' % (usalt, i)))
|
||||
stdin.append(line[len(cmdline):])
|
||||
elif line.startswith(conline):
|
||||
after.setdefault(prepos, []).append(line)
|
||||
stdin.append(line[len(conline):])
|
||||
elif not line.startswith(indent):
|
||||
after.setdefault(pos, []).append(line)
|
||||
stdin.append(b('echo %s %s $?\n' % (usalt, i + 1)))
|
||||
|
||||
output, retcode = execute(shell + ['-'], stdin=b('').join(stdin),
|
||||
stdout=PIPE, stderr=STDOUT, env=env)
|
||||
if retcode == 80:
|
||||
return (refout, None, [])
|
||||
|
||||
pos = -1
|
||||
ret = 0
|
||||
for i, line in enumerate(output[:-1].splitlines(True)):
|
||||
out, cmd = line, None
|
||||
if salt in line:
|
||||
out, cmd = line.split(salt, 1)
|
||||
|
||||
if out:
|
||||
if not out.endswith(b('\n')):
|
||||
out += b(' (no-eol)\n')
|
||||
|
||||
if _needescape(out):
|
||||
out = _escape(out)
|
||||
postout.append(indent + out)
|
||||
|
||||
if cmd:
|
||||
ret = int(cmd.split()[1])
|
||||
if ret != 0:
|
||||
postout.append(indent + b('[%s]\n' % (ret)))
|
||||
postout += after.pop(pos, [])
|
||||
pos = int(cmd.split()[0])
|
||||
|
||||
postout += after.pop(pos, [])
|
||||
|
||||
if testname:
|
||||
diffpath = testname
|
||||
errpath = diffpath + b('.err')
|
||||
else:
|
||||
diffpath = errpath = b('')
|
||||
diff = unified_diff(refout, postout, diffpath, errpath,
|
||||
matchers=[esc, glob, regex])
|
||||
for firstline in diff:
|
||||
return refout, postout, itertools.chain([firstline], diff)
|
||||
return refout, postout, []
|
||||
|
||||
def testfile(path, shell='/bin/sh', indent=2, env=None, cleanenv=True,
|
||||
debug=False, testname=None):
|
||||
"""Run test at path and return input, output, and diff.
|
||||
|
||||
This returns a 3-tuple containing the following:
|
||||
|
||||
(list of lines in test, same list with actual output, diff)
|
||||
|
||||
diff is a generator that yields the diff between the two lists.
|
||||
|
||||
If a test exits with return code 80, the actual output is set to
|
||||
None and diff is set to [].
|
||||
|
||||
Note that the TESTDIR, TESTFILE, and TESTSHELL environment
|
||||
variables are available to use in the test.
|
||||
|
||||
:param path: Path to test file
|
||||
:type path: bytes or str
|
||||
:param shell: Shell to run test in
|
||||
:type shell: bytes or str or list[bytes] or list[str]
|
||||
:param indent: Amount of indentation to use for shell commands
|
||||
:type indent: int
|
||||
:param env: Optional environment variables for the test shell
|
||||
:type env: dict or None
|
||||
:param cleanenv: Whether or not to sanitize the environment
|
||||
:type cleanenv: bool
|
||||
:param debug: Whether or not to run in debug mode (don't capture stdout)
|
||||
:type debug: bool
|
||||
:param testname: Optional test file name (used in diff output)
|
||||
:type testname: bytes or None
|
||||
:return: Input, output, and diff iterables
|
||||
:rtype: (list[bytes], list[bytes], collections.Iterable[bytes])
|
||||
"""
|
||||
f = open(path, 'rb')
|
||||
try:
|
||||
abspath = os.path.abspath(path)
|
||||
env = env or os.environ.copy()
|
||||
env['TESTDIR'] = envencode(os.path.dirname(abspath))
|
||||
env['TESTFILE'] = envencode(os.path.basename(abspath))
|
||||
if testname is None: # pragma: nocover
|
||||
testname = os.path.basename(abspath)
|
||||
return test(f, shell, indent=indent, testname=testname, env=env,
|
||||
cleanenv=cleanenv, debug=debug)
|
||||
finally:
|
||||
f.close()
|
||||
173
third_party/python/cram/cram/_xunit.py
vendored
173
third_party/python/cram/cram/_xunit.py
vendored
@@ -1,173 +0,0 @@
|
||||
"""xUnit XML output"""
|
||||
|
||||
import locale
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
from cram._encoding import u, ul
|
||||
|
||||
__all__ = ['runxunit']
|
||||
|
||||
_widecdataregex = ul(r"'(?:[^\x09\x0a\x0d\x20-\ud7ff\ue000-\ufffd"
|
||||
r"\U00010000-\U0010ffff]|]]>)'")
|
||||
_narrowcdataregex = ul(r"'(?:[^\x09\x0a\x0d\x20-\ud7ff\ue000-\ufffd]"
|
||||
r"|]]>)'")
|
||||
_widequoteattrregex = ul(r"'[^\x20\x21\x23-\x25\x27-\x3b\x3d"
|
||||
r"\x3f-\ud7ff\ue000-\ufffd"
|
||||
r"\U00010000-\U0010ffff]'")
|
||||
_narrowquoteattrregex = ul(r"'[^\x20\x21\x23-\x25\x27-\x3b\x3d"
|
||||
r"\x3f-\ud7ff\ue000-\ufffd]'")
|
||||
_replacementchar = ul(r"'\N{REPLACEMENT CHARACTER}'")
|
||||
|
||||
if sys.maxunicode >= 0x10ffff: # pragma: nocover
|
||||
_cdatasub = re.compile(_widecdataregex).sub
|
||||
_quoteattrsub = re.compile(_widequoteattrregex).sub
|
||||
else: # pragma: nocover
|
||||
_cdatasub = re.compile(_narrowcdataregex).sub
|
||||
_quoteattrsub = re.compile(_narrowquoteattrregex).sub
|
||||
|
||||
def _cdatareplace(m):
|
||||
"""Replace _cdatasub() regex match"""
|
||||
if m.group(0) == u(']]>'):
|
||||
return u(']]>]]><![CDATA[')
|
||||
else:
|
||||
return _replacementchar
|
||||
|
||||
def _cdata(s):
|
||||
r"""Escape a string as an XML CDATA block.
|
||||
|
||||
>>> from cram._encoding import ul
|
||||
>>> (_cdata('1<\'2\'>&"3\x00]]>\t\r\n') ==
|
||||
... ul(r"'<![CDATA[1<\'2\'>&\"3\ufffd]]>]]><![CDATA[\t\r\n]]>'"))
|
||||
True
|
||||
"""
|
||||
return u('<![CDATA[%s]]>') % _cdatasub(_cdatareplace, s)
|
||||
|
||||
def _quoteattrreplace(m):
|
||||
"""Replace _quoteattrsub() regex match"""
|
||||
return {u('\t'): u('	'),
|
||||
u('\n'): u(' '),
|
||||
u('\r'): u(' '),
|
||||
u('"'): u('"'),
|
||||
u('&'): u('&'),
|
||||
u('<'): u('<'),
|
||||
u('>'): u('>')}.get(m.group(0), _replacementchar)
|
||||
|
||||
def _quoteattr(s):
|
||||
r"""Escape a string for use as an XML attribute value.
|
||||
|
||||
>>> from cram._encoding import ul
|
||||
>>> (_quoteattr('1<\'2\'>&"3\x00]]>\t\r\n') ==
|
||||
... ul(r"'\"1<\'2\'>&"3\ufffd]]>	 \"'"))
|
||||
True
|
||||
"""
|
||||
return u('"%s"') % _quoteattrsub(_quoteattrreplace, s)
|
||||
|
||||
def _timestamp():
|
||||
"""Return the current time in ISO 8601 format"""
|
||||
tm = time.localtime()
|
||||
if tm.tm_isdst == 1: # pragma: nocover
|
||||
tz = time.altzone
|
||||
else: # pragma: nocover
|
||||
tz = time.timezone
|
||||
|
||||
timestamp = time.strftime('%Y-%m-%dT%H:%M:%S', tm)
|
||||
tzhours = int(-tz / 60 / 60)
|
||||
tzmins = int(abs(tz) / 60 % 60)
|
||||
timestamp += u('%+03d:%02d') % (tzhours, tzmins)
|
||||
return timestamp
|
||||
|
||||
def runxunit(tests, xmlpath):
|
||||
"""Run tests with xUnit XML output.
|
||||
|
||||
tests should be a sequence of 2-tuples containing the following:
|
||||
|
||||
(test path, test function)
|
||||
|
||||
This function yields a new sequence where each test function is wrapped
|
||||
with a function that writes test results to an xUnit XML file.
|
||||
"""
|
||||
suitestart = time.time()
|
||||
timestamp = _timestamp()
|
||||
hostname = socket.gethostname()
|
||||
total, skipped, failed = [0], [0], [0]
|
||||
testcases = []
|
||||
|
||||
for path, test in tests:
|
||||
def testwrapper():
|
||||
"""Run test and collect XML output"""
|
||||
total[0] += 1
|
||||
|
||||
start = time.time()
|
||||
refout, postout, diff = test()
|
||||
testtime = time.time() - start
|
||||
|
||||
classname = path.decode(locale.getpreferredencoding(), 'replace')
|
||||
name = os.path.basename(classname)
|
||||
|
||||
if postout is None:
|
||||
skipped[0] += 1
|
||||
testcase = (u(' <testcase classname=%(classname)s\n'
|
||||
' name=%(name)s\n'
|
||||
' time="%(time).6f">\n'
|
||||
' <skipped/>\n'
|
||||
' </testcase>\n') %
|
||||
{'classname': _quoteattr(classname),
|
||||
'name': _quoteattr(name),
|
||||
'time': testtime})
|
||||
elif diff:
|
||||
failed[0] += 1
|
||||
diff = list(diff)
|
||||
diffu = u('').join(l.decode(locale.getpreferredencoding(),
|
||||
'replace')
|
||||
for l in diff)
|
||||
testcase = (u(' <testcase classname=%(classname)s\n'
|
||||
' name=%(name)s\n'
|
||||
' time="%(time).6f">\n'
|
||||
' <failure>%(diff)s</failure>\n'
|
||||
' </testcase>\n') %
|
||||
{'classname': _quoteattr(classname),
|
||||
'name': _quoteattr(name),
|
||||
'time': testtime,
|
||||
'diff': _cdata(diffu)})
|
||||
else:
|
||||
testcase = (u(' <testcase classname=%(classname)s\n'
|
||||
' name=%(name)s\n'
|
||||
' time="%(time).6f"/>\n') %
|
||||
{'classname': _quoteattr(classname),
|
||||
'name': _quoteattr(name),
|
||||
'time': testtime})
|
||||
testcases.append(testcase)
|
||||
|
||||
return refout, postout, diff
|
||||
|
||||
yield path, testwrapper
|
||||
|
||||
suitetime = time.time() - suitestart
|
||||
header = (u('<?xml version="1.0" encoding="utf-8"?>\n'
|
||||
'<testsuite name="cram"\n'
|
||||
' tests="%(total)d"\n'
|
||||
' failures="%(failed)d"\n'
|
||||
' skipped="%(skipped)d"\n'
|
||||
' timestamp=%(timestamp)s\n'
|
||||
' hostname=%(hostname)s\n'
|
||||
' time="%(time).6f">\n') %
|
||||
{'total': total[0],
|
||||
'failed': failed[0],
|
||||
'skipped': skipped[0],
|
||||
'timestamp': _quoteattr(timestamp),
|
||||
'hostname': _quoteattr(hostname),
|
||||
'time': suitetime})
|
||||
footer = u('</testsuite>\n')
|
||||
|
||||
xmlfile = open(xmlpath, 'wb')
|
||||
try:
|
||||
xmlfile.write(header.encode('utf-8'))
|
||||
for testcase in testcases:
|
||||
xmlfile.write(testcase.encode('utf-8'))
|
||||
xmlfile.write(footer.encode('utf-8'))
|
||||
finally:
|
||||
xmlfile.close()
|
||||
1
third_party/python/pyproject.toml
vendored
1
third_party/python/pyproject.toml
vendored
@@ -15,7 +15,6 @@ dependencies = [
|
||||
"colorama==0.4.6",
|
||||
"compare-locales==9.0.1",
|
||||
"cookies==2.2.1",
|
||||
"cram==0.7",
|
||||
"distro==1.8.0",
|
||||
"ecdsa==0.15",
|
||||
"esprima==4.0.1",
|
||||
|
||||
3
third_party/python/requirements.txt
vendored
3
third_party/python/requirements.txt
vendored
@@ -191,9 +191,6 @@ cookiecutter==2.6.0 \
|
||||
cookies==2.2.1 \
|
||||
--hash=sha256:d6b698788cae4cfa4e62ef8643a9ca332b79bd96cb314294b864ae8d7eb3ee8e \
|
||||
--hash=sha256:15bee753002dff684987b8df8c235288eb8d45f8191ae056254812dfd42c81d3
|
||||
cram==0.7 \
|
||||
--hash=sha256:7da7445af2ce15b90aad5ec4792f857cef5786d71f14377e9eb994d8b8337f2f \
|
||||
--hash=sha256:008e4e8b4d325cf040964b5f62460535b004a7bc816d54f8527a4d299edfe4a3
|
||||
diskcache==5.6.3 \
|
||||
--hash=sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc \
|
||||
--hash=sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19
|
||||
|
||||
11
third_party/python/uv.lock
generated
vendored
11
third_party/python/uv.lock
generated
vendored
@@ -355,15 +355,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/60/557f84aa2db629e5124aa05408b975b1b5d0e1cec16cde0bfa06aae097d3/cookies-2.2.1-py2.py3-none-any.whl", hash = "sha256:15bee753002dff684987b8df8c235288eb8d45f8191ae056254812dfd42c81d3", size = 44423 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cram"
|
||||
version = "0.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/85/5a8a3397b2ccb2ffa3ba871f76a4d72c16531e43d0e58fc89a0f2983adbd/cram-0.7.tar.gz", hash = "sha256:7da7445af2ce15b90aad5ec4792f857cef5786d71f14377e9eb994d8b8337f2f", size = 33527 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/09/c119252f12b35c21b030c01d38d292936660c167e725ce07517fae2b01a0/cram-0.7-py2.py3-none-any.whl", hash = "sha256:008e4e8b4d325cf040964b5f62460535b004a7bc816d54f8527a4d299edfe4a3", size = 22141 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diskcache"
|
||||
version = "5.6.3"
|
||||
@@ -751,7 +742,6 @@ dependencies = [
|
||||
{ name = "colorama" },
|
||||
{ name = "compare-locales" },
|
||||
{ name = "cookies" },
|
||||
{ name = "cram" },
|
||||
{ name = "distro" },
|
||||
{ name = "ecdsa" },
|
||||
{ name = "esprima" },
|
||||
@@ -812,7 +802,6 @@ requires-dist = [
|
||||
{ name = "colorama", specifier = "==0.4.6" },
|
||||
{ name = "compare-locales", specifier = "==9.0.1" },
|
||||
{ name = "cookies", specifier = "==2.2.1" },
|
||||
{ name = "cram", specifier = "==0.7" },
|
||||
{ name = "distro", specifier = "==1.8.0" },
|
||||
{ name = "ecdsa", specifier = "==0.15" },
|
||||
{ name = "esprima", specifier = "==4.0.1" },
|
||||
|
||||
2
third_party/python/uv.lock.hash
vendored
2
third_party/python/uv.lock.hash
vendored
@@ -1 +1 @@
|
||||
8f3b27ce4a9f0bcab71b21d5133a3ef5b65ec655c3a696d6587955942fc95ec5
|
||||
35232ec676dd469de7810dbc063f1a4d1831d97adc9a05236e5a7645fe148b90
|
||||
@@ -19,7 +19,6 @@ test-manifest-toml:
|
||||
- '**/clippy.toml'
|
||||
- '**/config-lock.toml'
|
||||
- '**/config.toml'
|
||||
- '**/cram.toml'
|
||||
- '**/empty.toml'
|
||||
- '**/generated-mochitest.toml'
|
||||
- '**/l10n.toml'
|
||||
|
||||
Reference in New Issue
Block a user