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