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 "chrome/browser/printing/print_dialog_cloud.h"
6#include "chrome/browser/printing/print_dialog_cloud_internal.h"
7
8#include <string>
9#include <vector>
10
11#include "base/file_path.h"
12#include "base/file_util.h"
13#include "base/memory/weak_ptr.h"
14#include "base/path_service.h"
15#include "base/string_util.h"
16#include "base/utf_string_conversions.h"
17#include "base/values.h"
18#include "chrome/browser/printing/cloud_print/cloud_print_url.h"
19#include "chrome/common/chrome_paths.h"
20#include "chrome/common/url_constants.h"
21#include "chrome/test/testing_profile.h"
22#include "content/browser/browser_thread.h"
23#include "content/common/notification_details.h"
24#include "content/common/notification_source.h"
25#include "content/common/notification_type.h"
26#include "testing/gmock/include/gmock/gmock.h"
27#include "testing/gtest/include/gtest/gtest.h"
28
29using testing::A;
30using testing::AtLeast;
31using testing::Eq;
32using testing::HasSubstr;
33using testing::IsNull;
34using testing::NotNull;
35using testing::Return;
36using testing::StrEq;
37using testing::_;
38
39static const char* const kPDFTestFile = "printing/cloud_print_unittest.pdf";
40static const char* const kEmptyPDFTestFile =
41    "printing/cloud_print_emptytest.pdf";
42static const char* const kMockJobTitle = "Mock Job Title";
43
44FilePath GetTestDataFileName() {
45  FilePath test_data_directory;
46  PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
47  FilePath test_file = test_data_directory.AppendASCII(kPDFTestFile);
48  return test_file;
49}
50
51FilePath GetEmptyDataFileName() {
52  FilePath test_data_directory;
53  PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
54  FilePath test_file = test_data_directory.AppendASCII(kEmptyPDFTestFile);
55  return test_file;
56}
57
58char* GetTestData() {
59  static std::string sTestFileData;
60  if (sTestFileData.empty()) {
61    FilePath test_file = GetTestDataFileName();
62    file_util::ReadFileToString(test_file, &sTestFileData);
63  }
64  return &sTestFileData[0];
65}
66
67MATCHER_P(StringValueEq, expected, "StringValue") {
68  if (expected->Equals(&arg))
69    return true;
70  std::string expected_string, arg_string;
71  expected->GetAsString(&expected_string);
72  arg.GetAsString(&arg_string);
73  *result_listener << "'" << arg_string
74                   << "' (expected '" << expected_string << "')";
75  return false;
76}
77
78namespace internal_cloud_print_helpers {
79
80class MockCloudPrintFlowHandler
81    : public CloudPrintFlowHandler,
82      public base::SupportsWeakPtr<MockCloudPrintFlowHandler> {
83 public:
84  explicit MockCloudPrintFlowHandler(const FilePath& path,
85                                     const string16& title,
86                                     const std::string& file_type)
87      : CloudPrintFlowHandler(path, title, file_type) {}
88  MOCK_METHOD0(DestructorCalled, void());
89  MOCK_METHOD0(RegisterMessages, void());
90  MOCK_METHOD3(Observe,
91               void(NotificationType type,
92                    const NotificationSource& source,
93                    const NotificationDetails& details));
94  MOCK_METHOD1(SetDialogDelegate,
95               void(CloudPrintHtmlDialogDelegate* delegate));
96  MOCK_METHOD0(CreateCloudPrintDataSender,
97               scoped_refptr<CloudPrintDataSender>());
98};
99
100class MockCloudPrintHtmlDialogDelegate : public CloudPrintHtmlDialogDelegate {
101 public:
102  MOCK_CONST_METHOD0(IsDialogModal,
103      bool());
104  MOCK_CONST_METHOD0(GetDialogTitle,
105      std::wstring());
106  MOCK_CONST_METHOD0(GetDialogContentURL,
107      GURL());
108  MOCK_CONST_METHOD1(GetWebUIMessageHandlers,
109      void(std::vector<WebUIMessageHandler*>* handlers));
110  MOCK_CONST_METHOD1(GetDialogSize,
111      void(gfx::Size* size));
112  MOCK_CONST_METHOD0(GetDialogArgs,
113      std::string());
114  MOCK_METHOD1(OnDialogClosed,
115      void(const std::string& json_retval));
116  MOCK_METHOD2(OnCloseContents,
117      void(TabContents* source, bool *out_close_dialog));
118};
119
120}  // namespace internal_cloud_print_helpers
121
122using internal_cloud_print_helpers::CloudPrintDataSenderHelper;
123using internal_cloud_print_helpers::CloudPrintDataSender;
124
125class MockExternalHtmlDialogUI : public ExternalHtmlDialogUI {
126 public:
127  MOCK_METHOD1(RenderViewCreated,
128      void(RenderViewHost* render_view_host));
129};
130
131class MockCloudPrintDataSenderHelper : public CloudPrintDataSenderHelper {
132 public:
133  // TODO(scottbyer): At some point this probably wants to use a
134  // MockTabContents instead of NULL, and to pre-load it with a bunch
135  // of expects/results.
136  MockCloudPrintDataSenderHelper() : CloudPrintDataSenderHelper(NULL) {}
137  MOCK_METHOD1(CallJavascriptFunction, void(const std::wstring&));
138  MOCK_METHOD2(CallJavascriptFunction, void(const std::wstring&,
139                                            const Value& arg1));
140  MOCK_METHOD3(CallJavascriptFunction, void(const std::wstring&,
141                                            const Value& arg1,
142                                            const Value& arg2));
143};
144
145class CloudPrintURLTest : public testing::Test {
146 public:
147  CloudPrintURLTest() {}
148
149 protected:
150  virtual void SetUp() {
151    profile_.reset(new TestingProfile());
152  }
153
154  scoped_ptr<Profile> profile_;
155};
156
157TEST_F(CloudPrintURLTest, CheckDefaultURLs) {
158  std::string service_url =
159      CloudPrintURL(profile_.get()).
160      GetCloudPrintServiceURL().spec();
161  EXPECT_THAT(service_url, HasSubstr("www.google.com"));
162  EXPECT_THAT(service_url, HasSubstr("cloudprint"));
163
164  std::string dialog_url =
165      CloudPrintURL(profile_.get()).
166      GetCloudPrintServiceDialogURL().spec();
167  EXPECT_THAT(dialog_url, HasSubstr("www.google.com"));
168  EXPECT_THAT(dialog_url, HasSubstr("/cloudprint/"));
169  EXPECT_THAT(dialog_url, HasSubstr("/client/"));
170  EXPECT_THAT(dialog_url, Not(HasSubstr("cloudprint/cloudprint")));
171  EXPECT_THAT(dialog_url, HasSubstr("/dialog.html"));
172
173  // Repeat to make sure there isn't a transient glitch.
174  dialog_url =
175      CloudPrintURL(profile_.get()).
176      GetCloudPrintServiceDialogURL().spec();
177  EXPECT_THAT(dialog_url, HasSubstr("www.google.com"));
178  EXPECT_THAT(dialog_url, HasSubstr("/cloudprint/"));
179  EXPECT_THAT(dialog_url, HasSubstr("/client/"));
180  EXPECT_THAT(dialog_url, Not(HasSubstr("cloudprint/cloudprint")));
181  EXPECT_THAT(dialog_url, HasSubstr("/dialog.html"));
182
183  std::string manage_url =
184      CloudPrintURL(profile_.get()).
185      GetCloudPrintServiceManageURL().spec();
186  EXPECT_THAT(manage_url, HasSubstr("www.google.com"));
187  EXPECT_THAT(manage_url, HasSubstr("/cloudprint/"));
188  EXPECT_THAT(manage_url, Not(HasSubstr("/client/")));
189  EXPECT_THAT(manage_url, Not(HasSubstr("cloudprint/cloudprint")));
190  EXPECT_THAT(manage_url, HasSubstr("/manage"));
191
192  GURL learn_more_url = CloudPrintURL::GetCloudPrintLearnMoreURL();
193  std::string learn_more_path = learn_more_url.spec();
194  EXPECT_THAT(learn_more_path, HasSubstr("www.google.com"));
195  EXPECT_THAT(learn_more_path, HasSubstr("/support/"));
196  EXPECT_THAT(learn_more_path, HasSubstr("/cloudprint"));
197  EXPECT_TRUE(learn_more_url.has_path());
198  EXPECT_FALSE(learn_more_url.has_query());
199
200  GURL test_page_url = CloudPrintURL::GetCloudPrintTestPageURL();
201  std::string test_page_path = test_page_url.spec();
202  EXPECT_THAT(test_page_path, HasSubstr("www.google.com"));
203  EXPECT_THAT(test_page_path, HasSubstr("/landing/"));
204  EXPECT_THAT(test_page_path, HasSubstr("/cloudprint/"));
205  EXPECT_TRUE(test_page_url.has_path());
206  EXPECT_TRUE(test_page_url.has_query());
207}
208
209// Testing for CloudPrintDataSender needs a mock WebUI.
210class CloudPrintDataSenderTest : public testing::Test {
211 public:
212  CloudPrintDataSenderTest()
213      : file_thread_(BrowserThread::FILE, &message_loop_),
214        io_thread_(BrowserThread::IO, &message_loop_) {}
215
216 protected:
217  virtual void SetUp() {
218    string16 mock_job_title(ASCIIToUTF16(kMockJobTitle));
219    mock_helper_.reset(new MockCloudPrintDataSenderHelper);
220    print_data_sender_ =
221        new CloudPrintDataSender(mock_helper_.get(),
222                                 mock_job_title,
223                                 std::string("application/pdf"));
224  }
225
226  scoped_refptr<CloudPrintDataSender> print_data_sender_;
227  scoped_ptr<MockCloudPrintDataSenderHelper> mock_helper_;
228
229  MessageLoop message_loop_;
230  BrowserThread file_thread_;
231  BrowserThread io_thread_;
232};
233
234// TODO(scottbyer): DISABLED until the binary test file can get
235// checked in separate from the patch.
236TEST_F(CloudPrintDataSenderTest, CanSend) {
237  StringValue mock_job_title(kMockJobTitle);
238  EXPECT_CALL(*mock_helper_,
239              CallJavascriptFunction(_, _, StringValueEq(&mock_job_title))).
240      WillOnce(Return());
241
242  FilePath test_data_file_name = GetTestDataFileName();
243  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
244                          NewRunnableMethod(
245                              print_data_sender_.get(),
246                              &CloudPrintDataSender::ReadPrintDataFile,
247                              test_data_file_name));
248  MessageLoop::current()->RunAllPending();
249}
250
251TEST_F(CloudPrintDataSenderTest, BadFile) {
252  EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, _)).Times(0);
253
254#if defined(OS_WIN)
255  FilePath bad_data_file_name(L"/some/file/that/isnot/there");
256#else
257  FilePath bad_data_file_name("/some/file/that/isnot/there");
258#endif
259  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
260                          NewRunnableMethod(
261                              print_data_sender_.get(),
262                              &CloudPrintDataSender::ReadPrintDataFile,
263                             bad_data_file_name));
264  MessageLoop::current()->RunAllPending();
265}
266
267TEST_F(CloudPrintDataSenderTest, EmptyFile) {
268  EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, _)).Times(0);
269
270  FilePath empty_data_file_name = GetEmptyDataFileName();
271  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
272                          NewRunnableMethod(
273                              print_data_sender_.get(),
274                              &CloudPrintDataSender::ReadPrintDataFile,
275                              empty_data_file_name));
276  MessageLoop::current()->RunAllPending();
277}
278
279// Testing for CloudPrintFlowHandler needs a mock
280// CloudPrintHtmlDialogDelegate, mock CloudPrintDataSender, and a mock
281// WebUI.
282
283// Testing for CloudPrintHtmlDialogDelegate needs a mock
284// CloudPrintFlowHandler.
285
286using internal_cloud_print_helpers::MockCloudPrintFlowHandler;
287using internal_cloud_print_helpers::CloudPrintHtmlDialogDelegate;
288
289class CloudPrintHtmlDialogDelegateTest : public testing::Test {
290 public:
291  CloudPrintHtmlDialogDelegateTest()
292      : ui_thread_(BrowserThread::UI, &message_loop_) {}
293
294 protected:
295  virtual void SetUp() {
296    FilePath mock_path;
297    string16 mock_title;
298    std::string mock_file_type;
299    MockCloudPrintFlowHandler* handler =
300        new MockCloudPrintFlowHandler(mock_path, mock_title, mock_file_type);
301    mock_flow_handler_ = handler->AsWeakPtr();
302    EXPECT_CALL(*mock_flow_handler_.get(), SetDialogDelegate(_));
303    EXPECT_CALL(*mock_flow_handler_.get(), SetDialogDelegate(NULL));
304    delegate_.reset(new CloudPrintHtmlDialogDelegate(
305        mock_flow_handler_.get(), 100, 100, std::string(), true));
306  }
307
308  virtual void TearDown() {
309    delegate_.reset();
310    if (mock_flow_handler_)
311      delete mock_flow_handler_.get();
312  }
313
314  MessageLoopForUI message_loop_;
315  BrowserThread ui_thread_;
316  base::WeakPtr<MockCloudPrintFlowHandler> mock_flow_handler_;
317  scoped_ptr<CloudPrintHtmlDialogDelegate> delegate_;
318};
319
320TEST_F(CloudPrintHtmlDialogDelegateTest, BasicChecks) {
321  EXPECT_TRUE(delegate_->IsDialogModal());
322  EXPECT_THAT(delegate_->GetDialogContentURL().spec(),
323              StrEq(chrome::kCloudPrintResourcesURL));
324  EXPECT_TRUE(delegate_->GetDialogTitle().empty());
325
326  bool close_dialog = false;
327  delegate_->OnCloseContents(NULL, &close_dialog);
328  EXPECT_TRUE(close_dialog);
329}
330
331TEST_F(CloudPrintHtmlDialogDelegateTest, OwnedFlowDestroyed) {
332  delegate_.reset();
333  EXPECT_THAT(mock_flow_handler_.get(), IsNull());
334}
335
336TEST_F(CloudPrintHtmlDialogDelegateTest, UnownedFlowLetGo) {
337  std::vector<WebUIMessageHandler*> handlers;
338  delegate_->GetWebUIMessageHandlers(&handlers);
339  delegate_.reset();
340  EXPECT_THAT(mock_flow_handler_.get(), NotNull());
341}
342
343// Testing for ExternalHtmlDialogUI needs a mock TabContents, mock
344// CloudPrintHtmlDialogDelegate (provided through the mock
345// tab_contents)
346
347// Testing for PrintDialogCloud needs a mock Browser.
348