commit 8510b2178d608dd9f3dbc84e8c1c340e189d9987
parent dda3cfd1ff2b230838f01e62b6ef468526ee9c3f
Author: Dan Callaghan <djc@djc.id.au>
Date: Sun, 22 Jul 2012 16:59:59 +1000
handle timezones for datetime; added tests
Diffstat:
6 files changed, 209 insertions(+), 7 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,2 +1,3 @@
/glibrdf.pc
/libglibrdf.so*
+/tests
diff --git a/Makefile b/Makefile
@@ -19,6 +19,7 @@ LDFLAGS ?= -Wl,--as-needed -Wl,-O1
LIBS = $(shell pkg-config --libs $(REQUIRES))
OBJECTS = glibrdf.o
+TEST_OBJECTS = tests.o test_literal_gvalue.o
.PHONY: all
all: $(LIB) $(NAME).pc
@@ -35,9 +36,21 @@ $(LIB): $(OBJECTS)
glibrdf.o: glibrdf.h
+.PHONY: check
+check: tests
+ env MALLOC_CHECK_=2 \
+ G_DEBUG="fatal_warnings fatal_criticals" \
+ G_SLICE="debug-blocks" \
+ gtester ./tests --verbose
+
+tests: $(OBJECTS) $(TEST_OBJECTS)
+ $(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+test_literal_gvalue.o: glibrdf.h
+
.PHONY: clean
clean:
- rm -f $(LIB) $(OBJECTS) $(NAME).pc
+ rm -f $(LIB) $(OBJECTS) $(TEST_OBJECTS) $(NAME).pc
.PHONY: install
install:
diff --git a/glibrdf.c b/glibrdf.c
@@ -22,12 +22,30 @@ GType librdf_node_get_gtype(void) {
// glib should do this for me >:(
static GDate *parse_iso8601_date(const gchar *s) {
- struct tm tm;
- if (*strptime(s, "%Y-%m-%d", &tm) != '\0')
+ struct tm tm = {0};
+ if (*strptime(s, "%F", &tm) != '\0')
return NULL;
return g_date_new_dmy(tm.tm_mday, 1 + tm.tm_mon, 1900 + tm.tm_year);
}
+// g_time_val_from_iso8601 doesn't preserve timezone >:(((
+static GDateTime *parse_iso8601_datetime(const gchar *s) {
+ struct tm tm = {0};
+ // parse the date and time, timezone will be left at the end
+ const gchar *rest = strptime(s, "%FT%T", &tm);
+ GTimeZone *tz = g_time_zone_new(rest);
+ g_return_val_if_fail(tz != NULL, NULL);
+ GDateTime *result = g_date_time_new(tz,
+ 1900 + tm.tm_year,
+ 1 + tm.tm_mon,
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ (gdouble) tm.tm_sec);
+ g_time_zone_unref(tz);
+ return result;
+}
+
// XXX handle parse failures more gracefully?
// XXX should have some kind of registry so that callers can add new types
void librdf_node_get_literal_gvalue(librdf_node *node, GValue *value_out) {
@@ -60,11 +78,10 @@ void librdf_node_get_literal_gvalue(librdf_node *node, GValue *value_out) {
}
if (g_strcmp0(datatype_uri_string,
"http://www.w3.org/TR/xmlschema-2/#datetime") == 0) {
- GTimeVal tv;
- bool parsed = g_time_val_from_iso8601(lv, &tv);
- g_return_if_fail(parsed);
+ GDateTime *datetime = parse_iso8601_datetime(lv);
+ g_return_if_fail(datetime != NULL);
g_value_init(value_out, G_TYPE_DATE_TIME);
- g_value_set_boxed(value_out, g_date_time_new_from_timeval_utc(&tv));
+ g_value_set_boxed(value_out, datetime);
return;
}
g_warning("Unhandled RDF type %s", librdf_uri_as_string(datatype_uri));
diff --git a/test-data.xml b/test-data.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:ex="http://example.com/">
+ <rdf:Description rdf:about="http://example.com/Resource">
+ <ex:untyped>blah</ex:untyped>
+ <ex:integer rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">123</ex:integer>
+ <ex:date rdf:datatype="http://www.w3.org/TR/xmlschema-2/#date">1986-08-16</ex:date>
+ <ex:datetime rdf:datatype="http://www.w3.org/TR/xmlschema-2/#datetime">2012-07-22T15:57:41+10:00</ex:datetime>
+ </rdf:Description>
+</rdf:RDF>
diff --git a/test_literal_gvalue.c b/test_literal_gvalue.c
@@ -0,0 +1,144 @@
+
+/*
+ * test_literal_gvalue.c, part of glibrdf
+ * Copyright 2012 Dan Callaghan <djc@djc.id.au>
+ * Licensed under GPLv3
+ */
+
+#include "glibrdf.h"
+
+#define FIXTURE_TYPE LiteralGValueFixture
+#define SETUP literal_gvalue_setup
+#define TEARDOWN literal_gvalue_teardown
+#define DATA const void *user_data G_GNUC_UNUSED
+#define PACKAGE "/literal_gvalue/"
+#define TEST_ADD(func) g_test_add(PACKAGE #func, FIXTURE_TYPE, NULL, SETUP, func, TEARDOWN)
+
+typedef struct {
+ librdf_world *world;
+ librdf_storage *storage;
+ librdf_model *model;
+} FIXTURE_TYPE;
+
+static void SETUP(FIXTURE_TYPE *fixture, DATA) {
+ fixture->world = librdf_new_world();
+ g_assert(fixture->world != NULL);
+ librdf_world_open(fixture->world);
+ fixture->storage = librdf_new_storage(fixture->world,
+ "hashes", NULL, "hash-type='memory'");
+ g_assert(fixture->storage != NULL);
+ fixture->model = librdf_new_model(fixture->world, fixture->storage, "");
+ g_assert(fixture->model != NULL);
+
+ librdf_parser *parser = librdf_new_parser(fixture->world,
+ NULL, "application/rdf+xml", NULL);
+ g_assert(parser != NULL);
+ librdf_uri *source_uri = librdf_new_uri_from_filename(
+ fixture->world, "test-data.xml");
+ g_assert(source_uri != NULL);
+ int parse_error = librdf_parser_parse_into_model(parser,
+ source_uri, NULL, fixture->model);
+ g_assert(!parse_error);
+ librdf_free_uri(source_uri);
+ librdf_free_parser(parser);
+}
+
+static void TEARDOWN(FIXTURE_TYPE *fixture, DATA) {
+ librdf_free_model(fixture->model);
+ librdf_free_storage(fixture->storage);
+ librdf_free_world(fixture->world);
+}
+
+static void test_untyped(FIXTURE_TYPE *fixture, DATA) {
+ librdf_node *subj = librdf_new_node_from_uri_string(fixture->world,
+ (const unsigned char *)"http://example.com/Resource");
+ g_assert(subj != NULL);
+ librdf_node *prop = librdf_new_node_from_uri_string(fixture->world,
+ (const unsigned char *)"http://example.com/untyped");
+ g_assert(prop != NULL);
+ librdf_node *obj = librdf_model_get_target(fixture->model, subj, prop);
+ g_assert(obj != NULL);
+ GValue value = {0};
+ librdf_node_get_literal_gvalue(obj, &value);
+ g_assert(G_VALUE_HOLDS(&value, G_TYPE_STRING));
+ g_assert_cmpstr(g_value_get_string(&value), ==, "blah");
+ g_value_unset(&value);
+ librdf_free_node(obj);
+ librdf_free_node(prop);
+ librdf_free_node(subj);
+}
+
+static void test_integer(FIXTURE_TYPE *fixture, DATA) {
+ librdf_node *subj = librdf_new_node_from_uri_string(fixture->world,
+ (const unsigned char *)"http://example.com/Resource");
+ g_assert(subj != NULL);
+ librdf_node *prop = librdf_new_node_from_uri_string(fixture->world,
+ (const unsigned char *)"http://example.com/integer");
+ g_assert(prop != NULL);
+ librdf_node *obj = librdf_model_get_target(fixture->model, subj, prop);
+ g_assert(obj != NULL);
+ GValue value = {0};
+ librdf_node_get_literal_gvalue(obj, &value);
+ g_assert(G_VALUE_HOLDS(&value, G_TYPE_INT64));
+ g_assert_cmpint(g_value_get_int64(&value), ==, 123);
+ g_value_unset(&value);
+ librdf_free_node(obj);
+ librdf_free_node(prop);
+ librdf_free_node(subj);
+}
+
+static void test_date(FIXTURE_TYPE *fixture, DATA) {
+ librdf_node *subj = librdf_new_node_from_uri_string(fixture->world,
+ (const unsigned char *)"http://example.com/Resource");
+ g_assert(subj != NULL);
+ librdf_node *prop = librdf_new_node_from_uri_string(fixture->world,
+ (const unsigned char *)"http://example.com/date");
+ g_assert(prop != NULL);
+ librdf_node *obj = librdf_model_get_target(fixture->model, subj, prop);
+ g_assert(obj != NULL);
+ GValue value = {0};
+ librdf_node_get_literal_gvalue(obj, &value);
+ g_assert(G_VALUE_HOLDS(&value, G_TYPE_DATE));
+ GDate *date = (GDate *)g_value_get_boxed(&value);
+ g_assert_cmpuint(g_date_get_year(date), ==, 1986);
+ g_assert_cmpuint(g_date_get_month(date), ==, 8);
+ g_assert_cmpuint(g_date_get_day(date), ==, 16);
+ g_value_unset(&value);
+ librdf_free_node(obj);
+ librdf_free_node(prop);
+ librdf_free_node(subj);
+}
+
+static void test_datetime(FIXTURE_TYPE *fixture, DATA) {
+ librdf_node *subj = librdf_new_node_from_uri_string(fixture->world,
+ (const unsigned char *)"http://example.com/Resource");
+ g_assert(subj != NULL);
+ librdf_node *prop = librdf_new_node_from_uri_string(fixture->world,
+ (const unsigned char *)"http://example.com/datetime");
+ g_assert(prop != NULL);
+ librdf_node *obj = librdf_model_get_target(fixture->model, subj, prop);
+ g_assert(obj != NULL);
+ GValue value = {0};
+ librdf_node_get_literal_gvalue(obj, &value);
+ g_assert(G_VALUE_HOLDS(&value, G_TYPE_DATE_TIME));
+ GDateTime *datetime = (GDateTime *)g_value_get_boxed(&value);
+ g_assert_cmpint(g_date_time_get_year(datetime), ==, 2012);
+ g_assert_cmpint(g_date_time_get_month(datetime), ==, 7);
+ g_assert_cmpint(g_date_time_get_day_of_month(datetime), ==, 22);
+ g_assert_cmpint(g_date_time_get_hour(datetime), ==, 15);
+ g_assert_cmpint(g_date_time_get_minute(datetime), ==, 57);
+ g_assert_cmpfloat(g_date_time_get_seconds(datetime), ==, 41.0);
+ g_assert_cmpint(g_date_time_get_utc_offset(datetime), ==,
+ /* 10 hours in us */ 10 * 60 * 60 * 1000000LL);
+ g_value_unset(&value);
+ librdf_free_node(obj);
+ librdf_free_node(prop);
+ librdf_free_node(subj);
+}
+
+void add_literal_gvalue_tests(void) {
+ TEST_ADD(test_untyped);
+ TEST_ADD(test_integer);
+ TEST_ADD(test_date);
+ TEST_ADD(test_datetime);
+}
diff --git a/tests.c b/tests.c
@@ -0,0 +1,17 @@
+
+/*
+ * tests.c, part of glibrdf
+ * Copyright 2012 Dan Callaghan <djc@djc.id.au>
+ * Licensed under GPLv3
+ */
+
+#include <glib-object.h>
+
+void add_literal_gvalue_tests(void);
+
+int main(int argc, char *argv[]) {
+ g_type_init();
+ g_test_init(&argc, &argv, NULL);
+ add_literal_gvalue_tests();
+ return g_test_run();
+}