From 7d95bc4dc6842bf2eefa0f41ff8af5baae6f4ec9 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sun, 19 Jul 2020 01:02:38 +0200 Subject: [PATCH] testsuite: Support different models In particular, add a GtkTreeModel of GtkDirectoryList that enumerates the full tree of everything below G_TEST_SRCDIR. You can set it to ~ or / to get lots of files. Use this to test numeric sorting, string sorting, the treelistrow sorter and the multisorter. And while doing all of that, try to use something that is still realistic. --- testsuite/gtk/sort-performance.c | 384 ++++++++++++++++++++++++------- 1 file changed, 302 insertions(+), 82 deletions(-) diff --git a/testsuite/gtk/sort-performance.c b/testsuite/gtk/sort-performance.c index aadbf6ca3c..3bd48c5342 100644 --- a/testsuite/gtk/sort-performance.c +++ b/testsuite/gtk/sort-performance.c @@ -15,6 +15,16 @@ quick_random (guint prev) static guint comparisons = 0; +static int +count_comparisons (gconstpointer a, + gconstpointer b, + gpointer unused) +{ + comparisons++; + + return GTK_ORDERING_EQUAL; +} + static int compare_string_object (gconstpointer a, gconstpointer b, @@ -29,12 +39,6 @@ compare_string_object (gconstpointer a, gtk_string_object_get_string (sb))); } -static GtkSorter * -create_sorter (void) -{ - return gtk_custom_sorter_new (compare_string_object, NULL, NULL); -} - static void count_changed_cb (GListModel *model, guint position, @@ -80,22 +84,20 @@ set_model (const char *testname, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { gint64 start, end, max, total; GtkSliceListModel *slice; - GtkSorter *sorter; GListModel *sort; guint n_changed, size = 1000; slice = gtk_slice_list_model_new (source, 0, size); - sorter = create_sorter (); sort = g_object_new (type, "sorter", sorter, incremental ? "incremental" : NULL, TRUE, NULL); g_signal_connect (sort, "items-changed", G_CALLBACK (count_changed_cb), &n_changed); - g_object_unref (sorter); while (TRUE) { @@ -118,12 +120,11 @@ set_model (const char *testname, print_result (testname, type, incremental, size, total, max, comparisons, n_changed); - if (total > MAX_TIME) + if (total > MAX_TIME || + size >= g_list_model_get_n_items (source)) break; size *= 2; - if (4 * total > 2 * MAX_TIME) - break; g_object_set (sort, "model", NULL, NULL); gtk_slice_list_model_set_size (slice, size); @@ -138,24 +139,22 @@ append (const char *testname, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random, guint fraction) { gint64 start, end, max, total; GtkSliceListModel *slice; - GtkSorter *sorter; GListModel *sort; guint n_changed, size = 1000; slice = gtk_slice_list_model_new (source, 0, (fraction - 1) * size / fraction); - sorter = create_sorter (); sort = g_object_new (type, "model", slice, "sorter", sorter, incremental ? "incremental" : NULL, TRUE, NULL); g_signal_connect (sort, "items-changed", G_CALLBACK (count_changed_cb), &n_changed); - g_object_unref (sorter); while (TRUE) { @@ -179,13 +178,11 @@ append (const char *testname, print_result (testname, type, incremental, size, total, max, comparisons, n_changed); - if (total > MAX_TIME) + if (total > MAX_TIME || + size >= g_list_model_get_n_items (source)) break; size *= 2; - if (4 * total > 2 * MAX_TIME || - size > MAX_SIZE) - break; } g_object_unref (sort); @@ -197,9 +194,10 @@ append_half (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - append (name, type, incremental, source, random, 2); + append (name, type, incremental, source, sorter, random, 2); } static void @@ -207,9 +205,10 @@ append_10th (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - append (name, type, incremental, source, random, 10); + append (name, type, incremental, source, sorter, random, 10); } static void @@ -217,9 +216,10 @@ append_100th (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - append (name, type, incremental, source, random, 100); + append (name, type, incremental, source, sorter, random, 100); } static void @@ -227,24 +227,22 @@ remove_test (const char *testname, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random, guint fraction) { gint64 start, end, max, total; GtkSliceListModel *slice; - GtkSorter *sorter; GListModel *sort; guint n_changed, size = 1000; slice = gtk_slice_list_model_new (source, 0, size); - sorter = create_sorter (); sort = g_object_new (type, "model", slice, "sorter", sorter, incremental ? "incremental" : NULL, TRUE, NULL); g_signal_connect (sort, "items-changed", G_CALLBACK (count_changed_cb), &n_changed); - g_object_unref (sorter); while (TRUE) { @@ -268,13 +266,11 @@ remove_test (const char *testname, print_result (testname, type, incremental, size, total, max, comparisons, n_changed); - if (total > MAX_TIME) + if (total > MAX_TIME || + size >= g_list_model_get_n_items (source)) break; size *= 2; - if (4 * total > 2 * MAX_TIME || - size > MAX_SIZE) - break; } g_object_unref (sort); @@ -286,9 +282,10 @@ remove_half (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - remove_test (name, type, incremental, source, random, 2); + remove_test (name, type, incremental, source, sorter, random, 2); } static void @@ -296,9 +293,10 @@ remove_10th (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - remove_test (name, type, incremental, source, random, 10); + remove_test (name, type, incremental, source, sorter, random, 10); } static void @@ -306,9 +304,10 @@ remove_100th (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - remove_test (name, type, incremental, source, random, 100); + remove_test (name, type, incremental, source, sorter, random, 100); } static void @@ -316,24 +315,22 @@ append_n (const char *testname, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random, guint n) { gint64 start, end, max, total; GtkSliceListModel *slice; - GtkSorter *sorter; GListModel *sort; guint i, n_changed, size = 1000; slice = gtk_slice_list_model_new (source, 0, size); - sorter = create_sorter (); sort = g_object_new (type, "model", slice, "sorter", sorter, incremental ? "incremental" : NULL, TRUE, NULL); g_signal_connect (sort, "items-changed", G_CALLBACK (count_changed_cb), &n_changed); - g_object_unref (sorter); while (TRUE) { @@ -360,13 +357,11 @@ append_n (const char *testname, print_result (testname, type, incremental, size, total, max, comparisons, n_changed); - if (total > MAX_TIME) + if (total > MAX_TIME || + size >= g_list_model_get_n_items (source)) break; size *= 2; - if (4 * total > 2 * MAX_TIME || - size > MAX_SIZE) - break; } g_object_unref (sort); @@ -378,9 +373,10 @@ append_1 (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - append_n (name, type, incremental, source, random, 1); + append_n (name, type, incremental, source, sorter, random, 1); } static void @@ -388,9 +384,10 @@ append_2 (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - append_n (name, type, incremental, source, random, 2); + append_n (name, type, incremental, source, sorter, random, 2); } static void @@ -398,9 +395,10 @@ append_10 (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - append_n (name, type, incremental, source, random, 10); + append_n (name, type, incremental, source, sorter, random, 10); } static void @@ -408,24 +406,22 @@ remove_n (const char *testname, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random, guint n) { gint64 start, end, max, total; GtkSliceListModel *slice; - GtkSorter *sorter; GListModel *sort; guint i, n_changed, size = 1000; slice = gtk_slice_list_model_new (source, 0, size); - sorter = create_sorter (); sort = g_object_new (type, "model", slice, "sorter", sorter, incremental ? "incremental" : NULL, TRUE, NULL); g_signal_connect (sort, "items-changed", G_CALLBACK (count_changed_cb), &n_changed); - g_object_unref (sorter); while (TRUE) { @@ -452,13 +448,11 @@ remove_n (const char *testname, print_result (testname, type, incremental, size, total, max, comparisons, n_changed); - if (total > MAX_TIME) + if (total > MAX_TIME || + size >= g_list_model_get_n_items (source)) break; size *= 2; - if (4 * total > 2 * MAX_TIME || - size > MAX_SIZE) - break; } g_object_unref (sort); @@ -470,9 +464,10 @@ remove_1 (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - remove_n (name, type, incremental, source, random, 1); + remove_n (name, type, incremental, source, sorter, random, 1); } static void @@ -480,26 +475,158 @@ remove_2 (const char *name, GType type, gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - remove_n (name, type, incremental, source, random, 2); + remove_n (name, type, incremental, source, sorter, random, 2); } static void remove_10 (const char *name, GType type, - gboolean incremental, + gboolean incremental, GListModel *source, + GtkSorter *sorter, guint random) { - remove_n (name, type, incremental, source, random, 10); + remove_n (name, type, incremental, source, sorter, random, 10); } static void -run_test (GtkStringList *source, +done_loading_directory (GtkDirectoryList *dir, + GParamSpec *pspec, + gpointer data) +{ + /* When we get G_IO_ERROR_TOO_MANY_OPEN_FILES we enqueue directories here for reloading + * as more file descriptors get available + */ + static GSList *too_many = NULL; + + const GError *error; + guint *counters = data; + + /* happens when restarting the load below */ + if (gtk_directory_list_is_loading (dir)) + return; + + error = gtk_directory_list_get_error (dir); + if (error) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TOO_MANY_OPEN_FILES)) + { + too_many = g_slist_prepend (too_many, g_object_ref (dir)); + return; + } + } + counters[1]++; + if (too_many) + { + GtkDirectoryList *reload = too_many->data; + GFile *file; + + too_many = g_slist_remove (too_many, reload); + file = g_object_ref (gtk_directory_list_get_file (reload)); + gtk_directory_list_set_file (reload, NULL); + gtk_directory_list_set_file (reload, file); + g_object_unref (file); + } +} + +static gboolean +file_info_is_directory (GFileInfo *file_info) +{ + if (g_file_info_get_is_symlink (file_info)) + return FALSE; + + return g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY; +} + +static GListModel * +create_directory_list (gpointer item, + gpointer data) +{ + GFileInfo *file_info = G_FILE_INFO (item); + guint *counters = data; + GtkDirectoryList *dir; + GFile *file; + + if (!file_info_is_directory (file_info)) + return NULL; + file = G_FILE (g_file_info_get_attribute_object (file_info, "standard::file")); + if (file == NULL) + return NULL; + + dir = gtk_directory_list_new (G_FILE_ATTRIBUTE_STANDARD_TYPE + "," G_FILE_ATTRIBUTE_STANDARD_NAME + "," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME + "," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK, + NULL); + gtk_directory_list_set_io_priority (dir, G_PRIORITY_DEFAULT + g_random_int_range (-5, 5)); + gtk_directory_list_set_monitored (dir, FALSE); + gtk_directory_list_set_file (dir, file); + counters[0]++; + g_signal_connect (dir, "notify::loading", G_CALLBACK (done_loading_directory), counters); + g_assert (gtk_directory_list_is_loading (dir)); + + return G_LIST_MODEL (dir); +} + +static GListModel * +get_file_infos (void) +{ + static GtkTreeListModel *tree = NULL; + gint64 start, end, max; + GtkDirectoryList *dir; + GFile *root; + guint counters[2] = { 1, 0 }; + + if (tree) + return G_LIST_MODEL (g_object_ref (tree)); + + if (g_getenv ("G_TEST_SRCDIR")) + root = g_file_new_for_path (g_getenv ("G_TEST_SRCDIR")); + else + root = g_file_new_for_path (g_get_home_dir ()); + + start = end = g_get_monotonic_time (); + max = 0; + dir = gtk_directory_list_new (G_FILE_ATTRIBUTE_STANDARD_TYPE + "," G_FILE_ATTRIBUTE_STANDARD_NAME + "," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME + "," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK, + NULL); + gtk_directory_list_set_monitored (dir, FALSE); + gtk_directory_list_set_file (dir, root); + tree = gtk_tree_list_model_new (FALSE, + G_LIST_MODEL (dir), + TRUE, + create_directory_list, + counters, NULL); + g_signal_connect (dir, "notify::loading", G_CALLBACK (done_loading_directory), counters); + end = snapshot_time (end, &max); + while (counters[0] != counters[1]) + { + g_main_context_iteration (NULL, TRUE); + end = snapshot_time (end, &max); + } + //g_print ("%u/%u\n", counters[0], counters[1]); + + end = snapshot_time (end, &max); + + print_result ("load-directory", GTK_TYPE_DIRECTORY_LIST, FALSE, g_list_model_get_n_items (G_LIST_MODEL (tree)), end - start, max, 0, counters[0]); + + g_object_unref (dir); + g_object_unref (root); + + return G_LIST_MODEL (g_object_ref (tree)); +} + +static void +run_test (GListModel *source, + GtkSorter *sorter, const char * const *tests, const char *test_name, - void (* test_func) (const char *name, GType type, gboolean incremental, GListModel *source, guint random)) + void (* test_func) (const char *name, GType type, gboolean incremental, GListModel *source, GtkSorter *sorter, guint random)) { struct { GType type; @@ -524,51 +651,144 @@ run_test (GtkStringList *source, guint random = g_random_int (); guint i; - if (tests != NULL && !g_strv_contains (tests, test_name)) - return; - for (i = 0; i < G_N_ELEMENTS (types); i++) { - test_func (test_name, types[i].type, types[i].incremental, G_LIST_MODEL (source), random); + test_func (test_name, types[i].type, types[i].incremental, source, sorter, random); + } +} + +static GListModel * +get_string_list (void) +{ + static GtkStringList *string_list = NULL; + + if (string_list == NULL) + { + guint i, random; + random = g_test_rand_int (); + string_list = gtk_string_list_new (NULL); + for (i = 0; i < MAX_SIZE; i++) + { + gtk_string_list_take (string_list, g_strdup_printf ("%u", random)); + random = quick_random (random); + } + } + + return G_LIST_MODEL (g_object_ref (string_list)); +} + +static void +run_tests (const char * const *tests, + const char *test_name, + void (* test_func) (const char *name, GType type, gboolean incremental, GListModel *source, GtkSorter *sorter, guint random)) +{ + const char *suffixes[] = { "string", "tree", "filename" }; + gsize i; + + for (i = 0; i < G_N_ELEMENTS(suffixes); i++) + { + GListModel *source; + GtkSorter *sorter; + char *name; + + name = g_strdup_printf ("%s-%s", test_name, suffixes[i]); + if (tests != NULL && !g_strv_contains (tests, name)) + { + g_free (name); + continue; + } + + switch (i) + { + case 0: + source = get_string_list (); + sorter = gtk_custom_sorter_new (compare_string_object, NULL, NULL); + break; + + case 1: + source = get_file_infos (); + sorter = gtk_multi_sorter_new (); + gtk_multi_sorter_append (GTK_MULTI_SORTER (sorter), gtk_custom_sorter_new (count_comparisons, NULL, NULL)); + gtk_multi_sorter_append (GTK_MULTI_SORTER (sorter), + gtk_numeric_sorter_new (gtk_cclosure_expression_new (G_TYPE_BOOLEAN, + NULL, + 0, + NULL, + (GCallback) file_info_is_directory, + NULL, NULL))); + gtk_multi_sorter_append (GTK_MULTI_SORTER (sorter), + gtk_numeric_sorter_new (gtk_cclosure_expression_new (G_TYPE_UINT64, + NULL, + 1, + (GtkExpression *[1]) { + gtk_constant_expression_new (G_TYPE_STRING, G_FILE_ATTRIBUTE_STANDARD_SIZE) + }, + (GCallback) g_file_info_get_attribute_uint64, + NULL, NULL))); + sorter = gtk_tree_list_row_sorter_new (sorter); + break; + + case 2: + { + GListModel *infos = get_file_infos (); + source = G_LIST_MODEL (gtk_map_list_model_new (infos, + (GtkMapListModelMapFunc) gtk_tree_list_row_get_item, + NULL, NULL)); + sorter = gtk_string_sorter_new ( + gtk_cclosure_expression_new (G_TYPE_STRING, + NULL, + 1, + (GtkExpression *[1]) { + gtk_constant_expression_new (G_TYPE_STRING, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME) + }, + (GCallback) g_file_info_get_attribute_as_string, + NULL, + NULL)); + g_object_unref (infos); + } + break; + + default: + g_assert_not_reached (); + return; + } + + run_test (source, sorter, tests, name, test_func); + + g_free (name); + g_object_unref (sorter); + g_object_unref (source); } } int main (int argc, char *argv[]) { - GtkStringList *source; - guint random = g_random_int (); - guint i; const char * const *tests; gtk_test_init (&argc, &argv); - source = gtk_string_list_new (NULL); - for (i = 0; i < MAX_SIZE; i++) - { - gtk_string_list_take (source, g_strdup_printf ("%u", random)); - random = quick_random (random); - } - if (argc < 2) tests = NULL; else tests = (const char **) argv + 1; g_print ("# \"test\",\"model\",\"model size\",\"time\",\"max time\",\"comparisons\",\"changes\"\n"); - run_test (source, tests, "set-model", set_model); - run_test (source, tests, "append-half", append_half); - run_test (source, tests, "append-10th", append_10th); - run_test (source, tests, "append-100th", append_100th); - run_test (source, tests, "remove-half", remove_half); - run_test (source, tests, "remove-10th", remove_10th); - run_test (source, tests, "remove-100th", remove_100th); - run_test (source, tests, "append-1", append_1); - run_test (source, tests, "append-2", append_2); - run_test (source, tests, "append-10", append_10); - run_test (source, tests, "remove-1", remove_1); - run_test (source, tests, "remove-2", remove_2); - run_test (source, tests, "remove-10", remove_10); + run_tests (tests, "set-model", set_model); + run_tests (tests, "append-half", append_half); + run_tests (tests, "append-10th", append_10th); + run_tests (tests, "append-100th", append_100th); + run_tests (tests, "remove-half", remove_half); + run_tests (tests, "remove-10th", remove_10th); + run_tests (tests, "remove-100th", remove_100th); + run_tests (tests, "append-1", append_1); + run_tests (tests, "append-2", append_2); + run_tests (tests, "append-10", append_10); + run_tests (tests, "remove-1", remove_1); + run_tests (tests, "remove-2", remove_2); + run_tests (tests, "remove-10", remove_10); + run_tests (tests, "remove-10", remove_10); + return g_test_run (); }