| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| #include <string> |
| #include <utility> |
| |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/run_loop.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "chrome/browser/ash/fileapi/recent_file.h" |
| #include "chrome/browser/ash/fileapi/recent_model.h" |
| #include "chrome/browser/ash/fileapi/recent_model_factory.h" |
| #include "chrome/browser/ash/fileapi/test/fake_recent_source.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "storage/browser/file_system/file_system_url.h" |
| #include "storage/common/file_system/file_system_types.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/storage_key/storage_key.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| RecentFile MakeRecentFile(const std::string& name, |
| const base::Time& last_modified) { |
| storage::FileSystemURL url = storage::FileSystemURL::CreateForTest( |
| blink::StorageKey(), storage::kFileSystemTypeLocal, base::FilePath(name)); |
| return RecentFile(url, last_modified); |
| } |
| |
| std::vector<std::unique_ptr<RecentSource>> BuildDefaultSourcesWithLag( |
| uint32_t lag1_ms, |
| uint32_t lag2_ms) { |
| auto source1 = std::make_unique<FakeRecentSource>(); |
| source1->SetLag(base::Milliseconds(lag1_ms)); |
| source1->AddFile( |
| MakeRecentFile("aaa.jpg", base::Time::FromSecondsSinceUnixEpoch(1))); |
| source1->AddFile( |
| MakeRecentFile("ccc.mp4", base::Time::FromSecondsSinceUnixEpoch(3))); |
| |
| auto source2 = std::make_unique<FakeRecentSource>(); |
| source2->SetLag(base::Milliseconds(lag2_ms)); |
| source2->AddFile( |
| MakeRecentFile("bbb.png", base::Time::FromSecondsSinceUnixEpoch(2))); |
| source2->AddFile( |
| MakeRecentFile("ddd.ogg", base::Time::FromSecondsSinceUnixEpoch(4))); |
| |
| std::vector<std::unique_ptr<RecentSource>> sources; |
| sources.emplace_back(std::move(source1)); |
| sources.emplace_back(std::move(source2)); |
| return sources; |
| } |
| |
| std::vector<std::unique_ptr<RecentSource>> BuildDefaultSources() { |
| return BuildDefaultSourcesWithLag(0, 0); |
| } |
| |
| std::vector<RecentFile> GetRecentFiles(RecentModel* model, |
| base::TimeDelta now_delta, |
| RecentModel::FileType file_type, |
| bool invalidate_cache) { |
| std::vector<RecentFile> files; |
| |
| base::RunLoop run_loop; |
| |
| model->GetRecentFiles( |
| nullptr /* file_system_context */, GURL() /* origin */, |
| "" /* query: unused */, now_delta, file_type, invalidate_cache, |
| base::BindOnce( |
| [](base::RunLoop* run_loop, std::vector<RecentFile>* files_out, |
| const std::vector<RecentFile>& files) { |
| *files_out = files; |
| run_loop->Quit(); |
| }, |
| &run_loop, &files)); |
| |
| run_loop.Run(); |
| |
| return files; |
| } |
| |
| } // namespace |
| |
| class RecentModelTest : public testing::Test { |
| public: |
| RecentModelTest() |
| : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} |
| |
| protected: |
| using RecentSourceList = std::vector<std::unique_ptr<RecentSource>>; |
| using RecentSourceListFactory = base::RepeatingCallback<RecentSourceList()>; |
| |
| std::vector<RecentFile> BuildModelAndGetRecentFiles( |
| RecentSourceListFactory source_list_factory, |
| size_t max_files, |
| const base::TimeDelta& cutoff_delta, |
| RecentModel::FileType file_type, |
| bool invalidate_cache) { |
| RecentModel* model = static_cast<RecentModel*>( |
| RecentModelFactory::GetInstance()->SetTestingFactoryAndUse( |
| &profile_, |
| base::BindRepeating( |
| [](const RecentSourceListFactory& source_list_factory, |
| size_t max_files, content::BrowserContext* context) |
| -> std::unique_ptr<KeyedService> { |
| return RecentModel::CreateForTest(source_list_factory.Run(), |
| max_files); |
| }, |
| std::move(source_list_factory), max_files))); |
| |
| model->SetScanTimeout(base::Milliseconds(500)); |
| |
| return GetRecentFiles(model, cutoff_delta, file_type, invalidate_cache); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| TestingProfile profile_; |
| }; |
| |
| TEST_F(RecentModelTest, GetRecentFiles) { |
| std::vector<RecentFile> files = BuildModelAndGetRecentFiles( |
| base::BindRepeating(&BuildDefaultSources), 10, base::Days(30), |
| RecentModel::FileType::kAll, false); |
| |
| ASSERT_EQ(4u, files.size()); |
| EXPECT_EQ("ddd.ogg", files[0].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[0].last_modified()); |
| EXPECT_EQ("ccc.mp4", files[1].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[1].last_modified()); |
| EXPECT_EQ("bbb.png", files[2].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(2), files[2].last_modified()); |
| EXPECT_EQ("aaa.jpg", files[3].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(1), files[3].last_modified()); |
| } |
| |
| TEST_F(RecentModelTest, GetRecentFiles_MaxFiles) { |
| std::vector<RecentFile> files = BuildModelAndGetRecentFiles( |
| base::BindRepeating(&BuildDefaultSources), 3, base::Days(30), |
| RecentModel::FileType::kAll, false); |
| |
| ASSERT_EQ(3u, files.size()); |
| EXPECT_EQ("ddd.ogg", files[0].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[0].last_modified()); |
| EXPECT_EQ("ccc.mp4", files[1].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[1].last_modified()); |
| EXPECT_EQ("bbb.png", files[2].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(2), files[2].last_modified()); |
| } |
| |
| TEST_F(RecentModelTest, GetRecentFiles_CutoffTime) { |
| // TODO(b:307455066): Fix last modified time in created files. |
| // Files created for the tests have last modified time set to 1, 2, 3, and 4 |
| // seconds since Unix epoch. This allows for last modified time checks to be |
| // performed independently of when the test is run. However, this is not |
| // ideal. First, there is a repetitive base::Time::FromSecondsSinceUnixEpoch |
| // scattered in the tests. Changing, say, the last modified time for aaa.jpg |
| // would require changing it everywhere in the tests. In addition, now that |
| // one can pass time delta to GetRecentFiles method, this makes testing |
| // fragile as we need to hit the right span of a few seconds between now and |
| // the start of the Unix epoch. |
| std::vector<RecentFile> files = BuildModelAndGetRecentFiles( |
| base::BindRepeating(&BuildDefaultSources), 10, |
| base::Time::Now() - base::Time::FromMillisecondsSinceUnixEpoch(2500), |
| RecentModel::FileType::kAll, false); |
| |
| ASSERT_EQ(2u, files.size()); |
| EXPECT_EQ("ddd.ogg", files[0].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[0].last_modified()); |
| EXPECT_EQ("ccc.mp4", files[1].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[1].last_modified()); |
| } |
| |
| TEST_F(RecentModelTest, GetRecentFiles_UmaStats) { |
| base::HistogramTester histogram_tester; |
| |
| BuildModelAndGetRecentFiles( |
| base::BindRepeating([]() { return RecentSourceList(); }), 10, |
| base::Days(30), RecentModel::FileType::kAll, false); |
| |
| histogram_tester.ExpectTotalCount(RecentModel::kLoadHistogramName, 1); |
| } |
| |
| TEST_F(RecentModelTest, GetRecentFiles_Audio) { |
| std::vector<RecentFile> files = BuildModelAndGetRecentFiles( |
| base::BindRepeating(&BuildDefaultSources), 10, base::Days(30), |
| RecentModel::FileType::kAudio, false); |
| |
| ASSERT_EQ(1u, files.size()); |
| EXPECT_EQ("ddd.ogg", files[0].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[0].last_modified()); |
| } |
| |
| TEST_F(RecentModelTest, GetRecentFiles_Image) { |
| std::vector<RecentFile> files = BuildModelAndGetRecentFiles( |
| base::BindRepeating(&BuildDefaultSources), 10, base::Days(30), |
| RecentModel::FileType::kImage, false); |
| |
| ASSERT_EQ(2u, files.size()); |
| EXPECT_EQ("bbb.png", files[0].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(2), files[0].last_modified()); |
| EXPECT_EQ("aaa.jpg", files[1].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(1), files[1].last_modified()); |
| } |
| |
| TEST_F(RecentModelTest, GetRecentFiles_Video) { |
| std::vector<RecentFile> files = BuildModelAndGetRecentFiles( |
| base::BindRepeating(&BuildDefaultSources), 10, base::Days(30), |
| RecentModel::FileType::kVideo, false); |
| |
| ASSERT_EQ(1u, files.size()); |
| EXPECT_EQ("ccc.mp4", files[0].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[0].last_modified()); |
| } |
| |
| TEST_F(RecentModelTest, GetRecentFiles_OneSourceIsLate) { |
| std::vector<RecentFile> files = BuildModelAndGetRecentFiles( |
| base::BindRepeating(&BuildDefaultSourcesWithLag, 100, 501), 10, |
| base::Days(30), RecentModel::FileType::kAll, false); |
| |
| ASSERT_EQ(2u, files.size()); |
| EXPECT_EQ("ccc.mp4", files[0].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[0].last_modified()); |
| EXPECT_EQ("aaa.jpg", files[1].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(1), files[1].last_modified()); |
| } |
| |
| TEST_F(RecentModelTest, GetRecentFiles_NoSourceIsLate) { |
| std::vector<RecentFile> files = BuildModelAndGetRecentFiles( |
| base::BindRepeating(&BuildDefaultSourcesWithLag, 499, 111), 10, |
| base::Days(30), RecentModel::FileType::kAll, false); |
| |
| ASSERT_EQ(4u, files.size()); |
| EXPECT_EQ("ddd.ogg", files[0].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[0].last_modified()); |
| EXPECT_EQ("ccc.mp4", files[1].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[1].last_modified()); |
| EXPECT_EQ("bbb.png", files[2].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(2), files[2].last_modified()); |
| EXPECT_EQ("aaa.jpg", files[3].url().path().value()); |
| EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(1), files[3].last_modified()); |
| } |
| |
| // Do not use RecentModelTest fixture, because we need to get a reference of |
| // RecentModel and call GetRecentFiles() multiple times. |
| TEST(RecentModelCacheTest, GetRecentFiles_InvalidateCache) { |
| content::BrowserTaskEnvironment task_environment; |
| std::unique_ptr<RecentModel> model = |
| RecentModel::CreateForTest(BuildDefaultSources(), 10); |
| |
| std::vector<RecentFile> files1 = GetRecentFiles( |
| model.get(), base::TimeDelta::Max(), RecentModel::FileType::kAll, false); |
| ASSERT_EQ(4u, files1.size()); |
| |
| // Shutdown() will clear all sources. |
| model->Shutdown(); |
| |
| // The returned file list should still has 4 files even though all sources has |
| // been cleared in Shutdown(), because it hits cache. |
| std::vector<RecentFile> files2 = GetRecentFiles( |
| model.get(), base::TimeDelta::Max(), RecentModel::FileType::kAll, false); |
| ASSERT_EQ(4u, files2.size()); |
| |
| // Inalidate cache and query again. |
| std::vector<RecentFile> files3 = GetRecentFiles( |
| model.get(), base::Days(30), RecentModel::FileType::kAll, true); |
| ASSERT_EQ(0u, files3.size()); |
| } |
| |
| } // namespace ash |