| OLD | NEW |
| 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2009 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 #include "base/thread.h" | 5 #include "base/thread.h" |
| 6 #include "chrome/browser/sync/engine/syncapi.h" | 6 #include "chrome/browser/sync/engine/syncapi.h" |
| 7 #include "chrome/browser/sync/glue/bookmark_model_worker.h" | 7 #include "chrome/browser/sync/glue/ui_model_worker.h" |
| 8 #include "testing/gtest/include/gtest/gtest.h" | 8 #include "testing/gtest/include/gtest/gtest.h" |
| 9 | 9 |
| 10 using browser_sync::BookmarkModelWorker; | 10 using browser_sync::UIModelWorker; |
| 11 using namespace sync_api; | 11 using namespace sync_api; |
| 12 | 12 |
| 13 // Various boilerplate, primarily for the StopWithPendingWork test. | 13 // Various boilerplate, primarily for the StopWithPendingWork test. |
| 14 | 14 |
| 15 class BookmarkModelWorkerVisitor : public ModelSafeWorkerInterface::Visitor { | 15 class UIModelWorkerVisitor { |
| 16 public: | 16 public: |
| 17 BookmarkModelWorkerVisitor(MessageLoop* faux_ui_loop, | 17 UIModelWorkerVisitor(MessageLoop* faux_ui_loop, |
| 18 base::WaitableEvent* was_run, | 18 base::WaitableEvent* was_run, |
| 19 bool quit_loop) | 19 bool quit_loop) |
| 20 : faux_ui_loop_(faux_ui_loop), quit_loop_when_run_(quit_loop), | 20 : faux_ui_loop_(faux_ui_loop), quit_loop_when_run_(quit_loop), |
| 21 was_run_(was_run) { } | 21 was_run_(was_run) { } |
| 22 virtual ~BookmarkModelWorkerVisitor() { } | 22 virtual ~UIModelWorkerVisitor() { } |
| 23 | 23 |
| 24 virtual void DoWork() { | 24 virtual void DoWork() { |
| 25 EXPECT_EQ(MessageLoop::current(), faux_ui_loop_); | 25 EXPECT_EQ(MessageLoop::current(), faux_ui_loop_); |
| 26 was_run_->Signal(); | 26 was_run_->Signal(); |
| 27 if (quit_loop_when_run_) | 27 if (quit_loop_when_run_) |
| 28 MessageLoop::current()->Quit(); | 28 MessageLoop::current()->Quit(); |
| 29 } | 29 } |
| 30 | 30 |
| 31 private: | 31 private: |
| 32 MessageLoop* faux_ui_loop_; | 32 MessageLoop* faux_ui_loop_; |
| 33 bool quit_loop_when_run_; | 33 bool quit_loop_when_run_; |
| 34 base::WaitableEvent* was_run_; | 34 base::WaitableEvent* was_run_; |
| 35 DISALLOW_COPY_AND_ASSIGN(BookmarkModelWorkerVisitor); | 35 DISALLOW_COPY_AND_ASSIGN(UIModelWorkerVisitor); |
| 36 }; | 36 }; |
| 37 | 37 |
| 38 // A faux-syncer that only interacts with its model safe worker. | 38 // A faux-syncer that only interacts with its model safe worker. |
| 39 class Syncer { | 39 class Syncer { |
| 40 public: | 40 public: |
| 41 explicit Syncer(BookmarkModelWorker* worker) : worker_(worker) {} | 41 explicit Syncer(UIModelWorker* worker) : worker_(worker) {} |
| 42 ~Syncer() {} | 42 ~Syncer() {} |
| 43 | 43 |
| 44 void SyncShare(BookmarkModelWorkerVisitor* visitor) { | 44 void SyncShare(UIModelWorkerVisitor* visitor) { |
| 45 worker_->CallDoWorkFromModelSafeThreadAndWait(visitor); | 45 worker_->DoWorkAndWaitUntilDone(NewCallback(visitor, |
| 46 &UIModelWorkerVisitor::DoWork)); |
| 46 } | 47 } |
| 47 private: | 48 private: |
| 48 BookmarkModelWorker* worker_; | 49 UIModelWorker* worker_; |
| 49 DISALLOW_COPY_AND_ASSIGN(Syncer); | 50 DISALLOW_COPY_AND_ASSIGN(Syncer); |
| 50 }; | 51 }; |
| 51 | 52 |
| 52 // A task run from the SyncerThread to "sync share", ie tell the Syncer to | 53 // A task run from the SyncerThread to "sync share", ie tell the Syncer to |
| 53 // ask it's ModelSafeWorker to do something. | 54 // ask it's ModelSafeWorker to do something. |
| 54 class FakeSyncShareTask : public Task { | 55 class FakeSyncShareTask : public Task { |
| 55 public: | 56 public: |
| 56 FakeSyncShareTask(Syncer* syncer, BookmarkModelWorkerVisitor* visitor) | 57 FakeSyncShareTask(Syncer* syncer, UIModelWorkerVisitor* visitor) |
| 57 : syncer_(syncer), visitor_(visitor) { | 58 : syncer_(syncer), visitor_(visitor) { |
| 58 } | 59 } |
| 59 virtual void Run() { | 60 virtual void Run() { |
| 60 syncer_->SyncShare(visitor_); | 61 syncer_->SyncShare(visitor_); |
| 61 } | 62 } |
| 62 private: | 63 private: |
| 63 Syncer* syncer_; | 64 Syncer* syncer_; |
| 64 BookmarkModelWorkerVisitor* visitor_; | 65 UIModelWorkerVisitor* visitor_; |
| 65 DISALLOW_COPY_AND_ASSIGN(FakeSyncShareTask); | 66 DISALLOW_COPY_AND_ASSIGN(FakeSyncShareTask); |
| 66 }; | 67 }; |
| 67 | 68 |
| 68 // A task run from the CoreThread to simulate terminating syncapi. | 69 // A task run from the CoreThread to simulate terminating syncapi. |
| 69 class FakeSyncapiShutdownTask : public Task { | 70 class FakeSyncapiShutdownTask : public Task { |
| 70 public: | 71 public: |
| 71 FakeSyncapiShutdownTask(base::Thread* syncer_thread, | 72 FakeSyncapiShutdownTask(base::Thread* syncer_thread, |
| 72 BookmarkModelWorker* worker, | 73 UIModelWorker* worker, |
| 73 base::WaitableEvent** jobs, | 74 base::WaitableEvent** jobs, |
| 74 size_t job_count) | 75 size_t job_count) |
| 75 : syncer_thread_(syncer_thread), worker_(worker), jobs_(jobs), | 76 : syncer_thread_(syncer_thread), worker_(worker), jobs_(jobs), |
| 76 job_count_(job_count), all_jobs_done_(false, false) { } | 77 job_count_(job_count), all_jobs_done_(false, false) { } |
| 77 virtual void Run() { | 78 virtual void Run() { |
| 78 // In real life, we would try and close a sync directory, which would | 79 // In real life, we would try and close a sync directory, which would |
| 79 // result in the syncer calling it's own destructor, which results in | 80 // result in the syncer calling it's own destructor, which results in |
| 80 // the SyncerThread::HaltSyncer being called, which sets the | 81 // the SyncerThread::HaltSyncer being called, which sets the |
| 81 // syncer in RequestEarlyExit mode and waits until the Syncer finishes | 82 // syncer in RequestEarlyExit mode and waits until the Syncer finishes |
| 82 // SyncShare to remove the syncer from it's watch. Here we just manually | 83 // SyncShare to remove the syncer from it's watch. Here we just manually |
| 83 // wait until all outstanding jobs are done to simulate what happens in | 84 // wait until all outstanding jobs are done to simulate what happens in |
| 84 // SyncerThread::HaltSyncer. | 85 // SyncerThread::HaltSyncer. |
| 85 all_jobs_done_.WaitMany(jobs_, job_count_); | 86 all_jobs_done_.WaitMany(jobs_, job_count_); |
| 86 | 87 |
| 87 // These two calls are made from SyncBackendHost::Core::DoShutdown. | 88 // These two calls are made from SyncBackendHost::Core::DoShutdown. |
| 88 syncer_thread_->Stop(); | 89 syncer_thread_->Stop(); |
| 89 worker_->OnSyncerShutdownComplete(); | 90 worker_->OnSyncerShutdownComplete(); |
| 90 } | 91 } |
| 91 private: | 92 private: |
| 92 base::Thread* syncer_thread_; | 93 base::Thread* syncer_thread_; |
| 93 BookmarkModelWorker* worker_; | 94 UIModelWorker* worker_; |
| 94 base::WaitableEvent** jobs_; | 95 base::WaitableEvent** jobs_; |
| 95 size_t job_count_; | 96 size_t job_count_; |
| 96 base::WaitableEvent all_jobs_done_; | 97 base::WaitableEvent all_jobs_done_; |
| 97 DISALLOW_COPY_AND_ASSIGN(FakeSyncapiShutdownTask); | 98 DISALLOW_COPY_AND_ASSIGN(FakeSyncapiShutdownTask); |
| 98 }; | 99 }; |
| 99 | 100 |
| 100 class BookmarkModelWorkerTest : public testing::Test { | 101 class UIModelWorkerTest : public testing::Test { |
| 101 public: | 102 public: |
| 102 BookmarkModelWorkerTest() : faux_syncer_thread_("FauxSyncerThread"), | 103 UIModelWorkerTest() : faux_syncer_thread_("FauxSyncerThread"), |
| 103 faux_core_thread_("FauxCoreThread") { } | 104 faux_core_thread_("FauxCoreThread") { } |
| 104 | 105 |
| 105 virtual void SetUp() { | 106 virtual void SetUp() { |
| 106 faux_syncer_thread_.Start(); | 107 faux_syncer_thread_.Start(); |
| 107 bmw_.reset(new BookmarkModelWorker(&faux_ui_loop_)); | 108 bmw_.reset(new UIModelWorker(&faux_ui_loop_)); |
| 108 syncer_.reset(new Syncer(bmw_.get())); | 109 syncer_.reset(new Syncer(bmw_.get())); |
| 109 } | 110 } |
| 110 | 111 |
| 111 Syncer* syncer() { return syncer_.get(); } | 112 Syncer* syncer() { return syncer_.get(); } |
| 112 BookmarkModelWorker* bmw() { return bmw_.get(); } | 113 UIModelWorker* bmw() { return bmw_.get(); } |
| 113 base::Thread* core_thread() { return &faux_core_thread_; } | 114 base::Thread* core_thread() { return &faux_core_thread_; } |
| 114 base::Thread* syncer_thread() { return &faux_syncer_thread_; } | 115 base::Thread* syncer_thread() { return &faux_syncer_thread_; } |
| 115 MessageLoop* ui_loop() { return &faux_ui_loop_; } | 116 MessageLoop* ui_loop() { return &faux_ui_loop_; } |
| 116 private: | 117 private: |
| 117 MessageLoop faux_ui_loop_; | 118 MessageLoop faux_ui_loop_; |
| 118 base::Thread faux_syncer_thread_; | 119 base::Thread faux_syncer_thread_; |
| 119 base::Thread faux_core_thread_; | 120 base::Thread faux_core_thread_; |
| 120 scoped_ptr<BookmarkModelWorker> bmw_; | 121 scoped_ptr<UIModelWorker> bmw_; |
| 121 scoped_ptr<Syncer> syncer_; | 122 scoped_ptr<Syncer> syncer_; |
| 122 }; | 123 }; |
| 123 | 124 |
| 124 TEST_F(BookmarkModelWorkerTest, ScheduledWorkRunsOnUILoop) { | 125 TEST_F(UIModelWorkerTest, ScheduledWorkRunsOnUILoop) { |
| 125 base::WaitableEvent v_was_run(false, false); | 126 base::WaitableEvent v_was_run(false, false); |
| 126 scoped_ptr<BookmarkModelWorkerVisitor> v( | 127 scoped_ptr<UIModelWorkerVisitor> v( |
| 127 new BookmarkModelWorkerVisitor(ui_loop(), &v_was_run, true)); | 128 new UIModelWorkerVisitor(ui_loop(), &v_was_run, true)); |
| 128 | 129 |
| 129 syncer_thread()->message_loop()->PostTask(FROM_HERE, | 130 syncer_thread()->message_loop()->PostTask(FROM_HERE, |
| 130 new FakeSyncShareTask(syncer(), v.get())); | 131 new FakeSyncShareTask(syncer(), v.get())); |
| 131 | 132 |
| 132 // We are on the UI thread, so run our loop to process the | 133 // We are on the UI thread, so run our loop to process the |
| 133 // (hopefully) scheduled task from a SyncShare invocation. | 134 // (hopefully) scheduled task from a SyncShare invocation. |
| 134 MessageLoop::current()->Run(); | 135 MessageLoop::current()->Run(); |
| 135 | 136 |
| 136 bmw()->OnSyncerShutdownComplete(); | 137 bmw()->OnSyncerShutdownComplete(); |
| 137 bmw()->Stop(); | 138 bmw()->Stop(); |
| 138 syncer_thread()->Stop(); | 139 syncer_thread()->Stop(); |
| 139 } | 140 } |
| 140 | 141 |
| 141 TEST_F(BookmarkModelWorkerTest, StopWithPendingWork) { | 142 TEST_F(UIModelWorkerTest, StopWithPendingWork) { |
| 142 // What we want to set up is the following: | 143 // What we want to set up is the following: |
| 143 // ("ui_thread" is the thread we are currently executing on) | 144 // ("ui_thread" is the thread we are currently executing on) |
| 144 // 1 - simulate the user shutting down the browser, and the ui thread needing | 145 // 1 - simulate the user shutting down the browser, and the ui thread needing |
| 145 // to terminate the core thread. | 146 // to terminate the core thread. |
| 146 // 2 - the core thread is where the syncapi is accessed from, and so it needs | 147 // 2 - the core thread is where the syncapi is accessed from, and so it needs |
| 147 // to shut down the SyncerThread. | 148 // to shut down the SyncerThread. |
| 148 // 3 - the syncer is waiting on the BookmarkModelWorker to | 149 // 3 - the syncer is waiting on the UIModelWorker to |
| 149 // perform a task for it. | 150 // perform a task for it. |
| 150 // The BookmarkModelWorker's manual shutdown pump will save the day, as the | 151 // The UIModelWorker's manual shutdown pump will save the day, as the |
| 151 // UI thread is not actually trying to join() the core thread, it is merely | 152 // UI thread is not actually trying to join() the core thread, it is merely |
| 152 // waiting for the SyncerThread to give it work or to finish. After that, it | 153 // waiting for the SyncerThread to give it work or to finish. After that, it |
| 153 // will join the core thread which should succeed as the SyncerThread has left | 154 // will join the core thread which should succeed as the SyncerThread has left |
| 154 // the building. Unfortunately this test as written is not provably decidable, | 155 // the building. Unfortunately this test as written is not provably decidable, |
| 155 // as it will always halt on success, but it may not on failure (namely if | 156 // as it will always halt on success, but it may not on failure (namely if |
| 156 // the task scheduled by the Syncer is _never_ run). | 157 // the task scheduled by the Syncer is _never_ run). |
| 157 core_thread()->Start(); | 158 core_thread()->Start(); |
| 158 base::WaitableEvent v_ran(false, false); | 159 base::WaitableEvent v_ran(false, false); |
| 159 scoped_ptr<BookmarkModelWorkerVisitor> v(new BookmarkModelWorkerVisitor( | 160 scoped_ptr<UIModelWorkerVisitor> v(new UIModelWorkerVisitor( |
| 160 ui_loop(), &v_ran, false)); | 161 ui_loop(), &v_ran, false)); |
| 161 base::WaitableEvent* jobs[] = { &v_ran }; | 162 base::WaitableEvent* jobs[] = { &v_ran }; |
| 162 | 163 |
| 163 // The current message loop is not running, so queue a task to cause | 164 // The current message loop is not running, so queue a task to cause |
| 164 // BookmarkModelWorker::Stop() to play a crucial role. See comment below. | 165 // UIModelWorker::Stop() to play a crucial role. See comment below. |
| 165 syncer_thread()->message_loop()->PostTask(FROM_HERE, | 166 syncer_thread()->message_loop()->PostTask(FROM_HERE, |
| 166 new FakeSyncShareTask(syncer(), v.get())); | 167 new FakeSyncShareTask(syncer(), v.get())); |
| 167 | 168 |
| 168 // This is what gets the core_thread blocked on the syncer_thread. | 169 // This is what gets the core_thread blocked on the syncer_thread. |
| 169 core_thread()->message_loop()->PostTask(FROM_HERE, | 170 core_thread()->message_loop()->PostTask(FROM_HERE, |
| 170 new FakeSyncapiShutdownTask(syncer_thread(), bmw(), jobs, 1)); | 171 new FakeSyncapiShutdownTask(syncer_thread(), bmw(), jobs, 1)); |
| 171 | 172 |
| 172 // This is what gets the UI thread blocked until NotifyExitRequested, | 173 // This is what gets the UI thread blocked until NotifyExitRequested, |
| 173 // which is called when FakeSyncapiShutdownTask runs and deletes the syncer. | 174 // which is called when FakeSyncapiShutdownTask runs and deletes the syncer. |
| 174 bmw()->Stop(); | 175 bmw()->Stop(); |
| 175 | 176 |
| 176 EXPECT_FALSE(syncer_thread()->IsRunning()); | 177 EXPECT_FALSE(syncer_thread()->IsRunning()); |
| 177 core_thread()->Stop(); | 178 core_thread()->Stop(); |
| 178 } | 179 } |
| 179 | 180 |
| 180 TEST_F(BookmarkModelWorkerTest, HypotheticalManualPumpFlooding) { | 181 TEST_F(UIModelWorkerTest, HypotheticalManualPumpFlooding) { |
| 181 // This situation should not happen in real life because the Syncer should | 182 // This situation should not happen in real life because the Syncer should |
| 182 // never send more than one CallDoWork notification after early_exit_requested | 183 // never send more than one CallDoWork notification after early_exit_requested |
| 183 // has been set, but our BookmarkModelWorker is built to handle this case | 184 // has been set, but our UIModelWorker is built to handle this case |
| 184 // nonetheless. It may be needed in the future, and since we support it and | 185 // nonetheless. It may be needed in the future, and since we support it and |
| 185 // it is not actually exercised in the wild this test is essential. | 186 // it is not actually exercised in the wild this test is essential. |
| 186 // It is identical to above except we schedule more than one visitor. | 187 // It is identical to above except we schedule more than one visitor. |
| 187 core_thread()->Start(); | 188 core_thread()->Start(); |
| 188 | 189 |
| 189 // Our ammunition. | 190 // Our ammunition. |
| 190 base::WaitableEvent fox1_ran(false, false); | 191 base::WaitableEvent fox1_ran(false, false); |
| 191 scoped_ptr<BookmarkModelWorkerVisitor> fox1(new BookmarkModelWorkerVisitor( | 192 scoped_ptr<UIModelWorkerVisitor> fox1(new UIModelWorkerVisitor( |
| 192 ui_loop(), &fox1_ran, false)); | 193 ui_loop(), &fox1_ran, false)); |
| 193 base::WaitableEvent fox2_ran(false, false); | 194 base::WaitableEvent fox2_ran(false, false); |
| 194 scoped_ptr<BookmarkModelWorkerVisitor> fox2(new BookmarkModelWorkerVisitor( | 195 scoped_ptr<UIModelWorkerVisitor> fox2(new UIModelWorkerVisitor( |
| 195 ui_loop(), &fox2_ran, false)); | 196 ui_loop(), &fox2_ran, false)); |
| 196 base::WaitableEvent fox3_ran(false, false); | 197 base::WaitableEvent fox3_ran(false, false); |
| 197 scoped_ptr<BookmarkModelWorkerVisitor> fox3(new BookmarkModelWorkerVisitor( | 198 scoped_ptr<UIModelWorkerVisitor> fox3(new UIModelWorkerVisitor( |
| 198 ui_loop(), &fox3_ran, false)); | 199 ui_loop(), &fox3_ran, false)); |
| 199 base::WaitableEvent* jobs[] = { &fox1_ran, &fox2_ran, &fox3_ran }; | 200 base::WaitableEvent* jobs[] = { &fox1_ran, &fox2_ran, &fox3_ran }; |
| 200 | 201 |
| 201 // The current message loop is not running, so queue a task to cause | 202 // The current message loop is not running, so queue a task to cause |
| 202 // BookmarkModelWorker::Stop() to play a crucial role. See comment below. | 203 // UIModelWorker::Stop() to play a crucial role. See comment below. |
| 203 syncer_thread()->message_loop()->PostTask(FROM_HERE, | 204 syncer_thread()->message_loop()->PostTask(FROM_HERE, |
| 204 new FakeSyncShareTask(syncer(), fox1.get())); | 205 new FakeSyncShareTask(syncer(), fox1.get())); |
| 205 syncer_thread()->message_loop()->PostTask(FROM_HERE, | 206 syncer_thread()->message_loop()->PostTask(FROM_HERE, |
| 206 new FakeSyncShareTask(syncer(), fox2.get())); | 207 new FakeSyncShareTask(syncer(), fox2.get())); |
| 207 | 208 |
| 208 // This is what gets the core_thread blocked on the syncer_thread. | 209 // This is what gets the core_thread blocked on the syncer_thread. |
| 209 core_thread()->message_loop()->PostTask(FROM_HERE, | 210 core_thread()->message_loop()->PostTask(FROM_HERE, |
| 210 new FakeSyncapiShutdownTask(syncer_thread(), bmw(), jobs, 3)); | 211 new FakeSyncapiShutdownTask(syncer_thread(), bmw(), jobs, 3)); |
| 211 syncer_thread()->message_loop()->PostTask(FROM_HERE, | 212 syncer_thread()->message_loop()->PostTask(FROM_HERE, |
| 212 new FakeSyncShareTask(syncer(), fox3.get())); | 213 new FakeSyncShareTask(syncer(), fox3.get())); |
| 213 | 214 |
| 214 // This is what gets the UI thread blocked until NotifyExitRequested, | 215 // This is what gets the UI thread blocked until NotifyExitRequested, |
| 215 // which is called when FakeSyncapiShutdownTask runs and deletes the syncer. | 216 // which is called when FakeSyncapiShutdownTask runs and deletes the syncer. |
| 216 bmw()->Stop(); | 217 bmw()->Stop(); |
| 217 | 218 |
| 218 // Was the thread killed? | 219 // Was the thread killed? |
| 219 EXPECT_FALSE(syncer_thread()->IsRunning()); | 220 EXPECT_FALSE(syncer_thread()->IsRunning()); |
| 220 core_thread()->Stop(); | 221 core_thread()->Stop(); |
| 221 } | 222 } |
| OLD | NEW |