diff options
Diffstat (limited to 'tests/gvdb.c')
-rw-r--r-- | tests/gvdb.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/tests/gvdb.c b/tests/gvdb.c new file mode 100644 index 0000000..d054067 --- /dev/null +++ b/tests/gvdb.c @@ -0,0 +1,438 @@ +#include <glib.h> +#include "../gvdb/gvdb-reader.h" + +static void +test_reader_open_error (void) +{ + GError *error = NULL; + GvdbTable *table; + + table = gvdb_table_new (SRCDIR "/gvdbs/does_not_exist", TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT); + g_assert (table == NULL); + g_clear_error (&error); + + table = gvdb_table_new (SRCDIR "/gvdbs/file_empty", TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert (table == NULL); + g_clear_error (&error); + + table = gvdb_table_new (SRCDIR "/gvdbs/invalid_header", TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert (table == NULL); + g_clear_error (&error); + + table = gvdb_table_new (SRCDIR "/gvdbs/file_too_small", TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert (table == NULL); + g_clear_error (&error); +} + +static void +test_reader_empty (void) +{ + const gchar * strings[] = { "", "value", "/value", ".", NULL}; + GError *error = NULL; + GvdbTable *table; + gchar **names; + gint n_names; + gint i; + + table = gvdb_table_new (SRCDIR "/gvdbs/empty_gvdb", TRUE, &error); + g_assert_no_error (error); + g_assert (table != NULL); + + g_assert (gvdb_table_is_valid (table)); + + names = gvdb_table_get_names (table, &n_names); + g_assert_cmpint (n_names, ==, 0); + g_assert_cmpint (g_strv_length (names), ==, 0); + g_strfreev (names); + + names = gvdb_table_get_names (table, NULL); + g_assert_cmpint (g_strv_length (names), ==, 0); + g_strfreev (names); + + for (i = 0; strings[i]; i++) + { + const gchar *key = strings[i]; + GvdbTable *sub; + GVariant *val; + gboolean has; + gchar **list; + + sub = gvdb_table_get_table (table, key); + g_assert (sub == NULL); + + has = gvdb_table_has_value (table, key); + g_assert (!has); + + val = gvdb_table_get_value (table, key); + g_assert (val == NULL); + + val = gvdb_table_get_raw_value (table, key); + g_assert (val == NULL); + + list = gvdb_table_list (table, key); + g_assert (list == NULL); + } + + gvdb_table_free (table); +} + +static void +verify_table (GvdbTable *table) +{ + GVariant *value; + gchar **list; + gint n_names; + gboolean has; + + /* We could not normally expect these to be in a particular order but + * we are using a specific test file that we know to be layed out this + * way... + * + * It's pure luck that they happened to be layed out in this nice way. + */ + list = gvdb_table_get_names (table, &n_names); + g_assert_cmpint (n_names, ==, g_strv_length (list)); + g_assert_cmpint (n_names, ==, 5); + g_assert_cmpstr (list[0], ==, "/"); + g_assert_cmpstr (list[1], ==, "/values/"); + g_assert_cmpstr (list[2], ==, "/values/boolean"); + g_assert_cmpstr (list[3], ==, "/values/string"); + g_assert_cmpstr (list[4], ==, "/values/int32"); + g_strfreev (list); + + list = gvdb_table_list (table, "/"); + g_assert (list != NULL); + g_assert_cmpint (g_strv_length (list), ==, 1); + g_assert_cmpstr (list[0], ==, "values/"); + g_strfreev (list); + + list = gvdb_table_list (table, "/values/"); + g_assert (list != NULL); + g_assert_cmpint (g_strv_length (list), ==, 3); + g_assert_cmpstr (list[0], ==, "boolean"); + g_assert_cmpstr (list[1], ==, "int32"); + g_assert_cmpstr (list[2], ==, "string"); + g_strfreev (list); + + /* A directory is not a value */ + has = gvdb_table_has_value (table, "/"); + g_assert (!has); + has = gvdb_table_has_value (table, "/values/"); + g_assert (!has); + + has = gvdb_table_has_value (table, "/int32"); + g_assert (!has); + has = gvdb_table_has_value (table, "values/int32"); + g_assert (!has); + has = gvdb_table_has_value (table, "/values/int32"); + g_assert (has); + + value = gvdb_table_get_value (table, "/"); + g_assert (value == NULL); + value = gvdb_table_get_value (table, "/values/"); + g_assert (value == NULL); + value = gvdb_table_get_value (table, "/int32"); + g_assert (value == NULL); + value = gvdb_table_get_value (table, "values/int32"); + g_assert (value == NULL); + + value = gvdb_table_get_value (table, "/values/boolean"); + g_assert (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)); + g_assert (g_variant_get_boolean (value)); + g_variant_unref (value); + + value = gvdb_table_get_raw_value (table, "/values/boolean"); + g_assert (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)); + g_assert (g_variant_get_boolean (value)); + g_variant_unref (value); + + value = gvdb_table_get_value (table, "/values/int32"); + g_assert (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)); + g_assert_cmpint (g_variant_get_int32 (value), ==, 0x44332211); + g_variant_unref (value); + + value = gvdb_table_get_value (table, "/values/string"); + g_assert (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "a string"); + g_variant_unref (value); + + value = gvdb_table_get_raw_value (table, "/values/string"); + g_assert (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "a string"); + g_variant_unref (value); +} + +static void +test_reader_values (void) +{ + GError *error = NULL; + GvdbTable *table; + + table = gvdb_table_new (SRCDIR "/gvdbs/example_gvdb", TRUE, &error); + g_assert_no_error (error); + verify_table (table); + +#if G_BYTE_ORDER == G_BIG_ENDIAN + { + GVariant *value; + + value = gvdb_table_get_raw_value (table, "/values/int32"); + g_assert (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)); + g_assert_cmpint (g_variant_get_int32 (value), ==, 0x11223344); + g_variant_unref (value); + } +#endif + + gvdb_table_free (table); +} + +static void +test_reader_values_bigendian (void) +{ + GError *error = NULL; + GvdbTable *table; + + table = gvdb_table_new (SRCDIR "/gvdbs/example_gvdb.big-endian", TRUE, &error); + g_assert_no_error (error); + verify_table (table); + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + { + GVariant *value; + + value = gvdb_table_get_raw_value (table, "/values/int32"); + g_assert (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)); + g_assert_cmpint (g_variant_get_int32 (value), ==, 0x11223344); + g_variant_unref (value); + } +#endif + + gvdb_table_free (table); +} + +static void +test_nested (void) +{ + GError *error = NULL; + GvdbTable *table; + GvdbTable *locks; + gchar **names; + gint n_names; + gboolean has; + + table = gvdb_table_new (SRCDIR "/gvdbs/nested_gvdb", TRUE, &error); + g_assert_no_error (error); + + /* Note the more-random ordering here compared with above. */ + names = gvdb_table_get_names (table, &n_names); + g_assert_cmpint (n_names, ==, g_strv_length (names)); + g_assert_cmpint (n_names, ==, 6); + g_assert_cmpstr (names[0], ==, "/values/boolean"); + g_assert_cmpstr (names[1], ==, "/"); + g_assert_cmpstr (names[2], ==, "/values/int32"); + g_assert_cmpstr (names[3], ==, ".locks"); + g_assert_cmpstr (names[4], ==, "/values/"); + g_assert_cmpstr (names[5], ==, "/values/string"); + g_strfreev (names); + + locks = gvdb_table_get_table (table, "/"); + g_assert (locks == NULL); + locks = gvdb_table_get_table (table, "/values/"); + g_assert (locks == NULL); + locks = gvdb_table_get_table (table, "/values/int32"); + g_assert (locks == NULL); + + locks = gvdb_table_get_table (table, ".locks"); + g_assert (locks != NULL); + + has = gvdb_table_has_value (locks, "/first/lck"); + g_assert (!has); + + has = gvdb_table_has_value (locks, "/first/lock"); + g_assert (has); + + has = gvdb_table_has_value (locks, "/second"); + g_assert (has); + + gvdb_table_free (table); + gvdb_table_free (locks); +} + +/* This function exercises the API against @table but does not do any + * asserts on unexpected values (although it will assert on inconsistent + * values returned by the API). + */ +static void +inspect_carefully (GvdbTable *table, + gint level) +{ + const gchar * key_names[] = { + "/", "/values/", "/int32", "values/int32", + "/values/int32", "/values/boolean", "/values/string", + ".locks", "/first/lock", "/second", NULL + }; + gint found_items; + gchar **names; + gint n_names; + gint i; + + if (level > 100) + return; + + found_items = 0; + for (i = 0; key_names[i]; i++) + { + const gchar *key = key_names[i]; + GvdbTable *subtable; + GVariant *value; + gchar **list; + gboolean has; + + has = gvdb_table_has_value (table, key); + + list = gvdb_table_list (table, key); + g_assert (!has || list == NULL); + if (list) + { + gchar *joined = g_strjoinv (",", list); + g_strfreev (list); + g_free (joined); + found_items++; + } + + value = gvdb_table_get_value (table, key); + g_assert_cmpint (value != NULL, ==, has); + if (value) + { + gchar *printed = g_variant_print (value, FALSE); + g_variant_unref (value); + g_free (printed); + found_items++; + } + + value = gvdb_table_get_raw_value (table, key); + g_assert_cmpint (value != NULL, ==, has); + if (value) + { + gchar *printed = g_variant_print (value, FALSE); + g_variant_unref (value); + g_free (printed); + } + + subtable = gvdb_table_get_table (table, key); + g_assert (!has || subtable == NULL); + if (subtable) + { + inspect_carefully (subtable, level + 1); + gvdb_table_free (subtable); + found_items++; + } + } + + names = gvdb_table_get_names (table, &n_names); + g_assert_cmpint (n_names, ==, g_strv_length (names)); + g_assert_cmpint (found_items, <=, n_names); + g_free (g_strjoinv (" ", names)); + g_strfreev (names); +} + +static void +test_corrupted (gconstpointer user_data) +{ + gint percentage = GPOINTER_TO_INT (user_data); + GError *error = NULL; + GMappedFile *mapped; + + mapped = g_mapped_file_new (SRCDIR "/gvdbs/nested_gvdb", FALSE, &error); + g_assert_no_error (error); + g_assert (mapped); + + if (percentage) + { + GvdbTable *table; + const gchar *orig; + gsize length; + gchar *copy; + gint i; + + orig = g_mapped_file_get_contents (mapped); + length = g_mapped_file_get_length (mapped); + copy = g_memdup (orig, length); + + for (i = 0; i < 10000; i++) + { + GBytes *bytes; + gint j; + + /* Make a broken copy, but leave the signature intact so that + * we don't get too many boring trivial failures. + */ + for (j = 8; j < length; j++) + if (g_test_rand_int_range (0, 100) < percentage) + copy[j] = g_test_rand_int_range (0, 256); + else + copy[j] = orig[j]; + + bytes = g_bytes_new_static (copy, length); + table = gvdb_table_new_from_bytes (bytes, FALSE, &error); + g_bytes_unref (bytes); + + /* If we damaged the header, it may not open */ + if (table) + { + inspect_carefully (table, 0); + gvdb_table_free (table); + } + else + { + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_clear_error (&error); + } + } + + g_free (copy); + } + else + { + GvdbTable *table; + GBytes *bytes; + + bytes = g_mapped_file_get_bytes (mapped); + table = gvdb_table_new_from_bytes (bytes, FALSE, &error); + g_bytes_unref (bytes); + + g_assert_no_error (error); + g_assert (table); + + inspect_carefully (table, 0); + gvdb_table_free (table); + } + + g_mapped_file_unref (mapped); +} + +int +main (int argc, char **argv) +{ + gint i; + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gvdb/reader/open-error", test_reader_open_error); + g_test_add_func ("/gvdb/reader/empty", test_reader_empty); + g_test_add_func ("/gvdb/reader/values", test_reader_values); + g_test_add_func ("/gvdb/reader/values/big-endian", test_reader_values_bigendian); + g_test_add_func ("/gvdb/reader/nested", test_nested); + for (i = 0; i < 20; i++) + { + gchar test_name[80]; + g_snprintf (test_name, sizeof test_name, "/gvdb/reader/corrupted/%d%%", i); + g_test_add_data_func (test_name, GINT_TO_POINTER (i), test_corrupted); + } + + return g_test_run (); +} |