• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.quicksearchbox;
18 
19 import android.test.AndroidTestCase;
20 import android.text.Spanned;
21 
22 import androidx.test.filters.SmallTest;
23 
24 import com.android.quicksearchbox.MockTextAppearanceFactory.MockStyleSpan;
25 import com.android.quicksearchbox.util.LevenshteinDistance.Token;
26 
27 /**
28  * Tests for {@link LevenshteinSuggestionFormatter}.
29  */
30 @SmallTest
31 public class LevenshteinFormatterTest extends AndroidTestCase {
32 
33     private LevenshteinSuggestionFormatter mFormatter;
34     private int mSuggestedStyle;
35     private int mQueryStyle;
36 
37     @Override
setUp()38     protected void setUp() throws Exception {
39         mFormatter = new LevenshteinSuggestionFormatter(new MockTextAppearanceFactory());
40         mSuggestedStyle = MockTextAppearanceFactory.ID_SUGGESTED_TEXT;
41         mQueryStyle = MockTextAppearanceFactory.ID_QUERY_TEXT;
42     }
43 
verifyTokenizeResult(String input, String... output)44     private void verifyTokenizeResult(String input, String... output) {
45         Token[] tokens = mFormatter.tokenize(input);
46         assertEquals(output.length, tokens.length);
47         for (int i=0; i<output.length; ++i) {
48             assertEquals(output[i], tokens[i].toString());
49         }
50     }
51 
testTokenizeNoTokens()52     public void testTokenizeNoTokens() {
53         verifyTokenizeResult("");
54         verifyTokenizeResult("  ");
55     }
56 
testTokenizeSingleToken()57     public void testTokenizeSingleToken() {
58         verifyTokenizeResult("singleToken", "singleToken");
59     }
60 
testTokenizeTwoTokens()61     public void testTokenizeTwoTokens() {
62         verifyTokenizeResult("two tokens", "two", "tokens");
63     }
64 
testTokenizeLeadingSpaces()65     public void testTokenizeLeadingSpaces() {
66         verifyTokenizeResult(" evil kittens", "evil", "kittens");
67         verifyTokenizeResult("        furry lizards", "furry", "lizards");
68     }
69 
testTokenizeTrailingSpaces()70     public void testTokenizeTrailingSpaces() {
71         verifyTokenizeResult("mechanical elephant ", "mechanical", "elephant");
72         verifyTokenizeResult("disappointed dog       ", "disappointed", "dog");
73     }
74 
testTokenizeManySpaces()75     public void testTokenizeManySpaces() {
76         verifyTokenizeResult("happy     horses", "happy", "horses");
77     }
78 
testTokenizeLongSentence()79     public void testTokenizeLongSentence() {
80         verifyTokenizeResult("The fool looks at a finger that points at the sky",
81                 "The", "fool", "looks", "at", "a", "finger", "that", "points", "at", "the", "sky");
82     }
83 
testTokenizeWithPunctuation()84     public void testTokenizeWithPunctuation() {
85         verifyTokenizeResult("Hitchhiker's guide", "Hitchhiker's", "guide");
86         verifyTokenizeResult("full. stop. ", "full.", "stop.");
87         verifyTokenizeResult("' . ; . ..", "'", ".", ";", ".", "..");
88     }
89 
testTokenizeWithTabs()90     public void testTokenizeWithTabs() {
91         verifyTokenizeResult("don't\tpanic\t", "don't", "panic");
92     }
93 
verifyFindMatches(String source, String target, String... newTokensInTarget)94     private void verifyFindMatches(String source, String target, String... newTokensInTarget) {
95         Token[] sourceTokens = mFormatter.tokenize(source);
96         Token[] targetTokens = mFormatter.tokenize(target);
97 
98         int[] matches = mFormatter.findMatches(sourceTokens, targetTokens);
99         assertEquals(targetTokens.length, matches.length);
100         int newTokenCount = 0;
101         int lastSourceToken = -1;
102         for (int i=0; i<targetTokens.length; ++i) {
103 
104             int sourceIdx = matches[i];
105             if (sourceIdx < 0) {
106                 String targetToken = targetTokens[i].toString();
107                 assertTrue("Unexpected new token '" + targetToken + "'",
108                         newTokenCount < newTokensInTarget.length);
109 
110                 assertEquals(newTokensInTarget[newTokenCount], targetToken);
111                 ++newTokenCount;
112             } else {
113                 assertTrue("Source token out of order", lastSourceToken < sourceIdx);
114                 Token srcToken = sourceTokens[sourceIdx];
115                 Token trgToken = targetTokens[i];
116                 assertTrue("'" + srcToken + "' is not a prefix of '" + trgToken + "'",
117                         srcToken.prefixOf(trgToken));
118                 lastSourceToken = sourceIdx;
119             }
120         }
121     }
122 
testFindMatchesSameTokens()123     public void testFindMatchesSameTokens() {
124         verifyFindMatches("", "");
125         verifyFindMatches("one", "one");
126         verifyFindMatches("one two three", "one two three");
127     }
128 
testFindMatchesNewTokens()129     public void testFindMatchesNewTokens() {
130         verifyFindMatches("", "one", "one");
131         verifyFindMatches("one", "one two", "two");
132         verifyFindMatches("one", "one two three", "two", "three");
133         verifyFindMatches("two", "one two three", "one", "three");
134         verifyFindMatches("pictures", "pictures of kittens", "of", "kittens");
135     }
136 
testFindMatchesReplacedTokens()137     public void testFindMatchesReplacedTokens() {
138         verifyFindMatches("one", "two", "two");
139         verifyFindMatches("one", "two three", "two", "three");
140         verifyFindMatches("two", "one three", "one", "three");
141         verifyFindMatches("pictures", "of kittens", "of", "kittens");
142     }
143 
testFindMatchesDuplicateTokens()144     public void testFindMatchesDuplicateTokens() {
145         verifyFindMatches("badger", "badger badger", "badger");
146         verifyFindMatches("badger", "badger badger badger", "badger", "badger");
147         verifyFindMatches("badger badger", "badger badger badger", "badger");
148         verifyFindMatches("badger badger badger", "badger badger badger");
149         // mushroom!
150     }
151 
verifyFormatSuggestion(String query, String suggestion, SpanFormat... spans)152     private void verifyFormatSuggestion(String query, String suggestion, SpanFormat... spans) {
153         Spanned s = mFormatter.formatSuggestion(query, suggestion);
154         for (SpanFormat span : spans) {
155             span.verify(s);
156         }
157     }
158 
testFormatSuggestionEmptyStrings()159     public void testFormatSuggestionEmptyStrings() {
160         verifyFormatSuggestion("", "");
161     }
162 
testFormatSuggestionEmptyQuery()163     public void testFormatSuggestionEmptyQuery() {
164         verifyFormatSuggestion("", "suggestion",
165                 new SpanFormat(0, "suggestion", mSuggestedStyle));
166     }
167 
testFormatSuggestionQuerySuggested()168     public void testFormatSuggestionQuerySuggested() {
169         verifyFormatSuggestion("query", "query",
170                 new SpanFormat(0, "query", mQueryStyle));
171     }
172 
testFormatSuggestionExtraWordsSuggested()173     public void testFormatSuggestionExtraWordsSuggested() {
174         verifyFormatSuggestion("query", "query suggested",
175                 new SpanFormat(0, "query", mQueryStyle),
176                 new SpanFormat(6, "suggested", mSuggestedStyle));
177 
178         verifyFormatSuggestion("pictures", "pictures of kittens",
179                 new SpanFormat(0,  "pictures", mQueryStyle),
180                 new SpanFormat(9,  "of", mSuggestedStyle),
181                 new SpanFormat(12, "kittens", mSuggestedStyle));
182 
183         verifyFormatSuggestion("pictures of", "pictures of kittens dying",
184                 new SpanFormat(0,  "pictures", mQueryStyle),
185                 new SpanFormat(9,  "of", mQueryStyle),
186                 new SpanFormat(12, "kittens", mSuggestedStyle),
187                 new SpanFormat(20, "dying", mSuggestedStyle));
188     }
189 
testFormatSuggestionExtraWordSuggestedAtStart()190     public void testFormatSuggestionExtraWordSuggestedAtStart() {
191         verifyFormatSuggestion("query", "suggested query",
192                 new SpanFormat(0, "suggested", mSuggestedStyle),
193                 new SpanFormat(10, "query", mQueryStyle));
194     }
195 
testFormatSuggestionAlternativeWordSuggested()196     public void testFormatSuggestionAlternativeWordSuggested() {
197         verifyFormatSuggestion("query", "suggested",
198                 new SpanFormat(0, "suggested", mSuggestedStyle));
199     }
200 
testFormatSuggestionDuplicateWords()201     public void testFormatSuggestionDuplicateWords() {
202         verifyFormatSuggestion("", "badger badger",
203                 new SpanFormat(0, "badger", mSuggestedStyle),
204                 new SpanFormat(7, "badger", mSuggestedStyle));
205 
206         verifyFormatSuggestion("badger", "badger badger",
207                 new SpanFormat(0, "badger", mQueryStyle),
208                 new SpanFormat(7, "badger", mSuggestedStyle));
209 
210         verifyFormatSuggestion("badger badger", "badger badger",
211                 new SpanFormat(0, "badger", mQueryStyle),
212                 new SpanFormat(7, "badger", mQueryStyle));
213 
214         verifyFormatSuggestion("badger badger", "badger badger badger",
215                 new SpanFormat(0, "badger", mQueryStyle),
216                 new SpanFormat(7, "badger", mQueryStyle),
217                 new SpanFormat(14, "badger", mSuggestedStyle));
218     }
219 
testFormatSuggestionDuplicateSequences()220     public void testFormatSuggestionDuplicateSequences() {
221         verifyFormatSuggestion("dem bones", "dem bones dem bones",
222                 new SpanFormat(0, "dem", mQueryStyle),
223                 new SpanFormat(4, "bones", mQueryStyle),
224                 new SpanFormat(10, "dem", mSuggestedStyle),
225                 new SpanFormat(14, "bones", mSuggestedStyle)
226         );
227 
228         verifyFormatSuggestion("dem bones", "dem dry bones dem bones",
229                 new SpanFormat(0, "dem", mQueryStyle),
230                 new SpanFormat(4, "dry", mSuggestedStyle),
231                 new SpanFormat(8, "bones", mQueryStyle),
232                 new SpanFormat(14, "dem", mSuggestedStyle),
233                 new SpanFormat(18, "bones", mSuggestedStyle)
234         );
235 
236         verifyFormatSuggestion("dem dry bones", "dry bones dem dry bones dem dry bones",
237                 new SpanFormat(0, "dry", mSuggestedStyle),
238                 new SpanFormat(4, "bones", mSuggestedStyle),
239                 new SpanFormat(10, "dem", mQueryStyle),
240                 new SpanFormat(14, "dry", mQueryStyle),
241                 new SpanFormat(18, "bones", mQueryStyle),
242                 new SpanFormat(24, "dem", mSuggestedStyle),
243                 new SpanFormat(28, "dry", mSuggestedStyle),
244                 new SpanFormat(32, "bones", mSuggestedStyle)
245         );
246     }
247 
testFormatSuggestionWordCompletion()248     public void testFormatSuggestionWordCompletion() {
249         verifyFormatSuggestion("hitch", "hitchhiker",
250                 new SpanFormat(0, "hitch", mQueryStyle),
251                 new SpanFormat(5, "hiker", mSuggestedStyle)
252         );
253 
254         verifyFormatSuggestion("hitch", "hitchhiker's guide",
255                 new SpanFormat(0, "hitch", mQueryStyle),
256                 new SpanFormat(5, "hiker's", mSuggestedStyle),
257                 new SpanFormat(13, "guide", mSuggestedStyle)
258         );
259 
260         verifyFormatSuggestion("hitchhiker's g", "hitchhiker's guide",
261                 new SpanFormat(0, "hitchhiker's", mQueryStyle),
262                 new SpanFormat(13, "g", mQueryStyle),
263                 new SpanFormat(14, "uide", mSuggestedStyle)
264         );
265 
266         verifyFormatSuggestion("hitchhiker's g", "hitchhiker's guide to the galaxy",
267                 new SpanFormat(0, "hitchhiker's", mQueryStyle),
268                 new SpanFormat(13, "g", mQueryStyle),
269                 new SpanFormat(14, "uide", mSuggestedStyle),
270                 new SpanFormat(19, "to", mSuggestedStyle),
271                 new SpanFormat(22, "the", mSuggestedStyle),
272                 new SpanFormat(26, "galaxy", mSuggestedStyle)
273         );
274     }
275 
testFormatSuggestionWordSplitting()276     public void testFormatSuggestionWordSplitting() {
277         verifyFormatSuggestion("dimsum", "dim sum",
278                 new SpanFormat(0, "dim", mSuggestedStyle),
279                 new SpanFormat(4, "sum", mSuggestedStyle)
280         );
281 
282         verifyFormatSuggestion("dimsum london", "dim sum london",
283                 new SpanFormat(0, "dim", mSuggestedStyle),
284                 new SpanFormat(4, "sum", mSuggestedStyle),
285                 new SpanFormat(8, "london", mQueryStyle)
286         );
287 
288         verifyFormatSuggestion("dimsum london", "dim sum london yummy",
289                 new SpanFormat(0, "dim", mSuggestedStyle),
290                 new SpanFormat(4, "sum", mSuggestedStyle),
291                 new SpanFormat(8, "london", mQueryStyle),
292                 new SpanFormat(15, "yummy", mSuggestedStyle)
293         );
294     }
295 
testFormatSuggestionWordCombining()296     public void testFormatSuggestionWordCombining() {
297         verifyFormatSuggestion("hos pital", "hospital",
298                 new SpanFormat(0, "hos", mQueryStyle),
299                 new SpanFormat(3, "pital", mSuggestedStyle)
300         );
301 
302         verifyFormatSuggestion("hos pital", "hospital waiting times",
303                 new SpanFormat(0, "hos", mQueryStyle),
304                 new SpanFormat(3, "pital", mSuggestedStyle),
305                 new SpanFormat(9, "waiting", mSuggestedStyle),
306                 new SpanFormat(17, "times", mSuggestedStyle)
307         );
308 
309         verifyFormatSuggestion("hos pital waiting", "hospital waiting",
310                 new SpanFormat(0, "hos", mQueryStyle),
311                 new SpanFormat(3, "pital", mSuggestedStyle),
312                 new SpanFormat(9, "waiting", mQueryStyle)
313         );
314 
315         verifyFormatSuggestion("hospital wait ing times", "hospital waiting times",
316                 new SpanFormat(0, "hospital", mQueryStyle),
317                 new SpanFormat(9, "wait", mQueryStyle),
318                 new SpanFormat(13, "ing", mSuggestedStyle),
319                 new SpanFormat(17, "times", mQueryStyle)
320         );
321     }
322 
testFormatSuggestionCapitalization()323     public void testFormatSuggestionCapitalization() {
324         verifyFormatSuggestion("Flay", "flay",
325                 new SpanFormat(0, "flay", mQueryStyle));
326 
327         verifyFormatSuggestion("STEERPI", "steerpike",
328                 new SpanFormat(0, "steerpi", mQueryStyle),
329                 new SpanFormat(7, "ke", mSuggestedStyle));
330 
331         verifyFormatSuggestion("STEErpi", "steerpike",
332                 new SpanFormat(0, "steerpi", mQueryStyle),
333                 new SpanFormat(7, "ke", mSuggestedStyle));
334 
335         verifyFormatSuggestion("TITUS", "titus groan",
336                 new SpanFormat(0, "titus", mQueryStyle),
337                 new SpanFormat(6, "groan", mSuggestedStyle));
338 }
339 
340     private class SpanFormat {
341         private final int mStart;
342         private final int mEnd;
343         private final String mExpectedText;
344         private final int mStyle;
SpanFormat(int start, String expectedText, int style)345         public SpanFormat(int start, String expectedText, int style) {
346             mStart = start;
347             mEnd = start + expectedText.length();
348             mExpectedText = expectedText;
349             mStyle = style;
350         }
verify(Spanned spanned)351         public void verify(Spanned spanned) {
352             String spannedText = spanned.subSequence(mStart, mEnd).toString();
353             assertEquals("Test error", mExpectedText, spannedText);
354             MockStyleSpan[] spans = spanned.getSpans(mStart, mEnd, MockStyleSpan.class);
355             assertEquals("Wrong number of spans in '" + spannedText + "'", 1, spans.length);
356             assertEquals("Wrong style for '" + spannedText + "' at position " + mStart,
357                     mStyle, spans[0].getId());
358         }
359     }
360 
361 }
362