Base options for testing tools.

Introduced classes

abstract class UnitTest

nitc :: UnitTest

A unit test is an elementary test discovered, run and reported by nitunit.

Redefined classes

redef abstract class String

nitc :: testing_base $ String

Immutable sequence of characters.
redef class ToolContext

nitc :: testing_base $ ToolContext

Global context for tools

All class definitions

redef abstract class String

nitc :: testing_base $ String

Immutable sequence of characters.
redef class ToolContext

nitc :: testing_base $ ToolContext

Global context for tools
abstract class UnitTest

nitc $ UnitTest

A unit test is an elementary test discovered, run and reported by nitunit.
package_diagram nitc::testing_base testing_base nitc\>modelize\> modelize nitc::testing_base->nitc\>modelize\> nitc::parser_util parser_util nitc::testing_base->nitc::parser_util html html nitc::testing_base->html nitc nitc nitc\>modelize\>->nitc nitc\>parser\> parser nitc::parser_util->nitc\>parser\> nitc::toolcontext toolcontext nitc::parser_util->nitc::toolcontext template template html->template core core html->core ...nitc ... ...nitc->nitc ...nitc\>parser\> ... ...nitc\>parser\>->nitc\>parser\> ...nitc::toolcontext ... ...nitc::toolcontext->nitc::toolcontext ...template ... ...template->template ...core ... ...core->core nitc::testing_doc testing_doc nitc::testing_doc->nitc::testing_base nitc::testing_suite testing_suite nitc::testing_suite->nitc::testing_base nitc::testing_gen testing_gen nitc::testing_gen->nitc::testing_base nitc::testing testing nitc::testing->nitc::testing_doc nitc::testing->nitc::testing_suite nitc::testing->nitc::testing_gen nitc::testing... ... nitc::testing...->nitc::testing

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module annotation

nitc :: annotation

Management and utilities on annotations
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module console

console :: console

Defines some ANSI Terminal Control Escape Sequences.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module digraph

graph :: digraph

Implementation of directed graphs, also called digraphs.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module ini

ini :: ini

Read and write INI configuration files
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module lexer

nitc :: lexer

Lexer and its tokens.
module lexer_work

nitc :: lexer_work

Internal algorithm and data structures for the Nit lexer
module list

core :: list

This module handle double linked lists
module literal

nitc :: literal

Parsing of literal values in the abstract syntax tree.
module loader

nitc :: loader

Loading of Nit source files
module location

nitc :: location

Nit source-file and locations in source-file
module math

core :: math

Mathematical operations
module mdoc

nitc :: mdoc

Documentation of model entities
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module mmodule

nitc :: mmodule

modules and module hierarchies in the metamodel
module mmodule_data

nitc :: mmodule_data

Define and retrieve data in modules
module model

nitc :: model

Classes, types and properties
module model_base

nitc :: model_base

The abstract concept of model and related common things
module modelbuilder_base

nitc :: modelbuilder_base

Load nit source files and build the associated model
module modelize_class

nitc :: modelize_class

Analysis and verification of class definitions to instantiate model element
module modelize_property

nitc :: modelize_property

Analysis and verification of property definitions to instantiate model element
module more_collections

more_collections :: more_collections

Highly specific, but useful, collections-related classes.
module mpackage

nitc :: mpackage

Modelisation of a Nit package
module native

core :: native

Native structures for text and bytes
module nitpm_shared

nitc :: nitpm_shared

Services related to the Nit package manager
module numeric

core :: numeric

Advanced services for Numeric types
module opts

opts :: opts

Management of options on the command line
module ordered_tree

ordered_tree :: ordered_tree

Manipulation and presentation of ordered trees.
module parser

nitc :: parser

Parser.
module parser_nodes

nitc :: parser_nodes

AST nodes of the Nit language
module parser_prod

nitc :: parser_prod

Production AST nodes full definition.
module parser_work

nitc :: parser_work

Internal algorithm and data structures for the Nit parser
module phase

nitc :: phase

Phases of the processing of nit programs
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module tables

nitc :: tables

Module that interfaces the parsing tables.
module template

template :: template

Basic template system
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module toolcontext

nitc :: toolcontext

Common command-line tool infrastructure than handle options and error messages
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module version

nitc :: version

This file was generated by git-gen-version.sh

Parents

module html

html :: html

HTML output facilities
module modelize

nitc :: modelize

Create a model from nit source files
module parser_util

nitc :: parser_util

Utils and tools related to parsers and AST

Children

module testing_doc

nitc :: testing_doc

Testing from code comments.
module testing_gen

nitc :: testing_gen

Test Suites generation.
module testing_suite

nitc :: testing_suite

Testing from external files.

Descendants

module a_star-m

a_star-m

module nitunit

nitc :: nitunit

Testing tool.
module testing

nitc :: testing

Test unit generation and execution for Nit.
# Base options for testing tools.
module testing_base

import modelize
private import parser_util
import html
import console

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")
	# opt --nitc
	var opt_nitc = new OptionString("nitc compiler to use", "--nitc")
	# opt --no-time
	var opt_no_time = new OptionBool("Disable time information in XML", "--no-time")

	# Working directory for testing.
	fun test_dir: String do
		var dir = opt_dir.value
		if dir == null then return "nitunit.out"
		return dir
	end

	# Search the `nitc` compiler to use
	#
	# If not `nitc` is suitable, then prints an error and quit.
	fun find_nitc: String
	do
		var nitc = opt_nitc.value
		if nitc != null then
			if not nitc.file_exists then
				fatal_error(null, "error: cannot find `{nitc}` given by --nitc.")
				abort
			end
			return nitc
		end

		nitc = "NITC".environ
		if nitc != "" then
			if not nitc.file_exists then
				fatal_error(null, "error: cannot find `{nitc}` given by NITC.")
				abort
			end
			return nitc
		end

		var nit_dir = nit_dir or else "."
		nitc = nit_dir / "bin/nitc"
		if not nitc.file_exists then
			fatal_error(null, "Error: cannot find nitc. Set envvar NIT_DIR or NITC or use the --nitc option.")
			abort
		end
		return nitc
	end

	# Execute a system command in a more safe context than `Sys::system`.
	fun safe_exec(command: String): Int
	do
		info(command, 2)
		var real_command = """
bash -c "
ulimit -f {{{ulimit_file}}} 2> /dev/null
ulimit -t {{{ulimit_usertime}}} 2> /dev/null
{{{command}}}
"
"""
		return system(real_command)
	end

	# The maximum size (in KB) of files written by a command executed trough `safe_exec`
	#
	# Default: 64MB
	var ulimit_file = 65536 is writable

	# The maximum amount of cpu time (in seconds) for a command executed trough `safe_exec`
	#
	# Default: 10 CPU minute
	var ulimit_usertime = 600 is writable

	# Show a single-line status to use as a progression.
	#
	# If `has_progress_bar` is true, then the output is a progress bar.
	# The printed the line starts with `'\r'` and is not ended by a `'\n'`.
	# So it is expected that:
	# * no other output is printed between two calls
	# * the last `show_unit_status` is followed by a new-line
	#
	# If `has_progress_bar` is false, then only the first and last state is shown
	fun show_unit_status(name: String, tests: SequenceRead[UnitTest])
	do
		var line = "\r\x1B[K==== {name} ["
		var done = tests.length
		var fails = 0
		for t in tests do
			if not t.is_done then
				line += " "
				done -= 1
			else if t.error == null then
				line += ".".green.bold
			else
				line += "X".red.bold
				fails += 1
			end
		end

		if not has_progress_bar then
			if done == 0 then
				print "==== {name} | tests: {tests.length}"
			end
			return
		end

		if done < tests.length then
			line += "] {done}/{tests.length}"
		else
			line += "] tests: {tests.length} "
			if fails == 0 then
				line += "OK".green.bold
			else
				line += "KO: {fails}".red.bold
			end
		end
		printn "{line}"
	end

	# Is a progress bar printed?
	#
	# true if color (because and non-verbose mode
	# (because verbose mode messes up with the progress bar).
	fun has_progress_bar: Bool
	do
		return not opt_no_color.value and opt_verbose.value <= 0
	end

	# Clear the line if `has_progress_bar` (no-op else)
	fun clear_progress_bar
	do
		if has_progress_bar then printn "\r\x1B[K"
	end

	# Show the full description of the test-case.
	#
	# The output honors `--no-color`.
	#
	# `more message`, if any, is added after the error message.
	fun show_unit(test: UnitTest, more_message: nullable String) do
		print test.to_screen(more_message, not opt_no_color.value)
	end

	# Set the `NIT_TESTING_PATH` environment variable with `path`.
	#
	# If `path == null` then `NIT_TESTING_PATH` is set with the empty string.
	fun set_testing_path(path: nullable String) do
		"NIT_TESTING_PATH".setenv(path or else "")
	end
end

# A unit test is an elementary test discovered, run and reported by nitunit.
#
# This class factorizes `DocUnit` and `TestCase`.
abstract class UnitTest
	# The name of the unit to show in messages
	fun full_name: String is abstract

	# The location of the unit test to show in messages.
	fun location: Location is abstract

	# Flag that indicates if the unit test was compiled/run.
	var is_done: Bool = false is writable

	# Error message occurred during test-case execution (or compilation).
	#
	# e.g.: `Runtime Error`
	var error: nullable String = null is writable

	# Was the test case executed at least once?
	#
	# This will indicate the status of the test (failture or error)
	var was_exec = false is writable

	# The raw output of the execution (or compilation)
	#
	# It merges the standard output and error output
	var raw_output: nullable String = null is writable

	# The location where the error occurred, if it makes sense.
	var error_location: nullable Location = null is writable

	# Additional noteworthy information when a test success.
	var info: nullable String = null

	# Time for the execution, in seconds
	var real_time: Float = 0.0 is writable

	# A colorful `[OK]` or `[KO]`.
	fun status_tag(color: nullable Bool): String do
		color = color or else true
		if not is_done then
			return "[  ]"
		else if error != null then
			var res = "[KO]"
			if color then res = res.red.bold
			return res
		else
			var res = "[OK]"
			if color then res = res.green.bold
			return res
		end
	end

	# The full (color) description of the test-case.
	#
	# `more message`, if any, is added after the error message.
	fun to_screen(more_message: nullable String, color: nullable Bool): String do
		color = color or else true
		var res
		var error = self.error
		if error != null then
			if more_message != null then error += " " + more_message
			var loc = error_location or else location
			if color then
				res = "{status_tag(color)} {full_name}\n     {loc.to_s.yellow}: {error}\n{loc.colored_line("1;31")}"
			else
				res = "{status_tag(color)} {full_name}\n     {loc}: {error}"
			end
			var output = self.raw_output
			if output != null then
				res += "\n     Output\n\t{output.chomp.replace("\n", "\n\t")}\n"
			end
		else
			res = "{status_tag(color)} {full_name}"
			if more_message != null then res += more_message
			var info = self.info
			if info != null then
				res += "\n     {info}"
			end
		end
		return res
	end

	# Return a `<testcase>` XML node in format compatible with Jenkins unit tests.
	fun to_xml: HTMLTag do
		var tc = new HTMLTag("testcase")
		tc.attr("classname", xml_classname)
		tc.attr("name", xml_name)
		tc.attr("time", real_time.to_s)

		var output = self.raw_output
		if output != null then output = output.trunc(8192).filter_nonprintable
		var error = self.error
		if error != null then
			var node
			if was_exec then
				node = tc.open("error").attr("message", error)
			else
				node = tc.open("failure").attr("message", error)
			end
			if output != null then
				node.append(output)
			end
		else if output != null then
			tc.open("system-err").append(output)
		end
		return tc
	end

	# The `classname` attribute of the XML format.
	#
	# NOTE: jenkins expects a '.' in the classname attr
	#
	# See to_xml
	fun xml_classname: String is abstract

	# The `name` attribute of the XML format.
	#
	# See to_xml
	fun xml_name: String is abstract
end

redef class String
	# If needed, truncate `self` at `max_length` characters and append an informative `message`.
	#
	# ~~~
	# assert "hello".trunc(10) == "hello"
	# assert "hello".trunc(2) == "he[truncated. Full size is 5]"
	# assert "hello".trunc(2, "...") == "he..."
	# ~~~
	fun trunc(max_length: Int, message: nullable String): String
	do
		if length <= max_length then return self
		if message == null then message = "[truncated. Full size is {length}]"
		return substring(0, max_length) + message
	end

	# Use a special notation for whitespace characters that are not `'\n'` (LFD) or `' '` (space).
	#
	# ~~~
	# assert "hello".filter_nonprintable == "hello"
	# assert "\r\n\t".filter_nonprintable == "^13\n^9"
	# ~~~
	fun filter_nonprintable: String
	do
		var buf = new Buffer
		for c in self do
			var cp = c.code_point
			if cp < 32 and c != '\n' then
				buf.append "^{cp}"
			else
				buf.add c
			end
		end
		return buf.to_s
	end
end
src/testing/testing_base.nit:15,1--334,3