Test Suites generation.

Introduced classes

class NitUnitGenerator

nitc :: NitUnitGenerator

Used to generate a nitunit test file skeleton.

Redefined classes

redef class ModelBuilder

nitc :: testing_gen $ ModelBuilder

A model builder knows how to load nit source files and build the associated model
redef class ToolContext

nitc :: testing_gen $ ToolContext

Global context for tools

All class definitions

redef class ModelBuilder

nitc :: testing_gen $ ModelBuilder

A model builder knows how to load nit source files and build the associated model
class NitUnitGenerator

nitc $ NitUnitGenerator

Used to generate a nitunit test file skeleton.
redef class ToolContext

nitc :: testing_gen $ ToolContext

Global context for tools
package_diagram nitc::testing_gen testing_gen nitc::testing_base testing_base nitc::testing_gen->nitc::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\>modelize\> ... ...nitc\>modelize\>->nitc\>modelize\> ...nitc::parser_util ... ...nitc::parser_util->nitc::parser_util ...html ... ...html->html nitc::testing testing nitc::testing->nitc::testing_gen nitc::nitunit nitunit nitc::nitunit->nitc::testing nitc::nitunit... ... nitc::nitunit...->nitc::nitunit

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 html

html :: html

HTML output facilities
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

nitc :: modelize

Create a model from nit source files
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_util

nitc :: parser_util

Utils and tools related to parsers and AST
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 testing_base

nitc :: testing_base

Base options for testing tools.

Children

module testing

nitc :: testing

Test unit generation and execution for Nit.

Descendants

module a_star-m

a_star-m

module nitunit

nitc :: nitunit

Testing tool.
# Test Suites generation.
module testing_gen

import testing_base
import template

# Used to generate a nitunit test file skeleton.
class NitUnitGenerator

	var toolcontext: ToolContext

	# Generate the NitUnit test file skeleton for `mmodule` in `test_file`.
	fun gen_unit(mmodule: MModule, test_file: String): Template do
		var with_private = toolcontext.opt_gen_private.value
		var tpl = new Template
		tpl.addn "module test_{mmodule.name} is test"
		tpl.addn ""
		if with_private then
			tpl.addn "intrude import {mmodule.name}"
		else
			tpl.addn "import {mmodule.name}"
		end
		for mclassdef in mmodule.mclassdefs do
			if mclassdef.mclass.kind != concrete_kind then continue
			tpl.addn ""
			tpl.addn "class Test{mclassdef.name}"
			tpl.addn "\ttest"
			for mpropdef in mclassdef.mpropdefs do
				if not mpropdef isa MMethodDef then continue
				var mproperty = mpropdef.mproperty
				if mpropdef.is_abstract then continue
				if mproperty.is_init then continue
				if not with_private and mproperty.visibility <= protected_visibility then continue
				var case_name = case_name(mpropdef)
				tpl.addn ""
				tpl.addn "\tfun {case_name} is test do"
				tpl.addn "\t\tassert not_implemented: false # TODO remove once implemented"
				tpl.addn ""
				tpl.addn gen_init(mclassdef)
				var args = new Array[String]
				for mparameter in mpropdef.msignature.mparameters do
					tpl.addn gen_decl(mparameter.name, mparameter.mtype, mclassdef)
					args.add mparameter.name
				end
				var return_mtype = mpropdef.msignature.return_mtype
				if return_mtype != null then
					tpl.addn gen_decl("exp", return_mtype, mclassdef)
					tpl.add "\t\tvar res = "
				else
					tpl.add "\t\t"
				end
				tpl.addn gen_call(mpropdef, args)
				if return_mtype != null then
					tpl.addn "\t\tassert exp == res"
				end
				tpl.addn "\tend"
			end
			tpl.addn "end"
		end
		return tpl
	end

	# Generate case name based on `MMethodDef`.
	# special method name like "[]", "+"... are filtered
	private fun case_name(mmethoddef: MMethodDef): String do
		var name = mmethoddef.name
		if name == "[]" then return "test_bra"
		if name == "[]=" then return "test_bra_assign"
		if name == "+" then return "test_plus"
		if name == "-" then return "test_minus"
		if name == "*" then return "test_star"
		if name == "/" then return "test_slash"
		if name == "%" then return "test_percent"
		if name == "unary -" then return "test_unary_minus"
		if name == "==" then return "test_equals"
		if name == "!=" then return "test_not_equals"
		if name == "<" then return "test_lt"
		if name == "<=" then return "test_le"
		if name == "<=>" then return "test_compare"
		if name == ">=" then return "test_ge"
		if name == ">" then return "test_gt"
		return "test_{name}"
	end

	# Method names that do not need a "." in call.
	var nodot: Array[String] = ["+", "-", "*", "/", "%", "==", "!=", "<", "<=", "<=>", ">", ">=", ">"]

	# Generate subject init.
	private fun gen_init(mclassdef: MClassDef): Writable do
		if mclassdef.mclass.arity == 0 then
			return "\t\tvar subject: {mclassdef.name}"
		end
		return "\t\tvar subject: {mclassdef.name}[{mclassdef.bound_mtype.arguments.join(", ")}]"
	end

	private fun gen_decl(name: String, mtype: MType, mclassdef: MClassDef): String do
		if mtype.need_anchor then
			mtype = mtype.anchor_to(mclassdef.mmodule, mclassdef.bound_mtype)
		end
		return "\t\tvar {name}: {mtype.to_s}"
	end

	# Generate call to `method` using `args`.
	private fun gen_call(method: MMethodDef, args: Array[String]): Writable do
		# Here we handle the magic of the Nit syntax, have fun :)
		var name = method.name
		if name == "[]" then return "subject[{args.join(", ")}]"
		if name == "[]=" then
			var last = args.pop
			return "subject[{args.join(", ")}] = {last}"
		end
		if name == "unary -" then return "-subject"
		var tpl = new Template
		if nodot.has(name) then
			tpl.add "subject {name}"
			if args.length == 1 then
				tpl.add " {args.first}"
			else if args.length > 1 then
				tpl.add " ({args.join(", ")})"
			end
			return tpl
		end
		if name.has_suffix("=") then
			name = name.substring(0, name.length - 1)
			var last = args.pop
			tpl.add "subject.{name}"
			if not args.is_empty then
				tpl.add "({args.join(", ")})"
			end
			tpl.add " = {last}"
			return tpl
		end
		tpl.add "subject.{name}"
		if args.length == 1 then
			tpl.add " {args.first}"
		else if args.length > 1 then
			tpl.add "({args.join(", ")})"
		end
		return tpl
	end
end

redef class ModelBuilder
	# Generate NitUnit test file skeleton for `mmodule`.
	fun gen_test_unit(mmodule: MModule) do
		var test_file = "test_{mmodule.name}.nit"
		if test_file.file_exists and not toolcontext.opt_gen_force.value and not toolcontext.opt_gen_show.value then
			toolcontext.info("Skip generation for {mmodule}, file {test_file} already exists", 1)
			return
		end
		var generator = new NitUnitGenerator(toolcontext)
		var tpl = generator.gen_unit(mmodule, test_file)
		if toolcontext.opt_gen_show.value then
			tpl.write_to(sys.stdout)
		else
			tpl.write_to_file(test_file)
		end
	end
end

redef class ToolContext
	var opt_gen_unit = new OptionBool("Generate test suite skeleton for a module", "--gen-suite")
	var opt_gen_force = new OptionBool("Force test generation even if file exists", "-f", "--force")
	var opt_gen_private = new OptionBool("Also generate test case for private methods", "--private")
	var opt_gen_show = new OptionBool("Only display the skeleton, do not write any file", "--only-show")
end
src/testing/testing_gen.nit:15,1--180,3