test_output.py 18.5 KB
Newer Older
1 2
import json
import tempfile
3
import os
4
import re
5
import sys
6
import shutil
7
from xml.dom import minidom
8

9 10
import pkg_resources

11 12 13 14 15
if sys.version_info[:2] == (2, 6):
    import unittest2 as unittest
else:
    import unittest

16
from avocado.core import exit_codes
17
from avocado.core.output import TermSupport
18
from avocado.utils import process
19
from avocado.utils import script
20

21 22 23 24
basedir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..')
basedir = os.path.abspath(basedir)


25 26 27 28 29 30 31 32 33 34 35 36 37 38
PERL_TAP_PARSER_SNIPPET = """#!/bin/env perl
use TAP::Parser;

my $parser = TAP::Parser->new( { exec => ['./scripts/avocado', 'run', 'passtest.py', 'errortest.py', 'warntest.py', '--tap', '-'] } );

while ( my $result = $parser->next ) {
        $result->is_unknown && die "Unknown line \\"" . $result->as_string . "\\" in the TAP output!\n";
}
$parser->parse_errors == 0 || die "Parser errors!\n";
$parser->is_good_plan || die "Plan is not a good plan!\n";
$parser->plan eq '1..3' || die "Plan does not match what was expected!\n";
"""


39 40 41 42 43 44 45 46
def image_output_uncapable():
    try:
        import PIL
        return False
    except ImportError:
        return True


47 48 49 50 51 52 53 54
def html_uncapable():
    try:
        pkg_resources.require('avocado_result_html')
        return False
    except pkg_resources.DistributionNotFound:
        return True


55 56 57 58
def perl_tap_parser_uncapable():
    return os.system("perl -e 'use TAP::Parser;'") != 0


59 60
class OutputTest(unittest.TestCase):

61
    def setUp(self):
62
        self.tmpdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
63

64 65
    def test_output_doublefree(self):
        os.chdir(basedir)
66 67
        cmd_line = ('./scripts/avocado run --job-results-dir %s --sysinfo=off '
                    'doublefree.py' % self.tmpdir)
68
        result = process.run(cmd_line, ignore_status=True)
69
        expected_rc = exit_codes.AVOCADO_ALL_OK
70 71 72 73 74 75 76 77 78
        output = result.stdout + result.stderr
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
        bad_string = 'double free or corruption'
        self.assertNotIn(bad_string, output,
                         "Libc double free can be seen in avocado "
                         "doublefree output:\n%s" % output)

79 80 81
    def tearDown(self):
        shutil.rmtree(self.tmpdir)

82

83 84
class OutputPluginTest(unittest.TestCase):

85
    def setUp(self):
86
        self.tmpdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
87

88 89 90 91 92 93 94 95
    def check_output_files(self, debug_log):
        base_dir = os.path.dirname(debug_log)
        json_output = os.path.join(base_dir, 'results.json')
        self.assertTrue(os.path.isfile(json_output))
        with open(json_output, 'r') as fp:
            json.load(fp)
        xunit_output = os.path.join(base_dir, 'results.xml')
        self.assertTrue(os.path.isfile(json_output))
96 97 98 99 100
        try:
            minidom.parse(xunit_output)
        except Exception, details:
            raise AssertionError("Unable to parse xunit output: %s\n\n%s"
                                 % (details, open(xunit_output).read()))
101

102 103
    def test_output_incompatible_setup(self):
        os.chdir(basedir)
104 105
        cmd_line = ('./scripts/avocado run --job-results-dir %s --sysinfo=off '
                    '--xunit - --json - passtest.py' % self.tmpdir)
106
        result = process.run(cmd_line, ignore_status=True)
107
        expected_rc = exit_codes.AVOCADO_FAIL
108 109 110
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
111 112 113 114 115 116 117
        error_regex = re.compile(r'avocado run: error: argument ((--json)|'
                                 '(--xunit)): Options ((--xunit --json)|'
                                 '(--json --xunit)) are trying to use stdout '
                                 'simultaneously\n')
        self.assertIsNotNone(error_regex.match(result.stderr),
                             "Missing error message from output:\n%s" %
                             result.stderr)
118

119 120
    @unittest.skipIf(html_uncapable(),
                     "Uncapable of Avocado Result HTML plugin")
121
    def test_output_incompatible_setup_2(self):
122
        os.chdir(basedir)
123 124
        cmd_line = ('./scripts/avocado run --job-results-dir %s --sysinfo=off '
                    '--html - passtest.py' % self.tmpdir)
125
        result = process.run(cmd_line, ignore_status=True)
126
        expected_rc = exit_codes.AVOCADO_JOB_FAIL
127 128 129 130 131 132 133 134
        output = result.stdout + result.stderr
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
        error_excerpt = "HTML to stdout not supported"
        self.assertIn(error_excerpt, output,
                      "Missing excerpt error message from output:\n%s" % output)

135 136 137
    def test_output_compatible_setup(self):
        tmpfile = tempfile.mktemp()
        os.chdir(basedir)
138 139
        cmd_line = ('./scripts/avocado run --job-results-dir %s --sysinfo=off '
                    '--journal --xunit %s --json - passtest.py' %
140
                    (self.tmpdir, tmpfile))
141 142
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
143
        expected_rc = exit_codes.AVOCADO_ALL_OK
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
        try:
            self.assertEqual(result.exit_status, expected_rc,
                             "Avocado did not return rc %d:\n%s" %
                             (expected_rc, result))
            # Check if we are producing valid outputs
            json.loads(output)
            minidom.parse(tmpfile)
        finally:
            try:
                os.remove(tmpfile)
            except OSError:
                pass

    def test_output_compatible_setup_2(self):
        tmpfile = tempfile.mktemp()
        os.chdir(basedir)
160 161
        cmd_line = ('./scripts/avocado run --job-results-dir %s --sysinfo=off '
                    '--xunit - --json %s passtest.py' %
162
                    (self.tmpdir, tmpfile))
163 164
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
165
        expected_rc = exit_codes.AVOCADO_ALL_OK
166 167 168 169 170 171
        try:
            self.assertEqual(result.exit_status, expected_rc,
                             "Avocado did not return rc %d:\n%s" %
                             (expected_rc, result))
            # Check if we are producing valid outputs
            with open(tmpfile, 'r') as fp:
172 173 174
                json_results = json.load(fp)
                debug_log = json_results['debuglog']
                self.check_output_files(debug_log)
175 176 177 178 179 180 181
            minidom.parseString(output)
        finally:
            try:
                os.remove(tmpfile)
            except OSError:
                pass

182 183
    @unittest.skipIf(html_uncapable(),
                     "Uncapable of Avocado Result HTML plugin")
184
    def test_output_compatible_setup_3(self):
185 186 187
        tmpfile = tempfile.mktemp(prefix='avocado_' + __name__)
        tmpfile2 = tempfile.mktemp(prefix='avocado_' + __name__)
        tmpdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
188
        tmpfile3 = tempfile.mktemp(dir=tmpdir)
189
        os.chdir(basedir)
190 191
        cmd_line = ('./scripts/avocado run --job-results-dir %s --sysinfo=off '
                    '--xunit %s --json %s --html %s passtest.py' %
192
                    (self.tmpdir, tmpfile, tmpfile2, tmpfile3))
193 194
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
195
        expected_rc = exit_codes.AVOCADO_ALL_OK
196 197 198
        tmpdir_contents = os.listdir(tmpdir)
        self.assertEqual(len(tmpdir_contents), 5,
                         'Not all resources dir were created: %s' % tmpdir_contents)
199 200 201 202
        try:
            self.assertEqual(result.exit_status, expected_rc,
                             "Avocado did not return rc %d:\n%s" %
                             (expected_rc, result))
203
            self.assertNotEqual(output, "", "Output is empty")
204
            # Check if we are producing valid outputs
205 206 207 208 209 210 211 212 213
            with open(tmpfile2, 'r') as fp:
                json_results = json.load(fp)
                debug_log = json_results['debuglog']
                self.check_output_files(debug_log)
            minidom.parse(tmpfile)
        finally:
            try:
                os.remove(tmpfile)
                os.remove(tmpfile2)
214
                shutil.rmtree(tmpdir)
215 216 217 218 219 220 221
            except OSError:
                pass

    def test_output_compatible_setup_nooutput(self):
        tmpfile = tempfile.mktemp()
        tmpfile2 = tempfile.mktemp()
        os.chdir(basedir)
222
        # Verify --silent can be supplied as app argument
223 224
        cmd_line = ('./scripts/avocado --silent run --job-results-dir %s '
                    '--sysinfo=off --xunit %s --json %s passtest.py' %
225
                    (self.tmpdir, tmpfile, tmpfile2))
226 227
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
228
        expected_rc = exit_codes.AVOCADO_ALL_OK
229 230 231 232 233 234
        try:
            self.assertEqual(result.exit_status, expected_rc,
                             "Avocado did not return rc %d:\n%s" %
                             (expected_rc, result))
            self.assertEqual(output, "", "Output is not empty:\n%s" % output)
            # Check if we are producing valid outputs
235
            with open(tmpfile2, 'r') as fp:
236 237 238
                json_results = json.load(fp)
                debug_log = json_results['debuglog']
                self.check_output_files(debug_log)
239 240 241 242 243 244 245 246
            minidom.parse(tmpfile)
        finally:
            try:
                os.remove(tmpfile)
                os.remove(tmpfile2)
            except OSError:
                pass

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
    def test_nonprintable_chars(self):
        cmd_line = ("./scripts/avocado run '/bin/ls "
                    "NON_EXISTING_FILE_WITH_NONPRINTABLE_CHARS_IN_HERE\x1b' "
                    "--job-results-dir %s --sysinfo=off" % self.tmpdir)
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
        expected_rc = exit_codes.AVOCADO_TESTS_FAIL
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
        debug_log = None
        for line in output.splitlines():
            if "JOB LOG" in line:
                debug_log = line.split(':', 1)[-1].strip()
                break
        self.assertTrue(debug_log, "Unable to get JOB LOG from output:\n%s"
                        % output)
        self.check_output_files(debug_log)

266 267
    def test_show_job_log(self):
        os.chdir(basedir)
268 269
        cmd_line = ('./scripts/avocado run --job-results-dir %s --sysinfo=off '
                    'passtest.py --show-job-log' % self.tmpdir)
270
        result = process.run(cmd_line, ignore_status=True)
271
        expected_rc = exit_codes.AVOCADO_ALL_OK
272 273 274
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
275
        job_id_list = re.findall('Job ID: (.*)', result.stdout,
276
                                 re.MULTILINE)
277 278
        self.assertTrue(job_id_list, 'No Job ID in stdout:\n%s' %
                        result.stdout)
279 280
        job_id = job_id_list[0]
        self.assertEqual(len(job_id), 40)
281 282 283

    def test_silent_trumps_show_job_log(self):
        os.chdir(basedir)
284
        # Also verify --silent can be supplied as run option
285 286
        cmd_line = ('./scripts/avocado run --silent --job-results-dir %s '
                    '--sysinfo=off passtest.py --show-job-log' %
287
                    self.tmpdir)
288 289
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
290
        expected_rc = exit_codes.AVOCADO_ALL_OK
291 292 293 294 295
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
        self.assertEqual(output, "")

296 297
    def test_default_enabled_plugins(self):
        os.chdir(basedir)
298 299
        cmd_line = ('./scripts/avocado run --job-results-dir %s --sysinfo=off '
                    'passtest.py' % self.tmpdir)
300 301
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
302
        expected_rc = exit_codes.AVOCADO_ALL_OK
303 304 305 306
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
        output_lines = output.splitlines()
C
Cleber Rosa 已提交
307 308 309 310 311
        # The current human output produces 6 lines when running a single test,
        # with an optional 7th line when the HTML report generation is enabled
        self.assertGreaterEqual(len(output_lines), 6,
                                ('Basic human interface did not produce the '
                                 'expect output. Output produced: "%s"' % output))
312 313
        second_line = output_lines[1]
        debug_log = second_line.split()[-1]
314 315
        self.check_output_files(debug_log)

316 317 318 319
    def test_verify_whiteboard_save(self):
        tmpfile = tempfile.mktemp()
        try:
            os.chdir(basedir)
320 321
            cmd_line = ('./scripts/avocado run --job-results-dir %s '
                        '--sysinfo=off whiteboard.py --json %s' %
322
                        (self.tmpdir, tmpfile))
323
            result = process.run(cmd_line, ignore_status=True)
324
            expected_rc = exit_codes.AVOCADO_ALL_OK
325 326 327 328 329
            self.assertEqual(result.exit_status, expected_rc,
                             "Avocado did not return rc %d:\n%s" %
                             (expected_rc, result))
            with open(tmpfile, 'r') as fp:
                json_results = json.load(fp)
330 331 332
                logfile = json_results['tests'][0]['logfile']
                debug_dir = os.path.dirname(logfile)
                whiteboard_path = os.path.join(debug_dir, 'whiteboard')
333 334 335 336 337 338 339
                self.assertTrue(os.path.exists(whiteboard_path),
                                'Missing whiteboard file %s' % whiteboard_path)
        finally:
            try:
                os.remove(tmpfile)
            except OSError:
                pass
340

341
    @unittest.skipIf(image_output_uncapable(),
342
                     "Uncapable of generating images with PIL library")
343 344 345 346 347
    def test_gendata(self):
        tmpfile = tempfile.mktemp()
        try:
            os.chdir(basedir)
            cmd_line = ("./scripts/avocado run --job-results-dir %s "
348
                        "--sysinfo=off gendata.py --json %s" %
349 350
                        (self.tmpdir, tmpfile))
            result = process.run(cmd_line, ignore_status=True)
351
            expected_rc = exit_codes.AVOCADO_ALL_OK
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
            self.assertEqual(result.exit_status, expected_rc,
                             "Avocado did not return rc %d:\n%s" %
                             (expected_rc, result))
            with open(tmpfile, 'r') as fp:
                json_results = json.load(fp)
                bsod_dir = None
                json_dir = None
                for test in json_results['tests']:
                    if "test_bsod" in test['url']:
                        bsod_dir = test['logfile']
                    elif "test_json" in test['url']:
                        json_dir = test['logfile']
                self.assertTrue(bsod_dir, "Failed to get test_bsod output "
                                "directory")
                self.assertTrue(json_dir, "Failed to get test_json output "
                                "directory")
                bsod_dir = os.path.join(os.path.dirname(bsod_dir), "data",
                                        "bsod.png")
                json_dir = os.path.join(os.path.dirname(json_dir), "data",
                                        "test.json")
                self.assertTrue(os.path.exists(bsod_dir), "File %s produced by"
                                "test does not exist" % bsod_dir)
                self.assertTrue(os.path.exists(json_dir), "File %s produced by"
                                "test does not exist" % json_dir)
        finally:
            try:
                os.remove(tmpfile)
            except OSError:
                pass

382 383 384 385
    def test_redirect_output(self):
        redirected_output_path = tempfile.mktemp()
        try:
            os.chdir(basedir)
386 387
            cmd_line = ('./scripts/avocado run --job-results-dir %s '
                        '--sysinfo=off passtest.py > %s' %
388
                        (self.tmpdir, redirected_output_path))
389 390
            result = process.run(cmd_line, ignore_status=True, shell=True)
            output = result.stdout + result.stderr
391
            expected_rc = exit_codes.AVOCADO_ALL_OK
392 393 394
            self.assertEqual(result.exit_status, expected_rc,
                             "Avocado did not return rc %d:\n%s" %
                             (expected_rc, result))
395 396
            self.assertEqual(output, '',
                             'After redirecting to file, output is not empty: %s' % output)
397 398 399
            with open(redirected_output_path, 'r') as redirected_output_file_obj:
                redirected_output = redirected_output_file_obj.read()
                for code in TermSupport.ESCAPE_CODES:
R
Rudá Moura 已提交
400
                    self.assertNotIn(code, redirected_output,
401 402 403 404 405 406 407 408
                                     'Found terminal support code %s in redirected output\n%s' %
                                     (code, redirected_output))
        finally:
            try:
                os.remove(redirected_output_path)
            except OSError:
                pass

409 410 411 412 413 414 415 416 417
    @unittest.skipIf(perl_tap_parser_uncapable(),
                     "Uncapable of using Perl TAP::Parser library")
    def test_tap_parser(self):
        perl_script = script.TemporaryScript("tap_parser.pl",
                                             PERL_TAP_PARSER_SNIPPET)
        perl_script.save()
        os.chdir(basedir)
        process.run("perl %s" % perl_script)

418 419 420
    def test_broken_pipe(self):
        os.chdir(basedir)
        cmd_line = "(./scripts/avocado run --help | whacky-unknown-command)"
421 422
        result = process.run(cmd_line, shell=True, ignore_status=True,
                             env={"LC_ALL": "C"})
423 424 425 426 427 428 429 430 431
        expected_rc = 127
        self.assertEqual(result.exit_status, expected_rc,
                         ("avocado run to broken pipe did not return "
                          "rc %d:\n%s" % (expected_rc, result)))
        self.assertEqual(len(result.stderr.splitlines()), 1)
        self.assertIn("whacky-unknown-command", result.stderr)
        self.assertIn("not found", result.stderr)
        self.assertNotIn("Avocado crashed", result.stderr)

432 433 434
    def tearDown(self):
        shutil.rmtree(self.tmpdir)

435

436 437
if __name__ == '__main__':
    unittest.main()