// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/printing/print_dialog_cloud.h" #include "chrome/browser/printing/print_dialog_cloud_internal.h" #include #include #include "base/file_path.h" #include "base/file_util.h" #include "base/memory/weak_ptr.h" #include "base/path_service.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/printing/cloud_print/cloud_print_url.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/url_constants.h" #include "chrome/test/testing_profile.h" #include "content/browser/browser_thread.h" #include "content/common/notification_details.h" #include "content/common/notification_source.h" #include "content/common/notification_type.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using testing::A; using testing::AtLeast; using testing::Eq; using testing::HasSubstr; using testing::IsNull; using testing::NotNull; using testing::Return; using testing::StrEq; using testing::_; static const char* const kPDFTestFile = "printing/cloud_print_unittest.pdf"; static const char* const kEmptyPDFTestFile = "printing/cloud_print_emptytest.pdf"; static const char* const kMockJobTitle = "Mock Job Title"; FilePath GetTestDataFileName() { FilePath test_data_directory; PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory); FilePath test_file = test_data_directory.AppendASCII(kPDFTestFile); return test_file; } FilePath GetEmptyDataFileName() { FilePath test_data_directory; PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory); FilePath test_file = test_data_directory.AppendASCII(kEmptyPDFTestFile); return test_file; } char* GetTestData() { static std::string sTestFileData; if (sTestFileData.empty()) { FilePath test_file = GetTestDataFileName(); file_util::ReadFileToString(test_file, &sTestFileData); } return &sTestFileData[0]; } MATCHER_P(StringValueEq, expected, "StringValue") { if (expected->Equals(&arg)) return true; std::string expected_string, arg_string; expected->GetAsString(&expected_string); arg.GetAsString(&arg_string); *result_listener << "'" << arg_string << "' (expected '" << expected_string << "')"; return false; } namespace internal_cloud_print_helpers { class MockCloudPrintFlowHandler : public CloudPrintFlowHandler, public base::SupportsWeakPtr { public: explicit MockCloudPrintFlowHandler(const FilePath& path, const string16& title, const std::string& file_type) : CloudPrintFlowHandler(path, title, file_type) {} MOCK_METHOD0(DestructorCalled, void()); MOCK_METHOD0(RegisterMessages, void()); MOCK_METHOD3(Observe, void(NotificationType type, const NotificationSource& source, const NotificationDetails& details)); MOCK_METHOD1(SetDialogDelegate, void(CloudPrintHtmlDialogDelegate* delegate)); MOCK_METHOD0(CreateCloudPrintDataSender, scoped_refptr()); }; class MockCloudPrintHtmlDialogDelegate : public CloudPrintHtmlDialogDelegate { public: MOCK_CONST_METHOD0(IsDialogModal, bool()); MOCK_CONST_METHOD0(GetDialogTitle, std::wstring()); MOCK_CONST_METHOD0(GetDialogContentURL, GURL()); MOCK_CONST_METHOD1(GetWebUIMessageHandlers, void(std::vector* handlers)); MOCK_CONST_METHOD1(GetDialogSize, void(gfx::Size* size)); MOCK_CONST_METHOD0(GetDialogArgs, std::string()); MOCK_METHOD1(OnDialogClosed, void(const std::string& json_retval)); MOCK_METHOD2(OnCloseContents, void(TabContents* source, bool *out_close_dialog)); }; } // namespace internal_cloud_print_helpers using internal_cloud_print_helpers::CloudPrintDataSenderHelper; using internal_cloud_print_helpers::CloudPrintDataSender; class MockExternalHtmlDialogUI : public ExternalHtmlDialogUI { public: MOCK_METHOD1(RenderViewCreated, void(RenderViewHost* render_view_host)); }; class MockCloudPrintDataSenderHelper : public CloudPrintDataSenderHelper { public: // TODO(scottbyer): At some point this probably wants to use a // MockTabContents instead of NULL, and to pre-load it with a bunch // of expects/results. MockCloudPrintDataSenderHelper() : CloudPrintDataSenderHelper(NULL) {} MOCK_METHOD1(CallJavascriptFunction, void(const std::wstring&)); MOCK_METHOD2(CallJavascriptFunction, void(const std::wstring&, const Value& arg1)); MOCK_METHOD3(CallJavascriptFunction, void(const std::wstring&, const Value& arg1, const Value& arg2)); }; class CloudPrintURLTest : public testing::Test { public: CloudPrintURLTest() {} protected: virtual void SetUp() { profile_.reset(new TestingProfile()); } scoped_ptr profile_; }; TEST_F(CloudPrintURLTest, CheckDefaultURLs) { std::string service_url = CloudPrintURL(profile_.get()). GetCloudPrintServiceURL().spec(); EXPECT_THAT(service_url, HasSubstr("www.google.com")); EXPECT_THAT(service_url, HasSubstr("cloudprint")); std::string dialog_url = CloudPrintURL(profile_.get()). GetCloudPrintServiceDialogURL().spec(); EXPECT_THAT(dialog_url, HasSubstr("www.google.com")); EXPECT_THAT(dialog_url, HasSubstr("/cloudprint/")); EXPECT_THAT(dialog_url, HasSubstr("/client/")); EXPECT_THAT(dialog_url, Not(HasSubstr("cloudprint/cloudprint"))); EXPECT_THAT(dialog_url, HasSubstr("/dialog.html")); // Repeat to make sure there isn't a transient glitch. dialog_url = CloudPrintURL(profile_.get()). GetCloudPrintServiceDialogURL().spec(); EXPECT_THAT(dialog_url, HasSubstr("www.google.com")); EXPECT_THAT(dialog_url, HasSubstr("/cloudprint/")); EXPECT_THAT(dialog_url, HasSubstr("/client/")); EXPECT_THAT(dialog_url, Not(HasSubstr("cloudprint/cloudprint"))); EXPECT_THAT(dialog_url, HasSubstr("/dialog.html")); std::string manage_url = CloudPrintURL(profile_.get()). GetCloudPrintServiceManageURL().spec(); EXPECT_THAT(manage_url, HasSubstr("www.google.com")); EXPECT_THAT(manage_url, HasSubstr("/cloudprint/")); EXPECT_THAT(manage_url, Not(HasSubstr("/client/"))); EXPECT_THAT(manage_url, Not(HasSubstr("cloudprint/cloudprint"))); EXPECT_THAT(manage_url, HasSubstr("/manage")); GURL learn_more_url = CloudPrintURL::GetCloudPrintLearnMoreURL(); std::string learn_more_path = learn_more_url.spec(); EXPECT_THAT(learn_more_path, HasSubstr("www.google.com")); EXPECT_THAT(learn_more_path, HasSubstr("/support/")); EXPECT_THAT(learn_more_path, HasSubstr("/cloudprint")); EXPECT_TRUE(learn_more_url.has_path()); EXPECT_FALSE(learn_more_url.has_query()); GURL test_page_url = CloudPrintURL::GetCloudPrintTestPageURL(); std::string test_page_path = test_page_url.spec(); EXPECT_THAT(test_page_path, HasSubstr("www.google.com")); EXPECT_THAT(test_page_path, HasSubstr("/landing/")); EXPECT_THAT(test_page_path, HasSubstr("/cloudprint/")); EXPECT_TRUE(test_page_url.has_path()); EXPECT_TRUE(test_page_url.has_query()); } // Testing for CloudPrintDataSender needs a mock WebUI. class CloudPrintDataSenderTest : public testing::Test { public: CloudPrintDataSenderTest() : file_thread_(BrowserThread::FILE, &message_loop_), io_thread_(BrowserThread::IO, &message_loop_) {} protected: virtual void SetUp() { string16 mock_job_title(ASCIIToUTF16(kMockJobTitle)); mock_helper_.reset(new MockCloudPrintDataSenderHelper); print_data_sender_ = new CloudPrintDataSender(mock_helper_.get(), mock_job_title, std::string("application/pdf")); } scoped_refptr print_data_sender_; scoped_ptr mock_helper_; MessageLoop message_loop_; BrowserThread file_thread_; BrowserThread io_thread_; }; // TODO(scottbyer): DISABLED until the binary test file can get // checked in separate from the patch. TEST_F(CloudPrintDataSenderTest, CanSend) { StringValue mock_job_title(kMockJobTitle); EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, StringValueEq(&mock_job_title))). WillOnce(Return()); FilePath test_data_file_name = GetTestDataFileName(); BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, NewRunnableMethod( print_data_sender_.get(), &CloudPrintDataSender::ReadPrintDataFile, test_data_file_name)); MessageLoop::current()->RunAllPending(); } TEST_F(CloudPrintDataSenderTest, BadFile) { EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, _)).Times(0); #if defined(OS_WIN) FilePath bad_data_file_name(L"/some/file/that/isnot/there"); #else FilePath bad_data_file_name("/some/file/that/isnot/there"); #endif BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, NewRunnableMethod( print_data_sender_.get(), &CloudPrintDataSender::ReadPrintDataFile, bad_data_file_name)); MessageLoop::current()->RunAllPending(); } TEST_F(CloudPrintDataSenderTest, EmptyFile) { EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, _)).Times(0); FilePath empty_data_file_name = GetEmptyDataFileName(); BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, NewRunnableMethod( print_data_sender_.get(), &CloudPrintDataSender::ReadPrintDataFile, empty_data_file_name)); MessageLoop::current()->RunAllPending(); } // Testing for CloudPrintFlowHandler needs a mock // CloudPrintHtmlDialogDelegate, mock CloudPrintDataSender, and a mock // WebUI. // Testing for CloudPrintHtmlDialogDelegate needs a mock // CloudPrintFlowHandler. using internal_cloud_print_helpers::MockCloudPrintFlowHandler; using internal_cloud_print_helpers::CloudPrintHtmlDialogDelegate; class CloudPrintHtmlDialogDelegateTest : public testing::Test { public: CloudPrintHtmlDialogDelegateTest() : ui_thread_(BrowserThread::UI, &message_loop_) {} protected: virtual void SetUp() { FilePath mock_path; string16 mock_title; std::string mock_file_type; MockCloudPrintFlowHandler* handler = new MockCloudPrintFlowHandler(mock_path, mock_title, mock_file_type); mock_flow_handler_ = handler->AsWeakPtr(); EXPECT_CALL(*mock_flow_handler_.get(), SetDialogDelegate(_)); EXPECT_CALL(*mock_flow_handler_.get(), SetDialogDelegate(NULL)); delegate_.reset(new CloudPrintHtmlDialogDelegate( mock_flow_handler_.get(), 100, 100, std::string(), true)); } virtual void TearDown() { delegate_.reset(); if (mock_flow_handler_) delete mock_flow_handler_.get(); } MessageLoopForUI message_loop_; BrowserThread ui_thread_; base::WeakPtr mock_flow_handler_; scoped_ptr delegate_; }; TEST_F(CloudPrintHtmlDialogDelegateTest, BasicChecks) { EXPECT_TRUE(delegate_->IsDialogModal()); EXPECT_THAT(delegate_->GetDialogContentURL().spec(), StrEq(chrome::kCloudPrintResourcesURL)); EXPECT_TRUE(delegate_->GetDialogTitle().empty()); bool close_dialog = false; delegate_->OnCloseContents(NULL, &close_dialog); EXPECT_TRUE(close_dialog); } TEST_F(CloudPrintHtmlDialogDelegateTest, OwnedFlowDestroyed) { delegate_.reset(); EXPECT_THAT(mock_flow_handler_.get(), IsNull()); } TEST_F(CloudPrintHtmlDialogDelegateTest, UnownedFlowLetGo) { std::vector handlers; delegate_->GetWebUIMessageHandlers(&handlers); delegate_.reset(); EXPECT_THAT(mock_flow_handler_.get(), NotNull()); } // Testing for ExternalHtmlDialogUI needs a mock TabContents, mock // CloudPrintHtmlDialogDelegate (provided through the mock // tab_contents) // Testing for PrintDialogCloud needs a mock Browser.