| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2006-2008 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 #ifdef CHROME_PERSONALIZATION |
| 5 |
| 6 #include "base/message_loop.h" |
| 7 #include "chrome/browser/browser.h" |
| 8 #include "chrome/browser/tab_contents/tab_contents.h" |
| 9 #include "chrome/common/pref_names.h" |
| 10 #include "chrome/common/pref_service.h" |
| 11 #include "chrome/browser/sync/auth_error_state.h" |
| 12 #include "chrome/test/live_sync/profile_sync_service_test_harness.h" |
| 13 #include "chrome/test/ui_test_utils.h" |
| 14 #include "testing/gtest/include/gtest/gtest.h" |
| 15 |
| 16 // The default value for min_updates_needed_ when we're not in a call to |
| 17 // WaitForUpdatesRecievedAtLeast. |
| 18 static const int kMinUpdatesNeededNone = -1; |
| 19 |
| 20 static const ProfileSyncService::Status kInvalidStatus = {}; |
| 21 |
| 22 // Simple class to implement a timeout using PostDelayedTask. If it is not |
| 23 // aborted before picked up by a message queue, then it asserts with the message |
| 24 // provided. This class is not thread safe. |
| 25 class StateChangeTimeoutEvent |
| 26 : public base::RefCountedThreadSafe<StateChangeTimeoutEvent> { |
| 27 public: |
| 28 explicit StateChangeTimeoutEvent(ProfileSyncServiceTestHarness* caller, |
| 29 const std::string& message); |
| 30 ~StateChangeTimeoutEvent(); |
| 31 |
| 32 // The entry point to the class from PostDelayedTask. |
| 33 void Callback(); |
| 34 |
| 35 // Cancels the actions of the callback. Returns true if success, false |
| 36 // if the callback has already timed out. |
| 37 bool Abort(); |
| 38 |
| 39 private: |
| 40 bool aborted_; |
| 41 bool did_timeout_; |
| 42 |
| 43 // Due to synchronization of the IO loop, the caller will always be alive |
| 44 // if the class is not aborted. |
| 45 ProfileSyncServiceTestHarness* caller_; |
| 46 |
| 47 // Informative message to assert in the case of a timeout. |
| 48 std::string message_; |
| 49 |
| 50 DISALLOW_COPY_AND_ASSIGN(StateChangeTimeoutEvent); |
| 51 }; |
| 52 |
| 53 StateChangeTimeoutEvent::StateChangeTimeoutEvent( |
| 54 ProfileSyncServiceTestHarness* caller, |
| 55 const std::string& message) |
| 56 : aborted_(false), did_timeout_(false), caller_(caller), message_(message) { |
| 57 } |
| 58 |
| 59 StateChangeTimeoutEvent::~StateChangeTimeoutEvent() { |
| 60 } |
| 61 |
| 62 void StateChangeTimeoutEvent::Callback() { |
| 63 if (!aborted_) { |
| 64 if (!caller_->RunStateChangeMachine()) { |
| 65 // Report the message. |
| 66 did_timeout_ = true; |
| 67 EXPECT_FALSE(aborted_) << message_; |
| 68 caller_->SignalStateComplete(); |
| 69 } |
| 70 } |
| 71 } |
| 72 |
| 73 bool StateChangeTimeoutEvent::Abort() { |
| 74 aborted_ = true; |
| 75 caller_ = NULL; |
| 76 return !did_timeout_; |
| 77 } |
| 78 |
| 79 class ConflictTimeoutEvent |
| 80 : public base::RefCountedThreadSafe<ConflictTimeoutEvent> { |
| 81 public: |
| 82 explicit ConflictTimeoutEvent(ProfileSyncServiceTestHarness* caller) |
| 83 : caller_(caller), did_run_(false) { |
| 84 } |
| 85 ~ConflictTimeoutEvent() { } |
| 86 |
| 87 // The entry point to the class from PostDelayedTask. |
| 88 void Callback(); |
| 89 bool did_run_; |
| 90 |
| 91 private: |
| 92 |
| 93 // Due to synchronization of the IO loop, the caller will always be alive |
| 94 // if the class is not aborted. |
| 95 ProfileSyncServiceTestHarness* caller_; |
| 96 |
| 97 DISALLOW_COPY_AND_ASSIGN(ConflictTimeoutEvent); |
| 98 }; |
| 99 |
| 100 void ConflictTimeoutEvent::Callback() { |
| 101 caller_->SignalStateComplete(); |
| 102 did_run_ = true; |
| 103 } |
| 104 |
| 105 |
| 106 ProfileSyncServiceTestHarness::ProfileSyncServiceTestHarness( |
| 107 Profile* p, const std::string& username, const std::string& password) |
| 108 : wait_state_(WAITING_FOR_INITIAL_CALLBACK), profile_(p), service_(NULL), |
| 109 last_status_(kInvalidStatus), min_updates_needed_(kMinUpdatesNeededNone), |
| 110 username_(username), password_(password) { |
| 111 // Ensure the profile has enough prefs registered for use by sync. |
| 112 if (!p->GetPrefs()->IsPrefRegistered(prefs::kAcceptLanguages)) |
| 113 TabContents::RegisterUserPrefs(p->GetPrefs()); |
| 114 if (!p->GetPrefs()->IsPrefRegistered(prefs::kCookieBehavior)) |
| 115 Browser::RegisterUserPrefs(p->GetPrefs()); |
| 116 } |
| 117 |
| 118 bool ProfileSyncServiceTestHarness::SetupSync() { |
| 119 service_ = profile_->GetProfilePersonalization()->sync_service(); |
| 120 profile_->GetPrefs()->SetBoolean(prefs::kSyncHasSetupCompleted, true); |
| 121 service_->EnableForUser(); |
| 122 |
| 123 // Needed to avoid showing the login dialog. Well aware this is egregious. |
| 124 service_->expecting_first_run_auth_needed_event_ = false; |
| 125 service_->AddObserver(this); |
| 126 return WaitForServiceInit(); |
| 127 } |
| 128 |
| 129 void ProfileSyncServiceTestHarness::SignalStateCompleteWithNextState( |
| 130 WaitState next_state) { |
| 131 |
| 132 wait_state_ = next_state; |
| 133 SignalStateComplete(); |
| 134 } |
| 135 |
| 136 void ProfileSyncServiceTestHarness::SignalStateComplete() { |
| 137 MessageLoopForUI::current()->Quit(); |
| 138 } |
| 139 |
| 140 bool ProfileSyncServiceTestHarness::RunStateChangeMachine() { |
| 141 WaitState state = wait_state_; |
| 142 ProfileSyncService::Status status(service_->QueryDetailedSyncStatus()); |
| 143 switch (wait_state_) { |
| 144 case WAITING_FOR_INITIAL_CALLBACK: |
| 145 SignalStateCompleteWithNextState(WAITING_FOR_READY_TO_PROCESS_CHANGES); |
| 146 break; |
| 147 case WAITING_FOR_READY_TO_PROCESS_CHANGES: |
| 148 if (service_->ready_to_process_changes_) { |
| 149 SignalStateCompleteWithNextState(WAITING_FOR_NOTHING); |
| 150 } |
| 151 break; |
| 152 case WAITING_FOR_TIMESTAMP_UPDATE: { |
| 153 const base::Time current_timestamp = service_->last_synced_time(); |
| 154 if (current_timestamp == last_timestamp_) { |
| 155 break; |
| 156 } |
| 157 EXPECT_TRUE(last_timestamp_ < current_timestamp); |
| 158 last_timestamp_ = current_timestamp; |
| 159 SignalStateCompleteWithNextState(WAITING_FOR_NOTHING); |
| 160 break; |
| 161 } |
| 162 case WAITING_FOR_UPDATES: |
| 163 if (status.updates_received < min_updates_needed_) { |
| 164 break; |
| 165 } |
| 166 SignalStateCompleteWithNextState(WAITING_FOR_NOTHING); |
| 167 break; |
| 168 case WAITING_FOR_NOTHING: |
| 169 default: |
| 170 // Invalid state during observer callback which may be triggered by other |
| 171 // classes using the the UI message loop. Defer to their handling. |
| 172 break; |
| 173 } |
| 174 last_status_ = status; |
| 175 return state != wait_state_; |
| 176 } |
| 177 |
| 178 void ProfileSyncServiceTestHarness::OnStateChanged() { |
| 179 RunStateChangeMachine(); |
| 180 } |
| 181 |
| 182 bool ProfileSyncServiceTestHarness::AwaitSyncCycleCompletion( |
| 183 const std::string& reason) { |
| 184 wait_state_ = WAITING_FOR_TIMESTAMP_UPDATE; |
| 185 return AwaitStatusChangeWithTimeout(60, reason); |
| 186 } |
| 187 |
| 188 bool ProfileSyncServiceTestHarness::AwaitMutualSyncCycleCompletion( |
| 189 ProfileSyncServiceTestHarness* partner) { |
| 190 return AwaitSyncCycleCompletion("Sync cycle completion on active client.") && |
| 191 partner->AwaitSyncCycleCompletion( |
| 192 "Sync cycle completion on passive client."); |
| 193 } |
| 194 |
| 195 bool ProfileSyncServiceTestHarness::AwaitStatusChangeWithTimeout( |
| 196 int timeout_seconds, |
| 197 const std::string& reason) { |
| 198 scoped_refptr<StateChangeTimeoutEvent> timeout_signal( |
| 199 new StateChangeTimeoutEvent(this, reason)); |
| 200 MessageLoopForUI* loop = MessageLoopForUI::current(); |
| 201 loop->PostDelayedTask( |
| 202 FROM_HERE, |
| 203 NewRunnableMethod(timeout_signal.get(), |
| 204 &StateChangeTimeoutEvent::Callback), |
| 205 1000 * timeout_seconds); |
| 206 ui_test_utils::RunMessageLoop(); |
| 207 return timeout_signal->Abort(); |
| 208 } |
| 209 |
| 210 bool ProfileSyncServiceTestHarness::AwaitMutualSyncCycleCompletionWithConflict( |
| 211 ProfileSyncServiceTestHarness* partner) { |
| 212 |
| 213 if (!AwaitMutualSyncCycleCompletion(partner)) { |
| 214 return false; |
| 215 } |
| 216 if (!partner->AwaitMutualSyncCycleCompletion(this)) { |
| 217 return false; |
| 218 } |
| 219 |
| 220 scoped_refptr<ConflictTimeoutEvent> timeout_signal( |
| 221 new ConflictTimeoutEvent(this)); |
| 222 |
| 223 // Now we want to wait an extra 20 seconds to ensure any rebounding updates |
| 224 // due to a conflict are processed and observed by each client. |
| 225 MessageLoopForUI* loop = MessageLoopForUI::current(); |
| 226 loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(timeout_signal.get(), |
| 227 &ConflictTimeoutEvent::Callback), 1000 * 20); |
| 228 // It is possible that timeout has not run yet and loop exited due to |
| 229 // OnStateChanged event. So to avoid pre-mature termination of loop, |
| 230 // we are re-running the loop until did_run_ becomes true. |
| 231 while (!timeout_signal->did_run_) { |
| 232 ui_test_utils::RunMessageLoop(); |
| 233 } |
| 234 return true; |
| 235 } |
| 236 |
| 237 bool ProfileSyncServiceTestHarness::WaitForServiceInit() { |
| 238 // Wait for the initial (auth needed) callback. |
| 239 EXPECT_EQ(wait_state_, WAITING_FOR_INITIAL_CALLBACK); |
| 240 if (!AwaitStatusChangeWithTimeout(30, "Waiting for authwatcher calback.")) { |
| 241 return false; |
| 242 } |
| 243 |
| 244 // Wait for the OnBackendInitialized callback. |
| 245 service_->backend()->Authenticate(username_, password_); |
| 246 EXPECT_EQ(wait_state_, WAITING_FOR_READY_TO_PROCESS_CHANGES); |
| 247 if (!AwaitStatusChangeWithTimeout(30, "Waiting on backend initialization.")) { |
| 248 return false; |
| 249 } |
| 250 return service_->sync_initialized(); |
| 251 } |
| 252 |
| 253 #endif // CHROME_PERSONALIZATION |
| OLD | NEW |