midori/tests/completion.c
Christian Dywan b1ee80d55e Implement katze_utf8_stristr, katze_collfold and use in completion
Evidently normalizing any whole string is too slow for completion,
so we need to even out case and composition while iterating through
the strings.

The decompositing version of katze_utf8_stristr is disabled, since
it is too slow, and an ascii only version is used, for now.

A unit test 'compare' is added that solely measures performance of
katze_collfold and katze_utf8_stristr.
2009-12-28 23:59:54 +01:00

328 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Copyright (C) 2009 Christian Dywan <christian@twotoasts.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See the file COPYING for the full license text.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "midori.h"
/* This is a private function */
GtkWidget*
midori_location_action_entry_for_proxy (GtkWidget* proxy);
static const gchar* compare_urls[] = {
"http://en.wikipedia.org/wiki/Foul",
"http://de.wikipedia.org/wiki/Düsseldorf",
"http://de.wikipedia.org/wiki/Düsseldorf",
"http://ja.wikipedia.org/wiki/若井はんじ・けんじ",
"http://www.johannkönig.com",
"http://şøñđëřżēıċħęŋđőmæîņĭśŧşũþėŗ.de",
};
static void
completion_compare (void)
{
const guint runs = 10000;
guint t;
gdouble elapsed = 0.0;
for (t = 0; t < runs; t++)
{
g_test_timer_start ();
guint i, j;
for (i = 0; i < G_N_ELEMENTS (compare_urls); i++)
{
gchar* url = katze_collfold (compare_urls[i]);
for (j = 0; j < G_N_ELEMENTS (compare_urls); j++)
katze_utf8_stristr (compare_urls[i], url);
g_free (url);
}
elapsed += g_test_timer_elapsed ();
}
g_print ("%f seconds for comparison\n", elapsed / runs);
}
typedef struct
{
const gchar* uri;
const gchar* name;
} CompletionItem;
static const CompletionItem items[] = {
{ "http://one.com", "One" },
{ "http://one.com/", "One" }, /* Duplicate */
{ "http://one.com/", "One One" }, /* Duplicate */
{ "http://one.com", "One Two" }, /* Duplicate */
{ "http://two.com", "Two" },
{ "http://three.com", "Three" },
{ "http://one.com/one/", "One off" },
{ "http://four.org", "One" },
{ "https://four.org", "Four" },
{ "ftp://four.org/", "ごオルゴ" },
{ "http://muenchen.de/weißwürste/", "Münchner Weißwürste" }, /* Umlauts */
};
static const guint items_n = 9;
static void
completion_count (void)
{
MidoriLocationAction* action;
GtkWidget* toolitem;
GtkWidget* location_entry;
GtkWidget* entry;
GtkTreeModel* model;
guint n, i;
action = g_object_new (MIDORI_TYPE_LOCATION_ACTION, NULL);
toolitem = gtk_action_create_tool_item (GTK_ACTION (action));
location_entry = midori_location_action_entry_for_proxy (toolitem);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
model = gtk_combo_box_get_model (GTK_COMBO_BOX (location_entry));
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 0);
midori_location_action_add_item (action, "http://one.com", NULL, "One");
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 1);
midori_location_action_clear (action);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 0);
for (i = 0; i < G_N_ELEMENTS (items); i++)
midori_location_action_add_item (action, items[i].uri,
NULL, items[i].name);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, items_n);
/* Adding the exact same items again shouldn't increase the total amount */
for (i = 0; i < G_N_ELEMENTS (items); i++)
midori_location_action_add_item (action, items[i].uri,
NULL, items[i].name);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, items_n);
}
/* Components to construct test addresses, the numbers take into account
how many items minus duplicates are unique. */
static const gchar* protocols[] = {
"http", "https", "ftp"
};
static const guint protocols_n = 3;
static const gchar* subs[] = {
"", "www.", "ww2.", "ftp."
};
static const guint subs_n = 4;
static const gchar* slds[] = {
"one", "two", "four", "six", "seven"/*, "Seven", "SEVEN"*/
};
static const guint slds_n = 5;
static const gchar* tlds[] = {
"com", "org", "net", "de", "co.uk", "com.au"/*, "Com.Au", "COM.AU"*/
};
static const guint tlds_n = 6;
static const gchar* files[] = {
"/", "/index.html", "/img.png", /*"/weißwürste",*/ "/images/"
/*, "/Images", "/IMAGES/"*/
};
static const guint files_n = 4;
static const gchar* inputs[] = {
"http://www.one.com/index", "http://two.de/images", "http://six.com.au/img"/*,
"http://muenchen.de/weißwürste/"*/
};
static const gchar* additions[] = {
"http://www.one.com/invention", "http://two.de/island", "http://six.com.au/ish"
};
static void location_action_fill (MidoriLocationAction* action)
{
guint i, j, k, l, m;
for (i = 0; i < G_N_ELEMENTS (protocols); i++)
for (j = 0; j < G_N_ELEMENTS (subs); j++)
for (k = 0; k < G_N_ELEMENTS (slds); k++)
for (l = 0; l < G_N_ELEMENTS (tlds); l++)
for (m = 0; m < G_N_ELEMENTS (files); m++)
{
gchar* uri = g_strdup_printf ("%s://%s%s.%s%s",
protocols[i], subs[j], slds[k], tlds[l], files[m]);
midori_location_action_add_item (action, uri, NULL, uri);
g_free (uri);
}
}
static void
completion_fill (void)
{
MidoriLocationAction* action;
GtkWidget* toolitem;
GtkWidget* location_entry;
GtkWidget* entry;
GtkTreeModel* model;
guint i, items_added, items_added_effective, n;
gdouble elapsed;
action = g_object_new (MIDORI_TYPE_LOCATION_ACTION, NULL);
toolitem = gtk_action_create_tool_item (GTK_ACTION (action));
location_entry = midori_location_action_entry_for_proxy (toolitem);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
g_print ("...\n");
items_added = G_N_ELEMENTS (protocols) * G_N_ELEMENTS (subs)
* G_N_ELEMENTS (slds) * G_N_ELEMENTS (tlds) * G_N_ELEMENTS (files);
items_added_effective = protocols_n * subs_n * slds_n * tlds_n * files_n;
g_print ("Adding %d items, effectively %d items:\n",
items_added, items_added_effective);
/* Since adding items when the action is frozen is very fast,
we run it 5 times and take the average time. */
elapsed = 0.0;
for (i = 0; i < 5; i++)
{
midori_location_action_clear (action);
midori_location_action_freeze (action);
g_test_timer_start ();
location_action_fill (action);
elapsed += g_test_timer_elapsed ();
midori_location_action_thaw (action);
}
model = gtk_combo_box_get_model (GTK_COMBO_BOX (location_entry));
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, items_added_effective);
g_print ("%f seconds, the action is frozen\n", elapsed / 5.0);
midori_location_action_clear (action);
g_test_timer_start ();
location_action_fill (action);
elapsed = g_test_timer_elapsed ();
model = gtk_combo_box_get_model (GTK_COMBO_BOX (location_entry));
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, items_added_effective);
g_print ("%f seconds, the action updates normally\n", elapsed);
/* We don't clear the action intentionally in order to see
how long adding only duplicates takes. */
g_test_timer_start ();
location_action_fill (action);
elapsed = g_test_timer_elapsed ();
model = gtk_combo_box_get_model (GTK_COMBO_BOX (location_entry));
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, items_added_effective);
g_print ("%f seconds, adding exact duplicates\n", elapsed);
g_print ("...");
}
static guint matches = 0;
static guint matches_expected = 0;
static gboolean
entry_completion_insert_prefix_cb (GtkEntryCompletion* completion,
const gchar* prefix,
MidoriLocationAction* action)
{
GtkWidget* entry = gtk_entry_completion_get_entry (completion);
const gchar* text = gtk_entry_get_text (GTK_ENTRY (entry));
if (!g_strrstr (prefix, text))
g_print ("Match failed, input: %s, result: %s\n",
text, prefix);
g_assert (g_strrstr (prefix, text));
midori_location_action_delete_item_from_uri (action, text);
midori_location_action_add_uri (action, text);
matches++;
return FALSE;
}
static void
completion_match (void)
{
MidoriLocationAction* action;
GtkWidget* toolitem;
GtkWidget* location_entry;
GtkWidget* entry;
GtkEntryCompletion* completion;
guint i;
action = g_object_new (MIDORI_TYPE_LOCATION_ACTION, NULL);
midori_location_action_freeze (action);
location_action_fill (action);
midori_location_action_thaw (action);
toolitem = gtk_action_create_tool_item (GTK_ACTION (action));
location_entry = midori_location_action_entry_for_proxy (toolitem);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
completion = gtk_entry_get_completion (GTK_ENTRY (entry));
g_signal_connect (completion, "insert-prefix",
G_CALLBACK (entry_completion_insert_prefix_cb), action);
gtk_entry_completion_set_inline_completion (completion, TRUE);
gtk_entry_completion_set_popup_single_match (completion, FALSE);
for (i = 0; i < G_N_ELEMENTS (inputs); i++)
{
matches_expected++;
gtk_entry_set_text (GTK_ENTRY (entry), inputs[i]);
gtk_entry_completion_complete (completion);
gtk_entry_completion_insert_prefix (completion);
if (matches != matches_expected)
g_print ("Match failed, input: %s, result: %s\n",
inputs[i], gtk_entry_get_text (GTK_ENTRY (entry)));
g_assert_cmpint (matches, ==, matches_expected);
}
for (i = 0; i < G_N_ELEMENTS (additions); i++)
{
midori_location_action_add_uri (action, additions[i]);
midori_location_action_delete_item_from_uri (action, additions[i]);
midori_location_action_add_uri (action, additions[i]);
}
for (i = 0; i < G_N_ELEMENTS (inputs); i++)
{
matches_expected++;
gtk_entry_set_text (GTK_ENTRY (entry), inputs[i]);
gtk_entry_completion_complete (completion);
gtk_entry_completion_insert_prefix (completion);
if (matches != matches_expected)
g_print ("Match failed, input: %s, result: %s\n",
inputs[i], gtk_entry_get_text (GTK_ENTRY (entry)));
g_assert_cmpint (matches, ==, matches_expected);
}
for (i = 0; i < G_N_ELEMENTS (additions); i++)
{
matches_expected++;
gtk_entry_set_text (GTK_ENTRY (entry), additions[i]);
gtk_entry_completion_complete (completion);
gtk_entry_completion_insert_prefix (completion);
if (matches != matches_expected)
g_print ("Match failed, input: %s, result: %s\n",
additions[i], gtk_entry_get_text (GTK_ENTRY (entry)));
g_assert_cmpint (matches, ==, matches_expected);
}
}
int
main (int argc,
char** argv)
{
/* libSoup uses threads, so we need to initialize threads. */
if (!g_thread_supported ()) g_thread_init (NULL);
g_test_init (&argc, &argv, NULL);
gtk_init_check (&argc, &argv);
g_test_add_func ("/completion/compare", completion_compare);
g_test_add_func ("/completion/count", completion_count);
g_test_add_func ("/completion/fill", completion_fill);
g_test_add_func ("/completion/match", completion_match);
return g_test_run ();
}