1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/app_list/search/tokenized_string_match.h" 6 7 #include <string> 8 9 #include "base/basictypes.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "testing/gtest/include/gtest/gtest.h" 12 13 namespace app_list { 14 namespace test { 15 16 // Returns a string of |text| marked the hits in |match| using block bracket. 17 // e.g. text= "Text", hits = [{0,1}], returns "[T]ext". 18 std::string MatchHit(const base::string16& text, 19 const TokenizedStringMatch& match) { 20 base::string16 marked = text; 21 22 const TokenizedStringMatch::Hits& hits = match.hits(); 23 for (TokenizedStringMatch::Hits::const_reverse_iterator it = hits.rbegin(); 24 it != hits.rend(); ++it) { 25 const gfx::Range& hit = *it; 26 marked.insert(hit.end(), 1, ']'); 27 marked.insert(hit.start(), 1, '['); 28 } 29 30 return base::UTF16ToUTF8(marked); 31 } 32 33 TEST(TokenizedStringMatchTest, NotMatch) { 34 struct { 35 const char* text; 36 const char* query; 37 } kTestCases[] = { 38 { "", "" }, 39 { "", "query" }, 40 { "text", "" }, 41 { "!", "!@#$%^&*()<<<**>>>" }, 42 { "abd", "abcd"}, 43 { "cd", "abcd"}, 44 }; 45 46 TokenizedStringMatch match; 47 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { 48 const base::string16 text(base::UTF8ToUTF16(kTestCases[i].text)); 49 EXPECT_FALSE(match.Calculate(base::UTF8ToUTF16(kTestCases[i].query), text)) 50 << "Test case " << i 51 << " : text=" << kTestCases[i].text 52 << ", query=" << kTestCases[i].query; 53 } 54 } 55 56 TEST(TokenizedStringMatchTest, Match) { 57 struct { 58 const char* text; 59 const char* query; 60 const char* expect; 61 } kTestCases[] = { 62 { "ScratchPad", "pad", "Scratch[Pad]" }, 63 { "ScratchPad", "sp", "[S]cratch[P]ad" }, 64 { "Chess2", "che", "[Che]ss2" }, 65 { "Chess2", "c2", "[C]hess[2]" }, 66 { "Cut the rope", "cut ro", "[Cut] the [ro]pe" }, 67 { "Cut the rope", "cr", "[C]ut the [r]ope" }, 68 { "John Doe", "jdoe", "[J]ohn [Doe]" }, 69 { "John Doe", "johnd", "[John D]oe" }, 70 { "Secure Shell", "she", "Secure [She]ll" }, 71 { "Simple Secure Shell", "sish", "[Si]mple Secure [Sh]ell" }, 72 { "Netflix", "flix", "Net[flix]" }, 73 }; 74 75 TokenizedStringMatch match; 76 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { 77 const base::string16 text(base::UTF8ToUTF16(kTestCases[i].text)); 78 EXPECT_TRUE(match.Calculate(base::UTF8ToUTF16(kTestCases[i].query), text)); 79 EXPECT_EQ(kTestCases[i].expect, MatchHit(text, match)); 80 } 81 } 82 83 TEST(TokenizedStringMatchTest, Relevance) { 84 struct { 85 const char* text; 86 const char* query_low; 87 const char* query_high; 88 } kTestCases[] = { 89 // More matched chars are better. 90 { "Google Chrome", "g", "go" }, 91 { "Google Chrome", "go", "goo" }, 92 { "Google Chrome", "goo", "goog" }, 93 { "Google Chrome", "c", "ch" }, 94 { "Google Chrome", "ch", "chr" }, 95 // Acronym match is better than something in the middle. 96 { "Google Chrome", "ch", "gc" }, 97 // Prefix match is better than middle match and acronym match. 98 { "Google Chrome", "ch", "go" }, 99 { "Google Chrome", "gc", "go" }, 100 // Substring match has the lowest score. 101 { "Google Chrome", "oo", "gc" }, 102 { "Google Chrome", "oo", "go" }, 103 { "Google Chrome", "oo", "ch" }, 104 }; 105 106 TokenizedStringMatch match_low; 107 TokenizedStringMatch match_high; 108 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { 109 const base::string16 text(base::UTF8ToUTF16(kTestCases[i].text)); 110 EXPECT_TRUE( 111 match_low.Calculate(base::UTF8ToUTF16(kTestCases[i].query_low), text)); 112 EXPECT_TRUE(match_high.Calculate( 113 base::UTF8ToUTF16(kTestCases[i].query_high), text)); 114 EXPECT_LT(match_low.relevance(), match_high.relevance()) 115 << "Test case " << i 116 << " : text=" << kTestCases[i].text 117 << ", query_low=" << kTestCases[i].query_low 118 << ", query_high=" << kTestCases[i].query_high; 119 } 120 } 121 122 } // namespace test 123 } // namespace app_list 124