Update test/3rdparty with the packages required to run the test suite in parallel mode. Change the short command line flag to "-j", matching make.
--HG-- rename : test/3rdparty/testscenarios-0.2/.bzrignore => test/3rdparty/testscenarios-0.4/.bzrignore rename : test/3rdparty/testscenarios-0.2/Apache-2.0 => test/3rdparty/testscenarios-0.4/Apache-2.0 rename : test/3rdparty/testscenarios-0.2/BSD => test/3rdparty/testscenarios-0.4/BSD rename : test/3rdparty/testscenarios-0.2/COPYING => test/3rdparty/testscenarios-0.4/COPYING rename : test/3rdparty/testscenarios-0.2/GOALS => test/3rdparty/testscenarios-0.4/GOALS rename : test/3rdparty/testscenarios-0.2/HACKING => test/3rdparty/testscenarios-0.4/HACKING rename : test/3rdparty/testscenarios-0.2/MANIFEST.in => test/3rdparty/testscenarios-0.4/MANIFEST.in rename : test/3rdparty/testscenarios-0.2/Makefile => test/3rdparty/testscenarios-0.4/Makefile rename : test/3rdparty/testscenarios-0.2/doc/__init__.py => test/3rdparty/testscenarios-0.4/doc/__init__.py rename : test/3rdparty/testscenarios-0.2/doc/example.py => test/3rdparty/testscenarios-0.4/doc/example.py rename : test/3rdparty/testscenarios-0.2/doc/test_sample.py => test/3rdparty/testscenarios-0.4/doc/test_sample.py rename : test/3rdparty/testtools-0.9.12/doc/conf.py => test/3rdparty/testtools-0.9.34/doc/conf.py rename : test/3rdparty/testtools-0.9.12/doc/make.bat => test/3rdparty/testtools-0.9.34/doc/make.bat rename : test/3rdparty/testtools-0.9.12/testtools/_compat2x.py => test/3rdparty/testtools-0.9.34/testtools/_compat2x.py rename : test/3rdparty/testtools-0.9.12/testtools/_spinner.py => test/3rdparty/testtools-0.9.34/testtools/_spinner.py rename : test/3rdparty/testtools-0.9.12/testtools/distutilscmd.py => test/3rdparty/testtools-0.9.34/testtools/distutilscmd.py rename : test/3rdparty/testtools-0.9.12/testtools/monkey.py => test/3rdparty/testtools-0.9.34/testtools/monkey.py rename : test/3rdparty/testtools-0.9.12/testtools/tests/test_monkey.py => test/3rdparty/testtools-0.9.34/testtools/tests/test_monkey.py rename : test/3rdparty/testtools-0.9.12/testtools/tests/test_runtest.py => test/3rdparty/testtools-0.9.34/testtools/tests/test_runtest.py rename : test/3rdparty/testtools-0.9.12/testtools/utils.py => test/3rdparty/testtools-0.9.34/testtools/utils.py
This commit is contained in:
1320
test/3rdparty/python-subunit-0.0.16/python/subunit/__init__.py
vendored
Normal file
1320
test/3rdparty/python-subunit-0.0.16/python/subunit/__init__.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
185
test/3rdparty/python-subunit-0.0.16/python/subunit/chunked.py
vendored
Normal file
185
test/3rdparty/python-subunit-0.0.16/python/subunit/chunked.py
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
#
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
|
||||
# Copyright (C) 2011 Martin Pool <mbp@sourcefrog.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
"""Encoder/decoder for http style chunked encoding."""
|
||||
|
||||
from testtools.compat import _b
|
||||
|
||||
empty = _b('')
|
||||
|
||||
class Decoder(object):
|
||||
"""Decode chunked content to a byte stream."""
|
||||
|
||||
def __init__(self, output, strict=True):
|
||||
"""Create a decoder decoding to output.
|
||||
|
||||
:param output: A file-like object. Bytes written to the Decoder are
|
||||
decoded to strip off the chunking and written to the output.
|
||||
Up to a full write worth of data or a single control line may be
|
||||
buffered (whichever is larger). The close method should be called
|
||||
when no more data is available, to detect short streams; the
|
||||
write method will return none-None when the end of a stream is
|
||||
detected. The output object must accept bytes objects.
|
||||
|
||||
:param strict: If True (the default), the decoder will not knowingly
|
||||
accept input that is not conformant to the HTTP specification.
|
||||
(This does not imply that it will catch every nonconformance.)
|
||||
If False, it will accept incorrect input that is still
|
||||
unambiguous.
|
||||
"""
|
||||
self.output = output
|
||||
self.buffered_bytes = []
|
||||
self.state = self._read_length
|
||||
self.body_length = 0
|
||||
self.strict = strict
|
||||
self._match_chars = _b("0123456789abcdefABCDEF\r\n")
|
||||
self._slash_n = _b('\n')
|
||||
self._slash_r = _b('\r')
|
||||
self._slash_rn = _b('\r\n')
|
||||
self._slash_nr = _b('\n\r')
|
||||
|
||||
def close(self):
|
||||
"""Close the decoder.
|
||||
|
||||
:raises ValueError: If the stream is incomplete ValueError is raised.
|
||||
"""
|
||||
if self.state != self._finished:
|
||||
raise ValueError("incomplete stream")
|
||||
|
||||
def _finished(self):
|
||||
"""Finished reading, return any remaining bytes."""
|
||||
if self.buffered_bytes:
|
||||
buffered_bytes = self.buffered_bytes
|
||||
self.buffered_bytes = []
|
||||
return empty.join(buffered_bytes)
|
||||
else:
|
||||
raise ValueError("stream is finished")
|
||||
|
||||
def _read_body(self):
|
||||
"""Pass body bytes to the output."""
|
||||
while self.body_length and self.buffered_bytes:
|
||||
if self.body_length >= len(self.buffered_bytes[0]):
|
||||
self.output.write(self.buffered_bytes[0])
|
||||
self.body_length -= len(self.buffered_bytes[0])
|
||||
del self.buffered_bytes[0]
|
||||
# No more data available.
|
||||
if not self.body_length:
|
||||
self.state = self._read_length
|
||||
else:
|
||||
self.output.write(self.buffered_bytes[0][:self.body_length])
|
||||
self.buffered_bytes[0] = \
|
||||
self.buffered_bytes[0][self.body_length:]
|
||||
self.body_length = 0
|
||||
self.state = self._read_length
|
||||
return self.state()
|
||||
|
||||
def _read_length(self):
|
||||
"""Try to decode a length from the bytes."""
|
||||
count_chars = []
|
||||
for bytes in self.buffered_bytes:
|
||||
for pos in range(len(bytes)):
|
||||
byte = bytes[pos:pos+1]
|
||||
if byte not in self._match_chars:
|
||||
break
|
||||
count_chars.append(byte)
|
||||
if byte == self._slash_n:
|
||||
break
|
||||
if not count_chars:
|
||||
return
|
||||
if count_chars[-1] != self._slash_n:
|
||||
return
|
||||
count_str = empty.join(count_chars)
|
||||
if self.strict:
|
||||
if count_str[-2:] != self._slash_rn:
|
||||
raise ValueError("chunk header invalid: %r" % count_str)
|
||||
if self._slash_r in count_str[:-2]:
|
||||
raise ValueError("too many CRs in chunk header %r" % count_str)
|
||||
self.body_length = int(count_str.rstrip(self._slash_nr), 16)
|
||||
excess_bytes = len(count_str)
|
||||
while excess_bytes:
|
||||
if excess_bytes >= len(self.buffered_bytes[0]):
|
||||
excess_bytes -= len(self.buffered_bytes[0])
|
||||
del self.buffered_bytes[0]
|
||||
else:
|
||||
self.buffered_bytes[0] = self.buffered_bytes[0][excess_bytes:]
|
||||
excess_bytes = 0
|
||||
if not self.body_length:
|
||||
self.state = self._finished
|
||||
if not self.buffered_bytes:
|
||||
# May not call into self._finished with no buffered data.
|
||||
return empty
|
||||
else:
|
||||
self.state = self._read_body
|
||||
return self.state()
|
||||
|
||||
def write(self, bytes):
|
||||
"""Decode bytes to the output stream.
|
||||
|
||||
:raises ValueError: If the stream has already seen the end of file
|
||||
marker.
|
||||
:returns: None, or the excess bytes beyond the end of file marker.
|
||||
"""
|
||||
if bytes:
|
||||
self.buffered_bytes.append(bytes)
|
||||
return self.state()
|
||||
|
||||
|
||||
class Encoder(object):
|
||||
"""Encode content to a stream using HTTP Chunked coding."""
|
||||
|
||||
def __init__(self, output):
|
||||
"""Create an encoder encoding to output.
|
||||
|
||||
:param output: A file-like object. Bytes written to the Encoder
|
||||
will be encoded using HTTP chunking. Small writes may be buffered
|
||||
and the ``close`` method must be called to finish the stream.
|
||||
"""
|
||||
self.output = output
|
||||
self.buffered_bytes = []
|
||||
self.buffer_size = 0
|
||||
|
||||
def flush(self, extra_len=0):
|
||||
"""Flush the encoder to the output stream.
|
||||
|
||||
:param extra_len: Increase the size of the chunk by this many bytes
|
||||
to allow for a subsequent write.
|
||||
"""
|
||||
if not self.buffer_size and not extra_len:
|
||||
return
|
||||
buffered_bytes = self.buffered_bytes
|
||||
buffer_size = self.buffer_size
|
||||
self.buffered_bytes = []
|
||||
self.buffer_size = 0
|
||||
self.output.write(_b("%X\r\n" % (buffer_size + extra_len)))
|
||||
if buffer_size:
|
||||
self.output.write(empty.join(buffered_bytes))
|
||||
return True
|
||||
|
||||
def write(self, bytes):
|
||||
"""Encode bytes to the output stream."""
|
||||
bytes_len = len(bytes)
|
||||
if self.buffer_size + bytes_len >= 65536:
|
||||
self.flush(bytes_len)
|
||||
self.output.write(bytes)
|
||||
else:
|
||||
self.buffered_bytes.append(bytes)
|
||||
self.buffer_size += bytes_len
|
||||
|
||||
def close(self):
|
||||
"""Finish the stream. This does not close the output stream."""
|
||||
self.flush()
|
||||
self.output.write(_b("0\r\n"))
|
||||
119
test/3rdparty/python-subunit-0.0.16/python/subunit/details.py
vendored
Normal file
119
test/3rdparty/python-subunit-0.0.16/python/subunit/details.py
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
#
|
||||
# subunit: extensions to Python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
"""Handlers for outcome details."""
|
||||
|
||||
from testtools import content, content_type
|
||||
from testtools.compat import _b, BytesIO
|
||||
|
||||
from subunit import chunked
|
||||
|
||||
end_marker = _b("]\n")
|
||||
quoted_marker = _b(" ]")
|
||||
empty = _b('')
|
||||
|
||||
|
||||
class DetailsParser(object):
|
||||
"""Base class/API reference for details parsing."""
|
||||
|
||||
|
||||
class SimpleDetailsParser(DetailsParser):
|
||||
"""Parser for single-part [] delimited details."""
|
||||
|
||||
def __init__(self, state):
|
||||
self._message = _b("")
|
||||
self._state = state
|
||||
|
||||
def lineReceived(self, line):
|
||||
if line == end_marker:
|
||||
self._state.endDetails()
|
||||
return
|
||||
if line[0:2] == quoted_marker:
|
||||
# quoted ] start
|
||||
self._message += line[1:]
|
||||
else:
|
||||
self._message += line
|
||||
|
||||
def get_details(self, style=None):
|
||||
result = {}
|
||||
if not style:
|
||||
# We know that subunit/testtools serialise [] formatted
|
||||
# tracebacks as utf8, but perhaps we need a ReplacingContent
|
||||
# or something like that.
|
||||
result['traceback'] = content.Content(
|
||||
content_type.ContentType("text", "x-traceback",
|
||||
{"charset": "utf8"}),
|
||||
lambda:[self._message])
|
||||
else:
|
||||
if style == 'skip':
|
||||
name = 'reason'
|
||||
else:
|
||||
name = 'message'
|
||||
result[name] = content.Content(
|
||||
content_type.ContentType("text", "plain"),
|
||||
lambda:[self._message])
|
||||
return result
|
||||
|
||||
def get_message(self):
|
||||
return self._message
|
||||
|
||||
|
||||
class MultipartDetailsParser(DetailsParser):
|
||||
"""Parser for multi-part [] surrounded MIME typed chunked details."""
|
||||
|
||||
def __init__(self, state):
|
||||
self._state = state
|
||||
self._details = {}
|
||||
self._parse_state = self._look_for_content
|
||||
|
||||
def _look_for_content(self, line):
|
||||
if line == end_marker:
|
||||
self._state.endDetails()
|
||||
return
|
||||
# TODO error handling
|
||||
field, value = line[:-1].decode('utf8').split(' ', 1)
|
||||
try:
|
||||
main, sub = value.split('/')
|
||||
except ValueError:
|
||||
raise ValueError("Invalid MIME type %r" % value)
|
||||
self._content_type = content_type.ContentType(main, sub)
|
||||
self._parse_state = self._get_name
|
||||
|
||||
def _get_name(self, line):
|
||||
self._name = line[:-1].decode('utf8')
|
||||
self._body = BytesIO()
|
||||
self._chunk_parser = chunked.Decoder(self._body)
|
||||
self._parse_state = self._feed_chunks
|
||||
|
||||
def _feed_chunks(self, line):
|
||||
residue = self._chunk_parser.write(line)
|
||||
if residue is not None:
|
||||
# Line based use always ends on no residue.
|
||||
assert residue == empty, 'residue: %r' % (residue,)
|
||||
body = self._body
|
||||
self._details[self._name] = content.Content(
|
||||
self._content_type, lambda:[body.getvalue()])
|
||||
self._chunk_parser.close()
|
||||
self._parse_state = self._look_for_content
|
||||
|
||||
def get_details(self, for_skip=False):
|
||||
return self._details
|
||||
|
||||
def get_message(self):
|
||||
return None
|
||||
|
||||
def lineReceived(self, line):
|
||||
self._parse_state(line)
|
||||
206
test/3rdparty/python-subunit-0.0.16/python/subunit/filters.py
vendored
Normal file
206
test/3rdparty/python-subunit-0.0.16/python/subunit/filters.py
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
|
||||
from optparse import OptionParser
|
||||
import sys
|
||||
|
||||
from extras import safe_hasattr
|
||||
from testtools import CopyStreamResult, StreamResult, StreamResultRouter
|
||||
|
||||
from subunit import (
|
||||
DiscardStream, ProtocolTestCase, ByteStreamToStreamResult,
|
||||
StreamResultToBytes,
|
||||
)
|
||||
from subunit.test_results import CatFiles
|
||||
|
||||
|
||||
def make_options(description):
|
||||
parser = OptionParser(description=description)
|
||||
parser.add_option(
|
||||
"--no-passthrough", action="store_true",
|
||||
help="Hide all non subunit input.", default=False,
|
||||
dest="no_passthrough")
|
||||
parser.add_option(
|
||||
"-o", "--output-to",
|
||||
help="Send the output to this path rather than stdout.")
|
||||
parser.add_option(
|
||||
"-f", "--forward", action="store_true", default=False,
|
||||
help="Forward subunit stream on stdout. When set, received "
|
||||
"non-subunit output will be encapsulated in subunit.")
|
||||
return parser
|
||||
|
||||
|
||||
def run_tests_from_stream(input_stream, result, passthrough_stream=None,
|
||||
forward_stream=None, protocol_version=1, passthrough_subunit=True):
|
||||
"""Run tests from a subunit input stream through 'result'.
|
||||
|
||||
Non-test events - top level file attachments - are expected to be
|
||||
dropped by v2 StreamResults at the present time (as all the analysis code
|
||||
is in ExtendedTestResult API's), so to implement passthrough_stream they
|
||||
are diverted and copied directly when that is set.
|
||||
|
||||
:param input_stream: A stream containing subunit input.
|
||||
:param result: A TestResult that will receive the test events.
|
||||
NB: This should be an ExtendedTestResult for v1 and a StreamResult for
|
||||
v2.
|
||||
:param passthrough_stream: All non-subunit input received will be
|
||||
sent to this stream. If not provided, uses the ``TestProtocolServer``
|
||||
default, which is ``sys.stdout``.
|
||||
:param forward_stream: All subunit input received will be forwarded
|
||||
to this stream. If not provided, uses the ``TestProtocolServer``
|
||||
default, which is to not forward any input. Do not set this when
|
||||
transforming the stream - items would be double-reported.
|
||||
:param protocol_version: What version of the subunit protocol to expect.
|
||||
:param passthrough_subunit: If True, passthrough should be as subunit
|
||||
otherwise unwrap it. Only has effect when forward_stream is None.
|
||||
(when forwarding as subunit non-subunit input is always turned into
|
||||
subunit)
|
||||
"""
|
||||
if 1==protocol_version:
|
||||
test = ProtocolTestCase(
|
||||
input_stream, passthrough=passthrough_stream,
|
||||
forward=forward_stream)
|
||||
elif 2==protocol_version:
|
||||
# In all cases we encapsulate unknown inputs.
|
||||
if forward_stream is not None:
|
||||
# Send events to forward_stream as subunit.
|
||||
forward_result = StreamResultToBytes(forward_stream)
|
||||
# If we're passing non-subunit through, copy:
|
||||
if passthrough_stream is None:
|
||||
# Not passing non-test events - split them off to nothing.
|
||||
router = StreamResultRouter(forward_result)
|
||||
router.add_rule(StreamResult(), 'test_id', test_id=None)
|
||||
result = CopyStreamResult([router, result])
|
||||
else:
|
||||
# otherwise, copy all events to forward_result
|
||||
result = CopyStreamResult([forward_result, result])
|
||||
elif passthrough_stream is not None:
|
||||
if not passthrough_subunit:
|
||||
# Route non-test events to passthrough_stream, unwrapping them for
|
||||
# display.
|
||||
passthrough_result = CatFiles(passthrough_stream)
|
||||
else:
|
||||
passthrough_result = StreamResultToBytes(passthrough_stream)
|
||||
result = StreamResultRouter(result)
|
||||
result.add_rule(passthrough_result, 'test_id', test_id=None)
|
||||
test = ByteStreamToStreamResult(input_stream,
|
||||
non_subunit_name='stdout')
|
||||
else:
|
||||
raise Exception("Unknown protocol version.")
|
||||
result.startTestRun()
|
||||
test.run(result)
|
||||
result.stopTestRun()
|
||||
|
||||
|
||||
def filter_by_result(result_factory, output_path, passthrough, forward,
|
||||
input_stream=sys.stdin, protocol_version=1,
|
||||
passthrough_subunit=True):
|
||||
"""Filter an input stream using a test result.
|
||||
|
||||
:param result_factory: A callable that when passed an output stream
|
||||
returns a TestResult. It is expected that this result will output
|
||||
to the given stream.
|
||||
:param output_path: A path send output to. If None, output will be go
|
||||
to ``sys.stdout``.
|
||||
:param passthrough: If True, all non-subunit input will be sent to
|
||||
``sys.stdout``. If False, that input will be discarded.
|
||||
:param forward: If True, all subunit input will be forwarded directly to
|
||||
``sys.stdout`` as well as to the ``TestResult``.
|
||||
:param input_stream: The source of subunit input. Defaults to
|
||||
``sys.stdin``.
|
||||
:param protocol_version: The subunit protocol version to expect.
|
||||
:param passthrough_subunit: If True, passthrough should be as subunit.
|
||||
:return: A test result with the results of the run.
|
||||
"""
|
||||
if passthrough:
|
||||
passthrough_stream = sys.stdout
|
||||
else:
|
||||
if 1==protocol_version:
|
||||
passthrough_stream = DiscardStream()
|
||||
else:
|
||||
passthrough_stream = None
|
||||
|
||||
if forward:
|
||||
forward_stream = sys.stdout
|
||||
elif 1==protocol_version:
|
||||
forward_stream = DiscardStream()
|
||||
else:
|
||||
forward_stream = None
|
||||
|
||||
if output_path is None:
|
||||
output_to = sys.stdout
|
||||
else:
|
||||
output_to = file(output_path, 'wb')
|
||||
|
||||
try:
|
||||
result = result_factory(output_to)
|
||||
run_tests_from_stream(
|
||||
input_stream, result, passthrough_stream, forward_stream,
|
||||
protocol_version=protocol_version,
|
||||
passthrough_subunit=passthrough_subunit)
|
||||
finally:
|
||||
if output_path:
|
||||
output_to.close()
|
||||
return result
|
||||
|
||||
|
||||
def run_filter_script(result_factory, description, post_run_hook=None,
|
||||
protocol_version=1, passthrough_subunit=True):
|
||||
"""Main function for simple subunit filter scripts.
|
||||
|
||||
Many subunit filter scripts take a stream of subunit input and use a
|
||||
TestResult to handle the events generated by that stream. This function
|
||||
wraps a lot of the boiler-plate around that by making a script with
|
||||
options for handling passthrough information and stream forwarding, and
|
||||
that will exit with a successful return code (i.e. 0) if the input stream
|
||||
represents a successful test run.
|
||||
|
||||
:param result_factory: A callable that takes an output stream and returns
|
||||
a test result that outputs to that stream.
|
||||
:param description: A description of the filter script.
|
||||
:param protocol_version: What protocol version to consume/emit.
|
||||
:param passthrough_subunit: If True, passthrough should be as subunit.
|
||||
"""
|
||||
parser = make_options(description)
|
||||
(options, args) = parser.parse_args()
|
||||
result = filter_by_result(
|
||||
result_factory, options.output_to, not options.no_passthrough,
|
||||
options.forward, protocol_version=protocol_version,
|
||||
passthrough_subunit=passthrough_subunit,
|
||||
input_stream=find_stream(sys.stdin, args))
|
||||
if post_run_hook:
|
||||
post_run_hook(result)
|
||||
if not safe_hasattr(result, 'wasSuccessful'):
|
||||
result = result.decorated
|
||||
if result.wasSuccessful():
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def find_stream(stdin, argv):
|
||||
"""Find a stream to use as input for filters.
|
||||
|
||||
:param stdin: Standard in - used if no files are named in argv.
|
||||
:param argv: Command line arguments after option parsing. If one file
|
||||
is named, that is opened in read only binary mode and returned.
|
||||
A missing file will raise an exception, as will multiple file names.
|
||||
"""
|
||||
assert len(argv) < 2, "Too many filenames."
|
||||
if argv:
|
||||
return open(argv[0], 'rb')
|
||||
else:
|
||||
return stdin
|
||||
133
test/3rdparty/python-subunit-0.0.16/python/subunit/iso8601.py
vendored
Normal file
133
test/3rdparty/python-subunit-0.0.16/python/subunit/iso8601.py
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
# Copyright (c) 2007 Michael Twomey
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""ISO 8601 date time string parsing
|
||||
|
||||
Basic usage:
|
||||
>>> import iso8601
|
||||
>>> iso8601.parse_date("2007-01-25T12:00:00Z")
|
||||
datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.iso8601.Utc ...>)
|
||||
>>>
|
||||
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta, tzinfo
|
||||
import re
|
||||
import sys
|
||||
|
||||
__all__ = ["parse_date", "ParseError"]
|
||||
|
||||
# Adapted from http://delete.me.uk/2005/03/iso8601.html
|
||||
ISO8601_REGEX_PATTERN = (r"(?P<year>[0-9]{4})(-(?P<month>[0-9]{1,2})(-(?P<day>[0-9]{1,2})"
|
||||
r"((?P<separator>.)(?P<hour>[0-9]{2}):(?P<minute>[0-9]{2})(:(?P<second>[0-9]{2})(\.(?P<fraction>[0-9]+))?)?"
|
||||
r"(?P<timezone>Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?"
|
||||
)
|
||||
TIMEZONE_REGEX_PATTERN = "(?P<prefix>[+-])(?P<hours>[0-9]{2}).(?P<minutes>[0-9]{2})"
|
||||
ISO8601_REGEX = re.compile(ISO8601_REGEX_PATTERN.encode('utf8'))
|
||||
TIMEZONE_REGEX = re.compile(TIMEZONE_REGEX_PATTERN.encode('utf8'))
|
||||
|
||||
zulu = "Z".encode('latin-1')
|
||||
minus = "-".encode('latin-1')
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
bytes = str
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
"""Raised when there is a problem parsing a date string"""
|
||||
|
||||
# Yoinked from python docs
|
||||
ZERO = timedelta(0)
|
||||
class Utc(tzinfo):
|
||||
"""UTC
|
||||
|
||||
"""
|
||||
def utcoffset(self, dt):
|
||||
return ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
return "UTC"
|
||||
|
||||
def dst(self, dt):
|
||||
return ZERO
|
||||
UTC = Utc()
|
||||
|
||||
class FixedOffset(tzinfo):
|
||||
"""Fixed offset in hours and minutes from UTC
|
||||
|
||||
"""
|
||||
def __init__(self, offset_hours, offset_minutes, name):
|
||||
self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes)
|
||||
self.__name = name
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self.__offset
|
||||
|
||||
def tzname(self, dt):
|
||||
return self.__name
|
||||
|
||||
def dst(self, dt):
|
||||
return ZERO
|
||||
|
||||
def __repr__(self):
|
||||
return "<FixedOffset %r>" % self.__name
|
||||
|
||||
def parse_timezone(tzstring, default_timezone=UTC):
|
||||
"""Parses ISO 8601 time zone specs into tzinfo offsets
|
||||
|
||||
"""
|
||||
if tzstring == zulu:
|
||||
return default_timezone
|
||||
# This isn't strictly correct, but it's common to encounter dates without
|
||||
# timezones so I'll assume the default (which defaults to UTC).
|
||||
# Addresses issue 4.
|
||||
if tzstring is None:
|
||||
return default_timezone
|
||||
m = TIMEZONE_REGEX.match(tzstring)
|
||||
prefix, hours, minutes = m.groups()
|
||||
hours, minutes = int(hours), int(minutes)
|
||||
if prefix == minus:
|
||||
hours = -hours
|
||||
minutes = -minutes
|
||||
return FixedOffset(hours, minutes, tzstring)
|
||||
|
||||
def parse_date(datestring, default_timezone=UTC):
|
||||
"""Parses ISO 8601 dates into datetime objects
|
||||
|
||||
The timezone is parsed from the date string. However it is quite common to
|
||||
have dates without a timezone (not strictly correct). In this case the
|
||||
default timezone specified in default_timezone is used. This is UTC by
|
||||
default.
|
||||
"""
|
||||
if not isinstance(datestring, bytes):
|
||||
raise ParseError("Expecting bytes %r" % datestring)
|
||||
m = ISO8601_REGEX.match(datestring)
|
||||
if not m:
|
||||
raise ParseError("Unable to parse date string %r" % datestring)
|
||||
groups = m.groupdict()
|
||||
tz = parse_timezone(groups["timezone"], default_timezone=default_timezone)
|
||||
if groups["fraction"] is None:
|
||||
groups["fraction"] = 0
|
||||
else:
|
||||
groups["fraction"] = int(float("0.%s" % groups["fraction"].decode()) * 1e6)
|
||||
return datetime(int(groups["year"]), int(groups["month"]), int(groups["day"]),
|
||||
int(groups["hour"]), int(groups["minute"]), int(groups["second"]),
|
||||
int(groups["fraction"]), tz)
|
||||
106
test/3rdparty/python-subunit-0.0.16/python/subunit/progress_model.py
vendored
Normal file
106
test/3rdparty/python-subunit-0.0.16/python/subunit/progress_model.py
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
#
|
||||
# subunit: extensions to Python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
"""Support for dealing with progress state."""
|
||||
|
||||
class ProgressModel(object):
|
||||
"""A model of progress indicators as subunit defines it.
|
||||
|
||||
Instances of this class represent a single logical operation that is
|
||||
progressing. The operation may have many steps, and some of those steps may
|
||||
supply their own progress information. ProgressModel uses a nested concept
|
||||
where the overall state can be pushed, creating new starting state, and
|
||||
later pushed to return to the prior state. Many user interfaces will want
|
||||
to display an overall summary though, and accordingly the pos() and width()
|
||||
methods return overall summary information rather than information on the
|
||||
current subtask.
|
||||
|
||||
The default state is 0/0 - indicating that the overall progress is unknown.
|
||||
Anytime the denominator of pos/width is 0, rendering of a ProgressModel
|
||||
should should take this into consideration.
|
||||
|
||||
:ivar: _tasks. This private attribute stores the subtasks. Each is a tuple:
|
||||
pos, width, overall_numerator, overall_denominator. The overall fields
|
||||
store the calculated overall numerator and denominator for the state
|
||||
that was pushed.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Create a ProgressModel.
|
||||
|
||||
The new model has no progress data at all - it will claim a summary
|
||||
width of zero and position of 0.
|
||||
"""
|
||||
self._tasks = []
|
||||
self.push()
|
||||
|
||||
def adjust_width(self, offset):
|
||||
"""Adjust the with of the current subtask."""
|
||||
self._tasks[-1][1] += offset
|
||||
|
||||
def advance(self):
|
||||
"""Advance the current subtask."""
|
||||
self._tasks[-1][0] += 1
|
||||
|
||||
def pop(self):
|
||||
"""Pop a subtask off the ProgressModel.
|
||||
|
||||
See push for a description of how push and pop work.
|
||||
"""
|
||||
self._tasks.pop()
|
||||
|
||||
def pos(self):
|
||||
"""Return how far through the operation has progressed."""
|
||||
if not self._tasks:
|
||||
return 0
|
||||
task = self._tasks[-1]
|
||||
if len(self._tasks) > 1:
|
||||
# scale up the overall pos by the current task or preserve it if
|
||||
# no current width is known.
|
||||
offset = task[2] * (task[1] or 1)
|
||||
else:
|
||||
offset = 0
|
||||
return offset + task[0]
|
||||
|
||||
def push(self):
|
||||
"""Push a new subtask.
|
||||
|
||||
After pushing a new subtask, the overall progress hasn't changed. Calls
|
||||
to adjust_width, advance, set_width will only after the progress within
|
||||
the range that calling 'advance' would have before - the subtask
|
||||
represents progressing one step in the earlier task.
|
||||
|
||||
Call pop() to restore the progress model to the state before push was
|
||||
called.
|
||||
"""
|
||||
self._tasks.append([0, 0, self.pos(), self.width()])
|
||||
|
||||
def set_width(self, width):
|
||||
"""Set the width of the current subtask."""
|
||||
self._tasks[-1][1] = width
|
||||
|
||||
def width(self):
|
||||
"""Return the total width of the operation."""
|
||||
if not self._tasks:
|
||||
return 0
|
||||
task = self._tasks[-1]
|
||||
if len(self._tasks) > 1:
|
||||
# scale up the overall width by the current task or preserve it if
|
||||
# no current width is known.
|
||||
return task[3] * (task[1] or 1)
|
||||
else:
|
||||
return task[1]
|
||||
|
||||
131
test/3rdparty/python-subunit-0.0.16/python/subunit/run.py
vendored
Executable file
131
test/3rdparty/python-subunit-0.0.16/python/subunit/run.py
vendored
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Simple subunit testrunner for python
|
||||
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
"""Run a unittest testcase reporting results as Subunit.
|
||||
|
||||
$ python -m subunit.run mylib.tests.test_suite
|
||||
"""
|
||||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
|
||||
from testtools import ExtendedToStreamDecorator
|
||||
from testtools.testsuite import iterate_tests
|
||||
|
||||
from subunit import StreamResultToBytes, get_default_formatter
|
||||
from subunit.test_results import AutoTimingTestResultDecorator
|
||||
from testtools.run import (
|
||||
BUFFEROUTPUT,
|
||||
CATCHBREAK,
|
||||
FAILFAST,
|
||||
list_test,
|
||||
TestProgram,
|
||||
USAGE_AS_MAIN,
|
||||
)
|
||||
|
||||
|
||||
class SubunitTestRunner(object):
|
||||
def __init__(self, verbosity=None, failfast=None, buffer=None, stream=None):
|
||||
"""Create a TestToolsTestRunner.
|
||||
|
||||
:param verbosity: Ignored.
|
||||
:param failfast: Stop running tests at the first failure.
|
||||
:param buffer: Ignored.
|
||||
"""
|
||||
self.failfast = failfast
|
||||
self.stream = stream or sys.stdout
|
||||
|
||||
def run(self, test):
|
||||
"Run the given test case or test suite."
|
||||
result, _ = self._list(test)
|
||||
result = ExtendedToStreamDecorator(result)
|
||||
result = AutoTimingTestResultDecorator(result)
|
||||
if self.failfast is not None:
|
||||
result.failfast = self.failfast
|
||||
result.startTestRun()
|
||||
try:
|
||||
test(result)
|
||||
finally:
|
||||
result.stopTestRun()
|
||||
return result
|
||||
|
||||
def list(self, test):
|
||||
"List the test."
|
||||
result, errors = self._list(test)
|
||||
if errors:
|
||||
failed_descr = '\n'.join(errors).encode('utf8')
|
||||
result.status(file_name="import errors", runnable=False,
|
||||
file_bytes=failed_descr, mime_type="text/plain;charset=utf8")
|
||||
sys.exit(2)
|
||||
|
||||
def _list(self, test):
|
||||
test_ids, errors = list_test(test)
|
||||
try:
|
||||
fileno = self.stream.fileno()
|
||||
except:
|
||||
fileno = None
|
||||
if fileno is not None:
|
||||
stream = os.fdopen(fileno, 'wb', 0)
|
||||
else:
|
||||
stream = self.stream
|
||||
result = StreamResultToBytes(stream)
|
||||
for test_id in test_ids:
|
||||
result.status(test_id=test_id, test_status='exists')
|
||||
return result, errors
|
||||
|
||||
|
||||
class SubunitTestProgram(TestProgram):
|
||||
|
||||
USAGE = USAGE_AS_MAIN
|
||||
|
||||
def usageExit(self, msg=None):
|
||||
if msg:
|
||||
print (msg)
|
||||
usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '',
|
||||
'buffer': ''}
|
||||
if self.failfast != False:
|
||||
usage['failfast'] = FAILFAST
|
||||
if self.catchbreak != False:
|
||||
usage['catchbreak'] = CATCHBREAK
|
||||
if self.buffer != False:
|
||||
usage['buffer'] = BUFFEROUTPUT
|
||||
usage_text = self.USAGE % usage
|
||||
usage_lines = usage_text.split('\n')
|
||||
usage_lines.insert(2, "Run a test suite with a subunit reporter.")
|
||||
usage_lines.insert(3, "")
|
||||
print('\n'.join(usage_lines))
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def main():
|
||||
# Disable the default buffering, for Python 2.x where pdb doesn't do it
|
||||
# on non-ttys.
|
||||
stream = get_default_formatter()
|
||||
runner = SubunitTestRunner
|
||||
# Patch stdout to be unbuffered, so that pdb works well on 2.6/2.7.
|
||||
binstdout = io.open(sys.stdout.fileno(), 'wb', 0)
|
||||
if sys.version_info[0] > 2:
|
||||
sys.stdout = io.TextIOWrapper(binstdout, encoding=sys.stdout.encoding)
|
||||
else:
|
||||
sys.stdout = binstdout
|
||||
SubunitTestProgram(module=None, argv=sys.argv, testRunner=runner,
|
||||
stdout=sys.stdout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
729
test/3rdparty/python-subunit-0.0.16/python/subunit/test_results.py
vendored
Normal file
729
test/3rdparty/python-subunit-0.0.16/python/subunit/test_results.py
vendored
Normal file
@@ -0,0 +1,729 @@
|
||||
#
|
||||
# subunit: extensions to Python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
"""TestResult helper classes used to by subunit."""
|
||||
|
||||
import csv
|
||||
import datetime
|
||||
|
||||
import testtools
|
||||
from testtools.compat import all
|
||||
from testtools.content import (
|
||||
text_content,
|
||||
TracebackContent,
|
||||
)
|
||||
from testtools import StreamResult
|
||||
|
||||
from subunit import iso8601
|
||||
import subunit
|
||||
|
||||
|
||||
# NOT a TestResult, because we are implementing the interface, not inheriting
|
||||
# it.
|
||||
class TestResultDecorator(object):
|
||||
"""General pass-through decorator.
|
||||
|
||||
This provides a base that other TestResults can inherit from to
|
||||
gain basic forwarding functionality. It also takes care of
|
||||
handling the case where the target doesn't support newer methods
|
||||
or features by degrading them.
|
||||
"""
|
||||
|
||||
# XXX: Since lp:testtools r250, this is in testtools. Once it's released,
|
||||
# we should gut this and just use that.
|
||||
|
||||
def __init__(self, decorated):
|
||||
"""Create a TestResultDecorator forwarding to decorated."""
|
||||
# Make every decorator degrade gracefully.
|
||||
self.decorated = testtools.ExtendedToOriginalDecorator(decorated)
|
||||
|
||||
def startTest(self, test):
|
||||
return self.decorated.startTest(test)
|
||||
|
||||
def startTestRun(self):
|
||||
return self.decorated.startTestRun()
|
||||
|
||||
def stopTest(self, test):
|
||||
return self.decorated.stopTest(test)
|
||||
|
||||
def stopTestRun(self):
|
||||
return self.decorated.stopTestRun()
|
||||
|
||||
def addError(self, test, err=None, details=None):
|
||||
return self.decorated.addError(test, err, details=details)
|
||||
|
||||
def addFailure(self, test, err=None, details=None):
|
||||
return self.decorated.addFailure(test, err, details=details)
|
||||
|
||||
def addSuccess(self, test, details=None):
|
||||
return self.decorated.addSuccess(test, details=details)
|
||||
|
||||
def addSkip(self, test, reason=None, details=None):
|
||||
return self.decorated.addSkip(test, reason, details=details)
|
||||
|
||||
def addExpectedFailure(self, test, err=None, details=None):
|
||||
return self.decorated.addExpectedFailure(test, err, details=details)
|
||||
|
||||
def addUnexpectedSuccess(self, test, details=None):
|
||||
return self.decorated.addUnexpectedSuccess(test, details=details)
|
||||
|
||||
def _get_failfast(self):
|
||||
return getattr(self.decorated, 'failfast', False)
|
||||
|
||||
def _set_failfast(self, value):
|
||||
self.decorated.failfast = value
|
||||
failfast = property(_get_failfast, _set_failfast)
|
||||
|
||||
def progress(self, offset, whence):
|
||||
return self.decorated.progress(offset, whence)
|
||||
|
||||
def wasSuccessful(self):
|
||||
return self.decorated.wasSuccessful()
|
||||
|
||||
@property
|
||||
def shouldStop(self):
|
||||
return self.decorated.shouldStop
|
||||
|
||||
def stop(self):
|
||||
return self.decorated.stop()
|
||||
|
||||
@property
|
||||
def testsRun(self):
|
||||
return self.decorated.testsRun
|
||||
|
||||
def tags(self, new_tags, gone_tags):
|
||||
return self.decorated.tags(new_tags, gone_tags)
|
||||
|
||||
def time(self, a_datetime):
|
||||
return self.decorated.time(a_datetime)
|
||||
|
||||
|
||||
class HookedTestResultDecorator(TestResultDecorator):
|
||||
"""A TestResult which calls a hook on every event."""
|
||||
|
||||
def __init__(self, decorated):
|
||||
self.super = super(HookedTestResultDecorator, self)
|
||||
self.super.__init__(decorated)
|
||||
|
||||
def startTest(self, test):
|
||||
self._before_event()
|
||||
return self.super.startTest(test)
|
||||
|
||||
def startTestRun(self):
|
||||
self._before_event()
|
||||
return self.super.startTestRun()
|
||||
|
||||
def stopTest(self, test):
|
||||
self._before_event()
|
||||
return self.super.stopTest(test)
|
||||
|
||||
def stopTestRun(self):
|
||||
self._before_event()
|
||||
return self.super.stopTestRun()
|
||||
|
||||
def addError(self, test, err=None, details=None):
|
||||
self._before_event()
|
||||
return self.super.addError(test, err, details=details)
|
||||
|
||||
def addFailure(self, test, err=None, details=None):
|
||||
self._before_event()
|
||||
return self.super.addFailure(test, err, details=details)
|
||||
|
||||
def addSuccess(self, test, details=None):
|
||||
self._before_event()
|
||||
return self.super.addSuccess(test, details=details)
|
||||
|
||||
def addSkip(self, test, reason=None, details=None):
|
||||
self._before_event()
|
||||
return self.super.addSkip(test, reason, details=details)
|
||||
|
||||
def addExpectedFailure(self, test, err=None, details=None):
|
||||
self._before_event()
|
||||
return self.super.addExpectedFailure(test, err, details=details)
|
||||
|
||||
def addUnexpectedSuccess(self, test, details=None):
|
||||
self._before_event()
|
||||
return self.super.addUnexpectedSuccess(test, details=details)
|
||||
|
||||
def progress(self, offset, whence):
|
||||
self._before_event()
|
||||
return self.super.progress(offset, whence)
|
||||
|
||||
def wasSuccessful(self):
|
||||
self._before_event()
|
||||
return self.super.wasSuccessful()
|
||||
|
||||
@property
|
||||
def shouldStop(self):
|
||||
self._before_event()
|
||||
return self.super.shouldStop
|
||||
|
||||
def stop(self):
|
||||
self._before_event()
|
||||
return self.super.stop()
|
||||
|
||||
def time(self, a_datetime):
|
||||
self._before_event()
|
||||
return self.super.time(a_datetime)
|
||||
|
||||
|
||||
class AutoTimingTestResultDecorator(HookedTestResultDecorator):
|
||||
"""Decorate a TestResult to add time events to a test run.
|
||||
|
||||
By default this will cause a time event before every test event,
|
||||
but if explicit time data is being provided by the test run, then
|
||||
this decorator will turn itself off to prevent causing confusion.
|
||||
"""
|
||||
|
||||
def __init__(self, decorated):
|
||||
self._time = None
|
||||
super(AutoTimingTestResultDecorator, self).__init__(decorated)
|
||||
|
||||
def _before_event(self):
|
||||
time = self._time
|
||||
if time is not None:
|
||||
return
|
||||
time = datetime.datetime.utcnow().replace(tzinfo=iso8601.Utc())
|
||||
self.decorated.time(time)
|
||||
|
||||
def progress(self, offset, whence):
|
||||
return self.decorated.progress(offset, whence)
|
||||
|
||||
@property
|
||||
def shouldStop(self):
|
||||
return self.decorated.shouldStop
|
||||
|
||||
def time(self, a_datetime):
|
||||
"""Provide a timestamp for the current test activity.
|
||||
|
||||
:param a_datetime: If None, automatically add timestamps before every
|
||||
event (this is the default behaviour if time() is not called at
|
||||
all). If not None, pass the provided time onto the decorated
|
||||
result object and disable automatic timestamps.
|
||||
"""
|
||||
self._time = a_datetime
|
||||
return self.decorated.time(a_datetime)
|
||||
|
||||
|
||||
class TagsMixin(object):
|
||||
|
||||
def __init__(self):
|
||||
self._clear_tags()
|
||||
|
||||
def _clear_tags(self):
|
||||
self._global_tags = set(), set()
|
||||
self._test_tags = None
|
||||
|
||||
def _get_active_tags(self):
|
||||
global_new, global_gone = self._global_tags
|
||||
if self._test_tags is None:
|
||||
return set(global_new)
|
||||
test_new, test_gone = self._test_tags
|
||||
return global_new.difference(test_gone).union(test_new)
|
||||
|
||||
def _get_current_scope(self):
|
||||
if self._test_tags:
|
||||
return self._test_tags
|
||||
return self._global_tags
|
||||
|
||||
def _flush_current_scope(self, tag_receiver):
|
||||
new_tags, gone_tags = self._get_current_scope()
|
||||
if new_tags or gone_tags:
|
||||
tag_receiver.tags(new_tags, gone_tags)
|
||||
if self._test_tags:
|
||||
self._test_tags = set(), set()
|
||||
else:
|
||||
self._global_tags = set(), set()
|
||||
|
||||
def startTestRun(self):
|
||||
self._clear_tags()
|
||||
|
||||
def startTest(self, test):
|
||||
self._test_tags = set(), set()
|
||||
|
||||
def stopTest(self, test):
|
||||
self._test_tags = None
|
||||
|
||||
def tags(self, new_tags, gone_tags):
|
||||
"""Handle tag instructions.
|
||||
|
||||
Adds and removes tags as appropriate. If a test is currently running,
|
||||
tags are not affected for subsequent tests.
|
||||
|
||||
:param new_tags: Tags to add,
|
||||
:param gone_tags: Tags to remove.
|
||||
"""
|
||||
current_new_tags, current_gone_tags = self._get_current_scope()
|
||||
current_new_tags.update(new_tags)
|
||||
current_new_tags.difference_update(gone_tags)
|
||||
current_gone_tags.update(gone_tags)
|
||||
current_gone_tags.difference_update(new_tags)
|
||||
|
||||
|
||||
class TagCollapsingDecorator(HookedTestResultDecorator, TagsMixin):
|
||||
"""Collapses many 'tags' calls into one where possible."""
|
||||
|
||||
def __init__(self, result):
|
||||
super(TagCollapsingDecorator, self).__init__(result)
|
||||
self._clear_tags()
|
||||
|
||||
def _before_event(self):
|
||||
self._flush_current_scope(self.decorated)
|
||||
|
||||
def tags(self, new_tags, gone_tags):
|
||||
TagsMixin.tags(self, new_tags, gone_tags)
|
||||
|
||||
|
||||
class TimeCollapsingDecorator(HookedTestResultDecorator):
|
||||
"""Only pass on the first and last of a consecutive sequence of times."""
|
||||
|
||||
def __init__(self, decorated):
|
||||
super(TimeCollapsingDecorator, self).__init__(decorated)
|
||||
self._last_received_time = None
|
||||
self._last_sent_time = None
|
||||
|
||||
def _before_event(self):
|
||||
if self._last_received_time is None:
|
||||
return
|
||||
if self._last_received_time != self._last_sent_time:
|
||||
self.decorated.time(self._last_received_time)
|
||||
self._last_sent_time = self._last_received_time
|
||||
self._last_received_time = None
|
||||
|
||||
def time(self, a_time):
|
||||
# Don't upcall, because we don't want to call _before_event, it's only
|
||||
# for non-time events.
|
||||
if self._last_received_time is None:
|
||||
self.decorated.time(a_time)
|
||||
self._last_sent_time = a_time
|
||||
self._last_received_time = a_time
|
||||
|
||||
|
||||
def and_predicates(predicates):
|
||||
"""Return a predicate that is true iff all predicates are true."""
|
||||
# XXX: Should probably be in testtools to be better used by matchers. jml
|
||||
return lambda *args, **kwargs: all(p(*args, **kwargs) for p in predicates)
|
||||
|
||||
|
||||
def make_tag_filter(with_tags, without_tags):
|
||||
"""Make a callback that checks tests against tags."""
|
||||
|
||||
with_tags = with_tags and set(with_tags) or None
|
||||
without_tags = without_tags and set(without_tags) or None
|
||||
|
||||
def check_tags(test, outcome, err, details, tags):
|
||||
if with_tags and not with_tags <= tags:
|
||||
return False
|
||||
if without_tags and bool(without_tags & tags):
|
||||
return False
|
||||
return True
|
||||
|
||||
return check_tags
|
||||
|
||||
|
||||
class _PredicateFilter(TestResultDecorator, TagsMixin):
|
||||
|
||||
def __init__(self, result, predicate):
|
||||
super(_PredicateFilter, self).__init__(result)
|
||||
self._clear_tags()
|
||||
self.decorated = TimeCollapsingDecorator(
|
||||
TagCollapsingDecorator(self.decorated))
|
||||
self._predicate = predicate
|
||||
# The current test (for filtering tags)
|
||||
self._current_test = None
|
||||
# Has the current test been filtered (for outputting test tags)
|
||||
self._current_test_filtered = None
|
||||
# Calls to this result that we don't know whether to forward on yet.
|
||||
self._buffered_calls = []
|
||||
|
||||
def filter_predicate(self, test, outcome, error, details):
|
||||
return self._predicate(
|
||||
test, outcome, error, details, self._get_active_tags())
|
||||
|
||||
def addError(self, test, err=None, details=None):
|
||||
if (self.filter_predicate(test, 'error', err, details)):
|
||||
self._buffered_calls.append(
|
||||
('addError', [test, err], {'details': details}))
|
||||
else:
|
||||
self._filtered()
|
||||
|
||||
def addFailure(self, test, err=None, details=None):
|
||||
if (self.filter_predicate(test, 'failure', err, details)):
|
||||
self._buffered_calls.append(
|
||||
('addFailure', [test, err], {'details': details}))
|
||||
else:
|
||||
self._filtered()
|
||||
|
||||
def addSkip(self, test, reason=None, details=None):
|
||||
if (self.filter_predicate(test, 'skip', reason, details)):
|
||||
self._buffered_calls.append(
|
||||
('addSkip', [test, reason], {'details': details}))
|
||||
else:
|
||||
self._filtered()
|
||||
|
||||
def addExpectedFailure(self, test, err=None, details=None):
|
||||
if self.filter_predicate(test, 'expectedfailure', err, details):
|
||||
self._buffered_calls.append(
|
||||
('addExpectedFailure', [test, err], {'details': details}))
|
||||
else:
|
||||
self._filtered()
|
||||
|
||||
def addUnexpectedSuccess(self, test, details=None):
|
||||
self._buffered_calls.append(
|
||||
('addUnexpectedSuccess', [test], {'details': details}))
|
||||
|
||||
def addSuccess(self, test, details=None):
|
||||
if (self.filter_predicate(test, 'success', None, details)):
|
||||
self._buffered_calls.append(
|
||||
('addSuccess', [test], {'details': details}))
|
||||
else:
|
||||
self._filtered()
|
||||
|
||||
def _filtered(self):
|
||||
self._current_test_filtered = True
|
||||
|
||||
def startTest(self, test):
|
||||
"""Start a test.
|
||||
|
||||
Not directly passed to the client, but used for handling of tags
|
||||
correctly.
|
||||
"""
|
||||
TagsMixin.startTest(self, test)
|
||||
self._current_test = test
|
||||
self._current_test_filtered = False
|
||||
self._buffered_calls.append(('startTest', [test], {}))
|
||||
|
||||
def stopTest(self, test):
|
||||
"""Stop a test.
|
||||
|
||||
Not directly passed to the client, but used for handling of tags
|
||||
correctly.
|
||||
"""
|
||||
if not self._current_test_filtered:
|
||||
for method, args, kwargs in self._buffered_calls:
|
||||
getattr(self.decorated, method)(*args, **kwargs)
|
||||
self.decorated.stopTest(test)
|
||||
self._current_test = None
|
||||
self._current_test_filtered = None
|
||||
self._buffered_calls = []
|
||||
TagsMixin.stopTest(self, test)
|
||||
|
||||
def tags(self, new_tags, gone_tags):
|
||||
TagsMixin.tags(self, new_tags, gone_tags)
|
||||
if self._current_test is not None:
|
||||
self._buffered_calls.append(('tags', [new_tags, gone_tags], {}))
|
||||
else:
|
||||
return super(_PredicateFilter, self).tags(new_tags, gone_tags)
|
||||
|
||||
def time(self, a_time):
|
||||
return self.decorated.time(a_time)
|
||||
|
||||
def id_to_orig_id(self, id):
|
||||
if id.startswith("subunit.RemotedTestCase."):
|
||||
return id[len("subunit.RemotedTestCase."):]
|
||||
return id
|
||||
|
||||
|
||||
class TestResultFilter(TestResultDecorator):
|
||||
"""A pyunit TestResult interface implementation which filters tests.
|
||||
|
||||
Tests that pass the filter are handed on to another TestResult instance
|
||||
for further processing/reporting. To obtain the filtered results,
|
||||
the other instance must be interrogated.
|
||||
|
||||
:ivar result: The result that tests are passed to after filtering.
|
||||
:ivar filter_predicate: The callback run to decide whether to pass
|
||||
a result.
|
||||
"""
|
||||
|
||||
def __init__(self, result, filter_error=False, filter_failure=False,
|
||||
filter_success=True, filter_skip=False, filter_xfail=False,
|
||||
filter_predicate=None, fixup_expected_failures=None):
|
||||
"""Create a FilterResult object filtering to result.
|
||||
|
||||
:param filter_error: Filter out errors.
|
||||
:param filter_failure: Filter out failures.
|
||||
:param filter_success: Filter out successful tests.
|
||||
:param filter_skip: Filter out skipped tests.
|
||||
:param filter_xfail: Filter out expected failure tests.
|
||||
:param filter_predicate: A callable taking (test, outcome, err,
|
||||
details, tags) and returning True if the result should be passed
|
||||
through. err and details may be none if no error or extra
|
||||
metadata is available. outcome is the name of the outcome such
|
||||
as 'success' or 'failure'. tags is new in 0.0.8; 0.0.7 filters
|
||||
are still supported but should be updated to accept the tags
|
||||
parameter for efficiency.
|
||||
:param fixup_expected_failures: Set of test ids to consider known
|
||||
failing.
|
||||
"""
|
||||
predicates = []
|
||||
if filter_error:
|
||||
predicates.append(
|
||||
lambda t, outcome, e, d, tags: outcome != 'error')
|
||||
if filter_failure:
|
||||
predicates.append(
|
||||
lambda t, outcome, e, d, tags: outcome != 'failure')
|
||||
if filter_success:
|
||||
predicates.append(
|
||||
lambda t, outcome, e, d, tags: outcome != 'success')
|
||||
if filter_skip:
|
||||
predicates.append(
|
||||
lambda t, outcome, e, d, tags: outcome != 'skip')
|
||||
if filter_xfail:
|
||||
predicates.append(
|
||||
lambda t, outcome, e, d, tags: outcome != 'expectedfailure')
|
||||
if filter_predicate is not None:
|
||||
def compat(test, outcome, error, details, tags):
|
||||
# 0.0.7 and earlier did not support the 'tags' parameter.
|
||||
try:
|
||||
return filter_predicate(
|
||||
test, outcome, error, details, tags)
|
||||
except TypeError:
|
||||
return filter_predicate(test, outcome, error, details)
|
||||
predicates.append(compat)
|
||||
predicate = and_predicates(predicates)
|
||||
super(TestResultFilter, self).__init__(
|
||||
_PredicateFilter(result, predicate))
|
||||
if fixup_expected_failures is None:
|
||||
self._fixup_expected_failures = frozenset()
|
||||
else:
|
||||
self._fixup_expected_failures = fixup_expected_failures
|
||||
|
||||
def addError(self, test, err=None, details=None):
|
||||
if self._failure_expected(test):
|
||||
self.addExpectedFailure(test, err=err, details=details)
|
||||
else:
|
||||
super(TestResultFilter, self).addError(
|
||||
test, err=err, details=details)
|
||||
|
||||
def addFailure(self, test, err=None, details=None):
|
||||
if self._failure_expected(test):
|
||||
self.addExpectedFailure(test, err=err, details=details)
|
||||
else:
|
||||
super(TestResultFilter, self).addFailure(
|
||||
test, err=err, details=details)
|
||||
|
||||
def addSuccess(self, test, details=None):
|
||||
if self._failure_expected(test):
|
||||
self.addUnexpectedSuccess(test, details=details)
|
||||
else:
|
||||
super(TestResultFilter, self).addSuccess(test, details=details)
|
||||
|
||||
def _failure_expected(self, test):
|
||||
return (test.id() in self._fixup_expected_failures)
|
||||
|
||||
|
||||
class TestIdPrintingResult(testtools.TestResult):
|
||||
"""Print test ids to a stream.
|
||||
|
||||
Implements both TestResult and StreamResult, for compatibility.
|
||||
"""
|
||||
|
||||
def __init__(self, stream, show_times=False, show_exists=False):
|
||||
"""Create a FilterResult object outputting to stream."""
|
||||
super(TestIdPrintingResult, self).__init__()
|
||||
self._stream = stream
|
||||
self.show_exists = show_exists
|
||||
self.show_times = show_times
|
||||
|
||||
def startTestRun(self):
|
||||
self.failed_tests = 0
|
||||
self.__time = None
|
||||
self._test = None
|
||||
self._test_duration = 0
|
||||
self._active_tests = {}
|
||||
|
||||
def addError(self, test, err):
|
||||
self.failed_tests += 1
|
||||
self._test = test
|
||||
|
||||
def addFailure(self, test, err):
|
||||
self.failed_tests += 1
|
||||
self._test = test
|
||||
|
||||
def addSuccess(self, test):
|
||||
self._test = test
|
||||
|
||||
def addSkip(self, test, reason=None, details=None):
|
||||
self._test = test
|
||||
|
||||
def addUnexpectedSuccess(self, test, details=None):
|
||||
self.failed_tests += 1
|
||||
self._test = test
|
||||
|
||||
def addExpectedFailure(self, test, err=None, details=None):
|
||||
self._test = test
|
||||
|
||||
def reportTest(self, test_id, duration):
|
||||
if self.show_times:
|
||||
seconds = duration.seconds
|
||||
seconds += duration.days * 3600 * 24
|
||||
seconds += duration.microseconds / 1000000.0
|
||||
self._stream.write(test_id + ' %0.3f\n' % seconds)
|
||||
else:
|
||||
self._stream.write(test_id + '\n')
|
||||
|
||||
def startTest(self, test):
|
||||
self._start_time = self._time()
|
||||
|
||||
def status(self, test_id=None, test_status=None, test_tags=None,
|
||||
runnable=True, file_name=None, file_bytes=None, eof=False,
|
||||
mime_type=None, route_code=None, timestamp=None):
|
||||
if not test_id:
|
||||
return
|
||||
if timestamp is not None:
|
||||
self.time(timestamp)
|
||||
if test_status=='exists':
|
||||
if self.show_exists:
|
||||
self.reportTest(test_id, 0)
|
||||
elif test_status in ('inprogress', None):
|
||||
self._active_tests[test_id] = self._time()
|
||||
else:
|
||||
self._end_test(test_id)
|
||||
|
||||
def _end_test(self, test_id):
|
||||
test_start = self._active_tests.pop(test_id, None)
|
||||
if not test_start:
|
||||
test_duration = 0
|
||||
else:
|
||||
test_duration = self._time() - test_start
|
||||
self.reportTest(test_id, test_duration)
|
||||
|
||||
def stopTest(self, test):
|
||||
test_duration = self._time() - self._start_time
|
||||
self.reportTest(self._test.id(), test_duration)
|
||||
|
||||
def time(self, time):
|
||||
self.__time = time
|
||||
|
||||
def _time(self):
|
||||
return self.__time
|
||||
|
||||
def wasSuccessful(self):
|
||||
"Tells whether or not this result was a success"
|
||||
return self.failed_tests == 0
|
||||
|
||||
def stopTestRun(self):
|
||||
for test_id in list(self._active_tests.keys()):
|
||||
self._end_test(test_id)
|
||||
|
||||
|
||||
class TestByTestResult(testtools.TestResult):
|
||||
"""Call something every time a test completes."""
|
||||
|
||||
# XXX: In testtools since lp:testtools r249. Once that's released, just
|
||||
# import that.
|
||||
|
||||
def __init__(self, on_test):
|
||||
"""Construct a ``TestByTestResult``.
|
||||
|
||||
:param on_test: A callable that take a test case, a status (one of
|
||||
"success", "failure", "error", "skip", or "xfail"), a start time
|
||||
(a ``datetime`` with timezone), a stop time, an iterable of tags,
|
||||
and a details dict. Is called at the end of each test (i.e. on
|
||||
``stopTest``) with the accumulated values for that test.
|
||||
"""
|
||||
super(TestByTestResult, self).__init__()
|
||||
self._on_test = on_test
|
||||
|
||||
def startTest(self, test):
|
||||
super(TestByTestResult, self).startTest(test)
|
||||
self._start_time = self._now()
|
||||
# There's no supported (i.e. tested) behaviour that relies on these
|
||||
# being set, but it makes me more comfortable all the same. -- jml
|
||||
self._status = None
|
||||
self._details = None
|
||||
self._stop_time = None
|
||||
|
||||
def stopTest(self, test):
|
||||
self._stop_time = self._now()
|
||||
super(TestByTestResult, self).stopTest(test)
|
||||
self._on_test(
|
||||
test=test,
|
||||
status=self._status,
|
||||
start_time=self._start_time,
|
||||
stop_time=self._stop_time,
|
||||
# current_tags is new in testtools 0.9.13.
|
||||
tags=getattr(self, 'current_tags', None),
|
||||
details=self._details)
|
||||
|
||||
def _err_to_details(self, test, err, details):
|
||||
if details:
|
||||
return details
|
||||
return {'traceback': TracebackContent(err, test)}
|
||||
|
||||
def addSuccess(self, test, details=None):
|
||||
super(TestByTestResult, self).addSuccess(test)
|
||||
self._status = 'success'
|
||||
self._details = details
|
||||
|
||||
def addFailure(self, test, err=None, details=None):
|
||||
super(TestByTestResult, self).addFailure(test, err, details)
|
||||
self._status = 'failure'
|
||||
self._details = self._err_to_details(test, err, details)
|
||||
|
||||
def addError(self, test, err=None, details=None):
|
||||
super(TestByTestResult, self).addError(test, err, details)
|
||||
self._status = 'error'
|
||||
self._details = self._err_to_details(test, err, details)
|
||||
|
||||
def addSkip(self, test, reason=None, details=None):
|
||||
super(TestByTestResult, self).addSkip(test, reason, details)
|
||||
self._status = 'skip'
|
||||
if details is None:
|
||||
details = {'reason': text_content(reason)}
|
||||
elif reason:
|
||||
# XXX: What if details already has 'reason' key?
|
||||
details['reason'] = text_content(reason)
|
||||
self._details = details
|
||||
|
||||
def addExpectedFailure(self, test, err=None, details=None):
|
||||
super(TestByTestResult, self).addExpectedFailure(test, err, details)
|
||||
self._status = 'xfail'
|
||||
self._details = self._err_to_details(test, err, details)
|
||||
|
||||
def addUnexpectedSuccess(self, test, details=None):
|
||||
super(TestByTestResult, self).addUnexpectedSuccess(test, details)
|
||||
self._status = 'success'
|
||||
self._details = details
|
||||
|
||||
|
||||
class CsvResult(TestByTestResult):
|
||||
|
||||
def __init__(self, stream):
|
||||
super(CsvResult, self).__init__(self._on_test)
|
||||
self._write_row = csv.writer(stream).writerow
|
||||
|
||||
def _on_test(self, test, status, start_time, stop_time, tags, details):
|
||||
self._write_row([test.id(), status, start_time, stop_time])
|
||||
|
||||
def startTestRun(self):
|
||||
super(CsvResult, self).startTestRun()
|
||||
self._write_row(['test', 'status', 'start_time', 'stop_time'])
|
||||
|
||||
|
||||
class CatFiles(StreamResult):
|
||||
"""Cat file attachments received to a stream."""
|
||||
|
||||
def __init__(self, byte_stream):
|
||||
self.stream = subunit.make_stream_binary(byte_stream)
|
||||
|
||||
def status(self, test_id=None, test_status=None, test_tags=None,
|
||||
runnable=True, file_name=None, file_bytes=None, eof=False,
|
||||
mime_type=None, route_code=None, timestamp=None):
|
||||
if file_name is not None:
|
||||
self.stream.write(file_bytes)
|
||||
self.stream.flush()
|
||||
63
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/__init__.py
vendored
Normal file
63
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/__init__.py
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
import sys
|
||||
from unittest import TestLoader
|
||||
|
||||
|
||||
# Before the test module imports to avoid circularity.
|
||||
# For testing: different pythons have different str() implementations.
|
||||
if sys.version_info > (3, 0):
|
||||
_remote_exception_repr = "testtools.testresult.real._StringException"
|
||||
_remote_exception_str = "Traceback (most recent call last):\ntesttools.testresult.real._StringException"
|
||||
_remote_exception_str_chunked = "57\r\n" + _remote_exception_str + ": boo qux\n0\r\n"
|
||||
else:
|
||||
_remote_exception_repr = "_StringException"
|
||||
_remote_exception_str = "Traceback (most recent call last):\n_StringException"
|
||||
_remote_exception_str_chunked = "3D\r\n" + _remote_exception_str + ": boo qux\n0\r\n"
|
||||
|
||||
|
||||
from subunit.tests import (
|
||||
test_chunked,
|
||||
test_details,
|
||||
test_filters,
|
||||
test_progress_model,
|
||||
test_run,
|
||||
test_subunit_filter,
|
||||
test_subunit_stats,
|
||||
test_subunit_tags,
|
||||
test_tap2subunit,
|
||||
test_test_protocol,
|
||||
test_test_protocol2,
|
||||
test_test_results,
|
||||
)
|
||||
|
||||
|
||||
def test_suite():
|
||||
loader = TestLoader()
|
||||
result = loader.loadTestsFromModule(test_chunked)
|
||||
result.addTest(loader.loadTestsFromModule(test_details))
|
||||
result.addTest(loader.loadTestsFromModule(test_filters))
|
||||
result.addTest(loader.loadTestsFromModule(test_progress_model))
|
||||
result.addTest(loader.loadTestsFromModule(test_test_results))
|
||||
result.addTest(loader.loadTestsFromModule(test_test_protocol))
|
||||
result.addTest(loader.loadTestsFromModule(test_test_protocol2))
|
||||
result.addTest(loader.loadTestsFromModule(test_tap2subunit))
|
||||
result.addTest(loader.loadTestsFromModule(test_subunit_filter))
|
||||
result.addTest(loader.loadTestsFromModule(test_subunit_tags))
|
||||
result.addTest(loader.loadTestsFromModule(test_subunit_stats))
|
||||
result.addTest(loader.loadTestsFromModule(test_run))
|
||||
return result
|
||||
21
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/sample-script.py
vendored
Executable file
21
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/sample-script.py
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
if sys.platform == "win32":
|
||||
import msvcrt, os
|
||||
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
||||
if len(sys.argv) == 2:
|
||||
# subunit.tests.test_test_protocol.TestExecTestCase.test_sample_method_args
|
||||
# uses this code path to be sure that the arguments were passed to
|
||||
# sample-script.py
|
||||
print("test fail")
|
||||
print("error fail")
|
||||
sys.exit(0)
|
||||
print("test old mcdonald")
|
||||
print("success old mcdonald")
|
||||
print("test bing crosby")
|
||||
print("failure bing crosby [")
|
||||
print("foo.c:53:ERROR invalid state")
|
||||
print("]")
|
||||
print("test an error")
|
||||
print("error an error")
|
||||
sys.exit(0)
|
||||
7
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/sample-two-script.py
vendored
Executable file
7
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/sample-two-script.py
vendored
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
print("test old mcdonald")
|
||||
print("success old mcdonald")
|
||||
print("test bing crosby")
|
||||
print("success bing crosby")
|
||||
sys.exit(0)
|
||||
146
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_chunked.py
vendored
Normal file
146
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_chunked.py
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
#
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
|
||||
# Copyright (C) 2011 Martin Pool <mbp@sourcefrog.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
from testtools.compat import _b, BytesIO
|
||||
|
||||
import subunit.chunked
|
||||
|
||||
|
||||
class TestDecode(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
unittest.TestCase.setUp(self)
|
||||
self.output = BytesIO()
|
||||
self.decoder = subunit.chunked.Decoder(self.output)
|
||||
|
||||
def test_close_read_length_short_errors(self):
|
||||
self.assertRaises(ValueError, self.decoder.close)
|
||||
|
||||
def test_close_body_short_errors(self):
|
||||
self.assertEqual(None, self.decoder.write(_b('2\r\na')))
|
||||
self.assertRaises(ValueError, self.decoder.close)
|
||||
|
||||
def test_close_body_buffered_data_errors(self):
|
||||
self.assertEqual(None, self.decoder.write(_b('2\r')))
|
||||
self.assertRaises(ValueError, self.decoder.close)
|
||||
|
||||
def test_close_after_finished_stream_safe(self):
|
||||
self.assertEqual(None, self.decoder.write(_b('2\r\nab')))
|
||||
self.assertEqual(_b(''), self.decoder.write(_b('0\r\n')))
|
||||
self.decoder.close()
|
||||
|
||||
def test_decode_nothing(self):
|
||||
self.assertEqual(_b(''), self.decoder.write(_b('0\r\n')))
|
||||
self.assertEqual(_b(''), self.output.getvalue())
|
||||
|
||||
def test_decode_serialised_form(self):
|
||||
self.assertEqual(None, self.decoder.write(_b("F\r\n")))
|
||||
self.assertEqual(None, self.decoder.write(_b("serialised\n")))
|
||||
self.assertEqual(_b(''), self.decoder.write(_b("form0\r\n")))
|
||||
|
||||
def test_decode_short(self):
|
||||
self.assertEqual(_b(''), self.decoder.write(_b('3\r\nabc0\r\n')))
|
||||
self.assertEqual(_b('abc'), self.output.getvalue())
|
||||
|
||||
def test_decode_combines_short(self):
|
||||
self.assertEqual(_b(''), self.decoder.write(_b('6\r\nabcdef0\r\n')))
|
||||
self.assertEqual(_b('abcdef'), self.output.getvalue())
|
||||
|
||||
def test_decode_excess_bytes_from_write(self):
|
||||
self.assertEqual(_b('1234'), self.decoder.write(_b('3\r\nabc0\r\n1234')))
|
||||
self.assertEqual(_b('abc'), self.output.getvalue())
|
||||
|
||||
def test_decode_write_after_finished_errors(self):
|
||||
self.assertEqual(_b('1234'), self.decoder.write(_b('3\r\nabc0\r\n1234')))
|
||||
self.assertRaises(ValueError, self.decoder.write, _b(''))
|
||||
|
||||
def test_decode_hex(self):
|
||||
self.assertEqual(_b(''), self.decoder.write(_b('A\r\n12345678900\r\n')))
|
||||
self.assertEqual(_b('1234567890'), self.output.getvalue())
|
||||
|
||||
def test_decode_long_ranges(self):
|
||||
self.assertEqual(None, self.decoder.write(_b('10000\r\n')))
|
||||
self.assertEqual(None, self.decoder.write(_b('1' * 65536)))
|
||||
self.assertEqual(None, self.decoder.write(_b('10000\r\n')))
|
||||
self.assertEqual(None, self.decoder.write(_b('2' * 65536)))
|
||||
self.assertEqual(_b(''), self.decoder.write(_b('0\r\n')))
|
||||
self.assertEqual(_b('1' * 65536 + '2' * 65536), self.output.getvalue())
|
||||
|
||||
def test_decode_newline_nonstrict(self):
|
||||
"""Tolerate chunk markers with no CR character."""
|
||||
# From <http://pad.lv/505078>
|
||||
self.decoder = subunit.chunked.Decoder(self.output, strict=False)
|
||||
self.assertEqual(None, self.decoder.write(_b('a\n')))
|
||||
self.assertEqual(None, self.decoder.write(_b('abcdeabcde')))
|
||||
self.assertEqual(_b(''), self.decoder.write(_b('0\n')))
|
||||
self.assertEqual(_b('abcdeabcde'), self.output.getvalue())
|
||||
|
||||
def test_decode_strict_newline_only(self):
|
||||
"""Reject chunk markers with no CR character in strict mode."""
|
||||
# From <http://pad.lv/505078>
|
||||
self.assertRaises(ValueError,
|
||||
self.decoder.write, _b('a\n'))
|
||||
|
||||
def test_decode_strict_multiple_crs(self):
|
||||
self.assertRaises(ValueError,
|
||||
self.decoder.write, _b('a\r\r\n'))
|
||||
|
||||
def test_decode_short_header(self):
|
||||
self.assertRaises(ValueError,
|
||||
self.decoder.write, _b('\n'))
|
||||
|
||||
|
||||
class TestEncode(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
unittest.TestCase.setUp(self)
|
||||
self.output = BytesIO()
|
||||
self.encoder = subunit.chunked.Encoder(self.output)
|
||||
|
||||
def test_encode_nothing(self):
|
||||
self.encoder.close()
|
||||
self.assertEqual(_b('0\r\n'), self.output.getvalue())
|
||||
|
||||
def test_encode_empty(self):
|
||||
self.encoder.write(_b(''))
|
||||
self.encoder.close()
|
||||
self.assertEqual(_b('0\r\n'), self.output.getvalue())
|
||||
|
||||
def test_encode_short(self):
|
||||
self.encoder.write(_b('abc'))
|
||||
self.encoder.close()
|
||||
self.assertEqual(_b('3\r\nabc0\r\n'), self.output.getvalue())
|
||||
|
||||
def test_encode_combines_short(self):
|
||||
self.encoder.write(_b('abc'))
|
||||
self.encoder.write(_b('def'))
|
||||
self.encoder.close()
|
||||
self.assertEqual(_b('6\r\nabcdef0\r\n'), self.output.getvalue())
|
||||
|
||||
def test_encode_over_9_is_in_hex(self):
|
||||
self.encoder.write(_b('1234567890'))
|
||||
self.encoder.close()
|
||||
self.assertEqual(_b('A\r\n12345678900\r\n'), self.output.getvalue())
|
||||
|
||||
def test_encode_long_ranges_not_combined(self):
|
||||
self.encoder.write(_b('1' * 65536))
|
||||
self.encoder.write(_b('2' * 65536))
|
||||
self.encoder.close()
|
||||
self.assertEqual(_b('10000\r\n' + '1' * 65536 + '10000\r\n' +
|
||||
'2' * 65536 + '0\r\n'), self.output.getvalue())
|
||||
106
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_details.py
vendored
Normal file
106
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_details.py
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
#
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
from testtools.compat import _b, StringIO
|
||||
|
||||
import subunit.tests
|
||||
from subunit import content, content_type, details
|
||||
|
||||
|
||||
class TestSimpleDetails(unittest.TestCase):
|
||||
|
||||
def test_lineReceived(self):
|
||||
parser = details.SimpleDetailsParser(None)
|
||||
parser.lineReceived(_b("foo\n"))
|
||||
parser.lineReceived(_b("bar\n"))
|
||||
self.assertEqual(_b("foo\nbar\n"), parser._message)
|
||||
|
||||
def test_lineReceived_escaped_bracket(self):
|
||||
parser = details.SimpleDetailsParser(None)
|
||||
parser.lineReceived(_b("foo\n"))
|
||||
parser.lineReceived(_b(" ]are\n"))
|
||||
parser.lineReceived(_b("bar\n"))
|
||||
self.assertEqual(_b("foo\n]are\nbar\n"), parser._message)
|
||||
|
||||
def test_get_message(self):
|
||||
parser = details.SimpleDetailsParser(None)
|
||||
self.assertEqual(_b(""), parser.get_message())
|
||||
|
||||
def test_get_details(self):
|
||||
parser = details.SimpleDetailsParser(None)
|
||||
traceback = ""
|
||||
expected = {}
|
||||
expected['traceback'] = content.Content(
|
||||
content_type.ContentType("text", "x-traceback",
|
||||
{'charset': 'utf8'}),
|
||||
lambda:[_b("")])
|
||||
found = parser.get_details()
|
||||
self.assertEqual(expected.keys(), found.keys())
|
||||
self.assertEqual(expected['traceback'].content_type,
|
||||
found['traceback'].content_type)
|
||||
self.assertEqual(_b('').join(expected['traceback'].iter_bytes()),
|
||||
_b('').join(found['traceback'].iter_bytes()))
|
||||
|
||||
def test_get_details_skip(self):
|
||||
parser = details.SimpleDetailsParser(None)
|
||||
traceback = ""
|
||||
expected = {}
|
||||
expected['reason'] = content.Content(
|
||||
content_type.ContentType("text", "plain"),
|
||||
lambda:[_b("")])
|
||||
found = parser.get_details("skip")
|
||||
self.assertEqual(expected, found)
|
||||
|
||||
def test_get_details_success(self):
|
||||
parser = details.SimpleDetailsParser(None)
|
||||
traceback = ""
|
||||
expected = {}
|
||||
expected['message'] = content.Content(
|
||||
content_type.ContentType("text", "plain"),
|
||||
lambda:[_b("")])
|
||||
found = parser.get_details("success")
|
||||
self.assertEqual(expected, found)
|
||||
|
||||
|
||||
class TestMultipartDetails(unittest.TestCase):
|
||||
|
||||
def test_get_message_is_None(self):
|
||||
parser = details.MultipartDetailsParser(None)
|
||||
self.assertEqual(None, parser.get_message())
|
||||
|
||||
def test_get_details(self):
|
||||
parser = details.MultipartDetailsParser(None)
|
||||
self.assertEqual({}, parser.get_details())
|
||||
|
||||
def test_parts(self):
|
||||
parser = details.MultipartDetailsParser(None)
|
||||
parser.lineReceived(_b("Content-Type: text/plain\n"))
|
||||
parser.lineReceived(_b("something\n"))
|
||||
parser.lineReceived(_b("F\r\n"))
|
||||
parser.lineReceived(_b("serialised\n"))
|
||||
parser.lineReceived(_b("form0\r\n"))
|
||||
expected = {}
|
||||
expected['something'] = content.Content(
|
||||
content_type.ContentType("text", "plain"),
|
||||
lambda:[_b("serialised\nform")])
|
||||
found = parser.get_details()
|
||||
self.assertEqual(expected.keys(), found.keys())
|
||||
self.assertEqual(expected['something'].content_type,
|
||||
found['something'].content_type)
|
||||
self.assertEqual(_b('').join(expected['something'].iter_bytes()),
|
||||
_b('').join(found['something'].iter_bytes()))
|
||||
35
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_filters.py
vendored
Normal file
35
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_filters.py
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# subunit: extensions to Python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2013 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
import sys
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from testtools import TestCase
|
||||
|
||||
from subunit.filters import find_stream
|
||||
|
||||
|
||||
class TestFindStream(TestCase):
|
||||
|
||||
def test_no_argv(self):
|
||||
self.assertEqual('foo', find_stream('foo', []))
|
||||
|
||||
def test_opens_file(self):
|
||||
f = NamedTemporaryFile()
|
||||
f.write(b'foo')
|
||||
f.flush()
|
||||
stream = find_stream('bar', [f.name])
|
||||
self.assertEqual(b'foo', stream.read())
|
||||
112
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_progress_model.py
vendored
Normal file
112
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_progress_model.py
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
#
|
||||
# subunit: extensions to Python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
import subunit
|
||||
from subunit.progress_model import ProgressModel
|
||||
|
||||
|
||||
class TestProgressModel(unittest.TestCase):
|
||||
|
||||
def assertProgressSummary(self, pos, total, progress):
|
||||
"""Assert that a progress model has reached a particular point."""
|
||||
self.assertEqual(pos, progress.pos())
|
||||
self.assertEqual(total, progress.width())
|
||||
|
||||
def test_new_progress_0_0(self):
|
||||
progress = ProgressModel()
|
||||
self.assertProgressSummary(0, 0, progress)
|
||||
|
||||
def test_advance_0_0(self):
|
||||
progress = ProgressModel()
|
||||
progress.advance()
|
||||
self.assertProgressSummary(1, 0, progress)
|
||||
|
||||
def test_advance_1_0(self):
|
||||
progress = ProgressModel()
|
||||
progress.advance()
|
||||
self.assertProgressSummary(1, 0, progress)
|
||||
|
||||
def test_set_width_absolute(self):
|
||||
progress = ProgressModel()
|
||||
progress.set_width(10)
|
||||
self.assertProgressSummary(0, 10, progress)
|
||||
|
||||
def test_set_width_absolute_preserves_pos(self):
|
||||
progress = ProgressModel()
|
||||
progress.advance()
|
||||
progress.set_width(2)
|
||||
self.assertProgressSummary(1, 2, progress)
|
||||
|
||||
def test_adjust_width(self):
|
||||
progress = ProgressModel()
|
||||
progress.adjust_width(10)
|
||||
self.assertProgressSummary(0, 10, progress)
|
||||
progress.adjust_width(-10)
|
||||
self.assertProgressSummary(0, 0, progress)
|
||||
|
||||
def test_adjust_width_preserves_pos(self):
|
||||
progress = ProgressModel()
|
||||
progress.advance()
|
||||
progress.adjust_width(10)
|
||||
self.assertProgressSummary(1, 10, progress)
|
||||
progress.adjust_width(-10)
|
||||
self.assertProgressSummary(1, 0, progress)
|
||||
|
||||
def test_push_preserves_progress(self):
|
||||
progress = ProgressModel()
|
||||
progress.adjust_width(3)
|
||||
progress.advance()
|
||||
progress.push()
|
||||
self.assertProgressSummary(1, 3, progress)
|
||||
|
||||
def test_advance_advances_substack(self):
|
||||
progress = ProgressModel()
|
||||
progress.adjust_width(3)
|
||||
progress.advance()
|
||||
progress.push()
|
||||
progress.adjust_width(1)
|
||||
progress.advance()
|
||||
self.assertProgressSummary(2, 3, progress)
|
||||
|
||||
def test_adjust_width_adjusts_substack(self):
|
||||
progress = ProgressModel()
|
||||
progress.adjust_width(3)
|
||||
progress.advance()
|
||||
progress.push()
|
||||
progress.adjust_width(2)
|
||||
progress.advance()
|
||||
self.assertProgressSummary(3, 6, progress)
|
||||
|
||||
def test_set_width_adjusts_substack(self):
|
||||
progress = ProgressModel()
|
||||
progress.adjust_width(3)
|
||||
progress.advance()
|
||||
progress.push()
|
||||
progress.set_width(2)
|
||||
progress.advance()
|
||||
self.assertProgressSummary(3, 6, progress)
|
||||
|
||||
def test_pop_restores_progress(self):
|
||||
progress = ProgressModel()
|
||||
progress.adjust_width(3)
|
||||
progress.advance()
|
||||
progress.push()
|
||||
progress.adjust_width(1)
|
||||
progress.advance()
|
||||
progress.pop()
|
||||
self.assertProgressSummary(1, 3, progress)
|
||||
64
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_run.py
vendored
Normal file
64
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_run.py
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
#
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2011 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
from testtools.compat import BytesIO
|
||||
import unittest
|
||||
|
||||
from testtools import PlaceHolder, TestCase
|
||||
from testtools.testresult.doubles import StreamResult
|
||||
|
||||
import subunit
|
||||
from subunit import run
|
||||
from subunit.run import SubunitTestRunner
|
||||
|
||||
|
||||
class TestSubunitTestRunner(TestCase):
|
||||
|
||||
def test_includes_timing_output(self):
|
||||
io = BytesIO()
|
||||
runner = SubunitTestRunner(stream=io)
|
||||
test = PlaceHolder('name')
|
||||
runner.run(test)
|
||||
io.seek(0)
|
||||
eventstream = StreamResult()
|
||||
subunit.ByteStreamToStreamResult(io).run(eventstream)
|
||||
timestamps = [event[-1] for event in eventstream._events
|
||||
if event is not None]
|
||||
self.assertNotEqual([], timestamps)
|
||||
|
||||
def test_enumerates_tests_before_run(self):
|
||||
io = BytesIO()
|
||||
runner = SubunitTestRunner(stream=io)
|
||||
test1 = PlaceHolder('name1')
|
||||
test2 = PlaceHolder('name2')
|
||||
case = unittest.TestSuite([test1, test2])
|
||||
runner.run(case)
|
||||
io.seek(0)
|
||||
eventstream = StreamResult()
|
||||
subunit.ByteStreamToStreamResult(io).run(eventstream)
|
||||
self.assertEqual([
|
||||
('status', 'name1', 'exists'),
|
||||
('status', 'name2', 'exists'),
|
||||
], [event[:3] for event in eventstream._events[:2]])
|
||||
|
||||
def test_list_errors_if_errors_from_list_test(self):
|
||||
io = BytesIO()
|
||||
runner = SubunitTestRunner(stream=io)
|
||||
def list_test(test):
|
||||
return [], ['failed import']
|
||||
self.patch(run, 'list_test', list_test)
|
||||
exc = self.assertRaises(SystemExit, runner.list, None)
|
||||
self.assertEqual((2,), exc.args)
|
||||
346
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_subunit_filter.py
vendored
Normal file
346
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_subunit_filter.py
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
#
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
"""Tests for subunit.TestResultFilter."""
|
||||
|
||||
from datetime import datetime
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from subunit import iso8601
|
||||
import unittest
|
||||
|
||||
from testtools import TestCase
|
||||
from testtools.compat import _b, BytesIO
|
||||
from testtools.testresult.doubles import ExtendedTestResult, StreamResult
|
||||
|
||||
import subunit
|
||||
from subunit.test_results import make_tag_filter, TestResultFilter
|
||||
from subunit import ByteStreamToStreamResult, StreamResultToBytes
|
||||
|
||||
|
||||
class TestTestResultFilter(TestCase):
|
||||
"""Test for TestResultFilter, a TestResult object which filters tests."""
|
||||
|
||||
# While TestResultFilter works on python objects, using a subunit stream
|
||||
# is an easy pithy way of getting a series of test objects to call into
|
||||
# the TestResult, and as TestResultFilter is intended for use with subunit
|
||||
# also has the benefit of detecting any interface skew issues.
|
||||
example_subunit_stream = _b("""\
|
||||
tags: global
|
||||
test passed
|
||||
success passed
|
||||
test failed
|
||||
tags: local
|
||||
failure failed
|
||||
test error
|
||||
error error [
|
||||
error details
|
||||
]
|
||||
test skipped
|
||||
skip skipped
|
||||
test todo
|
||||
xfail todo
|
||||
""")
|
||||
|
||||
def run_tests(self, result_filter, input_stream=None):
|
||||
"""Run tests through the given filter.
|
||||
|
||||
:param result_filter: A filtering TestResult object.
|
||||
:param input_stream: Bytes of subunit stream data. If not provided,
|
||||
uses TestTestResultFilter.example_subunit_stream.
|
||||
"""
|
||||
if input_stream is None:
|
||||
input_stream = self.example_subunit_stream
|
||||
test = subunit.ProtocolTestCase(BytesIO(input_stream))
|
||||
test.run(result_filter)
|
||||
|
||||
def test_default(self):
|
||||
"""The default is to exclude success and include everything else."""
|
||||
filtered_result = unittest.TestResult()
|
||||
result_filter = TestResultFilter(filtered_result)
|
||||
self.run_tests(result_filter)
|
||||
# skips are seen as success by default python TestResult.
|
||||
self.assertEqual(['error'],
|
||||
[error[0].id() for error in filtered_result.errors])
|
||||
self.assertEqual(['failed'],
|
||||
[failure[0].id() for failure in
|
||||
filtered_result.failures])
|
||||
self.assertEqual(4, filtered_result.testsRun)
|
||||
|
||||
def test_tag_filter(self):
|
||||
tag_filter = make_tag_filter(['global'], ['local'])
|
||||
result = ExtendedTestResult()
|
||||
result_filter = TestResultFilter(
|
||||
result, filter_success=False, filter_predicate=tag_filter)
|
||||
self.run_tests(result_filter)
|
||||
tests_included = [
|
||||
event[1] for event in result._events if event[0] == 'startTest']
|
||||
tests_expected = list(map(
|
||||
subunit.RemotedTestCase,
|
||||
['passed', 'error', 'skipped', 'todo']))
|
||||
self.assertEquals(tests_expected, tests_included)
|
||||
|
||||
def test_tags_tracked_correctly(self):
|
||||
tag_filter = make_tag_filter(['a'], [])
|
||||
result = ExtendedTestResult()
|
||||
result_filter = TestResultFilter(
|
||||
result, filter_success=False, filter_predicate=tag_filter)
|
||||
input_stream = _b(
|
||||
"test: foo\n"
|
||||
"tags: a\n"
|
||||
"successful: foo\n"
|
||||
"test: bar\n"
|
||||
"successful: bar\n")
|
||||
self.run_tests(result_filter, input_stream)
|
||||
foo = subunit.RemotedTestCase('foo')
|
||||
self.assertEquals(
|
||||
[('startTest', foo),
|
||||
('tags', set(['a']), set()),
|
||||
('addSuccess', foo),
|
||||
('stopTest', foo),
|
||||
],
|
||||
result._events)
|
||||
|
||||
def test_exclude_errors(self):
|
||||
filtered_result = unittest.TestResult()
|
||||
result_filter = TestResultFilter(filtered_result, filter_error=True)
|
||||
self.run_tests(result_filter)
|
||||
# skips are seen as errors by default python TestResult.
|
||||
self.assertEqual([], filtered_result.errors)
|
||||
self.assertEqual(['failed'],
|
||||
[failure[0].id() for failure in
|
||||
filtered_result.failures])
|
||||
self.assertEqual(3, filtered_result.testsRun)
|
||||
|
||||
def test_fixup_expected_failures(self):
|
||||
filtered_result = unittest.TestResult()
|
||||
result_filter = TestResultFilter(filtered_result,
|
||||
fixup_expected_failures=set(["failed"]))
|
||||
self.run_tests(result_filter)
|
||||
self.assertEqual(['failed', 'todo'],
|
||||
[failure[0].id() for failure in filtered_result.expectedFailures])
|
||||
self.assertEqual([], filtered_result.failures)
|
||||
self.assertEqual(4, filtered_result.testsRun)
|
||||
|
||||
def test_fixup_expected_errors(self):
|
||||
filtered_result = unittest.TestResult()
|
||||
result_filter = TestResultFilter(filtered_result,
|
||||
fixup_expected_failures=set(["error"]))
|
||||
self.run_tests(result_filter)
|
||||
self.assertEqual(['error', 'todo'],
|
||||
[failure[0].id() for failure in filtered_result.expectedFailures])
|
||||
self.assertEqual([], filtered_result.errors)
|
||||
self.assertEqual(4, filtered_result.testsRun)
|
||||
|
||||
def test_fixup_unexpected_success(self):
|
||||
filtered_result = unittest.TestResult()
|
||||
result_filter = TestResultFilter(filtered_result, filter_success=False,
|
||||
fixup_expected_failures=set(["passed"]))
|
||||
self.run_tests(result_filter)
|
||||
self.assertEqual(['passed'],
|
||||
[passed.id() for passed in filtered_result.unexpectedSuccesses])
|
||||
self.assertEqual(5, filtered_result.testsRun)
|
||||
|
||||
def test_exclude_failure(self):
|
||||
filtered_result = unittest.TestResult()
|
||||
result_filter = TestResultFilter(filtered_result, filter_failure=True)
|
||||
self.run_tests(result_filter)
|
||||
self.assertEqual(['error'],
|
||||
[error[0].id() for error in filtered_result.errors])
|
||||
self.assertEqual([],
|
||||
[failure[0].id() for failure in
|
||||
filtered_result.failures])
|
||||
self.assertEqual(3, filtered_result.testsRun)
|
||||
|
||||
def test_exclude_skips(self):
|
||||
filtered_result = subunit.TestResultStats(None)
|
||||
result_filter = TestResultFilter(filtered_result, filter_skip=True)
|
||||
self.run_tests(result_filter)
|
||||
self.assertEqual(0, filtered_result.skipped_tests)
|
||||
self.assertEqual(2, filtered_result.failed_tests)
|
||||
self.assertEqual(3, filtered_result.testsRun)
|
||||
|
||||
def test_include_success(self):
|
||||
"""Successes can be included if requested."""
|
||||
filtered_result = unittest.TestResult()
|
||||
result_filter = TestResultFilter(filtered_result,
|
||||
filter_success=False)
|
||||
self.run_tests(result_filter)
|
||||
self.assertEqual(['error'],
|
||||
[error[0].id() for error in filtered_result.errors])
|
||||
self.assertEqual(['failed'],
|
||||
[failure[0].id() for failure in
|
||||
filtered_result.failures])
|
||||
self.assertEqual(5, filtered_result.testsRun)
|
||||
|
||||
def test_filter_predicate(self):
|
||||
"""You can filter by predicate callbacks"""
|
||||
# 0.0.7 and earlier did not support the 'tags' parameter, so we need
|
||||
# to test that we still support behaviour without it.
|
||||
filtered_result = unittest.TestResult()
|
||||
def filter_cb(test, outcome, err, details):
|
||||
return outcome == 'success'
|
||||
result_filter = TestResultFilter(filtered_result,
|
||||
filter_predicate=filter_cb,
|
||||
filter_success=False)
|
||||
self.run_tests(result_filter)
|
||||
# Only success should pass
|
||||
self.assertEqual(1, filtered_result.testsRun)
|
||||
|
||||
def test_filter_predicate_with_tags(self):
|
||||
"""You can filter by predicate callbacks that accept tags"""
|
||||
filtered_result = unittest.TestResult()
|
||||
def filter_cb(test, outcome, err, details, tags):
|
||||
return outcome == 'success'
|
||||
result_filter = TestResultFilter(filtered_result,
|
||||
filter_predicate=filter_cb,
|
||||
filter_success=False)
|
||||
self.run_tests(result_filter)
|
||||
# Only success should pass
|
||||
self.assertEqual(1, filtered_result.testsRun)
|
||||
|
||||
def test_time_ordering_preserved(self):
|
||||
# Passing a subunit stream through TestResultFilter preserves the
|
||||
# relative ordering of 'time' directives and any other subunit
|
||||
# directives that are still included.
|
||||
date_a = datetime(year=2000, month=1, day=1, tzinfo=iso8601.UTC)
|
||||
date_b = datetime(year=2000, month=1, day=2, tzinfo=iso8601.UTC)
|
||||
date_c = datetime(year=2000, month=1, day=3, tzinfo=iso8601.UTC)
|
||||
subunit_stream = _b('\n'.join([
|
||||
"time: %s",
|
||||
"test: foo",
|
||||
"time: %s",
|
||||
"error: foo",
|
||||
"time: %s",
|
||||
""]) % (date_a, date_b, date_c))
|
||||
result = ExtendedTestResult()
|
||||
result_filter = TestResultFilter(result)
|
||||
self.run_tests(result_filter, subunit_stream)
|
||||
foo = subunit.RemotedTestCase('foo')
|
||||
self.maxDiff = None
|
||||
self.assertEqual(
|
||||
[('time', date_a),
|
||||
('time', date_b),
|
||||
('startTest', foo),
|
||||
('addError', foo, {}),
|
||||
('stopTest', foo),
|
||||
('time', date_c)], result._events)
|
||||
|
||||
def test_time_passes_through_filtered_tests(self):
|
||||
# Passing a subunit stream through TestResultFilter preserves 'time'
|
||||
# directives even if a specific test is filtered out.
|
||||
date_a = datetime(year=2000, month=1, day=1, tzinfo=iso8601.UTC)
|
||||
date_b = datetime(year=2000, month=1, day=2, tzinfo=iso8601.UTC)
|
||||
date_c = datetime(year=2000, month=1, day=3, tzinfo=iso8601.UTC)
|
||||
subunit_stream = _b('\n'.join([
|
||||
"time: %s",
|
||||
"test: foo",
|
||||
"time: %s",
|
||||
"success: foo",
|
||||
"time: %s",
|
||||
""]) % (date_a, date_b, date_c))
|
||||
result = ExtendedTestResult()
|
||||
result_filter = TestResultFilter(result)
|
||||
result_filter.startTestRun()
|
||||
self.run_tests(result_filter, subunit_stream)
|
||||
result_filter.stopTestRun()
|
||||
foo = subunit.RemotedTestCase('foo')
|
||||
self.maxDiff = None
|
||||
self.assertEqual(
|
||||
[('startTestRun',),
|
||||
('time', date_a),
|
||||
('time', date_c),
|
||||
('stopTestRun',),], result._events)
|
||||
|
||||
def test_skip_preserved(self):
|
||||
subunit_stream = _b('\n'.join([
|
||||
"test: foo",
|
||||
"skip: foo",
|
||||
""]))
|
||||
result = ExtendedTestResult()
|
||||
result_filter = TestResultFilter(result)
|
||||
self.run_tests(result_filter, subunit_stream)
|
||||
foo = subunit.RemotedTestCase('foo')
|
||||
self.assertEquals(
|
||||
[('startTest', foo),
|
||||
('addSkip', foo, {}),
|
||||
('stopTest', foo), ], result._events)
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
# These tests require Python >=2.7.
|
||||
del test_fixup_expected_failures, test_fixup_expected_errors, test_fixup_unexpected_success
|
||||
|
||||
|
||||
class TestFilterCommand(TestCase):
|
||||
|
||||
def run_command(self, args, stream):
|
||||
root = os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
||||
script_path = os.path.join(root, 'filters', 'subunit-filter')
|
||||
command = [sys.executable, script_path] + list(args)
|
||||
ps = subprocess.Popen(
|
||||
command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
out, err = ps.communicate(stream)
|
||||
if ps.returncode != 0:
|
||||
raise RuntimeError("%s failed: %s" % (command, err))
|
||||
return out
|
||||
|
||||
def test_default(self):
|
||||
byte_stream = BytesIO()
|
||||
stream = StreamResultToBytes(byte_stream)
|
||||
stream.status(test_id="foo", test_status="inprogress")
|
||||
stream.status(test_id="foo", test_status="skip")
|
||||
output = self.run_command([], byte_stream.getvalue())
|
||||
events = StreamResult()
|
||||
ByteStreamToStreamResult(BytesIO(output)).run(events)
|
||||
ids = set(event[1] for event in events._events)
|
||||
self.assertEqual([
|
||||
('status', 'foo', 'inprogress'),
|
||||
('status', 'foo', 'skip'),
|
||||
], [event[:3] for event in events._events])
|
||||
|
||||
def test_tags(self):
|
||||
byte_stream = BytesIO()
|
||||
stream = StreamResultToBytes(byte_stream)
|
||||
stream.status(
|
||||
test_id="foo", test_status="inprogress", test_tags=set(["a"]))
|
||||
stream.status(
|
||||
test_id="foo", test_status="success", test_tags=set(["a"]))
|
||||
stream.status(test_id="bar", test_status="inprogress")
|
||||
stream.status(test_id="bar", test_status="inprogress")
|
||||
stream.status(
|
||||
test_id="baz", test_status="inprogress", test_tags=set(["a"]))
|
||||
stream.status(
|
||||
test_id="baz", test_status="success", test_tags=set(["a"]))
|
||||
output = self.run_command(
|
||||
['-s', '--with-tag', 'a'], byte_stream.getvalue())
|
||||
events = StreamResult()
|
||||
ByteStreamToStreamResult(BytesIO(output)).run(events)
|
||||
ids = set(event[1] for event in events._events)
|
||||
self.assertEqual(set(['foo', 'baz']), ids)
|
||||
|
||||
def test_no_passthrough(self):
|
||||
output = self.run_command(['--no-passthrough'], b'hi thar')
|
||||
self.assertEqual(b'', output)
|
||||
|
||||
def test_passthrough(self):
|
||||
output = self.run_command([], b'hi thar')
|
||||
byte_stream = BytesIO()
|
||||
stream = StreamResultToBytes(byte_stream)
|
||||
stream.status(file_name="stdout", file_bytes=b'hi thar')
|
||||
self.assertEqual(byte_stream.getvalue(), output)
|
||||
78
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_subunit_stats.py
vendored
Normal file
78
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_subunit_stats.py
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
#
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
"""Tests for subunit.TestResultStats."""
|
||||
|
||||
import unittest
|
||||
|
||||
from testtools.compat import _b, BytesIO, StringIO
|
||||
|
||||
import subunit
|
||||
|
||||
|
||||
class TestTestResultStats(unittest.TestCase):
|
||||
"""Test for TestResultStats, a TestResult object that generates stats."""
|
||||
|
||||
def setUp(self):
|
||||
self.output = StringIO()
|
||||
self.result = subunit.TestResultStats(self.output)
|
||||
self.input_stream = BytesIO()
|
||||
self.test = subunit.ProtocolTestCase(self.input_stream)
|
||||
|
||||
def test_stats_empty(self):
|
||||
self.test.run(self.result)
|
||||
self.assertEqual(0, self.result.total_tests)
|
||||
self.assertEqual(0, self.result.passed_tests)
|
||||
self.assertEqual(0, self.result.failed_tests)
|
||||
self.assertEqual(set(), self.result.seen_tags)
|
||||
|
||||
def setUpUsedStream(self):
|
||||
self.input_stream.write(_b("""tags: global
|
||||
test passed
|
||||
success passed
|
||||
test failed
|
||||
tags: local
|
||||
failure failed
|
||||
test error
|
||||
error error
|
||||
test skipped
|
||||
skip skipped
|
||||
test todo
|
||||
xfail todo
|
||||
"""))
|
||||
self.input_stream.seek(0)
|
||||
self.test.run(self.result)
|
||||
|
||||
def test_stats_smoke_everything(self):
|
||||
# Statistics are calculated usefully.
|
||||
self.setUpUsedStream()
|
||||
self.assertEqual(5, self.result.total_tests)
|
||||
self.assertEqual(2, self.result.passed_tests)
|
||||
self.assertEqual(2, self.result.failed_tests)
|
||||
self.assertEqual(1, self.result.skipped_tests)
|
||||
self.assertEqual(set(["global", "local"]), self.result.seen_tags)
|
||||
|
||||
def test_stat_formatting(self):
|
||||
expected = ("""
|
||||
Total tests: 5
|
||||
Passed tests: 2
|
||||
Failed tests: 2
|
||||
Skipped tests: 1
|
||||
Seen tags: global, local
|
||||
""")[1:]
|
||||
self.setUpUsedStream()
|
||||
self.result.formatStats()
|
||||
self.assertEqual(expected, self.output.getvalue())
|
||||
85
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_subunit_tags.py
vendored
Normal file
85
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_subunit_tags.py
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
#
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
"""Tests for subunit.tag_stream."""
|
||||
|
||||
from io import BytesIO
|
||||
|
||||
import testtools
|
||||
from testtools.matchers import Contains
|
||||
|
||||
import subunit
|
||||
import subunit.test_results
|
||||
|
||||
|
||||
class TestSubUnitTags(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSubUnitTags, self).setUp()
|
||||
self.original = BytesIO()
|
||||
self.filtered = BytesIO()
|
||||
|
||||
def test_add_tag(self):
|
||||
# Literal values to avoid set sort-order dependencies. Python code show
|
||||
# derivation.
|
||||
# reference = BytesIO()
|
||||
# stream = subunit.StreamResultToBytes(reference)
|
||||
# stream.status(
|
||||
# test_id='test', test_status='inprogress', test_tags=set(['quux', 'foo']))
|
||||
# stream.status(
|
||||
# test_id='test', test_status='success', test_tags=set(['bar', 'quux', 'foo']))
|
||||
reference = [
|
||||
b'\xb3)\x82\x17\x04test\x02\x04quux\x03foo\x05\x97n\x86\xb3)'
|
||||
b'\x83\x1b\x04test\x03\x03bar\x04quux\x03fooqn\xab)',
|
||||
b'\xb3)\x82\x17\x04test\x02\x04quux\x03foo\x05\x97n\x86\xb3)'
|
||||
b'\x83\x1b\x04test\x03\x04quux\x03foo\x03bar\xaf\xbd\x9d\xd6',
|
||||
b'\xb3)\x82\x17\x04test\x02\x04quux\x03foo\x05\x97n\x86\xb3)'
|
||||
b'\x83\x1b\x04test\x03\x04quux\x03bar\x03foo\x03\x04b\r',
|
||||
b'\xb3)\x82\x17\x04test\x02\x04quux\x03foo\x05\x97n\x86\xb3)'
|
||||
b'\x83\x1b\x04test\x03\x03bar\x03foo\x04quux\xd2\x18\x1bC',
|
||||
b'\xb3)\x82\x17\x04test\x02\x03foo\x04quux\xa6\xe1\xde\xec\xb3)'
|
||||
b'\x83\x1b\x04test\x03\x03foo\x04quux\x03bar\x08\xc2X\x83',
|
||||
b'\xb3)\x82\x17\x04test\x02\x03foo\x04quux\xa6\xe1\xde\xec\xb3)'
|
||||
b'\x83\x1b\x04test\x03\x03bar\x03foo\x04quux\xd2\x18\x1bC',
|
||||
b'\xb3)\x82\x17\x04test\x02\x03foo\x04quux\xa6\xe1\xde\xec\xb3)'
|
||||
b'\x83\x1b\x04test\x03\x03foo\x03bar\x04quux:\x05e\x80',
|
||||
]
|
||||
stream = subunit.StreamResultToBytes(self.original)
|
||||
stream.status(
|
||||
test_id='test', test_status='inprogress', test_tags=set(['foo']))
|
||||
stream.status(
|
||||
test_id='test', test_status='success', test_tags=set(['foo', 'bar']))
|
||||
self.original.seek(0)
|
||||
self.assertEqual(
|
||||
0, subunit.tag_stream(self.original, self.filtered, ["quux"]))
|
||||
self.assertThat(reference, Contains(self.filtered.getvalue()))
|
||||
|
||||
def test_remove_tag(self):
|
||||
reference = BytesIO()
|
||||
stream = subunit.StreamResultToBytes(reference)
|
||||
stream.status(
|
||||
test_id='test', test_status='inprogress', test_tags=set(['foo']))
|
||||
stream.status(
|
||||
test_id='test', test_status='success', test_tags=set(['foo']))
|
||||
stream = subunit.StreamResultToBytes(self.original)
|
||||
stream.status(
|
||||
test_id='test', test_status='inprogress', test_tags=set(['foo']))
|
||||
stream.status(
|
||||
test_id='test', test_status='success', test_tags=set(['foo', 'bar']))
|
||||
self.original.seek(0)
|
||||
self.assertEqual(
|
||||
0, subunit.tag_stream(self.original, self.filtered, ["-bar"]))
|
||||
self.assertEqual(reference.getvalue(), self.filtered.getvalue())
|
||||
387
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_tap2subunit.py
vendored
Normal file
387
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_tap2subunit.py
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
#
|
||||
# subunit: extensions to python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
"""Tests for TAP2SubUnit."""
|
||||
|
||||
from io import BytesIO, StringIO
|
||||
import unittest
|
||||
|
||||
from testtools import TestCase
|
||||
from testtools.compat import _u
|
||||
from testtools.testresult.doubles import StreamResult
|
||||
|
||||
import subunit
|
||||
|
||||
UTF8_TEXT = 'text/plain; charset=UTF8'
|
||||
|
||||
|
||||
class TestTAP2SubUnit(TestCase):
|
||||
"""Tests for TAP2SubUnit.
|
||||
|
||||
These tests test TAP string data in, and subunit string data out.
|
||||
This is ok because the subunit protocol is intended to be stable,
|
||||
but it might be easier/pithier to write tests against TAP string in,
|
||||
parsed subunit objects out (by hooking the subunit stream to a subunit
|
||||
protocol server.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestTAP2SubUnit, self).setUp()
|
||||
self.tap = StringIO()
|
||||
self.subunit = BytesIO()
|
||||
|
||||
def test_skip_entire_file(self):
|
||||
# A file
|
||||
# 1..- # Skipped: comment
|
||||
# results in a single skipped test.
|
||||
self.tap.write(_u("1..0 # Skipped: entire file skipped\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'file skip', 'skip', None, True,
|
||||
'tap comment', b'Skipped: entire file skipped', True, None, None,
|
||||
None)])
|
||||
|
||||
def test_ok_test_pass(self):
|
||||
# A file
|
||||
# ok
|
||||
# results in a passed test with name 'test 1' (a synthetic name as tap
|
||||
# does not require named fixtures - it is the first test in the tap
|
||||
# stream).
|
||||
self.tap.write(_u("ok\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1', 'success', None, False, None,
|
||||
None, True, None, None, None)])
|
||||
|
||||
def test_ok_test_number_pass(self):
|
||||
# A file
|
||||
# ok 1
|
||||
# results in a passed test with name 'test 1'
|
||||
self.tap.write(_u("ok 1\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1', 'success', None, False, None,
|
||||
None, True, None, None, None)])
|
||||
|
||||
def test_ok_test_number_description_pass(self):
|
||||
# A file
|
||||
# ok 1 - There is a description
|
||||
# results in a passed test with name 'test 1 - There is a description'
|
||||
self.tap.write(_u("ok 1 - There is a description\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1 - There is a description',
|
||||
'success', None, False, None, None, True, None, None, None)])
|
||||
|
||||
def test_ok_test_description_pass(self):
|
||||
# A file
|
||||
# ok There is a description
|
||||
# results in a passed test with name 'test 1 There is a description'
|
||||
self.tap.write(_u("ok There is a description\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1 There is a description',
|
||||
'success', None, False, None, None, True, None, None, None)])
|
||||
|
||||
def test_ok_SKIP_skip(self):
|
||||
# A file
|
||||
# ok # SKIP
|
||||
# results in a skkip test with name 'test 1'
|
||||
self.tap.write(_u("ok # SKIP\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1', 'skip', None, False, None,
|
||||
None, True, None, None, None)])
|
||||
|
||||
def test_ok_skip_number_comment_lowercase(self):
|
||||
self.tap.write(_u("ok 1 # skip no samba environment available, skipping compilation\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1', 'skip', None, False, 'tap comment',
|
||||
b'no samba environment available, skipping compilation', True,
|
||||
'text/plain; charset=UTF8', None, None)])
|
||||
|
||||
def test_ok_number_description_SKIP_skip_comment(self):
|
||||
# A file
|
||||
# ok 1 foo # SKIP Not done yet
|
||||
# results in a skip test with name 'test 1 foo' and a log of
|
||||
# Not done yet
|
||||
self.tap.write(_u("ok 1 foo # SKIP Not done yet\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1 foo', 'skip', None, False,
|
||||
'tap comment', b'Not done yet', True, 'text/plain; charset=UTF8',
|
||||
None, None)])
|
||||
|
||||
def test_ok_SKIP_skip_comment(self):
|
||||
# A file
|
||||
# ok # SKIP Not done yet
|
||||
# results in a skip test with name 'test 1' and a log of Not done yet
|
||||
self.tap.write(_u("ok # SKIP Not done yet\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1', 'skip', None, False,
|
||||
'tap comment', b'Not done yet', True, 'text/plain; charset=UTF8',
|
||||
None, None)])
|
||||
|
||||
def test_ok_TODO_xfail(self):
|
||||
# A file
|
||||
# ok # TODO
|
||||
# results in a xfail test with name 'test 1'
|
||||
self.tap.write(_u("ok # TODO\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1', 'xfail', None, False, None,
|
||||
None, True, None, None, None)])
|
||||
|
||||
def test_ok_TODO_xfail_comment(self):
|
||||
# A file
|
||||
# ok # TODO Not done yet
|
||||
# results in a xfail test with name 'test 1' and a log of Not done yet
|
||||
self.tap.write(_u("ok # TODO Not done yet\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([('status', 'test 1', 'xfail', None, False,
|
||||
'tap comment', b'Not done yet', True, 'text/plain; charset=UTF8',
|
||||
None, None)])
|
||||
|
||||
def test_bail_out_errors(self):
|
||||
# A file with line in it
|
||||
# Bail out! COMMENT
|
||||
# is treated as an error
|
||||
self.tap.write(_u("ok 1 foo\n"))
|
||||
self.tap.write(_u("Bail out! Lifejacket engaged\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([
|
||||
('status', 'test 1 foo', 'success', None, False, None, None, True,
|
||||
None, None, None),
|
||||
('status', 'Bail out! Lifejacket engaged', 'fail', None, False,
|
||||
None, None, True, None, None, None)])
|
||||
|
||||
def test_missing_test_at_end_with_plan_adds_error(self):
|
||||
# A file
|
||||
# 1..3
|
||||
# ok first test
|
||||
# not ok third test
|
||||
# results in three tests, with the third being created
|
||||
self.tap.write(_u('1..3\n'))
|
||||
self.tap.write(_u('ok first test\n'))
|
||||
self.tap.write(_u('not ok second test\n'))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([
|
||||
('status', 'test 1 first test', 'success', None, False, None,
|
||||
None, True, None, None, None),
|
||||
('status', 'test 2 second test', 'fail', None, False, None, None,
|
||||
True, None, None, None),
|
||||
('status', 'test 3', 'fail', None, False, 'tap meta',
|
||||
b'test missing from TAP output', True, 'text/plain; charset=UTF8',
|
||||
None, None)])
|
||||
|
||||
def test_missing_test_with_plan_adds_error(self):
|
||||
# A file
|
||||
# 1..3
|
||||
# ok first test
|
||||
# not ok 3 third test
|
||||
# results in three tests, with the second being created
|
||||
self.tap.write(_u('1..3\n'))
|
||||
self.tap.write(_u('ok first test\n'))
|
||||
self.tap.write(_u('not ok 3 third test\n'))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([
|
||||
('status', 'test 1 first test', 'success', None, False, None, None,
|
||||
True, None, None, None),
|
||||
('status', 'test 2', 'fail', None, False, 'tap meta',
|
||||
b'test missing from TAP output', True, 'text/plain; charset=UTF8',
|
||||
None, None),
|
||||
('status', 'test 3 third test', 'fail', None, False, None, None,
|
||||
True, None, None, None)])
|
||||
|
||||
def test_missing_test_no_plan_adds_error(self):
|
||||
# A file
|
||||
# ok first test
|
||||
# not ok 3 third test
|
||||
# results in three tests, with the second being created
|
||||
self.tap.write(_u('ok first test\n'))
|
||||
self.tap.write(_u('not ok 3 third test\n'))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([
|
||||
('status', 'test 1 first test', 'success', None, False, None, None,
|
||||
True, None, None, None),
|
||||
('status', 'test 2', 'fail', None, False, 'tap meta',
|
||||
b'test missing from TAP output', True, 'text/plain; charset=UTF8',
|
||||
None, None),
|
||||
('status', 'test 3 third test', 'fail', None, False, None, None,
|
||||
True, None, None, None)])
|
||||
|
||||
def test_four_tests_in_a_row_trailing_plan(self):
|
||||
# A file
|
||||
# ok 1 - first test in a script with no plan at all
|
||||
# not ok 2 - second
|
||||
# ok 3 - third
|
||||
# not ok 4 - fourth
|
||||
# 1..4
|
||||
# results in four tests numbered and named
|
||||
self.tap.write(_u('ok 1 - first test in a script with trailing plan\n'))
|
||||
self.tap.write(_u('not ok 2 - second\n'))
|
||||
self.tap.write(_u('ok 3 - third\n'))
|
||||
self.tap.write(_u('not ok 4 - fourth\n'))
|
||||
self.tap.write(_u('1..4\n'))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([
|
||||
('status', 'test 1 - first test in a script with trailing plan',
|
||||
'success', None, False, None, None, True, None, None, None),
|
||||
('status', 'test 2 - second', 'fail', None, False, None, None,
|
||||
True, None, None, None),
|
||||
('status', 'test 3 - third', 'success', None, False, None, None,
|
||||
True, None, None, None),
|
||||
('status', 'test 4 - fourth', 'fail', None, False, None, None,
|
||||
True, None, None, None)])
|
||||
|
||||
def test_four_tests_in_a_row_with_plan(self):
|
||||
# A file
|
||||
# 1..4
|
||||
# ok 1 - first test in a script with no plan at all
|
||||
# not ok 2 - second
|
||||
# ok 3 - third
|
||||
# not ok 4 - fourth
|
||||
# results in four tests numbered and named
|
||||
self.tap.write(_u('1..4\n'))
|
||||
self.tap.write(_u('ok 1 - first test in a script with a plan\n'))
|
||||
self.tap.write(_u('not ok 2 - second\n'))
|
||||
self.tap.write(_u('ok 3 - third\n'))
|
||||
self.tap.write(_u('not ok 4 - fourth\n'))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([
|
||||
('status', 'test 1 - first test in a script with a plan',
|
||||
'success', None, False, None, None, True, None, None, None),
|
||||
('status', 'test 2 - second', 'fail', None, False, None, None,
|
||||
True, None, None, None),
|
||||
('status', 'test 3 - third', 'success', None, False, None, None,
|
||||
True, None, None, None),
|
||||
('status', 'test 4 - fourth', 'fail', None, False, None, None,
|
||||
True, None, None, None)])
|
||||
|
||||
def test_four_tests_in_a_row_no_plan(self):
|
||||
# A file
|
||||
# ok 1 - first test in a script with no plan at all
|
||||
# not ok 2 - second
|
||||
# ok 3 - third
|
||||
# not ok 4 - fourth
|
||||
# results in four tests numbered and named
|
||||
self.tap.write(_u('ok 1 - first test in a script with no plan at all\n'))
|
||||
self.tap.write(_u('not ok 2 - second\n'))
|
||||
self.tap.write(_u('ok 3 - third\n'))
|
||||
self.tap.write(_u('not ok 4 - fourth\n'))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([
|
||||
('status', 'test 1 - first test in a script with no plan at all',
|
||||
'success', None, False, None, None, True, None, None, None),
|
||||
('status', 'test 2 - second', 'fail', None, False, None, None,
|
||||
True, None, None, None),
|
||||
('status', 'test 3 - third', 'success', None, False, None, None,
|
||||
True, None, None, None),
|
||||
('status', 'test 4 - fourth', 'fail', None, False, None, None,
|
||||
True, None, None, None)])
|
||||
|
||||
def test_todo_and_skip(self):
|
||||
# A file
|
||||
# not ok 1 - a fail but # TODO but is TODO
|
||||
# not ok 2 - another fail # SKIP instead
|
||||
# results in two tests, numbered and commented.
|
||||
self.tap.write(_u("not ok 1 - a fail but # TODO but is TODO\n"))
|
||||
self.tap.write(_u("not ok 2 - another fail # SKIP instead\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.subunit.seek(0)
|
||||
events = StreamResult()
|
||||
subunit.ByteStreamToStreamResult(self.subunit).run(events)
|
||||
self.check_events([
|
||||
('status', 'test 1 - a fail but', 'xfail', None, False,
|
||||
'tap comment', b'but is TODO', True, 'text/plain; charset=UTF8',
|
||||
None, None),
|
||||
('status', 'test 2 - another fail', 'skip', None, False,
|
||||
'tap comment', b'instead', True, 'text/plain; charset=UTF8',
|
||||
None, None)])
|
||||
|
||||
def test_leading_comments_add_to_next_test_log(self):
|
||||
# A file
|
||||
# # comment
|
||||
# ok
|
||||
# ok
|
||||
# results in a single test with the comment included
|
||||
# in the first test and not the second.
|
||||
self.tap.write(_u("# comment\n"))
|
||||
self.tap.write(_u("ok\n"))
|
||||
self.tap.write(_u("ok\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([
|
||||
('status', 'test 1', 'success', None, False, 'tap comment',
|
||||
b'# comment', True, 'text/plain; charset=UTF8', None, None),
|
||||
('status', 'test 2', 'success', None, False, None, None, True,
|
||||
None, None, None)])
|
||||
|
||||
def test_trailing_comments_are_included_in_last_test_log(self):
|
||||
# A file
|
||||
# ok foo
|
||||
# ok foo
|
||||
# # comment
|
||||
# results in a two tests, with the second having the comment
|
||||
# attached to its log.
|
||||
self.tap.write(_u("ok\n"))
|
||||
self.tap.write(_u("ok\n"))
|
||||
self.tap.write(_u("# comment\n"))
|
||||
self.tap.seek(0)
|
||||
result = subunit.TAP2SubUnit(self.tap, self.subunit)
|
||||
self.assertEqual(0, result)
|
||||
self.check_events([
|
||||
('status', 'test 1', 'success', None, False, None, None, True,
|
||||
None, None, None),
|
||||
('status', 'test 2', 'success', None, False, 'tap comment',
|
||||
b'# comment', True, 'text/plain; charset=UTF8', None, None)])
|
||||
|
||||
def check_events(self, events):
|
||||
self.subunit.seek(0)
|
||||
eventstream = StreamResult()
|
||||
subunit.ByteStreamToStreamResult(self.subunit).run(eventstream)
|
||||
self.assertEqual(events, eventstream._events)
|
||||
1362
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_test_protocol.py
vendored
Normal file
1362
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_test_protocol.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
436
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_test_protocol2.py
vendored
Normal file
436
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_test_protocol2.py
vendored
Normal file
@@ -0,0 +1,436 @@
|
||||
#
|
||||
# subunit: extensions to Python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2013 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
from io import BytesIO
|
||||
import datetime
|
||||
|
||||
from testtools import TestCase
|
||||
from testtools.matchers import Contains, HasLength
|
||||
from testtools.tests.test_testresult import TestStreamResultContract
|
||||
from testtools.testresult.doubles import StreamResult
|
||||
|
||||
import subunit
|
||||
import subunit.iso8601 as iso8601
|
||||
|
||||
CONSTANT_ENUM = b'\xb3)\x01\x0c\x03foo\x08U_\x1b'
|
||||
CONSTANT_INPROGRESS = b'\xb3)\x02\x0c\x03foo\x8e\xc1-\xb5'
|
||||
CONSTANT_SUCCESS = b'\xb3)\x03\x0c\x03fooE\x9d\xfe\x10'
|
||||
CONSTANT_UXSUCCESS = b'\xb3)\x04\x0c\x03fooX\x98\xce\xa8'
|
||||
CONSTANT_SKIP = b'\xb3)\x05\x0c\x03foo\x93\xc4\x1d\r'
|
||||
CONSTANT_FAIL = b'\xb3)\x06\x0c\x03foo\x15Po\xa3'
|
||||
CONSTANT_XFAIL = b'\xb3)\x07\x0c\x03foo\xde\x0c\xbc\x06'
|
||||
CONSTANT_EOF = b'\xb3!\x10\x08S\x15\x88\xdc'
|
||||
CONSTANT_FILE_CONTENT = b'\xb3!@\x13\x06barney\x03wooA5\xe3\x8c'
|
||||
CONSTANT_MIME = b'\xb3! #\x1aapplication/foo; charset=1x3Q\x15'
|
||||
CONSTANT_TIMESTAMP = b'\xb3+\x03\x13<\x17T\xcf\x80\xaf\xc8\x03barI\x96>-'
|
||||
CONSTANT_ROUTE_CODE = b'\xb3-\x03\x13\x03bar\x06source\x9cY9\x19'
|
||||
CONSTANT_RUNNABLE = b'\xb3(\x03\x0c\x03foo\xe3\xea\xf5\xa4'
|
||||
CONSTANT_TAGS = [
|
||||
b'\xb3)\x80\x15\x03bar\x02\x03foo\x03barTHn\xb4',
|
||||
b'\xb3)\x80\x15\x03bar\x02\x03bar\x03foo\xf8\xf1\x91o',
|
||||
]
|
||||
|
||||
|
||||
class TestStreamResultToBytesContract(TestCase, TestStreamResultContract):
|
||||
"""Check that StreamResult behaves as testtools expects."""
|
||||
|
||||
def _make_result(self):
|
||||
return subunit.StreamResultToBytes(BytesIO())
|
||||
|
||||
|
||||
class TestStreamResultToBytes(TestCase):
|
||||
|
||||
def _make_result(self):
|
||||
output = BytesIO()
|
||||
return subunit.StreamResultToBytes(output), output
|
||||
|
||||
def test_numbers(self):
|
||||
result = subunit.StreamResultToBytes(BytesIO())
|
||||
packet = []
|
||||
self.assertRaises(Exception, result._write_number, -1, packet)
|
||||
self.assertEqual([], packet)
|
||||
result._write_number(0, packet)
|
||||
self.assertEqual([b'\x00'], packet)
|
||||
del packet[:]
|
||||
result._write_number(63, packet)
|
||||
self.assertEqual([b'\x3f'], packet)
|
||||
del packet[:]
|
||||
result._write_number(64, packet)
|
||||
self.assertEqual([b'\x40\x40'], packet)
|
||||
del packet[:]
|
||||
result._write_number(16383, packet)
|
||||
self.assertEqual([b'\x7f\xff'], packet)
|
||||
del packet[:]
|
||||
result._write_number(16384, packet)
|
||||
self.assertEqual([b'\x80\x40', b'\x00'], packet)
|
||||
del packet[:]
|
||||
result._write_number(4194303, packet)
|
||||
self.assertEqual([b'\xbf\xff', b'\xff'], packet)
|
||||
del packet[:]
|
||||
result._write_number(4194304, packet)
|
||||
self.assertEqual([b'\xc0\x40\x00\x00'], packet)
|
||||
del packet[:]
|
||||
result._write_number(1073741823, packet)
|
||||
self.assertEqual([b'\xff\xff\xff\xff'], packet)
|
||||
del packet[:]
|
||||
self.assertRaises(Exception, result._write_number, 1073741824, packet)
|
||||
self.assertEqual([], packet)
|
||||
|
||||
def test_volatile_length(self):
|
||||
# if the length of the packet data before the length itself is
|
||||
# considered is right on the boundary for length's variable length
|
||||
# encoding, it is easy to get the length wrong by not accounting for
|
||||
# length itself.
|
||||
# that is, the encoder has to ensure that length == sum (length_of_rest
|
||||
# + length_of_length)
|
||||
result, output = self._make_result()
|
||||
# 1 byte short:
|
||||
result.status(file_name="", file_bytes=b'\xff'*0)
|
||||
self.assertThat(output.getvalue(), HasLength(10))
|
||||
self.assertEqual(b'\x0a', output.getvalue()[3:4])
|
||||
output.seek(0)
|
||||
output.truncate()
|
||||
# 1 byte long:
|
||||
result.status(file_name="", file_bytes=b'\xff'*53)
|
||||
self.assertThat(output.getvalue(), HasLength(63))
|
||||
self.assertEqual(b'\x3f', output.getvalue()[3:4])
|
||||
output.seek(0)
|
||||
output.truncate()
|
||||
# 2 bytes short
|
||||
result.status(file_name="", file_bytes=b'\xff'*54)
|
||||
self.assertThat(output.getvalue(), HasLength(65))
|
||||
self.assertEqual(b'\x40\x41', output.getvalue()[3:5])
|
||||
output.seek(0)
|
||||
output.truncate()
|
||||
# 2 bytes long
|
||||
result.status(file_name="", file_bytes=b'\xff'*16371)
|
||||
self.assertThat(output.getvalue(), HasLength(16383))
|
||||
self.assertEqual(b'\x7f\xff', output.getvalue()[3:5])
|
||||
output.seek(0)
|
||||
output.truncate()
|
||||
# 3 bytes short
|
||||
result.status(file_name="", file_bytes=b'\xff'*16372)
|
||||
self.assertThat(output.getvalue(), HasLength(16385))
|
||||
self.assertEqual(b'\x80\x40\x01', output.getvalue()[3:6])
|
||||
output.seek(0)
|
||||
output.truncate()
|
||||
# 3 bytes long
|
||||
result.status(file_name="", file_bytes=b'\xff'*4194289)
|
||||
self.assertThat(output.getvalue(), HasLength(4194303))
|
||||
self.assertEqual(b'\xbf\xff\xff', output.getvalue()[3:6])
|
||||
output.seek(0)
|
||||
output.truncate()
|
||||
self.assertRaises(Exception, result.status, file_name="",
|
||||
file_bytes=b'\xff'*4194290)
|
||||
|
||||
def test_trivial_enumeration(self):
|
||||
result, output = self._make_result()
|
||||
result.status("foo", 'exists')
|
||||
self.assertEqual(CONSTANT_ENUM, output.getvalue())
|
||||
|
||||
def test_inprogress(self):
|
||||
result, output = self._make_result()
|
||||
result.status("foo", 'inprogress')
|
||||
self.assertEqual(CONSTANT_INPROGRESS, output.getvalue())
|
||||
|
||||
def test_success(self):
|
||||
result, output = self._make_result()
|
||||
result.status("foo", 'success')
|
||||
self.assertEqual(CONSTANT_SUCCESS, output.getvalue())
|
||||
|
||||
def test_uxsuccess(self):
|
||||
result, output = self._make_result()
|
||||
result.status("foo", 'uxsuccess')
|
||||
self.assertEqual(CONSTANT_UXSUCCESS, output.getvalue())
|
||||
|
||||
def test_skip(self):
|
||||
result, output = self._make_result()
|
||||
result.status("foo", 'skip')
|
||||
self.assertEqual(CONSTANT_SKIP, output.getvalue())
|
||||
|
||||
def test_fail(self):
|
||||
result, output = self._make_result()
|
||||
result.status("foo", 'fail')
|
||||
self.assertEqual(CONSTANT_FAIL, output.getvalue())
|
||||
|
||||
def test_xfail(self):
|
||||
result, output = self._make_result()
|
||||
result.status("foo", 'xfail')
|
||||
self.assertEqual(CONSTANT_XFAIL, output.getvalue())
|
||||
|
||||
def test_unknown_status(self):
|
||||
result, output = self._make_result()
|
||||
self.assertRaises(Exception, result.status, "foo", 'boo')
|
||||
self.assertEqual(b'', output.getvalue())
|
||||
|
||||
def test_eof(self):
|
||||
result, output = self._make_result()
|
||||
result.status(eof=True)
|
||||
self.assertEqual(CONSTANT_EOF, output.getvalue())
|
||||
|
||||
def test_file_content(self):
|
||||
result, output = self._make_result()
|
||||
result.status(file_name="barney", file_bytes=b"woo")
|
||||
self.assertEqual(CONSTANT_FILE_CONTENT, output.getvalue())
|
||||
|
||||
def test_mime(self):
|
||||
result, output = self._make_result()
|
||||
result.status(mime_type="application/foo; charset=1")
|
||||
self.assertEqual(CONSTANT_MIME, output.getvalue())
|
||||
|
||||
def test_route_code(self):
|
||||
result, output = self._make_result()
|
||||
result.status(test_id="bar", test_status='success',
|
||||
route_code="source")
|
||||
self.assertEqual(CONSTANT_ROUTE_CODE, output.getvalue())
|
||||
|
||||
def test_runnable(self):
|
||||
result, output = self._make_result()
|
||||
result.status("foo", 'success', runnable=False)
|
||||
self.assertEqual(CONSTANT_RUNNABLE, output.getvalue())
|
||||
|
||||
def test_tags(self):
|
||||
result, output = self._make_result()
|
||||
result.status(test_id="bar", test_tags=set(['foo', 'bar']))
|
||||
self.assertThat(CONSTANT_TAGS, Contains(output.getvalue()))
|
||||
|
||||
def test_timestamp(self):
|
||||
timestamp = datetime.datetime(2001, 12, 12, 12, 59, 59, 45,
|
||||
iso8601.Utc())
|
||||
result, output = self._make_result()
|
||||
result.status(test_id="bar", test_status='success', timestamp=timestamp)
|
||||
self.assertEqual(CONSTANT_TIMESTAMP, output.getvalue())
|
||||
|
||||
|
||||
class TestByteStreamToStreamResult(TestCase):
|
||||
|
||||
def test_non_subunit_encapsulated(self):
|
||||
source = BytesIO(b"foo\nbar\n")
|
||||
result = StreamResult()
|
||||
subunit.ByteStreamToStreamResult(
|
||||
source, non_subunit_name="stdout").run(result)
|
||||
self.assertEqual([
|
||||
('status', None, None, None, True, 'stdout', b'f', False, None, None, None),
|
||||
('status', None, None, None, True, 'stdout', b'o', False, None, None, None),
|
||||
('status', None, None, None, True, 'stdout', b'o', False, None, None, None),
|
||||
('status', None, None, None, True, 'stdout', b'\n', False, None, None, None),
|
||||
('status', None, None, None, True, 'stdout', b'b', False, None, None, None),
|
||||
('status', None, None, None, True, 'stdout', b'a', False, None, None, None),
|
||||
('status', None, None, None, True, 'stdout', b'r', False, None, None, None),
|
||||
('status', None, None, None, True, 'stdout', b'\n', False, None, None, None),
|
||||
], result._events)
|
||||
self.assertEqual(b'', source.read())
|
||||
|
||||
def test_signature_middle_utf8_char(self):
|
||||
utf8_bytes = b'\xe3\xb3\x8a'
|
||||
source = BytesIO(utf8_bytes)
|
||||
# Should be treated as one character (it is u'\u3cca') and wrapped
|
||||
result = StreamResult()
|
||||
subunit.ByteStreamToStreamResult(
|
||||
source, non_subunit_name="stdout").run(
|
||||
result)
|
||||
self.assertEqual([
|
||||
('status', None, None, None, True, 'stdout', b'\xe3', False, None, None, None),
|
||||
('status', None, None, None, True, 'stdout', b'\xb3', False, None, None, None),
|
||||
('status', None, None, None, True, 'stdout', b'\x8a', False, None, None, None),
|
||||
], result._events)
|
||||
|
||||
def test_non_subunit_disabled_raises(self):
|
||||
source = BytesIO(b"foo\nbar\n")
|
||||
result = StreamResult()
|
||||
case = subunit.ByteStreamToStreamResult(source)
|
||||
e = self.assertRaises(Exception, case.run, result)
|
||||
self.assertEqual(b'f', e.args[1])
|
||||
self.assertEqual(b'oo\nbar\n', source.read())
|
||||
self.assertEqual([], result._events)
|
||||
|
||||
def test_trivial_enumeration(self):
|
||||
source = BytesIO(CONSTANT_ENUM)
|
||||
result = StreamResult()
|
||||
subunit.ByteStreamToStreamResult(
|
||||
source, non_subunit_name="stdout").run(result)
|
||||
self.assertEqual(b'', source.read())
|
||||
self.assertEqual([
|
||||
('status', 'foo', 'exists', None, True, None, None, False, None, None, None),
|
||||
], result._events)
|
||||
|
||||
def test_multiple_events(self):
|
||||
source = BytesIO(CONSTANT_ENUM + CONSTANT_ENUM)
|
||||
result = StreamResult()
|
||||
subunit.ByteStreamToStreamResult(
|
||||
source, non_subunit_name="stdout").run(result)
|
||||
self.assertEqual(b'', source.read())
|
||||
self.assertEqual([
|
||||
('status', 'foo', 'exists', None, True, None, None, False, None, None, None),
|
||||
('status', 'foo', 'exists', None, True, None, None, False, None, None, None),
|
||||
], result._events)
|
||||
|
||||
def test_inprogress(self):
|
||||
self.check_event(CONSTANT_INPROGRESS, 'inprogress')
|
||||
|
||||
def test_success(self):
|
||||
self.check_event(CONSTANT_SUCCESS, 'success')
|
||||
|
||||
def test_uxsuccess(self):
|
||||
self.check_event(CONSTANT_UXSUCCESS, 'uxsuccess')
|
||||
|
||||
def test_skip(self):
|
||||
self.check_event(CONSTANT_SKIP, 'skip')
|
||||
|
||||
def test_fail(self):
|
||||
self.check_event(CONSTANT_FAIL, 'fail')
|
||||
|
||||
def test_xfail(self):
|
||||
self.check_event(CONSTANT_XFAIL, 'xfail')
|
||||
|
||||
def check_events(self, source_bytes, events):
|
||||
source = BytesIO(source_bytes)
|
||||
result = StreamResult()
|
||||
subunit.ByteStreamToStreamResult(
|
||||
source, non_subunit_name="stdout").run(result)
|
||||
self.assertEqual(b'', source.read())
|
||||
self.assertEqual(events, result._events)
|
||||
#- any file attachments should be byte contents [as users assume that].
|
||||
for event in result._events:
|
||||
if event[5] is not None:
|
||||
self.assertIsInstance(event[6], bytes)
|
||||
|
||||
def check_event(self, source_bytes, test_status=None, test_id="foo",
|
||||
route_code=None, timestamp=None, tags=None, mime_type=None,
|
||||
file_name=None, file_bytes=None, eof=False, runnable=True):
|
||||
event = self._event(test_id=test_id, test_status=test_status,
|
||||
tags=tags, runnable=runnable, file_name=file_name,
|
||||
file_bytes=file_bytes, eof=eof, mime_type=mime_type,
|
||||
route_code=route_code, timestamp=timestamp)
|
||||
self.check_events(source_bytes, [event])
|
||||
|
||||
def _event(self, test_status=None, test_id=None, route_code=None,
|
||||
timestamp=None, tags=None, mime_type=None, file_name=None,
|
||||
file_bytes=None, eof=False, runnable=True):
|
||||
return ('status', test_id, test_status, tags, runnable, file_name,
|
||||
file_bytes, eof, mime_type, route_code, timestamp)
|
||||
|
||||
def test_eof(self):
|
||||
self.check_event(CONSTANT_EOF, test_id=None, eof=True)
|
||||
|
||||
def test_file_content(self):
|
||||
self.check_event(CONSTANT_FILE_CONTENT,
|
||||
test_id=None, file_name="barney", file_bytes=b"woo")
|
||||
|
||||
def test_file_content_length_into_checksum(self):
|
||||
# A bad file content length which creeps into the checksum.
|
||||
bad_file_length_content = b'\xb3!@\x13\x06barney\x04woo\xdc\xe2\xdb\x35'
|
||||
self.check_events(bad_file_length_content, [
|
||||
self._event(test_id="subunit.parser", eof=True,
|
||||
file_name="Packet data", file_bytes=bad_file_length_content,
|
||||
mime_type="application/octet-stream"),
|
||||
self._event(test_id="subunit.parser", test_status="fail", eof=True,
|
||||
file_name="Parser Error",
|
||||
file_bytes=b"File content extends past end of packet: claimed 4 bytes, 3 available",
|
||||
mime_type="text/plain;charset=utf8"),
|
||||
])
|
||||
|
||||
def test_packet_length_4_word_varint(self):
|
||||
packet_data = b'\xb3!@\xc0\x00\x11'
|
||||
self.check_events(packet_data, [
|
||||
self._event(test_id="subunit.parser", eof=True,
|
||||
file_name="Packet data", file_bytes=packet_data,
|
||||
mime_type="application/octet-stream"),
|
||||
self._event(test_id="subunit.parser", test_status="fail", eof=True,
|
||||
file_name="Parser Error",
|
||||
file_bytes=b"3 byte maximum given but 4 byte value found.",
|
||||
mime_type="text/plain;charset=utf8"),
|
||||
])
|
||||
|
||||
def test_mime(self):
|
||||
self.check_event(CONSTANT_MIME,
|
||||
test_id=None, mime_type='application/foo; charset=1')
|
||||
|
||||
def test_route_code(self):
|
||||
self.check_event(CONSTANT_ROUTE_CODE,
|
||||
'success', route_code="source", test_id="bar")
|
||||
|
||||
def test_runnable(self):
|
||||
self.check_event(CONSTANT_RUNNABLE,
|
||||
test_status='success', runnable=False)
|
||||
|
||||
def test_tags(self):
|
||||
self.check_event(CONSTANT_TAGS[0],
|
||||
None, tags=set(['foo', 'bar']), test_id="bar")
|
||||
|
||||
def test_timestamp(self):
|
||||
timestamp = datetime.datetime(2001, 12, 12, 12, 59, 59, 45,
|
||||
iso8601.Utc())
|
||||
self.check_event(CONSTANT_TIMESTAMP,
|
||||
'success', test_id='bar', timestamp=timestamp)
|
||||
|
||||
def test_bad_crc_errors_via_status(self):
|
||||
file_bytes = CONSTANT_MIME[:-1] + b'\x00'
|
||||
self.check_events( file_bytes, [
|
||||
self._event(test_id="subunit.parser", eof=True,
|
||||
file_name="Packet data", file_bytes=file_bytes,
|
||||
mime_type="application/octet-stream"),
|
||||
self._event(test_id="subunit.parser", test_status="fail", eof=True,
|
||||
file_name="Parser Error",
|
||||
file_bytes=b'Bad checksum - calculated (0x78335115), '
|
||||
b'stored (0x78335100)',
|
||||
mime_type="text/plain;charset=utf8"),
|
||||
])
|
||||
|
||||
def test_not_utf8_in_string(self):
|
||||
file_bytes = CONSTANT_ROUTE_CODE[:5] + b'\xb4' + CONSTANT_ROUTE_CODE[6:-4] + b'\xce\x56\xc6\x17'
|
||||
self.check_events(file_bytes, [
|
||||
self._event(test_id="subunit.parser", eof=True,
|
||||
file_name="Packet data", file_bytes=file_bytes,
|
||||
mime_type="application/octet-stream"),
|
||||
self._event(test_id="subunit.parser", test_status="fail", eof=True,
|
||||
file_name="Parser Error",
|
||||
file_bytes=b'UTF8 string at offset 2 is not UTF8',
|
||||
mime_type="text/plain;charset=utf8"),
|
||||
])
|
||||
|
||||
def test_NULL_in_string(self):
|
||||
file_bytes = CONSTANT_ROUTE_CODE[:6] + b'\x00' + CONSTANT_ROUTE_CODE[7:-4] + b'\xd7\x41\xac\xfe'
|
||||
self.check_events(file_bytes, [
|
||||
self._event(test_id="subunit.parser", eof=True,
|
||||
file_name="Packet data", file_bytes=file_bytes,
|
||||
mime_type="application/octet-stream"),
|
||||
self._event(test_id="subunit.parser", test_status="fail", eof=True,
|
||||
file_name="Parser Error",
|
||||
file_bytes=b'UTF8 string at offset 2 contains NUL byte',
|
||||
mime_type="text/plain;charset=utf8"),
|
||||
])
|
||||
|
||||
def test_bad_utf8_stringlength(self):
|
||||
file_bytes = CONSTANT_ROUTE_CODE[:4] + b'\x3f' + CONSTANT_ROUTE_CODE[5:-4] + b'\xbe\x29\xe0\xc2'
|
||||
self.check_events(file_bytes, [
|
||||
self._event(test_id="subunit.parser", eof=True,
|
||||
file_name="Packet data", file_bytes=file_bytes,
|
||||
mime_type="application/octet-stream"),
|
||||
self._event(test_id="subunit.parser", test_status="fail", eof=True,
|
||||
file_name="Parser Error",
|
||||
file_bytes=b'UTF8 string at offset 2 extends past end of '
|
||||
b'packet: claimed 63 bytes, 10 available',
|
||||
mime_type="text/plain;charset=utf8"),
|
||||
])
|
||||
|
||||
def test_route_code_and_file_content(self):
|
||||
content = BytesIO()
|
||||
subunit.StreamResultToBytes(content).status(
|
||||
route_code='0', mime_type='text/plain', file_name='bar',
|
||||
file_bytes=b'foo')
|
||||
self.check_event(content.getvalue(), test_id=None, file_name='bar',
|
||||
route_code='0', mime_type='text/plain', file_bytes=b'foo')
|
||||
566
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_test_results.py
vendored
Normal file
566
test/3rdparty/python-subunit-0.0.16/python/subunit/tests/test_test_results.py
vendored
Normal file
@@ -0,0 +1,566 @@
|
||||
#
|
||||
# subunit: extensions to Python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
import csv
|
||||
import datetime
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from testtools import TestCase
|
||||
from testtools.compat import StringIO
|
||||
from testtools.content import (
|
||||
text_content,
|
||||
TracebackContent,
|
||||
)
|
||||
from testtools.testresult.doubles import ExtendedTestResult
|
||||
|
||||
import subunit
|
||||
import subunit.iso8601 as iso8601
|
||||
import subunit.test_results
|
||||
|
||||
import testtools
|
||||
|
||||
|
||||
class LoggingDecorator(subunit.test_results.HookedTestResultDecorator):
|
||||
|
||||
def __init__(self, decorated):
|
||||
self._calls = 0
|
||||
super(LoggingDecorator, self).__init__(decorated)
|
||||
|
||||
def _before_event(self):
|
||||
self._calls += 1
|
||||
|
||||
|
||||
class AssertBeforeTestResult(LoggingDecorator):
|
||||
"""A TestResult for checking preconditions."""
|
||||
|
||||
def __init__(self, decorated, test):
|
||||
self.test = test
|
||||
super(AssertBeforeTestResult, self).__init__(decorated)
|
||||
|
||||
def _before_event(self):
|
||||
self.test.assertEqual(1, self.earlier._calls)
|
||||
super(AssertBeforeTestResult, self)._before_event()
|
||||
|
||||
|
||||
class TimeCapturingResult(unittest.TestResult):
|
||||
|
||||
def __init__(self):
|
||||
super(TimeCapturingResult, self).__init__()
|
||||
self._calls = []
|
||||
self.failfast = False
|
||||
|
||||
def time(self, a_datetime):
|
||||
self._calls.append(a_datetime)
|
||||
|
||||
|
||||
class TestHookedTestResultDecorator(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# An end to the chain
|
||||
terminal = unittest.TestResult()
|
||||
# Asserts that the call was made to self.result before asserter was
|
||||
# called.
|
||||
asserter = AssertBeforeTestResult(terminal, self)
|
||||
# The result object we call, which much increase its call count.
|
||||
self.result = LoggingDecorator(asserter)
|
||||
asserter.earlier = self.result
|
||||
self.decorated = asserter
|
||||
|
||||
def tearDown(self):
|
||||
# The hook in self.result must have been called
|
||||
self.assertEqual(1, self.result._calls)
|
||||
# The hook in asserter must have been called too, otherwise the
|
||||
# assertion about ordering won't have completed.
|
||||
self.assertEqual(1, self.decorated._calls)
|
||||
|
||||
def test_startTest(self):
|
||||
self.result.startTest(self)
|
||||
|
||||
def test_startTestRun(self):
|
||||
self.result.startTestRun()
|
||||
|
||||
def test_stopTest(self):
|
||||
self.result.stopTest(self)
|
||||
|
||||
def test_stopTestRun(self):
|
||||
self.result.stopTestRun()
|
||||
|
||||
def test_addError(self):
|
||||
self.result.addError(self, subunit.RemoteError())
|
||||
|
||||
def test_addError_details(self):
|
||||
self.result.addError(self, details={})
|
||||
|
||||
def test_addFailure(self):
|
||||
self.result.addFailure(self, subunit.RemoteError())
|
||||
|
||||
def test_addFailure_details(self):
|
||||
self.result.addFailure(self, details={})
|
||||
|
||||
def test_addSuccess(self):
|
||||
self.result.addSuccess(self)
|
||||
|
||||
def test_addSuccess_details(self):
|
||||
self.result.addSuccess(self, details={})
|
||||
|
||||
def test_addSkip(self):
|
||||
self.result.addSkip(self, "foo")
|
||||
|
||||
def test_addSkip_details(self):
|
||||
self.result.addSkip(self, details={})
|
||||
|
||||
def test_addExpectedFailure(self):
|
||||
self.result.addExpectedFailure(self, subunit.RemoteError())
|
||||
|
||||
def test_addExpectedFailure_details(self):
|
||||
self.result.addExpectedFailure(self, details={})
|
||||
|
||||
def test_addUnexpectedSuccess(self):
|
||||
self.result.addUnexpectedSuccess(self)
|
||||
|
||||
def test_addUnexpectedSuccess_details(self):
|
||||
self.result.addUnexpectedSuccess(self, details={})
|
||||
|
||||
def test_progress(self):
|
||||
self.result.progress(1, subunit.PROGRESS_SET)
|
||||
|
||||
def test_wasSuccessful(self):
|
||||
self.result.wasSuccessful()
|
||||
|
||||
def test_shouldStop(self):
|
||||
self.result.shouldStop
|
||||
|
||||
def test_stop(self):
|
||||
self.result.stop()
|
||||
|
||||
def test_time(self):
|
||||
self.result.time(None)
|
||||
|
||||
|
||||
class TestAutoTimingTestResultDecorator(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# And end to the chain which captures time events.
|
||||
terminal = TimeCapturingResult()
|
||||
# The result object under test.
|
||||
self.result = subunit.test_results.AutoTimingTestResultDecorator(
|
||||
terminal)
|
||||
self.decorated = terminal
|
||||
|
||||
def test_without_time_calls_time_is_called_and_not_None(self):
|
||||
self.result.startTest(self)
|
||||
self.assertEqual(1, len(self.decorated._calls))
|
||||
self.assertNotEqual(None, self.decorated._calls[0])
|
||||
|
||||
def test_no_time_from_progress(self):
|
||||
self.result.progress(1, subunit.PROGRESS_CUR)
|
||||
self.assertEqual(0, len(self.decorated._calls))
|
||||
|
||||
def test_no_time_from_shouldStop(self):
|
||||
self.decorated.stop()
|
||||
self.result.shouldStop
|
||||
self.assertEqual(0, len(self.decorated._calls))
|
||||
|
||||
def test_calling_time_inhibits_automatic_time(self):
|
||||
# Calling time() outputs a time signal immediately and prevents
|
||||
# automatically adding one when other methods are called.
|
||||
time = datetime.datetime(2009,10,11,12,13,14,15, iso8601.Utc())
|
||||
self.result.time(time)
|
||||
self.result.startTest(self)
|
||||
self.result.stopTest(self)
|
||||
self.assertEqual(1, len(self.decorated._calls))
|
||||
self.assertEqual(time, self.decorated._calls[0])
|
||||
|
||||
def test_calling_time_None_enables_automatic_time(self):
|
||||
time = datetime.datetime(2009,10,11,12,13,14,15, iso8601.Utc())
|
||||
self.result.time(time)
|
||||
self.assertEqual(1, len(self.decorated._calls))
|
||||
self.assertEqual(time, self.decorated._calls[0])
|
||||
# Calling None passes the None through, in case other results care.
|
||||
self.result.time(None)
|
||||
self.assertEqual(2, len(self.decorated._calls))
|
||||
self.assertEqual(None, self.decorated._calls[1])
|
||||
# Calling other methods doesn't generate an automatic time event.
|
||||
self.result.startTest(self)
|
||||
self.assertEqual(3, len(self.decorated._calls))
|
||||
self.assertNotEqual(None, self.decorated._calls[2])
|
||||
|
||||
def test_set_failfast_True(self):
|
||||
self.assertFalse(self.decorated.failfast)
|
||||
self.result.failfast = True
|
||||
self.assertTrue(self.decorated.failfast)
|
||||
|
||||
|
||||
class TestTagCollapsingDecorator(TestCase):
|
||||
|
||||
def test_tags_collapsed_outside_of_tests(self):
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TagCollapsingDecorator(result)
|
||||
tag_collapser.tags(set(['a']), set())
|
||||
tag_collapser.tags(set(['b']), set())
|
||||
tag_collapser.startTest(self)
|
||||
self.assertEquals(
|
||||
[('tags', set(['a', 'b']), set([])),
|
||||
('startTest', self),
|
||||
], result._events)
|
||||
|
||||
def test_tags_collapsed_outside_of_tests_are_flushed(self):
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TagCollapsingDecorator(result)
|
||||
tag_collapser.startTestRun()
|
||||
tag_collapser.tags(set(['a']), set())
|
||||
tag_collapser.tags(set(['b']), set())
|
||||
tag_collapser.startTest(self)
|
||||
tag_collapser.addSuccess(self)
|
||||
tag_collapser.stopTest(self)
|
||||
tag_collapser.stopTestRun()
|
||||
self.assertEquals(
|
||||
[('startTestRun',),
|
||||
('tags', set(['a', 'b']), set([])),
|
||||
('startTest', self),
|
||||
('addSuccess', self),
|
||||
('stopTest', self),
|
||||
('stopTestRun',),
|
||||
], result._events)
|
||||
|
||||
def test_tags_forwarded_after_tests(self):
|
||||
test = subunit.RemotedTestCase('foo')
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TagCollapsingDecorator(result)
|
||||
tag_collapser.startTestRun()
|
||||
tag_collapser.startTest(test)
|
||||
tag_collapser.addSuccess(test)
|
||||
tag_collapser.stopTest(test)
|
||||
tag_collapser.tags(set(['a']), set(['b']))
|
||||
tag_collapser.stopTestRun()
|
||||
self.assertEqual(
|
||||
[('startTestRun',),
|
||||
('startTest', test),
|
||||
('addSuccess', test),
|
||||
('stopTest', test),
|
||||
('tags', set(['a']), set(['b'])),
|
||||
('stopTestRun',),
|
||||
],
|
||||
result._events)
|
||||
|
||||
def test_tags_collapsed_inside_of_tests(self):
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TagCollapsingDecorator(result)
|
||||
test = subunit.RemotedTestCase('foo')
|
||||
tag_collapser.startTest(test)
|
||||
tag_collapser.tags(set(['a']), set())
|
||||
tag_collapser.tags(set(['b']), set(['a']))
|
||||
tag_collapser.tags(set(['c']), set())
|
||||
tag_collapser.stopTest(test)
|
||||
self.assertEquals(
|
||||
[('startTest', test),
|
||||
('tags', set(['b', 'c']), set(['a'])),
|
||||
('stopTest', test)],
|
||||
result._events)
|
||||
|
||||
def test_tags_collapsed_inside_of_tests_different_ordering(self):
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TagCollapsingDecorator(result)
|
||||
test = subunit.RemotedTestCase('foo')
|
||||
tag_collapser.startTest(test)
|
||||
tag_collapser.tags(set(), set(['a']))
|
||||
tag_collapser.tags(set(['a', 'b']), set())
|
||||
tag_collapser.tags(set(['c']), set())
|
||||
tag_collapser.stopTest(test)
|
||||
self.assertEquals(
|
||||
[('startTest', test),
|
||||
('tags', set(['a', 'b', 'c']), set()),
|
||||
('stopTest', test)],
|
||||
result._events)
|
||||
|
||||
def test_tags_sent_before_result(self):
|
||||
# Because addSuccess and friends tend to send subunit output
|
||||
# immediately, and because 'tags:' before a result line means
|
||||
# something different to 'tags:' after a result line, we need to be
|
||||
# sure that tags are emitted before 'addSuccess' (or whatever).
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TagCollapsingDecorator(result)
|
||||
test = subunit.RemotedTestCase('foo')
|
||||
tag_collapser.startTest(test)
|
||||
tag_collapser.tags(set(['a']), set())
|
||||
tag_collapser.addSuccess(test)
|
||||
tag_collapser.stopTest(test)
|
||||
self.assertEquals(
|
||||
[('startTest', test),
|
||||
('tags', set(['a']), set()),
|
||||
('addSuccess', test),
|
||||
('stopTest', test)],
|
||||
result._events)
|
||||
|
||||
|
||||
class TestTimeCollapsingDecorator(TestCase):
|
||||
|
||||
def make_time(self):
|
||||
# Heh heh.
|
||||
return datetime.datetime(
|
||||
2000, 1, self.getUniqueInteger(), tzinfo=iso8601.UTC)
|
||||
|
||||
def test_initial_time_forwarded(self):
|
||||
# We always forward the first time event we see.
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
|
||||
a_time = self.make_time()
|
||||
tag_collapser.time(a_time)
|
||||
self.assertEquals([('time', a_time)], result._events)
|
||||
|
||||
def test_time_collapsed_to_first_and_last(self):
|
||||
# If there are many consecutive time events, only the first and last
|
||||
# are sent through.
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
|
||||
times = [self.make_time() for i in range(5)]
|
||||
for a_time in times:
|
||||
tag_collapser.time(a_time)
|
||||
tag_collapser.startTest(subunit.RemotedTestCase('foo'))
|
||||
self.assertEquals(
|
||||
[('time', times[0]), ('time', times[-1])], result._events[:-1])
|
||||
|
||||
def test_only_one_time_sent(self):
|
||||
# If we receive a single time event followed by a non-time event, we
|
||||
# send exactly one time event.
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
|
||||
a_time = self.make_time()
|
||||
tag_collapser.time(a_time)
|
||||
tag_collapser.startTest(subunit.RemotedTestCase('foo'))
|
||||
self.assertEquals([('time', a_time)], result._events[:-1])
|
||||
|
||||
def test_duplicate_times_not_sent(self):
|
||||
# Many time events with the exact same time are collapsed into one
|
||||
# time event.
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
|
||||
a_time = self.make_time()
|
||||
for i in range(5):
|
||||
tag_collapser.time(a_time)
|
||||
tag_collapser.startTest(subunit.RemotedTestCase('foo'))
|
||||
self.assertEquals([('time', a_time)], result._events[:-1])
|
||||
|
||||
def test_no_times_inserted(self):
|
||||
result = ExtendedTestResult()
|
||||
tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
|
||||
a_time = self.make_time()
|
||||
tag_collapser.time(a_time)
|
||||
foo = subunit.RemotedTestCase('foo')
|
||||
tag_collapser.startTest(foo)
|
||||
tag_collapser.addSuccess(foo)
|
||||
tag_collapser.stopTest(foo)
|
||||
self.assertEquals(
|
||||
[('time', a_time),
|
||||
('startTest', foo),
|
||||
('addSuccess', foo),
|
||||
('stopTest', foo)], result._events)
|
||||
|
||||
|
||||
class TestByTestResultTests(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestByTestResultTests, self).setUp()
|
||||
self.log = []
|
||||
self.result = subunit.test_results.TestByTestResult(self.on_test)
|
||||
if sys.version_info >= (3, 0):
|
||||
self.result._now = iter(range(5)).__next__
|
||||
else:
|
||||
self.result._now = iter(range(5)).next
|
||||
|
||||
def assertCalled(self, **kwargs):
|
||||
defaults = {
|
||||
'test': self,
|
||||
'tags': set(),
|
||||
'details': None,
|
||||
'start_time': 0,
|
||||
'stop_time': 1,
|
||||
}
|
||||
defaults.update(kwargs)
|
||||
self.assertEqual([defaults], self.log)
|
||||
|
||||
def on_test(self, **kwargs):
|
||||
self.log.append(kwargs)
|
||||
|
||||
def test_no_tests_nothing_reported(self):
|
||||
self.result.startTestRun()
|
||||
self.result.stopTestRun()
|
||||
self.assertEqual([], self.log)
|
||||
|
||||
def test_add_success(self):
|
||||
self.result.startTest(self)
|
||||
self.result.addSuccess(self)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(status='success')
|
||||
|
||||
def test_add_success_details(self):
|
||||
self.result.startTest(self)
|
||||
details = {'foo': 'bar'}
|
||||
self.result.addSuccess(self, details=details)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(status='success', details=details)
|
||||
|
||||
def test_tags(self):
|
||||
if not getattr(self.result, 'tags', None):
|
||||
self.skipTest("No tags in testtools")
|
||||
self.result.tags(['foo'], [])
|
||||
self.result.startTest(self)
|
||||
self.result.addSuccess(self)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(status='success', tags=set(['foo']))
|
||||
|
||||
def test_add_error(self):
|
||||
self.result.startTest(self)
|
||||
try:
|
||||
1/0
|
||||
except ZeroDivisionError:
|
||||
error = sys.exc_info()
|
||||
self.result.addError(self, error)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(
|
||||
status='error',
|
||||
details={'traceback': TracebackContent(error, self)})
|
||||
|
||||
def test_add_error_details(self):
|
||||
self.result.startTest(self)
|
||||
details = {"foo": text_content("bar")}
|
||||
self.result.addError(self, details=details)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(status='error', details=details)
|
||||
|
||||
def test_add_failure(self):
|
||||
self.result.startTest(self)
|
||||
try:
|
||||
self.fail("intentional failure")
|
||||
except self.failureException:
|
||||
failure = sys.exc_info()
|
||||
self.result.addFailure(self, failure)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(
|
||||
status='failure',
|
||||
details={'traceback': TracebackContent(failure, self)})
|
||||
|
||||
def test_add_failure_details(self):
|
||||
self.result.startTest(self)
|
||||
details = {"foo": text_content("bar")}
|
||||
self.result.addFailure(self, details=details)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(status='failure', details=details)
|
||||
|
||||
def test_add_xfail(self):
|
||||
self.result.startTest(self)
|
||||
try:
|
||||
1/0
|
||||
except ZeroDivisionError:
|
||||
error = sys.exc_info()
|
||||
self.result.addExpectedFailure(self, error)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(
|
||||
status='xfail',
|
||||
details={'traceback': TracebackContent(error, self)})
|
||||
|
||||
def test_add_xfail_details(self):
|
||||
self.result.startTest(self)
|
||||
details = {"foo": text_content("bar")}
|
||||
self.result.addExpectedFailure(self, details=details)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(status='xfail', details=details)
|
||||
|
||||
def test_add_unexpected_success(self):
|
||||
self.result.startTest(self)
|
||||
details = {'foo': 'bar'}
|
||||
self.result.addUnexpectedSuccess(self, details=details)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(status='success', details=details)
|
||||
|
||||
def test_add_skip_reason(self):
|
||||
self.result.startTest(self)
|
||||
reason = self.getUniqueString()
|
||||
self.result.addSkip(self, reason)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(
|
||||
status='skip', details={'reason': text_content(reason)})
|
||||
|
||||
def test_add_skip_details(self):
|
||||
self.result.startTest(self)
|
||||
details = {'foo': 'bar'}
|
||||
self.result.addSkip(self, details=details)
|
||||
self.result.stopTest(self)
|
||||
self.assertCalled(status='skip', details=details)
|
||||
|
||||
def test_twice(self):
|
||||
self.result.startTest(self)
|
||||
self.result.addSuccess(self, details={'foo': 'bar'})
|
||||
self.result.stopTest(self)
|
||||
self.result.startTest(self)
|
||||
self.result.addSuccess(self)
|
||||
self.result.stopTest(self)
|
||||
self.assertEqual(
|
||||
[{'test': self,
|
||||
'status': 'success',
|
||||
'start_time': 0,
|
||||
'stop_time': 1,
|
||||
'tags': set(),
|
||||
'details': {'foo': 'bar'}},
|
||||
{'test': self,
|
||||
'status': 'success',
|
||||
'start_time': 2,
|
||||
'stop_time': 3,
|
||||
'tags': set(),
|
||||
'details': None},
|
||||
],
|
||||
self.log)
|
||||
|
||||
|
||||
class TestCsvResult(testtools.TestCase):
|
||||
|
||||
def parse_stream(self, stream):
|
||||
stream.seek(0)
|
||||
reader = csv.reader(stream)
|
||||
return list(reader)
|
||||
|
||||
def test_csv_output(self):
|
||||
stream = StringIO()
|
||||
result = subunit.test_results.CsvResult(stream)
|
||||
if sys.version_info >= (3, 0):
|
||||
result._now = iter(range(5)).__next__
|
||||
else:
|
||||
result._now = iter(range(5)).next
|
||||
result.startTestRun()
|
||||
result.startTest(self)
|
||||
result.addSuccess(self)
|
||||
result.stopTest(self)
|
||||
result.stopTestRun()
|
||||
self.assertEqual(
|
||||
[['test', 'status', 'start_time', 'stop_time'],
|
||||
[self.id(), 'success', '0', '1'],
|
||||
],
|
||||
self.parse_stream(stream))
|
||||
|
||||
def test_just_header_when_no_tests(self):
|
||||
stream = StringIO()
|
||||
result = subunit.test_results.CsvResult(stream)
|
||||
result.startTestRun()
|
||||
result.stopTestRun()
|
||||
self.assertEqual(
|
||||
[['test', 'status', 'start_time', 'stop_time']],
|
||||
self.parse_stream(stream))
|
||||
|
||||
def test_no_output_before_events(self):
|
||||
stream = StringIO()
|
||||
subunit.test_results.CsvResult(stream)
|
||||
self.assertEqual([], self.parse_stream(stream))
|
||||
495
test/3rdparty/python-subunit-0.0.16/python/subunit/v2.py
vendored
Normal file
495
test/3rdparty/python-subunit-0.0.16/python/subunit/v2.py
vendored
Normal file
@@ -0,0 +1,495 @@
|
||||
#
|
||||
# subunit: extensions to Python unittest to get test results from subprocesses.
|
||||
# Copyright (C) 2013 Robert Collins <robertc@robertcollins.net>
|
||||
#
|
||||
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
|
||||
# license at the users choice. A copy of both licenses are available in the
|
||||
# project source as Apache-2.0 and BSD. You may not use this file except in
|
||||
# compliance with one of these two licences.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# license you chose for the specific language governing permissions and
|
||||
# limitations under that license.
|
||||
#
|
||||
|
||||
import codecs
|
||||
utf_8_decode = codecs.utf_8_decode
|
||||
import datetime
|
||||
from io import UnsupportedOperation
|
||||
import os
|
||||
import select
|
||||
import struct
|
||||
import zlib
|
||||
|
||||
from extras import safe_hasattr, try_imports
|
||||
builtins = try_imports(['__builtin__', 'builtins'])
|
||||
|
||||
import subunit
|
||||
import subunit.iso8601 as iso8601
|
||||
|
||||
__all__ = [
|
||||
'ByteStreamToStreamResult',
|
||||
'StreamResultToBytes',
|
||||
]
|
||||
|
||||
SIGNATURE = b'\xb3'
|
||||
FMT_8 = '>B'
|
||||
FMT_16 = '>H'
|
||||
FMT_24 = '>HB'
|
||||
FMT_32 = '>I'
|
||||
FMT_TIMESTAMP = '>II'
|
||||
FLAG_TEST_ID = 0x0800
|
||||
FLAG_ROUTE_CODE = 0x0400
|
||||
FLAG_TIMESTAMP = 0x0200
|
||||
FLAG_RUNNABLE = 0x0100
|
||||
FLAG_TAGS = 0x0080
|
||||
FLAG_MIME_TYPE = 0x0020
|
||||
FLAG_EOF = 0x0010
|
||||
FLAG_FILE_CONTENT = 0x0040
|
||||
EPOCH = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=iso8601.Utc())
|
||||
NUL_ELEMENT = b'\0'[0]
|
||||
# Contains True for types for which 'nul in thing' falsely returns false.
|
||||
_nul_test_broken = {}
|
||||
|
||||
|
||||
def has_nul(buffer_or_bytes):
|
||||
"""Return True if a null byte is present in buffer_or_bytes."""
|
||||
# Simple "if NUL_ELEMENT in utf8_bytes:" fails on Python 3.1 and 3.2 with
|
||||
# memoryviews. See https://bugs.launchpad.net/subunit/+bug/1216246
|
||||
buffer_type = type(buffer_or_bytes)
|
||||
broken = _nul_test_broken.get(buffer_type)
|
||||
if broken is None:
|
||||
reference = buffer_type(b'\0')
|
||||
broken = not NUL_ELEMENT in reference
|
||||
_nul_test_broken[buffer_type] = broken
|
||||
if broken:
|
||||
return b'\0' in buffer_or_bytes
|
||||
else:
|
||||
return NUL_ELEMENT in buffer_or_bytes
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
"""Used to pass error messages within the parser."""
|
||||
|
||||
|
||||
class StreamResultToBytes(object):
|
||||
"""Convert StreamResult API calls to bytes.
|
||||
|
||||
The StreamResult API is defined by testtools.StreamResult.
|
||||
"""
|
||||
|
||||
status_mask = {
|
||||
None: 0,
|
||||
'exists': 0x1,
|
||||
'inprogress': 0x2,
|
||||
'success': 0x3,
|
||||
'uxsuccess': 0x4,
|
||||
'skip': 0x5,
|
||||
'fail': 0x6,
|
||||
'xfail': 0x7,
|
||||
}
|
||||
|
||||
zero_b = b'\0'[0]
|
||||
|
||||
def __init__(self, output_stream):
|
||||
"""Create a StreamResultToBytes with output written to output_stream.
|
||||
|
||||
:param output_stream: A file-like object. Must support write(bytes)
|
||||
and flush() methods. Flush will be called after each write.
|
||||
The stream will be passed through subunit.make_stream_binary,
|
||||
to handle regular cases such as stdout.
|
||||
"""
|
||||
self.output_stream = subunit.make_stream_binary(output_stream)
|
||||
|
||||
def startTestRun(self):
|
||||
pass
|
||||
|
||||
def stopTestRun(self):
|
||||
pass
|
||||
|
||||
def status(self, test_id=None, test_status=None, test_tags=None,
|
||||
runnable=True, file_name=None, file_bytes=None, eof=False,
|
||||
mime_type=None, route_code=None, timestamp=None):
|
||||
self._write_packet(test_id=test_id, test_status=test_status,
|
||||
test_tags=test_tags, runnable=runnable, file_name=file_name,
|
||||
file_bytes=file_bytes, eof=eof, mime_type=mime_type,
|
||||
route_code=route_code, timestamp=timestamp)
|
||||
|
||||
def _write_utf8(self, a_string, packet):
|
||||
utf8 = a_string.encode('utf-8')
|
||||
self._write_number(len(utf8), packet)
|
||||
packet.append(utf8)
|
||||
|
||||
def _write_len16(self, length, packet):
|
||||
assert length < 65536
|
||||
packet.append(struct.pack(FMT_16, length))
|
||||
|
||||
def _write_number(self, value, packet):
|
||||
packet.extend(self._encode_number(value))
|
||||
|
||||
def _encode_number(self, value):
|
||||
assert value >= 0
|
||||
if value < 64:
|
||||
return [struct.pack(FMT_8, value)]
|
||||
elif value < 16384:
|
||||
value = value | 0x4000
|
||||
return [struct.pack(FMT_16, value)]
|
||||
elif value < 4194304:
|
||||
value = value | 0x800000
|
||||
return [struct.pack(FMT_16, value >> 8),
|
||||
struct.pack(FMT_8, value & 0xff)]
|
||||
elif value < 1073741824:
|
||||
value = value | 0xc0000000
|
||||
return [struct.pack(FMT_32, value)]
|
||||
else:
|
||||
raise ValueError('value too large to encode: %r' % (value,))
|
||||
|
||||
def _write_packet(self, test_id=None, test_status=None, test_tags=None,
|
||||
runnable=True, file_name=None, file_bytes=None, eof=False,
|
||||
mime_type=None, route_code=None, timestamp=None):
|
||||
packet = [SIGNATURE]
|
||||
packet.append(b'FF') # placeholder for flags
|
||||
# placeholder for length, but see below as length is variable.
|
||||
packet.append(b'')
|
||||
flags = 0x2000 # Version 0x2
|
||||
if timestamp is not None:
|
||||
flags = flags | FLAG_TIMESTAMP
|
||||
since_epoch = timestamp - EPOCH
|
||||
nanoseconds = since_epoch.microseconds * 1000
|
||||
seconds = (since_epoch.seconds + since_epoch.days * 24 * 3600)
|
||||
packet.append(struct.pack(FMT_32, seconds))
|
||||
self._write_number(nanoseconds, packet)
|
||||
if test_id is not None:
|
||||
flags = flags | FLAG_TEST_ID
|
||||
self._write_utf8(test_id, packet)
|
||||
if test_tags:
|
||||
flags = flags | FLAG_TAGS
|
||||
self._write_number(len(test_tags), packet)
|
||||
for tag in test_tags:
|
||||
self._write_utf8(tag, packet)
|
||||
if runnable:
|
||||
flags = flags | FLAG_RUNNABLE
|
||||
if mime_type:
|
||||
flags = flags | FLAG_MIME_TYPE
|
||||
self._write_utf8(mime_type, packet)
|
||||
if file_name is not None:
|
||||
flags = flags | FLAG_FILE_CONTENT
|
||||
self._write_utf8(file_name, packet)
|
||||
self._write_number(len(file_bytes), packet)
|
||||
packet.append(file_bytes)
|
||||
if eof:
|
||||
flags = flags | FLAG_EOF
|
||||
if route_code is not None:
|
||||
flags = flags | FLAG_ROUTE_CODE
|
||||
self._write_utf8(route_code, packet)
|
||||
# 0x0008 - not used in v2.
|
||||
flags = flags | self.status_mask[test_status]
|
||||
packet[1] = struct.pack(FMT_16, flags)
|
||||
base_length = sum(map(len, packet)) + 4
|
||||
if base_length <= 62:
|
||||
# one byte to encode length, 62+1 = 63
|
||||
length_length = 1
|
||||
elif base_length <= 16381:
|
||||
# two bytes to encode length, 16381+2 = 16383
|
||||
length_length = 2
|
||||
elif base_length <= 4194300:
|
||||
# three bytes to encode length, 419430+3=4194303
|
||||
length_length = 3
|
||||
else:
|
||||
# Longer than policy:
|
||||
# TODO: chunk the packet automatically?
|
||||
# - strip all but file data
|
||||
# - do 4M chunks of that till done
|
||||
# - include original data in final chunk.
|
||||
raise ValueError("Length too long: %r" % base_length)
|
||||
packet[2:3] = self._encode_number(base_length + length_length)
|
||||
# We could either do a partial application of crc32 over each chunk
|
||||
# or a single join to a temp variable then a final join
|
||||
# or two writes (that python might then split).
|
||||
# For now, simplest code: join, crc32, join, output
|
||||
content = b''.join(packet)
|
||||
self.output_stream.write(content + struct.pack(
|
||||
FMT_32, zlib.crc32(content) & 0xffffffff))
|
||||
self.output_stream.flush()
|
||||
|
||||
|
||||
class ByteStreamToStreamResult(object):
|
||||
"""Parse a subunit byte stream.
|
||||
|
||||
Mixed streams that contain non-subunit content is supported when a
|
||||
non_subunit_name is passed to the contructor. The default is to raise an
|
||||
error containing the non-subunit byte after it has been read from the
|
||||
stream.
|
||||
|
||||
Typical use:
|
||||
|
||||
>>> case = ByteStreamToStreamResult(sys.stdin.buffer)
|
||||
>>> result = StreamResult()
|
||||
>>> result.startTestRun()
|
||||
>>> case.run(result)
|
||||
>>> result.stopTestRun()
|
||||
"""
|
||||
|
||||
status_lookup = {
|
||||
0x0: None,
|
||||
0x1: 'exists',
|
||||
0x2: 'inprogress',
|
||||
0x3: 'success',
|
||||
0x4: 'uxsuccess',
|
||||
0x5: 'skip',
|
||||
0x6: 'fail',
|
||||
0x7: 'xfail',
|
||||
}
|
||||
|
||||
def __init__(self, source, non_subunit_name=None):
|
||||
"""Create a ByteStreamToStreamResult.
|
||||
|
||||
:param source: A file like object to read bytes from. Must support
|
||||
read(<count>) and return bytes. The file is not closed by
|
||||
ByteStreamToStreamResult. subunit.make_stream_binary() is
|
||||
called on the stream to get it into bytes mode.
|
||||
:param non_subunit_name: If set to non-None, non subunit content
|
||||
encountered in the stream will be converted into file packets
|
||||
labelled with this name.
|
||||
"""
|
||||
self.non_subunit_name = non_subunit_name
|
||||
self.source = subunit.make_stream_binary(source)
|
||||
self.codec = codecs.lookup('utf8').incrementaldecoder()
|
||||
|
||||
def run(self, result):
|
||||
"""Parse source and emit events to result.
|
||||
|
||||
This is a blocking call: it will run until EOF is detected on source.
|
||||
"""
|
||||
self.codec.reset()
|
||||
mid_character = False
|
||||
while True:
|
||||
# We're in blocking mode; read one char
|
||||
content = self.source.read(1)
|
||||
if not content:
|
||||
# EOF
|
||||
return
|
||||
if not mid_character and content[0] == SIGNATURE[0]:
|
||||
self._parse_packet(result)
|
||||
continue
|
||||
if self.non_subunit_name is None:
|
||||
raise Exception("Non subunit content", content)
|
||||
try:
|
||||
if self.codec.decode(content):
|
||||
# End of a character
|
||||
mid_character = False
|
||||
else:
|
||||
mid_character = True
|
||||
except UnicodeDecodeError:
|
||||
# Bad unicode, not our concern.
|
||||
mid_character = False
|
||||
# Aggregate all content that is not subunit until either
|
||||
# 1MiB is accumulated or 50ms has passed with no input.
|
||||
# Both are arbitrary amounts intended to give a simple
|
||||
# balance between efficiency (avoiding death by a thousand
|
||||
# one-byte packets), buffering (avoiding overlarge state
|
||||
# being hidden on intermediary nodes) and interactivity
|
||||
# (when driving a debugger, slow response to typing is
|
||||
# annoying).
|
||||
buffered = [content]
|
||||
while len(buffered[-1]):
|
||||
try:
|
||||
self.source.fileno()
|
||||
except:
|
||||
# Won't be able to select, fallback to
|
||||
# one-byte-at-a-time.
|
||||
break
|
||||
# Note: this has a very low timeout because with stdin, the
|
||||
# BufferedIO layer typically has all the content available
|
||||
# from the stream when e.g. pdb is dropped into, leading to
|
||||
# select always timing out when in fact we could have read
|
||||
# (from the buffer layer) - we typically fail to aggregate
|
||||
# any content on 3.x Pythons.
|
||||
readable = select.select([self.source], [], [], 0.000001)[0]
|
||||
if readable:
|
||||
content = self.source.read(1)
|
||||
if not len(content):
|
||||
# EOF - break and emit buffered.
|
||||
break
|
||||
if not mid_character and content[0] == SIGNATURE[0]:
|
||||
# New packet, break, emit buffered, then parse.
|
||||
break
|
||||
buffered.append(content)
|
||||
# Feed into the codec.
|
||||
try:
|
||||
if self.codec.decode(content):
|
||||
# End of a character
|
||||
mid_character = False
|
||||
else:
|
||||
mid_character = True
|
||||
except UnicodeDecodeError:
|
||||
# Bad unicode, not our concern.
|
||||
mid_character = False
|
||||
if not readable or len(buffered) >= 1048576:
|
||||
# timeout or too much data, emit what we have.
|
||||
break
|
||||
result.status(
|
||||
file_name=self.non_subunit_name,
|
||||
file_bytes=b''.join(buffered))
|
||||
if mid_character or not len(content) or content[0] != SIGNATURE[0]:
|
||||
continue
|
||||
# Otherwise, parse a data packet.
|
||||
self._parse_packet(result)
|
||||
|
||||
def _parse_packet(self, result):
|
||||
try:
|
||||
packet = [SIGNATURE]
|
||||
self._parse(packet, result)
|
||||
except ParseError as error:
|
||||
result.status(test_id="subunit.parser", eof=True,
|
||||
file_name="Packet data", file_bytes=b''.join(packet),
|
||||
mime_type="application/octet-stream")
|
||||
result.status(test_id="subunit.parser", test_status='fail',
|
||||
eof=True, file_name="Parser Error",
|
||||
file_bytes=(error.args[0]).encode('utf8'),
|
||||
mime_type="text/plain;charset=utf8")
|
||||
|
||||
def _to_bytes(self, data, pos, length):
|
||||
"""Return a slice of data from pos for length as bytes."""
|
||||
# memoryview in 2.7.3 and 3.2 isn't directly usable with struct :(.
|
||||
# see https://bugs.launchpad.net/subunit/+bug/1216163
|
||||
result = data[pos:pos+length]
|
||||
if type(result) is not bytes:
|
||||
return result.tobytes()
|
||||
return result
|
||||
|
||||
def _parse_varint(self, data, pos, max_3_bytes=False):
|
||||
# because the only incremental IO we do is at the start, and the 32 bit
|
||||
# CRC means we can always safely read enough to cover any varint, we
|
||||
# can be sure that there should be enough data - and if not it is an
|
||||
# error not a normal situation.
|
||||
data_0 = struct.unpack(FMT_8, self._to_bytes(data, pos, 1))[0]
|
||||
typeenum = data_0 & 0xc0
|
||||
value_0 = data_0 & 0x3f
|
||||
if typeenum == 0x00:
|
||||
return value_0, 1
|
||||
elif typeenum == 0x40:
|
||||
data_1 = struct.unpack(FMT_8, self._to_bytes(data, pos+1, 1))[0]
|
||||
return (value_0 << 8) | data_1, 2
|
||||
elif typeenum == 0x80:
|
||||
data_1 = struct.unpack(FMT_16, self._to_bytes(data, pos+1, 2))[0]
|
||||
return (value_0 << 16) | data_1, 3
|
||||
else:
|
||||
if max_3_bytes:
|
||||
raise ParseError('3 byte maximum given but 4 byte value found.')
|
||||
data_1, data_2 = struct.unpack(FMT_24, self._to_bytes(data, pos+1, 3))
|
||||
result = (value_0 << 24) | data_1 << 8 | data_2
|
||||
return result, 4
|
||||
|
||||
def _parse(self, packet, result):
|
||||
# 2 bytes flags, at most 3 bytes length.
|
||||
packet.append(self.source.read(5))
|
||||
flags = struct.unpack(FMT_16, packet[-1][:2])[0]
|
||||
length, consumed = self._parse_varint(
|
||||
packet[-1], 2, max_3_bytes=True)
|
||||
remainder = self.source.read(length - 6)
|
||||
if len(remainder) != length - 6:
|
||||
raise ParseError(
|
||||
'Short read - got %d bytes, wanted %d bytes' % (
|
||||
len(remainder), length - 6))
|
||||
if consumed != 3:
|
||||
# Avoid having to parse torn values
|
||||
packet[-1] += remainder
|
||||
pos = 2 + consumed
|
||||
else:
|
||||
# Avoid copying potentially lots of data.
|
||||
packet.append(remainder)
|
||||
pos = 0
|
||||
crc = zlib.crc32(packet[0])
|
||||
for fragment in packet[1:-1]:
|
||||
crc = zlib.crc32(fragment, crc)
|
||||
crc = zlib.crc32(packet[-1][:-4], crc) & 0xffffffff
|
||||
packet_crc = struct.unpack(FMT_32, packet[-1][-4:])[0]
|
||||
if crc != packet_crc:
|
||||
# Bad CRC, report it and stop parsing the packet.
|
||||
raise ParseError(
|
||||
'Bad checksum - calculated (0x%x), stored (0x%x)'
|
||||
% (crc, packet_crc))
|
||||
if safe_hasattr(builtins, 'memoryview'):
|
||||
body = memoryview(packet[-1])
|
||||
else:
|
||||
body = packet[-1]
|
||||
# Discard CRC-32
|
||||
body = body[:-4]
|
||||
# One packet could have both file and status data; the Python API
|
||||
# presents these separately (perhaps it shouldn't?)
|
||||
if flags & FLAG_TIMESTAMP:
|
||||
seconds = struct.unpack(FMT_32, self._to_bytes(body, pos, 4))[0]
|
||||
nanoseconds, consumed = self._parse_varint(body, pos+4)
|
||||
pos = pos + 4 + consumed
|
||||
timestamp = EPOCH + datetime.timedelta(
|
||||
seconds=seconds, microseconds=nanoseconds/1000)
|
||||
else:
|
||||
timestamp = None
|
||||
if flags & FLAG_TEST_ID:
|
||||
test_id, pos = self._read_utf8(body, pos)
|
||||
else:
|
||||
test_id = None
|
||||
if flags & FLAG_TAGS:
|
||||
tag_count, consumed = self._parse_varint(body, pos)
|
||||
pos += consumed
|
||||
test_tags = set()
|
||||
for _ in range(tag_count):
|
||||
tag, pos = self._read_utf8(body, pos)
|
||||
test_tags.add(tag)
|
||||
else:
|
||||
test_tags = None
|
||||
if flags & FLAG_MIME_TYPE:
|
||||
mime_type, pos = self._read_utf8(body, pos)
|
||||
else:
|
||||
mime_type = None
|
||||
if flags & FLAG_FILE_CONTENT:
|
||||
file_name, pos = self._read_utf8(body, pos)
|
||||
content_length, consumed = self._parse_varint(body, pos)
|
||||
pos += consumed
|
||||
file_bytes = self._to_bytes(body, pos, content_length)
|
||||
if len(file_bytes) != content_length:
|
||||
raise ParseError('File content extends past end of packet: '
|
||||
'claimed %d bytes, %d available' % (
|
||||
content_length, len(file_bytes)))
|
||||
pos += content_length
|
||||
else:
|
||||
file_name = None
|
||||
file_bytes = None
|
||||
if flags & FLAG_ROUTE_CODE:
|
||||
route_code, pos = self._read_utf8(body, pos)
|
||||
else:
|
||||
route_code = None
|
||||
runnable = bool(flags & FLAG_RUNNABLE)
|
||||
eof = bool(flags & FLAG_EOF)
|
||||
test_status = self.status_lookup[flags & 0x0007]
|
||||
result.status(test_id=test_id, test_status=test_status,
|
||||
test_tags=test_tags, runnable=runnable, mime_type=mime_type,
|
||||
eof=eof, file_name=file_name, file_bytes=file_bytes,
|
||||
route_code=route_code, timestamp=timestamp)
|
||||
__call__ = run
|
||||
|
||||
def _read_utf8(self, buf, pos):
|
||||
length, consumed = self._parse_varint(buf, pos)
|
||||
pos += consumed
|
||||
utf8_bytes = buf[pos:pos+length]
|
||||
if length != len(utf8_bytes):
|
||||
raise ParseError(
|
||||
'UTF8 string at offset %d extends past end of packet: '
|
||||
'claimed %d bytes, %d available' % (pos - 2, length,
|
||||
len(utf8_bytes)))
|
||||
if has_nul(utf8_bytes):
|
||||
raise ParseError('UTF8 string at offset %d contains NUL byte' % (
|
||||
pos-2,))
|
||||
try:
|
||||
utf8, decoded_bytes = utf_8_decode(utf8_bytes)
|
||||
if decoded_bytes != length:
|
||||
raise ParseError("Invalid (partially decodable) string at "
|
||||
"offset %d, %d undecoded bytes" % (
|
||||
pos-2, length - decoded_bytes))
|
||||
return utf8, length+pos
|
||||
except UnicodeDecodeError:
|
||||
raise ParseError('UTF8 string at offset %d is not UTF8' % (pos-2,))
|
||||
|
||||
Reference in New Issue
Block a user