redef class ToolContext
# -- target-file
- var opt_file = new OptionString("Specify test suite location.", "-t", "--target-file")
+ var opt_file = new OptionString("Specify test suite location", "-t", "--target-file")
# --pattern
- var opt_pattern = new OptionString("Only run test case with name that match pattern. Examples: 'TestFoo', 'TestFoo*', 'TestFoo::test_foo', 'TestFoo::test_foo*', 'test_foo', 'test_foo*'", "-p", "--pattern")
+ var opt_pattern = new OptionString("Only run test case with name that match pattern", "-p", "--pattern")
end
# Used to test nitunit test files.
if not mclassdef.is_test then continue
if not suite_match_pattern(mclassdef) then continue
toolcontext.modelbuilder.total_classes += 1
- var before_test = mclassdef.before_test
- var after_test = mclassdef.after_test
for mpropdef in mclassdef.mpropdefs do
if not mpropdef isa MMethodDef or not mpropdef.is_test then continue
if not case_match_pattern(mpropdef) then continue
toolcontext.modelbuilder.total_tests += 1
var test = new TestCase(suite, mpropdef, toolcontext)
- test.before_test = before_test
- test.after_test = after_test
suite.add_test test
end
end
if not toolcontext.test_dir.file_exists then
toolcontext.test_dir.mkdir
end
+ write_to_nit
+ compile
toolcontext.info("Execute test-suite {mmodule.name}", 1)
var before_module = self.before_module
- if not before_module == null then run_case(before_module)
- for case in test_cases do run_case(case)
+ if not before_module == null then before_module.run
+ for case in test_cases do case.run
var after_module = self.after_module
- if not after_module == null then run_case(after_module)
+ if not after_module == null then after_module.run
end
- # Execute a test case
- fun run_case(test_case: TestCase) do
- test_case.write_to_nit
- test_case.compile
- test_case.run
+ # Write the test unit for `self` in a nit compilable file.
+ fun write_to_nit do
+ var file = new Template
+ file.addn "intrude import test_suite"
+ file.addn "import {mmodule.name}\n"
+ file.addn "var name = args.first"
+ for case in test_cases do
+ case.write_to_nit(file)
+ end
+ file.write_to_file("{test_file}.nit")
end
# Return the test suite in XML format compatible with Jenkins.
fun to_xml: HTMLTag do
var n = new HTMLTag("testsuite")
n.attr("package", mmodule.name)
- for test in test_cases do n.add test.to_xml
+ var failure = self.failure
+ if failure != null then
+ var f = new HTMLTag("failure")
+ f.attr("message", failure.to_s)
+ n.add f
+ else
+ for test in test_cases do n.add test.to_xml
+ end
return n
end
+
+ # Generated test file name.
+ fun test_file: String do
+ return toolcontext.test_dir / "gen_{mmodule.name.escape_to_c}"
+ end
+
+ # Compile all `test_cases` cases in one file.
+ fun compile do
+ # find nitc
+ var nitc = toolcontext.find_nitc
+ # compile test suite
+ var file = test_file
+ var module_file = mmodule.location.file
+ if module_file == null then
+ toolcontext.error(null, "Error: cannot find module file for {mmodule.name}.")
+ toolcontext.check_errors
+ return
+ end
+ var include_dir = module_file.filename.dirname
+ var cmd = "{nitc} --no-color '{file}.nit' -I {include_dir} -o '{file}.bin' > '{file}.out' 2>&1 </dev/null"
+ var res = toolcontext.safe_exec(cmd)
+ var f = new FileReader.open("{file}.out")
+ var msg = f.read_all
+ f.close
+ # set test case result
+ var loc = mmodule.location
+ if res != 0 then
+ failure = msg
+ toolcontext.warning(loc, "failure", "FAILURE: {mmodule.name} (in file {file}.nit): {msg}")
+ toolcontext.modelbuilder.failed_tests += 1
+ end
+ toolcontext.check_errors
+ end
+
+ # Error occured during test-suite compilation.
+ var failure: nullable String = null
end
# A test case is a unit test considering only a `MMethodDef`.
class TestCase
+ super UnitTest
# Test suite wich `self` belongs to.
var test_suite: TestSuite
# Test method to be compiled and tested.
var test_method: MMethodDef
- # `ToolContext` to use to display messages and find `nitg` bin.
+ # `ToolContext` to use to display messages and find `nitc` bin.
var toolcontext: ToolContext
- # `MMethodDef` to call before the test case.
- var before_test: nullable MMethodDef = null
-
- # `MMethodDef` to call after the test case.
- var after_test: nullable MMethodDef = null
-
- # Generated test file name.
- fun test_file: String do
- var dir = toolcontext.test_dir
- var mod = test_method.mclassdef.mmodule.name
- var cls = test_method.mclassdef.name
+ # Generate the test unit for `self` in `file`.
+ fun write_to_nit(file: Template) do
var name = test_method.name
- return "{dir}/{mod}_{cls}_{name}"
- end
-
- # Generate the test unit in a nit file.
- fun write_to_nit do
- var name = test_method.name
- var file = new Template
- file.addn "intrude import test_suite"
- file.addn "import {test_method.mclassdef.mmodule.name}\n"
+ file.addn "if name == \"{name}\" then"
if test_method.mproperty.is_toplevel then
- file.addn name
+ file.addn "\t{name}"
else
- file.addn "var subject = new {test_method.mclassdef.name}.nitunit"
- if before_test != null then file.addn "subject.{before_test.name}"
- file.addn "subject.{name}"
- if after_test != null then file.addn "subject.{after_test.name}"
+ file.addn "\tvar subject = new {test_method.mclassdef.name}.nitunit"
+ file.addn "\tsubject.before_test"
+ file.addn "\tsubject.{name}"
+ file.addn "\tsubject.after_test"
end
- file.write_to_file("{test_file}.nit")
- end
-
- # Compile all test cases in once.
- fun compile do
- # find nitg
- var nit_dir = toolcontext.nit_dir
- var nitg = nit_dir/"bin/nitg"
- if not nitg.file_exists then
- toolcontext.error(null, "Cannot find nitg. Set envvar NIT_DIR.")
- toolcontext.check_errors
- end
- # compile test suite
- var file = test_file
- var include_dir = test_method.mclassdef.mmodule.location.file.filename.dirname
- var cmd = "{nitg} --no-color '{file}.nit' -I {include_dir} -o '{file}.bin' > '{file}.out' 2>&1 </dev/null"
- var res = sys.system(cmd)
- var f = new IFStream.open("{file}.out")
- var msg = f.read_all
- f.close
- # set test case result
- var loc = test_method.location
- if res != 0 then
- failure = msg
- toolcontext.warning(loc, "failure", "FAILURE: {test_method.name} (in file {file}.nit): {msg}")
- toolcontext.modelbuilder.failed_tests += 1
- end
- toolcontext.check_errors
+ file.addn "end"
end
# Execute the test case.
was_exec = true
if toolcontext.opt_noact.value then return
# execute
- var file = test_file
- var res = sys.system("{file.to_program_name}.bin > '{file}.out1' 2>&1 </dev/null")
- var f = new IFStream.open("{file}.out1")
+ var method_name = test_method.name
+ var test_file = test_suite.test_file
+ var res_name = "{test_file}_{method_name.escape_to_c}"
+ var res = toolcontext.safe_exec("{test_file}.bin {method_name} > '{res_name}.out1' 2>&1 </dev/null")
+ var f = new FileReader.open("{res_name}.out1")
var msg = f.read_all
f.close
# set test case result
var loc = test_method.location
if res != 0 then
error = msg
- toolcontext.warning(loc, "failure", "ERROR: {test_method.name} (in file {file}.nit): {msg}")
+ toolcontext.warning(loc, "failure",
+ "ERROR: {method_name} (in file {test_file}.nit): {msg}")
toolcontext.modelbuilder.failed_tests += 1
+ else
+ var mmodule = test_method.mclassdef.mmodule
+ var file = mmodule.filepath
+ if file != null then
+ var sav = file.dirname / mmodule.name + ".sav" / test_method.name + ".res"
+ if sav.file_exists then
+ toolcontext.info("Diff output with {sav}", 1)
+ res = toolcontext.safe_exec("diff -u --label 'expected:{sav}' --label 'got:{res_name}.out1' '{sav}' '{res_name}.out1' > '{res_name}.diff' 2>&1 </dev/null")
+ if res != 0 then
+ msg = "Diff\n" + "{res_name}.diff".to_path.read_all
+ error = msg
+ toolcontext.warning(loc, "failure",
+ "ERROR: {method_name} (in file {test_file}.nit): {msg}")
+ toolcontext.modelbuilder.failed_tests += 1
+ end
+ else
+ toolcontext.info("No diff: {sav} not found", 2)
+ end
+ end
end
toolcontext.check_errors
end
- # Error occured during execution.
- var error: nullable String = null
-
- # Error occured during compilation.
- var failure: nullable String = null
-
- # Was the test case executed at least one?
- var was_exec = false
-
- # Return the `TestCase` in XML format compatible with Jenkins.
- fun to_xml: HTMLTag do
+ redef fun xml_classname do
var mclassdef = test_method.mclassdef
- var tc = new HTMLTag("testcase")
- # NOTE: jenkins expects a '.' in the classname attr
- tc.attr("classname", "nitunit." + mclassdef.mmodule.full_name + "." + mclassdef.mclass.full_name)
- tc.attr("name", test_method.mproperty.full_name)
- if was_exec then
- tc.add new HTMLTag("system-err")
- var n = new HTMLTag("system-out")
- n.append "out"
- tc.add n
- if error != null then
- n = new HTMLTag("error")
- n.attr("message", error.to_s)
- tc.add n
- end
- if failure != null then
- n = new HTMLTag("failure")
- n.attr("message", failure.to_s)
- tc.add n
- end
- end
- return tc
+ return "nitunit." + mclassdef.mmodule.full_name + "." + mclassdef.mclass.full_name
+ end
+
+ redef fun xml_name do
+ return test_method.mproperty.full_name
end
end
# i.e. begins with "test_"
private fun is_test: Bool do return name.has_prefix("test_")
- # Is the method a "before_test"?
- private fun is_before: Bool do return name == "before_test"
-
- # Is the method a "after_test"?
- private fun is_after: Bool do return name == "after_test"
-
# Is the method a "before_module"?
private fun is_before_module: Bool do return mproperty.is_toplevel and name == "before_module"
# Is the class a TestClass?
# i.e. begins with "Test"
private fun is_test: Bool do
+ var in_hierarchy = self.in_hierarchy
+ if in_hierarchy == null then return false
for sup in in_hierarchy.greaters do
if sup.name == "TestSuite" then return true
end
return false
end
-
- # "before_test" method for this classdef.
- private fun before_test: nullable MMethodDef do
- for mpropdef in mpropdefs do
- if mpropdef isa MMethodDef and mpropdef.is_before then return mpropdef
- end
- return null
- end
-
- # "after_test" method for this classdef.
- private fun after_test: nullable MMethodDef do
- for mpropdef in mpropdefs do
- if mpropdef isa MMethodDef and mpropdef.is_after then return mpropdef
- end
- return null
- end
end
redef class MModule
end
redef class ModelBuilder
+ # Number of test classes generated.
var total_classes = 0
+
+ # Number of tests generated.
var total_tests = 0
+
+ # Number of failed tests.
var failed_tests = 0
# Run NitUnit test file for mmodule (if exists).
if f != null then
test_file = f
else if not test_file.file_exists then
- var include_dir = mmodule.location.file.filename.dirname
+ var module_file = mmodule.location.file
+ if module_file == null then
+ toolcontext.info("Skip test for {mmodule}, no file found", 2)
+ return ts
+ end
+ var include_dir = module_file.filename.dirname
test_file = "{include_dir}/{test_file}"
end
if not test_file.file_exists then