1 /* Copyright (c) 2007 Eric Scott Albright
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to deal
5  * in the Software without restriction, including without limitation the rights
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  * copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19  * THE SOFTWARE.
20  */
21 
22 #include <UnitTest++/UnitTest++.h>
23 #include <enchant.h>
24 #include <vector>
25 #include <algorithm>
26 
27 #include "EnchantDictionaryTestFixture.h"
28 
29 static bool dictSuggestCalled;
30 std::string suggestWord;
31 
32 static enum SuggestBehavior{
33     returnNull,
34     returnZero,
35     returnFour,
36     returnFourOneInvalidUtf8,
37     returnFianceNfc
38 } suggestBehavior;
39 
40 struct EnchantDictionarySuggestTestFixtureBase : EnchantDictionaryTestFixture
41 {
42     //Setup
EnchantDictionarySuggestTestFixtureBaseEnchantDictionarySuggestTestFixtureBase43     EnchantDictionarySuggestTestFixtureBase(ConfigureHook userConfiguration):
44             EnchantDictionaryTestFixture(userConfiguration)
45     {
46         dictSuggestCalled = false;
47         _suggestions = NULL;
48         _pwl_suggestions = NULL;
49         suggestWord = std::string();
50         suggestBehavior = returnFour;
51     }
52     //Teardown
~EnchantDictionarySuggestTestFixtureBaseEnchantDictionarySuggestTestFixtureBase53     ~EnchantDictionarySuggestTestFixtureBase()
54     {
55         FreeStringList(_suggestions);
56         FreePwlStringList(_pwl_suggestions);
57     }
58 
59     char** _suggestions;
60     char** _pwl_suggestions;
61 };
62 
63 static char **
MyMockDictionarySuggest(EnchantDict * dict,const char * const word,size_t len,size_t * out_n_suggs)64 MyMockDictionarySuggest (EnchantDict * dict, const char *const word, size_t len, size_t * out_n_suggs)
65 {
66     dictSuggestCalled = true;
67     suggestWord = std::string(word,len);
68     *out_n_suggs = 0;
69     char **sugg_arr = NULL;
70 
71     switch(suggestBehavior)
72     {
73         case returnNull:
74             sugg_arr = NULL;
75             break;
76         case returnZero:
77             sugg_arr = g_new0 (char *, *out_n_suggs + 1);
78             break;
79         case returnFianceNfc:
80             *out_n_suggs = 1;
81             sugg_arr = g_new0 (char *, *out_n_suggs + 1);
82             sugg_arr[0] = g_strdup ("fianc\xc3\xa9");  // c3 a9 = utf8 for u00e9 = Latin small letter e with acute
83             break;
84         case returnFour:
85             sugg_arr = MockDictionarySuggest(dict, word, len, out_n_suggs);
86             break;
87         case returnFourOneInvalidUtf8:
88             sugg_arr = MockDictionarySuggest(dict, word, len, out_n_suggs);
89             g_free(sugg_arr[0]);
90             sugg_arr[0] = g_strdup ("\xa5\xf1\x08");
91             break;
92     }
93 
94     return sugg_arr;
95 }
96 
MockProviderRequestSuggestMockDictionary(EnchantProvider * me,const char * tag)97 static EnchantDict* MockProviderRequestSuggestMockDictionary(EnchantProvider * me, const char *tag)
98 {
99 
100     EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag);
101     dict->suggest = MyMockDictionarySuggest;
102     return dict;
103 }
104 
DictionarySuggest_ProviderConfiguration(EnchantProvider * me,const char *)105 static void DictionarySuggest_ProviderConfiguration (EnchantProvider * me, const char *)
106 {
107      me->request_dict = MockProviderRequestSuggestMockDictionary;
108      me->dispose_dict = MockProviderDisposeDictionary;
109 }
110 
111 
112 
113 
114 struct EnchantDictionarySuggest_TestFixture : EnchantDictionarySuggestTestFixtureBase
115 {
116     //Setup
EnchantDictionarySuggest_TestFixtureEnchantDictionarySuggest_TestFixture117     EnchantDictionarySuggest_TestFixture():
118             EnchantDictionarySuggestTestFixtureBase(DictionarySuggest_ProviderConfiguration)
119     { }
120 };
121 
122 
123 
124 
MockProviderRequestNoSuggestMockDictionary(EnchantProvider * me,const char * tag)125 static EnchantDict* MockProviderRequestNoSuggestMockDictionary(EnchantProvider * me, const char *tag)
126 {
127 
128     EnchantDict* dict = MockProviderRequestEmptyMockDictionary(me, tag);
129     dict->suggest = NULL;
130     return dict;
131 }
132 
DictionaryNoSuggest_ProviderConfiguration(EnchantProvider * me,const char *)133 static void DictionaryNoSuggest_ProviderConfiguration (EnchantProvider * me, const char *)
134 {
135      me->request_dict = MockProviderRequestNoSuggestMockDictionary;
136      me->dispose_dict = MockProviderDisposeDictionary;
137 }
138 
139 struct EnchantDictionarySuggestNotImplemented_TestFixture : EnchantDictionarySuggestTestFixtureBase
140 {
141     //Setup
EnchantDictionarySuggestNotImplemented_TestFixtureEnchantDictionarySuggestNotImplemented_TestFixture142     EnchantDictionarySuggestNotImplemented_TestFixture():
143             EnchantDictionarySuggestTestFixtureBase(DictionaryNoSuggest_ProviderConfiguration)
144     { }
145 };
146 
147 
148 /**
149  * enchant_dict_suggest
150  * @dict: A non-null #EnchantDict
151  * @word: The non-null word you wish to find suggestions for, in UTF-8 encoding
152  * @len: The byte length of @word, or -1 for strlen (@word)
153  * @out_n_suggs: The location to store the # of suggestions returned, or %null
154  *
155  * Will return an %null value if any of those pre-conditions
156  * are not met.
157  *
158  * Returns: A %null terminated list of UTF-8 encoded suggestions, or %null
159  */
160 /////////////////////////////////////////////////////////////////////////////
161 // Test Normal Operation
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_LenComputed)162 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
163              EnchantDictionarySuggest_LenComputed)
164 {
165     size_t cSuggestions;
166     _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions);
167     CHECK(_suggestions);
168     CHECK_EQUAL(std::string("helo"), suggestWord);
169     CHECK_EQUAL(4, cSuggestions);
170 
171     std::vector<std::string> suggestions;
172     if(_suggestions != NULL){
173         suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions);
174     }
175 
176     CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo"), suggestions, std::min((size_t)4,cSuggestions));
177 }
178 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_LenSpecified)179 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
180              EnchantDictionarySuggest_LenSpecified)
181 {
182     size_t cSuggestions;
183     _suggestions = enchant_dict_suggest(_dict, "helodisregard me", 4, &cSuggestions);
184     CHECK(_suggestions);
185     CHECK_EQUAL(std::string("helo"), suggestWord);
186     CHECK_EQUAL(4, cSuggestions);
187 
188     std::vector<std::string> suggestions;
189     if(_suggestions != NULL){
190         suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions);
191     }
192 
193     CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo"), suggestions, std::min((size_t)4,cSuggestions));
194 }
195 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_StringListFreed)196 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
197              EnchantDictionarySuggest_StringListFreed)
198 {
199     size_t cSuggs;
200     _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggs);
201     CHECK(dictSuggestCalled);
202 }
203 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_NullOutputSuggestionCount)204 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
205              EnchantDictionarySuggest_NullOutputSuggestionCount)
206 {
207     _suggestions = enchant_dict_suggest(_dict, "helo", -1, NULL);
208     CHECK(_suggestions);
209     CHECK(dictSuggestCalled);
210 }
211 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_InBrokerPwlSession)212 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
213              EnchantDictionarySuggest_InBrokerPwlSession)
214 {
215     enchant_dict_add(_pwl, "hello", -1);
216     _pwl_suggestions = enchant_dict_suggest(_pwl, "helo", -1, NULL);
217     CHECK(_pwl_suggestions);
218     CHECK(!dictSuggestCalled);
219 }
220 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_SuggestionsFromPersonal_addedToEnd)221 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
222              EnchantDictionarySuggest_SuggestionsFromPersonal_addedToEnd)
223 {
224     size_t cSuggestions;
225     enchant_dict_add(_dict, "hello", -1);
226     _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions);
227     CHECK(_suggestions);
228     CHECK_EQUAL(5, cSuggestions);
229 
230     std::vector<std::string> suggestions;
231     if(_suggestions != NULL){
232         suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions);
233     }
234 
235     std::vector<std::string> expected = GetExpectedSuggestions("helo");
236     expected.push_back("hello");
237 
238     CHECK_ARRAY_EQUAL(expected, suggestions, std::min((size_t)5,cSuggestions));
239 }
240 
241 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_DuplicateSuggestionsFromPersonal_notIncluded)242 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
243              EnchantDictionarySuggest_DuplicateSuggestionsFromPersonal_notIncluded)
244 {
245     size_t cSuggestions;
246 
247     enchant_dict_add(_dict, "aelo", -1);
248     _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions);
249     CHECK(_suggestions);
250     CHECK_EQUAL(4, cSuggestions);
251 
252     std::vector<std::string> suggestions;
253     if(_suggestions != NULL){
254         suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions);
255     }
256 
257     CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo"), suggestions, std::min((size_t)4,cSuggestions));
258 }
259 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_SuggestionExcluded_Null)260 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
261              EnchantDictionarySuggest_SuggestionExcluded_Null)
262 {
263     suggestBehavior = returnFianceNfc;
264     RemoveWordFromDictionary(Convert(L"fianc\xe9"));  // u00e9 = Latin small letter e with acute
265 
266     _suggestions = enchant_dict_suggest(_dict, "fiance", -1, NULL);
267     CHECK(!_suggestions);
268 }
269 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_HasPreviousError_ErrorCleared)270 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
271              EnchantDictionarySuggest_HasPreviousError_ErrorCleared)
272 {
273     SetErrorOnMockDictionary("something bad happened");
274 
275     _suggestions = enchant_dict_suggest(_dict, "helo", -1, NULL);
276     CHECK_EQUAL((void*)NULL, (void*)enchant_dict_get_error(_dict));
277 }
278 
279 /////////////////////////////////////////////////////////////////////////////
280 // Test Error Conditions
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_NullDictionary_NullSuggestions)281 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
282              EnchantDictionarySuggest_NullDictionary_NullSuggestions)
283 {
284     _suggestions = enchant_dict_suggest(NULL, "helo", -1, NULL);
285 
286     CHECK(!_suggestions);
287     CHECK(!dictSuggestCalled);
288 }
289 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_NullWord_NullSuggestions)290 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
291              EnchantDictionarySuggest_NullWord_NullSuggestions)
292 {
293     _suggestions = enchant_dict_suggest(_dict, NULL, -1, NULL);
294 
295     CHECK(!_suggestions);
296     CHECK(!dictSuggestCalled);
297 }
298 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_EmptyWord_NullSuggestions)299 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
300              EnchantDictionarySuggest_EmptyWord_NullSuggestions)
301 {
302     _suggestions = enchant_dict_suggest(_dict, "", -1, NULL);
303 
304     CHECK(!_suggestions);
305     CHECK(!dictSuggestCalled);
306 }
307 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_WordSize0_NullSuggestions)308 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
309              EnchantDictionarySuggest_WordSize0_NullSuggestions)
310 {
311     _suggestions = enchant_dict_suggest(_dict, "helo", 0, NULL);
312 
313     CHECK(!_suggestions);
314     CHECK(!dictSuggestCalled);
315 }
316 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_InvalidUtf8Correction_DoNothing)317 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
318              EnchantDictionarySuggest_InvalidUtf8Correction_DoNothing)
319 {
320     _suggestions = enchant_dict_suggest(_dict, "\xa5\xf1\x08", -1, NULL);
321 
322     CHECK(!_suggestions);
323     CHECK(!dictSuggestCalled);
324 }
325 
326 
TEST_FIXTURE(EnchantDictionarySuggestNotImplemented_TestFixture,EnchantDictionarySuggestNotImplemented_NullSuggestions)327 TEST_FIXTURE(EnchantDictionarySuggestNotImplemented_TestFixture,
328              EnchantDictionarySuggestNotImplemented_NullSuggestions)
329 {
330     _suggestions = enchant_dict_suggest(_dict, "helo", -1, NULL);
331 
332     CHECK(!_suggestions);
333     CHECK(!dictSuggestCalled);
334 }
335 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_SuggestionListWithInvalidUtf8_InvalidSuggestionIgnored_FreeCalled)336 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
337              EnchantDictionarySuggest_SuggestionListWithInvalidUtf8_InvalidSuggestionIgnored_FreeCalled)
338 {
339     suggestBehavior = returnFourOneInvalidUtf8;
340     size_t cSuggestions;
341     _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions);
342     CHECK(_suggestions);
343     CHECK(dictSuggestCalled);
344 
345     CHECK_EQUAL(3, cSuggestions);
346 
347     std::vector<std::string> suggestions;
348     if(_suggestions != NULL){
349         suggestions.insert(suggestions.begin(), _suggestions, _suggestions+cSuggestions);
350     }
351 
352     CHECK_ARRAY_EQUAL(GetExpectedSuggestions("helo",1), suggestions, std::min((size_t)3,cSuggestions));
353 }
354 
TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,EnchantDictionarySuggest_WordNfcInDictionaryNfdInPwl_ReturnsFromDict)355 TEST_FIXTURE(EnchantDictionarySuggest_TestFixture,
356              EnchantDictionarySuggest_WordNfcInDictionaryNfdInPwl_ReturnsFromDict)
357 {
358     suggestBehavior = returnFianceNfc;
359 
360     ExternalAddWordToDictionary(Convert(L"fiance\x301")); // NFD u0301 = Combining acute accent
361 
362     ReloadTestDictionary();
363 
364     size_t cSuggestions;
365     _suggestions = enchant_dict_suggest(_dict, "fiance", -1, &cSuggestions);
366     CHECK(_suggestions);
367 
368     CHECK_EQUAL(1, cSuggestions);
369     CHECK_EQUAL(Convert(L"fianc\xe9"), _suggestions[0]);
370 }
371 
TEST_FIXTURE(EnchantDictionarySuggestNotImplemented_TestFixture,EnchantDictionarySuggest_WordInDictionaryAndExclude_NotInSuggestions)372 TEST_FIXTURE(EnchantDictionarySuggestNotImplemented_TestFixture,
373              EnchantDictionarySuggest_WordInDictionaryAndExclude_NotInSuggestions)
374 {
375     ExternalAddWordToExclude("hello");
376     ExternalAddWordToDictionary("hello");
377 
378     ReloadTestDictionary();
379 
380     size_t cSuggestions;
381     _suggestions = enchant_dict_suggest(_dict, "helo", -1, &cSuggestions);
382     CHECK(!_suggestions);
383 
384     CHECK_EQUAL(0, cSuggestions);
385 }
386 
387