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