| OLD | NEW | 
|    1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |    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 |    2 // Use of this source code is governed by a BSD-style license that can be | 
|    3 // found in the LICENSE file. |    3 // found in the LICENSE file. | 
|    4  |    4  | 
|    5 // This test validates that the ProcessSingleton class properly makes sure |    5 // This test validates that the ProcessSingleton class properly makes sure | 
|    6 // that there is only one main browser process. |    6 // that there is only one main browser process. | 
|    7 // |    7 // | 
|    8 // It is currently compiled and run on Windows and Posix(non-Mac) platforms. |    8 // It is currently compiled and run on Windows and Posix(non-Mac) platforms. | 
|    9 // Mac uses system services and ProcessSingletonMac is a noop.  (Maybe it still |    9 // Mac uses system services and ProcessSingletonMac is a noop.  (Maybe it still | 
|   10 // makes sense to test that the system services are giving the behavior we |   10 // makes sense to test that the system services are giving the behavior we | 
|   11 // want?) |   11 // want?) | 
|   12  |   12  | 
|   13 #include <list> |  | 
|   14  |  | 
|   15 #include "base/bind.h" |   13 #include "base/bind.h" | 
 |   14 #include "base/command_line.h" | 
|   16 #include "base/file_path.h" |   15 #include "base/file_path.h" | 
|   17 #include "base/file_util.h" |   16 #include "base/file_util.h" | 
|   18 #include "base/memory/ref_counted.h" |   17 #include "base/memory/ref_counted.h" | 
|   19 #include "base/path_service.h" |   18 #include "base/path_service.h" | 
|   20 #include "base/process_util.h" |   19 #include "base/process_util.h" | 
|   21 #include "base/scoped_temp_dir.h" |   20 #include "base/scoped_temp_dir.h" | 
|   22 #include "base/synchronization/waitable_event.h" |   21 #include "base/synchronization/waitable_event.h" | 
|   23 #include "base/test/test_timeouts.h" |   22 #include "base/test/test_timeouts.h" | 
|   24 #include "base/threading/thread.h" |   23 #include "base/threading/thread.h" | 
|   25 #include "chrome/common/chrome_constants.h" |   24 #include "chrome/common/chrome_constants.h" | 
|   26 #include "chrome/common/chrome_paths.h" |   25 #include "chrome/common/chrome_paths.h" | 
|   27 #include "chrome/common/chrome_switches.h" |   26 #include "chrome/common/chrome_switches.h" | 
|   28 #include "chrome/test/base/test_launcher_utils.h" |   27 #include "chrome/test/base/test_launcher_utils.h" | 
|   29 #include "chrome/test/ui/ui_test.h" |   28 #include "chrome/test/base/in_process_browser_test.h" | 
|   30 #include "testing/gtest/include/gtest/gtest.h" |  | 
|   31  |   29  | 
|   32 namespace { |   30 namespace { | 
|   33  |   31  | 
|   34 // This is for the code that is to be ran in multiple threads at once, |   32 // This is for the code that is to be ran in multiple threads at once, | 
|   35 // to stress a race condition on first process start. |   33 // to stress a race condition on first process start. | 
|   36 // We use the thread safe ref counted base class so that we can use the |   34 // We use the thread safe ref counted base class so that we can use the | 
|   37 // base::Bind to run the StartChrome methods in many threads. |   35 // base::Bind to run the StartChrome methods in many threads. | 
|   38 class ChromeStarter : public base::RefCountedThreadSafe<ChromeStarter> { |   36 class ChromeStarter : public base::RefCountedThreadSafe<ChromeStarter> { | 
|   39  public: |   37  public: | 
|   40   ChromeStarter(int timeout_ms, const FilePath& user_data_dir) |   38   ChromeStarter(int timeout_ms, const FilePath& user_data_dir) | 
| (...skipping 12 matching lines...) Expand all  Loading... | 
|   53     done_event_.Reset(); |   51     done_event_.Reset(); | 
|   54     if (process_handle_ != base::kNullProcessHandle) |   52     if (process_handle_ != base::kNullProcessHandle) | 
|   55       base::CloseProcessHandle(process_handle_); |   53       base::CloseProcessHandle(process_handle_); | 
|   56     process_handle_ = base::kNullProcessHandle; |   54     process_handle_ = base::kNullProcessHandle; | 
|   57     process_terminated_ = false; |   55     process_terminated_ = false; | 
|   58   } |   56   } | 
|   59  |   57  | 
|   60   void StartChrome(base::WaitableEvent* start_event, bool first_run) { |   58   void StartChrome(base::WaitableEvent* start_event, bool first_run) { | 
|   61     // TODO(mattm): maybe stuff should be refactored to use |   59     // TODO(mattm): maybe stuff should be refactored to use | 
|   62     // UITest::LaunchBrowserHelper somehow? |   60     // UITest::LaunchBrowserHelper somehow? | 
|   63     FilePath browser_directory; |   61     FilePath program; | 
|   64     PathService::Get(chrome::DIR_APP, &browser_directory); |   62     ASSERT_TRUE(PathService::Get(base::FILE_EXE, &program)); | 
|   65     CommandLine command_line(browser_directory.Append( |   63     CommandLine command_line(program); | 
|   66         chrome::kBrowserProcessExecutablePath)); |  | 
|   67  |  | 
|   68     command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir_); |   64     command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir_); | 
|   69  |   65  | 
|   70     if (first_run) |   66     if (first_run) | 
|   71       command_line.AppendSwitch(switches::kFirstRun); |   67       command_line.AppendSwitch(switches::kFirstRun); | 
|   72     else |   68     else | 
|   73       command_line.AppendSwitch(switches::kNoFirstRun); |   69       command_line.AppendSwitch(switches::kNoFirstRun); | 
|   74  |   70  | 
|   75     // Add the normal test-mode switches, except for the ones we're adding |   71     // Add the normal test-mode switches, except for the ones we're adding | 
|   76     // ourselves. |   72     // ourselves. | 
|   77     CommandLine standard_switches(CommandLine::NO_PROGRAM); |   73     CommandLine standard_switches(CommandLine::NO_PROGRAM); | 
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  123     if (process_handle_ != base::kNullProcessHandle) |  119     if (process_handle_ != base::kNullProcessHandle) | 
|  124       base::CloseProcessHandle(process_handle_); |  120       base::CloseProcessHandle(process_handle_); | 
|  125   } |  121   } | 
|  126  |  122  | 
|  127   int timeout_ms_; |  123   int timeout_ms_; | 
|  128   FilePath user_data_dir_; |  124   FilePath user_data_dir_; | 
|  129  |  125  | 
|  130   DISALLOW_COPY_AND_ASSIGN(ChromeStarter); |  126   DISALLOW_COPY_AND_ASSIGN(ChromeStarter); | 
|  131 }; |  127 }; | 
|  132  |  128  | 
 |  129 }  // namespace | 
 |  130  | 
|  133 // Our test fixture that initializes and holds onto a few global vars. |  131 // Our test fixture that initializes and holds onto a few global vars. | 
|  134 class ProcessSingletonTest : public UITest { |  132 class ProcessSingletonTest : public InProcessBrowserTest { | 
|  135  public: |  133  public: | 
|  136   ProcessSingletonTest() |  134   ProcessSingletonTest() | 
|  137       // We use a manual reset so that all threads wake up at once when signaled |  135       // We use a manual reset so that all threads wake up at once when signaled | 
|  138       // and thus we must manually reset it for each attempt. |  136       // and thus we must manually reset it for each attempt. | 
|  139       : threads_waker_(true /* manual */, false /* signaled */) { |  137       : threads_waker_(true /* manual */, false /* signaled */) { | 
|  140     EXPECT_TRUE(temp_profile_dir_.CreateUniqueTempDir()); |  138     EXPECT_TRUE(temp_profile_dir_.CreateUniqueTempDir()); | 
|  141   } |  139   } | 
|  142  |  140  | 
|  143   void SetUp() { |  141   void SetUp() { | 
|  144     // Start the threads and create the starters. |  142     // Start the threads and create the starters. | 
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  183     } process_tree_filter(base::GetProcId(process_handle)); |  181     } process_tree_filter(base::GetProcId(process_handle)); | 
|  184  |  182  | 
|  185     // Start by explicitly killing the main process we know about... |  183     // Start by explicitly killing the main process we know about... | 
|  186     static const int kExitCode = 42; |  184     static const int kExitCode = 42; | 
|  187     EXPECT_TRUE(base::KillProcess(process_handle, kExitCode, true /* wait */)); |  185     EXPECT_TRUE(base::KillProcess(process_handle, kExitCode, true /* wait */)); | 
|  188  |  186  | 
|  189     // Then loop until we can't find any of its descendant. |  187     // Then loop until we can't find any of its descendant. | 
|  190     // But don't try more than kNbTries times... |  188     // But don't try more than kNbTries times... | 
|  191     static const int kNbTries = 10; |  189     static const int kNbTries = 10; | 
|  192     int num_tries = 0; |  190     int num_tries = 0; | 
|  193     while (base::GetProcessCount(chrome::kBrowserProcessExecutablePath, |  191     FilePath program; | 
|  194         &process_tree_filter) > 0 && num_tries++ < kNbTries) { |  192     ASSERT_TRUE(PathService::Get(base::FILE_EXE, &program)); | 
|  195       base::KillProcesses(chrome::kBrowserProcessExecutablePath, |  193     FilePath::StringType exe_name = program.BaseName().value(); | 
|  196                           kExitCode, &process_tree_filter); |  194     while (base::GetProcessCount(exe_name, &process_tree_filter) > 0 && | 
 |  195            num_tries++ < kNbTries) { | 
 |  196       base::KillProcesses(exe_name, kExitCode, &process_tree_filter); | 
|  197     } |  197     } | 
|  198     DLOG_IF(ERROR, num_tries >= kNbTries) << "Failed to kill all processes!"; |  198     DLOG_IF(ERROR, num_tries >= kNbTries) << "Failed to kill all processes!"; | 
|  199   } |  199   } | 
|  200  |  200  | 
|  201   // Since this is a hard to reproduce problem, we make a few attempts. |  201   // Since this is a hard to reproduce problem, we make a few attempts. | 
|  202   // We stop the attempts at the first error, and when there are no errors, |  202   // We stop the attempts at the first error, and when there are no errors, | 
|  203   // we don't time-out of any wait, so it executes quite fast anyway. |  203   // we don't time-out of any wait, so it executes quite fast anyway. | 
|  204   static const size_t kNbAttempts = 5; |  204   static const size_t kNbAttempts = 5; | 
|  205  |  205  | 
|  206   // The idea is to start chrome from multiple threads all at once. |  206   // The idea is to start chrome from multiple threads all at once. | 
|  207   static const size_t kNbThreads = 5; |  207   static const size_t kNbThreads = 5; | 
|  208   scoped_refptr<ChromeStarter> chrome_starters_[kNbThreads]; |  208   scoped_refptr<ChromeStarter> chrome_starters_[kNbThreads]; | 
|  209   scoped_ptr<base::Thread> chrome_starter_threads_[kNbThreads]; |  209   scoped_ptr<base::Thread> chrome_starter_threads_[kNbThreads]; | 
|  210  |  210  | 
|  211   // The event that will get all threads to wake up simultaneously and try |  211   // The event that will get all threads to wake up simultaneously and try | 
|  212   // to start a chrome process at the same time. |  212   // to start a chrome process at the same time. | 
|  213   base::WaitableEvent threads_waker_; |  213   base::WaitableEvent threads_waker_; | 
|  214  |  214  | 
|  215   // We don't want to use the default profile, but can't use UITest's since we |  215   // We don't want to use the default profile, but can't use UITest's since we | 
|  216   // don't use UITest::LaunchBrowser. |  216   // don't use UITest::LaunchBrowser. | 
|  217   ScopedTempDir temp_profile_dir_; |  217   ScopedTempDir temp_profile_dir_; | 
|  218 }; |  218 }; | 
|  219  |  219  | 
|  220 #if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) |  220 #if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) | 
|  221 // http://crbug.com/58219 |  221 // http://crbug.com/58219 | 
|  222 #define MAYBE_StartupRaceCondition FAILS_StartupRaceCondition |  222 #define MAYBE_StartupRaceCondition FAILS_StartupRaceCondition | 
|  223 #else |  223 #else | 
|  224 #define MAYBE_StartupRaceCondition StartupRaceCondition |  224 #define MAYBE_StartupRaceCondition StartupRaceCondition | 
|  225 #endif |  225 #endif | 
|  226 TEST_F(ProcessSingletonTest, MAYBE_StartupRaceCondition) { |  226 IN_PROC_BROWSER_TEST_F(ProcessSingletonTest, MAYBE_StartupRaceCondition) { | 
|  227   // We use this to stop the attempts loop on the first failure. |  227   // We use this to stop the attempts loop on the first failure. | 
|  228   bool failed = false; |  228   bool failed = false; | 
|  229   for (size_t attempt = 0; attempt < kNbAttempts && !failed; ++attempt) { |  229   for (size_t attempt = 0; attempt < kNbAttempts && !failed; ++attempt) { | 
|  230     SCOPED_TRACE(testing::Message() << "Attempt: " << attempt << "."); |  230     SCOPED_TRACE(testing::Message() << "Attempt: " << attempt << "."); | 
|  231     // We use a single event to get all threads to do the AppLaunch at the same |  231     // We use a single event to get all threads to do the AppLaunch at the same | 
|  232     // time... |  232     // time... | 
|  233     threads_waker_.Reset(); |  233     threads_waker_.Reset(); | 
|  234  |  234  | 
|  235     // Test both with and without the first-run dialog, since they exercise |  235     // Test both with and without the first-run dialog, since they exercise | 
|  236     // different paths. |  236     // different paths. | 
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  316     ASSERT_EQ(static_cast<size_t>(1), pending_starters.size()); |  316     ASSERT_EQ(static_cast<size_t>(1), pending_starters.size()); | 
|  317     size_t last_index = pending_starters.front(); |  317     size_t last_index = pending_starters.front(); | 
|  318     pending_starters.clear(); |  318     pending_starters.clear(); | 
|  319     if (chrome_starters_[last_index]->process_handle_ != |  319     if (chrome_starters_[last_index]->process_handle_ != | 
|  320         base::kNullProcessHandle) { |  320         base::kNullProcessHandle) { | 
|  321       KillProcessTree(chrome_starters_[last_index]->process_handle_); |  321       KillProcessTree(chrome_starters_[last_index]->process_handle_); | 
|  322       chrome_starters_[last_index]->done_event_.Wait(); |  322       chrome_starters_[last_index]->done_event_.Wait(); | 
|  323     } |  323     } | 
|  324   } |  324   } | 
|  325 } |  325 } | 
|  326  |  | 
|  327 }  // namespace |  | 
| OLD | NEW |