diff --git a/.travis.yml b/.travis.yml index 80f510ea..d22f600f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,11 @@ python: - "2.6" - "2.7" install: - - pip install flake8 + - pip install -r python/test_requirements.txt --use-mirrors compiler: - gcc - clang -script: flake8 --exclude=jedi --select=F,C9 --max-complexity=10 python && ./install.sh +script: flake8 --exclude=jedi --select=F,C9 --max-complexity=10 python && nosetests python && ./install.sh env: - YCM_TESTRUN=1 EXTRA_CMAKE_ARGS="" - YCM_TESTRUN=1 EXTRA_CMAKE_ARGS="-DUSE_CLANG_COMPLETER=ON" diff --git a/python/completers/cpp/flags.py b/python/completers/cpp/flags.py index 8fefa558..8623e8b1 100644 --- a/python/completers/cpp/flags.py +++ b/python/completers/cpp/flags.py @@ -26,6 +26,7 @@ NO_EXTRA_CONF_FILENAME_MESSAGE = ('No {0} file detected, so no compile flags ' 'are available. Thus no semantic support for C/C++/ObjC/ObjC++. See the ' 'docs for details.').format( extra_conf_store.YCM_EXTRA_CONF_FILENAME ) + class Flags( object ): """Keeps track of the flags necessary to compile a file. The flags are loaded from user-created python files (hereafter referred to as @@ -121,10 +122,18 @@ def _SanitizeFlags( flags ): def _RemoveUnusedFlags( flags, filename ): """Given an iterable object that produces strings (flags for Clang), removes the '-c' and '-o' options that Clang does not like to see when it's producing - completions for a file.""" + completions for a file. Also removes the first flag in the list if it does not + start with a '-' (it's highly likely to be the compiler name/path).""" new_flags = [] - skip = True + + # When flags come from the compile_commands.json file, the first flag is + # usually the path to the compiler that should be invoked. We want to strip + # that. + if not flags[ 0 ].startswith( '-' ): + flags = flags[ 1: ] + + skip = False for flag in flags: if skip: skip = False @@ -137,7 +146,7 @@ def _RemoveUnusedFlags( flags, filename ): skip = True; continue - if flag == filename or os.path.realpath(flag) == filename: + if flag == filename or os.path.realpath( flag ) == filename: continue new_flags.append( flag ) diff --git a/python/completers/cpp/tests/__init__.py b/python/completers/cpp/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/completers/cpp/tests/flags_test.py b/python/completers/cpp/tests/flags_test.py new file mode 100644 index 00000000..b283b3a1 --- /dev/null +++ b/python/completers/cpp/tests/flags_test.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# +# Copyright (C) 2011, 2012 Strahinja Val Markovic +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe. If not, see . + +from nose.tools import eq_ +from ycm_test_utils import MockVimModule +vim_mock = MockVimModule() +from .. import flags + + +def SanitizeFlags_Passthrough_test(): + eq_( [ '-foo', '-bar' ], + list( flags._SanitizeFlags( [ '-foo', '-bar' ] ) ) ) + + +def SanitizeFlags_ArchRemoved_test(): + expected = [ '-foo', '-bar' ] + to_remove = [ '-arch', 'arch_of_evil' ] + + eq_( expected, + list( flags._SanitizeFlags( expected + to_remove ) ) ) + + eq_( expected, + list( flags._SanitizeFlags( to_remove + expected ) ) ) + + eq_( expected, + list( flags._SanitizeFlags( + expected[ :1 ] + to_remove + expected[ -1: ] ) ) ) + + +def RemoveUnusedFlags_Passthrough_test(): + eq_( [ '-foo', '-bar' ], + flags._RemoveUnusedFlags( [ '-foo', '-bar' ], 'file' ) ) + + +def RemoveUnusedFlags_RemoveCompilerPathIfFirst_test(): + def tester( path ): + eq_( expected, + flags._RemoveUnusedFlags( [ path ] + expected, filename ) ) + + compiler_paths = [ 'c++', 'c', 'gcc', 'g++', 'clang', 'clang++', + '/usr/bin/c++', '/some/other/path', 'some_command' ] + expected = [ '-foo', '-bar' ] + filename = 'file' + + for compiler in compiler_paths: + yield tester, compiler + + +def RemoveUnusedFlags_RemoveDashC_test(): + expected = [ '-foo', '-bar' ] + to_remove = [ '-c' ] + filename = 'file' + + eq_( expected, + flags._RemoveUnusedFlags( expected + to_remove, filename ) ) + + eq_( expected, + flags._RemoveUnusedFlags( to_remove + expected, filename ) ) + + eq_( expected, + flags._RemoveUnusedFlags( + expected[ :1 ] + to_remove + expected[ -1: ], filename ) ) + + +def RemoveUnusedFlags_RemoveDashO_test(): + expected = [ '-foo', '-bar' ] + to_remove = [ '-o', 'output_name' ] + filename = 'file' + + eq_( expected, + flags._RemoveUnusedFlags( expected + to_remove, filename ) ) + + eq_( expected, + flags._RemoveUnusedFlags( to_remove + expected, filename ) ) + + eq_( expected, + flags._RemoveUnusedFlags( + expected[ :1 ] + to_remove + expected[ -1: ], filename ) ) + + +def RemoveUnusedFlags_RemoveFilename_test(): + expected = [ '-foo', '-bar' ] + to_remove = [ 'file' ] + filename = 'file' + + eq_( expected, + flags._RemoveUnusedFlags( expected + to_remove, filename ) ) + + eq_( expected, + flags._RemoveUnusedFlags( to_remove + expected, filename ) ) + + eq_( expected, + flags._RemoveUnusedFlags( + expected[ :1 ] + to_remove + expected[ -1: ], filename ) ) + diff --git a/python/test_requirements.txt b/python/test_requirements.txt new file mode 100644 index 00000000..65164842 --- /dev/null +++ b/python/test_requirements.txt @@ -0,0 +1,3 @@ +flake8>=2.0 +mock>=1.0.1 +nose>=1.3.0 diff --git a/python/ycm_test_utils.py b/python/ycm_test_utils.py new file mode 100644 index 00000000..4b48ef60 --- /dev/null +++ b/python/ycm_test_utils.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# +# Copyright (C) 2011, 2012 Strahinja Val Markovic +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe. If not, see . + +from mock import MagicMock +import sys + +def MockVimModule(): + """The 'vim' module is something that is only present when running inside the + Vim Python interpreter, so we replace it with a MagicMock for tests. """ + + vim_mock = MagicMock() + vim_mock.eval = MagicMock( return_value = '' ) + sys.modules[ 'vim' ] = vim_mock + return vim_mock