Used the Catalog API with Nitweb to provide a homepage.
Demo: http://nitweb.moz-code.org/
Pull-Request: #2162
Reviewed-by: Jean Privat <jean@pryen.org>
tags=educ,web,cli
maintainer=Alexis Laferrière <alexis.laf@xymus.net>
license=Apache-2.0
+[source]
+exclude=src/parser/parser_abs.nit
[upstream]
browse=https://github.com/nitlang/nit/tree/master/contrib/pep8analysis/
git=https://github.com/nitlang/nit.git
Sometimes, it is easier to validate a `TestCase` by comparing its output with a text file containing the expected result.
-For each TestCase `test_bar` of a TestSuite `test_mod.nit`, if the corresponding file `test_mod.sav/test_bar.res` exists, then the output of the test is compared with the file.
+For each TestCase `test_bar` of a TestSuite `test_mod.nit`, a corresponding file with the expected output is looked for:
+
+* "test_mod.sav/test_bar.res". I.e. test-cases grouped by test-suites.
+
+ This is the default and is useful if there is a lot of test-suites and test-cases in a directory
+
+* "sav/test_bar.res". I.e. all test-cases grouped in a common sub-directory.
+
+ Useful if there is a lot of test-suites OR test-cases in a directory.
+
+* "test_bar.res" raw in the directory.
+
+ Useful is there is a few test-suites and test-cases in a directory.
+
+All 3 are exclusive. If more than one exists, the test-case is failed.
+
+If a corresponding file then the output of the test-case is compared with the file.
The `diff(1)` command is used to perform the comparison.
The test is failed if non-zero is returned by `diff`.
If no corresponding `.res` file exists, then the output of the TestCase is ignored.
+To helps the management of the expected results, the option `--autosav` can be used to automatically create and update them.
+
+
## Configuring TestSuites
`TestSuites` also provide methods to configure the test run:
### `-t`, `--target-file`
Specify test suite location.
+### `--autosav`
+Automatically create/update .res files for black box testing.
+
+If a black block test fails because a difference between the expected result and the current result then the expected result file is updated (and the test is passed).
+
+If a test-case of a test-suite passes but that some output is generated, then an expected result file is created.
+
+It is expected that the created/updated files are checked since the tests are considered passed.
+A VCS like `git` is often a good tool to check the creation and modification of those files.
+
## SUITE GENERATION
### `--gen-suite`
var mgrouppath = path.join_path("..").simplify_path
var mgroup = identify_group(mgrouppath)
+ if mgroup != null then
+ var mpackage = mgroup.mpackage
+ if not mpackage.accept(path) then
+ mgroup = null
+ toolcontext.info("module `{path}` excluded from package `{mpackage}`", 2)
+ end
+ end
if mgroup == null then
# singleton package
var loc = new Location.opaque_file(path)
if not stopper.file_exists then
# Recursively get the parent group
parent = identify_group(parentpath)
+ if parent != null then do
+ var mpackage = parent.mpackage
+ if not mpackage.accept(dirpath) then
+ toolcontext.info("directory `{dirpath}` excluded from package `{mpackage}`", 2)
+ parent = null
+ end
+ end
if parent == null then
# Parent is not a group, thus we are not a group either
mgroups[rdp] = null
#
# Some packages, like stand-alone packages or virtual packages have no `ini` file associated.
var ini: nullable ConfigTree = null
+
+ # Array of relative source paths excluded according to the `source.exclude` key of the `ini`
+ var excludes: nullable Array[String] is lazy do
+ var ini = self.ini
+ if ini == null then return null
+ var exclude = ini["source.exclude"]
+ if exclude == null then return null
+ var excludes = exclude.split(":")
+ return excludes
+ end
+
+ # Does the source inclusion/inclusion rules of the package `ini` accept such path?
+ fun accept(filepath: String): Bool
+ do
+ var excludes = self.excludes
+ if excludes != null then
+ var relpath = root.filepath.relpath(filepath)
+ if excludes.has(relpath) then return false
+ end
+ return true
+ end
end
redef class MGroup
var name = qid.n_id.text
var qname = qid.full_name
+ if bad_class_names[mmodule].has(qname) then
+ error(qid, "Error: class `{qname}` not found in module `{mmodule}`.")
+ return
+ end
+ bad_class_names[mmodule].add(qname)
+
var all_classes = model.get_mclasses_by_name(name)
var hints = new Array[String]
error(qid, "Error: class `{qname}` not found in module `{mmodule}`.")
end
+ # List of already reported bad class names.
+ # Used to not perform and repeat hints again and again.
+ private var bad_class_names = new MultiHashMap[MModule, String]
+
# Return the static type associated to the node `ntype`.
# `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types)
# In case of problem, an error is displayed on `ntype` and null is returned.
var pa = mp.mgroup
while pa != null and not pa.is_interesting do pa = pa.parent
ot.add(pa, mp)
- if pa != null then mgroups.add pa
+ while pa != null do
+ mgroups.add pa
+ pa = pa.parent
+ end
end
for g in mgroups do
var pa = g.parent
var toolcontext = new ToolContext
-toolcontext.option_context.add_option(toolcontext.opt_full, toolcontext.opt_output, toolcontext.opt_dir, toolcontext.opt_noact, toolcontext.opt_pattern, toolcontext.opt_file, toolcontext.opt_gen_unit, toolcontext.opt_gen_force, toolcontext.opt_gen_private, toolcontext.opt_gen_show, toolcontext.opt_nitc)
+toolcontext.option_context.add_option(toolcontext.opt_full, toolcontext.opt_output, toolcontext.opt_dir, toolcontext.opt_noact, toolcontext.opt_pattern, toolcontext.opt_file, toolcontext.opt_autosav, toolcontext.opt_gen_unit, toolcontext.opt_gen_force, toolcontext.opt_gen_private, toolcontext.opt_gen_show, toolcontext.opt_nitc)
toolcontext.tooldescription = "Usage: nitunit [OPTION]... <file.nit>...\nExecutes the unit tests from Nit source files."
toolcontext.process_options(args)
tags=devel,cli
maintainer=Jean Privat <jean@pryen.org>
license=Apache-2.0
+[source]
+exclude=parser/parser_abs.nit:parser/.parser-nofact.nit
[upstream]
browse=https://github.com/nitlang/nit/tree/master/src
git=https://github.com/nitlang/nit.git
# 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
+
# A colorful `[OK]` or `[KO]`.
fun status_tag(color: nullable Bool): String do
color = color or else true
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
var opt_file = new OptionString("Specify test suite location", "-t", "--target-file")
# --pattern
var opt_pattern = new OptionString("Only run test case with name that match pattern", "-p", "--pattern")
+ # --autosav
+ var opt_autosav = new OptionBool("Automatically create/update .res files for black box testing", "--autosav")
end
# Used to test nitunit test files.
var test_file = test_suite.test_file
var res_name = "{test_file}_{method_name.escape_to_c}"
var res = toolcontext.safe_exec("{test_file}.bin {method_name} > '{res_name}.out1' 2>&1 </dev/null")
- self.raw_output = "{res_name}.out1".to_path.read_all
+ var raw_output = "{res_name}.out1".to_path.read_all
+ self.raw_output = raw_output
# set test case result
if res != 0 then
error = "Runtime Error in file {test_file}.nit"
var mmodule = test_method.mclassdef.mmodule
var file = mmodule.filepath
if file != null then
- var sav = file.dirname / mmodule.name + ".sav" / test_method.name + ".res"
- if sav.file_exists then
+ var tries = [ file.dirname / mmodule.name + ".sav" / test_method.name + ".res",
+ file.dirname / "sav" / test_method.name + ".res" ,
+ file.dirname / test_method.name + ".res" ]
+ var savs = [ for t in tries do if t.file_exists then t ]
+ if savs.length == 1 then
+ var sav = savs.first
toolcontext.info("Diff output with {sav}", 1)
res = toolcontext.safe_exec("diff -u --label 'expected:{sav}' --label 'got:{res_name}.out1' '{sav}' '{res_name}.out1' > '{res_name}.diff' 2>&1 </dev/null")
- if res != 0 then
+ if res == 0 then
+ # OK
+ else if toolcontext.opt_autosav.value then
+ raw_output.write_to_file(sav)
+ info = "Expected output updated: {sav} (--autoupdate)"
+ else
self.raw_output = "Diff\n" + "{res_name}.diff".to_path.read_all
error = "Difference with expected output: diff -u {sav} {res_name}.out1"
toolcontext.modelbuilder.failed_tests += 1
end
- else
- toolcontext.info("No diff: {sav} not found", 2)
+ else if savs.length > 1 then
+ toolcontext.info("Conflicting diffs: {savs.join(", ")}", 1)
+ error = "Conflicting expected output: {savs.join(", ", " and ")} all exist"
+ toolcontext.modelbuilder.failed_tests += 1
+ else if not raw_output.is_empty then
+ toolcontext.info("No diff: {tries.join(", ", " or ")} not found", 1)
+ if toolcontext.opt_autosav.value then
+ var sav = tries.first
+ sav.dirname.mkdir
+ raw_output.write_to_file(sav)
+ info = "Expected output saved: {sav} (--autoupdate)"
+ end
end
end
end
-t -r base_simple3.nit project1
-s base_simple3.nit project1
-M base_simple3.nit base_simple_import.nit
--td project1/module3.nit
+-td project1/module3.nit project1/subdir/subdir2/subdir3/submodule.nit
+test_prog --no-color
+test_prog/game/excluded.nit test_prog/game/excluded_dir/more.nit -t --no-color
--no-color -W test_advice_repeated_types.nit
--no-color base_simple3.nit; echo $?
--no-color error_mod_unk.nit; echo $?
+--no-color test_prog
--- /dev/null
+import module0
+Empty README for group `excluded` (readme-warning)
+Errors: 0. Warnings: 1.
+MGroupPage excluded
+ # excluded.section
+ ## excluded.intro
+ ## excluded.concerns
+ ## excluded.concern
+ ## excluded.concern
+ ## excluded-.concern
+ ### excluded-.definition
+ #### excluded-.intros_redefs
+ ##### list.group
+ ###### excluded-.intros
+ ###### excluded-.redefs
+
+MModulePage excluded
+ # excluded.section
+ ## excluded-.intro
+ ## excluded-.importation
+ ### excluded-.graph
+ ### list.group
+ #### excluded-.imports
+ #### excluded-.clients
+
OverviewPage Overview
# home.article
## packages.section
+ ### excluded.definition
### test_prog.definition
+ReadmePage excluded
+
ReadmePage test_prog
# mdarticle-0
#### test_prog__rpg__rpg.imports
#### test_prog__rpg__rpg.clients
-Generated 96 pages
+Generated 99 pages
list:
- MPropertyPage: 58 (60.41%)
- MClassPage: 20 (20.83%)
- MModulePage: 8 (8.33%)
- MGroupPage: 4 (4.16%)
- ReadmePage: 4 (4.16%)
- SearchPage: 1 (1.04%)
- OverviewPage: 1 (1.04%)
-Found 182 mentities
+ MPropertyPage: 58 (58.58%)
+ MClassPage: 20 (20.20%)
+ MModulePage: 9 (9.09%)
+ ReadmePage: 5 (5.05%)
+ MGroupPage: 5 (5.05%)
+ SearchPage: 1 (1.01%)
+ OverviewPage: 1 (1.01%)
+Found 185 mentities
list:
- MMethodDef: 68 (37.36%)
- MMethod: 57 (31.31%)
- MClassDef: 22 (12.08%)
- MClass: 20 (10.98%)
- MModule: 8 (4.39%)
- MGroup: 4 (2.19%)
+ MMethodDef: 68 (36.75%)
+ MMethod: 57 (30.81%)
+ MClassDef: 22 (11.89%)
+ MClass: 20 (10.81%)
+ MModule: 9 (4.86%)
+ MGroup: 5 (2.70%)
+ MPackage: 2 (1.08%)
MVirtualTypeDef: 1 (0.54%)
MVirtualTypeProp: 1 (0.54%)
- MPackage: 1 (0.54%)
quicksearch-list.js
|--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
`--subdir (\e[33mproject1/subdir\e[m)
|--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
- `--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+ |--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+ `--subdir2 (\e[33mproject1/subdir/subdir2\e[m)
+ `--subdir3 (\e[33mproject1/subdir/subdir2/subdir3\e[m)
+ `--\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
|--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
`--subdir (\e[33mproject1/subdir\e[m)
|--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
- `--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+ |--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+ `--subdir2 (\e[33mproject1/subdir/subdir2\e[m)
+ `--subdir3 (\e[33mproject1/subdir/subdir2/subdir3\e[m)
+ `--\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
|--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
`--subdir (\e[33mproject1/subdir\e[m)
|--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
- `--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+ |--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+ `--subdir2 (\e[33mproject1/subdir/subdir2\e[m)
+ `--subdir3 (\e[33mproject1/subdir/subdir2/subdir3\e[m)
+ `--\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
project2 (\e[33mproject1/project2\e[m)
|--\e[1mfoo\e[m (\e[33mproject1/project2/foo.nit\e[m)
`--\e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
project1>\e[1mmodule_01\e[m (\e[33mproject1/module_01.nit\e[m)
project1>\e[1mmodule_02\e[m (\e[33mproject1/module_02.nit\e[m)
project1>\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+project1>subdir>subdir2>subdir3>\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
|--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
|--\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)\e[37m (module4)\e[m
`--subdir (\e[33mproject1/subdir\e[m)
- `--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)\e[37m (module1)\e[m
+ |--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)\e[37m (module1)\e[m
+ `--subdir2 (\e[33mproject1/subdir/subdir2\e[m)
+ `--subdir3 (\e[33mproject1/subdir/subdir2/subdir3\e[m)
+ `--\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
--- /dev/null
+test_prog: Test program for model tools. (test_prog)
+|--game: Gaming group (test_prog/game)
+| `--\e[1mgame\e[m: A game abstraction for RPG. (test_prog/game/game.nit)
+|--platform: Fictive Crappy Platform. (test_prog/platform)
+| `--\e[1mplatform\e[m: Declares base types allowed on the platform. (test_prog/platform/platform.nit)
+|--rpg: Role Playing Game group (test_prog/rpg)
+| |--\e[1mcareers\e[m: Careers of the game. (test_prog/rpg/careers.nit)
+| |--\e[1mcharacter\e[m: Characters are playable entity in the world. (test_prog/rpg/character.nit)
+| |--\e[1mcombat\e[m: COmbat interactions between characters. (test_prog/rpg/combat.nit)
+| |--\e[1mraces\e[m: Races of the game. (test_prog/rpg/races.nit)
+| `--\e[1mrpg\e[m: A worlg RPG abstraction. (test_prog/rpg/rpg.nit)
+`--\e[1mtest_prog\e[m: A test program with a fake model to check model tools. (test_prog/test_prog.nit)
--- /dev/null
+\e[1mexcluded\e[m (test_prog/game/excluded.nit)
+\e[1mmore\e[m (test_prog/game/excluded_dir/more.nit)
-==== Test-suite of module test_nitunit4::test_nitunit4 | tests: 3
+==== Test-suite of module test_nitunit4::test_nitunit4 | tests: 4
[KO] test_nitunit4$TestTestSuite$test_foo
test_nitunit4/test_nitunit4.nit:22,2--26,4: Runtime Error in file nitunit.out/gen_test_nitunit4.nit
Output
[OK] test_nitunit4$TestTestSuite$test_bar
[KO] test_nitunit4$TestTestSuite$test_baz
- test_nitunit4/test_nitunit4.nit:32,2--34,4: Difference with expected output: diff -u test_nitunit4/test_nitunit4.sav/test_baz.res nitunit.out/gen_test_nitunit4_test_baz.out1
+ test_nitunit4/test_nitunit4.nit:32,2--34,4: Difference with expected output: diff -u test_nitunit4/test_baz.res nitunit.out/gen_test_nitunit4_test_baz.out1
Output
Diff
- --- expected:test_nitunit4/test_nitunit4.sav/test_baz.res
+ --- expected:test_nitunit4/test_baz.res
+++ got:nitunit.out/gen_test_nitunit4_test_baz.out1
@@ -1 +1,3 @@
-Bad result file
+Tested method
+After Test
+[KO] test_nitunit4$TestTestSuite$test_sav_conflict
+ test_nitunit4/test_nitunit4.nit:36,2--38,4: Conflicting expected output: test_nitunit4/test_nitunit4.sav/test_sav_conflict.res, test_nitunit4/sav/test_sav_conflict.res and test_nitunit4/test_sav_conflict.res all exist
+ Output
+ Before Test
+ Tested method
+ After Test
-Docunits: Entities: 12; Documented ones: 0; With nitunits: 0
-Test suites: Classes: 1; Test Cases: 3; Failures: 2
-[FAILURE] 2/3 tests failed.
+
+Docunits: Entities: 13; Documented ones: 0; With nitunits: 0
+Test suites: Classes: 1; Test Cases: 4; Failures: 3
+[FAILURE] 3/4 tests failed.
`nitunit.out` is not removed for investigation.
<testsuites><testsuite package="test_nitunit4>"></testsuite><testsuite package="test_nitunit4::nitunit4"></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_foo"><error>Runtime Error in file nitunit.out/gen_test_nitunit4.nit</error><system-err>Before Test
Tested method
</system-err></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_bar"><system-err>Before Test
Tested method
After Test
-</system-err></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_baz"><error>Difference with expected output: diff -u test_nitunit4/test_nitunit4.sav/test_baz.res nitunit.out/gen_test_nitunit4_test_baz.out1</error><system-err>Diff
---- expected:test_nitunit4/test_nitunit4.sav/test_baz.res
+</system-err></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_baz"><error>Difference with expected output: diff -u test_nitunit4/test_baz.res nitunit.out/gen_test_nitunit4_test_baz.out1</error><system-err>Diff
+--- expected:test_nitunit4/test_baz.res
+++ got:nitunit.out/gen_test_nitunit4_test_baz.out1
@@ -1 +1,3 @@
-Bad result file
+Before Test
+Tested method
+After Test
+</system-err></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_sav_conflict"><error>Conflicting expected output: test_nitunit4/test_nitunit4.sav/test_sav_conflict.res, test_nitunit4/sav/test_sav_conflict.res and test_nitunit4/test_sav_conflict.res all exist</error><system-err>Before Test
+Tested method
+After Test
</system-err></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4"></testsuite><testsuite></testsuite><testsuite package="test_nitunit4::test_nitunit4_base"></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
population: 3
minimum value: 2
- maximum value: 11
- total value: 16
- average value: 5.33
+ maximum value: 12
+ total value: 22
+ average value: 7.33
distribution:
- <=2: sub-population=1 (33.33%); cumulated value=2 (12.50%)
- <=4: sub-population=1 (33.33%); cumulated value=3 (18.75%)
- <=16: sub-population=1 (33.33%); cumulated value=11 (68.75%)
+ <=2: sub-population=1 (33.33%); cumulated value=2 (9.09%)
+ <=8: sub-population=1 (33.33%); cumulated value=8 (36.36%)
+ <=16: sub-population=1 (33.33%); cumulated value=12 (54.54%)
list:
- nit: 11 (68.75%)
- : 3 (18.75%)
- ini: 2 (12.50%)
+ nit: 12 (54.54%)
+ : 8 (36.36%)
+ ini: 2 (9.09%)
# mpackages:
-test_prog
+excluded test_prog
------------------------------------
-test_prog
+excluded test_prog
# mmodules:
-careers character combat game platform races rpg test_prog
+careers character combat excluded game platform races rpg test_prog
------------------------------------
-careers character combat game platform races rpg test_prog
+careers character combat excluded game platform races rpg test_prog
# mclasses:
Alcoholic Bool Career Character Combatable Dwarf Elf Float Game Human Int List Magician Object Race Starter String Sys Warrior Weapon
|--test_prog/README.md
|--test_prog/game
| |--test_prog/game/README.md
+| |--test_prog/game/excluded.nit
+| |--test_prog/game/excluded_dir
+| | `--test_prog/game/excluded_dir/more.nit
| `--test_prog/game/game.nit
|--test_prog/package.ini
|--test_prog/platform
fun test_baz do
print "Tested method"
end
+
+ fun test_sav_conflict do
+ print "Tested method"
+ end
end
--- /dev/null
+This is not a valid Nit program
--- /dev/null
+This is not a valid Nit program
maintainer=John Doe <jdoe@example.com> (http://www.example.com/~jdoe), Spider-Man
more_contributors=Riri <riri@example.com>, Fifi (http://www.example.com/~fifi), Loulou
license=Apache-2.0
+[source]
+exclude=game/excluded.nit:game/excluded_dir
[upstream]
browse=https://github.com/nitlang/nit/tree/master/tests/test_prog
git=https://github.com/nitlang/nit.git