mirror of
https://github.com/boostorg/build.git
synced 2026-02-16 01:12:13 +00:00
First version of regression testing support in V2.
* new/testing.jam: Loots of changes. * new/virtual-target.jam: (action.path): Handle <location-prefix> property. [SVN r20188]
This commit is contained in:
308
new/testing.jam
308
new/testing.jam
@@ -1,67 +1,303 @@
|
||||
# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
|
||||
# (C) Copyright David Abrahams 2002.
|
||||
# (C) Copyright Vladimir Prus 2002-2003.
|
||||
# Permission to copy, use, modify, sell and
|
||||
# distribute this software is granted provided this copyright notice appears in
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
# Testing support.
|
||||
# This module implements regression testing framework. It declares a number of
|
||||
# main target rules, which perform some action, and if the results are ok,
|
||||
# creates an output file.
|
||||
#
|
||||
# The exact list of rules is:
|
||||
# 'compile' -- creates .test file if compilation of sources was successfull
|
||||
# 'compile-fail' -- creates .test file if compilation of sources failed
|
||||
# 'run' -- creates .test file is running of executable produced from
|
||||
# sources was successfull. Also leaves behing .output file
|
||||
# with the output from program run.
|
||||
# 'run-fail' -- same as above, but .test file is created if running fails.
|
||||
# 'unit-test' -- same as 'run', except that output is not stored.
|
||||
#
|
||||
# In all cases, except for 'unit-test', presense of .test file is an incication that
|
||||
# the test passed. For more convenient reporting, you might want to use C++ Boost
|
||||
# regression testing utilities, see
|
||||
# http://www.boost.org/more/regression.html
|
||||
|
||||
# Things to do/consider:
|
||||
# - No-skipping on <threading>multi
|
||||
# - Teach compiler_status handle Jamfile.v2.
|
||||
# - Grab RUN_PATH logic from V1 testing.jam
|
||||
# - Implement all the parameters to 'run': args/input_files
|
||||
# - Add link/link-fail?
|
||||
# - <no-warn> is not implemented, since in Como-specific, and it's not clear how
|
||||
# to implement it
|
||||
# - std::locale-support is not impelemted (it's used in some tests).
|
||||
|
||||
|
||||
import targets ;
|
||||
import "class" : new ;
|
||||
import property ;
|
||||
import feature ;
|
||||
import toolset ;
|
||||
import alias ;
|
||||
import type ;
|
||||
import generators ;
|
||||
import project ;
|
||||
import property-set ;
|
||||
import virtual-target ;
|
||||
import path ;
|
||||
|
||||
rule init ( ) { }
|
||||
|
||||
# The feature which controls the name of program used to
|
||||
# lanch test programs.
|
||||
feature.feature testing.launcher : : optional free ;
|
||||
feature.feature location-prefix : : free ;
|
||||
feature.feature test-info : : free incidental ;
|
||||
|
||||
class unit-test-target-class : typed-target
|
||||
# Register target types.
|
||||
type.register TEST : test ;
|
||||
type.register COMPILE : : TEST : main ;
|
||||
type.register COMPILE_FAIL : : TEST : main ;
|
||||
type.register RUN_OUTPUT : run : : main ;
|
||||
type.register RUN : : TEST : main ;
|
||||
type.register RUN_FAIL : : TEST : main ;
|
||||
type.register UNIT_TEST : passed : TEST : main ;
|
||||
|
||||
# Declare the rules which create main targets.
|
||||
# While the 'type' module already creates rules with the same names for us,
|
||||
# we need extra convenience: default name of main target, so write
|
||||
# our own versions.
|
||||
|
||||
# Helper rule. Create a test target, using basename of first source of no
|
||||
# target name is explicitly passed. Remembers the created target in
|
||||
# a global variable.
|
||||
rule make-test ( target-type : sources + : requirements * : target-name ? )
|
||||
{
|
||||
rule __init__ ( name : project : sources * : requirements *
|
||||
: default-build * )
|
||||
{
|
||||
typed-target.__init__ $(name) : $(project) : EXE : $(sources)
|
||||
: $(requirements) : $(default-build) ;
|
||||
}
|
||||
|
||||
rule construct ( source-targets * : properties * )
|
||||
{
|
||||
local result =
|
||||
[ typed-target.construct $(source-targets) : $(properties) ] ;
|
||||
local exe = $(result[1]) ;
|
||||
local exe-action = [ $(exe).action ] ;
|
||||
local timestamp = [ new file-target $(self.name) : : $(self.project) ] ;
|
||||
$(timestamp).suffix "passed" ;
|
||||
target-name ?= $(sources[1]:D=:S=) ;
|
||||
|
||||
local a = [ new action $(timestamp) : $(exe) : testing.run :
|
||||
[ $(exe-action).properties-ps ] ] ;
|
||||
$(timestamp).action $(a) ;
|
||||
return $(timestamp) ;
|
||||
}
|
||||
local project = [ CALLER_MODULE 1 ] ;
|
||||
# The <location-prefix> forces the build system for generate paths in the form
|
||||
# $build_dir/array1.test/gcc/debug
|
||||
# This is necessary to allow post-processing tools to work.
|
||||
local t =
|
||||
[ targets.create-typed-target
|
||||
[ type.type-from-rule-name $(target-type) ] : $(project)
|
||||
: $(target-name) : $(sources)
|
||||
: $(requirements) <location-prefix>$(target-name).test ] ;
|
||||
|
||||
# Remember the test (for --dump-test).
|
||||
# A good way would be to collect all given a project.
|
||||
# This has some technical problems: e.g. we can't call this dump from
|
||||
# Jamfile since projects referred by 'build-project' are not available until
|
||||
# whole Jamfile is loaded.
|
||||
.all-tests += $(t) ;
|
||||
return $(t) ;
|
||||
}
|
||||
|
||||
toolset.flags testing.run LAUNCHER <testing.launcher> ;
|
||||
rule compile ( sources + : requirements * : target-name ? )
|
||||
{
|
||||
return [ make-test compile : $(sources) : $(requirements) : $(target-name) ] ;
|
||||
}
|
||||
|
||||
rule compile-fail ( sources + : requirements * : target-name ? )
|
||||
{
|
||||
return [ make-test compile-fail : $(sources) : $(requirements) : $(target-name) ] ;
|
||||
}
|
||||
|
||||
rule run ( sources + : args * : input-files * : requirements * : target-name ?
|
||||
: default-build * )
|
||||
{
|
||||
if $(args) || $(input-files) || $(default-build)
|
||||
{
|
||||
EXIT "NOT supported" ;
|
||||
}
|
||||
return [ make-test run : $(sources) : $(requirements) : $(target-name) ] ;
|
||||
}
|
||||
|
||||
rule run-fail ( sources + : args * : input-files * : requirements * : target-name ?
|
||||
: default-build * )
|
||||
{
|
||||
if $(args) || $(input-files) || $(default-build)
|
||||
{
|
||||
EXIT "NOT supported" ;
|
||||
}
|
||||
return [ make-test run-fail : $(sources) : $(requirements) : $(target-name) ] ;
|
||||
}
|
||||
|
||||
|
||||
actions run
|
||||
# Rule for grouping tests in suites.
|
||||
rule test-suite ( suite-name : tests + )
|
||||
{
|
||||
# In V2, if 'tests' are instances of 'abstract-target', they will be considered
|
||||
# 'inline-targets' and will suffer some adjustments. This will not be compatible
|
||||
# with V1 behaviour, so we get names of 'tests' and use them.
|
||||
local names ;
|
||||
for local t in $(tests)
|
||||
{
|
||||
names += [ $(t).name ] ;
|
||||
}
|
||||
modules.call-in [ CALLER_MODULE ] : alias $(suite-name) : $(names) ;
|
||||
}
|
||||
|
||||
# For all main target in 'project-module',
|
||||
# which are typed target with type derived from 'TEST',
|
||||
# produce some interesting information.
|
||||
rule dump-tests # ( project-module )
|
||||
{
|
||||
for local t in $(.all-tests)
|
||||
{
|
||||
dump-test $(t) ;
|
||||
}
|
||||
}
|
||||
|
||||
rule dump-test ( target )
|
||||
{
|
||||
local type = [ $(target).type ] ;
|
||||
local name = [ $(target).name ] ;
|
||||
local sources = [ $(target).sources ] ;
|
||||
local source-files ;
|
||||
for local s in $(sources)
|
||||
{
|
||||
if [ class.is-a $(s) : file-reference ]
|
||||
{
|
||||
source-files +=
|
||||
[ path.relative
|
||||
[ path.root [ $(s).location ] [ path.pwd ] ]
|
||||
/home/ghost/Work/boost ] ;
|
||||
}
|
||||
}
|
||||
|
||||
local r = [ $(t).requirements ] ;
|
||||
# Extract values of the <test-info> feature
|
||||
local test-info = [ $(r).get <test-info> ] ;
|
||||
|
||||
# Format them into a single string of quoted strings
|
||||
test-info = \"$(test-info:J=\"\ \")\" ;
|
||||
|
||||
ECHO boost-test($(type)) \"$(name)\"
|
||||
[$(test-info)]
|
||||
":" \"$(source-files)\"
|
||||
;
|
||||
}
|
||||
|
||||
# Register generators. Depending on target type, either
|
||||
# 'expect-success' or 'expect-failure' rule will be used.
|
||||
generators.register-standard testing.expect-success : OBJ : COMPILE ;
|
||||
generators.register-standard testing.expect-failure : OBJ : COMPILE_FAIL ;
|
||||
generators.register-standard testing.expect-success : RUN_OUTPUT : RUN ;
|
||||
generators.register-standard testing.expect-failure : RUN_OUTPUT : RUN_FAIL ;
|
||||
|
||||
# Generator which runs an EXE and captures output.
|
||||
generators.register-standard testing.capture-output : EXE : RUN_OUTPUT ;
|
||||
|
||||
# Generator which creates target if sources runs successfully.
|
||||
# Differers from RUN in that run output is not captured.
|
||||
generators.register-standard testing.unit-test : EXE : UNIT_TEST ;
|
||||
|
||||
# The action rules called by generators.
|
||||
|
||||
# Causes the 'target' to exist after bjam invocation if and only if all the
|
||||
# dependencies were successfully built.
|
||||
rule expect-success ( target : dependency + : requirements * )
|
||||
{
|
||||
**passed** $(target) : $(sources) ;
|
||||
}
|
||||
|
||||
# Causes the 'target' to exist after bjam invocation if and only if all some
|
||||
# of the dependencies were not successfully built.
|
||||
rule expect-failure ( target : dependency + : properties * )
|
||||
{
|
||||
local grist = [ MATCH ^<(.*)> : $(dependency:G) ] ;
|
||||
local marker = $(dependency:G=$(grist)*fail) ;
|
||||
(failed-as-expected) $(marker) ;
|
||||
FAIL_EXPECTED $(dependency) ;
|
||||
LOCATE on $(marker) = [ on $(dependency) return $(LOCATE) ] ;
|
||||
RMOLD $(marker) ;
|
||||
DEPENDS $(marker) : $(dependency) ;
|
||||
|
||||
DEPENDS $(target) : $(marker) ;
|
||||
**passed** $(target) : $(marker) ;
|
||||
}
|
||||
|
||||
# The rule/action combination used to report successfull passing
|
||||
# of a test.
|
||||
rule **passed**
|
||||
{
|
||||
# Dump all the tests, if needed.
|
||||
# We do it here, since dump should happen after all Jamfiles are read,
|
||||
# and there's no such place currently defined (but should).
|
||||
if ! $(.dumped-tests) && --dump-tests in [ modules.peek : ARGV ]
|
||||
{
|
||||
.dumped-tests = true ;
|
||||
dump-tests ;
|
||||
}
|
||||
|
||||
# Force deletion of the target, in case any dependencies failed
|
||||
# to build.
|
||||
RMOLD $(<) ;
|
||||
}
|
||||
|
||||
actions **passed**
|
||||
{
|
||||
echo passed > $(<)
|
||||
}
|
||||
|
||||
actions (failed-as-expected)
|
||||
{
|
||||
echo failed as expected > $(<)
|
||||
}
|
||||
|
||||
toolset.flags testing.unit-test LAUNCHER <testing.launcher> ;
|
||||
actions unit-test
|
||||
{
|
||||
$(LAUNCHER) $(>) && touch $(<)
|
||||
}
|
||||
|
||||
|
||||
|
||||
rule unit-test ( target-name : sources * : requirements * )
|
||||
rule capture-output ( target : source : properties * )
|
||||
{
|
||||
local project = [ CALLER_MODULE ] ;
|
||||
|
||||
# TODO: what to do with default build?
|
||||
targets.main-target-alternative
|
||||
[ new unit-test-target-class $(target-name) : $(project) : $(sources)
|
||||
: [ targets.main-target-requirements $(requirements) : $(project) ]
|
||||
: [ targets.main-target-default-build $(default-build) : $(project) ]
|
||||
] ;
|
||||
output-file on $(target) = $(target:S=.output) ;
|
||||
LOCATE on $(target:S=.output) = [ on $(target) return $(LOCATE) ] ;
|
||||
|
||||
# The INCLUDES kill a warning about independent target...
|
||||
INCLUDES $(target) : $(target:S=.output) ;
|
||||
# but it also puts .output into dependency graph, so we must tell jam
|
||||
# it's OK if it cannot find the target or updating rule.
|
||||
NOCARE $(target:S=.output) ;
|
||||
}
|
||||
|
||||
IMPORT $(__name__) : unit-test : : unit-test ;
|
||||
|
||||
if $(NT)
|
||||
{
|
||||
ENV_PATH = %PATH% ;
|
||||
CATENATE = type ;
|
||||
}
|
||||
else
|
||||
{
|
||||
ENV_PATH = $PATH ;
|
||||
ENV_LD_LIBRARY_PATH = $LD_LIBRARY_PATH ;
|
||||
CATENATE = cat ;
|
||||
CP = cp ;
|
||||
}
|
||||
|
||||
|
||||
RUN_OUTPUT_HEADER = "echo ====== BEGIN OUTPUT ====== &&" ;
|
||||
RUN_OUTPUT_FOOTER = " && echo ====== END OUTPUT ======" ;
|
||||
if --verbose-test in $(ARGV)
|
||||
{
|
||||
VERBOSE_CAT = "&& "$(RUN_OUTPUT_HEADER)" "$(CATENATE)" " ;
|
||||
}
|
||||
|
||||
actions capture-output bind INPUT_FILES output-file
|
||||
{
|
||||
$(SHELL_SET)PATH=$(RUN_PATH:J=$(SPLITPATH))
|
||||
$(SHELL_EXPORT)$(PATH_VAR)
|
||||
$(SHELL_SET)LD_LIBRARY_PATH=$(RUN_LD_LIBRARY_PATH:J=$(SPLITPATH))
|
||||
$(SHELL_EXPORT)$(LD_LIBRARY_PATH_VAR)
|
||||
$(>) $(ARGS) "$(INPUT_FILES)" > $(output-file) 2>&1 && $(CP) $(output-file) $(<) $(VERBOSE_CAT)$(<)$(RUN_OUTPUT_FOOTER) || ( $(RUN_OUTPUT_HEADER) $(CATENATE) $(output-file) $(RUN_OUTPUT_FOOTER) && exit 1 )
|
||||
}
|
||||
|
||||
IMPORT $(__name__) : compile compile-fail test-suite run run-fail
|
||||
: : compile compile-fail test-suite run run-fail ;
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@ IMPORT virtual-target : remember-binding : : virtual-target.remember-binding ;
|
||||
# not establish dependency relationship, but should do everything else.
|
||||
class action
|
||||
{
|
||||
import type toolset property-set indirect class ;
|
||||
import type toolset property-set indirect class path ;
|
||||
|
||||
rule __init__ ( targets + : sources * : action-name + : property-set ? )
|
||||
{
|
||||
@@ -655,6 +655,16 @@ class action
|
||||
rule path ( )
|
||||
{
|
||||
local p = [ $(self.properties).as-path ] ;
|
||||
# Really, an ugly hack. Boost regression test system requires
|
||||
# specific target paths, and it seems that changing it to handle
|
||||
# other directory layout is really hard. For that reason,
|
||||
# we teach V2 to do the things regression system requires.
|
||||
# The value o '<location-prefix>' is predended to the path.
|
||||
local prefix = [ $(self.properties).get <location-prefix> ] ;
|
||||
if $(prefix)
|
||||
{
|
||||
p = [ path.join $(prefix) $(p) ] ;
|
||||
}
|
||||
return $(p) ;
|
||||
}
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@ IMPORT virtual-target : remember-binding : : virtual-target.remember-binding ;
|
||||
# not establish dependency relationship, but should do everything else.
|
||||
class action
|
||||
{
|
||||
import type toolset property-set indirect class ;
|
||||
import type toolset property-set indirect class path ;
|
||||
|
||||
rule __init__ ( targets + : sources * : action-name + : property-set ? )
|
||||
{
|
||||
@@ -655,6 +655,16 @@ class action
|
||||
rule path ( )
|
||||
{
|
||||
local p = [ $(self.properties).as-path ] ;
|
||||
# Really, an ugly hack. Boost regression test system requires
|
||||
# specific target paths, and it seems that changing it to handle
|
||||
# other directory layout is really hard. For that reason,
|
||||
# we teach V2 to do the things regression system requires.
|
||||
# The value o '<location-prefix>' is predended to the path.
|
||||
local prefix = [ $(self.properties).get <location-prefix> ] ;
|
||||
if $(prefix)
|
||||
{
|
||||
p = [ path.join $(prefix) $(p) ] ;
|
||||
}
|
||||
return $(p) ;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,67 +1,303 @@
|
||||
# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
|
||||
# (C) Copyright David Abrahams 2002.
|
||||
# (C) Copyright Vladimir Prus 2002-2003.
|
||||
# Permission to copy, use, modify, sell and
|
||||
# distribute this software is granted provided this copyright notice appears in
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
# Testing support.
|
||||
# This module implements regression testing framework. It declares a number of
|
||||
# main target rules, which perform some action, and if the results are ok,
|
||||
# creates an output file.
|
||||
#
|
||||
# The exact list of rules is:
|
||||
# 'compile' -- creates .test file if compilation of sources was successfull
|
||||
# 'compile-fail' -- creates .test file if compilation of sources failed
|
||||
# 'run' -- creates .test file is running of executable produced from
|
||||
# sources was successfull. Also leaves behing .output file
|
||||
# with the output from program run.
|
||||
# 'run-fail' -- same as above, but .test file is created if running fails.
|
||||
# 'unit-test' -- same as 'run', except that output is not stored.
|
||||
#
|
||||
# In all cases, except for 'unit-test', presense of .test file is an incication that
|
||||
# the test passed. For more convenient reporting, you might want to use C++ Boost
|
||||
# regression testing utilities, see
|
||||
# http://www.boost.org/more/regression.html
|
||||
|
||||
# Things to do/consider:
|
||||
# - No-skipping on <threading>multi
|
||||
# - Teach compiler_status handle Jamfile.v2.
|
||||
# - Grab RUN_PATH logic from V1 testing.jam
|
||||
# - Implement all the parameters to 'run': args/input_files
|
||||
# - Add link/link-fail?
|
||||
# - <no-warn> is not implemented, since in Como-specific, and it's not clear how
|
||||
# to implement it
|
||||
# - std::locale-support is not impelemted (it's used in some tests).
|
||||
|
||||
|
||||
import targets ;
|
||||
import "class" : new ;
|
||||
import property ;
|
||||
import feature ;
|
||||
import toolset ;
|
||||
import alias ;
|
||||
import type ;
|
||||
import generators ;
|
||||
import project ;
|
||||
import property-set ;
|
||||
import virtual-target ;
|
||||
import path ;
|
||||
|
||||
rule init ( ) { }
|
||||
|
||||
# The feature which controls the name of program used to
|
||||
# lanch test programs.
|
||||
feature.feature testing.launcher : : optional free ;
|
||||
feature.feature location-prefix : : free ;
|
||||
feature.feature test-info : : free incidental ;
|
||||
|
||||
class unit-test-target-class : typed-target
|
||||
# Register target types.
|
||||
type.register TEST : test ;
|
||||
type.register COMPILE : : TEST : main ;
|
||||
type.register COMPILE_FAIL : : TEST : main ;
|
||||
type.register RUN_OUTPUT : run : : main ;
|
||||
type.register RUN : : TEST : main ;
|
||||
type.register RUN_FAIL : : TEST : main ;
|
||||
type.register UNIT_TEST : passed : TEST : main ;
|
||||
|
||||
# Declare the rules which create main targets.
|
||||
# While the 'type' module already creates rules with the same names for us,
|
||||
# we need extra convenience: default name of main target, so write
|
||||
# our own versions.
|
||||
|
||||
# Helper rule. Create a test target, using basename of first source of no
|
||||
# target name is explicitly passed. Remembers the created target in
|
||||
# a global variable.
|
||||
rule make-test ( target-type : sources + : requirements * : target-name ? )
|
||||
{
|
||||
rule __init__ ( name : project : sources * : requirements *
|
||||
: default-build * )
|
||||
{
|
||||
typed-target.__init__ $(name) : $(project) : EXE : $(sources)
|
||||
: $(requirements) : $(default-build) ;
|
||||
}
|
||||
|
||||
rule construct ( source-targets * : properties * )
|
||||
{
|
||||
local result =
|
||||
[ typed-target.construct $(source-targets) : $(properties) ] ;
|
||||
local exe = $(result[1]) ;
|
||||
local exe-action = [ $(exe).action ] ;
|
||||
local timestamp = [ new file-target $(self.name) : : $(self.project) ] ;
|
||||
$(timestamp).suffix "passed" ;
|
||||
target-name ?= $(sources[1]:D=:S=) ;
|
||||
|
||||
local a = [ new action $(timestamp) : $(exe) : testing.run :
|
||||
[ $(exe-action).properties-ps ] ] ;
|
||||
$(timestamp).action $(a) ;
|
||||
return $(timestamp) ;
|
||||
}
|
||||
local project = [ CALLER_MODULE 1 ] ;
|
||||
# The <location-prefix> forces the build system for generate paths in the form
|
||||
# $build_dir/array1.test/gcc/debug
|
||||
# This is necessary to allow post-processing tools to work.
|
||||
local t =
|
||||
[ targets.create-typed-target
|
||||
[ type.type-from-rule-name $(target-type) ] : $(project)
|
||||
: $(target-name) : $(sources)
|
||||
: $(requirements) <location-prefix>$(target-name).test ] ;
|
||||
|
||||
# Remember the test (for --dump-test).
|
||||
# A good way would be to collect all given a project.
|
||||
# This has some technical problems: e.g. we can't call this dump from
|
||||
# Jamfile since projects referred by 'build-project' are not available until
|
||||
# whole Jamfile is loaded.
|
||||
.all-tests += $(t) ;
|
||||
return $(t) ;
|
||||
}
|
||||
|
||||
toolset.flags testing.run LAUNCHER <testing.launcher> ;
|
||||
rule compile ( sources + : requirements * : target-name ? )
|
||||
{
|
||||
return [ make-test compile : $(sources) : $(requirements) : $(target-name) ] ;
|
||||
}
|
||||
|
||||
rule compile-fail ( sources + : requirements * : target-name ? )
|
||||
{
|
||||
return [ make-test compile-fail : $(sources) : $(requirements) : $(target-name) ] ;
|
||||
}
|
||||
|
||||
rule run ( sources + : args * : input-files * : requirements * : target-name ?
|
||||
: default-build * )
|
||||
{
|
||||
if $(args) || $(input-files) || $(default-build)
|
||||
{
|
||||
EXIT "NOT supported" ;
|
||||
}
|
||||
return [ make-test run : $(sources) : $(requirements) : $(target-name) ] ;
|
||||
}
|
||||
|
||||
rule run-fail ( sources + : args * : input-files * : requirements * : target-name ?
|
||||
: default-build * )
|
||||
{
|
||||
if $(args) || $(input-files) || $(default-build)
|
||||
{
|
||||
EXIT "NOT supported" ;
|
||||
}
|
||||
return [ make-test run-fail : $(sources) : $(requirements) : $(target-name) ] ;
|
||||
}
|
||||
|
||||
|
||||
actions run
|
||||
# Rule for grouping tests in suites.
|
||||
rule test-suite ( suite-name : tests + )
|
||||
{
|
||||
# In V2, if 'tests' are instances of 'abstract-target', they will be considered
|
||||
# 'inline-targets' and will suffer some adjustments. This will not be compatible
|
||||
# with V1 behaviour, so we get names of 'tests' and use them.
|
||||
local names ;
|
||||
for local t in $(tests)
|
||||
{
|
||||
names += [ $(t).name ] ;
|
||||
}
|
||||
modules.call-in [ CALLER_MODULE ] : alias $(suite-name) : $(names) ;
|
||||
}
|
||||
|
||||
# For all main target in 'project-module',
|
||||
# which are typed target with type derived from 'TEST',
|
||||
# produce some interesting information.
|
||||
rule dump-tests # ( project-module )
|
||||
{
|
||||
for local t in $(.all-tests)
|
||||
{
|
||||
dump-test $(t) ;
|
||||
}
|
||||
}
|
||||
|
||||
rule dump-test ( target )
|
||||
{
|
||||
local type = [ $(target).type ] ;
|
||||
local name = [ $(target).name ] ;
|
||||
local sources = [ $(target).sources ] ;
|
||||
local source-files ;
|
||||
for local s in $(sources)
|
||||
{
|
||||
if [ class.is-a $(s) : file-reference ]
|
||||
{
|
||||
source-files +=
|
||||
[ path.relative
|
||||
[ path.root [ $(s).location ] [ path.pwd ] ]
|
||||
/home/ghost/Work/boost ] ;
|
||||
}
|
||||
}
|
||||
|
||||
local r = [ $(t).requirements ] ;
|
||||
# Extract values of the <test-info> feature
|
||||
local test-info = [ $(r).get <test-info> ] ;
|
||||
|
||||
# Format them into a single string of quoted strings
|
||||
test-info = \"$(test-info:J=\"\ \")\" ;
|
||||
|
||||
ECHO boost-test($(type)) \"$(name)\"
|
||||
[$(test-info)]
|
||||
":" \"$(source-files)\"
|
||||
;
|
||||
}
|
||||
|
||||
# Register generators. Depending on target type, either
|
||||
# 'expect-success' or 'expect-failure' rule will be used.
|
||||
generators.register-standard testing.expect-success : OBJ : COMPILE ;
|
||||
generators.register-standard testing.expect-failure : OBJ : COMPILE_FAIL ;
|
||||
generators.register-standard testing.expect-success : RUN_OUTPUT : RUN ;
|
||||
generators.register-standard testing.expect-failure : RUN_OUTPUT : RUN_FAIL ;
|
||||
|
||||
# Generator which runs an EXE and captures output.
|
||||
generators.register-standard testing.capture-output : EXE : RUN_OUTPUT ;
|
||||
|
||||
# Generator which creates target if sources runs successfully.
|
||||
# Differers from RUN in that run output is not captured.
|
||||
generators.register-standard testing.unit-test : EXE : UNIT_TEST ;
|
||||
|
||||
# The action rules called by generators.
|
||||
|
||||
# Causes the 'target' to exist after bjam invocation if and only if all the
|
||||
# dependencies were successfully built.
|
||||
rule expect-success ( target : dependency + : requirements * )
|
||||
{
|
||||
**passed** $(target) : $(sources) ;
|
||||
}
|
||||
|
||||
# Causes the 'target' to exist after bjam invocation if and only if all some
|
||||
# of the dependencies were not successfully built.
|
||||
rule expect-failure ( target : dependency + : properties * )
|
||||
{
|
||||
local grist = [ MATCH ^<(.*)> : $(dependency:G) ] ;
|
||||
local marker = $(dependency:G=$(grist)*fail) ;
|
||||
(failed-as-expected) $(marker) ;
|
||||
FAIL_EXPECTED $(dependency) ;
|
||||
LOCATE on $(marker) = [ on $(dependency) return $(LOCATE) ] ;
|
||||
RMOLD $(marker) ;
|
||||
DEPENDS $(marker) : $(dependency) ;
|
||||
|
||||
DEPENDS $(target) : $(marker) ;
|
||||
**passed** $(target) : $(marker) ;
|
||||
}
|
||||
|
||||
# The rule/action combination used to report successfull passing
|
||||
# of a test.
|
||||
rule **passed**
|
||||
{
|
||||
# Dump all the tests, if needed.
|
||||
# We do it here, since dump should happen after all Jamfiles are read,
|
||||
# and there's no such place currently defined (but should).
|
||||
if ! $(.dumped-tests) && --dump-tests in [ modules.peek : ARGV ]
|
||||
{
|
||||
.dumped-tests = true ;
|
||||
dump-tests ;
|
||||
}
|
||||
|
||||
# Force deletion of the target, in case any dependencies failed
|
||||
# to build.
|
||||
RMOLD $(<) ;
|
||||
}
|
||||
|
||||
actions **passed**
|
||||
{
|
||||
echo passed > $(<)
|
||||
}
|
||||
|
||||
actions (failed-as-expected)
|
||||
{
|
||||
echo failed as expected > $(<)
|
||||
}
|
||||
|
||||
toolset.flags testing.unit-test LAUNCHER <testing.launcher> ;
|
||||
actions unit-test
|
||||
{
|
||||
$(LAUNCHER) $(>) && touch $(<)
|
||||
}
|
||||
|
||||
|
||||
|
||||
rule unit-test ( target-name : sources * : requirements * )
|
||||
rule capture-output ( target : source : properties * )
|
||||
{
|
||||
local project = [ CALLER_MODULE ] ;
|
||||
|
||||
# TODO: what to do with default build?
|
||||
targets.main-target-alternative
|
||||
[ new unit-test-target-class $(target-name) : $(project) : $(sources)
|
||||
: [ targets.main-target-requirements $(requirements) : $(project) ]
|
||||
: [ targets.main-target-default-build $(default-build) : $(project) ]
|
||||
] ;
|
||||
output-file on $(target) = $(target:S=.output) ;
|
||||
LOCATE on $(target:S=.output) = [ on $(target) return $(LOCATE) ] ;
|
||||
|
||||
# The INCLUDES kill a warning about independent target...
|
||||
INCLUDES $(target) : $(target:S=.output) ;
|
||||
# but it also puts .output into dependency graph, so we must tell jam
|
||||
# it's OK if it cannot find the target or updating rule.
|
||||
NOCARE $(target:S=.output) ;
|
||||
}
|
||||
|
||||
IMPORT $(__name__) : unit-test : : unit-test ;
|
||||
|
||||
if $(NT)
|
||||
{
|
||||
ENV_PATH = %PATH% ;
|
||||
CATENATE = type ;
|
||||
}
|
||||
else
|
||||
{
|
||||
ENV_PATH = $PATH ;
|
||||
ENV_LD_LIBRARY_PATH = $LD_LIBRARY_PATH ;
|
||||
CATENATE = cat ;
|
||||
CP = cp ;
|
||||
}
|
||||
|
||||
|
||||
RUN_OUTPUT_HEADER = "echo ====== BEGIN OUTPUT ====== &&" ;
|
||||
RUN_OUTPUT_FOOTER = " && echo ====== END OUTPUT ======" ;
|
||||
if --verbose-test in $(ARGV)
|
||||
{
|
||||
VERBOSE_CAT = "&& "$(RUN_OUTPUT_HEADER)" "$(CATENATE)" " ;
|
||||
}
|
||||
|
||||
actions capture-output bind INPUT_FILES output-file
|
||||
{
|
||||
$(SHELL_SET)PATH=$(RUN_PATH:J=$(SPLITPATH))
|
||||
$(SHELL_EXPORT)$(PATH_VAR)
|
||||
$(SHELL_SET)LD_LIBRARY_PATH=$(RUN_LD_LIBRARY_PATH:J=$(SPLITPATH))
|
||||
$(SHELL_EXPORT)$(LD_LIBRARY_PATH_VAR)
|
||||
$(>) $(ARGS) "$(INPUT_FILES)" > $(output-file) 2>&1 && $(CP) $(output-file) $(<) $(VERBOSE_CAT)$(<)$(RUN_OUTPUT_FOOTER) || ( $(RUN_OUTPUT_HEADER) $(CATENATE) $(output-file) $(RUN_OUTPUT_FOOTER) && exit 1 )
|
||||
}
|
||||
|
||||
IMPORT $(__name__) : compile compile-fail test-suite run run-fail
|
||||
: : compile compile-fail test-suite run run-fail ;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user