D9600: Add scan-build python implementation

llvm-svn: 257533
diff --git a/clang/tools/scan-build-py/tests/unit/test_runner.py b/clang/tools/scan-build-py/tests/unit/test_runner.py
new file mode 100644
index 0000000..ea10051
--- /dev/null
+++ b/clang/tools/scan-build-py/tests/unit/test_runner.py
@@ -0,0 +1,213 @@
+# -*- coding: utf-8 -*-
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+
+import libscanbuild.runner as sut
+from . import fixtures
+import unittest
+import re
+import os
+import os.path
+
+
+def run_analyzer(content, opts):
+    with fixtures.TempDir() as tmpdir:
+        filename = os.path.join(tmpdir, 'test.cpp')
+        with open(filename, 'w') as handle:
+            handle.write(content)
+
+        opts.update({
+            'directory': os.getcwd(),
+            'clang': 'clang',
+            'file': filename,
+            'language': 'c++',
+            'analyze': ['--analyze', '-x', 'c++', filename],
+            'output': ['-o', tmpdir]})
+        spy = fixtures.Spy()
+        result = sut.run_analyzer(opts, spy.call)
+        return (result, spy.arg)
+
+
+class RunAnalyzerTest(unittest.TestCase):
+
+    def test_run_analyzer(self):
+        content = "int div(int n, int d) { return n / d; }"
+        (result, fwds) = run_analyzer(content, dict())
+        self.assertEqual(None, fwds)
+        self.assertEqual(0, result['exit_code'])
+
+    def test_run_analyzer_crash(self):
+        content = "int div(int n, int d) { return n / d }"
+        (result, fwds) = run_analyzer(content, dict())
+        self.assertEqual(None, fwds)
+        self.assertEqual(1, result['exit_code'])
+
+    def test_run_analyzer_crash_and_forwarded(self):
+        content = "int div(int n, int d) { return n / d }"
+        (_, fwds) = run_analyzer(content, {'output_failures': True})
+        self.assertEqual('crash', fwds['error_type'])
+        self.assertEqual(1, fwds['exit_code'])
+        self.assertTrue(len(fwds['error_output']) > 0)
+
+
+class SetAnalyzerOutputTest(fixtures.TestCase):
+
+    def test_not_defined(self):
+        with fixtures.TempDir() as tmpdir:
+            opts = {'output_dir': tmpdir}
+            spy = fixtures.Spy()
+            sut.set_analyzer_output(opts, spy.call)
+            self.assertTrue(os.path.exists(spy.arg['output'][1]))
+            self.assertTrue(os.path.isdir(spy.arg['output'][1]))
+
+    def test_html(self):
+        with fixtures.TempDir() as tmpdir:
+            opts = {'output_dir': tmpdir, 'output_format': 'html'}
+            spy = fixtures.Spy()
+            sut.set_analyzer_output(opts, spy.call)
+            self.assertTrue(os.path.exists(spy.arg['output'][1]))
+            self.assertTrue(os.path.isdir(spy.arg['output'][1]))
+
+    def test_plist_html(self):
+        with fixtures.TempDir() as tmpdir:
+            opts = {'output_dir': tmpdir, 'output_format': 'plist-html'}
+            spy = fixtures.Spy()
+            sut.set_analyzer_output(opts, spy.call)
+            self.assertTrue(os.path.exists(spy.arg['output'][1]))
+            self.assertTrue(os.path.isfile(spy.arg['output'][1]))
+
+    def test_plist(self):
+        with fixtures.TempDir() as tmpdir:
+            opts = {'output_dir': tmpdir, 'output_format': 'plist'}
+            spy = fixtures.Spy()
+            sut.set_analyzer_output(opts, spy.call)
+            self.assertTrue(os.path.exists(spy.arg['output'][1]))
+            self.assertTrue(os.path.isfile(spy.arg['output'][1]))
+
+
+class ReportFailureTest(fixtures.TestCase):
+
+    def assertUnderFailures(self, path):
+        self.assertEqual('failures', os.path.basename(os.path.dirname(path)))
+
+    def test_report_failure_create_files(self):
+        with fixtures.TempDir() as tmpdir:
+            # create input file
+            filename = os.path.join(tmpdir, 'test.c')
+            with open(filename, 'w') as handle:
+                handle.write('int main() { return 0')
+            uname_msg = ' '.join(os.uname()) + os.linesep
+            error_msg = 'this is my error output'
+            # execute test
+            opts = {'directory': os.getcwd(),
+                    'clang': 'clang',
+                    'file': filename,
+                    'report': ['-fsyntax-only', '-E', filename],
+                    'language': 'c',
+                    'output_dir': tmpdir,
+                    'error_type': 'other_error',
+                    'error_output': error_msg,
+                    'exit_code': 13}
+            sut.report_failure(opts)
+            # verify the result
+            result = dict()
+            pp_file = None
+            for root, _, files in os.walk(tmpdir):
+                keys = [os.path.join(root, name) for name in files]
+                for key in keys:
+                    with open(key, 'r') as handle:
+                        result[key] = handle.readlines()
+                    if re.match(r'^(.*/)+clang(.*)\.i$', key):
+                        pp_file = key
+
+            # prepocessor file generated
+            self.assertUnderFailures(pp_file)
+            # info file generated and content dumped
+            info_file = pp_file + '.info.txt'
+            self.assertIn(info_file, result)
+            self.assertEqual('Other Error\n', result[info_file][1])
+            self.assertEqual(uname_msg, result[info_file][3])
+            # error file generated and content dumped
+            error_file = pp_file + '.stderr.txt'
+            self.assertIn(error_file, result)
+            self.assertEqual([error_msg], result[error_file])
+
+
+class AnalyzerTest(unittest.TestCase):
+
+    def test_set_language(self):
+        def test(expected, input):
+            spy = fixtures.Spy()
+            self.assertEqual(spy.success, sut.language_check(input, spy.call))
+            self.assertEqual(expected, spy.arg['language'])
+
+        l = 'language'
+        f = 'file'
+        i = 'c++'
+        test('c',   {f: 'file.c', l: 'c', i: False})
+        test('c++', {f: 'file.c', l: 'c++', i: False})
+        test('c++', {f: 'file.c', i: True})
+        test('c',   {f: 'file.c', i: False})
+        test('c++', {f: 'file.cxx', i: False})
+        test('c-cpp-output',   {f: 'file.i', i: False})
+        test('c++-cpp-output', {f: 'file.i', i: True})
+        test('c-cpp-output',   {f: 'f.i', l: 'c-cpp-output', i: True})
+
+    def test_arch_loop(self):
+        def test(input):
+            spy = fixtures.Spy()
+            sut.arch_check(input, spy.call)
+            return spy.arg
+
+        input = {'key': 'value'}
+        self.assertEqual(input, test(input))
+
+        input = {'archs_seen': ['i386']}
+        self.assertEqual({'arch': 'i386'}, test(input))
+
+        input = {'archs_seen': ['ppc']}
+        self.assertEqual(None, test(input))
+
+        input = {'archs_seen': ['i386', 'ppc']}
+        self.assertEqual({'arch': 'i386'}, test(input))
+
+        input = {'archs_seen': ['i386', 'sparc']}
+        result = test(input)
+        self.assertTrue(result == {'arch': 'i386'} or
+                        result == {'arch': 'sparc'})
+
+
+@sut.require([])
+def method_without_expecteds(opts):
+    return 0
+
+
+@sut.require(['this', 'that'])
+def method_with_expecteds(opts):
+    return 0
+
+
+@sut.require([])
+def method_exception_from_inside(opts):
+    raise Exception('here is one')
+
+
+class RequireDecoratorTest(unittest.TestCase):
+
+    def test_method_without_expecteds(self):
+        self.assertEqual(method_without_expecteds(dict()), 0)
+        self.assertEqual(method_without_expecteds({}), 0)
+        self.assertEqual(method_without_expecteds({'this': 2}), 0)
+        self.assertEqual(method_without_expecteds({'that': 3}), 0)
+
+    def test_method_with_expecteds(self):
+        self.assertRaises(KeyError, method_with_expecteds, dict())
+        self.assertRaises(KeyError, method_with_expecteds, {})
+        self.assertRaises(KeyError, method_with_expecteds, {'this': 2})
+        self.assertRaises(KeyError, method_with_expecteds, {'that': 3})
+        self.assertEqual(method_with_expecteds({'this': 0, 'that': 3}), 0)
+
+    def test_method_exception_not_caught(self):
+        self.assertRaises(Exception, method_exception_from_inside, dict())