summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Treinish <mtreinish@kortar.org>2021-06-10 13:47:44 -0400
committerGitHub <noreply@github.com>2021-06-10 13:47:44 -0400
commit0dd0bec5fa4b5a6ff32b76224cfe46e2e86ac482 (patch)
tree22799c84bef57eeb6a565a27697ddfc17d35d343
parentbbaeb704a3e7f6a4cc78cecc44f12f6077b33fc7 (diff)
parent1dafb884e47f5fc26232672b01c2a9574577e7be (diff)
downloadsubunit-git-0dd0bec5fa4b5a6ff32b76224cfe46e2e86ac482.tar.gz
Merge branch 'master' into fix-filter-entrypoints
-rw-r--r--.travis.yml12
-rw-r--r--NEWS30
-rw-r--r--configure.ac2
-rw-r--r--python/subunit/__init__.py2
-rw-r--r--python/subunit/_output.py29
-rw-r--r--python/subunit/tests/test_output_filter.py63
-rw-r--r--python/subunit/tests/test_subunit_tags.py2
-rw-r--r--python/subunit/tests/test_test_protocol.py42
-rwxr-xr-xsetup.py3
9 files changed, 161 insertions, 24 deletions
diff --git a/.travis.yml b/.travis.yml
index b5e2d34..27593b3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
sudo: false
+dist: focal
addons:
apt:
packages:
@@ -7,18 +8,17 @@ addons:
language: python
python:
- "2.7"
- - "3.4"
- "3.5"
- "3.6"
- "3.7"
- "3.8"
- - pypy
- - pypy3.5
+ - "3.9"
+ - "pypy2.7-7.3.1"
+ - "pypy3.6-7.3.1"
matrix:
include:
-# Travis nightly look to be 3.5.0a4, b3 is out and the error we see
-# doesn't happen in trunk.
-# - python: "nightly"
+ - dist: xenial
+ python: pypy3.5-7.0.0
install:
- pip install -U pip
- pip install -U wheel setuptools
diff --git a/NEWS b/NEWS
index 393ce8d..73f660a 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,36 @@ subunit release notes
NEXT (In development)
---------------------
+1.4.0
+-----
+
+IMPROVEMENTS
+~~~~~~~~~~~~
+
+* Drop Python 3.3 support, and test on 3.5 and 3.6.
+ (Jelmer Vernooij)
+
+* Add support for Python 3.7 and 3.8.
+ (Matthew Treinish)
+
+* Improve readability of SubUnit v2 spec.
+ (Sergey Bronnikov)
+
+* Add license to setup.py. (Sergiu)
+
+BUGFIXES
+~~~~~~~~
+
+* Migrate Gtk interface to GObject introspection.
+ (Haikel Guemar)
+
+* Fix file open for python3. (Quique Llorente)
+
+* Check written bytes are not None before summing them to offset.
+ (Federico Ressi, #1845631)
+
+* Correctly handle py3 RawIOBase read(). (Stephen Finucane, partially #1813147)
+
1.3.0
-----
diff --git a/configure.ac b/configure.ac
index 7fa92ad..a06bd48 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
m4_define([SUBUNIT_MAJOR_VERSION], [1])
-m4_define([SUBUNIT_MINOR_VERSION], [3])
+m4_define([SUBUNIT_MINOR_VERSION], [4])
m4_define([SUBUNIT_MICRO_VERSION], [0])
m4_define([SUBUNIT_VERSION],
m4_defn([SUBUNIT_MAJOR_VERSION]).m4_defn([SUBUNIT_MINOR_VERSION]).m4_defn([SUBUNIT_MICRO_VERSION]))
diff --git a/python/subunit/__init__.py b/python/subunit/__init__.py
index 81b773c..17a970a 100644
--- a/python/subunit/__init__.py
+++ b/python/subunit/__init__.py
@@ -153,7 +153,7 @@ from subunit.v2 import ByteStreamToStreamResult, StreamResultToBytes
# If the releaselevel is 'final', then the tarball will be major.minor.micro.
# Otherwise it is major.minor.micro~$(revno).
-__version__ = (1, 3, 0, 'final', 0)
+__version__ = (1, 4, 0, 'final', 0)
PROGRESS_SET = 0
PROGRESS_CUR = 1
diff --git a/python/subunit/_output.py b/python/subunit/_output.py
index aa92646..c598c81 100644
--- a/python/subunit/_output.py
+++ b/python/subunit/_output.py
@@ -22,6 +22,8 @@ from optparse import (
)
import sys
+from dateutil import parser as date_parser
+
from subunit import make_stream_binary
from subunit.iso8601 import UTC
from subunit.v2 import StreamResultToBytes
@@ -121,6 +123,18 @@ def parse_arguments(args=None, ParserClass=OptionParser):
dest="tags",
default=[]
)
+ parser.add_option(
+ "--start-time",
+ help="Specify a time for the test to start",
+ dest="start_time",
+ default=None
+ )
+ parser.add_option(
+ "--stop-time",
+ help="Specify a time for the test to finish executing",
+ dest="stop_time",
+ default=None
+ )
(options, args) = parser.parse_args(args)
if options.mimetype and not options.attach_file:
@@ -152,6 +166,14 @@ def set_status_cb(option, opt_str, value, parser, status_name):
def generate_stream_results(args, output_writer):
+ if args.start_time:
+ start_time = date_parser.parse(args.start_time)
+ else:
+ start_time = None
+ if args.stop_time:
+ stop_time = date_parser.parse(args.stop_time)
+ else:
+ stop_time = None
output_writer.startTestRun()
if args.attach_file:
@@ -170,6 +192,7 @@ def generate_stream_results(args, output_writer):
write_status = partial(write_status, mime_type=args.mimetype)
if args.tags:
write_status = partial(write_status, test_tags=set(args.tags))
+ timestamp = start_time or create_timestamp()
write_status = partial(write_status, timestamp=create_timestamp())
if args.action not in _FINAL_ACTIONS:
write_status = partial(write_status, test_status=args.action)
@@ -192,7 +215,11 @@ def generate_stream_results(args, output_writer):
if is_last_packet:
if args.action in _FINAL_ACTIONS:
- write_status = partial(write_status, test_status=args.action)
+ if stop_time:
+ write_status = partial(write_status, test_status=args.action,
+ timestamp=stop_time)
+ else:
+ write_status = partial(write_status, test_status=args.action)
write_status()
diff --git a/python/subunit/tests/test_output_filter.py b/python/subunit/tests/test_output_filter.py
index 0f61ac5..587fd06 100644
--- a/python/subunit/tests/test_output_filter.py
+++ b/python/subunit/tests/test_output_filter.py
@@ -472,6 +472,69 @@ class StatusStreamResultTests(TestCase):
])
)
+class TimeStampTests(TestCase):
+ scenarios = [
+ (s, dict(status=s, option='--' + s)) for s in _FINAL_ACTIONS
+ ]
+
+ _dummy_timestamp = datetime.datetime(1914, 6, 28, 10, 45, 2, 0, UTC)
+
+ def setUp(self):
+ super(TimeStampTests, self).setUp()
+ self.patch(_o, 'create_timestamp', lambda: self._dummy_timestamp)
+ self.test_id = self.getUniqueString()
+
+ def test_no_timestamps(self):
+ result = get_result_for([self.option, self.test_id, f.name])
+ self.assertThat(
+ result._events,
+ MatchesListwise([
+ MatchesStatusCall(call='startTestRun'),
+ MatchesStatusCall(test_id=self.test_id, timestamp=self._dummy_timestamp),
+ MatchesStatusCall(test_id=self.test_id, timestamp=None),
+ MatchesStatusCall(call='stopTestRun'),
+ ]))
+
+ def test_only_start_timestamp(self):
+ timestamp = datetime.datetime.utcnow()
+ result = get_result_for([self.option, self.test_id, f.name,
+ '--start-time', timestamp.isoformat()])
+ self.assertThat(
+ result._events,
+ MatchesListwise([
+ MatchesStatusCall(call='startTestRun'),
+ MatchesStatusCall(test_id=self.test_id, timestamp=timestamp),
+ MatchesStatusCall(test_id=self.test_id, timestamp=None),
+ MatchesStatusCall(call='stopTestRun'),
+ ]))
+
+ def test_only_stop_timestamp(self):
+ timestamp = datetime.datetime.utcnow()
+ result = get_result_for([self.option, self.test_id, f.name,
+ '--stop-time', timestamp.isoformat()])
+ self.assertThat(
+ result._events,
+ MatchesListwise([
+ MatchesStatusCall(call='startTestRun'),
+ MatchesStatusCall(test_id=self.test_id, timestamp=self._dummy_timestamp),
+ MatchesStatusCall(test_id=self.test_id, timestamp=timestamp),
+ MatchesStatusCall(call='stopTestRun'),
+ ]))
+
+ def test_start_and_stop_timestamp(self):
+ timestamp_start = datetime.datetime.utcnow()
+ timestamp_stop = timestamp_start + datetime.timedelta(minutes=5)
+ result = get_result_for([self.option, self.test_id, f.name,
+ '--start-time', timestamp_start.isoformat(),
+ '--stop-time', timestamp_stop.isoformat()])
+ self.assertThat(
+ result._events,
+ MatchesListwise([
+ MatchesStatusCall(call='startTestRun'),
+ MatchesStatusCall(test_id=self.test_id, timestamp=timestamp_start),
+ MatchesStatusCall(test_id=self.test_id, timestamp=timestamp_stop),
+ MatchesStatusCall(call='stopTestRun'),
+ ]))
class FileDataTests(TestCase):
diff --git a/python/subunit/tests/test_subunit_tags.py b/python/subunit/tests/test_subunit_tags.py
index a16edc1..3722eb2 100644
--- a/python/subunit/tests/test_subunit_tags.py
+++ b/python/subunit/tests/test_subunit_tags.py
@@ -56,6 +56,8 @@ class TestSubUnitTags(testtools.TestCase):
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',
+ b'\xb3)\x82\x17\x04test\x02\x03foo\x04quux\xa6\xe1\xde\xec\xb3)'
+ b'\x83\x1b\x04test\x03\x04quux\x03foo\x03bar\xaf\xbd\x9d\xd6',
]
stream = subunit.StreamResultToBytes(self.original)
stream.status(
diff --git a/python/subunit/tests/test_test_protocol.py b/python/subunit/tests/test_test_protocol.py
index 7427b12..86aa4a6 100644
--- a/python/subunit/tests/test_test_protocol.py
+++ b/python/subunit/tests/test_test_protocol.py
@@ -16,10 +16,9 @@
import datetime
import io
-import unittest2 as unittest
import os
-import sys
import tempfile
+import unittest
from testtools import PlaceHolder, skipIf, TestCase, TestResult
from testtools.compat import _b, _u, BytesIO
@@ -38,6 +37,7 @@ except ImportError:
ExtendedTestResult,
)
from testtools.matchers import Contains
+from testtools.testcase import six
import subunit
from subunit.tests import (
@@ -48,7 +48,7 @@ from subunit.tests import (
import subunit.iso8601 as iso8601
-tb_prelude = "Traceback (most recent call last):\n"
+tb_prelude = "Traceback (most recent call last):\n"
def details_to_str(details):
@@ -60,7 +60,7 @@ class TestHelpers(TestCase):
fd, file_path = tempfile.mkstemp()
self.addCleanup(os.remove, file_path)
fake_file = os.fdopen(fd, 'r')
- if sys.version_info > (3, 0):
+ if six.PY3:
self.assertEqual(fake_file.buffer,
subunit._unwrap_text(fake_file))
else:
@@ -70,7 +70,7 @@ class TestHelpers(TestCase):
fd, file_path = tempfile.mkstemp()
self.addCleanup(os.remove, file_path)
fake_file = os.fdopen(fd, 'w')
- if sys.version_info > (3, 0):
+ if six.PY3:
self.assertEqual(fake_file.buffer,
subunit._unwrap_text(fake_file))
else:
@@ -152,13 +152,20 @@ class TestTestProtocolServerPipe(unittest.TestCase):
protocol.readFrom(pipe)
bing = subunit.RemotedTestCase("bing crosby")
an_error = subunit.RemotedTestCase("an error")
- self.assertEqual(
- client.errors,
- [(an_error, tb_prelude + _remote_exception_repr + '\n')])
- self.assertEqual(
- client.failures,
- [(bing, tb_prelude + _remote_exception_repr + ": "
- + details_to_str({'traceback': text_content(traceback)}) + "\n")])
+ if six.PY3:
+ self.assertEqual(client.errors,
+ [(an_error, _remote_exception_repr + '\n')])
+ self.assertEqual(
+ client.failures,
+ [(bing, _remote_exception_repr + ": "
+ + details_to_str({'traceback': text_content(traceback)}) + "\n")])
+ else:
+ self.assertEqual(client.errors,
+ [(an_error, '_StringException\n')])
+ self.assertEqual(
+ client.failures,
+ [(bing, "_StringException: "
+ + details_to_str({'traceback': text_content(traceback)}) + "\n")])
self.assertEqual(client.testsRun, 3)
def test_non_test_characters_forwarded_immediately(self):
@@ -1012,9 +1019,14 @@ class TestRemotedTestCase(unittest.TestCase):
"'A test description'>", "%r" % test)
result = unittest.TestResult()
test.run(result)
- self.assertEqual([(test, tb_prelude + _remote_exception_repr + ": "
- "Cannot run RemotedTestCases.\n\n")],
- result.errors)
+ if six.PY3:
+ self.assertEqual([(test, _remote_exception_repr + ': ' +
+ "Cannot run RemotedTestCases.\n\n")],
+ result.errors)
+ else:
+ self.assertEqual([(test, "_StringException: " +
+ "Cannot run RemotedTestCases.\n\n")],
+ result.errors)
self.assertEqual(1, result.testsRun)
another_test = subunit.RemotedTestCase("A test description")
self.assertEqual(test, another_test)
diff --git a/setup.py b/setup.py
index 4fffb6d..81aef9e 100755
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,6 @@
#!/usr/bin/env python
import os.path
+
from setuptools import setup
@@ -38,6 +39,7 @@ setup(
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
'Topic :: Software Development :: Testing',
],
keywords='python test streaming',
@@ -84,4 +86,5 @@ setup(
'test': ['fixtures', 'testscenarios'],
'test:python_version!="3.2"': ['hypothesis'],
}
+ python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
)