Merge: core: fix some "call on nullable" warnings
authorJean Privat <jean@pryen.org>
Wed, 1 Jun 2016 01:24:31 +0000 (21:24 -0400)
committerJean Privat <jean@pryen.org>
Wed, 1 Jun 2016 01:24:31 +0000 (21:24 -0400)
Too tired too work but no enough to sleep...

According to Jenkins [here](http://gresil.org/jenkins/job/CI_github/4177/warnings2Result/package.795683049/), [here](http://gresil.org/jenkins/job/CI_github/4177/warnings2Result/package.-1960060028/) and [here](http://gresil.org/jenkins/job/CI_github/4177/warnings2Result/package.1056020115/), this PR should fix the remaining warning of `core/`.

The algorithm used for the human/pillow approach was:

~~~
1. can it be fixed shamelessly with a .as(not null) ?
    yes: add that sweet .as(not null) and hope for the best
2. is there more than one occurrence of the variable involved?
    yes: factorise with a local variable, add that sweet .as(not null) and hope for the best
3. is this code to complex for my human/pillow head?
    yes: shame = false, fallback to 1.
abort "Not yet occurred case..."
~~~

There is love for @R4PaSs (core::text), @xymus (core::re) and @privat (core::*).

TL;DR No more warnings in core.

Pull-Request: #2027
Reviewed-by: Jean Privat <jean@pryen.org>

49 files changed:
NOTICE
README.md
examples/fibonacci.nit
lib/core/file.nit
lib/core/math.nit
lib/nitcorn/file_server.nit
lib/popcorn/README.md
lib/popcorn/examples/angular/example_angular.nit
lib/popcorn/examples/static_files/example_static_default.nit [new file with mode: 0644]
lib/popcorn/examples/static_files/public/default.html [new file with mode: 0644]
lib/popcorn/pop_handlers.nit
lib/popcorn/tests/res/test_example_angular.res
lib/popcorn/tests/res/test_example_static_default.res [new file with mode: 0644]
lib/popcorn/tests/test_example_angular.nit
lib/popcorn/tests/test_example_static_default.nit [new file with mode: 0644]
lib/readline.ini [new file with mode: 0644]
lib/readline.nit [new file with mode: 0644]
share/man/nitunit.md
src/catalog.nit
src/nitunit.nit
src/nitweb.nit
src/rapid_type_analysis.nit
src/testing/testing_base.nit
src/testing/testing_doc.nit
src/testing/testing_suite.nit
src/web/model_api.nit [new file with mode: 0644]
src/web/web.nit
src/web/web_actions.nit
src/web/web_base.nit
src/web/web_views.nit
tests/Darwin.skip
tests/base_gen_infinite2.nit [new file with mode: 0644]
tests/nitunit.args
tests/sav/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcatalog_args1.res
tests/sav/nitce/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcg/fixme/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcs/fixme/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcsg/fixme/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitunit_args1.res
tests/sav/nitunit_args4.res
tests/sav/nitunit_args5.res
tests/sav/nitunit_args6.res
tests/sav/nitunit_args7.res
tests/sav/nitunit_args8.res
tests/sav/nitunit_args9.res
tests/sav/test_readline.res [new file with mode: 0644]
tests/test_readline.inputs [new file with mode: 0644]
tests/test_readline.nit [new file with mode: 0644]

diff --git a/NOTICE b/NOTICE
index 33a5cff..514ed50 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -30,6 +30,7 @@ Copyright: 2004-2016 Jean Privat <jean@pryen.org>
            2015      Jean-Philippe Caissy <jean-philippe.caissy@shopify.com>
            2015      Alexandre Blondin Massé <alexandre.blondin.masse@gmail.com>
            2015-2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
+           2016      Tony Gaetani <tony.gaetani@gmail.com>
 License: Apache 2.0 (see LICENSE)
 Comment: The main license of the work, except for the following exceptions
 
index d84731a..1a5dd3c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Requirements:
  * gcc         http://gcc.gnu.org/ (or a compatible C compiler)
  * pkg-config  http://www.freedesktop.org/wiki/Software/pkg-config/
  * ccache      http://ccache.samba.org/        to improve recompilation
- * libgc-dev   http://www.hpl.hp.com/personal/Hans_Boehm/gc/
+ * libgc-dev   http://hboehm.info/gc/
  * graphviz    http://www.graphviz.org/        to enable graphs with the nitdoc tool
  * libunwind   http://nongnu.org/libunwind
 
index d4330ba..66cbfb0 100644 (file)
@@ -21,12 +21,9 @@ redef class Int
        # 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.
index 7b1f5ee..8bf8078 100644 (file)
@@ -664,6 +664,19 @@ class Path
                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
index 0f8325f..7b51e2a 100644 (file)
@@ -169,6 +169,16 @@ redef class Int
                end
                return res
        end
+
+       # Is `self` a power of two ?
+       #
+       # ~~~nit
+       # assert not 3.is_pow2
+       # assert 2.is_pow2
+       # assert 1.is_pow2
+       # assert not 0.is_pow2
+       # ~~~
+       fun is_pow2: Bool do return self != 0 and (self & self - 1) == 0
 end
 
 redef class Byte
index 5995558..a9ef5f6 100644 (file)
@@ -74,6 +74,12 @@ class FileServer
        # 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
@@ -86,15 +92,14 @@ class FileServer
                # 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
@@ -108,34 +113,94 @@ class FileServer
                                        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">
@@ -157,33 +222,7 @@ class FileServer
 </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
index a136621..2bb6646 100644 (file)
@@ -175,6 +175,17 @@ directory from where you launch your app.
 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
@@ -832,8 +843,13 @@ to your angular controller:
 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.
index ba7f4f2..9212a8e 100644 (file)
@@ -40,5 +40,5 @@ end
 
 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)
diff --git a/lib/popcorn/examples/static_files/example_static_default.nit b/lib/popcorn/examples/static_files/example_static_default.nit
new file mode 100644 (file)
index 0000000..6aa5b34
--- /dev/null
@@ -0,0 +1,21 @@
+# 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)
diff --git a/lib/popcorn/examples/static_files/public/default.html b/lib/popcorn/examples/static_files/public/default.html
new file mode 100644 (file)
index 0000000..2abb789
--- /dev/null
@@ -0,0 +1,13 @@
+<!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>
index 1c20b67..5d84961 100644 (file)
@@ -235,10 +235,16 @@ class StaticHandler
        # 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
 
index c07813f..bb94961 100644 (file)
@@ -4,4 +4,19 @@
 [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>
diff --git a/lib/popcorn/tests/res/test_example_static_default.res b/lib/popcorn/tests/res/test_example_static_default.res
new file mode 100644 (file)
index 0000000..59392f6
--- /dev/null
@@ -0,0 +1,88 @@
+
+[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>
index d1ab509..821aa32 100644 (file)
@@ -24,13 +24,14 @@ class TestClient
                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
diff --git a/lib/popcorn/tests/test_example_static_default.nit b/lib/popcorn/tests/test_example_static_default.nit
new file mode 100644 (file)
index 0000000..ced04e2
--- /dev/null
@@ -0,0 +1,52 @@
+# 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
diff --git a/lib/readline.ini b/lib/readline.ini
new file mode 100644 (file)
index 0000000..c7e2ee9
--- /dev/null
@@ -0,0 +1,11 @@
+[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
diff --git a/lib/readline.nit b/lib/readline.nit
new file mode 100644 (file)
index 0000000..783e4e4
--- /dev/null
@@ -0,0 +1,58 @@
+# 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
index e80e80e..6f09402 100644 (file)
@@ -268,10 +268,13 @@ Output name (default is 'nitunit.xml').
 `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.
 
index 1743179..92510bc 100644 (file)
@@ -362,8 +362,9 @@ class Catalog
                var doc_score = 0.0
                for g in mpackage.mgroups do
                        mmodules += g.mmodules.length
-                       entity_score += 1.0
-                       if g.mdoc != null then doc_score += 1.0
+                       var gs = 1.0
+                       entity_score += gs
+                       if g.mdoc != null then doc_score += gs
                        for m in g.mmodules do
                                var source = m.location.file
                                if source != null then
@@ -382,21 +383,23 @@ class Catalog
                                                loc += file.line_starts.length - 1
                                        end
                                end
-                               entity_score += 1.0
-                               if m.mdoc != null then doc_score += 1.0
+                               var ms = gs
+                               if m.is_test_suite then ms /= 100.0
+                               entity_score += ms
+                               if m.mdoc != null then doc_score += ms else ms /= 10.0
                                for cd in m.mclassdefs do
-                                       var s = 0.2
-                                       if not cd.is_intro then s /= 10.0
-                                       if not cd.mclass.visibility <= private_visibility then s /= 10.0
-                                       entity_score += s
-                                       if cd.mdoc != null then doc_score += s
+                                       var cs = ms * 0.2
+                                       if not cd.is_intro then cs /= 100.0
+                                       if not cd.mclass.visibility <= private_visibility then cs /= 100.0
+                                       entity_score += cs
+                                       if cd.mdoc != null then doc_score += cs
                                        mclasses += 1
                                        for pd in cd.mpropdefs do
-                                               s = 0.1
-                                               if not pd.is_intro then s /= 10.0
-                                               if not pd.mproperty.visibility <= private_visibility then s /= 10.0
-                                               entity_score += s
-                                               if pd.mdoc != null then doc_score += s
+                                               var ps = ms * 0.1
+                                               if not pd.is_intro then ps /= 100.0
+                                               if not pd.mproperty.visibility <= private_visibility then ps /= 100.0
+                                               entity_score += ps
+                                               if pd.mdoc != null then doc_score += ps
                                                if not pd isa MMethodDef then continue
                                                mmethods += 1
                                        end
index 878d3f2..8d79e80 100644 (file)
@@ -98,19 +98,62 @@ end
 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
index c055ee9..1df4153 100644 (file)
@@ -48,12 +48,10 @@ private class NitwebPhase
                var port = toolcontext.opt_port.value
 
                var app = new App
-               app.use("/random", new RandomAction(model))
-               app.use("/doc/:namespace", new DocAction(model, modelbuilder))
-               app.use("/code/:namespace", new CodeAction(model, modelbuilder))
-               app.use("/search/:namespace", new SearchAction(model))
-               app.use("/uml/:namespace", new UMLDiagramAction(model, mainmodule))
-               app.use("/", new TreeAction(model))
+
+               app.use("/api", new APIRouter(model, modelbuilder, mainmodule))
+               app.use("/doc/:namespace", new DocAction(model, mainmodule, modelbuilder))
+               app.use("/", new TreeAction(model, mainmodule))
 
                app.listen(host, port.to_i)
        end
index c072314..2d621e8 100644 (file)
@@ -314,7 +314,9 @@ class RapidTypeAnalysis
                                if not check_depth(rt) then continue
                                #print "{ot}/{t} -> {rt}"
                                live_types.add(rt)
-                               todo_types.add(rt)
+                               # unshift means a deep-first visit.
+                               # So that the `check_depth` limit is reached sooner.
+                               todo_types.unshift(rt)
                        end
                end
                #print "MType {live_types.length}: {live_types.join(", ")}"
index 88651ef..ddc167f 100644 (file)
@@ -35,7 +35,7 @@ redef class ToolContext
        # 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
 
@@ -116,6 +116,14 @@ ulimit -t {{{ulimit_usertime}}} 2> /dev/null
                                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
@@ -123,6 +131,14 @@ ulimit -t {{{ulimit_usertime}}} 2> /dev/null
                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 by nitunit.
@@ -157,32 +173,42 @@ abstract class UnitTest
        var error_location: nullable Location = null is writable
 
        # A colorful `[OK]` or `[KO]`.
-       fun status_tag: String do
+       fun status_tag(color: nullable Bool): String do
+               color = color or else true
                if not is_done then
                        return "[  ]"
                else if error != null then
-                       return "[KO]".red.bold
+                       var res = "[KO]"
+                       if color then res = res.red.bold
+                       return res
                else
-                       return "[OK]".green.bold
+                       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): String do
+       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
-                       res = "{status_tag} {full_name}\n     {loc.to_s.yellow}: {error}\n{loc.colored_line("1;31")}"
+                       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} {full_name}"
+                       res = "{status_tag(color)} {full_name}"
                        if more_message != null then res += more_message
                end
                return res
index 2465d37..a2fa067 100644 (file)
@@ -119,7 +119,7 @@ class NitUnitExecutor
                print ""
 
                for du in docunits do
-                       print du.to_screen
+                       toolcontext.show_unit(du)
                end
 
                for du in docunits do
index bbe9372..f755d25 100644 (file)
@@ -159,7 +159,7 @@ class TestSuite
                var after_module = self.after_module
                if not after_module == null then after_module.run
                for case in test_cases do
-                       print case.to_screen
+                       toolcontext.show_unit(case)
                end
        end
 
diff --git a/src/web/model_api.nit b/src/web/model_api.nit
new file mode 100644 (file)
index 0000000..85a2360
--- /dev/null
@@ -0,0 +1,251 @@
+# 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 model_api
+
+import web_base
+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
+
+# Search mentities from a query string.
+#
+# Example: `GET /search?q=Arr`
+class APISearch
+       super APIHandler
+
+       redef fun get(req, res) do
+               var q = req.string_arg("q")
+               if q == null then
+                       res.error 400
+                       return
+               end
+               var arr = new JsonArray
+               for mentity in view.mentities do
+                       if mentity.name.has_prefix(q) then arr.add mentity
+               end
+               res.json arr
+       end
+end
+
+# List all mentities.
+#
+# MEntities can be filtered on their kind using the `k` parameter.
+# Allowed kinds are `package`, `group`, `module`, `class`, `classdef`, `property`, `propdef`.
+#
+# List size can be limited with the `n` parameter.
+#
+# Example: `GET /list?k=module?n=10`
+class APIList
+       super APIHandler
+
+       # List mentities depending on the `k` kind parameter.
+       fun list_mentities(req: HttpRequest): Array[MEntity] do
+               var k = req.string_arg("k")
+               var mentities = new Array[MEntity]
+               if k == "package" then
+                       for mentity in view.mpackages do mentities.add mentity
+               else if k == "group" then
+                       for mentity in view.mgroups do mentities.add mentity
+               else if k == "module" then
+                       for mentity in view.mmodules do mentities.add mentity
+               else if k == "class" then
+                       for mentity in view.mclasses do mentities.add mentity
+               else if k == "classdef" then
+                       for mentity in view.mclassdefs do mentities.add mentity
+               else if k == "property" then
+                       for mentity in view.mproperties do mentities.add mentity
+               else if k == "propdef" then
+                       for mentity in view.mpropdefs do mentities.add mentity
+               else
+                       for mentity in view.mentities do mentities.add mentity
+               end
+               return mentities
+       end
+
+       # Limit mentities depending on the `n` parameter.
+       fun limit_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
+               var n = req.int_arg("n")
+               if n != null then
+                       return mentities.sub(0, n)
+               end
+               return mentities
+       end
+
+       redef fun get(req, res) do
+               var mentities = list_mentities(req)
+               mentities = limit_mentities(req, mentities)
+               var arr = new JsonArray
+               for mentity in mentities do arr.add mentity
+               res.json arr
+       end
+end
+
+# Return a random list of MEntities.
+#
+# Example: `GET /random?n=10&k=module`
+class APIRandom
+       super APIList
+
+       # Randomize mentities order.
+       fun randomize_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
+               var res = mentities.to_a
+               res.shuffle
+               return res
+       end
+
+       redef fun get(req, res) do
+               var mentities = list_mentities(req)
+               mentities = limit_mentities(req, mentities)
+               mentities = randomize_mentities(req, mentities)
+               var arr = new JsonArray
+               for mentity in mentities do arr.add mentity
+               res.json arr
+       end
+end
+
+# Return the JSON representation of a MEntity.
+#
+# Example: `GET /entity/core::Array`
+class APIEntity
+       super APIHandler
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then return
+               res.json mentity.api_json(self)
+       end
+end
+
+
+# Return a UML representation of MEntity.
+#
+# Example: `GET /entity/core::Array/uml`
+class APIEntityUML
+       super APIHandler
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               var dot
+               if mentity isa MClassDef then mentity = mentity.mclass
+               if mentity isa MClass then
+                       var uml = new UMLModel(view, mainmodule)
+                       dot = uml.generate_class_uml.write_to_string
+               else if mentity isa MModule then
+                       var uml = new UMLModel(view, mentity)
+                       dot = uml.generate_package_uml.write_to_string
+               else
+                       res.error 404
+                       return
+               end
+               res.send render_svg(dot)
+       end
+
+       # Render a `dot` string as a svg image.
+       fun render_svg(dot: String): String do
+               var proc = new ProcessDuplex("dot", "-Tsvg")
+               var svg = proc.write_and_read(dot)
+               proc.close
+               proc.wait
+               return svg
+       end
+end
+
+# Return the source code of MEntity.
+#
+# Example: `GET /entity/core::Array/code`
+class APIEntityCode
+       super APIHandler
+
+       # Modelbuilder used to access sources.
+       var modelbuilder: ModelBuilder
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then return
+               var source = render_source(mentity)
+               if source == null then
+                       res.error 404
+                       return
+               end
+               res.send source
+       end
+
+       # Highlight `mentity` source code.
+       private fun render_source(mentity: MEntity): nullable HTMLTag do
+               var node = modelbuilder.mentity2node(mentity)
+               if node == null then return null
+               var hl = new HighlightVisitor
+               hl.enter_visit node
+               return hl.html
+       end
+end
index 6f581f4..c6a12f0 100644 (file)
@@ -16,3 +16,4 @@
 module web
 
 import web_actions
+import model_api
index e9198ae..cb193fd 100644 (file)
@@ -20,7 +20,7 @@ import uml
 
 # Display the tree of all loaded mentities.
 class TreeAction
-       super ModelAction
+       super ModelHandler
 
        redef fun get(req, res) do
                var model = init_model_view(req)
@@ -29,51 +29,9 @@ class TreeAction
        end
 end
 
-# Display the list of mentities matching `namespace`.
-class SearchAction
-       super ModelAction
-
-       # TODO handle more than full namespaces.
-       redef fun get(req, res) do
-               var namespace = req.param("namespace")
-               var model = init_model_view(req)
-               var mentity = find_mentity(model, namespace)
-               if mentity == null then
-                       res.error(404)
-                       return
-               end
-               if req.is_json_asked then
-                       res.json(mentity.to_json)
-                       return
-               end
-               var view = new HtmlResultPage(namespace or else "null", [mentity])
-               res.send_view(view)
-       end
-end
-
-# Display a MEntity source code.
-class CodeAction
-       super ModelAction
-
-       # Modelbuilder used to access sources.
-       var modelbuilder: ModelBuilder
-
-       redef fun get(req, res) do
-               var namespace = req.param("namespace")
-               var model = init_model_view(req)
-               var mentity = find_mentity(model, namespace)
-               if mentity == null then
-                       res.error(404)
-                       return
-               end
-               var view = new HtmlSourcePage(modelbuilder, mentity)
-               res.send_view(view)
-       end
-end
-
 # Display the doc of a MEntity.
 class DocAction
-       super ModelAction
+       super ModelHandler
 
        # Modelbuilder used to access sources.
        var modelbuilder: ModelBuilder
@@ -86,76 +44,7 @@ class DocAction
                        res.error(404)
                        return
                end
-               if req.is_json_asked then
-                       res.json(mentity.to_json)
-                       return
-               end
-
                var view = new HtmlDocPage(modelbuilder, mentity)
                res.send_view(view)
        end
 end
-
-# Return an UML diagram for `namespace`.
-class UMLDiagramAction
-       super ModelAction
-
-       # Mainmodule used for hierarchy flattening.
-       var mainmodule: MModule
-
-       redef fun get(req, res) do
-               var namespace = req.param("namespace")
-               var model = init_model_view(req)
-               var mentity = find_mentity(model, namespace)
-               if mentity == null then
-                       res.error(404)
-                       return
-               end
-
-               var dot
-               if mentity isa MClassDef then mentity = mentity.mclass
-               if mentity isa MClass then
-                       var uml = new UMLModel(model, mainmodule)
-                       dot = uml.generate_class_uml.write_to_string
-               else if mentity isa MModule then
-                       var uml = new UMLModel(model, mentity)
-                       dot = uml.generate_package_uml.write_to_string
-               else
-                       res.error(404)
-                       return
-               end
-               var view = new HtmlDotPage(dot, mentity.as(not null).html_name)
-               res.send_view(view)
-       end
-end
-
-# Return a random list of MEntities.
-class RandomAction
-       super ModelAction
-
-       redef fun get(req, res) do
-               var n = req.int_arg("n") or else 10
-               var k = req.string_arg("k") or else "modules"
-               var model = init_model_view(req)
-               var mentities: Array[MEntity]
-               if k == "modules" then
-                       mentities = model.mmodules.to_a
-               else if k == "classdefs" then
-                       mentities = model.mclassdefs.to_a
-               else
-                       mentities = model.mpropdefs.to_a
-               end
-               mentities.shuffle
-               mentities = mentities.sub(0, n)
-               if req.is_json_asked then
-                       var json = new JsonArray
-                       for mentity in mentities do
-                               json.add mentity.to_json
-                       end
-                       res.json(json)
-                       return
-               end
-               var view = new HtmlResultPage("random", mentities)
-               res.send_view(view)
-       end
-end
index 258ec60..ec9bf75 100644 (file)
@@ -17,15 +17,19 @@ module web_base
 
 import model::model_views
 import model::model_json
+import doc_down
 import popcorn
 
 # Specific nitcorn Action that uses a Model
-class ModelAction
+class ModelHandler
        super Handler
 
        # Model to use.
        var model: Model
 
+       # MModule used to flatten model.
+       var mainmodule: MModule
+
        # Find the MEntity ` with `full_name`.
        fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
                if full_name == null then return null
@@ -58,9 +62,147 @@ redef class HttpResponse
        fun send_view(view: NitView, status: nullable Int) do send(view.render, status)
 end
 
-redef class HttpRequest
-       # Does the client asked for a json formatted response?
-       #
-       # Checks the URL get parameter `?json=true`.
-       fun is_json_asked: Bool do return bool_arg("json") or else false
+redef class MEntity
+
+       # URL to `self` within the web interface.
+       fun web_url: String is abstract
+
+       # URL to `self` within the JSON api.
+       fun api_url: String do return "/api/entity/" / full_name
+
+       redef fun json do
+               var obj = super
+               obj["web_url"] = web_url
+               obj["api_url"] = api_url
+               return obj
+       end
+
+       # Get the full json repesentation of `self` with MEntityRefs resolved.
+       fun api_json(handler: ModelHandler): JsonObject do return json
+end
+
+redef class MEntityRef
+       redef fun json do
+               var obj = super
+               obj["web_url"] = mentity.web_url
+               obj["api_url"] = mentity.api_url
+               obj["name"] = mentity.name
+               obj["mdoc"] = mentity.mdoc_or_fallback
+               obj["visibility"] = mentity.visibility
+               obj["location"] = mentity.location
+               var modifiers = new JsonArray
+               for modifier in mentity.collect_modifiers do
+                       modifiers.add modifier
+               end
+               obj["modifiers"] = modifiers
+               return obj
+       end
+end
+
+redef class MDoc
+
+       # Add doc down processing
+       redef fun json do
+               var obj = super
+               obj["synopsis"] = synopsis
+               obj["documentation"] = documentation
+               obj["comment"] = comment
+               obj["html_synopsis"] = html_synopsis.write_to_string
+               obj["html_documentation"] = html_documentation.write_to_string
+               obj["html_comment"] = html_comment.write_to_string
+               return obj
+       end
+end
+
+redef class MPackage
+       redef var web_url = "/package/{full_name}" is lazy
+end
+
+redef class MGroup
+       redef var web_url = "/group/{full_name}" is lazy
+end
+
+redef class MModule
+       redef var web_url = "/module/{full_name}" is lazy
+
+       redef fun api_json(handler) do
+               var obj = super
+               obj["intro_mclassdefs"] = to_mentity_refs(collect_intro_mclassdefs(private_view))
+               obj["redef_mclassdefs"] = to_mentity_refs(collect_redef_mclassdefs(private_view))
+               obj["imports"] = to_mentity_refs(in_importation.direct_greaters)
+               return obj
+       end
+end
+
+redef class MClass
+       redef var web_url = "/class/{full_name}" is lazy
+
+       redef fun api_json(handler) do
+               var obj = super
+               obj["all_mproperties"] = to_mentity_refs(collect_accessible_mproperties(private_view))
+               obj["intro_mproperties"] = to_mentity_refs(collect_intro_mproperties(private_view))
+               obj["redef_mproperties"] = to_mentity_refs(collect_redef_mproperties(private_view))
+               var poset = hierarchy_poset(handler.mainmodule, private_view)
+               obj["parents"] = to_mentity_refs(poset[self].direct_greaters)
+               return obj
+       end
+end
+
+redef class MClassDef
+       redef var web_url = "/classdef/{full_name}" is lazy
+
+       redef fun json do
+               var obj = super
+               obj["intro"] = to_mentity_ref(mclass.intro)
+               obj["mpackage"] = to_mentity_ref(mmodule.mpackage)
+               return obj
+       end
+
+       redef fun api_json(handler) do
+               var obj = super
+               obj["intro_mpropdefs"] = to_mentity_refs(collect_intro_mpropdefs(private_view))
+               obj["redef_mpropdefs"] = to_mentity_refs(collect_redef_mpropdefs(private_view))
+               return obj
+       end
+end
+
+redef class MProperty
+       redef var web_url = "/property/{full_name}" is lazy
+
+       redef fun json do
+               var obj = super
+               obj["intro_mclass"] = to_mentity_ref(intro_mclassdef.mclass)
+               obj["mpackage"] = to_mentity_ref(intro_mclassdef.mmodule.mpackage)
+               return obj
+       end
+end
+
+redef class MPropDef
+       redef var web_url = "/propdef/{full_name}" is lazy
+
+       redef fun json do
+               var obj = super
+               obj["intro"] = to_mentity_ref(mproperty.intro)
+               obj["intro_mclassdef"] = to_mentity_ref(mproperty.intro.mclassdef)
+               obj["mmodule"] = to_mentity_ref(mclassdef.mmodule)
+               obj["mgroup"] = to_mentity_ref(mclassdef.mmodule.mgroup)
+               obj["mpackage"] = to_mentity_ref(mclassdef.mmodule.mpackage)
+               return obj
+       end
+end
+
+redef class MClassType
+       redef var web_url = mclass.web_url is lazy
+end
+
+redef class MNullableType
+       redef var web_url = mtype.web_url is lazy
+end
+
+redef class MParameterType
+       redef var web_url = mclass.web_url is lazy
+end
+
+redef class MVirtualType
+       redef var web_url = mproperty.web_url is lazy
 end
index 58016e2..d8f8c9f 100644 (file)
@@ -35,34 +35,6 @@ class HtmlHomePage
        end
 end
 
-# Display a search results list.
-class HtmlResultPage
-       super NitView
-
-       # Initial query.
-       var query: String
-
-       # Result set
-       var results: Array[MEntity]
-
-       redef fun render do
-               var tpl = new Template
-               tpl.add new Header(1, "Results for {query}")
-               if results.is_empty then
-                       tpl.add "<p>No result for {query}.<p>"
-                       return tpl
-               end
-               var list = new UnorderedList
-               for mentity in results do
-                       var link = mentity.html_link
-                       link.text = mentity.html_full_name
-                       list.add_li new ListItem(link)
-               end
-               tpl.add list
-               return tpl
-       end
-end
-
 # Display the source for each mentities
 class HtmlSourcePage
        super NitView
@@ -119,29 +91,3 @@ class HtmlDocPage
                return tpl
        end
 end
-
-# Display the source for each mentities
-class HtmlDotPage
-       super NitView
-
-       # Dot to process.
-       var dot: Text
-
-       # Page title.
-       var title: String
-
-       redef fun render do
-               var tpl = new Template
-               tpl.add new Header(1, title)
-               tpl.add render_dot
-               return tpl
-       end
-
-       private fun render_dot: String do
-               var proc = new ProcessDuplex("dot", "-Tsvg", "-Tcmapx")
-               var svg = proc.write_and_read(dot)
-               proc.close
-               proc.wait
-               return svg
-       end
-end
index b70b71d..6a693ff 100644 (file)
@@ -6,3 +6,4 @@ neo
 mpi
 emscripten
 ui_test
+readline
diff --git a/tests/base_gen_infinite2.nit b/tests/base_gen_infinite2.nit
new file mode 100644 (file)
index 0000000..05c4fb6
--- /dev/null
@@ -0,0 +1,32 @@
+# 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 end
+
+interface Object
+       type SELF: Object
+       fun output_class_name is intern
+end
+
+class A[E]
+       new do return new B[E]
+end
+
+class B[E]
+       super A[E]
+
+       fun foo: A[E] do return new A[SELF]
+end
+
+(new B[Object]).foo.output_class_name
index 390709a..2116656 100644 (file)
@@ -1,7 +1,7 @@
 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
diff --git a/tests/sav/base_gen_infinite2.res b/tests/sav/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..e38f38e
--- /dev/null
@@ -0,0 +1 @@
+B[B[Object]]
index 19b015f..f97fee4 100644 (file)
@@ -76,7 +76,7 @@
 <h3>Quality</h3>
 <ul class="box">
 <li>28 warnings (63/kloc)</li>
-<li>93% documented</li>
+<li>95% documented</li>
 </ul>
 <h3>Tags</h3>
 <a href="../index.html#tag_test">test</a>, <a href="../index.html#tag_game">game</a><h3>Requirements</h3>
diff --git a/tests/sav/nitce/base_gen_infinite2.res b/tests/sav/nitce/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..223b783
--- /dev/null
@@ -0,0 +1 @@
+B
diff --git a/tests/sav/nitcg/fixme/base_gen_infinite2.res b/tests/sav/nitcg/fixme/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..2f63d29
--- /dev/null
@@ -0,0 +1 @@
+Fatal Error: limitation in the rapidtype analysis engine: a type depth of 256 is too important, the problematic type is `A[A[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[Sys]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
diff --git a/tests/sav/nitcs/fixme/base_gen_infinite2.res b/tests/sav/nitcs/fixme/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..2f63d29
--- /dev/null
@@ -0,0 +1 @@
+Fatal Error: limitation in the rapidtype analysis engine: a type depth of 256 is too important, the problematic type is `A[A[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[Sys]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
diff --git a/tests/sav/nitcsg/fixme/base_gen_infinite2.res b/tests/sav/nitcsg/fixme/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..2f63d29
--- /dev/null
@@ -0,0 +1 @@
+Fatal Error: limitation in the rapidtype analysis engine: a type depth of 256 is too important, the problematic type is `A[A[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[Sys]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
index cbfe0bc..a45c8d4 100644 (file)
@@ -1,43 +1,36 @@
-\r\e[K* Docunits of module test_nitunit::test_nitunit [    ] 0/4\r\e[K* Docunits of module test_nitunit::test_nitunit [   \e[1m\e[31mX\e[m\e[m] 1/4 test_nitunit$X$foo1 \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Docunits of module test_nitunit::test_nitunit [\e[1m\e[32m.\e[m\e[m  \e[1m\e[31mX\e[m\e[m] 2/4 test_nitunit::test_nitunit \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Docunits of module test_nitunit::test_nitunit [\e[1m\e[32m.\e[m\e[m\e[1m\e[31mX\e[m\e[m \e[1m\e[31mX\e[m\e[m] 3/4 test_nitunit$X \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Docunits of module test_nitunit::test_nitunit [\e[1m\e[32m.\e[m\e[m\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m] 4/4 test_nitunit$X$foo \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Docunits of module test_nitunit::test_nitunit [\e[1m\e[32m.\e[m\e[m\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m] 4/4
-\e[1m\e[32m[OK]\e[m\e[m test_nitunit::test_nitunit
-\e[1m\e[31m[KO]\e[m\e[m test_nitunit$X
-     \e[33mtest_nitunit.nit:21,7--22,0\e[m: Runtime error in .nitunit/test_nitunit-2.nit
-       #     \e[1;31massert false\e[0m
-             ^
+* Docunits of module test_nitunit::test_nitunit (4 tests)
+
+[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/test_nitunit-2.nit:5)
+       Runtime error: Assert failed (nitunit.out/test_nitunit-2.nit:5)
 
-\e[1m\e[31m[KO]\e[m\e[m test_nitunit$X$foo
-     \e[33mtest_nitunit.nit:24,8--25,0\e[m: Compilation error in .nitunit/test_nitunit-3.nit
-               #     \e[1;31massert undefined_identifier\e[0m
-                     ^
+[KO] test_nitunit$X$foo
+     test_nitunit.nit:24,8--25,0: Compilation error in nitunit.out/test_nitunit-3.nit
      Output
-       .nitunit/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
+       nitunit.out/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
+
+[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)
 
-\e[1m\e[31m[KO]\e[m\e[m test_nitunit$X$foo1
-     \e[33mtest_nitunit.nit:28,15\e[m: Syntax Error: unexpected operator '!'.
-               #     assert \e[1;31m!\e[0m@#$%^&*()
-                            ^
-\r\e[K* Test-suite of module test_test_nitunit::test_test_nitunit [   ] 0/3\r\e[K* Test-suite of module test_test_nitunit::test_test_nitunit [\e[1m\e[32m.\e[m\e[m  ] 1/3 test_test_nitunit$TestX$test_foo \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Test-suite of module test_test_nitunit::test_test_nitunit [\e[1m\e[32m.\e[m\e[m\e[1m\e[31mX\e[m\e[m ] 2/3 test_test_nitunit$TestX$test_foo1 \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Test-suite of module test_test_nitunit::test_test_nitunit [\e[1m\e[32m.\e[m\e[m\e[1m\e[31mX\e[m\e[m\e[1m\e[32m.\e[m\e[m] 3/3 test_test_nitunit$TestX$test_foo2 \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Test-suite of module test_test_nitunit::test_test_nitunit [\e[1m\e[32m.\e[m\e[m\e[1m\e[31mX\e[m\e[m\e[1m\e[32m.\e[m\e[m] 3/3
-\e[1m\e[32m[OK]\e[m\e[m test_test_nitunit$TestX$test_foo
-\e[1m\e[31m[KO]\e[m\e[m test_test_nitunit$TestX$test_foo1
-     \e[33mtest_test_nitunit.nit:36,2--40,4\e[m: Runtime Error in file .nitunit/gen_test_test_nitunit.nit
-               \e[1;31m# will fail\e[0m
-               ^
+[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)
 
-\e[1m\e[32m[OK]\e[m\e[m test_test_nitunit$TestX$test_foo2
-DocUnits:
-Entities: 27; Documented ones: 4; With nitunits: 4; Failures: 3
-
-TestSuites:
-Class suites: 1; Test Cases: 3; Failures: 1
+[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.&lt;module&gt;" name="&lt;module&gt;"><system-err></system-err><system-out>assert true
-</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="&lt;class&gt;"><error>Runtime error in .nitunit&#47;test_nitunit-2.nit</error><system-err>Runtime error: Assert failed (.nitunit&#47;test_nitunit-2.nit:5)
+</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="&lt;class&gt;"><error>Runtime error in nitunit.out&#47;test_nitunit-2.nit</error><system-err>Runtime error: Assert failed (nitunit.out&#47;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&#47;test_nitunit-3.nit</failure><system-err>.nitunit&#47;test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
+</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="test_nitunit::X::foo"><failure>Compilation error in nitunit.out&#47;test_nitunit-3.nit</failure><system-err>nitunit.out&#47;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><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="test_nitunit::X::foo1"><failure>Syntax Error: unexpected operator &#39;!&#39;.</failure><system-out>assert !@#$%^&amp;*()
-</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&#47;gen_test_test_nitunit.nit</error><system-err>Runtime error: Assert failed (test_test_nitunit.nit:39)
+</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&#47;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
index 95b3751..a1f4cb1 100644 (file)
@@ -1,14 +1,11 @@
-\r\e[K* Docunits of module test_nitunit2::test_nitunit2 [   ] 0/3\r\e[K* Docunits of module test_nitunit2::test_nitunit2 [\e[1m\e[32m.\e[m\e[m  ] 1/3 test_nitunit2::test_nitunit2$core::Sys$foo1 \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Docunits of module test_nitunit2::test_nitunit2 [\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m ] 2/3 test_nitunit2::test_nitunit2$core::Sys$bar2 \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Docunits of module test_nitunit2::test_nitunit2 [\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m] 3/3 test_nitunit2::test_nitunit2$core::Sys$foo3 \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Docunits of module test_nitunit2::test_nitunit2 [\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m] 3/3
-\e[1m\e[32m[OK]\e[m\e[m test_nitunit2::test_nitunit2$core::Sys$foo1
-\e[1m\e[32m[OK]\e[m\e[m test_nitunit2::test_nitunit2$core::Sys$bar2
-\e[1m\e[32m[OK]\e[m\e[m test_nitunit2::test_nitunit2$core::Sys$foo3
-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
+[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
index f161044..3e25819 100644 (file)
@@ -1,14 +1,11 @@
-\r\e[K* Docunits of module test_doc2::test_doc2 [   ] 0/3\r\e[K* Docunits of module test_doc2::test_doc2 [\e[1m\e[32m.\e[m\e[m  ] 1/3 test_doc2::test_doc2$core::Sys$foo1 \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Docunits of module test_doc2::test_doc2 [\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m ] 2/3 test_doc2::test_doc2$core::Sys$foo2 \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Docunits of module test_doc2::test_doc2 [\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m] 3/3 test_doc2::test_doc2$core::Sys$foo3 \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Docunits of module test_doc2::test_doc2 [\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m\e[1m\e[32m.\e[m\e[m] 3/3
-\e[1m\e[32m[OK]\e[m\e[m test_doc2::test_doc2$core::Sys$foo1
-\e[1m\e[32m[OK]\e[m\e[m test_doc2::test_doc2$core::Sys$foo2
-\e[1m\e[32m[OK]\e[m\e[m test_doc2::test_doc2$core::Sys$foo3
-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
+[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
index 13ff875..2b931af 100644 (file)
@@ -1,25 +1,20 @@
-\r\e[K* Docunits of group test_nitunit3> [  ] 0/2\r\e[K* Docunits of group test_nitunit3> [ \e[1m\e[31mX\e[m\e[m] 1/2 test_nitunit3> \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Docunits of group test_nitunit3> [\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m] 2/2 test_nitunit3> \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Docunits of group test_nitunit3> [\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m] 2/2
-\e[1m\e[31m[KO]\e[m\e[m test_nitunit3>
-     \e[33mtest_nitunit3/README.md:4,2--15,0\e[m: Runtime error in .nitunit/test_nitunit3-0.nit with argument 1
-       ~\e[1;31m~\e[0m
-        ^
+* Docunits of group test_nitunit3> (2 tests)
+
+[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/test_nitunit3-0.nit:7)
+       Runtime error: Assert failed (nitunit.out/test_nitunit3-0.nit:7)
 
-\e[1m\e[31m[KO]\e[m\e[m test_nitunit3>
-     \e[33mtest_nitunit3/README.md:7,3--5\e[m: Syntax Error: unexpected malformed character '\].
-       ~~\e[1;31m~
-;\e[0m
-         ^
-\r\e[K* Docunits of module test_nitunit3::test_nitunit3 [ ] 0/1\r\e[K* Docunits of module test_nitunit3::test_nitunit3 [\e[1m\e[32m.\e[m\e[m] 1/1 test_nitunit3::test_nitunit3 \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Docunits of module test_nitunit3::test_nitunit3 [\e[1m\e[32m.\e[m\e[m] 1/1
-\e[1m\e[32m[OK]\e[m\e[m test_nitunit3::test_nitunit3
-DocUnits:
-Entities: 2; Documented ones: 2; With nitunits: 3; Failures: 2
+[KO] test_nitunit3>
+     test_nitunit3/README.md:7,3--5: Syntax Error: unexpected malformed character '\].
+* Docunits of module test_nitunit3::test_nitunit3 (1 tests)
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit3&gt;"><testcase classname="nitunit.test_nitunit3&gt;" name="&lt;group&gt;"><error>Runtime error in .nitunit&#47;test_nitunit3-0.nit with argument 1</error><system-err>Runtime error: Assert failed (.nitunit&#47;test_nitunit3-0.nit:7)
+[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&gt;"><testcase classname="nitunit.test_nitunit3&gt;" name="&lt;group&gt;"><error>Runtime error in nitunit.out&#47;test_nitunit3-0.nit with argument 1</error><system-err>Runtime error: Assert failed (nitunit.out&#47;test_nitunit3-0.nit:7)
 </system-err><system-out>assert false
 assert true
 </system-out></testcase><testcase classname="nitunit.test_nitunit3&gt;" name="&lt;group&gt;+1"><failure>Syntax Error: unexpected malformed character &#39;\].</failure><system-out>;&#39;\][]
index bf0c504..4550bb5 100644 (file)
@@ -1,18 +1,15 @@
-\r\e[K* Docunits of file test_nitunit_md.md:1,0--15,0 [ ] 0/1\r\e[K* Docunits of file test_nitunit_md.md:1,0--15,0 [\e[1m\e[31mX\e[m\e[m] 1/1 nitunit.<file>.test_nitunit_md.md:1,0--15,0 \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Docunits of file test_nitunit_md.md:1,0--15,0 [\e[1m\e[31mX\e[m\e[m] 1/1
-\e[1m\e[31m[KO]\e[m\e[m nitunit.<file>.test_nitunit_md.md:1,0--15,0
-     \e[33mtest_nitunit_md.md:4,2--16,0\e[m: Runtime error in .nitunit/file-0.nit with argument 1
-       ~\e[1;31m~\e[0m
-        ^
-     Output
-       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.&lt;file&gt;" name="test_nitunit_md.md:1,0--15,0"><error>Runtime error in .nitunit&#47;file-0.nit with argument 1</error><system-err>Runtime error: Assert failed (.nitunit&#47;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.&lt;file&gt;" name="test_nitunit_md.md:1,0--15,0"><error>Runtime error in nitunit.out&#47;file-0.nit with argument 1</error><system-err>Runtime error: Assert failed (nitunit.out&#47;file-0.nit:8)
 </system-err><system-out>var a = 1
 assert 1 == 1
 assert false
index c3e9bf7..65fd9b3 100644 (file)
@@ -1,22 +1,15 @@
-\r\e[K* Docunits of module test_doc3::test_doc3 [   ] 0/3\r\e[K* Docunits of module test_doc3::test_doc3 [\e[1m\e[31mX\e[m\e[m  ] 1/3 test_doc3::test_doc3$core::Sys$foo1 \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Docunits of module test_doc3::test_doc3 [\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m ] 2/3 test_doc3::test_doc3$core::Sys$foo2 \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Docunits of module test_doc3::test_doc3 [\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m] 3/3 test_doc3::test_doc3$core::Sys$foo3 \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Docunits of module test_doc3::test_doc3 [\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m\e[1m\e[31mX\e[m\e[m] 3/3
-\e[1m\e[31m[KO]\e[m\e[m test_doc3::test_doc3$core::Sys$foo1
-     \e[33mtest_doc3.nit:17,9--15\e[m: Syntax Error: unexpected identifier 'garbage'.
-       #      *\e[1;31mgarbage\e[0m*
-               ^
-\e[1m\e[31m[KO]\e[m\e[m test_doc3::test_doc3$core::Sys$foo2
-     \e[33mtest_doc3.nit:23,4--10\e[m: Syntax Error: unexpected identifier 'garbage'.
-       # *\e[1;31mgarbage\e[0m*
-          ^
-\e[1m\e[31m[KO]\e[m\e[m test_doc3::test_doc3$core::Sys$foo3
-     \e[33mtest_doc3.nit:30,4--10\e[m: Syntax Error: unexpected identifier 'garbage'.
-       # *\e[1;31mgarbage\e[0m*
-          ^
-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
+[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 &#39;garbage&#39;.</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 &#39;garbage&#39;.</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 &#39;garbage&#39;.</failure><system-out>*garbage*
index 5806e40..7aa0887 100644 (file)
@@ -1,45 +1,40 @@
-\r\e[K* Test-suite of module test_nitunit4::test_nitunit4 [   ] 0/3\r\e[K* Test-suite of module test_nitunit4::test_nitunit4 [\e[1m\e[31mX\e[m\e[m  ] 1/3 test_nitunit4$TestTestSuite$test_foo \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Test-suite of module test_nitunit4::test_nitunit4 [\e[1m\e[31mX\e[m\e[m\e[1m\e[32m.\e[m\e[m ] 2/3 test_nitunit4$TestTestSuite$test_bar \e[1m\e[32m[OK]\e[m\e[m\r\e[K* Test-suite of module test_nitunit4::test_nitunit4 [\e[1m\e[31mX\e[m\e[m\e[1m\e[32m.\e[m\e[m\e[1m\e[31mX\e[m\e[m] 3/3 test_nitunit4$TestTestSuite$test_baz \e[1m\e[31m[KO]\e[m\e[m\r\e[K* Test-suite of module test_nitunit4::test_nitunit4 [\e[1m\e[31mX\e[m\e[m\e[1m\e[32m.\e[m\e[m\e[1m\e[31mX\e[m\e[m] 3/3
-\e[1m\e[31m[KO]\e[m\e[m test_nitunit4$TestTestSuite$test_foo
-     \e[33mtest_nitunit4/test_nitunit4.nit:22,2--26,4\e[m: Runtime Error in file .nitunit/gen_test_nitunit4.nit
-               \e[1;31mfun test_foo do\e[0m
-               ^
+* Test-suite of module test_nitunit4::test_nitunit4 (3 tests)
+
+[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)
 
-\e[1m\e[32m[OK]\e[m\e[m test_nitunit4$TestTestSuite$test_bar
-\e[1m\e[31m[KO]\e[m\e[m test_nitunit4$TestTestSuite$test_baz
-     \e[33mtest_nitunit4/test_nitunit4.nit:32,2--34,4\e[m: Difference with expected output: diff -u test_nitunit4/test_nitunit4.sav/test_baz.res .nitunit/gen_test_nitunit4_test_baz.out1
-               \e[1;31mfun test_baz do\e[0m
-               ^
+[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/gen_test_nitunit4_test_baz.out1
+       +++ got:nitunit.out/gen_test_nitunit4_test_baz.out1
        @@ -1 +1,3 @@
        -Bad result file
        +Before Test
        +Tested method
        +After Test
 
-DocUnits:
-No doc units found
-Entities: 12; Documented ones: 0; With nitunits: 0; Failures: 0
-
-TestSuites:
-Class suites: 1; Test Cases: 3; Failures: 2
-<testsuites><testsuite package="test_nitunit4&gt;"></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&#47;gen_test_nitunit4.nit</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&gt;"></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&#47;gen_test_nitunit4.nit</error><system-err>Before Test
 Tested method
 After Test
 Runtime error: Assert failed (test_nitunit4&#47;test_nitunit4_base.nit:31)
 </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&#47;test_nitunit4.sav&#47;test_baz.res .nitunit&#47;gen_test_nitunit4_test_baz.out1</error><system-err>Diff
+</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&#47;test_nitunit4.sav&#47;test_baz.res nitunit.out&#47;gen_test_nitunit4_test_baz.out1</error><system-err>Diff
 --- expected:test_nitunit4&#47;test_nitunit4.sav&#47;test_baz.res
-+++ got:.nitunit&#47;gen_test_nitunit4_test_baz.out1
++++ got:nitunit.out&#47;gen_test_nitunit4_test_baz.out1
 @@ -1 +1,3 @@
 -Bad result file
 +Before Test
diff --git a/tests/sav/test_readline.res b/tests/sav/test_readline.res
new file mode 100644 (file)
index 0000000..666312a
--- /dev/null
@@ -0,0 +1,9 @@
+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
diff --git a/tests/test_readline.inputs b/tests/test_readline.inputs
new file mode 100644 (file)
index 0000000..59f6fe7
--- /dev/null
@@ -0,0 +1,4 @@
+line 1
+line 2
+line 2bis
+line 3 \bine\b\b\b3\b
diff --git a/tests/test_readline.nit b/tests/test_readline.nit
new file mode 100644 (file)
index 0000000..c254b24
--- /dev/null
@@ -0,0 +1,21 @@
+# 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