Extractor, Executor and Reporter for the tests in a module

Introduced properties

private var _cpt: Int

nitc :: NitUnitExecutor :: _cpt

used to generate distinct names
private var _docunits: Array[DocUnit]

nitc :: NitUnitExecutor :: _docunits

All extracted docunits
private var _last_docunit: nullable DocUnit

nitc :: NitUnitExecutor :: _last_docunit

The last docunit extracted from a mdoc.
private var _md_parser: MdParser

nitc :: NitUnitExecutor :: _md_parser

Markdown parse used to parse markdown comments and extract code
private var _md_visitor: NitunitMdVisitor

nitc :: NitUnitExecutor :: _md_visitor

Markdown visitor used to extract markdown code blocks
private var _mdoc: nullable MDoc

nitc :: NitUnitExecutor :: _mdoc

The associated documentation object
private var _mmodule: nullable MModule

nitc :: NitUnitExecutor :: _mmodule

The module to import, if any
private var _name: String

nitc :: NitUnitExecutor :: _name

The name of the suite
private var _prefix: String

nitc :: NitUnitExecutor :: _prefix

The prefix of the generated Nit source-file
private var _testsuite: HTMLTag

nitc :: NitUnitExecutor :: _testsuite

The XML node associated to the module
private var _toolcontext: ToolContext

nitc :: NitUnitExecutor :: _toolcontext

Toolcontext used to parse Nit code blocks.
private var _xml_classname: String

nitc :: NitUnitExecutor :: _xml_classname

Unit class name in XML output
private var _xml_name: String

nitc :: NitUnitExecutor :: _xml_name

Unit name in xml output
fun compile_simple_docunits(dus: Array[DocUnit])

nitc :: NitUnitExecutor :: compile_simple_docunits

Compiles multiples doc-units in a shared program.
private fun compile_single_docunits(dus: Array[DocUnit]): Int

nitc :: NitUnitExecutor :: compile_single_docunits

Compile a unit file and return the compiler return code
private fun compile_unitfile(file: String): Int

nitc :: NitUnitExecutor :: compile_unitfile

Compile a unit file and return the compiler return code
fun cpt: Int

nitc :: NitUnitExecutor :: cpt

used to generate distinct names
protected fun cpt=(cpt: Int)

nitc :: NitUnitExecutor :: cpt=

used to generate distinct names
private fun create_unitfile(file: String): Writer

nitc :: NitUnitExecutor :: create_unitfile

Create and fill the header of a unit file file.
init defaultinit(toolcontext: ToolContext, prefix: String, mmodule: nullable MModule, testsuite: HTMLTag, name: String)

nitc :: NitUnitExecutor :: defaultinit

fun docunits: Array[DocUnit]

nitc :: NitUnitExecutor :: docunits

All extracted docunits
protected fun docunits=(docunits: Array[DocUnit])

nitc :: NitUnitExecutor :: docunits=

All extracted docunits
fun execute_simple_docunit(du: DocUnit)

nitc :: NitUnitExecutor :: execute_simple_docunit

Execute a docunit compiled by test_single_docunit
fun extract(mdoc: MDoc, xml_classname: String, xml_name: String)

nitc :: NitUnitExecutor :: extract

The entry point for a new ndoc node
fun generate_single_docunit(du: DocUnit): String

nitc :: NitUnitExecutor :: generate_single_docunit

Produce a single unit file for the docunit du.
fun last_docunit: nullable DocUnit

nitc :: NitUnitExecutor :: last_docunit

The last docunit extracted from a mdoc.
protected fun last_docunit=(last_docunit: nullable DocUnit)

nitc :: NitUnitExecutor :: last_docunit=

The last docunit extracted from a mdoc.
fun mark_done(du: DocUnit)

nitc :: NitUnitExecutor :: mark_done

Update display when a test case is done
private fun md_parser: MdParser

nitc :: NitUnitExecutor :: md_parser

Markdown parse used to parse markdown comments and extract code
private fun md_parser=(md_parser: MdParser)

nitc :: NitUnitExecutor :: md_parser=

Markdown parse used to parse markdown comments and extract code
private fun md_visitor: NitunitMdVisitor

nitc :: NitUnitExecutor :: md_visitor

Markdown visitor used to extract markdown code blocks
private fun md_visitor=(md_visitor: NitunitMdVisitor)

nitc :: NitUnitExecutor :: md_visitor=

Markdown visitor used to extract markdown code blocks
fun mdoc: nullable MDoc

nitc :: NitUnitExecutor :: mdoc

The associated documentation object
protected fun mdoc=(mdoc: nullable MDoc)

nitc :: NitUnitExecutor :: mdoc=

The associated documentation object
fun mmodule: nullable MModule

nitc :: NitUnitExecutor :: mmodule

The module to import, if any
protected fun mmodule=(mmodule: nullable MModule)

nitc :: NitUnitExecutor :: mmodule=

The module to import, if any
fun name: String

nitc :: NitUnitExecutor :: name

The name of the suite
protected fun name=(name: String)

nitc :: NitUnitExecutor :: name=

The name of the suite
fun prefix: String

nitc :: NitUnitExecutor :: prefix

The prefix of the generated Nit source-file
protected fun prefix=(prefix: String)

nitc :: NitUnitExecutor :: prefix=

The prefix of the generated Nit source-file
fun run_tests

nitc :: NitUnitExecutor :: run_tests

Execute all the docunits
fun show_status

nitc :: NitUnitExecutor :: show_status

Display current testing status
fun test_single_docunit(du: DocUnit)

nitc :: NitUnitExecutor :: test_single_docunit

Executes a single doc-unit in its own program.
fun testsuite: HTMLTag

nitc :: NitUnitExecutor :: testsuite

The XML node associated to the module
protected fun testsuite=(testsuite: HTMLTag)

nitc :: NitUnitExecutor :: testsuite=

The XML node associated to the module
fun toolcontext: ToolContext

nitc :: NitUnitExecutor :: toolcontext

Toolcontext used to parse Nit code blocks.
protected fun toolcontext=(toolcontext: ToolContext)

nitc :: NitUnitExecutor :: toolcontext=

Toolcontext used to parse Nit code blocks.
fun xml_classname: String

nitc :: NitUnitExecutor :: xml_classname

Unit class name in XML output
protected fun xml_classname=(xml_classname: String)

nitc :: NitUnitExecutor :: xml_classname=

Unit class name in XML output
fun xml_name: String

nitc :: NitUnitExecutor :: xml_name

Unit name in xml output
protected fun xml_name=(xml_name: String)

nitc :: NitUnitExecutor :: xml_name=

Unit name in xml output

Redefined properties

redef type SELF: NitUnitExecutor

nitc $ NitUnitExecutor :: SELF

Type of this instance, automatically specialized in every class

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
private var _cpt: Int

nitc :: NitUnitExecutor :: _cpt

used to generate distinct names
private var _docunits: Array[DocUnit]

nitc :: NitUnitExecutor :: _docunits

All extracted docunits
private var _last_docunit: nullable DocUnit

nitc :: NitUnitExecutor :: _last_docunit

The last docunit extracted from a mdoc.
private var _md_parser: MdParser

nitc :: NitUnitExecutor :: _md_parser

Markdown parse used to parse markdown comments and extract code
private var _md_visitor: NitunitMdVisitor

nitc :: NitUnitExecutor :: _md_visitor

Markdown visitor used to extract markdown code blocks
private var _mdoc: nullable MDoc

nitc :: NitUnitExecutor :: _mdoc

The associated documentation object
private var _mmodule: nullable MModule

nitc :: NitUnitExecutor :: _mmodule

The module to import, if any
private var _name: String

nitc :: NitUnitExecutor :: _name

The name of the suite
private var _prefix: String

nitc :: NitUnitExecutor :: _prefix

The prefix of the generated Nit source-file
private var _testsuite: HTMLTag

nitc :: NitUnitExecutor :: _testsuite

The XML node associated to the module
private var _toolcontext: ToolContext

nitc :: NitUnitExecutor :: _toolcontext

Toolcontext used to parse Nit code blocks.
private var _xml_classname: String

nitc :: NitUnitExecutor :: _xml_classname

Unit class name in XML output
private var _xml_name: String

nitc :: NitUnitExecutor :: _xml_name

Unit name in xml output
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
fun compile_simple_docunits(dus: Array[DocUnit])

nitc :: NitUnitExecutor :: compile_simple_docunits

Compiles multiples doc-units in a shared program.
private fun compile_single_docunits(dus: Array[DocUnit]): Int

nitc :: NitUnitExecutor :: compile_single_docunits

Compile a unit file and return the compiler return code
private fun compile_unitfile(file: String): Int

nitc :: NitUnitExecutor :: compile_unitfile

Compile a unit file and return the compiler return code
fun cpt: Int

nitc :: NitUnitExecutor :: cpt

used to generate distinct names
protected fun cpt=(cpt: Int)

nitc :: NitUnitExecutor :: cpt=

used to generate distinct names
private fun create_unitfile(file: String): Writer

nitc :: NitUnitExecutor :: create_unitfile

Create and fill the header of a unit file file.
init defaultinit(toolcontext: ToolContext, prefix: String, mmodule: nullable MModule, testsuite: HTMLTag, name: String)

nitc :: NitUnitExecutor :: defaultinit

fun docunits: Array[DocUnit]

nitc :: NitUnitExecutor :: docunits

All extracted docunits
protected fun docunits=(docunits: Array[DocUnit])

nitc :: NitUnitExecutor :: docunits=

All extracted docunits
fun execute_simple_docunit(du: DocUnit)

nitc :: NitUnitExecutor :: execute_simple_docunit

Execute a docunit compiled by test_single_docunit
fun extract(mdoc: MDoc, xml_classname: String, xml_name: String)

nitc :: NitUnitExecutor :: extract

The entry point for a new ndoc node
fun generate_single_docunit(du: DocUnit): String

nitc :: NitUnitExecutor :: generate_single_docunit

Produce a single unit file for the docunit du.
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun hash: Int

core :: Object :: hash

The hash code of the object.
init init

core :: Object :: init

fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
fun last_docunit: nullable DocUnit

nitc :: NitUnitExecutor :: last_docunit

The last docunit extracted from a mdoc.
protected fun last_docunit=(last_docunit: nullable DocUnit)

nitc :: NitUnitExecutor :: last_docunit=

The last docunit extracted from a mdoc.
fun mark_done(du: DocUnit)

nitc :: NitUnitExecutor :: mark_done

Update display when a test case is done
private fun md_parser: MdParser

nitc :: NitUnitExecutor :: md_parser

Markdown parse used to parse markdown comments and extract code
private fun md_parser=(md_parser: MdParser)

nitc :: NitUnitExecutor :: md_parser=

Markdown parse used to parse markdown comments and extract code
private fun md_visitor: NitunitMdVisitor

nitc :: NitUnitExecutor :: md_visitor

Markdown visitor used to extract markdown code blocks
private fun md_visitor=(md_visitor: NitunitMdVisitor)

nitc :: NitUnitExecutor :: md_visitor=

Markdown visitor used to extract markdown code blocks
fun mdoc: nullable MDoc

nitc :: NitUnitExecutor :: mdoc

The associated documentation object
protected fun mdoc=(mdoc: nullable MDoc)

nitc :: NitUnitExecutor :: mdoc=

The associated documentation object
fun mmodule: nullable MModule

nitc :: NitUnitExecutor :: mmodule

The module to import, if any
protected fun mmodule=(mmodule: nullable MModule)

nitc :: NitUnitExecutor :: mmodule=

The module to import, if any
fun name: String

nitc :: NitUnitExecutor :: name

The name of the suite
protected fun name=(name: String)

nitc :: NitUnitExecutor :: name=

The name of the suite
private intern fun native_class_name: CString

core :: Object :: native_class_name

The class name of the object in CString format.
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
fun prefix: String

nitc :: NitUnitExecutor :: prefix

The prefix of the generated Nit source-file
protected fun prefix=(prefix: String)

nitc :: NitUnitExecutor :: prefix=

The prefix of the generated Nit source-file
fun run_tests

nitc :: NitUnitExecutor :: run_tests

Execute all the docunits
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
fun show_status

nitc :: NitUnitExecutor :: show_status

Display current testing status
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
fun test_single_docunit(du: DocUnit)

nitc :: NitUnitExecutor :: test_single_docunit

Executes a single doc-unit in its own program.
fun testsuite: HTMLTag

nitc :: NitUnitExecutor :: testsuite

The XML node associated to the module
protected fun testsuite=(testsuite: HTMLTag)

nitc :: NitUnitExecutor :: testsuite=

The XML node associated to the module
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
fun toolcontext: ToolContext

nitc :: NitUnitExecutor :: toolcontext

Toolcontext used to parse Nit code blocks.
protected fun toolcontext=(toolcontext: ToolContext)

nitc :: NitUnitExecutor :: toolcontext=

Toolcontext used to parse Nit code blocks.
fun xml_classname: String

nitc :: NitUnitExecutor :: xml_classname

Unit class name in XML output
protected fun xml_classname=(xml_classname: String)

nitc :: NitUnitExecutor :: xml_classname=

Unit class name in XML output
fun xml_name: String

nitc :: NitUnitExecutor :: xml_name

Unit name in xml output
protected fun xml_name=(xml_name: String)

nitc :: NitUnitExecutor :: xml_name=

Unit name in xml output
package_diagram nitc::NitUnitExecutor NitUnitExecutor core::Object Object nitc::NitUnitExecutor->core::Object

Parents

interface Object

core :: Object

The root of the class hierarchy.

Class definitions

nitc $ NitUnitExecutor
# Extractor, Executor and Reporter for the tests in a module
class NitUnitExecutor

	# Toolcontext used to parse Nit code blocks.
	var toolcontext: ToolContext

	# The prefix of the generated Nit source-file
	var prefix: String

	# The module to import, if any
	var mmodule: nullable MModule

	# The XML node associated to the module
	var testsuite: HTMLTag

	# The name of the suite
	var name: String

	# Markdown parse used to parse markdown comments and extract code
	private var md_parser = new MdParser

	# Markdown visitor used to extract markdown code blocks
	private var md_visitor = new NitunitMdVisitor(self) is lazy

	# 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

	# Unit class name in XML output
	var xml_classname: String is noautoinit

	# Unit name in xml output
	var xml_name: String is noautoinit

	# The entry point for a new `ndoc` node
	# Fill `docunits` with new discovered unit of tests.
	fun extract(mdoc: MDoc, xml_classname, xml_name: String)
	do
		last_docunit = null
		self.xml_classname = xml_classname
		self.xml_name = xml_name

		self.mdoc = mdoc

		# Populate `blocks` from the markdown decorator
		var md_node = md_parser.parse(mdoc.content.join("\n"))
		md_visitor.enter_visit(md_node)
	end

	# All extracted docunits
	var docunits = new Array[DocUnit]

	# Display current testing status
	fun show_status do toolcontext.show_unit_status(name, docunits)

	# Update display when a test case is done
	fun mark_done(du: DocUnit)
	do
		du.is_done = true
		toolcontext.clear_progress_bar
		toolcontext.show_unit(du)
		show_status
	end

	# Execute all the docunits
	fun run_tests
	do
		if docunits.is_empty then
			return
		end

		# Try to group each nitunit into a single source file to fasten the compilation
		var simple_du = new Array[DocUnit] # du that are simple statements
		var single_du = new Array[DocUnit] # du that are modules or include classes
		show_status
		for du in docunits do
			# Skip existing errors
			if du.error != null then
				continue
			end

			var ast = toolcontext.parse_something(du.block)
			if ast isa AExpr then
				simple_du.add du
			else
				single_du.add du
			end
		end

		# Try to mass compile all the simple du as a single nit module
		compile_simple_docunits(simple_du)
		# Try to mass compile all the single du in a single nitc invocation with many modules
		compile_single_docunits(single_du)
		# If the mass compilation fail, then each one will be compiled individually

		# Now test them in order
		for du in docunits do
			if du.error != null then
				# Nothing to execute. Conclude
			else if du.is_compiled then
				# Already compiled. Execute it.
				execute_simple_docunit(du)
			else
				# A mass compilation failed
				# Need to try to recompile it, then execute it
				test_single_docunit(du)
			end
			mark_done(du)
		end

		# Final status
		show_status
		print ""

		for du in docunits do
			testsuite.add du.to_xml
		end
	end

	# Compiles multiples doc-units in a shared program.
	# Used for docunits simple block of code (without modules, classes, functions etc.)
	#
	# In case of success, the docunits are compiled and the caller can call `execute_simple_docunit`.
	#
	# In case of compilation error, the docunits are let uncompiled.
	# The caller should fallbacks to `test_single_docunit` to
	# * locate exactly the compilation problem in the problematic docunit.
	# * permit the execution of the other docunits that may be correct.
	fun compile_simple_docunits(dus: Array[DocUnit])
	do
		if dus.is_empty then return

		var file = "{prefix}-0.nit"

		toolcontext.info("Compile {dus.length} simple(s) doc-unit(s) in {file}", 1)

		var dir = file.dirname
		if dir != "" then dir.mkdir
		var f
		f = create_unitfile(file)
		var i = 0
		for du in dus do
			i += 1
			f.write("fun run_{i} do\n")
			f.write("# {du.full_name}\n")
			f.write(du.block)
			f.write("end\n")
		end
		f.write("var a = args.first.to_i\n")
		for j in [1..i] do
			f.write("if a == {j} then run_{j}\n")
		end
		f.close

		if toolcontext.opt_noact.value then return

		var res = compile_unitfile(file)

		if res != 0 then
			# Compilation error.
			# They should be generated and compiled independently
			return
		end

		# Compilation was a success.
		# Store what need to be executed for each one.
		i = 0
		for du in dus do
			i += 1
			du.test_file = file
			du.test_arg = i
			du.is_compiled = true
		end
	end

	# Execute a docunit compiled by `test_single_docunit`
	fun execute_simple_docunit(du: DocUnit)
	do
		var file = du.test_file.as(not null)
		var i = du.test_arg or else 0
		toolcontext.info("Execute doc-unit {du.full_name} in {file} {i}", 1)
		var clock = new Clock
		var res2 = toolcontext.safe_exec("{file.to_program_name}.bin {i} >'{file}.out1' 2>&1 </dev/null")
		if not toolcontext.opt_no_time.value then du.real_time = clock.total
		du.was_exec = true

		var content = "{file}.out1".to_path.read_all
		du.raw_output = content

		if res2 != 0 then
			du.error = "Runtime error in {file} with argument {i}"
			toolcontext.modelbuilder.failed_entities += 1
		end
	end

	# Produce a single unit file for the docunit `du`.
	fun generate_single_docunit(du: DocUnit): String
	do
		cpt += 1
		var file = "{prefix}-{cpt}.nit"

		var f
		f = create_unitfile(file)
		f.write(du.block)
		f.close

		du.test_file = file
		return file
	end

	# Executes a single doc-unit in its own program.
	# Used for docunits larger than a single block of code (with modules, classes, functions etc.)
	fun test_single_docunit(du: DocUnit)
	do
		var file = generate_single_docunit(du)

		toolcontext.info("Compile doc-unit {du.full_name} in {file}", 1)

		if toolcontext.opt_noact.value then return

		var res = compile_unitfile(file)
		var content = "{file}.out1".to_path.read_all
		du.raw_output = content

		du.test_file = file

		if res != 0 then
			du.error = "Compilation error in {file}"
			toolcontext.modelbuilder.failed_entities += 1
			return
		end

		du.is_compiled = true
		execute_simple_docunit(du)
	end

	# Create and fill the header of a unit file `file`.
	#
	# A unit file is a Nit source file generated from one
	# or more docunits that will be compiled and executed.
	#
	# The handled on the file is returned and must be completed and closed.
	#
	# `file` should be a valid filepath for a Nit source file.
	private fun create_unitfile(file: String): Writer
	do
		var mmodule = self.mmodule
		var dir = file.dirname
		if dir != "" then dir.mkdir
		var f
		f = new FileWriter.open(file)
		f.write("# GENERATED FILE\n")
		f.write("# Docunits extracted from comments\n")
		if mmodule != null then
			f.write("intrude import {mmodule.name}\n")
		end
		f.write("\n")
		return f
	end

	# Compile a unit file and return the compiler return code
	#
	# Can terminate the program if the compiler is not found
	private fun compile_unitfile(file: String): Int
	do
		var mmodule = self.mmodule
		var nitc = toolcontext.find_nitc
		var opts = new Array[String]
		if mmodule != null then
			# FIXME playing this way with the include dir is not safe nor robust
			opts.add "-I {mmodule.filepath.as(not null).dirname}"
		end
		var cmd = "{nitc} --ignore-visibility --no-color -q '{file}' {opts.join(" ")} >'{file}.out1' 2>&1 </dev/null -o '{file}.bin'"
		var res = toolcontext.safe_exec(cmd)
		return res
	end

	# Compile a unit file and return the compiler return code
	#
	# Can terminate the program if the compiler is not found
	private fun compile_single_docunits(dus: Array[DocUnit]): Int
	do
		# Generate all unitfiles
		var files = new Array[String]
		for du in dus do
			files.add generate_single_docunit(du)
		end

		if files.is_empty then return 0

		toolcontext.info("Compile {dus.length} single(s) doc-unit(s) at once", 1)

		# Mass compile them
		var nitc = toolcontext.find_nitc
		var opts = new Array[String]
		if mmodule != null then
			# FIXME playing this way with the include dir is not safe nor robust
			opts.add "-I {mmodule.filepath.dirname}"
		end
		var cmd = "{nitc} --ignore-visibility --no-color -q '{files.join("' '")}' {opts.join(" ")} > '{prefix}.out1' 2>&1 </dev/null --dir {prefix.dirname}"
		var res = toolcontext.safe_exec(cmd)
		if res != 0 then
			# Mass compilation failure
			return res
		end

		# Rename each file into it expected binary name
		for du in dus do
			var f = du.test_file.as(not null)
			toolcontext.safe_exec("mv '{f.strip_extension(".nit")}' '{f}.bin'")
			du.is_compiled = true
		end

		return res
	end
end
src/testing/testing_doc.nit:24,1--346,3