X-Git-Url: http://nitlanguage.org diff --git a/src/nitunit.nit b/src/nitunit.nit index 5a96471..996fe27 100644 --- a/src/nitunit.nit +++ b/src/nitunit.nit @@ -12,239 +12,145 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Program to extract and execute unit tests from nit source files +# Testing tool. +# see `testing/README` module nitunit -import modelize_property -intrude import markdown -import parser_util +import testing -# Extractor, Executor an Reporter for the tests in a module -class NitUnitExecutor - super Doc2Mdwn - - # The name of the module to import - var modname: String +var toolcontext = new ToolContext - # The prefix of the generated Nit source-file - var prefix: String +toolcontext.option_context.add_option(toolcontext.opt_full, toolcontext.opt_output, toolcontext.opt_dir, toolcontext.opt_noact, toolcontext.opt_pattern, toolcontext.opt_autosav, toolcontext.opt_gen_unit, toolcontext.opt_gen_force, toolcontext.opt_gen_private, toolcontext.opt_gen_show, toolcontext.opt_nitc) +toolcontext.tooldescription = "Usage: nitunit [OPTION]... ...\nExecutes the unit tests from Nit source files." - # The XML node associated to the module - var testsuite: HTMLTag +toolcontext.process_options(args) +var args = toolcontext.option_context.rest - # Initialize a new e - init(toolcontext: ToolContext, prefix: String, modname: String, testsuite: HTMLTag) - do - super(toolcontext) - self.prefix = prefix - self.modname = modname - self.testsuite = testsuite +if toolcontext.opt_gen_unit.value then + if toolcontext.opt_pattern.value != null then + print "Option --pattern cannot be used with --gen-suite" + exit(0) end - - # All blocks of code from a same `ADoc` - var block = new Array[String] - - redef fun process_code(n: HTMLTag, text: String) - do - # Try to parse it - var ast = toolcontext.parse_something(text) - - # We want executable code - if not (ast isa AModule or ast isa ABlockExpr or ast isa AExpr) then return - - # Search `assert` in the AST - var v = new SearchAssertVisitor - v.enter_visit(ast) - if not v.foundit then return - - # Add it to the file - block.add(text) +else + if toolcontext.opt_gen_force.value then + print "Option --force must be used with --gen-suite" + exit(0) end - - # used to generate distinct names - var cpt = 0 - - # The entry point for a new `ndoc` node - # Fill the prepated `tc` (testcase) XTM node - fun extract(ndoc: ADoc, tc: HTMLTag) - do - block.clear - - work(ndoc.to_mdoc) - - if block.is_empty then return - - cpt += 1 - var file = "{prefix}{cpt}.nit" - - toolcontext.info("Execute {tc.attrs["classname"]}.{tc.attrs["name"]} in {file}", 2) - - var dir = file.dirname - if dir != "" then dir.mkdir - var f - f = new OFStream.open(file) - f.write("# GENERATED FILE\n") - f.write("# Example extracted from a documentation\n") - var modname = self.modname - f.write("import {modname}\n") - f.write("\n") - for text in block do - f.write(text) - end - f.close - - var cmd = "../bin/nitg --no-color '{file}' -I . >'{file}.out1' 2>&1 >'{file}.out1' 2>&1 ") - tc.attr("name", "") - d2m.extract(ndoc, tc) - end label x - for nclassdef in nmodule.n_classdefs do - var mclassdef = nclassdef.mclassdef.as(not null) - if nclassdef isa AStdClassdef then - var ndoc = nclassdef.n_doc - if ndoc != null then - tc = new HTMLTag("testcase") - tc.attr("classname", mmodule.full_name + "." + mclassdef.mclass.full_name) - tc.attr("name", "") - d2m.extract(ndoc, tc) - end - end - for npropdef in nclassdef.n_propdefs do - var mpropdef = npropdef.mpropdef.as(not null) - var ndoc = npropdef.n_doc - if ndoc != null then - tc = new HTMLTag("testcase") - tc.attr("classname", mmodule.full_name + "." + mclassdef.mclass.full_name) - tc.attr("name", mpropdef.mproperty.full_name) - d2m.extract(ndoc, tc) - end - end - end - - return ts - end -end - -redef class ToolContext - var opt_full = new OptionBool("Process also imported modules", "--full") - var opt_output = new OptionString("Output name (default is 'nitunit.xml')", "-o", "--output") - var opt_dir = new OptionString("Working directory (default is '.nitunit')", "--dir") -end - -var toolcontext = new ToolContext +var model = new Model +var modelbuilder = new ModelBuilder(model, toolcontext) -toolcontext.option_context.add_option(toolcontext.opt_full, toolcontext.opt_output, toolcontext.opt_dir) +var module_files = modelbuilder.filter_nit_source(args) +var mmodules = modelbuilder.parse_full(module_files) +modelbuilder.run_phases -toolcontext.process_options -var args = toolcontext.option_context.rest -if args.is_empty or toolcontext.opt_help.value then - print "usage: nitunit [options] file.nit..." - toolcontext.option_context.usage - return +if toolcontext.opt_gen_unit.value then + modelbuilder.gen_test_unit(mmodules.first) + exit(0) end -var model = new Model -var modelbuilder = new ModelBuilder(model, toolcontext) +"NIT_TESTING".setenv("true") +"NIT_TESTING_ID".setenv(pid.to_s) +"SRAND".setenv("0") -var mmodules = modelbuilder.parse(args) -modelbuilder.run_phases +var test_dir = toolcontext.test_dir +test_dir.mkdir +"# This file prevents the Nit modules of the directory to be part of the package".write_to_file(test_dir / "packages.ini") var page = new HTMLTag("testsuites") if toolcontext.opt_full.value then mmodules = model.mmodules +for a in args do + if not a.file_exists then + toolcontext.fatal_error(null, "Error: cannot load file or module `{a}`.") + end + # Try to load the file as a markdown document + var mdoc = modelbuilder.load_markdown(a) + page.add modelbuilder.test_mdoc(mdoc) +end + +for a in module_files do + var g = modelbuilder.identify_group(a) + if g == null then continue + page.add modelbuilder.test_group(g) +end + for m in mmodules do page.add modelbuilder.test_markdown(m) + var ts = modelbuilder.test_unit(m) + if ts != null then page.add ts end var file = toolcontext.opt_output.value if file == null then file = "nitunit.xml" page.write_to_file(file) + +# Print results +printn "Docunits: Entities: {modelbuilder.total_entities}; Documented ones: {modelbuilder.doc_entities}; With nitunits: {modelbuilder.unit_entities}" +if modelbuilder.unit_entities == 0 or toolcontext.opt_noact.value then + print "" +else + printn "; Failures: " + var cpt = modelbuilder.failed_entities + if toolcontext.opt_no_color.value then + print cpt + else if cpt == 0 then + print "0".green.bold + else + print cpt.to_s.red.bold + end +end +printn "Test suites: Classes: {modelbuilder.total_classes}; Test Cases: {modelbuilder.total_tests}" +if modelbuilder.total_tests == 0 or toolcontext.opt_noact.value then + print "" +else + printn "; Failures: " + var cpt = modelbuilder.failed_tests + if toolcontext.opt_no_color.value then + print cpt + else if cpt == 0 then + print "0".green.bold + else + print cpt.to_s.red.bold + end +end + +var total = modelbuilder.unit_entities + modelbuilder.total_tests +var fail = modelbuilder.failed_entities + modelbuilder.failed_tests +if toolcontext.opt_noact.value then + # nothing +else if total == 0 then + var head = "[NOTHING]" + if not toolcontext.opt_no_color.value then + head = head.yellow + end + print "{head} No unit tests to execute." +else if fail == 0 then + var head = "[SUCCESS]" + if not toolcontext.opt_no_color.value then + head = head.green.bold + end + print "{head} All {total} tests passed." +else + var head = "[FAILURE]" + if not toolcontext.opt_no_color.value then + head = head.red.bold + end + print "{head} {fail}/{total} tests failed." + + print "`{test_dir}` is not removed for investigation." + exit 1 +end + +test_dir.rmdir