• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2020 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Unit tests for external updater reviewers."""
15
16from typing import List, Mapping, Set
17import unittest
18
19import reviewers
20
21
22class ExternalUpdaterReviewersTest(unittest.TestCase):
23    """Unit tests for external updater reviewers."""
24
25    def setUp(self):
26        super().setUp()
27        # save constants in reviewers
28        self.saved_proj_reviewers = reviewers.PROJ_REVIEWERS
29        self.saved_rust_reviewers = reviewers.RUST_REVIEWERS
30        self.saved_rust_reviewer_list = reviewers.RUST_REVIEWER_LIST
31        self.saved_rust_crate_owners = reviewers.RUST_CRATE_OWNERS
32
33    def tearDown(self):
34        super().tearDown()
35        # restore constants in reviewers
36        reviewers.PROJ_REVIEWERS = self.saved_proj_reviewers
37        reviewers.RUST_REVIEWERS = self.saved_rust_reviewers
38        reviewers.RUST_REVIEWER_LIST = self.saved_rust_reviewer_list
39        reviewers.RUST_CRATE_OWNERS = self.saved_rust_crate_owners
40
41    # pylint: disable=no-self-use
42    def _collect_reviewers(self, num_runs, proj_path):
43        counters = {}
44        for _ in range(num_runs):
45            name = reviewers.find_reviewers(proj_path)
46            if name in counters:
47                counters[name] += 1
48            else:
49                counters[name] = 1
50        return counters
51
52    def test_reviewers_types(self):
53        """Check the types of PROJ_REVIEWERS and RUST_REVIEWERS."""
54        # Check type of PROJ_REVIEWERS
55        self.assertIsInstance(reviewers.PROJ_REVIEWERS, Mapping)
56        for key, value in reviewers.PROJ_REVIEWERS.items():
57            self.assertIsInstance(key, str)
58            # pylint: disable=isinstance-second-argument-not-valid-type
59            # https://github.com/PyCQA/pylint/issues/3507
60            if isinstance(value, (List, Set)):
61                for x in value:
62                    self.assertIsInstance(x, str)
63            else:
64                self.assertIsInstance(value, str)
65        # Check element types of the reviewers list and map.
66        self.assertIsInstance(reviewers.RUST_REVIEWERS, Mapping)
67        for (name, quota) in reviewers.RUST_REVIEWERS.items():
68            self.assertIsInstance(name, str)
69            self.assertIsInstance(quota, int)
70
71    def test_reviewers_constants(self):
72        """Check the constants associated to the reviewers."""
73        # There should be enough people in the reviewers pool.
74        self.assertGreaterEqual(len(reviewers.RUST_REVIEWERS), 3)
75        # Assume no project reviewers and recreate RUST_REVIEWER_LIST
76        reviewers.PROJ_REVIEWERS = {}
77        reviewers.RUST_REVIEWER_LIST = reviewers.create_rust_reviewer_list()
78        sum_projects = sum(reviewers.RUST_REVIEWERS.values())
79        self.assertEqual(sum_projects, len(reviewers.RUST_REVIEWER_LIST))
80
81    def test_reviewers_randomness(self):
82        """Check random selection of reviewers."""
83        # This might fail when the random.choice function is extremely unfair.
84        # With N * 20 tries, each reviewer should be picked at least twice.
85        # Assume no project reviewers and recreate RUST_REVIEWER_LIST
86        reviewers.PROJ_REVIEWERS = {}
87        reviewers.RUST_REVIEWER_LIST = reviewers.create_rust_reviewer_list()
88        num_tries = len(reviewers.RUST_REVIEWERS) * 20
89        counters = self._collect_reviewers(num_tries, "rust/crates/libc")
90        self.assertEqual(len(counters), len(reviewers.RUST_REVIEWERS))
91        for n in counters.values():
92            self.assertGreaterEqual(n, 5)
93        self.assertEqual(sum(counters.values()), num_tries)
94
95    def test_project_reviewers(self):
96        """For specific projects, select only the specified reviewers."""
97        reviewers.PROJ_REVIEWERS = {
98            "rust/crates/p1": "x@g.com",
99            "rust/crates/p_any": ["x@g.com", "y@g.com"],
100            "rust/crates/p_all": {"z@g", "x@g.com", "y@g.com"},
101        }
102        counters = self._collect_reviewers(20, "external/rust/crates/p1")
103        self.assertEqual(len(counters), 1)
104        self.assertTrue(counters["r=x@g.com"], 20)
105        counters = self._collect_reviewers(20, "external/rust/crates/p_any")
106        self.assertEqual(len(counters), 2)
107        self.assertGreater(counters["r=x@g.com"], 2)
108        self.assertGreater(counters["r=y@g.com"], 2)
109        self.assertTrue(counters["r=x@g.com"] + counters["r=y@g.com"], 20)
110        counters = self._collect_reviewers(20, "external/rust/crates/p_all")
111        # {x, y, z} reviewers should be sorted
112        self.assertEqual(counters["r=x@g.com,r=y@g.com,r=z@g"], 20)
113
114    def test_weighted_reviewers(self):
115        """Test create_rust_reviewer_list."""
116        reviewers.PROJ_REVIEWERS = {
117            "any_p1": "x@g",  # 1 for x@g
118            "any_p2": {"xyz", "x@g"},  # 1 for x@g, xyz is not a rust reviewer
119            "any_p3": {"abc", "x@g"},  # 0.5 for "abc" and "x@g"
120        }
121        reviewers.RUST_REVIEWERS = {
122            "x@g": 5,  # ceil(5 - 2.5) = 3
123            "abc": 2,  # ceil(2 - 0.5) = 2
124        }
125        reviewer_list = reviewers.create_rust_reviewer_list()
126        self.assertEqual(reviewer_list, ["x@g", "x@g", "x@g", "abc", "abc"])
127        # Error case: if nobody has project quota, reset everyone to 1.
128        reviewers.RUST_REVIEWERS = {
129            "x@g": 1,  # ceil(1 - 2.5) = -1
130            "abc": 0,  # ceil(0 - 0.5) = 0
131        }
132        reviewer_list = reviewers.create_rust_reviewer_list()
133        self.assertEqual(reviewer_list, ["x@g", "abc"])  # everyone got 1
134
135
136if __name__ == "__main__":
137    unittest.main(verbosity=2)
138