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 30 using content::BrowserThread; 31 using content::WebContents; 32 using content::WebUIMessageHandler; 33 using testing::A; 34 using testing::AtLeast; 35 using testing::Eq; 36 using testing::HasSubstr; 37 using testing::IsNull; 38 using testing::NotNull; 39 using testing::Return; 40 using testing::StrEq; 41 using testing::_; 42 using ui::ExternalWebDialogUI; 43 44 const char kPDFTestFile[] = "printing/cloud_print_unittest.pdf"; 45 const char kMockJobTitle[] = "Mock Job Title"; 46 const char kMockPrintTicket[] = "Resolution=300"; 47 48 49 base::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 56 char* 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 65 MATCHER_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 76 namespace internal_cloud_print_helpers { 77 78 class 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 98 class 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 120 using internal_cloud_print_helpers::CloudPrintDataSenderHelper; 121 using internal_cloud_print_helpers::CloudPrintDataSender; 122 123 class MockExternalWebDialogUI : public ExternalWebDialogUI { 124 public: 125 MOCK_METHOD1(RenderViewCreated, 126 void(content::RenderViewHost* render_view_host)); 127 }; 128 129 class 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. 141 class 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 169 TEST_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 185 TEST_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 196 TEST_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 216 using internal_cloud_print_helpers::MockCloudPrintFlowHandler; 217 using internal_cloud_print_helpers::CloudPrintWebDialogDelegate; 218 219 class 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 251 TEST_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 261 TEST_F(CloudPrintWebDialogDelegateTest, OwnedFlowDestroyed) { 262 delegate_.reset(); 263 EXPECT_THAT(mock_flow_handler_.get(), IsNull()); 264 } 265 266 TEST_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