r266726 - D17487: [analyzer][scan-build-py] flag filter modification for compilation database creation

Laszlo Nagy via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 19 05:03:06 PDT 2016


Author: rizsotto
Date: Tue Apr 19 07:03:03 2016
New Revision: 266726

URL: http://llvm.org/viewvc/llvm-project?rev=266726&view=rev
Log:
D17487: [analyzer][scan-build-py] flag filter modification for compilation database creation

Added:
    cfe/trunk/tools/scan-build-py/libscanbuild/compilation.py
    cfe/trunk/tools/scan-build-py/tests/functional/src/build/Makefile
    cfe/trunk/tools/scan-build-py/tests/unit/test_compilation.py
    cfe/trunk/tools/scan-build-py/tests/unit/test_libear.py
Removed:
    cfe/trunk/tools/scan-build-py/libscanbuild/command.py
    cfe/trunk/tools/scan-build-py/tests/unit/fixtures.py
    cfe/trunk/tools/scan-build-py/tests/unit/test_command.py
Modified:
    cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py
    cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py
    cfe/trunk/tools/scan-build-py/libscanbuild/runner.py
    cfe/trunk/tools/scan-build-py/tests/functional/cases/test_create_cdb.py
    cfe/trunk/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py
    cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cdb.py
    cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cmd.py
    cfe/trunk/tools/scan-build-py/tests/unit/__init__.py
    cfe/trunk/tools/scan-build-py/tests/unit/test_analyze.py
    cfe/trunk/tools/scan-build-py/tests/unit/test_clang.py
    cfe/trunk/tools/scan-build-py/tests/unit/test_intercept.py
    cfe/trunk/tools/scan-build-py/tests/unit/test_report.py
    cfe/trunk/tools/scan-build-py/tests/unit/test_runner.py

Modified: cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py (original)
+++ cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py Tue Apr 19 07:03:03 2016
@@ -25,8 +25,7 @@ from libscanbuild.runner import run
 from libscanbuild.intercept import capture
 from libscanbuild.report import report_directory, document
 from libscanbuild.clang import get_checkers
-from libscanbuild.runner import action_check
-from libscanbuild.command import classify_parameters, classify_source
+from libscanbuild.compilation import split_command
 
 __all__ = ['analyze_build_main', 'analyze_build_wrapper']
 
@@ -107,7 +106,7 @@ def run_analyzer(args, output_dir):
         'output_format': args.output_format,
         'output_failures': args.output_failures,
         'direct_args': analyzer_params(args),
-        'force_analyze_debug_code' : args.force_analyze_debug_code
+        'force_debug': args.force_debug
     }
 
     logging.debug('run analyzer against compilation database')
@@ -140,8 +139,7 @@ def setup_environment(args, destination,
         'ANALYZE_BUILD_REPORT_FORMAT': args.output_format,
         'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '',
         'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)),
-        'ANALYZE_BUILD_FORCE_ANALYZE_DEBUG_CODE'
-            : 'yes' if args.force_analyze_debug_code else ''
+        'ANALYZE_BUILD_FORCE_DEBUG': 'yes' if args.force_debug else ''
     })
     return environment
 
@@ -163,32 +161,34 @@ def analyze_build_wrapper(cplusplus):
         return result
     # ... and run the analyzer if all went well.
     try:
+        # check is it a compilation
+        compilation = split_command(sys.argv)
+        if compilation is None:
+            return result
         # collect the needed parameters from environment, crash when missing
-        consts = {
+        parameters = {
             'clang': os.getenv('ANALYZE_BUILD_CLANG'),
             'output_dir': os.getenv('ANALYZE_BUILD_REPORT_DIR'),
             'output_format': os.getenv('ANALYZE_BUILD_REPORT_FORMAT'),
             'output_failures': os.getenv('ANALYZE_BUILD_REPORT_FAILURES'),
             'direct_args': os.getenv('ANALYZE_BUILD_PARAMETERS',
                                      '').split(' '),
-            'force_analyze_debug_code':
-                os.getenv('ANALYZE_BUILD_FORCE_ANALYZE_DEBUG_CODE'),
+            'force_debug': os.getenv('ANALYZE_BUILD_FORCE_DEBUG'),
             'directory': os.getcwd(),
+            'command': [sys.argv[0], '-c'] + compilation.flags
         }
-        # get relevant parameters from command line arguments
-        args = classify_parameters(sys.argv)
-        filenames = args.pop('files', [])
-        for filename in (name for name in filenames if classify_source(name)):
-            parameters = dict(args, file=filename, **consts)
+        # call static analyzer against the compilation
+        for source in compilation.files:
+            parameters.update({'file': source})
             logging.debug('analyzer parameters %s', parameters)
-            current = action_check(parameters)
+            current = run(parameters)
             # display error message from the static analyzer
             if current is not None:
                 for line in current['error_output']:
                     logging.info(line.rstrip())
     except Exception:
         logging.exception("run analyzer inside compiler wrapper failed.")
-    return 0
+    return result
 
 
 def analyzer_params(args):
@@ -208,8 +208,8 @@ def analyzer_params(args):
     if args.store_model:
         result.append('-analyzer-store={0}'.format(args.store_model))
     if args.constraints_model:
-        result.append(
-            '-analyzer-constraints={0}'.format(args.constraints_model))
+        result.append('-analyzer-constraints={0}'.format(
+            args.constraints_model))
     if args.internal_stats:
         result.append('-analyzer-stats')
     if args.analyze_headers:
@@ -457,11 +457,10 @@ def create_parser(from_build_command):
                 the compilation database.""")
     advanced.add_argument(
         '--force-analyze-debug-code',
-        dest='force_analyze_debug_code',
+        dest='force_debug',
         action='store_true',
         help="""Tells analyzer to enable assertions in code even if they were
-                disabled during compilation, enabling more precise
-                results.""")
+                disabled during compilation, enabling more precise results.""")
 
     plugins = parser.add_argument_group('checker options')
     plugins.add_argument(

Removed: cfe/trunk/tools/scan-build-py/libscanbuild/command.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/libscanbuild/command.py?rev=266725&view=auto
==============================================================================
--- cfe/trunk/tools/scan-build-py/libscanbuild/command.py (original)
+++ cfe/trunk/tools/scan-build-py/libscanbuild/command.py (removed)
@@ -1,133 +0,0 @@
-# -*- coding: utf-8 -*-
-#                     The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-""" This module is responsible for to parse a compiler invocation. """
-
-import re
-import os
-
-__all__ = ['Action', 'classify_parameters', 'classify_source']
-
-
-class Action(object):
-    """ Enumeration class for compiler action. """
-
-    Link, Compile, Ignored = range(3)
-
-
-def classify_parameters(command):
-    """ Parses the command line arguments of the given invocation. """
-
-    # result value of this method.
-    # some value are preset, some will be set only when found.
-    result = {
-        'action': Action.Link,
-        'files': [],
-        'output': None,
-        'compile_options': [],
-        'c++': is_cplusplus_compiler(command[0])
-        # archs_seen
-        # language
-    }
-
-    # data structure to ignore compiler parameters.
-    # key: parameter name, value: number of parameters to ignore afterwards.
-    ignored = {
-        '-g': 0,
-        '-fsyntax-only': 0,
-        '-save-temps': 0,
-        '-install_name': 1,
-        '-exported_symbols_list': 1,
-        '-current_version': 1,
-        '-compatibility_version': 1,
-        '-init': 1,
-        '-e': 1,
-        '-seg1addr': 1,
-        '-bundle_loader': 1,
-        '-multiply_defined': 1,
-        '-sectorder': 3,
-        '--param': 1,
-        '--serialize-diagnostics': 1
-    }
-
-    args = iter(command[1:])
-    for arg in args:
-        # compiler action parameters are the most important ones...
-        if arg in {'-E', '-S', '-cc1', '-M', '-MM', '-###'}:
-            result.update({'action': Action.Ignored})
-        elif arg == '-c':
-            result.update({'action': max(result['action'], Action.Compile)})
-        # arch flags are taken...
-        elif arg == '-arch':
-            archs = result.get('archs_seen', [])
-            result.update({'archs_seen': archs + [next(args)]})
-        # explicit language option taken...
-        elif arg == '-x':
-            result.update({'language': next(args)})
-        # output flag taken...
-        elif arg == '-o':
-            result.update({'output': next(args)})
-        # warning disable options are taken...
-        elif re.match(r'^-Wno-', arg):
-            result['compile_options'].append(arg)
-        # warning options are ignored...
-        elif re.match(r'^-[mW].+', arg):
-            pass
-        # some preprocessor parameters are ignored...
-        elif arg in {'-MD', '-MMD', '-MG', '-MP'}:
-            pass
-        elif arg in {'-MF', '-MT', '-MQ'}:
-            next(args)
-        # linker options are ignored...
-        elif arg in {'-static', '-shared', '-s', '-rdynamic'} or \
-                re.match(r'^-[lL].+', arg):
-            pass
-        elif arg in {'-l', '-L', '-u', '-z', '-T', '-Xlinker'}:
-            next(args)
-        # some other options are ignored...
-        elif arg in ignored.keys():
-            for _ in range(ignored[arg]):
-                next(args)
-        # parameters which looks source file are taken...
-        elif re.match(r'^[^-].+', arg) and classify_source(arg):
-            result['files'].append(arg)
-        # and consider everything else as compile option.
-        else:
-            result['compile_options'].append(arg)
-
-    return result
-
-
-def classify_source(filename, cplusplus=False):
-    """ Return the language from file name extension. """
-
-    mapping = {
-        '.c': 'c++' if cplusplus else 'c',
-        '.i': 'c++-cpp-output' if cplusplus else 'c-cpp-output',
-        '.ii': 'c++-cpp-output',
-        '.m': 'objective-c',
-        '.mi': 'objective-c-cpp-output',
-        '.mm': 'objective-c++',
-        '.mii': 'objective-c++-cpp-output',
-        '.C': 'c++',
-        '.cc': 'c++',
-        '.CC': 'c++',
-        '.cp': 'c++',
-        '.cpp': 'c++',
-        '.cxx': 'c++',
-        '.c++': 'c++',
-        '.C++': 'c++',
-        '.txx': 'c++'
-    }
-
-    __, extension = os.path.splitext(os.path.basename(filename))
-    return mapping.get(extension)
-
-
-def is_cplusplus_compiler(name):
-    """ Returns true when the compiler name refer to a C++ compiler. """
-
-    match = re.match(r'^([^/]*/)*(\w*-)*(\w+\+\+)(-(\d+(\.\d+){0,3}))?$', name)
-    return False if match is None else True

Added: cfe/trunk/tools/scan-build-py/libscanbuild/compilation.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/libscanbuild/compilation.py?rev=266726&view=auto
==============================================================================
--- cfe/trunk/tools/scan-build-py/libscanbuild/compilation.py (added)
+++ cfe/trunk/tools/scan-build-py/libscanbuild/compilation.py Tue Apr 19 07:03:03 2016
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+""" This module is responsible for to parse a compiler invocation. """
+
+import re
+import os
+import collections
+
+__all__ = ['split_command', 'classify_source', 'compiler_language']
+
+# Ignored compiler options map for compilation database creation.
+# The map is used in `split_command` method. (Which does ignore and classify
+# parameters.) Please note, that these are not the only parameters which
+# might be ignored.
+#
+# Keys are the option name, value number of options to skip
+IGNORED_FLAGS = {
+    # compiling only flag, ignored because the creator of compilation
+    # database will explicitly set it.
+    '-c': 0,
+    # preprocessor macros, ignored because would cause duplicate entries in
+    # the output (the only difference would be these flags). this is actual
+    # finding from users, who suffered longer execution time caused by the
+    # duplicates.
+    '-MD': 0,
+    '-MMD': 0,
+    '-MG': 0,
+    '-MP': 0,
+    '-MF': 1,
+    '-MT': 1,
+    '-MQ': 1,
+    # linker options, ignored because for compilation database will contain
+    # compilation commands only. so, the compiler would ignore these flags
+    # anyway. the benefit to get rid of them is to make the output more
+    # readable.
+    '-static': 0,
+    '-shared': 0,
+    '-s': 0,
+    '-rdynamic': 0,
+    '-l': 1,
+    '-L': 1,
+    '-u': 1,
+    '-z': 1,
+    '-T': 1,
+    '-Xlinker': 1
+}
+
+# Known C/C++ compiler executable name patterns
+COMPILER_PATTERNS = frozenset([
+    re.compile(r'^(intercept-|analyze-|)c(c|\+\+)$'),
+    re.compile(r'^([^-]*-)*[mg](cc|\+\+)(-\d+(\.\d+){0,2})?$'),
+    re.compile(r'^([^-]*-)*clang(\+\+)?(-\d+(\.\d+){0,2})?$'),
+    re.compile(r'^llvm-g(cc|\+\+)$'),
+])
+
+
+def split_command(command):
+    """ Returns a value when the command is a compilation, None otherwise.
+
+    The value on success is a named tuple with the following attributes:
+
+        files:    list of source files
+        flags:    list of compile options
+        compiler: string value of 'c' or 'c++' """
+
+    # the result of this method
+    result = collections.namedtuple('Compilation',
+                                    ['compiler', 'flags', 'files'])
+    result.compiler = compiler_language(command)
+    result.flags = []
+    result.files = []
+    # quit right now, if the program was not a C/C++ compiler
+    if not result.compiler:
+        return None
+    # iterate on the compile options
+    args = iter(command[1:])
+    for arg in args:
+        # quit when compilation pass is not involved
+        if arg in {'-E', '-S', '-cc1', '-M', '-MM', '-###'}:
+            return None
+        # ignore some flags
+        elif arg in IGNORED_FLAGS:
+            count = IGNORED_FLAGS[arg]
+            for _ in range(count):
+                next(args)
+        elif re.match(r'^-(l|L|Wl,).+', arg):
+            pass
+        # some parameters could look like filename, take as compile option
+        elif arg in {'-D', '-I'}:
+            result.flags.extend([arg, next(args)])
+        # parameter which looks source file is taken...
+        elif re.match(r'^[^-].+', arg) and classify_source(arg):
+            result.files.append(arg)
+        # and consider everything else as compile option.
+        else:
+            result.flags.append(arg)
+    # do extra check on number of source files
+    return result if result.files else None
+
+
+def classify_source(filename, c_compiler=True):
+    """ Return the language from file name extension. """
+
+    mapping = {
+        '.c': 'c' if c_compiler else 'c++',
+        '.i': 'c-cpp-output' if c_compiler else 'c++-cpp-output',
+        '.ii': 'c++-cpp-output',
+        '.m': 'objective-c',
+        '.mi': 'objective-c-cpp-output',
+        '.mm': 'objective-c++',
+        '.mii': 'objective-c++-cpp-output',
+        '.C': 'c++',
+        '.cc': 'c++',
+        '.CC': 'c++',
+        '.cp': 'c++',
+        '.cpp': 'c++',
+        '.cxx': 'c++',
+        '.c++': 'c++',
+        '.C++': 'c++',
+        '.txx': 'c++'
+    }
+
+    __, extension = os.path.splitext(os.path.basename(filename))
+    return mapping.get(extension)
+
+
+def compiler_language(command):
+    """ A predicate to decide the command is a compiler call or not.
+
+    Returns 'c' or 'c++' when it match. None otherwise. """
+
+    cplusplus = re.compile(r'^(.+)(\+\+)(-.+|)$')
+
+    if command:
+        executable = os.path.basename(command[0])
+        if any(pattern.match(executable) for pattern in COMPILER_PATTERNS):
+            return 'c++' if cplusplus.match(executable) else 'c'
+    return None

Modified: cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py (original)
+++ cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py Tue Apr 19 07:03:03 2016
@@ -31,9 +31,9 @@ import argparse
 import logging
 import subprocess
 from libear import build_libear, TemporaryDirectory
-from libscanbuild import duplicate_check, tempdir, initialize_logging
 from libscanbuild import command_entry_point
-from libscanbuild.command import Action, classify_parameters
+from libscanbuild import duplicate_check, tempdir, initialize_logging
+from libscanbuild.compilation import split_command
 from libscanbuild.shell import encode, decode
 
 __all__ = ['capture', 'intercept_build_main', 'intercept_build_wrapper']
@@ -72,23 +72,23 @@ def capture(args, bin_dir):
         from the arguments. And do shell escaping on the command.
 
         To support incremental builds, it is desired to read elements from
-        an existing compilation database from a previous run. These elemets
+        an existing compilation database from a previous run. These elements
         shall be merged with the new elements. """
 
         # create entries from the current run
         current = itertools.chain.from_iterable(
             # creates a sequence of entry generators from an exec,
-            # but filter out non compiler calls before.
-            (format_entry(x) for x in commands if is_compiler_call(x)))
+            format_entry(command) for command in commands)
         # read entries from previous run
-        if 'append' in args and args.append and os.path.exists(args.cdb):
+        if 'append' in args and args.append and os.path.isfile(args.cdb):
             with open(args.cdb) as handle:
                 previous = iter(json.load(handle))
         else:
             previous = iter([])
         # filter out duplicate entries from both
         duplicate = duplicate_check(entry_hash)
-        return (entry for entry in itertools.chain(previous, current)
+        return (entry
+                for entry in itertools.chain(previous, current)
                 if os.path.exists(entry['file']) and not duplicate(entry))
 
     with TemporaryDirectory(prefix='intercept-', dir=tempdir()) as tmp_dir:
@@ -98,14 +98,14 @@ def capture(args, bin_dir):
         exit_code = subprocess.call(args.build, env=environment)
         logging.info('build finished with exit code: %d', exit_code)
         # read the intercepted exec calls
-        commands = itertools.chain.from_iterable(
+        exec_traces = itertools.chain.from_iterable(
             parse_exec_trace(os.path.join(tmp_dir, filename))
             for filename in sorted(glob.iglob(os.path.join(tmp_dir, '*.cmd'))))
         # do post processing only if that was requested
         if 'raw_entries' not in args or not args.raw_entries:
-            entries = post_processing(commands)
+            entries = post_processing(exec_traces)
         else:
-            entries = commands
+            entries = exec_traces
         # dump the compilation database
         with open(args.cdb, 'w+') as handle:
             json.dump(list(entries), handle, sort_keys=True, indent=4)
@@ -209,7 +209,7 @@ def parse_exec_trace(filename):
             }
 
 
-def format_entry(entry):
+def format_entry(exec_trace):
     """ Generate the desired fields for compilation database entries. """
 
     def abspath(cwd, name):
@@ -217,40 +217,20 @@ def format_entry(entry):
         fullname = name if os.path.isabs(name) else os.path.join(cwd, name)
         return os.path.normpath(fullname)
 
-    logging.debug('format this command: %s', entry['command'])
-    atoms = classify_parameters(entry['command'])
-    if atoms['action'] <= Action.Compile:
-        for source in atoms['files']:
-            compiler = 'c++' if atoms['c++'] else 'cc'
-            flags = atoms['compile_options']
-            flags += ['-o', atoms['output']] if atoms['output'] else []
-            flags += ['-x', atoms['language']] if 'language' in atoms else []
-            flags += [elem
-                      for arch in atoms.get('archs_seen', [])
-                      for elem in ['-arch', arch]]
-            command = [compiler, '-c'] + flags + [source]
+    logging.debug('format this command: %s', exec_trace['command'])
+    compilation = split_command(exec_trace['command'])
+    if compilation:
+        for source in compilation.files:
+            compiler = 'c++' if compilation.compiler == 'c++' else 'cc'
+            command = [compiler, '-c'] + compilation.flags + [source]
             logging.debug('formated as: %s', command)
             yield {
-                'directory': entry['directory'],
+                'directory': exec_trace['directory'],
                 'command': encode(command),
-                'file': abspath(entry['directory'], source)
+                'file': abspath(exec_trace['directory'], source)
             }
 
 
-def is_compiler_call(entry):
-    """ A predicate to decide the entry is a compiler call or not. """
-
-    patterns = [
-        re.compile(r'^([^/]*/)*intercept-c(c|\+\+)$'),
-        re.compile(r'^([^/]*/)*c(c|\+\+)$'),
-        re.compile(r'^([^/]*/)*([^-]*-)*[mg](cc|\+\+)(-\d+(\.\d+){0,2})?$'),
-        re.compile(r'^([^/]*/)*([^-]*-)*clang(\+\+)?(-\d+(\.\d+){0,2})?$'),
-        re.compile(r'^([^/]*/)*llvm-g(cc|\+\+)$'),
-    ]
-    executable = entry['command'][0]
-    return any((pattern.match(executable) for pattern in patterns))
-
-
 def is_preload_disabled(platform):
     """ Library-based interposition will fail silently if SIP is enabled,
     so this should be detected. You can detect whether SIP is enabled on

Modified: cfe/trunk/tools/scan-build-py/libscanbuild/runner.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/libscanbuild/runner.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/libscanbuild/runner.py (original)
+++ cfe/trunk/tools/scan-build-py/libscanbuild/runner.py Tue Apr 19 07:03:03 2016
@@ -5,18 +5,44 @@
 # License. See LICENSE.TXT for details.
 """ This module is responsible to run the analyzer commands. """
 
+import re
 import os
 import os.path
 import tempfile
 import functools
 import subprocess
 import logging
-from libscanbuild.command import classify_parameters, Action, classify_source
-from libscanbuild.clang import get_arguments, get_version
+from libscanbuild.compilation import classify_source, compiler_language
+from libscanbuild.clang import get_version, get_arguments
 from libscanbuild.shell import decode
 
 __all__ = ['run']
 
+# To have good results from static analyzer certain compiler options shall be
+# omitted. The compiler flag filtering only affects the static analyzer run.
+#
+# Keys are the option name, value number of options to skip
+IGNORED_FLAGS = {
+    '-c': 0,  # compile option will be overwritten
+    '-fsyntax-only': 0,  # static analyzer option will be overwritten
+    '-o': 1,  # will set up own output file
+    # flags below are inherited from the perl implementation.
+    '-g': 0,
+    '-save-temps': 0,
+    '-install_name': 1,
+    '-exported_symbols_list': 1,
+    '-current_version': 1,
+    '-compatibility_version': 1,
+    '-init': 1,
+    '-e': 1,
+    '-seg1addr': 1,
+    '-bundle_loader': 1,
+    '-multiply_defined': 1,
+    '-sectorder': 3,
+    '--param': 1,
+    '--serialize-diagnostics': 1
+}
+
 
 def require(required):
     """ Decorator for checking the required values in state.
@@ -29,8 +55,8 @@ def require(required):
         def wrapper(*args, **kwargs):
             for key in required:
                 if key not in args[0]:
-                    raise KeyError(
-                        '{0} not passed to {1}'.format(key, function.__name__))
+                    raise KeyError('{0} not passed to {1}'.format(
+                        key, function.__name__))
 
             return function(*args, **kwargs)
 
@@ -39,10 +65,15 @@ def require(required):
     return decorator
 
 
- at require(['command', 'directory', 'file',  # an entry from compilation database
-          'clang', 'direct_args',  # compiler name, and arguments from command
-          'force_analyze_debug_code',  # preprocessing options
-          'output_dir', 'output_format', 'output_failures'])
+ at require(['command',  # entry from compilation database
+          'directory',  # entry from compilation database
+          'file',  # entry from compilation database
+          'clang',  # clang executable name (and path)
+          'direct_args',  # arguments from command line
+          'force_debug',  # kill non debug macros
+          'output_dir',  # where generated report files shall go
+          'output_format',  # it's 'plist' or 'html' or both
+          'output_failures'])  # generate crash reports or not
 def run(opts):
     """ Entry point to run (or not) static analyzer against a single entry
     of the compilation database.
@@ -58,16 +89,17 @@ def run(opts):
 
     try:
         command = opts.pop('command')
+        command = command if isinstance(command, list) else decode(command)
         logging.debug("Run analyzer against '%s'", command)
-        opts.update(classify_parameters(decode(command)))
+        opts.update(classify_parameters(command))
 
-        return action_check(opts)
+        return arch_check(opts)
     except Exception:
         logging.error("Problem occured during analyzis.", exc_info=1)
         return None
 
 
- at require(['report', 'directory', 'clang', 'output_dir', 'language', 'file',
+ at require(['clang', 'directory', 'flags', 'file', 'output_dir', 'language',
           'error_type', 'error_output', 'exit_code'])
 def report_failure(opts):
     """ Create report when analyzer failed.
@@ -96,36 +128,49 @@ def report_failure(opts):
                                       dir=destination(opts))
     os.close(handle)
     cwd = opts['directory']
-    cmd = get_arguments([opts['clang']] + opts['report'] + ['-o', name], cwd)
+    cmd = get_arguments([opts['clang'], '-fsyntax-only', '-E'] +
+                        opts['flags'] + [opts['file'], '-o', name], cwd)
     logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
     subprocess.call(cmd, cwd=cwd)
-
+    # write general information about the crash
     with open(name + '.info.txt', 'w') as handle:
         handle.write(opts['file'] + os.linesep)
         handle.write(error.title().replace('_', ' ') + os.linesep)
         handle.write(' '.join(cmd) + os.linesep)
         handle.write(' '.join(os.uname()) + os.linesep)
-        handle.write(get_version(cmd[0]))
+        handle.write(get_version(opts['clang']))
         handle.close()
-
+    # write the captured output too
     with open(name + '.stderr.txt', 'w') as handle:
         handle.writelines(opts['error_output'])
         handle.close()
-
+    # return with the previous step exit code and output
     return {
         'error_output': opts['error_output'],
         'exit_code': opts['exit_code']
     }
 
 
- at require(['clang', 'analyze', 'directory', 'output'])
+ at require(['clang', 'directory', 'flags', 'direct_args', 'file', 'output_dir',
+          'output_format'])
 def run_analyzer(opts, continuation=report_failure):
     """ It assembles the analysis command line and executes it. Capture the
     output of the analysis and returns with it. If failure reports are
     requested, it calls the continuation to generate it. """
 
+    def output():
+        """ Creates output file name for reports. """
+        if opts['output_format'] in {'plist', 'plist-html'}:
+            (handle, name) = tempfile.mkstemp(prefix='report-',
+                                              suffix='.plist',
+                                              dir=opts['output_dir'])
+            os.close(handle)
+            return name
+        return opts['output_dir']
+
     cwd = opts['directory']
-    cmd = get_arguments([opts['clang']] + opts['analyze'] + opts['output'],
+    cmd = get_arguments([opts['clang'], '--analyze'] + opts['direct_args'] +
+                        opts['flags'] + [opts['file'], '-o', output()],
                         cwd)
     logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
     child = subprocess.Popen(cmd,
@@ -145,119 +190,124 @@ def run_analyzer(opts, continuation=repo
             'exit_code': child.returncode
         })
         return continuation(opts)
+    # return the output for logging and exit code for testing
     return {'error_output': output, 'exit_code': child.returncode}
 
 
- at require(['output_dir'])
-def set_analyzer_output(opts, continuation=run_analyzer):
-    """ Create output file if was requested.
-
-    This plays a role only if .plist files are requested. """
-
-    if opts.get('output_format') in {'plist', 'plist-html'}:
-        with tempfile.NamedTemporaryFile(prefix='report-',
-                                         suffix='.plist',
-                                         delete=False,
-                                         dir=opts['output_dir']) as output:
-            opts.update({'output': ['-o', output.name]})
-            return continuation(opts)
-    else:
-        opts.update({'output': ['-o', opts['output_dir']]})
-        return continuation(opts)
+ at require(['flags', 'force_debug'])
+def filter_debug_flags(opts, continuation=run_analyzer):
+    """ Filter out nondebug macros when requested. """
+
+    if opts.pop('force_debug'):
+        # lazy implementation just append an undefine macro at the end
+        opts.update({'flags': opts['flags'] + ['-UNDEBUG']})
 
-def force_analyze_debug_code(cmd):
-    """ Enable assert()'s by undefining NDEBUG. """
-    cmd.append('-UNDEBUG')
-
- at require(['file', 'directory', 'clang', 'direct_args',
-          'force_analyze_debug_code', 'language', 'output_dir',
-          'output_format', 'output_failures'])
-def create_commands(opts, continuation=set_analyzer_output):
-    """ Create command to run analyzer or failure report generation.
-
-    It generates commands (from compilation database entries) which contains
-    enough information to run the analyzer (and the crash report generation
-    if that was requested). """
-
-    common = []
-    if 'arch' in opts:
-        common.extend(['-arch', opts.pop('arch')])
-    common.extend(opts.pop('compile_options', []))
-    if opts['force_analyze_debug_code']:
-        force_analyze_debug_code(common)
-    common.extend(['-x', opts['language']])
-    common.append(os.path.relpath(opts['file'], opts['directory']))
-
-    opts.update({
-        'analyze': ['--analyze'] + opts['direct_args'] + common,
-        'report': ['-fsyntax-only', '-E'] + common
-    })
+    return continuation(opts)
+
+
+ at require(['file', 'directory'])
+def set_file_path_relative(opts, continuation=filter_debug_flags):
+    """ Set source file path to relative to the working directory.
+
+    The only purpose of this function is to pass the SATestBuild.py tests. """
+
+    opts.update({'file': os.path.relpath(opts['file'], opts['directory'])})
 
     return continuation(opts)
 
 
- at require(['file', 'c++'])
-def language_check(opts, continuation=create_commands):
+ at require(['language', 'compiler', 'file', 'flags'])
+def language_check(opts, continuation=set_file_path_relative):
     """ Find out the language from command line parameters or file name
     extension. The decision also influenced by the compiler invocation. """
 
-    accepteds = {
+    accepted = frozenset({
         'c', 'c++', 'objective-c', 'objective-c++', 'c-cpp-output',
         'c++-cpp-output', 'objective-c-cpp-output'
-    }
+    })
 
-    key = 'language'
-    language = opts[key] if key in opts else \
-        classify_source(opts['file'], opts['c++'])
+    # language can be given as a parameter...
+    language = opts.pop('language')
+    compiler = opts.pop('compiler')
+    # ... or find out from source file extension
+    if language is None and compiler is not None:
+        language = classify_source(opts['file'], compiler == 'c')
 
     if language is None:
         logging.debug('skip analysis, language not known')
         return None
-    elif language not in accepteds:
+    elif language not in accepted:
         logging.debug('skip analysis, language not supported')
         return None
     else:
         logging.debug('analysis, language: %s', language)
-        opts.update({key: language})
+        opts.update({'language': language,
+                     'flags': ['-x', language] + opts['flags']})
         return continuation(opts)
 
 
- at require([])
+ at require(['arch_list', 'flags'])
 def arch_check(opts, continuation=language_check):
     """ Do run analyzer through one of the given architectures. """
 
-    disableds = {'ppc', 'ppc64'}
+    disabled = frozenset({'ppc', 'ppc64'})
 
-    key = 'archs_seen'
-    if key in opts:
+    received_list = opts.pop('arch_list')
+    if received_list:
         # filter out disabled architectures and -arch switches
-        archs = [a for a in opts[key] if a not in disableds]
-
-        if not archs:
-            logging.debug('skip analysis, found not supported arch')
-            return None
-        else:
+        filtered_list = [a for a in received_list if a not in disabled]
+        if filtered_list:
             # There should be only one arch given (or the same multiple
             # times). If there are multiple arch are given and are not
             # the same, those should not change the pre-processing step.
             # But that's the only pass we have before run the analyzer.
-            arch = archs.pop()
-            logging.debug('analysis, on arch: %s', arch)
+            current = filtered_list.pop()
+            logging.debug('analysis, on arch: %s', current)
 
-            opts.update({'arch': arch})
-            del opts[key]
+            opts.update({'flags': ['-arch', current] + opts['flags']})
             return continuation(opts)
+        else:
+            logging.debug('skip analysis, found not supported arch')
+            return None
     else:
         logging.debug('analysis, on default arch')
         return continuation(opts)
 
 
- at require(['action'])
-def action_check(opts, continuation=arch_check):
-    """ Continue analysis only if it compilation or link. """
+def classify_parameters(command):
+    """ Prepare compiler flags (filters some and add others) and take out
+    language (-x) and architecture (-arch) flags for future processing. """
+
+    result = {
+        'flags': [],  # the filtered compiler flags
+        'arch_list': [],  # list of architecture flags
+        'language': None,  # compilation language, None, if not specified
+        'compiler': compiler_language(command)  # 'c' or 'c++'
+    }
 
-    if opts.pop('action') <= Action.Compile:
-        return continuation(opts)
-    else:
-        logging.debug('skip analysis, not compilation nor link')
-        return None
+    # iterate on the compile options
+    args = iter(command[1:])
+    for arg in args:
+        # take arch flags into a separate basket
+        if arg == '-arch':
+            result['arch_list'].append(next(args))
+        # take language
+        elif arg == '-x':
+            result['language'] = next(args)
+        # parameters which looks source file are not flags
+        elif re.match(r'^[^-].+', arg) and classify_source(arg):
+            pass
+        # ignore some flags
+        elif arg in IGNORED_FLAGS:
+            count = IGNORED_FLAGS[arg]
+            for _ in range(count):
+                next(args)
+        # we don't care about extra warnings, but we should suppress ones
+        # that we don't want to see.
+        elif re.match(r'^-W.+', arg) and not re.match(r'^-Wno-.+', arg):
+            pass
+        # and consider everything else as compilation flag.
+        else:
+            result['flags'].append(arg)
+
+    return result

Modified: cfe/trunk/tools/scan-build-py/tests/functional/cases/test_create_cdb.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/functional/cases/test_create_cdb.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/functional/cases/test_create_cdb.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/functional/cases/test_create_cdb.py Tue Apr 19 07:03:03 2016
@@ -4,7 +4,7 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
-from ...unit import fixtures
+import libear
 from . import make_args, silent_check_call, silent_call, create_empty_file
 import unittest
 
@@ -28,13 +28,13 @@ class CompilationDatabaseTest(unittest.T
             return len(content)
 
     def test_successful_build(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             result = self.run_intercept(tmpdir, ['build_regular'])
             self.assertTrue(os.path.isfile(result))
             self.assertEqual(5, self.count_entries(result))
 
     def test_successful_build_with_wrapper(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             result = os.path.join(tmpdir, 'cdb.json')
             make = make_args(tmpdir) + ['build_regular']
             silent_check_call(['intercept-build', '--cdb', result,
@@ -44,14 +44,14 @@ class CompilationDatabaseTest(unittest.T
 
     @unittest.skipIf(os.getenv('TRAVIS'), 'ubuntu make return -11')
     def test_successful_build_parallel(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             result = self.run_intercept(tmpdir, ['-j', '4', 'build_regular'])
             self.assertTrue(os.path.isfile(result))
             self.assertEqual(5, self.count_entries(result))
 
     @unittest.skipIf(os.getenv('TRAVIS'), 'ubuntu env remove clang from path')
     def test_successful_build_on_empty_env(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             result = os.path.join(tmpdir, 'cdb.json')
             make = make_args(tmpdir) + ['CC=clang', 'build_regular']
             silent_check_call(['intercept-build', '--cdb', result,
@@ -60,13 +60,13 @@ class CompilationDatabaseTest(unittest.T
             self.assertEqual(5, self.count_entries(result))
 
     def test_successful_build_all_in_one(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             result = self.run_intercept(tmpdir, ['build_all_in_one'])
             self.assertTrue(os.path.isfile(result))
             self.assertEqual(5, self.count_entries(result))
 
     def test_not_successful_build(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             result = os.path.join(tmpdir, 'cdb.json')
             make = make_args(tmpdir) + ['build_broken']
             silent_call(
@@ -84,12 +84,12 @@ class ExitCodeTest(unittest.TestCase):
             ['intercept-build', '--cdb', result] + make)
 
     def test_successful_build(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             exitcode = self.run_intercept(tmpdir, 'build_clean')
             self.assertFalse(exitcode)
 
     def test_not_successful_build(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             exitcode = self.run_intercept(tmpdir, 'build_broken')
             self.assertTrue(exitcode)
 
@@ -110,7 +110,7 @@ class ResumeFeatureTest(unittest.TestCas
             return len(content)
 
     def test_overwrite_existing_cdb(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             result = self.run_intercept(tmpdir, 'build_clean', [])
             self.assertTrue(os.path.isfile(result))
             result = self.run_intercept(tmpdir, 'build_regular', [])
@@ -118,7 +118,7 @@ class ResumeFeatureTest(unittest.TestCas
             self.assertEqual(2, self.count_entries(result))
 
     def test_append_to_existing_cdb(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             result = self.run_intercept(tmpdir, 'build_clean', [])
             self.assertTrue(os.path.isfile(result))
             result = self.run_intercept(tmpdir, 'build_regular', ['--append'])
@@ -138,7 +138,7 @@ class ResultFormatingTest(unittest.TestC
             return content
 
     def assert_creates_number_of_entries(self, command, count):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             filename = os.path.join(tmpdir, 'test.c')
             create_empty_file(filename)
             command.append(filename)
@@ -153,7 +153,7 @@ class ResultFormatingTest(unittest.TestC
         self.assert_creates_number_of_entries(['cc', '-c', '-MM'], 0)
 
     def assert_command_creates_entry(self, command, expected):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             filename = os.path.join(tmpdir, command[-1])
             create_empty_file(filename)
             cmd = ['sh', '-c', ' '.join(command)]

Modified: cfe/trunk/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py Tue Apr 19 07:03:03 2016
@@ -4,7 +4,7 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
-from ...unit import fixtures
+import libear
 import unittest
 
 import os.path
@@ -45,6 +45,6 @@ class ExecAnatomyTest(unittest.TestCase)
     def test_all_exec_calls(self):
         this_dir, _ = os.path.split(__file__)
         source_dir = os.path.normpath(os.path.join(this_dir, '..', 'exec'))
-        with fixtures.TempDir() as tmp_dir:
+        with libear.TemporaryDirectory() as tmp_dir:
             expected, result = run(source_dir, tmp_dir)
             self.assertEqualJson(expected, result)

Modified: cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cdb.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cdb.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cdb.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cdb.py Tue Apr 19 07:03:03 2016
@@ -4,13 +4,12 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
-from ...unit import fixtures
+import libear
 from . import call_and_report
 import unittest
 
 import os.path
 import string
-import subprocess
 import glob
 
 
@@ -37,19 +36,19 @@ def run_analyzer(directory, cdb, args):
 
 class OutputDirectoryTest(unittest.TestCase):
     def test_regular_keeps_report_dir(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
             exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
             self.assertTrue(os.path.isdir(reportdir))
 
     def test_clear_deletes_report_dir(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
             exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
             self.assertFalse(os.path.isdir(reportdir))
 
     def test_clear_keeps_report_dir_when_asked(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
             exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--keep-empty'])
             self.assertTrue(os.path.isdir(reportdir))
@@ -57,38 +56,38 @@ class OutputDirectoryTest(unittest.TestC
 
 class ExitCodeTest(unittest.TestCase):
     def test_regular_does_not_set_exit_code(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
             exit_code, __ = run_analyzer(tmpdir, cdb, [])
             self.assertFalse(exit_code)
 
     def test_clear_does_not_set_exit_code(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
             exit_code, __ = run_analyzer(tmpdir, cdb, [])
             self.assertFalse(exit_code)
 
     def test_regular_sets_exit_code_if_asked(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
             exit_code, __ = run_analyzer(tmpdir, cdb, ['--status-bugs'])
             self.assertTrue(exit_code)
 
     def test_clear_does_not_set_exit_code_if_asked(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
             exit_code, __ = run_analyzer(tmpdir, cdb, ['--status-bugs'])
             self.assertFalse(exit_code)
 
     def test_regular_sets_exit_code_if_asked_from_plist(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
             exit_code, __ = run_analyzer(
                 tmpdir, cdb, ['--status-bugs', '--plist'])
             self.assertTrue(exit_code)
 
     def test_clear_does_not_set_exit_code_if_asked_from_plist(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('clean', tmpdir)
             exit_code, __ = run_analyzer(
                 tmpdir, cdb, ['--status-bugs', '--plist'])
@@ -105,7 +104,7 @@ class OutputFormatTest(unittest.TestCase
         return len(glob.glob(os.path.join(directory, 'report-*.plist')))
 
     def test_default_creates_html_report(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
             exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
             self.assertTrue(
@@ -114,7 +113,7 @@ class OutputFormatTest(unittest.TestCase
             self.assertEqual(self.get_plist_count(reportdir), 0)
 
     def test_plist_and_html_creates_html_report(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
             exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist-html'])
             self.assertTrue(
@@ -123,7 +122,7 @@ class OutputFormatTest(unittest.TestCase
             self.assertEqual(self.get_plist_count(reportdir), 5)
 
     def test_plist_does_not_creates_html_report(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('regular', tmpdir)
             exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist'])
             self.assertFalse(
@@ -134,14 +133,14 @@ class OutputFormatTest(unittest.TestCase
 
 class FailureReportTest(unittest.TestCase):
     def test_broken_creates_failure_reports(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('broken', tmpdir)
             exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
             self.assertTrue(
                 os.path.isdir(os.path.join(reportdir, 'failures')))
 
     def test_broken_does_not_creates_failure_reports(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('broken', tmpdir)
             exit_code, reportdir = run_analyzer(
                 tmpdir, cdb, ['--no-failure-reports'])
@@ -170,13 +169,13 @@ class TitleTest(unittest.TestCase):
         self.assertEqual(result['page'], expected)
 
     def test_default_title_in_report(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('broken', tmpdir)
             exit_code, reportdir = run_analyzer(tmpdir, cdb, [])
             self.assertTitleEqual(reportdir, 'src - analyzer results')
 
     def test_given_title_in_report(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             cdb = prepare_cdb('broken', tmpdir)
             exit_code, reportdir = run_analyzer(
                 tmpdir, cdb, ['--html-title', 'this is the title'])

Modified: cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cmd.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cmd.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cmd.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/functional/cases/test_from_cmd.py Tue Apr 19 07:03:03 2016
@@ -4,7 +4,7 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
-from ...unit import fixtures
+import libear
 from . import make_args, check_call_and_report, create_empty_file
 import unittest
 
@@ -22,19 +22,19 @@ class OutputDirectoryTest(unittest.TestC
             cmd)
 
     def test_regular_keeps_report_dir(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             make = make_args(tmpdir) + ['build_regular']
             outdir = self.run_analyzer(tmpdir, [], make)
             self.assertTrue(os.path.isdir(outdir))
 
     def test_clear_deletes_report_dir(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             make = make_args(tmpdir) + ['build_clean']
             outdir = self.run_analyzer(tmpdir, [], make)
             self.assertFalse(os.path.isdir(outdir))
 
     def test_clear_keeps_report_dir_when_asked(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             make = make_args(tmpdir) + ['build_clean']
             outdir = self.run_analyzer(tmpdir, ['--keep-empty'], make)
             self.assertTrue(os.path.isdir(outdir))
@@ -47,7 +47,7 @@ class RunAnalyzerTest(unittest.TestCase)
         return len(glob.glob(os.path.join(directory, 'report-*.plist')))
 
     def test_interposition_works(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             make = make_args(tmpdir) + ['build_regular']
             outdir = check_call_and_report(
                 ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'],
@@ -57,7 +57,7 @@ class RunAnalyzerTest(unittest.TestCase)
             self.assertEqual(self.get_plist_count(outdir), 5)
 
     def test_intercept_wrapper_works(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             make = make_args(tmpdir) + ['build_regular']
             outdir = check_call_and_report(
                 ['scan-build', '--plist', '-o', tmpdir, '--intercept-first',
@@ -68,7 +68,7 @@ class RunAnalyzerTest(unittest.TestCase)
             self.assertEqual(self.get_plist_count(outdir), 5)
 
     def test_intercept_library_works(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             make = make_args(tmpdir) + ['build_regular']
             outdir = check_call_and_report(
                 ['scan-build', '--plist', '-o', tmpdir, '--intercept-first'],
@@ -88,21 +88,21 @@ class RunAnalyzerTest(unittest.TestCase)
         return ['sh', '-c', command]
 
     def test_interposition_cc_works(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             outdir = check_call_and_report(
                 ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'],
                 self.compile_empty_source_file(tmpdir, False))
             self.assertEqual(self.get_plist_count(outdir), 1)
 
     def test_interposition_cxx_works(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             outdir = check_call_and_report(
                 ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'],
                 self.compile_empty_source_file(tmpdir, True))
             self.assertEqual(self.get_plist_count(outdir), 1)
 
     def test_intercept_cc_works(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             outdir = check_call_and_report(
                 ['scan-build', '--plist', '-o', tmpdir, '--override-compiler',
                  '--intercept-first'],
@@ -110,7 +110,7 @@ class RunAnalyzerTest(unittest.TestCase)
             self.assertEqual(self.get_plist_count(outdir), 1)
 
     def test_intercept_cxx_works(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             outdir = check_call_and_report(
                 ['scan-build', '--plist', '-o', tmpdir, '--override-compiler',
                  '--intercept-first'],

Added: cfe/trunk/tools/scan-build-py/tests/functional/src/build/Makefile
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/functional/src/build/Makefile?rev=266726&view=auto
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/functional/src/build/Makefile (added)
+++ cfe/trunk/tools/scan-build-py/tests/functional/src/build/Makefile Tue Apr 19 07:03:03 2016
@@ -0,0 +1,42 @@
+SRCDIR := ..
+OBJDIR := .
+
+CFLAGS = -Wall -DDEBUG -Dvariable="value with space" -I $(SRCDIR)/include
+LDFLAGS =
+PROGRAM = $(OBJDIR)/prg
+
+$(OBJDIR)/main.o: $(SRCDIR)/main.c
+	$(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/main.c
+
+$(OBJDIR)/clean-one.o: $(SRCDIR)/clean-one.c
+	$(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/clean-one.c
+
+$(OBJDIR)/clean-two.o: $(SRCDIR)/clean-two.c
+	$(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/clean-two.c
+
+$(OBJDIR)/emit-one.o: $(SRCDIR)/emit-one.c
+	$(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/emit-one.c
+
+$(OBJDIR)/emit-two.o: $(SRCDIR)/emit-two.c
+	$(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/emit-two.c
+
+$(OBJDIR)/broken-one.o: $(SRCDIR)/broken-one.c
+	$(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/broken-one.c
+
+$(OBJDIR)/broken-two.o: $(SRCDIR)/broken-two.c
+	$(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/broken-two.c
+
+$(PROGRAM): $(OBJDIR)/main.o $(OBJDIR)/clean-one.o $(OBJDIR)/clean-two.o $(OBJDIR)/emit-one.o $(OBJDIR)/emit-two.o
+	$(CC) $(LDFLAGS) -o $@ $(OBJDIR)/main.o $(OBJDIR)/clean-one.o $(OBJDIR)/clean-two.o $(OBJDIR)/emit-one.o $(OBJDIR)/emit-two.o
+
+build_regular: $(PROGRAM)
+
+build_clean: $(OBJDIR)/main.o $(OBJDIR)/clean-one.o $(OBJDIR)/clean-two.o
+
+build_broken: $(OBJDIR)/main.o $(OBJDIR)/broken-one.o $(OBJDIR)/broken-two.o
+
+build_all_in_one: $(SRCDIR)/main.c $(SRCDIR)/clean-one.c $(SRCDIR)/clean-two.c $(SRCDIR)/emit-one.c $(SRCDIR)/emit-two.c
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $(PROGRAM) $(SRCDIR)/main.c $(SRCDIR)/clean-one.c $(SRCDIR)/clean-two.c $(SRCDIR)/emit-one.c $(SRCDIR)/emit-two.c
+
+clean:
+	rm -f $(PROGRAM) $(OBJDIR)/*.o

Modified: cfe/trunk/tools/scan-build-py/tests/unit/__init__.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/__init__.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/__init__.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/unit/__init__.py Tue Apr 19 07:03:03 2016
@@ -4,7 +4,8 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
-from . import test_command
+from . import test_libear
+from . import test_compilation
 from . import test_clang
 from . import test_runner
 from . import test_report
@@ -13,8 +14,9 @@ from . import test_intercept
 from . import test_shell
 
 
-def load_tests(loader, suite, pattern):
-    suite.addTests(loader.loadTestsFromModule(test_command))
+def load_tests(loader, suite, _):
+    suite.addTests(loader.loadTestsFromModule(test_libear))
+    suite.addTests(loader.loadTestsFromModule(test_compilation))
     suite.addTests(loader.loadTestsFromModule(test_clang))
     suite.addTests(loader.loadTestsFromModule(test_runner))
     suite.addTests(loader.loadTestsFromModule(test_report))

Removed: cfe/trunk/tools/scan-build-py/tests/unit/fixtures.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/fixtures.py?rev=266725&view=auto
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/fixtures.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/unit/fixtures.py (removed)
@@ -1,40 +0,0 @@
-# -*- 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 contextlib
-import tempfile
-import shutil
-import unittest
-
-
-class Spy(object):
-    def __init__(self):
-        self.arg = None
-        self.success = 0
-
-    def call(self, params):
-        self.arg = params
-        return self.success
-
-
- at contextlib.contextmanager
-def TempDir():
-    name = tempfile.mkdtemp(prefix='scan-build-test-')
-    try:
-        yield name
-    finally:
-        shutil.rmtree(name)
-
-
-class TestCase(unittest.TestCase):
-    def assertIn(self, element, collection):
-        found = False
-        for it in collection:
-            if element == it:
-                found = True
-
-        self.assertTrue(found, '{0} does not have {1}'.format(collection,
-                                                              element))

Modified: cfe/trunk/tools/scan-build-py/tests/unit/test_analyze.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/test_analyze.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/test_analyze.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/unit/test_analyze.py Tue Apr 19 07:03:03 2016
@@ -5,4 +5,3 @@
 # License. See LICENSE.TXT for details.
 
 import libscanbuild.analyze as sut
-from . import fixtures

Modified: cfe/trunk/tools/scan-build-py/tests/unit/test_clang.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/test_clang.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/test_clang.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/unit/test_clang.py Tue Apr 19 07:03:03 2016
@@ -4,14 +4,15 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
+import libear
 import libscanbuild.clang as sut
-from . import fixtures
+import unittest
 import os.path
 
 
-class GetClangArgumentsTest(fixtures.TestCase):
+class GetClangArgumentsTest(unittest.TestCase):
     def test_get_clang_arguments(self):
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             filename = os.path.join(tmpdir, 'test.c')
             with open(filename, 'w') as handle:
                 handle.write('')
@@ -20,8 +21,8 @@ class GetClangArgumentsTest(fixtures.Tes
                 ['clang', '-c', filename, '-DNDEBUG', '-Dvar="this is it"'],
                 tmpdir)
 
-            self.assertIn('NDEBUG', result)
-            self.assertIn('var="this is it"', result)
+            self.assertTrue('NDEBUG' in result)
+            self.assertTrue('var="this is it"' in result)
 
     def test_get_clang_arguments_fails(self):
         self.assertRaises(
@@ -29,7 +30,7 @@ class GetClangArgumentsTest(fixtures.Tes
             ['clang', '-###', '-fsyntax-only', '-x', 'c', 'notexist.c'], '.')
 
 
-class GetCheckersTest(fixtures.TestCase):
+class GetCheckersTest(unittest.TestCase):
     def test_get_checkers(self):
         # this test is only to see is not crashing
         result = sut.get_checkers('clang', [])

Removed: cfe/trunk/tools/scan-build-py/tests/unit/test_command.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/test_command.py?rev=266725&view=auto
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/test_command.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/unit/test_command.py (removed)
@@ -1,193 +0,0 @@
-# -*- 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.command as sut
-from . import fixtures
-import unittest
-
-
-class ParseTest(unittest.TestCase):
-
-    def test_action(self):
-        def test(expected, cmd):
-            opts = sut.classify_parameters(cmd)
-            self.assertEqual(expected, opts['action'])
-
-        Link = sut.Action.Link
-        test(Link, ['clang', 'source.c'])
-
-        Compile = sut.Action.Compile
-        test(Compile, ['clang', '-c', 'source.c'])
-        test(Compile, ['clang', '-c', 'source.c', '-MF', 'source.d'])
-
-        Preprocess = sut.Action.Ignored
-        test(Preprocess, ['clang', '-E', 'source.c'])
-        test(Preprocess, ['clang', '-c', '-E', 'source.c'])
-        test(Preprocess, ['clang', '-c', '-M', 'source.c'])
-        test(Preprocess, ['clang', '-c', '-MM', 'source.c'])
-
-    def test_optimalizations(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('compile_options', [])
-
-        self.assertEqual(['-O'],  test(['clang', '-c', 'source.c', '-O']))
-        self.assertEqual(['-O1'], test(['clang', '-c', 'source.c', '-O1']))
-        self.assertEqual(['-Os'], test(['clang', '-c', 'source.c', '-Os']))
-        self.assertEqual(['-O2'], test(['clang', '-c', 'source.c', '-O2']))
-        self.assertEqual(['-O3'], test(['clang', '-c', 'source.c', '-O3']))
-
-    def test_language(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('language')
-
-        self.assertEqual(None, test(['clang', '-c', 'source.c']))
-        self.assertEqual('c', test(['clang', '-c', 'source.c', '-x', 'c']))
-        self.assertEqual('cpp', test(['clang', '-c', 'source.c', '-x', 'cpp']))
-
-    def test_output(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('output')
-
-        self.assertEqual(None, test(['clang', '-c', 'source.c']))
-        self.assertEqual('source.o',
-                         test(['clang', '-c', '-o', 'source.o', 'source.c']))
-
-    def test_arch(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('archs_seen', [])
-
-        eq = self.assertEqual
-
-        eq([], test(['clang', '-c', 'source.c']))
-        eq(['mips'],
-           test(['clang', '-c', 'source.c', '-arch', 'mips']))
-        eq(['mips', 'i386'],
-           test(['clang', '-c', 'source.c', '-arch', 'mips', '-arch', 'i386']))
-
-    def test_input_file(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('files', [])
-
-        eq = self.assertEqual
-
-        eq(['src.c'], test(['clang', 'src.c']))
-        eq(['src.c'], test(['clang', '-c', 'src.c']))
-        eq(['s1.c', 's2.c'], test(['clang', '-c', 's1.c', 's2.c']))
-
-    def test_include(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('compile_options', [])
-
-        eq = self.assertEqual
-
-        eq([], test(['clang', '-c', 'src.c']))
-        eq(['-include', '/usr/local/include'],
-           test(['clang', '-c', 'src.c', '-include', '/usr/local/include']))
-        eq(['-I.'],
-           test(['clang', '-c', 'src.c', '-I.']))
-        eq(['-I', '.'],
-           test(['clang', '-c', 'src.c', '-I', '.']))
-        eq(['-I/usr/local/include'],
-           test(['clang', '-c', 'src.c', '-I/usr/local/include']))
-        eq(['-I', '/usr/local/include'],
-           test(['clang', '-c', 'src.c', '-I', '/usr/local/include']))
-        eq(['-I/opt', '-I', '/opt/otp/include'],
-           test(['clang', '-c', 'src.c', '-I/opt', '-I', '/opt/otp/include']))
-        eq(['-isystem', '/path'],
-           test(['clang', '-c', 'src.c', '-isystem', '/path']))
-        eq(['-isystem=/path'],
-           test(['clang', '-c', 'src.c', '-isystem=/path']))
-
-    def test_define(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('compile_options', [])
-
-        eq = self.assertEqual
-
-        eq([], test(['clang', '-c', 'src.c']))
-        eq(['-DNDEBUG'],
-           test(['clang', '-c', 'src.c', '-DNDEBUG']))
-        eq(['-UNDEBUG'],
-           test(['clang', '-c', 'src.c', '-UNDEBUG']))
-        eq(['-Dvar1=val1', '-Dvar2=val2'],
-           test(['clang', '-c', 'src.c', '-Dvar1=val1', '-Dvar2=val2']))
-        eq(['-Dvar="val ues"'],
-           test(['clang', '-c', 'src.c', '-Dvar="val ues"']))
-
-    def test_ignored_flags(self):
-        def test(flags):
-            cmd = ['clang', 'src.o']
-            opts = sut.classify_parameters(cmd + flags)
-            self.assertEqual(['src.o'], opts.get('compile_options'))
-
-        test([])
-        test(['-lrt', '-L/opt/company/lib'])
-        test(['-static'])
-        test(['-Wnoexcept', '-Wall'])
-        test(['-mtune=i386', '-mcpu=i386'])
-
-    def test_compile_only_flags(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('compile_options', [])
-
-        eq = self.assertEqual
-
-        eq(['-std=C99'],
-           test(['clang', '-c', 'src.c', '-std=C99']))
-        eq(['-nostdinc'],
-           test(['clang', '-c', 'src.c', '-nostdinc']))
-        eq(['-isystem', '/image/debian'],
-           test(['clang', '-c', 'src.c', '-isystem', '/image/debian']))
-        eq(['-iprefix', '/usr/local'],
-           test(['clang', '-c', 'src.c', '-iprefix', '/usr/local']))
-        eq(['-iquote=me'],
-           test(['clang', '-c', 'src.c', '-iquote=me']))
-        eq(['-iquote', 'me'],
-           test(['clang', '-c', 'src.c', '-iquote', 'me']))
-
-    def test_compile_and_link_flags(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('compile_options', [])
-
-        eq = self.assertEqual
-
-        eq(['-fsinged-char'],
-           test(['clang', '-c', 'src.c', '-fsinged-char']))
-        eq(['-fPIC'],
-           test(['clang', '-c', 'src.c', '-fPIC']))
-        eq(['-stdlib=libc++'],
-           test(['clang', '-c', 'src.c', '-stdlib=libc++']))
-        eq(['--sysroot', '/'],
-           test(['clang', '-c', 'src.c', '--sysroot', '/']))
-        eq(['-isysroot', '/'],
-           test(['clang', '-c', 'src.c', '-isysroot', '/']))
-        eq([],
-           test(['clang', '-c', 'src.c', '-fsyntax-only']))
-        eq([],
-           test(['clang', '-c', 'src.c', '-sectorder', 'a', 'b', 'c']))
-
-    def test_detect_cxx_from_compiler_name(self):
-        def test(cmd):
-            opts = sut.classify_parameters(cmd)
-            return opts.get('c++')
-
-        eq = self.assertEqual
-
-        eq(False, test(['cc', '-c', 'src.c']))
-        eq(True, test(['c++', '-c', 'src.c']))
-        eq(False, test(['clang', '-c', 'src.c']))
-        eq(True, test(['clang++', '-c', 'src.c']))
-        eq(False, test(['gcc', '-c', 'src.c']))
-        eq(True, test(['g++', '-c', 'src.c']))

Added: cfe/trunk/tools/scan-build-py/tests/unit/test_compilation.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/test_compilation.py?rev=266726&view=auto
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/test_compilation.py (added)
+++ cfe/trunk/tools/scan-build-py/tests/unit/test_compilation.py Tue Apr 19 07:03:03 2016
@@ -0,0 +1,122 @@
+# -*- 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.compilation as sut
+import unittest
+
+
+class CompilerTest(unittest.TestCase):
+
+    def test_is_compiler_call(self):
+        self.assertIsNotNone(sut.compiler_language(['clang']))
+        self.assertIsNotNone(sut.compiler_language(['clang-3.6']))
+        self.assertIsNotNone(sut.compiler_language(['clang++']))
+        self.assertIsNotNone(sut.compiler_language(['clang++-3.5.1']))
+        self.assertIsNotNone(sut.compiler_language(['cc']))
+        self.assertIsNotNone(sut.compiler_language(['c++']))
+        self.assertIsNotNone(sut.compiler_language(['gcc']))
+        self.assertIsNotNone(sut.compiler_language(['g++']))
+        self.assertIsNotNone(sut.compiler_language(['/usr/local/bin/gcc']))
+        self.assertIsNotNone(sut.compiler_language(['/usr/local/bin/g++']))
+        self.assertIsNotNone(sut.compiler_language(['/usr/local/bin/clang']))
+        self.assertIsNotNone(
+            sut.compiler_language(['armv7_neno-linux-gnueabi-g++']))
+
+        self.assertIsNone(sut.compiler_language([]))
+        self.assertIsNone(sut.compiler_language(['']))
+        self.assertIsNone(sut.compiler_language(['ld']))
+        self.assertIsNone(sut.compiler_language(['as']))
+        self.assertIsNone(sut.compiler_language(['/usr/local/bin/compiler']))
+
+
+class SplitTest(unittest.TestCase):
+
+    def test_detect_cxx_from_compiler_name(self):
+        def test(cmd):
+            result = sut.split_command([cmd, '-c', 'src.c'])
+            self.assertIsNotNone(result, "wrong input for test")
+            return result.compiler == 'c++'
+
+        self.assertFalse(test('cc'))
+        self.assertFalse(test('gcc'))
+        self.assertFalse(test('clang'))
+
+        self.assertTrue(test('c++'))
+        self.assertTrue(test('g++'))
+        self.assertTrue(test('g++-5.3.1'))
+        self.assertTrue(test('clang++'))
+        self.assertTrue(test('clang++-3.7.1'))
+        self.assertTrue(test('armv7_neno-linux-gnueabi-g++'))
+
+    def test_action(self):
+        self.assertIsNotNone(sut.split_command(['clang', 'source.c']))
+        self.assertIsNotNone(sut.split_command(['clang', '-c', 'source.c']))
+        self.assertIsNotNone(sut.split_command(['clang', '-c', 'source.c',
+                                                '-MF', 'a.d']))
+
+        self.assertIsNone(sut.split_command(['clang', '-E', 'source.c']))
+        self.assertIsNone(sut.split_command(['clang', '-c', '-E', 'source.c']))
+        self.assertIsNone(sut.split_command(['clang', '-c', '-M', 'source.c']))
+        self.assertIsNone(
+            sut.split_command(['clang', '-c', '-MM', 'source.c']))
+
+    def test_source_file(self):
+        def test(expected, cmd):
+            self.assertEqual(expected, sut.split_command(cmd).files)
+
+        test(['src.c'], ['clang', 'src.c'])
+        test(['src.c'], ['clang', '-c', 'src.c'])
+        test(['src.C'], ['clang', '-x', 'c', 'src.C'])
+        test(['src.cpp'], ['clang++', '-c', 'src.cpp'])
+        test(['s1.c', 's2.c'], ['clang', '-c', 's1.c', 's2.c'])
+        test(['s1.c', 's2.c'], ['cc', 's1.c', 's2.c', '-ldep', '-o', 'a.out'])
+        test(['src.c'], ['clang', '-c', '-I', './include', 'src.c'])
+        test(['src.c'], ['clang', '-c', '-I', '/opt/me/include', 'src.c'])
+        test(['src.c'], ['clang', '-c', '-D', 'config=file.c', 'src.c'])
+
+        self.assertIsNone(
+            sut.split_command(['cc', 'this.o', 'that.o', '-o', 'a.out']))
+        self.assertIsNone(
+            sut.split_command(['cc', 'this.o', '-lthat', '-o', 'a.out']))
+
+    def test_filter_flags(self):
+        def test(expected, flags):
+            command = ['clang', '-c', 'src.c'] + flags
+            self.assertEqual(expected, sut.split_command(command).flags)
+
+        def same(expected):
+            test(expected, expected)
+
+        def filtered(flags):
+            test([], flags)
+
+        same([])
+        same(['-I', '/opt/me/include', '-DNDEBUG', '-ULIMITS'])
+        same(['-O', '-O2'])
+        same(['-m32', '-mmms'])
+        same(['-Wall', '-Wno-unused', '-g', '-funroll-loops'])
+
+        filtered([])
+        filtered(['-lclien', '-L/opt/me/lib', '-L', '/opt/you/lib'])
+        filtered(['-static'])
+        filtered(['-MD', '-MT', 'something'])
+        filtered(['-MMD', '-MF', 'something'])
+
+
+class SourceClassifierTest(unittest.TestCase):
+
+    def test_sources(self):
+        self.assertIsNone(sut.classify_source('file.o'))
+        self.assertIsNone(sut.classify_source('file.exe'))
+        self.assertIsNone(sut.classify_source('/path/file.o'))
+        self.assertIsNone(sut.classify_source('clang'))
+
+        self.assertEqual('c', sut.classify_source('file.c'))
+        self.assertEqual('c', sut.classify_source('./file.c'))
+        self.assertEqual('c', sut.classify_source('/path/file.c'))
+        self.assertEqual('c++', sut.classify_source('file.c', False))
+        self.assertEqual('c++', sut.classify_source('./file.c', False))
+        self.assertEqual('c++', sut.classify_source('/path/file.c', False))

Modified: cfe/trunk/tools/scan-build-py/tests/unit/test_intercept.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/test_intercept.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/test_intercept.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/unit/test_intercept.py Tue Apr 19 07:03:03 2016
@@ -4,62 +4,37 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
+import libear
 import libscanbuild.intercept as sut
-from . import fixtures
+import unittest
 import os.path
 
 
-class InterceptUtilTest(fixtures.TestCase):
-
-    def test_is_compiler_call_filter(self):
-        def test(command):
-            return sut.is_compiler_call({'command': [command]})
-
-        self.assertTrue(test('clang'))
-        self.assertTrue(test('clang-3.6'))
-        self.assertTrue(test('clang++'))
-        self.assertTrue(test('clang++-3.5.1'))
-        self.assertTrue(test('cc'))
-        self.assertTrue(test('c++'))
-        self.assertTrue(test('gcc'))
-        self.assertTrue(test('g++'))
-        self.assertTrue(test('/usr/local/bin/gcc'))
-        self.assertTrue(test('/usr/local/bin/g++'))
-        self.assertTrue(test('/usr/local/bin/clang'))
-        self.assertTrue(test('armv7_neno-linux-gnueabi-g++'))
-
-        self.assertFalse(test(''))
-        self.assertFalse(test('ld'))
-        self.assertFalse(test('as'))
-        self.assertFalse(test('/usr/local/bin/compiler'))
+class InterceptUtilTest(unittest.TestCase):
 
     def test_format_entry_filters_action(self):
         def test(command):
-            return list(sut.format_entry(
-                {'command': command, 'directory': '/opt/src/project'}))
+            trace = {'command': command, 'directory': '/opt/src/project'}
+            return list(sut.format_entry(trace))
 
         self.assertTrue(test(['cc', '-c', 'file.c', '-o', 'file.o']))
         self.assertFalse(test(['cc', '-E', 'file.c']))
         self.assertFalse(test(['cc', '-MM', 'file.c']))
         self.assertFalse(test(['cc', 'this.o', 'that.o', '-o', 'a.out']))
-        self.assertFalse(test(['cc', '-print-prog-name']))
 
     def test_format_entry_normalize_filename(self):
-        directory = os.path.join(os.sep, 'home', 'me', 'project')
+        parent = os.path.join(os.sep, 'home', 'me')
+        current = os.path.join(parent, 'project')
 
-        def test(command):
-            result = list(sut.format_entry(
-                {'command': command, 'directory': directory}))
-            return result[0]['file']
-
-        self.assertEqual(test(['cc', '-c', 'file.c']),
-                         os.path.join(directory, 'file.c'))
-        self.assertEqual(test(['cc', '-c', './file.c']),
-                         os.path.join(directory, 'file.c'))
-        self.assertEqual(test(['cc', '-c', '../file.c']),
-                         os.path.join(os.path.dirname(directory), 'file.c'))
-        self.assertEqual(test(['cc', '-c', '/opt/file.c']),
-                         '/opt/file.c')
+        def test(filename):
+            trace = {'directory': current, 'command': ['cc', '-c', filename]}
+            return list(sut.format_entry(trace))[0]['file']
+
+        self.assertEqual(os.path.join(current, 'file.c'), test('file.c'))
+        self.assertEqual(os.path.join(current, 'file.c'), test('./file.c'))
+        self.assertEqual(os.path.join(parent, 'file.c'), test('../file.c'))
+        self.assertEqual(os.path.join(current, 'file.c'),
+                         test(os.path.join(current, 'file.c')))
 
     def test_sip(self):
         def create_status_report(filename, message):
@@ -92,7 +67,7 @@ class InterceptUtilTest(fixtures.TestCas
         OSX = 'darwin'
         LINUX = 'linux'
 
-        with fixtures.TempDir() as tmpdir:
+        with libear.TemporaryDirectory() as tmpdir:
             try:
                 saved = os.environ['PATH']
                 os.environ['PATH'] = tmpdir + ':' + saved

Added: cfe/trunk/tools/scan-build-py/tests/unit/test_libear.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/test_libear.py?rev=266726&view=auto
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/test_libear.py (added)
+++ cfe/trunk/tools/scan-build-py/tests/unit/test_libear.py Tue Apr 19 07:03:03 2016
@@ -0,0 +1,30 @@
+# -*- 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 libear as sut
+import unittest
+import os.path
+
+
+class TemporaryDirectoryTest(unittest.TestCase):
+    def test_creates_directory(self):
+        dirname = None
+        with sut.TemporaryDirectory() as tmpdir:
+            self.assertTrue(os.path.isdir(tmpdir))
+            dirname = tmpdir
+        self.assertIsNotNone(dirname)
+        self.assertFalse(os.path.exists(dirname))
+
+    def test_removes_directory_when_exception(self):
+        dirname = None
+        try:
+            with sut.TemporaryDirectory() as tmpdir:
+                self.assertTrue(os.path.isdir(tmpdir))
+                dirname = tmpdir
+                raise RuntimeError('message')
+        except:
+            self.assertIsNotNone(dirname)
+            self.assertFalse(os.path.exists(dirname))

Modified: cfe/trunk/tools/scan-build-py/tests/unit/test_report.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/test_report.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/test_report.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/unit/test_report.py Tue Apr 19 07:03:03 2016
@@ -4,15 +4,15 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
+import libear
 import libscanbuild.report as sut
-from . import fixtures
 import unittest
 import os
 import os.path
 
 
 def run_bug_parse(content):
-    with fixtures.TempDir() as tmpdir:
+    with libear.TemporaryDirectory() as tmpdir:
         file_name = os.path.join(tmpdir, 'test.html')
         with open(file_name, 'w') as handle:
             handle.writelines(content)
@@ -21,7 +21,7 @@ def run_bug_parse(content):
 
 
 def run_crash_parse(content, preproc):
-    with fixtures.TempDir() as tmpdir:
+    with libear.TemporaryDirectory() as tmpdir:
         file_name = os.path.join(tmpdir, preproc + '.info.txt')
         with open(file_name, 'w') as handle:
             handle.writelines(content)
@@ -77,20 +77,22 @@ class ParseFileTest(unittest.TestCase):
     def test_parse_real_crash(self):
         import libscanbuild.runner as sut2
         import re
-        with fixtures.TempDir() as tmpdir:
+        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 = {'directory': os.getcwd(),
-                    'clang': 'clang',
-                    'file': filename,
-                    'report': ['-fsyntax-only', '-E', filename],
-                    'language': 'c',
-                    'output_dir': tmpdir,
-                    'error_type': 'other_error',
-                    'error_output': 'some output',
-                    'exit_code': 13}
+            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
@@ -123,7 +125,7 @@ class ReportMethodTest(unittest.TestCase
                                                  '/prefix/src/file'))
 
 
-class GetPrefixFromCompilationDatabaseTest(fixtures.TestCase):
+class GetPrefixFromCompilationDatabaseTest(unittest.TestCase):
 
     def test_with_different_filenames(self):
         self.assertEqual(

Modified: cfe/trunk/tools/scan-build-py/tests/unit/test_runner.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/tests/unit/test_runner.py?rev=266726&r1=266725&r2=266726&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/tests/unit/test_runner.py (original)
+++ cfe/trunk/tools/scan-build-py/tests/unit/test_runner.py Tue Apr 19 07:03:03 2016
@@ -4,96 +4,164 @@
 # This file is distributed under the University of Illinois Open Source
 # License. See LICENSE.TXT for details.
 
+import libear
 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 FilteringFlagsTest(unittest.TestCase):
+
+    def test_language_captured(self):
+        def test(flags):
+            cmd = ['clang', '-c', 'source.c'] + flags
+            opts = sut.classify_parameters(cmd)
+            return opts['language']
+
+        self.assertEqual(None, test([]))
+        self.assertEqual('c', test(['-x', 'c']))
+        self.assertEqual('cpp', test(['-x', 'cpp']))
+
+    def test_arch(self):
+        def test(flags):
+            cmd = ['clang', '-c', 'source.c'] + flags
+            opts = sut.classify_parameters(cmd)
+            return opts['arch_list']
+
+        self.assertEqual([], test([]))
+        self.assertEqual(['mips'], test(['-arch', 'mips']))
+        self.assertEqual(['mips', 'i386'],
+                         test(['-arch', 'mips', '-arch', 'i386']))
+
+    def assertFlagsChanged(self, expected, flags):
+        cmd = ['clang', '-c', 'source.c'] + flags
+        opts = sut.classify_parameters(cmd)
+        self.assertEqual(expected, opts['flags'])
+
+    def assertFlagsUnchanged(self, flags):
+        self.assertFlagsChanged(flags, flags)
+
+    def assertFlagsFiltered(self, flags):
+        self.assertFlagsChanged([], flags)
+
+    def test_optimalizations_pass(self):
+        self.assertFlagsUnchanged(['-O'])
+        self.assertFlagsUnchanged(['-O1'])
+        self.assertFlagsUnchanged(['-Os'])
+        self.assertFlagsUnchanged(['-O2'])
+        self.assertFlagsUnchanged(['-O3'])
+
+    def test_include_pass(self):
+        self.assertFlagsUnchanged([])
+        self.assertFlagsUnchanged(['-include', '/usr/local/include'])
+        self.assertFlagsUnchanged(['-I.'])
+        self.assertFlagsUnchanged(['-I', '.'])
+        self.assertFlagsUnchanged(['-I/usr/local/include'])
+        self.assertFlagsUnchanged(['-I', '/usr/local/include'])
+        self.assertFlagsUnchanged(['-I/opt', '-I', '/opt/otp/include'])
+        self.assertFlagsUnchanged(['-isystem', '/path'])
+        self.assertFlagsUnchanged(['-isystem=/path'])
+
+    def test_define_pass(self):
+        self.assertFlagsUnchanged(['-DNDEBUG'])
+        self.assertFlagsUnchanged(['-UNDEBUG'])
+        self.assertFlagsUnchanged(['-Dvar1=val1', '-Dvar2=val2'])
+        self.assertFlagsUnchanged(['-Dvar="val ues"'])
+
+    def test_output_filtered(self):
+        self.assertFlagsFiltered(['-o', 'source.o'])
+
+    def test_some_warning_filtered(self):
+        self.assertFlagsFiltered(['-Wall'])
+        self.assertFlagsFiltered(['-Wnoexcept'])
+        self.assertFlagsFiltered(['-Wreorder', '-Wunused', '-Wundef'])
+        self.assertFlagsUnchanged(['-Wno-reorder', '-Wno-unused'])
+
+    def test_compile_only_flags_pass(self):
+        self.assertFlagsUnchanged(['-std=C99'])
+        self.assertFlagsUnchanged(['-nostdinc'])
+        self.assertFlagsUnchanged(['-isystem', '/image/debian'])
+        self.assertFlagsUnchanged(['-iprefix', '/usr/local'])
+        self.assertFlagsUnchanged(['-iquote=me'])
+        self.assertFlagsUnchanged(['-iquote', 'me'])
+
+    def test_compile_and_link_flags_pass(self):
+        self.assertFlagsUnchanged(['-fsinged-char'])
+        self.assertFlagsUnchanged(['-fPIC'])
+        self.assertFlagsUnchanged(['-stdlib=libc++'])
+        self.assertFlagsUnchanged(['--sysroot', '/'])
+        self.assertFlagsUnchanged(['-isysroot', '/'])
+
+    def test_some_flags_filtered(self):
+        self.assertFlagsFiltered(['-g'])
+        self.assertFlagsFiltered(['-fsyntax-only'])
+        self.assertFlagsFiltered(['-save-temps'])
+        self.assertFlagsFiltered(['-init', 'my_init'])
+        self.assertFlagsFiltered(['-sectorder', 'a', 'b', 'c'])
+
+
+class Spy(object):
+    def __init__(self):
+        self.arg = None
+        self.success = 0
+
+    def call(self, params):
+        self.arg = params
+        return self.success
 
 
 class RunAnalyzerTest(unittest.TestCase):
 
+    @staticmethod
+    def run_analyzer(content, failures_report):
+        with libear.TemporaryDirectory() as tmpdir:
+            filename = os.path.join(tmpdir, 'test.cpp')
+            with open(filename, 'w') as handle:
+                handle.write(content)
+
+            opts = {
+                'clang': 'clang',
+                'directory': os.getcwd(),
+                'flags': [],
+                'direct_args': [],
+                'file': filename,
+                'output_dir': tmpdir,
+                'output_format': 'plist',
+                'output_failures': failures_report
+            }
+            spy = Spy()
+            result = sut.run_analyzer(opts, spy.call)
+            return (result, spy.arg)
+
     def test_run_analyzer(self):
         content = "int div(int n, int d) { return n / d; }"
-        (result, fwds) = run_analyzer(content, dict())
+        (result, fwds) = RunAnalyzerTest.run_analyzer(content, False)
         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())
+        (result, fwds) = RunAnalyzerTest.run_analyzer(content, False)
         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})
+        (_, fwds) = RunAnalyzerTest.run_analyzer(content, 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):
+class ReportFailureTest(unittest.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:
+        with libear.TemporaryDirectory() as tmpdir:
             # create input file
             filename = os.path.join(tmpdir, 'test.c')
             with open(filename, 'w') as handle:
@@ -101,15 +169,17 @@ class ReportFailureTest(fixtures.TestCas
             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}
+            opts = {
+                'clang': 'clang',
+                'directory': os.getcwd(),
+                'flags': [],
+                'file': filename,
+                'output_dir': tmpdir,
+                'language': 'c',
+                'error_type': 'other_error',
+                'error_output': error_msg,
+                'exit_code': 13
+            }
             sut.report_failure(opts)
             # verify the result
             result = dict()
@@ -126,57 +196,110 @@ class ReportFailureTest(fixtures.TestCas
             self.assertUnderFailures(pp_file)
             # info file generated and content dumped
             info_file = pp_file + '.info.txt'
-            self.assertIn(info_file, result)
+            self.assertTrue(info_file in 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.assertTrue(error_file in result)
             self.assertEqual([error_msg], result[error_file])
 
 
 class AnalyzerTest(unittest.TestCase):
 
-    def test_set_language(self):
+    def test_nodebug_macros_appended(self):
+        def test(flags):
+            spy = Spy()
+            opts = {'flags': flags, 'force_debug': True}
+            self.assertEqual(spy.success,
+                             sut.filter_debug_flags(opts, spy.call))
+            return spy.arg['flags']
+
+        self.assertEqual(['-UNDEBUG'], test([]))
+        self.assertEqual(['-DNDEBUG', '-UNDEBUG'], test(['-DNDEBUG']))
+        self.assertEqual(['-DSomething', '-UNDEBUG'], test(['-DSomething']))
+
+    def test_set_file_relative_path(self):
         def test(expected, input):
-            spy = fixtures.Spy()
+            spy = Spy()
+            self.assertEqual(spy.success,
+                             sut.set_file_path_relative(input, spy.call))
+            self.assertEqual(expected, spy.arg['file'])
+
+        test('source.c',
+             {'file': '/home/me/source.c', 'directory': '/home/me'})
+        test('me/source.c',
+             {'file': '/home/me/source.c', 'directory': '/home'})
+        test('../home/me/source.c',
+             {'file': '/home/me/source.c', 'directory': '/tmp'})
+
+    def test_set_language_fall_through(self):
+        def language(expected, input):
+            spy = Spy()
+            input.update({'compiler': 'c', 'file': 'test.c'})
             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
+        language('c',   {'language': 'c', 'flags': []})
+        language('c++', {'language': 'c++', 'flags': []})
+
+    def test_set_language_stops_on_not_supported(self):
+        spy = Spy()
+        input = {
+            'compiler': 'c',
+            'flags': [],
+            'file': 'test.java',
+            'language': 'java'
+        }
+        self.assertIsNone(sut.language_check(input, spy.call))
+        self.assertIsNone(spy.arg)
+
+    def test_set_language_sets_flags(self):
+        def flags(expected, input):
+            spy = Spy()
+            input.update({'compiler': 'c', 'file': 'test.c'})
+            self.assertEqual(spy.success, sut.language_check(input, spy.call))
+            self.assertEqual(expected, spy.arg['flags'])
 
-        input = {'key': 'value'}
-        self.assertEqual(input, test(input))
+        flags(['-x', 'c'],   {'language': 'c', 'flags': []})
+        flags(['-x', 'c++'], {'language': 'c++', 'flags': []})
 
-        input = {'archs_seen': ['i386']}
-        self.assertEqual({'arch': 'i386'}, test(input))
+    def test_set_language_from_filename(self):
+        def language(expected, input):
+            spy = Spy()
+            input.update({'language': None, 'flags': []})
+            self.assertEqual(spy.success, sut.language_check(input, spy.call))
+            self.assertEqual(expected, spy.arg['language'])
 
-        input = {'archs_seen': ['ppc']}
-        self.assertEqual(None, test(input))
+        language('c',   {'file': 'file.c',   'compiler': 'c'})
+        language('c++', {'file': 'file.c',   'compiler': 'c++'})
+        language('c++', {'file': 'file.cxx', 'compiler': 'c'})
+        language('c++', {'file': 'file.cxx', 'compiler': 'c++'})
+        language('c++', {'file': 'file.cpp', 'compiler': 'c++'})
+        language('c-cpp-output',   {'file': 'file.i', 'compiler': 'c'})
+        language('c++-cpp-output', {'file': 'file.i', 'compiler': 'c++'})
+
+    def test_arch_loop_sets_flags(self):
+        def flags(archs):
+            spy = Spy()
+            input = {'flags': [], 'arch_list': archs}
+            sut.arch_check(input, spy.call)
+            return spy.arg['flags']
 
-        input = {'archs_seen': ['i386', 'ppc']}
-        self.assertEqual({'arch': 'i386'}, test(input))
+        self.assertEqual([], flags([]))
+        self.assertEqual(['-arch', 'i386'], flags(['i386']))
+        self.assertEqual(['-arch', 'i386'], flags(['i386', 'ppc']))
+        self.assertEqual(['-arch', 'sparc'], flags(['i386', 'sparc']))
+
+    def test_arch_loop_stops_on_not_supported(self):
+        def stop(archs):
+            spy = Spy()
+            input = {'flags': [], 'arch_list': archs}
+            self.assertIsNone(sut.arch_check(input, spy.call))
+            self.assertIsNone(spy.arg)
 
-        input = {'archs_seen': ['i386', 'sparc']}
-        result = test(input)
-        self.assertTrue(result == {'arch': 'i386'} or
-                        result == {'arch': 'sparc'})
+        stop(['ppc'])
+        stop(['ppc64'])
 
 
 @sut.require([])
@@ -211,14 +334,3 @@ class RequireDecoratorTest(unittest.Test
 
     def test_method_exception_not_caught(self):
         self.assertRaises(Exception, method_exception_from_inside, dict())
-
-class ForceAnalyzeDebugTest(unittest.TestCase):
-
-    def test_force_analyze_debug_code(self):
-        for a, b in [
-                ([], ['-UNDEBUG']),
-                (['-O2'], ['-O2', '-UNDEBUG']),
-                (['-Dkey=val'], ['-Dkey=val', '-UNDEBUG']),
-                (['-D', 'NDEBUG'], ['-D', 'NDEBUG', '-UNDEBUG']) ]:
-            sut.force_analyze_debug_code(a)
-            self.assertEqual(a, b)




More information about the cfe-commits mailing list