1 // Copyright 2013 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 "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service.h" 6 7 #include "base/bind.h" 8 #include "base/message_loop/message_loop_proxy.h" 9 #include "base/run_loop.h" 10 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_metadata_store.h" 11 #include "chrome/browser/sync_file_system/drive_backend_v1/fake_api_util.h" 12 #include "chrome/browser/sync_file_system/sync_file_system.pb.h" 13 #include "chrome/browser/sync_file_system/sync_file_system_test_util.h" 14 #include "chrome/browser/sync_file_system/syncable_file_system_util.h" 15 #include "chrome/test/base/testing_browser_process.h" 16 #include "chrome/test/base/testing_profile.h" 17 #include "chrome/test/base/testing_profile_manager.h" 18 #include "content/public/test/test_browser_thread_bundle.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 21 namespace sync_file_system { 22 23 using drive_backend::APIUtilInterface; 24 using drive_backend::FakeAPIUtil; 25 26 namespace { 27 28 const char kTestProfileName[] = "test-profile"; 29 30 const char kSyncRootResourceId[] = "folder:sync_root_resource_id"; 31 32 void DidInitialize(bool* done, SyncStatusCode status, bool created) { 33 EXPECT_EQ(SYNC_STATUS_OK, status); 34 *done = true; 35 } 36 37 void ExpectEqStatus(bool* done, 38 SyncStatusCode expected, 39 SyncStatusCode actual) { 40 EXPECT_FALSE(*done); 41 *done = true; 42 EXPECT_EQ(expected, actual); 43 } 44 45 void ExpectOkStatus(SyncStatusCode status) { 46 EXPECT_EQ(SYNC_STATUS_OK, status); 47 } 48 49 } // namespace 50 51 class DriveFileSyncServiceTest : public testing::Test { 52 public: 53 DriveFileSyncServiceTest() 54 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), 55 profile_manager_(TestingBrowserProcess::GetGlobal()), 56 fake_api_util_(NULL), 57 metadata_store_(NULL) {} 58 59 virtual void SetUp() OVERRIDE { 60 ASSERT_TRUE(profile_manager_.SetUp()); 61 62 RegisterSyncableFileSystem(); 63 fake_api_util_ = new FakeAPIUtil; 64 65 ASSERT_TRUE(scoped_base_dir_.CreateUniqueTempDir()); 66 base_dir_ = scoped_base_dir_.path(); 67 metadata_store_ = new DriveMetadataStore( 68 base_dir_, base::MessageLoopProxy::current().get()); 69 bool done = false; 70 metadata_store_->Initialize(base::Bind(&DidInitialize, &done)); 71 base::RunLoop().RunUntilIdle(); 72 metadata_store_->SetSyncRootDirectory(kSyncRootResourceId); 73 EXPECT_TRUE(done); 74 75 sync_service_ = DriveFileSyncService::CreateForTesting( 76 profile_manager_.CreateTestingProfile(kTestProfileName), 77 base_dir_, 78 scoped_ptr<APIUtilInterface>(fake_api_util_), 79 scoped_ptr<DriveMetadataStore>(metadata_store_)).Pass(); 80 base::RunLoop().RunUntilIdle(); 81 } 82 83 virtual void TearDown() OVERRIDE { 84 metadata_store_ = NULL; 85 fake_api_util_ = NULL; 86 sync_service_.reset(); 87 base::RunLoop().RunUntilIdle(); 88 89 base_dir_ = base::FilePath(); 90 RevokeSyncableFileSystem(); 91 } 92 93 virtual ~DriveFileSyncServiceTest() { 94 } 95 96 protected: 97 FakeAPIUtil* fake_api_util() { return fake_api_util_; } 98 DriveMetadataStore* metadata_store() { return metadata_store_; } 99 DriveFileSyncService* sync_service() { return sync_service_.get(); } 100 std::map<GURL, std::string>* pending_batch_sync_origins() { 101 return &(sync_service()->pending_batch_sync_origins_); 102 } 103 104 // Helper function to add an origin to the given origin sync status. To make 105 // naming easier, each origin, resourceID, etc. will all share the same 106 // prefixes and only be distinguished by the given suffix ID which could be a 107 // number (1, 2, 3, etc.) or a letter (A, B, C, etc.). 108 // e.g. originA, originB, folder:resource_idA, folder:resource_idB, etc. 109 void AddOrigin(std::string status, const char* suffix) { 110 const GURL origin(std::string("chrome-extension://app_") + suffix); 111 const std::string resource_id(std::string("folder:resource_id") + suffix); 112 113 if (status == "Pending") { 114 pending_batch_sync_origins()->insert(std::make_pair(origin, resource_id)); 115 } else if (status == "Enabled") { 116 metadata_store()->AddIncrementalSyncOrigin(origin, resource_id); 117 } else if (status == "Disabled") { 118 metadata_store()->AddIncrementalSyncOrigin(origin, resource_id); 119 metadata_store()->DisableOrigin(origin, base::Bind(&ExpectOkStatus)); 120 } else { 121 NOTREACHED(); 122 } 123 } 124 125 bool VerifyOriginStatusCount(size_t expected_pending, 126 size_t expected_enabled, 127 size_t expected_disabled) { 128 size_t actual_pending = pending_batch_sync_origins()->size(); 129 size_t actual_enabled = metadata_store()->incremental_sync_origins().size(); 130 size_t actual_disabled = metadata_store()->disabled_origins().size(); 131 132 // Prints which counts don't match up if any. 133 EXPECT_EQ(expected_pending, actual_pending); 134 EXPECT_EQ(expected_enabled, actual_enabled); 135 EXPECT_EQ(expected_disabled, actual_disabled); 136 137 // If any count doesn't match, the original line number can be printed by 138 // simply adding ASSERT_TRUE on the call to this function. 139 if (expected_pending == actual_pending && 140 expected_enabled == actual_enabled && 141 expected_disabled == actual_disabled) 142 return true; 143 144 return false; 145 } 146 147 private: 148 base::ScopedTempDir scoped_base_dir_; 149 content::TestBrowserThreadBundle thread_bundle_; 150 151 TestingProfileManager profile_manager_; 152 base::FilePath base_dir_; 153 154 FakeAPIUtil* fake_api_util_; // Owned by |sync_service_|. 155 DriveMetadataStore* metadata_store_; // Owned by |sync_service_|. 156 157 scoped_ptr<DriveFileSyncService> sync_service_; 158 159 DISALLOW_COPY_AND_ASSIGN(DriveFileSyncServiceTest); 160 }; 161 162 TEST_F(DriveFileSyncServiceTest, UninstallOrigin) { 163 // Add fake app origin directory using fake drive_sync_client. 164 std::string origin_dir_resource_id = "uninstalledappresourceid"; 165 fake_api_util()->PushRemoteChange("parent_id", 166 "parent_title", 167 "uninstall_me_folder", 168 origin_dir_resource_id, 169 "resource_md5", 170 SYNC_FILE_TYPE_FILE, 171 false); 172 173 // Add meta_data entry so GURL->resourse_id mapping is there. 174 const GURL origin_gurl("chrome-extension://uninstallme"); 175 metadata_store()->AddIncrementalSyncOrigin(origin_gurl, 176 origin_dir_resource_id); 177 178 // Delete the origin directory. 179 bool done = false; 180 sync_service()->UninstallOrigin( 181 origin_gurl, 182 RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE, 183 base::Bind(&ExpectEqStatus, &done, SYNC_STATUS_OK)); 184 base::RunLoop().RunUntilIdle(); 185 EXPECT_TRUE(done); 186 187 // Assert the App's origin folder was marked as deleted. 188 EXPECT_TRUE(fake_api_util()->remote_resources().find( 189 origin_dir_resource_id)->second.deleted); 190 } 191 192 TEST_F(DriveFileSyncServiceTest, UninstallUnpackedOrigin) { 193 // Add fake app origin directory using fake drive_sync_client. 194 std::string origin_dir_resource_id = "uninstalledappresourceid"; 195 fake_api_util()->PushRemoteChange("parent_id", 196 "parent_title", 197 "uninstall_me_folder", 198 origin_dir_resource_id, 199 "resource_md5", 200 SYNC_FILE_TYPE_FILE, 201 false); 202 203 // Add meta_data entry so GURL->resourse_id mapping is there. 204 const GURL origin_gurl("chrome-extension://uninstallme"); 205 metadata_store()->AddIncrementalSyncOrigin(origin_gurl, 206 origin_dir_resource_id); 207 208 // Uninstall the origin. 209 bool done = false; 210 sync_service()->UninstallOrigin( 211 origin_gurl, 212 RemoteFileSyncService::UNINSTALL_AND_KEEP_REMOTE, 213 base::Bind(&ExpectEqStatus, &done, SYNC_STATUS_OK)); 214 base::RunLoop().RunUntilIdle(); 215 EXPECT_TRUE(done); 216 217 // Assert the App's origin folder has not been deleted. 218 EXPECT_FALSE(fake_api_util()->remote_resources().find( 219 origin_dir_resource_id)->second.deleted); 220 } 221 222 TEST_F(DriveFileSyncServiceTest, UninstallOriginWithoutOriginDirectory) { 223 // Not add fake app origin directory. 224 std::string origin_dir_resource_id = "uninstalledappresourceid"; 225 226 // Add meta_data entry so GURL->resourse_id mapping is there. 227 const GURL origin_gurl("chrome-extension://uninstallme"); 228 metadata_store()->AddIncrementalSyncOrigin(origin_gurl, 229 origin_dir_resource_id); 230 231 // Delete the origin directory (but not found). 232 bool done = false; 233 sync_service()->UninstallOrigin( 234 origin_gurl, 235 RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE, 236 base::Bind(&ExpectEqStatus, &done, SYNC_STATUS_OK)); 237 base::RunLoop().RunUntilIdle(); 238 EXPECT_TRUE(done); 239 240 // Assert the App's origin folder does not exist. 241 const FakeAPIUtil::RemoteResourceByResourceId& remote_resources = 242 fake_api_util()->remote_resources(); 243 EXPECT_TRUE(remote_resources.find(origin_dir_resource_id) == 244 remote_resources.end()); 245 } 246 247 TEST_F(DriveFileSyncServiceTest, DisableOriginPendingOrigin) { 248 // Disable a pending origin after DriveFileSystemService has already started. 249 const GURL origin("chrome-extension://app"); 250 std::string origin_resource_id = "app_resource_id"; 251 pending_batch_sync_origins()->insert(std::make_pair(origin, 252 origin_resource_id)); 253 ASSERT_TRUE(VerifyOriginStatusCount(1u, 0u, 0u)); 254 255 // Pending origins that are disabled are dropped and do not go to disabled. 256 sync_service()->DisableOrigin(origin, base::Bind(&ExpectOkStatus)); 257 base::RunLoop().RunUntilIdle(); 258 ASSERT_TRUE(VerifyOriginStatusCount(0u, 0u, 0u)); 259 } 260 261 TEST_F(DriveFileSyncServiceTest, 262 DisableOriginIncrementalOrigin) { 263 // Disable a pending origin after DriveFileSystemService has already started. 264 const GURL origin("chrome-extension://app"); 265 std::string origin_resource_id = "app_resource_id"; 266 metadata_store()->AddIncrementalSyncOrigin(origin, origin_resource_id); 267 ASSERT_TRUE(VerifyOriginStatusCount(0u, 1u, 0u)); 268 269 sync_service()->DisableOrigin(origin, base::Bind(&ExpectOkStatus)); 270 base::RunLoop().RunUntilIdle(); 271 ASSERT_TRUE(VerifyOriginStatusCount(0u, 0u, 1u)); 272 } 273 274 TEST_F(DriveFileSyncServiceTest, EnableOrigin) { 275 const GURL origin("chrome-extension://app"); 276 std::string origin_resource_id = "app_resource_id"; 277 metadata_store()->AddIncrementalSyncOrigin(origin, origin_resource_id); 278 metadata_store()->DisableOrigin(origin, base::Bind(&ExpectOkStatus)); 279 ASSERT_TRUE(VerifyOriginStatusCount(0u, 0u, 1u)); 280 281 // Re-enable the previously disabled origin. It initially goes to pending 282 // status and then to enabled (incremental) again when NotifyTasksDone() in 283 // SyncTaskManager invokes MaybeStartFetchChanges() and pending 284 // origins > 0. 285 sync_service()->EnableOrigin(origin, base::Bind(&ExpectOkStatus)); 286 base::RunLoop().RunUntilIdle(); 287 ASSERT_TRUE(VerifyOriginStatusCount(0u, 1u, 0u)); 288 } 289 290 TEST_F(DriveFileSyncServiceTest, GetOriginStatusMap) { 291 scoped_ptr<RemoteFileSyncService::OriginStatusMap> origin_status_map; 292 sync_service()->GetOriginStatusMap( 293 CreateResultReceiver(&origin_status_map)); 294 ASSERT_EQ(0u, origin_status_map->size()); 295 296 // Add 3 pending, 2 enabled and 1 disabled sync origin. 297 AddOrigin("Pending", "p0"); 298 AddOrigin("Pending", "p1"); 299 AddOrigin("Pending", "p2"); 300 AddOrigin("Enabled", "e0"); 301 AddOrigin("Enabled", "e1"); 302 AddOrigin("Disabled", "d0"); 303 304 sync_service()->GetOriginStatusMap( 305 CreateResultReceiver(&origin_status_map)); 306 ASSERT_EQ(6u, origin_status_map->size()); 307 EXPECT_EQ("Pending", (*origin_status_map)[GURL("chrome-extension://app_p0")]); 308 EXPECT_EQ("Pending", (*origin_status_map)[GURL("chrome-extension://app_p1")]); 309 EXPECT_EQ("Pending", (*origin_status_map)[GURL("chrome-extension://app_p2")]); 310 EXPECT_EQ("Enabled", (*origin_status_map)[GURL("chrome-extension://app_e0")]); 311 EXPECT_EQ("Enabled", (*origin_status_map)[GURL("chrome-extension://app_e1")]); 312 EXPECT_EQ("Disabled", 313 (*origin_status_map)[GURL("chrome-extension://app_d0")]); 314 } 315 316 } // namespace sync_file_system 317