From 93dd053a514bc48607b68acefbe1e03a1198605f Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Wed, 27 Aug 2014 02:09:00 -0400 Subject: [PATCH] nitunit: split module into one executable and a group named `testing`. Introduce module `test_doc` that implements "doc-units". Signed-off-by: Alexandre Terrasa --- src/nitunit.nit | 257 +--------------------------------------- src/testing/testing.nit | 18 +++ src/testing/testing_base.nit | 30 +++++ src/testing/testing_doc.nit | 267 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 316 insertions(+), 256 deletions(-) create mode 100644 src/testing/testing.nit create mode 100644 src/testing/testing_base.nit create mode 100644 src/testing/testing_doc.nit diff --git a/src/nitunit.nit b/src/nitunit.nit index 627e223..137b207 100644 --- a/src/nitunit.nit +++ b/src/nitunit.nit @@ -15,262 +15,7 @@ # Program to extract and execute unit tests from nit source files module nitunit -import modelize_property -intrude import markdown -import parser_util - -# Extractor, Executor an 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 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) - 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 - 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 - 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) - end - - # The associated node to localize warnings - var ndoc: nullable ADoc - - # 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 - blocks.clear - - self.ndoc = ndoc - - work(ndoc.to_mdoc) - toolcontext.check_errors - - if blocks.is_empty then return - - for block in blocks do test_block(ndoc, tc, block) - end - - # Execute a block - fun test_block(ndoc: ADoc, tc: HTMLTag, block: Array[String]) - do - toolcontext.modelbuilder.unit_entities += 1 - - cpt += 1 - var file = "{prefix}{cpt}.nit" - - toolcontext.info("Execute {tc.attrs["name"]} in {file}", 1) - - 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") - f.write("import {mmodule.name}\n") - f.write("\n") - for text in block do - f.write(text) - end - f.close - - 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 - toolcontext.error(null, "Cannot find nitg. Set envvar NIT_DIR.") - toolcontext.check_errors - end - var cmd = "{nitg} --ignore-visibility --no-color '{file}' -I {mmodule.location.file.filename.dirname} >'{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 - total_entities += 1 - var ndoc = nclassdef.n_doc - if ndoc != null then - doc_entities += 1 - 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) - total_entities += 1 - var ndoc = npropdef.n_doc - if ndoc != null then - doc_entities += 1 - 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") - var opt_noact = new OptionBool("Does not compile and run tests", "--no-act") -end +import testing var toolcontext = new ToolContext diff --git a/src/testing/testing.nit b/src/testing/testing.nit new file mode 100644 index 0000000..95529db --- /dev/null +++ b/src/testing/testing.nit @@ -0,0 +1,18 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test unit generation and execution for Nit. +module testing + +import testing_doc diff --git a/src/testing/testing_base.nit b/src/testing/testing_base.nit new file mode 100644 index 0000000..7e0c616 --- /dev/null +++ b/src/testing/testing_base.nit @@ -0,0 +1,30 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Base options for testing tools. +module testing_base + +import modelize_property +import parser_util + +redef class ToolContext + # opt --full + var opt_full = new OptionBool("Process also imported modules", "--full") + # opt --output + var opt_output = new OptionString("Output name (default is 'nitunit.xml')", "-o", "--output") + # opt --dirr + var opt_dir = new OptionString("Working directory (default is '.nitunit')", "--dir") + # opt --no-act + var opt_noact = new OptionBool("Does not compile and run tests", "--no-act") +end diff --git a/src/testing/testing_doc.nit b/src/testing/testing_doc.nit new file mode 100644 index 0000000..9d93dc6 --- /dev/null +++ b/src/testing/testing_doc.nit @@ -0,0 +1,267 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Testing from code comments. +module testing_doc + +import testing_base +intrude import markdown + +# 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 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) + 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 + 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 + 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) + end + + # The associated node to localize warnings + var ndoc: nullable ADoc + + # 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 + blocks.clear + + self.ndoc = ndoc + + work(ndoc.to_mdoc) + toolcontext.check_errors + + if blocks.is_empty then return + + for block in blocks do test_block(ndoc, tc, block) + end + + # Execute a block + fun test_block(ndoc: ADoc, tc: HTMLTag, block: Array[String]) + do + toolcontext.modelbuilder.unit_entities += 1 + + cpt += 1 + var file = "{prefix}{cpt}.nit" + + toolcontext.info("Execute doc-unit {tc.attrs["name"]} in {file}", 1) + + 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") + f.write("import {mmodule.name}\n") + f.write("\n") + for text in block do + f.write(text) + end + f.close + + 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 + toolcontext.error(null, "Cannot find nitg. Set envvar NIT_DIR.") + toolcontext.check_errors + end + var cmd = "{nitg} --ignore-visibility --no-color '{file}' -I {mmodule.location.file.filename.dirname} >'{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 + if mclassdef == null then continue + if nclassdef isa AStdClassdef then + total_entities += 1 + var ndoc = nclassdef.n_doc + if ndoc != null then + doc_entities += 1 + 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 + if mpropdef == null then continue + total_entities += 1 + var ndoc = npropdef.n_doc + if ndoc != null then + doc_entities += 1 + 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 -- 1.7.9.5