test_output.py 18.9 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
from avocado.utils import path as utils_path
21

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


26 27 28
PERL_TAP_PARSER_SNIPPET = """#!/bin/env perl
use TAP::Parser;

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

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


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


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


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


60 61 62 63 64 65 66 67
def missing_binary(binary):
    try:
        utils_path.find_command(binary)
        return False
    except utils_path.CmdNotFoundError:
        return True


68 69
class OutputTest(unittest.TestCase):

70
    def setUp(self):
71
        self.tmpdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
72

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

90 91 92
    def tearDown(self):
        shutil.rmtree(self.tmpdir)

93

94 95
class OutputPluginTest(unittest.TestCase):

96
    def setUp(self):
97
        self.tmpdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
98

99 100 101 102 103 104 105 106
    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))
107 108 109 110 111
        try:
            minidom.parse(xunit_output)
        except Exception, details:
            raise AssertionError("Unable to parse xunit output: %s\n\n%s"
                                 % (details, open(xunit_output).read()))
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 208 209
        tmpdir_contents = os.listdir(tmpdir)
        self.assertEqual(len(tmpdir_contents), 5,
                         '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 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
    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)

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
            cmd_line = ('./scripts/avocado run --job-results-dir %s '
                        '--sysinfo=off whiteboard.py --json %s' %
333
                        (self.tmpdir, tmpfile))
334
            result = process.run(cmd_line, ignore_status=True)
335
            expected_rc = exit_codes.AVOCADO_ALL_OK
336 337 338 339 340
            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)
341 342 343
                logfile = json_results['tests'][0]['logfile']
                debug_dir = os.path.dirname(logfile)
                whiteboard_path = os.path.join(debug_dir, 'whiteboard')
344 345 346 347 348 349 350
                self.assertTrue(os.path.exists(whiteboard_path),
                                'Missing whiteboard file %s' % whiteboard_path)
        finally:
            try:
                os.remove(tmpfile)
            except OSError:
                pass
351

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

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

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

430 431 432
    def test_broken_pipe(self):
        os.chdir(basedir)
        cmd_line = "(./scripts/avocado run --help | whacky-unknown-command)"
433 434
        result = process.run(cmd_line, shell=True, ignore_status=True,
                             env={"LC_ALL": "C"})
435 436 437 438 439 440 441 442 443
        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)

444 445 446
    def tearDown(self):
        shutil.rmtree(self.tmpdir)

447

448 449
if __name__ == '__main__':
    unittest.main()