test_output.py 19.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 20
basedir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..')
basedir = os.path.abspath(basedir)


21 22 23
PERL_TAP_PARSER_SNIPPET = """#!/bin/env perl
use TAP::Parser;

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

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";
"""


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


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


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


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


63 64
class OutputTest(unittest.TestCase):

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

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

85 86 87
    def tearDown(self):
        shutil.rmtree(self.tmpdir)

88

89 90
class OutputPluginTest(unittest.TestCase):

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

94 95 96 97 98 99 100 101
    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))
102 103
        try:
            minidom.parse(xunit_output)
C
Cleber Rosa 已提交
104
        except Exception as details:
105 106
            raise AssertionError("Unable to parse xunit output: %s\n\n%s"
                                 % (details, open(xunit_output).read()))
107 108 109 110 111
        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)
112

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

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

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

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

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

258
    def test_nonprintable_chars(self):
259 260
        cmd_line = ("./scripts/avocado run --external-runner /bin/ls "
                    "'NON_EXISTING_FILE_WITH_NONPRINTABLE_CHARS_IN_HERE\x1b' "
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
                    "--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)

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

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

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

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

356
    @unittest.skipIf(image_output_uncapable(),
357
                     "Uncapable of generating images with PIL library")
358 359 360 361 362
    def test_gendata(self):
        tmpfile = tempfile.mktemp()
        try:
            os.chdir(basedir)
            cmd_line = ("./scripts/avocado run --job-results-dir %s "
363
                        "--sysinfo=off gendata.py --json %s" %
364 365
                        (self.tmpdir, tmpfile))
            result = process.run(cmd_line, ignore_status=True)
366
            expected_rc = exit_codes.AVOCADO_ALL_OK
367 368 369 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
            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

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

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

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

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

459 460 461
    def tearDown(self):
        shutil.rmtree(self.tmpdir)

462

463 464
if __name__ == '__main__':
    unittest.main()