Index: chrome/browser/chromeos/login/login_utils_browsertest.cc |
diff --git a/chrome/browser/chromeos/login/login_utils_browsertest.cc b/chrome/browser/chromeos/login/login_utils_browsertest.cc |
index afd77db8ddb68a17b44f163c7255c06d645ecb9f..1cdfc1b04fadfaf52d8dca502c9ef3d796a9cfd1 100644 |
--- a/chrome/browser/chromeos/login/login_utils_browsertest.cc |
+++ b/chrome/browser/chromeos/login/login_utils_browsertest.cc |
@@ -11,7 +11,9 @@ |
#include "base/path_service.h" |
#include "base/scoped_temp_dir.h" |
#include "base/string_util.h" |
+#include "base/synchronization/waitable_event.h" |
#include "base/threading/sequenced_worker_pool.h" |
+#include "base/threading/thread.h" |
#include "chrome/browser/chromeos/cros/cros_library.h" |
#include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" |
#include "chrome/browser/chromeos/input_method/mock_input_method_manager.h" |
@@ -94,6 +96,17 @@ void SetFlag(bool* flag) { |
*flag = true; |
} |
+// Single task of the fake IO loop used in the test, that just waits until |
+// it is signaled to quit or perform some work. |
+// |completion| is the event to wait for, and |work| is the task to invoke |
+// when signaled. If the task returns false then this quits the IO loop. |
+void BlockLoop(base::WaitableEvent* completion, base::Callback<bool()> work) { |
+ do { |
+ completion->Wait(); |
+ } while (work.Run()); |
+ MessageLoop::current()->QuitNow(); |
+} |
+ |
ACTION_P(MockSessionManagerClientRetrievePolicyCallback, policy) { |
arg0.Run(*policy); |
} |
@@ -115,20 +128,49 @@ class LoginUtilsTest : public testing::Test, |
// however, at one point in the test, temporarily set the message |
// loop for the IO thread. |
LoginUtilsTest() |
- : loop_(MessageLoop::TYPE_IO), |
+ : fake_io_thread_completion_(false, false), |
+ fake_io_thread_("fake_io_thread"), |
+ loop_(MessageLoop::TYPE_IO), |
browser_process_( |
static_cast<TestingBrowserProcess*>(g_browser_process)), |
local_state_(browser_process_), |
- ui_thread_(content::BrowserThread::UI, &loop_), |
- db_thread_(content::BrowserThread::DB), |
- file_thread_(content::BrowserThread::FILE, &loop_), |
- io_thread_(content::BrowserThread::IO), |
+ ui_thread_(BrowserThread::UI, &loop_), |
+ db_thread_(BrowserThread::DB), |
+ file_thread_(BrowserThread::FILE, &loop_), |
mock_async_method_caller_(NULL), |
connector_(NULL), |
cryptohome_(NULL), |
prepared_profile_(NULL) {} |
virtual void SetUp() OVERRIDE { |
+ // This test is not a full blown InProcessBrowserTest, and doesn't have |
+ // all the usual threads running. However a lot of subsystems pulled from |
+ // ProfileImpl post to IO (usually from ProfileIOData), and DCHECK that |
+ // those tasks were posted. Those tasks in turn depend on a lot of other |
+ // components that aren't there during this test, so this kludge is used to |
+ // have a running IO loop that doesn't really execute any tasks. |
+ // |
+ // See InvokeOnIO() below for a way to perform specific tasks on IO, when |
+ // that's necessary. |
+ |
+ // A thread is needed to create a new MessageLoop, since there can be only |
+ // one loop per thread. |
+ fake_io_thread_.StartWithOptions( |
+ base::Thread::Options(MessageLoop::TYPE_IO, 0)); |
+ MessageLoop* fake_io_loop = fake_io_thread_.message_loop(); |
+ // Make this loop enter the single task, BlockLoop(). Pass in the completion |
+ // event and the work callback. |
+ fake_io_loop->PostTask( |
+ FROM_HERE, |
+ base::Bind( |
+ BlockLoop, |
+ &fake_io_thread_completion_, |
+ base::Bind(&LoginUtilsTest::DoIOWork, base::Unretained(this)))); |
+ // Map BrowserThread::IO to this loop. This allows posting to IO but nothing |
+ // will be executed. |
+ io_thread_.reset( |
+ new content::TestBrowserThread(BrowserThread::IO, fake_io_loop)); |
+ |
ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); |
CommandLine* command_line = CommandLine::ForCurrentProcess(); |
@@ -227,34 +269,24 @@ class LoginUtilsTest : public testing::Test, |
cryptohome::AsyncMethodCaller::Shutdown(); |
mock_async_method_caller_ = NULL; |
- RunUntilIdle(); |
- { |
- // chrome_browser_net::Predictor usually skips its shutdown routines on |
- // unit_tests, but does the full thing when |
- // g_browser_process->profile_manager() is valid during initialization. |
- // Run a task on a temporary BrowserThread::IO that allows skipping |
- // these routines. |
- // |
- // It is important to not have a fake message loop on the IO |
- // thread for the whole test, see comment on LoginUtilsTest |
- // constructor for details. |
- io_thread_.DeprecatedSetMessageLoop(&loop_); |
- loop_.PostTask(FROM_HERE, |
- base::Bind(&LoginUtilsTest::TearDownOnIO, |
- base::Unretained(this))); |
- RunUntilIdle(); |
- io_thread_.DeprecatedSetMessageLoop(NULL); |
- } |
+ InvokeOnIO( |
+ base::Bind(&LoginUtilsTest::TearDownOnIO, base::Unretained(this))); |
// These trigger some tasks that have to run while BrowserThread::UI |
// exists. Delete all the profiles before deleting the connector. |
browser_process_->SetProfileManager(NULL); |
connector_ = NULL; |
browser_process_->SetBrowserPolicyConnector(NULL); |
+ QuitIOLoop(); |
RunUntilIdle(); |
} |
void TearDownOnIO() { |
+ // chrome_browser_net::Predictor usually skips its shutdown routines on |
+ // unit_tests, but does the full thing when |
+ // g_browser_process->profile_manager() is valid during initialization. |
+ // That includes a WaitableEvent on UI waiting for a task on IO, so that |
+ // task must execute. Do it directly from here now. |
std::vector<Profile*> profiles = |
browser_process_->profile_manager()->GetLoadedProfiles(); |
for (size_t i = 0; i < profiles.size(); ++i) { |
@@ -273,6 +305,33 @@ class LoginUtilsTest : public testing::Test, |
loop_.RunUntilIdle(); |
} |
+ // Invokes |task| on the IO loop and returns after it has executed. |
+ void InvokeOnIO(const base::Closure& task) { |
+ fake_io_thread_work_ = task; |
+ fake_io_thread_completion_.Signal(); |
+ content::RunMessageLoop(); |
+ } |
+ |
+ // Makes the fake IO loop return. |
+ void QuitIOLoop() { |
+ fake_io_thread_completion_.Signal(); |
+ content::RunMessageLoop(); |
+ } |
+ |
+ // Helper for BlockLoop, InvokeOnIO and QuitIOLoop. |
+ bool DoIOWork() { |
+ bool has_work = !fake_io_thread_work_.is_null(); |
+ if (has_work) |
+ fake_io_thread_work_.Run(); |
+ fake_io_thread_work_.Reset(); |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ MessageLoop::QuitWhenIdleClosure()); |
+ // If there was work then keep waiting for more work. |
+ // If there was no work then quit the fake IO loop. |
+ return has_work; |
+ } |
+ |
virtual void OnProfilePrepared(Profile* profile) OVERRIDE { |
EXPECT_FALSE(prepared_profile_); |
prepared_profile_ = profile; |
@@ -378,6 +437,10 @@ class LoginUtilsTest : public testing::Test, |
protected: |
ScopedStubCrosEnabler stub_cros_enabler_; |
+ base::Closure fake_io_thread_work_; |
+ base::WaitableEvent fake_io_thread_completion_; |
+ base::Thread fake_io_thread_; |
+ |
MessageLoop loop_; |
TestingBrowserProcess* browser_process_; |
ScopedTestingLocalState local_state_; |
@@ -385,7 +448,7 @@ class LoginUtilsTest : public testing::Test, |
content::TestBrowserThread ui_thread_; |
content::TestBrowserThread db_thread_; |
content::TestBrowserThread file_thread_; |
- content::TestBrowserThread io_thread_; |
+ scoped_ptr<content::TestBrowserThread> io_thread_; |
scoped_ptr<IOThread> io_thread_state_; |
MockDBusThreadManager mock_dbus_thread_manager_; |