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/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/bind.h"
12#include "base/callback.h"
13#include "base/files/file_path.h"
14#include "base/files/file_util.h"
15#include "base/memory/weak_ptr.h"
16#include "base/message_loop/message_loop.h"
17#include "base/path_service.h"
18#include "base/strings/utf_string_conversions.h"
19#include "base/values.h"
20#include "chrome/common/chrome_paths.h"
21#include "chrome/common/url_constants.h"
22#include "components/cloud_devices/common/cloud_devices_urls.h"
23#include "content/public/browser/notification_details.h"
24#include "content/public/browser/notification_source.h"
25#include "content/public/browser/notification_types.h"
26#include "content/public/test/test_browser_thread.h"
27#include "testing/gmock/include/gmock/gmock.h"
28#include "testing/gtest/include/gtest/gtest.h"
29
30using content::BrowserThread;
31using content::WebContents;
32using content::WebUIMessageHandler;
33using testing::A;
34using testing::AtLeast;
35using testing::Eq;
36using testing::HasSubstr;
37using testing::IsNull;
38using testing::NotNull;
39using testing::Return;
40using testing::StrEq;
41using testing::_;
42using ui::ExternalWebDialogUI;
43
44const char kPDFTestFile[] = "printing/cloud_print_unittest.pdf";
45const char kMockJobTitle[] = "Mock Job Title";
46const char kMockPrintTicket[] = "Resolution=300";
47
48
49base::FilePath GetTestDataFileName() {
50  base::FilePath test_data_directory;
51  PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
52  base::FilePath test_file = test_data_directory.AppendASCII(kPDFTestFile);
53  return test_file;
54}
55
56char* GetTestData() {
57  static std::string sTestFileData;
58  if (sTestFileData.empty()) {
59    base::FilePath test_file = GetTestDataFileName();
60    base::ReadFileToString(test_file, &sTestFileData);
61  }
62  return &sTestFileData[0];
63}
64
65MATCHER_P(StringValueEq, expected, "StringValue") {
66  if (expected->Equals(&arg))
67    return true;
68  std::string expected_string, arg_string;
69  expected->GetAsString(&expected_string);
70  arg.GetAsString(&arg_string);
71  *result_listener << "'" << arg_string
72                   << "' (expected '" << expected_string << "')";
73  return false;
74}
75
76namespace internal_cloud_print_helpers {
77
78class MockCloudPrintFlowHandler
79    : public CloudPrintFlowHandler,
80      public base::SupportsWeakPtr<MockCloudPrintFlowHandler> {
81 public:
82  MockCloudPrintFlowHandler(const base::string16& title,
83                            const base::string16& print_ticket,
84                            const std::string& file_type)
85      : CloudPrintFlowHandler(NULL, title, print_ticket, file_type) {}
86  MOCK_METHOD0(DestructorCalled, void());
87  MOCK_METHOD0(RegisterMessages, void());
88  MOCK_METHOD3(Observe,
89               void(int type,
90                    const content::NotificationSource& source,
91                    const content::NotificationDetails& details));
92  MOCK_METHOD1(SetDialogDelegate,
93               void(CloudPrintWebDialogDelegate* delegate));
94  MOCK_METHOD0(CreateCloudPrintDataSender,
95               scoped_refptr<CloudPrintDataSender>());
96};
97
98class MockCloudPrintWebDialogDelegate : public CloudPrintWebDialogDelegate {
99 public:
100  MOCK_CONST_METHOD0(GetDialogModalType,
101      ui::ModalType());
102  MOCK_CONST_METHOD0(GetDialogTitle,
103      base::string16());
104  MOCK_CONST_METHOD0(GetDialogContentURL,
105      GURL());
106  MOCK_CONST_METHOD1(GetWebUIMessageHandlers,
107      void(std::vector<WebUIMessageHandler*>* handlers));
108  MOCK_CONST_METHOD1(GetDialogSize,
109      void(gfx::Size* size));
110  MOCK_CONST_METHOD0(GetDialogArgs,
111      std::string());
112  MOCK_METHOD1(OnDialogClosed,
113      void(const std::string& json_retval));
114  MOCK_METHOD2(OnCloseContents,
115      void(WebContents* source, bool *out_close_dialog));
116};
117
118}  // namespace internal_cloud_print_helpers
119
120using internal_cloud_print_helpers::CloudPrintDataSenderHelper;
121using internal_cloud_print_helpers::CloudPrintDataSender;
122
123class MockExternalWebDialogUI : public ExternalWebDialogUI {
124 public:
125  MOCK_METHOD1(RenderViewCreated,
126               void(content::RenderViewHost* render_view_host));
127};
128
129class MockCloudPrintDataSenderHelper : public CloudPrintDataSenderHelper {
130 public:
131  // TODO(scottbyer): At some point this probably wants to use a
132  // MockTabContents instead of NULL, and to pre-load it with a bunch
133  // of expects/results.
134  MockCloudPrintDataSenderHelper() : CloudPrintDataSenderHelper(NULL) {}
135  MOCK_METHOD3(CallJavascriptFunction, void(const std::string&,
136                                            const base::Value& arg1,
137                                            const base::Value& arg2));
138};
139
140// Testing for CloudPrintDataSender needs a mock WebUI.
141class CloudPrintDataSenderTest : public testing::Test {
142 public:
143  CloudPrintDataSenderTest()
144      : file_thread_(BrowserThread::FILE, &message_loop_),
145        io_thread_(BrowserThread::IO, &message_loop_) {}
146
147 protected:
148  virtual void SetUp() {
149    mock_helper_.reset(new MockCloudPrintDataSenderHelper);
150  }
151
152  scoped_refptr<CloudPrintDataSender> CreateSender(
153      const base::RefCountedString* data) {
154    return new CloudPrintDataSender(mock_helper_.get(),
155                                    base::ASCIIToUTF16(kMockJobTitle),
156                                    base::ASCIIToUTF16(kMockPrintTicket),
157                                    std::string("application/pdf"),
158                                    data);
159  }
160
161  scoped_refptr<CloudPrintDataSender> print_data_sender_;
162  scoped_ptr<MockCloudPrintDataSenderHelper> mock_helper_;
163
164  base::MessageLoop message_loop_;
165  content::TestBrowserThread file_thread_;
166  content::TestBrowserThread io_thread_;
167};
168
169TEST_F(CloudPrintDataSenderTest, CanSend) {
170  base::StringValue mock_job_title(kMockJobTitle);
171  EXPECT_CALL(*mock_helper_,
172              CallJavascriptFunction(_, _, StringValueEq(&mock_job_title))).
173      WillOnce(Return());
174
175  std::string data("test_data");
176  scoped_refptr<CloudPrintDataSender> print_data_sender(
177      CreateSender(base::RefCountedString::TakeString(&data)));
178  base::FilePath test_data_file_name = GetTestDataFileName();
179  BrowserThread::PostTask(
180      BrowserThread::IO, FROM_HERE,
181      base::Bind(&CloudPrintDataSender::SendPrintData, print_data_sender));
182  base::MessageLoop::current()->RunUntilIdle();
183}
184
185TEST_F(CloudPrintDataSenderTest, NoData) {
186  EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, _)).Times(0);
187
188  scoped_refptr<CloudPrintDataSender> print_data_sender(CreateSender(NULL));
189  base::FilePath test_data_file_name = GetTestDataFileName();
190  BrowserThread::PostTask(
191      BrowserThread::IO, FROM_HERE,
192      base::Bind(&CloudPrintDataSender::SendPrintData, print_data_sender));
193  base::MessageLoop::current()->RunUntilIdle();
194}
195
196TEST_F(CloudPrintDataSenderTest, EmptyData) {
197  EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, _)).Times(0);
198
199  std::string data;
200  scoped_refptr<CloudPrintDataSender> print_data_sender(
201      CreateSender(base::RefCountedString::TakeString(&data)));
202  base::FilePath test_data_file_name = GetTestDataFileName();
203  BrowserThread::PostTask(
204      BrowserThread::IO, FROM_HERE,
205      base::Bind(&CloudPrintDataSender::SendPrintData, print_data_sender));
206  base::MessageLoop::current()->RunUntilIdle();
207}
208
209// Testing for CloudPrintFlowHandler needs a mock
210// CloudPrintWebDialogDelegate, mock CloudPrintDataSender, and a mock
211// WebUI.
212
213// Testing for CloudPrintWebDialogDelegate needs a mock
214// CloudPrintFlowHandler.
215
216using internal_cloud_print_helpers::MockCloudPrintFlowHandler;
217using internal_cloud_print_helpers::CloudPrintWebDialogDelegate;
218
219class CloudPrintWebDialogDelegateTest : public testing::Test {
220 public:
221  CloudPrintWebDialogDelegateTest()
222      : ui_thread_(BrowserThread::UI, &message_loop_) {}
223
224 protected:
225  virtual void SetUp() {
226    base::string16 mock_title;
227    base::string16 mock_print_ticket;
228    std::string mock_file_type;
229    MockCloudPrintFlowHandler* handler =
230        new MockCloudPrintFlowHandler(mock_print_ticket, mock_title,
231                                      mock_file_type);
232    mock_flow_handler_ = handler->AsWeakPtr();
233    EXPECT_CALL(*mock_flow_handler_.get(), SetDialogDelegate(_));
234    EXPECT_CALL(*mock_flow_handler_.get(), SetDialogDelegate(NULL));
235    delegate_.reset(new CloudPrintWebDialogDelegate(mock_flow_handler_.get(),
236                                                    std::string()));
237  }
238
239  virtual void TearDown() {
240    delegate_.reset();
241    if (mock_flow_handler_.get())
242      delete mock_flow_handler_.get();
243  }
244
245  base::MessageLoopForUI message_loop_;
246  content::TestBrowserThread ui_thread_;
247  base::WeakPtr<MockCloudPrintFlowHandler> mock_flow_handler_;
248  scoped_ptr<CloudPrintWebDialogDelegate> delegate_;
249};
250
251TEST_F(CloudPrintWebDialogDelegateTest, BasicChecks) {
252  EXPECT_THAT(delegate_->GetDialogContentURL().spec(),
253              StrEq(chrome::kChromeUICloudPrintResourcesURL));
254  EXPECT_TRUE(delegate_->GetDialogTitle().empty());
255
256  bool close_dialog = false;
257  delegate_->OnCloseContents(NULL, &close_dialog);
258  EXPECT_TRUE(close_dialog);
259}
260
261TEST_F(CloudPrintWebDialogDelegateTest, OwnedFlowDestroyed) {
262  delegate_.reset();
263  EXPECT_THAT(mock_flow_handler_.get(), IsNull());
264}
265
266TEST_F(CloudPrintWebDialogDelegateTest, UnownedFlowLetGo) {
267  std::vector<WebUIMessageHandler*> handlers;
268  delegate_->GetWebUIMessageHandlers(&handlers);
269  delegate_.reset();
270  EXPECT_THAT(mock_flow_handler_.get(), NotNull());
271}
272
273// Testing for ExternalWebDialogUI needs a mock WebContents and mock
274// CloudPrintWebDialogDelegate (attached to the mock web_contents).
275
276// Testing for PrintDialogCloud needs a mock Browser.
277