xref: /openbsd-src/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_report.py (revision a9ac8606c53d55cee9c3a39778b249c51df111ef)
1e5dd7070Spatrick# -*- coding: utf-8 -*-
2e5dd7070Spatrick# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3e5dd7070Spatrick# See https://llvm.org/LICENSE.txt for license information.
4e5dd7070Spatrick# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5e5dd7070Spatrick
6*a9ac8606Spatrickimport json
7e5dd7070Spatrickimport libear
8e5dd7070Spatrickimport libscanbuild.report as sut
9e5dd7070Spatrickimport unittest
10e5dd7070Spatrickimport os
11e5dd7070Spatrickimport os.path
12e5dd7070Spatrick
13e5dd7070Spatrick
14e5dd7070Spatrickdef run_bug_parse(content):
15e5dd7070Spatrick    with libear.TemporaryDirectory() as tmpdir:
16e5dd7070Spatrick        file_name = os.path.join(tmpdir, 'test.html')
17e5dd7070Spatrick        with open(file_name, 'w') as handle:
18e5dd7070Spatrick            handle.writelines(content)
19e5dd7070Spatrick        for bug in sut.parse_bug_html(file_name):
20e5dd7070Spatrick            return bug
21e5dd7070Spatrick
22e5dd7070Spatrick
23e5dd7070Spatrickdef run_crash_parse(content, preproc):
24e5dd7070Spatrick    with libear.TemporaryDirectory() as tmpdir:
25e5dd7070Spatrick        file_name = os.path.join(tmpdir, preproc + '.info.txt')
26e5dd7070Spatrick        with open(file_name, 'w') as handle:
27e5dd7070Spatrick            handle.writelines(content)
28e5dd7070Spatrick        return sut.parse_crash(file_name)
29e5dd7070Spatrick
30e5dd7070Spatrick
31e5dd7070Spatrickclass ParseFileTest(unittest.TestCase):
32e5dd7070Spatrick
33e5dd7070Spatrick    def test_parse_bug(self):
34e5dd7070Spatrick        content = [
35e5dd7070Spatrick            "some header\n",
36e5dd7070Spatrick            "<!-- BUGDESC Division by zero -->\n",
37e5dd7070Spatrick            "<!-- BUGTYPE Division by zero -->\n",
38e5dd7070Spatrick            "<!-- BUGCATEGORY Logic error -->\n",
39e5dd7070Spatrick            "<!-- BUGFILE xx -->\n",
40e5dd7070Spatrick            "<!-- BUGLINE 5 -->\n",
41e5dd7070Spatrick            "<!-- BUGCOLUMN 22 -->\n",
42e5dd7070Spatrick            "<!-- BUGPATHLENGTH 4 -->\n",
43e5dd7070Spatrick            "<!-- BUGMETAEND -->\n",
44e5dd7070Spatrick            "<!-- REPORTHEADER -->\n",
45e5dd7070Spatrick            "some tails\n"]
46e5dd7070Spatrick        result = run_bug_parse(content)
47e5dd7070Spatrick        self.assertEqual(result['bug_category'], 'Logic error')
48e5dd7070Spatrick        self.assertEqual(result['bug_path_length'], 4)
49e5dd7070Spatrick        self.assertEqual(result['bug_line'], 5)
50e5dd7070Spatrick        self.assertEqual(result['bug_description'], 'Division by zero')
51e5dd7070Spatrick        self.assertEqual(result['bug_type'], 'Division by zero')
52e5dd7070Spatrick        self.assertEqual(result['bug_file'], 'xx')
53e5dd7070Spatrick
54e5dd7070Spatrick    def test_parse_bug_empty(self):
55e5dd7070Spatrick        content = []
56e5dd7070Spatrick        result = run_bug_parse(content)
57e5dd7070Spatrick        self.assertEqual(result['bug_category'], 'Other')
58e5dd7070Spatrick        self.assertEqual(result['bug_path_length'], 1)
59e5dd7070Spatrick        self.assertEqual(result['bug_line'], 0)
60e5dd7070Spatrick
61e5dd7070Spatrick    def test_parse_crash(self):
62e5dd7070Spatrick        content = [
63e5dd7070Spatrick            "/some/path/file.c\n",
64e5dd7070Spatrick            "Some very serious Error\n",
65e5dd7070Spatrick            "bla\n",
66e5dd7070Spatrick            "bla-bla\n"]
67e5dd7070Spatrick        result = run_crash_parse(content, 'file.i')
68e5dd7070Spatrick        self.assertEqual(result['source'], content[0].rstrip())
69e5dd7070Spatrick        self.assertEqual(result['problem'], content[1].rstrip())
70e5dd7070Spatrick        self.assertEqual(os.path.basename(result['file']),
71e5dd7070Spatrick                         'file.i')
72e5dd7070Spatrick        self.assertEqual(os.path.basename(result['info']),
73e5dd7070Spatrick                         'file.i.info.txt')
74e5dd7070Spatrick        self.assertEqual(os.path.basename(result['stderr']),
75e5dd7070Spatrick                         'file.i.stderr.txt')
76e5dd7070Spatrick
77e5dd7070Spatrick    def test_parse_real_crash(self):
78e5dd7070Spatrick        import libscanbuild.analyze as sut2
79e5dd7070Spatrick        import re
80e5dd7070Spatrick        with libear.TemporaryDirectory() as tmpdir:
81e5dd7070Spatrick            filename = os.path.join(tmpdir, 'test.c')
82e5dd7070Spatrick            with open(filename, 'w') as handle:
83e5dd7070Spatrick                handle.write('int main() { return 0')
84e5dd7070Spatrick            # produce failure report
85e5dd7070Spatrick            opts = {
86e5dd7070Spatrick                'clang': 'clang',
87e5dd7070Spatrick                'directory': os.getcwd(),
88e5dd7070Spatrick                'flags': [],
89e5dd7070Spatrick                'file': filename,
90e5dd7070Spatrick                'output_dir': tmpdir,
91e5dd7070Spatrick                'language': 'c',
92e5dd7070Spatrick                'error_type': 'other_error',
93e5dd7070Spatrick                'error_output': 'some output',
94e5dd7070Spatrick                'exit_code': 13
95e5dd7070Spatrick            }
96e5dd7070Spatrick            sut2.report_failure(opts)
97e5dd7070Spatrick            # find the info file
98e5dd7070Spatrick            pp_file = None
99e5dd7070Spatrick            for root, _, files in os.walk(tmpdir):
100e5dd7070Spatrick                keys = [os.path.join(root, name) for name in files]
101e5dd7070Spatrick                for key in keys:
102e5dd7070Spatrick                    if re.match(r'^(.*/)+clang(.*)\.i$', key):
103e5dd7070Spatrick                        pp_file = key
104e5dd7070Spatrick            self.assertIsNot(pp_file, None)
105e5dd7070Spatrick            # read the failure report back
106e5dd7070Spatrick            result = sut.parse_crash(pp_file + '.info.txt')
107e5dd7070Spatrick            self.assertEqual(result['source'], filename)
108e5dd7070Spatrick            self.assertEqual(result['problem'], 'Other Error')
109e5dd7070Spatrick            self.assertEqual(result['file'], pp_file)
110e5dd7070Spatrick            self.assertEqual(result['info'], pp_file + '.info.txt')
111e5dd7070Spatrick            self.assertEqual(result['stderr'], pp_file + '.stderr.txt')
112e5dd7070Spatrick
113e5dd7070Spatrick
114e5dd7070Spatrickclass ReportMethodTest(unittest.TestCase):
115e5dd7070Spatrick
116e5dd7070Spatrick    def test_chop(self):
117e5dd7070Spatrick        self.assertEqual('file', sut.chop('/prefix', '/prefix/file'))
118e5dd7070Spatrick        self.assertEqual('file', sut.chop('/prefix/', '/prefix/file'))
119e5dd7070Spatrick        self.assertEqual('lib/file', sut.chop('/prefix/', '/prefix/lib/file'))
120e5dd7070Spatrick        self.assertEqual('/prefix/file', sut.chop('', '/prefix/file'))
121e5dd7070Spatrick
122e5dd7070Spatrick    def test_chop_when_cwd(self):
123e5dd7070Spatrick        self.assertEqual('../src/file', sut.chop('/cwd', '/src/file'))
124e5dd7070Spatrick        self.assertEqual('../src/file', sut.chop('/prefix/cwd',
125e5dd7070Spatrick                                                 '/prefix/src/file'))
126e5dd7070Spatrick
127e5dd7070Spatrick
128e5dd7070Spatrickclass GetPrefixFromCompilationDatabaseTest(unittest.TestCase):
129e5dd7070Spatrick
130e5dd7070Spatrick    def test_with_different_filenames(self):
131e5dd7070Spatrick        self.assertEqual(
132e5dd7070Spatrick            sut.commonprefix(['/tmp/a.c', '/tmp/b.c']), '/tmp')
133e5dd7070Spatrick
134e5dd7070Spatrick    def test_with_different_dirnames(self):
135e5dd7070Spatrick        self.assertEqual(
136e5dd7070Spatrick            sut.commonprefix(['/tmp/abs/a.c', '/tmp/ack/b.c']), '/tmp')
137e5dd7070Spatrick
138e5dd7070Spatrick    def test_no_common_prefix(self):
139e5dd7070Spatrick        self.assertEqual(
140e5dd7070Spatrick            sut.commonprefix(['/tmp/abs/a.c', '/usr/ack/b.c']), '/')
141e5dd7070Spatrick
142e5dd7070Spatrick    def test_with_single_file(self):
143e5dd7070Spatrick        self.assertEqual(
144e5dd7070Spatrick            sut.commonprefix(['/tmp/a.c']), '/tmp')
145e5dd7070Spatrick
146e5dd7070Spatrick    def test_empty(self):
147e5dd7070Spatrick        self.assertEqual(
148e5dd7070Spatrick            sut.commonprefix([]), '')
149*a9ac8606Spatrick
150*a9ac8606Spatrickclass MergeSarifTest(unittest.TestCase):
151*a9ac8606Spatrick
152*a9ac8606Spatrick    def test_merging_sarif(self):
153*a9ac8606Spatrick        sarif1 = {
154*a9ac8606Spatrick            '$schema': 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
155*a9ac8606Spatrick            'runs': [
156*a9ac8606Spatrick                {
157*a9ac8606Spatrick                    'artifacts': [
158*a9ac8606Spatrick                        {
159*a9ac8606Spatrick                            'length': 100,
160*a9ac8606Spatrick                            'location': {
161*a9ac8606Spatrick                                'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
162*a9ac8606Spatrick                            },
163*a9ac8606Spatrick                            'mimeType': 'text/plain',
164*a9ac8606Spatrick                            'roles': [
165*a9ac8606Spatrick                                'resultFile'
166*a9ac8606Spatrick                            ]
167*a9ac8606Spatrick                        }
168*a9ac8606Spatrick                    ],
169*a9ac8606Spatrick                    'columnKind': 'unicodeCodePoints',
170*a9ac8606Spatrick                    'results': [
171*a9ac8606Spatrick                        {
172*a9ac8606Spatrick                            'codeFlows': [
173*a9ac8606Spatrick                                {
174*a9ac8606Spatrick                                    'threadFlows': [
175*a9ac8606Spatrick                                        {
176*a9ac8606Spatrick                                            'locations': [
177*a9ac8606Spatrick                                                {
178*a9ac8606Spatrick                                                    'importance': 'important',
179*a9ac8606Spatrick                                                    'location': {
180*a9ac8606Spatrick                                                        'message': {
181*a9ac8606Spatrick                                                            'text': 'test message 1'
182*a9ac8606Spatrick                                                        },
183*a9ac8606Spatrick                                                        'physicalLocation': {
184*a9ac8606Spatrick                                                            'artifactLocation': {
185*a9ac8606Spatrick                                                                'index': 0,
186*a9ac8606Spatrick                                                                'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
187*a9ac8606Spatrick                                                            },
188*a9ac8606Spatrick                                                            'region': {
189*a9ac8606Spatrick                                                                'endColumn': 5,
190*a9ac8606Spatrick                                                                'startColumn': 1,
191*a9ac8606Spatrick                                                                'startLine': 2
192*a9ac8606Spatrick                                                            }
193*a9ac8606Spatrick                                                        }
194*a9ac8606Spatrick                                                    }
195*a9ac8606Spatrick                                                }
196*a9ac8606Spatrick                                            ]
197*a9ac8606Spatrick                                        }
198*a9ac8606Spatrick                                    ]
199*a9ac8606Spatrick                                }
200*a9ac8606Spatrick                            ]
201*a9ac8606Spatrick                        },
202*a9ac8606Spatrick                        {
203*a9ac8606Spatrick                            'codeFlows': [
204*a9ac8606Spatrick                                {
205*a9ac8606Spatrick                                    'threadFlows': [
206*a9ac8606Spatrick                                        {
207*a9ac8606Spatrick                                            'locations': [
208*a9ac8606Spatrick                                                {
209*a9ac8606Spatrick                                                    'importance': 'important',
210*a9ac8606Spatrick                                                    'location': {
211*a9ac8606Spatrick                                                        'message': {
212*a9ac8606Spatrick                                                            'text': 'test message 2'
213*a9ac8606Spatrick                                                        },
214*a9ac8606Spatrick                                                        'physicalLocation': {
215*a9ac8606Spatrick                                                            'artifactLocation': {
216*a9ac8606Spatrick                                                                'index': 0,
217*a9ac8606Spatrick                                                                'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
218*a9ac8606Spatrick                                                            },
219*a9ac8606Spatrick                                                            'region': {
220*a9ac8606Spatrick                                                                'endColumn': 23,
221*a9ac8606Spatrick                                                                'startColumn': 9,
222*a9ac8606Spatrick                                                                'startLine': 10
223*a9ac8606Spatrick                                                            }
224*a9ac8606Spatrick                                                        }
225*a9ac8606Spatrick                                                    }
226*a9ac8606Spatrick                                                }
227*a9ac8606Spatrick                                            ]
228*a9ac8606Spatrick                                        }
229*a9ac8606Spatrick                                    ]
230*a9ac8606Spatrick                                }
231*a9ac8606Spatrick                            ]
232*a9ac8606Spatrick                        }
233*a9ac8606Spatrick                    ],
234*a9ac8606Spatrick                    'tool': {
235*a9ac8606Spatrick                        'driver': {
236*a9ac8606Spatrick                            'fullName': 'clang static analyzer',
237*a9ac8606Spatrick                            'language': 'en-US',
238*a9ac8606Spatrick                            'name': 'clang',
239*a9ac8606Spatrick                            'rules': [
240*a9ac8606Spatrick                                {
241*a9ac8606Spatrick                                    'fullDescription': {
242*a9ac8606Spatrick                                        'text': 'test rule for merge sarif test'
243*a9ac8606Spatrick                                    },
244*a9ac8606Spatrick                                    'helpUrl': '//clang/tools/scan-build-py/tests/unit/test_report.py',
245*a9ac8606Spatrick                                    'id': 'testId',
246*a9ac8606Spatrick                                    'name': 'testName'
247*a9ac8606Spatrick                                }
248*a9ac8606Spatrick                            ],
249*a9ac8606Spatrick                            'version': 'test clang'
250*a9ac8606Spatrick                        }
251*a9ac8606Spatrick                    }
252*a9ac8606Spatrick                }
253*a9ac8606Spatrick            ],
254*a9ac8606Spatrick            'version': '2.1.0'
255*a9ac8606Spatrick        }
256*a9ac8606Spatrick        sarif2 = {
257*a9ac8606Spatrick            '$schema': 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
258*a9ac8606Spatrick            'runs': [
259*a9ac8606Spatrick                {
260*a9ac8606Spatrick                    'artifacts': [
261*a9ac8606Spatrick                        {
262*a9ac8606Spatrick                            'length': 1523,
263*a9ac8606Spatrick                            'location': {
264*a9ac8606Spatrick                                'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
265*a9ac8606Spatrick                            },
266*a9ac8606Spatrick                            'mimeType': 'text/plain',
267*a9ac8606Spatrick                            'roles': [
268*a9ac8606Spatrick                                'resultFile'
269*a9ac8606Spatrick                            ]
270*a9ac8606Spatrick                        }
271*a9ac8606Spatrick                    ],
272*a9ac8606Spatrick                    'columnKind': 'unicodeCodePoints',
273*a9ac8606Spatrick                    'results': [
274*a9ac8606Spatrick                        {
275*a9ac8606Spatrick                            'codeFlows': [
276*a9ac8606Spatrick                                {
277*a9ac8606Spatrick                                    'threadFlows': [
278*a9ac8606Spatrick                                        {
279*a9ac8606Spatrick                                            'locations': [
280*a9ac8606Spatrick                                                {
281*a9ac8606Spatrick                                                    'importance': 'important',
282*a9ac8606Spatrick                                                    'location': {
283*a9ac8606Spatrick                                                        'message': {
284*a9ac8606Spatrick                                                            'text': 'test message 3'
285*a9ac8606Spatrick                                                        },
286*a9ac8606Spatrick                                                        'physicalLocation': {
287*a9ac8606Spatrick                                                            'artifactLocation': {
288*a9ac8606Spatrick                                                                'index': 0,
289*a9ac8606Spatrick                                                                'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
290*a9ac8606Spatrick                                                            },
291*a9ac8606Spatrick                                                            'region': {
292*a9ac8606Spatrick                                                                'endColumn': 99,
293*a9ac8606Spatrick                                                                'startColumn': 99,
294*a9ac8606Spatrick                                                                'startLine': 17
295*a9ac8606Spatrick                                                            }
296*a9ac8606Spatrick                                                        }
297*a9ac8606Spatrick                                                    }
298*a9ac8606Spatrick                                                }
299*a9ac8606Spatrick                                            ]
300*a9ac8606Spatrick                                        }
301*a9ac8606Spatrick                                    ]
302*a9ac8606Spatrick                                }
303*a9ac8606Spatrick                            ]
304*a9ac8606Spatrick                        },
305*a9ac8606Spatrick                        {
306*a9ac8606Spatrick                            'codeFlows': [
307*a9ac8606Spatrick                                {
308*a9ac8606Spatrick                                    'threadFlows': [
309*a9ac8606Spatrick                                        {
310*a9ac8606Spatrick                                            'locations': [
311*a9ac8606Spatrick                                                {
312*a9ac8606Spatrick                                                    'importance': 'important',
313*a9ac8606Spatrick                                                    'location': {
314*a9ac8606Spatrick                                                        'message': {
315*a9ac8606Spatrick                                                            'text': 'test message 4'
316*a9ac8606Spatrick                                                        },
317*a9ac8606Spatrick                                                        'physicalLocation': {
318*a9ac8606Spatrick                                                            'artifactLocation': {
319*a9ac8606Spatrick                                                                'index': 0,
320*a9ac8606Spatrick                                                                'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
321*a9ac8606Spatrick                                                            },
322*a9ac8606Spatrick                                                            'region': {
323*a9ac8606Spatrick                                                                'endColumn': 305,
324*a9ac8606Spatrick                                                                'startColumn': 304,
325*a9ac8606Spatrick                                                                'startLine': 1
326*a9ac8606Spatrick                                                            }
327*a9ac8606Spatrick                                                        }
328*a9ac8606Spatrick                                                    }
329*a9ac8606Spatrick                                                }
330*a9ac8606Spatrick                                            ]
331*a9ac8606Spatrick                                        }
332*a9ac8606Spatrick                                    ]
333*a9ac8606Spatrick                                }
334*a9ac8606Spatrick                            ]
335*a9ac8606Spatrick                        }
336*a9ac8606Spatrick                    ],
337*a9ac8606Spatrick                    'tool': {
338*a9ac8606Spatrick                        'driver': {
339*a9ac8606Spatrick                            'fullName': 'clang static analyzer',
340*a9ac8606Spatrick                            'language': 'en-US',
341*a9ac8606Spatrick                            'name': 'clang',
342*a9ac8606Spatrick                            'rules': [
343*a9ac8606Spatrick                                {
344*a9ac8606Spatrick                                    'fullDescription': {
345*a9ac8606Spatrick                                        'text': 'test rule for merge sarif test'
346*a9ac8606Spatrick                                    },
347*a9ac8606Spatrick                                    'helpUrl': '//clang/tools/scan-build-py/tests/unit/test_report.py',
348*a9ac8606Spatrick                                    'id': 'testId',
349*a9ac8606Spatrick                                    'name': 'testName'
350*a9ac8606Spatrick                                }
351*a9ac8606Spatrick                            ],
352*a9ac8606Spatrick                            'version': 'test clang'
353*a9ac8606Spatrick                        }
354*a9ac8606Spatrick                    }
355*a9ac8606Spatrick                }
356*a9ac8606Spatrick            ],
357*a9ac8606Spatrick            'version': '2.1.0'
358*a9ac8606Spatrick        }
359*a9ac8606Spatrick
360*a9ac8606Spatrick        contents = [sarif1, sarif2]
361*a9ac8606Spatrick        with libear.TemporaryDirectory() as tmpdir:
362*a9ac8606Spatrick            for idx, content in enumerate(contents):
363*a9ac8606Spatrick                file_name = os.path.join(tmpdir, 'results-{}.sarif'.format(idx))
364*a9ac8606Spatrick                with open(file_name, 'w') as handle:
365*a9ac8606Spatrick                    json.dump(content, handle)
366*a9ac8606Spatrick
367*a9ac8606Spatrick            sut.merge_sarif_files(tmpdir, sort_files=True)
368*a9ac8606Spatrick
369*a9ac8606Spatrick            self.assertIn('results-merged.sarif', os.listdir(tmpdir))
370*a9ac8606Spatrick            with open(os.path.join(tmpdir, 'results-merged.sarif')) as f:
371*a9ac8606Spatrick                merged = json.load(f)
372*a9ac8606Spatrick                self.assertEqual(len(merged['runs']), 2)
373*a9ac8606Spatrick                self.assertEqual(len(merged['runs'][0]['results']), 2)
374*a9ac8606Spatrick                self.assertEqual(len(merged['runs'][1]['results']), 2)
375*a9ac8606Spatrick
376*a9ac8606Spatrick                expected = sarif1
377*a9ac8606Spatrick                for run in sarif2['runs']:
378*a9ac8606Spatrick                    expected['runs'].append(run)
379*a9ac8606Spatrick
380*a9ac8606Spatrick                self.assertEqual(merged, expected)
381*a9ac8606Spatrick
382*a9ac8606Spatrick    def test_merge_updates_embedded_link(self):
383*a9ac8606Spatrick        sarif1 = {
384*a9ac8606Spatrick            'runs': [
385*a9ac8606Spatrick                {
386*a9ac8606Spatrick                    'results': [
387*a9ac8606Spatrick                        {
388*a9ac8606Spatrick                            'codeFlows': [
389*a9ac8606Spatrick                                {
390*a9ac8606Spatrick                                    'message': {
391*a9ac8606Spatrick                                        'text': 'test message 1-1 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/1/results/0)'
392*a9ac8606Spatrick                                    },
393*a9ac8606Spatrick                                    'threadFlows': [
394*a9ac8606Spatrick                                        {
395*a9ac8606Spatrick                                            'message': {
396*a9ac8606Spatrick                                                'text': 'test message 1-2 [link](sarif:/runs/1/results/0)'
397*a9ac8606Spatrick                                            }
398*a9ac8606Spatrick                                        }
399*a9ac8606Spatrick                                    ]
400*a9ac8606Spatrick                                }
401*a9ac8606Spatrick                            ]
402*a9ac8606Spatrick                        }
403*a9ac8606Spatrick                    ]
404*a9ac8606Spatrick                },
405*a9ac8606Spatrick                {
406*a9ac8606Spatrick                    'results': [
407*a9ac8606Spatrick                        {
408*a9ac8606Spatrick                            'codeFlows': [
409*a9ac8606Spatrick                                {
410*a9ac8606Spatrick                                    'message': {
411*a9ac8606Spatrick                                        'text': 'test message 2-1 [link](sarif:/runs/0/results/0)'
412*a9ac8606Spatrick                                    },
413*a9ac8606Spatrick                                    'threadFlows': [
414*a9ac8606Spatrick                                        {
415*a9ac8606Spatrick                                            'message': {
416*a9ac8606Spatrick                                                'text': 'test message 2-2 [link](sarif:/runs/0/results/0)'
417*a9ac8606Spatrick                                            }
418*a9ac8606Spatrick                                        }
419*a9ac8606Spatrick                                    ]
420*a9ac8606Spatrick                                }
421*a9ac8606Spatrick                            ]
422*a9ac8606Spatrick                        }
423*a9ac8606Spatrick                    ]
424*a9ac8606Spatrick                }
425*a9ac8606Spatrick            ]
426*a9ac8606Spatrick        }
427*a9ac8606Spatrick        sarif2 = {
428*a9ac8606Spatrick            'runs': [
429*a9ac8606Spatrick                {
430*a9ac8606Spatrick                    'results': [
431*a9ac8606Spatrick                        {
432*a9ac8606Spatrick                            'codeFlows': [
433*a9ac8606Spatrick                                {
434*a9ac8606Spatrick                                    'message': {
435*a9ac8606Spatrick                                        'text': 'test message 3-1 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/1/results/0)'
436*a9ac8606Spatrick                                    },
437*a9ac8606Spatrick                                    'threadFlows': [
438*a9ac8606Spatrick                                        {
439*a9ac8606Spatrick                                            'message': {
440*a9ac8606Spatrick                                                'text': 'test message 3-2 [link](sarif:/runs/1/results/0)'
441*a9ac8606Spatrick                                            }
442*a9ac8606Spatrick                                        }
443*a9ac8606Spatrick                                    ]
444*a9ac8606Spatrick                                }
445*a9ac8606Spatrick                            ]
446*a9ac8606Spatrick                        }
447*a9ac8606Spatrick                    ],
448*a9ac8606Spatrick                },
449*a9ac8606Spatrick                {
450*a9ac8606Spatrick                    'results': [
451*a9ac8606Spatrick                        {
452*a9ac8606Spatrick                            'codeFlows': [
453*a9ac8606Spatrick                                {
454*a9ac8606Spatrick                                    'message': {
455*a9ac8606Spatrick                                        'text': 'test message 4-1 [link](sarif:/runs/0/results/0)'
456*a9ac8606Spatrick                                    },
457*a9ac8606Spatrick                                    'threadFlows': [
458*a9ac8606Spatrick                                        {
459*a9ac8606Spatrick                                            'message': {
460*a9ac8606Spatrick                                                'text': 'test message 4-2 [link](sarif:/runs/0/results/0)'
461*a9ac8606Spatrick                                            }
462*a9ac8606Spatrick                                        }
463*a9ac8606Spatrick                                    ]
464*a9ac8606Spatrick                                }
465*a9ac8606Spatrick                            ]
466*a9ac8606Spatrick                        }
467*a9ac8606Spatrick                    ]
468*a9ac8606Spatrick                }
469*a9ac8606Spatrick            ]
470*a9ac8606Spatrick        }
471*a9ac8606Spatrick        sarif3 = {
472*a9ac8606Spatrick            'runs': [
473*a9ac8606Spatrick                {
474*a9ac8606Spatrick                    'results': [
475*a9ac8606Spatrick                        {
476*a9ac8606Spatrick                            'codeFlows': [
477*a9ac8606Spatrick                                {
478*a9ac8606Spatrick                                    'message': {
479*a9ac8606Spatrick                                        'text': 'test message 5-1 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/1/results/0)'
480*a9ac8606Spatrick                                    },
481*a9ac8606Spatrick                                    'threadFlows': [
482*a9ac8606Spatrick                                        {
483*a9ac8606Spatrick                                            'message': {
484*a9ac8606Spatrick                                                'text': 'test message 5-2 [link](sarif:/runs/1/results/0)'
485*a9ac8606Spatrick                                            }
486*a9ac8606Spatrick                                        }
487*a9ac8606Spatrick                                    ]
488*a9ac8606Spatrick                                }
489*a9ac8606Spatrick                            ]
490*a9ac8606Spatrick                        }
491*a9ac8606Spatrick                    ],
492*a9ac8606Spatrick                },
493*a9ac8606Spatrick                {
494*a9ac8606Spatrick                    'results': [
495*a9ac8606Spatrick                        {
496*a9ac8606Spatrick                            'codeFlows': [
497*a9ac8606Spatrick                                {
498*a9ac8606Spatrick                                    'message': {
499*a9ac8606Spatrick                                        'text': 'test message 6-1 [link](sarif:/runs/0/results/0)'
500*a9ac8606Spatrick                                    },
501*a9ac8606Spatrick                                    'threadFlows': [
502*a9ac8606Spatrick                                        {
503*a9ac8606Spatrick                                            'message': {
504*a9ac8606Spatrick                                                'text': 'test message 6-2 [link](sarif:/runs/0/results/0)'
505*a9ac8606Spatrick                                            }
506*a9ac8606Spatrick                                        }
507*a9ac8606Spatrick                                    ]
508*a9ac8606Spatrick                                }
509*a9ac8606Spatrick                            ]
510*a9ac8606Spatrick                        }
511*a9ac8606Spatrick                    ]
512*a9ac8606Spatrick                }
513*a9ac8606Spatrick            ]
514*a9ac8606Spatrick        }
515*a9ac8606Spatrick
516*a9ac8606Spatrick        contents = [sarif1, sarif2, sarif3]
517*a9ac8606Spatrick
518*a9ac8606Spatrick        with libear.TemporaryDirectory() as tmpdir:
519*a9ac8606Spatrick            for idx, content in enumerate(contents):
520*a9ac8606Spatrick                file_name = os.path.join(tmpdir, 'results-{}.sarif'.format(idx))
521*a9ac8606Spatrick                with open(file_name, 'w') as handle:
522*a9ac8606Spatrick                    json.dump(content, handle)
523*a9ac8606Spatrick
524*a9ac8606Spatrick            sut.merge_sarif_files(tmpdir, sort_files=True)
525*a9ac8606Spatrick
526*a9ac8606Spatrick            self.assertIn('results-merged.sarif', os.listdir(tmpdir))
527*a9ac8606Spatrick            with open(os.path.join(tmpdir, 'results-merged.sarif')) as f:
528*a9ac8606Spatrick                merged = json.load(f)
529*a9ac8606Spatrick                self.assertEqual(len(merged['runs']), 6)
530*a9ac8606Spatrick
531*a9ac8606Spatrick                code_flows = [merged['runs'][x]['results'][0]['codeFlows'][0]['message']['text'] for x in range(6)]
532*a9ac8606Spatrick                thread_flows = [merged['runs'][x]['results'][0]['codeFlows'][0]['threadFlows'][0]['message']['text'] for x in range(6)]
533*a9ac8606Spatrick
534*a9ac8606Spatrick                # The run index should be updated for the second and third sets of runs
535*a9ac8606Spatrick                self.assertEqual(code_flows,
536*a9ac8606Spatrick                    [
537*a9ac8606Spatrick                        'test message 1-1 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/1/results/0)',
538*a9ac8606Spatrick                        'test message 2-1 [link](sarif:/runs/0/results/0)',
539*a9ac8606Spatrick                        'test message 3-1 [link](sarif:/runs/3/results/0) [link2](sarif:/runs/3/results/0)',
540*a9ac8606Spatrick                        'test message 4-1 [link](sarif:/runs/2/results/0)',
541*a9ac8606Spatrick                        'test message 5-1 [link](sarif:/runs/5/results/0) [link2](sarif:/runs/5/results/0)',
542*a9ac8606Spatrick                        'test message 6-1 [link](sarif:/runs/4/results/0)'
543*a9ac8606Spatrick                    ])
544*a9ac8606Spatrick                self.assertEquals(thread_flows,
545*a9ac8606Spatrick                    [
546*a9ac8606Spatrick                        'test message 1-2 [link](sarif:/runs/1/results/0)',
547*a9ac8606Spatrick                        'test message 2-2 [link](sarif:/runs/0/results/0)',
548*a9ac8606Spatrick                        'test message 3-2 [link](sarif:/runs/3/results/0)',
549*a9ac8606Spatrick                        'test message 4-2 [link](sarif:/runs/2/results/0)',
550*a9ac8606Spatrick                        'test message 5-2 [link](sarif:/runs/5/results/0)',
551*a9ac8606Spatrick                        'test message 6-2 [link](sarif:/runs/4/results/0)'
552*a9ac8606Spatrick                    ])
553*a9ac8606Spatrick
554*a9ac8606Spatrick    def test_overflow_run_count(self):
555*a9ac8606Spatrick        sarif1 = {
556*a9ac8606Spatrick            'runs': [
557*a9ac8606Spatrick                {'results': [{
558*a9ac8606Spatrick                    'message': {'text': 'run 1-0 [link](sarif:/runs/1/results/0)'}
559*a9ac8606Spatrick                }]},
560*a9ac8606Spatrick                {'results': [{
561*a9ac8606Spatrick                    'message': {'text': 'run 1-1 [link](sarif:/runs/2/results/0)'}
562*a9ac8606Spatrick                }]},
563*a9ac8606Spatrick                {'results': [{
564*a9ac8606Spatrick                    'message': {'text': 'run 1-2 [link](sarif:/runs/3/results/0)'}
565*a9ac8606Spatrick                }]},
566*a9ac8606Spatrick                {'results': [{
567*a9ac8606Spatrick                    'message': {'text': 'run 1-3 [link](sarif:/runs/4/results/0)'}
568*a9ac8606Spatrick                }]},
569*a9ac8606Spatrick                {'results': [{
570*a9ac8606Spatrick                    'message': {'text': 'run 1-4 [link](sarif:/runs/5/results/0)'}
571*a9ac8606Spatrick                }]},
572*a9ac8606Spatrick                {'results': [{
573*a9ac8606Spatrick                    'message': {'text': 'run 1-5 [link](sarif:/runs/6/results/0)'}
574*a9ac8606Spatrick                }]},
575*a9ac8606Spatrick                {'results': [{
576*a9ac8606Spatrick                    'message': {'text': 'run 1-6 [link](sarif:/runs/7/results/0)'}
577*a9ac8606Spatrick                }]},
578*a9ac8606Spatrick                {'results': [{
579*a9ac8606Spatrick                    'message': {'text': 'run 1-7 [link](sarif:/runs/8/results/0)'}
580*a9ac8606Spatrick                }]},
581*a9ac8606Spatrick                {'results': [{
582*a9ac8606Spatrick                    'message': {'text': 'run 1-8 [link](sarif:/runs/9/results/0)'}
583*a9ac8606Spatrick                }]},
584*a9ac8606Spatrick                {'results': [{
585*a9ac8606Spatrick                    'message': {'text': 'run 1-9 [link](sarif:/runs/0/results/0)'}
586*a9ac8606Spatrick                }]}
587*a9ac8606Spatrick            ]
588*a9ac8606Spatrick        }
589*a9ac8606Spatrick        sarif2 = {
590*a9ac8606Spatrick            'runs': [
591*a9ac8606Spatrick                {'results': [{
592*a9ac8606Spatrick                    'message': {'text': 'run 2-0 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/2/results/0)'}
593*a9ac8606Spatrick                }]},
594*a9ac8606Spatrick                {'results': [{
595*a9ac8606Spatrick                    'message': {'text': 'run 2-1 [link](sarif:/runs/2/results/0)'}
596*a9ac8606Spatrick                }]},
597*a9ac8606Spatrick                {'results': [{
598*a9ac8606Spatrick                    'message': {'text': 'run 2-2 [link](sarif:/runs/3/results/0)'}
599*a9ac8606Spatrick                }]},
600*a9ac8606Spatrick                {'results': [{
601*a9ac8606Spatrick                    'message': {'text': 'run 2-3 [link](sarif:/runs/4/results/0)'}
602*a9ac8606Spatrick                }]},
603*a9ac8606Spatrick                {'results': [{
604*a9ac8606Spatrick                    'message': {'text': 'run 2-4 [link](sarif:/runs/5/results/0)'}
605*a9ac8606Spatrick                }]},
606*a9ac8606Spatrick                {'results': [{
607*a9ac8606Spatrick                    'message': {'text': 'run 2-5 [link](sarif:/runs/6/results/0)'}
608*a9ac8606Spatrick                }]},
609*a9ac8606Spatrick                {'results': [{
610*a9ac8606Spatrick                    'message': {'text': 'run 2-6 [link](sarif:/runs/7/results/0)'}
611*a9ac8606Spatrick                }]},
612*a9ac8606Spatrick                {'results': [{
613*a9ac8606Spatrick                    'message': {'text': 'run 2-7 [link](sarif:/runs/8/results/0)'}
614*a9ac8606Spatrick                }]},
615*a9ac8606Spatrick                {'results': [{
616*a9ac8606Spatrick                    'message': {'text': 'run 2-8 [link](sarif:/runs/9/results/0)'}
617*a9ac8606Spatrick                }]},
618*a9ac8606Spatrick                {'results': [{
619*a9ac8606Spatrick                    'message': {'text': 'run 2-9 [link](sarif:/runs/0/results/0)'}
620*a9ac8606Spatrick                }]}
621*a9ac8606Spatrick            ]
622*a9ac8606Spatrick        }
623*a9ac8606Spatrick
624*a9ac8606Spatrick        contents = [sarif1, sarif2]
625*a9ac8606Spatrick        with libear.TemporaryDirectory() as tmpdir:
626*a9ac8606Spatrick            for idx, content in enumerate(contents):
627*a9ac8606Spatrick                file_name = os.path.join(tmpdir, 'results-{}.sarif'.format(idx))
628*a9ac8606Spatrick                with open(file_name, 'w') as handle:
629*a9ac8606Spatrick                    json.dump(content, handle)
630*a9ac8606Spatrick
631*a9ac8606Spatrick            sut.merge_sarif_files(tmpdir, sort_files=True)
632*a9ac8606Spatrick
633*a9ac8606Spatrick            self.assertIn('results-merged.sarif', os.listdir(tmpdir))
634*a9ac8606Spatrick            with open(os.path.join(tmpdir, 'results-merged.sarif')) as f:
635*a9ac8606Spatrick                merged = json.load(f)
636*a9ac8606Spatrick                self.assertEqual(len(merged['runs']), 20)
637*a9ac8606Spatrick
638*a9ac8606Spatrick                messages = [merged['runs'][x]['results'][0]['message']['text'] for x in range(20)]
639*a9ac8606Spatrick                self.assertEqual(messages,
640*a9ac8606Spatrick                    [
641*a9ac8606Spatrick                        'run 1-0 [link](sarif:/runs/1/results/0)',
642*a9ac8606Spatrick                        'run 1-1 [link](sarif:/runs/2/results/0)',
643*a9ac8606Spatrick                        'run 1-2 [link](sarif:/runs/3/results/0)',
644*a9ac8606Spatrick                        'run 1-3 [link](sarif:/runs/4/results/0)',
645*a9ac8606Spatrick                        'run 1-4 [link](sarif:/runs/5/results/0)',
646*a9ac8606Spatrick                        'run 1-5 [link](sarif:/runs/6/results/0)',
647*a9ac8606Spatrick                        'run 1-6 [link](sarif:/runs/7/results/0)',
648*a9ac8606Spatrick                        'run 1-7 [link](sarif:/runs/8/results/0)',
649*a9ac8606Spatrick                        'run 1-8 [link](sarif:/runs/9/results/0)',
650*a9ac8606Spatrick                        'run 1-9 [link](sarif:/runs/0/results/0)',
651*a9ac8606Spatrick                        'run 2-0 [link](sarif:/runs/11/results/0) [link2](sarif:/runs/12/results/0)',
652*a9ac8606Spatrick                        'run 2-1 [link](sarif:/runs/12/results/0)',
653*a9ac8606Spatrick                        'run 2-2 [link](sarif:/runs/13/results/0)',
654*a9ac8606Spatrick                        'run 2-3 [link](sarif:/runs/14/results/0)',
655*a9ac8606Spatrick                        'run 2-4 [link](sarif:/runs/15/results/0)',
656*a9ac8606Spatrick                        'run 2-5 [link](sarif:/runs/16/results/0)',
657*a9ac8606Spatrick                        'run 2-6 [link](sarif:/runs/17/results/0)',
658*a9ac8606Spatrick                        'run 2-7 [link](sarif:/runs/18/results/0)',
659*a9ac8606Spatrick                        'run 2-8 [link](sarif:/runs/19/results/0)',
660*a9ac8606Spatrick                        'run 2-9 [link](sarif:/runs/10/results/0)'
661*a9ac8606Spatrick                    ])
662