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

9 10
import pkg_resources

11
from avocado.core import exit_codes
12
from avocado.core.output import TermSupport
13
from avocado.utils import process
14
from avocado.utils import script
15
from avocado.utils import path as utils_path
16

17 18 19
basedir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..')
basedir = os.path.abspath(basedir)

20 21
AVOCADO = os.environ.get("UNITTEST_AVOCADO_CMD", "./scripts/avocado")

22

23 24 25
PERL_TAP_PARSER_SNIPPET = """#!/bin/env perl
use TAP::Parser;

26
my $parser = TAP::Parser->new( { exec => ['%s', 'run', 'passtest.py', 'errortest.py', 'warntest.py', '--tap', '-', '--sysinfo', 'off', '--job-results-dir', '%%s'] } );
27 28 29 30 31 32 33

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";
34
""" % AVOCADO
35 36


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


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


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


57 58 59 60 61 62 63 64
def missing_binary(binary):
    try:
        utils_path.find_command(binary)
        return False
    except utils_path.CmdNotFoundError:
        return True


65 66
class OutputTest(unittest.TestCase):

67
    def setUp(self):
68
        self.tmpdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
69

70 71
    @unittest.skipIf(missing_binary('cc'),
                     "C compiler is required by the underlying doublefree.py test")
72 73
    def test_output_doublefree(self):
        os.chdir(basedir)
74 75
        cmd_line = ('%s run --job-results-dir %s --sysinfo=off '
                    'doublefree.py' % (AVOCADO, self.tmpdir))
76
        result = process.run(cmd_line, ignore_status=True)
77
        expected_rc = exit_codes.AVOCADO_ALL_OK
78 79 80 81 82 83 84 85 86
        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)

87 88 89
    def tearDown(self):
        shutil.rmtree(self.tmpdir)

90

91 92
class OutputPluginTest(unittest.TestCase):

93
    def setUp(self):
94
        self.tmpdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
95

96 97 98 99 100 101 102 103
    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))
104 105
        try:
            minidom.parse(xunit_output)
C
Cleber Rosa 已提交
106
        except Exception as details:
107 108
            raise AssertionError("Unable to parse xunit output: %s\n\n%s"
                                 % (details, open(xunit_output).read()))
109 110 111 112 113
        tap_output = os.path.join(base_dir, "results.tap")
        self.assertTrue(os.path.isfile(tap_output))
        tap = open(tap_output).read()
        self.assertIn("..", tap)
        self.assertIn("\n# debug.log of ", tap)
114

115 116
    def test_output_incompatible_setup(self):
        os.chdir(basedir)
117 118
        cmd_line = ('%s run --job-results-dir %s --sysinfo=off '
                    '--xunit - --json - passtest.py' % (AVOCADO, self.tmpdir))
119
        result = process.run(cmd_line, ignore_status=True)
120
        expected_rc = exit_codes.AVOCADO_FAIL
121 122 123
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
124 125 126 127 128 129 130
        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)
131

132 133
    @unittest.skipIf(html_uncapable(),
                     "Uncapable of Avocado Result HTML plugin")
134
    def test_output_incompatible_setup_2(self):
135
        os.chdir(basedir)
136 137
        cmd_line = ('%s run --job-results-dir %s --sysinfo=off '
                    '--html - passtest.py' % (AVOCADO, self.tmpdir))
138
        result = process.run(cmd_line, ignore_status=True)
139
        expected_rc = exit_codes.AVOCADO_JOB_FAIL
140 141 142 143 144 145 146 147
        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)

148 149 150
    def test_output_compatible_setup(self):
        tmpfile = tempfile.mktemp()
        os.chdir(basedir)
151
        cmd_line = ('%s run --job-results-dir %s --sysinfo=off '
152
                    '--journal --xunit %s --json - passtest.py' %
153
                    (AVOCADO, self.tmpdir, tmpfile))
154 155
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
156
        expected_rc = exit_codes.AVOCADO_ALL_OK
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
        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)
173
        cmd_line = ('%s run --job-results-dir %s --sysinfo=off '
174
                    '--xunit - --json %s passtest.py' %
175
                    (AVOCADO, self.tmpdir, tmpfile))
176 177
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
178
        expected_rc = exit_codes.AVOCADO_ALL_OK
179 180 181 182 183 184
        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:
185 186 187
                json_results = json.load(fp)
                debug_log = json_results['debuglog']
                self.check_output_files(debug_log)
188 189 190 191 192 193 194
            minidom.parseString(output)
        finally:
            try:
                os.remove(tmpfile)
            except OSError:
                pass

195 196
    @unittest.skipIf(html_uncapable(),
                     "Uncapable of Avocado Result HTML plugin")
197
    def test_output_compatible_setup_3(self):
198 199 200
        tmpfile = tempfile.mktemp(prefix='avocado_' + __name__)
        tmpfile2 = tempfile.mktemp(prefix='avocado_' + __name__)
        tmpdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
201
        tmpfile3 = tempfile.mktemp(dir=tmpdir)
202
        os.chdir(basedir)
203 204 205
        cmd_line = ('%s run --job-results-dir %s --sysinfo=off '
                    '--xunit %s --json %s --html %s passtest.py'
                    % (AVOCADO, self.tmpdir, tmpfile, tmpfile2, tmpfile3))
206 207
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
208
        expected_rc = exit_codes.AVOCADO_ALL_OK
209
        tmpdir_contents = os.listdir(tmpdir)
210
        self.assertEqual(len(tmpdir_contents), 4,
211
                         'Not all resources dir were created: %s' % tmpdir_contents)
212 213 214 215
        try:
            self.assertEqual(result.exit_status, expected_rc,
                             "Avocado did not return rc %d:\n%s" %
                             (expected_rc, result))
216
            self.assertNotEqual(output, "", "Output is empty")
217
            # Check if we are producing valid outputs
218 219 220 221 222 223 224 225 226
            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)
227
                shutil.rmtree(tmpdir)
228 229 230 231 232 233 234
            except OSError:
                pass

    def test_output_compatible_setup_nooutput(self):
        tmpfile = tempfile.mktemp()
        tmpfile2 = tempfile.mktemp()
        os.chdir(basedir)
235
        # Verify --silent can be supplied as app argument
236 237 238
        cmd_line = ('%s --silent run --job-results-dir %s '
                    '--sysinfo=off --xunit %s --json %s passtest.py'
                    % (AVOCADO, self.tmpdir, tmpfile, tmpfile2))
239 240
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
241
        expected_rc = exit_codes.AVOCADO_ALL_OK
242 243 244 245 246 247
        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
248
            with open(tmpfile2, 'r') as fp:
249 250 251
                json_results = json.load(fp)
                debug_log = json_results['debuglog']
                self.check_output_files(debug_log)
252 253 254 255 256 257 258 259
            minidom.parse(tmpfile)
        finally:
            try:
                os.remove(tmpfile)
                os.remove(tmpfile2)
            except OSError:
                pass

260
    def test_nonprintable_chars(self):
261
        cmd_line = ("%s run --external-runner /bin/ls "
262
                    "'NON_EXISTING_FILE_WITH_NONPRINTABLE_CHARS_IN_HERE\x1b' "
263 264
                    "--job-results-dir %s --sysinfo=off"
                    % (AVOCADO, self.tmpdir))
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
        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)

280 281
    def test_show_job_log(self):
        os.chdir(basedir)
282 283
        cmd_line = ('%s run --job-results-dir %s --sysinfo=off '
                    'passtest.py --show-job-log' % (AVOCADO, self.tmpdir))
284
        result = process.run(cmd_line, ignore_status=True)
285
        expected_rc = exit_codes.AVOCADO_ALL_OK
286 287 288
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
289
        job_id_list = re.findall('Job ID: (.*)', result.stdout,
290
                                 re.MULTILINE)
291 292
        self.assertTrue(job_id_list, 'No Job ID in stdout:\n%s' %
                        result.stdout)
293 294
        job_id = job_id_list[0]
        self.assertEqual(len(job_id), 40)
295 296 297

    def test_silent_trumps_show_job_log(self):
        os.chdir(basedir)
298
        # Also verify --silent can be supplied as run option
299 300 301
        cmd_line = ('%s run --silent --job-results-dir %s '
                    '--sysinfo=off passtest.py --show-job-log'
                    % (AVOCADO, self.tmpdir))
302 303
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
304
        expected_rc = exit_codes.AVOCADO_ALL_OK
305 306 307 308 309
        self.assertEqual(result.exit_status, expected_rc,
                         "Avocado did not return rc %d:\n%s" %
                         (expected_rc, result))
        self.assertEqual(output, "")

310 311
    def test_default_enabled_plugins(self):
        os.chdir(basedir)
312 313
        cmd_line = ('%s run --job-results-dir %s --sysinfo=off '
                    'passtest.py' % (AVOCADO, self.tmpdir))
314 315
        result = process.run(cmd_line, ignore_status=True)
        output = result.stdout + result.stderr
316
        expected_rc = exit_codes.AVOCADO_ALL_OK
317 318 319 320
        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 已提交
321 322 323 324 325
        # 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))
326 327
        second_line = output_lines[1]
        debug_log = second_line.split()[-1]
328 329
        self.check_output_files(debug_log)

330 331 332 333
    def test_verify_whiteboard_save(self):
        tmpfile = tempfile.mktemp()
        try:
            os.chdir(basedir)
334 335 336 337
            config = os.path.join(self.tmpdir, "conf.ini")
            content = ("[datadir.paths]\nlogs_dir = %s"
                       % os.path.relpath(self.tmpdir, "."))
            script.Script(config, content).save()
338 339 340
            cmd_line = ('%s --config %s --show all run '
                        '--sysinfo=off whiteboard.py --json %s'
                        % (AVOCADO, config, tmpfile))
341
            result = process.run(cmd_line, ignore_status=True)
342
            expected_rc = exit_codes.AVOCADO_ALL_OK
343 344 345 346 347
            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)
348 349 350
                logfile = json_results['tests'][0]['logfile']
                debug_dir = os.path.dirname(logfile)
                whiteboard_path = os.path.join(debug_dir, 'whiteboard')
351 352 353 354 355 356 357
                self.assertTrue(os.path.exists(whiteboard_path),
                                'Missing whiteboard file %s' % whiteboard_path)
        finally:
            try:
                os.remove(tmpfile)
            except OSError:
                pass
358

359
    @unittest.skipIf(image_output_uncapable(),
360
                     "Uncapable of generating images with PIL library")
361 362 363 364
    def test_gendata(self):
        tmpfile = tempfile.mktemp()
        try:
            os.chdir(basedir)
365
            cmd_line = ("%s run --job-results-dir %s "
366
                        "--sysinfo=off gendata.py --json %s" %
367
                        (AVOCADO, self.tmpdir, tmpfile))
368
            result = process.run(cmd_line, ignore_status=True)
369
            expected_rc = exit_codes.AVOCADO_ALL_OK
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
            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

400 401 402 403
    def test_redirect_output(self):
        redirected_output_path = tempfile.mktemp()
        try:
            os.chdir(basedir)
404 405 406
            cmd_line = ('%s run --job-results-dir %s '
                        '--sysinfo=off passtest.py > %s'
                        % (AVOCADO, self.tmpdir, redirected_output_path))
407 408
            result = process.run(cmd_line, ignore_status=True, shell=True)
            output = result.stdout + result.stderr
409
            expected_rc = exit_codes.AVOCADO_ALL_OK
410 411 412
            self.assertEqual(result.exit_status, expected_rc,
                             "Avocado did not return rc %d:\n%s" %
                             (expected_rc, result))
413 414
            self.assertEqual(output, '',
                             'After redirecting to file, output is not empty: %s' % output)
415 416 417
            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 已提交
418
                    self.assertNotIn(code, redirected_output,
419 420 421 422 423 424 425 426
                                     'Found terminal support code %s in redirected output\n%s' %
                                     (code, redirected_output))
        finally:
            try:
                os.remove(redirected_output_path)
            except OSError:
                pass

427 428 429 430
    @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",
431 432
                                             PERL_TAP_PARSER_SNIPPET
                                             % self.tmpdir)
433 434 435 436
        perl_script.save()
        os.chdir(basedir)
        process.run("perl %s" % perl_script)

A
Amador Pahim 已提交
437 438
    def test_tap_totaltests(self):
        os.chdir(basedir)
439
        cmd_line = ("%s run passtest.py "
A
Amador Pahim 已提交
440 441
                    "-m examples/tests/sleeptest.py.data/sleeptest.yaml "
                    "--job-results-dir %s "
442
                    "--tap -" % (AVOCADO, self.tmpdir))
A
Amador Pahim 已提交
443 444 445 446 447
        result = process.run(cmd_line)
        expr = '1..4'
        self.assertIn(expr, result.stdout, "'%s' not found in:\n%s"
                      % (expr, result.stdout))

448 449
    def test_broken_pipe(self):
        os.chdir(basedir)
450
        cmd_line = "(%s run --help | whacky-unknown-command)" % AVOCADO
451 452
        result = process.run(cmd_line, shell=True, ignore_status=True,
                             env={"LC_ALL": "C"})
453 454 455 456 457 458 459 460 461
        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)

A
Amador Pahim 已提交
462 463 464 465
    def test_results_plugins_no_tests(self):
        os.chdir(basedir)
        cmd_line = ("%s run UNEXISTING --job-results-dir %s"
                    % (AVOCADO, self.tmpdir))
466 467
        result = process.run(cmd_line, ignore_status=True)
        self.assertEqual(result.exit_status, exit_codes.AVOCADO_JOB_FAIL)
A
Amador Pahim 已提交
468 469 470 471 472 473 474 475 476 477

        xunit_results = os.path.join(self.tmpdir, 'latest', 'results.xml')
        self.assertFalse(os.path.exists(xunit_results))

        json_results = os.path.join(self.tmpdir, 'latest', 'results.json')
        self.assertFalse(os.path.exists(json_results))

        tap_results = os.path.join(self.tmpdir, 'latest', 'results.tap')
        self.assertFalse(os.path.exists(tap_results))

478 479 480 481
        # Check that no UI output was generated
        self.assertNotIn("RESULTS    : PASS ", result.stdout)
        self.assertNotIn("JOB TIME   :", result.stdout)

482 483 484
        # Check that plugins do not produce errors
        self.assertNotIn("Error running method ", result.stderr)

485 486 487
    def tearDown(self):
        shutil.rmtree(self.tmpdir)

488

489 490
if __name__ == '__main__':
    unittest.main()