blob: 099beab662b19fca81e6a5b1074fda960a85630c [file] [log] [blame]
// Copyright (c) 2006-2009 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 "base/basictypes.h"
#include "base/command_line.h"
#include "base/eintr_wrapper.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/string_util.h"
#include "chrome/browser/net/url_fixer_upper.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/chrome_process_util.h"
#include "chrome/test/ui/ui_test.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_util.h"
#if defined(OS_MACOSX)
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/resource.h>
#endif
#ifndef NDEBUG
#define TEST_ITERATIONS "2"
#else
#define TEST_ITERATIONS "10"
#endif
// URL at which data files may be found for HTTP tests. The document root of
// this URL's server should point to data/page_cycler/.
static const char kBaseUrl[] = "http://localhost:8000/";
namespace {
#if defined(OS_MACOSX)
// TODO(tvl/stuart): remove all this fd limit setting on the Mac when/if we
// replace the Mac reference build. The trunk raises the limit within the
// browser app, but we do this here also so the current reference build without
// that code will pass the intl[12] tests. This keeps us on an older
// reference build for all the other reference tests (javascript benchmarks,
// tab switch, etc.).
rlim_t GetFileDescriptorLimit(void) {
struct rlimit limits;
if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
return limits.rlim_cur;
}
PLOG(ERROR) << "Failed to get file descriptor limit";
return 0;
}
void SetFileDescriptorLimit(rlim_t max_descriptors) {
struct rlimit limits;
if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
if (limits.rlim_max == 0) {
limits.rlim_cur = max_descriptors;
} else {
limits.rlim_cur = std::min(max_descriptors, limits.rlim_max);
}
if (setrlimit(RLIMIT_NOFILE, &limits) != 0) {
PLOG(ERROR) << "Failed to set file descriptor limit";
}
} else {
PLOG(ERROR) << "Failed to get file descriptor limit";
}
}
void PopulateUBC(const FilePath &test_dir) {
// This will recursively walk the directory given and read all the files it
// finds. This is done so the Mac UBC is likely to have as much loaded as
// possible. Without this, the tests of this build gets one set of timings
// and then the reference build test, gets slightly faster ones (even if the
// reference build is the same binary). The hope is by forcing all the
// possible data into the UBC we equalize the tests for comparing timing data.
// We don't want to walk into .svn dirs, so we have to do the tree walk
// ourselves.
std::vector<FilePath> dirs;
dirs.push_back(test_dir);
const FilePath svn_dir(FILE_PATH_LITERAL(".svn"));
for (size_t idx = 0; idx < dirs.size(); ++idx) {
file_util::FileEnumerator dir_enumerator(dirs[idx], false,
file_util::FileEnumerator::DIRECTORIES);
FilePath path;
for (path = dir_enumerator.Next();
!path.empty();
path = dir_enumerator.Next()) {
if (path.BaseName() != svn_dir)
dirs.push_back(path);
}
}
char buf[1024];
unsigned int loaded = 0;
// We seem to have some files in the data dirs that are just there for
// reference, make a quick attempt to skip them by matching suffixes.
std::vector<FilePath::StringType> ignore_suffixes;
ignore_suffixes.push_back(FILE_PATH_LITERAL(".orig.html"));
ignore_suffixes.push_back(FILE_PATH_LITERAL(".html-original"));
std::vector<FilePath>::const_iterator iter;
for (iter = dirs.begin(); iter != dirs.end(); ++iter) {
file_util::FileEnumerator file_enumerator(*iter, false,
file_util::FileEnumerator::FILES);
FilePath path;
for (path = file_enumerator.Next();
!path.empty();
path = file_enumerator.Next()) {
const FilePath base_name = path.BaseName();
const size_t base_name_size = base_name.value().size();
bool should_skip = false;
std::vector<FilePath::StringType>::const_iterator ignore_iter;
for (ignore_iter = ignore_suffixes.begin();
ignore_iter != ignore_suffixes.end();
++ignore_iter) {
const FilePath::StringType &suffix = *ignore_iter;
if ((base_name_size > suffix.size()) &&
(base_name.value().compare(base_name_size - suffix.size(),
suffix.size(), suffix) == 0)) {
should_skip = true;
break;
}
}
if (should_skip)
continue;
// Read the file to get it into the UBC
int fd = open(path.value().c_str(), O_RDONLY);
if (fd >= 0) {
++loaded;
while (HANDLE_EINTR(read(fd, buf, sizeof(buf))) > 0) {
}
HANDLE_EINTR(close(fd));
}
}
}
LOG(INFO) << "UBC should be loaded with " << loaded << " files.";
}
#endif // defined(OS_MACOSX)
class PageCyclerTest : public UITest {
#if defined(OS_MACOSX)
protected:
rlim_t fd_limit_;
#endif
public:
PageCyclerTest() {
show_window_ = true;
// Expose garbage collection for the page cycler tests.
launch_arguments_.AppendSwitchWithValue(switches::kJavaScriptFlags,
L"--expose_gc");
#if defined(OS_MACOSX)
static rlim_t initial_fd_limit = GetFileDescriptorLimit();
fd_limit_ = initial_fd_limit;
#endif
}
void SetUp() {
#if defined(OS_MACOSX)
SetFileDescriptorLimit(fd_limit_);
#endif
UITest::SetUp();
}
// For HTTP tests, the name must be safe for use in a URL without escaping.
void RunPageCycler(const char* name, std::wstring* pages,
std::string* timings, bool use_http) {
// Make sure the test data is checked out
FilePath test_path;
PathService::Get(base::DIR_SOURCE_ROOT, &test_path);
test_path = test_path.Append(FILE_PATH_LITERAL("data"));
test_path = test_path.Append(FILE_PATH_LITERAL("page_cycler"));
test_path = test_path.AppendASCII(name);
ASSERT_TRUE(file_util::PathExists(test_path)) << "Missing test data"
<< test_path.value();
#if defined(OS_MACOSX)
PopulateUBC(test_path);
#endif
GURL test_url;
if (use_http) {
test_url = GURL(std::string(kBaseUrl) + name + "/start.html");
} else {
test_path = test_path.Append(FILE_PATH_LITERAL("start.html"));
test_url = net::FilePathToFileURL(test_path);
}
// run N iterations
GURL::Replacements replacements;
const char query_string[] = "iterations=" TEST_ITERATIONS "&auto=1";
replacements.SetQuery(
query_string,
url_parse::Component(0, arraysize(query_string) - 1));
test_url = test_url.ReplaceComponents(replacements);
scoped_refptr<TabProxy> tab(GetActiveTab());
ASSERT_TRUE(tab.get());
tab->NavigateToURL(test_url);
// Wait for the test to finish.
ASSERT_TRUE(WaitUntilCookieValue(tab.get(), test_url, "__pc_done",
3000, UITest::test_timeout_ms(), "1"));
std::string cookie;
ASSERT_TRUE(tab->GetCookieByName(test_url, "__pc_pages", &cookie));
pages->assign(UTF8ToWide(cookie));
ASSERT_FALSE(pages->empty());
ASSERT_TRUE(tab->GetCookieByName(test_url, "__pc_timings", &cookie));
timings->assign(cookie);
ASSERT_FALSE(timings->empty());
}
// When use_http is true, the test name passed here will be used directly in
// the path to the test data, so it must be safe for use in a URL without
// escaping. (No pound (#), question mark (?), semicolon (;), non-ASCII, or
// other funny stuff.)
void RunTestWithSuffix(const char* name, bool use_http, const char* suffix) {
std::wstring pages;
std::string timings;
size_t start_size = base::GetSystemCommitCharge();
RunPageCycler(name, &pages, &timings, use_http);
if (timings.empty())
return;
// wait 2 seconds to allow automation messages to print
PlatformThread::Sleep(2000);
size_t stop_size = base::GetSystemCommitCharge();
FilePath data_dir;
PathService::Get(chrome::DIR_USER_DATA, &data_dir);
PrintMemoryUsageInfo(suffix, data_dir);
PrintIOPerfInfo(suffix, data_dir);
PrintSystemCommitCharge(suffix, stop_size - start_size,
false /* not important */);
std::string trace_name = "t" + std::string(suffix);
wprintf(L"\nPages: [%ls]\n", pages.c_str());
PrintResultList("times", "", trace_name, timings, "ms",
true /* important */);
// wait 2 seconds to allow automation messages to print
PlatformThread::Sleep(2000);
}
void RunTest(const char* name, bool use_http) {
RunTestWithSuffix(name, use_http, "");
}
};
class PageCyclerReferenceTest : public PageCyclerTest {
public:
// override the browser directory that is used by UITest::SetUp to cause it
// to use the reference build instead.
void SetUp() {
#if defined(OS_MACOSX)
fd_limit_ = 1024;
#endif
FilePath dir;
PathService::Get(chrome::DIR_TEST_TOOLS, &dir);
dir = dir.AppendASCII("reference_build");
#if defined(OS_WIN)
dir = dir.AppendASCII("chrome");
#elif defined(OS_LINUX)
dir = dir.AppendASCII("chrome_linux");
#elif defined(OS_MACOSX)
dir = dir.AppendASCII("chrome_mac");
#endif
browser_directory_ = dir;
PageCyclerTest::SetUp();
}
void RunTest(const char* name, bool use_http) {
std::wstring pages;
std::string timings;
size_t start_size = base::GetSystemCommitCharge();
RunPageCycler(name, &pages, &timings, use_http);
if (timings.empty())
return;
size_t stop_size = base::GetSystemCommitCharge();
FilePath data_dir;
PathService::Get(chrome::DIR_USER_DATA, &data_dir);
PrintMemoryUsageInfo("_ref", data_dir);
PrintIOPerfInfo("_ref", data_dir);
PrintSystemCommitCharge("_ref", stop_size - start_size,
false /* not important */);
PrintResultList("times", "", "t_ref", timings, "ms",
true /* important */);
}
};
class PageCyclerExtensionTest : public PageCyclerTest {
public:
void SetUp() {}
void RunTest(const char* extension_profile, const char* output_suffix,
const char* name, bool use_http) {
// Set up the extension profile directory.
ASSERT_TRUE(extension_profile != NULL);
FilePath data_dir;
PathService::Get(chrome::DIR_TEST_DATA, &data_dir);
data_dir = data_dir.AppendASCII("extensions").AppendASCII("profiles").
AppendASCII(extension_profile);
ASSERT_TRUE(file_util::DirectoryExists(data_dir));
set_template_user_data(data_dir);
// Now run the test.
PageCyclerTest::SetUp();
PageCyclerTest::RunTestWithSuffix(name, use_http, output_suffix);
}
};
// This macro simplifies setting up regular and reference build tests.
#define PAGE_CYCLER_TESTS(test, name, use_http) \
TEST_F(PageCyclerTest, name) { \
RunTest(test, use_http); \
} \
TEST_F(PageCyclerReferenceTest, name) { \
RunTest(test, use_http); \
}
// These are shorthand for File vs. Http tests.
#define PAGE_CYCLER_FILE_TESTS(test, name)\
PAGE_CYCLER_TESTS(test, name, false)
#define PAGE_CYCLER_HTTP_TESTS(test, name)\
PAGE_CYCLER_TESTS(test, name, true)
// This macro lets us define tests with 1 and 10 extensions with 1 content
// script each. The name for the 10-extension case is changed so as not
// to run by default on the buildbots.
#define PAGE_CYCLER_EXTENSIONS_FILE_TESTS(test, name) \
TEST_F(PageCyclerExtensionTest, name) { \
RunTest("content_scripts1", "_extcs1", test, false); \
} \
TEST_F(PageCyclerExtensionTest, name##10) { \
RunTest("content_scripts10", "_extcs10", test, false); \
}
// file-URL tests
PAGE_CYCLER_FILE_TESTS("moz", MozFile);
PAGE_CYCLER_EXTENSIONS_FILE_TESTS("moz", MozFile);
PAGE_CYCLER_FILE_TESTS("intl1", Intl1File);
PAGE_CYCLER_FILE_TESTS("intl2", Intl2File);
PAGE_CYCLER_FILE_TESTS("dom", DomFile);
PAGE_CYCLER_FILE_TESTS("dhtml", DhtmlFile);
PAGE_CYCLER_FILE_TESTS("morejs", MorejsFile);
PAGE_CYCLER_EXTENSIONS_FILE_TESTS("morejs", MorejsFile);
// http (localhost) tests
PAGE_CYCLER_HTTP_TESTS("moz", MozHttp);
PAGE_CYCLER_HTTP_TESTS("intl1", Intl1Http);
PAGE_CYCLER_HTTP_TESTS("intl2", Intl2Http);
PAGE_CYCLER_HTTP_TESTS("dom", DomHttp);
PAGE_CYCLER_HTTP_TESTS("bloat", BloatHttp);
} // namespace