| #!/usr/bin/env python3 |
| # Copyright 2020 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Unit tests for dependency_analysis.count_cycles.""" |
| |
| import itertools |
| import unittest |
| |
| import count_cycles |
| import graph |
| |
| |
| class TestFindCycles(unittest.TestCase): |
| """Unit tests for find_cycles.""" |
| KEY_0 = '0' |
| KEY_1 = '1' |
| KEY_2 = '2' |
| KEY_3 = '3' |
| MAX_CYCLE_LENGTH = 10 |
| |
| def test_no_self_cycles(self): |
| """Tests that self-cycles are not considered. |
| |
| 0 <---+ |
| ^ | |
| | v |
| +---> 1 (plus, 0 and 1 have self-cycles) |
| (one cycle, 010) |
| """ |
| test_graph = graph.Graph() |
| test_graph.add_edge_if_new(self.KEY_0, self.KEY_1) |
| test_graph.add_edge_if_new(self.KEY_1, self.KEY_0) |
| test_graph.add_edge_if_new(self.KEY_0, self.KEY_0) |
| test_graph.add_edge_if_new(self.KEY_1, self.KEY_1) |
| |
| res = count_cycles.find_cycles(test_graph, self.MAX_CYCLE_LENGTH) |
| expected_cycles = { |
| 2: 1, |
| } |
| for cycle_length, cycles in enumerate(res): |
| self.assertEqual(len(cycles), expected_cycles.get(cycle_length, 0)) |
| |
| def test_big_cycle(self): |
| """Tests using a graph with one big cycle. |
| |
| 0 -> 1 |
| ^ | |
| | v |
| 3 <- 2 |
| (one cycle, 01230) |
| """ |
| test_graph = graph.Graph() |
| test_graph.add_edge_if_new(self.KEY_0, self.KEY_1) |
| test_graph.add_edge_if_new(self.KEY_1, self.KEY_2) |
| test_graph.add_edge_if_new(self.KEY_2, self.KEY_3) |
| test_graph.add_edge_if_new(self.KEY_3, self.KEY_0) |
| |
| res = count_cycles.find_cycles(test_graph, self.MAX_CYCLE_LENGTH) |
| expected_cycles = { |
| 4: 1, |
| } |
| for cycle_length, cycles in enumerate(res): |
| self.assertEqual(len(cycles), expected_cycles.get(cycle_length, 0)) |
| |
| def test_multiple_cycles(self): |
| """Tests using a graph with multiple cycles. |
| |
| 0 -> 1 |
| ^ ^ |
| | v |
| +--- 2 -> 3 |
| (two cycles, 0120 and 121) |
| """ |
| test_graph = graph.Graph() |
| test_graph.add_edge_if_new(self.KEY_0, self.KEY_1) |
| test_graph.add_edge_if_new(self.KEY_1, self.KEY_2) |
| test_graph.add_edge_if_new(self.KEY_2, self.KEY_0) |
| test_graph.add_edge_if_new(self.KEY_2, self.KEY_1) |
| test_graph.add_edge_if_new(self.KEY_2, self.KEY_3) |
| |
| res = count_cycles.find_cycles(test_graph, self.MAX_CYCLE_LENGTH) |
| expected_cycles = { |
| 2: 1, |
| 3: 1, |
| } |
| for cycle_length, cycles in enumerate(res): |
| self.assertEqual(len(cycles), expected_cycles.get(cycle_length, 0)) |
| |
| def test_complete_graph(self): |
| """Tests using a complete graph on 4 nodes. |
| |
| +------------+ |
| v | |
| 0 <> 1 <--+ | |
| ^ ^ | | |
| | v v | |
| +--> 2 <> 3 <+ |
| (20 cycles, |
| 010, 020, 030, 121, 131, 232, |
| 0120, 0130, 0210, 0230, 0310, 0320, 1231, 1321, |
| 01230, 01320, 02130, 02310, 03120, 03210) |
| """ |
| test_graph = graph.Graph() |
| for ka, kb in itertools.permutations( |
| [self.KEY_0, self.KEY_1, self.KEY_2, self.KEY_3], 2): |
| test_graph.add_edge_if_new(ka, kb) |
| |
| res = count_cycles.find_cycles(test_graph, self.MAX_CYCLE_LENGTH) |
| expected_cycles = {2: 6, 3: 8, 4: 6} |
| for cycle_length, cycles in enumerate(res): |
| self.assertEqual(len(cycles), expected_cycles.get(cycle_length, 0)) |
| |
| def test_complete_graph_restricted_length(self): |
| """Tests using a complete graph on 4 nodes with maximum cycle length 2. |
| |
| +------------+ |
| v | |
| 0 <> 1 <--+ | |
| ^ ^ | | |
| | v v | |
| +--> 2 <> 3 <+ |
| (6 cycles, 010, 020, 030, 121, 131, 232) |
| """ |
| test_graph = graph.Graph() |
| for ka, kb in itertools.permutations( |
| [self.KEY_0, self.KEY_1, self.KEY_2, self.KEY_3], 2): |
| test_graph.add_edge_if_new(ka, kb) |
| |
| res = count_cycles.find_cycles(test_graph, 2) |
| expected_cycles = {2: 6} |
| for cycle_length, cycles in enumerate(res): |
| self.assertEqual(len(cycles), expected_cycles.get(cycle_length, 0)) |