// Copyright (c) 2012 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. // A test that exercises Chrome Frame's DLL Redirctor update code. This test // generates a new version of CF from the one already in the build folder and // then loads them both into the current process to verify the handoff. #include "base/file_path.h" #include "base/file_util.h" #include "base/file_version_info.h" #include "base/path_service.h" #include "base/scoped_native_library.h" #include "base/scoped_temp_dir.h" #include "base/shared_memory.h" #include "base/string_util.h" #include "base/version.h" #include "base/win/scoped_comptr.h" #include "chrome/installer/test/alternate_version_generator.h" #include "chrome/installer/util/delete_after_reboot_helper.h" #include "chrome_frame/chrome_tab.h" #include "chrome_frame/test_utils.h" #include "chrome_frame/utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace { const wchar_t kSharedMemoryPrefix[] = L"ChromeFrameVersionBeacon_"; const uint32 kSharedMemoryBytes = 128; } class DllRedirectorLoadingTest : public testing::Test { public: // This sets up the following directory structure: // TEMP\\npchrome_frame.dll // \\npchrome_frame.dll // // This structure emulates enough of the directory structure of a Chrome // install to test upgrades. static void SetUpTestCase() { // First ensure that we can find the built Chrome Frame DLL. FilePath build_chrome_frame_dll = GetChromeFrameBuildPath(); ASSERT_TRUE(file_util::PathExists(build_chrome_frame_dll)); // Then grab its version. scoped_ptr original_version_info( FileVersionInfo::CreateFileVersionInfo(build_chrome_frame_dll)); ASSERT_TRUE(original_version_info != NULL); original_version_.reset( new Version(WideToASCII(original_version_info->file_version()))); ASSERT_TRUE(original_version_->IsValid()); // Make a place for us to run the test from. ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); // Make a versioned dir for the original chrome frame dll to run under. FilePath original_version_dir( temp_dir_.path().AppendASCII(original_version_->GetString())); ASSERT_TRUE(file_util::CreateDirectory(original_version_dir)); // Now move the original DLL that we will operate on into a named-version // folder. original_chrome_frame_dll_ = original_version_dir.Append(build_chrome_frame_dll.BaseName()); ASSERT_TRUE(file_util::CopyFile(build_chrome_frame_dll, original_chrome_frame_dll_)); ASSERT_TRUE(file_util::PathExists(original_chrome_frame_dll_)); // Temporary location for the new Chrome Frame DLL. FilePath temporary_new_chrome_frame_dll( temp_dir_.path().Append(build_chrome_frame_dll.BaseName())); // Generate the version-bumped Chrome Frame DLL to a temporary path. ASSERT_TRUE( upgrade_test::GenerateAlternatePEFileVersion( original_chrome_frame_dll_, temporary_new_chrome_frame_dll, upgrade_test::NEXT_VERSION)); // Ensure it got created and grab its version. scoped_ptr new_version_info( FileVersionInfo::CreateFileVersionInfo(temporary_new_chrome_frame_dll)); ASSERT_TRUE(new_version_info != NULL); new_version_.reset( new Version(WideToASCII(new_version_info->file_version()))); ASSERT_TRUE(new_version_->IsValid()); // Make sure the new version is larger than the old. ASSERT_EQ(new_version_->CompareTo(*original_version_.get()), 1); // Now move the new Chrome Frame dll to its final resting place: FilePath new_version_dir( temp_dir_.path().AppendASCII(new_version_->GetString())); ASSERT_TRUE(file_util::CreateDirectory(new_version_dir)); new_chrome_frame_dll_ = new_version_dir.Append(build_chrome_frame_dll.BaseName()); ASSERT_TRUE(file_util::Move(temporary_new_chrome_frame_dll, new_chrome_frame_dll_)); ASSERT_TRUE(file_util::PathExists(new_chrome_frame_dll_)); } static void TearDownTestCase() { if (!temp_dir_.Delete()) { // The temp_dir cleanup has been observed to fail in some cases. It looks // like something is holding on to the Chrome Frame DLLs after they have // been explicitly unloaded. At least schedule them for cleanup on reboot. ScheduleDirectoryForDeletion(temp_dir_.path().value().c_str()); } } protected: static FilePath original_chrome_frame_dll_; static FilePath new_chrome_frame_dll_; static scoped_ptr original_version_; static scoped_ptr new_version_; static ScopedTempDir temp_dir_; }; // class DllRedirectorLoadingTest FilePath DllRedirectorLoadingTest::original_chrome_frame_dll_; FilePath DllRedirectorLoadingTest::new_chrome_frame_dll_; scoped_ptr DllRedirectorLoadingTest::original_version_; scoped_ptr DllRedirectorLoadingTest::new_version_; ScopedTempDir DllRedirectorLoadingTest::temp_dir_; #if defined(COMPONENT_BUILD) // Disabling since npchrome_frame.dll's DllMain can't handle being loaded into // a process that has already started services in base.dll such as logging; see // http://crbug.com/110492. #define MAYBE_TestDllRedirection DISABLED_TestDllRedirection #else #define MAYBE_TestDllRedirection TestDllRedirection #endif TEST_F(DllRedirectorLoadingTest, MAYBE_TestDllRedirection) { struct TestData { FilePath first_dll; FilePath second_dll; Version* expected_beacon_version; } test_data[] = { { original_chrome_frame_dll_, new_chrome_frame_dll_, original_version_.get() }, { new_chrome_frame_dll_, original_chrome_frame_dll_, new_version_.get() } }; for (int i = 0; i < arraysize(test_data); ++i) { // First load the original dll into our test process. base::ScopedNativeLibrary original_library(test_data[i].first_dll); ASSERT_TRUE(original_library.is_valid()); // Now query the original dll for its DllGetClassObject method and use that // to get the class factory for a known CLSID. LPFNGETCLASSOBJECT original_dgco_ptr = reinterpret_cast( original_library.GetFunctionPointer("DllGetClassObject")); ASSERT_TRUE(original_dgco_ptr != NULL); base::win::ScopedComPtr original_class_factory; HRESULT hr = original_dgco_ptr( CLSID_ChromeFrame, IID_IClassFactory, reinterpret_cast(original_class_factory.Receive())); ASSERT_HRESULT_SUCCEEDED(hr); // Now load the new dll into our test process. base::ScopedNativeLibrary new_library(test_data[i].second_dll); ASSERT_TRUE(new_library.is_valid()); // Now query the new dll for its DllGetClassObject method and use that // to get the class factory for a known CLSID. LPFNGETCLASSOBJECT new_dgco_ptr = reinterpret_cast( new_library.GetFunctionPointer("DllGetClassObject")); ASSERT_TRUE(new_dgco_ptr != NULL); base::win::ScopedComPtr new_class_factory; hr = new_dgco_ptr(CLSID_ChromeFrame, IID_IClassFactory, reinterpret_cast(new_class_factory.Receive())); ASSERT_HRESULT_SUCCEEDED(hr); base::win::ScopedComPtr qi_test; EXPECT_HRESULT_SUCCEEDED(original_class_factory.QueryInterface(IID_IUnknown, reinterpret_cast(qi_test.Receive()))); EXPECT_TRUE(new_class_factory.IsSameObject(qi_test)); // Check the version beacon. std::wstring beacon_name(kSharedMemoryPrefix); beacon_name += GetHostProcessName(false /* without extension */); base::SharedMemory beacon(beacon_name); EXPECT_TRUE(beacon.Open(WideToASCII(beacon_name), true /* read_only */)); EXPECT_TRUE(beacon.Map(0)); EXPECT_TRUE(beacon.memory()); char buffer[kSharedMemoryBytes] = {0}; memcpy(buffer, beacon.memory(), kSharedMemoryBytes - 1); Version beacon_version(buffer); ASSERT_TRUE(beacon_version.IsValid()); EXPECT_EQ(0, beacon_version.CompareTo(*test_data[i].expected_beacon_version)); } }