aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarvin W <git@larma.de>2017-05-13 17:43:51 +0200
committerMarvin W <git@larma.de>2017-08-12 11:59:38 +0200
commit6904bda756321c67bea0108342fa9ba859dd66e9 (patch)
tree24517bf54d6188af401afedd343c42b6ece3d1c6
parentdd88db7556a20707f6fe3c81b3c58df42a0f5224 (diff)
downloaddino-6904bda756321c67bea0108342fa9ba859dd66e9.tar.gz
dino-6904bda756321c67bea0108342fa9ba859dd66e9.zip
xmpp-vala: improve namespace handling, add some tests
-rwxr-xr-xconfigure6
-rw-r--r--xmpp-vala/CMakeLists.txt18
-rw-r--r--xmpp-vala/src/core/namespace_state.vala43
-rw-r--r--xmpp-vala/src/core/stanza_attribute.vala7
-rw-r--r--xmpp-vala/src/core/stanza_node.vala25
-rw-r--r--xmpp-vala/src/core/stanza_reader.vala27
-rw-r--r--xmpp-vala/src/core/xmpp_stream.vala2
-rw-r--r--xmpp-vala/tests/common.vala93
-rw-r--r--xmpp-vala/tests/stanza.vala105
-rw-r--r--xmpp-vala/tests/testcase.vala80
10 files changed, 375 insertions, 31 deletions
diff --git a/configure b/configure
index 22673bc4..51d2ab58 100755
--- a/configure
+++ b/configure
@@ -1,7 +1,7 @@
#!/bin/bash
OPTS=`getopt -o "h" --long \
-help,fetch-only,no-debug,disable-fast-vapi,\
+help,fetch-only,no-debug,disable-fast-vapi,with-tests,\
enable-plugin:,disable-plugin:,\
prefix:,program-prefix:,exec-prefix:,lib-suffix:,\
bindir:,libdir:,includedir:,datadir:,\
@@ -16,6 +16,7 @@ eval set -- "$OPTS"
PREFIX=${PREFIX:-/usr/local}
ENABLED_PLUGINS=
DISABLED_PLUGINS=
+BUILD_TESTS=
DISABLE_FAST_VAPI=
LIB_SUFFIX=
NO_DEBUG=
@@ -51,6 +52,7 @@ Configuration:
feature. fast-vapi mode is slower when doing
clean builds, but faster when doing incremental
builds (during development).
+ --with-tests Also build tests.
Plugin configuration:
--enable-plugin=PLUGIN Enable compilation of plugin PLUGIN.
@@ -108,6 +110,7 @@ while true; do
--disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;;
--no-debug ) NO_DEBUG=yes; shift ;;
--fetch-only ) FETCH_ONLY=yes; shift ;;
+ --with-tests ) BUILD_TESTS=yes; shift ;;
# Autotools paths
--program-prefix ) PREFIX="$2"; shift; shift ;;
--exec-prefix ) EXEC_PREFIX="$2"; shift; shift ;;
@@ -238,6 +241,7 @@ cmake -G "$cmake_type" \
-DCMAKE_INSTALL_PREFIX="$PREFIX" \
-DENABLED_PLUGINS="$ENABLED_PLUGINS" \
-DDISABLED_PLUGINS="$DISABLED_PLUGINS" \
+ -DBUILD_TESTS="$BUILD_TESTS" \
-DVALA_EXECUTABLE="$VALAC" \
-DCMAKE_VALA_FLAGS="$VALACFLAGS" \
-DDISABLE_FAST_VAPI="$DISABLE_FAST_VAPI" \
diff --git a/xmpp-vala/CMakeLists.txt b/xmpp-vala/CMakeLists.txt
index 779540cc..2e2287a6 100644
--- a/xmpp-vala/CMakeLists.txt
+++ b/xmpp-vala/CMakeLists.txt
@@ -85,3 +85,21 @@ set_target_properties(xmpp-vala PROPERTIES VERSION 0.1 SOVERSION 0)
install(TARGETS xmpp-vala ${TARGET_INSTALL})
install(FILES ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.deps DESTINATION ${VAPI_INSTALL_DIR})
install(FILES ${CMAKE_BINARY_DIR}/exports/xmpp-vala.h DESTINATION ${INCLUDE_INSTALL_DIR})
+
+if(BUILD_TESTS)
+ vala_precompile(ENGINE_TEST_VALA_C
+ SOURCES
+ "tests/common.vala"
+ "tests/testcase.vala"
+
+ "tests/stanza.vala"
+ CUSTOM_VAPIS
+ ${CMAKE_BINARY_DIR}/exports/xmpp-vala_internal.vapi
+ PACKAGES
+ ${ENGINE_PACKAGES}
+ )
+
+ add_definitions(${VALA_CFLAGS})
+ add_executable(xmpp-vala-test ${ENGINE_TEST_VALA_C})
+ target_link_libraries(xmpp-vala-test xmpp-vala ${SIGNAL_PROTOCOL_PACKAGES})
+endif(BUILD_TESTS)
diff --git a/xmpp-vala/src/core/namespace_state.vala b/xmpp-vala/src/core/namespace_state.vala
index e71607fa..b55812b4 100644
--- a/xmpp-vala/src/core/namespace_state.vala
+++ b/xmpp-vala/src/core/namespace_state.vala
@@ -1,68 +1,80 @@
using Gee;
namespace Xmpp.Core {
+
public class NamespaceState {
private HashMap<string, string> uri_to_name = new HashMap<string, string> ();
private HashMap<string, string> name_to_uri = new HashMap<string, string> ();
public string current_ns_uri;
- public NamespaceState () {
+ private NamespaceState parent;
+
+ public NamespaceState() {
add_assoc(XMLNS_URI, "xmlns");
- add_assoc("http://www.w3.org/XML/1998/namespace", "xml");
- current_ns_uri = "http://www.w3.org/XML/1998/namespace";
+ add_assoc(XML_URI, "xml");
+ current_ns_uri = XML_URI;
}
- public NamespaceState.for_stanza () {
+ public NamespaceState.for_stanza() {
this();
add_assoc("http://etherx.jabber.org/streams", "stream");
current_ns_uri = "jabber:client";
}
- public NamespaceState.copy (NamespaceState old) {
+ private NamespaceState.copy(NamespaceState old) {
foreach (string key in old.uri_to_name.keys) {
add_assoc(key, old.uri_to_name[key]);
}
set_current(old.current_ns_uri);
}
- public NamespaceState.with_assoc (NamespaceState old, string ns_uri, string name) {
+ private NamespaceState.with_parent(NamespaceState parent) {
+ this.copy(parent);
+ this.parent = parent;
+ }
+
+ public NamespaceState.with_assoc(NamespaceState old, string ns_uri, string name) {
this.copy(old);
add_assoc(ns_uri, name);
}
- public NamespaceState.with_current (NamespaceState old, string current_ns_uri) {
+ public NamespaceState.with_current(NamespaceState old, string current_ns_uri) {
this.copy(old);
set_current(current_ns_uri);
}
- public void add_assoc (string ns_uri, string name) {
+ public void add_assoc(string ns_uri, string name) {
name_to_uri[name] = ns_uri;
uri_to_name[ns_uri] = name;
}
- public void set_current (string current_ns_uri) {
+ public void set_current(string current_ns_uri) {
this.current_ns_uri = current_ns_uri;
}
- public string find_name (string ns_uri) throws XmlError {
+ public string find_name(string ns_uri) throws XmlError {
if (uri_to_name.has_key(ns_uri)) {
return uri_to_name[ns_uri];
}
throw new XmlError.NS_DICT_ERROR(@"NS URI $ns_uri not found.");
}
- public string find_uri (string name) throws XmlError {
+ public string find_uri(string name) throws XmlError {
if (name_to_uri.has_key(name)) {
return name_to_uri[name];
}
throw new XmlError.NS_DICT_ERROR(@"NS name $name not found.");
}
- public NamespaceState clone() {
- return new NamespaceState.copy(this);
+ public NamespaceState push() {
+ return new NamespaceState.with_parent(this);
}
- public string to_string () {
+ public NamespaceState pop() {
+ return parent;
+ }
+
+ public string to_string() {
StringBuilder sb = new StringBuilder ();
sb.append ("NamespaceState{");
foreach (string key in uri_to_name.keys) {
@@ -77,4 +89,5 @@ public class NamespaceState {
return sb.str;
}
}
-} \ No newline at end of file
+
+}
diff --git a/xmpp-vala/src/core/stanza_attribute.vala b/xmpp-vala/src/core/stanza_attribute.vala
index f751c801..ea776110 100644
--- a/xmpp-vala/src/core/stanza_attribute.vala
+++ b/xmpp-vala/src/core/stanza_attribute.vala
@@ -20,6 +20,13 @@ public class StanzaAttribute : StanzaEntry {
this.val = val;
}
+ public bool equals(StanzaAttribute other) {
+ if (other.ns_uri != ns_uri) return false;
+ if (other.name != name) return false;
+ if (other.val != val) return false;
+ return true;
+ }
+
internal string printf(string fmt, bool no_ns = false, string? ns_name = null) {
if (no_ns) {
return fmt.printf(name, (!)val);
diff --git a/xmpp-vala/src/core/stanza_node.vala b/xmpp-vala/src/core/stanza_node.vala
index 67b8db35..341e67ba 100644
--- a/xmpp-vala/src/core/stanza_node.vala
+++ b/xmpp-vala/src/core/stanza_node.vala
@@ -300,6 +300,25 @@ public class StanzaNode : StanzaEntry {
return this;
}
+ public bool equals(StanzaNode other) {
+ if (other.name != name) return false;
+ if (other.val != val) return false;
+ if (name == "#text") return true;
+ if (other.ns_uri != ns_uri) return false;
+
+ if (other.sub_nodes.size != sub_nodes.size) return false;
+ for (int i = 0; i < sub_nodes.size; i++) {
+ if (!other.sub_nodes[i].equals(sub_nodes[i])) return false;
+ }
+
+ if (other.attributes.size != attributes.size) return false;
+ for (int i = 0; i < attributes.size; i++) {
+ if (!other.attributes[i].equals(attributes[i])) return false;
+ }
+
+ return true;
+ }
+
private const string TAG_START_BEGIN_FORMAT = "%s<{%s}:%s";
private const string TAG_START_EMPTY_END = " />\n";
private const string TAG_START_CONTENT_END = ">\n";
@@ -358,12 +377,13 @@ public class StanzaNode : StanzaEntry {
public string to_xml(NamespaceState? state = null) throws XmlError {
NamespaceState my_state = state ?? new NamespaceState.for_stanza();
if (name == "#text") return val == null ? "" : (!)encoded_val;
+ my_state = my_state.push();
foreach (var xmlns in get_attributes_by_ns_uri (XMLNS_URI)) {
if (xmlns.val == null) continue;
if (xmlns.name == "xmlns") {
- my_state = new NamespaceState.with_current(my_state, (!)xmlns.val);
+ my_state.set_current((!)xmlns.val);
} else {
- my_state = new NamespaceState.with_assoc(my_state, (!)xmlns.val, xmlns.name);
+ my_state.add_assoc((!)xmlns.val, xmlns.name);
}
}
var sb = new StringBuilder();
@@ -391,6 +411,7 @@ public class StanzaNode : StanzaEntry {
}
}
}
+ my_state = my_state.pop();
return sb.str;
}
}
diff --git a/xmpp-vala/src/core/stanza_reader.vala b/xmpp-vala/src/core/stanza_reader.vala
index dd284fa6..f4b900d1 100644
--- a/xmpp-vala/src/core/stanza_reader.vala
+++ b/xmpp-vala/src/core/stanza_reader.vala
@@ -1,7 +1,9 @@
using Gee;
namespace Xmpp.Core {
+
public const string XMLNS_URI = "http://www.w3.org/2000/xmlns/";
+public const string XML_URI = "http://www.w3.org/XML/1998/namespace";
public const string JABBER_URI = "jabber:client";
public errordomain XmlError {
@@ -85,7 +87,7 @@ public class StanzaReader {
private string read_until_ws() throws XmlError {
var res = new StringBuilder();
var what = peek_single();
- while(!is_ws(what)) {
+ while (!is_ws(what)) {
res.append_c(read_single());
what = peek_single();
}
@@ -95,7 +97,7 @@ public class StanzaReader {
private string read_until_char_or_ws(char x, char y = 0) throws XmlError {
var res = new StringBuilder();
var what = peek_single();
- while(what != x && what != y && !is_ws(what)) {
+ while (what != x && what != y && !is_ws(what)) {
res.append_c(read_single());
what = peek_single();
}
@@ -105,11 +107,11 @@ public class StanzaReader {
private string read_until_char(char x) throws XmlError {
var res = new StringBuilder();
var what = peek_single();
- while(what != x) {
+ while (what != x) {
res.append_c(read_single());
what = peek_single();
}
- return res.str;
+ return res.str;
}
private StanzaAttribute read_attribute() throws XmlError {
@@ -169,7 +171,7 @@ public class StanzaReader {
eof = true;
skip_single();
res.name = read_until_char_or_ws('>');
- while(peek_single() != '>') {
+ while (peek_single() != '>') {
skip_single();
}
skip_single();
@@ -184,7 +186,7 @@ public class StanzaReader {
res.attributes.add(read_attribute());
skip_until_non_ws();
}
- if (read_single() == '/' || res.pseudo ) {
+ if (read_single() == '/' || res.pseudo) {
res.has_nodes = false;
skip_single();
} else {
@@ -215,8 +217,8 @@ public class StanzaReader {
}
}
- public StanzaNode read_stanza_node(NamespaceState? baseNs = null) throws XmlError {
- ns_state = baseNs ?? new NamespaceState.for_stanza();
+ public StanzaNode read_stanza_node() throws XmlError {
+ ns_state = ns_state.push();
var res = read_node_start();
if (res.has_nodes) {
bool finishNodeSeen = false;
@@ -238,8 +240,7 @@ public class StanzaReader {
}
finishNodeSeen = true;
} else {
- res.sub_nodes.add(read_stanza_node(ns_state.clone()));
- ns_state = baseNs ?? new NamespaceState.for_stanza();
+ res.sub_nodes.add(read_stanza_node());
}
} else {
res.sub_nodes.add(read_text_node());
@@ -247,16 +248,18 @@ public class StanzaReader {
} while (!finishNodeSeen);
if (res.sub_nodes.size == 0) res.has_nodes = false;
}
+ ns_state = ns_state.pop();
return res;
}
- public StanzaNode read_node(NamespaceState? baseNs = null) throws XmlError {
+ public StanzaNode read_node() throws XmlError {
skip_until_non_ws();
if (peek_single() == '<') {
- return read_stanza_node(baseNs ?? new NamespaceState.for_stanza());
+ return read_stanza_node();
} else {
return read_text_node();
}
}
}
+
}
diff --git a/xmpp-vala/src/core/xmpp_stream.vala b/xmpp-vala/src/core/xmpp_stream.vala
index eb6ffaa5..a8201a22 100644
--- a/xmpp-vala/src/core/xmpp_stream.vala
+++ b/xmpp-vala/src/core/xmpp_stream.vala
@@ -11,7 +11,7 @@ public errordomain IOStreamError {
}
public class XmppStream {
- private static string NS_URI = "http://etherx.jabber.org/streams";
+ public const string NS_URI = "http://etherx.jabber.org/streams";
public string remote_name;
public XmppLog log = new XmppLog();
diff --git a/xmpp-vala/tests/common.vala b/xmpp-vala/tests/common.vala
new file mode 100644
index 00000000..b393ba79
--- /dev/null
+++ b/xmpp-vala/tests/common.vala
@@ -0,0 +1,93 @@
+namespace Xmpp.Test {
+
+int main(string[] args) {
+ GLib.Test.init(ref args);
+ GLib.Test.set_nonfatal_assertions();
+ TestSuite.get_root().add_suite(new Xmpp.Test.StanzaTest().get_suite());
+ return GLib.Test.run();
+}
+
+bool fail_if(bool exp, string? reason = null) {
+ if (exp) {
+ if (reason != null) GLib.Test.message(reason);
+ GLib.Test.fail();
+ return true;
+ }
+ return false;
+}
+
+void fail_if_reached(string? reason = null) {
+ fail_if(true, reason);
+}
+
+delegate void ErrorFunc() throws Error;
+
+void fail_if_not_error_code(ErrorFunc func, int expectedCode, string? reason = null) {
+ try {
+ func();
+ fail_if_reached(@"$(reason + ": " ?? "")no error thrown");
+ } catch (Error e) {
+ fail_if_not_eq_int(e.code, expectedCode, @"$(reason + ": " ?? "")catched unexpected error");
+ }
+}
+
+bool fail_if_not(bool exp, string? reason = null) {
+ return fail_if(!exp, reason);
+}
+
+bool fail_if_eq_int(int left, int right, string? reason = null) {
+ return fail_if(left == right, @"$(reason + ": " ?? "")$left == $right");
+}
+
+bool fail_if_not_eq_node(Core.StanzaNode left, Core.StanzaNode right, string? reason = null) {
+ if (fail_if_not_eq_str(left.name, right.name, @"$(reason + ": " ?? "")name mismatch")) return true;
+ if (fail_if_not_eq_str(left.val, right.val, @"$(reason + ": " ?? "")val mismatch")) return true;
+ if (left.name == "#text") return false;
+ if (fail_if_not_eq_str(left.ns_uri, right.ns_uri, @"$(reason + ": " ?? "")ns_uri mismatch")) return true;
+ if (fail_if_not_eq_int(left.sub_nodes.size, right.sub_nodes.size, @"$(reason + ": " ?? "")sub node count mismatch")) return true;
+ if (fail_if_not_eq_int(left.attributes.size, right.attributes.size, @"$(reason + ": " ?? "")attributes count mismatch")) return true;
+ for (var i = 0; i < left.sub_nodes.size; i++) {
+ if (fail_if_not_eq_node(left.sub_nodes[i], right.sub_nodes[i], @"$(reason + ": " ?? "")$(i+1)th subnode mismatch")) return true;
+ }
+ for (var i = 0; i < left.attributes.size; i++) {
+ if (fail_if_not_eq_attr(left.attributes[i], right.attributes[i], @"$(reason + ": " ?? "")$(i+1)th attribute mismatch")) return true;
+ }
+ return false;
+}
+
+bool fail_if_not_eq_attr(Core.StanzaAttribute left, Core.StanzaAttribute right, string? reason = null) {
+ if (fail_if_not_eq_str(left.name, right.name, @"$(reason + ": " ?? "")name mismatch")) return true;
+ if (fail_if_not_eq_str(left.val, right.val, @"$(reason + ": " ?? "")val mismatch")) return true;
+ if (fail_if_not_eq_str(left.ns_uri, right.ns_uri, @"$(reason + ": " ?? "")ns_uri mismatch")) return true;
+ return false;
+}
+
+bool fail_if_not_eq_int(int left, int right, string? reason = null) {
+ return fail_if_not(left == right, @"$(reason + ": " ?? "")$left != $right");
+}
+
+bool fail_if_not_eq_str(string? left, string? right, string? reason = null) {
+ bool nullcheck = (left == null || right == null) && (left != null && right != null);
+ if (left == null) left = "(null)";
+ if (right == null) right = "(null)";
+ return fail_if_not(!nullcheck && left == right, @"$(reason + ": " ?? "")$left != $right");
+}
+
+bool fail_if_not_eq_uint8_arr(uint8[] left, uint8[] right, string? reason = null) {
+ if (fail_if_not_eq_int(left.length, right.length, @"$(reason + ": " ?? "")array length not equal")) return true;
+ return fail_if_not_eq_str(Base64.encode(left), Base64.encode(right), reason);
+}
+
+bool fail_if_not_zero_int(int zero, string? reason = null) {
+ return fail_if_not_eq_int(zero, 0, reason);
+}
+
+bool fail_if_zero_int(int zero, string? reason = null) {
+ return fail_if_eq_int(zero, 0, reason);
+}
+
+bool fail_if_null(void* what, string? reason = null) {
+ return fail_if(what == null || (size_t)what == 0, reason);
+}
+
+}
diff --git a/xmpp-vala/tests/stanza.vala b/xmpp-vala/tests/stanza.vala
new file mode 100644
index 00000000..1662c9bc
--- /dev/null
+++ b/xmpp-vala/tests/stanza.vala
@@ -0,0 +1,105 @@
+using Xmpp.Core;
+
+namespace Xmpp.Test {
+
+class StanzaTest : Gee.TestCase {
+ public StanzaTest() {
+ base("Stanza");
+
+ add_test("node_one", test_node_one);
+ add_test("typical_stream", test_typical_stream);
+ add_test("ack_stream", test_ack_stream);
+ }
+
+ private void test_node_one() {
+ var node1 = new StanzaNode.build("test", "ns1_uri")
+ .add_self_xmlns()
+ .put_attribute("ns2", "ns2_uri", XMLNS_URI)
+ .put_attribute("bla", "blub")
+ .put_node(new StanzaNode.build("testaa", "ns2_uri")
+ .put_attribute("ns3", "ns3_uri", XMLNS_URI))
+ .put_node(new StanzaNode.build("testbb", "ns3_uri")
+ .add_self_xmlns());
+
+ var xml1 = node1.to_xml();
+ var node2 = new StanzaReader.for_string(xml1).read_node();
+ fail_if_not(node1.equals(node2));
+ fail_if_not_eq_str(node1.to_string(), node2.to_string());
+ }
+
+ private void test_typical_stream() {
+ var stream = """
+ <?xml version='1.0' encoding='UTF-8'?>
+ <stream:stream
+ to='example.com'
+ xmlns='jabber:client'
+ xmlns:stream='http://etherx.jabber.org/streams'
+ version='1.0'>
+ <message from='laurence@example.net/churchyard'
+ to='juliet@example.com'
+ xml:lang='en'>
+ <body>I'll send a friar with speed, to Mantua, with my letters to thy lord.</body>
+ </message>
+ </stream:stream>
+ """;
+ var root_node_cmp = new StanzaNode.build("stream", "http://etherx.jabber.org/streams")
+ .put_attribute("to", "example.com")
+ .put_attribute("xmlns", "jabber:client")
+ .put_attribute("stream", "http://etherx.jabber.org/streams", XMLNS_URI)
+ .put_attribute("version", "1.0");
+ var node_cmp = new StanzaNode.build("message")
+ .put_attribute("from", "laurence@example.net/churchyard")
+ .put_attribute("to", "juliet@example.com")
+ .put_attribute("lang", "en", XML_URI)
+ .put_node(new StanzaNode.build("body")
+ .put_node(new StanzaNode.text("I'll send a friar with speed, to Mantua, with my letters to thy lord.")));
+
+ var reader = new StanzaReader.for_string(stream);
+ fail_if_not_eq_node(root_node_cmp, reader.read_root_node());
+ fail_if_not_eq_node(node_cmp, reader.read_node());
+ reader.read_node();
+ fail_if_not_error_code(() => reader.read_node(), 3, "end of stream should be reached");
+ }
+
+ private void test_ack_stream() {
+ var stream = """
+ <?xml version='1.0' encoding='UTF-8'?>
+ <stream:stream
+ to='example.com'
+ xmlns='jabber:client'
+ xmlns:stream='http://etherx.jabber.org/streams'
+ xmlns:ack='http://jabber.org/protocol/ack'
+ version='1.0'>
+ <stream:features>
+ <ack:ack/>
+ <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
+ <required/>
+ </bind>
+ </stream:features>
+ <ack:r/>
+ </stream:stream>
+ """;
+ var root_node_cmp = new StanzaNode.build("stream", "http://etherx.jabber.org/streams")
+ .put_attribute("to", "example.com")
+ .put_attribute("xmlns", "jabber:client")
+ .put_attribute("stream", "http://etherx.jabber.org/streams", XMLNS_URI)
+ .put_attribute("ack", "http://jabber.org/protocol/ack", XMLNS_URI)
+ .put_attribute("version", "1.0");
+ var node_cmp = new StanzaNode.build("features", XmppStream.NS_URI)
+ .put_node(new StanzaNode.build("ack", "http://jabber.org/protocol/ack"))
+ .put_node(new StanzaNode.build("bind", "urn:ietf:params:xml:ns:xmpp-bind")
+ .add_self_xmlns()
+ .put_node(new StanzaNode.build("required", "urn:ietf:params:xml:ns:xmpp-bind")));
+ var node2_cmp = new StanzaNode.build("r", "http://jabber.org/protocol/ack");
+
+ var reader = new StanzaReader.for_string(stream);
+ fail_if_not_eq_node(root_node_cmp, reader.read_root_node());
+ fail_if_not_eq_node(node_cmp, reader.read_node());
+ fail_if_not_eq_node(node2_cmp, reader.read_node());
+ reader.read_node();
+ fail_if_not_error_code(() => reader.read_node(), 3, "end of stream should be reached");
+ }
+
+}
+
+}
diff --git a/xmpp-vala/tests/testcase.vala b/xmpp-vala/tests/testcase.vala
new file mode 100644
index 00000000..dd1fdefd
--- /dev/null
+++ b/xmpp-vala/tests/testcase.vala
@@ -0,0 +1,80 @@
+/* testcase.vala
+ *
+ * Copyright (C) 2009 Julien Peeters
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Julien Peeters <contact@julienpeeters.fr>
+ */
+
+public abstract class Gee.TestCase : Object {
+
+ private GLib.TestSuite suite;
+ private Adaptor[] adaptors = new Adaptor[0];
+
+ public delegate void TestMethod ();
+
+ public TestCase (string name) {
+ this.suite = new GLib.TestSuite (name);
+ }
+
+ public void add_test (string name, owned TestMethod test) {
+ var adaptor = new Adaptor (name, (owned)test, this);
+ this.adaptors += adaptor;
+
+ this.suite.add (new GLib.TestCase (adaptor.name,
+ adaptor.set_up,
+ adaptor.run,
+ adaptor.tear_down ));
+ }
+
+ public virtual void set_up () {
+ }
+
+ public virtual void tear_down () {
+ }
+
+ public GLib.TestSuite get_suite () {
+ return this.suite;
+ }
+
+ private class Adaptor {
+ [CCode (notify = false)]
+ public string name { get; private set; }
+ private TestMethod test;
+ private TestCase test_case;
+
+ public Adaptor (string name,
+ owned TestMethod test,
+ TestCase test_case) {
+ this.name = name;
+ this.test = (owned)test;
+ this.test_case = test_case;
+ }
+
+ public void set_up (void* fixture) {
+ this.test_case.set_up ();
+ }
+
+ public void run (void* fixture) {
+ this.test ();
+ }
+
+ public void tear_down (void* fixture) {
+ this.test_case.tear_down ();
+ }
+ }
+}