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