Property definitions

nitc $ NitUnitGenerator :: defaultinit
# 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
src/testing/testing_gen.nit:21,1--155,3