diff options
author | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-05 19:16:07 +0000 |
---|---|---|
committer | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-05 19:16:07 +0000 |
commit | 8cc419448037675e5742586429bb6c00606ce8f9 (patch) | |
tree | 61cd63e10a4c4bd6c4515beb3c0f1489bba0b397 /chrome_frame/test | |
parent | 39549bf8f876b17f481e753161166370064458bd (diff) | |
download | chromium_src-8cc419448037675e5742586429bb6c00606ce8f9.zip chromium_src-8cc419448037675e5742586429bb6c00606ce8f9.tar.gz chromium_src-8cc419448037675e5742586429bb6c00606ce8f9.tar.bz2 |
Implementation of single-module-per-logon-session-per-profile implementation for Chrome Frame.
BUG=61383
TEST=Run IE with version X of CF loaded. Register version Y of CF. Run a second IE process, notice that version Y when loaded defers to version X.
Review URL: http://codereview.chromium.org/4144008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65236 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/test')
-rw-r--r-- | chrome_frame/test/module_utils_test.cc | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/chrome_frame/test/module_utils_test.cc b/chrome_frame/test/module_utils_test.cc new file mode 100644 index 0000000..4bb16ce --- /dev/null +++ b/chrome_frame/test/module_utils_test.cc @@ -0,0 +1,292 @@ +// 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 "chrome_frame/module_utils.h" + +#include "base/scoped_handle.h" +#include "base/shared_memory.h" +#include "base/utf_string_conversions.h" +#include "base/version.h" +#include "gtest/gtest.h" + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +const char kMockVersionString[] = "42.42.42.42"; +const char kMockVersionString2[] = "133.33.33.7"; + +const HMODULE kMockModuleHandle = reinterpret_cast<HMODULE>(42); +const HMODULE kMockModuleHandle2 = reinterpret_cast<HMODULE>(43); + +const char kTestVersionBeaconName[] = "DllRedirectorTestVersionBeacon"; +const uint32 kSharedMemorySize = 128; + +// The maximum amount of time we are willing to let a test that Waits timeout +// before failing. +const uint32 kWaitTestTimeout = 20000; + +using base::win::ScopedHandle; + +class MockDllRedirector : public DllRedirector { + public: + explicit MockDllRedirector(const char* beacon_name) + : DllRedirector(beacon_name) {} + + virtual HMODULE LoadVersionedModule() { + return kMockModuleHandle; + } + + virtual Version* GetCurrentModuleVersion() { + return Version::GetVersionFromString(kMockVersionString); + } + + virtual HMODULE GetFirstModule() { + return DllRedirector::GetFirstModule(); + } + + Version* GetFirstModuleVersion() { + // Lazy man's copy. + return Version::GetVersionFromString(dll_version_->GetString()); + } + + base::SharedMemory* shared_memory() { + return shared_memory_.get(); + } +}; + +class MockDllRedirector2 : public MockDllRedirector { + public: + explicit MockDllRedirector2(const char* beacon_name) + : MockDllRedirector(beacon_name) {} + + virtual HMODULE LoadVersionedModule() { + return kMockModuleHandle2; + } + + virtual Version* GetCurrentModuleVersion() { + return Version::GetVersionFromString(kMockVersionString2); + } +}; + +class DllRedirectorTest : public testing::Test { + public: + virtual void SetUp() { + shared_memory_.reset(new base::SharedMemory); + mock_version_.reset(Version::GetVersionFromString(kMockVersionString)); + mock_version2_.reset(Version::GetVersionFromString(kMockVersionString2)); + } + + virtual void TearDown() { + CloseBeacon(); + } + + void CreateVersionBeacon(const std::string& name, + const std::string& version_string) { + // Abort the test if we can't create and map a new named memory object. + EXPECT_TRUE(shared_memory_->CreateNamed(name, false, + kSharedMemorySize)); + EXPECT_TRUE(shared_memory_->Map(0)); + EXPECT_TRUE(shared_memory_->memory()); + + if (shared_memory_->memory()) { + memcpy(shared_memory_->memory(), + version_string.c_str(), + std::min(kSharedMemorySize, version_string.length() + 1)); + } + } + + // Opens the named beacon and returns the version. + Version* OpenAndReadVersionFromBeacon(const std::string& name) { + // Abort the test if we can't open and map the named memory object. + EXPECT_TRUE(shared_memory_->Open(name, true /* read_only */)); + EXPECT_TRUE(shared_memory_->Map(0)); + EXPECT_TRUE(shared_memory_->memory()); + + char buffer[kSharedMemorySize] = {0}; + memcpy(buffer, shared_memory_->memory(), kSharedMemorySize - 1); + return Version::GetVersionFromString(buffer); + } + + void CloseBeacon() { + shared_memory_->Close(); + } + + // Shared memory segment that contains the version beacon. + scoped_ptr<base::SharedMemory> shared_memory_; + scoped_ptr<Version> mock_version_; + scoped_ptr<Version> mock_version2_; +}; + +TEST_F(DllRedirectorTest, RegisterAsFirstModule) { + scoped_ptr<MockDllRedirector> redirector( + new MockDllRedirector(kTestVersionBeaconName)); + EXPECT_TRUE(redirector->RegisterAsFirstCFModule()); + + base::SharedMemory* redirector_memory = redirector->shared_memory(); + char buffer[kSharedMemorySize] = {0}; + memcpy(buffer, redirector_memory->memory(), kSharedMemorySize - 1); + scoped_ptr<Version> redirector_version(Version::GetVersionFromString(buffer)); + ASSERT_TRUE(redirector_version.get()); + EXPECT_TRUE(redirector_version->Equals(*mock_version_.get())); + redirector_memory = NULL; + + scoped_ptr<Version> memory_version( + OpenAndReadVersionFromBeacon(kTestVersionBeaconName)); + ASSERT_TRUE(memory_version.get()); + EXPECT_TRUE(redirector_version->Equals(*memory_version.get())); + CloseBeacon(); + + redirector.reset(); + EXPECT_FALSE(shared_memory_->Open(kTestVersionBeaconName, true)); +} + +TEST_F(DllRedirectorTest, SecondModuleLoading) { + scoped_ptr<MockDllRedirector> first_redirector( + new MockDllRedirector(kTestVersionBeaconName)); + EXPECT_TRUE(first_redirector->RegisterAsFirstCFModule()); + + scoped_ptr<MockDllRedirector2> second_redirector( + new MockDllRedirector2(kTestVersionBeaconName)); + EXPECT_FALSE(second_redirector->RegisterAsFirstCFModule()); + + scoped_ptr<Version> first_redirector_version( + first_redirector->GetFirstModuleVersion()); + scoped_ptr<Version> second_redirector_version( + second_redirector->GetFirstModuleVersion()); + + EXPECT_TRUE( + second_redirector_version->Equals(*first_redirector_version.get())); + EXPECT_TRUE( + second_redirector_version->Equals(*mock_version_.get())); +} + +// This test ensures that the beacon remains alive as long as there is a single +// module that used it to determine its version still loaded. +TEST_F(DllRedirectorTest, TestBeaconOwnershipHandoff) { + scoped_ptr<MockDllRedirector> first_redirector( + new MockDllRedirector(kTestVersionBeaconName)); + EXPECT_TRUE(first_redirector->RegisterAsFirstCFModule()); + + scoped_ptr<MockDllRedirector2> second_redirector( + new MockDllRedirector2(kTestVersionBeaconName)); + EXPECT_FALSE(second_redirector->RegisterAsFirstCFModule()); + + scoped_ptr<Version> first_redirector_version( + first_redirector->GetFirstModuleVersion()); + scoped_ptr<Version> second_redirector_version( + second_redirector->GetFirstModuleVersion()); + + EXPECT_TRUE( + second_redirector_version->Equals(*first_redirector_version.get())); + EXPECT_TRUE( + second_redirector_version->Equals(*mock_version_.get())); + + // Clear out the first redirector. The second, still holding a reference + // to the shared memory should ensure that the beacon stays alive. + first_redirector.reset(); + + scoped_ptr<MockDllRedirector2> third_redirector( + new MockDllRedirector2(kTestVersionBeaconName)); + EXPECT_FALSE(third_redirector->RegisterAsFirstCFModule()); + + scoped_ptr<Version> third_redirector_version( + third_redirector->GetFirstModuleVersion()); + + EXPECT_TRUE( + third_redirector_version->Equals(*second_redirector_version.get())); + EXPECT_TRUE( + third_redirector_version->Equals(*mock_version_.get())); + + // Now close all remaining redirectors, which should destroy the beacon. + second_redirector.reset(); + third_redirector.reset(); + + // Now create a fourth, expecting that this time it should be the first in. + scoped_ptr<MockDllRedirector2> fourth_redirector( + new MockDllRedirector2(kTestVersionBeaconName)); + EXPECT_TRUE(fourth_redirector->RegisterAsFirstCFModule()); + + scoped_ptr<Version> fourth_redirector_version( + fourth_redirector->GetFirstModuleVersion()); + + EXPECT_TRUE( + fourth_redirector_version->Equals(*mock_version2_.get())); +} + +struct LockSquattingThreadParams { + ScopedHandle is_squatting; + ScopedHandle time_to_die; +}; + +DWORD WINAPI LockSquattingThread(void* in_params) { + LockSquattingThreadParams* params = + reinterpret_cast<LockSquattingThreadParams*>(in_params); + DCHECK(params); + + // Grab the lock for the shared memory region and hold onto it. + base::SharedMemory squatter(ASCIIToWide(kTestVersionBeaconName)); + base::SharedMemoryAutoLock squatter_lock(&squatter); + + // Notify our caller that we're squatting. + BOOL ret = ::SetEvent(params->is_squatting); + DCHECK(ret); + + // And then wait to be told to shut down. + DWORD result = ::WaitForSingleObject(params->time_to_die, kWaitTestTimeout); + EXPECT_EQ(WAIT_OBJECT_0, result); + + return 0; +} + +// Test that the Right Thing happens when someone else is holding onto the +// beacon lock and not letting go. (The Right Thing being that the redirector +// assumes that it is the right version and doesn't attempt to use the shared +// memory region.) +TEST_F(DllRedirectorTest, LockSquatting) { + scoped_ptr<MockDllRedirector> first_redirector( + new MockDllRedirector(kTestVersionBeaconName)); + EXPECT_TRUE(first_redirector->RegisterAsFirstCFModule()); + + LockSquattingThreadParams params; + params.is_squatting.Set(::CreateEvent(NULL, FALSE, FALSE, NULL)); + params.time_to_die.Set(::CreateEvent(NULL, FALSE, FALSE, NULL)); + DWORD tid = 0; + ScopedHandle lock_squat_thread( + ::CreateThread(NULL, 0, LockSquattingThread, ¶ms, 0, &tid)); + + // Make sure the squatter has started squatting. + DWORD wait_result = ::WaitForSingleObject(params.is_squatting, + kWaitTestTimeout); + EXPECT_EQ(WAIT_OBJECT_0, wait_result); + + scoped_ptr<MockDllRedirector2> second_redirector( + new MockDllRedirector2(kTestVersionBeaconName)); + EXPECT_TRUE(second_redirector->RegisterAsFirstCFModule()); + + scoped_ptr<Version> second_redirector_version( + second_redirector->GetFirstModuleVersion()); + EXPECT_TRUE( + second_redirector_version->Equals(*mock_version2_.get())); + + // Shut down the squatting thread. + DWORD ret = ::SetEvent(params.time_to_die); + DCHECK(ret); + + wait_result = ::WaitForSingleObject(lock_squat_thread, kWaitTestTimeout); + EXPECT_EQ(WAIT_OBJECT_0, wait_result); +} + +TEST_F(DllRedirectorTest, BadVersionNumber) { + std::string bad_version("I am not a version number"); + CreateVersionBeacon(kTestVersionBeaconName, bad_version); + + // The redirector should fail to read the version number and defer to + // its own version. + scoped_ptr<MockDllRedirector> first_redirector( + new MockDllRedirector(kTestVersionBeaconName)); + EXPECT_TRUE(first_redirector->RegisterAsFirstCFModule()); + + HMODULE first_module = first_redirector->GetFirstModule(); + EXPECT_EQ(reinterpret_cast<HMODULE>(&__ImageBase), first_module); +} + |