Skip to content

Commit

Permalink
Initial port to unittest
Browse files Browse the repository at this point in the history
  • Loading branch information
bstaletic committed Sep 7, 2021
1 parent 5fdb530 commit 9af7f0f
Show file tree
Hide file tree
Showing 122 changed files with 26,966 additions and 27,619 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
echo -e "import coverage\ncoverage.process_startup()" > $(python -c "print(__import__('sysconfig').get_path('purelib'))")/sitecustomize.py
- name: Run tests
if: matrix.benchmark == false
run: python3 run_tests.py --no-parallel --quiet
run: python3 run_tests.py --quiet
- name: Run benchmarks
if: matrix.benchmark == true
run: python3 benchmark.py --quiet
Expand Down Expand Up @@ -118,7 +118,7 @@ jobs:
- name: Lint
run: |
YCM_TESTRUN=1 python3 build.py --clang-completer --clang-tidy --valgrind
python3 run_tests.py --valgrind --skip-build --no-flake8 --no-parallel --quiet
python3 run_tests.py --valgrind --skip-build --no-flake8 --quiet
windows:
strategy:
Expand Down Expand Up @@ -174,7 +174,7 @@ jobs:
run: python3 benchmark.py --msvc ${{ matrix.msvc }} --quiet
- name: Run tests
if: matrix.benchmark == false
run: python3 run_tests.py --msvc ${{ matrix.msvc }} --no-parallel --quiet
run: python3 run_tests.py --msvc ${{ matrix.msvc }} --quiet
- name: Upload coverage data
if: matrix.benchmark == false
run: codecov --name ${{ matrix.runs-on }}-${{ matrix.python-arch }} >null
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ third_party/clangd
# Rust
third_party/rust-analyzer
ycmd/tests/rust/testdata/common/target
ycmd/tests/rust/testdata/formatting/target
ycmd/tests/rust/testdata/macro/target

# generic LSP
third_party/generic_server/node_modules
Expand Down
14 changes: 6 additions & 8 deletions TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@ To run the full suite, just run `run_tests.py`. Options are:
Windows only;
* `--coverage`: generate code coverage data.

Remaining arguments are passed to "pytest" directly. This means that you
Remaining arguments are passed to "unittest" directly. This means that you
can run a specific script or a specific test as follows:

* Specific script: `./run_tests.py ycmd/tests/<module_name>.py`
* Specific test: `./run_tests.py ycmd/tests/<module_name>.py:<function name>`
* Specific test: `./run_tests.py -k <test name> ycmd/tests/<module_name>.py`

For example:

* `./run_tests.py ycmd/tests/subcommands_test.py`
* `./run_tests.py ycmd/tests/subcommands_test.py:Subcommands_Basic_test`
* `./run_tests.py -k Subcommands_Basic_test ycmd/tests/subcommands_test.py`

NOTE: you must have UTF-8 support in your terminal when you do this, e.g.:

Expand All @@ -76,9 +76,8 @@ C++ coverage testing is available only on Linux/Mac and uses gcov.
Stricly speaking, we use the `-coverage` option to your compiler, which in the
case of GNU and LLVM compilers, generate gcov-compatible data.

For Python, there's a coverage module which works nicely with `pytest`. This
is very useful for highlighting areas of your code which are not covered by the
automated integration tests.
For Python, there's a coverage module. This is very useful for highlighting
areas of your code which are not covered by the automated integration tests.

Run it like this:

Expand All @@ -88,8 +87,7 @@ Run it like this:

This will print a summary and generate HTML output in `./cover`.

More information: https://coverage.readthedocs.org and
https://pytest-cov.readthedocs.io/en/stable/
More information: https://coverage.readthedocs.org

## Troubleshooting

Expand Down
9 changes: 0 additions & 9 deletions pytest.ini

This file was deleted.

74 changes: 31 additions & 43 deletions run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
import sys
import urllib.request

BASE_PYTEST_ARGS = [ '-v', '--color=yes' ]

BASE_UNITTEST_ARGS = [ '-p', '*_test.py' ]
DIR_OF_THIS_SCRIPT = p.dirname( p.abspath( __file__ ) )
DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, 'third_party' )
DIR_OF_WATCHDOG_DEPS = p.join( DIR_OF_THIRD_PARTY, 'watchdog_deps' )
Expand Down Expand Up @@ -161,18 +160,15 @@ def ParseArguments():
parser.add_argument( '--valgrind',
action = 'store_true',
help = 'Run tests inside valgrind.' )
parser.add_argument( '--no-parallel', action='store_true',
help='Run tests in serial, default is to parallelize '
'tests execution' )

parsed_args, pytests_args = parser.parse_known_args()
parsed_args, unittest_args = parser.parse_known_args()

parsed_args.completers = FixupCompleters( parsed_args )

if 'COVERAGE' in os.environ:
parsed_args.coverage = ( os.environ[ 'COVERAGE' ] == 'true' )

return parsed_args, pytests_args
return parsed_args, unittest_args


def FixupCompleters( parsed_args ):
Expand Down Expand Up @@ -228,22 +224,22 @@ def BuildYcmdLibs( args ):
subprocess.check_call( build_cmd )


def PytestValgrind( parsed_args, extra_pytests_args ):
pytests_args = BASE_PYTEST_ARGS
def UnittestValgrind( parsed_args, extra_unittest_args ):
unittest_args = BASE_UNITTEST_ARGS
if parsed_args.quiet:
pytests_args[ 0 ] = '-q'
unittest_args.append( '-q' )

if extra_pytests_args:
pytests_args.extend( extra_pytests_args )
if extra_unittest_args:
unittest_args.extend( extra_unittest_args )
else:
pytests_args += glob.glob(
unittest_args += glob.glob(
p.join( DIR_OF_THIS_SCRIPT, 'ycmd', 'tests', 'bindings', '*_test.py' ) )
pytests_args += glob.glob(
unittest_args += glob.glob(
p.join( DIR_OF_THIS_SCRIPT, 'ycmd', 'tests', 'clang', '*_test.py' ) )
pytests_args += glob.glob(
unittest_args += glob.glob(
p.join( DIR_OF_THIS_SCRIPT, 'ycmd', 'tests', '*_test.py' ) )
# Avoids needing all completers for a valgrind run
pytests_args += [ '-m', 'not valgrind_skip' ]
# # Avoids needing all completers for a valgrind run
# unittest_args += [ '-m', 'not valgrind_skip' ]

new_env = os.environ.copy()
new_env[ 'PYTHONMALLOC' ] = 'malloc'
Expand All @@ -257,36 +253,25 @@ def PytestValgrind( parsed_args, extra_pytests_args ):
'--suppressions=' + p.join( DIR_OF_THIS_SCRIPT,
'valgrind.suppressions' ) ]
subprocess.check_call( cmd +
[ sys.executable, '-m', 'pytest' ] +
pytests_args,
[ sys.executable, '-m', 'unittest' ] +
unittest_args,
env = new_env )


def PytestTests( parsed_args, extra_pytests_args ):
pytests_args = BASE_PYTEST_ARGS
def UnittestTests( parsed_args, extra_unittest_args ):
unittest_args = BASE_UNITTEST_ARGS
if parsed_args.quiet:
pytests_args[ 0 ] = '-q'
unittest_args.append( '-q' )

for key in COMPLETERS:
if key not in parsed_args.completers:
pytests_args.extend( COMPLETERS[ key ][ 'test' ] )

if parsed_args.coverage:
# We need to exclude the ycmd/tests/python/testdata directory since it
# contains Python files and its base name starts with "test".
pytests_args += [ '--ignore=ycmd/tests/python/testdata', '--cov=ycmd' ]

if not parsed_args.no_parallel:
# Execute tests in parallel with n workers where n = NUMCPUS. Tests are
# grouped by module for test functions and by class for test methods.Groups
# are distributed to available workers as whole units. This guarantees that
# all tests in a group run in the same process.
pytests_args += [ '-n', 'auto', '--dist', 'loadscope' ]

if extra_pytests_args:
pytests_args.extend( extra_pytests_args )
unittest_args.extend( COMPLETERS[ key ][ 'test' ] )

if extra_unittest_args:
unittest_args.extend( extra_unittest_args )
else:
pytests_args.append( p.join( DIR_OF_THIS_SCRIPT, 'ycmd' ) )
unittest_args.append( '-s' )
unittest_args.append( p.join( DIR_OF_THIS_SCRIPT, 'ycmd', 'tests' ) )

env = os.environ.copy()

Expand All @@ -301,7 +286,10 @@ def PytestTests( parsed_args, extra_pytests_args ):
else:
env[ 'LD_LIBRARY_PATH' ] = LIBCLANG_DIR

subprocess.check_call( [ sys.executable, '-m', 'pytest' ] + pytests_args,
subprocess.check_call( [ sys.executable,
'-m',
'unittest',
'discover' ] + unittest_args,
env=env )


Expand Down Expand Up @@ -371,7 +359,7 @@ def SetUpJavaCompleter():


def Main():
parsed_args, pytests_args = ParseArguments()
parsed_args, unittest_args = ParseArguments()
if parsed_args.dump_path:
print( os.environ[ 'PYTHONPATH' ] )
sys.exit()
Expand All @@ -384,9 +372,9 @@ def Main():
RunFlake8()
BuildYcmdLibs( parsed_args )
if parsed_args.valgrind:
PytestValgrind( parsed_args, pytests_args )
UnittestValgrind( parsed_args, unittest_args )
else:
PytestTests( parsed_args, pytests_args )
UnittestTests( parsed_args, unittest_args )


if __name__ == "__main__":
Expand Down
4 changes: 0 additions & 4 deletions test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,4 @@ WebTest >= 2.0.20
psutil >= 5.6.6
coverage >= 4.2
codecov >= 2.0.5
pytest
pytest-cov
pytest-rerunfailures
requests
pytest-xdist
32 changes: 30 additions & 2 deletions ycmd/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2016-2020 ycmd contributors
# Copyright (C) 2016-2021 ycmd contributors
#
# This file is part of ycmd.
#
Expand All @@ -15,10 +15,38 @@
# You should have received a copy of the GNU General Public License
# along with ycmd. If not, see <http://www.gnu.org/licenses/>.

import functools
import os
from ycmd.tests.conftest import * # noqa
from ycmd.tests.test_utils import ClearCompletionsCache, IsolatedApp, SetUpApp

shared_app = None


def PathToTestFile( *args ):
dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) )
return os.path.join( dir_of_current_script, 'testdata', *args )


def SharedYcmd( test ):
"""Defines a decorator to be attached to tests of this package. This decorator
passes the shared ycmd application as a parameter.
Do NOT attach it to test generators but directly to the yielded tests."""
global shared_app
if shared_app is None:
shared_app = SetUpApp()

@functools.wraps( test )
def Wrapper( *args, **kwargs ):
ClearCompletionsCache()
return test( args[ 0 ], shared_app, *args[ 1: ], **kwargs )
return Wrapper


def IsolatedYcmd( custom_options = {} ):
def Decorator( test ):
@functools.wraps( test )
def Wrapper( *args, **kwargs ):
with IsolatedApp( custom_options ) as app:
test( args[ 0 ], app, *args[ 1: ], **kwargs )
return Wrapper
return Decorator
Loading

0 comments on commit 9af7f0f

Please sign in to comment.