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