From 0fd0be4f9a51af7c0ab0a0a007287e538d755c35 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 14 May 2019 03:20:09 +0200 Subject: [PATCH] testsuite: Redo node-parser Base the rewrite on testsuite/css/parser/test-css-parser - we now require the node file to match a reference node and track the errors it triggers. We also no longer use gtester. --- gsk/meson.build | 2 +- testsuite/gsk/meson.build | 40 ++- testsuite/gsk/node-parser.c | 345 +++++++++++++++++++++-- testsuite/gsk/nodeparser/crash1.errors | 4 + testsuite/gsk/nodeparser/crash1.ref.node | 0 testsuite/gsk/nodeparser/crash2.errors | 5 + testsuite/gsk/nodeparser/crash2.ref.node | 0 testsuite/gsk/nodeparser/crash3.ref.node | 17 ++ testsuite/gsk/nodeparser/crash4.errors | 1 + testsuite/gsk/nodeparser/crash4.ref.node | 7 + 10 files changed, 382 insertions(+), 39 deletions(-) create mode 100644 testsuite/gsk/nodeparser/crash1.errors create mode 100644 testsuite/gsk/nodeparser/crash1.ref.node create mode 100644 testsuite/gsk/nodeparser/crash2.errors create mode 100644 testsuite/gsk/nodeparser/crash2.ref.node create mode 100644 testsuite/gsk/nodeparser/crash3.ref.node create mode 100644 testsuite/gsk/nodeparser/crash4.errors create mode 100644 testsuite/gsk/nodeparser/crash4.ref.node diff --git a/gsk/meson.build b/gsk/meson.build index a3b3ba002b..5b3e4d7ee6 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -26,6 +26,7 @@ gsk_public_sources = files([ 'gskrenderer.c', 'gskrendernode.c', 'gskrendernodeimpl.c', + 'gskrendernodeparser.c', 'gskroundedrect.c', 'gsktransform.c', 'gl/gskglrenderer.c', @@ -36,7 +37,6 @@ gsk_private_sources = files([ 'gskdebug.c', 'gskprivate.c', 'gskprofiler.c', - 'gskrendernodeparser.c', 'gl/gskshaderbuilder.c', 'gl/gskglprofiler.c', 'gl/gskglglyphcache.c', diff --git a/testsuite/gsk/meson.build b/testsuite/gsk/meson.build index 20851649b3..608ec49d27 100644 --- a/testsuite/gsk/meson.build +++ b/testsuite/gsk/meson.build @@ -69,24 +69,34 @@ foreach renderer : renderers endforeach node_parser_tests = [ - 'crash1', - 'crash2', - 'crash3', - 'crash4', + 'crash1.errors', + 'crash1.node', + 'crash1.ref.node', + 'crash2.errors', + 'crash2.node', + 'crash2.ref.node', + 'crash3.node', + 'crash3.ref.node', + 'crash4.errors', + 'crash4.node', + 'crash4.ref.node', ] foreach test : node_parser_tests - test('parser ' + test, node_parser, - args: [join_paths(meson.current_source_dir(), 'nodeparser', test + '.node')], - env: [ 'GIO_USE_VOLUME_MONITOR=unix', - 'GSETTINGS_BACKEND=memory', - 'GTK_CSD=1', - 'G_ENABLE_DIAGNOSTIC=0', - 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), - 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), - 'GSK_RENDERER=opengl' - ], - suite: 'gsk') + if test.endswith('.node') and not test.endswith('.ref.node') + test('parser ' + test, node_parser, + args: [ join_paths(meson.current_source_dir(), 'nodeparser', test) + ], + env: [ 'GIO_USE_VOLUME_MONITOR=unix', + 'GSETTINGS_BACKEND=memory', + 'GTK_CSD=1', + 'G_ENABLE_DIAGNOSTIC=0', + 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), + 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), + 'GSK_RENDERER=opengl' + ], + suite: 'gsk') + endif endforeach serialize_deserialize_tests = [ diff --git a/testsuite/gsk/node-parser.c b/testsuite/gsk/node-parser.c index 9db6df204b..f0e1e7d889 100644 --- a/testsuite/gsk/node-parser.c +++ b/testsuite/gsk/node-parser.c @@ -1,44 +1,343 @@ +/* + * Copyright (C) 2011 Red Hat Inc. + * + * Author: + * Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + #include +static char * +test_get_reference_file (const char *node_file) +{ + GString *file = g_string_new (NULL); + + if (g_str_has_suffix (node_file, ".node")) + g_string_append_len (file, node_file, strlen (node_file) - 5); + else + g_string_append (file, node_file); + + g_string_append (file, ".ref.node"); + + if (!g_file_test (file->str, G_FILE_TEST_EXISTS)) + { + g_string_free (file, TRUE); + return g_strdup (node_file); + } + + return g_string_free (file, FALSE); +} + +static char * +test_get_errors_file (const char *node_file) +{ + GString *file = g_string_new (NULL); + + if (g_str_has_suffix (node_file, ".node")) + g_string_append_len (file, node_file, strlen (node_file) - 5); + else + g_string_append (file, node_file); + + g_string_append (file, ".errors"); + + if (!g_file_test (file->str, G_FILE_TEST_EXISTS)) + { + g_string_free (file, TRUE); + return NULL; + } + + return g_string_free (file, FALSE); +} + +static GBytes * +diff_with_file (const char *file1, + GBytes *input, + GError **error) +{ + GSubprocess *process; + GBytes *output; + + process = g_subprocess_new (G_SUBPROCESS_FLAGS_STDIN_PIPE + | G_SUBPROCESS_FLAGS_STDOUT_PIPE, + error, + "diff", "-u", file1, "-", NULL); + if (process == NULL) + return NULL; + + if (!g_subprocess_communicate (process, + input, + NULL, + &output, + NULL, + error)) + { + g_object_unref (process); + return NULL; + } + + return output; +} + +static void +append_error_value (GString *string, + GType enum_type, + guint value) +{ + GEnumClass *enum_class; + GEnumValue *enum_value; + + enum_class = g_type_class_ref (enum_type); + enum_value = g_enum_get_value (enum_class, value); + + g_string_append (string, enum_value->value_name); + + g_type_class_unref (enum_class); +} + static void deserialize_error_func (const GtkCssSection *section, const GError *error, gpointer user_data) { - char *section_str = gtk_css_section_to_string (section); + GString *errors = user_data; + char *section_string; - /* We want to parse invalid node files in this test and simply assert that the - * parser doesn't crash. So, just g_message() here instead of a warning or error. */ - g_message ("Error at %s: %s", section_str, error->message); + section_string = gtk_css_section_to_string (section); - free (section_str); + g_string_append_printf (errors, + "%s: error: ", + section_string); + g_free (section_string); + + if (error->domain == GTK_CSS_PARSER_ERROR) + append_error_value (errors, GTK_TYPE_CSS_PARSER_ERROR, error->code); + else if (error->domain == GTK_CSS_PARSER_WARNING) + append_error_value (errors, GTK_TYPE_CSS_PARSER_WARNING, error->code); + else + g_string_append_printf (errors, + "%s %u\n", + g_quark_to_string (error->domain), + error->code); + + g_string_append_c (errors, '\n'); +} + +static gboolean +parse_node_file (GFile *file, gboolean generate) +{ + char *node_file, *reference_file, *errors_file; + GskRenderNode *node; + GString *errors; + GBytes *diff, *bytes; + GError *error = NULL; + gboolean result = TRUE; + + bytes = g_file_load_bytes (file, NULL, NULL, &error); + if (error) + { + g_print ("Error loading file: %s\n", error->message); + g_clear_error (&error); + return FALSE; + } + g_assert (bytes != NULL); + + errors = g_string_new (""); + + node = gsk_render_node_deserialize (bytes, deserialize_error_func, errors); + g_bytes_unref (bytes); + bytes = gsk_render_node_serialize (node); + gsk_render_node_unref (node); + + if (generate) + { + g_print ("%s", (char *) g_bytes_get_data (bytes, NULL)); + g_bytes_unref (bytes); + g_string_free (errors, TRUE); + return TRUE; + } + + node_file = g_file_get_path (file); + reference_file = test_get_reference_file (node_file); + + diff = diff_with_file (reference_file, bytes, &error); + g_assert_no_error (error); + + if (diff && g_bytes_get_size (diff) > 0) + { + g_print ("Resulting file doesn't match reference:\n%s\n", + (const char *) g_bytes_get_data (diff, NULL)); + result = FALSE; + } + g_free (reference_file); + g_clear_pointer (&diff, g_bytes_unref); + + errors_file = test_get_errors_file (node_file); + + if (errors_file) + { + GBytes *error_bytes = g_string_free_to_bytes (errors); + diff = diff_with_file (errors_file, error_bytes, &error); + g_assert_no_error (error); + + if (diff && g_bytes_get_size (diff) > 0) + { + g_print ("Errors don't match expected errors:\n%s\n", + (const char *) g_bytes_get_data (diff, NULL)); + result = FALSE; + } + g_clear_pointer (&diff, g_bytes_unref); + g_clear_pointer (&error_bytes, g_bytes_unref); + } + else if (errors->str[0]) + { + g_print ("Unexpected errors:\n%s\n", errors->str); + result = FALSE; + g_string_free (errors, TRUE); + } + else + { + g_string_free (errors, TRUE); + } + + g_free (errors_file); + g_free (node_file); + g_bytes_unref (bytes); + + return result; +} + +static gboolean +test_file (GFile *file) +{ + return parse_node_file (file, FALSE); +} + +static int +compare_files (gconstpointer a, gconstpointer b) +{ + GFile *file1 = G_FILE (a); + GFile *file2 = G_FILE (b); + char *path1, *path2; + int result; + + path1 = g_file_get_path (file1); + path2 = g_file_get_path (file2); + + result = strcmp (path1, path2); + + g_free (path1); + g_free (path2); + + return result; +} + +static gboolean +test_files_in_directory (GFile *dir) +{ + GFileEnumerator *enumerator; + GFileInfo *info; + GList *l, *files; + GError *error = NULL; + gboolean result = TRUE; + + enumerator = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error); + g_assert_no_error (error); + files = NULL; + + while ((info = g_file_enumerator_next_file (enumerator, NULL, &error))) + { + const char *filename; + + filename = g_file_info_get_name (info); + + if (!g_str_has_suffix (filename, ".node") || + g_str_has_suffix (filename, ".out.node") || + g_str_has_suffix (filename, ".ref.node")) + { + g_object_unref (info); + continue; + } + + files = g_list_prepend (files, g_file_get_child (dir, filename)); + + g_object_unref (info); + } + + g_assert_no_error (error); + g_object_unref (enumerator); + + files = g_list_sort (files, compare_files); + for (l = files; l; l = l->next) + { + result &= test_file (l->data); + } + g_list_free_full (files, g_object_unref); + + return result; } int main (int argc, char **argv) { - GError *error = NULL; - GskRenderNode *node; - GBytes *bytes; - GFile *file; + gboolean success; - g_assert (argc == 2); + gtk_test_init (&argc, &argv); - gtk_init (); + if (argc < 2) + { + const char *basedir; + GFile *dir; - file = g_file_new_for_commandline_arg (argv[1]); - bytes = g_file_load_bytes (file, NULL, NULL, &error); - g_assert_no_error (error); - g_assert (bytes != NULL); + basedir = g_test_get_dir (G_TEST_DIST); + dir = g_file_new_for_path (basedir); + success = test_files_in_directory (dir); - node = gsk_render_node_deserialize (bytes, deserialize_error_func, NULL); - if (error) - g_test_message ("Error: %s\n", error->message); + g_object_unref (dir); + } + else if (strcmp (argv[1], "--generate") == 0) + { + if (argc >= 3) + { + GFile *file = g_file_new_for_commandline_arg (argv[2]); - g_clear_error (&error); - g_clear_pointer (&node, gsk_render_node_unref); - g_bytes_unref (bytes); - g_object_unref (file); + success = parse_node_file (file, TRUE); - return 0; + g_object_unref (file); + } + else + success = FALSE; + } + else + { + guint i; + + success = TRUE; + + for (i = 1; i < argc; i++) + { + GFile *file = g_file_new_for_commandline_arg (argv[i]); + + success &= test_file (file); + + g_object_unref (file); + } + } + + return success ? 0 : 1; } + diff --git a/testsuite/gsk/nodeparser/crash1.errors b/testsuite/gsk/nodeparser/crash1.errors new file mode 100644 index 0000000000..9e36437262 --- /dev/null +++ b/testsuite/gsk/nodeparser/crash1.errors @@ -0,0 +1,4 @@ +:2:9-10: error: GTK_CSS_PARSER_ERROR_SYNTAX +:2:17-3:1: error: GTK_CSS_PARSER_WARNING_SYNTAX +:3:1: error: GTK_CSS_PARSER_ERROR_SYNTAX +:1:1-3:1: error: GTK_CSS_PARSER_WARNING_SYNTAX diff --git a/testsuite/gsk/nodeparser/crash1.ref.node b/testsuite/gsk/nodeparser/crash1.ref.node new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testsuite/gsk/nodeparser/crash2.errors b/testsuite/gsk/nodeparser/crash2.errors new file mode 100644 index 0000000000..540ad3c099 --- /dev/null +++ b/testsuite/gsk/nodeparser/crash2.errors @@ -0,0 +1,5 @@ +:2:3-7: error: GTK_CSS_PARSER_ERROR_SYNTAX +:2:15-4:1: error: GTK_CSS_PARSER_WARNING_SYNTAX +:4:1: error: GTK_CSS_PARSER_ERROR_SYNTAX +:4:1: error: GTK_CSS_PARSER_ERROR_SYNTAX +:1:1-4:1: error: GTK_CSS_PARSER_WARNING_SYNTAX diff --git a/testsuite/gsk/nodeparser/crash2.ref.node b/testsuite/gsk/nodeparser/crash2.ref.node new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testsuite/gsk/nodeparser/crash3.ref.node b/testsuite/gsk/nodeparser/crash3.ref.node new file mode 100644 index 0000000000..ccc74d2851 --- /dev/null +++ b/testsuite/gsk/nodeparser/crash3.ref.node @@ -0,0 +1,17 @@ +cross-fade { + end: color { + bounds: 5 5 10 10; + color: rgb(0,0,255); + } + progress: 0.2; + start: container { + color { + bounds: 0 0 10 10; + color: rgb(255,0,0); + } + color { + bounds: 10 10 10 10; + color: rgb(0,128,0); + } + } +} diff --git a/testsuite/gsk/nodeparser/crash4.errors b/testsuite/gsk/nodeparser/crash4.errors new file mode 100644 index 0000000000..90d2cc278c --- /dev/null +++ b/testsuite/gsk/nodeparser/crash4.errors @@ -0,0 +1 @@ +:6:3-7:1: error: GTK_CSS_PARSER_WARNING_SYNTAX diff --git a/testsuite/gsk/nodeparser/crash4.ref.node b/testsuite/gsk/nodeparser/crash4.ref.node new file mode 100644 index 0000000000..e85e6e5eca --- /dev/null +++ b/testsuite/gsk/nodeparser/crash4.ref.node @@ -0,0 +1,7 @@ +blur { + blur: 40; + child: color { + bounds: 100 100 100 100; + color: rgb(0,0,0); + } +}