3 # Copyright (c) 2016 Cable Television Laboratories, Inc. and others.
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 """Define the parent class to run unittest.TestSuite as TestCase."""
12 from __future__ import division
13 from io import BytesIO
21 from subunit.run import SubunitTestRunner
23 from xtesting.core import testcase
25 __author__ = ("Steven Pisarski <s.pisarski@cablelabs.com>, "
26 "Cedric Ollivier <cedric.ollivier@orange.com>")
29 class Suite(testcase.TestCase):
30 """Base model for running unittest.TestSuite."""
32 __logger = logging.getLogger(__name__)
34 def __init__(self, **kwargs):
35 super(Suite, self).__init__(**kwargs)
39 def generate_stats(cls, stream):
40 """Generate stats from subunit stream
46 stats = subprocess.Popen(
47 ['subunit-stats'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
48 output, _ = stats.communicate(stream.read())
49 cls.__logger.info("\n\n%s", output.decode("utf-8"))
51 def generate_xunit(self, stream):
52 """Generate junit report from subunit stream
58 with open("{}/results.xml".format(self.res_dir), "w") as xml:
59 stats = subprocess.Popen(
60 ['subunit2junitxml'], stdin=subprocess.PIPE,
61 stdout=subprocess.PIPE)
62 output, _ = stats.communicate(stream.read())
63 xml.write(output.decode("utf-8"))
65 def generate_html(self, stream):
66 """Generate html report from subunit stream
71 cmd = ['subunit2html', stream, '{}/results.html'.format(self.res_dir)]
72 output = subprocess.check_output(cmd)
73 self.__logger.debug("\n%s\n\n%s", ' '.join(cmd), output)
75 def run(self, **kwargs):
76 """Run the test suite.
78 It allows running any unittest.TestSuite and getting its
81 By default, it runs the suite defined as instance attribute.
82 It can be overriden by passing name as arg. It must
83 conform with TestLoader.loadTestsFromName().
85 It sets the following attributes required to push the results
94 kwargs: Arbitrary keyword arguments.
97 TestCase.EX_OK if any TestSuite has been run
98 TestCase.EX_RUN_ERROR otherwise.
101 name = kwargs["name"]
103 self.suite = unittest.TestLoader().loadTestsFromName(name)
105 self.__logger.error("Can not import %s", name)
106 return testcase.TestCase.EX_RUN_ERROR
111 self.start_time = time.time()
112 if not os.path.isdir(self.res_dir):
113 os.makedirs(self.res_dir)
115 result = SubunitTestRunner(
116 stream=stream, verbosity=2).run(self.suite).decorated
117 self.generate_stats(stream)
118 self.generate_xunit(stream)
119 with open('{}/subunit_stream'.format(self.res_dir), 'wb') as subfd:
121 shutil.copyfileobj(stream, subfd)
122 self.generate_html('{}/subunit_stream'.format(self.res_dir))
123 self.stop_time = time.time()
125 "testsRun": result.testsRun,
126 "failures": len(result.failures),
127 "errors": len(result.errors)}
128 self.result = 100 * (
129 (result.testsRun - (len(result.failures) +
130 len(result.errors))) /
132 return testcase.TestCase.EX_OK
133 except AssertionError:
134 self.__logger.error("No suite is defined")
135 return testcase.TestCase.EX_RUN_ERROR
136 except ZeroDivisionError:
137 self.__logger.error("No test has been run")
138 return testcase.TestCase.EX_RUN_ERROR
139 except Exception: # pylint: disable=broad-except
140 self.__logger.exception("something wrong occurs")
141 return testcase.TestCase.EX_RUN_ERROR