X-Git-Url: http://nitlanguage.org diff --git a/src/testing/testing_doc.nit b/src/testing/testing_doc.nit index 1d4490f..cadf871 100644 --- a/src/testing/testing_doc.nit +++ b/src/testing/testing_doc.nit @@ -15,12 +15,17 @@ # Testing from code comments. module testing_doc +private import parser_util import testing_base -intrude import docdown +import markdown +import html # Extractor, Executor and Reporter for the tests in a module class NitUnitExecutor - super Doc2Mdwn + super HTMLDecorator + + # Toolcontext used to parse Nit code blocks. + var toolcontext: ToolContext # The prefix of the generated Nit source-file var prefix: String @@ -31,65 +36,44 @@ class NitUnitExecutor # The XML node associated to the module var testsuite: HTMLTag - # All blocks of code from a same `ADoc` - var blocks = new Array[Array[String]] + # The current test-case xml element + var tc: HTMLTag is noautoinit # All failures from a same `ADoc` var failures = new Array[String] - redef fun process_code(n: HTMLTag, text: String, tag: nullable String) - do - # Skip non-blocks - if n.tag != "pre" then return - - # Skip strict non-nit - if tag != null and tag != "nit" and tag != "" then - return - end - - # Try to parse it - var ast = toolcontext.parse_something(text) + # Markdown processor used to parse markdown comments and extract code. + var mdproc = new MarkdownProcessor - # Skip pure comments - if ast isa TComment then return - - # We want executable code - if not (ast isa AModule or ast isa ABlockExpr or ast isa AExpr) then - var message = "" - if ast isa AError then message = " At {ast.location}: {ast.message}." - toolcontext.warning(ndoc.location, "invalid-block", "Error: There is a block of code that is not valid Nit, thus not considered a nitunit. To suppress this warning, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).{message}") - failures.add("{ndoc.location}: Invalid block of code.{message}") - return - end - - # Create a first block - # Or create a new block for modules that are more than a main part - if blocks.is_empty or ast isa AModule then - blocks.add(new Array[String]) - end - - # Add it to the file - blocks.last.add(text) + init do + mdproc.emitter.decorator = new NitunitDecorator(self) end - # The associated node to localize warnings - var ndoc: nullable ADoc = null + # The associated documentation object + var mdoc: nullable MDoc = null # used to generate distinct names var cpt = 0 + # The last docunit extracted from a mdoc. + # + # Is used because a new code-block might just be added to it. + var last_docunit: nullable DocUnit = null + # The entry point for a new `ndoc` node # Fill `docunits` with new discovered unit of tests. # # `tc` (testcase) is the pre-filled XML node - fun extract(ndoc: ADoc, tc: HTMLTag) + fun extract(mdoc: MDoc, tc: HTMLTag) do - blocks.clear + last_docunit = null failures.clear + self.tc = tc - self.ndoc = ndoc + self.mdoc = mdoc - work(ndoc.to_mdoc) + # Populate `blocks` from the markdown decorator + mdproc.process(mdoc.content.join("\n")) toolcontext.check_errors @@ -98,15 +82,10 @@ class NitUnitExecutor var ne = new HTMLTag("failure") ne.attr("message", msg) tc.add ne + toolcontext.modelbuilder.unit_entities += 1 toolcontext.modelbuilder.failed_entities += 1 end - if blocks.is_empty then testsuite.add(tc) - end - - if blocks.is_empty then return - - for block in blocks do - docunits.add new DocUnit(ndoc, tc, block.join("")) + if last_docunit == null then testsuite.add(tc) end end @@ -127,6 +106,10 @@ class NitUnitExecutor end test_simple_docunits(simple_du) + + for du in docunits do + testsuite.add du.to_xml + end end # Executes multiples doc-units in a shared program. @@ -179,30 +162,17 @@ class NitUnitExecutor toolcontext.modelbuilder.unit_entities += 1 i += 1 toolcontext.info("Execute doc-unit {du.testcase.attrs["name"]} in {file} {i}", 1) - var res2 = sys.system("{file.to_program_name}.bin {i} >>'{file}.out1' 2>&1 '{file}.out1' 2>&1 >'{file}.out1' 2>&1 '{file}.out1' 2>&1 '{file}.out1' 2>&1 '{file}.out1' 2>&1 ") tc.attr("name", "") - d2m.extract(ndoc, tc) + d2m.extract(ndoc.to_mdoc, tc) end label x for nclassdef in nmodule.n_classdefs do var mclassdef = nclassdef.mclassdef @@ -382,7 +456,7 @@ redef class ModelBuilder tc = new HTMLTag("testcase") tc.attr("classname", "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name) tc.attr("name", "") - d2m.extract(ndoc, tc) + d2m.extract(ndoc.to_mdoc, tc) end end for npropdef in nclassdef.n_propdefs do @@ -395,7 +469,7 @@ redef class ModelBuilder tc = new HTMLTag("testcase") tc.attr("classname", "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name) tc.attr("name", mpropdef.mproperty.full_name) - d2m.extract(ndoc, tc) + d2m.extract(ndoc.to_mdoc, tc) end end end @@ -404,4 +478,67 @@ redef class ModelBuilder return ts end + + # Extracts and executes all the docunits in the readme of the `mgroup` + # Returns a JUnit-compatible `` XML element that contains the results of the executions. + fun test_group(mgroup: MGroup): HTMLTag + do + var ts = new HTMLTag("testsuite") + toolcontext.info("nitunit: doc-unit group {mgroup}", 2) + + # usually, only the default module must be imported in the unit test. + var o = mgroup.default_mmodule + + ts.attr("package", mgroup.full_name) + + var prefix = toolcontext.test_dir + prefix = prefix.join_path(mgroup.to_s) + var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts) + + var tc + + total_entities += 1 + var mdoc = mgroup.mdoc + if mdoc == null then return ts + + doc_entities += 1 + tc = new HTMLTag("testcase") + # NOTE: jenkins expects a '.' in the classname attr + tc.attr("classname", "nitunit." + mgroup.full_name) + tc.attr("name", "") + d2m.extract(mdoc, tc) + + d2m.run_tests + + return ts + end + + # Test a document object unrelated to a Nit entity + fun test_mdoc(mdoc: MDoc): HTMLTag + do + var ts = new HTMLTag("testsuite") + var file = mdoc.location.to_s + + toolcontext.info("nitunit: doc-unit file {file}", 2) + + ts.attr("package", file) + + var prefix = toolcontext.test_dir / "file" + var d2m = new NitUnitExecutor(toolcontext, prefix, null, ts) + + var tc + + total_entities += 1 + doc_entities += 1 + + tc = new HTMLTag("testcase") + # NOTE: jenkins expects a '.' in the classname attr + tc.attr("classname", "nitunit.") + tc.attr("name", file) + + d2m.extract(mdoc, tc) + d2m.run_tests + + return ts + end end