Home | History | Annotate | Download | only in perf
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/basictypes.h"
      6 #include "base/command_line.h"
      7 #include "base/file_util.h"
      8 #include "base/files/file_path.h"
      9 #include "base/path_service.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/threading/platform_thread.h"
     12 #include "base/threading/thread_restrictions.h"
     13 #include "chrome/common/chrome_constants.h"
     14 #include "chrome/common/chrome_paths.h"
     15 #include "chrome/common/chrome_switches.h"
     16 #include "chrome/common/net/url_fixer_upper.h"
     17 #include "chrome/test/automation/automation_proxy.h"
     18 #include "chrome/test/automation/browser_proxy.h"
     19 #include "chrome/test/automation/tab_proxy.h"
     20 #include "chrome/test/automation/window_proxy.h"
     21 #include "chrome/test/base/chrome_process_util.h"
     22 #include "chrome/test/perf/perf_test.h"
     23 #include "chrome/test/ui/ui_perf_test.h"
     24 #include "gpu/command_buffer/service/gpu_switches.h"
     25 #include "net/base/net_util.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 #include "url/gurl.h"
     28 
     29 namespace {
     30 
     31 static const base::FilePath::CharType kTempDirName[] =
     32     FILE_PATH_LITERAL("memory_test_profile");
     33 
     34 class MemoryTest : public UIPerfTest {
     35  public:
     36   MemoryTest() : cleanup_temp_dir_on_exit_(false) {}
     37 
     38   virtual ~MemoryTest() {
     39     // Cleanup our temporary directory.
     40     if (cleanup_temp_dir_on_exit_)
     41       base::DeleteFile(temp_dir_, true);
     42   }
     43 
     44   // Called from SetUp() to determine the user data dir to copy.
     45   virtual base::FilePath GetUserDataDirSource() const = 0;
     46 
     47   // Called from RunTest() to determine the set of URLs to retrieve.
     48   // Returns the length of the list.
     49   virtual size_t GetUrlList(std::string** list) = 0;
     50 
     51   virtual void SetUp() {
     52     show_window_ = true;
     53 
     54     // For now, turn off plugins because they crash like crazy.
     55     // TODO(mbelshe): Fix Chrome to not crash with plugins.
     56     launch_arguments_.AppendSwitch(switches::kDisablePlugins);
     57 
     58     launch_arguments_.AppendSwitch(switches::kEnableLogging);
     59 
     60 #if defined (OS_MACOSX)
     61     // On Mac the OpenGL driver will leave around active pages for GPU
     62     // memory, resulting in a massive reported memory regression. Limit
     63     // the impact of this by minimizing the amount of GPU memory used.
     64     // http://crbug.com/178531
     65     launch_arguments_.AppendSwitchASCII(switches::kForceGpuMemAvailableMb,
     66                                         "1");
     67 #endif
     68 
     69     // In order to record a dataset to cache for future playback,
     70     // set the |playback| to false and run the test. The source user data dir
     71     // will be populated with an appropriate cache set.  If any of source
     72     // urls are particularly slow, setting |kMaxWaitTime| higher while record
     73     // may be useful.
     74     bool playback = true;
     75     if (playback) {
     76       // Use the playback cache, but don't use playback events.
     77       launch_arguments_.AppendSwitch(switches::kPlaybackMode);
     78       launch_arguments_.AppendSwitch(switches::kNoEvents);
     79       launch_arguments_.AppendSwitch(switches::kDisableGpuShaderDiskCache);
     80 
     81       // Get the specified user data dir (optional)
     82       base::FilePath profile_dir =
     83           CommandLine::ForCurrentProcess()->GetSwitchValuePath(
     84           switches::kUserDataDir);
     85 
     86       if (profile_dir.empty()) {
     87         if (!SetupTempDirectory(GetUserDataDirSource())) {
     88           // There isn't really a way to fail gracefully here.
     89           // Neither this constructor nor the SetUp() method return
     90           // status to the caller.  So, just fall through using the
     91           // default profile and log this.  The failure will be
     92           // obvious.
     93           LOG(ERROR) << "Error preparing temp directory for test";
     94         }
     95       }
     96     } else {  // Record session.
     97       launch_arguments_.AppendSwitch(switches::kRecordMode);
     98       launch_arguments_.AppendSwitch(switches::kNoEvents);
     99 
    100       user_data_dir_ = GetUserDataDirSource();
    101     }
    102 
    103     launch_arguments_.AppendSwitchPath(switches::kUserDataDir, user_data_dir_);
    104     UITest::SetUp();
    105   }
    106 
    107   // This memory test loads a set of URLs across a set of tabs, maintaining the
    108   // number of concurrent open tabs at num_target_tabs.
    109   // <NEWTAB> is a special URL which informs the loop when we should create a
    110   // new tab.
    111   // <PAUSE> is a special URL that informs the loop to pause before proceeding
    112   // to the next URL.
    113   void RunTest(const char* test_name, int num_target_tabs) {
    114     std::string* urls;
    115     size_t urls_length = GetUrlList(&urls);
    116 
    117     // Record the initial CommitCharge.  This is a system-wide measurement,
    118     // so if other applications are running, they can create variance in this
    119     // test.
    120     size_t start_size = base::GetSystemCommitCharge();
    121 
    122     // Cycle through the URLs.
    123     scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
    124     ASSERT_TRUE(window.get());
    125     int active_window = 0;  // The index of the window we are currently using.
    126     scoped_refptr<TabProxy> tab(window->GetActiveTab());
    127     ASSERT_TRUE(tab.get());
    128     int expected_tab_count = 1;
    129     for (unsigned counter = 0; counter < urls_length; ++counter) {
    130       std::string url = urls[counter];
    131 
    132       SCOPED_TRACE(url);
    133 
    134       if (url == "<PAUSE>") {  // Special command to delay on this page
    135         base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
    136         continue;
    137       }
    138 
    139       if (url == "<NEWTAB>") {  // Special command to create a new tab
    140         if (++counter >= urls_length)
    141           continue;  // Newtab was specified at end of list.  ignore.
    142 
    143         url = urls[counter];
    144         if (GetTabCount() < num_target_tabs) {
    145           EXPECT_TRUE(window->AppendTab(GURL(url)));
    146           expected_tab_count++;
    147           WaitUntilTabCount(expected_tab_count);
    148           tab = window->GetActiveTab();
    149           ASSERT_TRUE(tab.get());
    150           continue;
    151         }
    152 
    153         int tab_index = counter % num_target_tabs;  // A pseudo-random tab.
    154         tab = window->GetTab(tab_index);
    155         ASSERT_TRUE(tab.get());
    156       }
    157 
    158       if (url == "<NEXTTAB>") {  // Special command to select the next tab.
    159         int tab_index, tab_count;
    160         EXPECT_TRUE(window->GetActiveTabIndex(&tab_index));
    161         EXPECT_TRUE(window->GetTabCount(&tab_count));
    162         tab_index = (tab_index + 1) % tab_count;
    163         tab = window->GetTab(tab_index);
    164         ASSERT_TRUE(tab.get());
    165         continue;
    166       }
    167 
    168       if (url == "<NEWWINDOW>") {  // Special command to create a new window.
    169         if (counter + 1 >= urls_length)
    170           continue;  // Newwindows was specified at end of list.  ignore.
    171 
    172         int window_count;
    173         EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count));
    174         EXPECT_TRUE(automation()->OpenNewBrowserWindow(Browser::TYPE_TABBED,
    175                                                        show_window_));
    176         int expected_window_count = window_count + 1;
    177         EXPECT_TRUE(automation()->WaitForWindowCountToBecome(
    178             expected_window_count));
    179         EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count));
    180         EXPECT_EQ(expected_window_count, window_count);
    181 
    182         // A new window will not load a url if requested too soon. The window
    183         // stays on the new tab page instead.
    184         base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
    185 
    186         active_window = window_count - 1;
    187         window = automation()->GetBrowserWindow(active_window);
    188         ASSERT_TRUE(window.get());
    189         tab = window->GetActiveTab();
    190         ASSERT_TRUE(tab.get());
    191         continue;
    192       }
    193 
    194       if (url == "<NEXTWINDOW>") {  // Select the next window.
    195         int window_count;
    196         EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count));
    197         active_window = (active_window + 1) % window_count;
    198         window = automation()->GetBrowserWindow(active_window);
    199         ASSERT_TRUE(window.get());
    200         tab = window->GetActiveTab();
    201         ASSERT_TRUE(tab.get());
    202         continue;
    203       }
    204 
    205       EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
    206           tab->NavigateToURL(GURL(urls[counter])));
    207 
    208       // TODO(mbelshe): Bug 2953
    209       // The automation crashes periodically if we cycle too quickly.
    210       // To make these tests more reliable, slowing them down a bit.
    211       base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
    212     }
    213 
    214     size_t stop_size = base::GetSystemCommitCharge();
    215     PrintIOPerfInfo(test_name);
    216     PrintMemoryUsageInfo(test_name);
    217     perf_test::PrintSystemCommitCharge(test_name, stop_size - start_size,
    218                                        true /* important */);
    219   }
    220 
    221  private:
    222   // Setup a temporary directory to store the profile to use
    223   // with these tests.
    224   // Input:
    225   //   src_dir is set to the source directory
    226   // Output:
    227   //   On success, modifies user_data_dir_ to be a new profile directory
    228   //   sets temp_dir_ to the containing temporary directory,
    229   //   and sets cleanup_temp_dir_on_exit_ to true.
    230   bool SetupTempDirectory(const base::FilePath& src_dir) {
    231     VLOG(1) << "Setting up temp directory in " << src_dir.value();
    232     // We create a copy of the test dir and use it so that each
    233     // run of this test starts with the same data.  Running this
    234     // test has the side effect that it will change the profile.
    235     if (!file_util::CreateNewTempDirectory(kTempDirName, &temp_dir_)) {
    236       LOG(ERROR) << "Could not create temp directory:" << kTempDirName;
    237       return false;
    238     }
    239 
    240     if (!base::CopyDirectory(src_dir, temp_dir_, true)) {
    241       LOG(ERROR) << "Could not copy temp directory";
    242       return false;
    243     }
    244 
    245     // The profile directory was copied in to the containing temp
    246     // directory as its base name, so point user_data_dir_ there.
    247     user_data_dir_ = temp_dir_.Append(src_dir.BaseName());
    248     cleanup_temp_dir_on_exit_ = true;
    249     VLOG(1) << "Finished temp directory setup.";
    250     return true;
    251   }
    252 
    253   bool cleanup_temp_dir_on_exit_;
    254   base::FilePath temp_dir_;
    255   base::FilePath user_data_dir_;
    256   base::ThreadRestrictions::ScopedAllowIO allow_io_;
    257 };
    258 
    259 class GeneralMixMemoryTest : public MemoryTest {
    260  public:
    261   virtual base::FilePath GetUserDataDirSource() const OVERRIDE {
    262     base::FilePath profile_dir;
    263     PathService::Get(base::DIR_SOURCE_ROOT, &profile_dir);
    264     profile_dir = profile_dir.AppendASCII("data");
    265     profile_dir = profile_dir.AppendASCII("memory_test");
    266     profile_dir = profile_dir.AppendASCII("general_mix");
    267     return profile_dir;
    268   }
    269 
    270   virtual size_t GetUrlList(std::string** list) OVERRIDE {
    271     *list = urls_;
    272     return urls_length_;
    273   }
    274 
    275  private:
    276   static std::string urls_[];
    277   static size_t urls_length_;
    278 };
    279 
    280 // TODO(mbelshe): Separate this data to an external file.
    281 std::string GeneralMixMemoryTest::urls_[] = {
    282   "http://www.yahoo.com/",
    283   "http://hotjobs.yahoo.com/career-articles-the_biggest_resume_mistake_you_can_make-436",
    284   "http://news.yahoo.com/s/ap/20080804/ap_on_re_mi_ea/odd_israel_home_alone",
    285   "http://news.yahoo.com/s/nm/20080729/od_nm/subway_dc",
    286   "http://search.yahoo.com/search?p=new+york+subway&ygmasrchbtn=web+search&fr=ush-news",
    287   "<NEWTAB>",
    288   "http://www.cnn.com/",
    289   "http://www.cnn.com/2008/SHOWBIZ/TV/08/03/applegate.cancer.ap/index.html",
    290   "http://www.cnn.com/2008/HEALTH/conditions/07/29/black.aids.report/index.html",
    291   "http://www.cnn.com/POLITICS/",
    292   "http://search.cnn.com/search.jsp?query=obama&type=web&sortBy=date&intl=false",
    293   "<NEWTAB>",
    294   "http://mail.google.com/",
    295   "http://mail.google.com/mail/?shva=1",
    296   "http://mail.google.com/mail/?shva=1#search/ipsec",
    297   "http://mail.google.com/mail/?shva=1#search/ipsec/ee29ae66165d417",
    298   "http://mail.google.com/mail/?shva=1#compose",
    299   "<NEWTAB>",
    300   "http://docs.google.com/",
    301   "<NEWTAB>",
    302   "http://calendar.google.com/",
    303   "<NEWTAB>",
    304   "http://maps.google.com/",
    305   "http://maps.google.com/maps/mpl?moduleurl=http://earthquake.usgs.gov/eqcenter/mapplets/earthquakes.xml&ie=UTF8&ll=20,170&spn=140.625336,73.828125&t=k&z=2",
    306   "http://maps.google.com/maps?f=q&hl=en&geocode=&q=1600+amphitheater+parkway,+mountain+view,+ca&ie=UTF8&z=13",
    307   "<NEWTAB>",
    308   "http://www.google.com/",
    309   "http://www.google.com/search?hl=en&q=food&btnG=Google+Search",
    310   "http://books.google.com/books?hl=en&q=food&um=1&ie=UTF-8&sa=N&tab=wp",
    311   "http://images.google.com/images?hl=en&q=food&um=1&ie=UTF-8&sa=N&tab=pi",
    312   "http://news.google.com/news?hl=en&q=food&um=1&ie=UTF-8&sa=N&tab=in",
    313   "http://www.google.com/products?sa=N&tab=nf&q=food",
    314   "<NEWTAB>",
    315   "http://www.scoundrelspoint.com/polyhedra/shuttle/index.html",
    316   "<PAUSE>",
    317   "<NEWTAB>",
    318   "http://ctho.ath.cx/toys/3d.html",
    319   "<PAUSE>",
    320   "<NEWTAB>",
    321   "http://www.youtube.com/",
    322   "http://www.youtube.com/results?search_query=funny&search_type=&aq=f",
    323   "http://www.youtube.com/watch?v=GuMMfgWhm3g",
    324   "<NEWTAB>",
    325   "http://www.craigslist.com/",
    326   "http://sfbay.craigslist.org/",
    327   "http://sfbay.craigslist.org/apa/",
    328   "http://sfbay.craigslist.org/sfc/apa/782398209.html",
    329   "http://sfbay.craigslist.org/sfc/apa/782347795.html",
    330   "http://sfbay.craigslist.org/sby/apa/782342791.html",
    331   "http://sfbay.craigslist.org/sfc/apa/782344396.html",
    332   "<NEWTAB>",
    333   "http://www.whitehouse.gov/",
    334   "http://www.whitehouse.gov/news/releases/2008/07/20080729.html",
    335   "http://www.whitehouse.gov/infocus/afghanistan/",
    336   "http://www.whitehouse.gov/infocus/africa/",
    337   "<NEWTAB>",
    338   "http://www.msn.com/",
    339   "http://msn.foxsports.com/horseracing/story/8409670/Big-Brown-rebounds-in-Haskell-Invitational?MSNHPHMA",
    340   "http://articles.moneycentral.msn.com/Investing/StockInvestingTrading/TheBiggestRiskToYourRetirement_SeriesHome.aspx",
    341   "http://articles.moneycentral.msn.com/Investing/StockInvestingTrading/TheSmartWayToGetRich.aspx",
    342   "http://articles.moneycentral.msn.com/Investing/ContrarianChronicles/TheFictionOfCorporateTransparency.aspx",
    343   "<NEWTAB>",
    344   "http://flickr.com/",
    345   "http://flickr.com/explore/interesting/2008/03/18/",
    346   "http://flickr.com/photos/chavals/2344906748/",
    347   "http://flickr.com/photos/rosemary/2343058024/",
    348   "http://flickr.com/photos/arbaa/2343235019/",
    349   "<NEWTAB>",
    350   "http://zh.wikipedia.org/wiki/%E6%B1%B6%E5%B7%9D%E5%A4%A7%E5%9C%B0%E9%9C%87",
    351   "http://zh.wikipedia.org/wiki/5%E6%9C%8812%E6%97%A5",
    352   "http://zh.wikipedia.org/wiki/5%E6%9C%8820%E6%97%A5",
    353   "http://zh.wikipedia.org/wiki/%E9%A6%96%E9%A1%B5",
    354   "<NEWTAB>",
    355   "http://www.nytimes.com/pages/technology/index.html",
    356   "http://pogue.blogs.nytimes.com/2008/07/17/a-candy-store-for-the-iphone/",
    357   "http://www.nytimes.com/2008/07/21/technology/21pc.html?_r=1&ref=technology&oref=slogin",
    358   "http://bits.blogs.nytimes.com/2008/07/19/a-wikipedian-challenge-convincing-arabic-speakers-to-write-in-arabic/",
    359   "<NEWTAB>",
    360   "http://www.amazon.com/exec/obidos/tg/browse/-/502394/ref=topnav_storetab_p",
    361   "http://www.amazon.com/Panasonic-DMC-TZ5K-Digital-Optical-Stabilized/dp/B0011Z8CCG/ref=pd_ts_p_17?ie=UTF8&s=photo",
    362   "http://www.amazon.com/Nikon-Coolpix-Digital-Vibration-Reduction/dp/B0012OI6HW/ref=pd_ts_p_24?ie=UTF8&s=photo",
    363   "http://www.amazon.com/Digital-SLRs-Cameras-Photo/b/ref=sv_p_2?ie=UTF8&node=3017941",
    364   "<NEWTAB>",
    365   "http://www.boston.com/bigpicture/2008/07/californias_continuing_fires.html",
    366   "http://www.boston.com/business/",
    367   "http://www.boston.com/business/articles/2008/07/29/staples_has_a_games_plan/",
    368   "http://www.boston.com/business/personalfinance/articles/2008/08/04/a_grim_forecast_for_heating_costs/",
    369   "<NEWTAB>",
    370   "http://arstechnica.com/",
    371   "http://arstechnica.com/news.ars/post/20080721-this-years-e3-substance-over-styleand-far-from-dead.html",
    372   "http://arstechnica.com/news.ars/post/20080729-ifpi-italian-police-take-down-italian-bittorrent-tracker.html",
    373   "http://arstechnica.com/news.ars/post/20080804-congress-wants-privacy-answers-from-google-ms-aol.html",
    374   "<NEWTAB>",
    375   "http://finance.google.com/finance?q=NASDAQ:AAPL",
    376   "http://finance.google.com/finance?q=GOOG&hl=en",
    377   "<NEWTAB>",
    378   "http://blog.wired.com/underwire/2008/07/futurama-gets-m.html",
    379   "http://blog.wired.com/cars/2008/07/gas-prices-hit.html",
    380   "<NEWTAB>",
    381   "http://del.icio.us/popular/programming",
    382   "http://del.icio.us/popular/",
    383   "http://del.icio.us/tag/",
    384   "<NEWTAB>",
    385   "http://gadgets.boingboing.net/2008/07/21/boom-computing.html",
    386   "http://3533.spreadshirt.com/us/US/Shop/",
    387   "<NEWTAB>",
    388   "http://www.autoblog.com/",
    389   "http://www.autoblog.com/2008/07/21/audi-introduces-the-next-mmi/",
    390   "http://www.autoblog.com/categories/auto-types/",
    391   "http://www.autoblog.com/category/sports/",
    392   "<NEWTAB>",
    393   "http://www.wikipedia.org/",
    394   "http://en.wikipedia.org/wiki/Main_Page",
    395   "http://fr.wikipedia.org/wiki/Accueil",
    396   "http://de.wikipedia.org/wiki/Hauptseite",
    397   "http://ja.wikipedia.org/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8",
    398   "http://it.wikipedia.org/wiki/Pagina_principale",
    399   "http://nl.wikipedia.org/wiki/Hoofdpagina",
    400   "http://pt.wikipedia.org/wiki/P%C3%A1gina_principal",
    401   "http://es.wikipedia.org/wiki/Portada",
    402   "http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0",
    403   "<NEWTAB>",
    404   "http://www.google.com/translate_t?hl=en&text=This%20Is%20A%20Test%20Of%20missspellingsdfdf&sl=en&tl=ja"
    405 };
    406 
    407 size_t GeneralMixMemoryTest::urls_length_ =
    408     arraysize(GeneralMixMemoryTest::urls_);
    409 
    410 class GeneralMixReferenceMemoryTest : public GeneralMixMemoryTest {
    411  public:
    412   virtual void SetUp() {
    413     UseReferenceBuild();
    414     GeneralMixMemoryTest::SetUp();
    415   }
    416 };
    417 
    418 class MembusterMemoryTest : public MemoryTest {
    419  public:
    420   MembusterMemoryTest() : test_urls_(NULL) {}
    421 
    422   virtual ~MembusterMemoryTest() {
    423     delete[] test_urls_;
    424   }
    425 
    426   virtual base::FilePath GetUserDataDirSource() const OVERRIDE {
    427     base::FilePath profile_dir;
    428     PathService::Get(base::DIR_SOURCE_ROOT, &profile_dir);
    429     profile_dir = profile_dir.AppendASCII("data");
    430     profile_dir = profile_dir.AppendASCII("memory_test");
    431     profile_dir = profile_dir.AppendASCII("membuster");
    432     return profile_dir;
    433   }
    434 
    435   virtual size_t GetUrlList(std::string** list) OVERRIDE {
    436     size_t total_url_entries = urls_length_ * kIterations_ * 2 - 1;
    437     if (!test_urls_) {
    438       test_urls_ = new std::string[total_url_entries];
    439 
    440       // Open url_length_ + 1 windows as we access urls. We start with one
    441       // open window.
    442       test_urls_[0] = source_urls_[0];
    443       size_t fill_position = 1;
    444       size_t source_url_index = 1;
    445       for (; fill_position <= urls_length_ * 2; fill_position += 2) {
    446         test_urls_[fill_position] = "<NEWWINDOW>";
    447         test_urls_[fill_position + 1] = source_urls_[source_url_index];
    448         source_url_index = (source_url_index + 1) % urls_length_;
    449       }
    450 
    451       // Then cycle through all the urls to fill out the list.
    452       for (; fill_position < total_url_entries; fill_position += 2) {
    453         test_urls_[fill_position] = "<NEXTWINDOW>";
    454         test_urls_[fill_position + 1] = source_urls_[source_url_index];
    455         source_url_index = (source_url_index + 1) % urls_length_;
    456       }
    457     }
    458     *list = test_urls_;
    459     return total_url_entries;
    460   }
    461 
    462  private:
    463   static const int kIterations_ = 11;
    464 
    465   static std::string source_urls_[];
    466   static size_t urls_length_;
    467 
    468   std::string* test_urls_;
    469 };
    470 
    471 // membuster traverses the list in reverse order.  We just list them that way.
    472 std::string MembusterMemoryTest::source_urls_[] = {
    473   "http://joi.ito.com/archives/email/",
    474   "http://joi.ito.com/jp/",
    475   "http://forums.studentdoctor.net/showthread.php?t=469342",
    476   "http://forums.studentdoctor.net/forumdisplay.php?s=718b9d0e8692d7c3f4cc7c64faffd17b&f=10",
    477   "http://de.wikipedia.org/wiki/Hauptseite",
    478   "http://zh.wikipedia.org/wiki/",
    479   "http://ru.wikipedia.org/wiki/",
    480   "http://ja.wikipedia.org/wiki/",
    481   "http://en.wikipedia.org/wiki/Main_Page",
    482   "http://wikitravel.org/ru/",
    483   "http://wikitravel.org/hi/",
    484   "http://wikitravel.org/he/",
    485   "http://wikitravel.org/ja/",
    486   "http://wikitravel.org/en/Main_Page",
    487   "http://wikitravel.org/en/China",
    488   "http://www.vodcars.com/",
    489   "http://en.wikinews.org/wiki/Main_Page",
    490   "http://creativecommons.org/",
    491   "http://pushingdaisies.wikia.com/wiki/Pushing_Daisies",
    492   "http://www.wowwiki.com/Main_Page",
    493   "http://spademanns.wikia.com/wiki/Forside",
    494   "http://ja.uncyclopedia.info/wiki/",
    495   "http://uncyclopedia.org/wiki/Babel:Vi",
    496   "http://uncyclopedia.org/wiki/Main_Page",
    497   "http://en.marveldatabase.com/Main_Page",
    498   "http://bioshock.wikia.com/wiki/Main_Page",
    499   "http://www.armchairgm.com/Special:ImageRating",
    500   "http://www.armchairgm.com/Anderson_Continues_to_Thrive_for_Cleveland",
    501   "http://www.armchairgm.com/Main_Page"
    502 };
    503 
    504 size_t MembusterMemoryTest::urls_length_ =
    505     arraysize(MembusterMemoryTest::source_urls_);
    506 
    507 #define QUOTE(x) #x
    508 #define GENERAL_MIX_MEMORY_TESTS(name, tabs) \
    509 TEST_F(GeneralMixMemoryTest, name) { \
    510   RunTest(QUOTE(_##tabs##t), tabs); \
    511 } \
    512 TEST_F(GeneralMixReferenceMemoryTest, name) { \
    513   RunTest(QUOTE(_##tabs##t_ref), tabs); \
    514 }
    515 
    516 GENERAL_MIX_MEMORY_TESTS(SingleTabTest, 1);
    517 GENERAL_MIX_MEMORY_TESTS(FiveTabTest, 5);
    518 GENERAL_MIX_MEMORY_TESTS(TwelveTabTest, 12);
    519 
    520 // Commented out until the recorded cache data is added.
    521 //TEST_F(MembusterMemoryTest, Windows) {
    522 //  RunTest("membuster", 0);
    523 //}
    524 
    525 }  // namespace
    526