// Copyright (c) 2010 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 #include #include "app/win_util.h" #include "chrome_frame/test/test_server.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock_mutant.h" #include "chrome_frame/urlmon_url_request.h" #include "chrome_frame/urlmon_url_request_private.h" #include "chrome_frame/test/chrome_frame_test_utils.h" #include "chrome_frame/test/http_server.h" #include "chrome_frame/test/test_with_web_server.h" using testing::CreateFunctor; const int kChromeFrameLongNavigationTimeoutInSeconds = 10; static void AppendToStream(IStream* s, void* buffer, ULONG cb) { ULONG bytes_written; LARGE_INTEGER current_pos; LARGE_INTEGER zero = {0}; // Remember current position. ASSERT_HRESULT_SUCCEEDED(s->Seek(zero, STREAM_SEEK_CUR, reinterpret_cast(¤t_pos))); // Seek to the end. ASSERT_HRESULT_SUCCEEDED(s->Seek(zero, STREAM_SEEK_END, NULL)); ASSERT_HRESULT_SUCCEEDED(s->Write(buffer, cb, &bytes_written)); ASSERT_EQ(cb, bytes_written); // Seek to original position. ASSERT_HRESULT_SUCCEEDED(s->Seek(current_pos, STREAM_SEEK_SET, NULL)); } class MockUrlDelegate : public PluginUrlRequestDelegate { public: MOCK_METHOD7(OnResponseStarted, void(int request_id, const char* mime_type, const char* headers, int size, base::Time last_modified, const std::string& redirect_url, int redirect_status)); MOCK_METHOD2(OnReadComplete, void(int request_id, const std::string& data)); MOCK_METHOD2(OnResponseEnd, void(int request_id, const URLRequestStatus& status)); MOCK_METHOD4(OnCookiesRetrieved, void(bool success, const GURL& url, const std::string& cookie, int cookie_id)); static bool ImplementsThreadSafeReferenceCounting() { return false; } void AddRef() {} void Release() {} void PostponeReadRequest(chrome_frame_test::TimedMsgLoop* loop, UrlmonUrlRequest* request, int bytes_to_read) { loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(this, &MockUrlDelegate::RequestRead, request, bytes_to_read), 0); } private: void RequestRead(UrlmonUrlRequest* request, int bytes_to_read) { request->Read(bytes_to_read); } }; // Simplest UrlmonUrlRequest. Retrieve a file from local web server. TEST(UrlmonUrlRequestTest, Simple1) { MockUrlDelegate mock; chrome_frame_test::TimedMsgLoop loop; testing::StrictMock mock_server(1337, L"127.0.0.1", chrome_frame_test::GetTestDataFolder()); mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE)); win_util::ScopedCOMInitializer init_com; CComObjectStackEx request; request.AddRef(); request.Initialize(&mock, 1, // request_id WideToUTF8(mock_server.Resolve(L"chrome_frame_window_open.html")), "get", "", // referrer "", // extra request NULL, // upload data ResourceType::MAIN_FRAME, // resource type true); // frame busting testing::InSequence s; EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_, testing::_, testing::_, testing::_)) .Times(1) .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs(CreateFunctor( &request, &UrlmonUrlRequest::Read, 512)))); EXPECT_CALL(mock, OnReadComplete(1, testing::Property(&std::string::size, testing::Gt(0u)))) .Times(testing::AtLeast(1)) .WillRepeatedly(testing::InvokeWithoutArgs(CreateFunctor(&mock, &MockUrlDelegate::PostponeReadRequest, &loop, &request, 64))); EXPECT_CALL(mock, OnResponseEnd(1, testing::_)) .Times(1) .WillOnce(QUIT_LOOP_SOON(loop, 2)); request.Start(); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); request.Release(); } // Same as Simple1 except we use the HEAD verb to fetch only the headers // from the server. TEST(UrlmonUrlRequestTest, Head) { MockUrlDelegate mock; chrome_frame_test::TimedMsgLoop loop; // Use SimpleWebServer instead of the python server to support HEAD // requests. test_server::SimpleWebServer server(13337); test_server::SimpleResponse head_response("/head", ""); server.AddResponse(&head_response); win_util::ScopedCOMInitializer init_com; CComObjectStackEx request; request.AddRef(); request.Initialize(&mock, 1, // request_id "http://localhost:13337/head", "head", "", // referrer "", // extra request NULL, // upload data ResourceType::MAIN_FRAME, // resource type true); // frame busting testing::InSequence s; EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_, testing::_, testing::_, testing::_)) .Times(1) .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs(CreateFunctor( &request, &UrlmonUrlRequest::Read, 512)))); // For HEAD requests we don't expect content reads. EXPECT_CALL(mock, OnReadComplete(1, testing::_)).Times(0); EXPECT_CALL(mock, OnResponseEnd(1, testing::_)) .Times(1) .WillOnce(QUIT_LOOP_SOON(loop, 2)); request.Start(); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); request.Release(); } TEST(UrlmonUrlRequestTest, UnreachableUrl) { MockUrlDelegate mock; chrome_frame_test::TimedMsgLoop loop; win_util::ScopedCOMInitializer init_com; CComObjectStackEx request; testing::StrictMock mock_server(1337, L"127.0.0.1", chrome_frame_test::GetTestDataFolder()); mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE)); GURL unreachable(WideToUTF8(mock_server.Resolve( L"non_existing.html"))); request.AddRef(); request.Initialize(&mock, 1, // request_id unreachable.spec(), "get", "", // referrer "", // extra request NULL, // upload data ResourceType::MAIN_FRAME, // resource type true); // frame busting // Expect headers EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::StartsWith("HTTP/1.1 404"), testing::_, testing::_, testing::_, testing::_)) .Times(1) .WillOnce(QUIT_LOOP_SOON(loop, 2)); EXPECT_CALL(mock, OnResponseEnd(1, testing::Property( &URLRequestStatus::os_error, net::ERR_TUNNEL_CONNECTION_FAILED))) .Times(testing::AtMost(1)); request.Start(); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); request.Release(); } TEST(UrlmonUrlRequestTest, ZeroLengthResponse) { MockUrlDelegate mock; chrome_frame_test::TimedMsgLoop loop; testing::StrictMock mock_server(1337, L"127.0.0.1", chrome_frame_test::GetTestDataFolder()); mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE)); win_util::ScopedCOMInitializer init_com; CComObjectStackEx request; request.AddRef(); request.Initialize(&mock, 1, // request_id WideToUTF8(mock_server.Resolve(L"empty.html")), "get", "", // referrer "", // extra request NULL, // upload data ResourceType::MAIN_FRAME, // resource type true); // frame busting // Expect headers EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_, testing::_, testing::_, testing::_)) .Times(1) .WillOnce(QUIT_LOOP(loop)); request.Start(); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); EXPECT_FALSE(loop.WasTimedOut()); // Should stay quiet, since we do not ask for anything for awhile. EXPECT_CALL(mock, OnResponseEnd(1, testing::_)).Times(0); loop.RunFor(3); // Invoke read. Only now the response end ("server closed the connection") // is supposed to be delivered. EXPECT_CALL(mock, OnResponseEnd(1, testing::Property( &URLRequestStatus::is_success, true))) .Times(1); request.Read(512); request.Release(); } ACTION_P4(ManagerRead, loop, mgr, request_id, bytes_to_read) { loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(mgr, &UrlmonUrlRequestManager::ReadUrlRequest, 0, request_id, bytes_to_read), 0); } ACTION_P3(ManagerEndRequest, loop, mgr, request_id) { loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(mgr, &UrlmonUrlRequestManager::EndUrlRequest, 0, request_id, URLRequestStatus()), 0); } // Simplest test - retrieve file from local web server. TEST(UrlmonUrlRequestManagerTest, Simple1) { MockUrlDelegate mock; chrome_frame_test::TimedMsgLoop loop; testing::StrictMock mock_server(1337, L"127.0.0.1", chrome_frame_test::GetTestDataFolder()); mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE)); scoped_ptr mgr(new UrlmonUrlRequestManager()); mgr->set_delegate(&mock); IPC::AutomationURLRequest r1 = { WideToUTF8(mock_server.Resolve(L"chrome_frame_window_open.html")), "get" }; EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_, testing::_, testing::_, testing::_)) .Times(1) .WillOnce(ManagerRead(&loop, mgr.get(), 1, 512)); EXPECT_CALL(mock, OnReadComplete(1, testing::Property(&std::string::size, testing::Gt(0u)))) .Times(testing::AtLeast(1)) .WillRepeatedly(ManagerRead(&loop, mgr.get(), 1, 2)); EXPECT_CALL(mock, OnResponseEnd(1, testing::_)) .Times(1) .WillOnce(QUIT_LOOP_SOON(loop, 2)); mgr->StartUrlRequest(0, 1, r1); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); mgr.reset(); } TEST(UrlmonUrlRequestManagerTest, Abort1) { MockUrlDelegate mock; chrome_frame_test::TimedMsgLoop loop; testing::StrictMock mock_server(1337, L"127.0.0.1", chrome_frame_test::GetTestDataFolder()); mock_server.ExpectAndServeAnyRequests(CFInvocation(CFInvocation::NONE)); scoped_ptr mgr(new UrlmonUrlRequestManager()); mgr->set_delegate(&mock); IPC::AutomationURLRequest r1 = { WideToUTF8(mock_server.Resolve(L"chrome_frame_window_open.html")), "get" }; EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_, testing::_, testing::_, testing::_)) .Times(1) .WillOnce(testing::DoAll( ManagerEndRequest(&loop, mgr.get(), 1), QUIT_LOOP_SOON(loop, 3))); EXPECT_CALL(mock, OnReadComplete(1, testing::_)) .Times(0); EXPECT_CALL(mock, OnResponseEnd(1, testing::_)) .Times(0); mgr->StartUrlRequest(0, 1, r1); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); mgr.reset(); }