brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.2 KiB · b9b0524 Raw
428 lines · python
1# -*- coding: utf-8 -*-2# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3# See https://llvm.org/LICENSE.txt for license information.4# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5 6import unittest7import re8import os9import os.path10import libear11import libscanbuild.analyze as sut12 13 14class ReportDirectoryTest(unittest.TestCase):15 16    # Test that successive report directory names ascend in lexicographic17    # order. This is required so that report directories from two runs of18    # scan-build can be easily matched up to compare results.19    def test_directory_name_comparison(self):20        with libear.TemporaryDirectory() as tmpdir, sut.report_directory(21            tmpdir, False, "html"22        ) as report_dir1, sut.report_directory(23            tmpdir, False, "html"24        ) as report_dir2, sut.report_directory(25            tmpdir, False, "html"26        ) as report_dir3:27            self.assertLess(report_dir1, report_dir2)28            self.assertLess(report_dir2, report_dir3)29 30 31class FilteringFlagsTest(unittest.TestCase):32    def test_language_captured(self):33        def test(flags):34            cmd = ["clang", "-c", "source.c"] + flags35            opts = sut.classify_parameters(cmd)36            return opts["language"]37 38        self.assertEqual(None, test([]))39        self.assertEqual("c", test(["-x", "c"]))40        self.assertEqual("cpp", test(["-x", "cpp"]))41 42    def test_arch(self):43        def test(flags):44            cmd = ["clang", "-c", "source.c"] + flags45            opts = sut.classify_parameters(cmd)46            return opts["arch_list"]47 48        self.assertEqual([], test([]))49        self.assertEqual(["mips"], test(["-arch", "mips"]))50        self.assertEqual(["mips", "i386"], test(["-arch", "mips", "-arch", "i386"]))51 52    def assertFlagsChanged(self, expected, flags):53        cmd = ["clang", "-c", "source.c"] + flags54        opts = sut.classify_parameters(cmd)55        self.assertEqual(expected, opts["flags"])56 57    def assertFlagsUnchanged(self, flags):58        self.assertFlagsChanged(flags, flags)59 60    def assertFlagsFiltered(self, flags):61        self.assertFlagsChanged([], flags)62 63    def test_optimalizations_pass(self):64        self.assertFlagsUnchanged(["-O"])65        self.assertFlagsUnchanged(["-O1"])66        self.assertFlagsUnchanged(["-Os"])67        self.assertFlagsUnchanged(["-O2"])68        self.assertFlagsUnchanged(["-O3"])69 70    def test_include_pass(self):71        self.assertFlagsUnchanged([])72        self.assertFlagsUnchanged(["-include", "/usr/local/include"])73        self.assertFlagsUnchanged(["-I."])74        self.assertFlagsUnchanged(["-I", "."])75        self.assertFlagsUnchanged(["-I/usr/local/include"])76        self.assertFlagsUnchanged(["-I", "/usr/local/include"])77        self.assertFlagsUnchanged(["-I/opt", "-I", "/opt/otp/include"])78        self.assertFlagsUnchanged(["-isystem", "/path"])79        self.assertFlagsUnchanged(["-isystem=/path"])80 81    def test_define_pass(self):82        self.assertFlagsUnchanged(["-DNDEBUG"])83        self.assertFlagsUnchanged(["-UNDEBUG"])84        self.assertFlagsUnchanged(["-Dvar1=val1", "-Dvar2=val2"])85        self.assertFlagsUnchanged(['-Dvar="val ues"'])86 87    def test_output_filtered(self):88        self.assertFlagsFiltered(["-o", "source.o"])89 90    def test_some_warning_filtered(self):91        self.assertFlagsFiltered(["-Wall"])92        self.assertFlagsFiltered(["-Wnoexcept"])93        self.assertFlagsFiltered(["-Wreorder", "-Wunused", "-Wundef"])94        self.assertFlagsUnchanged(["-Wno-reorder", "-Wno-unused"])95 96    def test_compile_only_flags_pass(self):97        self.assertFlagsUnchanged(["-std=C99"])98        self.assertFlagsUnchanged(["-nostdinc"])99        self.assertFlagsUnchanged(["-isystem", "/image/debian"])100        self.assertFlagsUnchanged(["-iprefix", "/usr/local"])101        self.assertFlagsUnchanged(["-iquote=me"])102        self.assertFlagsUnchanged(["-iquote", "me"])103 104    def test_compile_and_link_flags_pass(self):105        self.assertFlagsUnchanged(["-fsinged-char"])106        self.assertFlagsUnchanged(["-fPIC"])107        self.assertFlagsUnchanged(["-stdlib=libc++"])108        self.assertFlagsUnchanged(["--sysroot", "/"])109        self.assertFlagsUnchanged(["-isysroot", "/"])110 111    def test_some_flags_filtered(self):112        self.assertFlagsFiltered(["-g"])113        self.assertFlagsFiltered(["-fsyntax-only"])114        self.assertFlagsFiltered(["-save-temps"])115        self.assertFlagsFiltered(["-init", "my_init"])116        self.assertFlagsFiltered(["-sectorder", "a", "b", "c"])117 118 119class Spy(object):120    def __init__(self):121        self.arg = None122        self.success = 0123 124    def call(self, params):125        self.arg = params126        return self.success127 128 129class RunAnalyzerTest(unittest.TestCase):130    @staticmethod131    def run_analyzer(content, failures_report, output_format="plist"):132        with libear.TemporaryDirectory() as tmpdir:133            filename = os.path.join(tmpdir, "test.cpp")134            with open(filename, "w") as handle:135                handle.write(content)136 137            opts = {138                "clang": "clang",139                "directory": os.getcwd(),140                "flags": [],141                "direct_args": [],142                "file": filename,143                "output_dir": tmpdir,144                "output_format": output_format,145                "output_failures": failures_report,146            }147            spy = Spy()148            result = sut.run_analyzer(opts, spy.call)149            output_files = []150            for entry in os.listdir(tmpdir):151                output_files.append(entry)152            return (result, spy.arg, output_files)153 154    def test_run_analyzer(self):155        content = "int div(int n, int d) { return n / d; }"156        (result, fwds, _) = RunAnalyzerTest.run_analyzer(content, False)157        self.assertEqual(None, fwds)158        self.assertEqual(0, result["exit_code"])159 160    def test_run_analyzer_crash(self):161        content = "int div(int n, int d) { return n / d }"162        (result, fwds, _) = RunAnalyzerTest.run_analyzer(content, False)163        self.assertEqual(None, fwds)164        self.assertEqual(1, result["exit_code"])165 166    def test_run_analyzer_crash_and_forwarded(self):167        content = "int div(int n, int d) { return n / d }"168        (_, fwds, _) = RunAnalyzerTest.run_analyzer(content, True)169        self.assertEqual(1, fwds["exit_code"])170        self.assertTrue(len(fwds["error_output"]) > 0)171 172    def test_run_analyzer_with_sarif(self):173        content = "int div(int n, int d) { return n / d; }"174        (result, fwds, output_files) = RunAnalyzerTest.run_analyzer(175            content, False, output_format="sarif"176        )177        self.assertEqual(None, fwds)178        self.assertEqual(0, result["exit_code"])179 180        pattern = re.compile(r"^result-.+\.sarif$")181        for f in output_files:182            if re.match(pattern, f):183                return184        self.fail("no result sarif files found in output")185 186 187class ReportFailureTest(unittest.TestCase):188    def assertUnderFailures(self, path):189        self.assertEqual("failures", os.path.basename(os.path.dirname(path)))190 191    def test_report_failure_create_files(self):192        with libear.TemporaryDirectory() as tmpdir:193            # create input file194            filename = os.path.join(tmpdir, "test.c")195            with open(filename, "w") as handle:196                handle.write("int main() { return 0")197            uname_msg = " ".join(os.uname()) + os.linesep198            error_msg = "this is my error output"199            # execute test200            opts = {201                "clang": "clang",202                "directory": os.getcwd(),203                "flags": [],204                "file": filename,205                "output_dir": tmpdir,206                "language": "c",207                "error_type": "other_error",208                "error_output": error_msg,209                "exit_code": 13,210            }211            sut.report_failure(opts)212            # verify the result213            result = dict()214            pp_file = None215            for root, _, files in os.walk(tmpdir):216                keys = [os.path.join(root, name) for name in files]217                for key in keys:218                    with open(key, "r") as handle:219                        result[key] = handle.readlines()220                    if re.match(r"^(.*/)+clang(.*)\.i$", key):221                        pp_file = key222 223            # prepocessor file generated224            self.assertUnderFailures(pp_file)225            # info file generated and content dumped226            info_file = pp_file + ".info.txt"227            self.assertTrue(info_file in result)228            self.assertEqual("Other Error\n", result[info_file][1])229            self.assertEqual(uname_msg, result[info_file][3])230            # error file generated and content dumped231            error_file = pp_file + ".stderr.txt"232            self.assertTrue(error_file in result)233            self.assertEqual([error_msg], result[error_file])234 235 236class AnalyzerTest(unittest.TestCase):237    def test_nodebug_macros_appended(self):238        def test(flags):239            spy = Spy()240            opts = {"flags": flags, "force_debug": True}241            self.assertEqual(spy.success, sut.filter_debug_flags(opts, spy.call))242            return spy.arg["flags"]243 244        self.assertEqual(["-UNDEBUG"], test([]))245        self.assertEqual(["-DNDEBUG", "-UNDEBUG"], test(["-DNDEBUG"]))246        self.assertEqual(["-DSomething", "-UNDEBUG"], test(["-DSomething"]))247 248    def test_set_language_fall_through(self):249        def language(expected, input):250            spy = Spy()251            input.update({"compiler": "c", "file": "test.c"})252            self.assertEqual(spy.success, sut.language_check(input, spy.call))253            self.assertEqual(expected, spy.arg["language"])254 255        language("c", {"language": "c", "flags": []})256        language("c++", {"language": "c++", "flags": []})257 258    def test_set_language_stops_on_not_supported(self):259        spy = Spy()260        input = {"compiler": "c", "flags": [], "file": "test.java", "language": "java"}261        self.assertIsNone(sut.language_check(input, spy.call))262        self.assertIsNone(spy.arg)263 264    def test_set_language_sets_flags(self):265        def flags(expected, input):266            spy = Spy()267            input.update({"compiler": "c", "file": "test.c"})268            self.assertEqual(spy.success, sut.language_check(input, spy.call))269            self.assertEqual(expected, spy.arg["flags"])270 271        flags(["-x", "c"], {"language": "c", "flags": []})272        flags(["-x", "c++"], {"language": "c++", "flags": []})273 274    def test_set_language_from_filename(self):275        def language(expected, input):276            spy = Spy()277            input.update({"language": None, "flags": []})278            self.assertEqual(spy.success, sut.language_check(input, spy.call))279            self.assertEqual(expected, spy.arg["language"])280 281        language("c", {"file": "file.c", "compiler": "c"})282        language("c++", {"file": "file.c", "compiler": "c++"})283        language("c++", {"file": "file.cxx", "compiler": "c"})284        language("c++", {"file": "file.cxx", "compiler": "c++"})285        language("c++", {"file": "file.cpp", "compiler": "c++"})286        language("c-cpp-output", {"file": "file.i", "compiler": "c"})287        language("c++-cpp-output", {"file": "file.i", "compiler": "c++"})288 289    def test_arch_loop_sets_flags(self):290        def flags(archs):291            spy = Spy()292            input = {"flags": [], "arch_list": archs}293            sut.arch_check(input, spy.call)294            return spy.arg["flags"]295 296        self.assertEqual([], flags([]))297        self.assertEqual(["-arch", "i386"], flags(["i386"]))298        self.assertEqual(["-arch", "i386"], flags(["i386", "ppc"]))299        self.assertEqual(["-arch", "sparc"], flags(["i386", "sparc"]))300 301    def test_arch_loop_stops_on_not_supported(self):302        def stop(archs):303            spy = Spy()304            input = {"flags": [], "arch_list": archs}305            self.assertIsNone(sut.arch_check(input, spy.call))306            self.assertIsNone(spy.arg)307 308        stop(["ppc"])309        stop(["ppc64"])310 311 312@sut.require([])313def method_without_expecteds(opts):314    return 0315 316 317@sut.require(["this", "that"])318def method_with_expecteds(opts):319    return 0320 321 322@sut.require([])323def method_exception_from_inside(opts):324    raise Exception("here is one")325 326 327class RequireDecoratorTest(unittest.TestCase):328    def test_method_without_expecteds(self):329        self.assertEqual(method_without_expecteds(dict()), 0)330        self.assertEqual(method_without_expecteds({}), 0)331        self.assertEqual(method_without_expecteds({"this": 2}), 0)332        self.assertEqual(method_without_expecteds({"that": 3}), 0)333 334    def test_method_with_expecteds(self):335        self.assertRaises(KeyError, method_with_expecteds, dict())336        self.assertRaises(KeyError, method_with_expecteds, {})337        self.assertRaises(KeyError, method_with_expecteds, {"this": 2})338        self.assertRaises(KeyError, method_with_expecteds, {"that": 3})339        self.assertEqual(method_with_expecteds({"this": 0, "that": 3}), 0)340 341    def test_method_exception_not_caught(self):342        self.assertRaises(Exception, method_exception_from_inside, dict())343 344 345class PrefixWithTest(unittest.TestCase):346    def test_gives_empty_on_empty(self):347        res = sut.prefix_with(0, [])348        self.assertFalse(res)349 350    def test_interleaves_prefix(self):351        res = sut.prefix_with(0, [1, 2, 3])352        self.assertListEqual([0, 1, 0, 2, 0, 3], res)353 354 355class MergeCtuMapTest(unittest.TestCase):356    def test_no_map_gives_empty(self):357        pairs = sut.create_global_ctu_extdef_map([])358        self.assertFalse(pairs)359 360    def test_multiple_maps_merged(self):361        concat_map = [362            "c:@F@fun1#I# ast/fun1.c.ast",363            "c:@F@fun2#I# ast/fun2.c.ast",364            "c:@F@fun3#I# ast/fun3.c.ast",365        ]366        pairs = sut.create_global_ctu_extdef_map(concat_map)367        self.assertTrue(("c:@F@fun1#I#", "ast/fun1.c.ast") in pairs)368        self.assertTrue(("c:@F@fun2#I#", "ast/fun2.c.ast") in pairs)369        self.assertTrue(("c:@F@fun3#I#", "ast/fun3.c.ast") in pairs)370        self.assertEqual(3, len(pairs))371 372    def test_not_unique_func_left_out(self):373        concat_map = [374            "c:@F@fun1#I# ast/fun1.c.ast",375            "c:@F@fun2#I# ast/fun2.c.ast",376            "c:@F@fun1#I# ast/fun7.c.ast",377        ]378        pairs = sut.create_global_ctu_extdef_map(concat_map)379        self.assertFalse(("c:@F@fun1#I#", "ast/fun1.c.ast") in pairs)380        self.assertFalse(("c:@F@fun1#I#", "ast/fun7.c.ast") in pairs)381        self.assertTrue(("c:@F@fun2#I#", "ast/fun2.c.ast") in pairs)382        self.assertEqual(1, len(pairs))383 384    def test_duplicates_are_kept(self):385        concat_map = [386            "c:@F@fun1#I# ast/fun1.c.ast",387            "c:@F@fun2#I# ast/fun2.c.ast",388            "c:@F@fun1#I# ast/fun1.c.ast",389        ]390        pairs = sut.create_global_ctu_extdef_map(concat_map)391        self.assertTrue(("c:@F@fun1#I#", "ast/fun1.c.ast") in pairs)392        self.assertTrue(("c:@F@fun2#I#", "ast/fun2.c.ast") in pairs)393        self.assertEqual(2, len(pairs))394 395    def test_space_handled_in_source(self):396        concat_map = ["c:@F@fun1#I# ast/f un.c.ast"]397        pairs = sut.create_global_ctu_extdef_map(concat_map)398        self.assertTrue(("c:@F@fun1#I#", "ast/f un.c.ast") in pairs)399        self.assertEqual(1, len(pairs))400 401 402class ExtdefMapSrcToAstTest(unittest.TestCase):403    def test_empty_gives_empty(self):404        fun_ast_lst = sut.extdef_map_list_src_to_ast([])405        self.assertFalse(fun_ast_lst)406 407    def test_sources_to_asts(self):408        fun_src_lst = [409            "c:@F@f1#I# " + os.path.join(os.sep + "path", "f1.c"),410            "c:@F@f2#I# " + os.path.join(os.sep + "path", "f2.c"),411        ]412        fun_ast_lst = sut.extdef_map_list_src_to_ast(fun_src_lst)413        self.assertTrue(414            "c:@F@f1#I# " + os.path.join("ast", "path", "f1.c.ast") in fun_ast_lst415        )416        self.assertTrue(417            "c:@F@f2#I# " + os.path.join("ast", "path", "f2.c.ast") in fun_ast_lst418        )419        self.assertEqual(2, len(fun_ast_lst))420 421    def test_spaces_handled(self):422        fun_src_lst = ["c:@F@f1#I# " + os.path.join(os.sep + "path", "f 1.c")]423        fun_ast_lst = sut.extdef_map_list_src_to_ast(fun_src_lst)424        self.assertTrue(425            "c:@F@f1#I# " + os.path.join("ast", "path", "f 1.c.ast") in fun_ast_lst426        )427        self.assertEqual(1, len(fun_ast_lst))428