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.
4
5#include "chrome/browser/google/google_search_counter.h"
6#include "components/google/core/browser/google_search_metrics.h"
7#include "content/public/browser/navigation_controller.h"
8#include "content/public/browser/navigation_details.h"
9#include "content/public/browser/navigation_entry.h"
10#include "content/public/browser/notification_service.h"
11#include "content/public/browser/notification_types.h"
12#include "testing/gmock/include/gmock/gmock.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15namespace {
16
17class MockSearchMetrics : public GoogleSearchMetrics {
18 public:
19  MOCK_CONST_METHOD1(RecordGoogleSearch,
20      void(GoogleSearchMetrics::AccessPoint ap));
21};
22
23}  // namespace
24
25class GoogleSearchCounterTest : public testing::Test {
26 protected:
27  GoogleSearchCounterTest();
28  virtual ~GoogleSearchCounterTest();
29
30  // testing::Test
31  virtual void SetUp();
32  virtual void TearDown();
33
34  // Test if |url| is a Google search for specific types. When |is_omnibox| is
35  // true, this method will append Omnibox identifiers to the simulated URL
36  // navigation. If |expected_metric| is set and not AP_BOUNDARY, we'll also use
37  // the Search Metrics mock class to ensure that the type of metric recorded is
38  // correct. Note that when |expected_metric| is AP_BOUNDARY, we strictly
39  // forbid any metrics from being logged at all. See implementation below for
40  // details.
41  void TestGoogleSearch(const std::string& url,
42                        bool is_omnibox,
43                        GoogleSearchMetrics::AccessPoint expected_metric);
44
45 private:
46  void ExpectMetricsLogged(GoogleSearchMetrics::AccessPoint ap);
47
48  // Weak ptr. Actual instance owned by GoogleSearchCounter.
49  ::testing::StrictMock<MockSearchMetrics>* mock_search_metrics_;
50};
51
52GoogleSearchCounterTest::GoogleSearchCounterTest()
53    : mock_search_metrics_(NULL) {
54}
55
56GoogleSearchCounterTest::~GoogleSearchCounterTest() {
57}
58
59void GoogleSearchCounterTest::SetUp() {
60  // Keep a weak ptr to MockSearchMetrics so we can run expectations. The
61  // GoogleSearchCounter singleton will own and clean up MockSearchMetrics.
62  mock_search_metrics_ = new ::testing::StrictMock<MockSearchMetrics>;
63  GoogleSearchCounter::GetInstance()->SetSearchMetricsForTesting(
64      mock_search_metrics_);
65}
66
67void GoogleSearchCounterTest::TearDown() {
68  mock_search_metrics_ = NULL;
69}
70
71void GoogleSearchCounterTest::TestGoogleSearch(
72    const std::string& url,
73    bool is_omnibox,
74    GoogleSearchMetrics::AccessPoint expected_metric) {
75  content::LoadCommittedDetails details;
76  scoped_ptr<content::NavigationEntry> entry(
77      content::NavigationEntry::Create());
78  if (is_omnibox) {
79    entry->SetTransitionType(ui::PageTransitionFromInt(
80        ui::PAGE_TRANSITION_GENERATED |
81            ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
82  }
83  entry->SetURL(GURL(url));
84  details.entry = entry.get();
85
86  // Since the internal mocked metrics object is strict, if |expect_metrics| is
87  // false, the absence of this call to ExpectMetricsLogged will be noticed and
88  // cause the test to complain, as expected. We use this behaviour to test
89  // negative test cases (such as bad searches).
90  if (expected_metric != GoogleSearchMetrics::AP_BOUNDARY)
91    ExpectMetricsLogged(expected_metric);
92
93  // For now we don't care about the notification source, but when we start
94  // listening for additional access points, we will have to pass in a valid
95  // controller.
96  GoogleSearchCounter::GetInstance()->Observe(
97      content::NOTIFICATION_NAV_ENTRY_COMMITTED,
98      content::Source<content::NavigationController>(NULL),
99      content::Details<content::LoadCommittedDetails>(&details));
100}
101
102void GoogleSearchCounterTest::ExpectMetricsLogged(
103    GoogleSearchMetrics::AccessPoint ap) {
104  EXPECT_CALL(*mock_search_metrics_, RecordGoogleSearch(ap)).Times(1);
105}
106
107TEST_F(GoogleSearchCounterTest, EmptySearch) {
108  TestGoogleSearch(std::string(), false, GoogleSearchMetrics::AP_BOUNDARY);
109}
110
111TEST_F(GoogleSearchCounterTest, GoodOmniboxSearch) {
112  TestGoogleSearch("http://www.google.com/search?q=something", true,
113                   GoogleSearchMetrics::AP_OMNIBOX);
114}
115
116TEST_F(GoogleSearchCounterTest, BadOmniboxSearch) {
117  TestGoogleSearch("http://www.google.com/search?other=something", true,
118                   GoogleSearchMetrics::AP_BOUNDARY);
119}
120
121TEST_F(GoogleSearchCounterTest, EmptyOmniboxSearch) {
122  TestGoogleSearch(std::string(), true, GoogleSearchMetrics::AP_BOUNDARY);
123}
124
125TEST_F(GoogleSearchCounterTest, GoodOtherSearch) {
126  TestGoogleSearch("http://www.google.com/search?q=something", false,
127                   GoogleSearchMetrics::AP_OTHER);
128}
129
130TEST_F(GoogleSearchCounterTest, BadOtherSearch) {
131  TestGoogleSearch("http://www.google.com/search?other=something", false,
132                   GoogleSearchMetrics::AP_BOUNDARY);
133}
134
135TEST_F(GoogleSearchCounterTest, SearchAppSearch) {
136  TestGoogleSearch("http://www.google.com/webhp?source=search_app#q=something",
137                   false, GoogleSearchMetrics::AP_SEARCH_APP);
138}
139
140TEST_F(GoogleSearchCounterTest, SearchAppStart) {
141  // Starting the search app takes you to this URL, but it should not be
142  // considered an actual search event. Note that this URL is not considered an
143  // actual search because it has no query string parameter ("q").
144  TestGoogleSearch("http://www.google.com/webhp?source=search_app",
145                   false, GoogleSearchMetrics::AP_BOUNDARY);
146}
147
148// TODO(stevet): Add a regression test to protect against the particular
149// bad-flags handling case that asvitkine pointed out.
150