1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Test Suites generation.
21 # Used to generate a nitunit test file skeleton.
22 class NitUnitGenerator
24 var toolcontext
: ToolContext
26 # Generate the NitUnit test file skeleton for `mmodule` in `test_file`.
27 fun gen_unit
(mmodule
: MModule, test_file
: String): Template do
28 var with_private
= toolcontext
.opt_gen_private
.value
29 var tpl
= new Template
30 tpl
.addn
"module test_{mmodule.name} is test_suite"
32 tpl
.addn
"import test_suite"
34 tpl
.addn
"intrude import {mmodule.name}"
36 tpl
.addn
"import {mmodule.name}"
38 for mclassdef
in mmodule
.mclassdefs
do
39 if mclassdef
.mclass
.kind
!= concrete_kind
then continue
41 tpl
.addn
"class Test{mclassdef.name}"
42 tpl
.addn
"\tsuper TestSuite"
43 for mpropdef
in mclassdef
.mpropdefs
do
44 if not mpropdef
isa MMethodDef then continue
45 var mproperty
= mpropdef
.mproperty
46 if mpropdef
.is_abstract
then continue
47 if mproperty
.is_init
then continue
48 if not with_private
and mproperty
.visibility
<= protected_visibility
then continue
49 var case_name
= case_name
(mpropdef
)
51 tpl
.addn
"\tfun {case_name} do"
52 tpl
.addn
"\t\tassert not_implemented: false # TODO remove once implemented"
54 tpl
.addn gen_init
(mclassdef
)
55 var args
= new Array[String]
56 for mparameter
in mpropdef
.msignature
.mparameters
do
57 tpl
.addn gen_decl
(mparameter
.name
, mparameter
.mtype
, mclassdef
)
58 args
.add mparameter
.name
60 var return_mtype
= mpropdef
.msignature
.return_mtype
61 if return_mtype
!= null then
62 tpl
.addn gen_decl
("exp", return_mtype
, mclassdef
)
63 tpl
.add
"\t\tvar res = "
67 tpl
.addn gen_call
(mpropdef
, args
)
68 if return_mtype
!= null then
69 tpl
.addn
"\t\tassert exp == res"
78 # Generate case name based on `MMethodDef`.
79 # special method name like "[]", "+"... are filtered
80 private fun case_name
(mmethoddef
: MMethodDef): String do
81 var name
= mmethoddef
.name
82 if name
== "[]" then return "test_bra"
83 if name
== "[]=" then return "test_bra_assign"
84 if name
== "+" then return "test_plus"
85 if name
== "-" then return "test_minus"
86 if name
== "*" then return "test_star"
87 if name
== "/" then return "test_slash"
88 if name
== "%" then return "test_percent"
89 if name
== "unary -" then return "test_unary_minus"
90 if name
== "==" then return "test_equals"
91 if name
== "!=" then return "test_not_equals"
92 if name
== "<" then return "test_lt"
93 if name
== "<=" then return "test_le"
94 if name
== "<=>" then return "test_compare"
95 if name
== ">=" then return "test_ge"
96 if name
== ">" then return "test_gt"
100 # Method names that do not need a "." in call.
101 var nodot
: Array[String] = ["+", "-", "*", "/", "%", "==", "!=", "<", "<=", "<=>", ">", ">=", ">"]
103 # Generate subject init.
104 private fun gen_init
(mclassdef
: MClassDef): Writable do
105 if mclassdef
.mclass
.arity
== 0 then
106 return "\t\tvar subject: {mclassdef.name}"
108 return "\t\tvar subject: {mclassdef.name}[{mclassdef.bound_mtype.arguments.join(", ")}]"
111 private fun gen_decl
(name
: String, mtype
: MType, mclassdef
: MClassDef): String do
112 if mtype
.need_anchor
then
113 mtype
= mtype
.anchor_to
(mclassdef
.mmodule
, mclassdef
.bound_mtype
)
115 return "\t\tvar {name}: {mtype.to_s}"
118 # Generate call to `method` using `args`.
119 private fun gen_call
(method
: MMethodDef, args
: Array[String]): Writable do
120 # Here we handle the magic of the Nit syntax, have fun :)
121 var name
= method
.name
122 if name
== "[]" then return "subject[{args.join(", ")}]"
123 if name
== "[]=" then
125 return "subject[{args.join(", ")}] = {last}"
127 if name
== "unary -" then return "-subject"
128 var tpl
= new Template
129 if nodot
.has
(name
) then
130 tpl
.add
"subject {name}"
131 if args
.length
== 1 then
132 tpl
.add
" {args.first}"
133 else if args
.length
> 1 then
134 tpl
.add
" ({args.join(", ")})"
138 if name
.has_suffix
("=") then
139 name
= name
.substring
(0, name
.length
- 1)
141 tpl
.add
"subject.{name}"
142 if not args
.is_empty
then
143 tpl
.add
"({args.join(", ")})"
148 tpl
.add
"subject.{name}"
149 if args
.length
== 1 then
150 tpl
.add
" {args.first}"
151 else if args
.length
> 1 then
152 tpl
.add
"({args.join(", ")})"
158 redef class ModelBuilder
159 # Generate NitUnit test file skeleton for `mmodule`.
160 fun gen_test_unit
(mmodule
: MModule) do
161 var test_file
= "test_{mmodule.name}.nit"
162 if test_file
.file_exists
and not toolcontext
.opt_gen_force
.value
and not toolcontext
.opt_gen_show
.value
then
163 toolcontext
.info
("Skip generation for {mmodule}, file {test_file} already exists", 1)
166 var generator
= new NitUnitGenerator(toolcontext
)
167 var tpl
= generator
.gen_unit
(mmodule
, test_file
)
168 if toolcontext
.opt_gen_show
.value
then
169 tpl
.write_to
(sys
.stdout
)
171 tpl
.write_to_file
(test_file
)
176 redef class ToolContext
177 var opt_gen_unit
= new OptionBool("Generate test suite skeleton for a module", "--gen-suite")
178 var opt_gen_force
= new OptionBool("Force test generation even if file exists", "-f", "--force")
179 var opt_gen_private
= new OptionBool("Also generate test case for private methods", "--private")
180 var opt_gen_show
= new OptionBool("Only display skeleton, do not write file", "--only-show")