xref: /llvm-project/clang/utils/check_cfc/test_check_cfc.py (revision dd3c26a045c081620375a878159f536758baba6e)
1#!/usr/bin/env python
2
3"""Test internal functions within check_cfc.py."""
4
5import check_cfc
6import os
7import platform
8import unittest
9
10
11class TestCheckCFC(unittest.TestCase):
12    def test_flip_dash_g(self):
13        self.assertIn("-g", check_cfc.flip_dash_g(["clang", "-c"]))
14        self.assertNotIn("-g", check_cfc.flip_dash_g(["clang", "-c", "-g"]))
15        self.assertNotIn("-g", check_cfc.flip_dash_g(["clang", "-g", "-c", "-g"]))
16
17    def test_remove_dir_from_path(self):
18        bin_path = r"/usr/bin"
19        space_path = r"/home/user/space in path"
20        superstring_path = r"/usr/bin/local"
21
22        # Test removing last thing in path
23        self.assertNotIn(bin_path, check_cfc.remove_dir_from_path(bin_path, bin_path))
24
25        # Test removing one entry and leaving others
26        # Also tests removing repeated path
27        path_var = os.pathsep.join([superstring_path, bin_path, space_path, bin_path])
28        stripped_path_var = check_cfc.remove_dir_from_path(path_var, bin_path)
29        self.assertIn(superstring_path, stripped_path_var)
30        self.assertNotIn(bin_path, stripped_path_var.split(os.pathsep))
31        self.assertIn(space_path, stripped_path_var)
32
33        # Test removing non-canonical path
34        self.assertNotIn(
35            r"/usr//bin", check_cfc.remove_dir_from_path(r"/usr//bin", bin_path)
36        )
37
38        if platform == "Windows":
39            # Windows is case insensitive so should remove a different case
40            # path
41            self.assertNotIn(
42                bin_path, check_cfc.remove_dir_from_path(path_var, r"/USR/BIN")
43            )
44        else:
45            # Case sensitive so will not remove different case path
46            self.assertIn(
47                bin_path, check_cfc.remove_dir_from_path(path_var, r"/USR/BIN")
48            )
49
50    def test_is_output_specified(self):
51        self.assertTrue(check_cfc.is_output_specified(["clang", "-o", "test.o"]))
52        self.assertTrue(check_cfc.is_output_specified(["clang", "-otest.o"]))
53        self.assertFalse(check_cfc.is_output_specified(["clang", "-gline-tables-only"]))
54        # Not specified for implied output file name
55        self.assertFalse(check_cfc.is_output_specified(["clang", "test.c"]))
56
57    def test_get_output_file(self):
58        self.assertEqual(check_cfc.get_output_file(["clang", "-o", "test.o"]), "test.o")
59        self.assertEqual(check_cfc.get_output_file(["clang", "-otest.o"]), "test.o")
60        self.assertIsNone(check_cfc.get_output_file(["clang", "-gline-tables-only"]))
61        # Can't get output file if more than one input file
62        self.assertIsNone(
63            check_cfc.get_output_file(["clang", "-c", "test.cpp", "test2.cpp"])
64        )
65        # No output file specified
66        self.assertIsNone(check_cfc.get_output_file(["clang", "-c", "test.c"]))
67
68    def test_derive_output_file(self):
69        # Test getting implicit output file
70        self.assertEqual(
71            check_cfc.derive_output_file(["clang", "-c", "test.c"]), "test.o"
72        )
73        self.assertEqual(
74            check_cfc.derive_output_file(["clang", "-c", "test.cpp"]), "test.o"
75        )
76        self.assertIsNone(check_cfc.derive_output_file(["clang", "--version"]))
77
78    def test_is_normal_compile(self):
79        self.assertTrue(
80            check_cfc.is_normal_compile(["clang", "-c", "test.cpp", "-o", "test2.o"])
81        )
82        self.assertTrue(check_cfc.is_normal_compile(["clang", "-c", "test.cpp"]))
83        # Outputting bitcode is not a normal compile
84        self.assertFalse(
85            check_cfc.is_normal_compile(["clang", "-c", "test.cpp", "-flto"])
86        )
87        self.assertFalse(
88            check_cfc.is_normal_compile(["clang", "-c", "test.cpp", "-emit-llvm"])
89        )
90        # Outputting preprocessed output or assembly is not a normal compile
91        self.assertFalse(
92            check_cfc.is_normal_compile(["clang", "-E", "test.cpp", "-o", "test.ii"])
93        )
94        self.assertFalse(
95            check_cfc.is_normal_compile(["clang", "-S", "test.cpp", "-o", "test.s"])
96        )
97        # Input of preprocessed or assembly is not a "normal compile"
98        self.assertFalse(
99            check_cfc.is_normal_compile(["clang", "-c", "test.s", "-o", "test.o"])
100        )
101        self.assertFalse(
102            check_cfc.is_normal_compile(["clang", "-c", "test.ii", "-o", "test.o"])
103        )
104        # Specifying --version and -c is not a normal compile
105        self.assertFalse(
106            check_cfc.is_normal_compile(["clang", "-c", "test.cpp", "--version"])
107        )
108        self.assertFalse(
109            check_cfc.is_normal_compile(["clang", "-c", "test.cpp", "--help"])
110        )
111        # Outputting dependency files is not a normal compile
112        self.assertFalse(check_cfc.is_normal_compile(["clang", "-c", "-M", "test.cpp"]))
113        self.assertFalse(
114            check_cfc.is_normal_compile(["clang", "-c", "-MM", "test.cpp"])
115        )
116        # Creating a dependency file as a side effect still outputs an object file
117        self.assertTrue(check_cfc.is_normal_compile(["clang", "-c", "-MD", "test.cpp"]))
118        self.assertTrue(
119            check_cfc.is_normal_compile(["clang", "-c", "-MMD", "test.cpp"])
120        )
121
122    def test_replace_output_file(self):
123        self.assertEqual(
124            check_cfc.replace_output_file(["clang", "-o", "test.o"], "testg.o"),
125            ["clang", "-o", "testg.o"],
126        )
127        self.assertEqual(
128            check_cfc.replace_output_file(["clang", "-otest.o"], "testg.o"),
129            ["clang", "-otestg.o"],
130        )
131        with self.assertRaises(Exception):
132            check_cfc.replace_output_file(["clang"], "testg.o")
133
134    def test_add_output_file(self):
135        self.assertEqual(
136            check_cfc.add_output_file(["clang"], "testg.o"), ["clang", "-o", "testg.o"]
137        )
138
139    def test_set_output_file(self):
140        # Test output not specified
141        self.assertEqual(
142            check_cfc.set_output_file(["clang"], "test.o"), ["clang", "-o", "test.o"]
143        )
144        # Test output is specified
145        self.assertEqual(
146            check_cfc.set_output_file(["clang", "-o", "test.o"], "testb.o"),
147            ["clang", "-o", "testb.o"],
148        )
149
150    def test_get_input_file(self):
151        # No input file
152        self.assertIsNone(check_cfc.get_input_file(["clang"]))
153        # Input C file
154        self.assertEqual(check_cfc.get_input_file(["clang", "test.c"]), "test.c")
155        # Input C++ file
156        self.assertEqual(check_cfc.get_input_file(["clang", "test.cpp"]), "test.cpp")
157        # Multiple input files
158        self.assertIsNone(check_cfc.get_input_file(["clang", "test.c", "test2.cpp"]))
159        self.assertIsNone(check_cfc.get_input_file(["clang", "test.c", "test2.c"]))
160        # Don't handle preprocessed files
161        self.assertIsNone(check_cfc.get_input_file(["clang", "test.i"]))
162        self.assertIsNone(check_cfc.get_input_file(["clang", "test.ii"]))
163        # Test identifying input file with quotes
164        self.assertEqual(check_cfc.get_input_file(["clang", '"test.c"']), '"test.c"')
165        self.assertEqual(check_cfc.get_input_file(["clang", "'test.c'"]), "'test.c'")
166        # Test multiple quotes
167        self.assertEqual(
168            check_cfc.get_input_file(["clang", "\"'test.c'\""]), "\"'test.c'\""
169        )
170
171    def test_set_input_file(self):
172        self.assertEqual(
173            check_cfc.set_input_file(["clang", "test.c"], "test.s"), ["clang", "test.s"]
174        )
175
176
177if __name__ == "__main__":
178    unittest.main()
179