1// 2// Copyright (C) 2012 The Android Open Source Project 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17#include "shill/profile.h" 18 19#include <string> 20#include <vector> 21 22#include <base/files/file_util.h> 23#include <base/strings/stringprintf.h> 24#include <base/strings/string_util.h> 25#include <gtest/gtest.h> 26 27#include "shill/fake_store.h" 28#include "shill/mock_manager.h" 29#include "shill/mock_metrics.h" 30#include "shill/mock_profile.h" 31#include "shill/mock_service.h" 32#include "shill/mock_store.h" 33#include "shill/property_store_unittest.h" 34#include "shill/service_under_test.h" 35#include "shill/store_factory.h" 36 37using base::FilePath; 38using std::set; 39using std::string; 40using std::vector; 41using testing::_; 42using testing::Invoke; 43using testing::Mock; 44using testing::Return; 45using testing::SetArgumentPointee; 46using testing::StrictMock; 47 48namespace shill { 49 50class ProfileTest : public PropertyStoreTest { 51 public: 52 ProfileTest() : mock_metrics_(new MockMetrics(nullptr)) { 53 Profile::Identifier id("rather", "irrelevant"); 54 profile_ = new Profile( 55 control_interface(), metrics(), manager(), id, FilePath(), false); 56 57 // Install a FakeStore by default. In tests that actually care 58 // about the interaction between Profile and StoreInterface, we'll 59 // replace this with a MockStore. 60 profile_->set_storage(new FakeStore()); 61 } 62 63 MockService* CreateMockService() { 64 return new StrictMock<MockService>(control_interface(), 65 dispatcher(), 66 metrics(), 67 manager()); 68 } 69 70 bool ProfileInitStorage(const Profile::Identifier& id, 71 Profile::InitStorageOption storage_option, 72 bool save, 73 Error::Type error_type) { 74 // Note: this code uses neither FakeStore, nor MockStore. Instead, 75 // it exercises a real StoreInterface implemenation. 76 Error error; 77 ProfileRefPtr profile( 78 new Profile(control_interface(), mock_metrics_.get(), manager(), id, 79 FilePath(storage_path()), false)); 80 bool ret = profile->InitStorage(storage_option, &error); 81 EXPECT_EQ(error_type, error.type()); 82 if (ret && save) { 83 EXPECT_TRUE(profile->Save()); 84 } 85 return ret; 86 } 87 88 protected: 89 std::unique_ptr<MockMetrics> mock_metrics_; 90 ProfileRefPtr profile_; 91}; 92 93TEST_F(ProfileTest, DeleteEntry) { 94 std::unique_ptr<MockManager> manager(new StrictMock<MockManager>( 95 control_interface(), dispatcher(), metrics())); 96 profile_->manager_ = manager.get(); 97 98 MockStore* storage(new StrictMock<MockStore>()); 99 profile_->storage_.reset(storage); // Passes ownership 100 const string kEntryName("entry_name"); 101 102 // If entry does not appear in storage, DeleteEntry() should return an error. 103 EXPECT_CALL(*storage, ContainsGroup(kEntryName)) 104 .WillOnce(Return(false)); 105 { 106 Error error; 107 profile_->DeleteEntry(kEntryName, &error); 108 EXPECT_EQ(Error::kNotFound, error.type()); 109 } 110 111 Mock::VerifyAndClearExpectations(storage); 112 113 // If HandleProfileEntryDeletion() returns false, Profile should call 114 // DeleteGroup() itself. 115 EXPECT_CALL(*storage, ContainsGroup(kEntryName)) 116 .WillOnce(Return(true)); 117 EXPECT_CALL(*manager.get(), HandleProfileEntryDeletion(_, kEntryName)) 118 .WillOnce(Return(false)); 119 EXPECT_CALL(*storage, DeleteGroup(kEntryName)) 120 .WillOnce(Return(true)); 121 EXPECT_CALL(*storage, Flush()) 122 .WillOnce(Return(true)); 123 { 124 Error error; 125 profile_->DeleteEntry(kEntryName, &error); 126 EXPECT_TRUE(error.IsSuccess()); 127 } 128 129 Mock::VerifyAndClearExpectations(storage); 130 131 // If HandleProfileEntryDeletion() returns true, Profile should not call 132 // DeleteGroup() itself. 133 EXPECT_CALL(*storage, ContainsGroup(kEntryName)) 134 .WillOnce(Return(true)); 135 EXPECT_CALL(*manager.get(), HandleProfileEntryDeletion(_, kEntryName)) 136 .WillOnce(Return(true)); 137 EXPECT_CALL(*storage, DeleteGroup(kEntryName)) 138 .Times(0); 139 EXPECT_CALL(*storage, Flush()) 140 .WillOnce(Return(true)); 141 { 142 Error error; 143 profile_->DeleteEntry(kEntryName, &error); 144 EXPECT_TRUE(error.IsSuccess()); 145 } 146} 147 148TEST_F(ProfileTest, IsValidIdentifierToken) { 149 EXPECT_FALSE(Profile::IsValidIdentifierToken("")); 150 EXPECT_FALSE(Profile::IsValidIdentifierToken(" ")); 151 EXPECT_FALSE(Profile::IsValidIdentifierToken("-")); 152 EXPECT_FALSE(Profile::IsValidIdentifierToken("~")); 153 EXPECT_FALSE(Profile::IsValidIdentifierToken("_")); 154 EXPECT_TRUE(Profile::IsValidIdentifierToken("a")); 155 EXPECT_TRUE(Profile::IsValidIdentifierToken("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); 156 EXPECT_TRUE(Profile::IsValidIdentifierToken("abcdefghijklmnopqrstuvwxyz")); 157 EXPECT_TRUE(Profile::IsValidIdentifierToken("0123456789")); 158} 159 160TEST_F(ProfileTest, ParseIdentifier) { 161 Profile::Identifier identifier; 162 EXPECT_FALSE(Profile::ParseIdentifier("", &identifier)); 163 EXPECT_FALSE(Profile::ParseIdentifier("~", &identifier)); 164 EXPECT_FALSE(Profile::ParseIdentifier("~foo", &identifier)); 165 EXPECT_FALSE(Profile::ParseIdentifier("~/", &identifier)); 166 EXPECT_FALSE(Profile::ParseIdentifier("~bar/", &identifier)); 167 EXPECT_FALSE(Profile::ParseIdentifier("~/zoo", &identifier)); 168 EXPECT_FALSE(Profile::ParseIdentifier("~./moo", &identifier)); 169 EXPECT_FALSE(Profile::ParseIdentifier("~valid/?", &identifier)); 170 EXPECT_FALSE(Profile::ParseIdentifier("~no//no", &identifier)); 171 EXPECT_FALSE(Profile::ParseIdentifier("~no~no", &identifier)); 172 173 static const char kUser[] = "user"; 174 static const char kIdentifier[] = "identifier"; 175 EXPECT_TRUE(Profile::ParseIdentifier( 176 base::StringPrintf("~%s/%s", kUser, kIdentifier), 177 &identifier)); 178 EXPECT_EQ(kUser, identifier.user); 179 EXPECT_EQ(kIdentifier, identifier.identifier); 180 181 EXPECT_FALSE(Profile::ParseIdentifier("!", &identifier)); 182 EXPECT_FALSE(Profile::ParseIdentifier("/nope", &identifier)); 183 184 static const char kIdentifier2[] = "something"; 185 EXPECT_TRUE(Profile::ParseIdentifier(kIdentifier2, &identifier)); 186 EXPECT_EQ("", identifier.user); 187 EXPECT_EQ(kIdentifier2, identifier.identifier); 188} 189 190TEST_F(ProfileTest, IdentifierToString) { 191 Profile::Identifier identifier; 192 static const char kUser[] = "user"; 193 static const char kIdentifier[] = "identifier"; 194 identifier.user = kUser; 195 identifier.identifier = kIdentifier; 196 EXPECT_EQ(base::StringPrintf("~%s/%s", kUser, kIdentifier), 197 Profile::IdentifierToString(identifier)); 198} 199 200TEST_F(ProfileTest, GetFriendlyName) { 201 static const char kUser[] = "theUser"; 202 static const char kIdentifier[] = "theIdentifier"; 203 Profile::Identifier id(kIdentifier); 204 ProfileRefPtr profile(new Profile( 205 control_interface(), metrics(), manager(), id, FilePath(), false)); 206 EXPECT_EQ(kIdentifier, profile->GetFriendlyName()); 207 id.user = kUser; 208 profile = new Profile( 209 control_interface(), metrics(), manager(), id, FilePath(), false); 210 EXPECT_EQ(string(kUser) + "/" + kIdentifier, profile->GetFriendlyName()); 211} 212 213TEST_F(ProfileTest, GetStoragePath) { 214 static const char kUser[] = "chronos"; 215 static const char kIdentifier[] = "someprofile"; 216 static const char kDirectory[] = "/a/place/for/"; 217 Profile::Identifier id(kIdentifier); 218 ProfileRefPtr profile(new Profile( 219 control_interface(), metrics(), manager(), id, FilePath(), false)); 220 EXPECT_TRUE(profile->persistent_profile_path_.empty()); 221 id.user = kUser; 222 profile = new Profile( 223 control_interface(), metrics(), manager(), id, FilePath(kDirectory), 224 false); 225#if defined(ENABLE_JSON_STORE) 226 EXPECT_EQ("/a/place/for/chronos/someprofile.profile.json", 227 profile->persistent_profile_path_.value()); 228#else 229 EXPECT_EQ("/a/place/for/chronos/someprofile.profile", 230 profile->persistent_profile_path_.value()); 231#endif 232} 233 234TEST_F(ProfileTest, ServiceManagement) { 235 scoped_refptr<MockService> service1(CreateMockService()); 236 scoped_refptr<MockService> service2(CreateMockService()); 237 238 EXPECT_CALL(*service1.get(), Save(_)) 239 .WillRepeatedly(Invoke(service1.get(), &MockService::FauxSave)); 240 EXPECT_CALL(*service2.get(), Save(_)) 241 .WillRepeatedly(Invoke(service2.get(), &MockService::FauxSave)); 242 243 ASSERT_TRUE(profile_->AdoptService(service1)); 244 ASSERT_TRUE(profile_->AdoptService(service2)); 245 246 // Ensure services are in the profile now. 247 ASSERT_TRUE(profile_->ContainsService(service1)); 248 ASSERT_TRUE(profile_->ContainsService(service2)); 249 250 // Ensure we can't add them twice. 251 ASSERT_FALSE(profile_->AdoptService(service1)); 252 ASSERT_FALSE(profile_->AdoptService(service2)); 253 254 // Ensure that we can abandon individually, and that doing so is idempotent. 255 ASSERT_TRUE(profile_->AbandonService(service1)); 256 ASSERT_FALSE(profile_->ContainsService(service1)); 257 ASSERT_TRUE(profile_->AbandonService(service1)); 258 ASSERT_TRUE(profile_->ContainsService(service2)); 259 260 // Clean up. 261 ASSERT_TRUE(profile_->AbandonService(service2)); 262 ASSERT_FALSE(profile_->ContainsService(service1)); 263 ASSERT_FALSE(profile_->ContainsService(service2)); 264} 265 266TEST_F(ProfileTest, ServiceConfigure) { 267 ServiceRefPtr service1(new ServiceUnderTest(control_interface(), 268 dispatcher(), 269 metrics(), 270 manager())); 271 // Change priority from default. 272 service1->SetPriority(service1->priority() + 1, nullptr); 273 ASSERT_TRUE(profile_->AdoptService(service1)); 274 ASSERT_TRUE(profile_->ContainsService(service1)); 275 276 // Create new service; ask Profile to merge it with a known, matching, 277 // service; ensure that settings from |service1| wind up in |service2|. 278 ServiceRefPtr service2(new ServiceUnderTest(control_interface(), 279 dispatcher(), 280 metrics(), 281 manager())); 282 int32_t orig_priority = service2->priority(); 283 ASSERT_TRUE(profile_->ConfigureService(service2)); 284 ASSERT_EQ(service1->priority(), service2->priority()); 285 ASSERT_NE(orig_priority, service2->priority()); 286 287 // Clean up. 288 ASSERT_TRUE(profile_->AbandonService(service1)); 289 ASSERT_FALSE(profile_->ContainsService(service1)); 290 ASSERT_FALSE(profile_->ContainsService(service2)); 291} 292 293TEST_F(ProfileTest, Save) { 294 scoped_refptr<MockService> service1(CreateMockService()); 295 scoped_refptr<MockService> service2(CreateMockService()); 296 EXPECT_CALL(*service1.get(), Save(_)).WillOnce(Return(true)); 297 EXPECT_CALL(*service2.get(), Save(_)).WillOnce(Return(true)); 298 299 ASSERT_TRUE(profile_->AdoptService(service1)); 300 ASSERT_TRUE(profile_->AdoptService(service2)); 301 302 profile_->Save(); 303} 304 305TEST_F(ProfileTest, EntryEnumeration) { 306 scoped_refptr<MockService> service1(CreateMockService()); 307 scoped_refptr<MockService> service2(CreateMockService()); 308 string service1_storage_name = Technology::NameFromIdentifier( 309 Technology::kCellular) + "_1"; 310 string service2_storage_name = Technology::NameFromIdentifier( 311 Technology::kCellular) + "_2"; 312 EXPECT_CALL(*service1.get(), Save(_)) 313 .WillRepeatedly(Invoke(service1.get(), &MockService::FauxSave)); 314 EXPECT_CALL(*service2.get(), Save(_)) 315 .WillRepeatedly(Invoke(service2.get(), &MockService::FauxSave)); 316 EXPECT_CALL(*service1.get(), GetStorageIdentifier()) 317 .WillRepeatedly(Return(service1_storage_name)); 318 EXPECT_CALL(*service2.get(), GetStorageIdentifier()) 319 .WillRepeatedly(Return(service2_storage_name)); 320 321 string service1_name(service1->unique_name()); 322 string service2_name(service2->unique_name()); 323 324 ASSERT_TRUE(profile_->AdoptService(service1)); 325 ASSERT_TRUE(profile_->AdoptService(service2)); 326 327 Error error; 328 ASSERT_EQ(2, profile_->EnumerateEntries(&error).size()); 329 330 ASSERT_TRUE(profile_->AbandonService(service1)); 331 ASSERT_EQ(service2_storage_name, profile_->EnumerateEntries(&error)[0]); 332 333 ASSERT_TRUE(profile_->AbandonService(service2)); 334 ASSERT_EQ(0, profile_->EnumerateEntries(&error).size()); 335} 336 337TEST_F(ProfileTest, LoadUserProfileList) { 338 FilePath list_path(FilePath(storage_path()).Append("test.profile")); 339 vector<Profile::Identifier> identifiers = 340 Profile::LoadUserProfileList(list_path); 341 EXPECT_TRUE(identifiers.empty()); 342 343 const char kUser0[] = "scarecrow"; 344 const char kUser1[] = "jeans"; 345 const char kIdentifier0[] = "rattlesnake"; 346 const char kIdentifier1[] = "ceiling"; 347 const char kHash0[] = "neighbors"; 348 string data(base::StringPrintf("\n" 349 "~userbut/nospacehere\n" 350 "defaultprofile notaccepted\n" 351 "~%s/%s %s\n" 352 "~userbutno/hash\n" 353 " ~dontaccept/leadingspaces hash\n" 354 "~this_username_fails_to_parse/id hash\n" 355 "~%s/%s \n\n", 356 kUser0, kIdentifier0, kHash0, 357 kUser1, kIdentifier1)); 358 EXPECT_EQ(data.size(), base::WriteFile(list_path, data.data(), data.size())); 359 identifiers = Profile::LoadUserProfileList(list_path); 360 EXPECT_EQ(2, identifiers.size()); 361 EXPECT_EQ(kUser0, identifiers[0].user); 362 EXPECT_EQ(kIdentifier0, identifiers[0].identifier); 363 EXPECT_EQ(kHash0, identifiers[0].user_hash); 364 EXPECT_EQ(kUser1, identifiers[1].user); 365 EXPECT_EQ(kIdentifier1, identifiers[1].identifier); 366 EXPECT_EQ("", identifiers[1].user_hash); 367} 368 369TEST_F(ProfileTest, SaveUserProfileList) { 370 const char kUser0[] = "user0"; 371 const char kIdentifier0[] = "id0"; 372 Profile::Identifier id0(kUser0, kIdentifier0); 373 const char kHash0[] = "hash0"; 374 id0.user_hash = kHash0; 375 vector<ProfileRefPtr> profiles; 376 profiles.push_back(new Profile( 377 control_interface(), metrics(), manager(), id0, FilePath(), false)); 378 379 const char kUser1[] = "user1"; 380 const char kIdentifier1[] = "id1"; 381 Profile::Identifier id1(kUser1, kIdentifier1); 382 const char kHash1[] = "hash1"; 383 id1.user_hash = kHash1; 384 profiles.push_back(new Profile( 385 control_interface(), metrics(), manager(), id1, FilePath(), false)); 386 387 388 const char kIdentifier2[] = "id2"; 389 Profile::Identifier id2("", kIdentifier2); 390 const char kHash2[] = "hash2"; 391 id1.user_hash = kHash2; 392 profiles.push_back(new Profile( 393 control_interface(), metrics(), manager(), id2, FilePath(), false)); 394 395 FilePath list_path(FilePath(storage_path()).Append("test.profile")); 396 EXPECT_TRUE(Profile::SaveUserProfileList(list_path, profiles)); 397 398 string profile_data; 399 EXPECT_TRUE(base::ReadFileToString(list_path, &profile_data)); 400 EXPECT_EQ(base::StringPrintf("~%s/%s %s\n~%s/%s %s\n", 401 kUser0, kIdentifier0, kHash0, 402 kUser1, kIdentifier1, kHash1), 403 profile_data); 404} 405 406TEST_F(ProfileTest, MatchesIdentifier) { 407 static const char kUser[] = "theUser"; 408 static const char kIdentifier[] = "theIdentifier"; 409 Profile::Identifier id(kUser, kIdentifier); 410 ProfileRefPtr profile(new Profile( 411 control_interface(), metrics(), manager(), id, FilePath(), false)); 412 EXPECT_TRUE(profile->MatchesIdentifier(id)); 413 EXPECT_FALSE(profile->MatchesIdentifier(Profile::Identifier(kUser, ""))); 414 EXPECT_FALSE( 415 profile->MatchesIdentifier(Profile::Identifier("", kIdentifier))); 416 EXPECT_FALSE( 417 profile->MatchesIdentifier(Profile::Identifier(kIdentifier, kUser))); 418} 419 420TEST_F(ProfileTest, InitStorage) { 421 Profile::Identifier id("theUser", "theIdentifier"); 422 ASSERT_TRUE(base::CreateDirectory( 423 base::FilePath(storage_path()).Append("theUser"))); 424 425 // Profile doesn't exist but we wanted it to. 426 EXPECT_FALSE(ProfileInitStorage(id, Profile::kOpenExisting, false, 427 Error::kNotFound)); 428 429 // Success case, with a side effect of creating the profile. 430 EXPECT_TRUE(ProfileInitStorage(id, Profile::kCreateNew, true, 431 Error::kSuccess)); 432 433 // The results from our two test cases above will now invert since 434 // the profile now exists. First, we now succeed if we require that 435 // the profile already exist... 436 EXPECT_TRUE(ProfileInitStorage(id, Profile::kOpenExisting, false, 437 Error::kSuccess)); 438 439 // And we fail if we require that it doesn't. 440 EXPECT_FALSE(ProfileInitStorage(id, Profile::kCreateNew, false, 441 Error::kAlreadyExists)); 442 443 // As a sanity check, ensure "create or open" works for both profile-exists... 444 EXPECT_TRUE(ProfileInitStorage(id, Profile::kCreateOrOpenExisting, false, 445 Error::kSuccess)); 446 447 // ...and for a new profile that doesn't exist. 448 Profile::Identifier id2("theUser", "theIdentifier2"); 449 // Let's just make double-check that this profile really doesn't exist. 450 ASSERT_FALSE(ProfileInitStorage(id2, Profile::kOpenExisting, false, 451 Error::kNotFound)); 452 453 // Then test that with "create or open" we succeed. 454 EXPECT_TRUE(ProfileInitStorage(id2, Profile::kCreateOrOpenExisting, false, 455 Error::kSuccess)); 456 457 // Corrupt the profile storage. 458 FilePath final_path( 459 base::StringPrintf("%s/%s/%s.profile", storage_path().c_str(), 460 id.user.c_str(), id.identifier.c_str())); 461#ifdef ENABLE_JSON_STORE 462 final_path = final_path.AddExtension("json"); 463#endif 464 string data = "]corrupt_data["; 465 EXPECT_EQ(data.size(), base::WriteFile(final_path, data.data(), data.size())); 466 467 // Then test that we fail to open this file. 468 EXPECT_CALL(*mock_metrics_, NotifyCorruptedProfile()); 469 EXPECT_FALSE(ProfileInitStorage(id, Profile::kOpenExisting, false, 470 Error::kInternalError)); 471 Mock::VerifyAndClearExpectations(mock_metrics_.get()); 472 473 // But then on a second try the file no longer exists. 474 EXPECT_CALL(*mock_metrics_, NotifyCorruptedProfile()).Times(0); 475 ASSERT_FALSE(ProfileInitStorage(id, Profile::kOpenExisting, false, 476 Error::kNotFound)); 477} 478 479TEST_F(ProfileTest, UpdateDevice) { 480 EXPECT_FALSE(profile_->UpdateDevice(nullptr)); 481} 482 483TEST_F(ProfileTest, GetServiceFromEntry) { 484 std::unique_ptr<MockManager> manager(new StrictMock<MockManager>( 485 control_interface(), dispatcher(), metrics())); 486 profile_->manager_ = manager.get(); 487 488 MockStore* storage(new StrictMock<MockStore>()); 489 profile_->storage_.reset(storage); // Passes ownership 490 const string kEntryName("entry_name"); 491 492 // If entry does not appear in storage, GetServiceFromEntry() should return 493 // an error. 494 EXPECT_CALL(*storage, ContainsGroup(kEntryName)) 495 .WillOnce(Return(false)); 496 { 497 Error error; 498 profile_->GetServiceFromEntry(kEntryName, &error); 499 EXPECT_EQ(Error::kNotFound, error.type()); 500 } 501 Mock::VerifyAndClearExpectations(storage); 502 503 EXPECT_CALL(*storage, ContainsGroup(kEntryName)) 504 .WillRepeatedly(Return(true)); 505 506 // Service entry already registered with the manager, the registered service 507 // is returned. 508 scoped_refptr<MockService> registered_service(CreateMockService()); 509 EXPECT_CALL(*manager.get(), 510 GetServiceWithStorageIdentifier(profile_, kEntryName, _)) 511 .WillOnce(Return(registered_service)); 512 { 513 Error error; 514 EXPECT_EQ(registered_service, 515 profile_->GetServiceFromEntry(kEntryName, &error)); 516 EXPECT_TRUE(error.IsSuccess()); 517 } 518 Mock::VerifyAndClearExpectations(manager.get()); 519 520 // Service entry not registered with the manager, a temporary service is 521 // created/returned. 522 scoped_refptr<MockService> temporary_service(CreateMockService()); 523 EXPECT_CALL(*manager.get(), 524 GetServiceWithStorageIdentifier(profile_, kEntryName, _)) 525 .WillOnce(Return(nullptr)); 526 EXPECT_CALL(*manager.get(), 527 CreateTemporaryServiceFromProfile(profile_, kEntryName, _)) 528 .WillOnce(Return(temporary_service)); 529 { 530 Error error; 531 EXPECT_EQ(temporary_service, 532 profile_->GetServiceFromEntry(kEntryName, &error)); 533 EXPECT_TRUE(error.IsSuccess()); 534 } 535 Mock::VerifyAndClearExpectations(manager.get()); 536} 537 538} // namespace shill 539