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
7from . import call_and_report
8import unittest
9
10import os.path
11import string
12import glob
13
14
15def prepare_cdb(name, target_dir):
16    target_file = 'build_{0}.json'.format(name)
17    this_dir, _ = os.path.split(__file__)
18    path = os.path.abspath(os.path.join(this_dir, '..', 'src'))
19    source_dir = os.path.join(path, 'compilation_database')
20    source_file = os.path.join(source_dir, target_file + '.in')
21    target_file = os.path.join(target_dir, 'compile_commands.json')
22    with open(source_file, 'r') as in_handle:
23        with open(target_file, 'w') as out_handle:
24            for line in in_handle:
25                temp = string.Template(line)
26                out_handle.write(temp.substitute(path=path))
27    return target_file
28
29
30def run_analyzer(directory, cdb, args):
31    cmd = ['analyze-build', '--cdb', cdb, '--output', directory] \
32        + args
33    return call_and_report(cmd, [])
34
35
36class OutputDirectoryTest(unittest.TestCase):
37    def test_regular_keeps_report_dir(self):
38        with libear.TemporaryDirectory() as tmpdir:
39            cdb = prepare_cdb('regular', tmpdir)
40            exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
41            self.assertTrue(os.path.isdir(reportdir))
42
43    def test_clear_deletes_report_dir(self):
44        with libear.TemporaryDirectory() as tmpdir:
45            cdb = prepare_cdb('clean', tmpdir)
46            exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
47            self.assertFalse(os.path.isdir(reportdir))
48
49    def test_clear_keeps_report_dir_when_asked(self):
50        with libear.TemporaryDirectory() as tmpdir:
51            cdb = prepare_cdb('clean', tmpdir)
52            exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--keep-empty'])
53            self.assertTrue(os.path.isdir(reportdir))
54
55
56class ExitCodeTest(unittest.TestCase):
57    def test_regular_does_not_set_exit_code(self):
58        with libear.TemporaryDirectory() as tmpdir:
59            cdb = prepare_cdb('regular', tmpdir)
60            exit_code, __ = run_analyzer(tmpdir, cdb, [])
61            self.assertFalse(exit_code)
62
63    def test_clear_does_not_set_exit_code(self):
64        with libear.TemporaryDirectory() as tmpdir:
65            cdb = prepare_cdb('clean', tmpdir)
66            exit_code, __ = run_analyzer(tmpdir, cdb, [])
67            self.assertFalse(exit_code)
68
69    def test_regular_sets_exit_code_if_asked(self):
70        with libear.TemporaryDirectory() as tmpdir:
71            cdb = prepare_cdb('regular', tmpdir)
72            exit_code, __ = run_analyzer(tmpdir, cdb, ['--status-bugs'])
73            self.assertTrue(exit_code)
74
75    def test_clear_does_not_set_exit_code_if_asked(self):
76        with libear.TemporaryDirectory() as tmpdir:
77            cdb = prepare_cdb('clean', tmpdir)
78            exit_code, __ = run_analyzer(tmpdir, cdb, ['--status-bugs'])
79            self.assertFalse(exit_code)
80
81    def test_regular_sets_exit_code_if_asked_from_plist(self):
82        with libear.TemporaryDirectory() as tmpdir:
83            cdb = prepare_cdb('regular', tmpdir)
84            exit_code, __ = run_analyzer(
85                tmpdir, cdb, ['--status-bugs', '--plist'])
86            self.assertTrue(exit_code)
87
88    def test_clear_does_not_set_exit_code_if_asked_from_plist(self):
89        with libear.TemporaryDirectory() as tmpdir:
90            cdb = prepare_cdb('clean', tmpdir)
91            exit_code, __ = run_analyzer(
92                tmpdir, cdb, ['--status-bugs', '--plist'])
93            self.assertFalse(exit_code)
94
95
96class OutputFormatTest(unittest.TestCase):
97    @staticmethod
98    def get_html_count(directory):
99        return len(glob.glob(os.path.join(directory, 'report-*.html')))
100
101    @staticmethod
102    def get_plist_count(directory):
103        return len(glob.glob(os.path.join(directory, 'report-*.plist')))
104
105    @staticmethod
106    def get_sarif_count(directory):
107        return len(glob.glob(os.path.join(directory, 'result-*.sarif')))
108
109    def test_default_only_creates_html_report(self):
110        with libear.TemporaryDirectory() as tmpdir:
111            cdb = prepare_cdb('regular', tmpdir)
112            exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
113            self.assertTrue(
114                os.path.exists(os.path.join(reportdir, 'index.html')))
115            self.assertEqual(self.get_html_count(reportdir), 2)
116            self.assertEqual(self.get_plist_count(reportdir), 0)
117            self.assertEqual(self.get_sarif_count(reportdir), 0)
118
119    def test_plist_and_html_creates_html_and_plist_reports(self):
120        with libear.TemporaryDirectory() as tmpdir:
121            cdb = prepare_cdb('regular', tmpdir)
122            exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist-html'])
123            self.assertTrue(
124                os.path.exists(os.path.join(reportdir, 'index.html')))
125            self.assertEqual(self.get_html_count(reportdir), 2)
126            self.assertEqual(self.get_plist_count(reportdir), 5)
127            self.assertEqual(self.get_sarif_count(reportdir), 0)
128
129    def test_plist_only_creates_plist_report(self):
130        with libear.TemporaryDirectory() as tmpdir:
131            cdb = prepare_cdb('regular', tmpdir)
132            exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist'])
133            self.assertFalse(
134                os.path.exists(os.path.join(reportdir, 'index.html')))
135            self.assertEqual(self.get_html_count(reportdir), 0)
136            self.assertEqual(self.get_plist_count(reportdir), 5)
137            self.assertEqual(self.get_sarif_count(reportdir), 0)
138
139    def test_sarif_only_creates_sarif_result(self):
140        with libear.TemporaryDirectory() as tmpdir:
141            cdb = prepare_cdb('regular', tmpdir)
142            exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--sarif'])
143            self.assertFalse(
144                os.path.exists(os.path.join(reportdir, 'index.html')))
145            self.assertTrue(
146                os.path.exists(os.path.join(reportdir, 'results-merged.sarif')))
147            self.assertEqual(self.get_html_count(reportdir), 0)
148            self.assertEqual(self.get_plist_count(reportdir), 0)
149            self.assertEqual(self.get_sarif_count(reportdir), 5)
150
151    def test_sarif_and_html_creates_sarif_and_html_reports(self):
152        with libear.TemporaryDirectory() as tmpdir:
153            cdb = prepare_cdb('regular', tmpdir)
154            exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--sarif-html'])
155            self.assertTrue(
156                os.path.exists(os.path.join(reportdir, 'index.html')))
157            self.assertTrue(
158                os.path.exists(os.path.join(reportdir, 'results-merged.sarif')))
159            self.assertEqual(self.get_html_count(reportdir), 2)
160            self.assertEqual(self.get_plist_count(reportdir), 0)
161            self.assertEqual(self.get_sarif_count(reportdir), 5)
162
163
164class FailureReportTest(unittest.TestCase):
165    def test_broken_creates_failure_reports(self):
166        with libear.TemporaryDirectory() as tmpdir:
167            cdb = prepare_cdb('broken', tmpdir)
168            exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
169            self.assertTrue(
170                os.path.isdir(os.path.join(reportdir, 'failures')))
171
172    def test_broken_does_not_creates_failure_reports(self):
173        with libear.TemporaryDirectory() as tmpdir:
174            cdb = prepare_cdb('broken', tmpdir)
175            exit_code, reportdir = run_analyzer(
176                tmpdir, cdb, ['--no-failure-reports'])
177            self.assertFalse(
178                os.path.isdir(os.path.join(reportdir, 'failures')))
179
180
181class TitleTest(unittest.TestCase):
182    def assertTitleEqual(self, directory, expected):
183        import re
184        patterns = [
185            re.compile(r'<title>(?P<page>.*)</title>'),
186            re.compile(r'<h1>(?P<head>.*)</h1>')
187        ]
188        result = dict()
189
190        index = os.path.join(directory, 'index.html')
191        with open(index, 'r') as handler:
192            for line in handler.readlines():
193                for regex in patterns:
194                    match = regex.match(line.strip())
195                    if match:
196                        result.update(match.groupdict())
197                        break
198        self.assertEqual(result['page'], result['head'])
199        self.assertEqual(result['page'], expected)
200
201    def test_default_title_in_report(self):
202        with libear.TemporaryDirectory() as tmpdir:
203            cdb = prepare_cdb('broken', tmpdir)
204            exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
205            self.assertTitleEqual(reportdir, 'src - analyzer results')
206
207    def test_given_title_in_report(self):
208        with libear.TemporaryDirectory() as tmpdir:
209            cdb = prepare_cdb('broken', tmpdir)
210            exit_code, reportdir = run_analyzer(
211                tmpdir, cdb, ['--html-title', 'this is the title'])
212            self.assertTitleEqual(reportdir, 'this is the title')
213