1 // Copyright (c) 2012 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.
5 #include "chrome/browser/autocomplete/scored_history_match.h"
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
17 using base::ASCIIToUTF16
;
18 using testing::ElementsAre
;
23 // Returns a VisitInfoVector that includes |num_visits| spread over the
24 // last |frequency|*|num_visits| days (relative to |now|). A frequency of
25 // one means one visit each day, two means every other day, etc.
26 VisitInfoVector
CreateVisitInfoVector(int num_visits
,
29 VisitInfoVector visits
;
30 for (int i
= 0; i
< num_visits
; ++i
) {
32 std::make_pair(now
- base::TimeDelta::FromDays(i
* frequency
),
33 ui::PAGE_TRANSITION_LINK
));
40 class ScoredHistoryMatchTest
: public testing::Test
{
42 // Convenience function to create a history::URLRow with basic data for |url|,
43 // |title|, |visit_count|, and |typed_count|. |days_since_last_visit| gives
44 // the number of days ago to which to set the URL's last_visit.
45 history::URLRow
MakeURLRow(const char* url
,
48 int days_since_last_visit
,
51 // Convenience function to set the word starts information from a
52 // history::URLRow's URL and title.
53 void PopulateWordStarts(const history::URLRow
& url_row
,
54 RowWordStarts
* word_starts
);
56 // Convenience functions for easily creating vectors of search terms.
57 String16Vector
Make1Term(const char* term
) const;
58 String16Vector
Make2Terms(const char* term_1
, const char* term_2
) const;
60 // Convenience function for GetTopicalityScore() that builds the term match
61 // and word break information automatically that are needed to call
62 // GetTopicalityScore(). It only works for scoring a single term, not
64 float GetTopicalityScoreOfTermAgainstURLAndTitle(const base::string16
& term
,
65 const base::string16
& url
,
66 const base::string16
& title
);
69 history::URLRow
ScoredHistoryMatchTest::MakeURLRow(const char* url
,
72 int days_since_last_visit
,
74 history::URLRow
row(GURL(url
), 0);
75 row
.set_title(ASCIIToUTF16(title
));
76 row
.set_visit_count(visit_count
);
77 row
.set_typed_count(typed_count
);
78 row
.set_last_visit(base::Time::NowFromSystemTime() -
79 base::TimeDelta::FromDays(days_since_last_visit
));
83 void ScoredHistoryMatchTest::PopulateWordStarts(const history::URLRow
& url_row
,
84 RowWordStarts
* word_starts
) {
85 String16SetFromString16(ASCIIToUTF16(url_row
.url().spec()),
86 &word_starts
->url_word_starts_
);
87 String16SetFromString16(url_row
.title(), &word_starts
->title_word_starts_
);
90 String16Vector
ScoredHistoryMatchTest::Make1Term(const char* term
) const {
91 String16Vector original_terms
;
92 original_terms
.push_back(ASCIIToUTF16(term
));
93 return original_terms
;
96 String16Vector
ScoredHistoryMatchTest::Make2Terms(const char* term_1
,
97 const char* term_2
) const {
98 String16Vector original_terms
;
99 original_terms
.push_back(ASCIIToUTF16(term_1
));
100 original_terms
.push_back(ASCIIToUTF16(term_2
));
101 return original_terms
;
104 float ScoredHistoryMatchTest::GetTopicalityScoreOfTermAgainstURLAndTitle(
105 const base::string16
& term
,
106 const base::string16
& url
,
107 const base::string16
& title
) {
108 // Make an empty match and simply populate the fields we need in order
109 // to call GetTopicalityScore().
110 ScoredHistoryMatch scored_match
;
111 scored_match
.url_matches
= MatchTermInString(term
, url
, 0);
112 scored_match
.title_matches
= MatchTermInString(term
, title
, 0);
113 RowWordStarts word_starts
;
114 String16SetFromString16(url
, &word_starts
.url_word_starts_
);
115 String16SetFromString16(title
, &word_starts
.title_word_starts_
);
116 WordStarts
one_word_no_offset(1, 0u);
117 return scored_match
.GetTopicalityScore(1, url
, one_word_no_offset
,
121 TEST_F(ScoredHistoryMatchTest
, Scoring
) {
122 // We use NowFromSystemTime() because MakeURLRow uses the same function
123 // to calculate last visit time when building a row.
124 base::Time now
= base::Time::NowFromSystemTime();
126 history::URLRow
row_a(MakeURLRow("http://fedcba", "abcd bcd", 3, 30, 1));
127 RowWordStarts word_starts_a
;
128 PopulateWordStarts(row_a
, &word_starts_a
);
129 WordStarts
one_word_no_offset(1, 0u);
130 VisitInfoVector visits_a
= CreateVisitInfoVector(3, 30, now
);
131 // Mark one visit as typed.
132 visits_a
[0].second
= ui::PAGE_TRANSITION_TYPED
;
133 ScoredHistoryMatch
scored_a(row_a
, visits_a
, std::string(),
134 ASCIIToUTF16("abc"), Make1Term("abc"),
135 one_word_no_offset
, word_starts_a
, false, now
);
137 // Test scores based on visit_count.
138 history::URLRow
row_b(MakeURLRow("http://abcdef", "abcd bcd", 10, 30, 1));
139 RowWordStarts word_starts_b
;
140 PopulateWordStarts(row_b
, &word_starts_b
);
141 VisitInfoVector visits_b
= CreateVisitInfoVector(10, 30, now
);
142 visits_b
[0].second
= ui::PAGE_TRANSITION_TYPED
;
143 ScoredHistoryMatch
scored_b(row_b
, visits_b
, std::string(),
144 ASCIIToUTF16("abc"), Make1Term("abc"),
145 one_word_no_offset
, word_starts_b
, false, now
);
146 EXPECT_GT(scored_b
.raw_score
, scored_a
.raw_score
);
148 // Test scores based on last_visit.
149 history::URLRow
row_c(MakeURLRow("http://abcdef", "abcd bcd", 3, 10, 1));
150 RowWordStarts word_starts_c
;
151 PopulateWordStarts(row_c
, &word_starts_c
);
152 VisitInfoVector visits_c
= CreateVisitInfoVector(3, 10, now
);
153 visits_c
[0].second
= ui::PAGE_TRANSITION_TYPED
;
154 ScoredHistoryMatch
scored_c(row_c
, visits_c
, std::string(),
155 ASCIIToUTF16("abc"), Make1Term("abc"),
156 one_word_no_offset
, word_starts_c
, false, now
);
157 EXPECT_GT(scored_c
.raw_score
, scored_a
.raw_score
);
159 // Test scores based on typed_count.
160 history::URLRow
row_d(MakeURLRow("http://abcdef", "abcd bcd", 3, 30, 3));
161 RowWordStarts word_starts_d
;
162 PopulateWordStarts(row_d
, &word_starts_d
);
163 VisitInfoVector visits_d
= CreateVisitInfoVector(3, 30, now
);
164 visits_d
[0].second
= ui::PAGE_TRANSITION_TYPED
;
165 visits_d
[1].second
= ui::PAGE_TRANSITION_TYPED
;
166 visits_d
[2].second
= ui::PAGE_TRANSITION_TYPED
;
167 ScoredHistoryMatch
scored_d(row_d
, visits_d
, std::string(),
168 ASCIIToUTF16("abc"), Make1Term("abc"),
169 one_word_no_offset
, word_starts_d
, false, now
);
170 EXPECT_GT(scored_d
.raw_score
, scored_a
.raw_score
);
172 // Test scores based on a terms appearing multiple times.
173 history::URLRow
row_e(MakeURLRow(
174 "http://csi.csi.csi/csi_csi",
175 "CSI Guide to CSI Las Vegas, CSI New York, CSI Provo", 3, 30, 3));
176 RowWordStarts word_starts_e
;
177 PopulateWordStarts(row_e
, &word_starts_e
);
178 const VisitInfoVector visits_e
= visits_d
;
179 ScoredHistoryMatch
scored_e(row_e
, visits_e
, std::string(),
180 ASCIIToUTF16("csi"), Make1Term("csi"),
181 one_word_no_offset
, word_starts_e
, false, now
);
182 EXPECT_LT(scored_e
.raw_score
, 1400);
184 // Test that a result with only a mid-term match (i.e., not at a word
185 // boundary) scores 0.
186 ScoredHistoryMatch
scored_f(row_a
, visits_a
, std::string(),
187 ASCIIToUTF16("cd"), Make1Term("cd"),
188 one_word_no_offset
, word_starts_a
, false, now
);
189 EXPECT_EQ(scored_f
.raw_score
, 0);
192 TEST_F(ScoredHistoryMatchTest
, ScoringBookmarks
) {
193 // We use NowFromSystemTime() because MakeURLRow uses the same function
194 // to calculate last visit time when building a row.
195 base::Time now
= base::Time::NowFromSystemTime();
197 std::string
url_string("http://fedcba");
198 const GURL
url(url_string
);
199 history::URLRow
row(MakeURLRow(url_string
.c_str(), "abcd bcd", 8, 3, 1));
200 RowWordStarts word_starts
;
201 PopulateWordStarts(row
, &word_starts
);
202 WordStarts
one_word_no_offset(1, 0u);
203 VisitInfoVector visits
= CreateVisitInfoVector(8, 3, now
);
204 ScoredHistoryMatch
scored(row
, visits
, std::string(), ASCIIToUTF16("abc"),
205 Make1Term("abc"), one_word_no_offset
, word_starts
,
207 // Now check that if URL is bookmarked then its score increases.
208 base::AutoReset
<int> reset(&ScoredHistoryMatch::bookmark_value_
, 5);
209 ScoredHistoryMatch
scored_with_bookmark(
210 row
, visits
, std::string(), ASCIIToUTF16("abc"), Make1Term("abc"),
211 one_word_no_offset
, word_starts
, true, now
);
212 EXPECT_GT(scored_with_bookmark
.raw_score
, scored
.raw_score
);
215 TEST_F(ScoredHistoryMatchTest
, ScoringTLD
) {
216 // We use NowFromSystemTime() because MakeURLRow uses the same function
217 // to calculate last visit time when building a row.
218 base::Time now
= base::Time::NowFromSystemTime();
220 // By default the URL should not be returned for a query that includes "com".
221 std::string
url_string("http://fedcba.com/");
222 const GURL
url(url_string
);
223 history::URLRow
row(MakeURLRow(url_string
.c_str(), "", 8, 3, 1));
224 RowWordStarts word_starts
;
225 PopulateWordStarts(row
, &word_starts
);
226 WordStarts
two_words_no_offsets(2, 0u);
227 VisitInfoVector visits
= CreateVisitInfoVector(8, 3, now
);
228 ScoredHistoryMatch
scored(row
, visits
, std::string(), ASCIIToUTF16("fed com"),
229 Make2Terms("fed", "com"), two_words_no_offsets
,
230 word_starts
, false, now
);
231 EXPECT_EQ(0, scored
.raw_score
);
233 // Now allow credit for the match in the TLD.
234 base::AutoReset
<bool> reset(&ScoredHistoryMatch::allow_tld_matches_
, true);
235 ScoredHistoryMatch
scored_with_tld(
236 row
, visits
, std::string(), ASCIIToUTF16("fed com"),
237 Make2Terms("fed", "com"), two_words_no_offsets
, word_starts
, false, now
);
238 EXPECT_GT(scored_with_tld
.raw_score
, 0);
241 TEST_F(ScoredHistoryMatchTest
, ScoringScheme
) {
242 // We use NowFromSystemTime() because MakeURLRow uses the same function
243 // to calculate last visit time when building a row.
244 base::Time now
= base::Time::NowFromSystemTime();
246 // By default the URL should not be returned for a query that includes "http".
247 std::string
url_string("http://fedcba/");
248 const GURL
url(url_string
);
249 history::URLRow
row(MakeURLRow(url_string
.c_str(), "", 8, 3, 1));
250 RowWordStarts word_starts
;
251 PopulateWordStarts(row
, &word_starts
);
252 WordStarts
two_words_no_offsets(2, 0u);
253 VisitInfoVector visits
= CreateVisitInfoVector(8, 3, now
);
254 ScoredHistoryMatch
scored(row
, visits
, std::string(),
255 ASCIIToUTF16("fed http"), Make2Terms("fed", "http"),
256 two_words_no_offsets
, word_starts
, false, now
);
257 EXPECT_EQ(0, scored
.raw_score
);
259 // Now allow credit for the match in the scheme.
260 base::AutoReset
<bool> reset(&ScoredHistoryMatch::allow_scheme_matches_
, true);
261 ScoredHistoryMatch
scored_with_scheme(
262 row
, visits
, std::string(), ASCIIToUTF16("fed http"),
263 Make2Terms("fed", "http"), two_words_no_offsets
, word_starts
, false, now
);
264 EXPECT_GT(scored_with_scheme
.raw_score
, 0);
267 TEST_F(ScoredHistoryMatchTest
, Inlining
) {
268 // We use NowFromSystemTime() because MakeURLRow uses the same function
269 // to calculate last visit time when building a row.
270 base::Time now
= base::Time::NowFromSystemTime();
271 RowWordStarts word_starts
;
272 WordStarts
one_word_no_offset(1, 0u);
273 VisitInfoVector visits
;
277 MakeURLRow("http://www.google.com", "abcdef", 3, 30, 1));
278 PopulateWordStarts(row
, &word_starts
);
279 ScoredHistoryMatch
scored_a(row
, visits
, std::string(), ASCIIToUTF16("g"),
280 Make1Term("g"), one_word_no_offset
, word_starts
,
282 EXPECT_TRUE(scored_a
.can_inline
);
283 EXPECT_FALSE(scored_a
.match_in_scheme
);
284 ScoredHistoryMatch
scored_b(row
, visits
, std::string(), ASCIIToUTF16("w"),
285 Make1Term("w"), one_word_no_offset
, word_starts
,
287 EXPECT_TRUE(scored_b
.can_inline
);
288 EXPECT_FALSE(scored_b
.match_in_scheme
);
289 ScoredHistoryMatch
scored_c(row
, visits
, std::string(), ASCIIToUTF16("h"),
290 Make1Term("h"), one_word_no_offset
, word_starts
,
292 EXPECT_TRUE(scored_c
.can_inline
);
293 EXPECT_TRUE(scored_c
.match_in_scheme
);
294 ScoredHistoryMatch
scored_d(row
, visits
, std::string(), ASCIIToUTF16("o"),
295 Make1Term("o"), one_word_no_offset
, word_starts
,
297 EXPECT_FALSE(scored_d
.can_inline
);
298 EXPECT_FALSE(scored_d
.match_in_scheme
);
302 history::URLRow
row(MakeURLRow("http://teams.foo.com", "abcdef", 3, 30, 1));
303 PopulateWordStarts(row
, &word_starts
);
304 ScoredHistoryMatch
scored_a(row
, visits
, std::string(), ASCIIToUTF16("t"),
305 Make1Term("t"), one_word_no_offset
, word_starts
,
307 EXPECT_TRUE(scored_a
.can_inline
);
308 EXPECT_FALSE(scored_a
.match_in_scheme
);
309 ScoredHistoryMatch
scored_b(row
, visits
, std::string(), ASCIIToUTF16("f"),
310 Make1Term("f"), one_word_no_offset
, word_starts
,
312 EXPECT_FALSE(scored_b
.can_inline
);
313 EXPECT_FALSE(scored_b
.match_in_scheme
);
314 ScoredHistoryMatch
scored_c(row
, visits
, std::string(), ASCIIToUTF16("o"),
315 Make1Term("o"), one_word_no_offset
, word_starts
,
317 EXPECT_FALSE(scored_c
.can_inline
);
318 EXPECT_FALSE(scored_c
.match_in_scheme
);
323 MakeURLRow("https://www.testing.com", "abcdef", 3, 30, 1));
324 PopulateWordStarts(row
, &word_starts
);
325 ScoredHistoryMatch
scored_a(row
, visits
, std::string(), ASCIIToUTF16("t"),
326 Make1Term("t"), one_word_no_offset
, word_starts
,
328 EXPECT_TRUE(scored_a
.can_inline
);
329 EXPECT_FALSE(scored_a
.match_in_scheme
);
330 ScoredHistoryMatch
scored_b(row
, visits
, std::string(), ASCIIToUTF16("h"),
331 Make1Term("h"), one_word_no_offset
, word_starts
,
333 EXPECT_TRUE(scored_b
.can_inline
);
334 EXPECT_TRUE(scored_b
.match_in_scheme
);
335 ScoredHistoryMatch
scored_c(row
, visits
, std::string(), ASCIIToUTF16("w"),
336 Make1Term("w"), one_word_no_offset
, word_starts
,
338 EXPECT_TRUE(scored_c
.can_inline
);
339 EXPECT_FALSE(scored_c
.match_in_scheme
);
343 TEST_F(ScoredHistoryMatchTest
, GetTopicalityScoreTrailingSlash
) {
344 const float hostname
= GetTopicalityScoreOfTermAgainstURLAndTitle(
345 ASCIIToUTF16("def"), ASCIIToUTF16("http://abc.def.com/"),
346 ASCIIToUTF16("Non-Matching Title"));
347 const float hostname_no_slash
= GetTopicalityScoreOfTermAgainstURLAndTitle(
348 ASCIIToUTF16("def"), ASCIIToUTF16("http://abc.def.com"),
349 ASCIIToUTF16("Non-Matching Title"));
350 EXPECT_EQ(hostname_no_slash
, hostname
);
353 // This function only tests scoring of single terms that match exactly
354 // once somewhere in the URL or title.
355 TEST_F(ScoredHistoryMatchTest
, GetTopicalityScore
) {
356 base::string16 url
= ASCIIToUTF16(
357 "http://abc.def.com/path1/path2?"
358 "arg1=val1&arg2=val2#hash_component");
359 base::string16 title
= ASCIIToUTF16("here is a title");
360 const float hostname_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
361 ASCIIToUTF16("abc"), url
, title
);
362 const float hostname_mid_word_score
=
363 GetTopicalityScoreOfTermAgainstURLAndTitle(ASCIIToUTF16("bc"), url
,
365 const float domain_name_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
366 ASCIIToUTF16("def"), url
, title
);
367 const float domain_name_mid_word_score
=
368 GetTopicalityScoreOfTermAgainstURLAndTitle(ASCIIToUTF16("ef"), url
,
370 const float tld_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
371 ASCIIToUTF16("com"), url
, title
);
372 const float tld_mid_word_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
373 ASCIIToUTF16("om"), url
, title
);
374 const float path_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
375 ASCIIToUTF16("path1"), url
, title
);
376 const float path_mid_word_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
377 ASCIIToUTF16("ath1"), url
, title
);
378 const float arg_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
379 ASCIIToUTF16("arg2"), url
, title
);
380 const float arg_mid_word_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
381 ASCIIToUTF16("rg2"), url
, title
);
382 const float protocol_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
383 ASCIIToUTF16("htt"), url
, title
);
384 const float protocol_mid_word_score
=
385 GetTopicalityScoreOfTermAgainstURLAndTitle(ASCIIToUTF16("tt"), url
,
387 const float title_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
388 ASCIIToUTF16("her"), url
, title
);
389 const float title_mid_word_score
= GetTopicalityScoreOfTermAgainstURLAndTitle(
390 ASCIIToUTF16("er"), url
, title
);
391 // Verify hostname and domain name > path > arg.
392 EXPECT_GT(hostname_score
, path_score
);
393 EXPECT_GT(domain_name_score
, path_score
);
394 EXPECT_GT(path_score
, arg_score
);
395 // Verify that domain name > path and domain name > arg for non-word
397 EXPECT_GT(hostname_mid_word_score
, path_mid_word_score
);
398 EXPECT_GT(domain_name_mid_word_score
, path_mid_word_score
);
399 EXPECT_GT(domain_name_mid_word_score
, arg_mid_word_score
);
400 EXPECT_GT(hostname_mid_word_score
, arg_mid_word_score
);
401 // Also verify that the matches at non-word-boundaries all score
402 // worse than the matches at word boundaries. These three sets suffice.
403 EXPECT_GT(arg_score
, hostname_mid_word_score
);
404 EXPECT_GT(arg_score
, domain_name_mid_word_score
);
405 EXPECT_GT(title_score
, title_mid_word_score
);
406 // Check that title matches fit somewhere reasonable compared to the
407 // various types of URL matches.
408 EXPECT_GT(title_score
, arg_score
);
409 EXPECT_GT(arg_score
, title_mid_word_score
);
410 // Finally, verify that protocol matches and top level domain name
411 // matches (.com, .net, etc.) score worse than some of the mid-word
412 // matches that actually count.
413 EXPECT_GT(hostname_mid_word_score
, protocol_score
);
414 EXPECT_GT(hostname_mid_word_score
, protocol_mid_word_score
);
415 EXPECT_GT(hostname_mid_word_score
, tld_score
);
416 EXPECT_GT(hostname_mid_word_score
, tld_mid_word_score
);
419 // Test the function GetFinalRelevancyScore().
420 TEST_F(ScoredHistoryMatchTest
, GetFinalRelevancyScore
) {
421 // hqp_relevance_buckets = "0.0:100,1.0:200,4.0:500,8.0:900,10.0:1000";
422 std::vector
<ScoredHistoryMatch::ScoreMaxRelevance
> hqp_buckets
;
423 hqp_buckets
.push_back(std::make_pair(0.0, 100));
424 hqp_buckets
.push_back(std::make_pair(1.0, 200));
425 hqp_buckets
.push_back(std::make_pair(4.0, 500));
426 hqp_buckets
.push_back(std::make_pair(8.0, 900));
427 hqp_buckets
.push_back(std::make_pair(10.0, 1000));
428 // Check when topicality score is zero.
429 float topicality_score
= 0.0;
430 float frequency_score
= 10.0;
431 // intermediate_score = 0.0 * 10.0 = 0.0.
432 EXPECT_EQ(0, ScoredHistoryMatch::GetFinalRelevancyScore(
433 topicality_score
, frequency_score
, hqp_buckets
));
435 // Check when intermediate score falls at the border range.
436 topicality_score
= 0.4f
;
437 frequency_score
= 10.0f
;
438 // intermediate_score = 0.5 * 10.0 = 4.0.
439 EXPECT_EQ(500, ScoredHistoryMatch::GetFinalRelevancyScore(
440 topicality_score
, frequency_score
, hqp_buckets
));
442 // Checking the score that falls into one of the buckets.
443 topicality_score
= 0.5f
;
444 frequency_score
= 10.0f
;
445 // intermediate_score = 0.5 * 10.0 = 5.0.
446 EXPECT_EQ(600, // 500 + (((900 - 500)/(8 -4)) * 1) = 600.
447 ScoredHistoryMatch::GetFinalRelevancyScore(
448 topicality_score
, frequency_score
, hqp_buckets
));
450 // Never give the score greater than maximum specified.
451 topicality_score
= 0.5f
;
452 frequency_score
= 22.0f
;
453 // intermediate_score = 0.5 * 22.0 = 11.0
454 EXPECT_EQ(1000, ScoredHistoryMatch::GetFinalRelevancyScore(
455 topicality_score
, frequency_score
, hqp_buckets
));
458 // Test the function GetHQPBucketsFromString().
459 TEST_F(ScoredHistoryMatchTest
, GetHQPBucketsFromString
) {
460 std::string buckets_str
= "0.0:400,1.5:600,12.0:1300,20.0:1399";
461 std::vector
<ScoredHistoryMatch::ScoreMaxRelevance
> hqp_buckets
;
464 ScoredHistoryMatch::GetHQPBucketsFromString(buckets_str
, &hqp_buckets
));
465 EXPECT_THAT(hqp_buckets
, ElementsAre(Pair(0.0, 400), Pair(1.5, 600),
466 Pair(12.0, 1300), Pair(20.0, 1399)));
468 buckets_str
= "0.0,400,1.5,600";
470 ScoredHistoryMatch::GetHQPBucketsFromString(buckets_str
, &hqp_buckets
));