17db96d56Sopenharmony_ciimport graphlib
27db96d56Sopenharmony_ciimport os
37db96d56Sopenharmony_ciimport unittest
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_cifrom test.support.script_helper import assert_python_ok
67db96d56Sopenharmony_ci
77db96d56Sopenharmony_ciclass TestTopologicalSort(unittest.TestCase):
87db96d56Sopenharmony_ci    def _test_graph(self, graph, expected):
97db96d56Sopenharmony_ci        def static_order_with_groups(ts):
107db96d56Sopenharmony_ci            ts.prepare()
117db96d56Sopenharmony_ci            while ts.is_active():
127db96d56Sopenharmony_ci                nodes = ts.get_ready()
137db96d56Sopenharmony_ci                for node in nodes:
147db96d56Sopenharmony_ci                    ts.done(node)
157db96d56Sopenharmony_ci                yield tuple(sorted(nodes))
167db96d56Sopenharmony_ci
177db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter(graph)
187db96d56Sopenharmony_ci        self.assertEqual(list(static_order_with_groups(ts)), list(expected))
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter(graph)
217db96d56Sopenharmony_ci        # need to be a bit careful comparing the result of ts.static_order and
227db96d56Sopenharmony_ci        # expected, because the order within a group is dependent on set
237db96d56Sopenharmony_ci        # iteration order
247db96d56Sopenharmony_ci        it = iter(ts.static_order())
257db96d56Sopenharmony_ci        for group in expected:
267db96d56Sopenharmony_ci            tsgroup = {next(it) for element in group}
277db96d56Sopenharmony_ci            self.assertEqual(set(group), tsgroup)
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ci    def _assert_cycle(self, graph, cycle):
307db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
317db96d56Sopenharmony_ci        for node, dependson in graph.items():
327db96d56Sopenharmony_ci            ts.add(node, *dependson)
337db96d56Sopenharmony_ci        try:
347db96d56Sopenharmony_ci            ts.prepare()
357db96d56Sopenharmony_ci        except graphlib.CycleError as e:
367db96d56Sopenharmony_ci            _, seq = e.args
377db96d56Sopenharmony_ci            self.assertIn(" ".join(map(str, cycle)), " ".join(map(str, seq * 2)))
387db96d56Sopenharmony_ci        else:
397db96d56Sopenharmony_ci            raise
407db96d56Sopenharmony_ci
417db96d56Sopenharmony_ci    def test_simple_cases(self):
427db96d56Sopenharmony_ci        self._test_graph(
437db96d56Sopenharmony_ci            {2: {11}, 9: {11, 8}, 10: {11, 3}, 11: {7, 5}, 8: {7, 3}},
447db96d56Sopenharmony_ci            [(3, 5, 7), (8, 11), (2, 9, 10)],
457db96d56Sopenharmony_ci        )
467db96d56Sopenharmony_ci
477db96d56Sopenharmony_ci        self._test_graph({1: {}}, [(1,)])
487db96d56Sopenharmony_ci
497db96d56Sopenharmony_ci        self._test_graph(
507db96d56Sopenharmony_ci            {x: {x + 1} for x in range(10)}, [(x,) for x in range(10, -1, -1)]
517db96d56Sopenharmony_ci        )
527db96d56Sopenharmony_ci
537db96d56Sopenharmony_ci        self._test_graph(
547db96d56Sopenharmony_ci            {2: {3}, 3: {4}, 4: {5}, 5: {1}, 11: {12}, 12: {13}, 13: {14}, 14: {15}},
557db96d56Sopenharmony_ci            [(1, 15), (5, 14), (4, 13), (3, 12), (2, 11)],
567db96d56Sopenharmony_ci        )
577db96d56Sopenharmony_ci
587db96d56Sopenharmony_ci        self._test_graph(
597db96d56Sopenharmony_ci            {
607db96d56Sopenharmony_ci                0: [1, 2],
617db96d56Sopenharmony_ci                1: [3],
627db96d56Sopenharmony_ci                2: [5, 6],
637db96d56Sopenharmony_ci                3: [4],
647db96d56Sopenharmony_ci                4: [9],
657db96d56Sopenharmony_ci                5: [3],
667db96d56Sopenharmony_ci                6: [7],
677db96d56Sopenharmony_ci                7: [8],
687db96d56Sopenharmony_ci                8: [4],
697db96d56Sopenharmony_ci                9: [],
707db96d56Sopenharmony_ci            },
717db96d56Sopenharmony_ci            [(9,), (4,), (3, 8), (1, 5, 7), (6,), (2,), (0,)],
727db96d56Sopenharmony_ci        )
737db96d56Sopenharmony_ci
747db96d56Sopenharmony_ci        self._test_graph({0: [1, 2], 1: [], 2: [3], 3: []}, [(1, 3), (2,), (0,)])
757db96d56Sopenharmony_ci
767db96d56Sopenharmony_ci        self._test_graph(
777db96d56Sopenharmony_ci            {0: [1, 2], 1: [], 2: [3], 3: [], 4: [5], 5: [6], 6: []},
787db96d56Sopenharmony_ci            [(1, 3, 6), (2, 5), (0, 4)],
797db96d56Sopenharmony_ci        )
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_ci    def test_no_dependencies(self):
827db96d56Sopenharmony_ci        self._test_graph({1: {2}, 3: {4}, 5: {6}}, [(2, 4, 6), (1, 3, 5)])
837db96d56Sopenharmony_ci
847db96d56Sopenharmony_ci        self._test_graph({1: set(), 3: set(), 5: set()}, [(1, 3, 5)])
857db96d56Sopenharmony_ci
867db96d56Sopenharmony_ci    def test_the_node_multiple_times(self):
877db96d56Sopenharmony_ci        # Test same node multiple times in dependencies
887db96d56Sopenharmony_ci        self._test_graph({1: {2}, 3: {4}, 0: [2, 4, 4, 4, 4, 4]}, [(2, 4), (0, 1, 3)])
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci        # Test adding the same dependency multiple times
917db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
927db96d56Sopenharmony_ci        ts.add(1, 2)
937db96d56Sopenharmony_ci        ts.add(1, 2)
947db96d56Sopenharmony_ci        ts.add(1, 2)
957db96d56Sopenharmony_ci        self.assertEqual([*ts.static_order()], [2, 1])
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ci    def test_graph_with_iterables(self):
987db96d56Sopenharmony_ci        dependson = (2 * x + 1 for x in range(5))
997db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter({0: dependson})
1007db96d56Sopenharmony_ci        self.assertEqual(list(ts.static_order()), [1, 3, 5, 7, 9, 0])
1017db96d56Sopenharmony_ci
1027db96d56Sopenharmony_ci    def test_add_dependencies_for_same_node_incrementally(self):
1037db96d56Sopenharmony_ci        # Test same node multiple times
1047db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
1057db96d56Sopenharmony_ci        ts.add(1, 2)
1067db96d56Sopenharmony_ci        ts.add(1, 3)
1077db96d56Sopenharmony_ci        ts.add(1, 4)
1087db96d56Sopenharmony_ci        ts.add(1, 5)
1097db96d56Sopenharmony_ci
1107db96d56Sopenharmony_ci        ts2 = graphlib.TopologicalSorter({1: {2, 3, 4, 5}})
1117db96d56Sopenharmony_ci        self.assertEqual([*ts.static_order()], [*ts2.static_order()])
1127db96d56Sopenharmony_ci
1137db96d56Sopenharmony_ci    def test_empty(self):
1147db96d56Sopenharmony_ci        self._test_graph({}, [])
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci    def test_cycle(self):
1177db96d56Sopenharmony_ci        # Self cycle
1187db96d56Sopenharmony_ci        self._assert_cycle({1: {1}}, [1, 1])
1197db96d56Sopenharmony_ci        # Simple cycle
1207db96d56Sopenharmony_ci        self._assert_cycle({1: {2}, 2: {1}}, [1, 2, 1])
1217db96d56Sopenharmony_ci        # Indirect cycle
1227db96d56Sopenharmony_ci        self._assert_cycle({1: {2}, 2: {3}, 3: {1}}, [1, 3, 2, 1])
1237db96d56Sopenharmony_ci        # not all elements involved in a cycle
1247db96d56Sopenharmony_ci        self._assert_cycle({1: {2}, 2: {3}, 3: {1}, 5: {4}, 4: {6}}, [1, 3, 2, 1])
1257db96d56Sopenharmony_ci        # Multiple cycles
1267db96d56Sopenharmony_ci        self._assert_cycle({1: {2}, 2: {1}, 3: {4}, 4: {5}, 6: {7}, 7: {6}}, [1, 2, 1])
1277db96d56Sopenharmony_ci        # Cycle in the middle of the graph
1287db96d56Sopenharmony_ci        self._assert_cycle({1: {2}, 2: {3}, 3: {2, 4}, 4: {5}}, [3, 2])
1297db96d56Sopenharmony_ci
1307db96d56Sopenharmony_ci    def test_calls_before_prepare(self):
1317db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
1327db96d56Sopenharmony_ci
1337db96d56Sopenharmony_ci        with self.assertRaisesRegex(ValueError, r"prepare\(\) must be called first"):
1347db96d56Sopenharmony_ci            ts.get_ready()
1357db96d56Sopenharmony_ci        with self.assertRaisesRegex(ValueError, r"prepare\(\) must be called first"):
1367db96d56Sopenharmony_ci            ts.done(3)
1377db96d56Sopenharmony_ci        with self.assertRaisesRegex(ValueError, r"prepare\(\) must be called first"):
1387db96d56Sopenharmony_ci            ts.is_active()
1397db96d56Sopenharmony_ci
1407db96d56Sopenharmony_ci    def test_prepare_multiple_times(self):
1417db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
1427db96d56Sopenharmony_ci        ts.prepare()
1437db96d56Sopenharmony_ci        with self.assertRaisesRegex(ValueError, r"cannot prepare\(\) more than once"):
1447db96d56Sopenharmony_ci            ts.prepare()
1457db96d56Sopenharmony_ci
1467db96d56Sopenharmony_ci    def test_invalid_nodes_in_done(self):
1477db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
1487db96d56Sopenharmony_ci        ts.add(1, 2, 3, 4)
1497db96d56Sopenharmony_ci        ts.add(2, 3, 4)
1507db96d56Sopenharmony_ci        ts.prepare()
1517db96d56Sopenharmony_ci        ts.get_ready()
1527db96d56Sopenharmony_ci
1537db96d56Sopenharmony_ci        with self.assertRaisesRegex(ValueError, "node 2 was not passed out"):
1547db96d56Sopenharmony_ci            ts.done(2)
1557db96d56Sopenharmony_ci        with self.assertRaisesRegex(ValueError, r"node 24 was not added using add\(\)"):
1567db96d56Sopenharmony_ci            ts.done(24)
1577db96d56Sopenharmony_ci
1587db96d56Sopenharmony_ci    def test_done(self):
1597db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
1607db96d56Sopenharmony_ci        ts.add(1, 2, 3, 4)
1617db96d56Sopenharmony_ci        ts.add(2, 3)
1627db96d56Sopenharmony_ci        ts.prepare()
1637db96d56Sopenharmony_ci
1647db96d56Sopenharmony_ci        self.assertEqual(ts.get_ready(), (3, 4))
1657db96d56Sopenharmony_ci        # If we don't mark anything as done, get_ready() returns nothing
1667db96d56Sopenharmony_ci        self.assertEqual(ts.get_ready(), ())
1677db96d56Sopenharmony_ci        ts.done(3)
1687db96d56Sopenharmony_ci        # Now 2 becomes available as 3 is done
1697db96d56Sopenharmony_ci        self.assertEqual(ts.get_ready(), (2,))
1707db96d56Sopenharmony_ci        self.assertEqual(ts.get_ready(), ())
1717db96d56Sopenharmony_ci        ts.done(4)
1727db96d56Sopenharmony_ci        ts.done(2)
1737db96d56Sopenharmony_ci        # Only 1 is missing
1747db96d56Sopenharmony_ci        self.assertEqual(ts.get_ready(), (1,))
1757db96d56Sopenharmony_ci        self.assertEqual(ts.get_ready(), ())
1767db96d56Sopenharmony_ci        ts.done(1)
1777db96d56Sopenharmony_ci        self.assertEqual(ts.get_ready(), ())
1787db96d56Sopenharmony_ci        self.assertFalse(ts.is_active())
1797db96d56Sopenharmony_ci
1807db96d56Sopenharmony_ci    def test_is_active(self):
1817db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
1827db96d56Sopenharmony_ci        ts.add(1, 2)
1837db96d56Sopenharmony_ci        ts.prepare()
1847db96d56Sopenharmony_ci
1857db96d56Sopenharmony_ci        self.assertTrue(ts.is_active())
1867db96d56Sopenharmony_ci        self.assertEqual(ts.get_ready(), (2,))
1877db96d56Sopenharmony_ci        self.assertTrue(ts.is_active())
1887db96d56Sopenharmony_ci        ts.done(2)
1897db96d56Sopenharmony_ci        self.assertTrue(ts.is_active())
1907db96d56Sopenharmony_ci        self.assertEqual(ts.get_ready(), (1,))
1917db96d56Sopenharmony_ci        self.assertTrue(ts.is_active())
1927db96d56Sopenharmony_ci        ts.done(1)
1937db96d56Sopenharmony_ci        self.assertFalse(ts.is_active())
1947db96d56Sopenharmony_ci
1957db96d56Sopenharmony_ci    def test_not_hashable_nodes(self):
1967db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
1977db96d56Sopenharmony_ci        self.assertRaises(TypeError, ts.add, dict(), 1)
1987db96d56Sopenharmony_ci        self.assertRaises(TypeError, ts.add, 1, dict())
1997db96d56Sopenharmony_ci        self.assertRaises(TypeError, ts.add, dict(), dict())
2007db96d56Sopenharmony_ci
2017db96d56Sopenharmony_ci    def test_order_of_insertion_does_not_matter_between_groups(self):
2027db96d56Sopenharmony_ci        def get_groups(ts):
2037db96d56Sopenharmony_ci            ts.prepare()
2047db96d56Sopenharmony_ci            while ts.is_active():
2057db96d56Sopenharmony_ci                nodes = ts.get_ready()
2067db96d56Sopenharmony_ci                ts.done(*nodes)
2077db96d56Sopenharmony_ci                yield set(nodes)
2087db96d56Sopenharmony_ci
2097db96d56Sopenharmony_ci        ts = graphlib.TopologicalSorter()
2107db96d56Sopenharmony_ci        ts.add(3, 2, 1)
2117db96d56Sopenharmony_ci        ts.add(1, 0)
2127db96d56Sopenharmony_ci        ts.add(4, 5)
2137db96d56Sopenharmony_ci        ts.add(6, 7)
2147db96d56Sopenharmony_ci        ts.add(4, 7)
2157db96d56Sopenharmony_ci
2167db96d56Sopenharmony_ci        ts2 = graphlib.TopologicalSorter()
2177db96d56Sopenharmony_ci        ts2.add(1, 0)
2187db96d56Sopenharmony_ci        ts2.add(3, 2, 1)
2197db96d56Sopenharmony_ci        ts2.add(4, 7)
2207db96d56Sopenharmony_ci        ts2.add(6, 7)
2217db96d56Sopenharmony_ci        ts2.add(4, 5)
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci        self.assertEqual(list(get_groups(ts)), list(get_groups(ts2)))
2247db96d56Sopenharmony_ci
2257db96d56Sopenharmony_ci    def test_static_order_does_not_change_with_the_hash_seed(self):
2267db96d56Sopenharmony_ci        def check_order_with_hash_seed(seed):
2277db96d56Sopenharmony_ci            code = """if 1:
2287db96d56Sopenharmony_ci                import graphlib
2297db96d56Sopenharmony_ci                ts = graphlib.TopologicalSorter()
2307db96d56Sopenharmony_ci                ts.add('blech', 'bluch', 'hola')
2317db96d56Sopenharmony_ci                ts.add('abcd', 'blech', 'bluch', 'a', 'b')
2327db96d56Sopenharmony_ci                ts.add('a', 'a string', 'something', 'b')
2337db96d56Sopenharmony_ci                ts.add('bluch', 'hola', 'abcde', 'a', 'b')
2347db96d56Sopenharmony_ci                print(list(ts.static_order()))
2357db96d56Sopenharmony_ci                """
2367db96d56Sopenharmony_ci            env = os.environ.copy()
2377db96d56Sopenharmony_ci            # signal to assert_python not to do a copy
2387db96d56Sopenharmony_ci            # of os.environ on its own
2397db96d56Sopenharmony_ci            env["__cleanenv"] = True
2407db96d56Sopenharmony_ci            env["PYTHONHASHSEED"] = str(seed)
2417db96d56Sopenharmony_ci            out = assert_python_ok("-c", code, **env)
2427db96d56Sopenharmony_ci            return out
2437db96d56Sopenharmony_ci
2447db96d56Sopenharmony_ci        run1 = check_order_with_hash_seed(1234)
2457db96d56Sopenharmony_ci        run2 = check_order_with_hash_seed(31415)
2467db96d56Sopenharmony_ci
2477db96d56Sopenharmony_ci        self.assertNotEqual(run1, "")
2487db96d56Sopenharmony_ci        self.assertNotEqual(run2, "")
2497db96d56Sopenharmony_ci        self.assertEqual(run1, run2)
2507db96d56Sopenharmony_ci
2517db96d56Sopenharmony_ciif __name__ == "__main__":
2527db96d56Sopenharmony_ci    unittest.main()
253