#include static void test_parse_empty (void) { GtkCssProvider *provider; GError *error; gboolean res; provider = gtk_css_provider_new (); error = NULL; res = gtk_css_provider_load_from_data (provider, "", -1, &error); g_assert (res); g_assert_no_error (error); g_clear_error (&error); g_object_unref (provider); } static void test_parse_at (void) { GtkCssProvider *provider; GError *error; gboolean res; gint i; const gchar *valid[] = { "@import \"" SRCDIR "/test.css\";", "@import '" SRCDIR "/test.css';", "@import url(\"" SRCDIR "/test.css\");", "@import url('" SRCDIR "/test.css');", "@import\nurl (\t\"" SRCDIR "/test.css\" ) ;", "@define-color bg_color #f9a039;", "@define-color color @bg_color;", "@define-color color rgb(100, 99, 88);", "@define-color color rgba(50%, 50%, 50%, 0.5);", "@define-color color lighter(#f9a039);", "@define-color color darker ( @blue ) ;", "@define-color color shade(@blue, 1.3);", "@define-color color alpha(@blue, 1.3);", "@define-color color mix(@blue, @red, 0.2);", "@define-color color red;", "@define-color color mix(shade (#121212, 0.5), mix (rgb(10%,20%,100%), @blue,0.5), 0.2);", "@define-color blue @blue;", "@define-color blue123_a-b #123;", "@binding-set gtk-emacs-menu { bind \"n\" { \"move-current\" (next) }; };", "@binding-set gtk-emacs-text-view {\n" " bind \"u\" { \"move-cursor\" (paragraph-ends, -1, 0)\n" " \"delete-from-cursor\" (paragraph-ends, 1) };\n" "};", "@binding-set test {\n" " bind \"space\" { \"set-anchor\" () };\n" " unbind \"v\";\n" "};", NULL }; const gchar *invalid[] = { "@import " SRCDIR "/test.css ;", "@import url ( \"" SRCDIR "/test.css\" xyz );", "@import url(\");", "@import url(');", "@import url(\"abc');", "@ import ;", "@define_color blue red;", "@define-color blue #12234;", "@define-color blue #12g234;", "@define-color blue @@;", "@define-color blue 5!#%4@DG$##x;", "@define-color color mix(@red, @blue, @green);", "@define-color color mix(@blue, 0.2, @red);", "@define-color color mix(0.2, @blue, @red);", "@define-color color mix(@blue, @red);", "@define-color color mix(@blue);", "@define-color color mix();", "@define-color color rgba(50%, 50%, 50%);", "@define-color color rgb(50%, a);", "@define-color 1col rgb(50%, a);", "@three-dee { some other crap };", "@binding-set \"foo\";", "@binding-set foo { bind key { \"action\"() }; };", "@binding-set foo { bind key { \"action\"() }; };", "@binding-set foo { bind \"key\" { action() }; };", "@binding-set foo { bind \"key\"; };", "@binding-set foo { unbind \"key\" { \"bla\" () }; };", NULL }; error = NULL; for (i = 0; valid[i]; i++) { provider = gtk_css_provider_new (); res = gtk_css_provider_load_from_data (provider, valid[i], -1, &error); if (error) g_print ("parsing '%s': got unexpected error: %s\n", valid[i], error->message); g_assert_no_error (error); g_assert (res); g_object_unref (provider); } for (i = 0; invalid[i]; i++) { provider = gtk_css_provider_new (); res = gtk_css_provider_load_from_data (provider, invalid[i], -1, &error); g_assert_error (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_FAILED); g_assert (!res); g_object_unref (provider); g_clear_error (&error); } } static void test_parse_selectors (void) { GtkCssProvider *provider; GError *error; gboolean res; gint i; const gchar *valid[] = { "* {}", "E {}", "E F {}", "E > F {}", "E#id {}", "#id {}", "tab:first-child {}", "tab:last-child {}", "tab:nth-child(first) {}", "tab:nth-child(last) {}", "tab:nth-child(even) {}", "tab:nth-child(odd) {}", "tab:sorted {}", ".some-class {}", ".some-class.another-class {}", ".some-class .another-class {}", "E * {}", "E .class {}", "E > .foo {}", "E > #id {}", "E:active {}", "E:prelight {}", "E:hover {}", "E:selected {}", "E:insensitive {}", "E:inconsistent {}", "E:focused {}", "E:active:prelight {}", "* > .notebook tab:first-child .label:focused {}", "E, F {}", "E, F /* comment here */ {}", "E,/* comment here */ F {}", "E1.e1_2 #T3_4 {}", NULL }; const gchar *invalid[] = { /* nth-child and similar pseudo classes can only * be used with regions, not with types */ "E:first-child {}", "E:last-child {}", "E:nth-child(first) {}", "E:nth-child(last) {}", "E:nth-child(even) {}", "E:nth-child(odd) {}", "E:sorted {}", /* widget state pseudo-classes can only be used for * the last element */ "E:focused tab {}", NULL }; error = NULL; for (i = 0; valid[i]; i++) { provider = gtk_css_provider_new (); res = gtk_css_provider_load_from_data (provider, valid[i], -1, &error); if (error) g_print ("parsing '%s': got unexpected error: %s\n", valid[i], error->message); g_assert_no_error (error); g_assert (res); g_object_unref (provider); } for (i = 0; invalid[i]; i++) { provider = gtk_css_provider_new (); res = gtk_css_provider_load_from_data (provider, invalid[i], -1, &error); g_assert_error (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_FAILED); g_assert (!res); g_object_unref (provider); g_clear_error (&error); } } static void test_parse_declarations (void) { GtkCssProvider *provider; GError *error; gboolean res; gint i; const gchar *valid[] = { "* {}", "* { font: Sans 15 }", "* { font: Sans 15; }", "* { font: bold }", "* { color: red }", "* { /* just a comment */ }", "* { /* multi\nline\ncomment */ }", "* { font: /* comment here */ Sans 15 }", "* { color: red; background-color: shade (@bg_color, 0.5) }", "* { margin: 5 }", "* { margin: 5 10 }", "* { margin: 5 10 3 }", "* { margin: 5 10 3 5 }", "* { padding: 5 }", "* { padding: 5 10 }", "* { border-width: 5; border-radius: 10 }", "* { border-color: #ff00ff }", "* { engine: clearlooks }", "* { background-image: -gtk-gradient (linear, \n" " left top, right top, \n" " from (#fff), to (#000)) }", "* { background-image: -gtk-gradient (linear, \n" " 0.0 0.5, 0.5 1.0, \n" " from (#fff), \n" " color-stop (0.5, #f00),\n" " to (#000)) }", "* { background-image: -gtk-gradient (radial, \n" " center center, 0.2, \n" " center center, 0.8, \n" " color-stop (0.0,#fff),\n" " color-stop (1.0,#000))}\n", "* { border-image: url (\"" SRCDIR "/test.png\") 3 4 3 4 stretch }", "* { border-image: url (\"" SRCDIR "/test.png\") 3 4 3 4 repeat stretch}", "* { transition: 150ms ease-in-out }", "* { transition: 1s linear loop }", NULL }; const gchar *invalid[] = { "* { color }", "* { color:green; color }", "* { color:red; color; color:green }", "* { color:green; color: }", "* { color:red; color:; color:green }", "* { color:green; color{;color:maroon} }", "* { color:red; color{;color:maroon}; color:green }", "* { content: 'Hello", NULL }; error = NULL; for (i = 0; valid[i]; i++) { provider = gtk_css_provider_new (); res = gtk_css_provider_load_from_data (provider, valid[i], -1, &error); if (error) g_print ("parsing '%s': got unexpected error: %s\n", valid[i], error->message); g_assert_no_error (error); g_assert (res); g_object_unref (provider); } for (i = 0; invalid[i]; i++) { provider = gtk_css_provider_new (); res = gtk_css_provider_load_from_data (provider, invalid[i], -1, &error); g_assert_error (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_FAILED); g_assert (!res); g_object_unref (provider); g_clear_error (&error); } } static void test_path (void) { GtkWidgetPath *path; GtkWidgetPath *path2; gint pos; GtkRegionFlags flags; path = gtk_widget_path_new (); g_assert_cmpint (gtk_widget_path_length (path), ==, 0); pos = gtk_widget_path_append_type (path, GTK_TYPE_WINDOW); g_assert_cmpint (pos, ==, 0); g_assert_cmpint (gtk_widget_path_length (path), ==, 1); g_assert (gtk_widget_path_iter_get_object_type (path, 0) == GTK_TYPE_WINDOW); g_assert (gtk_widget_path_is_type (path, GTK_TYPE_WIDGET)); g_assert (gtk_widget_path_iter_get_name (path, 0) == NULL); pos = gtk_widget_path_append_type (path, GTK_TYPE_WIDGET); g_assert_cmpint (pos, ==, 1); g_assert_cmpint (gtk_widget_path_length (path), ==, 2); gtk_widget_path_iter_set_object_type (path, pos, GTK_TYPE_BUTTON); g_assert (gtk_widget_path_is_type (path, GTK_TYPE_BUTTON)); g_assert (gtk_widget_path_has_parent (path, GTK_TYPE_WIDGET)); g_assert (gtk_widget_path_has_parent (path, GTK_TYPE_WINDOW)); g_assert (!gtk_widget_path_has_parent (path, GTK_TYPE_DIALOG)); g_assert (gtk_widget_path_iter_get_name (path, 1) == NULL); gtk_widget_path_iter_set_name (path, 1, "name"); g_assert (gtk_widget_path_iter_has_name (path, 1, "name")); gtk_widget_path_iter_add_class (path, 1, "class1"); gtk_widget_path_iter_add_class (path, 1, "class2"); g_assert (gtk_widget_path_iter_has_class (path, 1, "class1")); g_assert (gtk_widget_path_iter_has_class (path, 1, "class2")); g_assert (!gtk_widget_path_iter_has_class (path, 1, "class3")); path2 = gtk_widget_path_copy (path); g_assert (gtk_widget_path_iter_has_class (path2, 1, "class1")); g_assert (gtk_widget_path_iter_has_class (path2, 1, "class2")); g_assert (!gtk_widget_path_iter_has_class (path2, 1, "class3")); gtk_widget_path_free (path2); gtk_widget_path_iter_remove_class (path, 1, "class2"); g_assert (gtk_widget_path_iter_has_class (path, 1, "class1")); g_assert (!gtk_widget_path_iter_has_class (path, 1, "class2")); gtk_widget_path_iter_clear_classes (path, 1); g_assert (!gtk_widget_path_iter_has_class (path, 1, "class1")); gtk_widget_path_iter_add_region (path, 1, "tab", 0); gtk_widget_path_iter_add_region (path, 1, "title", GTK_REGION_EVEN | GTK_REGION_FIRST); g_assert (gtk_widget_path_iter_has_region (path, 1, "tab", &flags) && flags == 0); g_assert (gtk_widget_path_iter_has_region (path, 1, "title", &flags) && flags == (GTK_REGION_EVEN | GTK_REGION_FIRST)); g_assert (!gtk_widget_path_iter_has_region (path, 1, "extension", NULL)); path2 = gtk_widget_path_copy (path); g_assert (gtk_widget_path_iter_has_region (path2, 1, "tab", &flags) && flags == 0); g_assert (gtk_widget_path_iter_has_region (path2, 1, "title", &flags) && flags == (GTK_REGION_EVEN | GTK_REGION_FIRST)); g_assert (!gtk_widget_path_iter_has_region (path2, 1, "extension", NULL)); gtk_widget_path_free (path2); gtk_widget_path_free (path); } static void test_match (void) { GtkStyleContext *context; GtkWidgetPath *path; GtkCssProvider *provider; GError *error; const gchar *data; GdkRGBA color; GdkRGBA expected; error = NULL; provider = gtk_css_provider_new (); gdk_rgba_parse (&expected, "#fff"); context = gtk_style_context_new (); path = gtk_widget_path_new (); gtk_widget_path_append_type (path, GTK_TYPE_WINDOW); gtk_widget_path_append_type (path, GTK_TYPE_BOX); gtk_widget_path_append_type (path, GTK_TYPE_BUTTON); gtk_widget_path_iter_set_name (path, 0, "mywindow"); gtk_widget_path_iter_add_class (path, 2, "button"); gtk_style_context_set_path (context, path); gtk_widget_path_free (path); gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); data = "* { color: #fff }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); data = "* { color: #f00 }\n" "GtkButton { color: #fff }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); data = "* { color: #f00 }\n" "GtkButton { color: #fff }\n" "GtkWindow > GtkButton { color: #000 }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); data = "* { color: #f00 }\n" ".button { color: #fff }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); data = "* { color: #f00 }\n" "GtkButton { color: #000 }\n" ".button { color: #fff }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); data = "* { color: #f00 }\n" "GtkButton { color: #000 }\n" "GtkWindow GtkButton { color: #fff }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); data = "* { color: #f00 }\n" ".button { color: #000 }\n" "GtkWindow .button { color: #fff }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); data = "* { color: #f00 }\n" "* .button { color: #000 }\n" "#mywindow .button { color: #fff }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); data = "* { color: #f00 }\n" "GtkWindow .button { color: #000 }\n" "GtkWindow#mywindow .button { color: #fff }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); data = "* { color: #f00 }\n" "GtkWindow .button { color: #fff }\n" "GObject .button { color: #000 }"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color); g_assert (gdk_rgba_equal (&color, &expected)); g_object_unref (provider); g_object_unref (context); } static void test_style_property (void) { GtkStyleContext *context; GtkWidgetPath *path; GtkCssProvider *provider; GError *error; const gchar *data; gint x; GdkRGBA color; GdkRGBA expected; error = NULL; provider = gtk_css_provider_new (); context = gtk_style_context_new (); path = gtk_widget_path_new (); gtk_widget_path_append_type (path, GTK_TYPE_WINDOW); gtk_widget_path_append_type (path, GTK_TYPE_BOX); gtk_widget_path_append_type (path, GTK_TYPE_BUTTON); gtk_style_context_set_path (context, path); gtk_widget_path_free (path); gtk_style_context_set_state (context, GTK_STATE_FLAG_PRELIGHT); /* Since we set the prelight state on the context, we expect * only the third selector to match, even though the second one * has higher specificity, and the fourth one comes later. * * In particular, we want to verify that widget style properties and * CSS properties follow the same matching rules, ie we expect * color to be #003 and child-displacement-x to be 3. */ data = "GtkButton:insensitive { color: #001; -GtkButton-child-displacement-x: 1 }\n" "GtkBox GtkButton:selected { color: #002; -GtkButton-child-displacement-x: 2 }\n" "GtkButton:prelight { color: #003; -GtkButton-child-displacement-x: 3 }\n" "GtkButton:focused { color: #004; -GtkButton-child-displacement-x: 4 }\n"; gtk_css_provider_load_from_data (provider, data, -1, &error); g_assert_no_error (error); gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); gtk_style_context_invalidate (context); gtk_style_context_get_color (context, GTK_STATE_FLAG_PRELIGHT, &color); gdk_rgba_parse (&expected, "#003"); g_assert (gdk_rgba_equal (&color, &expected)); gtk_style_context_get_style (context, "child-displacement-x", &x, NULL); g_assert_cmpint (x, ==, 3); g_object_unref (provider); g_object_unref (context); } void test_basic_properties (void) { GtkStyleContext *context; GtkWidgetPath *path; GdkRGBA *color; GdkRGBA *bg_color; PangoFontDescription *font; context = gtk_style_context_new (); path = gtk_widget_path_new (); gtk_style_context_set_path (context, path); gtk_widget_path_free (path); gtk_style_context_get (context, 0, "color", &color, "background-color", &bg_color, "font", &font, NULL); g_assert (color != NULL); g_assert (bg_color != NULL); g_assert (font != NULL); gdk_rgba_free (color); gdk_rgba_free (bg_color); pango_font_description_free (font); g_object_unref (context); } int main (int argc, char *argv[]) { gtk_init (NULL, NULL); g_test_init (&argc, &argv, NULL); g_test_add_func ("/style/parse/empty", test_parse_empty); g_test_add_func ("/style/parse/at", test_parse_at); g_test_add_func ("/style/parse/selectors", test_parse_selectors); g_test_add_func ("/style/parse/declarations", test_parse_declarations); g_test_add_func ("/style/path", test_path); g_test_add_func ("/style/match", test_match); g_test_add_func ("/style/style-property", test_style_property); g_test_add_func ("/style/basic", test_basic_properties); return g_test_run (); }