Add an extension capability for adding targets

This is meant to facilitate anything else that wants to look at the
subunit stream and do something with it.

The unit test for main is simply intended to ensure that found targets
are passed in to the reader. The unit test for ReadSubunit verifies that
any targets passed in are in fact copied into the final result
processor.

Change-Id: I5200521a3c33d61ea4e4af8cfa900eff2c698355
Implements: counter-inspection
This commit is contained in:
Clint Byrum 2015-11-06 00:02:39 -08:00
parent 8af81fea4a
commit 35b10a9de7
5 changed files with 69 additions and 10 deletions

View File

@ -0,0 +1,18 @@
=================
Target Extensions
=================
Subunit2SQL is meant to be generic. But while processing subunit streams,
you may have some site-specific behavior you'd like to enable, such as
loading attachments into a storage location that can be extracted later.
To do this, you must create a plugin in the `subunit2sql.target` entry
point namespace, and it will be invoked along with the other targets
that subunit2sql invokes normally to store tests in a SQL database.
The plugin's entry point should be a class that extends
`testtools.StreamResult`. It should also add a class method, `enabled`,
which returns False if the plugin has no chance of functioning. The
`enabled` method can also do any configuration file preparation if you
are using oslo.config. The constructor will not be executed until after
all config options are loaded.

View File

@ -33,14 +33,18 @@ def get_duration(start, end):
class ReadSubunit(object):
def __init__(self, stream_file, attachments=False, attr_regex=None):
def __init__(self, stream_file, attachments=False, attr_regex=None,
targets=None):
if targets is None:
targets = []
self.stream_file = stream_file
self.stream = subunit.ByteStreamToStreamResult(stream_file)
starts = testtools.StreamResult()
summary = testtools.StreamSummary()
outcomes = testtools.StreamToDict(functools.partial(
self.parse_outcome))
self.result = testtools.CopyStreamResult([starts, outcomes, summary])
targets.extend([starts, outcomes, summary])
self.result = testtools.CopyStreamResult(targets)
self.results = {}
self.attachments = attachments
if attr_regex:

View File

@ -19,6 +19,7 @@ import sys
from oslo_config import cfg
from oslo_db import options
from pbr import version
from stevedore import enabled
from subunit2sql.db import api
from subunit2sql import exceptions
@ -182,19 +183,30 @@ def process_results(results):
def main():
cli_opts()
def check_enabled(ext):
return ext.plugin.enabled()
extensions = enabled.EnabledExtensionManager('subunit2sql.target',
check_func=check_enabled)
parse_args(sys.argv)
try:
targets = list(extensions.map(lambda ext: ext.plugin()))
except RuntimeError:
targets = []
if CONF.subunit_files:
if len(CONF.subunit_files) > 1 and CONF.run_id:
print("You can not specify a run id for adding more than 1 stream")
return 3
streams = [subunit.ReadSubunit(open(s, 'r'),
attachments=CONF.store_attachments,
attr_regex=CONF.attr_regex)
attr_regex=CONF.attr_regex,
targets=targets)
for s in CONF.subunit_files]
else:
streams = [subunit.ReadSubunit(sys.stdin,
attachments=CONF.store_attachments,
attr_regex=CONF.attr_regex)]
attr_regex=CONF.attr_regex,
targets=targets)]
for stream in streams:
process_results(stream.get_results())

View File

@ -170,3 +170,8 @@ class TestReadSubunit(base.TestCase):
test_name = read.cleanup_test_name(fake_id, strip_tags=True,
strip_scenarios=False)
self.assertEqual(fake_id, test_name)
@mock.patch('testtools.CopyStreamResult')
def test_targets_added_to_result(self, ttc_mock):
subunit.ReadSubunit(mock.MagicMock(), targets=['foo'])
self.assertIn('foo', ttc_mock.call_args[0][0])

View File

@ -156,7 +156,8 @@ class TestMain(base.TestCase):
shell.main()
read_subunit_mock.assert_called_once_with(sys.stdin,
attachments=False,
attr_regex='\[(.*)\]')
attr_regex='\[(.*)\]',
targets=[])
process_results_mock.assert_called_once_with(fake_get_results)
@mock.patch('subunit2sql.read_subunit.ReadSubunit')
@ -165,8 +166,8 @@ class TestMain(base.TestCase):
read_subunit_mock):
tfile1 = tempfile.NamedTemporaryFile()
tfile2 = tempfile.NamedTemporaryFile()
tfile1.write('test me later 1')
tfile2.write('test me later 2')
tfile1.write(b'test me later 1')
tfile2.write(b'test me later 2')
tfile1.flush()
tfile2.flush()
self.fake_args.extend([tfile1.name, tfile2.name])
@ -180,17 +181,36 @@ class TestMain(base.TestCase):
shell.main()
read_subunit_mock.assert_called_with(mock.ANY,
attachments=False,
attr_regex='\[(.*)\]')
attr_regex='\[(.*)\]',
targets=[])
self.assertEqual(2, len(read_subunit_mock.call_args_list))
file_1 = read_subunit_mock.call_args_list[0][0][0]
self.assertIsInstance(file_1, file)
file_1.seek(0)
self.assertEqual('test me later 1', file_1.read())
file_2 = read_subunit_mock.call_args_list[1][0][0]
self.assertIsInstance(file_2, file)
file_2.seek(0)
self.assertEqual('test me later 2', file_2.read())
self.assertEqual(fake_get_results_1,
process_results_mock.call_args_list[0][0][0])
self.assertEqual(fake_get_results_2,
process_results_mock.call_args_list[1][0][0])
@mock.patch('stevedore.enabled.EnabledExtensionManager')
@mock.patch('subunit2sql.read_subunit.ReadSubunit')
@mock.patch('subunit2sql.shell.process_results')
def test_main_with_targets(self, process_results_mock, read_subunit_mock,
ext_mock):
exts = mock.MagicMock('EnabledExtensionManager()')
ext_mock.return_value = exts
exts.map = mock.MagicMock('extensions.map')
exts.map.return_value = [mock.sentinel.extension]
fake_read_subunit = mock.MagicMock('ReadSubunit')
fake_get_results = 'fake results'
fake_read_subunit.get_results = mock.MagicMock('get_results')
fake_read_subunit.get_results.return_value = fake_get_results
read_subunit_mock.return_value = fake_read_subunit
shell.main()
read_subunit_mock.assert_called_once_with(
sys.stdin, attachments=False, attr_regex='\[(.*)\]',
targets=[mock.sentinel.extension])
process_results_mock.assert_called_once_with(fake_get_results)