chromium/chrome/test/webapps/file_reading_unittest.py

#!/usr/bin/env python3
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import os
import csv
import tempfile
from typing import List
import shutil
import unittest

from file_reading import enumerate_all_argument_combinations
from file_reading import expand_tests_from_action_parameter_wildcards
from file_reading import enumerate_markdown_file_lines_to_table_rows
from file_reading import human_friendly_name_to_canonical_action_name
from file_reading import generate_test_id_from_test_steps
from file_reading import get_and_maybe_delete_tests_in_browsertest
from file_reading import read_actions_file
from file_reading import read_enums_file
from file_reading import read_platform_supported_actions
from file_reading import resolve_bash_style_replacement
from file_reading import read_unprocessed_coverage_tests_file
from models import ActionsByName
from models import ArgEnum
from models import CoverageTest
from models import TestIdTestNameTuple
from models import TestPlatform

TEST_DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                             "test_data")


class TestAnalysisTest(unittest.TestCase):
    def test_markdown_file_mapping(self):
        test_input = [
            "Test", "# Hello", "| #test |", "| ------- | Value |",
            "| WML | Value | Value", " | ADF |"
        ]
        output = enumerate_markdown_file_lines_to_table_rows(test_input)
        expected = [(4, ["WML", "Value", "Value"]), (5, ["ADF"])]
        self.assertEqual(expected, output)

    def test_argument_combinations(self):
        argument_types: List[ArgEnum] = []
        argument_types.append(ArgEnum("T1", ["T1V1", "T1V2"], None))
        argument_types.append(ArgEnum("T2", ["T2V1", "T2V2"], None))

        combinations = enumerate_all_argument_combinations(argument_types)

        self.assertEqual([["T1V1", "T2V1"], ["T1V1", "T2V2"], ["T1V2", "T2V1"],
                          ["T1V2", "T2V2"]], combinations)

    def test_resolving_output_action_names(self):
        self.assertEqual("Test",
                         resolve_bash_style_replacement("$1", ["Test"]))
        self.assertEqual(
            "Correct sentence! over over.",
            resolve_bash_style_replacement("$1 $2ence! $3 $3.",
                                           ["Correct", "sent", "over"]))

    def test_human_friendly_name_to_canonical_action_name(self):
        self.assertEqual(
            "action_with_arg1_arg2",
            human_friendly_name_to_canonical_action_name(
                "action_with(arg1, arg2)", {}))
        self.assertEqual(
            "action_with_arg1_arg2",
            human_friendly_name_to_canonical_action_name(
                "action_with", {"action_with": "arg1_arg2"}))

    def test_enums(self):
        enums_file = os.path.join(TEST_DATA_DIR, "test_enums.md")

        with open(enums_file, "r", encoding="utf-8") \
                    as enum_types:
            enums = read_enums_file(enum_types.readlines())
            self.assertEqual(len(enums), 3)
            self.assertIn("Animal", enums)
            animal = enums["Animal"]
            self.assertEqual("Animal", animal.type_name)
            self.assertEqual(["Chicken", "Dog"], animal.values)
            self.assertEqual("Chicken", animal.default_value)

            self.assertIn("AnimalLess", enums)
            animal = enums["AnimalLess"]
            self.assertEqual("AnimalLess", animal.type_name)
            self.assertEqual(["Chicken"], animal.values)
            self.assertEqual("Chicken", animal.default_value)

            self.assertIn("Color", enums)
            color = enums["Color"]
            self.assertEqual("Color", color.type_name)
            self.assertEqual(["Green", "Red"], color.values)
            self.assertEqual(None, color.default_value)

    def test_supported_actions(self):
        supported_actions_filename = os.path.join(
            TEST_DATA_DIR, "framework_supported_actions.csv")

        with open(supported_actions_filename, "r", encoding="utf-8") \
                    as supported_actions:
            supported = read_platform_supported_actions(
                csv.reader(supported_actions, delimiter=','))
            self.assertEqual(len(supported), 4)
            (check_a_partial, check_a_full) = supported["check_a"]
            self.assertEqual(len(check_a_partial), 1)
            self.assertEqual(len(check_a_full), 3)
            (check_b_partial, check_b_full) = supported["check_b"]
            self.assertEqual(len(check_b_partial), 1)
            self.assertEqual(len(check_b_full), 3)
            (state_change_a_partial,
             state_change_a_full) = supported["state_change_a"]
            self.assertEqual(len(state_change_a_partial), 0)
            self.assertEqual(len(state_change_a_full), 4)
            (state_change_b_partial,
             state_change_b_full) = supported["state_change_b"]
            self.assertEqual(len(state_change_b_partial), 0)
            self.assertEqual(len(state_change_b_full), 3)

    def test_action_file_reading(self):
        actions_filename = os.path.join(TEST_DATA_DIR, "test_actions.md")
        supported_actions_filename = os.path.join(
            TEST_DATA_DIR, "framework_supported_actions.csv")
        enums_filename = os.path.join(TEST_DATA_DIR, "test_enums.md")

        with open(actions_filename, "r", encoding="utf-8") as f, \
                open(supported_actions_filename, "r", encoding="utf-8") \
                    as supported_actions, \
                open (enums_filename, "r", encoding="utf-8") as enums:
            supported_actions = read_platform_supported_actions(
                csv.reader(supported_actions, delimiter=','))
            actions_tsv = f.readlines()
            enums = read_enums_file(enums.readlines())

            (actions, action_base_name_to_default_param) = read_actions_file(
                actions_tsv, enums, supported_actions)
            self.assertEqual(len(actions), 13)
            self.assertEqual(len(action_base_name_to_default_param), 3)

            # Check Cpp methods
            self.assertIn('check_b_Chicken_Green', actions)
            self.assertEqual("CheckB(Animal::kChicken, Color::kGreen)",
                             actions['check_b_Chicken_Green'].cpp_method)

            # Check parameterized action state.
            self.assertIn('changes_Chicken', actions)
            self.assertIn('changes_Dog', actions)
            self.assertTrue('checks' in actions)
            checks_output_actions = actions['checks'].output_actions
            self.assertEqual(len(checks_output_actions), 2)
            self.assertCountEqual(
                checks_output_actions,
                [actions['check_a_Chicken'], actions['check_b_Chicken_Green']])

    def test_coverage_file_reading(self):
        actions_filename = os.path.join(TEST_DATA_DIR, "test_actions.md")
        supported_actions_filename = os.path.join(
            TEST_DATA_DIR, "framework_supported_actions.csv")
        enums_filename = os.path.join(TEST_DATA_DIR, "test_enums.md")

        actions: ActionsByName = {}
        action_base_name_to_default_param = {}
        with open(actions_filename) as f, \
                open(supported_actions_filename, "r", encoding="utf-8") \
                    as supported_actions, \
                open(enums_filename, "r", encoding="utf-8") as enums:
            supported_actions = read_platform_supported_actions(
                csv.reader(supported_actions, delimiter=','))
            actions_tsv = f.readlines()
            enums = read_enums_file(enums.readlines())
            (actions, action_base_name_to_default_param) = read_actions_file(
                actions_tsv, enums, supported_actions)

        coverage_filename = os.path.join(TEST_DATA_DIR,
                                         "test_unprocessed_coverage.md")
        coverage_tests: List[CoverageTest] = []
        with open(coverage_filename) as f:
            coverage_tsv = f.readlines()
            coverage_tests = read_unprocessed_coverage_tests_file(
                coverage_tsv, actions, enums,
                action_base_name_to_default_param)
        self.assertEqual(6, len(coverage_tests))

    def test_browsertest_detection(self):
        browsertest_filename = os.path.join(TEST_DATA_DIR, "tests_default.cc")
        tests_and_platforms = get_and_maybe_delete_tests_in_browsertest(
            browsertest_filename)
        expected_key = TestIdTestNameTuple(
            "state_change_a_Chicken_check_a_Chicken_check_b_Chicken_Green",
            "3Chicken_1Chicken_2ChickenGreen")
        self.assertListEqual(list(tests_and_platforms.keys()), [expected_key])
        tests_and_platforms = tests_and_platforms[expected_key]
        self.assertEqual(
            {TestPlatform.LINUX, TestPlatform.CHROME_OS, TestPlatform.MAC},
            tests_and_platforms)

    def test_browertest_in_place_deletion(self):
        input_file = os.path.join(TEST_DATA_DIR, "tests_for_deletion.cc")
        after_deletion_file = os.path.join(TEST_DATA_DIR, "tests_default.cc")
        with tempfile.TemporaryDirectory(dir=TEST_DATA_DIR) as tmpdirname:
            output_file = os.path.join(tmpdirname, "output.cc")
            shutil.copyfile(input_file, output_file)
            tests_and_platforms = get_and_maybe_delete_tests_in_browsertest(
                output_file, {
                    TestIdTestNameTuple(
                        "state_change_a_Chicken_check_a_Chicken_check_b_Chicken_Green",
                        "StateChangeAChicken")
                },
                delete_in_place=True)

            with open(output_file, 'r') as f, open(after_deletion_file,
                                                   'r') as f2:
                self.assertTrue(f.read(), f2.read())

            tests_and_platforms = tests_and_platforms[TestIdTestNameTuple(
                "state_change_a_Chicken_check_a_Chicken_check_b_Chicken_Green",
                "3Chicken_1Chicken_2ChickenGreen")]
            self.assertEqual(
                {TestPlatform.LINUX, TestPlatform.CHROME_OS, TestPlatform.MAC},
                tests_and_platforms)

    def test_action_param_expansion(self):
        enum_map: Dict[str, ArgEnum] = {
            "EnumType": ArgEnum("EnumType", ["Value1", "Value2"], None)
        }
        actions: List[str] = [
            "Action1(EnumType::All)", "Action2(EnumType::All, EnumType::All)"
        ]

        combinations = expand_tests_from_action_parameter_wildcards(
            enum_map, actions)
        expected = [['Action1(Value1)', 'Action2(Value1, Value1)'],
                    ['Action1(Value2)', 'Action2(Value1, Value1)'],
                    ['Action1(Value1)', 'Action2(Value1, Value2)'],
                    ['Action1(Value2)', 'Action2(Value1, Value2)'],
                    ['Action1(Value1)', 'Action2(Value2, Value1)'],
                    ['Action1(Value2)', 'Action2(Value2, Value1)'],
                    ['Action1(Value1)', 'Action2(Value2, Value2)'],
                    ['Action1(Value2)', 'Action2(Value2, Value2)']]
        self.assertCountEqual(combinations, expected)

    def test_generate_test_id_from_test_steps(self):
        test_steps = [
            "helper_.StateChangeA(Animal::kChicken);",
            "helper_.CheckB(Animal::kChicken, Color::kGreen);",
            "helper_.StateChangeB();"
        ]
        test_id = generate_test_id_from_test_steps(test_steps)
        expected_test_id = (
            "state_change_a_Chicken_check_b_Chicken_Green_state_change_b"
        )
        self.assertEqual(test_id, expected_test_id)


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