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