1# Copyright (c) Facebook, Inc. and its affiliates.
2#
3# This source code is licensed under the MIT license found in the
4# LICENSE file in the root directory of this source tree.
5
6import os
7import os.path
8
9import libcst.codegen.gen_matcher_classes as matcher_codegen
10import libcst.codegen.gen_type_mapping as type_codegen
11import libcst.codegen.gen_visitor_functions as visitor_codegen
12from libcst.codegen.generate import clean_generated_code, format_file
13from libcst.testing.utils import UnitTest
14
15
16class TestCodegenClean(UnitTest):
17    def test_codegen_clean_visitor_functions(self) -> None:
18        """
19        Verifies that codegen of visitor functions would not result in a
20        changed file. If this test fails, please run 'tox -e codegen' to
21        generate new files.
22        """
23        new_code = clean_generated_code("\n".join(visitor_codegen.generated_code))
24        new_file = os.path.join(
25            os.path.dirname(os.path.abspath(__file__)), "visitor_codegen.py.deleteme"
26        )
27        with open(new_file, "w") as fp:
28            fp.write(new_code)
29        try:
30            format_file(new_file)
31        except Exception:
32            # We failed to format, but this is probably due to invalid code that
33            # black doesn't like. This test will still fail and report to run codegen.
34            pass
35        with open(new_file, "r") as fp:
36            new_code = fp.read()
37        os.remove(new_file)
38        with open(
39            os.path.join(
40                os.path.dirname(os.path.abspath(__file__)), "../../_typed_visitor.py"
41            ),
42            "r",
43        ) as fp:
44            old_code = fp.read()
45
46        # Now that we've done simple codegen, verify that it matches.
47        self.assertTrue(
48            old_code == new_code, "libcst._typed_visitor needs new codegen!"
49        )
50
51    def test_codegen_clean_matcher_classes(self) -> None:
52        """
53        Verifies that codegen of matcher classes would not result in a
54        changed file. If this test fails, please run 'tox -e codegen' to
55        generate new files.
56        """
57        new_code = clean_generated_code("\n".join(matcher_codegen.generated_code))
58        new_file = os.path.join(
59            os.path.dirname(os.path.abspath(__file__)), "matcher_codegen.py.deleteme"
60        )
61        with open(new_file, "w") as fp:
62            fp.write(new_code)
63        try:
64            format_file(new_file)
65        except Exception:
66            # We failed to format, but this is probably due to invalid code that
67            # black doesn't like. This test will still fail and report to run codegen.
68            pass
69        with open(new_file, "r") as fp:
70            new_code = fp.read()
71        os.remove(new_file)
72        with open(
73            os.path.join(
74                os.path.dirname(os.path.abspath(__file__)), "../../matchers/__init__.py"
75            ),
76            "r",
77        ) as fp:
78            old_code = fp.read()
79
80        # Now that we've done simple codegen, verify that it matches.
81        self.assertTrue(
82            old_code == new_code, "libcst.matchers.__init__ needs new codegen!"
83        )
84
85    def test_codegen_clean_return_types(self) -> None:
86        """
87        Verifies that codegen of return types would not result in a
88        changed file. If this test fails, please run 'tox -e codegen' to
89        generate new files.
90        """
91        new_code = clean_generated_code("\n".join(type_codegen.generated_code))
92        new_file = os.path.join(
93            os.path.dirname(os.path.abspath(__file__)), "type_codegen.py.deleteme"
94        )
95        with open(new_file, "w") as fp:
96            fp.write(new_code)
97        try:
98            format_file(new_file)
99        except Exception:
100            # We failed to format, but this is probably due to invalid code that
101            # black doesn't like. This test will still fail and report to run codegen.
102            pass
103        with open(new_file, "r") as fp:
104            new_code = fp.read()
105        os.remove(new_file)
106        with open(
107            os.path.join(
108                os.path.dirname(os.path.abspath(__file__)),
109                "../../matchers/_return_types.py",
110            ),
111            "r",
112        ) as fp:
113            old_code = fp.read()
114
115        # Now that we've done simple codegen, verify that it matches.
116        self.assertTrue(
117            old_code == new_code, "libcst.matchers._return_types needs new codegen!"
118        )
119