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`
--- /dev/null
+<div ng-if='listContributors.length > 0'>
+ <h3 id={{listId}}>
+ <span>{{listTitle}}</span>
+ </h3>
+ <ul class='list-unstyled user-list'>
+ <li ng-repeat='contributor in listContributors'>
+ <img class='avatar' src="https://secure.gravatar.com/avatar/{{contributor.hash}}?size=20&default=retro">
+ {{contributor.name}}
+ </li>
+ </ul>
+</div>
<script src='/javascripts/model.js'></script>
<script src='/javascripts/entities.js'></script>
<script src='/javascripts/ui.js'></script>
+ <script src='/javascripts/index.js'></script>
</body>
</html>
restrict: 'E',
scope: {
listEntities: '=',
+ listId: '@',
listTitle: '@',
listObjectFilter: '=',
},
--- /dev/null
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+ angular
+ .module('index', ['model', 'ngSanitize'])
+
+ .run(['$anchorScroll', function($anchorScroll) {
+ $anchorScroll.yOffset = 80;
+ }])
+
+ .controller('IndexCtrl', ['Catalog', '$routeParams', '$sce', '$scope', '$location', '$anchorScroll', function(Catalog, $routeParams, $sce, $scope, $location, $anchorScroll) {
+ this.loadHighlighted = function() {
+ Catalog.loadHightlighted(
+ function(data) {
+ $scope.highlighted = data;
+ }, function(err) {
+ $scope.error = err;
+ });
+ };
+
+ this.loadMostRequired = function() {
+ Catalog.loadMostRequired(
+ function(data) {
+ $scope.required = data;
+ }, function(err) {
+ $scope.error = err;
+ });
+ };
+
+ this.loadByTags = function() {
+ Catalog.loadByTags(
+ function(data) {
+ $scope.bytags = data;
+ }, function(err) {
+ $scope.error = err;
+ });
+ };
+
+ this.loadStats = function() {
+ Catalog.loadStats(
+ function(data) {
+ $scope.stats = data;
+ }, function(err) {
+ $scope.error = err;
+ });
+ };
+
+ this.loadContributors = function() {
+ Catalog.loadContributors(
+ function(data) {
+ $scope.contributors = data;
+ }, function(err) {
+ $scope.error = err;
+ });
+ };
+
+
+ this.scrollTo = function(hash) {
+ $anchorScroll(hash);
+ }
+
+ this.loadHighlighted();
+ this.loadStats();
+ this.loadContributors();
+ }])
+
+ .directive('contributorList', ['Model', function(Model) {
+ return {
+ restrict: 'E',
+ scope: {
+ listId: '@',
+ listTitle: '@',
+ listContributors: '='
+ },
+ templateUrl: '/directives/contributor-list.html'
+ };
+ }])
+})();
.factory('Model', [ '$http', function($http) {
return {
+
loadEntity: function(id, cb, cbErr) {
$http.get(apiUrl + '/entity/' + id)
.success(cb)
}
};
}])
+
+ .factory('Catalog', [ '$http', function($http) {
+ return {
+ loadHightlighted: function(cb, cbErr) {
+ $http.get(apiUrl + '/catalog/highlighted')
+ .success(cb)
+ .error(cbErr);
+ },
+
+ loadMostRequired: function(cb, cbErr) {
+ $http.get(apiUrl + '/catalog/required')
+ .success(cb)
+ .error(cbErr);
+ },
+
+ loadByTags: function(cb, cbErr) {
+ $http.get(apiUrl + '/catalog/bytags')
+ .success(cb)
+ .error(cbErr);
+ },
+
+ loadStats: function(cb, cbErr) {
+ $http.get(apiUrl + '/catalog/stats')
+ .success(cb)
+ .error(cbErr);
+ },
+
+ loadContributors: function(cb, cbErr) {
+ $http.get(apiUrl + '/catalog/contributors')
+ .success(cb)
+ .error(cbErr);
+ },
+ }
+ }])
})();
*/
(function() {
- angular.module('nitweb', ['ngRoute', 'ngSanitize', 'entities'])
+ angular.module('nitweb', ['ngRoute', 'ngSanitize', 'entities', 'index'])
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
- templateUrl: 'views/index.html'
+ templateUrl: 'views/index.html',
+ controller: 'IndexCtrl',
+ controllerAs: 'indexCtrl'
})
.when('/package/:id', {
templateUrl: 'views/package.html',
margin-bottom: 0px;
}
/*
+ * Users
+ */
+
+.avatar {
+ border-radius: 2px;
+}
+
+/*
* Code Highlighting
*/
-<h1>Hello nitweb!</h1>
+<div class='container-fluid'>
+ <div class='page-header'>
+ <h2>Welcome to NitWeb!</h2>
+ <p class='text-muted'>The Nit knowledge base.</p>
+ </div>
+
+ <ul class='nav nav-tabs' role='tablist'>
+ <li role='presentation' class='active'>
+ <a data-toggle='tab' role='tab' data-target='#highlighted' aria-controls='highlighted'>
+ <span class='glyphicon glyphicon-book'/> Highlighed
+ </a>
+ </li>
+ <li role='presentation'>
+ <a data-toggle='tab' role='tab' data-target='#required' aria-controls='required'
+ ng-click='indexCtrl.loadMostRequired()'>
+ <span class='glyphicon glyphicon-book'/> Most required
+ </a>
+ </li>
+ <li role='presentation'>
+ <a data-toggle='tab' role='tab' data-target='#bytags' aria-controls='bytags'
+ ng-click='indexCtrl.loadByTags()'>
+ <span class='glyphicon glyphicon-book'/> By tags
+ </a>
+ </li>
+ </ul>
+ <table class='table'>
+ <tr>
+ <td ng-repeat='(key, value) in stats'>
+ <h5><strong>{{value}}</strong> <span>{{key}}</span></h5>
+ </td>
+ </tr>
+ </table>
+
+ <div class='container-fluid'>
+ <div class='col-xs-9'>
+ <div class='tab-content'>
+ <div role='tabpanel' class='tab-pane fade in active' id='highlighted'>
+ <entity-list list-title='Highlighted packages'
+ list-entities='highlighted'
+ list-object-filter='{}' />
+ </div>
+ <div role='tabpanel' class='tab-pane fade' id='required'>
+ <entity-list list-title='Most required'
+ list-entities='required'
+ list-object-filter='{}' />
+ </div>
+ <div role='tabpanel' class='tab-pane fade' id='bytags'>
+ <h3>Tags</h3>
+ <div class='container-fluid'>
+ <div class='col-xs-3' ng-repeat='(tag, packages) in bytags'>
+ <span class='badge'>{{packages.length}}</span>
+ <a ng-click='indexCtrl.scrollTo(tag)'>{{tag}}</a>
+ </div>
+ </div>
+ <div ng-repeat='(tag, packages) in bytags'>
+ <entity-list list-id='{{tag}}' list-title='{{tag}}'
+ list-entities='packages'
+ list-object-filter='{}' />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class='col-xs-3'>
+ <contributor-list list-title='Maintainers'
+ list-contributors='contributors.maintainers' />
+ <contributor-list list-title='Contributors'
+ list-contributors='contributors.contributors' />
+ </div>
+ </div>
+</div>
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)
var model = mainmodule.model
var modelbuilder = toolcontext.modelbuilder
+ # Build catalog
+ var catalog = new Catalog(modelbuilder)
+ for mpackage in model.mpackages do
+ catalog.deps.add_node(mpackage)
+ for mgroup in mpackage.mgroups do
+ for mmodule in mgroup.mmodules do
+ for imported in mmodule.in_importation.direct_greaters do
+ var ip = imported.mpackage
+ if ip == null or ip == mpackage then continue
+ catalog.deps.add_edge(mpackage, ip)
+ end
+ end
+ end
+ catalog.git_info(mpackage)
+ catalog.package_page(mpackage)
+ end
+
# Run the server
var host = toolcontext.opt_host.value or else "localhost"
var port = toolcontext.opt_port.value
var app = new App
app.use_before("/*", new RequestClock)
- app.use("/api", new APIRouter(model, modelbuilder, mainmodule))
+ app.use("/api", new APIRouter(model, modelbuilder, mainmodule, catalog))
app.use("/doc/:namespace", new DocAction(model, mainmodule, modelbuilder))
app.use("/*", new StaticHandler(toolcontext.share_dir / "nitweb", "index.html"))
app.use_after("/*", new ConsoleLog)
end
end
+# Group all api handlers in one router.
+class APIRouter
+ super Router
+
+ # Model to pass to handlers.
+ var model: Model
+
+ # ModelBuilder to pass to handlers.
+ var modelbuilder: ModelBuilder
+
+ # Mainmodule to pass to handlers.
+ var mainmodule: MModule
+
+ # Catalog to pass to handlers.
+ var catalog: Catalog
+
+ init do
+ use("/catalog", new APICatalogRouter(model, mainmodule, catalog))
+ use("/list", new APIList(model, mainmodule))
+ use("/search", new APISearch(model, mainmodule))
+ use("/random", new APIRandom(model, mainmodule))
+ use("/entity/:id", new APIEntity(model, mainmodule))
+ use("/code/:id", new APIEntityCode(model, mainmodule, modelbuilder))
+ use("/uml/:id", new APIEntityUML(model, mainmodule))
+ end
+end
+
# build toolcontext
var toolcontext = new ToolContext
var tpl = new Template
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
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module api_catalog
+
+import web_base
+import catalog
+
+# Group all api handlers in one router.
+class APICatalogRouter
+ super Router
+
+ # Model to pass to handlers.
+ var model: Model
+
+ # Mainmodule to pass to handlers.
+ var mainmodule: MModule
+
+ # Catalog to pass to handlers.
+ var catalog: Catalog
+
+ init do
+ use("/highlighted", new APICatalogHighLighted(model, mainmodule, catalog))
+ use("/required", new APICatalogMostRequired(model, mainmodule, catalog))
+ use("/bytags", new APICatalogByTags(model, mainmodule, catalog))
+ use("/contributors", new APICatalogContributors(model, mainmodule, catalog))
+ use("/stats", new APICatalogStats(model, mainmodule, catalog))
+ end
+end
+
+abstract class APICatalogHandler
+ super APIHandler
+
+ var catalog: Catalog
+
+ # List the 10 best packages from `cpt`
+ fun list_best(cpt: Counter[MPackage]): JsonArray do
+ var res = new JsonArray
+ var best = cpt.sort
+ for i in [1..10] do
+ if i > best.length then break
+ res.add best[best.length-i]
+ end
+ return res
+ end
+
+ # List packages by group.
+ fun list_by(map: MultiHashMap[Object, MPackage]): JsonObject do
+ var res = new JsonObject
+ var keys = map.keys.to_a
+ alpha_comparator.sort(keys)
+ for k in keys do
+ var projs = map[k].to_a
+ alpha_comparator.sort(projs)
+ res[k.to_s.html_escape] = new JsonArray.from(projs)
+ end
+ return res
+ end
+end
+
+class APICatalogStats
+ super APICatalogHandler
+
+ redef fun get(req, res) do
+ var obj = new JsonObject
+ obj["packages"] = model.mpackages.length
+ obj["maintainers"] = catalog.maint2proj.length
+ obj["contributors"] = catalog.contrib2proj.length
+ obj["modules"] = catalog.mmodules.sum
+ obj["classes"] = catalog.mclasses.sum
+ obj["methods"] = catalog.mmethods.sum
+ obj["loc"] = catalog.loc.sum
+ res.json obj
+ end
+end
+
+class APICatalogHighLighted
+ super APICatalogHandler
+
+ redef fun get(req, res) do res.json list_best(catalog.score)
+end
+
+class APICatalogMostRequired
+ super APICatalogHandler
+
+ redef fun get(req, res) do
+ if catalog.deps.not_empty then
+ var reqs = new Counter[MPackage]
+ for p in model.mpackages do
+ reqs[p] = catalog.deps[p].smallers.length - 1
+ end
+ res.json list_best(reqs)
+ return
+ end
+ res.json new JsonArray
+ end
+end
+
+class APICatalogByTags
+ super APICatalogHandler
+
+ redef fun get(req, res) do res.json list_by(catalog.tag2proj)
+end
+
+class APICatalogContributors
+ super APICatalogHandler
+
+ redef fun get(req, res) do
+ var obj = new JsonObject
+ obj["maintainers"] = new JsonArray.from(catalog.maint2proj.keys)
+ obj["contributors"] = new JsonArray.from(catalog.contrib2proj.keys)
+ res.json obj
+ end
+end
+
+redef class Person
+ super Jsonable
+
+ redef fun to_json do
+ var obj = new JsonObject
+ obj["name"] = name
+ obj["email"] = email
+ obj["page"] = page
+ obj["hash"] = (email or else "").md5.to_lower
+ return obj.to_json
+ end
+end
import highlight
import uml
-# Specific handler for nitweb API.
-abstract class APIHandler
- super ModelHandler
-
- # The JSON API does not filter anything by default.
- #
- # So we can cache the model view.
- var view: ModelView is lazy do
- var view = new ModelView(model)
- view.min_visibility = private_visibility
- view.include_fictive = true
- view.include_empty_doc = true
- view.include_attribute = true
- view.include_test_suite = true
- return view
- end
-
- # Try to load the mentity from uri with `/:id`.
- #
- # Send 400 if `:id` is null.
- # Send 404 if no entity is found.
- # Return null in both cases.
- fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
- var id = req.param("id")
- if id == null then
- res.error 400
- return null
- end
- var mentity = find_mentity(view, id)
- if mentity == null then
- res.error 404
- end
- return mentity
- end
-end
-
-# Group all api handlers in one router.
-class APIRouter
- super Router
-
- # Model to pass to handlers.
- var model: Model
-
- # ModelBuilder to pass to handlers.
- var modelbuilder: ModelBuilder
-
- # Mainmodule to pass to handlers.
- var mainmodule: MModule
-
- init do
- use("/list", new APIList(model, mainmodule))
- use("/search", new APISearch(model, mainmodule))
- use("/random", new APIRandom(model, mainmodule))
- use("/entity/:id", new APIEntity(model, mainmodule))
- use("/code/:id", new APIEntityCode(model, mainmodule, modelbuilder))
- use("/uml/:id", new APIEntityUML(model, mainmodule))
- end
-end
-
# List all mentities.
#
# MEntities can be filtered on their kind using the `k` parameter.
import web_actions
import model_api
+import api_catalog
end
end
+# Specific handler for nitweb API.
+abstract class APIHandler
+ super ModelHandler
+
+ # The JSON API does not filter anything by default.
+ #
+ # So we can cache the model view.
+ var view: ModelView is lazy do
+ var view = new ModelView(model)
+ view.min_visibility = private_visibility
+ view.include_fictive = true
+ view.include_empty_doc = true
+ view.include_attribute = true
+ view.include_test_suite = true
+ return view
+ end
+
+ # Try to load the mentity from uri with `/:id`.
+ #
+ # Send 400 if `:id` is null.
+ # Send 404 if no entity is found.
+ # Return null in both cases.
+ fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
+ var id = req.param("id")
+ if id == null then
+ res.error 400
+ return null
+ end
+ var mentity = find_mentity(view, id)
+ if mentity == null then
+ res.error 404
+ end
+ return mentity
+ end
+end
+
# A NitView is rendered by an action.
interface NitView
# Renders this view and returns something that can be written to a HTTP response.
-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