Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(480)

Side by Side Diff: extensions/browser/api/lock_screen_data/item_storage_unittest.cc

Issue 2934293003: The chrome.lockScreen.data API implementation (Closed)
Patch Set: remove FilePath*UTF8Unsafe methods Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 "extensions/browser/api/lock_screen_data/item_storage.h"
6
7 #include <map>
8 #include <memory>
9 #include <queue>
10 #include <utility>
11 #include <vector>
12
13 #include "base/callback.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/files/scoped_temp_dir.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/memory/ref_counted.h"
19 #include "base/run_loop.h"
20 #include "base/task_scheduler/post_task.h"
21 #include "base/time/time.h"
22 #include "chromeos/login/login_state.h"
23 #include "components/prefs/scoped_user_pref_update.h"
24 #include "components/prefs/testing_pref_service.h"
25 #include "components/sync_preferences/testing_pref_service_syncable.h"
26 #include "components/user_prefs/user_prefs.h"
27 #include "content/public/test/test_browser_context.h"
28 #include "content/public/test/test_browser_thread_bundle.h"
29 #include "extensions/browser/api/lock_screen_data/data_item.h"
30 #include "extensions/browser/api/lock_screen_data/operation_result.h"
31 #include "extensions/browser/event_router.h"
32 #include "extensions/browser/event_router_factory.h"
33 #include "extensions/browser/extension_registry.h"
34 #include "extensions/browser/extensions_test.h"
35 #include "extensions/browser/test_extensions_browser_client.h"
36 #include "extensions/common/api/lock_screen_data.h"
37 #include "extensions/common/extension_builder.h"
38 #include "extensions/common/value_builder.h"
39 #include "testing/gtest/include/gtest/gtest.h"
40
41 namespace extensions {
42 namespace lock_screen_data {
43
44 namespace {
45
46 const char kTestUserIdHash[] = "user_id_hash";
47 const char kTestSymmetricKey[] = "fake_symmetric_key";
48
49 const char kDataItemsPrefKey[] = "lockScreenDataItems";
50 const char kDataItemFileKey[] = "backing_file";
51 const char kDataItemIdKey[] = "id";
52
53 void RecordWriteResult(OperationResult* result_out, OperationResult result) {
54 *result_out = result;
55 }
56
57 void WriteNotCalled(OperationResult result) {
58 ADD_FAILURE() << "Called unexpectedly";
59 }
60
61 void RecordReadResult(OperationResult* result_out,
62 std::unique_ptr<std::vector<char>>* content_out,
63 OperationResult result,
64 std::unique_ptr<std::vector<char>> content) {
65 *result_out = result;
66 *content_out = std::move(content);
67 }
68
69 void ReadNotCalled(OperationResult result,
70 std::unique_ptr<std::vector<char>> content) {
71 ADD_FAILURE() << "Called unexpectedly";
72 }
73
74 class TestEventRouter : public extensions::EventRouter {
75 public:
76 explicit TestEventRouter(content::BrowserContext* context)
77 : extensions::EventRouter(context, nullptr) {}
78 ~TestEventRouter() override = default;
79
80 bool ExtensionHasEventListener(const std::string& extension_id,
81 const std::string& event_name) const override {
82 return event_name ==
83 extensions::api::lock_screen_data::OnDataItemsAvailable::kEventName;
84 }
85
86 void BroadcastEvent(std::unique_ptr<extensions::Event> event) override {}
87
88 void DispatchEventToExtension(
89 const std::string& extension_id,
90 std::unique_ptr<extensions::Event> event) override {
91 if (event->event_name !=
92 extensions::api::lock_screen_data::OnDataItemsAvailable::kEventName) {
93 return;
94 }
95 ASSERT_TRUE(event->event_args);
96 const base::Value* arg_value = nullptr;
97 ASSERT_TRUE(event->event_args->Get(0, &arg_value));
98 ASSERT_TRUE(arg_value);
99
100 std::unique_ptr<extensions::api::lock_screen_data::DataItemsAvailableEvent>
101 event_args = extensions::api::lock_screen_data::
102 DataItemsAvailableEvent::FromValue(*arg_value);
103 ASSERT_TRUE(event_args);
104 was_locked_values_.push_back(event_args->was_locked);
105 }
106
107 const std::vector<bool>& was_locked_values() const {
108 return was_locked_values_;
109 }
110
111 void ClearWasLockedValues() { was_locked_values_.clear(); }
112
113 private:
114 std::vector<bool> was_locked_values_;
115
116 DISALLOW_COPY_AND_ASSIGN(TestEventRouter);
117 };
118
119 std::unique_ptr<KeyedService> TestEventRouterFactoryFunction(
120 content::BrowserContext* context) {
121 return base::MakeUnique<TestEventRouter>(context);
122 }
123
124 // Keeps track of all operations requested from the test data item.
125 // The operations will remain in pending state until completed by calling
126 // CompleteNextOperation.
127 // This is owned by the test class, but data items created during the test have
128 // a reference to the object. More than one data item can have a reference to
129 // this - data items with the same ID will get the same operation queue.
130 class OperationQueue {
131 public:
132 enum class OperationType { kWrite, kRead, kDelete };
133
134 struct PendingOperation {
135 explicit PendingOperation(OperationType type) : type(type) {}
136
137 OperationType type;
138 // Set only for write - data to be written.
139 std::vector<char> data;
140
141 // Callback for write operation.
142 DataItem::WriteCallback write_callback;
143
144 // Callback for read operation.
145 DataItem::ReadCallback read_callback;
146
147 // Callback for delete operation.
148 DataItem::DeleteCallback delete_callback;
149 };
150
151 OperationQueue() = default;
152
153 ~OperationQueue() = default;
154
155 void AddWrite(const base::FilePath& path,
156 const std::vector<char>& data,
157 const DataItem::WriteCallback& callback) {
158 ASSERT_FALSE(path.empty());
159 if (path_.empty())
160 path_ = path;
161 ASSERT_EQ(path_, path);
162
163 PendingOperation operation(OperationType::kWrite);
164 operation.data = data;
165 operation.write_callback = callback;
166
167 pending_operations_.emplace(std::move(operation));
168 }
169
170 void AddRead(const base::FilePath& path,
171 const DataItem::ReadCallback& callback) {
172 ASSERT_FALSE(path_.empty());
173 ASSERT_EQ(path_, path);
174
175 PendingOperation operation(OperationType::kRead);
176 operation.read_callback = callback;
177
178 pending_operations_.emplace(std::move(operation));
179 }
180
181 void AddDelete(const base::FilePath& path,
182 const DataItem::DeleteCallback& callback) {
183 ASSERT_FALSE(path_.empty());
184 ASSERT_EQ(path_, path);
185
186 PendingOperation operation(OperationType::kDelete);
187 operation.delete_callback = callback;
188
189 pending_operations_.emplace(std::move(operation));
190 }
191
192 // Completes the next pendig operation.
193 // |expected_type| - the expected type of the next operation - this will fail
194 // if the operation does not match.
195 // |result| - the intended operation result.
196 void CompleteNextOperation(OperationType expected_type,
197 OperationResult result) {
198 ASSERT_FALSE(pending_operations_.empty());
199 ASSERT_FALSE(deleted_);
200
201 const PendingOperation& operation = pending_operations_.front();
202
203 ASSERT_EQ(expected_type, operation.type);
204
205 switch (expected_type) {
206 case OperationType::kWrite: {
207 if (result == OperationResult::kSuccess)
208 content_ = operation.data;
209 DataItem::WriteCallback callback = operation.write_callback;
210 pending_operations_.pop();
211 callback.Run(result);
212 break;
213 }
214 case OperationType::kDelete: {
215 if (result == OperationResult::kSuccess) {
216 deleted_ = true;
217 content_ = std::vector<char>();
218 }
219
220 DataItem::DeleteCallback callback = operation.delete_callback;
221 pending_operations_.pop();
222 callback.Run(result);
223 break;
224 }
225 case OperationType::kRead: {
226 std::unique_ptr<std::vector<char>> result_data;
227 if (result == OperationResult::kSuccess) {
228 result_data = base::MakeUnique<std::vector<char>>(content_.begin(),
229 content_.end());
230 }
231
232 DataItem::ReadCallback callback = operation.read_callback;
233 pending_operations_.pop();
234 callback.Run(result, std::move(result_data));
235 break;
236 }
237 default:
238 ADD_FAILURE() << "Unexpected operation";
239 return;
240 }
241 }
242
243 bool HasPendingOperations() const { return !pending_operations_.empty(); }
244
245 bool deleted() const { return deleted_; }
246
247 const std::vector<char>& content() const { return content_; }
248
249 const base::FilePath& path() const { return path_; }
250
251 private:
252 std::queue<PendingOperation> pending_operations_;
253 std::vector<char> content_;
254 bool deleted_ = false;
255 base::FilePath path_;
256
257 DISALLOW_COPY_AND_ASSIGN(OperationQueue);
258 };
259
260 // Test data item - routes all requests to the OperationQueue provided through
261 // the ctor - the owning test is responsible for completing the started
262 // operations.
263 class TestDataItem : public DataItem {
264 public:
265 // |operations| - Operation queue used by this data item - not owned by this,
266 // and expected to outlive this object.
267 TestDataItem(const std::string& id, OperationQueue* operations)
268 : DataItem(id), operations_(operations) {}
269
270 ~TestDataItem() override = default;
271
272 OperationResult Write(
273 const std::vector<char>& data,
274 const std::string& crypto_key,
275 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
276 const WriteCallback& callback) override {
277 if (backing_file().empty()) {
278 ADD_FAILURE() << "Attempt to write item with unset file.";
279 return OperationResult::kNoBackingFile;
280 }
281 EXPECT_EQ(kTestSymmetricKey, crypto_key);
282
283 operations_->AddWrite(backing_file(), data, callback);
284 return OperationResult::kPending;
285 }
286
287 OperationResult Read(
288 const std::string& crypto_key,
289 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
290 const ReadCallback& callback) override {
291 if (backing_file().empty()) {
292 ADD_FAILURE() << "Attempt to read item with unset file.";
293 return OperationResult::kNoBackingFile;
294 }
295
296 EXPECT_EQ(kTestSymmetricKey, crypto_key);
297
298 operations_->AddRead(backing_file(), callback);
299 return OperationResult::kPending;
300 }
301
302 OperationResult Delete(
303 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
304 const DeleteCallback& callback) override {
305 if (backing_file().empty()) {
306 ADD_FAILURE() << "Attempt to delete item with unset file.";
307 return OperationResult::kNoBackingFile;
308 }
309
310 operations_->AddDelete(backing_file(), callback);
311 set_backing_file(base::FilePath());
312 return OperationResult::kPending;
313 }
314
315 private:
316 OperationQueue* operations_;
317
318 DISALLOW_COPY_AND_ASSIGN(TestDataItem);
319 };
320
321 class ItemStorageTest : public ExtensionsTest {
322 public:
323 ItemStorageTest()
324 : ExtensionsTest(base::MakeUnique<content::TestBrowserThreadBundle>()) {}
325 ~ItemStorageTest() override = default;
326
327 void SetUp() override {
328 ExtensionsTest::SetUp();
329
330 ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
331 ItemStorage::RegisterLocalState(local_state_.registry());
332 user_prefs::UserPrefs::Set(browser_context(), &testing_pref_service_);
333 extensions_browser_client()->set_lock_screen_context(&lock_screen_context_);
334
335 chromeos::LoginState::Initialize();
336 chromeos::LoginState::Get()->SetLoggedInStateAndPrimaryUser(
337 chromeos::LoginState::LOGGED_IN_ACTIVE,
338 chromeos::LoginState::LOGGED_IN_USER_REGULAR, kTestUserIdHash);
339
340 CreateTestExtension();
341
342 // Inject custom data item factory to be used with ItemStorage instances.
343 item_factory_ =
344 base::Bind(&ItemStorageTest::CreateItem, base::Unretained(this));
345 ItemStorage::SetItemFactoryForTesting(&item_factory_);
346
347 ResetItemStorage();
348 }
349
350 void TearDown() override {
351 item_storage_.reset();
352 ItemStorage::SetItemFactoryForTesting(nullptr);
353 chromeos::LoginState::Shutdown();
354 ExtensionsTest::TearDown();
355 }
356
357 OperationQueue* GetOperations(const std::string& id) {
358 return operations_[id].get();
359 }
360
361 void UnsetItemStorage() { item_storage_.reset(); }
362
363 void ResetItemStorage() {
364 item_storage_.reset();
365 item_storage_ =
366 base::MakeUnique<ItemStorage>(browser_context(), &local_state_,
367 kTestSymmetricKey, test_dir_.GetPath());
368 }
369
370 // Utility method for setting test item content.
371 bool SetItemContent(const std::string& id, const std::vector<char>& content) {
372 OperationQueue* item_operations = GetOperations(id);
373 if (!item_operations) {
374 ADD_FAILURE() << "No item operations";
375 return false;
376 }
377 OperationResult write_result = OperationResult::kFailed;
378 if (item_storage()->SetItemContent(
379 extension()->id(), id, content,
380 base::Bind(&RecordWriteResult, &write_result)) !=
381 OperationResult::kPending) {
382 ADD_FAILURE() << "Failed to initiate write";
383 return false;
384 }
385 if (!item_operations->HasPendingOperations()) {
386 ADD_FAILURE() << "Write not registered";
387 return false;
388 }
389 item_operations->CompleteNextOperation(
390 OperationQueue::OperationType::kWrite, OperationResult::kSuccess);
391 EXPECT_EQ(OperationResult::kSuccess, write_result);
392 return write_result == OperationResult::kSuccess;
393 }
394
395 // Utility method for creating a new testing data item, and setting its
396 // content.
397 const DataItem* CreateItemWithContent(const std::vector<char>& content) {
398 const DataItem* item = item_storage()->CreateItem(extension()->id());
399 if (!item) {
400 ADD_FAILURE() << "Item creation failed";
401 return nullptr;
402 }
403
404 if (!SetItemContent(item->id(), content))
405 return nullptr;
406
407 return item;
408 }
409
410 // Finds an item with the ID |id| in list of items |items|.
411 const DataItem* FindItem(const std::string& id,
412 const std::vector<const DataItem*> items) {
413 for (const auto* item : items) {
414 if (item && item->id() == id)
415 return item;
416 }
417 return nullptr;
418 }
419
420 ItemStorage* item_storage() { return item_storage_.get(); }
421
422 content::BrowserContext* lock_screen_context() {
423 return &lock_screen_context_;
424 }
425
426 const Extension* extension() const { return extension_.get(); }
427
428 const base::FilePath& test_dir() const { return test_dir_.GetPath(); }
429
430 PrefService* local_state() { return &local_state_; }
431
432 private:
433 void CreateTestExtension() {
434 DictionaryBuilder app_builder;
435 app_builder.Set("background",
436 DictionaryBuilder()
437 .Set("scripts", ListBuilder().Append("script").Build())
438 .Build());
439 ListBuilder app_handlers_builder;
440 app_handlers_builder.Append(DictionaryBuilder()
441 .Set("action", "new_note")
442 .SetBoolean("enabled_on_lock_screen", true)
443 .Build());
444 extension_ =
445 ExtensionBuilder()
446 .SetManifest(
447 DictionaryBuilder()
448 .Set("name", "Test app")
449 .Set("version", "1.0")
450 .Set("manifest_version", 2)
451 .Set("app", app_builder.Build())
452 .Set("action_handlers", app_handlers_builder.Build())
453 .Set("permissions",
454 ListBuilder().Append("lockScreen").Build())
455 .Build())
456 .Build();
457 ExtensionRegistry::Get(browser_context())->AddEnabled(extension_);
458 }
459
460 // Callback for creating test data items - this is the callback passed to
461 // ItemStorage via SetItemFactoryForTesting.
462 std::unique_ptr<DataItem> CreateItem(const std::string& id) {
463 // If there is an operation queue for the item id, reuse it in order to
464 // retain state on ItemStorage restart.
465 OperationQueue* operation_queue = GetOperations(id);
466 if (!operation_queue) {
467 operations_[id] = base::MakeUnique<OperationQueue>();
468 operation_queue = operations_[id].get();
469 }
470 return base::MakeUnique<TestDataItem>(id, operation_queue);
471 }
472
473 std::unique_ptr<ItemStorage> item_storage_;
474
475 content::TestBrowserContext lock_screen_context_;
476 TestingPrefServiceSimple local_state_;
477
478 base::ScopedTempDir test_dir_;
479
480 sync_preferences::TestingPrefServiceSyncable testing_pref_service_;
481
482 ItemStorage::ItemFactoryCallback item_factory_;
483
484 scoped_refptr<const Extension> extension_;
485
486 std::map<std::string, std::unique_ptr<OperationQueue>> operations_;
487
488 DISALLOW_COPY_AND_ASSIGN(ItemStorageTest);
489 };
490
491 } // namespace
492
493 TEST_F(ItemStorageTest, GetDependingOnSessionState) {
494 // Session state not initialized.
495 EXPECT_FALSE(ItemStorage::Get(browser_context()));
496 EXPECT_FALSE(ItemStorage::Get(lock_screen_context()));
497
498 // Locked session.
499 item_storage()->SetSessionLocked(true);
500 EXPECT_FALSE(ItemStorage::Get(browser_context()));
501 EXPECT_EQ(item_storage(), ItemStorage::Get(lock_screen_context()));
502
503 item_storage()->SetSessionLocked(false);
504
505 EXPECT_EQ(item_storage(), ItemStorage::Get(browser_context()));
506 EXPECT_FALSE(ItemStorage::Get(lock_screen_context()));
507 }
508
509 TEST_F(ItemStorageTest, SetAndGetContent) {
510 item_storage()->SetSessionLocked(true);
511
512 const DataItem* item = item_storage()->CreateItem(extension()->id());
513 ASSERT_TRUE(item);
514
515 std::vector<const DataItem*> all_items =
516 item_storage()->GetAllForExtension(extension()->id());
517 ASSERT_EQ(1u, all_items.size());
518 EXPECT_EQ(item->id(), all_items[0]->id());
519
520 OperationQueue* item_operations = GetOperations(item->id());
521 ASSERT_TRUE(item_operations);
522 EXPECT_FALSE(item_operations->HasPendingOperations());
523
524 std::vector<char> content = {'f', 'i', 'l', 'e'};
525 OperationResult write_result = OperationResult::kFailed;
526 EXPECT_EQ(OperationResult::kPending,
527 item_storage()->SetItemContent(
528 extension()->id(), item->id(), content,
529 base::Bind(&RecordWriteResult, &write_result)));
530
531 EXPECT_FALSE(item->backing_file().empty());
532 EXPECT_TRUE(test_dir()
533 .AppendASCII(kTestUserIdHash)
534 .AppendASCII(extension()->id())
535 .IsParent(item->backing_file()));
536
537 item_operations->CompleteNextOperation(OperationQueue::OperationType::kWrite,
538 OperationResult::kSuccess);
539
540 EXPECT_EQ(OperationResult::kSuccess, write_result);
541 EXPECT_EQ(content, item_operations->content());
542
543 OperationResult read_result = OperationResult::kFailed;
544 std::unique_ptr<std::vector<char>> read_content;
545
546 EXPECT_EQ(OperationResult::kPending,
547 item_storage()->GetItemContent(
548 extension()->id(), item->id(),
549 base::Bind(&RecordReadResult, &read_result, &read_content)));
550
551 item_operations->CompleteNextOperation(OperationQueue::OperationType::kRead,
552 OperationResult::kSuccess);
553 EXPECT_EQ(OperationResult::kSuccess, read_result);
554 EXPECT_EQ(content, *read_content);
555
556 item_storage()->DeleteItem(extension()->id(), item->id());
557
558 item_operations->CompleteNextOperation(OperationQueue::OperationType::kDelete,
559 OperationResult::kSuccess);
560 EXPECT_TRUE(item_operations->deleted());
561 }
562
563 TEST_F(ItemStorageTest, HandleNonExistent) {
564 item_storage()->SetSessionLocked(true);
565
566 const DataItem* item = item_storage()->CreateItem(extension()->id());
567 ASSERT_TRUE(item);
568
569 std::vector<char> content = {'x'};
570 EXPECT_EQ(
571 OperationResult::kNotFound,
572 item_storage()->SetItemContent(extension()->id(), "non_existent", content,
573 base::Bind(&WriteNotCalled)));
574 EXPECT_EQ(OperationResult::kNotFound,
575 item_storage()->SetItemContent("non_existent", item->id(), content,
576 base::Bind(&WriteNotCalled)));
577
578 EXPECT_EQ(OperationResult::kNotFound,
579 item_storage()->GetItemContent(extension()->id(), "non_existent",
580 base::Bind(&ReadNotCalled)));
581 EXPECT_EQ(OperationResult::kNotFound,
582 item_storage()->GetItemContent("non_existent", item->id(),
583 base::Bind(&ReadNotCalled)));
584
585 EXPECT_EQ(OperationResult::kNotFound,
586 item_storage()->DeleteItem(extension()->id(), "non_existen"));
587 EXPECT_EQ(OperationResult::kNotFound,
588 item_storage()->DeleteItem("non_existent", item->id()));
589 }
590
591 TEST_F(ItemStorageTest, HandleFailure) {
592 item_storage()->SetSessionLocked(true);
593
594 const DataItem* item = CreateItemWithContent({'x'});
595 ASSERT_TRUE(item);
596 OperationQueue* operations = GetOperations(item->id());
597 ASSERT_TRUE(operations);
598
599 OperationResult write_result = OperationResult::kFailed;
600 EXPECT_EQ(OperationResult::kPending,
601 item_storage()->SetItemContent(
602 extension()->id(), item->id(), {'x'},
603 base::Bind(&RecordWriteResult, &write_result)));
604 operations->CompleteNextOperation(OperationQueue::OperationType::kWrite,
605 OperationResult::kInvalidKey);
606 EXPECT_EQ(OperationResult::kInvalidKey, write_result);
607
608 OperationResult read_result = OperationResult::kFailed;
609 std::unique_ptr<std::vector<char>> read_content;
610 EXPECT_EQ(OperationResult::kPending,
611 item_storage()->GetItemContent(
612 extension()->id(), item->id(),
613 base::Bind(&RecordReadResult, &read_result, &read_content)));
614 operations->CompleteNextOperation(OperationQueue::OperationType::kRead,
615 OperationResult::kWrongKey);
616 EXPECT_EQ(OperationResult::kWrongKey, read_result);
617 EXPECT_FALSE(read_content);
618
619 EXPECT_FALSE(operations->HasPendingOperations());
620 }
621
622 TEST_F(ItemStorageTest, GetUnsetContent) {
623 item_storage()->SetSessionLocked(true);
624
625 const DataItem* item = item_storage()->CreateItem(extension()->id());
626 ASSERT_TRUE(item);
627
628 EXPECT_EQ(OperationResult::kNoBackingFile,
629 item_storage()->GetItemContent(extension()->id(), item->id(),
630 base::Bind(&ReadNotCalled)));
631 OperationQueue* operations = GetOperations(item->id());
632 ASSERT_TRUE(operations);
633 EXPECT_FALSE(operations->HasPendingOperations());
634 }
635
636 TEST_F(ItemStorageTest, ItemPersistence) {
637 item_storage()->SetSessionLocked(true);
638
639 const DataItem* first_item = CreateItemWithContent({'f', 'i', 'l', 'e', '1'});
640 ASSERT_TRUE(first_item);
641 const std::string first_id = first_item->id();
642 const base::FilePath first_file = first_item->backing_file();
643
644 const DataItem* second_item =
645 CreateItemWithContent({'f', 'i', 'l', 'e', '2'});
646 ASSERT_TRUE(second_item);
647 const std::string second_id = second_item->id();
648 const base::FilePath second_file = second_item->backing_file();
649
650 const DataItem* empty_item = item_storage()->CreateItem(extension()->id());
651 ASSERT_TRUE(empty_item);
652 const std::string empty_id = empty_item->id();
653
654 const DataItem* deleted_item = CreateItemWithContent({'x'});
655 ASSERT_TRUE(deleted_item);
656 EXPECT_EQ(OperationResult::kSuccess,
657 item_storage()->DeleteItem(extension()->id(), deleted_item->id()));
658
659 ResetItemStorage();
660
661 // Items that have been written should have been restored, unlike the empty
662 // one.
663 std::vector<const DataItem*> items =
664 item_storage()->GetAllForExtension(extension()->id());
665 ASSERT_EQ(3u, items.size());
666
667 const DataItem* first_recovered = FindItem(first_id, items);
668 ASSERT_TRUE(first_recovered);
669 EXPECT_EQ(first_recovered->backing_file(), first_file);
670
671 const DataItem* second_recovered = FindItem(second_id, items);
672 EXPECT_EQ(second_recovered->backing_file(), second_file);
673
674 const DataItem* empty_recovered = FindItem(empty_id, items);
675 EXPECT_TRUE(empty_recovered->backing_file().empty());
676 }
677
678 TEST_F(ItemStorageTest, ExtensionUninstall) {
679 item_storage()->SetSessionLocked(true);
680
681 const DataItem* item = CreateItemWithContent({'f', 'i', 'l', 'e', '1'});
682 const base::FilePath item_path = item->backing_file();
683
684 // Create a backing file to verify it gets deleted on extension install.
685 ASSERT_TRUE(base::CreateDirectoryAndGetError(
686 test_dir().AppendASCII(kTestUserIdHash).AppendASCII(extension()->id()),
687 nullptr));
688 ASSERT_EQ(5, base::WriteFile(item->backing_file(), "file1", 5));
689
690 ExtensionRegistry::Get(browser_context())->RemoveEnabled(extension()->id());
691 ExtensionRegistry::Get(browser_context())
692 ->TriggerOnUninstalled(extension(), UNINSTALL_REASON_FOR_TESTING);
693
694 // Drain task loop.
695 base::RunLoop run_loop;
696 item_storage()->task_runner_for_testing()->PostTaskAndReply(
697 FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
698 run_loop.Run();
699
700 EXPECT_FALSE(base::PathExists(item_path));
701 EXPECT_FALSE(item_storage()->CreateItem(extension()->id()));
702 EXPECT_TRUE(item_storage()->GetAllForExtension(extension()->id()).empty());
703
704 ResetItemStorage();
705 item_storage()->SetSessionLocked(true);
706
707 EXPECT_TRUE(item_storage()->GetAllForExtension(extension()->id()).empty());
708 }
709
710 TEST_F(ItemStorageTest, ExtensionUninstallWhileStorageNotSet) {
711 item_storage()->SetSessionLocked(true);
712
713 const DataItem* item = CreateItemWithContent({'f', 'i', 'l', 'e', '1'});
714 const base::FilePath item_path = item->backing_file();
715
716 // Create a backing file to verify it gets deleted on extension install.
717 ASSERT_TRUE(base::CreateDirectoryAndGetError(
718 test_dir().AppendASCII(kTestUserIdHash).AppendASCII(extension()->id()),
719 nullptr));
720 ASSERT_EQ(5, base::WriteFile(item->backing_file(), "file1", 5));
721
722 UnsetItemStorage();
723
724 ExtensionRegistry::Get(browser_context())->RemoveEnabled(extension()->id());
725 ExtensionRegistry::Get(browser_context())
726 ->TriggerOnUninstalled(extension(), UNINSTALL_REASON_FOR_TESTING);
727
728 ResetItemStorage();
729
730 // Drain task loop.
731 base::RunLoop run_loop;
732 item_storage()->task_runner_for_testing()->PostTaskAndReply(
733 FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
734 run_loop.Run();
735
736 EXPECT_FALSE(item_storage()->CreateItem(extension()->id()));
737 EXPECT_TRUE(item_storage()->GetAllForExtension(extension()->id()).empty());
738 EXPECT_FALSE(item_storage()->CreateItem(extension()->id()));
739 }
740
741 TEST_F(ItemStorageTest, ReloadWithCorruptedLocalState) {
742 item_storage()->SetSessionLocked(true);
743
744 const DataItem* item = CreateItemWithContent({'f', 'i', 'l', 'e', '1'});
745 const base::FilePath item_path = item->backing_file();
746 const std::string item_id = item->id();
747
748 // Create a backing file to verify it gets deleted on extension install.
749 ASSERT_TRUE(base::CreateDirectoryAndGetError(
750 test_dir().AppendASCII(kTestUserIdHash).AppendASCII(extension()->id()),
751 nullptr));
752 ASSERT_EQ(5, base::WriteFile(item->backing_file(), "file1", 5));
753
754 UnsetItemStorage();
755
756 {
757 DictionaryPrefUpdate update(local_state(), kDataItemsPrefKey);
758 update->SetString(
759 base::StringPrintf("%s.%s", kTestUserIdHash, extension()->id().c_str()),
760 "invalid");
761 }
762
763 ResetItemStorage();
764
765 // Drain task loop.
766 base::RunLoop run_loop;
767 item_storage()->task_runner_for_testing()->PostTaskAndReply(
768 FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
769 run_loop.Run();
770
771 EXPECT_TRUE(item_storage()->GetAllForExtension(extension()->id()).empty());
772 }
773
774 TEST_F(ItemStorageTest, ReloadWithCorruptedItemInLocalState) {
775 item_storage()->SetSessionLocked(true);
776
777 const DataItem* item = CreateItemWithContent({'f', 'i', 'l', 'e', '1'});
778 const std::string item_id = item->id();
779
780 const DataItem* corrupted_item =
781 CreateItemWithContent({'f', 'i', 'l', 'e', '2'});
782 const std::string corrupted_item_id = corrupted_item->id();
783
784 UnsetItemStorage();
785
786 {
787 DictionaryPrefUpdate update(local_state(), kDataItemsPrefKey);
788 update->SetInteger(
789 base::StringPrintf("%s.%s.%s.%s", kTestUserIdHash,
790 extension()->id().c_str(), corrupted_item_id.c_str(),
791 kDataItemIdKey),
792 123456);
793 }
794
795 ResetItemStorage();
796
797 // Drain task loop.
798 base::RunLoop run_loop;
799 item_storage()->task_runner_for_testing()->PostTaskAndReply(
800 FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
801 run_loop.Run();
802
803 std::vector<const DataItem*> items =
804 item_storage()->GetAllForExtension(extension()->id());
805 ASSERT_EQ(1u, items.size());
806 EXPECT_EQ(item_id, items[0]->id());
807 }
808
809 TEST_F(ItemStorageTest, ReloadWithWrongPathInLocalState) {
810 item_storage()->SetSessionLocked(true);
811
812 const DataItem* item = CreateItemWithContent({'f', 'i', 'l', 'e', '1'});
813 const std::string item_id = item->id();
814
815 const DataItem* corrupted_item =
816 CreateItemWithContent({'f', 'i', 'l', 'e', '2'});
817 const std::string corrupted_item_id = corrupted_item->id();
818
819 UnsetItemStorage();
820
821 {
822 // Set local state item path to a path that is not allowed - i.e. that is
823 // not under storage root.
824 DictionaryPrefUpdate update(local_state(), kDataItemsPrefKey);
825 update->SetString(
826 base::StringPrintf("%s.%s.%s.%s", kTestUserIdHash,
827 extension()->id().c_str(), corrupted_item_id.c_str(),
828 kDataItemFileKey),
829 "/home/chronos/user/file");
830 }
831
832 ResetItemStorage();
833
834 // Drain task loop.
835 base::RunLoop run_loop;
836 item_storage()->task_runner_for_testing()->PostTaskAndReply(
837 FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
838 run_loop.Run();
839
840 std::vector<const DataItem*> items =
841 item_storage()->GetAllForExtension(extension()->id());
842 ASSERT_EQ(1u, items.size());
843 EXPECT_EQ(item_id, items[0]->id());
844 }
845
846 TEST_F(ItemStorageTest, DataItemsAvailableEventOnUnlock) {
847 TestEventRouter* event_router = static_cast<TestEventRouter*>(
848 extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
849 browser_context(), &TestEventRouterFactoryFunction));
850 ASSERT_TRUE(event_router);
851
852 EXPECT_TRUE(event_router->was_locked_values().empty());
853
854 item_storage()->SetSessionLocked(true);
855 EXPECT_TRUE(event_router->was_locked_values().empty());
856
857 // No event since no data items associated with the app exist.
858 item_storage()->SetSessionLocked(false);
859 EXPECT_TRUE(event_router->was_locked_values().empty());
860
861 item_storage()->SetSessionLocked(true);
862 const DataItem* item = CreateItemWithContent({'f', 'i', 'l', 'e', '1'});
863 const std::string item_id = item->id();
864 EXPECT_TRUE(event_router->was_locked_values().empty());
865
866 // There's an available data item, so unlock should trigger the event.
867 item_storage()->SetSessionLocked(false);
868 EXPECT_EQ(std::vector<bool>({true}), event_router->was_locked_values());
869 event_router->ClearWasLockedValues();
870
871 // Update the item content while the session is unlocked.
872 EXPECT_TRUE(SetItemContent(item_id, {'f', 'i', 'l', 'e', '2'}));
873
874 item_storage()->SetSessionLocked(true);
875
876 // Data item is still around - notify the app it's available.
877 item_storage()->SetSessionLocked(false);
878 EXPECT_EQ(std::vector<bool>({true}), event_router->was_locked_values());
879 event_router->ClearWasLockedValues();
880
881 item_storage()->SetSessionLocked(true);
882
883 EXPECT_TRUE(SetItemContent(item_id, {'f', 'i', 'l', 'e', '3'}));
884
885 item_storage()->SetSessionLocked(false);
886 EXPECT_EQ(std::vector<bool>({true}), event_router->was_locked_values());
887 event_router->ClearWasLockedValues();
888
889 // When the item is deleted, the data item avilable event should stop firing.
890 EXPECT_EQ(OperationResult::kSuccess,
891 item_storage()->DeleteItem(extension()->id(), item_id));
892 OperationQueue* operations = GetOperations(item_id);
893 ASSERT_TRUE(operations);
894 operations->CompleteNextOperation(OperationQueue::OperationType::kDelete,
895 OperationResult::kSuccess);
896 item_storage()->SetSessionLocked(false);
897 item_storage()->SetSessionLocked(true);
898
899 EXPECT_TRUE(event_router->was_locked_values().empty());
900 }
901
902 TEST_F(ItemStorageTest, DataItemsAvailableEventOnRestart) {
903 TestEventRouter* event_router = static_cast<TestEventRouter*>(
904 extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
905 browser_context(), &TestEventRouterFactoryFunction));
906 ASSERT_TRUE(event_router);
907
908 EXPECT_TRUE(event_router->was_locked_values().empty());
909
910 item_storage()->SetSessionLocked(true);
911 EXPECT_TRUE(event_router->was_locked_values().empty());
912
913 const DataItem* item = CreateItemWithContent({'f', 'i', 'l', 'e', '1'});
914 EXPECT_TRUE(event_router->was_locked_values().empty());
915 const std::string item_id = item->id();
916
917 ResetItemStorage();
918
919 EXPECT_TRUE(event_router->was_locked_values().empty());
920 item_storage()->SetSessionLocked(false);
921
922 EXPECT_EQ(std::vector<bool>({false}), event_router->was_locked_values());
923 event_router->ClearWasLockedValues();
924
925 // The event should be dispatched on next unlock event, as long as a valid
926 // item exists.
927 ResetItemStorage();
928 item_storage()->SetSessionLocked(false);
929
930 EXPECT_EQ(std::vector<bool>({false}), event_router->was_locked_values());
931 event_router->ClearWasLockedValues();
932
933 ResetItemStorage();
934
935 EXPECT_EQ(OperationResult::kSuccess,
936 item_storage()->DeleteItem(extension()->id(), item_id));
937 OperationQueue* operations = GetOperations(item_id);
938 ASSERT_TRUE(operations);
939 operations->CompleteNextOperation(OperationQueue::OperationType::kDelete,
940 OperationResult::kSuccess);
941
942 item_storage()->SetSessionLocked(false);
943 EXPECT_TRUE(event_router->was_locked_values().empty());
944 }
945
946 } // namespace lock_screen_data
947 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698