chromium/third_party/blink/tools/blinkpy/web_tests/web_test_analyzers/analyzer_unittest.py

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

import unittest

from blinkpy.web_tests.web_test_analyzers import analyzer
from blinkpy.web_tests.web_test_analyzers import data_types as dt


class FuzzyMatchingAnalyzerTest(unittest.TestCase):
    def test_run_analyzer_in_image_diff_num_threshold(self) -> None:
        fuzzy_match_analyzer = analyzer.FuzzyMatchingAnalyzer(
            fuzzy_match_image_diff_num_threshold=4,
            fuzzy_match_distinct_diff_num_threshold=5)

        test_data = \
        {
            tuple(['win']): [
                dt.ImageDiffTagTupleType(
                    10, 40, 'http://ci.chromium.org/b/1111'),
                dt.ImageDiffTagTupleType(
                    15, 42, 'http://ci.chromium.org/b/2222')],
            tuple(['linux']): [
                dt.ImageDiffTagTupleType(
                    10, 40, 'http://ci.chromium.org/b/3333')],
        }
        actual_result = fuzzy_match_analyzer.run_analyzer(
            test_data).analysis_result
        expected_result = 'Total image diff number is less than 4, no result'
        self.assertEqual(actual_result, expected_result)

    def test_run_analyzer_in_distinct_diff_num_threshold(self) -> None:
        fuzzy_match_analyzer = analyzer.FuzzyMatchingAnalyzer(
            fuzzy_match_image_diff_num_threshold=3,
            fuzzy_match_distinct_diff_num_threshold=4)

        test_data = \
        {
            tuple(['win']): [
                dt.ImageDiffTagTupleType(
                    10, 40, 'http://ci.chromium.org/b/1111'),
                dt.ImageDiffTagTupleType(
                    15, 42, 'http://ci.chromium.org/b/2222')],
            tuple(['linux']): [
                dt.ImageDiffTagTupleType(
                    10, 40, 'http://ci.chromium.org/b/3333')],
        }
        actual_result = fuzzy_match_analyzer.run_analyzer(
            test_data).analysis_result
        expected_result = 'Total test number that have image diff is 3. ' \
                          'Total distinct image diff results is less than 4. ' \
                          'Suggested check these image diff result ' \
                          '(color_difference, pixel_difference) ' \
                          'individually to fix the issue: (10, 40) ' \
                          'with total test number 2 ' \
                          '(15, 42) with total test number 1'
        self.assertEqual(actual_result, expected_result)

    def test_run_analyzer_in_fuzzy_match_range_in_different_platform(
            self) -> None:
        fuzzy_match_analyzer = analyzer.FuzzyMatchingAnalyzer(
            fuzzy_match_image_diff_num_threshold=3,
            fuzzy_match_distinct_diff_num_threshold=3)

        test_data = \
        {
            tuple(['win']): [
                dt.ImageDiffTagTupleType(
                    10, 40, 'http://ci.chromium.org/b/1111'),
                dt.ImageDiffTagTupleType(
                    15, 42, 'http://ci.chromium.org/b/2222'),
                dt.ImageDiffTagTupleType(
                    16, 46, 'http://ci.chromium.org/b/4444'),
                dt.ImageDiffTagTupleType(
                    303, 8000, 'http://ci.chromium.org/b/5555')],
            tuple(['linux']): [
                dt.ImageDiffTagTupleType(
                    10, 40, 'http://ci.chromium.org/b/3333')],
        }
        actual_result = fuzzy_match_analyzer.run_analyzer(
            test_data).analysis_result
        expected_result = 'Total test number that have image diff is 5. ' \
                          'The list of fuzzy match range suggested for this ' \
                          'test: \nFor color difference:\n15 to cover 50 percentile, 16 ' \
                          'to cover 75 percentile, 16 to cover 90 percentile, ' \
                          '16 to cover 95 percentile, 303 to cover all.\nFor ' \
                          'pixel difference:\n42 to cover 50 percentile, 46 to cover ' \
                          '75 percentile, 46 to cover 90 percentile, 46 to cover ' \
                          '95 percentile, 8000 to cover all.'
        self.assertEqual(actual_result, expected_result)

    def test_run_analyzer_in_fuzzy_match_range_with_large_even_number_tests(
            self) -> None:
        fuzzy_match_analyzer = analyzer.FuzzyMatchingAnalyzer(
            fuzzy_match_image_diff_num_threshold=3,
            fuzzy_match_distinct_diff_num_threshold=3)
        image_diffs = []
        for i in range(1, 101):
            image_diffs.append(dt.ImageDiffTagTupleType(i, 100 + i, ''))
        test_data = \
        {
            tuple(['win']): image_diffs,
        }
        actual_result = fuzzy_match_analyzer.run_analyzer(
            test_data).analysis_result
        expected_result = 'Total test number that have image diff is 100. ' \
                          'The list of fuzzy match range suggested for this ' \
                          'test: \nFor color difference:\n50 to cover 50 percentile, 75 ' \
                          'to cover 75 percentile, 90 to cover 90 percentile, ' \
                          '95 to cover 95 percentile, 100 to cover all.\nFor ' \
                          'pixel difference:\n150 to cover 50 percentile, 175 to cover ' \
                          '75 percentile, 190 to cover 90 percentile, 195 to cover ' \
                          '95 percentile, 200 to cover all.'
        self.assertEqual(actual_result, expected_result)

    def test_run_analyzer_in_fuzzy_match_range_with_large_odd_number_tests(
            self) -> None:
        fuzzy_match_analyzer = analyzer.FuzzyMatchingAnalyzer(
            fuzzy_match_image_diff_num_threshold=3,
            fuzzy_match_distinct_diff_num_threshold=3)
        image_diffs = []
        for i in range(1, 102):
            image_diffs.append(dt.ImageDiffTagTupleType(i, 100 + i, ''))
        test_data = \
        {
            tuple(['win']): image_diffs,
        }
        actual_result = fuzzy_match_analyzer.run_analyzer(
            test_data).analysis_result
        expected_result = 'Total test number that have image diff is 101. ' \
                          'The list of fuzzy match range suggested for this ' \
                          'test: \nFor color difference:\n51 to cover 50 percentile, 76 ' \
                          'to cover 75 percentile, 91 to cover 90 percentile, ' \
                          '96 to cover 95 percentile, 101 to cover all.\nFor ' \
                          'pixel difference:\n151 to cover 50 percentile, 176 to cover ' \
                          '75 percentile, 191 to cover 90 percentile, 196 to cover ' \
                          '95 percentile, 201 to cover all.'
        self.assertEqual(actual_result, expected_result)

    def test_invalid_args(self) -> None:
        # Test negative number.
        with self.assertRaises(AssertionError):
            analyzer.FuzzyMatchingAnalyzer(
                fuzzy_match_image_diff_num_threshold=-1,
                fuzzy_match_distinct_diff_num_threshold=0)
        # Test negative number.
        with self.assertRaises(AssertionError):
            analyzer.FuzzyMatchingAnalyzer(
                fuzzy_match_image_diff_num_threshold=0,
                fuzzy_match_distinct_diff_num_threshold=-1)


class SlowTestAnalyzerTest(unittest.TestCase):
    def test_run_analyzer_in_bad_slow_ratio_threshold(self) -> None:
        slow_test_analyzer = analyzer.SlowTestAnalyzer(0.95, 0)

        test_data = \
        [
            dt.TestSlownessTupleType('builder1', 1, 30, 1.1, 2, 6),
            dt.TestSlownessTupleType('builder2', 30, 5, 3.1, 5, 6),
        ]
        actual_result = slow_test_analyzer.run_analyzer(
            test_data).analysis_result
        expected_result = 'Test does not meet threshold in all builders.'
        self.assertEqual(actual_result, expected_result)

    def test_run_analyzer_in_good_slow_ratio_threshold(self) -> None:
        slow_test_analyzer = analyzer.SlowTestAnalyzer(0.9, 0)

        test_data = \
        [
            dt.TestSlownessTupleType('builder1', 1, 30, 1.1, 2, 6),
            dt.TestSlownessTupleType('builder2', 30, 3, 4.9, 5, 6),
        ]
        actual_result = slow_test_analyzer.run_analyzer(
            test_data).analysis_result
        expected_result = 'Test is slow in the below list of builders:\n' \
                          'builder2 : timeout count: 5, slow count: 30, ' \
                          'slow ratio: 0.91, avg duration: 4.90\n'
        self.assertEqual(actual_result, expected_result)

    def test_run_analyzer_in_bad_timeout_count_threshold(self) -> None:
        slow_test_analyzer = analyzer.SlowTestAnalyzer(0.9, 10)

        test_data = \
        [
            dt.TestSlownessTupleType('builder1', 20, 1, 5.1, 2, 6),
            dt.TestSlownessTupleType('builder2', 30, 1, 3.1, 5, 6),
        ]
        actual_result = slow_test_analyzer.run_analyzer(
            test_data).analysis_result
        expected_result = 'Test does not meet threshold in all builders.'
        self.assertEqual(actual_result, expected_result)

    def test_run_analyzer_in_good_timeout_count_threshold(self) -> None:
        slow_test_analyzer = analyzer.SlowTestAnalyzer(0.9, 4)

        test_data = \
        [
            dt.TestSlownessTupleType('builder1', 20, 1, 3.1, 6, 6),
            dt.TestSlownessTupleType('builder2', 30, 1, 5.1, 5, 6),
        ]
        actual_result = slow_test_analyzer.run_analyzer(
            test_data).analysis_result
        expected_result = 'Test is slow in the below list of builders:\n' \
                          'builder2 : timeout count: 5, slow count: 30, ' \
                          'slow ratio: 0.97, avg duration: 5.10\n'
        self.assertEqual(actual_result, expected_result)

    def test_invalid_args(self) -> None:
        # Test negative number.
        with self.assertRaises(AssertionError):
            analyzer.SlowTestAnalyzer(slow_result_ratio_threshold=-1,
                                      timeout_result_threshold=0)
        # Test number greater than 1.
        with self.assertRaises(AssertionError):
            analyzer.SlowTestAnalyzer(slow_result_ratio_threshold=1.1,
                                      timeout_result_threshold=0)
        # Test negative number.
        with self.assertRaises(AssertionError):
            analyzer.SlowTestAnalyzer(slow_result_ratio_threshold=0,
                                      timeout_result_threshold=-1)