blob: 614cb0de805b38910b03d417abcfb20bd025f7ec [file] [log] [blame]
// 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/test/perf/chrome_frame_perftest.h"
#include <atlwin.h>
#include <atlhost.h>
#include <map>
#include <vector>
#include <string>
#include "chrome_tab.h" // Generated from chrome_tab.idl.
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/registry.h"
#include "base/scoped_ptr.h"
#include "base/scoped_bstr_win.h"
#include "base/scoped_comptr_win.h"
#include "base/scoped_variant_win.h"
#include "base/string_util.h"
#include "base/time.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_paths_internal.h"
#include "chrome/test/chrome_process_util.h"
#include "chrome/test/ui/ui_test.h"
#include "chrome_frame/test_utils.h"
#include "chrome_frame/utils.h"
const wchar_t kSilverlightControlKey[] =
L"CLSID\\{DFEAF541-F3E1-4c24-ACAC-99C30715084A}\\InprocServer32";
const wchar_t kFlashControlKey[] =
L"CLSID\\{D27CDB6E-AE6D-11cf-96B8-444553540000}\\InprocServer32";
using base::TimeDelta;
using base::TimeTicks;
// This class implements an ActiveX container which hosts the ChromeFrame
// ActiveX control. It provides hooks which can be implemented by derived
// classes for implementing performance measurement, etc.
class ChromeFrameActiveXContainer
: public CWindowImpl<ChromeFrameActiveXContainer, CWindow, CWinTraits <
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
WS_EX_APPWINDOW | WS_EX_WINDOWEDGE> >,
public CComObjectRootEx<CComSingleThreadModel>,
public IPropertyNotifySink {
public:
~ChromeFrameActiveXContainer() {
if (m_hWnd)
DestroyWindow();
}
DECLARE_WND_CLASS_EX(L"ChromeFrameActiveX_container", 0, 0)
BEGIN_COM_MAP(ChromeFrameActiveXContainer)
COM_INTERFACE_ENTRY(IPropertyNotifySink)
END_COM_MAP()
BEGIN_MSG_MAP(ChromeFrameActiveXContainer)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
END_MSG_MAP()
HRESULT OnMessageCallback(const VARIANT* param) {
DLOG(INFO) << __FUNCTION__;
OnMessageCallbackImpl(param);
return S_OK;
}
HRESULT OnLoadErrorCallback(const VARIANT* param) {
DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
OnLoadErrorCallbackImpl(param);
return S_OK;
}
HRESULT OnLoadCallback(const VARIANT* param) {
DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
OnLoadCallbackImpl(param);
return S_OK;
}
ChromeFrameActiveXContainer() :
prop_notify_cookie_(0),
onmsg_(this, &ChromeFrameActiveXContainer::OnMessageCallback),
onloaderror_(this, &ChromeFrameActiveXContainer::OnLoadErrorCallback),
onload_(this, &ChromeFrameActiveXContainer::OnLoadCallback) {
}
LRESULT OnCreate(UINT , WPARAM , LPARAM , BOOL& ) {
chromeview_.Attach(m_hWnd);
return 0;
}
// This will be called twice.
// Once from CAxHostWindow::OnDestroy (through DefWindowProc)
// and once more from the ATL since CAxHostWindow::OnDestroy claims the
// message is not handled.
LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL& handled) { // NOLINT
if (prop_notify_cookie_) {
AtlUnadvise(tab_, IID_IPropertyNotifySink, prop_notify_cookie_);
prop_notify_cookie_ = 0;
}
tab_.Release();
return 0;
}
virtual void OnFinalMessage(HWND /*hWnd*/) {
::PostQuitMessage(6);
}
static const wchar_t* GetWndCaption() {
return L"ChromeFrame Container";
}
// IPropertyNotifySink
STDMETHOD(OnRequestEdit)(DISPID disp_id) {
OnRequestEditImpl(disp_id);
return S_OK;
}
STDMETHOD(OnChanged)(DISPID disp_id) {
if (disp_id != DISPID_READYSTATE)
return S_OK;
long ready_state;
HRESULT hr = tab_->get_readyState(&ready_state);
DCHECK(hr == S_OK);
OnReadyStateChanged(ready_state);
if (ready_state == READYSTATE_COMPLETE) {
if (!starting_url_.empty()) {
Navigate(starting_url_.c_str());
} else {
PostMessage(WM_CLOSE);
}
} else if (ready_state == READYSTATE_UNINITIALIZED) {
DLOG(ERROR) << __FUNCTION__ << " Chrome launch failed.";
}
return S_OK;
}
void CreateChromeFrameWindow(const std::string& starting_url) {
starting_url_ = starting_url;
RECT rc = { 0, 0, 800, 600 };
Create(NULL, rc);
DCHECK(m_hWnd);
ShowWindow(SW_SHOWDEFAULT);
}
void CreateControl(bool setup_event_sinks) {
HRESULT hr = chromeview_.CreateControl(L"ChromeTab.ChromeFrame");
EXPECT_HRESULT_SUCCEEDED(hr);
hr = chromeview_.QueryControl(tab_.Receive());
EXPECT_HRESULT_SUCCEEDED(hr);
if (setup_event_sinks)
SetupEventSinks();
}
void Navigate(const char* url) {
BeforeNavigateImpl(url);
HRESULT hr = tab_->put_src(ScopedBstr(UTF8ToWide(url).c_str()));
DCHECK(hr == S_OK) << "Chrome frame NavigateToURL(" << url
<< StringPrintf(L") failed 0x%08X", hr);
}
void SetupEventSinks() {
HRESULT hr = AtlAdvise(tab_, this, IID_IPropertyNotifySink,
&prop_notify_cookie_);
DCHECK(hr == S_OK) << "AtlAdvice for IPropertyNotifySink failed " << hr;
ScopedVariant onmessage(onmsg_.ToDispatch());
ScopedVariant onloaderror(onloaderror_.ToDispatch());
ScopedVariant onload(onload_.ToDispatch());
EXPECT_HRESULT_SUCCEEDED(tab_->put_onmessage(onmessage));
EXPECT_HRESULT_SUCCEEDED(tab_->put_onloaderror(onloaderror));
EXPECT_HRESULT_SUCCEEDED(tab_->put_onload(onload));
}
protected:
// These functions are implemented by derived classes for special behavior
// like performance measurement, etc.
virtual void OnReadyStateChanged(long ready_state) {}
virtual void OnRequestEditImpl(DISPID disp_id) {}
virtual void OnMessageCallbackImpl(const VARIANT* param) {}
virtual void OnLoadCallbackImpl(const VARIANT* param) {
PostMessage(WM_CLOSE);
}
virtual void OnLoadErrorCallbackImpl(const VARIANT* param) {
PostMessage(WM_CLOSE);
}
virtual void BeforeNavigateImpl(const char* url) {}
CAxWindow chromeview_;
ScopedComPtr<IChromeFrame> tab_;
DWORD prop_notify_cookie_;
DispCallback<ChromeFrameActiveXContainer> onmsg_;
DispCallback<ChromeFrameActiveXContainer> onloaderror_;
DispCallback<ChromeFrameActiveXContainer> onload_;
std::string starting_url_;
};
// This class overrides the hooks provided by the ChromeFrameActiveXContainer
// class and measures performance at various stages, like initialzation of
// the Chrome frame widget, navigation, etc.
class ChromeFrameActiveXContainerPerf : public ChromeFrameActiveXContainer {
public:
ChromeFrameActiveXContainerPerf() {}
void CreateControl(bool setup_event_sinks) {
perf_initialize_.reset(new PerfTimeLogger("Fully initialized"));
PerfTimeLogger perf_create("Create Control");
HRESULT hr = chromeview_.CreateControl(L"ChromeTab.ChromeFrame");
EXPECT_HRESULT_SUCCEEDED(hr);
hr = chromeview_.QueryControl(tab_.Receive());
EXPECT_HRESULT_SUCCEEDED(hr);
perf_create.Done();
if (setup_event_sinks)
SetupEventSinks();
}
protected:
virtual void OnReadyStateChanged(long ready_state) {
// READYSTATE_COMPLETE is fired when the automation server is ready.
if (ready_state == READYSTATE_COMPLETE) {
perf_initialize_->Done();
} else if (ready_state == READYSTATE_INTERACTIVE) {
// Window ready. Currently we never receive this notification because it
// is fired before we finish setting up our hosting environment.
// This is because of how ATL is written. Moving forward we might
// have our own hosting classes and then have more control over when we
// set up the prop notify sink.
} else {
DCHECK(ready_state != READYSTATE_UNINITIALIZED) << "failed to initialize";
}
}
virtual void OnLoadCallbackImpl(const VARIANT* param) {
PostMessage(WM_CLOSE);
perf_navigate_->Done();
}
virtual void OnLoadErrorCallbackImpl(const VARIANT* param) {
PostMessage(WM_CLOSE);
perf_navigate_->Done();
}
virtual void BeforeNavigateImpl(const char* url ) {
std::string test_name = "Navigate ";
test_name += url;
perf_navigate_.reset(new PerfTimeLogger(test_name.c_str()));
}
scoped_ptr<PerfTimeLogger> perf_initialize_;
scoped_ptr<PerfTimeLogger> perf_navigate_;
};
// This class provides common functionality which can be used for most of the
// ChromeFrame/Tab performance tests.
class ChromeFramePerfTestBase : public UITest {
public:
ChromeFramePerfTestBase() {}
protected:
scoped_ptr<ScopedChromeFrameRegistrar> chrome_frame_registrar_;
};
class ChromeFrameStartupTest : public ChromeFramePerfTestBase {
public:
ChromeFrameStartupTest() {}
virtual void SetUp() {
ASSERT_TRUE(PathService::Get(chrome::DIR_APP, &dir_app_));
chrome_dll_ = dir_app_.Append(FILE_PATH_LITERAL("chrome.dll"));
chrome_exe_ = dir_app_.Append(
FilePath::FromWStringHack(chrome::kBrowserProcessExecutableName));
chrome_frame_dll_ = dir_app_.Append(FILE_PATH_LITERAL("servers"));
chrome_frame_dll_ = chrome_frame_dll_.Append(
FilePath::FromWStringHack(kChromeFrameDllName));
icu_dll_ = dir_app_.Append(FILE_PATH_LITERAL("icudt42.dll"));
gears_dll_ = dir_app_.Append(FILE_PATH_LITERAL("gears.dll"));
avcodec52_dll_ = dir_app_.Append(FILE_PATH_LITERAL("avcodec-52.dll"));
avformat52_dll_ = dir_app_.Append(FILE_PATH_LITERAL("avformat-52.dll"));
avutil50_dll_ = dir_app_.Append(FILE_PATH_LITERAL("avutil-50.dll"));
}
// TODO(iyengar)
// This function is similar to the RunStartupTest function used in chrome
// startup tests. Refactor into a common implementation.
void RunStartupTest(const char* graph, const char* trace,
const char* startup_url, bool test_cold,
int total_binaries, const FilePath binaries_to_evict[],
bool important, bool ignore_cache_error) {
const int kNumCycles = 20;
startup_url_ = startup_url;
TimeDelta timings[kNumCycles];
for (int i = 0; i < kNumCycles; ++i) {
if (test_cold) {
for (int binary_index = 0; binary_index < total_binaries;
binary_index++) {
bool result = EvictFileFromSystemCacheWrapper(
binaries_to_evict[binary_index]);
if (!ignore_cache_error) {
ASSERT_TRUE(result);
} else if (!result) {
LOG(ERROR) << GetLastError();
printf("\nFailed to evict file %ls from cache. Not running test\n",
binaries_to_evict[binary_index].value().c_str());
return;
}
}
}
TimeTicks start_time, end_time;
RunStartupTestImpl(&start_time, &end_time);
timings[i] = end_time - start_time;
CoFreeUnusedLibraries();
// TODO(beng): Can't shut down so quickly. Figure out why, and fix. If we
// do, we crash.
PlatformThread::Sleep(50);
}
std::string times;
for (int i = 0; i < kNumCycles; ++i)
StringAppendF(&times, "%.2f,", timings[i].InMillisecondsF());
PrintResultList(graph, "", trace, times, "ms", important);
}
FilePath dir_app_;
FilePath chrome_dll_;
FilePath chrome_exe_;
FilePath chrome_frame_dll_;
FilePath icu_dll_;
FilePath gears_dll_;
FilePath avcodec52_dll_;
FilePath avformat52_dll_;
FilePath avutil50_dll_;
protected:
// Individual startup tests should implement this function.
virtual void RunStartupTestImpl(TimeTicks* start_time,
TimeTicks* end_time) {}
// The host is torn down by this function. It should not be used after
// this function returns.
static void ReleaseHostComReferences(CAxWindow& host) {
CComPtr<IAxWinHostWindow> spWinHost;
host.QueryHost(&spWinHost);
ASSERT_TRUE(spWinHost != NULL);
// Hack to get the host to release all interfaces and thus ensure that
// the COM server can be unloaded.
CAxHostWindow* host_window = static_cast<CAxHostWindow*>(spWinHost.p);
host_window->ReleaseAll();
host.DestroyWindow();
}
std::string startup_url_;
};
class ChromeFrameStartupTestActiveX : public ChromeFrameStartupTest {
public:
virtual void SetUp() {
// Register the Chrome Frame DLL in the build directory.
chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
ChromeFrameStartupTest::SetUp();
}
protected:
virtual void RunStartupTestImpl(TimeTicks* start_time,
TimeTicks* end_time) {
*start_time = TimeTicks::Now();
SimpleModule module;
AtlAxWinInit();
CComObjectStackEx<ChromeFrameActiveXContainer> wnd;
wnd.CreateChromeFrameWindow(startup_url_);
wnd.CreateControl(true);
module.RunMessageLoop();
*end_time = TimeTicks::Now();
}
};
// This class measures the load time of chrome and chrome frame binaries
class ChromeFrameBinariesLoadTest : public ChromeFrameStartupTestActiveX {
static const size_t kStepSize = 4 * 1024;
public:
ChromeFrameBinariesLoadTest()
: pre_read_(false),
step_size_(kStepSize),
bytes_to_read_(0) {}
protected:
virtual void RunStartupTestImpl(TimeTicks* start_time,
TimeTicks* end_time) {
*start_time = TimeTicks::Now();
if (pre_read_) {
EXPECT_TRUE(file_util::PreReadImage(chrome_exe_.value().c_str(),
bytes_to_read_,
step_size_));
EXPECT_TRUE(file_util::PreReadImage(chrome_dll_.value().c_str(),
bytes_to_read_,
step_size_));
}
HMODULE chrome_exe = LoadLibrary(chrome_exe_.value().c_str());
EXPECT_TRUE(chrome_exe != NULL);
HMODULE chrome_dll = LoadLibrary(chrome_dll_.value().c_str());
EXPECT_TRUE(chrome_dll != NULL);
*end_time = TimeTicks::Now();
FreeLibrary(chrome_exe);
FreeLibrary(chrome_dll);
}
bool pre_read_;
size_t bytes_to_read_;
size_t step_size_;
};
// This class provides functionality to run the startup performance test for
// the ChromeFrame ActiveX against a reference build. At this point we only run
// this test in warm mode.
class ChromeFrameStartupTestActiveXReference
: public ChromeFrameStartupTestActiveX {
public:
// override the browser directory to use the reference build instead.
virtual void SetUp() {
// Register the reference build Chrome Frame DLL.
chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
chrome_frame_registrar_->RegisterReferenceChromeFrameBuild();
ChromeFrameStartupTest::SetUp();
chrome_frame_dll_ = FilePath(
chrome_frame_registrar_->GetReferenceChromeFrameDllPath());
DLOG(INFO) << __FUNCTION__ << ": " << chrome_frame_dll_.value();
}
virtual void TearDown() {
// Reregister the Chrome Frame DLL in the build directory.
chrome_frame_registrar_.reset(NULL);
}
};
// This class provides base functionality to measure ChromeFrame memory
// usage.
// TODO(iyengar)
// Some of the functionality in this class like printing the results, etc
// is based on the chrome\test\memory_test.cc. We need to factor out
// the common code.
class ChromeFrameMemoryTest : public ChromeFramePerfTestBase {
// Contains information about the memory consumption of a process.
class ProcessMemoryInfo {
public:
// Default constructor
// Added to enable us to add ProcessMemoryInfo instances to a map.
ProcessMemoryInfo()
: process_id_(0),
virtual_size_(0),
working_set_size_(0),
chrome_browser_process_(false),
chrome_frame_memory_test_instance_(NULL) {}
ProcessMemoryInfo(base::ProcessId process_id, bool chrome_browser_process,
ChromeFrameMemoryTest* memory_test_instance)
: process_id_(process_id),
virtual_size_(0),
working_set_size_(0),
chrome_browser_process_(chrome_browser_process),
chrome_frame_memory_test_instance_(memory_test_instance) {}
bool GetMemoryConsumptionDetails() {
base::ProcessHandle process_handle;
if (!base::OpenPrivilegedProcessHandle(process_id_, &process_handle)) {
NOTREACHED();
}
// TODO(sgk): if/when base::ProcessMetrics can return real memory
// stats on mac, convert to:
//
// scoped_ptr<base::ProcessMetrics> process_metrics;
// process_metrics.reset(
// base::ProcessMetrics::CreateProcessMetrics(process_handle));
scoped_ptr<ChromeTestProcessMetrics> process_metrics;
process_metrics.reset(
ChromeTestProcessMetrics::CreateProcessMetrics(process_handle));
virtual_size_ = process_metrics->GetPagefileUsage();
working_set_size_ = process_metrics->GetWorkingSetSize();
return true;
}
void Print(const char* test_name) {
std::string trace_name(test_name);
ASSERT_TRUE(chrome_frame_memory_test_instance_ != NULL);
if (chrome_browser_process_) {
chrome_frame_memory_test_instance_->PrintResult(
"vm_final_browser", "", trace_name + "_vm_b",
virtual_size_ / 1024, "KB", false /* not important */);
chrome_frame_memory_test_instance_->PrintResult(
"ws_final_browser", "", trace_name + "_ws_b",
working_set_size_ / 1024, "KB", false /* not important */);
} else if (process_id_ == base::GetCurrentProcId()) {
chrome_frame_memory_test_instance_->PrintResult(
"vm_current_process", "", trace_name + "_vm_c",
virtual_size_ / 1024, "KB", false /* not important */);
chrome_frame_memory_test_instance_->PrintResult(
"ws_current_process", "", trace_name + "_ws_c",
working_set_size_ / 1024, "KB", false /* not important */);
}
printf("\n");
}
base::ProcessId process_id_;
size_t virtual_size_;
size_t working_set_size_;
// Set to true if this is the chrome browser process.
bool chrome_browser_process_;
// A reference to the ChromeFrameMemoryTest instance. Used to print memory
// consumption information.
ChromeFrameMemoryTest* chrome_frame_memory_test_instance_;
};
// This map tracks memory usage for a process. It is keyed on the process
// id.
typedef std::map<DWORD, ProcessMemoryInfo> ProcessMemoryConsumptionMap;
public:
ChromeFrameMemoryTest() : current_url_index_(0) {
}
virtual void SetUp() {
// Register the Chrome Frame DLL in the build directory.
chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
}
void RunTest(const char* test_name, char* urls[], int total_urls) {
ASSERT_TRUE(urls != NULL);
ASSERT_GT(total_urls, 0);
// Record the initial CommitCharge. This is a system-wide measurement,
// so if other applications are running, they can create variance in this
// test.
start_commit_charge_ = base::GetSystemCommitCharge();
for (int i = 0; i < total_urls; i++)
urls_.push_back(urls[i]);
std::string url;
GetNextUrl(&url);
ASSERT_TRUE(!url.empty());
StartTest(url, test_name);
}
void OnNavigationSuccess(const VARIANT* param) {
ASSERT_TRUE(param != NULL);
ASSERT_EQ(VT_BSTR, param->vt);
DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
InitiateNextNavigation();
}
void OnNavigationFailure(const VARIANT* param) {
ASSERT_TRUE(param != NULL);
ASSERT_EQ(VT_BSTR, param->vt);
DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
InitiateNextNavigation();
}
protected:
bool GetNextUrl(std::string* url) {
if (current_url_index_ >= urls_.size())
return false;
*url = urls_[current_url_index_++];
return true;
}
void InitiateNextNavigation() {
// Get the memory consumption information for the child processes
// of the chrome browser.
ChromeProcessList child_processes = GetBrowserChildren();
ChromeProcessList::iterator index;
for (index = child_processes.begin(); index != child_processes.end();
++index) {
AccountProcessMemoryUsage(*index);
}
// TODO(iyengar): Bug 2953
// Need to verify if this is still true.
// The automation crashes periodically if we cycle too quickly.
// To make these tests more reliable, slowing them down a bit.
Sleep(200);
std::string url;
bool next_url = GetNextUrl(&url);
if (!url.empty()) {
NavigateImpl(url);
} else {
TestCompleted();
}
}
void PrintResults(const char* test_name) {
PrintMemoryUsageInfo(test_name);
memory_consumption_map_.clear();
// Added to give the OS some time to flush the used pages for the
// chrome processes which would have exited by now.
Sleep(200);
size_t end_commit_charge = base::GetSystemCommitCharge();
size_t commit_size = (end_commit_charge - start_commit_charge_) * 1024;
std::string trace_name(test_name);
trace_name.append("_cc");
PrintResult("commit_charge", "", trace_name,
commit_size / 1024, "KB", true /* important */);
printf("\n");
}
ChromeProcessList GetBrowserChildren() {
ChromeProcessList list = GetRunningChromeProcesses(browser_process_id());
ChromeProcessList::iterator browser =
std::find(list.begin(), list.end(), browser_process_id());
if (browser != list.end()) {
list.erase(browser);
}
return list;
}
void AccountProcessMemoryUsage(DWORD process_id) {
ProcessMemoryInfo process_memory_info(
process_id, process_id == browser_process_id(), this);
ASSERT_TRUE(process_memory_info.GetMemoryConsumptionDetails());
memory_consumption_map_[process_id] = process_memory_info;
}
void PrintMemoryUsageInfo(const char* test_name) {
printf("\n");
std::string trace_name(test_name);
ProcessMemoryConsumptionMap::iterator index;
size_t total_virtual_size = 0;
size_t total_working_set_size = 0;
for (index = memory_consumption_map_.begin();
index != memory_consumption_map_.end();
++index) {
ProcessMemoryInfo& memory_info = (*index).second;
memory_info.Print(test_name);
total_virtual_size += memory_info.virtual_size_;
total_working_set_size += memory_info.working_set_size_;
}
printf("\n");
PrintResult("vm_final_total", "", trace_name + "_vm",
total_virtual_size / 1024, "KB",
false /* not important */);
PrintResult("ws_final_total", "", trace_name + "_ws",
total_working_set_size / 1024, "KB",
true /* important */);
}
// Should never get called.
virtual void StartTest(const std::string& url,
const std::string& test_name) = 0 {
ASSERT_FALSE(false);
}
// Should never get called.
virtual void NavigateImpl(const std::string& url) = 0 {
ASSERT_FALSE(false);
}
virtual void TestCompleted() = 0 {
ASSERT_FALSE(false);
}
// Holds the commit charge in KBytes at the start of the memory test run.
size_t start_commit_charge_;
// The index of the URL being tested.
size_t current_url_index_;
// Contains the list of urls against which the tests are run.
std::vector<std::string> urls_;
ProcessMemoryConsumptionMap memory_consumption_map_;
};
// This class provides functionality to run the memory test against a reference
// chrome frame build.
class ChromeFrameMemoryTestReference : public ChromeFrameMemoryTest {
public:
virtual void SetUp() {
chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
chrome_frame_registrar_->RegisterReferenceChromeFrameBuild();
}
virtual void TearDown() {
// Reregisters the chrome frame DLL in the build directory.
chrome_frame_registrar_.reset(NULL);
}
};
// This class overrides the hooks provided by the ChromeFrameActiveXContainer
// class and calls back into the ChromeFrameMemoryTest object instance,
// which measures ChromeFrame memory usage.
class ChromeFrameActiveXContainerMemory : public ChromeFrameActiveXContainer {
public:
ChromeFrameActiveXContainerMemory()
: delegate_(NULL) {}
~ChromeFrameActiveXContainerMemory() {}
void Initialize(ChromeFrameMemoryTest* delegate) {
ASSERT_TRUE(delegate != NULL);
delegate_ = delegate;
}
protected:
virtual void OnLoadCallbackImpl(const VARIANT* param) {
delegate_->OnNavigationSuccess(param);
}
virtual void OnLoadErrorCallbackImpl(const VARIANT* param) {
delegate_->OnNavigationFailure(param);
}
ChromeFrameMemoryTest* delegate_;
};
// This class runs memory tests against the ChromeFrame ActiveX.
template<class MemoryTestBase>
class ChromeFrameActiveXMemoryTest : public MemoryTestBase {
public:
ChromeFrameActiveXMemoryTest()
: chrome_frame_container_(NULL),
test_completed_(false) {}
~ChromeFrameActiveXMemoryTest() {
}
void StartTest(const std::string& url, const std::string& test_name) {
ASSERT_TRUE(chrome_frame_container_ == NULL);
test_name_ = test_name;
SimpleModule module;
AtlAxWinInit();
CComObject<ChromeFrameActiveXContainerMemory>::CreateInstance(
&chrome_frame_container_);
chrome_frame_container_->AddRef();
chrome_frame_container_->Initialize(this);
chrome_frame_container_->CreateChromeFrameWindow(url.c_str());
chrome_frame_container_->CreateControl(true);
module.RunMessageLoop();
chrome_frame_container_->Release();
PrintResults(test_name_.c_str());
CoFreeUnusedLibraries();
PlatformThread::Sleep(100);
}
void NavigateImpl(const std::string& url) {
ASSERT_TRUE(chrome_frame_container_ != NULL);
ASSERT_TRUE(!url.empty());
chrome_frame_container_->Navigate(url.c_str());
}
void TestCompleted() {
// This can get called multiple times if the last url results in a
// redirect.
if (!test_completed_) {
// Measure memory usage for the browser process.
AccountProcessMemoryUsage(browser_process_id());
// Measure memory usage for the current process.
AccountProcessMemoryUsage(GetCurrentProcessId());
test_completed_ = true;
EXPECT_TRUE(PostMessage(static_cast<HWND>(*chrome_frame_container_),
WM_CLOSE, 0, 0));
}
}
protected:
CComObject<ChromeFrameActiveXContainerMemory>* chrome_frame_container_;
std::string test_name_;
bool test_completed_;
};
// This class runs tests to measure chrome frame creation only. This will help
// track overall page load performance with chrome frame instances.
class ChromeFrameCreationTest : public ChromeFrameStartupTest {
protected:
virtual void RunStartupTestImpl(TimeTicks* start_time,
TimeTicks* end_time) {
SimpleModule module;
AtlAxWinInit();
CComObjectStackEx<ChromeFrameActiveXContainer> wnd;
wnd.CreateChromeFrameWindow(startup_url_);
*start_time = TimeTicks::Now();
wnd.CreateControl(false);
*end_time = TimeTicks::Now();
}
};
// This class provides functionality to run the chrome frame
// performance test against a reference build.
class ChromeFrameCreationTestReference : public ChromeFrameCreationTest {
public:
// override the browser directory to use the reference build instead.
virtual void SetUp() {
chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
chrome_frame_registrar_->RegisterReferenceChromeFrameBuild();
ChromeFrameStartupTest::SetUp();
}
virtual void TearDown() {
chrome_frame_registrar_.reset(NULL);
}
};
// This class measures the creation time for Flash, which would be used
// as a baseline to measure chrome frame creation performance.
class FlashCreationTest : public ChromeFrameStartupTest {
protected:
virtual void RunStartupTestImpl(TimeTicks* start_time,
TimeTicks* end_time) {
SimpleModule module;
AtlAxWinInit();
CAxWindow host;
RECT rc = {0, 0, 800, 600};
host.Create(NULL, rc, NULL,
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
EXPECT_TRUE(host.m_hWnd != NULL);
*start_time = TimeTicks::Now();
HRESULT hr = host.CreateControl(L"ShockwaveFlash.ShockwaveFlash");
EXPECT_HRESULT_SUCCEEDED(hr);
*end_time = TimeTicks::Now();
ReleaseHostComReferences(host);
}
};
// This class measures the creation time for Silverlight, which would be used
// as a baseline to measure chrome frame creation performance.
class SilverlightCreationTest : public ChromeFrameStartupTest {
protected:
virtual void RunStartupTestImpl(TimeTicks* start_time,
TimeTicks* end_time) {
SimpleModule module;
AtlAxWinInit();
CAxWindow host;
RECT rc = {0, 0, 800, 600};
host.Create(NULL, rc, NULL,
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
EXPECT_TRUE(host.m_hWnd != NULL);
*start_time = TimeTicks::Now();
HRESULT hr = host.CreateControl(L"AgControl.AgControl");
EXPECT_HRESULT_SUCCEEDED(hr);
*end_time = TimeTicks::Now();
ReleaseHostComReferences(host);
}
};
TEST(ChromeFramePerf, DISABLED_HostActiveX) {
// TODO(stoyan): Create a low integrity level thread && perform the test there
SimpleModule module;
AtlAxWinInit();
CComObjectStackEx<ChromeFrameActiveXContainerPerf> wnd;
wnd.CreateChromeFrameWindow("http://www.google.com");
wnd.CreateControl(true);
module.RunMessageLoop();
}
TEST(ChromeFramePerf, DISABLED_HostActiveXInvalidURL) {
// TODO(stoyan): Create a low integrity level thread && perform the test there
SimpleModule module;
AtlAxWinInit();
CComObjectStackEx<ChromeFrameActiveXContainerPerf> wnd;
wnd.CreateChromeFrameWindow("http://non-existent-domain.org/");
wnd.CreateControl(true);
module.RunMessageLoop();
}
TEST_F(ChromeFrameStartupTestActiveX, PerfWarm) {
RunStartupTest("warm", "t", "about:blank", false /* cold */, 0, NULL,
true /* important */, false);
}
TEST_F(ChromeFrameBinariesLoadTest, PerfWarm) {
RunStartupTest("binary_load_warm", "t", "", false /* cold */, 0, NULL,
true /* important */, false);
}
TEST_F(ChromeFrameStartupTestActiveX, PerfCold) {
SetConfigInt(L"PreRead", 0);
FilePath binaries_to_evict[] = { gears_dll_, avcodec52_dll_,
avformat52_dll_, avutil50_dll_, chrome_exe_, chrome_dll_,
chrome_frame_dll_};
RunStartupTest("cold", "t", "about:blank", true /* cold */,
arraysize(binaries_to_evict), binaries_to_evict,
false /* not important */, false);
DeleteConfigValue(L"PreRead");
}
TEST_F(ChromeFrameStartupTestActiveX, PerfColdPreRead) {
SetConfigInt(L"PreRead", 1);
FilePath binaries_to_evict[] = { gears_dll_, avcodec52_dll_,
avformat52_dll_, avutil50_dll_, chrome_exe_, chrome_dll_,
chrome_frame_dll_};
RunStartupTest("cold_preread", "t", "about:blank", true /* cold */,
arraysize(binaries_to_evict), binaries_to_evict,
false /* not important */, false);
DeleteConfigValue(L"PreRead");
}
TEST_F(ChromeFrameBinariesLoadTest, PerfCold) {
FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_};
RunStartupTest("binary_load_cold", "t", "", true /* cold */,
arraysize(binaries_to_evict), binaries_to_evict,
false /* not important */, false);
}
TEST_F(ChromeFrameBinariesLoadTest, PerfColdPreRead) {
FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_};
pre_read_ = true;
RunStartupTest("binary_load_cold_preread", "t", "", true /* cold */,
arraysize(binaries_to_evict), binaries_to_evict,
false /* not important */, false);
}
TEST_F(ChromeFrameStartupTestActiveXReference, PerfWarm) {
RunStartupTest("warm", "t_ref", "about:blank", false /* cold */, 0, NULL,
true /* important */, false);
}
TEST_F(ChromeFrameStartupTestActiveX, PerfChromeFrameInitializationWarm) {
RunStartupTest("ChromeFrame_init_warm", "t", "", false /* cold */, 0,
NULL, true /* important */, false);
}
TEST_F(ChromeFrameStartupTestActiveX, PerfChromeFrameInitializationCold) {
FilePath binaries_to_evict[] = {chrome_frame_dll_};
RunStartupTest("ChromeFrame_init_cold", "t", "", true /* cold */,
arraysize(binaries_to_evict), binaries_to_evict,
false /* not important */, false);
}
TEST_F(ChromeFrameStartupTestActiveXReference,
PerfChromeFrameInitializationWarm) {
RunStartupTest("ChromeFrame_init_warm", "t_ref", "", false /* cold */, 0,
NULL, true /* important */, false);
}
typedef ChromeFrameActiveXMemoryTest<ChromeFrameMemoryTest>
RegularChromeFrameActiveXMemoryTest;
TEST_F(RegularChromeFrameActiveXMemoryTest, MemoryTestAboutBlank) {
char *urls[] = {"about:blank"};
RunTest("memory_about_blank", urls, arraysize(urls));
}
// TODO(iyengar)
// Revisit why the chrome frame dll does not unload correctly when this test is
// run.
// http://code.google.com/p/chromium/issues/detail?id=47812
TEST_F(RegularChromeFrameActiveXMemoryTest, DISABLED_MemoryTestUrls) {
// TODO(iyengar)
// We should use static pages to measure memory usage.
char *urls[] = {
"http://www.youtube.com/watch?v=PN2HAroA12w",
"http://www.youtube.com/watch?v=KmLJDrsaJmk&feature=channel"
};
RunTest("memory", urls, arraysize(urls));
}
typedef ChromeFrameActiveXMemoryTest<ChromeFrameMemoryTestReference>
ReferenceBuildChromeFrameActiveXMemoryTest;
// Disabled to investigate why the chrome frame dll does not unload while
// running this test.
// http://code.google.com/p/chromium/issues/detail?id=47812
TEST_F(ReferenceBuildChromeFrameActiveXMemoryTest,
DISABLED_MemoryTestAboutBlank) {
char *urls[] = {"about:blank"};
RunTest("memory_about_blank_reference", urls, arraysize(urls));
}
// TODO(iyengar)
// Revisit why the chrome frame dll does not unload correctly when this test is
// run.
TEST_F(ReferenceBuildChromeFrameActiveXMemoryTest, DISABLED_MemoryTestUrls) {
// TODO(iyengar)
// We should use static pages to measure memory usage.
char *urls[] = {
"http://www.youtube.com/watch?v=PN2HAroA12w",
"http://www.youtube.com/watch?v=KmLJDrsaJmk&feature=channel"
};
RunTest("memory_reference", urls, arraysize(urls));
}
TEST_F(ChromeFrameCreationTest, PerfWarm) {
RunStartupTest("creation_warm", "t", "", false /* cold */, 0,
NULL, true /* important */, false);
}
TEST_F(ChromeFrameCreationTestReference, PerfWarm) {
RunStartupTest("creation_warm", "t_ref", "about:blank", false /* cold */, 0,
NULL, true /* not important */, false);
}
TEST_F(FlashCreationTest, PerfWarm) {
RunStartupTest("creation_warm", "t_flash", "", false /* cold */, 0, NULL,
true /* not important */, false);
}
TEST_F(SilverlightCreationTest, DISABLED_PerfWarm) {
RunStartupTest("creation_warm", "t_silverlight", "", false /* cold */, 0,
NULL, false /* not important */, false);
}
TEST_F(ChromeFrameCreationTest, PerfCold) {
FilePath binaries_to_evict[] = {chrome_frame_dll_};
RunStartupTest("creation_cold", "t", "", true /* cold */,
arraysize(binaries_to_evict), binaries_to_evict,
true /* important */, false);
}
// Attempt to evict the Flash control can fail on the buildbot as the dll
// is marked read only. The test run is aborted if we fail to evict the file
// from the cache. This could also fail if the Flash control is in use.
// On Vista this could fail because of UAC
TEST_F(FlashCreationTest, PerfCold) {
RegKey flash_key(HKEY_CLASSES_ROOT, kFlashControlKey);
std::wstring plugin_path;
ASSERT_TRUE(flash_key.ReadValue(L"", &plugin_path));
ASSERT_FALSE(plugin_path.empty());
FilePath flash_path = FilePath::FromWStringHack(plugin_path);
FilePath binaries_to_evict[] = {flash_path};
RunStartupTest("creation_cold", "t_flash", "", true /* cold */,
arraysize(binaries_to_evict), binaries_to_evict,
false/* important */, true);
}
// This test would fail on Vista due to UAC or if the Silverlight control is
// in use. The test run is aborted if we fail to evict the file from the cache.
// Disabling this test as the Silverlight dll does not seem to get unloaded
// correctly causing the attempt to evict the dll from the system cache to
// fail.
TEST_F(SilverlightCreationTest, DISABLED_PerfCold) {
RegKey silverlight_key(HKEY_CLASSES_ROOT, kSilverlightControlKey);
std::wstring plugin_path;
ASSERT_TRUE(silverlight_key.ReadValue(L"", &plugin_path));
ASSERT_FALSE(plugin_path.empty());
FilePath silverlight_path = FilePath::FromWStringHack(plugin_path);
FilePath binaries_to_evict[] = {silverlight_path};
RunStartupTest("creation_cold", "t_silverlight", "", true /* cold */,
arraysize(binaries_to_evict), binaries_to_evict,
false /* important */, true);
}