nitunit: enforce the testing of docunits (unless explicitely fence-tagged)
[nit.git] / src / testing / testing_doc.nit
index e370b8a..27e6fac 100644 (file)
 module testing_doc
 
 import testing_base
-intrude import markdown
+intrude import docdown
 
 # Extractor, Executor and Reporter for the tests in a module
 class NitUnitExecutor
        super Doc2Mdwn
 
-       # The module to import
-       var mmodule: MModule
-
        # The prefix of the generated Nit source-file
        var prefix: String
 
+       # The module to import
+       var mmodule: MModule
+
        # The XML node associated to the module
        var testsuite: HTMLTag
 
-       # Initialize a new e
-       init(toolcontext: ToolContext, prefix: String, mmodule: MModule, testsuite: HTMLTag)
-       do
-               super(toolcontext)
-               self.prefix = prefix
-               self.mmodule = mmodule
-               self.testsuite = testsuite
-       end
-
        # All blocks of code from a same `ADoc`
        var blocks = new Array[Array[String]]
 
-       redef fun process_code(n: HTMLTag, text: String)
+       # 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)
 
+               # 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
-                       if ndoc != null and n.tag == "pre" and toolcontext.opt_warn.value > 1 then
-                               toolcontext.warning(ndoc.location, "Warning: There is a block of code that is not valid Nit, thus not considered a nitunit")
-                               if ast isa AError then toolcontext.warning(ast.location, ast.message)
-                               ndoc = null # To avoid multiple warning in the same node
-                       end
-                       return
-               end
-
-               # Search `assert` in the AST
-               var v = new SearchAssertVisitor
-               v.enter_visit(ast)
-               if not v.foundit then
-                       if ndoc != null and n.tag == "pre" and toolcontext.opt_warn.value > 1 then
-                               toolcontext.warning(ndoc.location, "Warning: There is a block of Nit code without `assert`, thus not considered a nitunit")
-                               ndoc = null # To avoid multiple warning in the same node
-                       end
+                       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
 
@@ -80,7 +73,7 @@ class NitUnitExecutor
        end
 
        # The associated node to localize warnings
-       var ndoc: nullable ADoc
+       var ndoc: nullable ADoc = null
 
        # used to generate distinct names
        var cpt = 0
@@ -90,12 +83,24 @@ class NitUnitExecutor
        fun extract(ndoc: ADoc, tc: HTMLTag)
        do
                blocks.clear
+               failures.clear
 
                self.ndoc = ndoc
 
                work(ndoc.to_mdoc)
+
                toolcontext.check_errors
 
+               if not failures.is_empty then
+                       for msg in failures do
+                               var ne = new HTMLTag("failure")
+                               ne.attr("message", msg)
+                               tc.add ne
+                               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 test_block(ndoc, tc, block)
@@ -107,7 +112,7 @@ class NitUnitExecutor
                toolcontext.modelbuilder.unit_entities += 1
 
                cpt += 1
-               var file = "{prefix}{cpt}.nit"
+               var file = "{prefix}-{cpt}.nit"
 
                toolcontext.info("Execute doc-unit {tc.attrs["name"]} in {file}", 1)
 
@@ -127,8 +132,8 @@ class NitUnitExecutor
                if toolcontext.opt_noact.value then return
 
                var nit_dir = toolcontext.nit_dir
-               var nitg = "{nit_dir or else ""}/bin/nitg"
-               if nit_dir == null or not nitg.file_exists then
+               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
@@ -136,7 +141,7 @@ class NitUnitExecutor
                var res = sys.system(cmd)
                var res2 = 0
                if res == 0 then
-                       res2 = sys.system("./{file}.bin >>'{file}.out1' 2>&1 </dev/null")
+                       res2 = sys.system("{file.to_program_name}.bin >>'{file}.out1' 2>&1 </dev/null")
                end
 
                var msg
@@ -156,13 +161,13 @@ class NitUnitExecutor
                        var ne = new HTMLTag("failure")
                        ne.attr("message", msg)
                        tc.add ne
-                       toolcontext.warning(ndoc.location, "FAILURE: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}): {msg}")
+                       toolcontext.warning(ndoc.location, "failure", "FAILURE: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}): {msg}")
                        toolcontext.modelbuilder.failed_entities += 1
                else if res2 != 0 then
                        var ne = new HTMLTag("error")
                        ne.attr("message", msg)
                        tc.add ne
-                       toolcontext.warning(ndoc.location, "ERROR: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}): {msg}")
+                       toolcontext.warning(ndoc.location, "error", "ERROR: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}): {msg}")
                        toolcontext.modelbuilder.failed_entities += 1
                end
                toolcontext.check_errors
@@ -200,7 +205,6 @@ redef class ModelBuilder
                if not mmodule2nmodule.has_key(mmodule) then return ts
 
                var nmodule = mmodule2nmodule[mmodule]
-               assert nmodule != null
 
                # usualy, only the original module must be imported in the unit test.
                var o = mmodule
@@ -228,7 +232,7 @@ redef class ModelBuilder
                        doc_entities += 1
                        tc = new HTMLTag("testcase")
                        # NOTE: jenkins expects a '.' in the classname attr
-                       tc.attr("classname", mmodule.full_name + ".<module>")
+                       tc.attr("classname", "nitunit." + mmodule.full_name + ".<module>")
                        tc.attr("name", "<module>")
                        d2m.extract(ndoc, tc)
                end label x
@@ -241,7 +245,7 @@ redef class ModelBuilder
                                if ndoc != null then
                                        doc_entities += 1
                                        tc = new HTMLTag("testcase")
-                                       tc.attr("classname", mmodule.full_name + "." + mclassdef.mclass.full_name)
+                                       tc.attr("classname", "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name)
                                        tc.attr("name", "<class>")
                                        d2m.extract(ndoc, tc)
                                end
@@ -254,7 +258,7 @@ redef class ModelBuilder
                                if ndoc != null then
                                        doc_entities += 1
                                        tc = new HTMLTag("testcase")
-                                       tc.attr("classname", mmodule.full_name + "." + mclassdef.mclass.full_name)
+                                       tc.attr("classname", "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name)
                                        tc.attr("name", mpropdef.mproperty.full_name)
                                        d2m.extract(ndoc, tc)
                                end