Future improvement of nitunit require a saner codebase.
This PR does not bring a lot of features but propose instead some code improvements.
Summary:
* a new class UnitTest to factorize DocUnit and TestCase
* DocUnit are created, with its metadata, while discovered
* DocUnit do not enclose a XML node to fill but will generate one with to_xml.
* Easter egg: precise locations are included in the DocUnits. Each line of *collected* code can be located back to the original source code. This feature is not really used yet, except to locate the docunit itself.
The last commits are divided in order to make the reviewing more easy. I tought that is was better than a big stashed change.
Future PR will target usability since unit-tests are now reified in a common way on both sides (docunits and testsuites).
Pull-Request: #2114
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
bin/asteronits: $(shell ${NITLS} -M src/asteronits.nit linux) ${NITC} pre-build
${NITC} src/asteronits.nit -m linux -o $@
-bin/texture_atlas_parser: src/texture_atlas_parser.nit
- ${NITC} src/texture_atlas_parser.nit -o $@
+bin/texture_atlas_parser: ../../lib/gamnit/texture_atlas_parser.nit
+ ${NITC} ../../lib/gamnit/texture_atlas_parser.nit -o $@
src/controls.nit: art/controls.svg
make -C ../inkscape_tools/
../inkscape_tools/bin/svg_to_png_and_nit art/controls.svg -a assets/ -s src/ -x 2.0 -g
-src/spritesheet_city.nit: bin/texture_atlas_parser
+src/spritesheet.nit: bin/texture_atlas_parser
bin/texture_atlas_parser art/sheet.xml --dir src/ -n spritesheet
-pre-build: src/controls.nit src/spritesheet_city.nit
+pre-build: src/controls.nit src/spritesheet.nit
check: bin/asteronits
NIT_TESTING=true bin/asteronits
--- /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.
+
+# VR mode for Android with Google Cardboard
+#
+# This version is not playable and very laggy as it is not modified
+# or optimized in any way for VR.
+# This module is made available as a minimal example of a VR game.
+module tinks_vr
+
+import gamnit::vr
# Coordinates on the texture per vertex
var texture_coords = new Array[Float] is lazy, writable
+ # `GLDrawMode` used to display this mesh, defaults to `gl_TRIANGLES`
+ fun draw_mode: GLDrawMode do return gl_TRIANGLES
+
# Create an UV sphere of `radius` with `n_meridians` and `n_parallels`
init uv_sphere(radius: Float, n_meridians, n_parallels: Int)
do
# Execute draw
if mesh.indices.is_empty then
- glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
+ glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
else
- glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+ glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
end
end
end
var xd = sample_used_texture.offset_right - xa
var ya = sample_used_texture.offset_top
var yd = sample_used_texture.offset_bottom - ya
+ xd *= 0.999
+ yd *= 0.999
var tex_coords = new Array[Float].with_capacity(mesh.texture_coords.length)
for i in [0..mesh.texture_coords.length/2[ do
tex_coords[i*2] = xa + xd * mesh.texture_coords[i*2]
- tex_coords[i*2+1] = ya + yd * mesh.texture_coords[i*2+1]
+ tex_coords[i*2+1] = 1.0 - (ya + yd * mesh.texture_coords[i*2+1])
end
program.tex_coord.array(tex_coords, 2)
program.camera.uniform(app.world_camera.position.x, app.world_camera.position.y, app.world_camera.position.z)
if mesh.indices.is_empty then
- glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
+ glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
else
- glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+ glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
end
end
end
program.normal.array(mesh.normals, 3)
if mesh.indices.is_empty then
- glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
+ glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
else
- glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+ glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
end
end
end
var d = [1.0, 0.0]
var texture_coords = new Array[Float]
- for v in [c, d, a, a, d, b] do for i in 6.times do texture_coords.add_all v
+ var face = [a, c, d, a, d, b]
+ for i in 6.times do for v in face do texture_coords.add_all v
return texture_coords
end
if leaves.is_empty then
# Nothing was loaded, use a cube with the default material
- var leaf = new LeafModel(new Cube, new SmoothMaterial.default)
+ var leaf = placeholder_model
leaves.add leaf
end
end
# Load textures need for these materials
for name in texture_names do
if not asset_textures_by_name.keys.has(name) then
- var tex = new GamnitAssetTexture(name)
+ var tex = new TextureAsset(name)
asset_textures_by_name[name] = tex
end
end
redef class Sys
# Textures loaded from .mtl files for models
- var asset_textures_by_name = new Map[String, GamnitAssetTexture]
+ var asset_textures_by_name = new Map[String, TextureAsset]
# All instantiated asset models
var models = new Set[ModelAsset]
+
+ # Blue cube of 1 unit on each side, acting as placeholder for models failing to load
+ #
+ # This model can be freely used by any `Actor` as placeholder or for debugging.
+ var placeholder_model = new LeafModel(new Cube, new SmoothMaterial.default) is lazy
end
# Only affects the desktop implementations.
var show_cursor: Bool = true is writable
+ # Number of bits used for the red value in the color buffer
+ fun red_bits: Int do return 8
+
+ # Number of bits used for the green value in the color buffer
+ fun green_bits: Int do return 8
+
+ # Number of bits used for the blue value in the color buffer
+ fun blue_bits: Int do return 8
+
# Prepare this display
#
# The implementation varies per platform.
setup_egl_display native_display
# We need 8 bits per color for selection by color
- select_egl_config(8, 8, 8, 0, 8, 0, 0)
+ select_egl_config(red_bits, green_bits, blue_bits, 0, 8, 0, 0)
var format = egl_config.attribs(egl_display).native_visual_id
native_window.set_buffers_geometry(0, 0, format)
redef fun close do close_egl
end
-redef class GamnitAssetTexture
+redef class TextureAsset
redef fun load_from_platform
do
setup_egl_display x11_display
if debug_gamnit then print "Setting up EGL context"
- select_egl_config(8, 8, 8, 8, 8, 0, 0)
+ select_egl_config(red_bits, green_bits, blue_bits, 8, 8, 0, 0)
setup_egl_context window_handle
end
end
end
-redef class GamnitAssetTexture
+redef class TextureAsset
redef fun load_from_platform
do
end
# Select an EGL config
- protected fun select_egl_config(blue, green, red, alpha, depth, stencil, sample: Int)
+ protected fun select_egl_config(red, green, blue, alpha, depth, stencil, sample: Int)
do
var config_chooser = new EGLConfigChooser
config_chooser.renderable_type_egl
config_chooser.surface_type_egl
- config_chooser.blue_size = blue
- config_chooser.green_size = green
config_chooser.red_size = red
+ config_chooser.green_size = green
+ config_chooser.blue_size = blue
if alpha > 0 then config_chooser.alpha_size = alpha
if depth > 0 then config_chooser.depth_size = depth
if stencil > 0 then config_chooser.stencil_size = stencil
# Current frame-rate
#
- # Updated each 5 seconds.
- var current_fps = 0.0
+ # Updated each 5 seconds, initialized at the value of `maximum_fps`.
+ var current_fps: Float = maximum_fps is lazy
redef fun frame_full
do
private var frame_count = 0
# Deadline used to compute `current_fps`
- private var frame_count_deadline = 0
+ private var frame_count_deadline = 5
# Check and sleep to maintain a frame-rate bellow `maximum_fps`
#
end
end
+ # Diagnose possible problems with the shaders of the program
+ #
+ # Lists to the console inactive uniforms and attributes.
+ # These may not be problematic but they can help to debug the program.
+ fun diagnose
+ do
+ if gl_program == null then compile_and_link
+
+ print "# Diagnose {class_name}"
+ for k,v in uniforms do
+ if not v.is_active then print "* Uniform {v.name} is inactive"
+ end
+ for k,v in attributes do
+ if not v.is_active then print "* Attribute {v.name} is inactive"
+ end
+ end
+
# Attributes of this program organized by name
#
# Active attributes are gathered at `compile_and_link`.
# Insert the first attribute, to load the root texture
var png_file = "images" / xml_file.basename("xml") + "png"
attributes.add """
- var root_texture = new Texture("{{{png_file}}}")"""
+ var root_texture = new TextureAsset("{{{png_file}}}")"""
# Read XML file
var content = xml_file.to_path.read_all
abstract class Texture
# Prepare a texture located at `path` within the `assets` folder
- new (path: Text) do return new GamnitAssetTexture(path.to_s)
+ new (path: Text) do return new TextureAsset(path.to_s)
# Root texture of which `self` is derived
fun root: GamnitRootTexture is abstract
# OpenGL handle to this texture
fun gl_texture: Int do return root.gl_texture
- # Prepare a subtexture from this texture
+ # Prepare a subtexture from this texture, from the given pixel offsets
fun subtexture(left, top, width, height: Numeric): GamnitSubtexture
do
# Setup the subtexture
end
# Texture loaded from the assets folder
-class GamnitAssetTexture
+class TextureAsset
super GamnitRootTexture
# Path to this texture within the `assets` folder
end
# Split a polygon into triangles
-# Useful for converting a concave polygon into multiple convex ones
-fun triangulate(pts: Array[Point[Float]], results: Array[ConvexPolygon]) do
- var poly = new Polygon(pts)
- pts = poly.points
- recursive_triangulate(pts, results)
+#
+# Useful for converting a concave polygon into multiple convex ones.
+#
+# See: the alternative `triangulate_recursive` uses arrays in-place.
+fun triangulate(points: Array[Point[Float]]): Array[ConvexPolygon]
+do
+ var results = new Array[ConvexPolygon]
+ triangulate_recursive(points.clone, results)
+ return results
end
-private fun recursive_triangulate(pts: Array[Point[Float]], results: Array[ConvexPolygon]) do
- if pts.length == 3 then
- results.add(new ConvexPolygon(pts))
+# Split a polygon into triangles using arrays in-place
+#
+# Consumes the content of `points` and add the triangles to `results`.
+#
+# See: the alternative `triangulate` which does not modify `points` and returns a new array.
+fun triangulate_recursive(points: Array[Point[Float]], results: Array[ConvexPolygon]) do
+ if points.length == 3 then
+ results.add(new ConvexPolygon(points))
return
end
- var prev = pts[pts.length - 1]
- var curr = pts[0]
- var next = pts[1]
- for i in [1..pts.length[ do
+ var prev = points[points.length - 1]
+ var curr = points[0]
+ var next = points[1]
+ for i in [1..points.length[ do
if turn_left(prev, curr, next) then
- prev = pts[i-1]
+ prev = points[i-1]
curr = next
- if i+1 == pts.length then next = pts[pts.length - 1] else next = pts[i+1]
+ if i+1 == points.length then next = points[points.length - 1] else next = points[i+1]
continue
end
var contains = false
var triangle = new ConvexPolygon(new Array[Point[Float]].with_items(prev, curr, next))
- for p in pts do
+ for p in points do
if p != prev and p != curr and p != next then
if triangle.contain(p) then
contains = true
end
if not contains then
results.add(triangle)
- pts.remove(curr)
- recursive_triangulate(pts, results)
+ points.remove(curr)
+ triangulate_recursive(points, results)
break
end
- prev = pts[i-1]
+ prev = points[i-1]
curr = next
- if i+1 == pts.length then next = pts[pts.length - 1] else next = pts[i+1]
+ if i+1 == points.length then next = points[points.length - 1] else next = points[i+1]
end
end
&& strip c_src/nitc bin/nit* \
&& ccache -C \
&& rm -rf .git
+
+ENV NIT_DIR /root/nit
+ENV PATH $NIT_DIR/bin:$PATH
+WORKDIR $NIT_DIR
--- /dev/null
+# Supported tags and respective Dockerfile links
+
+* [latest](https://github.com/nitlang/nit/blob/master/misc/docker/Dockerfile)
+* [full](https://github.com/nitlang/nit/blob/master/misc/docker/full/Dockerfile)
+
+## What is Nit?
+
+Nit is an expressive language with a script-like syntax, a friendly type-system and aims at elegance, simplicity and intuitiveness.
+
+Nit has a simple straightforward style and can usually be picked up quickly, particularly by anyone who has programmed before.
+While object-oriented, it allows procedural styles.
+
+More information on
+
+* Website <http://www.nitlanguage.org>
+* Github <https://github.com/nitlang/nit>
+* Chatroom <https://gitter.im/nitlang/nit>
+
+## How to use this image
+
+You can use these images to build then run your programs.
+
+### Experimenting with Nit
+
+~~~
+host$ docker run -ti nitlang/nit
+root@ce9b671dd9fc:/root/nit# nitc examples/hello_world.nit
+root@ce9b671dd9fc:/root/nit# ./hello_world
+hello world
+~~~
+
+### Build and Run Programs
+
+In your Dockerfile, write something like:
+
+~~~Dockerfile
+FROM nitlang/nit
+
+# Create a workdir
+RUN mkdir -p /root/work
+WORKDIR /root/work
+
+# Copy the source code in /root/work/
+COPY . /root/work/
+
+# Compile
+RUN nitc src/hello.nit --dir . \
+ # Clear disk space
+ && ccache -C
+# You can also use a Makefile or any build system you want.
+
+# Run
+CMD ["./hello"]
+~~~
+
+Then, build and execute
+
+~~~
+host$ docker build -t nithello .
+host$ docker run --rm nithello
+hello!
+~~~
+
+See the full example at <https://github.com/nitlang/nit/blob/master/misc/docker/hello/Dockerfile>
--- /dev/null
+FROM nitlang/nit
+
+# Create a workdir
+RUN mkdir -p /root/work
+WORKDIR /root/work
+
+# Copy the source code in /root/work/
+COPY . /root/work/
+
+# Compile
+RUN nitc src/hello.nit --dir . \
+ # Clear disk space
+ && ccache -C
+# You can also use a Makefile or what you want
+
+# Say what to run
+CMD ["./hello"]
--- /dev/null
+print "hello"
var host = toolcontext.opt_host.value or else "localhost"
var port = toolcontext.opt_port.value
- var srv = new NitServer(host, port.to_i)
- srv.routes.add new Route("/random", new RandomAction(srv, model))
- srv.routes.add new Route("/doc/:namespace", new DocAction(srv, model, modelbuilder))
- srv.routes.add new Route("/code/:namespace", new CodeAction(srv, model, modelbuilder))
- srv.routes.add new Route("/search/:namespace", new SearchAction(srv, model))
- srv.routes.add new Route("/uml/:namespace", new UMLDiagramAction(srv, model, mainmodule))
- srv.routes.add new Route("/", new TreeAction(srv, model))
+ 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))
- srv.listen
+ app.listen(host, port.to_i)
end
end
return ""
else
for arg in args do
- var format_error = "Syntax Eror: `{name}` expects its arguments to be of type Int or a call to `git_revision`."
-
var value
value = arg.as_int
if value != null then
# Get Git short revision
var proc = new ProcessReader("git", "rev-parse", "--short", "HEAD")
proc.wait
- assert proc.status == 0
+ if proc.status != 0 then
+ # Fallback if this is not a git repository or git bins are missing
+ version_fields.add "0"
+ modelbuilder.warning(self, "git_revision", "Warning: `git_revision` used outside of a git repository or git binaries not available")
+ continue
+ end
+
var lines = proc.read_all
var revision = lines.split("\n").first
continue
end
+ var format_error = "Syntax Error: `{name}` expects its arguments to be of type Int or a call to `git_revision`."
modelbuilder.error(self, format_error)
return ""
end
class TreeAction
super ModelAction
- redef fun answer(request, url) do
- var model = init_model_view(request)
+ redef fun get(req, res) do
+ var model = init_model_view(req)
var view = new HtmlHomePage(model.to_tree)
- return render_view(view)
+ res.send_view(view)
end
end
super ModelAction
# TODO handle more than full namespaces.
- redef fun answer(request, url) do
- var namespace = request.param("namespace")
- var model = init_model_view(request)
+ 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
- return render_error(404, "No mentity found")
+ res.error(404)
+ return
end
- if request.is_json_asked then
- return render_json(mentity.to_json)
+ if req.is_json_asked then
+ res.json(mentity.to_json)
+ return
end
var view = new HtmlResultPage(namespace or else "null", [mentity])
- return render_view(view)
+ res.send_view(view)
end
end
# Modelbuilder used to access sources.
var modelbuilder: ModelBuilder
- redef fun answer(request, url) do
- var namespace = request.param("namespace")
- var model = init_model_view(request)
+ 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
- return render_error(404, "No mentity found")
+ res.error(404)
+ return
end
var view = new HtmlSourcePage(modelbuilder, mentity)
- return render_view(view)
+ res.send_view(view)
end
end
# Modelbuilder used to access sources.
var modelbuilder: ModelBuilder
- # TODO handle more than full namespaces.
- redef fun answer(request, url) do
- var namespace = request.param("namespace")
- var model = init_model_view(request)
+ 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
- return render_error(404, "No mentity found")
+ res.error(404)
+ return
+ end
+ if req.is_json_asked then
+ res.json(mentity.to_json)
+ return
end
+
var view = new HtmlDocPage(modelbuilder, mentity)
- return render_view(view)
+ res.send_view(view)
end
end
# Mainmodule used for hierarchy flattening.
var mainmodule: MModule
- redef fun answer(request, url) do
- var namespace = request.param("namespace")
- var model = init_model_view(request)
+ 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
- return render_error(404, "No mentity found")
+ res.error(404)
+ return
end
var dot
var uml = new UMLModel(model, mentity)
dot = uml.generate_package_uml.write_to_string
else
- return render_error(404, "No diagram matching this namespace.")
+ res.error(404)
+ return
end
var view = new HtmlDotPage(dot, mentity.as(not null).html_name)
- return render_view(view)
+ res.send_view(view)
end
end
class RandomAction
super ModelAction
- # TODO handle more than full namespaces.
- redef fun answer(request, url) do
- var n = request.int_arg("n") or else 10
- var k = request.string_arg("k") or else "modules"
- var model = init_model_view(request)
+ 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
end
mentities.shuffle
mentities = mentities.sub(0, n)
- if request.is_json_asked then
+ if req.is_json_asked then
var json = new JsonArray
for mentity in mentities do
json.add mentity.to_json
end
- return render_json(json)
+ res.json(json)
+ return
end
var view = new HtmlResultPage("random", mentities)
- return render_view(view)
+ res.send_view(view)
end
end
import model::model_views
import model::model_json
-import nitcorn
-
-# Nitcorn server runned by `nitweb`.
-#
-# Usage:
-#
-# ~~~nitish
-# var srv = new NitServer("localhost", 3000)
-# srv.routes.add new Route("/", new MyAction)
-# src.listen
-# ~~~
-class NitServer
-
- # Host to bind.
- var host: String
-
- # Port to use.
- var port: Int
-
- # Routes knwon by the server.
- var routes = new Array[Route]
-
- # Start listen on `host:port`.
- fun listen do
- var iface = "{host}:{port}"
- print "Launching server on http://{iface}/"
-
- var vh = new VirtualHost(iface)
- for route in routes do vh.routes.add route
-
- var fac = new HttpFactory.and_libevent
- fac.config.virtual_hosts.add vh
- fac.run
- end
-end
-
-# Specific nitcorn Action for nitweb.
-class NitAction
- super Action
-
- # Link to the NitServer that runs this action.
- var srv: NitServer
-
- # Build a custom http response for errors.
- fun render_error(code: Int, message: String): HttpResponse do
- var response = new HttpResponse(code)
- var tpl = new Template
- tpl.add "<h1>Error {code}</h1>"
- tpl.add "<pre><code>{message.html_escape}</code></pre>"
- response.body = tpl.write_to_string
- return response
- end
-
- # Render a view as a HttpResponse 200.
- fun render_view(view: NitView): HttpResponse do
- var response = new HttpResponse(200)
- response.body = view.render(srv).write_to_string
- return response
- end
-
- # Return a HttpResponse containing `json`.
- fun render_json(json: Jsonable): HttpResponse do
- var response = new HttpResponse(200)
- response.body = json.to_json
- return response
- end
-end
+import popcorn
# Specific nitcorn Action that uses a Model
class ModelAction
- super NitAction
+ super Handler
# Model to use.
var model: Model
# A NitView is rendered by an action.
interface NitView
# Renders this view and returns something that can be written to a HTTP response.
- fun render(srv: NitServer): Writable is abstract
+ fun render: Writable is abstract
+end
+
+redef class HttpResponse
+ # Render a NitView as response.
+ fun send_view(view: NitView, status: nullable Int) do send(view.render, status)
end
redef class HttpRequest
# Loaded model to display.
var tree: MEntityTree
- redef fun render(srv) do
+ redef fun render do
var tpl = new Template
tpl.add new Header(1, "Loaded model")
tpl.add tree.html_list
# Result set
var results: Array[MEntity]
- redef fun render(srv) do
+ redef fun render do
var tpl = new Template
tpl.add new Header(1, "Results for {query}")
if results.is_empty then
# HiglightVisitor used to hilight the source code
var hl = new HighlightVisitor
- redef fun render(srv) do
+ redef fun render do
var tpl = new Template
tpl.add new Header(1, "Source Code")
tpl.add render_source
class HtmlDocPage
super HtmlSourcePage
- redef fun render(srv) do
+ redef fun render do
var tpl = new Template
tpl.add new Header(1, mentity.html_name)
tpl.add "<p>"
# Page title.
var title: String
- redef fun render(srv) do
+ redef fun render do
var tpl = new Template
tpl.add new Header(1, title)
tpl.add render_dot