# Gen a test db with a random name (to avoid race conditions).
fun gen_test_db: MongoDb do
- var db_name = "test_nitrpg_{get_time}_{1000.rand}"
+ var testid = "NIT_TESTING_ID".environ.to_i
+ var db_name = "test_nitrpg_{testid}"
var db = load_db(db_name)
test_dbs.add db
return db
# Calculate the self-th element of the fibonacci sequence.
fun fibonacci: Int
do
- if self < 2 then
- return 1
- else
- return (self-2).fibonacci + (self-1).fibonacci
- end
- end
+ if self < 2 then return self
+ return (self-2).fibonacci + (self-1).fibonacci
+ end
end
# Print usage and exit.
do
return command.to_cstring.system
end
+
+ # The pid of the program
+ fun pid: Int `{ return getpid(); `}
end
redef class NativeString
return res
end
+ # Is `self` the path to an existing directory ?
+ #
+ # ~~~nit
+ # assert ".".to_path.is_dir
+ # assert not "/etc/issue".to_path.is_dir
+ # assert not "/should/not/exist".to_path.is_dir
+ # ~~~
+ fun is_dir: Bool do
+ var st = stat
+ if st == null then return false
+ return st.is_dir
+ end
+
# Delete a directory and all of its content
#
# Does not go through symbolic links and may get stuck in a cycle if there
# Show directory listing?
var show_directory_listing = true is writable
+ # Default file returned when no static file matches the requested URI.
+ #
+ # If no `default_file` is provided, the FileServer responds 404 error to
+ # unmatched queries.
+ var default_file: nullable String = null is writable
+
redef fun answer(request, turi)
do
var response
# This make sure that the requested file is within the root folder.
if (local_file + "/").has_prefix(root) then
# Does it exists?
- if local_file.file_exists then
- if local_file.file_stat.is_dir then
+ var file_stat = local_file.file_stat
+ if file_stat != null then
+ if file_stat.is_dir then
# If we target a directory without an ending `/`,
# redirect to the directory ending with `/`.
- if not request.uri.is_empty and
- request.uri.chars.last != '/' then
- response = new HttpResponse(303)
- response.header["Location"] = request.uri + "/"
- return response
+ var uri = request.uri
+ if not uri.is_empty and uri.chars.last != '/' then
+ return answer_redirection(request.uri + "/")
end
# Show index file instead of the directory listing
end
end
- var is_dir = local_file.file_stat.is_dir
- if show_directory_listing and is_dir then
- # Show the directory listing
- var title = turi
- var files = local_file.files
+ file_stat = local_file.file_stat
+ if show_directory_listing and file_stat != null and file_stat.is_dir then
+ response = answer_directory_listing(request, turi, local_file)
+ else if file_stat != null and not file_stat.is_dir then # It's a single file
+ response = answer_file(local_file)
+ else response = answer_default
+ else response = answer_default
+ else response = new HttpResponse(403)
- alpha_comparator.sort files
+ if response.status_code != 200 then
+ var tmpl = error_page(response.status_code)
+ if header != null and tmpl isa ErrorTemplate then tmpl.header = header
+ response.body = tmpl.to_s
+ end
- var links = new Array[String]
- if turi.length > 1 then
- var path = (request.uri + "/..").simplify_path
- links.add "<a href=\"{path}/\">..</a>"
- end
- for file in files do
- var local_path = local_file.join_path(file).simplify_path
- var web_path = file.simplify_path
- if local_path.file_stat.is_dir then web_path = web_path + "/"
- links.add "<a href=\"{web_path}\">{file}</a>"
- end
+ return response
+ end
+
+ # Answer the `default_file` if any.
+ fun answer_default: HttpResponse do
+ var default_file = self.default_file
+ if default_file == null then
+ return new HttpResponse(404)
+ end
+
+ var local_file = (root / default_file).simplify_path
+ return answer_file(local_file)
+ end
- var header = self.header
- var header_code
- if header != null then
- header_code = header.write_to_string
- else header_code = ""
+ # Answer a 303 redirection to `location`.
+ fun answer_redirection(location: String): HttpResponse do
+ var response = new HttpResponse(303)
+ response.header["Location"] = location
+ return response
+ end
+
+ # Build a reponse containing a single `local_file`.
+ #
+ # Returns a 404 error if local_file does not exists.
+ fun answer_file(local_file: String): HttpResponse do
+ if not local_file.file_exists then return new HttpResponse(404)
+
+ var response = new HttpResponse(200)
+ response.files.add local_file
+
+ # Set Content-Type depending on the file extension
+ var ext = local_file.file_extension
+ if ext != null then
+ var media_type = media_types[ext]
+ if media_type != null then
+ response.header["Content-Type"] = media_type
+ else response.header["Content-Type"] = "application/octet-stream"
+ end
- response = new HttpResponse(200)
- response.body = """
+ # Cache control
+ response.header["cache-control"] = cache_control
+ return response
+ end
+
+ # Answer with a directory listing for files within `local_files`.
+ fun answer_directory_listing(request: HttpRequest, turi, local_file: String): HttpResponse do
+ # Show the directory listing
+ var title = turi
+ var files = local_file.files
+
+ alpha_comparator.sort files
+
+ var links = new Array[String]
+ if turi.length > 1 then
+ var path = (request.uri + "/..").simplify_path
+ links.add "<a href=\"{path}/\">..</a>"
+ end
+ for file in files do
+ var local_path = local_file.join_path(file).simplify_path
+ var web_path = file.simplify_path
+ var file_stat = local_path.file_stat
+ if file_stat != null and file_stat.is_dir then web_path = web_path + "/"
+ links.add "<a href=\"{web_path}\">{file}</a>"
+ end
+
+ var header = self.header
+ var header_code
+ if header != null then
+ header_code = header.write_to_string
+ else header_code = ""
+
+ var response = new HttpResponse(200)
+ response.body = """
<!DOCTYPE html>
<head>
<meta charset="utf-8">
</body>
</html>"""
- response.header["Content-Type"] = media_types["html"].as(not null)
- else if not is_dir then
- # It's a single file
- response = new HttpResponse(200)
- response.files.add local_file
-
- var ext = local_file.file_extension
- if ext != null then
- var media_type = media_types[ext]
- if media_type != null then
- response.header["Content-Type"] = media_type
- else response.header["Content-Type"] = "application/octet-stream"
- end
-
- # Cache control
- response.header["cache-control"] = cache_control
-
- else response = new HttpResponse(404)
- else response = new HttpResponse(404)
- else response = new HttpResponse(403)
-
- if response.status_code != 200 then
- var tmpl = error_page(response.status_code)
- if header != null and tmpl isa ErrorTemplate then tmpl.header = header
- response.body = tmpl.to_s
- end
-
+ response.header["Content-Type"] = media_types["html"].as(not null)
return response
end
end
If you run the app from another directory, it’s safer to use the absolute path of
the directory that you want to serve.
+In some cases, you can want to redirect request to static files to a default file
+instead of returning a 404 error.
+This can be achieved by specifying a default file in the StaticHandler:
+
+~~~
+app.use("/static/", new StaticHandler("public/", "default.html"))
+~~~
+
+This way all non-matched queries to the StaticHandler will be answered with the
+`default.html` file.
+
## Advanced Routing
**Routing** refers to the definition of application end points (URIs) and how
* `res.send()` Send a response of various types.
* `res.error()` Set the response status code and send its message as the response body.
+## Response cycle
+
+When the popcorn `App` receives a request, the response cycle is the following:
+
+1. `pre-middlewares` lookup matching middlewares registered with `use_before(pre_middleware)`:
+ 1. execute matching middleware by registration order
+ 2. if a middleware send a response then let the `pre-middlewares` loop continue
+ with the next middleware
+2. `response-handlers` lookup matching handlers registered with `use(handler)`:
+ 1. execute matching middleware by registration order
+ 2. if a middleware send a response then stop the `response-handlers` loop
+ 3. if no hander matches or sends a response, generate a 404 response
+3. `post-middlewares` lookup matching handlers registered with `use_after(post_handler)`:
+ 1. execute matching middleware by registration order
+ 2. if a middleware send a response then let the `post-middlewares` loop continue
+ with the next middleware
+
## Middlewares
### Overview
var app = new App
-app.use("/*", new MyLogger)
+app.use_before("/*", new MyLogger)
app.use("/", new HelloHandler)
app.listen("localhost", 3000)
~~~
(even 404 ones) pass through the middleware handler.
This handler just prints “Request Logged!” when a request is received.
-The order of middleware loading is important: middleware functions that are loaded first are also executed first.
-In the above example, `MyLogger` will be executed before `HelloHandler`.
+Be default, the order of middleware execution is that are loaded first are also executed first.
+To ensure our middleware `MyLogger` will be executed before all the other, we add it
+with the `use_before` method.
### Ultra cool, more advanced logger example
end
var app = new App
-app.use("/*", new RequestTimeHandler)
+app.use_before("/*", new RequestTimeHandler)
app.use("/", new HelloHandler)
-app.use("/*", new LogHandler)
+app.use_after("/*", new LogHandler)
app.listen("localhost", 3000)
~~~
from the `req` parameter.
We use the new middleware called `RequestTimeHandler` to initialize the request timer.
+Because of the `use_before` method, the `RequestTimeHandler` middleware will be executed
+before all the others.
+
+We then let the `HelloHandler` produce the response.
Finally, our `LogHandler` will display a bunch of data and use the request `timer`
to display the time it took to process the request.
+Because of the `use_after` method, the `LogHandler` middleware will be executed after
+all the others.
The app now uses the `RequestTimeHandler` middleware for every requests received
by the Popcorn app.
import popcorn
var app = new App
-app.use("/*", new StaticHandler("my-ng-app/"))
+app.use("/*", new StaticHandler("my-ng-app/", "index.html"))
app.listen("localhost", 3000)
~~~
+Because the StaticHandler will not find the angular routes as static files,
+you must specify the path to the default angular controller.
+In this example, the StaticHandler will redirect any unknown requests to the `index.html`
+angular controller.
+
See the examples for a more detailed use case working with a JSON API.
var app = new App
app.use("/counter", new CounterAPI)
-app.use("/*", new StaticHandler("www/"))
+app.use("/*", new StaticHandler("www/", "index.html"))
app.listen("localhost", 3000)
end
var app = new App
-app.use("/*", new RequestTimeHandler)
+app.use_before("/*", new RequestTimeHandler)
app.use("/", new HelloHandler)
-app.use("/*", new LogHandler)
+app.use_after("/*", new LogHandler)
app.listen("localhost", 3000)
var app = new App
-app.use("/*", new LogHandler)
+app.use_before("/*", new LogHandler)
app.use("/", new HelloHandler)
app.listen("localhost", 3000)
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+import popcorn
+
+var app = new App
+app.use("/", new StaticHandler("public/", "default.html"))
+app.listen("localhost", 3000)
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+
+ <title>Some Popcorn love</title>
+
+ <link rel="stylesheet" type="text/css" href="/css/style.css">
+ </head>
+ <body>
+ <h1>Default Page</h1>
+ </body>
+</html>
# Static files directory to serve.
var static_dir: String
+ # Default file to serve if nothing matches the request.
+ #
+ # `null` for no default file.
+ var default_file: nullable String
+
# Internal file server used to lookup and render files.
var file_server: FileServer is lazy do
var srv = new FileServer(static_dir)
srv.show_directory_listing = false
+ srv.default_file = default_file
return srv
end
# List of handlers to match with requests.
private var handlers = new Map[AppRoute, Handler]
+ # List of handlers to match before every other.
+ private var pre_handlers = new Map[AppRoute, Handler]
+
+ # List of handlers to match after every other.
+ private var post_handlers = new Map[AppRoute, Handler]
+
# Register a `handler` for a route `path`.
#
# Route paths are matched in registration order.
fun use(path: String, handler: Handler) do
- var route
- if handler isa Router or handler isa StaticHandler then
- route = new AppGlobRoute(path)
- else if path.has_suffix("*") then
- route = new AppGlobRoute(path)
- else
- route = new AppParamRoute(path)
- end
+ var route = build_route(handler, path)
handlers[route] = handler
end
+ # Register a pre-handler for a route `path`.
+ #
+ # Prehandlers are matched before every other handlers in registrastion order.
+ fun use_before(path: String, handler: Handler) do
+ var route = build_route(handler, path)
+ pre_handlers[route] = handler
+ end
+
+ # Register a post-handler for a route `path`.
+ #
+ # Posthandlers are matched after every other handlers in registrastion order.
+ fun use_after(path: String, handler: Handler) do
+ var route = build_route(handler, path)
+ post_handlers[route] = handler
+ end
+
redef fun handle(route, uri, req, res) do
if not route.match(uri) then return
+ handle_pre(route, uri, req, res)
+ handle_in(route, uri, req, res)
+ handle_post(route, uri, req, res)
+ end
+
+ private fun handle_pre(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+ for hroute, handler in pre_handlers do
+ handler.handle(hroute, route.uri_root(uri), req, res)
+ end
+ end
+
+ private fun handle_in(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
for hroute, handler in handlers do
handler.handle(hroute, route.uri_root(uri), req, res)
if res.sent then break
end
end
+
+ private fun handle_post(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+ for hroute, handler in post_handlers do
+ handler.handle(hroute, route.uri_root(uri), req, res)
+ end
+ end
+
+ private fun build_route(handler: Handler, path: String): AppRoute do
+ if handler isa Router or handler isa StaticHandler then
+ return new AppGlobRoute(path)
+ else if path.has_suffix("*") then
+ return new AppGlobRoute(path)
+ else
+ return new AppParamRoute(path)
+ end
+ end
end
# Popcorn application.
redef fun answer(req, uri) do
uri = uri.simplify_path
var res = new HttpResponse(404)
+ for route, handler in pre_handlers do
+ handler.handle(route, uri, req, res)
+ end
for route, handler in handlers do
handler.handle(route, uri, req, res)
+ if res.sent then break
end
if not res.sent then
res.send(error_tpl(res.status_code, res.status_message), 404)
end
+ for route, handler in post_handlers do
+ handler.handle(route, uri, req, res)
+ end
res.session = req.session
return res
end
[Client] curl -s localhost:*****/counter -X POST
{"label":"Visitors","value":1}
[Client] curl -s localhost:*****/counter
-{"label":"Visitors","value":1}
\ No newline at end of file
+{"label":"Visitors","value":1}
+[Client] curl -s localhost:*****/not_found
+<!DOCTYPE html>
+<html lang='en' ng-app='ng-example'>
+ <head>
+ <base href='/'>
+ <title>ng-example</title>
+ </head>
+ <body>
+ <div ng-view></div>
+
+ <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js'></script>
+ <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-route.js'></script>
+ <script src='/javascripts/ng-example.js'></script>
+ </body>
+</html>
--- /dev/null
+
+[Client] curl -s localhost:*****/css/style.css
+body {
+ color: blue;
+ padding: 20px;
+}
+
+[Client] curl -s localhost:*****/js/app.js
+alert("Hello World!");
+
+[Client] curl -s localhost:*****/hello.html
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+
+ <title>Some Popcorn love</title>
+
+ <link rel="stylesheet" type="text/css" href="/css/style.css">
+ </head>
+ <body>
+ <h1>Hello Popcorn!</h1>
+
+ <img src="/images/trollface.jpg" alt="maybe it's a kitten?" />
+
+ <script src="/js/app.js"></script>
+ </body>
+</html>
+
+[Client] curl -s localhost:*****/
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+
+ <title>Some Popcorn love</title>
+
+ <link rel="stylesheet" type="text/css" href="/css/style.css">
+ </head>
+ <body>
+ <h1>Default Page</h1>
+ </body>
+</html>
+
+[Client] curl -s localhost:*****/css/not_found.nit
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+
+ <title>Some Popcorn love</title>
+
+ <link rel="stylesheet" type="text/css" href="/css/style.css">
+ </head>
+ <body>
+ <h1>Default Page</h1>
+ </body>
+</html>
+
+[Client] curl -s localhost:*****/static/css/not_found.nit
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+
+ <title>Some Popcorn love</title>
+
+ <link rel="stylesheet" type="text/css" href="/css/style.css">
+ </head>
+ <body>
+ <h1>Default Page</h1>
+ </body>
+</html>
+
+[Client] curl -s localhost:*****/not_found.nit
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+
+ <title>Some Popcorn love</title>
+
+ <link rel="stylesheet" type="text/css" href="/css/style.css">
+ </head>
+ <body>
+ <h1>Default Page</h1>
+ </body>
+</html>
</html>
[Client] curl -s localhost:*****/
-Warning: Headers already sent!
-<!DOCTYPE html>
-<html>
- <body>
- <h1>Another Index</h1>
- </body>
-</html>
<!DOCTYPE html>
<html>
<body>
end
var app = new App
-app.use("/*", new RequestTimeHandler)
+app.use_before("/*", new RequestTimeHandler)
app.use("/", new HelloHandler)
-app.use("/*", new LogHandler)
+app.use_after("/*", new LogHandler)
var host = test_host
var port = test_port
system "curl -s {host}:{port}/counter"
system "curl -s {host}:{port}/counter -X POST"
system "curl -s {host}:{port}/counter"
+ system "curl -s {host}:{port}/not_found" # handled by angular controller
return null
end
end
var app = new App
app.use("/counter", new CounterAPI)
-app.use("/*", new StaticHandler("www/"))
+app.use("/*", new StaticHandler("../examples/angular/www/", "index.html"))
var host = test_host
var port = test_port
end
var app = new App
-app.use("/*", new LogHandler)
+app.use_before("/*", new LogHandler)
app.use("/", new HelloHandler)
var host = test_host
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+import base_tests
+import example_static_default
+
+class TestClient
+ super ClientThread
+
+ redef fun main do
+ system "curl -s {host}:{port}/css/style.css"
+ system "curl -s {host}:{port}/js/app.js"
+ system "curl -s {host}:{port}/hello.html"
+ system "curl -s {host}:{port}/"
+
+ system "curl -s {host}:{port}/css/not_found.nit"
+ system "curl -s {host}:{port}/static/css/not_found.nit"
+ system "curl -s {host}:{port}/not_found.nit"
+
+ return null
+ end
+end
+
+var app = new App
+app.use("/", new StaticHandler("../examples/static_files/public/", "default.html"))
+
+var host = test_host
+var port = test_port
+
+var server = new AppThread(host, port, app)
+server.start
+0.1.sleep
+
+var client = new TestClient(host, port)
+client.start
+client.join
+0.1.sleep
+
+exit 0
--- /dev/null
+[package]
+name=readline
+tags=lib
+maintainer=Frédéric Vachon <fredvac@gmail.com>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/readline.nit
+git=https://github.com/nitlang/nit.git
+git.directory=lib/readline.nit
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2016 Frédéric Vachon <fredvac@gmail.com>
+#
+# 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.
+
+# GNU readline library wrapper
+module readline is ldflags "-lreadline"
+
+in "C" `{
+ #include <readline/readline.h>
+ #include <readline/history.h>
+`}
+
+private fun native_readline(prompt: NativeString): NativeString `{
+ return readline(prompt);
+`}
+
+private fun native_add_history(data: NativeString) `{
+ if (data == NULL) return;
+ add_history(data);
+`}
+
+# Set emacs keybindings mode
+fun set_vi_mode `{ rl_editing_mode = 0; `}
+
+# Set emacs keybindings mode
+fun set_emacs_mode `{ rl_editing_mode = 1; `}
+
+# Use the GNU Library readline function
+# Returns `null` if EOF is read
+# If `with_history` is true, it will save all commands in the history except
+# empty strings and white characters strings
+fun readline(message: String, with_history: nullable Bool): nullable String do
+ var line = native_readline(message.to_cstring)
+ if line.address_is_null then return null
+
+ var nit_str = line.to_s
+
+ if with_history != null and with_history then
+ if nit_str.trim != "" then native_add_history(line)
+ end
+
+ return nit_str
+end
+
+# Adds the data String to the history no matter what it contains
+fun add_history(data: String) do native_add_history data.to_cstring
## CUSTOMIZATION
-### `--sharedir`
-Directory containing nitdoc assets.
+### `--share-dir`
+Directory containing tools assets.
By default `$NIT_DIR/share/nitdoc/` is used.
`nitunit` produces a XML file compatible with JUnit.
### `--dir`
-Working directory (default is '.nitunit').
+Working directory (default is 'nitunit.out').
In order to execute the tests, nit files are generated then compiled and executed in the giver working directory.
+In case of success, the directory is removed.
+In case of failure, it is kept as is so files can be investigated.
+
### `--nitc`
nitc compiler to use.
Indicate the specific Nit compiler executable to use. See `--nitc`.
+### `NIT_TESTING`
+
+The environment variable `NIT_TESTING` is set to `true` during the execution of program tests.
+Some libraries of programs can use it to produce specific reproducible results; or just to exit their executions.
+
+Unit-tests may unset this environment variable to retrieve the original behavior of such piece of software.
+
+### `SRAND`
+
+In order to maximize reproducibility, `SRAND` is set to 0.
+This make the pseudo-random generator no random at all.
+See `Sys::srand` for details.
+
+To retrieve the randomness, unit-tests may unset this environment variable then call `srand`.
+
+### `NIT_TESTING_ID`
+
+Parallel executions can cause some race collisions on named resources (e.g. DB table names).
+To solve this issue, `NIT_TESTING_ID` is initialized with a distinct integer identifier that can be used to give unique names to resources.
+
+Note: `rand` is not a recommended way to get a distinct identifier because its randomness is disabled by default. See `SRAND`.
+
+
# SEE ALSO
The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
var opt_source = new OptionString("Format to link source code (%f for filename, " +
"%l for first line, %L for last line)", "--source")
- # Directory where the CSS and JS is stored.
- var opt_sharedir = new OptionString("Directory containing nitdoc assets", "--sharedir")
-
# Use a shareurl instead of copy shared files.
#
# This is usefull if you don't want to store the Nitdoc templates with your
super
option_context.add_option(
- opt_source, opt_sharedir, opt_shareurl, opt_custom_title,
+ opt_source, opt_share_dir, opt_shareurl, opt_custom_title,
opt_custom_footer, opt_custom_intro, opt_custom_brand,
opt_github_upstream, opt_github_base_sha1, opt_github_gitdir,
opt_piwik_tracker, opt_piwik_site_id,
var output_dir = ctx.output_dir
if not output_dir.file_exists then output_dir.mkdir
# locate share dir
- var sharedir = ctx.opt_sharedir.value
- if sharedir == null then
- var dir = ctx.nit_dir
- sharedir = dir/"share/nitdoc"
- if not sharedir.file_exists then
- print "Error: cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
- abort
- end
- end
+ var sharedir = ctx.share_dir / "nitdoc"
# copy shared files
if ctx.opt_shareurl.value == null then
sys.system("cp -r -- {sharedir.to_s.escape_to_sh}/* {output_dir.to_s.escape_to_sh}/")
import ordered_tree
private import more_collections
+redef class MEntity
+ # The visibility of the MEntity.
+ #
+ # MPackages, MGroups and MModules are always public.
+ # The visibility of `MClass` and `MProperty` is defined by the keyword used.
+ # `MClassDef` and `MPropDef` return the visibility of `MClass` and `MProperty`.
+ fun visibility: MVisibility do return public_visibility
+end
+
redef class Model
# All known classes
var mclasses = new Array[MClass]
# The visibility of the class
# In Nit, the visibility of a class cannot evolve in refinements
- var visibility: MVisibility
+ redef var visibility
init
do
# Is `self` and abstract class?
var is_abstract: Bool is lazy do return kind == abstract_kind
+
+ redef fun mdoc_or_fallback do return intro.mdoc_or_fallback
end
redef var location: Location
+ redef fun visibility do return mclass.visibility
+
# Internal name combining the module and the class
# Example: "mymodule$MyClass"
redef var to_s is noinit
redef var location
+ redef fun mdoc_or_fallback do return intro.mdoc_or_fallback
+
# The canonical name of the property.
#
# It is currently the short-`name` prefixed by the short-name of the class and the full-name of the module.
end
# The visibility of the property
- var visibility: MVisibility
+ redef var visibility
# Is the property usable as an initializer?
var is_autoinit = false is writable
redef var location: Location
+ redef fun visibility do return mproperty.visibility
+
init
do
mclassdef.mpropdefs.add(self)
var mmodules = new HashSet[MModule]
mmodules.add self
mmodules.add_all collect_ancestors(view)
- mmodules.add_all collect_parents(view)
- mmodules.add_all collect_children(view)
mmodules.add_all collect_descendants(view)
return view.mmodules_poset(mmodules)
end
return res
end
+ # Build a class hierarchy poset for `self` based on its ancestors and descendants.
+ fun hierarchy_poset(mainmodule: MModule, view: ModelView): POSet[MClass] do
+ var mclasses = new HashSet[MClass]
+ mclasses.add self
+ mclasses.add_all collect_ancestors(view)
+ mclasses.add_all collect_descendants(view)
+ return view.mclasses_poset(mainmodule, mclasses)
+ end
+
# Collect all mproperties introduced in 'self' with `visibility >= min_visibility`.
fun collect_intro_mproperties(view: ModelView): Set[MProperty] do
var set = new HashSet[MProperty]
obj["class_name"] = class_name
obj["full_name"] = full_name
obj["mdoc"] = mdoc_or_fallback
+ obj["visibility"] = visibility
+ obj["location"] = location
var modifiers = new JsonArray
for modifier in collect_modifiers do
modifiers.add modifier
redef fun json do
var obj = super
- obj["visibility"] = public_visibility
if ini != null then
obj["ini"] = new JsonObject.from(ini.as(not null).to_map)
end
redef class MGroup
redef fun json do
var obj = super
- obj["visibility"] = public_visibility
obj["is_root"] = is_root
obj["mpackage"] = to_mentity_ref(mpackage)
obj["default_mmodule"] = to_mentity_ref(default_mmodule)
redef class MModule
redef fun json do
var obj = super
- obj["location"] = location
- obj["visibility"] = public_visibility
obj["mpackage"] = to_mentity_ref(mpackage)
obj["mgroup"] = to_mentity_ref(mgroup)
obj["intro_mclasses"] = to_mentity_refs(intro_mclasses)
redef class MClass
redef fun json do
var obj = super
- obj["visibility"] = visibility
var arr = new JsonArray
for mparameter in mparameters do arr.add mparameter
obj["mparameters"] = arr
redef class MClassDef
redef fun json do
var obj = super
- obj["visibility"] = mclass.visibility
- obj["location"] = location
obj["is_intro"] = is_intro
var arr = new JsonArray
for mparameter in mclass.mparameters do arr.add mparameter
redef class MProperty
redef fun json do
var obj = super
- obj["visibility"] = visibility
obj["intro"] = to_mentity_ref(intro)
obj["intro_mclassdef"] = to_mentity_ref(intro_mclassdef)
obj["mpropdefs"] = to_mentity_refs(mpropdefs)
redef class MPropDef
redef fun json do
var obj = super
- obj["visibility"] = mproperty.visibility
- obj["location"] = location
obj["is_intro"] = is_intro
obj["mclassdef"] = to_mentity_ref(mclassdef)
obj["mproperty"] = to_mentity_ref(mproperty)
# See the specific implementation in the subclasses.
fun visit_all(v: ModelVisitor) do end
- private fun accept_visibility(min_visibility: nullable MVisibility): Bool do return true
+ private fun accept_visibility(min_visibility: nullable MVisibility): Bool do
+ if min_visibility == null then return true
+ return visibility >= min_visibility
+ end
end
redef class Model
end
end
-redef class MClass
- redef fun accept_visibility(min_visibility) do
- if min_visibility == null then return true
- return visibility >= min_visibility
- end
-end
-
redef class MClassDef
# Visit all the classes and class definitions of the module.
#
v.enter_visit(x)
end
end
-
- redef fun accept_visibility(min_visibility) do
- if min_visibility == null then return true
- return mclass.visibility >= min_visibility
- end
-end
-
-redef class MProperty
- redef fun accept_visibility(min_visibility) do
- if min_visibility == null then return true
- return visibility >= min_visibility
- end
-end
-
-redef class MPropDef
- redef fun accept_visibility(min_visibility) do
- if min_visibility == null then return true
- return mproperty.visibility >= min_visibility
- end
end
end
"NIT_TESTING".setenv("true")
+"NIT_TESTING_ID".setenv(pid.to_s)
+"SRAND".setenv("0")
var test_dir = toolcontext.test_dir
test_dir.mkdir
var file = toolcontext.opt_output.value
if file == null then file = "nitunit.xml"
page.write_to_file(file)
-# print docunits results
-print "DocUnits:"
-if modelbuilder.unit_entities == 0 then
- print "No doc units found"
-else if modelbuilder.failed_entities == 0 and not toolcontext.opt_noact.value then
- print "DocUnits Success"
+
+# Print results
+printn "Docunits: Entities: {modelbuilder.total_entities}; Documented ones: {modelbuilder.doc_entities}; With nitunits: {modelbuilder.unit_entities}"
+if modelbuilder.unit_entities == 0 or toolcontext.opt_noact.value then
+ print ""
+else
+ printn "; Failures: "
+ var cpt = modelbuilder.failed_entities
+ if toolcontext.opt_no_color.value then
+ print cpt
+ else if cpt == 0 then
+ print "0".green.bold
+ else
+ print cpt.to_s.red.bold
+ end
end
-print "Entities: {modelbuilder.total_entities}; Documented ones: {modelbuilder.doc_entities}; With nitunits: {modelbuilder.unit_entities}; Failures: {modelbuilder.failed_entities}"
-# print testsuites results
-print "\nTestSuites:"
-if modelbuilder.total_tests == 0 then
- print "No test cases found"
-else if modelbuilder.failed_tests == 0 and not toolcontext.opt_noact.value then
- print "TestSuites Success"
+printn "Test suites: Classes: {modelbuilder.total_classes}; Test Cases: {modelbuilder.total_tests}"
+if modelbuilder.total_tests == 0 or toolcontext.opt_noact.value then
+ print ""
+else
+ printn "; Failures: "
+ var cpt = modelbuilder.failed_tests
+ if toolcontext.opt_no_color.value then
+ print cpt
+ else if cpt == 0 then
+ print "0".green.bold
+ else
+ print cpt.to_s.red.bold
+ end
end
-print "Class suites: {modelbuilder.total_classes}; Test Cases: {modelbuilder.total_tests}; Failures: {modelbuilder.failed_tests}"
+
+var total = modelbuilder.unit_entities + modelbuilder.total_tests
+var fail = modelbuilder.failed_entities + modelbuilder.failed_tests
+if toolcontext.opt_noact.value then
+ # nothing
+else if total == 0 then
+ var head = "[NOTHING]"
+ if not toolcontext.opt_no_color.value then
+ head = head.yellow
+ end
+ print "{head} No unit tests to execute."
+else if fail == 0 then
+ var head = "[SUCCESS]"
+ if not toolcontext.opt_no_color.value then
+ head = head.green.bold
+ end
+ print "{head} All {total} tests passed."
+else
+ var head = "[FAILURE]"
+ if not toolcontext.opt_no_color.value then
+ head = head.red.bold
+ end
+ print "{head} {fail}/{total} tests failed."
+
+ print "`{test_dir}` is not removed for investigation."
+ exit 1
+end
+
+test_dir.rmdir
import modelize
private import parser_util
import html
+import console
redef class ToolContext
# opt --full
# Working directory for testing.
fun test_dir: String do
var dir = opt_dir.value
- if dir == null then return ".nitunit"
+ if dir == null then return "nitunit.out"
return dir
end
#
# Default: 10 CPU minute
var ulimit_usertime = 600 is writable
+
+ # Show a single-line status to use as a progression.
+ #
+ # Note that the line starts with `'\r'` and is not ended by a `'\n'`.
+ # So it is expected that:
+ # * no other output is printed between two calls
+ # * the last `show_unit_status` is followed by a new-line
+ fun show_unit_status(name: String, tests: SequenceRead[UnitTest], more_message: nullable String)
+ do
+ var esc = 27.code_point.to_s
+ var line = "\r{esc}[K* {name} ["
+ var done = tests.length
+ for t in tests do
+ if not t.is_done then
+ line += " "
+ done -= 1
+ else if t.error == null then
+ line += ".".green.bold
+ else
+ line += "X".red.bold
+ end
+ end
+
+ if opt_no_color.value then
+ if done == 0 then
+ print "* {name} ({tests.length} tests)"
+ end
+ return
+ end
+
+ line += "] {done}/{tests.length}"
+ if more_message != null then
+ line += " " + more_message
+ end
+ printn "{line}"
+ end
+
+ # Shoe the full description of the test-case.
+ #
+ # The output honors `--no-color`.
+ #
+ # `more message`, if any, is added after the error message.
+ fun show_unit(test: UnitTest, more_message: nullable String) do
+ print test.to_screen(more_message, not opt_no_color.value)
+ end
end
-# A unit test is an elementary test discovered, run and reported bu nitunit.
+# A unit test is an elementary test discovered, run and reported by nitunit.
#
# This class factorizes `DocUnit` and `TestCase`.
abstract class UnitTest
+ # The name of the unit to show in messages
+ fun full_name: String is abstract
+
+ # The location of the unit test to show in messages.
+ fun location: Location is abstract
- # Error occurred during test-case execution.
+ # Flag that indicates if the unit test was compiled/run.
+ var is_done: Bool = false is writable
+
+ # Error message occurred during test-case execution (or compilation).
+ #
+ # e.g.: `Runtime Error`
var error: nullable String = null is writable
# Was the test case executed at least once?
+ #
+ # This will indicate the status of the test (failture or error)
var was_exec = false is writable
- # Return the `TestCase` in XML format compatible with Jenkins.
+ # The raw output of the execution (or compilation)
#
- # See to_xml
+ # It merges the standard output and error output
+ var raw_output: nullable String = null is writable
+
+ # The location where the error occurred, if it makes sense.
+ var error_location: nullable Location = null is writable
+
+ # A colorful `[OK]` or `[KO]`.
+ fun status_tag(color: nullable Bool): String do
+ color = color or else true
+ if not is_done then
+ return "[ ]"
+ else if error != null then
+ var res = "[KO]"
+ if color then res = res.red.bold
+ return res
+ else
+ var res = "[OK]"
+ if color then res = res.green.bold
+ return res
+ end
+ end
+
+ # The full (color) description of the test-case.
+ #
+ # `more message`, if any, is added after the error message.
+ fun to_screen(more_message: nullable String, color: nullable Bool): String do
+ color = color or else true
+ var res
+ var error = self.error
+ if error != null then
+ if more_message != null then error += " " + more_message
+ var loc = error_location or else location
+ if color then
+ res = "{status_tag(color)} {full_name}\n {loc.to_s.yellow}: {error}\n{loc.colored_line("1;31")}"
+ else
+ res = "{status_tag(color)} {full_name}\n {loc}: {error}"
+ end
+ var output = self.raw_output
+ if output != null then
+ res += "\n Output\n\t{output.chomp.replace("\n", "\n\t")}\n"
+ end
+ else
+ res = "{status_tag(color)} {full_name}"
+ if more_message != null then res += more_message
+ end
+ return res
+ end
+
+ # Return a `<testcase>` XML node in format compatible with Jenkins unit tests.
fun to_xml: HTMLTag do
var tc = new HTMLTag("testcase")
tc.attr("classname", xml_classname)
var error = self.error
if error != null then
if was_exec then
- tc.open("error").append("Runtime Error")
+ tc.open("error").append(error)
else
- tc.open("failure").append("Compilation Error")
+ tc.open("failure").append(error)
end
- tc.open("system-err").append(error.trunc(8192).filter_nonprintable)
+ end
+ var output = self.raw_output
+ if output != null then
+ tc.open("system-err").append(output.trunc(8192).filter_nonprintable)
end
return tc
end
# The `classname` attribute of the XML format.
#
# NOTE: jenkins expects a '.' in the classname attr
+ #
+ # See to_xml
fun xml_classname: String is abstract
# The `name` attribute of the XML format.
# The XML node associated to the module
var testsuite: HTMLTag
+ # The name of the suite
+ var name: String
+
# Markdown processor used to parse markdown comments and extract code.
var mdproc = new MarkdownProcessor
# Populate `blocks` from the markdown decorator
mdproc.process(mdoc.content.join("\n"))
-
- toolcontext.check_errors
end
# All extracted docunits
var docunits = new Array[DocUnit]
+ fun show_status(more_message: nullable String)
+ do
+ toolcontext.show_unit_status(name, docunits, more_message)
+ end
+
+ fun mark_done(du: DocUnit)
+ do
+ du.is_done = true
+ show_status(du.full_name + " " + du.status_tag)
+ end
+
# Execute all the docunits
fun run_tests
do
+ if docunits.is_empty then
+ return
+ end
+
var simple_du = new Array[DocUnit]
+ show_status
for du in docunits do
# Skip existing errors
- if du.error != null then continue
+ if du.error != null then
+ mark_done(du)
+ continue
+ end
var ast = toolcontext.parse_something(du.block)
if ast isa AExpr then
test_simple_docunits(simple_du)
+ show_status
+ print ""
+
+ for du in docunits do
+ toolcontext.show_unit(du)
+ end
+
for du in docunits do
testsuite.add du.to_xml
end
i += 1
toolcontext.info("Execute doc-unit {du.full_name} in {file} {i}", 1)
var res2 = toolcontext.safe_exec("{file.to_program_name}.bin {i} >'{file}.out1' 2>&1 </dev/null")
+ du.was_exec = true
var content = "{file}.out1".to_path.read_all
- var msg = content.trunc(8192).filter_nonprintable
+ du.raw_output = content
if res2 != 0 then
- du.error = content
- toolcontext.warning(du.location, "error", "ERROR: {du.full_name} (in {file}): Runtime error\n{msg}")
+ du.error = "Runtime error in {file} with argument {i}"
toolcontext.modelbuilder.failed_entities += 1
end
- toolcontext.check_errors
+ mark_done(du)
end
end
var res2 = 0
if res == 0 then
res2 = toolcontext.safe_exec("{file.to_program_name}.bin >'{file}.out1' 2>&1 </dev/null")
+ du.was_exec = true
end
var content = "{file}.out1".to_path.read_all
- var msg = content.trunc(8192).filter_nonprintable
+ du.raw_output = content
if res != 0 then
- du.error = content
- toolcontext.warning(du.location, "failure", "FAILURE: {du.full_name} (in {file}):\n{msg}")
+ du.error = "Compilation error in {file}"
toolcontext.modelbuilder.failed_entities += 1
else if res2 != 0 then
- toolcontext.warning(du.location, "error", "ERROR: {du.full_name} (in {file}):\n{msg}")
+ du.error = "Runtime error in {file}"
toolcontext.modelbuilder.failed_entities += 1
end
- toolcontext.check_errors
+ mark_done(du)
end
# Create and fill the header of a unit file `file`.
message = "Error: Invalid Nit code."
end
- executor.toolcontext.warning(location, "invalid-block", "{message} To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).")
- executor.toolcontext.modelbuilder.failed_entities += 1
-
var du = new_docunit
du.block += code
- du.error = "{location}: {message}"
+ du.error_location = location
+ du.error = message
+ executor.toolcontext.modelbuilder.failed_entities += 1
return
end
# The numbering of self in mdoc (starting with 0)
var number: Int
- # The name of the unit to show in messages
- fun full_name: String do
+ redef fun full_name do
var mentity = mdoc.original_mentity
- if mentity != null then return mentity.full_name
- return xml_classname + "." + xml_name
+ if mentity != null then
+ return mentity.full_name
+ else
+ return xml_classname + "." + xml_name
+ end
end
# The text of the code to execute.
#
# If `self` is made of multiple code-blocks, then the location
# starts at the first code-books and finish at the last one, thus includes anything between.
- var location: Location is lazy do
+ redef var location is lazy do
return new Location(mdoc.location.file, lines.first, lines.last+1, columns.first+1, 0)
end
var prefix = toolcontext.test_dir
prefix = prefix.join_path(mmodule.to_s)
- var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts)
+ var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts, "Docunits of module {mmodule.full_name}")
do
total_entities += 1
var prefix = toolcontext.test_dir
prefix = prefix.join_path(mgroup.to_s)
- var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts)
+ var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts, "Docunits of group {mgroup.full_name}")
total_entities += 1
var mdoc = mgroup.mdoc
ts.attr("package", file)
var prefix = toolcontext.test_dir / "file"
- var d2m = new NitUnitExecutor(toolcontext, prefix, null, ts)
+ var d2m = new NitUnitExecutor(toolcontext, prefix, null, ts, "Docunits of file {file}")
total_entities += 1
doc_entities += 1
# Test to be executed after the whole test suite.
var after_module: nullable TestCase = null
+ fun show_status(more_message: nullable String)
+ do
+ toolcontext.show_unit_status("Test-suite of module " + mmodule.full_name, test_cases, more_message)
+ end
+
# Execute the test suite
fun run do
+ show_status
if not toolcontext.test_dir.file_exists then
toolcontext.test_dir.mkdir
end
toolcontext.info("Execute test-suite {mmodule.name}", 1)
var before_module = self.before_module
if not before_module == null then before_module.run
- for case in test_cases do case.run
+ for case in test_cases do
+ case.run
+ show_status(case.full_name + " " + case.status_tag)
+ end
+
+ show_status
+ print ""
+
var after_module = self.after_module
if not after_module == null then after_module.run
+ for case in test_cases do
+ toolcontext.show_unit(case)
+ end
end
# Write the test unit for `self` in a nit compilable file.
# Test method to be compiled and tested.
var test_method: MMethodDef
+ redef fun full_name do return test_method.full_name
+
+ redef fun location do return test_method.location
+
# `ToolContext` to use to display messages and find `nitc` bin.
var toolcontext: ToolContext
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")
- var f = new FileReader.open("{res_name}.out1")
- var msg = f.read_all
- f.close
+ self.raw_output = "{res_name}.out1".to_path.read_all
# set test case result
- var loc = test_method.location
if res != 0 then
- error = msg
- toolcontext.warning(loc, "failure",
- "ERROR: {method_name} (in file {test_file}.nit): {msg}")
+ error = "Runtime Error in file {test_file}.nit"
toolcontext.modelbuilder.failed_tests += 1
else
+ # no error, check with res file, if any.
var mmodule = test_method.mclassdef.mmodule
var file = mmodule.filepath
if file != null then
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
- msg = "Diff\n" + "{res_name}.diff".to_path.read_all
- error = msg
- toolcontext.warning(loc, "failure",
- "ERROR: {method_name} (in file {test_file}.nit): {msg}")
+ 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
end
end
end
- toolcontext.check_errors
+ is_done = true
end
redef fun xml_classname do
# Option --nit-dir
var opt_nit_dir = new OptionString("Base directory of the Nit installation", "--nit-dir")
+ # Option --share-dir
+ var opt_share_dir = new OptionString("Directory containing tools assets", "--share-dir")
+
# Option --help
var opt_help = new OptionBool("Show Help (This screen)", "-h", "-?", "--help")
# The identified root directory of the Nit package
var nit_dir: String is noinit
+ # Shared files directory.
+ #
+ # Most often `nit/share/`.
+ var share_dir: String is lazy do
+ var sharedir = opt_share_dir.value
+ if sharedir == null then
+ sharedir = nit_dir / "share"
+ if not sharedir.file_exists then
+ fatal_error(null, "Fatal Error: cannot locate shared files directory in {sharedir}. Uses --share-dir to define it's location.")
+ end
+ end
+ return sharedir
+ end
+
private fun compute_nit_dir: String
do
# the option has precedence
test_nitunit.nit --no-color -o $WRITE
test_nitunit.nit --gen-suite --only-show
test_nitunit.nit --gen-suite --only-show --private
-test_nitunit2.nit -o $WRITE
+test_nitunit2.nit --no-color -o $WRITE
test_doc2.nit --no-color -o $WRITE
test_nitunit3 --no-color -o $WRITE
test_nitunit_md.md --no-color -o $WRITE
-test_nitunit.nit:21,7--22,0: ERROR: test_nitunit$X (in .nitunit/test_nitunit-2.nit):
-Runtime error: Assert failed (.nitunit/test_nitunit-2.nit:5)
+* Docunits of module test_nitunit::test_nitunit (4 tests)
-test_nitunit.nit:24,8--25,0: FAILURE: test_nitunit$X$foo (in .nitunit/test_nitunit-3.nit):
-.nitunit/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
+[OK] test_nitunit::test_nitunit
+[KO] test_nitunit$X
+ test_nitunit.nit:21,7--22,0: Runtime error in nitunit.out/test_nitunit-2.nit
+ Output
+ Runtime error: Assert failed (nitunit.out/test_nitunit-2.nit:5)
-test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/gen_test_test_nitunit.nit): Runtime error: Assert failed (test_test_nitunit.nit:39)
+[KO] test_nitunit$X$foo
+ test_nitunit.nit:24,8--25,0: Compilation error in nitunit.out/test_nitunit-3.nit
+ Output
+ nitunit.out/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
-DocUnits:
-Entities: 27; Documented ones: 3; With nitunits: 3; Failures: 2
+[KO] test_nitunit$X$foo1
+ test_nitunit.nit:28,15: Syntax Error: unexpected operator '!'.
+* Test-suite of module test_test_nitunit::test_test_nitunit (3 tests)
-TestSuites:
-Class suites: 1; Test Cases: 3; Failures: 1
-<testsuites><testsuite package="test_nitunit::test_nitunit"><testcase classname="nitunit.test_nitunit::test_nitunit.<module>" name="<module>"><system-out>assert true
-</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="<class>"><system-out>assert false
-</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="test_nitunit::X::foo"><failure>Compilation Error</failure><system-err>.nitunit/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
+[OK] test_test_nitunit$TestX$test_foo
+[KO] test_test_nitunit$TestX$test_foo1
+ test_test_nitunit.nit:36,2--40,4: Runtime Error in file nitunit.out/gen_test_test_nitunit.nit
+ Output
+ Runtime error: Assert failed (test_test_nitunit.nit:39)
+
+[OK] test_test_nitunit$TestX$test_foo2
+Docunits: Entities: 27; Documented ones: 4; With nitunits: 4; Failures: 3
+Test suites: Classes: 1; Test Cases: 3; Failures: 1
+[FAILURE] 4/7 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit::test_nitunit"><testcase classname="nitunit.test_nitunit::test_nitunit.<module>" name="<module>"><system-err></system-err><system-out>assert true
+</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="<class>"><error>Runtime error in nitunit.out/test_nitunit-2.nit</error><system-err>Runtime error: Assert failed (nitunit.out/test_nitunit-2.nit:5)
+</system-err><system-out>assert false
+</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="test_nitunit::X::foo"><failure>Compilation error in nitunit.out/test_nitunit-3.nit</failure><system-err>nitunit.out/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
</system-err><system-out>assert undefined_identifier
-</system-out></testcase></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo"></testcase><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo1"><error>Runtime Error</error><system-err>Runtime error: Assert failed (test_test_nitunit.nit:39)
-</system-err></testcase><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo2"></testcase></testsuite></testsuites>
\ No newline at end of file
+</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="test_nitunit::X::foo1"><failure>Syntax Error: unexpected operator '!'.</failure><system-out>assert !@#$%^&*()
+</system-out></testcase></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo"><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo1"><error>Runtime Error in file nitunit.out/gen_test_test_nitunit.nit</error><system-err>Runtime error: Assert failed (test_test_nitunit.nit:39)
+</system-err></testcase><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo2"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
-DocUnits:
-DocUnits Success
-Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0
+* Docunits of module test_nitunit2::test_nitunit2 (3 tests)
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit2::test_nitunit2"><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="test_nitunit2::test_nitunit2::Sys::foo1"><system-out>if true then
+[OK] test_nitunit2::test_nitunit2$core::Sys$foo1
+[OK] test_nitunit2::test_nitunit2$core::Sys$bar2
+[OK] test_nitunit2::test_nitunit2$core::Sys$foo3
+Docunits: Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0
+Test suites: Classes: 0; Test Cases: 0
+[SUCCESS] All 3 tests passed.
+<testsuites><testsuite package="test_nitunit2::test_nitunit2"><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="test_nitunit2::test_nitunit2::Sys::foo1"><system-err></system-err><system-out>if true then
assert true
end
-</system-out></testcase><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="test_nitunit2::test_nitunit2::Sys::bar2"><system-out>if true then
+</system-out></testcase><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="test_nitunit2::test_nitunit2::Sys::bar2"><system-err></system-err><system-out>if true then
assert true
end
-</system-out></testcase><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="test_nitunit2::test_nitunit2::Sys::foo3"><system-out>var a = 1
+</system-out></testcase><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="test_nitunit2::test_nitunit2::Sys::foo3"><system-err></system-err><system-out>var a = 1
assert a == 1
assert a == 1
</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
-DocUnits:
-DocUnits Success
-Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 0
+* Docunits of module test_doc2::test_doc2 (3 tests)
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_doc2::test_doc2"><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="test_doc2::test_doc2::Sys::foo1"><system-out>assert true # tested
-</system-out></testcase><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="test_doc2::test_doc2::Sys::foo2"><system-out>assert true # tested
-</system-out></testcase><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="test_doc2::test_doc2::Sys::foo3"><system-out>assert true # tested
+[OK] test_doc2::test_doc2$core::Sys$foo1
+[OK] test_doc2::test_doc2$core::Sys$foo2
+[OK] test_doc2::test_doc2$core::Sys$foo3
+Docunits: Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 0
+Test suites: Classes: 0; Test Cases: 0
+[SUCCESS] All 3 tests passed.
+<testsuites><testsuite package="test_doc2::test_doc2"><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="test_doc2::test_doc2::Sys::foo1"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="test_doc2::test_doc2::Sys::foo2"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="test_doc2::test_doc2::Sys::foo3"><system-err></system-err><system-out>assert true # tested
</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
-test_nitunit3/README.md:7,3--5: Syntax Error: unexpected malformed character '\]. To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).
-test_nitunit3/README.md:4,2--15,0: ERROR: test_nitunit3> (in .nitunit/test_nitunit3-0.nit): Runtime error
-Runtime error: Assert failed (.nitunit/test_nitunit3-0.nit:7)
+* Docunits of group test_nitunit3> (2 tests)
-DocUnits:
-Entities: 2; Documented ones: 2; With nitunits: 3; Failures: 2
+[KO] test_nitunit3>
+ test_nitunit3/README.md:4,2--15,0: Runtime error in nitunit.out/test_nitunit3-0.nit with argument 1
+ Output
+ Runtime error: Assert failed (nitunit.out/test_nitunit3-0.nit:7)
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit3>"><testcase classname="nitunit.test_nitunit3>" name="<group>"><failure>Compilation Error</failure><system-err>Runtime error: Assert failed (.nitunit/test_nitunit3-0.nit:7)
+[KO] test_nitunit3>
+ test_nitunit3/README.md:7,3--5: Syntax Error: unexpected malformed character '\].
+* Docunits of module test_nitunit3::test_nitunit3 (1 tests)
+
+[OK] test_nitunit3::test_nitunit3
+Docunits: Entities: 2; Documented ones: 2; With nitunits: 3; Failures: 2
+Test suites: Classes: 0; Test Cases: 0
+[FAILURE] 2/3 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit3>"><testcase classname="nitunit.test_nitunit3>" name="<group>"><error>Runtime error in nitunit.out/test_nitunit3-0.nit with argument 1</error><system-err>Runtime error: Assert failed (nitunit.out/test_nitunit3-0.nit:7)
</system-err><system-out>assert false
assert true
-</system-out></testcase><testcase classname="nitunit.test_nitunit3>" name="<group>+1"><failure>Compilation Error</failure><system-err>test_nitunit3/README.md:7,3--5: Syntax Error: unexpected malformed character '\].</system-err><system-out>;'\][]
-</system-out></testcase></testsuite><testsuite package="test_nitunit3::test_nitunit3"><testcase classname="nitunit.test_nitunit3::test_nitunit3.<module>" name="<module>"><system-out>assert true
+</system-out></testcase><testcase classname="nitunit.test_nitunit3>" name="<group>+1"><failure>Syntax Error: unexpected malformed character '\].</failure><system-out>;'\][]
+</system-out></testcase></testsuite><testsuite package="test_nitunit3::test_nitunit3"><testcase classname="nitunit.test_nitunit3::test_nitunit3.<module>" name="<module>"><system-err></system-err><system-out>assert true
</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
-test_nitunit_md.md:4,2--16,0: ERROR: nitunit.<file>.test_nitunit_md.md:1,0--15,0 (in .nitunit/file-0.nit): Runtime error
-Runtime error: Assert failed (.nitunit/file-0.nit:8)
+* Docunits of file test_nitunit_md.md:1,0--15,0 (1 tests)
-DocUnits:
-Entities: 1; Documented ones: 1; With nitunits: 1; Failures: 1
+[KO] nitunit.<file>.test_nitunit_md.md:1,0--15,0
+ test_nitunit_md.md:4,2--16,0: Runtime error in nitunit.out/file-0.nit with argument 1
+ Output
+ Runtime error: Assert failed (nitunit.out/file-0.nit:8)
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit_md.md:1,0--15,0"><testcase classname="nitunit.<file>" name="test_nitunit_md.md:1,0--15,0"><failure>Compilation Error</failure><system-err>Runtime error: Assert failed (.nitunit/file-0.nit:8)
+Docunits: Entities: 1; Documented ones: 1; With nitunits: 1; Failures: 1
+Test suites: Classes: 0; Test Cases: 0
+[FAILURE] 1/1 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit_md.md:1,0--15,0"><testcase classname="nitunit.<file>" name="test_nitunit_md.md:1,0--15,0"><error>Runtime error in nitunit.out/file-0.nit with argument 1</error><system-err>Runtime error: Assert failed (nitunit.out/file-0.nit:8)
</system-err><system-out>var a = 1
assert 1 == 1
assert false
-test_doc3.nit:17,9--15: Syntax Error: unexpected identifier 'garbage'. To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).
-test_doc3.nit:23,4--10: Syntax Error: unexpected identifier 'garbage'. To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).
-test_doc3.nit:30,4--10: Syntax Error: unexpected identifier 'garbage'. To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).
-DocUnits:
-Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 3
+* Docunits of module test_doc3::test_doc3 (3 tests)
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_doc3::test_doc3"><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo1"><failure>Compilation Error</failure><system-err>test_doc3.nit:17,9--15: Syntax Error: unexpected identifier 'garbage'.</system-err><system-out> *garbage*
-</system-out></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo2"><failure>Compilation Error</failure><system-err>test_doc3.nit:23,4--10: Syntax Error: unexpected identifier 'garbage'.</system-err><system-out>*garbage*
-</system-out></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo3"><failure>Compilation Error</failure><system-err>test_doc3.nit:30,4--10: Syntax Error: unexpected identifier 'garbage'.</system-err><system-out>*garbage*
+[KO] test_doc3::test_doc3$core::Sys$foo1
+ test_doc3.nit:17,9--15: Syntax Error: unexpected identifier 'garbage'.
+[KO] test_doc3::test_doc3$core::Sys$foo2
+ test_doc3.nit:23,4--10: Syntax Error: unexpected identifier 'garbage'.
+[KO] test_doc3::test_doc3$core::Sys$foo3
+ test_doc3.nit:30,4--10: Syntax Error: unexpected identifier 'garbage'.
+Docunits: Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 3
+Test suites: Classes: 0; Test Cases: 0
+[FAILURE] 3/3 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_doc3::test_doc3"><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo1"><failure>Syntax Error: unexpected identifier 'garbage'.</failure><system-out> *garbage*
+</system-out></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo2"><failure>Syntax Error: unexpected identifier 'garbage'.</failure><system-out>*garbage*
+</system-out></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo3"><failure>Syntax Error: unexpected identifier 'garbage'.</failure><system-out>*garbage*
</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
-test_nitunit4/test_nitunit4.nit:22,2--26,4: ERROR: test_foo (in file .nitunit/gen_test_nitunit4.nit): Before Test
-Tested method
-After Test
-Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:31)
+* Test-suite of module test_nitunit4::test_nitunit4 (3 tests)
-test_nitunit4/test_nitunit4.nit:32,2--34,4: ERROR: test_baz (in file .nitunit/gen_test_nitunit4.nit): Diff
---- expected:test_nitunit4/test_nitunit4.sav/test_baz.res
-+++ got:.nitunit/gen_test_nitunit4_test_baz.out1
-@@ -1 +1,3 @@
--Bad result file
-+Before Test
-+Tested method
-+After Test
+[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
+ Before Test
+ Tested method
+ After Test
+ Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:31)
-DocUnits:
-No doc units found
-Entities: 12; Documented ones: 0; With nitunits: 0; Failures: 0
+[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
+ Output
+ Diff
+ --- expected:test_nitunit4/test_nitunit4.sav/test_baz.res
+ +++ got:nitunit.out/gen_test_nitunit4_test_baz.out1
+ @@ -1 +1,3 @@
+ -Bad result file
+ +Before Test
+ +Tested method
+ +After Test
-TestSuites:
-Class suites: 1; Test Cases: 3; Failures: 2
-<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</error><system-err>Before Test
+Docunits: Entities: 12; Documented ones: 0; With nitunits: 0
+Test suites: Classes: 1; Test Cases: 3; Failures: 2
+[FAILURE] 2/3 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
After Test
Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:31)
-</system-err></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_bar"></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_baz"><error>Runtime Error</error><system-err>Diff
+</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
-+++ got:.nitunit/gen_test_nitunit4_test_baz.out1
++++ got:nitunit.out/gen_test_nitunit4_test_baz.out1
@@ -1 +1,3 @@
-Bad result file
+Before Test
names::n3$::n1::P1$A::a MMethodDef names/n3.nit:31,2--32,19 a refinement (3 distinct modules)
names::n3$::n1::P1$::n0::P::p MMethodDef names/n3.nit:33,2--34,19 a refinement (3 distinct modules)
names::n0 MModule names/n0.nit:15,1--67,3 Root module
-names::Object MClass names/n0.nit:20,1--22,3
+names::Object MClass names/n0.nit:20,1--22,3 Root interface
names$Object MClassDef names/n0.nit:20,1--22,3 Root interface
names::Object::init MMethod names/n0.nit:20,1--22,3
names$Object$init MMethodDef names/n0.nit:20,1--22,3
-names::A MClass names/n0.nit:24,1--31,3
+names::A MClass names/n0.nit:24,1--31,3 A public class
names$A MClassDef names/n0.nit:24,1--31,3 A public class
-names::A::a MMethod names/n0.nit:26,2--27,13
+names::A::a MMethod names/n0.nit:26,2--27,13 A public method in a public class
names$A$a MMethodDef names/n0.nit:26,2--27,13 A public method in a public class
-names::n0::A::z MMethod names/n0.nit:29,2--30,21
+names::n0::A::z MMethod names/n0.nit:29,2--30,21 A private method in a public class
names$A$z MMethodDef names/n0.nit:29,2--30,21 A private method in a public class
-names::A0 MClass names/n0.nit:33,1--46,3
+names::A0 MClass names/n0.nit:33,1--46,3 A public subclass in the same module
names$A0 MClassDef names/n0.nit:33,1--46,3 A public subclass in the same module
names$A0$A::a MMethodDef names/n0.nit:38,2--39,19 Redefinition it the same module of a public method
names$A0$::n0::A::z MMethodDef names/n0.nit:41,2--42,19 Redefinition it the same module of a private method
names$A0$::n0::P::p MMethodDef names/n0.nit:44,2--45,19 Redefinition it the same module of a private method
-names::n0::P MClass names/n0.nit:48,1--52,3
+names::n0::P MClass names/n0.nit:48,1--52,3 A private class
names::n0$P MClassDef names/n0.nit:48,1--52,3 A private class
-names::n0::P::p MMethod names/n0.nit:50,2--51,13
+names::n0::P::p MMethod names/n0.nit:50,2--51,13 A private method in a private class
names::n0$P$p MMethodDef names/n0.nit:50,2--51,13 A private method in a private class
-names::n0::P0 MClass names/n0.nit:54,1--67,3
+names::n0::P0 MClass names/n0.nit:54,1--67,3 A private subclass introduced in the same module
names::n0$P0 MClassDef names/n0.nit:54,1--67,3 A private subclass introduced in the same module
names::n0$P0$A::a MMethodDef names/n0.nit:59,2--60,19 Redefinition it the same module of a public method
names::n0$P0$::n0::A::z MMethodDef names/n0.nit:62,2--63,19 Redefinition it the same module of a private method
names::n1$A MClassDef names/n1.nit:20,1--30,3 A refinement of a class
names::n1$A$a MMethodDef names/n1.nit:22,2--23,19 A refinement in the same class
names::n1$A$z MMethodDef names/n1.nit:25,2--26,19 A refinement in the same class
-names::n1::A::b MMethod names/n1.nit:28,2--29,13
+names::n1::A::b MMethod names/n1.nit:28,2--29,13 A public method introduced in a refinement
names::n1$A$b MMethodDef names/n1.nit:28,2--29,13 A public method introduced in a refinement
names::n1$A0 MClassDef names/n1.nit:32,1--42,3 A refinement of a subclass
names::n1$A0$A::a MMethodDef names/n1.nit:34,2--35,19 A refinement+redefinition
names::n1$A0$::n0::A::z MMethodDef names/n1.nit:37,2--38,19 A refinement+redefinition
names::n1$A0$::n0::P::p MMethodDef names/n1.nit:40,2--41,19 A refinement+redefinition
-names::A1 MClass names/n1.nit:44,1--57,3
+names::A1 MClass names/n1.nit:44,1--57,3 A subclass introduced in a submodule
names$A1 MClassDef names/n1.nit:44,1--57,3 A subclass introduced in a submodule
names$A1$A::a MMethodDef names/n1.nit:49,2--50,19 A redefinition in a subclass from a different module
names$A1$::n0::A::z MMethodDef names/n1.nit:52,2--53,19 A redefinition in a subclass from a different module
names::n1$::n0::P0$A::a MMethodDef names/n1.nit:67,2--68,19 A refinement+redefinition
names::n1$::n0::P0$::n0::A::z MMethodDef names/n1.nit:70,2--71,19 A refinement+redefinition
names::n1$::n0::P0$::n0::P::p MMethodDef names/n1.nit:73,2--74,19 A refinement+redefinition
-names::n1::P1 MClass names/n1.nit:77,1--90,3
+names::n1::P1 MClass names/n1.nit:77,1--90,3 A private subclass introduced in a different module
names::n1$P1 MClassDef names/n1.nit:77,1--90,3 A private subclass introduced in a different module
names::n1$P1$A::a MMethodDef names/n1.nit:82,2--83,19 A redefinition in a subclass from a different module
names::n1$P1$::n0::A::z MMethodDef names/n1.nit:85,2--86,19 A redefinition in a subclass from a different module
names::n1$P1$::n0::P::p MMethodDef names/n1.nit:88,2--89,19 A redefinition in a subclass from a different module
names::n2 MModule names/n2.nit:15,1--33,3 A alternative second module, used to make name conflicts
names::n2$A MClassDef names/n2.nit:20,1--27,3 A refinement of a class
-names::n2::A::b MMethod names/n2.nit:22,2--23,13
+names::n2::A::b MMethod names/n2.nit:22,2--23,13 Name conflict? A second public method
names::n2$A$b MMethodDef names/n2.nit:22,2--23,13 Name conflict? A second public method
-names::n2::A::z MMethod names/n2.nit:25,2--26,13
+names::n2::A::z MMethod names/n2.nit:25,2--26,13 Name conflict? A second private method
names::n2$A$z MMethodDef names/n2.nit:25,2--26,13 Name conflict? A second private method
-names::n2::P MClass names/n2.nit:29,1--33,3
+names::n2::P MClass names/n2.nit:29,1--33,3 Name conflict? A second private class
names::n2$P MClassDef names/n2.nit:29,1--33,3 Name conflict? A second private class
-names::n2::P::p MMethod names/n2.nit:31,2--32,13
+names::n2::P::p MMethod names/n2.nit:31,2--32,13 Name conflict? A private method in an homonym class.
names::n2$P$p MMethodDef names/n2.nit:31,2--32,13 Name conflict? A private method in an homonym class.
names1 MPackage names1.nit An alternative second module in a distinct package
names1> MGroup names1.nit An alternative second module in a distinct package
names1::names1$names::A MClassDef names1.nit:20,1--30,3 A refinement of a class
names1::names1$names::A$a MMethodDef names1.nit:22,2--23,19 A refinement in the same class
names1::names1$names::A$z MMethodDef names1.nit:25,2--26,19 A refinement in the same class
-names1::names1::A::b MMethod names1.nit:28,2--29,13
+names1::names1::A::b MMethod names1.nit:28,2--29,13 A public method introduced in a refinement
names1::names1$names::A$b MMethodDef names1.nit:28,2--29,13 A public method introduced in a refinement
names1::names1$names::A0 MClassDef names1.nit:32,1--42,3 A refinement of a subclass
names1::names1$names::A0$names::A::a MMethodDef names1.nit:34,2--35,19 A refinement+redefinition
names1::names1$names::A0$names::n0::A::z MMethodDef names1.nit:37,2--38,19 A refinement+redefinition
names1::names1$names::A0$names::n0::P::p MMethodDef names1.nit:40,2--41,19 A refinement+redefinition
-names1::A1 MClass names1.nit:44,1--57,3
+names1::A1 MClass names1.nit:44,1--57,3 A subclass introduced in a submodule
names1$A1 MClassDef names1.nit:44,1--57,3 A subclass introduced in a submodule
names1$A1$names::A::a MMethodDef names1.nit:49,2--50,19 A redefinition in a subclass from a different module
names1$A1$names::n0::A::z MMethodDef names1.nit:52,2--53,19 A redefinition in a subclass from a different module
names1::names1$names::n0::P0$names::A::a MMethodDef names1.nit:67,2--68,19 A refinement+redefinition
names1::names1$names::n0::P0$names::n0::A::z MMethodDef names1.nit:70,2--71,19 A refinement+redefinition
names1::names1$names::n0::P0$names::n0::P::p MMethodDef names1.nit:73,2--74,19 A refinement+redefinition
-names1::names1::P1 MClass names1.nit:77,1--90,3
+names1::names1::P1 MClass names1.nit:77,1--90,3 A private subclass introduced in a different module
names1::names1$P1 MClassDef names1.nit:77,1--90,3 A private subclass introduced in a different module
names1::names1$P1$names::A::a MMethodDef names1.nit:82,2--83,19 A redefinition in a subclass from a different module
names1::names1$P1$names::n0::A::z MMethodDef names1.nit:85,2--86,19 A redefinition in a subclass from a different module
--- /dev/null
+prompt>line 1
+line 1
+prompt>line 2
+line 2
+prompt>line 2bis
+line 2bis
+prompt>line 3 \b \bine\b \b\b \b\b \b3\b \b
+line 3
+prompt>
\ No newline at end of file
redef class Sys
var iface: String is lazy do
- srand
- return "localhost:{10000+20000.rand}"
+ var testid = "NIT_TESTING_ID".environ.to_i
+ return "localhost:{10000+testid}"
end
var fs_path: String = getcwd / "../lib/nitcorn/examples/www/hello_world/dir" is lazy
# assert undefined_identifier
fun foo do end
+ # a 'failure' unit test (does not parse)
+ # assert !@#$%^&*()
fun foo1(a, b: Int) do end
private fun foo2: Bool do return true
--- /dev/null
+line 1
+line 2
+line 2bis
+line 3 \bine\b\b\b3\b
--- /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.
+
+import readline
+
+loop
+ var line = readline("prompt>", true)
+ if line == null then break
+ print line
+end
export LANG=C
export LC_ALL=C
export NIT_TESTING=true
+# Use the pid as a collision prevention
+export NIT_TESTING_ID=$$
export NIT_SRAND=0
unset NIT_DIR