chromium/third_party/blink/tools/blinkpy/web_tests/models/testharness_results_unittest.py

# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import textwrap
import unittest

from blinkpy.web_tests.models import testharness_results
from blinkpy.web_tests.models.testharness_results import (
    TestharnessLine,
    LineType,
    Status,
)


class TestResultCheckerTest(unittest.TestCase):
    def test_is_all_pass_test_result_positive_cases(self):
        self.assertTrue(
            testharness_results.is_all_pass_test_result(
                'This is a testharness.js-based test.\n'
                '[PASS] foo bar \n'
                'Harness: the test ran to completion.'))
        self.assertTrue(
            testharness_results.is_all_pass_test_result(
                'This is a wdspec test.\n'
                '[PASS] foo bar \n'
                'Harness: the test ran to completion.'))
        self.assertTrue(
            testharness_results.is_all_pass_test_result(
                'This is a testharness.js-based test.\n'
                '[PASS] \'grid\' with: grid-template-areas: "a b"\n'
                '"c d";\n'
                'Harness: the test ran to completion.\n'))

    def test_is_all_pass_test_result_negative_cases(self):
        self.assertFalse(
            testharness_results.is_all_pass_test_result(
                'This is a testharness.js-based test.\n'
                'CONSOLE WARNING: This is a warning.\n'
                'Test ran to completion.'))
        self.assertFalse(
            testharness_results.is_all_pass_test_result(
                'This is a testharness.js-based test.\n'
                '[PASS] foo bar \n'
                '[FAIL]  \n'
                ' Harness: the test ran to completion.'))
        self.assertFalse(
            testharness_results.is_all_pass_test_result(
                'This is a testharness.js-based test.\n'
                'Harness Error. harness_status.status = 1\n'
                '[PASS] foo bar\n'
                'Harness: the test ran to completion.'))

    def test_is_testharness_output_positive_cases(self):
        self.assertTrue(
            testharness_results.is_testharness_output(
                'This is a testharness.js-based test.\n'
                'Harness: the test ran to completion.'))
        self.assertTrue(
            testharness_results.is_testharness_output(
                '\n'
                ' \r This is a testharness.js-based test. \n'
                ' \r  \n'
                ' \rHarness: the test ran to completion.   \n'
                '\n'))
        self.assertTrue(
            testharness_results.is_testharness_output(
                'This is a testharness.js-based test.\n'
                'Foo bar \n'
                ' Harness: the test ran to completion.'))
        self.assertTrue(
            testharness_results.is_testharness_output(
                'This is a testharness.js-based test.\n'
                '[FAIL] bah \n'
                ' Harness: the test ran to completion.\n'
                '\n'
                '\n'))

    def test_is_testharness_output_negative_cases(self):
        self.assertFalse(testharness_results.is_testharness_output('foo'))
        self.assertFalse(testharness_results.is_testharness_output(''))
        self.assertFalse(testharness_results.is_testharness_output('   '))
        self.assertFalse(
            testharness_results.is_testharness_output(
                'This is a testharness.js-based test.  Harness: the test ran to completion.'
            ))
        self.assertFalse(
            testharness_results.is_testharness_output(
                '   This    \n'
                'is a testharness.js-based test.\n'
                'Harness: the test ran to completion.'))

    def test_is_test_output_passing_empty_content(self):
        self.assertTrue(
            testharness_results.is_test_output_passing(
                'This is a testharness.js-based test.\n'
                '   Harness: the test ran to completion.'))

    def test_is_test_output_passing_with_pass_and_random_text(self):
        self.assertTrue(
            testharness_results.is_test_output_passing(
                'RANDOM TEXT.\n'
                'This is a testharness.js-based test.\n'
                '[PASS] things are fine.\n'
                ' Harness: the test ran to completion.\n'
                '\n'))

    def test_is_test_output_passing_basic_examples(self):
        self.assertTrue(
            testharness_results.is_test_output_passing(
                'This is a testharness.js-based test.\n'
                '[PASS] foo bar \n'
                'Harness: the test ran to completion.'))
        self.assertTrue(
            testharness_results.is_test_output_passing(
                'This is a testharness.js-based test.\n'
                '[PASS] foo bar FAIL  \n'
                ' Harness: the test ran to completion.'))
        self.assertFalse(
            testharness_results.is_test_output_passing(
                'This is a testharness.js-based test.\n'
                '[PASS] foo bar \n'
                '[FAIL]  \n'
                ' Harness: the test ran to completion.'))
        self.assertFalse(
            testharness_results.is_test_output_passing(
                'This is a testharness.js-based test.\n'
                '[FAIL] bah \n'
                'Harness: the test ran to completion.'))

    def test_is_test_output_passing_with_console_messages(self):
        self.assertTrue(
            testharness_results.is_test_output_passing(
                'This is a testharness.js-based test.\n'
                ' CONSOLE ERROR: BLAH  \n'
                ' Harness: the test ran to completion.'))
        self.assertTrue(
            testharness_results.is_test_output_passing(
                'This is a testharness.js-based test.\n'
                ' CONSOLE WARNING: BLAH  \n'
                '[PASS] some passing method\n'
                'Harness: the test ran to completion.'))
        self.assertTrue(
            testharness_results.is_test_output_passing(
                'CONSOLE LOG: error.\n'
                'This is a testharness.js-based test.\n'
                '[PASS] things are fine.\n'
                'Harness: the test ran to completion.\n'
                '\n'))
        self.assertTrue(
            testharness_results.is_test_output_passing(
                'CONSOLE ERROR: error.\n'
                'This is a testharness.js-based test.\n'
                '[PASS] things are fine.\n'
                'Harness: the test ran to completion.\n'
                '\n'))
        self.assertTrue(
            testharness_results.is_test_output_passing(
                'CONSOLE WARNING: error.\n'
                'This is a testharness.js-based test.\n'
                '[PASS] things are fine.\n'
                'Harness: the test ran to completion.\n'
                '\n'))

    def test_is_test_output_passing_with_timeout_or_notrun(self):
        self.assertFalse(
            testharness_results.is_test_output_passing(
                'This is a testharness.js-based test.\n'
                '[TIMEOUT] bah \n'
                ' Harness: the test ran to completion.'))
        self.assertFalse(
            testharness_results.is_test_output_passing(
                'This is a testharness.js-based test.\n'
                '[NOTRUN] bah \n'
                ' Harness: the test ran to completion.'))

    def test_has_other_useful_output_positive_cases(self):
        self.assertTrue(
            testharness_results.has_other_useful_output(
                'This is a testharness.js-based test.\n'
                'CONSOLE ERROR: This is an error.\n'
                'Test ran to completion.'))
        self.assertTrue(
            testharness_results.has_other_useful_output(
                'This is a testharness.js-based test.\n'
                'CONSOLE WARNING: This is a warning.\n'
                'Test ran to completion.'))
        self.assertTrue(
            testharness_results.has_other_useful_output(
                'CONSOLE ERROR: This is an error.\n'
                'Test ran to completion.'))
        self.assertTrue(
            testharness_results.has_other_useful_output(
                'CONSOLE WARNING: This is a warning.\n'
                'Test ran to completion.'))
        self.assertTrue(
            testharness_results.has_other_useful_output(
                'This is a testharness.js-based test.\n'
                'CONSOLE ERROR: This is an error.'))
        self.assertTrue(
            testharness_results.has_other_useful_output(
                'CONSOLE ERROR: This is an error.'))
        self.assertTrue(
            testharness_results.has_other_useful_output(
                'CONSOLE WARNING: This is a warning.'))
        self.assertTrue(
            testharness_results.has_other_useful_output('ALERT: alert!'))
        self.assertTrue(
            testharness_results.has_other_useful_output('CONFIRM: confirm?'))
        self.assertTrue(
            testharness_results.has_other_useful_output('PROMPT: prompt.'))

    def test_has_other_useful_output_negative_cases(self):
        self.assertFalse(
            testharness_results.has_other_useful_output(
                'This is a testharness.js-based test.\n'
                'CONSOLE MESSAGE: This is not error.'))
        self.assertFalse(
            testharness_results.has_other_useful_output(
                'This is a testharness.js-based test.\n'
                'No errors here.'))
        self.assertFalse(
            testharness_results.has_other_useful_output(
                'This is not a CONSOLE ERROR, sorry.'))
        self.assertFalse(
            testharness_results.has_other_useful_output(
                'This is not a CONSOLE WARNING, sorry.'))
        self.assertFalse(
            testharness_results.has_other_useful_output('Not an ALERT'))
        self.assertFalse(
            testharness_results.has_other_useful_output('Not a CONFRIM'))
        self.assertFalse(
            testharness_results.has_other_useful_output('Not a PROMPT'))

    def test_parse_testharness_baseline(self):
        results = testharness_results.parse_testharness_baseline(
            textwrap.dedent("""\
                This is a testharness.js-based test.
                Harness Error. harness_status.status = 1 , harness_status.message = ReferenceError: ShadowRealm is not defined
                [PASS] \t Query "geolocation" permission
                [ FAIL  TIMEOUT ] Window interface: attribute\\n\\0\\r\\\\nevent
                  assert_true: property should be enumerable\\n\\0\\r\\\\n  expected true got false

                CONSOLE ERROR: Console error
                Harness: the test ran to completion.
                """))
        self.assertEqual(len(results), 6)

        self.assertIs(results[0].line_type, LineType.TESTHARNESS_HEADER)
        self.assertEqual(results[0].statuses, frozenset())
        self.assertIsNone(results[0].subtest)
        self.assertIsNone(results[0].message)

        self.assertIs(results[1].line_type, LineType.HARNESS_ERROR)
        self.assertEqual(results[1].statuses, {Status.ERROR})
        self.assertIsNone(results[1].subtest)
        self.assertEqual(results[1].message,
                         'ReferenceError: ShadowRealm is not defined')

        self.assertIs(results[2].line_type, LineType.SUBTEST)
        self.assertEqual(results[2].statuses, {Status.PASS})
        self.assertEqual(results[2].subtest,
                         '\t Query "geolocation" permission')
        self.assertIsNone(results[2].message)

        self.assertIs(results[3].line_type, LineType.SUBTEST)
        self.assertEqual(results[3].statuses, {Status.FAIL, Status.TIMEOUT})
        self.assertEqual(results[3].subtest,
                         'Window interface: attribute\n\0\r\\nevent')
        self.assertEqual(
            results[3].message, 'assert_true: property should be enumerable'
            '\n\0\r\\n  expected true got false')

        self.assertIs(results[4].line_type, LineType.CONSOLE_ERROR)
        self.assertEqual(results[4].statuses, frozenset())
        self.assertIsNone(results[4].subtest)
        self.assertEqual(results[4].message, 'Console error')

        self.assertIs(results[5].line_type, LineType.FOOTER)
        self.assertEqual(results[5].statuses, frozenset())
        self.assertIsNone(results[5].subtest)
        self.assertIsNone(results[5].message)

    def test_parse_testharness_baseline_other_newlines(self):
        _, subtest, _ = testharness_results.parse_testharness_baseline(
            textwrap.dedent("""\
                This is a testharness.js-based test.
                [FAIL] not line breaks: \v \f \x1c \x1e \x85
                  assert_true: not line breaks: \u2028 \u2029.
                Harness: the test ran to completion.
                """))
        self.assertEqual(subtest.line_type, LineType.SUBTEST)
        self.assertEqual(subtest.statuses, {Status.FAIL})
        self.assertEqual(subtest.subtest,
                         'not line breaks: \v \f \x1c \x1e \x85')
        self.assertEqual(subtest.message,
                         'assert_true: not line breaks: \u2028 \u2029.')

    def test_format_testharness_baseline(self):
        lines = [
            TestharnessLine(LineType.CONSOLE_WARNING,
                            message='warning before test'),
            TestharnessLine(LineType.TESTHARNESS_HEADER),
            TestharnessLine(LineType.HARNESS_ERROR, {Status.ERROR},
                            'SyntaxError'),
            TestharnessLine(LineType.SUBTEST, {Status.PASS, Status.TIMEOUT},
                            'fake-message\n\r\0\\n', 'subtest-1\n\r\0\\n'),
            TestharnessLine(LineType.SUBTEST, {Status.NOTRUN},
                            subtest='subtest-2'),
            TestharnessLine(LineType.FOOTER),
        ]
        self.assertEqual(
            testharness_results.format_testharness_baseline(lines),
            textwrap.dedent("""\
                CONSOLE WARNING: warning before test
                This is a testharness.js-based test.
                Harness Error. harness_status.status = 1 , harness_status.message = SyntaxError
                [PASS TIMEOUT] subtest-1\\n\\r\\0\\\\n
                  fake-message\\n\\r\\0\\\\n
                [NOTRUN] subtest-2
                Harness: the test ran to completion.
                """))

    def test_format_all_pass_testharness_baseline(self):
        lines = [
            TestharnessLine(LineType.TESTHARNESS_HEADER),
            TestharnessLine(LineType.SUBTEST, {Status.PASS},
                            subtest='subtest'),
            TestharnessLine(LineType.FOOTER),
        ]
        # No failure counts written. Note that it is the caller's responsibility
        # to detect that this is an all-pass baseline, and possibly not write
        # it.
        self.assertEqual(
            testharness_results.format_testharness_baseline(lines),
            textwrap.dedent("""\
                This is a testharness.js-based test.
                Harness: the test ran to completion.
                """))

    def test_format_status_counts(self):
        lines = [
            TestharnessLine(LineType.SUBTEST, {Status.FAIL},
                            subtest=f'subtest-{i}') for i in range(50)
        ]
        lines = [
            TestharnessLine(LineType.TESTHARNESS_HEADER),
            *lines,
            TestharnessLine(LineType.FOOTER),
        ]
        self.assertIn(
            'Found 50 FAIL, 0 TIMEOUT, 0 NOTRUN.',
            testharness_results.format_testharness_baseline(
                lines).splitlines())