diff --git a/cafe/common/reporting/reporter.py b/cafe/common/reporting/reporter.py index b7b7207..abe00a4 100644 --- a/cafe/common/reporting/reporter.py +++ b/cafe/common/reporting/reporter.py @@ -13,6 +13,7 @@ from cafe.common.reporting.json_report import JSONReport from cafe.common.reporting.xml_report import XMLReport +from cafe.common.reporting.subunit_report import SubunitReport class Reporter: @@ -29,6 +30,8 @@ class Reporter: report = JSONReport() elif result_type == 'xml': report = XMLReport() + elif result_type == 'subunit': + report = SubunitReport() report.generate_report( result_parser=self.result_parser, all_results=self.all_results, diff --git a/cafe/common/reporting/subunit_report.py b/cafe/common/reporting/subunit_report.py new file mode 100644 index 0000000..868d806 --- /dev/null +++ b/cafe/common/reporting/subunit_report.py @@ -0,0 +1,20 @@ +# Copyright 2015 Rackspace +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from cafe.common.reporting.base_report import BaseReport + + +class SubunitReport(BaseReport): + def generate_report(self, result_parser, all_results=None, path=None): + raise NotImplementedError( + "SubunitReport is not available without the subunit plugin!") diff --git a/cafe/drivers/unittest/arguments.py b/cafe/drivers/unittest/arguments.py index 6ed93ea..61fd5ac 100644 --- a/cafe/drivers/unittest/arguments.py +++ b/cafe/drivers/unittest/arguments.py @@ -260,7 +260,7 @@ class ArgumentParser(argparse.ArgumentParser): self.add_argument( "--result", "-R", - choices=["json", "xml"], + choices=["json", "xml", "subunit"], help="Generates a specified formatted result file") self.add_argument( diff --git a/cafe/drivers/unittest/runner.py b/cafe/drivers/unittest/runner.py index fded772..58ee292 100755 --- a/cafe/drivers/unittest/runner.py +++ b/cafe/drivers/unittest/runner.py @@ -606,7 +606,7 @@ class _UnittestRunnerCLI(object): argparser.add_argument( "--result", - choices=["json", "xml"], + choices=["json", "xml", "subunit"], help="generates a specified formatted result file") argparser.add_argument( diff --git a/cafe/plugins/subunit/cafe/__init__.py b/cafe/plugins/subunit/cafe/__init__.py new file mode 100644 index 0000000..d3e89f9 --- /dev/null +++ b/cafe/plugins/subunit/cafe/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2015 Rackspace +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +__import__('pkg_resources').declare_namespace(__name__) diff --git a/cafe/plugins/subunit/cafe/common/__init__.py b/cafe/plugins/subunit/cafe/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cafe/plugins/subunit/cafe/common/reporting/__init__.py b/cafe/plugins/subunit/cafe/common/reporting/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cafe/plugins/subunit/cafe/common/reporting/subunit_report.py b/cafe/plugins/subunit/cafe/common/reporting/subunit_report.py new file mode 100644 index 0000000..c9d2dd1 --- /dev/null +++ b/cafe/plugins/subunit/cafe/common/reporting/subunit_report.py @@ -0,0 +1,68 @@ +# Copyright 2015 Rackspace +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from datetime import datetime +import os +import sys +import uuid + +import pytz +import subunit + +from cafe.common.reporting.base_report import BaseReport + + +class SubunitReport(BaseReport): + def generate_report(self, result_parser, all_results=None, path=None): + """ Generates a Subunit report in the specified directory. """ + + result_path = path or os.getcwd() + if os.path.isdir(result_path): + result_path += "/subunit_results" + + with open(result_path, 'wb') as result_file: + output = subunit.v2.StreamResultToBytes(result_file) + output.startTestRun() + + # Convert Result objects to dicts for processing + for result in all_results: + test_result = result.__dict__ + if test_result.get('failure_trace') is not None: + test_result['result'] = "fail" + elif test_result.get('skipped_msg') is not None: + test_result['result'] = "skip" + elif test_result.get('error_trace') is not None: + test_result['result'] = "fail" + else: + test_result['result'] = "success" + if test_result['test_method_name'] == "setUpClass": + # This case is to match the tempest format + test_id = "{0} ({1})".format( + test_result['test_method_name'], + test_result['test_class_name']) + else: + test_id = "{0}.{1}".format( + test_result['test_class_name'], + test_result['test_method_name']) + kwargs = { + "timestamp": datetime.now(pytz.UTC), + "test_id": unicode(test_id)} + output.status(**kwargs) + kwargs["test_status"] = test_result['result'] + kwargs["file_bytes"] = bytes(test_result.get( + 'failure_trace') or test_result.get('error_trace') or "0") + kwargs["file_name"] = "stdout" + kwargs["mime_type"] = unicode("text/plain;charset=utf8") + output.status(**kwargs) + + output.stopTestRun() diff --git a/cafe/plugins/subunit/setup.py b/cafe/plugins/subunit/setup.py new file mode 100644 index 0000000..7b44813 --- /dev/null +++ b/cafe/plugins/subunit/setup.py @@ -0,0 +1,26 @@ +# Copyright 2015 Rackspace +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from setuptools import setup, find_packages + +setup( + name='cafe_subunit_plugin', + version='0.0.1', + description='Subunit Plugin for CAFE', + author='Rackspace Cloud QE', + author_email='cloud-cafe@lists.rackspace.com', + url='http://rackspace.com', + packages=find_packages(), + namespace_packages=['cafe'], + install_requires=['python-subunit', 'pytz'], + zip_safe=False)