mirror of
https://git.jabber.space/devs/cproof.git
synced 2026-07-20 09:56:22 +00:00
Autocompletion failed for nicknames using non Latin scripts. We used `g_str_to_ascii`, which replaces characters it cannot transliterate with '?', leading search failures and false matches between different scripts. Now we do: Use `g_utf8_casefold` for case-insensitive UTF-8 comparison. This ensures that like 'Σ' correctly match 'σ' across all Unicode scripts, providing correct results for non English nicknames. If we don't fine anything typing a base ASCII character matches an accented one (typing `e` matches `è`). This pass uses `g_str_to_ascii` followed by `g_ascii_strdown` for comparison. It is now restricted to run if the search string itself is valid ASCII, preventing the "everything matches '?'" bug in non Latin scripts. Autocomplete items are sorted using `strcmp`. `g_utf8_collate` provides linguistical ordering for a specific language. But its behavior is locale dependent and undefined when comparing strings from different scripts. `strcmp` does byte-wise ordering that correctly follows Unicode code order for UTF-8 strings.
348 lines
8.2 KiB
C
348 lines
8.2 KiB
C
#include <glib.h>
|
|
#include "prof_cmocka.h"
|
|
#include <stdlib.h>
|
|
|
|
#include "xmpp/contact.h"
|
|
#include "tools/autocomplete.h"
|
|
|
|
void
|
|
autocomplete_complete__returns__null_when_empty(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
char* result = autocomplete_complete(ac, "test", TRUE, FALSE);
|
|
assert_null(result);
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_reset__updates__after_create(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_reset(ac);
|
|
autocomplete_free(ac);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__null_after_create(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
char* result = autocomplete_complete(ac, "hello", TRUE, FALSE);
|
|
assert_null(result);
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_create_list__returns__null_after_create(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
GList* result = autocomplete_create_list(ac);
|
|
|
|
assert_null(result);
|
|
|
|
autocomplete_free(ac);
|
|
g_list_free_full(result, free);
|
|
}
|
|
|
|
void
|
|
autocomplete_add__updates__one_and_complete(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "Hello");
|
|
char* result = autocomplete_complete(ac, "Hel", TRUE, FALSE);
|
|
|
|
assert_string_equal("Hello", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__first_of_two(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "Hello");
|
|
autocomplete_add(ac, "Help");
|
|
char* result = autocomplete_complete(ac, "Hel", TRUE, FALSE);
|
|
|
|
assert_string_equal("Hello", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__second_of_two(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "Hello");
|
|
autocomplete_add(ac, "Help");
|
|
char* result1 = autocomplete_complete(ac, "Hel", TRUE, FALSE);
|
|
char* result2 = autocomplete_complete(ac, result1, TRUE, FALSE);
|
|
|
|
assert_string_equal("Help", result2);
|
|
|
|
autocomplete_free(ac);
|
|
free(result1);
|
|
free(result2);
|
|
}
|
|
|
|
void
|
|
autocomplete_add__updates__two_elements(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "Hello");
|
|
autocomplete_add(ac, "Help");
|
|
GList* result = autocomplete_create_list(ac);
|
|
|
|
assert_int_equal(2, g_list_length(result));
|
|
|
|
autocomplete_free(ac);
|
|
g_list_free_full(result, free);
|
|
}
|
|
|
|
void
|
|
autocomplete_add__updates__only_once_for_same_value(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "Hello");
|
|
autocomplete_add(ac, "Hello");
|
|
GList* result = autocomplete_create_list(ac);
|
|
|
|
assert_int_equal(1, g_list_length(result));
|
|
|
|
autocomplete_free(ac);
|
|
g_list_free_full(result, free);
|
|
}
|
|
|
|
void
|
|
autocomplete_add__updates__existing_value(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "Hello");
|
|
autocomplete_add(ac, "Hello");
|
|
GList* result = autocomplete_create_list(ac);
|
|
|
|
GList* first = g_list_nth(result, 0);
|
|
|
|
char* str = first->data;
|
|
|
|
assert_string_equal("Hello", str);
|
|
|
|
autocomplete_free(ac);
|
|
g_list_free_full(result, free);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__accented_with_accented(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "èâîô");
|
|
|
|
char* result = autocomplete_complete(ac, "èâ", TRUE, FALSE);
|
|
|
|
assert_string_equal("èâîô", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__accented_with_base(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "èâîô");
|
|
|
|
char* result = autocomplete_complete(ac, "ea", TRUE, FALSE);
|
|
|
|
assert_string_equal("èâîô", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__both_with_accented(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "eaooooo");
|
|
autocomplete_add(ac, "èâîô");
|
|
|
|
char* result1 = autocomplete_complete(ac, "èâ", TRUE, FALSE);
|
|
char* result2 = autocomplete_complete(ac, result1, TRUE, FALSE);
|
|
|
|
assert_string_equal("èâîô", result2);
|
|
|
|
autocomplete_free(ac);
|
|
free(result1);
|
|
free(result2);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__both_with_base(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "eaooooo");
|
|
autocomplete_add(ac, "èâîô");
|
|
|
|
char* result1 = autocomplete_complete(ac, "ea", TRUE, FALSE);
|
|
char* result2 = autocomplete_complete(ac, result1, TRUE, FALSE);
|
|
|
|
assert_string_equal("èâîô", result2);
|
|
|
|
autocomplete_free(ac);
|
|
|
|
free(result1);
|
|
free(result2);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__is__case_insensitive(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "MyBuddy");
|
|
|
|
char* result = autocomplete_complete(ac, "myb", TRUE, FALSE);
|
|
|
|
assert_string_equal("MyBuddy", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__previous(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "MyBuddy1");
|
|
autocomplete_add(ac, "MyBuddy2");
|
|
autocomplete_add(ac, "MyBuddy3");
|
|
|
|
char* result1 = autocomplete_complete(ac, "myb", TRUE, FALSE);
|
|
char* result2 = autocomplete_complete(ac, result1, TRUE, FALSE);
|
|
char* result3 = autocomplete_complete(ac, result2, TRUE, FALSE);
|
|
char* result4 = autocomplete_complete(ac, result3, TRUE, TRUE);
|
|
|
|
assert_string_equal("MyBuddy2", result4);
|
|
|
|
autocomplete_free(ac);
|
|
|
|
free(result1);
|
|
free(result2);
|
|
free(result3);
|
|
free(result4);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__greek_false_match(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
// Σωκράτης (Socrates) and Πλάτων (Plato)
|
|
autocomplete_add(ac, "Σωκράτης");
|
|
autocomplete_add(ac, "Πλάτων");
|
|
|
|
char* result = autocomplete_complete(ac, "Π", TRUE, FALSE);
|
|
|
|
assert_string_equal("Πλάτων", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__greek(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "Αριστοτέλης");
|
|
|
|
char* result = autocomplete_complete(ac, "Αριστ", TRUE, FALSE);
|
|
|
|
assert_non_null(result);
|
|
assert_string_equal("Αριστοτέλης", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__greek_case_insensitive(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "Σωκράτης");
|
|
|
|
// Case insensitive search for Socrates
|
|
// σω is the lowercase of Σω
|
|
char* result = autocomplete_complete(ac, "σω", TRUE, FALSE);
|
|
|
|
assert_non_null(result);
|
|
assert_string_equal("Σωκράτης", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__russian(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "Достоевский");
|
|
autocomplete_add(ac, "Толстой");
|
|
autocomplete_add(ac, "Пушкин");
|
|
|
|
char* result = autocomplete_complete(ac, "дост", TRUE, FALSE);
|
|
|
|
assert_non_null(result);
|
|
assert_string_equal("Достоевский", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__chinese(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "孙子");
|
|
autocomplete_add(ac, "诸葛亮");
|
|
|
|
char* result = autocomplete_complete(ac, "孙", TRUE, FALSE);
|
|
|
|
assert_non_null(result);
|
|
assert_string_equal("孙子", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__transliterated(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "München");
|
|
|
|
// Match 'ü' with 'u'
|
|
char* result = autocomplete_complete(ac, "mun", TRUE, FALSE);
|
|
|
|
assert_non_null(result);
|
|
assert_string_equal("München", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|
|
|
|
void
|
|
autocomplete_complete__returns__regular_ascii(void** state)
|
|
{
|
|
Autocomplete ac = autocomplete_new();
|
|
autocomplete_add(ac, "London");
|
|
|
|
char* result = autocomplete_complete(ac, "lon", TRUE, FALSE);
|
|
|
|
assert_non_null(result);
|
|
assert_string_equal("London", result);
|
|
|
|
autocomplete_free(ac);
|
|
free(result);
|
|
}
|