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 <map>
6#include <string>
7
8#include "base/memory/scoped_ptr.h"
9#include "chrome/renderer/extensions/extension_localization_peer.h"
10#include "extensions/common/message_bundle.h"
11#include "ipc/ipc_sender.h"
12#include "ipc/ipc_sync_message.h"
13#include "net/base/net_errors.h"
14#include "net/url_request/redirect_info.h"
15#include "net/url_request/url_request_status.h"
16#include "testing/gmock/include/gmock/gmock.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19using testing::_;
20using testing::DoAll;
21using testing::Invoke;
22using testing::StrEq;
23using testing::Return;
24
25static const char* const kExtensionUrl_1 =
26    "chrome-extension://some_id/popup.css";
27
28static const char* const kExtensionUrl_2 =
29    "chrome-extension://some_id2/popup.css";
30
31static const char* const kExtensionUrl_3 =
32    "chrome-extension://some_id3/popup.css";
33
34void MessageDeleter(IPC::Message* message) {
35  delete message;
36}
37
38class MockIpcMessageSender : public IPC::Sender {
39 public:
40  MockIpcMessageSender() {
41    ON_CALL(*this, Send(_))
42        .WillByDefault(DoAll(Invoke(MessageDeleter), Return(true)));
43  }
44
45  virtual ~MockIpcMessageSender() {}
46
47  MOCK_METHOD1(Send, bool(IPC::Message* message));
48
49 private:
50  DISALLOW_COPY_AND_ASSIGN(MockIpcMessageSender);
51};
52
53class MockRequestPeer : public content::RequestPeer {
54 public:
55  MockRequestPeer() {}
56  virtual ~MockRequestPeer() {}
57
58  MOCK_METHOD2(OnUploadProgress, void(uint64 position, uint64 size));
59  MOCK_METHOD2(OnReceivedRedirect,
60               bool(const net::RedirectInfo& redirect_info,
61                    const content::ResourceResponseInfo& info));
62  MOCK_METHOD1(OnReceivedResponse,
63               void(const content::ResourceResponseInfo& info));
64  MOCK_METHOD2(OnDownloadedData, void(int len, int encoded_data_length));
65  MOCK_METHOD3(OnReceivedData, void(const char* data,
66                                    int data_length,
67                                    int encoded_data_length));
68  MOCK_METHOD6(OnCompletedRequest, void(
69      int error_code,
70      bool was_ignored_by_handler,
71      bool stale_copy_in_cache,
72      const std::string& security_info,
73      const base::TimeTicks& completion_time,
74      int64_t total_transfer_size));
75
76 private:
77  DISALLOW_COPY_AND_ASSIGN(MockRequestPeer);
78};
79
80class ExtensionLocalizationPeerTest : public testing::Test {
81 protected:
82  virtual void SetUp() {
83    sender_.reset(new MockIpcMessageSender());
84    original_peer_.reset(new MockRequestPeer());
85    filter_peer_.reset(
86        ExtensionLocalizationPeer::CreateExtensionLocalizationPeer(
87            original_peer_.get(), sender_.get(), "text/css",
88            GURL(kExtensionUrl_1)));
89  }
90
91  ExtensionLocalizationPeer* CreateExtensionLocalizationPeer(
92      const std::string& mime_type,
93      const GURL& request_url) {
94    return ExtensionLocalizationPeer::CreateExtensionLocalizationPeer(
95        original_peer_.get(), sender_.get(), mime_type, request_url);
96  }
97
98  std::string GetData(ExtensionLocalizationPeer* filter_peer) {
99    EXPECT_TRUE(NULL != filter_peer);
100    return filter_peer->data_;
101  }
102
103  void SetData(ExtensionLocalizationPeer* filter_peer,
104               const std::string& data) {
105    EXPECT_TRUE(NULL != filter_peer);
106    filter_peer->data_ = data;
107  }
108
109  scoped_ptr<MockIpcMessageSender> sender_;
110  scoped_ptr<MockRequestPeer> original_peer_;
111  scoped_ptr<ExtensionLocalizationPeer> filter_peer_;
112};
113
114TEST_F(ExtensionLocalizationPeerTest, CreateWithWrongMimeType) {
115  filter_peer_.reset(
116      CreateExtensionLocalizationPeer("text/html", GURL(kExtensionUrl_1)));
117  EXPECT_TRUE(NULL == filter_peer_.get());
118}
119
120TEST_F(ExtensionLocalizationPeerTest, CreateWithValidInput) {
121  EXPECT_TRUE(NULL != filter_peer_.get());
122}
123
124TEST_F(ExtensionLocalizationPeerTest, OnReceivedData) {
125  EXPECT_TRUE(GetData(filter_peer_.get()).empty());
126
127  const std::string data_chunk("12345");
128  filter_peer_->OnReceivedData(data_chunk.c_str(), data_chunk.length(), -1);
129
130  EXPECT_EQ(data_chunk, GetData(filter_peer_.get()));
131
132  filter_peer_->OnReceivedData(data_chunk.c_str(), data_chunk.length(), -1);
133  EXPECT_EQ(data_chunk + data_chunk, GetData(filter_peer_.get()));
134}
135
136MATCHER_P(IsURLRequestEqual, status, "") { return arg.status() == status; }
137
138TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestBadURLRequestStatus) {
139  // It will self-delete once it exits OnCompletedRequest.
140  ExtensionLocalizationPeer* filter_peer = filter_peer_.release();
141
142  EXPECT_CALL(*original_peer_, OnReceivedResponse(_));
143  EXPECT_CALL(*original_peer_, OnCompletedRequest(
144      net::ERR_ABORTED, false, false, "", base::TimeTicks(), -1));
145
146  filter_peer->OnCompletedRequest(
147      net::ERR_FAILED, false, false, std::string(), base::TimeTicks(), -1);
148}
149
150TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestEmptyData) {
151  // It will self-delete once it exits OnCompletedRequest.
152  ExtensionLocalizationPeer* filter_peer = filter_peer_.release();
153
154  EXPECT_CALL(*original_peer_, OnReceivedData(_, _, _)).Times(0);
155  EXPECT_CALL(*sender_, Send(_)).Times(0);
156
157  EXPECT_CALL(*original_peer_, OnReceivedResponse(_));
158  EXPECT_CALL(*original_peer_, OnCompletedRequest(
159      net::OK, false, false, "", base::TimeTicks(), -1));
160
161  filter_peer->OnCompletedRequest(
162      net::OK, false, false, std::string(), base::TimeTicks(), -1);
163}
164
165TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestNoCatalogs) {
166  // It will self-delete once it exits OnCompletedRequest.
167  ExtensionLocalizationPeer* filter_peer = filter_peer_.release();
168
169  SetData(filter_peer, "some text");
170
171  EXPECT_CALL(*sender_, Send(_));
172
173  std::string data = GetData(filter_peer);
174  EXPECT_CALL(*original_peer_,
175              OnReceivedData(StrEq(data.data()), data.length(), -1)).Times(2);
176
177  EXPECT_CALL(*original_peer_, OnReceivedResponse(_)).Times(2);
178  EXPECT_CALL(*original_peer_, OnCompletedRequest(
179      net::OK, false, false, "", base::TimeTicks(), -1)).Times(2);
180
181  filter_peer->OnCompletedRequest(
182      net::OK, false, false, std::string(), base::TimeTicks(), -1);
183
184  // Test if Send gets called again (it shouldn't be) when first call returned
185  // an empty dictionary.
186  filter_peer =
187      CreateExtensionLocalizationPeer("text/css", GURL(kExtensionUrl_1));
188  SetData(filter_peer, "some text");
189  filter_peer->OnCompletedRequest(
190      net::OK, false, false, std::string(), base::TimeTicks(), -1);
191}
192
193TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestWithCatalogs) {
194  // It will self-delete once it exits OnCompletedRequest.
195  ExtensionLocalizationPeer* filter_peer =
196      CreateExtensionLocalizationPeer("text/css", GURL(kExtensionUrl_2));
197
198  extensions::L10nMessagesMap messages;
199  messages.insert(std::make_pair("text", "new text"));
200  extensions::ExtensionToL10nMessagesMap& l10n_messages_map =
201      *extensions::GetExtensionToL10nMessagesMap();
202  l10n_messages_map["some_id2"] = messages;
203
204  SetData(filter_peer, "some __MSG_text__");
205
206  // We already have messages in memory, Send will be skipped.
207  EXPECT_CALL(*sender_, Send(_)).Times(0);
208
209  // __MSG_text__ gets replaced with "new text".
210  std::string data("some new text");
211  EXPECT_CALL(*original_peer_,
212              OnReceivedData(StrEq(data.data()), data.length(), -1));
213
214  EXPECT_CALL(*original_peer_, OnReceivedResponse(_));
215  EXPECT_CALL(*original_peer_, OnCompletedRequest(
216      net::OK, false, false, "", base::TimeTicks(), -1));
217
218  filter_peer->OnCompletedRequest(
219      net::OK, false, false, std::string(), base::TimeTicks(), -1);
220}
221
222TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestReplaceMessagesFails) {
223  // It will self-delete once it exits OnCompletedRequest.
224  ExtensionLocalizationPeer* filter_peer =
225      CreateExtensionLocalizationPeer("text/css", GURL(kExtensionUrl_3));
226
227  extensions::L10nMessagesMap messages;
228  messages.insert(std::make_pair("text", "new text"));
229  extensions::ExtensionToL10nMessagesMap& l10n_messages_map =
230      *extensions::GetExtensionToL10nMessagesMap();
231  l10n_messages_map["some_id3"] = messages;
232
233  std::string message("some __MSG_missing_message__");
234  SetData(filter_peer, message);
235
236  // We already have messages in memory, Send will be skipped.
237  EXPECT_CALL(*sender_, Send(_)).Times(0);
238
239  // __MSG_missing_message__ is missing, so message stays the same.
240  EXPECT_CALL(*original_peer_,
241              OnReceivedData(StrEq(message.data()), message.length(), -1));
242
243  EXPECT_CALL(*original_peer_, OnReceivedResponse(_));
244  EXPECT_CALL(*original_peer_, OnCompletedRequest(
245      net::OK, false, false, "", base::TimeTicks(), -1));
246
247  filter_peer->OnCompletedRequest(
248      net::OK, false, false, std::string(), base::TimeTicks(), -1);
249}
250