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-exception
5
6import libear
7import libscanbuild.report as sut
8import unittest
9import os
10import os.path
11
12
13def run_bug_parse(content):
14    with libear.TemporaryDirectory() as tmpdir:
15        file_name = os.path.join(tmpdir, 'test.html')
16        with open(file_name, 'w') as handle:
17            handle.writelines(content)
18        for bug in sut.parse_bug_html(file_name):
19            return bug
20
21
22def run_crash_parse(content, preproc):
23    with libear.TemporaryDirectory() as tmpdir:
24        file_name = os.path.join(tmpdir, preproc + '.info.txt')
25        with open(file_name, 'w') as handle:
26            handle.writelines(content)
27        return sut.parse_crash(file_name)
28
29
30class ParseFileTest(unittest.TestCase):
31
32    def test_parse_bug(self):
33        content = [
34            "some header\n",
35            "<!-- BUGDESC Division by zero -->\n",
36            "<!-- BUGTYPE Division by zero -->\n",
37            "<!-- BUGCATEGORY Logic error -->\n",
38            "<!-- BUGFILE xx -->\n",
39            "<!-- BUGLINE 5 -->\n",
40            "<!-- BUGCOLUMN 22 -->\n",
41            "<!-- BUGPATHLENGTH 4 -->\n",
42            "<!-- BUGMETAEND -->\n",
43            "<!-- REPORTHEADER -->\n",
44            "some tails\n"]
45        result = run_bug_parse(content)
46        self.assertEqual(result['bug_category'], 'Logic error')
47        self.assertEqual(result['bug_path_length'], 4)
48        self.assertEqual(result['bug_line'], 5)
49        self.assertEqual(result['bug_description'], 'Division by zero')
50        self.assertEqual(result['bug_type'], 'Division by zero')
51        self.assertEqual(result['bug_file'], 'xx')
52
53    def test_parse_bug_empty(self):
54        content = []
55        result = run_bug_parse(content)
56        self.assertEqual(result['bug_category'], 'Other')
57        self.assertEqual(result['bug_path_length'], 1)
58        self.assertEqual(result['bug_line'], 0)
59
60    def test_parse_crash(self):
61        content = [
62            "/some/path/file.c\n",
63            "Some very serious Error\n",
64            "bla\n",
65            "bla-bla\n"]
66        result = run_crash_parse(content, 'file.i')
67        self.assertEqual(result['source'], content[0].rstrip())
68        self.assertEqual(result['problem'], content[1].rstrip())
69        self.assertEqual(os.path.basename(result['file']),
70                         'file.i')
71        self.assertEqual(os.path.basename(result['info']),
72                         'file.i.info.txt')
73        self.assertEqual(os.path.basename(result['stderr']),
74                         'file.i.stderr.txt')
75
76    def test_parse_real_crash(self):
77        import libscanbuild.analyze as sut2
78        import re
79        with libear.TemporaryDirectory() as tmpdir:
80            filename = os.path.join(tmpdir, 'test.c')
81            with open(filename, 'w') as handle:
82                handle.write('int main() { return 0')
83            # produce failure report
84            opts = {
85                'clang': 'clang',
86                'directory': os.getcwd(),
87                'flags': [],
88                'file': filename,
89                'output_dir': tmpdir,
90                'language': 'c',
91                'error_type': 'other_error',
92                'error_output': 'some output',
93                'exit_code': 13
94            }
95            sut2.report_failure(opts)
96            # find the info file
97            pp_file = None
98            for root, _, files in os.walk(tmpdir):
99                keys = [os.path.join(root, name) for name in files]
100                for key in keys:
101                    if re.match(r'^(.*/)+clang(.*)\.i$', key):
102                        pp_file = key
103            self.assertIsNot(pp_file, None)
104            # read the failure report back
105            result = sut.parse_crash(pp_file + '.info.txt')
106            self.assertEqual(result['source'], filename)
107            self.assertEqual(result['problem'], 'Other Error')
108            self.assertEqual(result['file'], pp_file)
109            self.assertEqual(result['info'], pp_file + '.info.txt')
110            self.assertEqual(result['stderr'], pp_file + '.stderr.txt')
111
112
113class ReportMethodTest(unittest.TestCase):
114
115    def test_chop(self):
116        self.assertEqual('file', sut.chop('/prefix', '/prefix/file'))
117        self.assertEqual('file', sut.chop('/prefix/', '/prefix/file'))
118        self.assertEqual('lib/file', sut.chop('/prefix/', '/prefix/lib/file'))
119        self.assertEqual('/prefix/file', sut.chop('', '/prefix/file'))
120
121    def test_chop_when_cwd(self):
122        self.assertEqual('../src/file', sut.chop('/cwd', '/src/file'))
123        self.assertEqual('../src/file', sut.chop('/prefix/cwd',
124                                                 '/prefix/src/file'))
125
126
127class GetPrefixFromCompilationDatabaseTest(unittest.TestCase):
128
129    def test_with_different_filenames(self):
130        self.assertEqual(
131            sut.commonprefix(['/tmp/a.c', '/tmp/b.c']), '/tmp')
132
133    def test_with_different_dirnames(self):
134        self.assertEqual(
135            sut.commonprefix(['/tmp/abs/a.c', '/tmp/ack/b.c']), '/tmp')
136
137    def test_no_common_prefix(self):
138        self.assertEqual(
139            sut.commonprefix(['/tmp/abs/a.c', '/usr/ack/b.c']), '/')
140
141    def test_with_single_file(self):
142        self.assertEqual(
143            sut.commonprefix(['/tmp/a.c']), '/tmp')
144
145    def test_empty(self):
146        self.assertEqual(
147            sut.commonprefix([]), '')
148