OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "storage/browser/blob/blob_storage_context.h" | 5 #include "storage/browser/blob/blob_storage_context.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include <limits> | 9 #include <limits> |
10 #include <memory> | 10 #include <memory> |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 } | 92 } |
93 | 93 |
94 void SaveBlobStatus(BlobStatus* status_ptr, BlobStatus status) { | 94 void SaveBlobStatus(BlobStatus* status_ptr, BlobStatus status) { |
95 *status_ptr = status; | 95 *status_ptr = status; |
96 } | 96 } |
97 | 97 |
98 void SaveBlobStatusAndFiles(BlobStatus* status_ptr, | 98 void SaveBlobStatusAndFiles(BlobStatus* status_ptr, |
99 std::vector<FileCreationInfo>* files_ptr, | 99 std::vector<FileCreationInfo>* files_ptr, |
100 BlobStatus status, | 100 BlobStatus status, |
101 std::vector<FileCreationInfo> files) { | 101 std::vector<FileCreationInfo> files) { |
102 EXPECT_FALSE(BlobStatusIsError(status)); | |
103 *status_ptr = status; | 102 *status_ptr = status; |
104 for (FileCreationInfo& info : files) { | 103 for (FileCreationInfo& info : files) { |
105 files_ptr->push_back(std::move(info)); | 104 files_ptr->push_back(std::move(info)); |
106 } | 105 } |
107 } | 106 } |
108 | 107 |
109 void IncrementNumber(size_t* number, BlobStatus status) { | |
110 EXPECT_EQ(BlobStatus::DONE, status); | |
111 *number = *number + 1; | |
112 } | |
113 | |
114 } // namespace | 108 } // namespace |
115 | 109 |
116 class BlobStorageContextTest : public testing::Test { | 110 class BlobStorageContextTest : public testing::Test { |
117 protected: | 111 protected: |
118 BlobStorageContextTest() {} | 112 BlobStorageContextTest() {} |
119 ~BlobStorageContextTest() override {} | 113 ~BlobStorageContextTest() override {} |
120 | 114 |
121 void SetUp() override { | 115 void SetUp() override { context_ = base::MakeUnique<BlobStorageContext>(); } |
122 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
123 context_ = base::MakeUnique<BlobStorageContext>(); | |
124 } | |
125 | |
126 void TearDown() override { | |
127 base::RunLoop().RunUntilIdle(); | |
128 file_runner_->RunPendingTasks(); | |
129 ASSERT_TRUE(temp_dir_.Delete()); | |
130 } | |
131 | 116 |
132 std::unique_ptr<BlobDataHandle> SetupBasicBlob(const std::string& id) { | 117 std::unique_ptr<BlobDataHandle> SetupBasicBlob(const std::string& id) { |
133 BlobDataBuilder builder(id); | 118 BlobDataBuilder builder(id); |
134 builder.AppendData("1", 1); | 119 builder.AppendData("1", 1); |
135 builder.set_content_type("text/plain"); | 120 builder.set_content_type("text/plain"); |
136 return context_->AddFinishedBlob(builder); | 121 return context_->AddFinishedBlob(builder); |
137 } | 122 } |
138 | 123 |
139 void SetTestMemoryLimits() { | 124 void SetTestMemoryLimits() { |
140 BlobStorageLimits limits; | 125 BlobStorageLimits limits; |
141 limits.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes; | 126 limits.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes; |
142 limits.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes; | 127 limits.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes; |
143 limits.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize; | 128 limits.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize; |
144 limits.max_blob_disk_space = kTestBlobStorageMaxDiskSpace; | 129 limits.max_blob_disk_space = kTestBlobStorageMaxDiskSpace; |
145 limits.min_page_file_size = kTestBlobStorageMinFileSizeBytes; | 130 limits.min_page_file_size = kTestBlobStorageMinFileSizeBytes; |
146 limits.max_file_size = kTestBlobStorageMaxFileSizeBytes; | 131 limits.max_file_size = kTestBlobStorageMaxFileSizeBytes; |
147 context_->mutable_memory_controller()->set_limits_for_testing(limits); | 132 context_->mutable_memory_controller()->set_limits_for_testing(limits); |
148 } | 133 } |
149 | 134 |
150 void IncrementRefCount(const std::string& uuid) { | 135 void IncrementRefCount(const std::string& uuid) { |
151 context_->IncrementBlobRefCount(uuid); | 136 context_->IncrementBlobRefCount(uuid); |
152 } | 137 } |
153 | 138 |
154 void DecrementRefCount(const std::string& uuid) { | 139 void DecrementRefCount(const std::string& uuid) { |
155 context_->DecrementBlobRefCount(uuid); | 140 context_->DecrementBlobRefCount(uuid); |
156 } | 141 } |
157 | 142 |
158 std::vector<FileCreationInfo> files_; | 143 std::vector<FileCreationInfo> files_; |
159 base::ScopedTempDir temp_dir_; | |
160 scoped_refptr<TestSimpleTaskRunner> file_runner_ = new TestSimpleTaskRunner(); | |
161 | 144 |
162 base::MessageLoop fake_io_message_loop_; | 145 base::MessageLoop fake_io_message_loop_; |
163 std::unique_ptr<BlobStorageContext> context_; | 146 std::unique_ptr<BlobStorageContext> context_; |
164 }; | 147 }; |
165 | 148 |
166 TEST_F(BlobStorageContextTest, BuildBlobAsync) { | 149 TEST_F(BlobStorageContextTest, BuildBlobAsync) { |
167 const std::string kId("id"); | 150 const std::string kId("id"); |
168 const size_t kSize = 10u; | 151 const size_t kSize = 10u; |
169 BlobStatus status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | 152 BlobStatus status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; |
170 | 153 |
(...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle->CreateSnapshot(); | 504 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle->CreateSnapshot(); |
522 EXPECT_EQ(*data, builder); | 505 EXPECT_EQ(*data, builder); |
523 EXPECT_FALSE(data_handle->HasOneRef()) | 506 EXPECT_FALSE(data_handle->HasOneRef()) |
524 << "Data handle was destructed while context and builder still exist."; | 507 << "Data handle was destructed while context and builder still exist."; |
525 } | 508 } |
526 EXPECT_TRUE(data_handle->HasOneRef()) | 509 EXPECT_TRUE(data_handle->HasOneRef()) |
527 << "Data handle was not destructed along with blob storage context."; | 510 << "Data handle was not destructed along with blob storage context."; |
528 base::RunLoop().RunUntilIdle(); | 511 base::RunLoop().RunUntilIdle(); |
529 } | 512 } |
530 | 513 |
531 TEST_F(BlobStorageContextTest, BuildFutureFileOnlyBlob) { | |
532 const std::string kId1("id1"); | |
533 context_ = | |
534 base::MakeUnique<BlobStorageContext>(temp_dir_.GetPath(), file_runner_); | |
535 SetTestMemoryLimits(); | |
536 | |
537 BlobDataBuilder builder(kId1); | |
538 builder.set_content_type("text/plain"); | |
539 builder.AppendFutureFile(0, 10, 0); | |
540 | |
541 BlobStatus status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
542 std::unique_ptr<BlobDataHandle> handle = context_->BuildBlob( | |
543 builder, base::Bind(&SaveBlobStatusAndFiles, &status, &files_)); | |
544 | |
545 size_t blobs_finished = 0; | |
546 EXPECT_EQ(BlobStatus::PENDING_QUOTA, handle->GetBlobStatus()); | |
547 EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, status); | |
548 handle->RunOnConstructionComplete( | |
549 base::Bind(&IncrementNumber, &blobs_finished)); | |
550 EXPECT_EQ(0u, blobs_finished); | |
551 | |
552 EXPECT_TRUE(file_runner_->HasPendingTask()); | |
553 file_runner_->RunPendingTasks(); | |
554 EXPECT_EQ(0u, blobs_finished); | |
555 EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, status); | |
556 EXPECT_EQ(BlobStatus::PENDING_QUOTA, handle->GetBlobStatus()); | |
557 base::RunLoop().RunUntilIdle(); | |
558 | |
559 EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, status); | |
560 EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, handle->GetBlobStatus()); | |
561 EXPECT_EQ(0u, blobs_finished); | |
562 | |
563 ASSERT_EQ(1u, files_.size()); | |
564 | |
565 builder.PopulateFutureFile(0, files_[0].file_reference, base::Time::Max()); | |
566 context_->NotifyTransportComplete(kId1); | |
567 | |
568 EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus()); | |
569 EXPECT_EQ(0u, blobs_finished); | |
570 base::RunLoop().RunUntilIdle(); | |
571 EXPECT_EQ(1u, blobs_finished); | |
572 | |
573 builder.Clear(); | |
574 handle.reset(); | |
575 files_.clear(); | |
576 base::RunLoop().RunUntilIdle(); | |
577 // We should have file cleanup tasks. | |
578 EXPECT_TRUE(file_runner_->HasPendingTask()); | |
579 file_runner_->RunPendingTasks(); | |
580 base::RunLoop().RunUntilIdle(); | |
581 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
582 EXPECT_EQ(0lu, context_->memory_controller().disk_usage()); | |
583 } | |
584 | |
585 TEST_F(BlobStorageContextTest, CompoundBlobs) { | 514 TEST_F(BlobStorageContextTest, CompoundBlobs) { |
586 const std::string kId1("id1"); | 515 const std::string kId1("id1"); |
587 const std::string kId2("id2"); | 516 const std::string kId2("id2"); |
588 const std::string kId3("id3"); | 517 const std::string kId3("id3"); |
589 | 518 |
590 // Setup a set of blob data for testing. | 519 // Setup a set of blob data for testing. |
591 base::Time time1, time2; | 520 base::Time time1, time2; |
592 base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1); | 521 base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1); |
593 base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2); | 522 base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2); |
594 | 523 |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
719 builder3.AppendData("data"); | 648 builder3.AppendData("data"); |
720 builder3.AppendBlob(kBuildingId); | 649 builder3.AppendBlob(kBuildingId); |
721 handle = context_->AddFinishedBlob(builder3); | 650 handle = context_->AddFinishedBlob(builder3); |
722 EXPECT_TRUE(handle->IsBroken()); | 651 EXPECT_TRUE(handle->IsBroken()); |
723 EXPECT_TRUE(context_->registry().HasEntry(kReferencingId)); | 652 EXPECT_TRUE(context_->registry().HasEntry(kReferencingId)); |
724 handle.reset(); | 653 handle.reset(); |
725 base::RunLoop().RunUntilIdle(); | 654 base::RunLoop().RunUntilIdle(); |
726 EXPECT_FALSE(context_->registry().HasEntry(kReferencingId)); | 655 EXPECT_FALSE(context_->registry().HasEntry(kReferencingId)); |
727 } | 656 } |
728 | 657 |
729 namespace { | 658 // TODO(michaeln): tests for the depcrecated url stuff |
730 constexpr size_t kTotalRawBlobs = 200; | |
731 constexpr size_t kTotalSlicedBlobs = 100; | |
732 constexpr char kTestDiskCacheData[] = "Test Blob Data"; | |
733 | 659 |
734 // Appends data and data types that depend on the index. This is designed to | 660 } // namespace content |
735 // exercise all types of combinations of data, future data, files, future files, | |
736 // and disk cache entries. | |
737 size_t AppendDataInBuilder(BlobDataBuilder* builder, | |
738 size_t index, | |
739 disk_cache::Entry* cache_entry) { | |
740 size_t size = 0; | |
741 // We can't have both future data and future files, so split those up. | |
742 if (index % 2 != 0) { | |
743 builder->AppendFutureData(5u); | |
744 size += 5u; | |
745 if (index % 3 == 1) { | |
746 builder->AppendData("abcdefghij", 4u); | |
747 size += 4u; | |
748 } | |
749 if (index % 3 == 0) { | |
750 builder->AppendFutureData(1u); | |
751 size += 1u; | |
752 } | |
753 } else if (index % 3 == 0) { | |
754 builder->AppendFutureFile(0lu, 3lu, 0); | |
755 size += 3u; | |
756 } | |
757 if (index % 5 != 0) { | |
758 builder->AppendFile( | |
759 base::FilePath::FromUTF8Unsafe(base::SizeTToString(index)), 0ul, 20ul, | |
760 base::Time::Max()); | |
761 size += 20u; | |
762 } | |
763 if (index % 3 != 0) { | |
764 scoped_refptr<BlobDataBuilder::DataHandle> disk_cache_data_handle = | |
765 new EmptyDataHandle(); | |
766 builder->AppendDiskCacheEntry(disk_cache_data_handle, cache_entry, | |
767 kTestDiskCacheStreamIndex); | |
768 size += strlen(kTestDiskCacheData); | |
769 } | |
770 return size; | |
771 } | |
772 | |
773 bool DoesBuilderHaveFutureData(size_t index) { | |
774 return index < kTotalRawBlobs && (index % 2 != 0 || index % 3 == 0); | |
775 } | |
776 | |
777 void PopulateDataInBuilder(BlobDataBuilder* builder, | |
778 size_t index, | |
779 base::TaskRunner* file_runner) { | |
780 if (index % 2 != 0) { | |
781 builder->PopulateFutureData(0, "abcde", 0, 5); | |
782 if (index % 3 == 0) { | |
783 builder->PopulateFutureData(1, "z", 0, 1); | |
784 } | |
785 } else if (index % 3 == 0) { | |
786 scoped_refptr<ShareableFileReference> file_ref = | |
787 ShareableFileReference::GetOrCreate( | |
788 base::FilePath::FromUTF8Unsafe( | |
789 base::SizeTToString(index + kTotalRawBlobs)), | |
790 ShareableFileReference::DONT_DELETE_ON_FINAL_RELEASE, file_runner); | |
791 builder->PopulateFutureFile(0, file_ref, base::Time::Max()); | |
792 } | |
793 } | |
794 } // namespace | |
795 | |
796 TEST_F(BlobStorageContextTest, BuildBlobCombinations) { | |
797 const std::string kId("id"); | |
798 | |
799 context_ = | |
800 base::MakeUnique<BlobStorageContext>(temp_dir_.GetPath(), file_runner_); | |
801 | |
802 SetTestMemoryLimits(); | |
803 std::unique_ptr<disk_cache::Backend> cache = CreateInMemoryDiskCache(); | |
804 ASSERT_TRUE(cache); | |
805 disk_cache::ScopedEntryPtr entry = | |
806 CreateDiskCacheEntry(cache.get(), "test entry", kTestDiskCacheData); | |
807 | |
808 // This tests mixed blob content with both synchronous and asynchronous | |
809 // construction. Blobs should also be paged to disk during execution. | |
810 std::vector<std::unique_ptr<BlobDataBuilder>> builders; | |
811 std::vector<size_t> sizes; | |
812 for (size_t i = 0; i < kTotalRawBlobs; i++) { | |
813 builders.emplace_back(new BlobDataBuilder(base::SizeTToString(i))); | |
814 auto& builder = *builders.back(); | |
815 size_t size = AppendDataInBuilder(&builder, i, entry.get()); | |
816 EXPECT_NE(0u, size); | |
817 sizes.push_back(size); | |
818 } | |
819 | |
820 for (size_t i = 0; i < kTotalSlicedBlobs; i++) { | |
821 builders.emplace_back( | |
822 new BlobDataBuilder(base::SizeTToString(i + kTotalRawBlobs))); | |
823 size_t source_size = sizes[i]; | |
824 size_t offset = source_size == 1 ? 0 : i % (source_size - 1); | |
825 size_t size = (i % (source_size - offset)) + 1; | |
826 builders.back()->AppendBlob(base::SizeTToString(i), offset, size); | |
827 } | |
828 | |
829 size_t total_finished_blobs = 0; | |
830 std::vector<std::unique_ptr<BlobDataHandle>> handles; | |
831 std::vector<BlobStatus> statuses; | |
832 std::vector<bool> populated; | |
833 statuses.resize(kTotalRawBlobs, | |
834 BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS); | |
835 populated.resize(kTotalRawBlobs, false); | |
836 for (size_t i = 0; i < builders.size(); i++) { | |
837 BlobDataBuilder& builder = *builders[i]; | |
838 builder.set_content_type("text/plain"); | |
839 bool has_pending_memory = DoesBuilderHaveFutureData(i); | |
840 std::unique_ptr<BlobDataHandle> handle = context_->BuildBlob( | |
841 builder, | |
842 has_pending_memory | |
843 ? base::Bind(&SaveBlobStatusAndFiles, &statuses[0] + i, &files_) | |
844 : BlobStorageContext::TransportAllowedCallback()); | |
845 handle->RunOnConstructionComplete( | |
846 base::Bind(&IncrementNumber, &total_finished_blobs)); | |
847 handles.push_back(std::move(handle)); | |
848 } | |
849 base::RunLoop().RunUntilIdle(); | |
850 | |
851 // We should be needing to send a page or two to disk. | |
852 EXPECT_TRUE(file_runner_->HasPendingTask()); | |
853 do { | |
854 file_runner_->RunPendingTasks(); | |
855 base::RunLoop().RunUntilIdle(); | |
856 // Continue populating data for items that can fit. | |
857 for (size_t i = 0; i < kTotalRawBlobs; i++) { | |
858 BlobDataBuilder* builder = builders[i].get(); | |
859 if (DoesBuilderHaveFutureData(i) && !populated[i] && | |
860 statuses[i] == BlobStatus::PENDING_TRANSPORT) { | |
861 PopulateDataInBuilder(builder, i, file_runner_.get()); | |
862 context_->NotifyTransportComplete(base::SizeTToString(i)); | |
863 populated[i] = true; | |
864 } | |
865 } | |
866 base::RunLoop().RunUntilIdle(); | |
867 } while (file_runner_->HasPendingTask()); | |
868 | |
869 // Check all builders with future items were signalled and populated. | |
870 for (size_t i = 0; i < populated.size(); i++) { | |
871 if (DoesBuilderHaveFutureData(i)) { | |
872 EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, statuses[i]) << i; | |
873 EXPECT_TRUE(populated[i]) << i; | |
874 } | |
875 } | |
876 base::RunLoop().RunUntilIdle(); | |
877 | |
878 // We should be completely built now. | |
879 EXPECT_EQ(kTotalRawBlobs + kTotalSlicedBlobs, total_finished_blobs); | |
880 for (std::unique_ptr<BlobDataHandle>& handle : handles) { | |
881 EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus()); | |
882 } | |
883 handles.clear(); | |
884 base::RunLoop().RunUntilIdle(); | |
885 files_.clear(); | |
886 // We should have file cleanup tasks. | |
887 EXPECT_TRUE(file_runner_->HasPendingTask()); | |
888 file_runner_->RunPendingTasks(); | |
889 base::RunLoop().RunUntilIdle(); | |
890 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
891 EXPECT_EQ(0lu, context_->memory_controller().disk_usage()); | |
892 } | |
893 | |
894 // TODO(michaeln): tests for the deprecated url stuff | |
895 | |
896 } // namespace storage | |
OLD | NEW |