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
13namespace app_list {
14namespace 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".
18std::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
33TEST(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
56TEST(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
83TEST(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