text: # -*- coding: utf-8 -*-
  # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  # See https://llvm.org/LICENSE.txt for license information.
  # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  
  import json
  import libear
  import libscanbuild.report as sut
  import unittest
  import os
  import os.path
  
  
  def run_bug_parse(content):
      with libear.TemporaryDirectory() as tmpdir:
          file_name = os.path.join(tmpdir, 'test.html')
          with open(file_name, 'w') as handle:
              handle.writelines(content)
          for bug in sut.parse_bug_html(file_name):
              return bug
  
  
  def run_crash_parse(content, preproc):
      with libear.TemporaryDirectory() as tmpdir:
          file_name = os.path.join(tmpdir, preproc + '.info.txt')
          with open(file_name, 'w') as handle:
              handle.writelines(content)
          return sut.parse_crash(file_name)
  
  
  class ParseFileTest(unittest.TestCase):
  
      def test_parse_bug(self):
          content = [
              "some header\n",
              "<!-- BUGDESC Division by zero -->\n",
              "<!-- BUGTYPE Division by zero -->\n",
              "<!-- BUGCATEGORY Logic error -->\n",
              "<!-- BUGFILE xx -->\n",
              "<!-- BUGLINE 5 -->\n",
              "<!-- BUGCOLUMN 22 -->\n",
              "<!-- BUGPATHLENGTH 4 -->\n",
              "<!-- BUGMETAEND -->\n",
              "<!-- REPORTHEADER -->\n",
              "some tails\n"]
          result = run_bug_parse(content)
          self.assertEqual(result['bug_category'], 'Logic error')
          self.assertEqual(result['bug_path_length'], 4)
          self.assertEqual(result['bug_line'], 5)
          self.assertEqual(result['bug_description'], 'Division by zero')
          self.assertEqual(result['bug_type'], 'Division by zero')
          self.assertEqual(result['bug_file'], 'xx')
  
      def test_parse_bug_empty(self):
          content = []
          result = run_bug_parse(content)
          self.assertEqual(result['bug_category'], 'Other')
          self.assertEqual(result['bug_path_length'], 1)
          self.assertEqual(result['bug_line'], 0)
  
      def test_parse_crash(self):
          content = [
              "/some/path/file.c\n",
              "Some very serious Error\n",
              "bla\n",
              "bla-bla\n"]
          result = run_crash_parse(content, 'file.i')
          self.assertEqual(result['source'], content[0].rstrip())
          self.assertEqual(result['problem'], content[1].rstrip())
          self.assertEqual(os.path.basename(result['file']),
                           'file.i')
          self.assertEqual(os.path.basename(result['info']),
                           'file.i.info.txt')
          self.assertEqual(os.path.basename(result['stderr']),
                           'file.i.stderr.txt')
  
      def test_parse_real_crash(self):
          import libscanbuild.analyze as sut2
          import re
          with libear.TemporaryDirectory() as tmpdir:
              filename = os.path.join(tmpdir, 'test.c')
              with open(filename, 'w') as handle:
                  handle.write('int main() { return 0')
              # produce failure report
              opts = {
                  'clang': 'clang',
                  'directory': os.getcwd(),
                  'flags': [],
                  'file': filename,
                  'output_dir': tmpdir,
                  'language': 'c',
                  'error_type': 'other_error',
                  'error_output': 'some output',
                  'exit_code': 13
              }
              sut2.report_failure(opts)
              # find the info file
              pp_file = None
              for root, _, files in os.walk(tmpdir):
                  keys = [os.path.join(root, name) for name in files]
                  for key in keys:
                      if re.match(r'^(.*/)+clang(.*)\.i$', key):
                          pp_file = key
              self.assertIsNot(pp_file, None)
              # read the failure report back
              result = sut.parse_crash(pp_file + '.info.txt')
              self.assertEqual(result['source'], filename)
              self.assertEqual(result['problem'], 'Other Error')
              self.assertEqual(result['file'], pp_file)
              self.assertEqual(result['info'], pp_file + '.info.txt')
              self.assertEqual(result['stderr'], pp_file + '.stderr.txt')
  
  
  class ReportMethodTest(unittest.TestCase):
  
      def test_chop(self):
          self.assertEqual('file', sut.chop('/prefix', '/prefix/file'))
          self.assertEqual('file', sut.chop('/prefix/', '/prefix/file'))
          self.assertEqual('lib/file', sut.chop('/prefix/', '/prefix/lib/file'))
          self.assertEqual('/prefix/file', sut.chop('', '/prefix/file'))
  
      def test_chop_when_cwd(self):
          self.assertEqual('../src/file', sut.chop('/cwd', '/src/file'))
          self.assertEqual('../src/file', sut.chop('/prefix/cwd',
                                                   '/prefix/src/file'))
  
  
  class GetPrefixFromCompilationDatabaseTest(unittest.TestCase):
  
      def test_with_different_filenames(self):
          self.assertEqual(
              sut.commonprefix(['/tmp/a.c', '/tmp/b.c']), '/tmp')
  
      def test_with_different_dirnames(self):
          self.assertEqual(
              sut.commonprefix(['/tmp/abs/a.c', '/tmp/ack/b.c']), '/tmp')
  
      def test_no_common_prefix(self):
          self.assertEqual(
              sut.commonprefix(['/tmp/abs/a.c', '/usr/ack/b.c']), '/')
  
      def test_with_single_file(self):
          self.assertEqual(
              sut.commonprefix(['/tmp/a.c']), '/tmp')
  
      def test_empty(self):
          self.assertEqual(
              sut.commonprefix([]), '')
  
  class MergeSarifTest(unittest.TestCase):
  
      def test_merging_sarif(self):
          sarif1 = {
              '$schema': 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
              'runs': [
                  {
                      'artifacts': [
                          {
                              'length': 100,
                              'location': {
                                  'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
                              },
                              'mimeType': 'text/plain',
                              'roles': [
                                  'resultFile'
                              ]
                          }
                      ],
                      'columnKind': 'unicodeCodePoints',
                      'results': [
                          {
                              'codeFlows': [
                                  {
                                      'threadFlows': [
                                          {
                                              'locations': [
                                                  {
                                                      'importance': 'important',
                                                      'location': {
                                                          'message': {
                                                              'text': 'test message 1'
                                                          },
                                                          'physicalLocation': {
                                                              'artifactLocation': {
                                                                  'index': 0,
                                                                  'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
                                                              },
                                                              'region': {
                                                                  'endColumn': 5,
                                                                  'startColumn': 1,
                                                                  'startLine': 2
                                                              }
                                                          }
                                                      }
                                                  }
                                              ]
                                          }
                                      ]
                                  }
                              ]
                          },
                          {
                              'codeFlows': [
                                  {
                                      'threadFlows': [
                                          {
                                              'locations': [
                                                  {
                                                      'importance': 'important',
                                                      'location': {
                                                          'message': {
                                                              'text': 'test message 2'
                                                          },
                                                          'physicalLocation': {
                                                              'artifactLocation': {
                                                                  'index': 0,
                                                                  'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
                                                              },
                                                              'region': {
                                                                  'endColumn': 23,
                                                                  'startColumn': 9,
                                                                  'startLine': 10
                                                              }
                                                          }
                                                      }
                                                  }
                                              ]
                                          }
                                      ]
                                  }
                              ]
                          }
                      ],
                      'tool': {
                          'driver': {
                              'fullName': 'clang static analyzer',
                              'language': 'en-US',
                              'name': 'clang',
                              'rules': [
                                  {
                                      'fullDescription': {
                                          'text': 'test rule for merge sarif test'
                                      },
                                      'helpUrl': '//clang/tools/scan-build-py/tests/unit/test_report.py',
                                      'id': 'testId',
                                      'name': 'testName'
                                  }
                              ],
                              'version': 'test clang'
                          }
                      }
                  }
              ],
              'version': '2.1.0'
          }
          sarif2 = {
              '$schema': 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
              'runs': [
                  {
                      'artifacts': [
                          {
                              'length': 1523,
                              'location': {
                                  'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
                              },
                              'mimeType': 'text/plain',
                              'roles': [
                                  'resultFile'
                              ]
                          }
                      ],
                      'columnKind': 'unicodeCodePoints',
                      'results': [
                          {
                              'codeFlows': [
                                  {
                                      'threadFlows': [
                                          {
                                              'locations': [
                                                  {
                                                      'importance': 'important',
                                                      'location': {
                                                          'message': {
                                                              'text': 'test message 3'
                                                          },
                                                          'physicalLocation': {
                                                              'artifactLocation': {
                                                                  'index': 0,
                                                                  'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
                                                              },
                                                              'region': {
                                                                  'endColumn': 99,
                                                                  'startColumn': 99,
                                                                  'startLine': 17
                                                              }
                                                          }
                                                      }
                                                  }
                                              ]
                                          }
                                      ]
                                  }
                              ]
                          },
                          {
                              'codeFlows': [
                                  {
                                      'threadFlows': [
                                          {
                                              'locations': [
                                                  {
                                                      'importance': 'important',
                                                      'location': {
                                                          'message': {
                                                              'text': 'test message 4'
                                                          },
                                                          'physicalLocation': {
                                                              'artifactLocation': {
                                                                  'index': 0,
                                                                  'uri': '//clang/tools/scan-build-py/tests/unit/test_report.py'
                                                              },
                                                              'region': {
                                                                  'endColumn': 305,
                                                                  'startColumn': 304,
                                                                  'startLine': 1
                                                              }
                                                          }
                                                      }
                                                  }
                                              ]
                                          }
                                      ]
                                  }
                              ]
                          }
                      ],
                      'tool': {
                          'driver': {
                              'fullName': 'clang static analyzer',
                              'language': 'en-US',
                              'name': 'clang',
                              'rules': [
                                  {
                                      'fullDescription': {
                                          'text': 'test rule for merge sarif test'
                                      },
                                      'helpUrl': '//clang/tools/scan-build-py/tests/unit/test_report.py',
                                      'id': 'testId',
                                      'name': 'testName'
                                  }
                              ],
                              'version': 'test clang'
                          }
                      }
                  }
              ],
              'version': '2.1.0'
          }
  
          contents = [sarif1, sarif2]
          with libear.TemporaryDirectory() as tmpdir:
              for idx, content in enumerate(contents):
                  file_name = os.path.join(tmpdir, 'results-{}.sarif'.format(idx))
                  with open(file_name, 'w') as handle:
                      json.dump(content, handle)
  
              sut.merge_sarif_files(tmpdir, sort_files=True)
  
              self.assertIn('results-merged.sarif', os.listdir(tmpdir))
              with open(os.path.join(tmpdir, 'results-merged.sarif')) as f:
                  merged = json.load(f)
                  self.assertEqual(len(merged['runs']), 2)
                  self.assertEqual(len(merged['runs'][0]['results']), 2)
                  self.assertEqual(len(merged['runs'][1]['results']), 2)
  
                  expected = sarif1
                  for run in sarif2['runs']:
                      expected['runs'].append(run)
  
                  self.assertEqual(merged, expected)
  
      def test_merge_updates_embedded_link(self):
          sarif1 = {
              'runs': [
                  {
                      'results': [
                          {
                              'codeFlows': [
                                  {
                                      'message': {
                                          'text': 'test message 1-1 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/1/results/0)'
                                      },
                                      'threadFlows': [
                                          {
                                              'message': {
                                                  'text': 'test message 1-2 [link](sarif:/runs/1/results/0)'
                                              }
                                          }
                                      ]
                                  }
                              ]
                          }
                      ]
                  },
                  {
                      'results': [
                          {
                              'codeFlows': [
                                  {
                                      'message': {
                                          'text': 'test message 2-1 [link](sarif:/runs/0/results/0)'
                                      },
                                      'threadFlows': [
                                          {
                                              'message': {
                                                  'text': 'test message 2-2 [link](sarif:/runs/0/results/0)'
                                              }
                                          }
                                      ]
                                  }
                              ]
                          }
                      ]
                  }
              ]
          }
          sarif2 = {
              'runs': [
                  {
                      'results': [
                          {
                              'codeFlows': [
                                  {
                                      'message': {
                                          'text': 'test message 3-1 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/1/results/0)'
                                      },
                                      'threadFlows': [
                                          {
                                              'message': {
                                                  'text': 'test message 3-2 [link](sarif:/runs/1/results/0)'
                                              }
                                          }
                                      ]
                                  }
                              ]
                          }
                      ],
                  },
                  {
                      'results': [
                          {
                              'codeFlows': [
                                  {
                                      'message': {
                                          'text': 'test message 4-1 [link](sarif:/runs/0/results/0)'
                                      },
                                      'threadFlows': [
                                          {
                                              'message': {
                                                  'text': 'test message 4-2 [link](sarif:/runs/0/results/0)'
                                              }
                                          }
                                      ]
                                  }
                              ]
                          }
                      ]
                  }
              ]
          }
          sarif3 = {
              'runs': [
                  {
                      'results': [
                          {
                              'codeFlows': [
                                  {
                                      'message': {
                                          'text': 'test message 5-1 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/1/results/0)'
                                      },
                                      'threadFlows': [
                                          {
                                              'message': {
                                                  'text': 'test message 5-2 [link](sarif:/runs/1/results/0)'
                                              }
                                          }
                                      ]
                                  }
                              ]
                          }
                      ],
                  },
                  {
                      'results': [
                          {
                              'codeFlows': [
                                  {
                                      'message': {
                                          'text': 'test message 6-1 [link](sarif:/runs/0/results/0)'
                                      },
                                      'threadFlows': [
                                          {
                                              'message': {
                                                  'text': 'test message 6-2 [link](sarif:/runs/0/results/0)'
                                              }
                                          }
                                      ]
                                  }
                              ]
                          }
                      ]
                  }
              ]
          }
  
          contents = [sarif1, sarif2, sarif3]
  
          with libear.TemporaryDirectory() as tmpdir:
              for idx, content in enumerate(contents):
                  file_name = os.path.join(tmpdir, 'results-{}.sarif'.format(idx))
                  with open(file_name, 'w') as handle:
                      json.dump(content, handle)
  
              sut.merge_sarif_files(tmpdir, sort_files=True)
  
              self.assertIn('results-merged.sarif', os.listdir(tmpdir))
              with open(os.path.join(tmpdir, 'results-merged.sarif')) as f:
                  merged = json.load(f)
                  self.assertEqual(len(merged['runs']), 6)
  
                  code_flows = [merged['runs'][x]['results'][0]['codeFlows'][0]['message']['text'] for x in range(6)]
                  thread_flows = [merged['runs'][x]['results'][0]['codeFlows'][0]['threadFlows'][0]['message']['text'] for x in range(6)]
  
                  # The run index should be updated for the second and third sets of runs
                  self.assertEqual(code_flows,
                      [
                          'test message 1-1 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/1/results/0)',
                          'test message 2-1 [link](sarif:/runs/0/results/0)',
                          'test message 3-1 [link](sarif:/runs/3/results/0) [link2](sarif:/runs/3/results/0)',
                          'test message 4-1 [link](sarif:/runs/2/results/0)',
                          'test message 5-1 [link](sarif:/runs/5/results/0) [link2](sarif:/runs/5/results/0)',
                          'test message 6-1 [link](sarif:/runs/4/results/0)'
                      ])
                  self.assertEquals(thread_flows,
                      [
                          'test message 1-2 [link](sarif:/runs/1/results/0)',
                          'test message 2-2 [link](sarif:/runs/0/results/0)',
                          'test message 3-2 [link](sarif:/runs/3/results/0)',
                          'test message 4-2 [link](sarif:/runs/2/results/0)',
                          'test message 5-2 [link](sarif:/runs/5/results/0)',
                          'test message 6-2 [link](sarif:/runs/4/results/0)'
                      ])
  
      def test_overflow_run_count(self):
          sarif1 = {
              'runs': [
                  {'results': [{
                      'message': {'text': 'run 1-0 [link](sarif:/runs/1/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 1-1 [link](sarif:/runs/2/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 1-2 [link](sarif:/runs/3/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 1-3 [link](sarif:/runs/4/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 1-4 [link](sarif:/runs/5/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 1-5 [link](sarif:/runs/6/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 1-6 [link](sarif:/runs/7/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 1-7 [link](sarif:/runs/8/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 1-8 [link](sarif:/runs/9/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 1-9 [link](sarif:/runs/0/results/0)'}
                  }]}
              ]
          }
          sarif2 = {
              'runs': [
                  {'results': [{
                      'message': {'text': 'run 2-0 [link](sarif:/runs/1/results/0) [link2](sarif:/runs/2/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 2-1 [link](sarif:/runs/2/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 2-2 [link](sarif:/runs/3/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 2-3 [link](sarif:/runs/4/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 2-4 [link](sarif:/runs/5/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 2-5 [link](sarif:/runs/6/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 2-6 [link](sarif:/runs/7/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 2-7 [link](sarif:/runs/8/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 2-8 [link](sarif:/runs/9/results/0)'}
                  }]},
                  {'results': [{
                      'message': {'text': 'run 2-9 [link](sarif:/runs/0/results/0)'}
                  }]}
              ]
          }
  
          contents = [sarif1, sarif2]
          with libear.TemporaryDirectory() as tmpdir:
              for idx, content in enumerate(contents):
                  file_name = os.path.join(tmpdir, 'results-{}.sarif'.format(idx))
                  with open(file_name, 'w') as handle:
                      json.dump(content, handle)
  
              sut.merge_sarif_files(tmpdir, sort_files=True)
  
              self.assertIn('results-merged.sarif', os.listdir(tmpdir))
              with open(os.path.join(tmpdir, 'results-merged.sarif')) as f:
                  merged = json.load(f)
                  self.assertEqual(len(merged['runs']), 20)
  
                  messages = [merged['runs'][x]['results'][0]['message']['text'] for x in range(20)]
                  self.assertEqual(messages,
                      [
                          'run 1-0 [link](sarif:/runs/1/results/0)',
                          'run 1-1 [link](sarif:/runs/2/results/0)',
                          'run 1-2 [link](sarif:/runs/3/results/0)',
                          'run 1-3 [link](sarif:/runs/4/results/0)',
                          'run 1-4 [link](sarif:/runs/5/results/0)',
                          'run 1-5 [link](sarif:/runs/6/results/0)',
                          'run 1-6 [link](sarif:/runs/7/results/0)',
                          'run 1-7 [link](sarif:/runs/8/results/0)',
                          'run 1-8 [link](sarif:/runs/9/results/0)',
                          'run 1-9 [link](sarif:/runs/0/results/0)',
                          'run 2-0 [link](sarif:/runs/11/results/0) [link2](sarif:/runs/12/results/0)',
                          'run 2-1 [link](sarif:/runs/12/results/0)',
                          'run 2-2 [link](sarif:/runs/13/results/0)',
                          'run 2-3 [link](sarif:/runs/14/results/0)',
                          'run 2-4 [link](sarif:/runs/15/results/0)',
                          'run 2-5 [link](sarif:/runs/16/results/0)',
                          'run 2-6 [link](sarif:/runs/17/results/0)',
                          'run 2-7 [link](sarif:/runs/18/results/0)',
                          'run 2-8 [link](sarif:/runs/19/results/0)',
                          'run 2-9 [link](sarif:/runs/10/results/0)'
                      ])
  