waf_unit_test: add new option --dump-test-runner that creates python scripts to manually run or debug test executions

this can be very useful when debugging unit test problems as when run outside waf then the LD_LIBRARY_PATH (or PYTHONPATH if used with pytest extra) is set dynamically by waf and therefore running the executable manually or via gdb is not immediate
all the environment will be dumped in python script that can then be executed to run manually tests.
This commit is contained in:
fedepell 2016-11-11 11:07:52 +01:00 committed by ita1024
parent 24cc1dc057
commit e8942d7f22
1 changed files with 47 additions and 1 deletions

View File

@ -31,9 +31,13 @@ the predefined callback::
bld(features='cxx cxxprogram test', source='main.c', target='app')
from waflib.Tools import waf_unit_test
bld.add_post_fun(waf_unit_test.summary)
By passing --dump-test-runner the execution will also create in the build directory a python file named as the task name with -run.py appended
that can be executed manually to reproduce the exact execution manually for debugging purposes.
If the script is run with arguments these will be used instead of the command itself. This can be used to run for example a debugger or profiler on the test manually.
"""
import os
import os, sys
from waflib.TaskGen import feature, after_method, taskgen_method
from waflib import Utils, Task, Logs, Options
from waflib.Tools import ccroot
@ -151,6 +155,13 @@ class utest(Task.Task):
def exec_command(self, cmd, **kw):
Logs.debug('runner: %r', cmd)
if getattr(Options.options, 'dump_test_runner', False):
if not self.generator.name:
Logs.warn('waf_unit_test: --dump-test-runner task with no name, outputs may collide')
runnername=os.path.join(str(self.generator.path.get_bld()), ( "%s-run.py" % (self.generator.name or 'dump-test-runner')))
Utils.writef(runnername, ENVFILE_TEXT % { 'pyexe': sys.executable, 'env': self.get_test_env(), 'cwd': self.get_cwd().abspath(), 'cmd': cmd[0], 'cmdline': cmd })
os.chmod(runnername, Utils.O755)
proc = Utils.subprocess.Popen(cmd, cwd=self.get_cwd().abspath(), env=self.get_test_env(),
stderr=Utils.subprocess.PIPE, stdout=Utils.subprocess.PIPE)
(stdout, stderr) = proc.communicate()
@ -224,4 +235,39 @@ def options(opt):
help = 'Run the unit tests using the test-cmd string'
' example "--test-cmd="valgrind --error-exitcode=1'
' %s" to run under valgrind', dest='testcmd')
opt.add_option('--dump-test-runner', action='store_true', default=False,
help='Create a python script with full environment to run the test manually for debugging purposes', dest='dump_test_runner')
"""
ENVFILE_TEXT specifies the contents of the test runner script generated if
--dump-test-runner is used. This can be used to manually run tests which are
failing in the exact environment as waf tests are run.
By default when executed it will just run the test and exit. If any arguments
are passed then those are executed. The use case is for example to pass
'bash' and this will open the shell when gdb can then be used in the same
environment as waf would have run the test.
Test command line is exported as $UT_CMDLINE so this can be used inside the
executed environment.
To execute directly gdb something like 'bash -c "gdb \$UT_CMDLINE"' can be
specified as argument as well.
"""
ENVFILE_TEXT = """#! %(pyexe)s
# If executed with no parametrs the script will run the test exactly as waf
# If parameters are passed they will be executed instead but in a waf-like
# environment. Example executions:
# Create a shell when you can manually invoke gdb or other tools:
# <script> bash
# Start immediately gdb:
# <script> bash -c "gdb \$UT_CMDLINE"
import subprocess, sys
env = %(env)s
env['UT_CMDLINE'] = " ".join(%(cmdline)s)
if len(sys.argv) > 1:
status = subprocess.call(sys.argv[1:], env = env, cwd = '%(cwd)s')
else:
status = subprocess.call(%(cmdline)s , env = env, cwd = '%(cwd)s')
sys.exit(status)
"""