super
var gamepad = new VirtualGamepad
- gamepad.add_dpad
- gamepad.controls.first.as(DPad).show_down = false
+
+ var dpad = gamepad.add_dpad
+ if dpad != null then dpad.show_down = false
+
gamepad.add_button("space", gamepad_spritesheet.fire)
gamepad.visible = true
self.gamepad = gamepad
# Write the model to file next to the Nit module
var model_path = file_name.strip_extension + ".jwrapper.bin"
var model_stream = model_path.to_path.open_wo
- var serializer = new BinarySerializer(model_stream)
+ var serializer = new MsgPackSerializer(model_stream)
serializer.serialize model
model_stream.close
end
import more_collections
import opts
import poset
-import binary::serialization
+import msgpack
import jtype_converter
end
# Short name of the class, mangled to remove `$` (e.g. `Set`)
- var id: String is lazy do return identifier.last.replace("$", "")
+ var id: String is lazy, noserialize do return identifier.last.replace("$", "")
# Full name of this class as used in java code (e.g. `java.lang.Set`)
- var java_full_name: String is lazy do return identifier.join(".").replace("$", ".")
+ var java_full_name: String is lazy, noserialize do return identifier.join(".").replace("$", ".")
# Full name of this class as used by jni (e.g. `android.graphics.BitmapFactory$Options`)
- var jni_full_name: String is lazy do return identifier.join(".")
+ var jni_full_name: String is lazy, noserialize do return identifier.join(".")
# Name of this class for the extern declaration in Nit (e.g. `java.lang.Set[]`)
- var extern_equivalent: String is lazy do return jni_full_name + "[]" * array_dimension
+ var extern_equivalent: String is lazy, noserialize do return jni_full_name + "[]" * array_dimension
# Full name of this class with arrays and generic values (e.g. `java.lang.Set<E>[]`)
redef fun to_s do
end
var file = model_path.to_path.open_ro
- var d = new BinaryDeserializer(file)
+ var d = new MsgPackDeserializer(file)
var model = d.deserialize
file.close
if d.errors.not_empty then
- print_error "Error: failed to deserialize model file '{model_path}' with: {d.errors.join(", ")}"
+ print_error "Error: failed to deserialize model file '{model_path}' with:\n* {d.errors.join("\n* ")}"
continue
end
end
# Does this model have access to the `java.lang.Object`?
- var knows_the_object_class: Bool = all_classes.keys.has("java.lang.Object") is lazy
+ var knows_the_object_class: Bool = all_classes.keys.has("java.lang.Object") is lazy, noserialize
# Add a class in `classes`
fun add_class(jclass: JavaClass)
var path: String
# Name of the module
- var name: String is lazy do return path.basename(".nit")
+ var name: String is lazy, noserialize do return path.basename(".nit")
redef fun to_s do return self.name
redef fun ==(other) do return other isa NitModuleRef and self.path == other.path
var opt_extern_class_prefix = new OptionString("Prefix to extern classes (By default uses the full namespace)", "-p")
# Prefix used to name extern classes, if `null` use the full namespace
- var extern_class_prefix: nullable String is lazy do return opt_extern_class_prefix.value
+ var extern_class_prefix: nullable String is lazy, noserialize do return opt_extern_class_prefix.value
# Libraries to search for existing wrappers
var opt_libs = new OptionArray("Paths to libraries with wrappers of Java classes ('auto' to use the full Nit lib)", "-i")
# limitations under the License.
# Test module for `achievements.nit`
-module test_achievements is test_suite
+module test_achievements is test
import test_helper
import achievements
class TestGame
super NitrpgTestHelper
+ test
- fun test_add_achievement do
+ fun test_add_achievement is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var a1 = new Achievement(game, "test_id1", "test_name", "test_desc", 15)
assert game.load_achievements.length == 2
end
- fun test_load_achievement do
+ fun test_load_achievement is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var a1 = new Achievement(game, "test_id1", "test_name", "test_desc", 15)
assert game.load_achievement(a2.id) == null
end
- fun test_load_achievements do
+ fun test_load_achievements is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var a1 = new Achievement(game, "test_id1", "test_name", "test_desc", 15)
class TestPlayer
super NitrpgTestHelper
+ test
- fun test_add_achievement do
+ fun test_add_achievement is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var player1 = new Player(game, "Morriar")
assert player1.load_achievements.length == 2
end
- fun test_load_achievement do
+ fun test_load_achievement is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var player1 = new Player(game, "Morriar")
assert player2.load_achievement(a1.id) == null
end
- fun test_load_achievements do
+ fun test_load_achievements is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var player1 = new Player(game, "Morriar")
class TestAchievement
super NitrpgTestHelper
+ test
- fun test_init do
+ fun test_init is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var a = new Achievement(game, "test_id", "test_name", "test_desc", 15)
assert a.reward == 15
end
- fun test_init_from_json do
+ fun test_init_from_json is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var json = """{
# limitations under the License.
# Test module for `events.nit`
-module test_events is test_suite
+module test_events is test
import test_helper
import events
class TestGame
super NitrpgTestHelper
+ test
- fun test_add_event do
+ fun test_add_event is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var event1 = new GameEvent(game, "test_kind", new JsonObject)
assert game.load_events.length == 2
end
- fun test_load_event do
+ fun test_load_event is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var event1 = new GameEvent(game, "test_kind", new JsonObject)
assert game.load_event(event2.internal_id) == null
end
- fun test_load_events do
+ fun test_load_events is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var event1 = new GameEvent(game, "test_kind", new JsonObject)
class TestPlayer
super NitrpgTestHelper
+ test
- fun test_add_event do
+ fun test_add_event is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var player1 = new Player(game, "Morriar")
assert player2.load_events.length == 0
end
- fun test_load_event do
+ fun test_load_event is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var player1 = new Player(game, "Morriar")
assert player2.load_event(event1.internal_id) == null
end
- fun test_load_events do
+ fun test_load_events is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var player1 = new Player(game, "Morriar")
class TestGameEvent
super NitrpgTestHelper
+ test
- fun test_init do
+ fun test_init is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var event = new GameEvent(game, "test_kind", new JsonObject)
assert event.to_json_object["kind"] == "test_kind"
end
- fun test_init_from_json do
+ fun test_init_from_json is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var json = """{
# limitations under the License.
# Test module for `game.nit`.
-module test_game is test_suite
+module test_game is test
import test_helper
class TestGame
super NitrpgTestHelper
+ test
- fun test_add_player do
+ fun test_add_player is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var users = ["Morriar", "xymus"]
end
end
- fun test_load_player do
+ fun test_load_player is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var ogame = load_game("Morriar/nit", db)
assert ogame.load_player("Morriar") == null
end
- fun test_load_players do
+ fun test_load_players is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var ogame = load_game("Morriar/nit", db)
class TestPlayer
super NitrpgTestHelper
+ test
- fun test_init do
+ fun test_init is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var player = new Player(game, "Morriar")
assert player.nitcoins == 0
end
- fun test_init_from_json do
+ fun test_init_from_json is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var json = """{"name": "Morriar", "nitcoins": 10}""".parse_json
assert player.nitcoins == 10
end
- fun test_save do
+ fun test_save is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var json = """{"name": "Morriar", "nitcoins": 10}""".parse_json.as(JsonObject)
assert game.db.collection("players").find(json) != null
end
- fun test_game_add_player do
+ fun test_game_add_player is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
game.add_player(game.api.load_user("Morriar").as(not null))
assert game.db.collection("players").find(json) != null
end
- fun test_game_load_player do
+ fun test_game_load_player is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var json = """{"name": "Morriar", "nitcoins": 10}""".parse_json.as(JsonObject)
class TestUser
super NitrpgTestHelper
+ test
- fun test_player do
+ fun test_player is test do
var db = gen_test_db
var api = new GithubAPI(get_github_oauth)
var game = load_game("privat/nit", db)
# Test tools for NitRPG.
module test_helper
-import test_suite
import game
import github::cache
# Used to factorize test treatments.
abstract class NitrpgTestHelper
- super TestSuite
# Github API client
var api: GithubAPI do
db.drop
end
- redef fun after_test do drop_test_db
+ # Drop the databse after each test
+ fun after_test is after do drop_test_db
end
# limitations under the License.
# Test module for `listener.nit`
-module test_listener is test_suite
+module test_listener is test
import test_helper
import reactors
private class DummyListener
super NitrpgTestHelper
+ test
var reactors = new Array[GameReactor]
class TestListener
super NitrpgTestHelper
+ test
var generator = new EventsGenerator(api)
var repo: Repo is lazy do return load_repo("Morriar/nit")
- fun test_game_issue_stats do
+ fun test_game_issue_stats is test do
var db = gen_test_db
var l = new DummyListener
l.add_reactor(new StatisticsReactor)
assert game.stats.overall["issues_open"] == 1
end
- fun test_player_issue_stats do
+ fun test_player_issue_stats is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var l = new DummyListener
assert player.stats.overall["issues_open"] == 1
end
- fun test_game_pr_stats do
+ fun test_game_pr_stats is test do
var db = gen_test_db
var l = new DummyListener
l.add_reactor(new StatisticsReactor)
assert game.stats.overall["commits"] == 2
end
- fun test_game_issue_comment_stats do
+ fun test_game_issue_comment_stats is test do
var db = gen_test_db
var l = new DummyListener
l.add_reactor(new StatisticsReactor)
assert game.stats.overall["reviews"] == 1
end
- fun test_player_pull_reactor do
+ fun test_player_pull_reactor is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var l = new DummyListener
assert player.stats.overall["nitcoins"] == 12
end
- fun test_player_review_reactor do
+ fun test_player_review_reactor is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var l = new DummyListener
assert player.stats.overall["nitcoins"] == 4
end
- fun test_X_issues_achievements do
+ fun test_X_issues_achievements is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var l = new DummyListener
assert player.stats.overall["nitcoins"] == 1110
end
- fun test_X_pulls_achievements do
+ fun test_X_pulls_achievements is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var l = new DummyListener
assert player.stats.overall["nitcoins"] == 1110
end
- fun test_X_commits_achievements do
+ fun test_X_commits_achievements is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var l = new DummyListener
assert player.stats.overall["nitcoins"] == 11110
end
- fun test_X_comments_achievements do
+ fun test_X_comments_achievements is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var l = new DummyListener
assert player.stats.overall["nitcoins"] == 1110
end
- fun test_issues_achievements do
+ fun test_issues_achievements is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var l = new DummyListener
assert player.stats.overall["nitcoins"] == 20
end
- fun test_comments_reactor do
+ fun test_comments_reactor is test do
var db = gen_test_db
var game = load_game("Morriar/nit", db)
var l = new DummyListener
# limitations under the License.
# Test module for `stats.nit`
-module test_statistics is test_suite
+module test_statistics is test
import test_helper
import statistics
class TestGame
super NitrpgTestHelper
+ test
- fun test_game_stats do
+ fun test_game_stats is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var stats = game.stats
class TestPlayer
super NitrpgTestHelper
+ test
- fun test_player_stats do
+ fun test_player_stats is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var player = new Player(game, "Morriar")
class TestGameStats
super NitrpgTestHelper
+ test
- fun test_init_from_json do
+ fun test_init_from_json is test do
var db = gen_test_db
var game = load_game("privat/nit", db)
var owner = new Player(game, "Morriar")
#
#
# A module for testing the classes in the bitmap module
-module test_bitmap is test_suite
+module test_bitmap is test
import bitmap
-import test_suite
class TestBitmap
- super TestSuite
+ test
- fun test_grayscale do
+ fun test_grayscale is test do
var bitmap = new Bitmap.with_size(400, 300)
for y in [0..300] do
for x in [0..200] do bitmap.set_pixel(x, y, 0x0077AAAA)
return new FlatString.full(ns, elen, 0, elen)
end
- # Interprets `self` as a big-endian positive integer.
+ # Interprets `self` as a big-endian integer (unsigned by default)
#
# ~~~
# var b = "0102".hexdigest_to_bytes
# assert b.to_i == 258
+ #
+ # assert "01".hexdigest_to_bytes.to_i == 1
+ # assert "FF".hexdigest_to_bytes.to_i == 255
+ # assert "0000".hexdigest_to_bytes.to_i == 0
# ~~~
#
- # Nul bytes on the left are trimmed.
- # 0 is returned for an empty Bytes object.
+ # If `self.is_empty`, 0 is returned.
#
# ~~~
- # assert "01".hexdigest_to_bytes.to_i == 1
- # assert "0001".hexdigest_to_bytes.to_i == 1
- #
- # assert "0000".hexdigest_to_bytes.to_i == 0
- # assert "00".hexdigest_to_bytes.to_i == 0
# assert "".hexdigest_to_bytes.to_i == 0
# ~~~
#
+ # If `signed == true`, the bytes are read as a signed integer.
+ # As usual, the sign bit is the left most bit, no matter the
+ # `length` of `self`.
+ #
+ # ~~~
+ # assert "01".hexdigest_to_bytes.to_i(true) == 1
+ # assert "FF".hexdigest_to_bytes.to_i(true) == -1
+ # assert "00FF".hexdigest_to_bytes.to_i(true) == 255
+ # assert "E0".hexdigest_to_bytes.to_i(true) == -32
+ # assert "FE00".hexdigest_to_bytes.to_i(true) == -512
+ # assert "FEFEFE".hexdigest_to_bytes.to_i(true) == -65794
+ # ~~~
+ #
# `Int::to_bytes` is a loosely reverse method.
#
# ~~~
# assert b.to_i.to_bytes == b
# assert (b.to_i + 1).to_bytes.hexdigest == "0103"
# assert "0001".hexdigest_to_bytes.to_i.to_bytes.hexdigest == "01"
+ #
+ # assert (-32).to_bytes.to_i(true) == -32
# ~~~
#
# Warning: `Int` might overflow for bytes with more than 60 bits.
- fun to_i: Int do
+ fun to_i(signed: nullable Bool): Int do
var res = 0
var i = 0
while i < length do
res += self[i].to_i
i += 1
end
+
+ # Two's complement is `signed`
+ if signed == true and not_empty and first > 0x80u8 then
+ var ff = 0
+ for j in [0..length[ do
+ ff *= 0x100
+ ff += 0xFF
+ end
+
+ res = -((res ^ ff) + 1)
+ end
+
return res
end
end
redef class Int
- # A big-endian representation of self.
+ # A signed big-endian representation of `self`
#
# ~~~
# assert 1.to_bytes.hexdigest == "01"
# assert 65536.to_bytes.hexdigest == "010000"
# ~~~
#
+ # Negative values are converted to their two's complement.
+ # Be careful as the result can be ambiguous.
+ #
+ # ~~~
+ # assert (-1).to_bytes.hexdigest == "FF"
+ # assert (-32).to_bytes.hexdigest == "E0"
+ # assert (-512).to_bytes.hexdigest == "FE00"
+ # assert (-65794).to_bytes.hexdigest == "FEFEFE"
+ # ~~~
+ #
+ # Optionally, set `n_bytes` to the desired number of bytes in the output.
+ # This setting can disambiguate the result between positive and negative
+ # integers. Be careful with this parameter as the result may overflow.
+ #
+ # ~~~
+ # assert 1.to_bytes(2).hexdigest == "0001"
+ # assert 65535.to_bytes(2).hexdigest == "FFFF"
+ # assert (-1).to_bytes(2).hexdigest == "FFFF"
+ # assert (-512).to_bytes(4).hexdigest == "FFFFFE00"
+ # assert 0x123456.to_bytes(2).hexdigest == "3456"
+ # ~~~
+ #
# For 0, a Bytes object with single nul byte is returned (instead of an empty Bytes object).
#
# ~~~
# assert 0.to_bytes.hexdigest == "00"
# ~~~
#
- # `Bytes::to_i` can be used to do the reverse operation.
+ # For positive integers, `Bytes::to_i` can reverse the operation.
#
# ~~~
# assert 1234.to_bytes.to_i == 1234
# ~~~
#
# Require self >= 0
- fun to_bytes: Bytes do
- if self == 0 then return "\0".to_bytes
- assert self > 0
+ fun to_bytes(n_bytes: nullable Int): Bytes do
+
+ # If 0, force using at least one byte
+ if self == 0 and n_bytes == null then n_bytes = 1
# Compute the len (log256)
var len = 1
var max = 256
- while self >= max do
+ var s = self.abs
+ while s >= max do
len += 1
max *= 256
end
+ # Two's complement
+ s = self
+ if self < 0 then
+ var ff = 0
+ for j in [0..len[ do
+ ff *= 0x100
+ ff += 0xFF
+ end
+
+ s = ((-self) ^ ff) + 1
+ end
+
+ # Cut long values
+ if n_bytes != null and len > n_bytes then len = n_bytes
+
# Allocate the buffer
- var res = new Bytes.with_capacity(len)
- for i in [0..len[ do res[i] = 0u8
+ var cap = n_bytes or else len
+ var res = new Bytes.with_capacity(cap)
+
+ var filler = if self < 0 then 0xFFu8 else 0u8
+ for i in [0..cap[ do res[i] = filler
# Fill it starting with the end
- var i = len
- var sum = self
- while i > 0 do
+ var i = cap
+ var sum = s
+ while i > cap - len do
i -= 1
res[i] = (sum % 256).to_b
sum /= 256
end
+
return res
end
end
# Standard classes and methods used by default by Nit programs and libraries.
# This module is implicitly imported by every module.
-module core
+module core is
+ new_annotation test
+ new_annotation before
+ new_annotation before_all
+ new_annotation after
+ new_annotation after_all
+end
import posix
import environ
# You are allowed to redistribute it and sell it, alone or is a part of
# another product.
-module test_abstract_text is test_suite
+module test_abstract_text is test
-import test_suite
import text
intrude import ropes
class TestText
- super TestSuite
+ test
private var factories: Collection[TextFactory] = [
new ConcatFactory,
new FlatBufferFactory
: TextFactory]
- fun test_escape_to_c do
+ fun test_escape_to_c is test do
for f in factories do
assert f.create("abAB12<>&").escape_to_c == "abAB12<>&"
assert f.create("\n\"'\\").escape_to_c == "\\n\\\"\\'\\\\"
var dy = 0.0
var text_width = 0.0
var line_sprites = new Array[Sprite]
+ var height = 0.0
+
+ # Has the current line height been added to `height`?
+ var line_height_counted = false
# TextSprite customization
var max_width = text_sprites.max_width
dy -= line_height
if max_height != null and max_height < -dy + line_height then break
dx = 0.0
+ if not line_height_counted then
+ # Force to account for empty lines
+ height += line_height
+ end
+ line_height_counted = false
prev_char = null
continue
else if c == pld then
dy -= partial_line_skip
+ height += partial_line_skip
word_break = true
continue
else if c == plu then
dy += partial_line_skip
+ height -= partial_line_skip # We could keep two heights and return the max
word_break = true
continue
else if c.is_whitespace then
var w = text[wi]
if w == '\n' or w == pld or w == plu or w.is_whitespace or (in_link and w == ']') then break
+
+ if not desc.chars.keys.has(w) then
+ var rc = replacement_char
+ if rc == null then continue
+ w = rc
+ end
+
word_len += advance(prev_w, w) * scale
prev_w = w
end
# Would the line be too long?
if dx + word_len > max_width then
- if text_sprites.wrap then
- # Wrap
- justify(line_sprites, text_sprites.align, dx)
- dy -= line_height
- if max_height != null and max_height < -dy + line_height then break
- dx = 0.0
- else
- # Cut short
- justify(line_sprites, text_sprites.align, dx)
- dy -= line_height
- if max_height != null and max_height < -dy + line_height then break
- dx = 0.0
+ justify(line_sprites, text_sprites.align, dx)
+ dy -= line_height
+ if max_height != null and max_height < -dy + line_height then break
+ dx = 0.0
+ line_height_counted = false
+
+ if not text_sprites.wrap then
+ # Cut short, skip everything until the next new line
while c != '\n' and i < text.length - 1 do
i += 1
c = text[i]
prev_char = c
text_width = text_width.max(dx)
+
+ if not line_height_counted then
+ # Increase `height` only once per line iff there's a caracter
+ line_height_counted = true
+ height += line_height
+ end
end
justify(line_sprites, text_sprites.align, dx)
end
text_sprites.width = text_width.max(dx)
- text_sprites.height = dy + line_height
+ text_sprites.height = height
end
# Character replacing other characters missing from the font
# Usually, there is one `LeafModel` per material.
# At each frame, each material is asked to draw all the live `LeafModel` instaces.
fun leaves: Array[LeafModel] is abstract
+
+ # Sub-models with names, usually declared in the asset file
+ var named_parts = new Map[Text, Model]
end
# Model composed of one or many other `LeafModel`
module more_lights
import depth_core
+intrude import cameras_cache
# TODO
#class PointLight
return view
end
- redef fun mvp_matrix
+ private fun create_mvp_matrix: Matrix
do
- # TODO cache
-
var near = -light.depth/2.0
var far = light.depth/2.0
near, far)
return view * projection
end
+
+ redef fun mvp_matrix
+ do
+ var m = mvp_matrix_cache
+ if m == null or check_position_changed then
+ m = create_mvp_matrix
+ mvp_matrix_cache = m
+ end
+ return m
+ end
+
+ private var pitch_cache = 0.0
+ private var yaw_cache = 0.0
+ private var width_cache = 0.0
+ private var height_cache = 0.0
+ private var depth_cache = 0.0
+
+ redef fun check_position_changed
+ do
+ if super then return true
+
+ if light.pitch != pitch_cache or
+ light.yaw != yaw_cache or
+ light.width != width_cache or
+ light.height != height_cache or
+ light.depth != depth_cache then
+ pitch_cache = light.pitch
+ yaw_cache = light.yaw
+ width_cache = light.width
+ height_cache = light.height
+ depth_cache = light.depth
+ return true
+ end
+
+ return false
+ end
end
uniform mat4 rotation;
// Lights config
- uniform int light_kind;
+ uniform lowp int light_kind;
uniform vec3 light_center;
uniform mat4 light_mvp;
uniform sampler2D map_normal;
// Shadow
- uniform int light_kind;
+ uniform lowp int light_kind;
uniform bool use_shadows;
uniform sampler2D depth_texture;
uniform float depth_texture_size;
redef fun load
do
+ if loaded then return
+
var ext = path.file_extension
if ext == "obj" then
load_obj_file
loaded = true
end
+ private fun lazy_load
+ do
+ if loaded then return
+
+ # Lazy load
+ load
+
+ # Print errors when lazy loading only
+ if errors.length == 1 then
+ print_error errors.first
+ else if errors.length > 1 then
+ print_error "Loading model at '{path}' raised {errors.length} errors:\n* "
+ print_error errors.join("\n* ")
+ end
+ end
+
private fun load_obj_file
do
# Read .obj description from assets
if debug_gamnit then assert obj_def.is_coherent
# Build models
- var converter = new ModelFromObj(path, obj_def)
- converter.models leaves_cache
+ var converter = new BuildModelFromObj(path, obj_def)
+ converter.fill_leaves self
errors.add_all converter.errors
end
redef fun leaves
do
- if not loaded then
- # Lazy load
- load
-
- # Print errors when lazy loading only
- if errors.length == 1 then
- print_error errors.first
- else if errors.length > 1 then
- print_error "Loading model at '{path}' raised {errors.length} errors:\n* "
- print_error errors.join("\n* ")
- end
- end
-
+ lazy_load
return leaves_cache
end
private var leaves_cache = new Array[LeafModel]
+
+ redef fun named_parts
+ do
+ lazy_load
+ return named_leaves_cache
+ end
+
+ private var named_leaves_cache = new Map[String, Model]
end
-# Short-lived service to convert an `ObjDef` to `models`
+# Short-lived service to convert an `ObjDef` to `fill_leaves`
#
# Limitations: This service only support faces with 3 or 4 vertices.
# Faces with more vertices should be triangulated by the modeling tool.
-private class ModelFromObj
+private class BuildModelFromObj
# Path to the .obj file in the assets folder, used to find .mtl files
var path: String
# Parsed .obj definition
var obj_def: ObjDef
- # Errors raised by calls to `models`
+ # Errors raised by calls to `fill_leaves`
var errors = new Array[Error]
- # Fill `leaves` with models described in `obj_def`
- fun models(leaves: Array[LeafModel])
+ # Fill `leaves` with objects described in `obj_def`
+ fun fill_leaves(target_model: ModelAsset)
do
- # Sort faces by material
- var mtl_to_faces = new MultiHashMap[String, ObjFace]
- for face in obj_def.faces do
- var mtl_lib_name = face.material_lib
- var mtl_name = face.material_name
-
- var full_name = ""
- if mtl_lib_name != null and mtl_name != null then full_name = mtl_lib_name / mtl_name
+ var leaves = target_model.leaves_cache
- mtl_to_faces[full_name].add face
+ # Sort faces by material
+ var obj_mtl_to_faces = new Map[ObjObj, MultiHashMap[String, ObjFace]]
+ for obj in obj_def.objects do
+ var mtl_to_faces = new MultiHashMap[String, ObjFace]
+ obj_mtl_to_faces[obj] = mtl_to_faces
+ for face in obj.faces do
+ var mtl_lib_name = face.material_lib
+ var mtl_name = face.material_name
+
+ var full_name = ""
+ if mtl_lib_name != null and mtl_name != null then full_name = mtl_lib_name / mtl_name
+
+ mtl_to_faces[full_name].add face
+ end
end
# Load material libs
mtl_libs[asset_path] = mtl_lib
end
- # Create 1 mesh per material, and prepare materials
+ # Create 1 mesh per material per object, and prepare materials
var mesh_to_mtl = new Map[Mesh, nullable MtlDef]
+ var mesh_to_name = new Map[Mesh, String]
var texture_names = new Set[String]
- for full_name, faces in mtl_to_faces do
-
- # Create mesh
- var mesh = new Mesh
- mesh.vertices = vertices(faces)
- mesh.normals = normals(faces)
- mesh.texture_coords = texture_coords(faces)
-
- # Material
- var mtl_def = null
-
- var mtl_lib_name = faces.first.material_lib
- var mtl_name = faces.first.material_name
- if mtl_lib_name != null and mtl_name != null then
- var asset_path = self.path.dirname / mtl_lib_name
- var mtl_lib = mtl_libs[asset_path]
- var mtl = mtl_lib.get_or_null(mtl_name)
- if mtl != null then
- mtl_def = mtl
-
- for e in mtl.maps do
- texture_names.add self.path.dirname / e
+ for obj in obj_def.objects do
+ var mtl_to_faces = obj_mtl_to_faces[obj]
+ for mtl_path, faces in mtl_to_faces do
+
+ # Create mesh
+ var mesh = new Mesh
+ mesh.vertices = vertices(faces)
+ mesh.normals = normals(faces)
+ mesh.texture_coords = texture_coords(faces)
+
+ # Material
+ var mtl_def = null
+
+ var mtl_lib_name = faces.first.material_lib
+ var mtl_name = faces.first.material_name
+ if mtl_lib_name != null and mtl_name != null then
+ var asset_path = self.path.dirname / mtl_lib_name
+ var mtl_lib = mtl_libs[asset_path]
+ var mtl = mtl_lib.get_or_null(mtl_name)
+ if mtl != null then
+ mtl_def = mtl
+
+ for e in mtl.maps do
+ texture_names.add self.path.dirname / e
+ end
+ else
+ errors.add new Error("Error loading model at '{path}': mtl '{mtl_name}' not found in '{asset_path}'")
end
- else
- errors.add new Error("Error loading model at '{path}': mtl '{mtl_name}' not found in '{asset_path}'")
end
- end
- mesh_to_mtl[mesh] = mtl_def
+ mesh_to_mtl[mesh] = mtl_def
+ mesh_to_name[mesh] = obj.name
+ end
end
# Load textures need for these materials
end
# Create models and store them
+ var name_to_leaves = new MultiHashMap[String, LeafModel]
for mesh, mtl_def in mesh_to_mtl do
+
var material = materials.get_or_null(mtl_def)
if material == null then material = new Material
var model = new LeafModel(mesh, material)
leaves.add model
+
+ name_to_leaves[mesh_to_name[mesh]].add model
+ end
+
+ # Collect objects with a name
+ for name, models in name_to_leaves do
+ if models.length == 1 then
+ target_model.named_leaves_cache[name] = models.first
+ else
+ var named_model = new CompositeModel
+ named_model.leaves.add_all models
+ target_model.named_leaves_cache[name] = named_model
+ end
end
end
jni_env.pop_local_frame
end
end
+
+redef class Pointer
+ # Disable out premultiply as we use only the one from Android
+ redef fun premultiply_alpha(width, height) do end
+end
# Asset font used to display text
var font = new BMFontAsset("Josefin_Sans/font.fnt")
- # Anchor texture used to identify the anchor coordinate of each `TextSprites`
+ # Anchor texture identifying the anchor coordinates of each `TextSprites`
var anchor = new Texture("anchor.png")
+ # Bottom right corner
+ var corner = new Texture("corner.png")
+
redef fun on_create
do
super
# Add the anchor effects to all TextSprites
for t in texts do ui_sprites.add new Sprite(anchor, t.anchor)
+
+ for t in texts do
+ # Bottom right
+ var br = t.anchor.offset(t.width*(1.0-t.align), -t.height*(1.0-t.valign), 1.0)
+ ui_sprites.add new Sprite(corner, br)
+ end
end
redef fun accept_event(event)
# var parser = new ObjFileParser(obj_src)
# var parsed_obj = parser.parse
# assert parsed_obj.is_coherent
+# assert parsed_obj.objects.first.name == "Cube"
# ~~~
class ObjFileParser
super StringProcessor
# Execute parsing of `src` to extract an `ObjDef`
fun parse: nullable ObjDef
do
+ var obj_obj = null
while not eof do
var token = read_token
if token.is_empty or token == "#" then
geometry.params.add vec
else if token == "f" then # Faces
var face = read_face
- geometry.faces.add face
+ if obj_obj != null then obj_obj.faces.add face
else if token == "mtllib" then
current_material_lib = read_until_eol_or_comment
else if token == "usemtl" then
# TODO other line type headers
else if token == "s" then
else if token == "o" then
+ obj_obj = new ObjObj(read_until_eol_or_comment)
+ geometry.objects.add obj_obj
else if token == "g" then
end
skip_eol
# Surface parameters
var params = new Array[Vec3]
- # Faces
- var faces = new Array[ObjFace]
+ # Sub-objects
+ var objects = new Array[ObjObj]
# Relative paths to referenced material libraries
fun material_libs: Set[String] do
var libs = new Set[String]
- for face in faces do
- var lib = face.material_lib
- if lib != null then libs.add lib
+ for obj in objects do
+ for face in obj.faces do
+ var lib = face.material_lib
+ if lib != null then libs.add lib
+ end
end
return libs
end
# be executed at each execution of a game.
fun is_coherent: Bool
do
- for f in faces do
- if f.vertices.length < 3 then return error("ObjFace with less than 3 vertices")
+ for obj in objects do
+ for f in obj.faces do
+ if f.vertices.length < 3 then return error("Face with less than 3 vertices")
+ end
- for v in f.vertices do
+ for f in obj.faces do for v in f.vertices do
var i = v.vertex_point_index
if i < 1 then return error("Vertex point index < 1")
if i > vertex_points.length then return error("Vertex point index > than length")
end
end
+# Sub-object within an `ObjDef`
+class ObjObj
+
+ # Sub-object name as declared in the source file
+ var name: String
+
+ # Sub-object faces
+ var faces = new Array[ObjFace]
+end
+
# Flat surface of an `ObjDef`
class ObjFace
- # Vertex composing this surface, thene should be 3 or more
+ # Vertex composing this surface, there should be 3 or more
var vertices = new Array[ObjVertex]
# Relative path to the .mtl material lib
var socket: nullable TCPStream = null
# Is this connection connected?
- fun connected: Bool do return socket != null and socket.connected
+ fun connected: Bool
+ do
+ var socket = socket
+ return socket != null and socket.connected
+ end
- # `BinarySerializer` used to send data to this client through `socket`
- var writer: BinarySerializer is noinit
+ # `MsgPackSerializer` used to send data to this client through `socket`
+ var writer: MsgPackSerializer is noinit
- # `BinaryDeserializer` used to receive data from this client through `socket`
- var reader: BinaryDeserializer is noinit
+ # `MsgPackDeserializer` used to receive data from this client through `socket`
+ var reader: MsgPackDeserializer is noinit
# Attempt connection with the remote server
fun connect: Bool
end
# Setup serialization
- writer = new BinarySerializer(socket)
+ writer = new MsgPackSerializer(socket)
writer.cache = new AsyncCache(false)
- reader = new BinaryDeserializer(socket)
+ reader = new MsgPackDeserializer(socket)
writer.link reader
return true
# App name
var app_name = sys.handshake_app_name
- socket.write_string app_name
+ socket.serialize_msgpack app_name
- var server_app = socket.read_string
+ var server_app = socket.deserialize_msgpack("String")
if server_app != app_name then
- print_error "Handshake Error: server app name is '{server_app}'"
+ print_error "Handshake Error: server app name is '{server_app or else "<invalid>"}'"
socket.close
return false
end
# App version
- socket.write_string sys.handshake_app_version
+ socket.serialize_msgpack sys.handshake_app_version
- var server_version = socket.read_string
+ var server_version = socket.deserialize_msgpack("String")
if server_version != sys.handshake_app_version then
- print_error "Handshake Error: server version is different '{server_version}'"
+ print_error "Handshake Error: server version is different '{server_version or else "<invalid>"}'"
socket.close
return false
end
module common
import socket
-import binary::serialization
+import msgpack
# Unique name of the application to use in the handshake
#
# Is this client connected?
fun connected: Bool do return socket.connected
- # `BinarySerializer` used to send data to this client through `socket`
- var writer: BinarySerializer is noinit
+ # `MsgPackSerializer` used to send data to this client through `socket`
+ var writer: MsgPackSerializer is noinit
- # `BinaryDeserializer` used to receive data from this client through `socket`
- var reader: BinaryDeserializer is noinit
+ # `MsgPackDeserializer` used to receive data from this client through `socket`
+ var reader: MsgPackDeserializer is noinit
init
do
# Setup serialization
- writer = new BinarySerializer(socket)
+ writer = new MsgPackSerializer(socket)
writer.cache = new AsyncCache(true)
- reader = new BinaryDeserializer(socket)
+ reader = new MsgPackDeserializer(socket)
writer.link reader
end
# Make sure it is the same app
var server_app = sys.handshake_app_name
- var client_app = socket.read_string
+ var client_app = socket.deserialize_msgpack
if server_app != client_app then
- print_error "Server Error: Client app name is '{client_app}'"
+ print_error "Server Error: Client app name is '{client_app or else "<invalid>"}'"
# Send an empty string so the client read it and give up
- socket.write_string ""
+ socket.serialize_msgpack ""
socket.close
return false
end
- socket.write_string server_app
+ socket.serialize_msgpack server_app
# App version
var app_version = sys.handshake_app_version
- var client_version = socket.read_string
+ var client_version = socket.deserialize_msgpack
if client_version != app_version then
- print_error "Handshake Error: client version is different '{client_version}'"
- socket.write_string ""
+ print_error "Handshake Error: client version is different '{client_version or else "<invalid>"}'"
+ socket.serialize_msgpack ""
socket.close
return false
end
- socket.write_string app_version
+ socket.serialize_msgpack app_version
return true
end
for i in [0..4[ do cpixels[offset+i] = bytes[i]
end
- # Overwrite all pixels with `color`
+ # Overwrite all pixels with `color`, return `self`
#
# The argument `color` should be an array of up to 4 floats (RGBA).
# If `color` has less than 4 items, the missing items are replaced by 1.0.
#
# Require: `not loaded`
- fun fill(color: Array[Float])
+ fun fill(color: Array[Float]): SELF
do
assert not loaded else print_error "{class_name}::fill already loaded"
i += 4
end
end
+
+ return self
end
redef fun load(force)
private fun load_from_pixels(pixels: Pointer, width, height: Int, format: GLPixelFormat)
do
var max_texture_size = glGetIntegerv(gl_MAX_TEXTURE_SIZE, 0)
- if width > max_texture_size or height > max_texture_size then
- error = new Error("Texture {self} width or height is over the GL_MAX_TEXTURE_SIZE of {max_texture_size}")
+ if width > max_texture_size then
+ error = new Error("Texture width larger than gl_MAX_TEXTURE_SIZE ({max_texture_size}) in {self} at {width}")
+ return
+ else if height > max_texture_size then
+ error = new Error("Texture height larger than gl_MAX_TEXTURE_SIZE ({max_texture_size}) in {self} at {height}")
return
end
redef class App
# Current touch gamepad, still may be invisible
- var gamepad: nullable VirtualGamepad = null
+ var gamepad: nullable VirtualGamepad = null is writable
# Textures used for `DPad` and available to clients
var gamepad_spritesheet = new VirtualGamepadSpritesheet
# and `add_button`.
var controls = new Array[RoundControl]
- # Add a directional pad (`DPad`) to a default location
+ # Add and return a directional pad (`DPad`) to a default location
#
# The 4 buttons fire events with the corresponding name in `names`.
# Items in `names` should be in order of top, left, down and right.
# added by `add_button`.
#
# Require: `names == null or names.length == 4`
- fun add_dpad(names: nullable Array[String])
+ fun add_dpad(names: nullable Array[String]): nullable DPad
do
if names == null then names = ["w","a","s","d"]
assert names.length == 4
if n_dpads == 0 then
- controls.add new DPad(app.ui_camera.bottom_left.offset(200.0, 100.0, 0.0), names)
+ var dpad = new DPad(app.ui_camera.bottom_left.offset(200.0, 100.0, 0.0), names)
+ controls.add dpad
+ return dpad
else if n_dpads == 1 then
- controls.add new DPad(app.ui_camera.bottom_right.offset(-200.0, 100.0, 0.0), names)
+ var dpad = new DPad(app.ui_camera.bottom_right.offset(-200.0, 100.0, 0.0), names)
+ controls.add dpad
+ return dpad
else
print_error "Too many DPad ({n_dpads}) in {self}"
+ return null
end
end
new Point[Float](-350.0, 350.0),
new Point[Float](-350.0, 550.0))
- # Add a round button to a default location
+ # Add and return a round button to a default location
#
# Fired events use `name`, it should usually correspond to a
# keyboard key like "space" or "a".
#
# A maximum of 6 buttons may be added using this method when
# there is less than 2 `DPad`. Otherwise, only 2 buttons can be added.
- fun add_button(name: String, texture: Texture)
+ fun add_button(name: String, texture: Texture): nullable RoundButton
do
if n_dpads == 2 and button_positions.length == 6 then
# Drop the bottom 4 buttons
assert button_positions.not_empty else print_error "Too many buttons in {self}"
var pos = button_positions.shift
- controls.add new RoundButton(
+ var but = new RoundButton(
app.ui_camera.bottom_right.offset(pos.x, pos.y, 0.0), name, texture)
+ controls.add but
+ return but
end
private fun prepare
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_github_curl is test_suite
+module test_github_curl is test
import github::github_curl
-import test_suite
class TestGithubCurl
- super TestSuite
+ test
var auth: String = get_github_oauth
var user_agent: String = "nit"
var testee: GithubCurl is noinit
- redef fun before_test do
+ fun before_test is before do
testee = new GithubCurl(auth, user_agent)
end
- fun test_get_repo do
+ fun test_get_repo is test do
var uri = "https://api.github.com/repos/nitlang/nit"
var res = testee.get_and_check(uri)
assert res["owner"] isa JsonObject
end
- fun test_get_user do
+ fun test_get_user is test do
var uri = "https://api.github.com/users/Morriar"
var res = testee.get_and_check(uri)
# http://www.khronos.org/opengles/sdk/docs/man/
module glesv2 is
pkgconfig
+ no_warning "missing-doc"
new_annotation glsl_vertex_shader
new_annotation glsl_fragment_shader
ldflags("-lGLESv2")@android
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_native_gmp is test_suite
+module test_native_gmp is test
-import test_suite
import native_gmp
class TestNativeMPZ
- super TestSuite
+ test
var op1: NativeMPZ
var op2: NativeMPZ
var r: NativeMPQ
var res: NativeMPZ
- init do end
-
- redef fun before_test do
+ fun before_test is before do
op1 = new NativeMPZ
op2 = new NativeMPZ
ui = new UInt64
res = new NativeMPZ
end
- redef fun after_test do
+ fun after_test is after do
op1.finalize
op2.finalize
ui.free
end
class TestNativeMPQ
- super TestSuite
+ test
var op1: NativeMPQ
var op2: NativeMPQ
var l: NativeMPZ
var res: NativeMPQ
- init do end
-
- redef fun before_test do
+ fun before_test is before do
op1 = new NativeMPQ
op2 = new NativeMPQ
l = new NativeMPZ
res = new NativeMPQ
end
- redef fun after_test do
+ fun after_test is after do
op1.finalize
op2.finalize
l.finalize
# limitations under the License.
# Test suites for module `markdown`
-module test_markdown is test_suite
+module test_markdown is test
-import test_suite
intrude import markdown
class TestMarkdownProcessor
- super TestSuite
+ test
- fun test_process_empty do
+ fun test_process_empty is test do
var test = ""
var exp = ""
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_tabs do
+ fun test_process_tabs is test do
var test = """
some code
"""
end
- fun test_process_par1 do
+ fun test_process_par1 is test do
var test = "test"
var exp = "<p>test</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_par2 do
+ fun test_process_par2 is test do
var test = """
line1
line2
assert res == exp
end
- fun test_process_par3 do
+ fun test_process_par3 is test do
var test = """
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Aliquam hendrerit mi posuere lectus.
assert res == exp
end
- fun test_process_headings_1 do
+ fun test_process_headings_1 is test do
var test = """
This is a H1
=============
assert res == exp
end
- fun test_process_headings_2 do
+ fun test_process_headings_2 is test do
var test = """
# This is a H1
assert res == exp
end
- fun test_process_headings_3 do
+ fun test_process_headings_3 is test do
var test = """
# This is a H1 #
assert res == exp
end
- fun test_process_hr do
+ fun test_process_hr is test do
var test = """
* * *
assert res == exp
end
- fun test_process_bquote1 do
+ fun test_process_bquote1 is test do
var test = """
> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
assert res == exp
end
- fun test_process_bquote2 do
+ fun test_process_bquote2 is test do
var test = """
> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
assert res == exp
end
- fun test_process_bquote3 do
+ fun test_process_bquote3 is test do
var test = """
> This is the first level of quoting.
>
assert res == exp
end
- fun test_process_list1 do
+ fun test_process_list1 is test do
var test = """
* Red
* Green
assert res == exp
end
- fun test_process_list2 do
+ fun test_process_list2 is test do
var test = """
+ Red
+ Green
assert res == exp
end
- fun test_process_list3 do
+ fun test_process_list3 is test do
var test = """
- Red
- Green
assert res == exp
end
- fun test_process_list4 do
+ fun test_process_list4 is test do
var test = """
1. Bird
2. McHale
assert res == exp
end
- fun test_process_list5 do
+ fun test_process_list5 is test do
var test = """
3. Bird
1. McHale
assert res == exp
end
- fun test_process_list6 do
+ fun test_process_list6 is test do
var test = """
* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
assert res == exp
end
- fun test_process_list7 do
+ fun test_process_list7 is test do
var test = """
* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
assert res == exp
end
- fun test_process_list8 do
+ fun test_process_list8 is test do
var test = """
* Bird
assert res == exp
end
- fun test_process_list9 do
+ fun test_process_list9 is test do
var test = """
1. This is a list item with two paragraphs. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
assert res == exp
end
- fun test_process_list10 do
+ fun test_process_list10 is test do
var test = """
* This is a list item with two paragraphs.
assert res == exp
end
- fun test_process_list11 do
+ fun test_process_list11 is test do
var test = """
This is a paragraph
* and this is not a list
assert res == exp
end
- fun test_process_list_ext do
+ fun test_process_list_ext is test do
var test = """
This is a paragraph
* and this is not a list
assert res == exp
end
- fun test_process_code1 do
+ fun test_process_code1 is test do
var test = """
This is a normal paragraph:
assert res == exp
end
- fun test_process_code2 do
+ fun test_process_code2 is test do
var test = """
Here is an example of AppleScript:
assert res == exp
end
- fun test_process_code_ext1 do
+ fun test_process_code_ext1 is test do
var test = """
Here is an example of AppleScript:
~~~
assert res == exp
end
- fun test_process_code_ext2 do
+ fun test_process_code_ext2 is test do
var test = """
Here is an example of AppleScript:
```
assert res == exp
end
- fun test_process_code_ext3 do
+ fun test_process_code_ext3 is test do
var proc = new MarkdownProcessor
proc.ext_mode = false
assert res == exp
end
- fun test_process_code_ext4 do
+ fun test_process_code_ext4 is test do
var test = """
Here is an example of AppleScript:
beep
assert res == exp
end
- fun test_process_code_ext5 do
+ fun test_process_code_ext5 is test do
var test = """
```nit
print "Hello World!"
assert res == exp
end
- fun test_process_code_ext6 do
+ fun test_process_code_ext6 is test do
var test = """
~~~
print "Hello"
assert res == exp
end
- fun test_process_code_ext7 do
+ fun test_process_code_ext7 is test do
var test = """
~~~
print "Hello"
assert res == exp
end
- fun test_process_nesting1 do
+ fun test_process_nesting1 is test do
var test = """
> ## This is a header.
>
assert res == exp
end
- fun test_process_nesting2 do
+ fun test_process_nesting2 is test do
var test = """
* A list item with a blockquote:
assert res == exp
end
- fun test_process_nesting3 do
+ fun test_process_nesting3 is test do
var test = """
* A list item with a code block:
assert res == exp
end
- fun test_process_nesting4 do
+ fun test_process_nesting4 is test do
var test = """
* Tab
* Tab
end
# TODO
- # fun test_process_nesting5 do
+ # fun test_process_nesting5 is test do
# var test = """
# * this
#
# assert res == exp
# end
- fun test_process_emph1 do
+ fun test_process_emph1 is test do
var test = """
*single asterisks*
assert res == exp
end
- fun test_process_emph2 do
+ fun test_process_emph2 is test do
var test = "un*frigging*believable"
var exp = "<p>un<em>frigging</em>believable</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_emph3 do
+ fun test_process_emph3 is test do
var proc = new MarkdownProcessor
proc.ext_mode = false
var test = "Con_cat_this"
assert res == exp
end
- fun test_process_emph_ext do
+ fun test_process_emph_ext is test do
var test = "Con_cat_this"
var exp = "<p>Con_cat_this</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_xml1 do
+ fun test_process_xml1 is test do
var test = """
This is a regular paragraph.
assert res == exp
end
- fun test_process_xml2 do
+ fun test_process_xml2 is test do
var test = """
This is an image <img src="foo/bar" alt="baz"/> in a regular paragraph.
"""
assert res == exp
end
- fun test_process_xml3 do
+ fun test_process_xml3 is test do
var test = """
<div style=">"/>
"""
assert res == exp
end
- fun test_process_xml4 do
+ fun test_process_xml4 is test do
var test = """
<p>This is an example of a block element that should be escaped.</p>
<p>Idem for the second paragraph.</p>
assert res == exp
end
- fun test_process_xml5 do
+ fun test_process_xml5 is test do
var test = """
# Some more XML tests
assert res == exp
end
- fun test_process_span_code1 do
+ fun test_process_span_code1 is test do
var test = "Use the `printf()` function."
var exp = "<p>Use the <code>printf()</code> function.</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_span_code2 do
+ fun test_process_span_code2 is test do
var test = "``There is a literal backtick (`) here.``"
var exp = "<p><code>There is a literal backtick (`) here.</code></p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_span_code3 do
+ fun test_process_span_code3 is test do
var test = """
A single backtick in a code span: `` ` ``
assert res == exp
end
- fun test_process_span_code4 do
+ fun test_process_span_code4 is test do
var test = "Please don't use any `<blink>` tags."
var exp = "<p>Please don't use any <code><blink></code> tags.</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_span_code5 do
+ fun test_process_span_code5 is test do
var test = "`—` is the decimal-encoded equivalent of `—`."
var exp = "<p><code>&#8212;</code> is the decimal-encoded equivalent of <code>&mdash;</code>.</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_escape1 do
+ fun test_process_escape1 is test do
var test = "\\*this text is surrounded by literal asterisks\\*"
var exp = "<p>*this text is surrounded by literal asterisks*</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_escape2 do
+ fun test_process_escape2 is test do
var test = "1986\\. What a great season."
var exp = "<p>1986. What a great season.</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_escape3 do
+ fun test_process_escape3 is test do
var test = "Ben & Lux"
var exp = "<p>Ben & Lux</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_link1 do
+ fun test_process_link1 is test do
var test = """
This is [an example](http://example.com/ "Title") inline link.
assert res == exp
end
- fun test_process_link2 do
+ fun test_process_link2 is test do
var test = "See my [About](/about/) page for details."
var exp = "<p>See my <a href=\"/about/\">About</a> page for details.</p>\n"
var res = test.md_to_html.write_to_string
assert res == exp
end
- fun test_process_link3 do
+ fun test_process_link3 is test do
var test = """
This is [an example][id] reference-style link.
assert res == exp
end
- fun test_process_link4 do
+ fun test_process_link4 is test do
var test = """
This is multiple examples: [foo][1], [bar][2], [baz][3].
assert res == exp
end
- fun test_process_link5 do
+ fun test_process_link5 is test do
var test = """
This is multiple examples: [foo][a], [bar][A], [a].
assert res == exp
end
- fun test_process_link6 do
+ fun test_process_link6 is test do
var test = """
I get 10 times more traffic from [Google][] than from [Yahoo][] or [MSN][].
assert res == exp
end
- fun test_process_link7 do
+ fun test_process_link7 is test do
var test = """
Visit [Daring Fireball][] for more information.
assert res == exp
end
- fun test_process_link8 do
+ fun test_process_link8 is test do
var test = """
This one has a [line
break].
end
# FIXME unignore test once escape strings fixed
- # fun test_process_link9 do
+ # fun test_process_link9 is test do
# var test = """
# Foo [bar][].
#
# assert res == exp
# end
- fun test_process_img1 do
+ fun test_process_img1 is test do
var test = """
![Alt text](/path/to/img.jpg)
assert res == exp
end
- fun test_process_img2 do
+ fun test_process_img2 is test do
var test = """
![Alt text][id]
assert res == exp
end
- fun test_process_strike do
+ fun test_process_strike is test do
var proc = new MarkdownProcessor
proc.ext_mode = false
var test = "This is how you ~~strike text~~"
assert exp == res
end
- fun test_process_strike_ext do
+ fun test_process_strike_ext is test do
var test = "This is how you ~~strike text~~"
var exp = "<p>This is how you <del>strike text</del></p>\n"
var res = test.md_to_html.write_to_string
assert exp == res
end
- fun test_escape_bad_html do
+ fun test_escape_bad_html is test do
var test = "-1 if < , +1 if > and 0 otherwise"
var exp = "<p>-1 if < , +1 if > and 0 otherwise</p>\n"
var res = test.md_to_html.write_to_string
assert exp == res
end
- fun test_daring_encoding do
+ fun test_daring_encoding is test do
var test = """
AT&T has an ampersand in their name.
end
- fun test_daring_autolinks do
+ fun test_daring_autolinks is test do
var test = """
Link: <http://example.com/>.
assert res == exp
end
- fun test_daring_escape do
+ fun test_daring_escape is test do
var test = """
These should all get escaped:
assert res == exp
end
- fun test_daring_blockquotes do
+ fun test_daring_blockquotes is test do
var test = """
> Example:
>
assert res == exp
end
- fun test_daring_code_blocks do
+ fun test_daring_code_blocks is test do
var test = """
code block on the first line
assert res == exp
end
- fun test_daring_code_spans do
+ fun test_daring_code_spans is test do
var test = """
`<test a="` content of attribute `">`
assert res == exp
end
- fun test_daring_pars do
+ fun test_daring_pars is test do
var proc = new MarkdownProcessor
proc.ext_mode = false
assert res == exp
end
- fun test_daring_rules do
+ fun test_daring_rules is test do
var test = """
Dashes:
assert res == exp
end
- fun test_daring_images do
+ fun test_daring_images is test do
var test = """
![Alt text](/path/to/img.jpg)
assert res == exp
end
- fun test_daring_inline_html1 do
+ fun test_daring_inline_html1 is test do
var test = """
Here's a simple block:
assert res == exp
end
- fun test_daring_inline_html2 do
+ fun test_daring_inline_html2 is test do
var test = """
Simple block on one line:
assert res == exp
end
- fun test_daring_inline_html3 do
+ fun test_daring_inline_html3 is test do
var test = """
Paragraph one.
assert res == exp
end
- fun test_daring_links1 do
+ fun test_daring_links1 is test do
var test = """
Just a [URL](/url/).
assert res == exp
end
- fun test_daring_links2 do
+ fun test_daring_links2 is test do
var test = """
Foo [bar] [1].
assert res == exp
end
- fun test_daring_links3 do
+ fun test_daring_links3 is test do
var test = """
This is the [simple case].
assert res == exp
end
- fun test_daring_nested do
+ fun test_daring_nested is test do
var test = """
> foo
>
assert res == exp
end
- fun test_daring_list do
+ fun test_daring_list is test do
var test = """
## Unordered
assert res == exp
end
- fun test_daring_strong_em do
+ fun test_daring_strong_em is test do
var test = """
***This is strong and em.***
assert res == exp
end
- fun test_daring_tabs do
+ fun test_daring_tabs is test do
var test = """
+ this is a list item
indented with tabs
assert res == exp
end
- fun test_daring_tidyness do
+ fun test_daring_tidyness is test do
var test = """
> A list within a blockquote:
>
end
class TestBlock
- super TestSuite
+ test
# A dummy location for testing purposes.
var loc = new MDLocation(0, 0, 0, 0)
- fun test_has_blocks do
+ fun test_has_blocks is test do
var subject = new MDBlock(loc)
assert not subject.has_blocks
subject.first_block = new MDBlock(loc)
assert subject.has_blocks
end
- fun test_count_blocks do
+ fun test_count_blocks is test do
var subject = new MDBlock(loc)
assert subject.count_blocks == 0
subject.first_block = new MDBlock(loc)
assert subject.count_blocks == 2
end
- fun test_has_lines do
+ fun test_has_lines is test do
var subject = new MDBlock(loc)
assert not subject.has_lines
subject.first_line = new MDLine(loc, "")
assert subject.has_lines
end
- fun test_count_lines do
+ fun test_count_lines is test do
var subject = new MDBlock(loc)
assert subject.count_lines == 0
subject.first_line = new MDLine(loc, "")
assert subject.count_lines == 2
end
- fun test_split do
+ fun test_split is test do
var line1 = new MDLine(loc, "line1")
var line2 = new MDLine(loc, "line2")
var line3 = new MDLine(loc, "line3")
assert block.last_line == line2
end
- fun test_add_line do
+ fun test_add_line is test do
var subject = new MDBlock(loc)
assert subject.count_lines == 0
subject.add_line new MDLine(loc, "")
assert subject.count_lines == 2
end
- fun test_remove_line do
+ fun test_remove_line is test do
var line1 = new MDLine(loc, "line1")
var line2 = new MDLine(loc, "line2")
var line3 = new MDLine(loc, "line3")
assert subject.last_line == line3
end
- fun test_transform_headline1 do
+ fun test_transform_headline1 is test do
var subject = new MDBlock(loc)
var kind = new BlockHeadline(subject)
subject.add_line new MDLine(loc, " # Title 1 ")
assert subject.first_line.value == "Title 1"
end
- fun test_transform_headline2 do
+ fun test_transform_headline2 is test do
var subject = new MDBlock(loc)
var kind = new BlockHeadline(subject)
subject.add_line new MDLine(loc, " #####Title 5 ")
assert subject.first_line.value == "Title 5"
end
- fun test_remove_quote_prefix do
+ fun test_remove_quote_prefix is test do
var subject = new MDBlock(loc)
var kind = new BlockQuote(subject)
subject.add_line new MDLine(loc, " > line 1")
assert subject.first_line.next.next.value == "line 3"
end
- fun test_remove_leading_empty_lines_1 do
+ fun test_remove_leading_empty_lines_1 is test do
var block = new MDBlock(loc)
block.add_line new MDLine(loc, "")
block.add_line new MDLine(loc, "")
assert block.first_line.value == " text"
end
- fun test_remove_leading_empty_lines_2 do
+ fun test_remove_leading_empty_lines_2 is test do
var block = new MDBlock(loc)
block.add_line new MDLine(loc, " text")
block.remove_leading_empty_lines
assert block.first_line.value == " text"
end
- fun test_remove_trailing_empty_lines_1 do
+ fun test_remove_trailing_empty_lines_1 is test do
var block = new MDBlock(loc)
block.add_line new MDLine(loc, "")
block.add_line new MDLine(loc, "text")
assert block.last_line.value == "text"
end
- fun test_remove_trailing_empty_lines_2 do
+ fun test_remove_trailing_empty_lines_2 is test do
var block = new MDBlock(loc)
block.add_line new MDLine(loc, "text ")
assert not block.remove_trailing_empty_lines
assert block.last_line.value == "text "
end
- fun test_remove_surrounding_empty_lines do
+ fun test_remove_surrounding_empty_lines is test do
var block = new MDBlock(loc)
block.add_line new MDLine(loc, "")
block.add_line new MDLine(loc, "text")
end
class TestLine
- super TestSuite
+ test
# A dummy location for testing purposes.
var loc = new MDLocation(0, 0, 0, 0)
var subject: MDLine
- fun test_is_empty do
+ fun test_is_empty is test do
subject = new MDLine(loc, "")
assert subject.is_empty
subject = new MDLine(loc, " ")
assert not subject.is_empty
end
- fun test_leading do
+ fun test_leading is test do
subject = new MDLine(loc, "")
assert subject.leading == 0
subject = new MDLine(loc, " ")
assert subject.leading == 4
end
- fun test_trailing do
+ fun test_trailing is test do
subject = new MDLine(loc, "")
assert subject.trailing == 0
subject = new MDLine(loc, " ")
assert subject.trailing == 1
end
- fun test_line_type do
+ fun test_line_type is test do
var v = new MarkdownProcessor
subject = new MDLine(loc, "")
assert v.line_kind(subject) isa LineEmpty
assert v.line_kind(subject) isa LineOList
end
- fun test_line_type_ext do
+ fun test_line_type_ext is test do
var v = new MarkdownProcessor
subject = new MDLine(loc, " ~~~")
assert v.line_kind(subject) isa LineFence
assert v.line_kind(subject) isa LineFence
end
- fun test_count_chars do
+ fun test_count_chars is test do
subject = new MDLine(loc, "")
assert subject.count_chars('*') == 0
subject = new MDLine(loc, "* ")
assert subject.count_chars('*') == 0
end
- fun test_count_chars_start do
+ fun test_count_chars_start is test do
subject = new MDLine(loc, "")
assert subject.count_chars_start('*') == 0
subject = new MDLine(loc, "* ")
end
class TestHTMLDecorator
- super TestSuite
+ test
- fun test_headlines do
+ fun test_headlines is test do
var test = """
# **a**
## a.a
end
class TestTokenLocation
- super TestSuite
+ test
- fun test_token_location1 do
+ fun test_token_location1 is test do
var string = "**Hello** `World`"
var stack = [
"TokenStrongStar at 1,1--1,1",
(new TestTokenProcessor(stack)).process(string)
end
- fun test_token_location2 do
+ fun test_token_location2 is test do
var string = "**Hello**\n`World`\n*Bonjour*\n[le monde]()"
var stack = [
"TokenStrongStar at 1,1--1,1",
(new TestTokenProcessor(stack)).process(string)
end
- fun test_token_location3 do
+ fun test_token_location3 is test do
var string = """**Hello**
`World`
*Bonjour*
(new TestTokenProcessor(stack)).process(string)
end
- fun test_token_location4 do
+ fun test_token_location4 is test do
var string = "**Hello**\n\n`World`"
var stack = [
"TokenStrongStar at 1,1--1,1",
(new TestTokenProcessor(stack)).process(string)
end
- fun test_token_location5 do
+ fun test_token_location5 is test do
var string = "# *Title1*\n\n# *Title2*"
var stack = [
"TokenEmStar at 1,3--1,3",
end
class TestBlockLocation
- super TestSuite
+ test
var proc = new MarkdownProcessor
- fun test_block_location1 do
+ fun test_block_location1 is test do
var stack = [
"BlockHeadline: 1,1--1,8",
"BlockListItem: 2,1--2,6",
proc.process(string)
end
- fun test_block_location2 do
+ fun test_block_location2 is test do
var stack = [
"BlockHeadline: 1,1--1,11",
"BlockFence: 3,1--5,4",
proc.process(string)
end
- fun test_block_location3 do
+ fun test_block_location3 is test do
var stack = [
"BlockHeadline: 1,1--1,8",
"BlockHeadline: 3,1--3,10"]
# limitations under the License.
# Test suites for module `markdown`
-module test_wikilinks is test_suite
+module test_wikilinks is test
import test_markdown
import wikilinks
class TestTokenWikilink
- super TestSuite
+ test
- fun test_token_location1 do
+ fun test_token_location1 is test do
var string = "[[wikilink]]"
var stack = ["TokenWikiLink at 1,1--1,1"]
(new TestTokenProcessor(stack)).process(string)
end
- fun test_token_location2 do
+ fun test_token_location2 is test do
var string = "Hello [[World]]"
var stack = ["TokenWikiLink at 1,7--1,7"]
(new TestTokenProcessor(stack)).process(string)
end
- fun test_token_location3 do
+ fun test_token_location3 is test do
var string = "\nHello\nworld [[wikilink]] !"
var stack = ["TokenWikiLink at 3,7--3,7"]
(new TestTokenProcessor(stack)).process(string)
end
- fun test_token_location4 do
+ fun test_token_location4 is test do
var string = "[[link1]]\n\n[[link2]]"
var stack = [
"TokenWikiLink at 1,1--1,1",
(new TestTokenProcessor(stack)).process(string)
end
- fun test_token_location5 do
+ fun test_token_location5 is test do
var string = "[[link1]]\n[[link2]]"
var stack = [
"TokenWikiLink at 1,1--1,1",
(new TestTokenProcessor(stack)).process(string)
end
- fun test_token_location6 do
+ fun test_token_location6 is test do
var string = """
[[doc: github]]
--- /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.
+
+# Application specific MessagePack extension `MsgPackExt`
+module ext
+
+import serialization
+
+# Application specific MessagePack extension
+class MsgPackExt
+ serialize
+
+ # Custom type code, in [0..127]
+ var typ: Byte
+
+ # Data bytes
+ var data: Bytes
+
+ redef fun hash do return typ.hash + data.hash*8
+ redef fun ==(o) do return o isa MsgPackExt and o.typ == typ and o.data == data
+ redef fun to_s do return "<{class_name} typ: {typ}, data: {data.chexdigest}>"
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# MessagePack, an efficient binary serialization format
+#
+# This modules provides services at different levels:
+#
+# * Serialize Nit objects using either the quick and easy `Serializable::serialize_msgpack`
+# and `Write::serialize_msgpack`, or the extensible `MsgPackSerializer`.
+#
+# * Deserialize MessagePack to Nit objects using the quick and easy methods
+# `Reader|Bytes::deserialize_msgpack`, or inspect errors with the extensible
+# `MsgPackDeserializer`.
+#
+# * Read and write MessagePack data at a low-level (for engines and the likes)
+# by importing `msgpack::read` or `msgpack::write`. These services support
+# only which support only core Nit types with a corresponding MessagePack type.
+# See `Reader::read_msgpack` and services on `Writer` including `write_msgpack_ext`.
+#
+# Here we discuss the recommended serialization services supporting core
+# Nit and MessagePack types as well as full Nit objects.
+#
+# ## Primitive MessagePack types
+#
+# Most Nit core types are serialized to the smallest corresponding MessagePack type.
+#
+# ~~~
+# assert false.serialize_msgpack == b"\xC2"
+# assert b"\xC2".deserialize_msgpack == false
+#
+# assert true.serialize_msgpack == b"\xC3"
+# assert b"\xC3".deserialize_msgpack == true
+#
+# assert 1.234.serialize_msgpack == b"\xCB\x3F\xF3\xBE\x76\xC8\xB4\x39\x58"
+# assert b"\xCB\x3F\xF3\xBE\x76\xC8\xB4\x39\x58".deserialize_msgpack == 1.234
+#
+# assert "ABC".serialize_msgpack == b"\xA3ABC"
+# assert b"\xA3ABC".deserialize_msgpack == "ABC"
+#
+# assert [0x11, 0x22, 0x33].serialize_msgpack(plain=true) == b"\x93\x11\x22\x33"
+# assert b"\x93\x11\x22\x33".deserialize_msgpack == [0x11, 0x22, 0x33]
+#
+# var map = new Map[String, nullable Object]
+# map["i"] = 1
+# map["o"] = null
+# assert map.serialize_msgpack(plain=true) == b"\x82\xA1\x69\x01\xA1\x6F\xC0"
+# ~~~
+#
+# Ints are serialized to the smallest MessagePack type, so a small integer fits
+# in a single byte and larger integers take more bytes as needed.
+#
+# ~~~
+# assert 1.serialize_msgpack == b"\x01"
+# assert (-32).serialize_msgpack == b"\xE0"
+# assert 0x7F.serialize_msgpack == b"\x7F"
+# assert 0x80.serialize_msgpack == b"\xCC\x80"
+# assert 0x1234.serialize_msgpack == b"\xCD\x12\x34"
+# assert (-0x1234).serialize_msgpack == b"\xD1\xED\xCC"
+# assert 0x12345678.serialize_msgpack == b"\xCE\x12\x34\x56\x78"
+# assert 0x0123456789.serialize_msgpack == b"\xCF\x00\x00\x00\x01\x23\x45\x67\x89"
+#
+# assert b"\x01".deserialize_msgpack == 1
+# assert b"\xE0".deserialize_msgpack == -32
+# assert b"\x7F".deserialize_msgpack == 0x7F
+# assert b"\xCC\x80".deserialize_msgpack == 0x80
+# assert b"\xCD\x12\x34".deserialize_msgpack == 0x1234
+# assert b"\xD1\xED\xCC".deserialize_msgpack == -0x1234
+# assert b"\xCE\x12\x34\x56\x78".deserialize_msgpack == 0x12345678
+# assert b"\xCF\x00\x00\x00\x01\x23\x45\x67\x89".deserialize_msgpack == 0x0123456789
+# ~~~
+#
+# ## Primitive Nit type without a MessagePack equivalent
+#
+# Chars are serialized as a string in plain mode.
+#
+# ~~~
+# assert 'A'.serialize_msgpack(plain=true) == b"\xA1\x41"
+# assert b"\xA1\x41".deserialize_msgpack == "A" # Not a Char
+# ~~~
+#
+# Or, with metadata, chars are serialized to an ext with id 0x7C.
+#
+# ~~~
+# assert 'A'.serialize_msgpack == b"\xD4\x7C\x41"
+# assert b"\xD4\x7C\x41".deserialize_msgpack == 'A'
+# ~~~
+#
+# Byte instances are serialized as an integer in plain mode.
+#
+# ~~~
+# assert 0x01u8.serialize_msgpack(plain=true) == b"\x01"
+# assert b"\x01".deserialize_msgpack == 1 # Not a Byte
+# ~~~
+#
+# Or, with metadata, byte instances are serialized to an ext with id 0x7E.
+#
+# ~~~
+# assert 0x01u8.serialize_msgpack == b"\xD4\x7E\x01"
+# assert b"\xD4\x7E\x01".deserialize_msgpack == 0x01u8
+# ~~~
+#
+# ## Full objects
+#
+# Objects are serialized to a map in plain mode, replacing cycles by `null` values.
+# This creates plain MessagePack easy to read for other non-Nit programs,
+# but cycles and the dynamic type information are lost.
+#
+# ~~~
+# class A
+# serialize
+#
+# var i = 1
+# var o: nullable A = self
+#
+# redef fun ==(o) do return o isa A and o.i == i # Skip the cyclic `o`
+# end
+#
+# var a = new A
+# var bytes = a.serialize_msgpack(plain=true)
+# assert bytes == b"\x82\xA1\x69\x01\xA1\x6F\xC0"
+# assert bytes.deserialize_msgpack isa Map[nullable Serializable, nullable Serializable] # Not an A
+# ~~~
+#
+# Or, with metadata, the same object is serialized with information on object
+# uniqueness (with an id and references) and its dynamic type.
+# The whole object is contained in a MessagePack array:
+#
+# * The array holds the metadata and attributes or each object,
+# here it is a fixarray of 3 items: 0x93
+# * Define an object (ext type 0x7B) with the id 0, here a fixext1: 0xD47B00
+# * The dynamic type name, here a fixstr with the letter 'A': 0xA141
+# * The attributes as a map, here a fixmap of 2 items: 0x82
+# * First attribute name, here a fixstr for "i": 0xA169
+# * First attribute value, here a fixint for 1: 0x01
+# * Second attribute name, here a fixstr for "o": 0xA16F
+# * Second attribute value, a reference (ext type 0x7D) to object id 0,
+# here a fixext1: 0xD47D00
+#
+# ~~~
+# bytes = a.serialize_msgpack
+# assert bytes == b"\x93\xD4\x7B\x00\xA1\x41\x82\xA1\x69\x01\xA1\x6F\xD4\x7D\x00"
+# assert bytes.deserialize_msgpack == a
+# ~~~
+#
+# ## References
+#
+# Format description and other implementations: http://msgpack.org/
+#
+# Format specification: https://github.com/msgpack/msgpack/blob/master/spec.md
+module msgpack
+
+import serialization_write
+import serialization_read
--- /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.
+
+# Convert MessagePack format to JSON
+module msgpack_to_json
+
+import msgpack::read
+import json
+
+if args.has("-h") then
+ print "Usage: nit msgpack::msgpack_to_json [source_file.msgpack]"
+ print "Convert MessagePack format to JSON. Read from stdin if no source_file is given."
+ exit 0
+end
+
+var reader = if args.length >= 1 then
+ new FileReader.open(args.first)
+ else stdin
+
+while reader.last_error == null and not reader.eof do
+ var deserialized = reader.read_msgpack
+
+ if deserialized != null then
+ print deserialized.serialize_to_json(plain=true, pretty=true)
+ else
+ print "null"
+ end
+end
--- /dev/null
+[package]
+name=msgpack
+tags=format,lib
+maintainer=Alexis Laferrière <alexis.laf@xymus.net>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/msgpack/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/msgpack/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
--- /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.
+
+# Low-level read MessagePack format from `Reader` streams
+module read
+
+import serialization
+private import binary
+
+import ext
+
+redef class Reader
+
+ # Read the next MessagePack object and return it as a simple Nit object
+ #
+ # The return value is composed of:
+ # * the simple types `null`, `Bool`, `Int`, `Float`, `String` and `Bytes`,
+ # * collections of simple Nit objects `Array[nullable Serializable]`
+ # and `Map[nullable Serializable, nullable Serializable]`,
+ # * and `MsgPackExt` for custom MessagePack *ext* data.
+ #
+ # This method reads plain MessagePack data, as written by `MsgPackSerializer`
+ # when `plain_msgpack == true`. To deserialize full Nit objects from
+ # MessagePack with metadata use `Reader::deserialize_msgpack`.
+ fun read_msgpack: nullable Serializable
+ do
+ if last_error != null then return 0
+
+ var typ = read_byte
+ if typ == null then
+ # Error, return default `null`
+ return null
+
+ else if typ & 0b1000_0000u8 == 0u8 or typ & 0b1110_0000u8 == 0b1110_0000u8 then
+ # fixint
+ var bytes = new Bytes.with_capacity(1)
+ bytes.add typ
+ return bytes.to_i(signed=true)
+
+ else if typ & 0b1111_0000u8 == 0b1000_0000u8 then
+ # fixmap
+ var len = typ & 0b0000_1111u8
+ return read_msgpack_map_data(len.to_i)
+
+ else if typ & 0b1111_0000u8 == 0b1001_0000u8 then
+ # fixarray
+ var len = typ & 0b0000_1111u8
+ return read_msgpack_array_data(len.to_i)
+
+ else if typ & 0b1110_0000u8 == 0b1010_0000u8 then
+ # fixstr
+ var len = typ & 0b0001_1111u8
+ return read_bytes(len.to_i).to_s
+
+ else if typ == 0xC0u8 then
+ return null
+ else if typ == 0xC2u8 then
+ return false
+ else if typ == 0xC3u8 then
+ return true
+
+ else if typ == 0xCCu8 then
+ # uint8
+ return (read_byte or else 0u8).to_i
+ else if typ == 0xCDu8 then
+ # uint16
+ return read_bytes(2).to_i
+ else if typ == 0xCEu8 then
+ # uint32
+ return read_bytes(4).to_i
+ else if typ == 0xCFu8 then
+ # uint64
+ return read_bytes(8).to_i
+ else if typ == 0xD0u8 then
+ # int8
+ return read_bytes(1).to_i(true)
+ else if typ == 0xD1u8 then
+ # int16
+ return read_bytes(2).to_i(true)
+ else if typ == 0xD2u8 then
+ # int32
+ return read_bytes(4).to_i(true)
+ else if typ == 0xD3u8 then
+ # int64
+ return read_int64
+
+ else if typ == 0xCAu8 then
+ return read_float
+ else if typ == 0xCBu8 then
+ return read_double
+
+ else if typ == 0xD9u8 then
+ # str8
+ var len = read_byte
+ if len == null then return null
+ return read_bytes(len.to_i).to_s
+ else if typ == 0xDAu8 then
+ # str16
+ var len = read_bytes(2)
+ return read_bytes(len.to_i).to_s
+ else if typ == 0xDBu8 then
+ # str32
+ var len = read_bytes(4)
+ return read_bytes(len.to_i).to_s
+
+ else if typ == 0xC4u8 then
+ # bin8
+ var len = read_byte
+ if len == null then return null
+ return read_bytes(len.to_i)
+ else if typ == 0xC5u8 then
+ # bin16
+ var len = read_bytes(2)
+ return read_bytes(len.to_i)
+ else if typ == 0xC6u8 then
+ # bin32
+ var len = read_bytes(4)
+ return read_bytes(len.to_i)
+
+ else if typ == 0xDCu8 then
+ # array16
+ var len = read_bytes(2)
+ return read_msgpack_array_data(len.to_i)
+ else if typ == 0xDDu8 then
+ # array32
+ var len = read_bytes(4)
+ return read_msgpack_array_data(len.to_i)
+
+ else if typ == 0xDEu8 then
+ # map16
+ var len = read_bytes(2)
+ return read_msgpack_map_data(len.to_i)
+ else if typ == 0xDFu8 then
+ # map32
+ var len = read_bytes(4)
+ return read_msgpack_map_data(len.to_i)
+
+ else if typ == 0xD4u8 then
+ # fixext1
+ return read_msgpack_fixext_data(1)
+ else if typ == 0xD5u8 then
+ # fixext2
+ return read_msgpack_fixext_data(2)
+ else if typ == 0xD6u8 then
+ # fixext4
+ return read_msgpack_fixext_data(4)
+ else if typ == 0xD7u8 then
+ # fixext8
+ return read_msgpack_fixext_data(8)
+ else if typ == 0xD8u8 then
+ # fixext16
+ return read_msgpack_fixext_data(16)
+
+ else if typ == 0xC7u8 then
+ # ext1
+ return read_msgpack_ext_data(1)
+ else if typ == 0xC8u8 then
+ # ext2
+ return read_msgpack_ext_data(2)
+ else if typ == 0xC9u8 then
+ # ext4
+ return read_msgpack_ext_data(4)
+ end
+
+ print_error "MessagePack Warning: Found no match for typ {typ} / 0b{typ.to_i.to_base(2)}"
+ return null
+ end
+
+ # Read the content of a map, `len` keys and values
+ private fun read_msgpack_map_data(len: Int): Map[nullable Serializable, nullable Serializable]
+ do
+ var map = new Map[nullable Serializable, nullable Serializable]
+ for i in [0..len.to_i[ do map[read_msgpack] = read_msgpack
+ return map
+ end
+
+ # Read the content of an array of `len` items
+ private fun read_msgpack_array_data(len: Int): Array[nullable Serializable]
+ do
+ return [for i in [0..len[ do read_msgpack]
+ end
+
+ # Read the content of a *fixext* of `len` bytes
+ #
+ # ~~~
+ # var reader = new BytesReader(b"\xC7\x03\x0A\x0B\x0C\x0D")
+ # var ext = reader.read_msgpack
+ # assert ext isa MsgPackExt
+ # assert ext.typ == 0x0Au8
+ # assert ext.data == b"\x0B\x0C\x0D"
+ # ~~~
+ private fun read_msgpack_fixext_data(len: Int): MsgPackExt
+ do
+ var exttyp = read_byte or else 0u8
+ var data = read_bytes(len)
+ return new MsgPackExt(exttyp, data)
+ end
+
+ # Read the content of a dynamic *ext* including the length on `len_len` bytes
+ private fun read_msgpack_ext_data(len_len: Int): MsgPackExt
+ do
+ var len = read_bytes(len_len).to_i
+ return read_msgpack_fixext_data(len)
+ end
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Serialization services for `serialization_write` and `serialization_read`
+module serialization_common
+
+# MessagePack serialization or deserialization engine
+abstract class MsgPackEngine
+
+ # *ext type* byte for object definitions, defaults to 0x7Bu8 or '{'
+ var ext_typ_obj: Byte = 0x7Bu8 is writable
+
+ # *ext type* byte for object references, defaults to 0x7Du8 or '}'
+ var ext_typ_ref: Byte = 0x7Du8 is writable
+
+ # *ext type* byte to identify a char, defaults to 0x7Cu8 or '~'
+ var ext_typ_char: Byte = 0x7Cu8 is writable
+
+ # *ext type* byte to identify a byte, defaults to 0x7Eu8 or '|'
+ var ext_typ_byte: Byte = 0x7Eu8 is writable
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Deserialize full Nit objects from MessagePack format
+#
+# See the package `msgpack` for more details on the serialization
+# of Nit objects.
+module serialization_read
+
+import serialization::caching
+import serialization::safe
+private import json # for class_inheritance_metamodel
+private import serialization::engine_tools
+
+import serialization_common
+private import read
+import ext
+
+# ---
+# Easy services
+
+redef class Bytes
+
+ # Deserialize full Nit `nullable Object` from MessagePack formated data
+ #
+ # The dynamic type of the deserialized object can be limited to `static_type`.
+ #
+ # Warning: Deserialization errors are reported with `print_error`,
+ # the returned object may be partial or fall back on `null`.
+ # To handle the errors programmatically, use a `MsgPackDeserializer`.
+ fun deserialize_msgpack(static_type: nullable String): nullable Object
+ do
+ var stream = new BytesReader(self)
+ var res = stream.deserialize_msgpack(static_type)
+ stream.close
+ return res
+ end
+end
+
+redef class Reader
+
+ # Deserialize full Nit `nullable Object` from MessagePack formated data
+ #
+ # This method use metadata in the MessagePack source to recreate full
+ # Nit objects serialized by `Writer::serialize_msgpack` or
+ # `MsgPackSerializer`.
+ #
+ # The dynamic type of the deserialized object can be limited to `static_type`.
+ #
+ # Warning: Deserialization errors are reported with `print_error`,
+ # the returned object may be partial or fall back on `null`.
+ # To handle the errors programmatically, use a `MsgPackDeserializer`.
+ fun deserialize_msgpack(static_type: nullable String): nullable Object
+ do
+ var deserializer = new MsgPackDeserializer(self)
+ var res = deserializer.deserialize(static_type)
+
+ if deserializer.errors.length == 1 then
+ print_error deserializer.errors.join("")
+ else if deserializer.errors.not_empty then
+ print_error "Deserialization Errors:\n* {deserializer.errors.join("\n* ")}"
+ end
+
+ return res
+ end
+end
+
+# ---
+# Engine
+
+# Deserialize MessagePack format to full Nit objects
+class MsgPackDeserializer
+ super CachingDeserializer
+ super MsgPackEngine
+ super SafeDeserializer
+
+ # Source stream
+ var stream: Reader
+
+ # Map of attributes from the root deserialized object to the current object
+ private var path = new Array[Map[nullable Serializable, nullable Serializable]]
+
+ # Metadata arrays with from the root deserialized object to the current object
+ var path_arrays = new Array[nullable Array[nullable Object]]
+
+ # Names of the attributes from the root to the object currently being deserialized
+ var attributes_path = new Array[String]
+
+ # Last encountered object reference id.
+ #
+ # See `id_to_object`.
+ private var just_opened_id: nullable Int = null
+
+ redef fun deserialize_attribute(name, static_type)
+ do
+ if path.is_empty then
+ # The was a parsing error or the root is not an object
+ deserialize_attribute_missing = false
+ return null
+ end
+
+ var current = path.last
+
+ var serialized_value = null
+ var serialized_value_found = false
+ if current.keys.has(name) then
+ # Non-cached string
+ serialized_value = current[name]
+ serialized_value_found = true
+ else
+ # It may be cached, deserialize all keys until we find it
+ for key in current.keys.to_a do
+ if key isa Array[nullable Serializable] or key isa MsgPackExt then
+ var str = convert_object(key, "String")
+ if str isa String then
+ var value = current[key]
+ current.keys.remove key
+ current[str] = value
+
+ if str == name then
+ serialized_value = value
+ serialized_value_found = true
+ break
+ end
+ end
+ end
+ end
+ end
+
+ if not serialized_value_found then
+ # Let the generated code / caller of `deserialize_attribute` raise the missing attribute error
+ deserialize_attribute_missing = true
+ return null
+ end
+
+ attributes_path.add name
+ var res = convert_object(serialized_value, static_type)
+ attributes_path.pop
+
+ deserialize_attribute_missing = false
+ return res
+ end
+
+ # This may be called multiple times by the same object from defs of a same constructor
+ redef fun notify_of_creation(new_object)
+ do
+ var id = just_opened_id
+ if id == null then return
+ cache[id] = new_object
+ end
+
+ # Convert the simple JSON `object` to a Nit object
+ private fun convert_object(object: nullable Object, static_type: nullable String): nullable Object
+ do
+ #print "convert_object {if object != null then object.class_name else "null"}"
+ if object isa Array[nullable Object] and object.length >= 1 then
+ # Serialized object?
+ var first = object.first
+ if first isa MsgPackExt then
+ if first.typ == ext_typ_obj then
+ # An array starts with a *ext*, it must be a serialized object
+
+ # New object declaration
+ var id = first.data.to_i
+
+ if cache.has_id(id) then
+ # FIXME use Warning
+ errors.add new Error("Deserialization Error: object with id {id} is deserialized twice.")
+ # Keep going
+ end
+
+ var type_name = null
+ var i = 1
+
+ # Read dynamic type
+ if object.length >= 2 then
+
+ # Try to get the type name as a string
+ var o = object[i]
+ if o isa String and static_type == "String" and object.length == 2 then
+ cache[id] = o
+ return o
+ else
+ var typ = convert_object(object[i], "String")
+ if typ isa String then
+ type_name = typ
+ i += 1
+ end
+ end
+ end
+
+ if type_name == null then
+ # There was no dynamic type
+
+ # We could use a `class_name_heuristic` here...
+
+ # Fallback to the static type
+ if static_type != null then
+ type_name = static_type.strip_nullable
+ end
+
+ if type_name == null then
+ errors.add new Error("Deserialization Error: could not determine dynamic type of `{object}`.")
+ return null
+ end
+ end
+
+ if not accept(type_name, static_type) then return null
+
+ var attributes = null
+ if object.length > i then attributes = object[i]
+ if not attributes isa Map[nullable Serializable, nullable Serializable] then
+ # Some other type (could be an error), or there's no attributes
+ attributes = new Map[nullable Serializable, nullable Serializable]
+ end
+
+ # advance on path
+ path.push attributes
+ path_arrays.push object
+
+ just_opened_id = id
+ var value = deserialize_class(type_name)
+ just_opened_id = null
+
+ # revert on path
+ path.pop
+ path_arrays.pop
+
+ return value
+ else
+ errors.add new Error("Deserialization Error: unknown MessagePack ext '{first.typ}'.")
+ end
+ end
+
+ # Plain array? Try to convert it to the desired static_type
+ if static_type != null then
+ return deserialize_class(static_type.strip_nullable)
+ end
+ return object
+ end
+
+ if object isa Map[nullable Serializable, nullable Serializable] then
+ # Plain map
+ # TODO parse it as an instance of `static_type`
+
+ if static_type != null then
+ path.push object
+ path_arrays.push null
+
+ just_opened_id = null
+ var value = deserialize_class(static_type.strip_nullable)
+
+ path.pop
+ path_arrays.pop
+
+ return value
+ end
+
+ return object
+ end
+
+ if object isa MsgPackExt then
+
+ # First try the custom extensions
+ var custom = deserialize_ext(object, static_type)
+ if custom == null then
+
+ # No custom, go for deser standard references
+ if object.typ == ext_typ_ref then
+ # Reference to an object
+ var id = object.data.to_i
+ if not cache.has_id(id) then
+ errors.add new Error("Deserialization Error: object reference id unknown.")
+ return object
+ end
+ return cache.object_for(id)
+
+ else if object.typ == ext_typ_char then
+ # Char
+ return object.data.to_s.first
+
+ else if object.typ == ext_typ_byte then
+ # Byte
+ return object.data.first
+ end
+ end
+ end
+
+ if object isa String and object.length == 1 and static_type == "Char" then
+ # Char serialized as a string
+ return object.chars.first
+ end
+
+ if object isa Int and static_type == "Byte" then
+ # Byte serialized as an integer
+ return object.to_b
+ end
+
+ return object
+ end
+
+ redef fun deserialize(static_type)
+ do
+ errors.clear
+
+ var root = stream.read_msgpack
+ return convert_object(root, static_type)
+ end
+
+ # Hook to customize the deserialization of MessagePack extensions
+ #
+ # Redefine this method in subclasses to return custom Nit objects from
+ # an application specific extension.
+ #
+ # This method is invoked before dealing with the extensions used by the
+ # Nit serialization metadata [0x40..0x43]. In general, you should ignore
+ # them by returning `null`, but they can also be intercepted to comply to
+ # a format from a remote server.
+ protected fun deserialize_ext(ext: MsgPackExt, static_type: nullable String): nullable Object
+ do
+ return null
+ end
+end
+
+redef class SimpleCollection[E]
+ redef init from_deserializer(v)
+ do
+ super
+ if v isa MsgPackDeserializer then
+ v.notify_of_creation self
+ init
+
+ var open_array = v.path_arrays.last
+ var msgpack_items = null
+ if open_array != null then msgpack_items = open_array.last
+
+ if not msgpack_items isa Array[nullable Serializable] then
+ v.errors.add new Error("Deserialization Error: no items in source of `{class_name}`")
+ return
+ end
+
+ # Name of the dynamic name of E
+ var items_type_name = (new GetName[E]).to_s
+
+ # Fill array
+ for o in msgpack_items do
+ var obj = v.convert_object(o, items_type_name)
+ if obj isa E then
+ add obj
+ else v.errors.add new AttributeTypeError(self, "items", obj, items_type_name)
+ end
+ end
+ end
+end
+
+redef class Map[K, V]
+ redef init from_deserializer(v)
+ do
+ super
+
+ if v isa MsgPackDeserializer then
+ v.notify_of_creation self
+ init
+
+ var open_object = v.path_arrays.last
+ var msgpack_items
+ if open_object != null then
+ # Metadata available
+ msgpack_items = open_object.last
+ else
+ msgpack_items = v.path.last
+ end
+
+ if not msgpack_items isa Map[nullable Object, nullable Object] then
+ v.errors.add new Error("Deserialization Error: no key/values in source of `{class_name}`")
+ return
+ end
+
+ var keys_type_name = (new GetName[K]).to_s
+ var values_type_name = (new GetName[V]).to_s
+
+ for key_src, value_src in msgpack_items do
+ var key = v.convert_object(key_src, keys_type_name)
+ if not key isa K then
+ v.errors.add new AttributeTypeError(self, "keys", key, keys_type_name)
+ continue
+ end
+
+ var value = v.convert_object(value_src, values_type_name)
+ if not value isa V then
+ v.errors.add new AttributeTypeError(self, "values", value, values_type_name)
+ continue
+ end
+
+ self[key] = value
+ end
+ end
+ end
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Serialize full Nit objects to MessagePack format
+#
+# There are 3 main entrypoint services:
+# * `Writer::serialize_msgpack` adds an object to any stream writer.
+# * `Serializable::serialize_msgpack` serializes the object to bytes.
+# * `MsgPackSerializer` gives full control over the serialization of
+# Nit objets to the MessagePack format.
+module serialization_write
+
+import serialization::caching
+private import serialization::engine_tools
+
+import serialization_common
+private import write
+import ext
+
+# MessagePack deserialization engine
+class MsgPackSerializer
+ super CachingSerializer
+ super MsgPackEngine
+
+ # Target writing stream
+ var stream: Writer
+
+ # Write plain MessagePack without metadata for deserialization?
+ #
+ # If `false`, the default, serialize to support deserialization:
+ #
+ # * Each object is encapsulated in an array that contains metadata and
+ # the actual object attributes in a map. The metadata includes the type
+ # name and references to already serialized object. This information
+ # supports deserializing the message, including cycles.
+ # * Preserve the Nit `Char` and `Byte` types as an object.
+ # * The generated MessagePack is standard and can be read by non-Nit programs.
+ # However, it contains some complexity that may make it harder to use.
+ #
+ # If `true`, serialize only the real data or non-Nit programs:
+ #
+ # * Nit objects are serialized to pure and standard MessagePack so they can
+ # be easily read by non-Nit programs.
+ # * Nit objects are serialized at every reference, so they may be duplicated.
+ # It is easier to read but it creates a larger output and it does not support
+ # cycles. Cyclic references are replaced by `null`.
+ # * The serialized data can only be deserialized to their expected static
+ # types, losing the knowledge of their dynamic type.
+ var plain_msgpack = false is writable
+
+ # Should strings declaring the objects type and attributes name be cached?
+ #
+ # If `true` metadata strings are cached using `cache`.
+ # The first occurrence is written as an object declaration,
+ # successive occurrences are written as an object reference.
+ #
+ # If `false`, the default, metadata strings are written as pure MessagePack
+ # strings, without their own metadata.
+ #
+ # Using the cache may save some space by avoiding the repetition of
+ # names used by many types or attributes.
+ # However, it adds complexity to the generated message and may be less
+ # safe for versioning.
+ var cache_metadata_strings = false is writable
+
+ # List of the current open objects, the first is the main target of the serialization
+ #
+ # Used only when `plain_msgpack == true` to detect cycles in serialization.
+ private var open_objects = new Array[Object]
+
+ redef var current_object = null
+
+ redef fun serialize(object)
+ do
+ if object == null then
+ stream.write_msgpack_null
+ else
+ if plain_msgpack then
+ for o in open_objects do
+ if object.is_same_serialized(o) then
+ # Cycle, can't be managed in plain_msgpack mode
+ warn "Cycle detected in serialized object, replacing reference with 'null'."
+ stream.write_msgpack_null
+ return
+ end
+ end
+
+ open_objects.add object
+ end
+
+ var last_object = current_object
+ current_object = object
+ object.accept_msgpack_serializer self
+ current_object = last_object
+
+ if plain_msgpack then open_objects.pop
+ end
+ end
+
+ redef fun serialize_attribute(name, value)
+ do
+ serialize_meta_string name
+ super
+ end
+
+ redef fun serialize_reference(object)
+ do
+ if not plain_msgpack and cache.has_object(object) then
+ # if already serialized, add local reference
+ var id = cache.id_for(object)
+ stream.write_msgpack_ext(ext_typ_ref, id.to_bytes)
+ else
+ # serialize
+ serialize object
+ end
+ end
+
+ private fun serialize_meta_string(type_name: String)
+ do
+ if plain_msgpack or not cache_metadata_strings then
+ # String only version
+ stream.write_msgpack_str type_name
+ return
+ end
+
+ if cache.has_object(type_name) then
+ # if already serialized, add reference
+ var id = cache.id_for(type_name)
+ stream.write_msgpack_ext(ext_typ_ref, id.to_bytes)
+ else
+ # serialize
+ var id = cache.new_id_for(type_name)
+ stream.write_msgpack_array 2 # obj+id, type_name
+ stream.write_msgpack_ext(ext_typ_obj, id.to_bytes)
+ stream.write_msgpack_str type_name
+ end
+ end
+end
+
+# Serialization visitor to count attribute in `Serializable` objects
+class AttributeCounter
+ super Serializer
+
+ # Number of attributes counted
+ var count = 0
+
+ redef fun serialize_attribute(name, object) do count += 1
+end
+
+# ---
+# Services and serializables
+
+redef class Writer
+ # Serialize `value` in MessagePack format
+ fun serialize_msgpack(value: nullable Serializable, plain: nullable Bool)
+ do
+ var serializer = new MsgPackSerializer(self)
+ serializer.plain_msgpack = plain or else false
+ serializer.serialize value
+ end
+end
+
+redef class Serializable
+
+ # Serialize `self` to MessagePack bytes
+ #
+ # Set `plain = true` to generate standard MessagePack, without deserialization metadata.
+ # Use this option if the generated MessagePack will be read by non-Nit programs.
+ # Use the default, `plain = false`, if the MessagePack bytes are to be deserialized by a Nit program.
+ fun serialize_msgpack(plain: nullable Bool): Bytes
+ do
+ var stream = new BytesWriter
+ stream.serialize_msgpack(self, plain)
+ stream.close
+ return stream.bytes
+ end
+
+ # Hook to customize the serialization of this class to MessagePack
+ #
+ # This method can be refined to customize the serialization by either
+ # writing pure JSON directly on the stream `v.stream` or
+ # by using other services of `MsgPackSerializer`.
+ #
+ # Most of the time, it is better to refine the method `core_serialize_to`
+ # which is used by all the serialization engines, not just MessagePack.
+ protected fun accept_msgpack_serializer(v: MsgPackSerializer)
+ do
+
+ # Count the number of attributes
+ var attribute_counter = new AttributeCounter
+ accept_msgpack_attribute_counter attribute_counter
+ var n_attributes = attribute_counter.count
+
+ if not v.plain_msgpack then
+
+ var n_meta_items = 2
+ if n_attributes > 0 then n_meta_items += 1
+ n_meta_items += msgpack_extra_array_items # obj+id, class_name, attributes
+
+ # Metadata
+ var id = v.cache.new_id_for(self)
+ v.stream.write_msgpack_array n_meta_items
+ v.stream.write_msgpack_ext(v.ext_typ_obj, id.to_bytes)
+ v.serialize_meta_string class_name
+
+ if n_attributes > 0 then v.stream.write_msgpack_map n_attributes
+ else
+ v.stream.write_msgpack_map n_attributes
+ end
+
+ v.serialize_core self
+ end
+
+ # Hook to customize the behavior of the `AttributeCounter`
+ #
+ # By default, this method makes `v` visits all serializable attributes.
+ protected fun accept_msgpack_attribute_counter(v: AttributeCounter)
+ do
+ v.serialize_core self
+ end
+
+ # Hook to request a larger than usual metadata array
+ #
+ # Use by `SimpleCollection` and `Map` to append the items after
+ # the metadata and attributes.
+ protected fun msgpack_extra_array_items: Int do return 0
+end
+
+redef class MsgPackExt
+ redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_ext(typ, data)
+end
+
+redef class Text
+ redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_str self
+end
+
+redef class Int
+ redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_int self
+end
+
+redef class Float
+ redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_double self
+end
+
+redef class Bool
+ redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_bool self
+end
+
+redef class Byte
+ redef fun accept_msgpack_serializer(v)
+ do
+ if v.plain_msgpack then
+ # Write as a string
+ v.stream.write_msgpack_int to_i
+ else
+ # Write as ext
+ var bytes = new Bytes.with_capacity(1)
+ bytes.add self
+ v.stream.write_msgpack_ext(v.ext_typ_byte, bytes)
+ end
+ end
+end
+
+redef class Char
+ redef fun accept_msgpack_serializer(v)
+ do
+ if v.plain_msgpack then
+ # Write as a string
+ v.stream.write_msgpack_fixstr to_s
+ else
+ # Write as ext
+ var bytes = to_s.to_bytes
+ v.stream.write_msgpack_ext(v.ext_typ_char, bytes)
+ end
+ end
+end
+
+redef class Bytes
+ redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_bin self
+end
+
+redef class CString
+ redef fun accept_msgpack_serializer(v) do to_s.accept_msgpack_serializer(v)
+end
+
+redef class SimpleCollection[E]
+ redef fun accept_msgpack_serializer(v)
+ do
+ if not v.plain_msgpack then
+ # Add metadata and other attributes
+ super
+ end
+
+ # Header
+ v.stream.write_msgpack_array length
+
+ # Items
+ for e in self do
+ if not v.try_to_serialize(e) then
+ assert e != null # null would have been serialized
+ v.warn "element of type {e.class_name} is not serializable."
+ v.stream.write_msgpack_null
+ end
+ end
+ end
+
+ redef fun msgpack_extra_array_items do return 1
+end
+
+redef class Map[K, V]
+ redef fun accept_msgpack_serializer(v)
+ do
+ if not v.plain_msgpack then
+ # Add metadata and other attributes
+ super
+ end
+
+ # Header
+ v.stream.write_msgpack_map keys.length
+
+ # Key / values, alternating
+ for key, val in self do
+ if not v.try_to_serialize(key) then
+ assert val != null # null would have been serialized
+ v.warn "element of type {val.class_name} is not serializable."
+ v.stream.write_msgpack_null
+ end
+
+ if not v.try_to_serialize(val) then
+ assert val != null # null would have been serialized
+ v.warn "element of type {val.class_name} is not serializable."
+ v.stream.write_msgpack_null
+ end
+ end
+ end
+
+ redef fun msgpack_extra_array_items do return 1
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Low-level write in MessagePack format to `Writer` streams
+module write
+
+import binary
+
+redef class Writer
+
+ # Write `null`, or nil, in MessagePack format
+ fun write_msgpack_null do write_byte 0xC0u8
+
+ # Write `bool` in MessagePack format
+ fun write_msgpack_bool(bool: Bool)
+ do write_byte(if bool then 0xC3u8 else 0xC2u8)
+
+ # ---
+ # Integers
+
+ # Write the integer `value` either as the shortest possible MessagePack _int_
+ fun write_msgpack_int(value: Int)
+ do
+ if value >= -0x20 and value <= 0x7F then
+ write_msgpack_fixint value
+ else if value >= 0 then
+ if value <= 0xFF then
+ write_msgpack_uint8 value
+ else if value <= 0xFFFF then
+ write_msgpack_uint16 value
+ else if value <= 0xFFFF_FFFF then
+ write_msgpack_uint32 value
+ else #if value <= 0xFFFF_FFFF_FFFF_FFFF then
+ write_msgpack_uint64 value
+ end
+ else if value >= -128 then
+ write_msgpack_int8 value
+ else if value >= -32768 then
+ write_msgpack_int16 value
+ else if value >= -2147483648 then
+ write_msgpack_int32 value
+ else
+ write_msgpack_int64 value
+ end
+ end
+
+ # Write `value` as a single byte with metadata
+ #
+ # Require: `value >= -0x20 and value <= 0x7F`
+ fun write_msgpack_fixint(value: Int)
+ do
+ assert value >= -0x20 and value <= 0x7F
+ write_byte value.to_b
+ end
+
+ # Write `value` over one unsigned byte, following 1 metadata byte
+ #
+ # Require: `value >= 0x00 and value <= 0xFF`
+ fun write_msgpack_uint8(value: Int)
+ do
+ write_byte 0xCCu8
+ write_bytes value.to_bytes(n_bytes=1)
+ end
+
+ # Write `value` over two unsigned bytes, following 1 metadata byte
+ #
+ # Require: `value >= 0x00 and value <= 0xFFFF`
+ fun write_msgpack_uint16(value: Int)
+ do
+ write_byte 0xCDu8
+ write_bytes value.to_bytes(n_bytes=2)
+ end
+
+ # Write `value` over 4 unsigned bytes, following 1 metadata byte
+ #
+ # Require: `value >= 0x00 and value <= 0xFFFF_FFFF`
+ fun write_msgpack_uint32(value: Int)
+ do
+ write_byte 0xCEu8
+ write_bytes value.to_bytes(n_bytes=4)
+ end
+
+ # Write `value` over 8 unsigned bytes, following 1 metadata byte
+ #
+ # Require: `value >= 0x00 and value <= 0xFFFF_FFFF_FFFF_FFFF`
+ fun write_msgpack_uint64(value: Int)
+ do
+ write_byte 0xCFu8
+ write_bytes value.to_bytes(n_bytes=8)
+ end
+
+ # Write `value` over one signed byte, following 1 metadata byte
+ #
+ # Require: `value >= -128 and value <= 127`
+ fun write_msgpack_int8(value: Int)
+ do
+ write_byte 0xD0u8
+ write_bytes value.to_bytes(n_bytes=1)
+ end
+
+ # Write `value` over two signed bytes, following 1 metadata byte
+ fun write_msgpack_int16(value: Int)
+ do
+ write_byte 0xD1u8
+ write_bytes value.to_bytes(n_bytes=2)
+ end
+
+ # Write `value` over 4 signed bytes, following 1 metadata byte
+ fun write_msgpack_int32(value: Int)
+ do
+ write_byte 0xD2u8
+ write_bytes value.to_bytes(n_bytes=4)
+ end
+
+ # Write `value` over 8 signed bytes, following 1 metadata byte
+ fun write_msgpack_int64(value: Int)
+ do
+ write_byte 0xD3u8
+ write_int64 value
+ end
+
+ # ---
+ # Floats
+
+ # Write `value` as a MessagePack float (losing precision)
+ fun write_msgpack_float(value: Float)
+ do
+ write_byte 0xCAu8
+ write_float value
+ end
+
+ # Write `value` as a MessagePack double
+ fun write_msgpack_double(value: Float)
+ do
+ write_byte 0xCBu8
+ write_double value
+ end
+
+ # ---
+ # Strings
+
+ # Write `text` in the shortest possible MessagePack format
+ #
+ # Require: `text.byte_length <= 0xFFFF_FFFF`
+ fun write_msgpack_str(text: Text)
+ do
+ var len = text.byte_length
+ if len <= 0x1F then
+ write_msgpack_fixstr text
+ else if len <= 0xFF then
+ write_msgpack_str8 text
+ else if len <= 0xFFFF then
+ write_msgpack_str16 text
+ else if len <= 0xFFFF_FFFF then
+ write_msgpack_str32 text
+ else
+ abort
+ end
+ end
+
+ # Write `text` in _fixstr_ format, max of 0x1F bytes
+ #
+ # Require: `text.byte_length <= 0x1F`
+ fun write_msgpack_fixstr(text: Text)
+ do
+ var len = text.byte_length
+ assert len <= 0x1F
+
+ var b = 0b1010_0000u8 | len.to_b
+ write_byte b
+
+ write text
+ end
+
+ # Write `text` in _str8_ format, max of 0xFF bytes
+ #
+ # Require: `text.byte_length <= 0xFF`
+ fun write_msgpack_str8(text: Text)
+ do
+ var len = text.byte_length
+ assert len <= 0xFF
+
+ write_byte 0xD9u8
+ write_byte len.to_b
+ write text
+ end
+
+ # Write `text` in _str16_ format, max of 0xFFFF bytes
+ #
+ # Require: `text.byte_length <= 0xFFFF`
+ fun write_msgpack_str16(text: Text)
+ do
+ var len = text.byte_length
+ assert len <= 0xFFFF
+
+ write_byte 0xDAu8
+ var len_bytes = len.to_bytes
+ write_byte len_bytes[0]
+ write_byte if len_bytes.length > 1 then len_bytes[1] else 0u8
+ write text
+ end
+
+ # Write `text` in _str32_ format, max of 0xFFFF_FFFF bytes
+ #
+ # Require: `text.byte_length <= 0xFFFF_FFFF`
+ fun write_msgpack_str32(text: Text)
+ do
+ var len = text.byte_length
+ assert len <= 0xFFFF_FFFF
+
+ write_byte 0xDBu8
+ var len_bytes = len.to_bytes
+ write_byte len_bytes[0]
+ for i in [1..4[ do
+ write_byte if len_bytes.length > i then len_bytes[i] else 0u8
+ end
+ write text
+ end
+
+ # ---
+ # Binary data
+
+ # Write `data` in the shortest possible MessagePack _bin_ format
+ #
+ # Require: `data.length <= 0xFFFF_FFFF`
+ fun write_msgpack_bin(data: Bytes)
+ do
+ var len = data.length
+ if len <= 0xFF then
+ write_msgpack_bin8 data
+ else if len <= 0xFFFF then
+ write_msgpack_bin16 data
+ else if len <= 0xFFFF_FFFF then
+ write_msgpack_bin32 data
+ else abort
+ end
+
+ # Write `data` in _bin8_ format, max of 0xFF bytes
+ #
+ # Require: `data.length <= 0xFF`
+ fun write_msgpack_bin8(data: Bytes)
+ do
+ var len = data.length
+ assert len <= 0xFF
+
+ write_byte 0xC4u8
+ write_byte len.to_b
+ write_bytes data
+ end
+
+ # Write `data` in _bin16_ format, max of 0xFFFF bytes
+ #
+ # Require: `data.length <= 0xFFFF`
+ fun write_msgpack_bin16(data: Bytes)
+ do
+ var len = data.length
+ assert len <= 0xFFFF
+
+ write_byte 0xC5u8
+ write_bytes len.to_bytes(n_bytes=2)
+ write_bytes data
+ end
+
+ # Write `data` in _bin32_ format, max of 0xFFFF_FFFF bytes
+ #
+ # Require: `data.length <= 0xFFFF_FFFF`
+ fun write_msgpack_bin32(data: Bytes)
+ do
+ var len = data.length
+ assert len <= 0xFFFF_FFFF
+
+ write_byte 0xC6u8
+ write_bytes len.to_bytes(n_bytes=4)
+ write_bytes data
+ end
+
+ # ---
+ # Arrays
+
+ # Write an array header for `len` items in the shortest possible MessagePack _array_ format
+ #
+ # After writing the header, clients should write the array items.
+ #
+ # Require: `len <= 0xFFFF_FFFF`
+ fun write_msgpack_array(len: Int)
+ do
+ if len <= 0x0F then
+ write_msgpack_fixarray len
+ else if len <= 0xFFFF then
+ write_msgpack_array16 len
+ else if len <= 0xFFFF_FFFF then
+ write_msgpack_array32 len
+ else
+ abort
+ end
+ end
+
+ # Write an array header for `len` items, max of 0x0F items
+ #
+ # After writing the header, clients should write the array items.
+ #
+ # Require: `len <= 0x0F`
+ fun write_msgpack_fixarray(len: Int)
+ do
+ assert len <= 0x0F
+ write_byte 0b1001_0000u8 | len.to_b
+ end
+
+ # Write an array header for `len` items, max of 0xFFFF items
+ #
+ # After writing the header, clients should write the array items.
+ #
+ # Require: `len <= 0xFFFF`
+ fun write_msgpack_array16(len: Int)
+ do
+ assert len <= 0xFFFF
+ write_byte 0xDCu8
+ write_bytes len.to_bytes(n_bytes=2)
+ end
+
+ # Write an array header for `len` items, max of 0xFFFF_FFFF items
+ #
+ # After writing the header, clients should write the array items.
+ #
+ # Require: `len <= 0xFFFF_FFFF`
+ fun write_msgpack_array32(len: Int)
+ do
+ assert len <= 0xFFFF_FFFF
+ write_byte 0xDDu8
+ write_bytes len.to_bytes(n_bytes=4)
+ end
+
+ # ---
+ # Map
+
+ # Write a map header for `len` keys/value pairs in the shortest possible MessagePack _map_ format
+ #
+ # After writing the header, clients should write the map data, alternating
+ # between keys and values.
+ #
+ # Require: `len <= 0xFFFF_FFFF`
+ fun write_msgpack_map(len: Int)
+ do
+ if len <= 0x0F then
+ write_msgpack_fixmap len
+ else if len <= 0xFFFF then
+ write_msgpack_map16 len
+ else if len <= 0xFFFF_FFFF then
+ write_msgpack_map32 len
+ else
+ abort
+ end
+ end
+
+ # Write a map header for `len` key/value pairs, max of 0x0F pairs
+ #
+ # After writing the header, clients should write the map data, alternating
+ # between keys and values.
+ #
+ # Require: `len <= 0x0F`
+ fun write_msgpack_fixmap(len: Int)
+ do
+ assert len <= 0x0F
+ write_byte 0b1000_0000u8 | len.to_b
+ end
+
+ # Write a map header for `len` key/value pairs, max of 0xFFFF pairs
+ #
+ # After writing the header, clients should write the map data, alternating
+ # between keys and values.
+ #
+ # Require: `len <= 0xFFFF`
+ fun write_msgpack_map16(len: Int)
+ do
+ assert len <= 0xFFFF
+ write_byte 0xDEu8
+ write_bytes len.to_bytes(n_bytes=2)
+ end
+
+ # Write a map header for `len` key/value pairs, max of 0xFFFF_FFFF pairs
+ #
+ # After writing the header, clients should write the map data, alternating
+ # between keys and values.
+ #
+ # Require: `len <= 0xFFFF_FFFF`
+ fun write_msgpack_map32(len: Int)
+ do
+ assert len <= 0xFFFF_FFFF
+ write_byte 0xDFu8
+ write_bytes len.to_bytes(n_bytes=4)
+ end
+
+ # ---
+ # Ext
+
+ # Write an application-specific extension for `typ` and `bytes` in the shortest possible MessagePack _ext_ format
+ #
+ # Require: `bytes.length <= 0xFFFF_FFFF`
+ #
+ # ~~~
+ # var writer = new BytesWriter
+ # writer.write_msgpack_ext(0x0Au8, b"\x0B\x0C\x0D")
+ # assert writer.bytes == b"\xC7\x03\x0A\x0B\x0C\x0D"
+ # ~~~
+ fun write_msgpack_ext(typ: Byte, bytes: Bytes)
+ do
+ var len = bytes.length
+ if len == 1 then
+ write_msgpack_fixext1 typ
+ write_byte bytes.first
+ else if len == 2 then
+ write_msgpack_fixext2 typ
+ write_bytes bytes
+ else if len == 4 then
+ write_msgpack_fixext4 typ
+ write_bytes bytes
+ else if len == 8 then
+ write_msgpack_fixext8 typ
+ write_bytes bytes
+ else if len == 16 then
+ write_msgpack_fixext16 typ
+ write_bytes bytes
+ else if len <= 0xFF then
+ write_msgpack_ext8(typ, len)
+ write_bytes bytes
+ else if len <= 0xFFFF then
+ write_msgpack_ext16(typ, len)
+ write_bytes bytes
+ else if len <= 0xFFFF_FFFF then
+ write_msgpack_ext32(typ, len)
+ write_bytes bytes
+ else
+ abort
+ end
+ end
+
+ # Write the header for an application-specific extension of one data byte
+ #
+ # After writing the header, clients should write the data byte.
+ fun write_msgpack_fixext1(typ: Byte)
+ do
+ write_byte 0xD4u8
+ write_byte typ
+ end
+
+ # Write the header for an application-specific extension of two data bytes
+ #
+ # After writing the header, clients should write the two data bytes.
+ fun write_msgpack_fixext2(typ: Byte)
+ do
+ write_byte 0xD5u8
+ write_byte typ
+ end
+
+ # Write the header for an application-specific extension of 4 data bytes
+ #
+ # After writing the header, clients should write the 4 data bytes.
+ fun write_msgpack_fixext4(typ: Byte)
+ do
+ write_byte 0xD6u8
+ write_byte typ
+ end
+
+ # Write the header for an application-specific extension of 8 data bytes
+ #
+ # After writing the header, clients should write the 8 data bytes.
+ fun write_msgpack_fixext8(typ: Byte)
+ do
+ write_byte 0xD7u8
+ write_byte typ
+ end
+
+ # Write the header for an application-specific extension of 16 data bytes
+ #
+ # After writing the header, clients should write the 16 data bytes.
+ fun write_msgpack_fixext16(typ: Byte)
+ do
+ write_byte 0xD8u8
+ write_byte typ
+ end
+
+ # Write the header for an application-specific extension of `len` data bytes
+ #
+ # After writing the header, clients should write the data bytes.
+ #
+ # Require: `len >= 0 and <= 0xFF`
+ fun write_msgpack_ext8(typ: Byte, len: Int)
+ do
+ assert len >= 0 and len <= 0xFF
+ write_byte 0xC7u8
+ write_byte len.to_b
+ write_byte typ
+ end
+
+ # Write the header for an application-specific extension of `len` data bytes
+ #
+ # After writing the header, clients should write the data bytes.
+ #
+ # Require: `len >= 0 and <= 0xFFFF`
+ fun write_msgpack_ext16(typ: Byte, len: Int)
+ do
+ assert len >= 0 and len <= 0xFFFF
+ write_byte 0xC8u8
+ write_bytes len.to_bytes(n_bytes=2)
+ write_byte typ
+ end
+
+ # Write the header for an application-specific extension of `len` data bytes
+ #
+ # After writing the header, clients should write the data bytes.
+ #
+ # Require: `len >= 0 and <= 0xFFFF_FFFF`
+ fun write_msgpack_ext32(typ: Byte, len: Int)
+ do
+ assert len >= 0 and len <= 0xFFFF_FFFF
+ write_byte 0xC9u8
+ write_bytes len.to_bytes(n_bytes=4)
+ write_byte typ
+ end
+
+ # TODO timestamps
+end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_angular is test_suite
+module test_example_angular is test
import pop_tests
import example_angular
class TestExampleAngular
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/counter"
system "curl -s {host}:{port}/not_found" # handled by angular controller
end
- fun test_example_angular do
+ fun test_example_angular is test do
var app = new App
app.use("/counter", new CounterAPI)
app.use("/*", new StaticHandler(test_path / "../www/", "index.html"))
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_post_handler is test_suite
+module test_example_post_handler is test
import pop_tests
import example_post_handler
class TestExamplePostHandler
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/ -X POST"
system "curl -s {host}:{port}/"
end
- fun test_example_post_handler do
+ fun test_example_post_handler is test do
var app = new App
app.use("/", new PostHandler)
run_test(app)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_query_string is test_suite
+module test_example_query_string is test
import pop_tests
import example_query_string
class TestExampleQueryString
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
system "curl -s {host}:{port}/?items=10\\&order=asc"
end
- fun test_example_query_string do
+ fun test_example_query_string is test do
var app = new App
app.use("/", new QueryStringHandler)
run_test(app)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_hello is test_suite
+module test_example_hello is test
import pop_tests
import example_hello
class TestExampleHello
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}"
system "curl -s {host}:{port}/not_found/not_found"
end
- fun test_example_hello do
+ fun test_example_hello is test do
var app = new App
app.use("/", new HelloHandler)
run_test(app)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_advanced_logger is test_suite
+module test_example_advanced_logger is test
import pop_tests
import example_advanced_logger
class TestExampleAdvancedLogger
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
system "curl -s {host}:{port}/about"
end
- fun test_example_advanced_logger do
+ fun test_example_advanced_logger is test do
var app = new App
app.use_before("/*", new RequestTimeHandler)
app.use("/", new AnotherHandler)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_html_error_handler is test_suite
+module test_example_html_error_handler is test
import pop_tests
import example_html_error_handler
class TestExampleHtmlErrorHandler
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
system "curl -s {host}:{port}/about"
end
- fun test_example_html_error_handler do
+ fun test_example_html_error_handler is test do
var app = new App
app.use("/*", new HtmlErrorHandler)
run_test(app)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_simple_error_handler is test_suite
+module test_example_simple_error_handler is test
import pop_tests
import example_simple_error_handler
class TestExampleSimpleErrorHandler
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
system "curl -s {host}:{port}/about"
end
- fun test_example_simple_error_handler do
+ fun test_example_simple_error_handler is test do
var app = new App
app.use("/", new SomeHandler)
app.use("/*", new SimpleErrorHandler)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_simple_logger is test_suite
+module test_example_simple_logger is test
import pop_tests
import example_simple_logger
class TestExampleSimpleLogger
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
system "curl -s {host}:{port}/about"
end
- fun test_example_simple_logger do
+ fun test_example_simple_logger is test do
var app = new App
app.use_before("/*", new SimpleLoggerHandler)
app.use("/", new MyOtherHandler)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_glob_route is test_suite
+module test_example_glob_route is test
import pop_tests
import example_glob_route
class TestExampleGlobRoute
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/user/Morriar/item/10"
system "curl -s {host}:{port}/not_found/not_found"
end
- fun test_example_glob_route do
+ fun test_example_glob_route is test do
var app = new App
app.use("/user/:user/item/:item/*", new UserItem)
run_test(app)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_param_route is test_suite
+module test_example_param_route is test
import pop_tests
import example_param_route
class TestExampleParamRoute
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/Morriar"
system "curl -s {host}:{port}/not_found/not_found"
end
- fun test_example_param_route do
+ fun test_example_param_route is test do
var app = new App
app.use("/:user", new UserHome)
run_test(app)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_router is test_suite
+module test_example_router is test
import pop_tests
import example_router
class TestExampleRouter
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}"
system "curl -s {host}:{port}/products/not_found"
end
- fun test_example_router do
+ fun test_example_router is test do
var user_router = new Router
user_router.use("/*", new UserLogger)
user_router.use("/", new UserHomepage)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_session is test_suite
+module test_example_session is test
import pop_tests
import example_session
class TestExampleSession
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
system "curl -s {host}:{port}/products/not_found"
end
- fun test_example_session do
+ fun test_example_session is test do
var app = new App
app.use("/*", new SessionInit)
app.use("/", new AppLogin)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_static is test_suite
+module test_example_static is test
import pop_tests
import example_static
class TestExampleStatic
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/css/style.css"
system "curl -s {host}:{port}/not_found.nit"
end
- fun test_example_static do
+ fun test_example_static is test do
var app = new App
app.use("/", new StaticHandler(test_path / "../public/"))
run_test(app)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_static_default is test_suite
+module test_example_static_default is test
import pop_tests
import example_static_default
class TestExampleStaticDefault
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/css/style.css"
system "curl -s {host}:{port}/not_found.nit"
end
- fun test_example_static_default do
+ fun test_example_static_default is test do
var app = new App
app.use("/", new StaticHandler(test_path / "../public/", "default.html"))
run_test(app)
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_static_multiple is test_suite
+module test_example_static_multiple is test
import pop_tests
import example_static_multiple
class TestExampleStaticMultiple
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/css/style.css"
system "curl -s {host}:{port}/not_found.nit"
end
- fun test_example_static_multiple do
+ fun test_example_static_multiple is test do
var app = new App
app.use("/", new StaticHandler(test_path / "../public/"))
app.use("/", new StaticHandler(test_path / "../files/"))
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_example_templates is test_suite
+module test_example_templates is test
import pop_tests
intrude import example_templates
class TestExampleTemplate
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
end
- fun test_example_template do
+ fun test_example_template is test do
var app = new App
app.use("/", new MyTemplateHandler)
run_test(app)
class TestExampleTemplateString
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
end
- fun test_example_template_string do
+ fun test_example_template_string is test do
var app = new App
app.use("/", new MyTemplateStringHandler)
run_test(app)
class TestExampleTemplateFile
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
end
- fun test_example_template_file do
+ fun test_example_template_file is test do
var app = new App
var handler = new MyTemplateFileHandler
handler.tpl_file = test_path / "../example_template.tpl"
class TestExampleTemplatePug
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
end
- fun test_example_template_pug do
+ fun test_example_template_pug is test do
var app = new App
app.use("/", new MyTemplatePugHandler)
run_test(app)
class TestExampleTemplatePugFile
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}/"
end
- fun test_example_template_pug_file do
+ fun test_example_template_pug_file is test do
var app = new App
var handler = new MyTemplatePugFileHandler
handler.pug_file = test_path / "../example_template.pug"
# Here we use `curl` to access some URI on the app.
#
# ~~~nitish
-# module test_example_hello is test_suite
+# module test_example_hello is test
#
# import pop_tests
# import example_hello
#
# class TestExampleHello
# super TestPopcorn
+# test
#
-# fun test_example_hello do
+# fun example_hello is test do
# var app = new App
# app.use("/", new HelloHandler)
# run_test(app)
# See `examples/hello_world` for the complete example.
module pop_tests
-import test_suite
import popcorn
import pthreads
# TestSuite for Popcorn blackbox testing.
class TestPopcorn
- super TestSuite
# Host used to run App.
var host: String = test_host
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_pop_routes is test_suite
+module test_pop_routes is test
import pop_routes
-import test_suite
class TestAppRoute
- super TestSuite
+ test
- fun test_root_match_only_one_uri do
+ fun test_root_match_only_one_uri is test do
var r = new AppRoute("/")
assert r.match("")
assert r.match("/")
assert not r.match("/user")
end
- fun test_strict_route_match_only_one_uri do
+ fun test_strict_route_match_only_one_uri is test do
var r = new AppRoute("/user")
assert not r.match("/")
assert r.match("/user")
end
class TestAppParamRoute
- super TestSuite
+ test
- fun test_param_route_match_good_uri_params_1 do
+ fun test_param_route_match_good_uri_params_1 is test do
var r = new AppParamRoute("/:id")
assert not r.match("/")
assert r.match("/user")
assert not r.match("/user/10")
end
- fun test_param_route_match_good_uri_params_2 do
+ fun test_param_route_match_good_uri_params_2 is test do
var r = new AppParamRoute("/user/:id")
assert not r.match("/")
assert not r.match("/user")
assert not r.match("/user/10/profile")
end
- fun test_param_route_match_good_uri_params_3 do
+ fun test_param_route_match_good_uri_params_3 is test do
var r = new AppParamRoute("/user/:id/profile")
assert not r.match("/")
assert not r.match("/user")
assert not r.match("/user/10/foo")
end
- fun test_param_route_match_good_uri_params_4 do
+ fun test_param_route_match_good_uri_params_4 is test do
var r = new AppParamRoute("/:id/:foo")
assert not r.match("/")
assert not r.match("/user")
assert not r.match("/user/10/10")
end
- fun test_param_route_match_good_uri_params_5 do
+ fun test_param_route_match_good_uri_params_5 is test do
var r = new AppParamRoute("/user/:id/:foo")
assert not r.match("/")
assert not r.match("/user")
assert not r.match("/user/10/10/profile")
end
- fun test_param_route_match_good_uri_params_6 do
+ fun test_param_route_match_good_uri_params_6 is test do
var r = new AppParamRoute("/user/:id/settings/:foo")
assert not r.match("/")
assert not r.match("/user")
end
class TestRouteMatching
- super TestSuite
+ test
- fun test_glob_route_match_good_uri_prefix1 do
+ fun test_glob_route_match_good_uri_prefix1 is test do
var r = new AppGlobRoute("/*")
assert r.match("/")
assert r.match("/user")
assert r.match("/user/10")
end
- fun test_glob_route_match_good_uri_prefix2 do
+ fun test_glob_route_match_good_uri_prefix2 is test do
var r = new AppGlobRoute("/user/*")
assert not r.match("/")
assert r.match("/user")
assert r.match("/user/10")
end
- fun test_glob_route_match_good_uri_prefix3 do
+ fun test_glob_route_match_good_uri_prefix3 is test do
var r = new AppGlobRoute("/user*")
assert not r.match("/")
assert r.match("/user")
assert r.match("/user/10")
end
- fun test_glob_route_work_with_parameters_1 do
+ fun test_glob_route_work_with_parameters_1 is test do
var r = new AppGlobRoute("/:id/*")
assert not r.match("/")
assert r.match("/user")
assert r.match("/user/10/profile")
end
- fun test_glob_route_work_with_parameters_2 do
+ fun test_glob_route_work_with_parameters_2 is test do
var r = new AppGlobRoute("/:id*")
assert not r.match("/")
assert r.match("/user")
assert r.match("/user/10")
end
- fun test_glob_route_work_with_parameters_3 do
+ fun test_glob_route_work_with_parameters_3 is test do
var r = new AppGlobRoute("/user/:id/*")
assert not r.match("/")
assert not r.match("/user")
assert r.match("/user/10/profile")
end
- fun test_glob_route_work_with_parameters_4 do
+ fun test_glob_route_work_with_parameters_4 is test do
var r = new AppGlobRoute("/user/:id*")
assert not r.match("/")
assert not r.match("/user")
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_popcorn is test_suite
+module test_popcorn is test
import pop_tests
import popcorn
class TestPopcornRouter
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}"
system "curl -s {host}:{port}/products/not_found"
end
- fun test_router do
+ fun test_router is test do
var app = new App
app.use("/", new TestHandler("/"))
app.use("/about", new TestHandler("/about"))
class TestPopcornRoutes
super TestPopcorn
+ test
redef fun client_test do
system "curl -s {host}:{port}"
system "curl -s {host}:{port}/user/id/not_found"
end
- fun test_routes do
+ fun test_routes is test do
var app = new App
app.use("/", new TestHandler("/"))
app.use("/user", new TestHandler("/user"))
# Pre order sets and partial order set (ie hierarchies)
module poset
-import serialization
+import serialization::serialization_core
# Pre-order set graph.
# This class models an incremental pre-order graph where new nodes and edges can be added (but not removed).
# another product.
# Test suite for `attributes_impl`.
-module test_attributes_impl is test_suite
+module test_attributes_impl is test
-import test_suite
import sax::helpers::attributes_impl
class TestAttributesImpl
- super TestSuite
+ test
private fun sample: AttributesImpl do
var subject = new AttributesImpl
return subject
end
- fun test_length do
+ fun test_length is test do
var subject = new AttributesImpl
assert 0 == subject.length
assert 0 == subject.length
end
- fun test_uri do
+ fun test_uri is test do
var subject = sample
assert "http://example.com/" == subject.uri(0)
assert subject.uri(0) == null
end
- fun test_local_name do
+ fun test_local_name is test do
var subject = sample
assert "bar" == subject.local_name(0)
assert subject.local_name(0) == null
end
- fun test_qname do
+ fun test_qname is test do
var subject = sample
assert "foo:bar" == subject.qname(0)
assert subject.qname(0) == null
end
- fun test_type_of do
+ fun test_type_of is test do
var subject = sample
assert "CDATA" == subject.type_of(0)
assert subject.type_of(0) == null
end
- fun test_type_of_qname do
+ fun test_type_of_qname is test do
var subject = sample
assert "CDATA" == subject.type_of("foo:bar")
assert subject.type_of("xml:lang") == null
end
- fun test_value_of do
+ fun test_value_of is test do
var subject = sample
assert "baz" == subject.value_of(0)
assert subject.value_of(0) == null
end
- fun test_value_of_qname do
+ fun test_value_of_qname is test do
var subject = sample
assert "baz" == subject.value_of("foo:bar")
assert subject.value_of("xml:lang") == null
end
- fun test_index_ns do
+ fun test_index_ns is test do
var subject = sample
assert 0 == subject.index_ns("http://example.com/", "bar")
assert -1 == subject.index_ns("http://example.com/", "bar")
end
- fun test_index_of do
+ fun test_index_of is test do
var subject = sample
assert 0 == subject.index_of("foo:bar")
assert -1 == subject.index_of("foo:bar")
end
- fun test_type_ns do
+ fun test_type_ns is test do
var subject = sample
assert "CDATA" == subject.type_ns("http://example.com/", "bar")
assert subject.type_ns("http://example.com/", "bar") == null
end
- fun test_value_ns do
+ fun test_value_ns is test do
var subject = sample
assert "baz" == subject.value_ns("http://example.com/", "bar")
assert subject.value_ns("http://example.com/", "bar") == null
end
- fun test_attributes_set do
+ fun test_attributes_set is test do
var subject = sample
var subject2 = new AttributesImpl
assert subject.length == 4
end
- fun test_set do
+ fun test_set is test do
var subject = sample
subject.set(1, "urn:is:not:often:used", "i-am_ME", "i-am_ME", "ID",
assert "NMTOKENS" == subject.type_of(0)
end
- fun test_remove_at do
+ fun test_remove_at is test do
var subject = sample
subject.remove_at(1)
assert "xml:lang" == subject.qname(1)
end
- fun test_uri_set do
+ fun test_uri_set is test do
var subject = sample
subject.uri(0) = "https://example.org/serious"
assert "https://example.org/serious" == subject.uri(0)
end
- fun test_local_name_set do
+ fun test_local_name_set is test do
var subject = sample
subject.local_name(0) = "trololol"
assert "ImYou42" == subject.local_name(1)
end
- fun test_qname_set do
+ fun test_qname_set is test do
var subject = sample
subject.qname(0) = "go-to:bar"
assert "yo:i-am_ME" == subject.qname(1)
end
- fun test_type_of_set do
+ fun test_type_of_set is test do
var subject = sample
subject.type_of(0) = "NMTOKENS"
assert "ENTITY" == subject.type_of(1)
end
- fun test_value_of_set do
+ fun test_value_of_set is test do
var subject = sample
subject.value_of(0) = "buz"
# another product.
# Test suite for `namespace_support`.
-module test_namespace_support is test_suite
+module test_namespace_support is test
-import test_suite
import sax::helpers::namespace_support
class TestNamespaceSupport
- super TestSuite
+ test
private fun sample: NamespaceSupport do
var subject = new NamespaceSupport
return subject
end
- fun test_reset do
+ fun test_reset is test do
var subject = sample
subject.reset
assert 2 == subject.prefixes.length
end
- fun test_push_context_override_default do
+ fun test_push_context_override_default is test do
var subject = sample
subject.push_context
assert 3 == subject.prefixes.length
end
- fun test_push_context_override_dc do
+ fun test_push_context_override_dc is test do
var subject = sample
subject.push_context
assert 3 == subject.prefixes.length
end
- fun test_push_context_undeclare do
+ fun test_push_context_undeclare is test do
var subject = sample
subject.push_context
assert 2 == subject.prefixes.length
end
- fun test_pop_context do
+ fun test_pop_context is test do
var subject = sample
subject.pop_context
#fun test_declare_prefix # SEE: test_push_context_*
- fun test_process_name do
+ fun test_process_name is test do
var subject = sample
var parts = new Array[String]
assert ["", "p", "p"] == subject.process_name("p", parts, true)
end
- fun test_process_name_xmlns do
+ fun test_process_name_xmlns is test do
var subject = sample
var parts = new Array[String].with_capacity(3)
assert ["http://www.w3.org/1999/xhtml", "xmlns", "xmlns"] == subject.process_name("xmlns", parts, false)
end
- fun test_declare_prefix_illegal do
+ fun test_declare_prefix_illegal is test do
var subject = sample
assert not subject.declare_prefix("xml", "http://example.org")
assert 2 == subject.declared_prefixes.length
end
- fun test_uri do
+ fun test_uri is test do
var subject = sample
assert "http://www.w3.org/1999/xhtml" == subject.uri("")
assert subject.uri("foo") == null
end
- fun test_prefixes do
+ fun test_prefixes is test do
var res = sample.prefixes
assert 3 == res.length else
assert res.has("xmlns")
end
- fun test_prefix do
+ fun test_prefix is test do
var subject = sample
assert subject.prefix("http://www.w3.org/1999/xhtml") == null
assert subject.prefix("https://example.org/serious") == null
end
- fun test_prefixes_of do
+ fun test_prefixes_of is test do
var subject = sample
var res: Collection[String]
assert res.has_all(["dc", "dc2"])
end
- fun test_declared_prefixes do
+ fun test_declared_prefixes is test do
var res = sample.declared_prefixes
assert 2 == res.length else
# another product.
# Tests for SAXophoNit
-module test_saxophonit is test_suite
+module test_saxophonit is test
import sax::helpers::sax_locator_impl
import sax::helpers::attributes_impl
class TestSaxophonit
super SAXTestSuite
+ test
redef fun create_reader do return new XophonReader
- fun test_empty do
+ fun test_empty is test do
before_test
parse_string("<foo />")
expected.document_locator = new SAXLocatorImpl
assert_equals
end
- fun test_simple_element do
+ fun test_simple_element is test do
before_test
parse_string("<foo>bar</foo>")
expected.document_locator = new SAXLocatorImpl
assert_equals
end
- fun test_type_mismatch do
+ fun test_type_mismatch is test do
before_test
parse_string("<a></b>")
expected.document_locator = new SAXLocatorImpl
assert_equals
end
- fun test_attributes do
+ fun test_attributes is test do
var atts = new AttributesImpl
before_test
assert_equals
end
- fun test_nested do
+ fun test_nested is test do
var atts = new AttributesImpl
before_test
assert_equals
end
- fun test_xmldecl do
+ fun test_xmldecl is test do
before_test
parse_string("<?xml version='1.0'?><foo />")
expected.document_locator = new SAXLocatorImpl
assert_equals
end
- fun test_xmldecl_encoding do
+ fun test_xmldecl_encoding is test do
before_test
parse_string("<?xml version=\"1.0\" encoding='utf-8'?><foo />")
expected.document_locator = new SAXLocatorImpl
assert_equals
end
- fun test_xmldecl_standalone do
+ fun test_xmldecl_standalone is test do
before_test
parse_string("<?xml version='1.0' standalone=\"yes\"?><foo />")
expected.document_locator = new SAXLocatorImpl
assert_equals
end
- fun test_xmldecl_both do
+ fun test_xmldecl_both is test do
before_test
parse_string("<?xml version='1.0' encoding='utf-8' standalone=\"yes\"?><foo />")
expected.document_locator = new SAXLocatorImpl
assert_equals
end
- fun test_reference_builtin do
+ fun test_reference_builtin is test do
before_test
parse_string("<foo>&"'<>00:</foo>")
expected.document_locator = new SAXLocatorImpl
assert_equals
end
- fun test_comments do
+ fun test_comments is test do
# TODO For the moment, comments are simply ignored.
before_test
parse_string("<!-- I--><foo>bar<!--l-i-k-e--></foo><!--comments -->")
assert_equals
end
- fun test_ns_simple do
+ fun test_ns_simple is test do
before_test
parse_string("<foo:bar xmlns:foo='https://s.exemple.org' />")
expected.document_locator = new SAXLocatorImpl
assert_equals
end
- fun test_ns_prefix do
+ fun test_ns_prefix is test do
var atts = new AttributesImpl
before_test
assert_equals
end
- fun test_mixed do
+ fun test_mixed is test do
var atts = new AttributesImpl
# TODO For the moment, ignorable white space is not detected.
# another product.
# Test suite for `testing`.
-module test_testing is test_suite
+module test_testing is test
import saxophonit::testing
-import test_suite
class TestSaxEventLogger
- super TestSuite
+ test
# Constants for diff formatting.
private var init_done: Bool = false
- redef fun before_test do
- super
+ fun before_test is before do
if not init_done then
default = a.term_default
ins = a.term_insertion
end
end
- fun test_diff_empty do
+ fun test_diff_empty is test do
assert "" == a.diff(b).to_s
assert "" == b.diff(a).to_s
end
- fun test_diff_equal1 do
+ fun test_diff_equal1 is test do
b.start_document
a.start_document
assert "" == a.diff(b).to_s
assert "" == b.diff(a).to_s
end
- fun test_diff_equal2 do
+ fun test_diff_equal2 is test do
b.start_document
b.end_document
a.start_document
assert "" == b.diff(a).to_s
end
- fun test_diff_insertion do
+ fun test_diff_insertion is test do
var exp: String
var test: String
assert_equals(2, exp, test)
end
- fun test_diff_edition do
+ fun test_diff_edition is test do
var exp: String
var test: String
import sax::ext::decl_handler
import sax::ext::lexical_handler
import console
-import test_suite
# A filter that internally log events it recieves.
#
# Base class for test suites on a SAX reader.
abstract class SAXTestSuite
- super TestSuite
# Logger of the expected event sequence.
var expected = new SAXEventLogger
private var init_done: Bool = false
- redef fun before_test do
- super
+ fun before_test is before do
if not init_done then
reader = create_reader
actual.parent = reader
# Services for caching serialization engines
module caching
-import serialization
+import serialization_core
private import engine_tools
# A `Serializer` with a `cache`
# Advanced services for serialization engines
module engine_tools
-import serialization
+import serialization_core
intrude import core::collection::hash_collection
# Maps instances to a value, uses `is_same_serialized` and `serialization_hash`.
fun serialization_hash: Int do return object_id
end
+redef class String
+ redef fun serialization_hash do return hash
+ redef fun is_same_serialized(o) do return self == o
+end
+
redef class Text
# Strip the `nullable` prefix from the type name `self`
--- /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.
+
+# Refine `Serializable::inspect` to show more useful information
+module inspect
+
+import serialization_core
+private import caching
+
+private fun inspect_testing: Bool do return "NIT_TESTING".environ == "true"
+
+# Serialization engine writing the object attributes to strings
+private class InspectSerializer
+ super CachingSerializer
+
+ # Target writing stream
+ var stream: Writer
+
+ redef var current_object = null
+
+ var first_object: nullable Object = null
+
+ redef fun serialize(object)
+ do
+ if object == null then
+ stream.write "null"
+ else
+ if current_object == null then
+ first_object = object
+ end
+
+ var last_object = current_object
+ current_object = object
+ object.accept_inspect_serializer self
+ current_object = last_object
+ end
+ end
+
+ var first_attribute_serialized = false
+
+ redef fun serialize_attribute(name, value)
+ do
+ if first_attribute_serialized then
+ stream.write ", "
+ else
+ stream.write " "
+ first_attribute_serialized = true
+ end
+
+ stream.write name
+ stream.write ":"
+
+ super
+ end
+
+ redef fun serialize_reference(object)
+ do
+ if cache.has_object(object) then
+ # Cycle
+ var id = object.object_id
+ if inspect_testing then id = cache.id_for(object)
+
+ stream.write "<"
+ stream.write object.class_name
+ stream.write "#"
+ stream.write id.to_s
+ stream.write ">"
+ else if object != first_object and (not object isa DirectSerializable) then
+ # Another object, print class and id only
+ var id = object.object_id
+ if inspect_testing then id = cache.new_id_for(object)
+
+ stream.write "<"
+ stream.write object.class_name
+ stream.write "#"
+ stream.write id.to_s
+ stream.write ">"
+ else
+ # Main object
+ serialize object
+ end
+ end
+end
+
+redef class Serializable
+
+ # Improve the default inspection reading serializable attributes
+ #
+ # Simple immutable data are inspected as they would be written in Nit code.
+ #
+ # ~~~
+ # assert 123.inspect == "123"
+ # assert 1.5.inspect == "1.5"
+ # assert 0xa1u8.inspect == "0xa1u8"
+ # assert 'c'.inspect == "'c'"
+ # assert "asdf\n".inspect == "\"asdf\\n\""
+ # ~~~
+ #
+ # Inspections of mutable serializable objects show their dynamic type,
+ # their `object_id` and their first level attributes. When testing,
+ # the `object_id` is replaced by an id unique to each call to `inspect`.
+ #
+ # ~~~
+ # class MyClass
+ # serialize
+ #
+ # var i: Int
+ # var o: nullable Object
+ # end
+ #
+ # var class_with_null = new MyClass(123)
+ # assert class_with_null.to_s == class_with_null.inspect
+ # assert class_with_null.to_s == "<MyClass#0 i:123, o:null>"
+ #
+ # var class_with_other = new MyClass(456, class_with_null)
+ # assert class_with_other.to_s == "<MyClass#0 i:456, o:<MyClass#1>>"
+ #
+ # var class_with_cycle = new MyClass(789)
+ # class_with_cycle.o = class_with_cycle
+ # assert class_with_cycle.to_s == "<MyClass#0 i:789, o:<MyClass#0>>"
+ # ~~~
+ #
+ # Items of collections are flattened and appended to the output.
+ #
+ # ~~~
+ # assert [1, 2, 3].inspect == "<Array[Int]#0 [1, 2, 3]>"
+ #
+ # var set = new HashSet[Object].from([1, 1.5, "two": Object])
+ # assert set.inspect == """<HashSet[Object]#0 [1, 1.5, "two"]>"""
+ #
+ # var map = new Map[Int, String]
+ # map[1] = "one"
+ # map[2] = "two"
+ # assert map.inspect == """<HashMap[Int, String]#0 {1:"one", 2:"two"}>"""
+ # ~~~
+ #
+ # Inspections producing over 80 characters are cut short.
+ #
+ # ~~~
+ # var long_class = new MyClass(123456789, "Some " + "very "*8 + "long string")
+ # assert long_class.to_s == "<MyClass#0 i:123456789, o:\"Some very very very very very very very very long s…>"
+ # ~~~
+ redef fun inspect
+ do
+ var stream = new StringWriter
+ var serializer = new InspectSerializer(stream)
+ serializer.serialize self
+ stream.close
+ var str = stream.to_s
+
+ # Cut long inspects
+ var max_length = 80
+ if str.length > max_length then
+ str = str.substring(0, max_length-2) + "…>"
+ end
+
+ return str
+ end
+
+ private fun accept_inspect_serializer(v: InspectSerializer)
+ do
+ v.stream.write "<"
+
+ v.stream.write class_name
+ v.stream.write "#"
+
+ var id = object_id
+ if inspect_testing then id = v.cache.new_id_for(self)
+ v.stream.write id.to_s
+
+ accept_inspect_serializer_core v
+
+ v.stream.write ">"
+ end
+
+ private fun accept_inspect_serializer_core(v: InspectSerializer)
+ do v.serialize_core(self)
+end
+
+redef class Int
+ redef fun accept_inspect_serializer(v) do v.stream.write to_s
+end
+
+redef class Float
+ redef fun accept_inspect_serializer(v) do v.stream.write to_s
+end
+
+redef class Bool
+ redef fun accept_inspect_serializer(v) do v.stream.write to_s
+end
+
+redef class Char
+ redef fun accept_inspect_serializer(v)
+ do
+ v.stream.write "'"
+ v.stream.write to_s.escape_to_nit
+ v.stream.write "'"
+ end
+end
+
+redef class Byte
+ redef fun accept_inspect_serializer(v)
+ do
+ v.stream.write to_s
+ v.stream.write "u8"
+ end
+end
+
+redef class CString
+ redef fun accept_inspect_serializer_core(v)
+ do
+ v.stream.write " \""
+ v.stream.write to_s.escape_to_nit
+ v.stream.write_char '"'
+ end
+end
+
+redef class Text
+
+ redef fun accept_inspect_serializer(v)
+ do
+ v.stream.write "\""
+ v.stream.write escape_to_nit
+ v.stream.write "\""
+ end
+end
+
+redef class Collection[E]
+ private fun serialize_as_inspect(v: InspectSerializer)
+ do
+ v.stream.write "["
+ var is_first = true
+ for e in self do
+ if is_first then
+ is_first = false
+ else
+ v.stream.write ", "
+ end
+
+ if not v.try_to_serialize(e) then
+ assert e != null
+ v.stream.write e.inspect
+ end
+ end
+ v.stream.write "]"
+ end
+end
+
+redef class SimpleCollection[E]
+ redef fun accept_inspect_serializer_core(v)
+ do
+ v.stream.write " "
+ serialize_as_inspect v
+ end
+end
+
+redef class Map[K, V]
+ redef fun accept_inspect_serializer_core(v)
+ do
+ v.stream.write " \{"
+
+ var first = true
+ for key, val in self do
+ if not first then
+ v.stream.write ", "
+ else first = false
+
+ if not v.try_to_serialize(key) then
+ assert key != null
+ v.stream.write key.inspect
+ end
+
+ v.stream.write ":"
+
+ if not v.try_to_serialize(val) then
+ assert val != null
+ v.stream.write val.inspect
+ end
+ end
+
+ v.stream.write "\}"
+ end
+end
# See the License for the specific language governing permissions and
# limitations under the License.
-# Abstract services to serialize Nit objects to different formats
-#
-# This module declares the `serialize` annotation to mark Nit classes as serializable.
-# For an introduction to this service, refer to the documentation of the `serialization` group.
-# This documentation provides more technical information on interesting entitie of this module.
-#
-# Interesting entities for end users of serializable classes:
-#
-# * Serialize an instance subclass of `Serializable` with either
-# `Serializer::serializable` and `Serializable::serialize`.
-# * Deserialize an object using `Deserializer::deserialize`.
-# The object type must the be checked with an `assert` or otherwise.
-#
-# Interesting entities to create custom serializable classes:
-#
-# * Subclass `Serializable` to declare a class as serializable and to customize
-# the serialization and deserialization behavior.
-# * Redefine `Serializable::core_serialize_to` to customize the serialization
-# of the receiver class.
-# * Redefine `Deserializer::deserialize_class` to customize the deserialization
-# of a specific class by name.
-#
-# Interesting entities for serialization format:
-#
-# * Subclass `Serializer` and `Deserializer` with custom serices.
-# * In `Serializer`, `serialize` and `serialize_reference` must be redefined.
-# * In `Deserializer`; `deserialize`, `deserialize_attribute and
-# `notify_of_creation` must be redefined.
-module serialization is
- new_annotation auto_serializable
- new_annotation serialize
- new_annotation noserialize
- new_annotation serialize_as
-end
-
-intrude import core::queue
-import meta
-
-# Abstract serialization service to be sub-classed by specialized services.
-interface Serializer
- # Entry point method of this service, serialize the `object`
- #
- # This method, and refinements, should handle `null` and probably
- # use double dispatch to customize the bahavior per serializable objects.
- fun serialize(object: nullable Serializable) is abstract
-
- # The object currently serialized by `serialized`
- #
- # Can be used by a custom serializer to add domain-specific serialization behavior.
- protected fun current_object: nullable Object is abstract
-
- # Serialize an object, with full serialization or a simple reference
- protected fun serialize_reference(object: Serializable) is abstract
-
- # Serialize an attribute to compose a serializable object
- #
- # This method should be called from `Serializable::core_serialize_to`.
- fun serialize_attribute(name: String, value: nullable Object)
- do
- if not try_to_serialize(value) then
- assert value != null # null would have been serialized
- warn("argument {name} of type {value.class_name} is not serializable.")
- end
- end
-
- # Serialize `value` is possible, i.e. it is `Serializable` or `null`
- fun try_to_serialize(value: nullable Object): Bool
- do
- if value isa Serializable then
- value.serialize_to_or_delay(self)
- else if value == null then
- serialize value
- else return false
- return true
- end
-
- # The method is called when a standard `value` is serialized
- #
- # The default behavior is to call `value.core_serialize_to(self)` but it
- # can be redefined by a custom serializer to add domain-specific serialization behavior.
- fun serialize_core(value: Serializable)
- do
- value.core_serialize_to(self)
- end
-
- # Warn of problems and potential errors (such as if an attribute
- # is not serializable)
- fun warn(msg: String) do print "Serialization warning: {msg}"
-end
-
-# Abstract deserialization service
-#
-# The main service is `deserialize`.
-abstract class Deserializer
- # Deserialize and return an object, storing errors in the attribute `errors`
- #
- # If a `static_type` is given, only subtypes of the `static_type` are accepted.
- #
- # This method behavior varies according to the implementation engines.
- fun deserialize(static_type: nullable String): nullable Object is abstract
-
- # Deserialize the attribute with `name` from the object open for deserialization
- #
- # The `static_type` restricts what kind of object can be deserialized.
- #
- # Return the deserialized value or null on error, and set
- # `deserialize_attribute_missing` to whether the attribute was missing.
- #
- # Internal method to be implemented by the engines.
- fun deserialize_attribute(name: String, static_type: nullable String): nullable Object is abstract
-
- # Was the attribute queried by the last call to `deserialize_attribute` missing?
- var deserialize_attribute_missing = false
-
- # Register a newly allocated object (even if not completely built)
- #
- # Internal method called by objects in creation, to be implemented by the engines.
- fun notify_of_creation(new_object: Object) is abstract
-
- # Deserialize the next available object as an instance of `class_name`
- #
- # Return the deserialized object on success and
- # record in `errors` if `class_name` is unknown.
- #
- # This method should be redefined for each custom subclass of `Serializable`.
- # All refinement should look for a precise `class_name` and call super
- # on unsupported classes.
- protected fun deserialize_class(class_name: Text): nullable Object do
- if class_name == "Error" then return new Error.from_deserializer(self)
- return deserialize_class_intern(class_name)
- end
-
- # Generated service to deserialize the next available object as an instance of `class_name`
- #
- # Refinements to this method will be generated by the serialization phase.
- # To avoid conflicts, there should not be any other refinements to this method.
- # You can instead use `deserialize_class`.
- protected fun deserialize_class_intern(class_name: Text): nullable Object do
- errors.add new Error("Deserialization Error: Doesn't know how to deserialize class \"{class_name}\"")
- return null
- end
-
- # Should `self` keep trying to deserialize an object after an error?
- #
- # This behavior takes effect after each attribute deserialization with
- # errors such as a missing attribute or the value is of the wrong type.
- # If `keep_going`, the attribute will be skipped but the engine will
- # deserialize the next attribute.
- # If `not keep_going`, the engine stops deserializing right away.
- #
- # When at `true`, this may cause the accumulation of a lot of entries in `errors`.
- #
- # Default at `true`.
- var keep_going: nullable Bool = null is writable
-
- # Errors encountered in the last call to `deserialize`
- var errors = new Array[Error]
-end
-
-# Deserialization error related to an attribute of `receiver`
-abstract class AttributeError
- super Error
-
- # Parent object of the problematic attribute
- var receiver: Object
-
- # Name of the problematic attribute in `receiver`
- var attribute_name: String
-end
-
-# Invalid dynamic type for a deserialized attribute
-class AttributeTypeError
- super AttributeError
-
- autoinit receiver, attribute_name, attribute, expected_type
-
- # Deserialized object that isn't of the `expected_type`
- var attribute: nullable Object
-
- # Name of the type expected for `attribute`
- var expected_type: String
-
- redef var message is lazy do
- var attribute = attribute
- var found_type = if attribute != null then attribute.class_name else "null"
-
- return "Deserialization Error: {
- }Wrong type on `{receiver.class_name}::{attribute_name}` expected `{expected_type}`, got `{found_type}`"
- end
-end
-
-# Missing attribute at deserialization
-class AttributeMissingError
- super AttributeError
-
- autoinit receiver, attribute_name
-
- redef var message is lazy do
- return "Deserialization Error: Missing attribute `{receiver.class_name}::{attribute_name}`"
- end
-end
-
-# Instances of this class can be passed to `Serializer::serialize`
-interface Serializable
- # Serialize `self` to `serializer`
- #
- # This is a shortcut to `Serializer::serialize`.
- fun serialize_to(serializer: Serializer) do serializer.serialize(self)
-
- # Actual serialization of `self` to `serializer`
- #
- # This writes the full data of `self` to `serializer`.
- #
- # This method can be redefined in sub classes and refinements.
- # It should use `Serializer::serialize_attribute` to to register real or
- # logical attributes.
- #
- # Any refinement should have its equivalent refinement of
- # `Deserializer::deserialize_class` to support this custom deserialization.
- fun core_serialize_to(serializer: Serializer) do end
-
- # Accept references or force direct serialization (using `serialize_to`)
- #
- # The subclass change the default behavior, which will accept references,
- # to force to always serialize copies of `self`.
- private fun serialize_to_or_delay(v: Serializer) do v.serialize_reference(self)
-
- # Create an instance of this class from the `deserializer`
- #
- # This constructor is refined by subclasses to correctly build their instances.
- init from_deserializer(deserializer: Deserializer) is nosuper do end
-end
-
-# Instances of this class are not delayed and instead serialized immediately
-# This applies mainly to `universal` types
-interface DirectSerializable
- super Serializable
-
- redef fun serialize_to_or_delay(v) do serialize_to(v)
-end
-
-redef class Bool super DirectSerializable end
-redef class Char super DirectSerializable end
-redef class Byte super DirectSerializable end
-redef class Int super DirectSerializable end
-redef class Float super DirectSerializable end
-redef class CString super DirectSerializable end
-redef class Text super DirectSerializable end
-redef class SimpleCollection[E] super Serializable end
-redef class Map[K, V] super Serializable end
-
-redef class Couple[F, S]
- super Serializable
-
- redef init from_deserializer(v)
- do
- v.notify_of_creation self
- var first = v.deserialize_attribute("first")
- var second = v.deserialize_attribute("second")
- init(first, second)
- end
-
- redef fun core_serialize_to(v)
- do
- v.serialize_attribute("first", first)
- v.serialize_attribute("second", second)
- end
-end
-
-redef class Ref[E]
- super Serializable
-
- redef init from_deserializer(v)
- do
- v.notify_of_creation self
- var item = v.deserialize_attribute("item")
- init item
- end
-
- redef fun core_serialize_to(v)
- do
- v.serialize_attribute("item", first)
- end
-end
-
-redef class Error
- super Serializable
-
- redef init from_deserializer(v)
- do
- v.notify_of_creation self
-
- var message = v.deserialize_attribute("message")
- if not message isa String then message = ""
- init message
-
- var cause = v.deserialize_attribute("cause")
- if cause isa nullable Error then self.cause = cause
- end
-
- redef fun core_serialize_to(v)
- do
- v.serialize_attribute("message", message)
- v.serialize_attribute("cause", cause)
- end
-end
-
-# ---
-# core::queue classes
-
-redef abstract class ProxyQueue[E]
-
- redef init from_deserializer(v)
- do
- v.notify_of_creation self
-
- var seq = v.deserialize_attribute("seq", (new GetName[Sequence[E]]).to_s)
- if not seq isa Sequence[E] then seq = new Array[E]
- if v.deserialize_attribute_missing then
- v.errors.add new AttributeMissingError(self, "seq")
- end
-
- init seq
- end
-
- redef fun core_serialize_to(v) do v.serialize_attribute("seq", seq)
-end
-
-redef class RandQueue[E]
-
- redef init from_deserializer(v)
- do
- v.notify_of_creation self
-
- var seq = v.deserialize_attribute("seq", (new GetName[SimpleCollection[E]]).to_s)
- if not seq isa SimpleCollection[E] then seq = new Array[E]
- if v.deserialize_attribute_missing then
- v.errors.add new AttributeMissingError(self, "seq")
- end
-
- init seq
- end
-
- redef fun core_serialize_to(v) do v.serialize_attribute("seq", seq)
-end
-
-redef class MinHeap[E]
-
- redef init from_deserializer(v)
- do
- v.notify_of_creation self
-
- var items = v.deserialize_attribute("items", (new GetName[SimpleCollection[E]]).to_s)
- if not items isa Array[E] then items = new Array[E]
- if v.deserialize_attribute_missing then
- v.errors.add new AttributeMissingError(self, "items")
- end
-
- var comparator = v.deserialize_attribute("comparator", "Comparator")
- if not comparator isa Comparator then comparator = default_comparator
- if v.deserialize_attribute_missing then
- v.errors.add new AttributeMissingError(self, "comparator")
- end
-
- init comparator
- self.items.add_all items
- end
+# General serialization services
+module serialization
- redef fun core_serialize_to(v)
- do
- v.serialize_attribute("items", items)
- v.serialize_attribute("comparator", comparator)
- end
-end
+import serialization_core
+import inspect
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+# Abstract services to serialize Nit objects to different formats
+#
+# This module declares the `serialize` annotation to mark Nit classes as serializable.
+# For an introduction to this service, refer to the documentation of the `serialization` group.
+# This documentation provides more technical information on interesting entitie of this module.
+#
+# Interesting entities for end users of serializable classes:
+#
+# * Serialize an instance subclass of `Serializable` with either
+# `Serializer::serializable` and `Serializable::serialize`.
+# * Deserialize an object using `Deserializer::deserialize`.
+# The object type must the be checked with an `assert` or otherwise.
+#
+# Interesting entities to create custom serializable classes:
+#
+# * Subclass `Serializable` to declare a class as serializable and to customize
+# the serialization and deserialization behavior.
+# * Redefine `Serializable::core_serialize_to` to customize the serialization
+# of the receiver class.
+# * Redefine `Deserializer::deserialize_class` to customize the deserialization
+# of a specific class by name.
+#
+# Interesting entities for serialization format:
+#
+# * Subclass `Serializer` and `Deserializer` with custom serices.
+# * In `Serializer`, `serialize` and `serialize_reference` must be redefined.
+# * In `Deserializer`; `deserialize`, `deserialize_attribute and
+# `notify_of_creation` must be redefined.
+module serialization_core is
+ new_annotation auto_serializable
+ new_annotation serialize
+ new_annotation noserialize
+ new_annotation serialize_as
+end
+
+intrude import core::queue
+import meta
+
+# Abstract serialization service to be sub-classed by specialized services.
+interface Serializer
+ # Entry point method of this service, serialize the `object`
+ #
+ # This method, and refinements, should handle `null` and probably
+ # use double dispatch to customize the bahavior per serializable objects.
+ fun serialize(object: nullable Serializable) is abstract
+
+ # The object currently serialized by `serialized`
+ #
+ # Can be used by a custom serializer to add domain-specific serialization behavior.
+ protected fun current_object: nullable Object is abstract
+
+ # Serialize an object, with full serialization or a simple reference
+ protected fun serialize_reference(object: Serializable) is abstract
+
+ # Serialize an attribute to compose a serializable object
+ #
+ # This method should be called from `Serializable::core_serialize_to`.
+ fun serialize_attribute(name: String, value: nullable Object)
+ do
+ if not try_to_serialize(value) then
+ assert value != null # null would have been serialized
+ warn("argument {name} of type {value.class_name} is not serializable.")
+ end
+ end
+
+ # Serialize `value` is possible, i.e. it is `Serializable` or `null`
+ fun try_to_serialize(value: nullable Object): Bool
+ do
+ if value isa Serializable then
+ value.serialize_to_or_delay(self)
+ else if value == null then
+ serialize value
+ else return false
+ return true
+ end
+
+ # The method is called when a standard `value` is serialized
+ #
+ # The default behavior is to call `value.core_serialize_to(self)` but it
+ # can be redefined by a custom serializer to add domain-specific serialization behavior.
+ fun serialize_core(value: Serializable)
+ do
+ value.core_serialize_to(self)
+ end
+
+ # Warn of problems and potential errors (such as if an attribute
+ # is not serializable)
+ fun warn(msg: String) do print "Serialization warning: {msg}"
+end
+
+# Abstract deserialization service
+#
+# The main service is `deserialize`.
+abstract class Deserializer
+ # Deserialize and return an object, storing errors in the attribute `errors`
+ #
+ # If a `static_type` is given, only subtypes of the `static_type` are accepted.
+ #
+ # This method behavior varies according to the implementation engines.
+ fun deserialize(static_type: nullable String): nullable Object is abstract
+
+ # Deserialize the attribute with `name` from the object open for deserialization
+ #
+ # The `static_type` restricts what kind of object can be deserialized.
+ #
+ # Return the deserialized value or null on error, and set
+ # `deserialize_attribute_missing` to whether the attribute was missing.
+ #
+ # Internal method to be implemented by the engines.
+ fun deserialize_attribute(name: String, static_type: nullable String): nullable Object is abstract
+
+ # Was the attribute queried by the last call to `deserialize_attribute` missing?
+ var deserialize_attribute_missing = false
+
+ # Register a newly allocated object (even if not completely built)
+ #
+ # Internal method called by objects in creation, to be implemented by the engines.
+ fun notify_of_creation(new_object: Object) is abstract
+
+ # Deserialize the next available object as an instance of `class_name`
+ #
+ # Return the deserialized object on success and
+ # record in `errors` if `class_name` is unknown.
+ #
+ # This method should be redefined for each custom subclass of `Serializable`.
+ # All refinement should look for a precise `class_name` and call super
+ # on unsupported classes.
+ protected fun deserialize_class(class_name: Text): nullable Object do
+ if class_name == "Error" then return new Error.from_deserializer(self)
+ return deserialize_class_intern(class_name)
+ end
+
+ # Generated service to deserialize the next available object as an instance of `class_name`
+ #
+ # Refinements to this method will be generated by the serialization phase.
+ # To avoid conflicts, there should not be any other refinements to this method.
+ # You can instead use `deserialize_class`.
+ protected fun deserialize_class_intern(class_name: Text): nullable Object do
+ errors.add new Error("Deserialization Error: Doesn't know how to deserialize class \"{class_name}\"")
+ return null
+ end
+
+ # Should `self` keep trying to deserialize an object after an error?
+ #
+ # This behavior takes effect after each attribute deserialization with
+ # errors such as a missing attribute or the value is of the wrong type.
+ # If `keep_going`, the attribute will be skipped but the engine will
+ # deserialize the next attribute.
+ # If `not keep_going`, the engine stops deserializing right away.
+ #
+ # When at `true`, this may cause the accumulation of a lot of entries in `errors`.
+ #
+ # Default at `true`.
+ var keep_going: nullable Bool = null is writable
+
+ # Errors encountered in the last call to `deserialize`
+ var errors = new Array[Error]
+end
+
+# Deserialization error related to an attribute of `receiver`
+abstract class AttributeError
+ super Error
+
+ # Parent object of the problematic attribute
+ var receiver: Object
+
+ # Name of the problematic attribute in `receiver`
+ var attribute_name: String
+end
+
+# Invalid dynamic type for a deserialized attribute
+class AttributeTypeError
+ super AttributeError
+
+ autoinit receiver, attribute_name, attribute, expected_type
+
+ # Deserialized object that isn't of the `expected_type`
+ var attribute: nullable Object
+
+ # Name of the type expected for `attribute`
+ var expected_type: String
+
+ redef var message is lazy do
+ var attribute = attribute
+ var found_type = if attribute != null then attribute.class_name else "null"
+
+ return "Deserialization Error: {
+ }Wrong type on `{receiver.class_name}::{attribute_name}` expected `{expected_type}`, got `{found_type}`"
+ end
+end
+
+# Missing attribute at deserialization
+class AttributeMissingError
+ super AttributeError
+
+ autoinit receiver, attribute_name
+
+ redef var message is lazy do
+ return "Deserialization Error: Missing attribute `{receiver.class_name}::{attribute_name}`"
+ end
+end
+
+# Instances of this class can be passed to `Serializer::serialize`
+interface Serializable
+ # Serialize `self` to `serializer`
+ #
+ # This is a shortcut to `Serializer::serialize`.
+ fun serialize_to(serializer: Serializer) do serializer.serialize(self)
+
+ # Actual serialization of `self` to `serializer`
+ #
+ # This writes the full data of `self` to `serializer`.
+ #
+ # This method can be redefined in sub classes and refinements.
+ # It should use `Serializer::serialize_attribute` to to register real or
+ # logical attributes.
+ #
+ # Any refinement should have its equivalent refinement of
+ # `Deserializer::deserialize_class` to support this custom deserialization.
+ fun core_serialize_to(serializer: Serializer) do end
+
+ # Accept references or force direct serialization (using `serialize_to`)
+ #
+ # The subclass change the default behavior, which will accept references,
+ # to force to always serialize copies of `self`.
+ private fun serialize_to_or_delay(v: Serializer) do v.serialize_reference(self)
+
+ # Create an instance of this class from the `deserializer`
+ #
+ # This constructor is refined by subclasses to correctly build their instances.
+ init from_deserializer(deserializer: Deserializer) is nosuper do end
+end
+
+# Instances of this class are not delayed and instead serialized immediately
+# This applies mainly to `universal` types
+interface DirectSerializable
+ super Serializable
+
+ redef fun serialize_to_or_delay(v) do serialize_to(v)
+end
+
+redef class Bool super DirectSerializable end
+redef class Char super DirectSerializable end
+redef class Byte super DirectSerializable end
+redef class Int super DirectSerializable end
+redef class Float super DirectSerializable end
+redef class CString super DirectSerializable end
+redef class Text super DirectSerializable end
+redef class SimpleCollection[E] super Serializable end
+redef class Map[K, V] super Serializable end
+
+redef class Couple[F, S]
+ super Serializable
+
+ redef init from_deserializer(v)
+ do
+ v.notify_of_creation self
+ var first = v.deserialize_attribute("first")
+ var second = v.deserialize_attribute("second")
+ init(first, second)
+ end
+
+ redef fun core_serialize_to(v)
+ do
+ v.serialize_attribute("first", first)
+ v.serialize_attribute("second", second)
+ end
+end
+
+redef class Ref[E]
+ super Serializable
+
+ redef init from_deserializer(v)
+ do
+ v.notify_of_creation self
+ var item = v.deserialize_attribute("item")
+ init item
+ end
+
+ redef fun core_serialize_to(v)
+ do
+ v.serialize_attribute("item", first)
+ end
+end
+
+redef class Error
+ super Serializable
+
+ redef init from_deserializer(v)
+ do
+ v.notify_of_creation self
+
+ var message = v.deserialize_attribute("message")
+ if not message isa String then message = ""
+ init message
+
+ var cause = v.deserialize_attribute("cause")
+ if cause isa nullable Error then self.cause = cause
+ end
+
+ redef fun core_serialize_to(v)
+ do
+ v.serialize_attribute("message", message)
+ v.serialize_attribute("cause", cause)
+ end
+end
+
+# ---
+# core::queue classes
+
+redef abstract class ProxyQueue[E]
+
+ redef init from_deserializer(v)
+ do
+ v.notify_of_creation self
+
+ var seq = v.deserialize_attribute("seq", (new GetName[Sequence[E]]).to_s)
+ if not seq isa Sequence[E] then seq = new Array[E]
+ if v.deserialize_attribute_missing then
+ v.errors.add new AttributeMissingError(self, "seq")
+ end
+
+ init seq
+ end
+
+ redef fun core_serialize_to(v) do v.serialize_attribute("seq", seq)
+end
+
+redef class RandQueue[E]
+
+ redef init from_deserializer(v)
+ do
+ v.notify_of_creation self
+
+ var seq = v.deserialize_attribute("seq", (new GetName[SimpleCollection[E]]).to_s)
+ if not seq isa SimpleCollection[E] then seq = new Array[E]
+ if v.deserialize_attribute_missing then
+ v.errors.add new AttributeMissingError(self, "seq")
+ end
+
+ init seq
+ end
+
+ redef fun core_serialize_to(v) do v.serialize_attribute("seq", seq)
+end
+
+redef class MinHeap[E]
+
+ redef init from_deserializer(v)
+ do
+ v.notify_of_creation self
+
+ var items = v.deserialize_attribute("items", (new GetName[SimpleCollection[E]]).to_s)
+ if not items isa Array[E] then items = new Array[E]
+ if v.deserialize_attribute_missing then
+ v.errors.add new AttributeMissingError(self, "items")
+ end
+
+ var comparator = v.deserialize_attribute("comparator", "Comparator")
+ if not comparator isa Comparator then comparator = default_comparator
+ if v.deserialize_attribute_missing then
+ v.errors.add new AttributeMissingError(self, "comparator")
+ end
+
+ init comparator
+ self.items.add_all items
+ end
+
+ redef fun core_serialize_to(v)
+ do
+ v.serialize_attribute("items", items)
+ v.serialize_attribute("comparator", comparator)
+ end
+end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_macro is test_suite
+module test_macro is test
-import test_suite
import macro
class TestTemplateString
- super TestSuite
+ test
- fun test_tpl_parse_1 do
+ fun test_tpl_parse_1 is test do
var tpl = """
<!DOCTYPE html>
<html lang="en">
assert res == tpl
end
- fun test_tpl_parse_2 do
+ fun test_tpl_parse_2 is test do
var tpl = """
<!DOCTYPE html>
<html lang="en">
assert res == tpl
end
- fun test_tpl_parse_3 do
+ fun test_tpl_parse_3 is test do
var tpl = """
<!DOCTYPE html>
<html lang="en">
assert res == tpl
end
- fun test_tpl_parse_4 do
+ fun test_tpl_parse_4 is test do
var tpl = """
<!DOCTYPE html>
<html lang="en">
assert res == tpl
end
- fun test_tpl_parse_5 do
+ fun test_tpl_parse_5 is test do
var tpl = """
<!DOCTYPE html>
<html lang="en">
assert res == tpl
end
- fun test_tpl_parse_6 do
+ fun test_tpl_parse_6 is test do
var tpl = """
<!DOCTYPE html>
<html lang="en">
assert res == tpl
end
- fun test_tpl_replace_1 do
+ fun test_tpl_replace_1 is test do
var tpl = """
<!DOCTYPE html>
<html lang="en">
assert res == exp
end
- fun test_tpl_replace_2 do
+ fun test_tpl_replace_2 is test do
var tpl = """
<!DOCTYPE html>
<html lang="en">
assert res == exp
end
- fun test_tpl_replace_3 do
+ fun test_tpl_replace_3 is test do
var tpl = """
<!DOCTYPE html>
<html lang="en">
+++ /dev/null
-[package]
-name=test_suite
-tags=devel,lib
-maintainer=Alexandre Terrasa <alexandre@moz-code.org>
-license=Apache-2.0
-[upstream]
-browse=https://github.com/nitlang/nit/tree/master/lib/test_suite.nit
-git=https://github.com/nitlang/nit.git
-git.directory=lib/test_suite.nit
-homepage=http://nitlanguage.org
-issues=https://github.com/nitlang/nit/issues
+++ /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.
-
-# Common interface for `nitunit` test-suites.
-module test_suite is
- # Annotation used by test-suite modules.
- new_annotation test_suite
-end
-
-# A test-suite that can be executed by `nitunit`.
-#
-# All test-suite classes must implement `TestSuite`.
-class TestSuite
- # Internal empty init.
- private init nitunit do end
-
- # Method called before each test-case.
- #
- # Redefine this method to factorize code that have to be
- # executed before every test.
- fun before_test do end
-
- # Method called after each test-case.
- #
- # Redefine this method to factorize code that have to be
- # executed after every test.
- fun after_test do end
-end
-
-redef class Sys
- # Internal empty init.
- private init nitunit do end
-
- # No before test for the module
- private fun before_test do end
-
- # No after test for the module
- private fun after_test do end
-
- # Method called before each test-suite.
- #
- # Redefine this method to factorize code that have to be
- # executed before every test suite.
- fun before_module do end
-
- # Method called after each test-suite.
- #
- # Redefine this method to factorize code that have to be
- # executed after every test suite.
- fun after_module do end
-end
TestSuites are Nit modules that define a set of TestCases.
-A test suite is a module that uses the annotation `is test_suite`.
+A test suite is a module that uses the annotation `is test`.
It is common that a test suite focuses on testing a single module.
-In this case, the name of the test_suite is often `test_foo.nit` where `foo.nit` is the tested module.
+In this case, the name of the test suite is often `test_foo.nit` where `foo.nit` is the tested module.
The structure of a test suite is the following:
~~~~
# test suite for module `foo`
-module test_foo is test_suite
+module test_foo is test
-import test_suite
import foo # can be intrude to test private things
class TestFoo
- super TestSuite
+ test
# test case for `foo::Foo::baz`
- fun test_baz do
+ fun baz is test do
var subject = new Foo
assert subject.baz(1, 2) == 3
end
$ nitunit foo.nit
-`nitunit` will execute a test for each method named `test_*` in a class
-subclassing `TestSuite` so multiple tests can be executed for a single method:
+`nitunit` will execute a test for each method with the `test` annotation in a class
+also annotated with `test` so multiple tests can be executed for a single method:
~~~~
class TestFoo
- super TestSuite
+ test
- fun test_baz_1 do
+ fun baz_1 is test do
var subject = new Foo
assert subject.baz(1, 2) == 3
end
- fun test_baz_2 do
+ fun baz_2 is test do
var subject = new Foo
assert subject.baz(1, -2) == -1
end
The test is failed if non-zero is returned by `diff`.
~~~
-module test_mod is test_suite
+module test_mod is test
class TestFoo
- super TestSuite
+ test
- fun test_bar do
+ fun bar is test do
print "Hello!"
end
end
## Configuring TestSuites
-`TestSuite`s also provide methods to configure the test run:
-
-`before_test` and `after_test`: methods called before/after each test case.
+`TestSuite`s also provide annotations to configure the test run:
+`before` and `after` annotations can be applied to methods that must be called before/after each test case.
They can be used to factorize repetitive tasks:
~~~~
class TestFoo
- super TestSuite
+ test
+
var subject: Foo
+
# Mandatory empty init
init do end
+
# Method executed before each test
- fun before_test do
+ fun set_up is before do
subject = new Foo
end
- fun test_baz_1 do
+ fun baz_1 is test do
assert subject.baz(1, 2) == 3
end
- fun test_baz_2 do
+ fun baz_2 is test do
assert subject.baz(1, -2) == -1
end
end
When using custom test attributes, an empty `init` must be declared to allow automatic test running.
-`before_module` and `after_module`: methods called before/after each test suite.
+`before_all` and `after_all` annotations can be applied to methods that must be called before/after each test suite.
They have to be declared at top level:
~~~~
The `NIT_TESTING_PATH` environment variable contains the current test suite
file path.
Nitunit define this variable before the execution of each test suite.
-It can be used to access files based on the current test_suite location:
+It can be used to access files based on the current test suite location:
~~~
class TestWithPath
- super TestSuite
+ test
fun test_suite_path do
assert "NIT_TESTING_PATH".environ != ""
+++ /dev/null
-<div ng-if='listContributors.length > 0'>
- <h3 id={{listId}}>
- <span>{{listTitle}}</span>
- </h3>
- <ul class='list-unstyled user-list'>
- <li ng-repeat='contributor in listContributors'>
- <img class='avatar' src="https://secure.gravatar.com/avatar/{{contributor.hash}}?size=20&default=retro">
- {{contributor.name}}
- </li>
- </ul>
-</div>
<div class='card'>
- <div class='card-left text-center' ng-if='!noSynopsis'>
+ <div class='card-left text-center'>
<entity-tag mentity='mentity' />
</div>
- <div class='card-body'>
- <h5 class='card-heading' ng-if='!noSynopsis'>
- <entity-signature mentity='mentity'/>
+ <div class='card-body' ng-if='mentity.class_name == "MPackage"' style='width: 75%'>
+ <h5 class='card-heading'>
+ <entity-signature mentity='mentity' />
+ <small ng-if='mentity.class_name == "MPackage"'>
+ <span ng-repeat='tag in mentity.metadata.tags'>
+ <a ui-sref='tag({id: tag})' class='text-muted'>{{tag}}</a>
+ <span ng-if='!$last'>,</span>
+ </span>
+ </small>
</h5>
- <div class='tab-content'>
- <div id='{{mentity.html_id}}-signature' class='tab-pane' ng-if='!noSynopsis'
- ng-class='currentTab == "signature" ? "active" : ""'>
- <span class='synopsis' ng-bind-html='mentity.mdoc.html_synopsis' />
- </div>
- <div id='{{mentity.html_id}}-grade' class='tab-pane'
- ng-class='currentTab == "grade" ? "active" : ""'>
- <entity-rating mentity='mentity' ratings='ratings'>
- </div>
- </div>
+ <span class='synopsis' ng-bind-html='mentity.mdoc.html_synopsis' />
</div>
- <div class='card-right'>
- <div class='dropdown'>
- <button class='btn btn-link dropdown-toggle' type='button' data-toggle='dropdown'>
- <span class='glyphicon glyphicon-chevron-down'></span>
- </button>
- <ul class='dropdown-menu dropdown-menu-right'>
- <li ng-class='currentTab == "signature" ? "active" : ""' ng-if='!noSynopsis'>
- <a ng-click='currentTab = "signature"'>Signature</a>
- </li>
- <li ng-class='currentTab == "grade" ? "active" : ""'>
- <a ng-click='loadEntityStars(); currentTab = "grade"'>Grade</a>
- </li>
- </ul>
- </div>
+ <div class='card-body' ng-if='mentity.class_name != "MPackage"'>
+ <h5 class='card-heading'>
+ <entity-signature mentity='mentity' />
+ </h5>
+ <span class='synopsis' ng-bind-html='mentity.mdoc.html_synopsis' />
+ </div>
+ <div class='card-right' ng-if='mentity.class_name == "MPackage"' style='width: 25%'>
+ <span ng-repeat='maintainer in mentity.metadata.maintainers'>
+ <img class='avatar' src='https://secure.gravatar.com/avatar/{{maintainer.gravatar}}?size=14&default=retro' />
+ <a ui-sref='person({id: maintainer.name})'>{{maintainer.name}}</a>
+ </span>
+ <br>
+ <span ng-if='mentity.metadata.license'>
+ <span class='text-muted'>
+ <a href='http://opensource.org/licenses/{{mentity.license}}' class='text-muted'>{{mentity.metadata.license}}</a>
+ </span>
+ </span>
</div>
</div>
+++ /dev/null
-<div class='card card-search'>
- <div class='card-left text-center'>
- <entity-tag mentity='mentity' />
- </div>
- <div class='card-body'>
- <h5 class='card-heading'>
- <entity-signature mentity='mentity'/>
- <br>
- <small><entity-namespace namespace='mentity.namespace' /></small>
- </h5>
- <span class='synopsis' ng-bind-html='mentity.mdoc.html_synopsis' />
- </div>
-</div>
+++ /dev/null
-<form>
- <div class='form-group has-icon'>
- <input placeholder='Search...' type='text' class='form-control search-input'
- ng-model-options='{ debounce: 300 }' ng-model='query'
- ng-keydown='update($event)' ng-change='search()'>
- <span class='glyphicon glyphicon-search form-control-icon text-muted'></span>
- </div>
- <div ng-if='results.length > 0' class='search-results'>
- <div class='card-list'>
- <search-card ng-click='selectEnter()' ng-class='{active: activeItem == $index}' ng-mouseover='setActive($index)' mentity='mentity' ng-repeat='mentity in results' />
- </div>
- </div>
-</form>
--- /dev/null
+<nav>
+ <ul class='pagination' ng-if='pagination.pagination.max > 1'>
+ <li ng-class='{disabled: pagination.pagination.page <= 1}'>
+ <a ng-click='pagination.changePage(pagination.pagination.page - 1, pagination.pagination.limit)'><span>«</span></a>
+ </li>
+ <li ng-repeat='page in pagination.pages'
+ ng-class='{disabled: pagination.pagination.page == page}'>
+ <a ng-click='pagination.changePage(page, pagination.pagination.limit)'>{{page}}</a>
+ </li>
+ <li ng-class='{disabled: pagination.pagination.page >= pagination.pagination.max}'>
+ <a ng-click='pagination.changePage(pagination.pagination.page + 1, pagination.pagination.limit)'><span>»</span></a>
+ </li>
+ </ul>
+</nav>
<div class='form-group has-icon'>
<input placeholder='Search...' type='text' class='form-control search-input'
ng-model-options='{ debounce: 300 }' ng-model='vm.query'
- ng-keydown='update($event)' ng-change='vm.search()'>
+ ng-keydown='vm.update($event)' ng-change='vm.search()'>
<span class='glyphicon glyphicon-search form-control-icon text-muted'></span>
</div>
+ <div ng-if='vm.results.results.length > 0' class='card-list search-results'>
+ <entity-card ng-click='vm.selectEnter()' ng-class='{active: vm.activeItem == $index}' ng-mouseover='vm.setActive($index)' mentity='mentity' ng-repeat='mentity in vm.results.results' />
+ <div class='card' ng-click='vm.selectEnter()' ng-mouseover='vm.setActive(vm.results.results.length)' ng-class='{active: vm.activeItem == vm.results.results.length}'>
+ <div class='card-body'>
+ Show all {{vm.results.total}} results for <a>"{{vm.query}}"</a>
+ </div>
+ </div>
+ </div>
</form>
</div>
</div>
<div class='col-xs-7'>
- <search-field />
+ <ui-search-field />
</div>
<div class='col-xs-2'>
<user-menu />
<script src='/javascripts/nitweb.js'></script>
<script src='/javascripts/entities.js'></script>
<script src='/javascripts/ui.js'></script>
- <script src='/javascripts/index.js'></script>
+ <script src='/javascripts/catalog.js'></script>
<script src='/javascripts/docdown.js'></script>
<script src='/javascripts/metrics.js'></script>
<script src='/javascripts/users.js'></script>
--- /dev/null
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+ angular.module('catalog', [])
+
+ /* Router */
+
+ .config(function($stateProvider, $locationProvider) {
+ $stateProvider
+ .state('catalog', {
+ url: '/?p&n',
+ controller: 'CatalogCtrl',
+ controllerAs: 'vm',
+ templateUrl: 'views/catalog/index.html',
+ resolve: {
+ packages: function(Catalog, $q, $stateParams, $state) {
+ var d = $q.defer();
+ var page = $stateParams.p ? $stateParams.p : 1;
+ var limit = $stateParams.n ? $stateParams.n : 10;
+ Catalog.packages(page, limit, d.resolve,
+ function(err) {
+ $state.go('404', null, { location: false })
+ });
+ return d.promise;
+ },
+ tags: function(Catalog, $q, $state) {
+ var d = $q.defer();
+ Catalog.tags(d.resolve,
+ function(err) {
+ $state.go('404', null, { location: false })
+ });
+ return d.promise;
+ },
+ stats: function(Catalog, $q, $state) {
+ var d = $q.defer();
+ Catalog.stats(d.resolve,
+ function(err) {
+ $state.go('404', null, { location: false })
+ });
+ return d.promise;
+
+ }
+ }
+ })
+ .state('person', {
+ url: '/person/:id?p1&n1&p2&n2',
+ controller: 'PersonCtrl',
+ controllerAs: 'vm',
+ templateUrl: 'views/catalog/person.html',
+ resolve: {
+ person: function(Catalog, $q, $stateParams, $state) {
+ var d = $q.defer();
+ Catalog.person($stateParams.id, d.resolve,
+ function(err) {
+ $state.go('404', null, { location: false })
+ });
+ return d.promise;
+ },
+ maintaining: function(Catalog, $q, $stateParams, $state) {
+ var d = $q.defer();
+ var p1 = $stateParams.p2 ? $stateParams.p1 : 1;
+ var n1 = $stateParams.n2 ? $stateParams.n1 : 10;
+ Catalog.personMaintaining($stateParams.id, p1, n1, d.resolve,
+ function(err) {
+ $state.go('404', null, { location: false })
+ });
+ return d.promise;
+ },
+ contributing: function(Catalog, $q, $stateParams, $state) {
+ var d = $q.defer();
+ var p2 = $stateParams.p2 ? $stateParams.p2 : 1;
+ var n2 = $stateParams.n2 ? $stateParams.n2 : 10;
+ Catalog.personContributing($stateParams.id, p2, n2, d.resolve,
+ function(err) {
+ $state.go('404', null, { location: false })
+ });
+ return d.promise;
+
+ }
+ }
+ })
+ .state('tag', {
+ url: '/tag/:id?p&n',
+ controller: 'TagCtrl',
+ controllerAs: 'vm',
+ templateUrl: 'views/catalog/tag.html',
+ resolve: {
+ tag: function(Catalog, $q, $stateParams, $state) {
+ var d = $q.defer();
+ var page = $stateParams.p ? $stateParams.p : 1;
+ var limit = $stateParams.l ? $stateParams.l : 10;
+ Catalog.tag($stateParams.id, page, limit, d.resolve,
+ function() {
+ $state.go('404', null, { location: false })
+ });
+ return d.promise;
+ }
+ }
+ })
+ })
+
+ /* Factories */
+
+ .factory('Catalog', [ '$http', function($http) {
+ return {
+ stats: function(cb, cbErr) {
+ $http.get('/api/catalog/stats')
+ .success(cb)
+ .error(cbErr);
+ },
+ packages: function(p, n, cb, cbErr) {
+ $http.get('/api/catalog/packages?p=' + p + '&n=' + n)
+ .success(cb)
+ .error(cbErr);
+ },
+ tags: function(cb, cbErr) {
+ $http.get('/api/catalog/tags')
+ .success(cb)
+ .error(cbErr);
+ },
+ person: function(id, cb, cbErr) {
+ $http.get('/api/catalog/person/' + id)
+ .success(cb)
+ .error(cbErr);
+ },
+ personMaintaining: function(id, p, n, cb, cbErr) {
+ $http.get('/api/catalog/person/' + id + '/maintaining?p=' + p + '&n=' + n)
+ .success(cb)
+ .error(cbErr);
+ },
+ personContributing: function(id, p, n, cb, cbErr) {
+ $http.get('/api/catalog/person/' + id + '/contributing?p=' + p + '&n=' + n)
+ .success(cb)
+ .error(cbErr);
+ },
+ tag: function(id, p, n, cb, cbErr) {
+ $http.get('/api/catalog/tag/' + id + '?p=' + p + '&n=' + n)
+ .success(cb)
+ .error(cbErr);
+ }
+ };
+ }])
+
+ /* Controllers */
+
+ .controller('CatalogCtrl', function($scope, $state, packages, tags, stats) {
+ var vm = this;
+ vm.packages = packages;
+ vm.tags = tags;
+ vm.stats = stats;
+
+ $scope.$on('change-page', function(e, page, limit) {
+ $state.go('catalog', {p: page, l: limit});
+ })
+ })
+
+ .controller('PersonCtrl', function($scope, $state, $stateParams, person, maintaining, contributing) {
+ var vm = this;
+ vm.person = person;
+ vm.maintaining = maintaining;
+ vm.contributing = contributing;
+
+ var p1 = $stateParams.p1 ? $stateParams.p1 : 1;
+ var n1 = $stateParams.n1 ? $stateParams.n1 : 10;
+ var p2 = $stateParams.p2 ? $stateParams.p2 : 1;
+ var n2 = $stateParams.n2 ? $stateParams.n2 : 10;
+
+ $scope.$on('change-page1', function(e, page, limit) {
+ $state.go('person', {id: $stateParams.id, p1: page, n1: limit, p2: p2, n2: n2});
+ })
+
+ $scope.$on('change-page2', function(e, page, limit) {
+ $state.go('person', {id: $stateParams.id, p1: p1, n1: n1, p2: page, n2: limit});
+ })
+ })
+
+ .controller('TagCtrl', function($state, $scope, tag) {
+ var vm = this;
+ vm.tag = tag;
+
+ $scope.$on('change-page', function(e, page, limit) {
+ $state.go('tag', {id: vm.tag.tag, p: page, l: limit});
+ })
+ })
+})();
controller: function(mentity, doc) {
this.mentity = mentity;
this.doc = doc;
+
+ this.date = function(date) {
+ return new Date(date);
+ }
},
controllerAs: 'vm',
})
.success(cb)
.error(cbErr);
},
-
- search: function(q, n, cb, cbErr) {
- $http.get('/api/search?q=' + q + '&n=' + n)
+ search: function(q, p, n, cb, cbErr) {
+ $http.get('/api/search?q=' + q + '&p=' + p + '&n=' + n)
.success(cb)
.error(cbErr);
}
return {
restrict: 'E',
scope: {
- mentity: '=',
- defaultTab: '@',
- noSynopsis: '='
+ mentity: '='
},
replace: true,
- templateUrl: '/directives/entity/card.html',
- link: function ($scope, element, attrs) {
- $scope.currentTab = $scope.defaultTab ? $scope.defaultTab : 'signature';
-
- $scope.loadEntityStars = function() {
- Feedback.loadEntityStars($scope.mentity.full_name,
- function(data) {
- $scope.ratings = data;
- }, function(message, status) {
- $scope.error = {message: message, status: status};
- });
- };
- }
+ templateUrl: '/directives/entity/card.html'
};
}])
.config(function($stateProvider, $locationProvider) {
$stateProvider
+ .state('doc.entity.grades', {
+ url: '/grades',
+ templateUrl: 'views/doc/grades.html',
+ resolve: {
+ metrics: function(Feedback, $q, $stateParams, $state) {
+ var d = $q.defer();
+ Feedback.loadEntityStars($stateParams.id, d.resolve,
+ function() {
+ $state.go('404', null, { location: false })
+ });
+ return d.promise;
+ }
+ },
+ controller: function(mentity, metrics) {
+ this.mentity = mentity;
+ this.metrics = metrics;
+ },
+ controllerAs: 'vm',
+ })
.state('grades', {
url: '/grades',
templateUrl: 'views/grades.html',
+++ /dev/null
-/*
- * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- angular
- .module('index', [])
-
- .config(function($stateProvider, $locationProvider) {
- $stateProvider
- .state('catalog', {
- url: '/',
- templateUrl: 'views/catalog/index.html',
- controller: 'CatalogCtrl',
- controllerAs: 'vm',
- abstract: true
- })
- .state('catalog.highlighted', {
- url: '',
- templateUrl: 'views/catalog/highlighted.html',
- controller: 'CatalogHighlightedCtrl',
- controllerAs: 'vm'
- })
- .state('catalog.required', {
- url: 'required',
- templateUrl: 'views/catalog/most_required.html',
- controller: 'CatalogRequiredCtrl',
- controllerAs: 'vm'
- })
- .state('catalog.tags', {
- url: 'tags',
- templateUrl: 'views/catalog/by_tags.html',
- controller: 'CatalogTagsCtrl',
- controllerAs: 'vm'
- })
- })
-
- .factory('Catalog', [ '$http', function($http) {
- return {
- loadHightlighted: function(cb, cbErr) {
- $http.get('/api/catalog/highlighted')
- .success(cb)
- .error(cbErr);
- },
-
- loadMostRequired: function(cb, cbErr) {
- $http.get('/api/catalog/required')
- .success(cb)
- .error(cbErr);
- },
-
- loadByTags: function(cb, cbErr) {
- $http.get('/api/catalog/bytags')
- .success(cb)
- .error(cbErr);
- },
-
- loadStats: function(cb, cbErr) {
- $http.get('/api/catalog/stats')
- .success(cb)
- .error(cbErr);
- },
-
- loadContributors: function(cb, cbErr) {
- $http.get('/api/catalog/contributors')
- .success(cb)
- .error(cbErr);
- },
- }
- }])
-
- .controller('CatalogCtrl', function(Catalog) {
- var vm = this;
-
- Catalog.loadContributors(
- function(data) {
- vm.contributors = data;
- }, function(err) {
- vm.error = err;
- });
-
- Catalog.loadStats(
- function(data) {
- vm.stats = data;
- }, function(err) {
- vm.error = err;
- });
- })
-
- .controller('CatalogHighlightedCtrl', function(Catalog) {
- var vm = this;
-
- Catalog.loadHightlighted(
- function(data) {
- vm.highlighted = data;
- }, function(err) {
- vm.error = err;
- });
- })
-
- .controller('CatalogRequiredCtrl', function(Catalog) {
- var vm = this;
-
- Catalog.loadMostRequired(
- function(data) {
- vm.required = data;
- }, function(err) {
- vm.error = err;
- });
- })
-
- .controller('CatalogTagsCtrl', function(Catalog, $anchorScroll, $location) {
- var vm = this;
-
- Catalog.loadByTags(
- function(data) {
- vm.bytags = data;
- }, function(err) {
- vm.error = err;
- });
-
-
- vm.scrollTo = function(hash) {
- $location.hash(hash);
- $anchorScroll();
- }
- })
-
- .directive('contributorList', function(Model) {
- return {
- restrict: 'E',
- scope: {
- listId: '@',
- listTitle: '@',
- listContributors: '='
- },
- templateUrl: '/directives/contributor-list.html'
- };
- })
-})();
*/
(function() {
- angular.module('nitweb', ['ui.router', 'ngSanitize', 'angular-loading-bar', 'index', 'entities', 'docdown', 'metrics', 'users', 'grades'])
+ angular.module('nitweb', ['ui.router', 'ngSanitize', 'angular-loading-bar', 'catalog', 'entities', 'docdown', 'metrics', 'users', 'grades'])
.config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
cfpLoadingBarProvider.includeSpinner = false;
}])
- .run(['$anchorScroll', function($anchorScroll) {
+ .run(function($rootScope, $anchorScroll) {
$anchorScroll.yOffset = 80;
- }])
+ $rootScope.$on('$stateChangeSuccess', function() {
+ $anchorScroll();
+ });
+ })
.config(function($stateProvider, $locationProvider) {
$stateProvider
angular
.module('ui', [])
- .controller('SearchCtrl', function(Model, $scope, $location, $document) {
-
- $scope.query = '';
-
- $scope.reset = function() {
- $scope.activeItem = 0;
- $scope.results = [];
- }
-
- $scope.update = function(e) {
- if(e.keyCode == 38) {
- $scope.selectUp();
- } else if(e.keyCode == 40) {
- $scope.selectDown();
- } else if(e.keyCode == 27) {
- $scope.selectEscape();
- } else if(e.keyCode == 13) {
- $scope.selectEnter();
- }
- }
-
- $scope.selectUp = function() {
- if($scope.activeItem > 0) {
- $scope.activeItem -= 1;
- }
- }
-
- $scope.selectDown = function() {
- if($scope.activeItem < $scope.results.length - 1) {
- $scope.activeItem += 1;
- }
- }
-
- $scope.selectEnter = function(e) {
- $location.url($scope.results[$scope.activeItem].web_url);
- $scope.reset();
- }
+ /* Search */
- $scope.selectEscape = function() {
- $scope.reset();
- }
-
- $scope.setActive = function(index) {
- $scope.activeItem = index;
- }
+ .config(function($stateProvider, $locationProvider) {
+ $stateProvider
+ .state('search', {
+ url: '/search?q&p&n',
+ controller: 'SearchCtrl',
+ controllerAs: 'vm',
+ templateUrl: 'views/search.html',
+ resolve: {
+ entities: function(Model, $q, $stateParams, $state) {
+ var d = $q.defer();
+ var query = $stateParams.q;
+ var page = $stateParams.p ? $stateParams.p : 1;
+ var limit = $stateParams.n ? $stateParams.n : 10;
+ Model.search(query, page, limit, d.resolve,
+ function() {
+ $state.go('404', null, { location: false })
+ });
+ return d.promise;
+ }
+ }
+ })
+ })
- $scope.search = function() {
- if(!$scope.query) {
- $scope.reset();
- return;
- }
- Model.search($scope.query, 10,
- function(data) {
- $scope.reset();
- $scope.results = data;
- }, function(err) {
- $scope.reset();
- $scope.error = err;
- });
- }
+ .controller('SearchCtrl', function($scope, $state, $stateParams, entities) {
+ var vm = this;
+ vm.entities = entities;
+ vm.query = $stateParams.q;
- $scope.reset();
+ $scope.$on('change-page', function(e, page, limit) {
+ $state.go('search', {q: vm.query, p: page, l: limit});
+ })
})
- .directive('searchField', function($document) {
+ .directive('uiSearchField', function($document) {
return {
restrict: 'E',
replace: true,
- controller: 'SearchCtrl',
- controllerAs: 'searchCtrl',
- templateUrl: '/directives/search/field.html',
- link: function ($scope, element, attrs) {
+ controller: function($scope, $state, $stateParams, $location, Model) {
+ var vm = this;
+ vm.search = function() {
+ if(!vm.query) {
+ vm.reset();
+ return;
+ }
+ Model.search(vm.query, 1, 8,
+ function(data) {
+ vm.reset();
+ vm.results = data;
+ }, function(err) {
+ vm.reset();
+ vm.error = err;
+ });
+ }
+
+ vm.reset = function() {
+ vm.activeItem = -1;
+ vm.results = {
+ results: []
+ };
+ }
+
+ vm.update = function(e) {
+ if(e.keyCode == 38) {
+ vm.selectUp();
+ } else if(e.keyCode == 40) {
+ vm.selectDown();
+ } else if(e.keyCode == 27) {
+ vm.selectEscape();
+ } else if(e.keyCode == 13) {
+ vm.selectEnter();
+ }
+ }
+
+ vm.selectUp = function() {
+ if(vm.activeItem >= 0) {
+ vm.activeItem -= 1;
+ }
+ }
+
+ vm.selectDown = function() {
+ if(vm.activeItem < vm.results.results.length) {
+ vm.activeItem += 1;
+ }
+ }
+
+ vm.selectEnter = function(e) {
+ if(vm.activeItem >= 0 && vm.activeItem < vm.results.results.length) {
+ $location.url(vm.results.results[vm.activeItem].web_url);
+ } else {
+ $state.go('search', {q: vm.query, p: 1});
+ }
+ vm.reset();
+ }
+
+ vm.selectEscape = function() {
+ vm.reset();
+ }
+
+ vm.setActive = function(index) {
+ vm.activeItem = index;
+ }
+
+ vm.reset();
+
+ $scope.$watch(function() {
+ return $stateParams.q;
+ }, function(q) {
+ if(q) vm.query = q;
+ });
+ },
+ controllerAs: 'vm',
+ templateUrl: 'directives/ui/search-field.html',
+ link: function ($scope, element, attrs, ctrl) {
$document.bind('click', function (event) {
var isChild = $(element).has(event.target).length > 0;
var isSelf = element[0] == event.target;
var isInside = isChild || isSelf;
if (!isInside) {
- $scope.reset();
+ ctrl.reset();
$scope.$apply();
}
});
};
})
- .directive('searchCard', function() {
- return {
- restrict: 'E',
- scope: {
- mentity: '='
- },
- replace: true,
- templateUrl: '/directives/search/card.html'
- };
- })
+ /* Filters */
.directive('uiFilters', function() {
return {
}
};
})
+
+ /* Pagination */
+
+ .directive('uiPagination', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ bindToController: {
+ pagination: '=',
+ suffix: '=?'
+ },
+ controller: function($scope) {
+ var vm = this;
+
+ $scope.$watch('pagination.pagination', function(pagination) {
+ if(!pagination) return;
+ vm.computePages(pagination);
+ })
+
+ vm.computePages = function(pagination) {
+ vm.pages = [];
+ var len = 11;
+ var page = pagination.page;
+ var start = page - Math.floor(len / 2);
+ var end = page + Math.floor(len / 2);
+
+ if(start < 1) {
+ end = Math.min(pagination.max, end + Math.abs(start) + 1)
+ start = 1
+ } else if(end > pagination.max) {
+ start = Math.max(1, start - Math.abs(end - pagination.max))
+ end = pagination.max;
+ }
+
+ for(var i = start; i <= end; i++) {
+ vm.pages.push(i);
+ }
+ }
+
+ vm.changePage = function(page, limit) {
+ if(page <= 0 || page > vm.pagination.max) return;
+ var suffix = vm.suffix ? vm.suffix : '';
+ $scope.$emit('change-page' + suffix, page, limit);
+ }
+ },
+ controllerAs: 'pagination',
+ templateUrl: 'directives/ui/pagination.html'
+ };
+ })
})();
display: table;
width: 100%;
background: #fff;
- border: 1px solid #ccc;
+ border: 1px solid #eee;
margin-top: 10px;
- box-shadow: 0 -1px 0 #e5e5e5,0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);
+ box-shadow: -1px -1px 3px rgba(0,0,0,.06), 1px 1px 3px rgba(0,0,0,.12);
}
.card-body, .card-left, .card-right {
}
.card-list > .card:first-child {
- border-top: 1px solid #ccc;
+ border-top: 1px solid #ddd;
}
.card-list > .card {
background: #FF8100;
}
+[ng-click] {
+ cursor: pointer;
+}
+
/* Body */
body {
pointer-events: none;
}
-/* search */
-
-.search-input {
- width: 100%;
-}
-
-.search-results {
- position: absolute;
- margin-top: 2px;
- right: 15px;
- left: 15px;
-}
-
-.search-results .card.active {
- background: #eee;
- border-color: #eee;
-}
-
-.card-search {
- cursor: pointer;
-}
-
/* navs */
.nav-tabs li { cursor: pointer; }
.navbar-fixed-top *:-moz-placeholder { color: #fff; }
.navbar-fixed-top *::-moz-placeholder { color: #fff; }
.navbar-fixed-top *:-ms-input-placeholder { color: #fff; }
+
+.search-input {
+ width: 100%;
+}
+
+.card-list.search-results {
+ position: absolute;
+ margin-top: 2px;
+ right: 15px;
+ left: 15px;
+ box-shadow: 1px 1px 3px rgba(0,0,0,0.12), -1px -1px 3px rgba(0,0,0,.12);
+}
+
+.search-results .card.active {
+ background: #eee;
+ border-color: #eee;
+}
+++ /dev/null
-<div>
- <h3>Tags</h3>
- <div class='container-fluid'>
- <div class='col-xs-3' ng-repeat='(tag, packages) in vm.bytags'>
- <span class='badge'>{{packages.length}}</span>
- <a ng-click='vm.scrollTo(tag)'>{{tag}}</a>
- </div>
- </div>
- <div ng-repeat='(tag, packages) in vm.bytags'>
- <entity-list list-id='{{tag}}' list-title='{{tag}}'
- list-entities='packages'
- list-object-filter='{}' />
- </div>
-</div>
+++ /dev/null
-<div>
- <entity-list list-title='Highlighted packages'
- list-entities='vm.highlighted'
- list-object-filter='{}' />
-</div>
-<div class='container-fluid'>
+<div class='container'>
<div class='page-header'>
<h2>Welcome to NitWeb!</h2>
<p class='text-muted'>The Nit knowledge base.</p>
</div>
- <ul class='nav nav-tabs' role='tablist'>
- <li role='presentation' ui-sref-active='active'>
- <a ui-sref='catalog.highlighted'>
- <span class='glyphicon glyphicon-book'/> Highlighed
- </a>
- </li>
- <li role='presentation' ui-sref-active='active'>
- <a ui-sref='catalog.required'>
- <span class='glyphicon glyphicon-book'/> Most required
- </a>
- </li>
- <li role='presentation' ui-sref-active='active'>
- <a ui-sref='catalog.tags'>
- <span class='glyphicon glyphicon-book'/> By tags
- </a>
- </li>
- </ul>
- <table class='table'>
- <tr>
- <td ng-repeat='(key, value) in vm.stats'>
- <h5><strong>{{value}}</strong> <span>{{key}}</span></h5>
- </td>
- </tr>
- </table>
+ <div ng-if='vm.stats' class='container-fluid no-padding'>
+ <span ng-repeat='(key, value) in vm.stats' class='text-muted small'>
+ <strong>{{value}}</strong> <span>{{key}}</span>
+
+ </span>
+ </div>
+ <hr/>
- <div class='container-fluid'>
- <div class='col-xs-9'>
- <div class='tab-content'>
- <div role='tabpanel' class='tab-pane fade in active'>
- <ui-view />
- </div>
+ <div class='col-md-3 col-md-push-9 no-padding' ng-if='vm.tags'>
+ <h2>Tags</h2>
+ <div class='container-fluid no-padding'>
+ <div class='col-xs-3 col-md-12' ng-repeat='(tag, packages) in vm.tags'>
+ <span class='badge'>{{packages}}</span>
+ <a ui-sref='tag({id: tag})'>{{tag}}</a>
</div>
</div>
- <div class='col-xs-3'>
- <contributor-list list-title='Maintainers'
- list-contributors='vm.contributors.maintainers' />
- <contributor-list list-title='Contributors'
- list-contributors='vm.contributors.contributors' />
+ <hr/>
+ </div>
+
+ <div class='col-md-9 col-md-pull-3 no-padding'>
+ <h2>Packages</h2>
+ <div class='card-list'>
+ <entity-card mentity='package' ng-repeat='package in vm.packages.results' />
+ </div>
+ <div class='container text-center' ng-if='vm.packages'>
+ <ui-pagination pagination='vm.packages'/>
</div>
</div>
</div>
+++ /dev/null
-<div>
- <entity-list list-title='Most required'
- list-entities='vm.required'
- list-object-filter='{}' />
-</div>
--- /dev/null
+<div class='container'>
+ <div class='col-xs-2 no-padding'>
+ <img class='avatar' style='width:100%; max-width:100px' src='https://secure.gravatar.com/avatar/{{vm.person.gravatar}}?size=100&default=retro' />
+ </div>
+ <div class='col-xs-10'>
+ <h1>
+ {{vm.person.name}}<br>
+ <small>{{vm.person.email}}</small>
+ </h1>
+ </div>
+ <hr/>
+</div>
+<br><br>
+<div class='container'>
+ <div ng-if='vm.maintaining.results.length > 0'>
+ <h3 id='maintaining'>{{vm.maintaining.total}} maintained projects</h3>
+ <div class='card-list'>
+ <entity-card mentity='package' ng-repeat='package in vm.maintaining.results' />
+ </div>
+ <div class='container text-center' ng-if='vm.maintaining'>
+ <ui-pagination pagination='vm.maintaining' suffix='1'/>
+ </div>
+ </div>
+ <div ng-if='vm.contributing.results.length > 0'>
+ <h3 id='contributing'>{{vm.contributing.total}} contributed projects</h3>
+ <div class='card-list'>
+ <entity-card mentity='package' ng-repeat='package in vm.contributing.results' />
+ </div>
+ <div class='container text-center' ng-if='vm.contributing'>
+ <ui-pagination pagination='vm.contributing' suffix='2' />
+ </div>
+ </div>
+</div>
--- /dev/null
+<div class='container'>
+ <h1>
+ {{vm.tag.tag}}
+ <small>{{vm.tag.packages.total}} packages</small>
+ </h1>
+ <hr/>
+
+ <div class='container-fluid no-padding' ng-if='vm.tag.packages.results.length > 0'>
+ <div class='card-list'>
+ <entity-card mentity='package' ng-repeat='package in vm.tag.packages.results' />
+ </div>
+ <div class='container text-center' ng-if='vm.tag.packages'>
+ <ui-pagination pagination='vm.tag.packages'/>
+ </div>
+ </div>
+</div>
<div>
- <div class='col-xs-3'>
+ <div class='col-lg-2 col-sm-3 col-xs-12'>
<ui-summary target='#summary-content' />
</div>
- <div class='col-xs-9' id='summary-content'>
+ <div class='col-lg-10 col-sm-9 col-xs-12' id='summary-content' ng-class='{
+ "col-lg-8 col-sm-6 col-xs-12": vm.mentity.class_name == "MPackage"
+ }'>
<div class='card'>
<div class='card-body'>
<div ng-if='vm.doc'>
list-object-filter='{is_init: "!true"}' />
</div>
+
+ <div class='col-lg-2 col-sm-3 col-xs-12' ng-class='{
+ "hidden": vm.mentity.class_name != "MPackage"
+ }'>
+ <br>
+ <p ng-repeat='maintainer in vm.mentity.metadata.maintainers' class='lead'>
+ <img class='avatar' src='https://secure.gravatar.com/avatar/{{maintainer.gravatar}}?size=20&default=retro' />
+ <span>
+ <a ui-sref='person({id: maintainer.name})'>{{maintainer.name}}</a>
+ </span>
+ <br>
+ </p>
+ <span ng-if='vm.mentity.metadata.license'>
+ <span class='text-muted'>
+ <a href='http://opensource.org/licenses/{{vm.mentity.metadata.license}}'>{{vm.mentity.metadata.license}}</a>
+ license
+ </span>
+ <br>
+ </span>
+
+ <div ng-if='vm.mentity.metadata.homepage || vm.mentity.metadata.issues'>
+ <h3>Links</h3>
+ <ul class='list-unstyled'>
+ <li ng-if='vm.mentity.metadata.homepage'>
+ <a href='{{vm.mentity.metadata.homepage}}'>Homepage</a>
+ </li>
+ <li ng-if='vm.mentity.metadata.browse'>
+ <a href='{{vm.mentity.metadata.browse}}'>Source Code</a>
+ </li>
+ <li ng-if='vm.mentity.metadata.issues'>
+ <a href='{{vm.mentity.metadata.issues}}'>Issues</a>
+ </li>
+ </ul>
+ </div>
+
+ <div ng-if='vm.mentity.metadata.git || vm.mentity.stats.commits'>
+ <h3>Git</h3>
+ <ul class='list-unstyled' style='white-space: nowrap; overflow: hidden; text-overflow: ellipsis;'>
+ <li ng-if='vm.mentity.metadata.git'>
+ <a href='{{vm.mentity.metadata.git}}'>{{vm.mentity.metadata.git}}</a>
+ </li>
+ <li ng-if='vm.mentity.stats.commits' class='text-muted'>
+ <br><b>{{vm.mentity.stats.commits}} commits</b>
+ </li>
+ <li ng-if='vm.mentity.metadata.last_date'><b class='text-muted'>Last:</b> {{vm.date(vm.mentity.metadata.last_date) | date: 'medium'}}</li>
+ <li ng-if='vm.mentity.metadata.first_date'><b class='text-muted'>First: </b>{{vm.date(vm.mentity.metadata.first_date) | date: 'medium'}}</li>
+ </ul>
+ </div>
+
+ <div ng-if='vm.mentity.stats'>
+ <h3>Quality</h3>
+ <ul class='list-unstyled'>
+ <li ng-if='vm.mentity.stats.documentation_score'>
+ {{vm.mentity.stats.documentation_score}}% documented
+ </li>
+ <li ng-if='vm.mentity.stats.errors' class='text-danger'>
+ {{vm.mentity.stats.errors}} errors
+ </li>
+ <li ng-if='vm.mentity.stats.warnings' class='text-warning'>
+ {{vm.mentity.stats.warnings}} warnings
+ ({{vm.mentity.stats.warnings_per_kloc}} / kloc)
+ </li>
+ </ul>
+ </div>
+
+ <div ng-if='vm.mentity.metadata.tags.length > 0'>
+ <h3>Tags</h3>
+ <span ng-repeat='tag in vm.mentity.metadata.tags'>
+ <a ui-sref='tag({id: tag})'>{{tag}}</a><span ng-if='!$last'>,</span>
+ </span>
+ </div>
+
+ <div ng-if='vm.mentity.dependencies.length > 0'>
+ <h3>Requirements</h3>
+ <span ng-repeat='parent in vm.mentity.dependencies'>
+ <a ui-sref='mentity({id: parent.name})' title='{{parent.synopsis}}'>{{parent.name}}</a><span ng-if='!$last'>,</span>
+ </span>
+ </div>
+
+ <div ng-if='vm.mentity.clients.length > 0'>
+ <h3>Clients</h3>
+ <span ng-repeat='client in vm.mentity.clients'>
+ <a ui-sref='mentity({id: client.name})' title='{{client.synopsis}}'>{{client.name}}</a><span ng-if='!$last'>,</span>
+ </span>
+ </div>
+
+ <div ng-if='vm.mentity.metadata.contributors.length > 1'>
+ <h3>Contributors</h3>
+ <ul class='list-unstyled'>
+ <li ng-repeat='contributor in vm.mentity.metadata.contributors'>
+ <img class='avatar' src='https://secure.gravatar.com/avatar/{{contributor.gravatar}}?size=20&default=retro' />
+ <a ui-sref='person({id: contributor.name})'>
+ {{contributor.name}}</a>
+ </li>
+ </ul>
+ </div>
+
+ <div ng-if='vm.mentity.stats'>
+ <h3>Stats</h3>
+ <ul class='list-unstyled'>
+ <li>{{vm.mentity.stats.mmodules}} modules</li>
+ <li>{{vm.mentity.stats.mclasses}} classes</li>
+ <li>{{vm.mentity.stats.mmethods}} methods</li>
+ <li>{{vm.mentity.stats.loc}} loc</li>
+ </ul>
+ </div>
+ </div>
</div>
<span class='glyphicon glyphicon-stats'/> Metrics
</a>
</li>
+
+ <!-- grades -->
+ <li role='presentation' ui-sref-active='active'>
+ <a ui-sref='.grades'>
+ <span class='glyphicon glyphicon-star'/> Grades
+ </a>
+ </li>
</ul>
<br>
<ui-view />
--- /dev/null
+<div class='card'>
+ <div class='card-body'>
+ <entity-rating mentity='vm.mentity' ratings='vm.ratings'>
+ </div>
+</div>
--- /dev/null
+<div class='container'>
+ <h2>
+ {{vm.entities.total}} matches for
+ <a ui-sref='search({q: vm.query})'>{{vm.query}}</a>
+ </h2>
+ <div class='card-list'>
+ <entity-card mentity='mentity' ng-repeat='mentity in vm.entities.results' />
+ </div>
+ <div class='container text-center' ng-if='vm.entities'>
+ <ui-pagination pagination='vm.entities'/>
+ </div>
+</div>
import modelize # To process and count classes and methods
redef class MPackage
+
+ # Metadata related to this package
+ var metadata = new MPackageMetadata(self)
+end
+
+# The metadata extracted from a MPackage
+class MPackageMetadata
+
+ # The mpacakge this metadata belongs to
+ var mpackage: MPackage
+
# Return the associated metadata from the `ini`, if any
- fun metadata(key: String): nullable String
- do
- var ini = self.ini
+ fun metadata(key: String): nullable String do
+ var ini = mpackage.ini
if ini == null then return null
return ini[key]
end
# The consolidated list of tags
- var tags = new Array[String]
+ var tags: Array[String] is lazy do
+ var tags = new Array[String]
+ var string = metadata("package.tags")
+ if string == null then return tags
+ for tag in string.split(",") do
+ tag = tag.trim
+ if tag.is_empty then continue
+ tags.add tag
+ end
+ if tryit != null then tags.add "tryit"
+ if apk != null then tags.add "apk"
+ if tags.is_empty then tags.add "none"
+ return tags
+ end
- # The list of maintainers
+ # The list of all maintainers
var maintainers = new Array[Person]
# The list of contributors
# The date of the oldest commit
var first_date: nullable String = null
+
+ # Key: package.maintainer`
+ var maintainer: nullable String is lazy do return metadata("package.maintainer")
+
+ # Key: `package.more_contributors`
+ var more_contributors: Array[String] is lazy do
+ var res = new Array[String]
+ var string = metadata("package.more_contributors")
+ if string == null then return res
+ for c in string.split(",") do
+ c = c.trim
+ if c.is_empty then continue
+ res.add c
+ end
+ return res
+ end
+
+ # Key: `package.license`
+ var license: nullable String is lazy do return metadata("package.license")
+
+ # Key: `upstream.tryit`
+ var tryit: nullable String is lazy do return metadata("upstream.tryit")
+
+ # Key: `upstream.apk`
+ var apk: nullable String is lazy do return metadata("upstream.apk")
+
+ # Key: `upstream.homepage`
+ var homepage: nullable String is lazy do return metadata("upstream.homepage")
+
+ # Key: `upstream.browse`
+ var browse: nullable String is lazy do return metadata("upstream.browse")
+
+ # Package git clone address
+ var git: nullable String is lazy do return metadata("upstream.git")
+
+ # Package issue tracker
+ var issues: nullable String is lazy do return metadata("upstream.issues")
end
redef class Int
# Some homepage. Eg "http://example.com/~jdoe"
var page: nullable String is writable
+ # Gravatar id
+ var gravatar: nullable String is lazy do
+ var email = self.email
+ if email == null then return null
+ return email.md5.to_lower
+ end
+
# Return a full-featured link to a person
fun to_html: String
do
if page != null then
res += "<a href=\"{page.html_escape}\">"
end
- var email = self.email
- if email != null then
- var md5 = email.md5.to_lower
- res += "<img src=\"https://secure.gravatar.com/avatar/{md5}?size=20&default=retro\"> "
+ var gravatar = self.gravatar
+ if gravatar != null then
+ res += "<img src=\"https://secure.gravatar.com/avatar/{gravatar}?size=20&default=retro\"> "
end
res += e
if page != null then res += "</a>"
# used to access the files and count source lines of code
var modelbuilder: ModelBuilder
+ # List of all packages by their names
+ var mpackages = new HashMap[String, MPackage]
+
# Packages by tag
var tag2proj = new MultiHashMap[String, MPackage]
# The score is loosely computed using other metrics
var score = new Counter[MPackage]
- # List of known people
+ # List of known people by their git string
var persons = new HashMap[String, Person]
+ # Map person short names to person objects
+ var name2person = new HashMap[String, Person]
+
+ # Package statistics cache
+ var mpackages_stats = new HashMap[MPackage, MPackageStats]
+
# Scan, register and add a contributor to a package
fun register_contrib(person: String, mpackage: MPackage): Person
do
var projs = contrib2proj[p]
if not projs.has(mpackage) then
projs.add mpackage
- mpackage.contributors.add p
+ mpackage.metadata.contributors.add p
end
+ name2person[p.name] = p
return p
end
# Compute information for a package
fun package_page(mpackage: MPackage)
do
+ mpackages[mpackage.full_name] = mpackage
+
var score = score[mpackage].to_f
var mdoc = mpackage.mdoc_or_fallback
score += 100.0
score += mdoc.content.length.score
end
+ var metadata = mpackage.metadata
-
- var tryit = mpackage.metadata("upstream.tryit")
+ var tryit = metadata.tryit
if tryit != null then
score += 1.0
end
- var apk = mpackage.metadata("upstream.apk")
+ var apk = metadata.apk
if apk != null then
score += 1.0
end
-
- var homepage = mpackage.metadata("upstream.homepage")
+ var homepage = metadata.homepage
if homepage != null then
score += 5.0
end
- var maintainer = mpackage.metadata("package.maintainer")
+ var maintainer = metadata.maintainer
if maintainer != null then
score += 5.0
var person = register_contrib(maintainer, mpackage)
- mpackage.maintainers.add person
+ mpackage.metadata.maintainers.add person
var projs = maint2proj[person]
if not projs.has(mpackage) then projs.add mpackage
end
- var license = mpackage.metadata("package.license")
+ var license = metadata.license
if license != null then
score += 5.0
end
-
- var browse = mpackage.metadata("upstream.browse")
+ var browse = metadata.browse
if browse != null then
score += 5.0
end
-
- var tags = mpackage.metadata("package.tags")
- var ts = mpackage.tags
- if tags != null then
- for t in tags.split(",") do
- t = t.trim
- if t == "" then continue
- ts.add t
- end
+ var tags = metadata.tags
+ for tag in tags do
+ tag2proj[tag].add mpackage
end
- if ts.is_empty then ts.add "none"
- if tryit != null then ts.add "tryit"
- if apk != null then ts.add "apk"
- for t in ts do
- tag2proj[t].add mpackage
+ if tags.not_empty then
+ var cat = tags.first
+ cat2proj[cat].add mpackage
+ score += tags.length.score
end
- var cat = ts.first
- cat2proj[cat].add mpackage
- score += ts.length.score
-
if deps.has(mpackage) then
score += deps[mpackage].greaters.length.score
score += deps[mpackage].direct_greaters.length.score
score += deps[mpackage].direct_smallers.length.score
end
- var contributors = mpackage.contributors
- var more_contributors = mpackage.metadata("package.more_contributors")
- if more_contributors != null then
- for c in more_contributors.split(",") do
- register_contrib(c.trim, mpackage)
- end
+ var contributors = mpackage.metadata.contributors
+ var more_contributors = metadata.more_contributors
+ for c in more_contributors do
+ register_contrib(c, mpackage)
end
score += contributors.length.to_f
-
var mmodules = 0
var mclasses = 0
var mmethods = 0
end
end
var ms = gs
- if m.is_test_suite then ms /= 100.0
+ if m.is_test 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
end
var documentation_score = (100.0 * doc_score / entity_score).to_i
self.documentation_score[mpackage] = documentation_score
-
#score += mmodules.score
score += mclasses.score
score += mmethods.score
var ini = mpackage.ini
if ini == null then return
+ var root = mpackage.root
+ if root == null then return
+
# TODO use real git info
#var repo = ini.get_or_null("upstream.git")
#var branch = ini.get_or_null("upstream.git.branch")
#var directory = ini.get_or_null("upstream.git.directory")
- var dirpath = mpackage.root.filepath
+ var dirpath = root.filepath
if dirpath == null then return
# Collect commits info
if s.length != 2 or s.last == "" then continue
# Collect date of last and first commit
- if mpackage.last_date == null then mpackage.last_date = s.first
- mpackage.first_date = s.first
+ if mpackage.metadata.last_date == null then mpackage.metadata.last_date = s.first
+ mpackage.metadata.first_date = s.first
# Count contributors
contributors.inc(s.last)
for c in contributors.sort.reverse_iterator do
register_contrib(c, mpackage)
end
+ end
+
+ # Compose package stats
+ fun mpackage_stats(mpackage: MPackage): MPackageStats do
+ var stats = new MPackageStats
+ stats.mmodules = mmodules[mpackage]
+ stats.mclasses = mclasses[mpackage]
+ stats.mmethods = mmethods[mpackage]
+ stats.loc = loc[mpackage]
+ stats.errors = errors[mpackage]
+ stats.warnings = warnings[mpackage]
+ stats.warnings_per_kloc = warnings_per_kloc[mpackage]
+ stats.documentation_score = documentation_score[mpackage]
+ stats.commits = commits[mpackage]
+ stats.score = score[mpackage]
+
+ mpackages_stats[mpackage] = stats
+ return stats
+ end
+ # Compose catalog stats
+ var catalog_stats: CatalogStats is lazy do
+ var stats = new CatalogStats
+ stats.packages = mpackages.length
+ stats.maintainers = maint2proj.length
+ stats.contributors = contrib2proj.length
+ stats.tags = tag2proj.length
+ stats.modules = mmodules.sum
+ stats.classes = mclasses.sum
+ stats.methods = mmethods.sum
+ stats.loc = loc.sum
+ return stats
end
end
+# Catalog statistics
+class CatalogStats
+
+ # Number of packages
+ var packages = 0
+
+ # Number of maintainers
+ var maintainers = 0
+
+ # Number of contributors
+ var contributors = 0
+
+ # Number of tags
+ var tags = 0
+
+ # Number of modules
+ var modules = 0
+
+ # Number of classes
+ var classes = 0
+
+ # Number of methods
+ var methods = 0
+
+ # Number of line of codes
+ var loc = 0
+end
+
+# MPackage statistics for the catalog
+class MPackageStats
+
+ # Number of modules
+ var mmodules = 0
+
+ # Number of classes
+ var mclasses = 0
+
+ # Number of methods
+ var mmethods = 0
+
+ # Number of lines of code
+ var loc = 0
+
+ # Number of errors
+ var errors = 0
+
+ # Number of warnings and advices
+ var warnings = 0
+
+ # Number of warnings per 1000 lines of code (w/kloc)
+ var warnings_per_kloc = 0
+
+ # Documentation score (between 0 and 100)
+ var documentation_score = 0
+
+ # Number of commits by package
+ var commits = 0
+
+ # Score by package
+ #
+ # The score is loosely computed using other metrics
+ var score = 0
+end
+
+# Sort the mpackages by their score
+class CatalogScoreSorter
+ super Comparator
+
+ # Catalog used to access scores
+ var catalog: Catalog
+
+ redef type COMPARED: MPackage
+
+ redef fun compare(a, b) do
+ if not catalog.mpackages_stats.has_key(a) then return 1
+ if not catalog.mpackages_stats.has_key(b) then return -1
+ var astats = catalog.mpackages_stats[a]
+ var bstats = catalog.mpackages_stats[b]
+ return bstats.score <=> astats.score
+ end
+end
+
+# Sort tabs alphabetically
+class CatalogTagsSorter
+ super Comparator
+
+ redef type COMPARED: String
+
+ redef fun compare(a, b) do return a <=> b
+end
+
# Execute a git command and return the result
fun git_run(command: String...): String
do
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_coloring is test_suite
+module test_coloring is test
-import test_suite
import coloring
class TestPOSetColorer
- super TestSuite
+ test
- fun test_ids_strictly_positive do
+ fun test_ids_strictly_positive is test do
var poset = new POSet[String]
poset.add_node "A"
var modelbuilder = toolcontext.modelbuilder
- # Disable `missing-doc` for `test_suite`
- if source != null and not nmoduledecl.get_annotations("test_suite").is_empty then
+ # Disable `missing-doc` for `test`
+ if source != null and not nmoduledecl.get_annotations("test").is_empty then
toolcontext.warning_blacklist[source].add("missing-doc")
end
redef class MModule
super AnnotatedMEntity
+
+ redef var is_test is lazy do return has_annotation("test")
end
redef class MClassDef
super AnnotatedMEntity
+
+ redef var is_test is lazy do return has_annotation("test")
end
redef class MPropDef
super AnnotatedMEntity
+
+ redef var is_test is lazy do return has_annotation("test")
+
+ redef var is_before is lazy do return has_annotation("before")
+
+ redef var is_before_all is lazy do return has_annotation("before_all")
+
+ redef var is_after is lazy do return has_annotation("after")
+
+ redef var is_after_all is lazy do return has_annotation("after_all")
end
end
var code = """
-redef init from_deserializer(v: Deserializer) do abort"""
+redef init from_deserializer(v) do abort"""
var npropdef = toolcontext.parse_propdef(code).as(AMethPropdef)
npropdefs.add npropdef
mmodule.mdoc = mdoc
mdoc.original_mentity = mmodule
end
- # Is the module a test suite?
- mmodule.is_test_suite = not decl.get_annotations("test_suite").is_empty
# Is the module generated?
mmodule.is_generated = not decl.get_annotations("generated").is_empty
end
end
end
- # Is `self` a unit test module used by `nitunit`?
- var is_test_suite: Bool = false is writable
-
# Is `self` a module generated by a tool?
#
# This flag has no effect on the semantic.
# Is `self` and abstract class?
var is_abstract: Bool is lazy do return kind == abstract_kind
+ redef var is_test is lazy do return intro.is_test
+
redef fun mdoc_or_fallback
do
# Don’t use `intro.mdoc_or_fallback` because it would create an infinite
end
private var lookup_all_definitions_cache = new HashMap2[MModule, MType, Array[MPROPDEF]]
+
+ redef var is_test is lazy do return intro.is_test
+
+ # Does self have the `before` annotation?
+ var is_before: Bool is lazy do return intro.is_before
+
+ # Does self have the `before_all` annotation?
+ var is_before_all: Bool is lazy do return intro.is_before_all
+
+ # Does self have the `after` annotation?
+ var is_after: Bool is lazy do return intro.is_after
+
+ # Does self have the `after_all` annotation?
+ var is_after_all: Bool is lazy do return intro.is_after_all
end
# A global method
end
redef fun mdoc_or_fallback do return mdoc or else mproperty.mdoc_or_fallback
+
+ # Does self have the `before` annotation?
+ var is_before = false is writable
+
+ # Does self have the `before_all` annotation?
+ var is_before_all = false is writable
+
+ # Does self have the `after` annotation?
+ var is_after = false is writable
+
+ # Does self have the `after_all` annotation?
+ var is_after_all = false is writable
end
# A local definition of a method
# Fictive entities are used internally but they should not be
# exposed to the final user.
var is_fictive: Bool = false is writable
+
+ # Is `self` created for unit testing purpose?
+ #
+ # See `nitunit`.
+ var is_test: Bool = false is writable
end
# Something that represents a concern
v.include_fictive = self.include_fictive
v.include_empty_doc = self.include_empty_doc
v.include_attribute = self.include_attribute
- v.include_test_suite = self.include_test_suite
+ v.include_test = self.include_test
end
# Searches the MEntity that matches `full_name`.
# Should we accept nitunit test suites?
#
# Default is `false`.
- var include_test_suite = false is writable
+ var include_test = false is writable
# Can we accept this `mentity` regarding its test suite status?
- fun accept_test_suite(mentity: MEntity): Bool do
- if include_test_suite then return true
+ fun accept_test(mentity: MEntity): Bool do
+ if include_test then return true
if not mentity isa MModule then return true
- return not mentity.is_test_suite
+ return not mentity.is_test
end
# Should we accept `MAttribute` instances?
if not accept_visibility(mentity) then return false
if not accept_fictive(mentity) then return false
if not accept_empty_doc(mentity) then return false
- if not accept_test_suite(mentity) then return false
+ if not accept_test(mentity) then return false
if not accept_attribute(mentity) then return false
return true
end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_model_json is test_suite
+module test_model_json is test
-import test_suite
import model_json
import frontend
class TestModelSerialization
- super TestSuite
+ test
var suite_path: String = "NIT_TESTING_PATH".environ
var lib_path: String = "{suite_path.dirname}/../../tests/test_prog"
return model
end
- fun test_refs_to_full_json do
+ fun test_refs_to_full_json is test do
var mentities = new Array[MEntity]
mentities.add model.mpackages.first
mentities.add model.mmodules.first
end
end
- fun test_packages_to_full_json do
+ fun test_packages_to_full_json is test do
for mentity in model.mpackages do
print mentity.to_pretty_full_json
end
end
- fun test_groups_to_full_json do
+ fun test_groups_to_full_json is test do
for mpackage in model.mpackages do
for mentity in mpackage.mgroups do
print mentity.to_pretty_full_json
end
end
- fun test_modules_to_full_json do
+ fun test_modules_to_full_json is test do
for mentity in model.mmodules do
print mentity.to_pretty_full_json
end
end
- fun test_classes_to_full_json do
+ fun test_classes_to_full_json is test do
for mentity in model.mclasses do
print mentity.to_pretty_full_json
end
end
- fun test_classdefs_to_full_json do
+ fun test_classdefs_to_full_json is test do
for mclass in model.mclasses do
for mentity in mclass.mclassdefs do
print mentity.to_pretty_full_json
end
end
- fun test_props_to_full_json do
+ fun test_props_to_full_json is test do
for mentity in model.mproperties do
print mentity.to_pretty_full_json
end
end
- fun test_propdefs_to_full_json do
+ fun test_propdefs_to_full_json is test do
for mprop in model.mproperties do
for mentity in mprop.mpropdefs do
print mentity.to_pretty_full_json
<div class="sidebar">
<ul class="box">
"""
- var tryit = mpackage.metadata("upstream.tryit")
+ var tryit = mpackage.metadata.metadata("upstream.tryit")
if tryit != null then
var e = tryit.html_escape
res.add "<li><a href=\"{e}\">Try<span style=\"color:white\">n</span>it!</a></li>\n"
end
- var apk = mpackage.metadata("upstream.apk")
+ var apk = mpackage.metadata.metadata("upstream.apk")
if apk != null then
var e = apk.html_escape
res.add "<li><a href=\"{e}\">Android apk</a></li>\n"
res.add """</ul>\n<ul class="box">\n"""
- var homepage = mpackage.metadata("upstream.homepage")
+ var homepage = mpackage.metadata.metadata("upstream.homepage")
if homepage != null then
var e = homepage.html_escape
res.add "<li><a href=\"{e}\">{e}</a></li>\n"
end
- for maintainer in mpackage.maintainers do
+ for maintainer in mpackage.metadata.maintainers do
res.add "<li>{maintainer.to_html}</li>"
end
- var license = mpackage.metadata("package.license")
+ var license = mpackage.metadata.metadata("package.license")
if license != null then
var e = license.html_escape
res.add "<li><a href=\"http://opensource.org/licenses/{e}\">{e}</a> license</li>\n"
res.add "</ul>\n"
res.add "<h3>Source Code</h3>\n<ul class=\"box\">\n"
- var browse = mpackage.metadata("upstream.browse")
+ var browse = mpackage.metadata.metadata("upstream.browse")
if browse != null then
var e = browse.html_escape
res.add "<li><a href=\"{e}\">{e}</a></li>\n"
end
- var git = mpackage.metadata("upstream.git")
+ var git = mpackage.metadata.metadata("upstream.git")
if git != null then
var e = git.html_escape
res.add "<li><tt>{e}</tt></li>\n"
end
- var last_date = mpackage.last_date
+ var last_date = mpackage.metadata.last_date
if last_date != null then
var e = last_date.html_escape
res.add "<li>most recent commit: {e}</li>\n"
end
- var first_date = mpackage.first_date
+ var first_date = mpackage.metadata.first_date
if first_date != null then
var e = first_date.html_escape
res.add "<li>oldest commit: {e}</li>\n"
res.add "<h3>Tags</h3>\n"
var ts2 = new Array[String]
- for t in mpackage.tags do
+ for t in mpackage.metadata.tags do
t = t.html_escape
ts2.add "<a href=\"../index.html#tag_{t}\">{t}</a>"
end
end
end
- var contributors = mpackage.contributors
+ var contributors = mpackage.metadata.contributors
if not contributors.is_empty then
res.add "<h3>Contributors</h3>\n<ul class=\"box\">"
for c in contributors do
res.add "<tr>"
res.add "<td><a href=\"p/{p.name}.html\">{p.name}</a></td>"
var maint = "?"
- if p.maintainers.not_empty then maint = p.maintainers.first.name.html_escape
+ if p.metadata.maintainers.not_empty then maint = p.metadata.maintainers.first.name.html_escape
res.add "<td>{maint}</td>"
- res.add "<td>{p.contributors.length}</td>"
+ res.add "<td>{p.metadata.contributors.length}</td>"
if deps.not_empty then
res.add "<td>{deps[p].greaters.length-1}</td>"
res.add "<td>{deps[p].direct_greaters.length}</td>"
TestSuites are Nit files that define a set of TestCase for a particular module.
-The test suite module must be declared using the `test_suite` annotation.
+The test suite module must be declared using the `test` annotation.
The structure of a test suite is the following:
# test suite for module `foo`
- module test_foo is test_suite
+ module test_foo is test
- import test_suite # import the `TestSuite` class and the `test_suite` annotation
import foo # can be intrude to test private things
class TestFoo
- super TestSuite
+ test
# test case for `foo::Foo::baz`
- fun test_baz do
+ fun baz is test do
var subject = new Foo
assert subject.baz(1, 2) == 3
end
$ nitunit foo.nit -t my_test_suite.nit
-`nitunit` will execute a test for each method named `test_*` in a class named `Test*`
+`nitunit` will execute a test for each method annotated with `test` in a class also annotated with `test`
so multiple tests can be executed for a single method:
class TestFoo
- super TestSuite
+ test
- fun test_baz_1 do
+ fun baz_1 is test do
var subject = new Foo
assert subject.baz(1, 2) == 3
end
- fun test_baz_2 do
+ fun baz_2 is test do
var subject = new Foo
assert subject.baz(1, -2) == -1
end
`TestSuites` also provide methods to configure the test run:
-`before_test` and `after_test`: methods called before/after each test case.
+`before` and `after` annotations can be added to methods that must be called before/after each test case.
They can be used to factorize repetitive tasks:
class TestFoo
- super TestSuite
+ test
var subject: Foo is noinit
# Method executed before each test
- redef fun before_test do
+ redef fun set_up is before do
subject = new Foo
end
- fun test_baz_1 do
+ fun baz_1 is test do
assert subject.baz(1, 2) == 3
end
- fun test_baz_2 do
+ fun baz_2 is test do
assert subject.baz(1, -2) == -1
end
end
When using custom test attributes, a empty init must be declared to allow automatic test running.
-`before_module` and `after_module`: methods called before/after each test suite.
+`before_all` and `after_all` annotations can be set on methods that must be called before/after each test suite.
They have to be declared at top level:
module test_bdd_connector
- import test_suite
import bdd_connector
# Testing the bdd_connector
class TestConnector
- super TestSuite
+ test
# test cases using a server
end
# Method executed before testing the module
- redef fun before_module do
+ fun before_module is before_all do
# start server before all test cases
end
# Method executed after testing the module
- redef fun after_module do
+ fun after_module is after_all do
# stop server after all test cases
end
fun gen_unit(mmodule: MModule, test_file: String): Template do
var with_private = toolcontext.opt_gen_private.value
var tpl = new Template
- tpl.addn "module test_{mmodule.name} is test_suite"
+ tpl.addn "module test_{mmodule.name} is test"
tpl.addn ""
- tpl.addn "import test_suite"
if with_private then
tpl.addn "intrude import {mmodule.name}"
else
if mclassdef.mclass.kind != concrete_kind then continue
tpl.addn ""
tpl.addn "class Test{mclassdef.name}"
- tpl.addn "\tsuper TestSuite"
+ tpl.addn "\ttest"
for mpropdef in mclassdef.mpropdefs do
if not mpropdef isa MMethodDef then continue
var mproperty = mpropdef.mproperty
if not with_private and mproperty.visibility <= protected_visibility then continue
var case_name = case_name(mpropdef)
tpl.addn ""
- tpl.addn "\tfun {case_name} do"
+ tpl.addn "\tfun {case_name} is test do"
tpl.addn "\t\tassert not_implemented: false # TODO remove once implemented"
tpl.addn ""
tpl.addn gen_init(mclassdef)
import testing_base
import html
-private import annotation
+private import parse_annotations
private import realtime
redef class ToolContext
var toolcontext = mbuilder.toolcontext
var suite = new TestSuite(mmodule, toolcontext)
# method to execute before all tests in the module
- var before_module = mmodule.before_test
- if before_module != null then
+ for mmethod in mmodule.before_all do
toolcontext.modelbuilder.total_tests += 1
- suite.before_module = new TestCase(suite, before_module, toolcontext)
+ suite.before_all.add new TestCase(suite, mmethod, toolcontext)
end
# generate all test cases
for mclassdef in mmodule.mclassdefs do
if not mclassdef.is_test then continue
if not suite_match_pattern(mclassdef) then continue
toolcontext.modelbuilder.total_classes += 1
+
+ var before = mclassdef.before
+ var after = mclassdef.after
+
for mpropdef in mclassdef.mpropdefs do
if not mpropdef isa MMethodDef or not mpropdef.is_test then continue
if not case_match_pattern(mpropdef) then continue
toolcontext.modelbuilder.total_tests += 1
var test = new TestCase(suite, mpropdef, toolcontext)
- suite.add_test test
+ test.before = before
+ test.after = after
+ suite.test_cases.add test
end
end
# method to execute after all tests in the module
- var after_module = mmodule.after_test
- if after_module != null then
+ for mmethod in mmodule.after_all do
toolcontext.modelbuilder.total_tests += 1
- suite.after_module = new TestCase(suite, after_module, toolcontext)
+ suite.after_all.add new TestCase(suite, mmethod, toolcontext)
end
suite.run
return suite
# List of `TestCase` to be executed in this suite.
var test_cases = new Array[TestCase]
- # Add a `TestCase` to the suite.
- fun add_test(case: TestCase) do test_cases.add case
-
- # Test to be executed before the whole test suite.
- var before_module: nullable TestCase = null
+ # Tests to be executed before the whole test suite.
+ var before_all = new Array[TestCase]
- # Test to be executed after the whole test suite.
- var after_module: nullable TestCase = null
+ # Tests to be executed after the whole test suite.
+ var after_all = new Array[TestCase]
# Display test suite status in std-out.
fun show_status do
var test_cases = self.test_cases.to_a
- if before_module != null then test_cases.add before_module.as(not null)
- if after_module != null then test_cases.add after_module.as(not null)
+ test_cases.add_all before_all
+ test_cases.add_all after_all
toolcontext.show_unit_status("Test-suite of module " + mmodule.full_name, test_cases)
end
end
toolcontext.info("Execute test-suite {mmodule.name}", 1)
- var before_module = self.before_module
- var after_module = self.after_module
-
- if before_module != null then
+ for before_module in before_all do
before_module.run
toolcontext.clear_progress_bar
toolcontext.show_unit(before_module)
toolcontext.clear_progress_bar
toolcontext.show_unit(case)
end
- if after_module != null then
+ for after_module in after_all do
after_module.fail "Nitunit Error: before_module test failed"
toolcontext.clear_progress_bar
toolcontext.show_unit(after_module)
show_status
end
- if not after_module == null then
+ for after_module in after_all do
after_module.run
toolcontext.clear_progress_bar
toolcontext.show_unit(after_module)
# Write the test unit for `self` in a nit compilable file.
fun write_to_nit do
var file = new Template
- file.addn "intrude import test_suite"
+ file.addn "intrude import core"
file.addn "import {mmodule.name}\n"
file.addn "var name = args.first"
- var before_module = self.before_module
- if before_module != null then
+ for before_module in before_all do
before_module.write_to_nit(file)
end
for case in test_cases do
case.write_to_nit(file)
end
- var after_module = self.after_module
- if after_module != null then
+ for after_module in after_all do
after_module.write_to_nit(file)
end
file.write_to_file("{test_file}.nit")
# Test method to be compiled and tested.
var test_method: MMethodDef
+ # Cases to execute before this one
+ var before = new Array[MMethodDef]
+
+ # Cases to execute after this one
+ var after = new Array[MMethodDef]
+
redef fun full_name do return test_method.full_name
redef fun location do return test_method.location
if test_method.mproperty.is_toplevel then
file.addn "\t{name}"
else
- file.addn "\tvar subject = new {test_method.mclassdef.name}.nitunit"
- file.addn "\tsubject.before_test"
+ file.addn "\tvar subject = new {test_method.mclassdef.name}.intern"
+ for mmethod in before do
+ file.addn "\tsubject.{mmethod.name}"
+ end
file.addn "\tsubject.{name}"
- file.addn "\tsubject.after_test"
+ for mmethod in after do
+ file.addn "\tsubject.{mmethod.name}"
+ end
end
file.addn "end"
end
end
end
-redef class MMethodDef
- # TODO use annotations?
-
- # Is the method a test_method?
- # i.e. begins with "test_"
- private fun is_test: Bool do return name.has_prefix("test_")
+redef class MClassDef
+ # Methods tagged with `before` in this class definition
+ private fun before: Array[MMethodDef] do
+ var res = new Array[MMethodDef]
+ for mpropdef in mpropdefs do
+ if mpropdef isa MMethodDef and mpropdef.is_before then
+ res.add mpropdef
+ end
+ end
+ var in_hierarchy = self.in_hierarchy
+ if in_hierarchy == null then return res
+ for mclassdef in in_hierarchy.direct_greaters do
+ res.add_all mclassdef.before
+ end
+ return res
+ end
- # Is the method a "before_module"?
- private fun is_before_module: Bool do return name == "before_module"
+ # Methods tagged with `before_all` in this class definition
+ private fun before_all: Array[MMethodDef] do
+ var res = new Array[MMethodDef]
+ for mpropdef in mpropdefs do
+ if mpropdef isa MMethodDef and mpropdef.is_before_all then
+ res.add mpropdef
+ end
+ end
+ var in_hierarchy = self.in_hierarchy
+ if in_hierarchy == null then return res
+ for mclassdef in in_hierarchy.direct_greaters do
+ res.add_all mclassdef.before_all
+ end
+ return res
+ end
- # Is the method a "after_module"?
- private fun is_after_module: Bool do return name == "after_module"
-end
+ # Methods tagged with `after` in this class definition
+ private fun after: Array[MMethodDef] do
+ var res = new Array[MMethodDef]
+ for mpropdef in mpropdefs do
+ if mpropdef isa MMethodDef and mpropdef.is_after then
+ res.add mpropdef
+ end
+ end
+ var in_hierarchy = self.in_hierarchy
+ if in_hierarchy == null then return res
+ for mclassdef in in_hierarchy.direct_greaters do
+ res.add_all mclassdef.after
+ end
+ return res
+ end
-redef class MClassDef
- # Is the class a TestClass?
- # i.e. is a subclass of `TestSuite`
- private fun is_test: Bool do
+ # Methods tagged with `after_all` in this class definition
+ private fun after_all: Array[MMethodDef] do
+ var res = new Array[MMethodDef]
+ for mpropdef in mpropdefs do
+ if mpropdef isa MMethodDef and mpropdef.is_after_all then
+ res.add mpropdef
+ end
+ end
var in_hierarchy = self.in_hierarchy
- if in_hierarchy == null then return false
- for sup in in_hierarchy.greaters do
- if sup.name == "TestSuite" then return true
+ if in_hierarchy == null then return res
+ for mclassdef in in_hierarchy.direct_greaters do
+ res.add_all mclassdef.after_all
end
- return false
+ return res
end
end
redef class MModule
- # "before_module" method for this module.
- private fun before_test: nullable MMethodDef do
+ # Methods tagged with `before_all` at the module level (in `Sys`)
+ private fun before_all: Array[MMethodDef] do
for mclassdef in mclassdefs do
- if not mclassdef.name == "Sys" then continue
- for mpropdef in mclassdef.mpropdefs do
- if mpropdef isa MMethodDef and mpropdef.is_before_module then return mpropdef
- end
+ if mclassdef.name == "Sys" then return mclassdef.before_all
end
- return null
+ return new Array[MMethodDef]
end
- # "after_module" method for this module.
- private fun after_test: nullable MMethodDef do
+ # Methods tagged with `after_all` at the module level (in `Sys`)
+ private fun after_all: Array[MMethodDef] do
for mclassdef in mclassdefs do
- if not mclassdef.name == "Sys" then continue
- for mpropdef in mclassdef.mpropdefs do
- if mpropdef isa MMethodDef and mpropdef.is_after_module then return mpropdef
- end
+ if mclassdef.name == "Sys" then return mclassdef.after_all
end
- return null
+ return new Array[MMethodDef]
end
end
# Run NitUnit test suite for `mmodule` (if it is one).
fun test_unit(mmodule: MModule): nullable HTMLTag do
# is the module a test_suite?
- if get_mmodule_annotation("test_suite", mmodule) == null then return null
+ if not mmodule.is_test then return null
toolcontext.info("nitunit: test-suite {mmodule}", 2)
var tester = new NitUnitTester(self)
module api_catalog
-import web_base
+import api_model
import catalog
redef class NitwebConfig
#
# This method should be called at nitweb startup.
fun build_catalog do
- var catalog = new Catalog(modelbuilder)
- for mpackage in model.mpackages do
- catalog.deps.add_node(mpackage)
- for mgroup in mpackage.mgroups do
- for mmodule in mgroup.mmodules do
- for imported in mmodule.in_importation.direct_greaters do
- var ip = imported.mpackage
- if ip == null or ip == mpackage then continue
- catalog.deps.add_edge(mpackage, ip)
- end
- end
- end
- catalog.git_info(mpackage)
- catalog.package_page(mpackage)
- end
- self.catalog = catalog
+ self.catalog = new Catalog(modelbuilder)
+ self.catalog.build_catalog(model.mpackages)
end
end
redef class APIRouter
redef init do
super
- use("/catalog/highlighted", new APICatalogHighLighted(config))
- use("/catalog/required", new APICatalogMostRequired(config))
- use("/catalog/bytags", new APICatalogByTags(config))
- use("/catalog/contributors", new APICatalogContributors(config))
+ use("/catalog/packages/", new APICatalogPackages(config))
use("/catalog/stats", new APICatalogStats(config))
+
+ use("/catalog/tags", new APICatalogTags(config))
+ use("/catalog/tag/:tid", new APICatalogTag(config))
+
+ use("/catalog/person/:pid", new APICatalogPerson(config))
+ use("/catalog/person/:pid/maintaining", new APICatalogMaintaining(config))
+ use("/catalog/person/:pid/contributing", new APICatalogContributing(config))
end
end
abstract class APICatalogHandler
super APIHandler
- # List the 10 best packages from `cpt`
- fun list_best(cpt: Counter[MPackage]): JsonArray do
- var res = new JsonArray
- var best = cpt.sort
- for i in [1..10] do
- if i > best.length then break
- res.add best[best.length-i]
- end
- return res
- end
+ # Sorter used to sort packages
+ #
+ # Sorting is based on mpackage score.
+ var mpackages_sorter = new CatalogScoreSorter(config.catalog) is lazy
+end
- # List packages by group.
- fun list_by(map: MultiHashMap[Object, MPackage]): JsonObject do
- var res = new JsonObject
- var keys = map.keys.to_a
- alpha_comparator.sort(keys)
- for k in keys do
- var projs = map[k].to_a
- alpha_comparator.sort(projs)
- res[k.to_s.html_escape] = new JsonArray.from(projs)
- end
- return res
+# Get all the packages from the catalog using pagination
+#
+# `GET /packages?p=1&n=10`: get the list of catalog by page
+class APICatalogPackages
+ super APICatalogHandler
+
+ redef fun get(req, res) do
+ var page = req.int_arg("p")
+ var limit = req.int_arg("n")
+ var mpackages = config.catalog.mpackages.values.to_a
+ mpackages_sorter.sort(mpackages)
+ var response = new JsonArray.from(mpackages)
+ res.json paginate(response, response.length, page, limit)
end
end
+# Get the catalog statistics
+#
+# `GET /stats`: return the catalog statistics
class APICatalogStats
super APICatalogHandler
redef fun get(req, res) do
- var obj = new JsonObject
- obj["packages"] = config.model.mpackages.length
- obj["maintainers"] = config.catalog.maint2proj.length
- obj["contributors"] = config.catalog.contrib2proj.length
- obj["modules"] = config.catalog.mmodules.sum
- obj["classes"] = config.catalog.mclasses.sum
- obj["methods"] = config.catalog.mmethods.sum
- obj["loc"] = config.catalog.loc.sum
- res.json obj
+ res.json config.catalog.catalog_stats
end
end
-class APICatalogHighLighted
+# Get all the tags from the catalog
+#
+# `GET /tags`: the list of tags associated with their number of packages
+class APICatalogTags
super APICatalogHandler
- redef fun get(req, res) do res.json list_best(config.catalog.score)
+ # Sorter to sort tags alphabetically
+ var tags_sorter = new CatalogTagsSorter
+
+ redef fun get(req, res) do
+ var obj = new JsonObject
+
+ var tags = config.catalog.tag2proj.keys.to_a
+ tags_sorter.sort(tags)
+
+ for tag in tags do
+ if not config.catalog.tag2proj.has_key(tag) then continue
+ obj[tag] = config.catalog.tag2proj[tag].length
+ end
+ res.json obj
+ end
end
-class APICatalogMostRequired
+# Get the packages related to a tag
+#
+# `GET /tag/:tid?p=1&n=10`: return a paginated list of packages
+class APICatalogTag
super APICatalogHandler
redef fun get(req, res) do
- if config.catalog.deps.not_empty then
- var reqs = new Counter[MPackage]
- for p in config.model.mpackages do
- reqs[p] = config.catalog.deps[p].smallers.length - 1
- end
- res.json list_best(reqs)
+ var page = req.int_arg("p")
+ var limit = req.int_arg("n")
+ var id = req.param("tid")
+ if id == null then
+ res.api_error(400, "Missing tag")
+ return
+ end
+ id = id.from_percent_encoding
+ if not config.catalog.tag2proj.has_key(id) then
+ res.api_error(404, "Tag not found")
return
end
- res.json new JsonArray
+ var obj = new JsonObject
+ obj["tag"] = id
+ var mpackages = config.catalog.tag2proj[id]
+ mpackages_sorter.sort(mpackages)
+ var response = new JsonArray.from(mpackages)
+ obj["packages"] = paginate(response, response.length, page, limit)
+ res.json obj
end
end
-class APICatalogByTags
+# Get a person existing in the catalog
+#
+# `GET /person/:pid`: get the person with `pid`
+class APICatalogPerson
super APICatalogHandler
- redef fun get(req, res) do res.json list_by(config.catalog.tag2proj)
+ # Get the person with `:pid` or throw a 404 error
+ fun get_person(req: HttpRequest, res: HttpResponse): nullable Person do
+ var id = req.param("pid")
+ if id == null then
+ res.api_error(400, "Missing package full_name")
+ return null
+ end
+ id = id.from_percent_encoding
+ if not config.catalog.name2person.has_key(id) then
+ res.api_error(404, "Person not found")
+ return null
+ end
+ return config.catalog.name2person[id]
+ end
+
+ redef fun get(req, res) do
+ var person = get_person(req, res)
+ if person == null then return
+ res.json person
+ end
end
-class APICatalogContributors
- super APICatalogHandler
+# Get the list of mpackages maintained by a person
+#
+# `GET /person/:pid/maintaining?p=1&n=10`: return a paginated list of packages
+class APICatalogMaintaining
+ super APICatalogPerson
redef fun get(req, res) do
- var obj = new JsonObject
- obj["maintainers"] = new JsonArray.from(config.catalog.maint2proj.keys)
- obj["contributors"] = new JsonArray.from(config.catalog.contrib2proj.keys)
- res.json obj
+ var person = get_person(req, res)
+ if person == null then return
+
+ var page = req.int_arg("p")
+ var limit = req.int_arg("n")
+ var array = new Array[MPackage]
+ if config.catalog.maint2proj.has_key(person) then
+ array = config.catalog.maint2proj[person].to_a
+ end
+ mpackages_sorter.sort(array)
+ var response = new JsonArray.from(array)
+ res.json paginate(response, response.length, page, limit)
+ end
+end
+
+# Get the list of mpackages contributed by a person
+#
+# `GET /person/:pid/contributing?p=1&n=10`: return a paginated list of packages
+class APICatalogContributing
+ super APICatalogPerson
+
+ redef fun get(req, res) do
+ var person = get_person(req, res)
+ if person == null then return
+
+ var page = req.int_arg("p")
+ var limit = req.int_arg("n")
+ var array = new Array[MPackage]
+ if config.catalog.contrib2proj.has_key(person) then
+ array = config.catalog.contrib2proj[person].to_a
+ end
+ mpackages_sorter.sort(array)
+ var response = new JsonArray.from(array)
+ res.json paginate(response, response.length, page, limit)
+ end
+end
+
+redef class APIEntity
+ redef fun get(req, res) do
+ var mentity = mentity_from_uri(req, res)
+ if mentity == null then return
+
+ # Special case for packages (catalog view)
+ if mentity isa MPackage then
+ res.raw_json mentity.to_full_catalog_json(plain=true, config.catalog)
+ else
+ res.raw_json mentity.to_full_json
+ end
+ end
+end
+
+redef class APISearch
+ super APICatalogHandler
+
+ redef fun search(query, limit) do
+ var index = config.view.index
+
+ # lookup by name prefix
+ var matches = index.find_by_name_prefix(query).uniq.
+ sort(lname_sorter, name_sorter, kind_sorter)
+ matches = matches.rerank.sort(vis_sorter, score_sorter)
+
+ # lookup by tags
+ var malus = matches.length
+ if config.catalog.tag2proj.has_key(query) then
+ for mpackage in config.catalog.tag2proj[query] do
+ matches.add new IndexMatch(mpackage, malus)
+ malus += 1
+ end
+ matches = matches.uniq.rerank.sort(vis_sorter, score_sorter)
+ end
+
+ # lookup by full_name prefix
+ malus = matches.length
+ var full_matches = new IndexMatches
+ for match in index.find_by_full_name_prefix(query).
+ sort(lfname_sorter, fname_sorter) do
+ match.score += 1
+ full_matches.add match
+ end
+ matches = matches.uniq
+
+ # lookup by similarity
+ malus = matches.length
+ var sim_matches = new IndexMatches
+ for match in index.find_by_similarity(query).sort(score_sorter, lname_sorter, name_sorter) do
+ if match.score > query.length then break
+ match.score += 1
+ sim_matches.add match
+ end
+ matches.add_all sim_matches
+ matches = matches.uniq
+ return matches.rerank.sort(vis_sorter, score_sorter).mentities
+ end
+
+ private var score_sorter = new ScoreComparator
+ private var vis_sorter = new VisibilityComparator
+ private var name_sorter = new NameComparator
+ private var lname_sorter = new NameLengthComparator
+ private var fname_sorter = new FullNameComparator
+ private var lfname_sorter = new FullNameLengthComparator
+ private var kind_sorter = new MEntityComparator
+end
+
+redef class Catalog
+
+ # Build the catalog from `mpackages`
+ fun build_catalog(mpackages: Array[MPackage]) do
+ # Compute the poset
+ for p in mpackages do
+ var g = p.root
+ assert g != null
+ modelbuilder.scan_group(g)
+
+ deps.add_node(p)
+ for gg in p.mgroups do for m in gg.mmodules do
+ for im in m.in_importation.direct_greaters do
+ var ip = im.mpackage
+ if ip == null or ip == p then continue
+ deps.add_edge(p, ip)
+ end
+ end
+ end
+ # Build the catalog
+ for mpackage in mpackages do
+ package_page(mpackage)
+ git_info(mpackage)
+ mpackage_stats(mpackage)
+ end
+ end
+end
+
+redef class MPackageMetadata
+ serialize
+
+ redef fun core_serialize_to(v) do
+ super
+ v.serialize_attribute("license", license)
+ v.serialize_attribute("maintainers", maintainers)
+ v.serialize_attribute("contributors", contributors)
+ v.serialize_attribute("tags", tags)
+ v.serialize_attribute("tryit", tryit)
+ v.serialize_attribute("apk", apk)
+ v.serialize_attribute("homepage", homepage)
+ v.serialize_attribute("browse", browse)
+ v.serialize_attribute("git", git)
+ v.serialize_attribute("issues", issues)
+ v.serialize_attribute("first_date", first_date)
+ v.serialize_attribute("last_date", last_date)
+ end
+end
+
+# Catalog statistics
+redef class CatalogStats
+ serialize
+
+ redef fun core_serialize_to(v) do
+ super
+ v.serialize_attribute("packages", packages)
+ v.serialize_attribute("maintainers", maintainers)
+ v.serialize_attribute("contributors", contributors)
+ v.serialize_attribute("tags", tags)
+ v.serialize_attribute("modules", modules)
+ v.serialize_attribute("classes", classes)
+ v.serialize_attribute("methods", methods)
+ v.serialize_attribute("loc", loc)
+ end
+end
+
+# MPackage statistics for the catalog
+redef class MPackageStats
+ serialize
+
+ redef fun core_serialize_to(v) do
+ super
+ v.serialize_attribute("mmodules", mmodules)
+ v.serialize_attribute("mclasses", mclasses)
+ v.serialize_attribute("mmethods", mmethods)
+ v.serialize_attribute("loc", loc)
+ v.serialize_attribute("errors", errors)
+ v.serialize_attribute("warnings", warnings)
+ v.serialize_attribute("warnings_per_kloc", warnings_per_kloc)
+ v.serialize_attribute("documentation_score", documentation_score)
+ v.serialize_attribute("commits", commits)
+ v.serialize_attribute("score", score)
end
end
redef class Person
- super Serializable
+ serialize
redef fun core_serialize_to(v) do
+ super
v.serialize_attribute("name", name)
v.serialize_attribute("email", email)
- v.serialize_attribute("page", page)
- v.serialize_attribute("hash", (email or else "").md5.to_lower)
+ v.serialize_attribute("gravatar", gravatar)
+ end
+end
+
+redef class MPackage
+ # Serialize the full catalog version of `self` to JSON
+ #
+ # See: `FullCatalogSerializer`
+ fun to_full_catalog_json(catalog: Catalog, plain, pretty: nullable Bool): String do
+ var stream = new StringWriter
+ var serializer = new FullCatalogSerializer(stream, catalog)
+ serializer.plain_json = plain or else false
+ serializer.pretty_json = pretty or else false
+ serializer.serialize self
+ stream.close
+ return stream.to_s
+ end
+
+ redef fun core_serialize_to(v) do
+ super
+ v.serialize_attribute("metadata", metadata)
+ if v isa FullCatalogSerializer then
+ v.serialize_attribute("stats", v.catalog.mpackages_stats[self])
+
+ var parents = v.catalog.deps[self].direct_greaters.to_a
+ v.serialize_attribute("dependencies", v.deps_to_json(parents))
+ var children = v.catalog.deps[self].direct_smallers.to_a
+ v.serialize_attribute("clients", v.deps_to_json(children))
+ end
+ end
+end
+
+# CatalogSerializer decorate the Package JSON with full catalog metadata
+#
+# See MEntity::to_full_catalog_json.
+class FullCatalogSerializer
+ super FullJsonSerializer
+
+ # Catalog used to decorate the MPackages
+ var catalog: Catalog
+
+ private fun deps_to_json(mpackages: Array[MPackage]): JsonArray do
+ var res = new JsonArray
+ for mpackage in mpackages do
+ res.add dep_to_json(mpackage)
+ end
+ return res
+ end
+
+ private fun dep_to_json(mpackage: MPackage): JsonObject do
+ var obj = new JsonObject
+ obj["name"] = mpackage.name
+ var mdoc = mpackage.mdoc_or_fallback
+ if mdoc != null then
+ obj["synopsis"] = mdoc.synopsis.write_to_string
+ end
+ return obj
end
end
super APIList
redef fun get(req, res) do
- var q = req.string_arg("q")
- if q == null then
- res.json new JsonArray
+ var query = req.string_arg("q")
+ if query == null then
+ res.api_error(400, "Missing search string")
return
end
- var n = req.int_arg("n")
- res.json new JsonArray.from(config.view.find(q, n))
+ var page = req.int_arg("p")
+ var limit = req.int_arg("n")
+ var response = new JsonArray.from(search(query, limit))
+ res.json paginate(response, response.length, page, limit)
+ end
+
+ fun search(query: String, limit: nullable Int): Array[MEntity] do
+ return config.view.find(query)
end
end
view.include_fictive = true
view.include_empty_doc = true
view.include_attribute = true
- view.include_test_suite = true
+ view.include_test = true
return view
end
end
end
return mentity
end
+
+ # Paginate a json array
+ #
+ # Returns only a subset of `results` depending on the current `page` and the
+ # number of elements to return set by `limit`.
+ #
+ # Transforms the json array into an object:
+ # ~~~json
+ # {
+ # "page": 2,
+ # "limit": 10,
+ # "results: [ ... ],
+ # "max": 5,
+ # "total": 49
+ # }
+ # ~~~
+ fun paginate(results: JsonArray, count: Int, page, limit: nullable Int): JsonObject do
+ if page == null or page <= 0 then page = 1
+ if limit == null or limit <= 0 then limit = 20
+
+ var max = count / limit
+ if max == 0 then
+ page = 1
+ max = 1
+ else if page > max then
+ page = max
+ end
+
+ var lstart = (page - 1) * limit
+ var lend = limit
+ if lstart + lend > count then lend = count - lstart
+
+ var res = new JsonObject
+ res["page"] = page
+ res["limit"] = limit
+ res["results"] = new JsonArray.from(results.subarray(lstart, lend))
+ res["max"] = max
+ res["total"] = count
+ return res
+ end
end
# A Rooter dedicated to APIHandlers.
--- /dev/null
+# Custom:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Inspect:
+<A#0 b:true, c:'a', f:0.123, i:1234, serialization_specific_name:"asdf", n:null>
+
+# Custom:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Inspect:
+<B#0 b:false, c:'b', f:123.123, i:2345, serialization_specific_name:"hjkl", n:…>
+
+# Custom:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Inspect:
+<C#0 a:<A#1>, b:<B#2>, aa:<A#1>>
+
+# Custom:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# Inspect:
+<D#0 b:false, c:'b', f:123.123, i:2345, serialization_specific_name:"new line …>
+
+# Custom:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Inspect:
+<E#0 a:<Array#1>, b:<Array#2>>
+
+# Custom:
+<F: 2222>
+
+# Inspect:
+<F#0 n:2222>
+
+# Custom:
+<F: 33.33>
+
+# Inspect:
+<F#0 n:33.33>
+
+# Custom:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Inspect:
+<G#0 hs:<HashSet#1>, s:<ArraySet#2>, hm:<HashMap#3>, am:<ArrayMap#4>>
+
--- /dev/null
+# 1. Nit source:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 2. MsgPack:
+\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+{
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+}
+
+# 4. Back in Nit (no metadata):
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 1. Nit source:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 2. MsgPack:
+\x88\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72
+
+# 3. JSON:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+}
+
+# 4. Back in Nit (no metadata):
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 1. Nit source:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 2. MsgPack:
+\x83\xA1\x61\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0\xA1\x62\x88\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72\xA2\x61\x61\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+{
+ "a": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ },
+ "b": {
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+ },
+ "aa": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ }
+}
+
+# 4. Back in Nit (no metadata):
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
+# 1. Nit source:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 2. MsgPack:
+\x89\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xAE\x6E\x65\x77\x20\x6C\x69\x6E\x65\x20\x2D\x3E\x0A\x3C\x2D\xA1\x6E\xC0\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA6\x09\x66\x22\x0D\x5C\x2F\xA1\x64\xC0
+
+# 3. JSON:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "new line ->\n<-",
+ "n": null,
+ "ii": 1111,
+ "ss": "\tf\"\r\\/",
+ "d": null
+}
+
+# 4. Back in Nit (no metadata):
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
+# 1. Nit source:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 2. MsgPack:
+\x82\xA1\x61\x93\xA5\x68\x65\x6C\x6C\x6F\xCD\x04\xD2\xCB\x40\x5E\xD9\x99\x99\x99\x99\x9A\xA1\x62\x93\xA5\x68\x65\x6C\x6C\x61\xCD\x09\x29\xCB\x40\x6D\x50\x00\x00\x00\x00\x00
+
+# 3. JSON:
+{
+ "a": ["hello", 1234, 123.4],
+ "b": ["hella", 2345, 234.5]
+}
+
+# 4. Back in Nit (no metadata):
+<E: a: ; b: >
+
+# 1. Nit source:
+<F: 2222>
+
+# 2. MsgPack:
+\x81\xA1\x6E\xCD\x08\xAE
+
+# 3. JSON:
+{
+ "n": 2222
+}
+
+# 4. Back in Nit (no metadata):
+null
+
+# 1. Nit source:
+<F: 33.33>
+
+# 2. MsgPack:
+\x81\xA1\x6E\xCB\x40\x40\xAA\x3D\x70\xA3\xD7\x0A
+
+# 3. JSON:
+{
+ "n": 33.33
+}
+
+# 4. Back in Nit (no metadata):
+null
+
+# 1. Nit source:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# 2. MsgPack:
+\x84\xA2\x68\x73\x92\xFF\x00\xA1\x73\x92\xA3\x6F\x6E\x65\xA3\x74\x77\x6F\xA2\x68\x6D\x82\xA3\x6F\x6E\x65\x01\xA3\x74\x77\x6F\x02\xA2\x61\x6D\x82\xA5\x74\x68\x72\x65\x65\xA1\x33\xA4\x66\x6F\x75\x72\xA1\x34
+
+# 3. JSON:
+{
+ "hs": [-1, 0],
+ "s": ["one", "two"],
+ "hm": {
+ "one": 1,
+ "two": 2
+ },
+ "am": {
+ "three": "3",
+ "four": "4"
+ }
+}
+
+# 4. Back in Nit (no metadata):
+<G: hs: ; s: ; hm: one. 1, two. 2; am: three. 3, four. 4>
+
--- /dev/null
+# 1. Nit source:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x41\x86\xA1\x62\xC3\xA1\x63\xD4\x7C\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "A", {
+ "b": true,
+ "c": {
+ "typ": 124,
+ "data": [97]
+ },
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+}]
+
+# 4. Back in Nit (with metadata):
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 1. Nit source:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x42\x88\xA1\x62\xC2\xA1\x63\xD4\x7C\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "B", {
+ "b": false,
+ "c": {
+ "typ": 124,
+ "data": [98]
+ },
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+}]
+
+# 4. Back in Nit (with metadata):
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 1. Nit source:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x43\x83\xA1\x61\x93\xD4\x7B\x01\xA1\x41\x86\xA1\x62\xC3\xA1\x63\xD4\x7C\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0\xA1\x62\x93\xD4\x7B\x02\xA1\x42\x88\xA1\x62\xC2\xA1\x63\xD4\x7C\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72\xA2\x61\x61\xD4\x7D\x01
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "C", {
+ "a": [{
+ "typ": 123,
+ "data": [1]
+ }, "A", {
+ "b": true,
+ "c": {
+ "typ": 124,
+ "data": [97]
+ },
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ }],
+ "b": [{
+ "typ": 123,
+ "data": [2]
+ }, "B", {
+ "b": false,
+ "c": {
+ "typ": 124,
+ "data": [98]
+ },
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+ }],
+ "aa": {
+ "typ": 125,
+ "data": [1]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 1. Nit source:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x44\x89\xA1\x62\xC2\xA1\x63\xD4\x7C\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xAE\x6E\x65\x77\x20\x6C\x69\x6E\x65\x20\x2D\x3E\x0A\x3C\x2D\xA1\x6E\xC0\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA6\x09\x66\x22\x0D\x5C\x2F\xA1\x64\xD4\x7D\x00
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "D", {
+ "b": false,
+ "c": {
+ "typ": 124,
+ "data": [98]
+ },
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "new line ->\n<-",
+ "n": null,
+ "ii": 1111,
+ "ss": "\tf\"\r\\/",
+ "d": {
+ "typ": 125,
+ "data": [0]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 1. Nit source:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x45\x82\xA1\x61\x93\xD4\x7B\x01\xA5\x41\x72\x72\x61\x79\x93\xA5\x68\x65\x6C\x6C\x6F\xCD\x04\xD2\xCB\x40\x5E\xD9\x99\x99\x99\x99\x9A\xA1\x62\x93\xD4\x7B\x02\xA5\x41\x72\x72\x61\x79\x93\xA5\x68\x65\x6C\x6C\x61\xCD\x09\x29\xCB\x40\x6D\x50\x00\x00\x00\x00\x00
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "E", {
+ "a": [{
+ "typ": 123,
+ "data": [1]
+ }, "Array", ["hello", 1234, 123.4]],
+ "b": [{
+ "typ": 123,
+ "data": [2]
+ }, "Array", ["hella", 2345, 234.5]]
+}]
+
+# 4. Back in Nit (with metadata):
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 1. Nit source:
+<F: 2222>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x46\x81\xA1\x6E\xCD\x08\xAE
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "F", {
+ "n": 2222
+}]
+
+# 4. Back in Nit (with metadata):
+null
+
+# 1. Nit source:
+<F: 33.33>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x46\x81\xA1\x6E\xCB\x40\x40\xAA\x3D\x70\xA3\xD7\x0A
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "F", {
+ "n": 33.33
+}]
+
+# 4. Back in Nit (with metadata):
+null
+
+# 1. Nit source:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x47\x84\xA2\x68\x73\x93\xD4\x7B\x01\xA7\x48\x61\x73\x68\x53\x65\x74\x92\xFF\x00\xA1\x73\x93\xD4\x7B\x02\xA8\x41\x72\x72\x61\x79\x53\x65\x74\x92\xA3\x6F\x6E\x65\xA3\x74\x77\x6F\xA2\x68\x6D\x93\xD4\x7B\x03\xA7\x48\x61\x73\x68\x4D\x61\x70\x82\xA3\x6F\x6E\x65\x01\xA3\x74\x77\x6F\x02\xA2\x61\x6D\x93\xD4\x7B\x04\xA8\x41\x72\x72\x61\x79\x4D\x61\x70\x82\xA5\x74\x68\x72\x65\x65\xA1\x33\xA4\x66\x6F\x75\x72\xA1\x34
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "G", {
+ "hs": [{
+ "typ": 123,
+ "data": [1]
+ }, "HashSet", [-1, 0]],
+ "s": [{
+ "typ": 123,
+ "data": [2]
+ }, "ArraySet", ["one", "two"]],
+ "hm": [{
+ "typ": 123,
+ "data": [3]
+ }, "HashMap", {
+ "one": 1,
+ "two": 2
+ }],
+ "am": [{
+ "typ": 123,
+ "data": [4]
+ }, "ArrayMap", {
+ "three": "3",
+ "four": "4"
+ }]
+}]
+
+# 4. Back in Nit (with metadata):
+<G: hs: ; s: ; hm: ; am: >
+
--- /dev/null
+# 1. Nit source:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x41\x86\x92\xD4\x7B\x02\xA1\x62\xC3\x92\xD4\x7B\x03\xA1\x63\xD4\x7C\x61\x92\xD4\x7B\x04\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\x92\xD4\x7B\x05\xA1\x69\xCD\x04\xD2\x92\xD4\x7B\x06\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\x92\xD4\x7B\x07\xA1\x6E\xC0
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "A"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,b]": true,
+ "[<MsgPackExt typ: 0x7b, data: \\x03>,c]": {
+ "typ": 124,
+ "data": [97]
+ },
+ "[<MsgPackExt typ: 0x7b, data: \\x04>,f]": 0.123,
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,i]": 1234,
+ "[<MsgPackExt typ: 0x7b, data: \\x06>,serialization_specific_name]": "asdf",
+ "[<MsgPackExt typ: 0x7b, data: \\x07>,n]": null
+}]
+
+# 4. Back in Nit (with metadata):
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 1. Nit source:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x42\x88\x92\xD4\x7B\x02\xA1\x62\xC2\x92\xD4\x7B\x03\xA1\x63\xD4\x7C\x62\x92\xD4\x7B\x04\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\x92\xD4\x7B\x05\xA1\x69\xCD\x09\x29\x92\xD4\x7B\x06\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\x92\xD4\x7B\x07\xA1\x6E\x0C\x92\xD4\x7B\x08\xA2\x69\x69\xCD\x04\x57\x92\xD4\x7B\x09\xA2\x73\x73\xA4\x71\x77\x65\x72
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "B"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,b]": false,
+ "[<MsgPackExt typ: 0x7b, data: \\x03>,c]": {
+ "typ": 124,
+ "data": [98]
+ },
+ "[<MsgPackExt typ: 0x7b, data: \\x04>,f]": 123.123,
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,i]": 2345,
+ "[<MsgPackExt typ: 0x7b, data: \\x06>,serialization_specific_name]": "hjkl",
+ "[<MsgPackExt typ: 0x7b, data: \\x07>,n]": 12,
+ "[<MsgPackExt typ: 0x7b, data: \\x08>,ii]": 1111,
+ "[<MsgPackExt typ: 0x7b, data: \\x09>,ss]": "qwer"
+}]
+
+# 4. Back in Nit (with metadata):
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 1. Nit source:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x43\x83\x92\xD4\x7B\x02\xA1\x61\x93\xD4\x7B\x03\x92\xD4\x7B\x04\xA1\x41\x86\x92\xD4\x7B\x05\xA1\x62\xC3\x92\xD4\x7B\x06\xA1\x63\xD4\x7C\x61\x92\xD4\x7B\x07\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\x92\xD4\x7B\x08\xA1\x69\xCD\x04\xD2\x92\xD4\x7B\x09\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\x92\xD4\x7B\x0A\xA1\x6E\xC0\xD4\x7D\x05\x93\xD4\x7B\x0B\x92\xD4\x7B\x0C\xA1\x42\x88\xD4\x7D\x05\xC2\xD4\x7D\x06\xD4\x7C\x62\xD4\x7D\x07\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xD4\x7D\x08\xCD\x09\x29\xD4\x7D\x09\xA4\x68\x6A\x6B\x6C\xD4\x7D\x0A\x0C\x92\xD4\x7B\x0D\xA2\x69\x69\xCD\x04\x57\x92\xD4\x7B\x0E\xA2\x73\x73\xA4\x71\x77\x65\x72\x92\xD4\x7B\x0F\xA2\x61\x61\xD4\x7D\x03
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "C"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,a]": [{
+ "typ": 123,
+ "data": [3]
+ }, [{
+ "typ": 123,
+ "data": [4]
+ }, "A"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,b]": true,
+ "[<MsgPackExt typ: 0x7b, data: \\x06>,c]": {
+ "typ": 124,
+ "data": [97]
+ },
+ "[<MsgPackExt typ: 0x7b, data: \\x07>,f]": 0.123,
+ "[<MsgPackExt typ: 0x7b, data: \\x08>,i]": 1234,
+ "[<MsgPackExt typ: 0x7b, data: \\x09>,serialization_specific_name]": "asdf",
+ "[<MsgPackExt typ: 0x7b, data: \\x0A>,n]": null
+ }],
+ "<MsgPackExt typ: 0x7d, data: \\x05>": [{
+ "typ": 123,
+ "data": [11]
+ }, [{
+ "typ": 123,
+ "data": [12]
+ }, "B"], {
+ "<MsgPackExt typ: 0x7d, data: \\x05>": false,
+ "<MsgPackExt typ: 0x7d, data: \\x06>": {
+ "typ": 124,
+ "data": [98]
+ },
+ "<MsgPackExt typ: 0x7d, data: \\x07>": 123.123,
+ "<MsgPackExt typ: 0x7d, data: \\x08>": 2345,
+ "<MsgPackExt typ: 0x7d, data: \\x09>": "hjkl",
+ "<MsgPackExt typ: 0x7d, data: \\x0A>": 12,
+ "[<MsgPackExt typ: 0x7b, data: \\x0D>,ii]": 1111,
+ "[<MsgPackExt typ: 0x7b, data: \\x0E>,ss]": "qwer"
+ }],
+ "[<MsgPackExt typ: 0x7b, data: \\x0F>,aa]": {
+ "typ": 125,
+ "data": [3]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 1. Nit source:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x44\x89\x92\xD4\x7B\x02\xA1\x62\xC2\x92\xD4\x7B\x03\xA1\x63\xD4\x7C\x62\x92\xD4\x7B\x04\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\x92\xD4\x7B\x05\xA1\x69\xCD\x09\x29\x92\xD4\x7B\x06\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xAE\x6E\x65\x77\x20\x6C\x69\x6E\x65\x20\x2D\x3E\x0A\x3C\x2D\x92\xD4\x7B\x07\xA1\x6E\xC0\x92\xD4\x7B\x08\xA2\x69\x69\xCD\x04\x57\x92\xD4\x7B\x09\xA2\x73\x73\xA6\x09\x66\x22\x0D\x5C\x2F\x92\xD4\x7B\x0A\xA1\x64\xD4\x7D\x00
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "D"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,b]": false,
+ "[<MsgPackExt typ: 0x7b, data: \\x03>,c]": {
+ "typ": 124,
+ "data": [98]
+ },
+ "[<MsgPackExt typ: 0x7b, data: \\x04>,f]": 123.123,
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,i]": 2345,
+ "[<MsgPackExt typ: 0x7b, data: \\x06>,serialization_specific_name]": "new line ->\n<-",
+ "[<MsgPackExt typ: 0x7b, data: \\x07>,n]": null,
+ "[<MsgPackExt typ: 0x7b, data: \\x08>,ii]": 1111,
+ "[<MsgPackExt typ: 0x7b, data: \\x09>,ss]": "\tf\"\r\\/",
+ "[<MsgPackExt typ: 0x7b, data: \\x0A>,d]": {
+ "typ": 125,
+ "data": [0]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 1. Nit source:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x45\x82\x92\xD4\x7B\x02\xA1\x61\x93\xD4\x7B\x03\x92\xD4\x7B\x04\xA5\x41\x72\x72\x61\x79\x93\xA5\x68\x65\x6C\x6C\x6F\xCD\x04\xD2\xCB\x40\x5E\xD9\x99\x99\x99\x99\x9A\x92\xD4\x7B\x05\xA1\x62\x93\xD4\x7B\x06\xD4\x7D\x04\x93\xA5\x68\x65\x6C\x6C\x61\xCD\x09\x29\xCB\x40\x6D\x50\x00\x00\x00\x00\x00
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "E"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,a]": [{
+ "typ": 123,
+ "data": [3]
+ }, [{
+ "typ": 123,
+ "data": [4]
+ }, "Array"], ["hello", 1234, 123.4]],
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,b]": [{
+ "typ": 123,
+ "data": [6]
+ }, {
+ "typ": 125,
+ "data": [4]
+ }, ["hella", 2345, 234.5]]
+}]
+
+# 4. Back in Nit (with metadata):
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 1. Nit source:
+<F: 2222>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x46\x81\x92\xD4\x7B\x02\xA1\x6E\xCD\x08\xAE
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "F"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,n]": 2222
+}]
+
+# 4. Back in Nit (with metadata):
+null
+
+# 1. Nit source:
+<F: 33.33>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x46\x81\x92\xD4\x7B\x02\xA1\x6E\xCB\x40\x40\xAA\x3D\x70\xA3\xD7\x0A
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "F"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,n]": 33.33
+}]
+
+# 4. Back in Nit (with metadata):
+null
+
+# 1. Nit source:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x47\x84\x92\xD4\x7B\x02\xA2\x68\x73\x93\xD4\x7B\x03\x92\xD4\x7B\x04\xA7\x48\x61\x73\x68\x53\x65\x74\x92\xFF\x00\x92\xD4\x7B\x05\xA1\x73\x93\xD4\x7B\x06\x92\xD4\x7B\x07\xA8\x41\x72\x72\x61\x79\x53\x65\x74\x92\xA3\x6F\x6E\x65\xA3\x74\x77\x6F\x92\xD4\x7B\x08\xA2\x68\x6D\x93\xD4\x7B\x09\x92\xD4\x7B\x0A\xA7\x48\x61\x73\x68\x4D\x61\x70\x82\xA3\x6F\x6E\x65\x01\xA3\x74\x77\x6F\x02\x92\xD4\x7B\x0B\xA2\x61\x6D\x93\xD4\x7B\x0C\x92\xD4\x7B\x0D\xA8\x41\x72\x72\x61\x79\x4D\x61\x70\x82\xA5\x74\x68\x72\x65\x65\xA1\x33\xA4\x66\x6F\x75\x72\xA1\x34
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "G"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,hs]": [{
+ "typ": 123,
+ "data": [3]
+ }, [{
+ "typ": 123,
+ "data": [4]
+ }, "HashSet"], [-1, 0]],
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,s]": [{
+ "typ": 123,
+ "data": [6]
+ }, [{
+ "typ": 123,
+ "data": [7]
+ }, "ArraySet"], ["one", "two"]],
+ "[<MsgPackExt typ: 0x7b, data: \\x08>,hm]": [{
+ "typ": 123,
+ "data": [9]
+ }, [{
+ "typ": 123,
+ "data": [10]
+ }, "HashMap"], {
+ "one": 1,
+ "two": 2
+ }],
+ "[<MsgPackExt typ: 0x7b, data: \\x0B>,am]": [{
+ "typ": 123,
+ "data": [12]
+ }, [{
+ "typ": 123,
+ "data": [13]
+ }, "ArrayMap"], {
+ "three": "3",
+ "four": "4"
+ }]
+}]
+
+# 4. Back in Nit (with metadata):
+<G: hs: ; s: ; hm: ; am: >
+
--- /dev/null
+# 1. Nit source:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 2. MsgPack:
+\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+{
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+}
+
+# 4. Back in Nit (no metadata):
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 1. Nit source:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 2. MsgPack:
+\x88\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72
+
+# 3. JSON:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+}
+
+# 4. Back in Nit (no metadata):
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 1. Nit source:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 2. MsgPack:
+\x83\xA1\x61\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0\xA1\x62\x88\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72\xA2\x61\x61\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+{
+ "a": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ },
+ "b": {
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+ },
+ "aa": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ }
+}
+
+# 4. Back in Nit (no metadata):
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
+# 1. Nit source:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 2. MsgPack:
+\x89\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xAE\x6E\x65\x77\x20\x6C\x69\x6E\x65\x20\x2D\x3E\x0A\x3C\x2D\xA1\x6E\xC0\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA6\x09\x66\x22\x0D\x5C\x2F\xA1\x64\xC0
+
+# 3. JSON:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "new line ->\n<-",
+ "n": null,
+ "ii": 1111,
+ "ss": "\tf\"\r\\/",
+ "d": null
+}
+
+# 4. Back in Nit (no metadata):
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
+# 1. Nit source:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 2. MsgPack:
+\x82\xA1\x61\x93\xA5\x68\x65\x6C\x6C\x6F\xCD\x04\xD2\xCB\x40\x5E\xD9\x99\x99\x99\x99\x9A\xA1\x62\x93\xA5\x68\x65\x6C\x6C\x61\xCD\x09\x29\xCB\x40\x6D\x50\x00\x00\x00\x00\x00
+
+# 3. JSON:
+{
+ "a": ["hello", 1234, 123.4],
+ "b": ["hella", 2345, 234.5]
+}
+
+# 4. Back in Nit (no metadata):
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 1. Nit source:
+<F: 2222>
+
+# 2. MsgPack:
+\x81\xA1\x6E\xCD\x08\xAE
+
+# 3. JSON:
+{
+ "n": 2222
+}
+
+# 4. Back in Nit (no metadata):
+<F: 2222>
+
+# 1. Nit source:
+<F: 33.33>
+
+# 2. MsgPack:
+\x81\xA1\x6E\xCB\x40\x40\xAA\x3D\x70\xA3\xD7\x0A
+
+# 3. JSON:
+{
+ "n": 33.33
+}
+
+# 4. Back in Nit (no metadata):
+<F: 33.33>
+
+# 1. Nit source:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# 2. MsgPack:
+\x84\xA2\x68\x73\x92\xFF\x00\xA1\x73\x92\xA3\x6F\x6E\x65\xA3\x74\x77\x6F\xA2\x68\x6D\x82\xA3\x6F\x6E\x65\x01\xA3\x74\x77\x6F\x02\xA2\x61\x6D\x82\xA5\x74\x68\x72\x65\x65\xA1\x33\xA4\x66\x6F\x75\x72\xA1\x34
+
+# 3. JSON:
+{
+ "hs": [-1, 0],
+ "s": ["one", "two"],
+ "hm": {
+ "one": 1,
+ "two": 2
+ },
+ "am": {
+ "three": "3",
+ "four": "4"
+ }
+}
+
+# 4. Back in Nit (no metadata):
+<G: hs: ; s: ; hm: one. 1, two. 2; am: three. 3, four. 4>
+
if name == "Array[Error]" then return new Array[Error].from_deserializer(self)
if name == "StrictHashMap[Int, Object]" then return new StrictHashMap[Int, Object].from_deserializer(self)
if name == "POSet[String]" then return new POSet[String].from_deserializer(self)
+ if name == "StrictHashMap[Serializable, Int]" then return new StrictHashMap[Serializable, Int].from_deserializer(self)
if name == "Array[Int]" then return new Array[Int].from_deserializer(self)
if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
if name == "HashSet[String]" then return new HashSet[String].from_deserializer(self)
if name == "Array[Float]" then return new Array[Float].from_deserializer(self)
if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
- if name == "StrictHashMap[Serializable, Int]" then return new StrictHashMap[Serializable, Int].from_deserializer(self)
if name == "POSetElement[String]" then return new POSetElement[String].from_deserializer(self)
if name == "HashMap[String, POSetElement[String]]" then return new HashMap[String, POSetElement[String]].from_deserializer(self)
if name == "Array[Match]" then return new Array[Match].from_deserializer(self)
==== Test-suite of module test_test_nitunit::test_test_nitunit | tests: 3
[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
+ test_test_nitunit.nit:35,2--39,4: Runtime Error in file nitunit.out/gen_test_test_nitunit.nit
Output
- Runtime error: Assert failed (test_test_nitunit.nit:39)
+ Runtime error: Assert failed (test_test_nitunit.nit:38)
[OK] test_test_nitunit$TestX$test_foo2
</system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="foo" time="0.0"><failure message="Compilation error in nitunit.out/test_nitunit-3.nit">nitunit.out/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
</failure><system-out>assert undefined_identifier
</system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="foo1" time="0.0"><failure message="Syntax Error: unexpected operator '!'."></failure><system-out>assert !@#$%^&*()
-</system-out></testcase></testsuite><testsuite package="test_test_nitunit::test_test_nitunit"></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo1" time="0.0"><error message="Runtime Error in file nitunit.out/gen_test_test_nitunit.nit">Runtime error: Assert failed (test_test_nitunit.nit:39)
+</system-out></testcase></testsuite><testsuite package="test_test_nitunit::test_test_nitunit"></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo1" time="0.0"><error message="Runtime Error in file nitunit.out/gen_test_test_nitunit.nit">Runtime error: Assert failed (test_test_nitunit.nit:38)
</error></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo2" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
==== Test-suite of module test_nitunit6::test_nitunit6 | tests: 3
[KO] test_nitunit6::test_nitunit6$core::Sys$before_module
- test_nitunit6.nit:27,1--29,3: Runtime Error in file nitunit.out/gen_test_nitunit6.nit
+ test_nitunit6.nit:25,1--27,3: Runtime Error in file nitunit.out/gen_test_nitunit6.nit
Output
- Runtime error: Assert failed (test_nitunit6.nit:28)
+ Runtime error: Assert failed (test_nitunit6.nit:26)
[KO] test_nitunit6$TestNitunit6$test_foo
- test_nitunit6.nit:22,2--24,4: Nitunit Error: before_module test failed
+ test_nitunit6.nit:20,2--22,4: Nitunit Error: before_module test failed
[KO] test_nitunit6::test_nitunit6$core::Sys$after_module
- test_nitunit6.nit:31,1--33,3: Nitunit Error: before_module test failed
+ test_nitunit6.nit:29,1--31,3: Nitunit Error: before_module test failed
Docunits: Entities: 5; Documented ones: 0; With nitunits: 0
Test suites: Classes: 1; Test Cases: 3; Failures: 3
[OK] test_nitunit7::test_nitunit7$core::Sys$before_module
[OK] test_nitunit7$TestNitunit7$test_foo
[KO] test_nitunit7::test_nitunit7$core::Sys$after_module
- test_nitunit7.nit:31,1--33,3: Runtime Error in file nitunit.out/gen_test_nitunit7.nit
+ test_nitunit7.nit:29,1--31,3: Runtime Error in file nitunit.out/gen_test_nitunit7.nit
Output
- Runtime error: Assert failed (test_nitunit7.nit:32)
+ Runtime error: Assert failed (test_nitunit7.nit:30)
Docunits: Entities: 5; Documented ones: 0; With nitunits: 0
-module test_test_nitunit is test_suite
+module test_test_nitunit is test
-import test_suite
import test_nitunit
class TestX
- super TestSuite
+ test
- fun test_foo do
+ fun test_foo is test do
assert not_implemented: false # TODO remove once implemented
var subject: X
subject.foo
end
- fun test_foo1 do
+ fun test_foo1 is test do
assert not_implemented: false # TODO remove once implemented
var subject: X
subject.foo1(a, b)
end
- fun test_foo3 do
+ fun test_foo3 is test do
assert not_implemented: false # TODO remove once implemented
var subject: X
end
class TestY
- super TestSuite
+ test
- fun test_bra do
+ fun test_bra is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_bra_assign do
+ fun test_bra_assign is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
subject[e] = i
end
- fun test_plus do
+ fun test_plus is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_minus do
+ fun test_minus is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_star do
+ fun test_star is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_slash do
+ fun test_slash is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_percent do
+ fun test_percent is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_unary_minus do
+ fun test_unary_minus is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_equals do
+ fun test_equals is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_not_equals do
+ fun test_not_equals is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_lt do
+ fun test_lt is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_le do
+ fun test_le is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_compare do
+ fun test_compare is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_ge do
+ fun test_ge is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_gt do
+ fun test_gt is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
end
class TestZ
- super TestSuite
+ test
- fun test_bra do
+ fun test_bra is test do
assert not_implemented: false # TODO remove once implemented
var subject: Z
assert exp == res
end
- fun test_bra_assign do
+ fun test_bra_assign is test do
assert not_implemented: false # TODO remove once implemented
var subject: Z
subject[i, j] = k
end
- fun test_foo= do
+ fun test_foo= is test do
assert not_implemented: false # TODO remove once implemented
var subject: Z
subject.foo(i) = j
end
- fun test_bar= do
+ fun test_bar= is test do
assert not_implemented: false # TODO remove once implemented
var subject: Z
-module test_test_nitunit is test_suite
+module test_test_nitunit is test
-import test_suite
intrude import test_nitunit
class TestX
- super TestSuite
+ test
- fun test_foo do
+ fun test_foo is test do
assert not_implemented: false # TODO remove once implemented
var subject: X
subject.foo
end
- fun test_foo1 do
+ fun test_foo1 is test do
assert not_implemented: false # TODO remove once implemented
var subject: X
subject.foo1(a, b)
end
- fun test_foo2 do
+ fun test_foo2 is test do
assert not_implemented: false # TODO remove once implemented
var subject: X
assert exp == res
end
- fun test_foo3 do
+ fun test_foo3 is test do
assert not_implemented: false # TODO remove once implemented
var subject: X
assert exp == res
end
- fun test_foo3= do
+ fun test_foo3= is test do
assert not_implemented: false # TODO remove once implemented
var subject: X
end
class TestY
- super TestSuite
+ test
- fun test_bra do
+ fun test_bra is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_bra_assign do
+ fun test_bra_assign is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
subject[e] = i
end
- fun test_plus do
+ fun test_plus is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_minus do
+ fun test_minus is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_star do
+ fun test_star is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_slash do
+ fun test_slash is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_percent do
+ fun test_percent is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_unary_minus do
+ fun test_unary_minus is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_equals do
+ fun test_equals is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_not_equals do
+ fun test_not_equals is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_lt do
+ fun test_lt is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_le do
+ fun test_le is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_compare do
+ fun test_compare is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_ge do
+ fun test_ge is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
assert exp == res
end
- fun test_gt do
+ fun test_gt is test do
assert not_implemented: false # TODO remove once implemented
var subject: Y[X]
end
class TestZ
- super TestSuite
+ test
- fun test_bra do
+ fun test_bra is test do
assert not_implemented: false # TODO remove once implemented
var subject: Z
assert exp == res
end
- fun test_bra_assign do
+ fun test_bra_assign is test do
assert not_implemented: false # TODO remove once implemented
var subject: Z
subject[i, j] = k
end
- fun test_foo= do
+ fun test_foo= is test do
assert not_implemented: false # TODO remove once implemented
var subject: Z
subject.foo(i) = j
end
- fun test_bar= do
+ fun test_bar= is test do
assert not_implemented: false # TODO remove once implemented
var subject: Z
-test_nitunit4/test_bad_comp.nit:27,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
-test_nitunit4/test_bad_comp2.nit:19,7--22: Error: a class named `test_nitunit4::TestSuiteBadComp` is already defined in module `test_bad_comp` at test_nitunit4/test_bad_comp.nit:19,1--29,3.
+test_nitunit4/test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+test_nitunit4/test_bad_comp2.nit:17,7--22: Error: a class named `test_nitunit4::TestSuiteBadComp` is already defined in module `test_bad_comp` at test_nitunit4/test_bad_comp.nit:17,1--27,3.
==== Test-suite of module test_nitunit4::test_bad_comp | tests: 2
[KO] test_nitunit4$TestSuiteBadComp$test_good
- test_nitunit4/test_bad_comp.nit:22,2--24,4: Compilation Error
+ test_nitunit4/test_bad_comp.nit:20,2--22,4: Compilation Error
Output
- test_nitunit4/test_bad_comp.nit:27,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+ test_nitunit4/test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
[KO] test_nitunit4$TestSuiteBadComp$test_bad
- test_nitunit4/test_bad_comp.nit:26,2--28,4: Compilation Error
+ test_nitunit4/test_bad_comp.nit:24,2--26,4: Compilation Error
Output
- test_nitunit4/test_bad_comp.nit:27,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+ test_nitunit4/test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
==== Test-suite of module test_nitunit4::test_bad_comp2 | tests: 2
[KO] test_nitunit4$TestSuiteBadComp$test_good
- test_nitunit4/test_bad_comp2.nit:22,2--24,4: Compilation Error
+ test_nitunit4/test_bad_comp2.nit:20,2--22,4: Compilation Error
Output
- nitunit.out/gen_test_bad_comp2.nit:14,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+ nitunit.out/gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
[KO] test_nitunit4$TestSuiteBadComp$test_bad
- test_nitunit4/test_bad_comp2.nit:26,2--28,4: Compilation Error
+ test_nitunit4/test_bad_comp2.nit:24,2--26,4: Compilation Error
Output
- nitunit.out/gen_test_bad_comp2.nit:14,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+ nitunit.out/gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
==== Test-suite of module test_nitunit4::test_nitunit4 | tests: 4
[KO] test_nitunit4$TestTestSuite$test_foo
- test_nitunit4/test_nitunit4.nit:22,2--26,4: Runtime Error in file nitunit.out/gen_test_nitunit4.nit
+ test_nitunit4/test_nitunit4.nit:23,2--27,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)
+ Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:28)
[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_baz.res nitunit.out/gen_test_nitunit4_test_baz.out1
+ test_nitunit4/test_nitunit4.nit:33,2--35,4: Difference with expected output: diff -u test_nitunit4/test_baz.res nitunit.out/gen_test_nitunit4_test_baz.out1
Output
Diff
--- expected:test_nitunit4/test_baz.res
+After Test
[KO] test_nitunit4$TestTestSuite$test_sav_conflict
- test_nitunit4/test_nitunit4.nit:36,2--38,4: Conflicting expected output: test_nitunit4/test_nitunit4.sav/test_sav_conflict.res, test_nitunit4/sav/test_sav_conflict.res and test_nitunit4/test_sav_conflict.res all exist
+ test_nitunit4/test_nitunit4.nit:37,2--39,4: Conflicting expected output: test_nitunit4/test_nitunit4.sav/test_sav_conflict.res, test_nitunit4/sav/test_sav_conflict.res and test_nitunit4/test_sav_conflict.res all exist
Output
Before Test
Tested method
After Test
-==== Test-suite of module test_nitunit4::test_nitunit4_base | tests: 0
-==== Test-suite of module test_nitunit4::test_nitunit4_base | tests: 0
-
Docunits: Entities: 21; Documented ones: 0; With nitunits: 0
-Test suites: Classes: 4; Test Cases: 8; Failures: 7
+Test suites: Classes: 3; Test Cases: 8; Failures: 7
[FAILURE] 7/8 tests failed.
`nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit4>"></testsuite><testsuite package="test_nitunit4::nitunit4"></testsuite><testsuite package="test_nitunit4::test_bad_comp"></testsuite><testsuite package="test_bad_comp"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">test_nitunit4/test_bad_comp.nit:27,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
-</failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">test_nitunit4/test_bad_comp.nit:27,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
-</failure></testcase></testsuite><testsuite package="test_nitunit4::test_bad_comp2"></testsuite><testsuite package="test_bad_comp2"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">nitunit.out/gen_test_bad_comp2.nit:14,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
-</failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">nitunit.out/gen_test_bad_comp2.nit:14,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+<testsuites><testsuite package="test_nitunit4>"></testsuite><testsuite package="test_nitunit4::nitunit4"></testsuite><testsuite package="test_nitunit4::test_bad_comp"></testsuite><testsuite package="test_bad_comp"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">test_nitunit4/test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+</failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">test_nitunit4/test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+</failure></testcase></testsuite><testsuite package="test_nitunit4::test_bad_comp2"></testsuite><testsuite package="test_bad_comp2"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">nitunit.out/gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+</failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">nitunit.out/gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
</failure></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4"></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_foo" time="0.0"><error message="Runtime Error in file nitunit.out/gen_test_nitunit4.nit">Before Test
Tested method
After Test
-Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:31)
+Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:28)
</error></testcase><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_bar" time="0.0"><system-err>Before Test
Tested method
After Test
</error></testcase><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_sav_conflict" time="0.0"><error message="Conflicting expected output: test_nitunit4/test_nitunit4.sav/test_sav_conflict.res, test_nitunit4/sav/test_sav_conflict.res and test_nitunit4/test_sav_conflict.res all exist">Before Test
Tested method
After Test
-</error></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4_base"></testsuite><testsuite package="test_nitunit4_base"></testsuite></testsuites>
\ No newline at end of file
+</error></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4_base"></testsuite></testsuites>
\ No newline at end of file
syntax_annotations3.nit:16,2--20: Warning: unknown annotation `invariant`.
syntax_annotations3.nit:19,3--12: Warning: unknown annotation `pre`.
syntax_annotations3.nit:20,3--22: Warning: unknown annotation `post`.
-syntax_annotations3.nit:21,3--19: Warning: unknown annotation `test`.
syntax_annotations3.nit:28,3--7: Warning: unknown annotation `inter`.
syntax_annotations3.nit:33,16--18: Warning: unknown annotation `u32`.
-syntax_annotations3.nit:34,19--36: Warning: unknown annotation `after`.
syntax_annotations3.nit:34,12--36: Warning: unknown annotation `daemon`.
syntax_annotations3.nit:34,3--37: Warning: unknown annotation `ondebug`.
syntax_annotations3.nit:35,3--7: Warning: unknown annotation `final`.
--- /dev/null
+# Custom:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Inspect:
+<A#0 b:true, c:'a', f:0.123, i:1234, serialization_specific_name:"asdf", n:null>
+
+# Custom:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Inspect:
+<B#0 b:false, c:'b', f:123.123, i:2345, serialization_specific_name:"hjkl", n:…>
+
+# Custom:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Inspect:
+<C#0 a:<A#1>, b:<B#2>, aa:<A#1>>
+
+# Custom:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# Inspect:
+<D#0 b:false, c:'b', f:123.123, i:2345, serialization_specific_name:"new line …>
+
+# Custom:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Inspect:
+<E#0 a:<Array[Object]#1>, b:<Array[nullable Serializable]#2>>
+
+# Custom:
+<F: 2222>
+
+# Inspect:
+<F[Int]#0 n:2222>
+
+# Custom:
+<F: 33.33>
+
+# Inspect:
+<F[Float]#0 n:33.33>
+
+# Custom:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Inspect:
+<G#0 hs:<HashSet[Int]#1>, s:<ArraySet[String]#2>, hm:<HashMap[String, Int]#3>,…>
+
--- /dev/null
+# 1. Nit source:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 2. MsgPack:
+\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+{
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+}
+
+# 4. Back in Nit (no metadata):
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 1. Nit source:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 2. MsgPack:
+\x88\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72
+
+# 3. JSON:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+}
+
+# 4. Back in Nit (no metadata):
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 1. Nit source:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 2. MsgPack:
+\x83\xA1\x61\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0\xA1\x62\x88\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72\xA2\x61\x61\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+{
+ "a": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ },
+ "b": {
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+ },
+ "aa": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ }
+}
+
+# 4. Back in Nit (no metadata):
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
+# 1. Nit source:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 2. MsgPack:
+\x89\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xAE\x6E\x65\x77\x20\x6C\x69\x6E\x65\x20\x2D\x3E\x0A\x3C\x2D\xA1\x6E\xC0\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA6\x09\x66\x22\x0D\x5C\x2F\xA1\x64\xC0
+
+# 3. JSON:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "new line ->\n<-",
+ "n": null,
+ "ii": 1111,
+ "ss": "\tf\"\r\\/",
+ "d": null
+}
+
+# 4. Back in Nit (no metadata):
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
--- /dev/null
+# 1. Nit source:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x41\x86\xA1\x62\xC3\xA1\x63\xD4\x7C\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "A", {
+ "b": true,
+ "c": {
+ "typ": 124,
+ "data": [97]
+ },
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+}]
+
+# 4. Back in Nit (with metadata):
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 1. Nit source:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x42\x88\xA1\x62\xC2\xA1\x63\xD4\x7C\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "B", {
+ "b": false,
+ "c": {
+ "typ": 124,
+ "data": [98]
+ },
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+}]
+
+# 4. Back in Nit (with metadata):
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 1. Nit source:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x43\x83\xA1\x61\x93\xD4\x7B\x01\xA1\x41\x86\xA1\x62\xC3\xA1\x63\xD4\x7C\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0\xA1\x62\x93\xD4\x7B\x02\xA1\x42\x88\xA1\x62\xC2\xA1\x63\xD4\x7C\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72\xA2\x61\x61\xD4\x7D\x01
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "C", {
+ "a": [{
+ "typ": 123,
+ "data": [1]
+ }, "A", {
+ "b": true,
+ "c": {
+ "typ": 124,
+ "data": [97]
+ },
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ }],
+ "b": [{
+ "typ": 123,
+ "data": [2]
+ }, "B", {
+ "b": false,
+ "c": {
+ "typ": 124,
+ "data": [98]
+ },
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+ }],
+ "aa": {
+ "typ": 125,
+ "data": [1]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 1. Nit source:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x44\x89\xA1\x62\xC2\xA1\x63\xD4\x7C\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xAE\x6E\x65\x77\x20\x6C\x69\x6E\x65\x20\x2D\x3E\x0A\x3C\x2D\xA1\x6E\xC0\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA6\x09\x66\x22\x0D\x5C\x2F\xA1\x64\xD4\x7D\x00
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "D", {
+ "b": false,
+ "c": {
+ "typ": 124,
+ "data": [98]
+ },
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "new line ->\n<-",
+ "n": null,
+ "ii": 1111,
+ "ss": "\tf\"\r\\/",
+ "d": {
+ "typ": 125,
+ "data": [0]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
--- /dev/null
+# 1. Nit source:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 2. MsgPack:
+\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+{
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+}
+
+# 4. Back in Nit (no metadata):
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 1. Nit source:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 2. MsgPack:
+\x88\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72
+
+# 3. JSON:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+}
+
+# 4. Back in Nit (no metadata):
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 1. Nit source:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 2. MsgPack:
+\x83\xA1\x61\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0\xA1\x62\x88\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72\xA2\x61\x61\x86\xA1\x62\xC3\xA1\x63\xA1\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+{
+ "a": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ },
+ "b": {
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+ },
+ "aa": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ }
+}
+
+# 4. Back in Nit (no metadata):
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
+# 1. Nit source:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 2. MsgPack:
+\x89\xA1\x62\xC2\xA1\x63\xA1\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xAE\x6E\x65\x77\x20\x6C\x69\x6E\x65\x20\x2D\x3E\x0A\x3C\x2D\xA1\x6E\xC0\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA6\x09\x66\x22\x0D\x5C\x2F\xA1\x64\xC0
+
+# 3. JSON:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "new line ->\n<-",
+ "n": null,
+ "ii": 1111,
+ "ss": "\tf\"\r\\/",
+ "d": null
+}
+
+# 4. Back in Nit (no metadata):
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
+# 1. Nit source:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 2. MsgPack:
+\x82\xA1\x61\x93\xA5\x68\x65\x6C\x6C\x6F\xCD\x04\xD2\xCB\x40\x5E\xD9\x99\x99\x99\x99\x9A\xA1\x62\x93\xA5\x68\x65\x6C\x6C\x61\xCD\x09\x29\xCB\x40\x6D\x50\x00\x00\x00\x00\x00
+
+# 3. JSON:
+{
+ "a": ["hello", 1234, 123.4],
+ "b": ["hella", 2345, 234.5]
+}
+
+# 4. Back in Nit (no metadata):
+<E: a: ; b: >
+
+# 1. Nit source:
+<F: 2222>
+
+# 2. MsgPack:
+\x81\xA1\x6E\xCD\x08\xAE
+
+# 3. JSON:
+{
+ "n": 2222
+}
+
+# 4. Back in Nit (no metadata):
+<F: 2222>
+
+# 1. Nit source:
+<F: 33.33>
+
+# 2. MsgPack:
+\x81\xA1\x6E\xCB\x40\x40\xAA\x3D\x70\xA3\xD7\x0A
+
+# 3. JSON:
+{
+ "n": 33.33
+}
+
+# 4. Back in Nit (no metadata):
+<F: 33.33>
+
+# 1. Nit source:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# 2. MsgPack:
+\x84\xA2\x68\x73\x92\xFF\x00\xA1\x73\x92\xA3\x6F\x6E\x65\xA3\x74\x77\x6F\xA2\x68\x6D\x82\xA3\x6F\x6E\x65\x01\xA3\x74\x77\x6F\x02\xA2\x61\x6D\x82\xA5\x74\x68\x72\x65\x65\xA1\x33\xA4\x66\x6F\x75\x72\xA1\x34
+
+# 3. JSON:
+{
+ "hs": [-1, 0],
+ "s": ["one", "two"],
+ "hm": {
+ "one": 1,
+ "two": 2
+ },
+ "am": {
+ "three": "3",
+ "four": "4"
+ }
+}
+
+# 4. Back in Nit (no metadata):
+<G: hs: ; s: ; hm: one. 1, two. 2; am: three. 3, four. 4>
+
--- /dev/null
+# 1. Nit source:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x41\x86\xA1\x62\xC3\xA1\x63\xD4\x7C\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "A", {
+ "b": true,
+ "c": {
+ "typ": 124,
+ "data": [97]
+ },
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+}]
+
+# 4. Back in Nit (with metadata):
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 1. Nit source:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x42\x88\xA1\x62\xC2\xA1\x63\xD4\x7C\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "B", {
+ "b": false,
+ "c": {
+ "typ": 124,
+ "data": [98]
+ },
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+}]
+
+# 4. Back in Nit (with metadata):
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 1. Nit source:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x43\x83\xA1\x61\x93\xD4\x7B\x01\xA1\x41\x86\xA1\x62\xC3\xA1\x63\xD4\x7C\x61\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\xA1\x69\xCD\x04\xD2\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\xA1\x6E\xC0\xA1\x62\x93\xD4\x7B\x02\xA1\x42\x88\xA1\x62\xC2\xA1\x63\xD4\x7C\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\xA1\x6E\x0C\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA4\x71\x77\x65\x72\xA2\x61\x61\xD4\x7D\x01
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "C", {
+ "a": [{
+ "typ": 123,
+ "data": [1]
+ }, "A", {
+ "b": true,
+ "c": {
+ "typ": 124,
+ "data": [97]
+ },
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ }],
+ "b": [{
+ "typ": 123,
+ "data": [2]
+ }, "B", {
+ "b": false,
+ "c": {
+ "typ": 124,
+ "data": [98]
+ },
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+ }],
+ "aa": {
+ "typ": 125,
+ "data": [1]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 1. Nit source:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x44\x89\xA1\x62\xC2\xA1\x63\xD4\x7C\x62\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xA1\x69\xCD\x09\x29\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xAE\x6E\x65\x77\x20\x6C\x69\x6E\x65\x20\x2D\x3E\x0A\x3C\x2D\xA1\x6E\xC0\xA2\x69\x69\xCD\x04\x57\xA2\x73\x73\xA6\x09\x66\x22\x0D\x5C\x2F\xA1\x64\xD4\x7D\x00
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "D", {
+ "b": false,
+ "c": {
+ "typ": 124,
+ "data": [98]
+ },
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "new line ->\n<-",
+ "n": null,
+ "ii": 1111,
+ "ss": "\tf\"\r\\/",
+ "d": {
+ "typ": 125,
+ "data": [0]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 1. Nit source:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x45\x82\xA1\x61\x93\xD4\x7B\x01\xAD\x41\x72\x72\x61\x79\x5B\x4F\x62\x6A\x65\x63\x74\x5D\x93\xA5\x68\x65\x6C\x6C\x6F\xCD\x04\xD2\xCB\x40\x5E\xD9\x99\x99\x99\x99\x9A\xA1\x62\x93\xD4\x7B\x02\xBC\x41\x72\x72\x61\x79\x5B\x6E\x75\x6C\x6C\x61\x62\x6C\x65\x20\x53\x65\x72\x69\x61\x6C\x69\x7A\x61\x62\x6C\x65\x5D\x93\xA5\x68\x65\x6C\x6C\x61\xCD\x09\x29\xCB\x40\x6D\x50\x00\x00\x00\x00\x00
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "E", {
+ "a": [{
+ "typ": 123,
+ "data": [1]
+ }, "Array[Object]", ["hello", 1234, 123.4]],
+ "b": [{
+ "typ": 123,
+ "data": [2]
+ }, "Array[nullable Serializable]", ["hella", 2345, 234.5]]
+}]
+
+# 4. Back in Nit (with metadata):
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 1. Nit source:
+<F: 2222>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA6\x46\x5B\x49\x6E\x74\x5D\x81\xA1\x6E\xCD\x08\xAE
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "F[Int]", {
+ "n": 2222
+}]
+
+# 4. Back in Nit (with metadata):
+<F: 2222>
+
+# 1. Nit source:
+<F: 33.33>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA8\x46\x5B\x46\x6C\x6F\x61\x74\x5D\x81\xA1\x6E\xCB\x40\x40\xAA\x3D\x70\xA3\xD7\x0A
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "F[Float]", {
+ "n": 33.33
+}]
+
+# 4. Back in Nit (with metadata):
+<F: 33.33>
+
+# 1. Nit source:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\xA1\x47\x84\xA2\x68\x73\x93\xD4\x7B\x01\xAC\x48\x61\x73\x68\x53\x65\x74\x5B\x49\x6E\x74\x5D\x92\xFF\x00\xA1\x73\x93\xD4\x7B\x02\xB0\x41\x72\x72\x61\x79\x53\x65\x74\x5B\x53\x74\x72\x69\x6E\x67\x5D\x92\xA3\x6F\x6E\x65\xA3\x74\x77\x6F\xA2\x68\x6D\x93\xD4\x7B\x03\xB4\x48\x61\x73\x68\x4D\x61\x70\x5B\x53\x74\x72\x69\x6E\x67\x2C\x20\x49\x6E\x74\x5D\x82\xA3\x6F\x6E\x65\x01\xA3\x74\x77\x6F\x02\xA2\x61\x6D\x93\xD4\x7B\x04\xB8\x41\x72\x72\x61\x79\x4D\x61\x70\x5B\x53\x74\x72\x69\x6E\x67\x2C\x20\x53\x74\x72\x69\x6E\x67\x5D\x82\xA5\x74\x68\x72\x65\x65\xA1\x33\xA4\x66\x6F\x75\x72\xA1\x34
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, "G", {
+ "hs": [{
+ "typ": 123,
+ "data": [1]
+ }, "HashSet[Int]", [-1, 0]],
+ "s": [{
+ "typ": 123,
+ "data": [2]
+ }, "ArraySet[String]", ["one", "two"]],
+ "hm": [{
+ "typ": 123,
+ "data": [3]
+ }, "HashMap[String, Int]", {
+ "one": 1,
+ "two": 2
+ }],
+ "am": [{
+ "typ": 123,
+ "data": [4]
+ }, "ArrayMap[String, String]", {
+ "three": "3",
+ "four": "4"
+ }]
+}]
+
+# 4. Back in Nit (with metadata):
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
--- /dev/null
+# 1. Nit source:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x41\x86\x92\xD4\x7B\x02\xA1\x62\xC3\x92\xD4\x7B\x03\xA1\x63\xD4\x7C\x61\x92\xD4\x7B\x04\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\x92\xD4\x7B\x05\xA1\x69\xCD\x04\xD2\x92\xD4\x7B\x06\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\x92\xD4\x7B\x07\xA1\x6E\xC0
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "A"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,b]": true,
+ "[<MsgPackExt typ: 0x7b, data: \\x03>,c]": {
+ "typ": 124,
+ "data": [97]
+ },
+ "[<MsgPackExt typ: 0x7b, data: \\x04>,f]": 0.123,
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,i]": 1234,
+ "[<MsgPackExt typ: 0x7b, data: \\x06>,serialization_specific_name]": "asdf",
+ "[<MsgPackExt typ: 0x7b, data: \\x07>,n]": null
+}]
+
+# 4. Back in Nit (with metadata):
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# 1. Nit source:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x42\x88\x92\xD4\x7B\x02\xA1\x62\xC2\x92\xD4\x7B\x03\xA1\x63\xD4\x7C\x62\x92\xD4\x7B\x04\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\x92\xD4\x7B\x05\xA1\x69\xCD\x09\x29\x92\xD4\x7B\x06\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x68\x6A\x6B\x6C\x92\xD4\x7B\x07\xA1\x6E\x0C\x92\xD4\x7B\x08\xA2\x69\x69\xCD\x04\x57\x92\xD4\x7B\x09\xA2\x73\x73\xA4\x71\x77\x65\x72
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "B"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,b]": false,
+ "[<MsgPackExt typ: 0x7b, data: \\x03>,c]": {
+ "typ": 124,
+ "data": [98]
+ },
+ "[<MsgPackExt typ: 0x7b, data: \\x04>,f]": 123.123,
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,i]": 2345,
+ "[<MsgPackExt typ: 0x7b, data: \\x06>,serialization_specific_name]": "hjkl",
+ "[<MsgPackExt typ: 0x7b, data: \\x07>,n]": 12,
+ "[<MsgPackExt typ: 0x7b, data: \\x08>,ii]": 1111,
+ "[<MsgPackExt typ: 0x7b, data: \\x09>,ss]": "qwer"
+}]
+
+# 4. Back in Nit (with metadata):
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# 1. Nit source:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x43\x83\x92\xD4\x7B\x02\xA1\x61\x93\xD4\x7B\x03\x92\xD4\x7B\x04\xA1\x41\x86\x92\xD4\x7B\x05\xA1\x62\xC3\x92\xD4\x7B\x06\xA1\x63\xD4\x7C\x61\x92\xD4\x7B\x07\xA1\x66\xCB\x3F\xBF\x97\x24\x74\x53\x8E\xF3\x92\xD4\x7B\x08\xA1\x69\xCD\x04\xD2\x92\xD4\x7B\x09\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xA4\x61\x73\x64\x66\x92\xD4\x7B\x0A\xA1\x6E\xC0\xD4\x7D\x05\x93\xD4\x7B\x0B\x92\xD4\x7B\x0C\xA1\x42\x88\xD4\x7D\x05\xC2\xD4\x7D\x06\xD4\x7C\x62\xD4\x7D\x07\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\xD4\x7D\x08\xCD\x09\x29\xD4\x7D\x09\xA4\x68\x6A\x6B\x6C\xD4\x7D\x0A\x0C\x92\xD4\x7B\x0D\xA2\x69\x69\xCD\x04\x57\x92\xD4\x7B\x0E\xA2\x73\x73\xA4\x71\x77\x65\x72\x92\xD4\x7B\x0F\xA2\x61\x61\xD4\x7D\x03
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "C"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,a]": [{
+ "typ": 123,
+ "data": [3]
+ }, [{
+ "typ": 123,
+ "data": [4]
+ }, "A"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,b]": true,
+ "[<MsgPackExt typ: 0x7b, data: \\x06>,c]": {
+ "typ": 124,
+ "data": [97]
+ },
+ "[<MsgPackExt typ: 0x7b, data: \\x07>,f]": 0.123,
+ "[<MsgPackExt typ: 0x7b, data: \\x08>,i]": 1234,
+ "[<MsgPackExt typ: 0x7b, data: \\x09>,serialization_specific_name]": "asdf",
+ "[<MsgPackExt typ: 0x7b, data: \\x0A>,n]": null
+ }],
+ "<MsgPackExt typ: 0x7d, data: \\x05>": [{
+ "typ": 123,
+ "data": [11]
+ }, [{
+ "typ": 123,
+ "data": [12]
+ }, "B"], {
+ "<MsgPackExt typ: 0x7d, data: \\x05>": false,
+ "<MsgPackExt typ: 0x7d, data: \\x06>": {
+ "typ": 124,
+ "data": [98]
+ },
+ "<MsgPackExt typ: 0x7d, data: \\x07>": 123.123,
+ "<MsgPackExt typ: 0x7d, data: \\x08>": 2345,
+ "<MsgPackExt typ: 0x7d, data: \\x09>": "hjkl",
+ "<MsgPackExt typ: 0x7d, data: \\x0A>": 12,
+ "[<MsgPackExt typ: 0x7b, data: \\x0D>,ii]": 1111,
+ "[<MsgPackExt typ: 0x7b, data: \\x0E>,ss]": "qwer"
+ }],
+ "[<MsgPackExt typ: 0x7b, data: \\x0F>,aa]": {
+ "typ": 125,
+ "data": [3]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# 1. Nit source:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x44\x89\x92\xD4\x7B\x02\xA1\x62\xC2\x92\xD4\x7B\x03\xA1\x63\xD4\x7C\x62\x92\xD4\x7B\x04\xA1\x66\xCB\x40\x5E\xC7\xDF\x3B\x64\x5A\x1D\x92\xD4\x7B\x05\xA1\x69\xCD\x09\x29\x92\xD4\x7B\x06\xBB\x73\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x5F\x73\x70\x65\x63\x69\x66\x69\x63\x5F\x6E\x61\x6D\x65\xAE\x6E\x65\x77\x20\x6C\x69\x6E\x65\x20\x2D\x3E\x0A\x3C\x2D\x92\xD4\x7B\x07\xA1\x6E\xC0\x92\xD4\x7B\x08\xA2\x69\x69\xCD\x04\x57\x92\xD4\x7B\x09\xA2\x73\x73\xA6\x09\x66\x22\x0D\x5C\x2F\x92\xD4\x7B\x0A\xA1\x64\xD4\x7D\x00
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "D"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,b]": false,
+ "[<MsgPackExt typ: 0x7b, data: \\x03>,c]": {
+ "typ": 124,
+ "data": [98]
+ },
+ "[<MsgPackExt typ: 0x7b, data: \\x04>,f]": 123.123,
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,i]": 2345,
+ "[<MsgPackExt typ: 0x7b, data: \\x06>,serialization_specific_name]": "new line ->\n<-",
+ "[<MsgPackExt typ: 0x7b, data: \\x07>,n]": null,
+ "[<MsgPackExt typ: 0x7b, data: \\x08>,ii]": 1111,
+ "[<MsgPackExt typ: 0x7b, data: \\x09>,ss]": "\tf\"\r\\/",
+ "[<MsgPackExt typ: 0x7b, data: \\x0A>,d]": {
+ "typ": 125,
+ "data": [0]
+ }
+}]
+
+# 4. Back in Nit (with metadata):
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# 1. Nit source:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x45\x82\x92\xD4\x7B\x02\xA1\x61\x93\xD4\x7B\x03\x92\xD4\x7B\x04\xAD\x41\x72\x72\x61\x79\x5B\x4F\x62\x6A\x65\x63\x74\x5D\x93\xA5\x68\x65\x6C\x6C\x6F\xCD\x04\xD2\xCB\x40\x5E\xD9\x99\x99\x99\x99\x9A\x92\xD4\x7B\x05\xA1\x62\x93\xD4\x7B\x06\x92\xD4\x7B\x07\xBC\x41\x72\x72\x61\x79\x5B\x6E\x75\x6C\x6C\x61\x62\x6C\x65\x20\x53\x65\x72\x69\x61\x6C\x69\x7A\x61\x62\x6C\x65\x5D\x93\xA5\x68\x65\x6C\x6C\x61\xCD\x09\x29\xCB\x40\x6D\x50\x00\x00\x00\x00\x00
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "E"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,a]": [{
+ "typ": 123,
+ "data": [3]
+ }, [{
+ "typ": 123,
+ "data": [4]
+ }, "Array[Object]"], ["hello", 1234, 123.4]],
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,b]": [{
+ "typ": 123,
+ "data": [6]
+ }, [{
+ "typ": 123,
+ "data": [7]
+ }, "Array[nullable Serializable]"], ["hella", 2345, 234.5]]
+}]
+
+# 4. Back in Nit (with metadata):
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# 1. Nit source:
+<F: 2222>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA6\x46\x5B\x49\x6E\x74\x5D\x81\x92\xD4\x7B\x02\xA1\x6E\xCD\x08\xAE
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "F[Int]"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,n]": 2222
+}]
+
+# 4. Back in Nit (with metadata):
+<F: 2222>
+
+# 1. Nit source:
+<F: 33.33>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA8\x46\x5B\x46\x6C\x6F\x61\x74\x5D\x81\x92\xD4\x7B\x02\xA1\x6E\xCB\x40\x40\xAA\x3D\x70\xA3\xD7\x0A
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "F[Float]"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,n]": 33.33
+}]
+
+# 4. Back in Nit (with metadata):
+<F: 33.33>
+
+# 1. Nit source:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# 2. MsgPack:
+\x93\xD4\x7B\x00\x92\xD4\x7B\x01\xA1\x47\x84\x92\xD4\x7B\x02\xA2\x68\x73\x93\xD4\x7B\x03\x92\xD4\x7B\x04\xAC\x48\x61\x73\x68\x53\x65\x74\x5B\x49\x6E\x74\x5D\x92\xFF\x00\x92\xD4\x7B\x05\xA1\x73\x93\xD4\x7B\x06\x92\xD4\x7B\x07\xB0\x41\x72\x72\x61\x79\x53\x65\x74\x5B\x53\x74\x72\x69\x6E\x67\x5D\x92\xA3\x6F\x6E\x65\xA3\x74\x77\x6F\x92\xD4\x7B\x08\xA2\x68\x6D\x93\xD4\x7B\x09\x92\xD4\x7B\x0A\xB4\x48\x61\x73\x68\x4D\x61\x70\x5B\x53\x74\x72\x69\x6E\x67\x2C\x20\x49\x6E\x74\x5D\x82\xA3\x6F\x6E\x65\x01\xA3\x74\x77\x6F\x02\x92\xD4\x7B\x0B\xA2\x61\x6D\x93\xD4\x7B\x0C\x92\xD4\x7B\x0D\xB8\x41\x72\x72\x61\x79\x4D\x61\x70\x5B\x53\x74\x72\x69\x6E\x67\x2C\x20\x53\x74\x72\x69\x6E\x67\x5D\x82\xA5\x74\x68\x72\x65\x65\xA1\x33\xA4\x66\x6F\x75\x72\xA1\x34
+
+# 3. JSON:
+[{
+ "typ": 123,
+ "data": [0]
+}, [{
+ "typ": 123,
+ "data": [1]
+}, "G"], {
+ "[<MsgPackExt typ: 0x7b, data: \\x02>,hs]": [{
+ "typ": 123,
+ "data": [3]
+ }, [{
+ "typ": 123,
+ "data": [4]
+ }, "HashSet[Int]"], [-1, 0]],
+ "[<MsgPackExt typ: 0x7b, data: \\x05>,s]": [{
+ "typ": 123,
+ "data": [6]
+ }, [{
+ "typ": 123,
+ "data": [7]
+ }, "ArraySet[String]"], ["one", "two"]],
+ "[<MsgPackExt typ: 0x7b, data: \\x08>,hm]": [{
+ "typ": 123,
+ "data": [9]
+ }, [{
+ "typ": 123,
+ "data": [10]
+ }, "HashMap[String, Int]"], {
+ "one": 1,
+ "two": 2
+ }],
+ "[<MsgPackExt typ: 0x7b, data: \\x0B>,am]": [{
+ "typ": 123,
+ "data": [12]
+ }, [{
+ "typ": 123,
+ "data": [13]
+ }, "ArrayMap[String, String]"], {
+ "three": "3",
+ "four": "4"
+ }]
+}]
+
+# 4. Back in Nit (with metadata):
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import test_deserialization
+
+var entities = new TestEntities
+var tests = entities.with_generics
+
+for o in tests do
+ print "# Custom:\n{o}\n"
+ print "# Inspect:\n{o.inspect}\n"
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# alt0 non-generics plain
+# alt1 non-generics metadata
+# alt2 generics plain
+# alt3 generics metadata
+# alt4 generics metadata cache_metadata_strings
+
+import test_deserialization
+import msgpack
+import msgpack::read
+import json
+#alt2# import test_deserialization_serial
+#alt3# import test_deserialization_serial
+#alt4# import test_deserialization_serial
+
+var entities = new TestEntities
+
+var tests = entities.without_generics
+#alt2#tests = entities.with_generics
+#alt3#tests = entities.with_generics
+#alt4#tests = entities.with_generics
+
+for o in tests do
+ var plain = true
+ #alt1#plain = false
+ #alt3#plain = false
+ #alt4#plain = false
+
+ var writer = new BytesWriter
+ var serializer = new MsgPackSerializer(writer)
+ serializer.plain_msgpack = plain
+ #alt4#serializer.cache_metadata_strings = true
+ serializer.serialize o
+ var bytes = writer.bytes
+
+ # Nit source
+ print "# 1. Nit source:\n{o}\n"
+
+ # Generated MessagePack
+ print "# 2. MsgPack:\n{bytes.chexdigest}\n"
+
+ # Python deserialization line to ensure compliance and otherwise debug
+ #print "msgpack.unpackb(b'{bytes.chexdigest}')"
+
+ # Show readable structure of the generated MessagePack as JSON
+ var msg = (new BytesReader(bytes)).read_msgpack
+ var json = if msg != null then
+ msg.serialize_to_json(plain=true, pretty=true)
+ else "null"
+ print "# 3. JSON:\n{json}\n"
+
+ # Deserialize
+ var deserializer = new MsgPackDeserializer(new BytesReader(bytes))
+ if serializer.plain_msgpack then
+ var deserialized = deserializer.deserialize(o.class_name)
+ print "# 4. Back in Nit (no metadata):\n{deserialized or else "null"}\n"
+ else
+ var deserialized = deserializer.deserialize
+ print "# 4. Back in Nit (with metadata):\n{deserialized or else "null"}\n"
+ end
+end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_bad_comp is test_suite
-
-import test_suite
+module test_bad_comp is test
class TestSuiteBadComp
- super TestSuite
+ test
- fun test_good do
+ fun test_good is test do
assert true
end
- fun test_bad do
+ fun test_bad is test do
assert bad_method
end
end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_bad_comp2 is test_suite
-
-import test_suite
+module test_bad_comp2 is test
class TestSuiteBadComp
- super TestSuite
+ test
- fun test_good do
+ fun test_good is test do
assert true
end
- fun test_bad(param: Bool) do
+ fun test_bad(param: Bool) is test do
assert param
end
end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_nitunit4 is test_suite
+module test_nitunit4 is test
import test_nitunit4_base
class TestTestSuite
super SuperTestSuite
+ test
- fun test_foo do
+ fun test_foo is test do
print "Tested method"
assert before
before = false
end
- fun test_bar do
+ fun test_bar is test do
print "Tested method"
end
- fun test_baz do
+ fun test_baz is test do
print "Tested method"
end
- fun test_sav_conflict do
+ fun test_sav_conflict is test do
print "Tested method"
end
end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_nitunit4_base is test_suite
-
-import test_suite
+module test_nitunit4_base
class SuperTestSuite
- super TestSuite
var before = false
- redef fun before_test do
+ fun before_test is before do
print "Before Test"
before = true
end
- redef fun after_test do
+ fun after_test is after do
print "After Test"
assert before
end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_nitunit5 is test_suite
-
-import test_suite
+module test_nitunit5 is test
class TestNitunit5
- super TestSuite
+ test
- fun test_path_is_set do
+ fun test_path_is_set is test do
assert "NIT_TESTING_PATH".environ != ""
end
- fun test_path_is_suite_path do
+ fun test_path_is_suite_path is test do
assert "NIT_TESTING_PATH".environ.basename == "test_nitunit5.nit"
end
end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_nitunit6 is test_suite
-
-import test_suite
+module test_nitunit6 is test
class TestNitunit6
- super TestSuite
+ test
- fun test_foo do
+ fun test_foo is test do
assert true
end
end
-redef fun before_module do
+fun before_module is before_all do
assert false
end
-redef fun after_module do
+fun after_module is after_all do
assert false
end
# See the License for the specific language governing permissions and
# limitations under the License.
-module test_nitunit7 is test_suite
-
-import test_suite
+module test_nitunit7 is test
class TestNitunit7
- super TestSuite
+ test
- fun test_foo do
+ fun test_foo is test do
assert true
end
end
-redef fun before_module do
+fun before_module is before_all do
assert true
end
-redef fun after_module do
+fun after_module is after_all do
assert false
end
# limitations under the License.
# NitUnit file for test_nitunit module.
-module test_test_nitunit is test_suite
+module test_test_nitunit is test
-import test_suite
intrude import test_nitunit
class TestX
- super TestSuite
+ test
var subject: X is noinit
- redef fun before_test do
+ fun before_test is before do
subject = new X
end
- fun test_foo do
+ fun test_foo is test do
subject.foo
end
# will fail
- fun test_foo1 do
+ fun test_foo1 is test do
subject.foo1(10, 20)
assert false
end
- fun test_foo2 do
+ fun test_foo2 is test do
assert subject.foo2
end
end