// 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 <memory>
#include <string>
#include <vector>
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/fileapi/recent_disk_source.h"
#include "chrome/browser/ash/fileapi/recent_file.h"
#include "chrome/browser/ash/fileapi/recent_source.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/browser/test/test_file_system_context.h"
#include "storage/common/file_system/file_system_mount_option.h"
#include "storage/common/file_system/file_system_types.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace ash {
namespace {
// Allows one to specify just the parameters that are relevant to a given test.
struct TestParams {
TestParams& Query(const std::string query) {
query_ = query;
return *this;
}
TestParams& CutoffTime(const base::Time& cutoff_time) {
cutoff_time_ = cutoff_time;
return *this;
}
TestParams& FileType(const RecentSource::FileType type) {
file_type_ = type;
return *this;
}
TestParams& IgnoreDotFiles(bool ignore_dot_files) {
ignore_dot_files_ = ignore_dot_files;
return *this;
}
TestParams& MaxDepth(int max_depth) {
max_depth_ = max_depth;
return *this;
}
TestParams& MaxFiles(int max_files) {
max_files_ = max_files;
return *this;
}
RecentSource::Params MakeParams(storage::FileSystemContext* context,
const int32_t call_id,
const GURL& origin) {
return RecentSource::Params(context, call_id, origin, query_, max_files_,
cutoff_time_, base::TimeTicks::Max(),
file_type_);
}
std::unique_ptr<RecentDiskSource> MakeSource(
const std::string& mount_point_name,
const std::string& uma_histogram_name) {
return std::make_unique<RecentDiskSource>(
extensions::api::file_manager_private::VolumeType::kTesting,
mount_point_name, ignore_dot_files_, max_depth_, uma_histogram_name);
}
std::string query_ = "";
base::Time cutoff_time_ = base::Time::Min();
RecentSource::FileType file_type_ = RecentSource::FileType::kAll;
bool ignore_dot_files_ = false;
int max_depth_ = 0;
size_t max_files_ = 100;
};
class RecentDiskSourceTest : public testing::Test {
public:
RecentDiskSourceTest() : origin_("https://example.com/") {}
void SetUp() override {
profile_ = std::make_unique<TestingProfile>();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
file_system_context_ = storage::CreateFileSystemContextForTesting(
/*quota_manager_proxy=*/nullptr, temp_dir_.GetPath());
mount_point_name_ =
file_manager::util::GetDownloadsMountPointName(profile_.get());
ASSERT_TRUE(
storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
mount_point_name_, storage::kFileSystemTypeTest,
storage::FileSystemMountOption(), base::FilePath()));
}
void TearDown() override {
storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
mount_point_name_);
}
protected:
bool CreateEmptyFile(const std::string& filename, const base::Time& time) {
base::File file(temp_dir_.GetPath().Append(filename),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
if (!file.IsValid()) {
return false;
}
return file.SetTimes(time, time);
}
std::vector<RecentFile> GetRecentFiles(TestParams params) {
std::vector<RecentFile> files;
base::RunLoop run_loop;
auto source = params.MakeSource(mount_point_name_, uma_histogram_name_);
source->GetRecentFiles(
params.MakeParams(file_system_context_.get(), 0, origin_),
base::BindOnce(
[](base::RunLoop* run_loop, std::vector<RecentFile>* out_files,
std::vector<RecentFile> files) {
run_loop->Quit();
*out_files = std::move(files);
},
&run_loop, &files));
run_loop.Run();
return files;
}
content::BrowserTaskEnvironment task_environment_;
const GURL origin_;
std::unique_ptr<TestingProfile> profile_;
base::ScopedTempDir temp_dir_;
scoped_refptr<storage::FileSystemContext> file_system_context_;
std::string mount_point_name_;
const std::string uma_histogram_name_ = "uma_histogram_name";
base::Time base_time_;
};
TEST_F(RecentDiskSourceTest, GetRecentFiles) {
// Oldest
ASSERT_TRUE(
CreateEmptyFile("1.jpg", base::Time::FromSecondsSinceUnixEpoch(1)));
ASSERT_TRUE(
CreateEmptyFile("2.jpg", base::Time::FromSecondsSinceUnixEpoch(2)));
ASSERT_TRUE(
CreateEmptyFile("3.jpg", base::Time::FromSecondsSinceUnixEpoch(3)));
ASSERT_TRUE(
CreateEmptyFile("4.jpg", base::Time::FromSecondsSinceUnixEpoch(4)));
// Newest
std::vector<RecentFile> files = GetRecentFiles(TestParams().MaxFiles(3));
ASSERT_EQ(3u, files.size());
EXPECT_EQ("4.jpg", files[0].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[0].last_modified());
EXPECT_EQ("3.jpg", files[1].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[1].last_modified());
EXPECT_EQ("2.jpg", files[2].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(2), files[2].last_modified());
files = GetRecentFiles(TestParams().Query("4").MaxFiles(3));
ASSERT_EQ(1u, files.size());
EXPECT_EQ("4.jpg", files[0].url().path().BaseName().value());
files = GetRecentFiles(TestParams().Query("foo").MaxFiles(3));
ASSERT_EQ(0u, files.size());
}
TEST_F(RecentDiskSourceTest, GetRecentFiles_CutoffTime) {
// Oldest
ASSERT_TRUE(
CreateEmptyFile("1.jpg", base::Time::FromSecondsSinceUnixEpoch(1)));
ASSERT_TRUE(
CreateEmptyFile("2.jpg", base::Time::FromSecondsSinceUnixEpoch(2)));
ASSERT_TRUE(
CreateEmptyFile("3.jpg", base::Time::FromSecondsSinceUnixEpoch(3)));
ASSERT_TRUE(
CreateEmptyFile("4.jpg", base::Time::FromSecondsSinceUnixEpoch(4)));
// Newest
std::vector<RecentFile> files = GetRecentFiles(
TestParams()
.CutoffTime(base::Time::FromMillisecondsSinceUnixEpoch(2500))
.MaxFiles(3));
ASSERT_EQ(2u, files.size());
EXPECT_EQ("4.jpg", files[0].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[0].last_modified());
EXPECT_EQ("3.jpg", files[1].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[1].last_modified());
}
TEST_F(RecentDiskSourceTest, IgnoreDotFiles) {
ASSERT_TRUE(base::CreateDirectory(temp_dir_.GetPath().Append(".ignore")));
ASSERT_TRUE(base::CreateDirectory(temp_dir_.GetPath().Append("noignore")));
// Oldest
ASSERT_TRUE(CreateEmptyFile("noignore/1.jpg",
base::Time::FromSecondsSinceUnixEpoch(1)));
ASSERT_TRUE(CreateEmptyFile(".ignore/2.jpg",
base::Time::FromSecondsSinceUnixEpoch(2)));
ASSERT_TRUE(
CreateEmptyFile("3.jpg", base::Time::FromSecondsSinceUnixEpoch(3)));
ASSERT_TRUE(
CreateEmptyFile(".4.jpg", base::Time::FromSecondsSinceUnixEpoch(4)));
// Newest
std::vector<RecentFile> files = GetRecentFiles(TestParams().MaxFiles(4));
ASSERT_EQ(4u, files.size());
EXPECT_EQ(".4.jpg", files[0].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[0].last_modified());
EXPECT_EQ("3.jpg", files[1].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[1].last_modified());
EXPECT_EQ("2.jpg", files[2].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(2), files[2].last_modified());
EXPECT_EQ("1.jpg", files[3].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(1), files[3].last_modified());
files = GetRecentFiles(TestParams().IgnoreDotFiles(true).MaxFiles(4));
ASSERT_EQ(2u, files.size());
EXPECT_EQ("3.jpg", files[0].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[0].last_modified());
EXPECT_EQ("1.jpg", files[1].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(1), files[1].last_modified());
}
TEST_F(RecentDiskSourceTest, MaxDepth) {
ASSERT_TRUE(base::CreateDirectory(temp_dir_.GetPath().Append("a")));
ASSERT_TRUE(base::CreateDirectory(temp_dir_.GetPath().Append("a/b")));
ASSERT_TRUE(base::CreateDirectory(temp_dir_.GetPath().Append("a/b/c")));
// Oldest
ASSERT_TRUE(
CreateEmptyFile("1.jpg", base::Time::FromSecondsSinceUnixEpoch(1)));
ASSERT_TRUE(
CreateEmptyFile("a/2.jpg", base::Time::FromSecondsSinceUnixEpoch(2)));
ASSERT_TRUE(
CreateEmptyFile("a/b/3.jpg", base::Time::FromSecondsSinceUnixEpoch(3)));
ASSERT_TRUE(
CreateEmptyFile("a/b/c/4.jpg", base::Time::FromSecondsSinceUnixEpoch(4)));
// Newest
std::vector<RecentFile> files = GetRecentFiles(TestParams().MaxFiles(4));
ASSERT_EQ(4u, files.size());
files = GetRecentFiles(TestParams().MaxFiles(4).MaxDepth(2));
ASSERT_EQ(2u, files.size());
EXPECT_EQ("2.jpg", files[0].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(2), files[0].last_modified());
EXPECT_EQ("1.jpg", files[1].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(1), files[1].last_modified());
}
TEST_F(RecentDiskSourceTest, GetAudioFiles) {
// Oldest
ASSERT_TRUE(
CreateEmptyFile("1.jpg", base::Time::FromSecondsSinceUnixEpoch(1)));
ASSERT_TRUE(
CreateEmptyFile("2.mp4", base::Time::FromSecondsSinceUnixEpoch(2)));
ASSERT_TRUE(
CreateEmptyFile("3.png", base::Time::FromSecondsSinceUnixEpoch(3)));
ASSERT_TRUE(
CreateEmptyFile("4.mp3", base::Time::FromSecondsSinceUnixEpoch(4)));
ASSERT_TRUE(
CreateEmptyFile("5.gif", base::Time::FromSecondsSinceUnixEpoch(5)));
ASSERT_TRUE(
CreateEmptyFile("6.webm", base::Time::FromSecondsSinceUnixEpoch(6)));
ASSERT_TRUE(
CreateEmptyFile("7.amr", base::Time::FromSecondsSinceUnixEpoch(7)));
// Newest
std::vector<RecentFile> files =
GetRecentFiles(TestParams().FileType(RecentSource::FileType::kAudio));
ASSERT_EQ(2u, files.size());
EXPECT_EQ("7.amr", files[0].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(7), files[0].last_modified());
EXPECT_EQ("4.mp3", files[1].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[1].last_modified());
files = GetRecentFiles(
TestParams().Query("7").FileType(RecentSource::FileType::kAudio));
ASSERT_EQ(1u, files.size());
EXPECT_EQ("7.amr", files[0].url().path().BaseName().value());
files = GetRecentFiles(
TestParams().Query("6").FileType(RecentSource::FileType::kAudio));
ASSERT_EQ(0u, files.size());
}
TEST_F(RecentDiskSourceTest, GetImageFiles) {
// Oldest
ASSERT_TRUE(
CreateEmptyFile("1.jpg", base::Time::FromSecondsSinceUnixEpoch(1)));
ASSERT_TRUE(
CreateEmptyFile("2.mp4", base::Time::FromSecondsSinceUnixEpoch(2)));
ASSERT_TRUE(
CreateEmptyFile("3.png", base::Time::FromSecondsSinceUnixEpoch(3)));
ASSERT_TRUE(
CreateEmptyFile("4.mp3", base::Time::FromSecondsSinceUnixEpoch(4)));
ASSERT_TRUE(
CreateEmptyFile("5.gif", base::Time::FromSecondsSinceUnixEpoch(5)));
ASSERT_TRUE(
CreateEmptyFile("6.webm", base::Time::FromSecondsSinceUnixEpoch(6)));
// RAW images are supported
ASSERT_TRUE(
CreateEmptyFile("7.dng", base::Time::FromSecondsSinceUnixEpoch(7)));
ASSERT_TRUE(
CreateEmptyFile("8.nef", base::Time::FromSecondsSinceUnixEpoch(8)));
// Newest
std::vector<RecentFile> files =
GetRecentFiles(TestParams().FileType(RecentSource::FileType::kImage));
ASSERT_EQ(5u, files.size());
EXPECT_EQ("8.nef", files[0].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(8), files[0].last_modified());
EXPECT_EQ("7.dng", files[1].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(7), files[1].last_modified());
EXPECT_EQ("5.gif", files[2].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(5), files[2].last_modified());
EXPECT_EQ("3.png", files[3].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(3), files[3].last_modified());
EXPECT_EQ("1.jpg", files[4].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(1), files[4].last_modified());
}
TEST_F(RecentDiskSourceTest, GetVideoFiles) {
// Oldest
ASSERT_TRUE(
CreateEmptyFile("1.jpg", base::Time::FromSecondsSinceUnixEpoch(1)));
ASSERT_TRUE(
CreateEmptyFile("2.mp4", base::Time::FromSecondsSinceUnixEpoch(2)));
ASSERT_TRUE(
CreateEmptyFile("3.png", base::Time::FromSecondsSinceUnixEpoch(3)));
ASSERT_TRUE(
CreateEmptyFile("4.mp3", base::Time::FromSecondsSinceUnixEpoch(4)));
ASSERT_TRUE(
CreateEmptyFile("5.gif", base::Time::FromSecondsSinceUnixEpoch(5)));
ASSERT_TRUE(
CreateEmptyFile("6.webm", base::Time::FromSecondsSinceUnixEpoch(6)));
ASSERT_TRUE(
CreateEmptyFile("7.avi", base::Time::FromSecondsSinceUnixEpoch(7)));
ASSERT_TRUE(
CreateEmptyFile("8.mov", base::Time::FromSecondsSinceUnixEpoch(8)));
// *.wmv is not supported yet.
ASSERT_TRUE(
CreateEmptyFile("9.wmv", base::Time::FromSecondsSinceUnixEpoch(9)));
// Newest
std::vector<RecentFile> files =
GetRecentFiles(TestParams().FileType(RecentSource::FileType::kVideo));
ASSERT_EQ(4u, files.size());
EXPECT_EQ("8.mov", files[0].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(8), files[0].last_modified());
EXPECT_EQ("7.avi", files[1].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(7), files[1].last_modified());
EXPECT_EQ("6.webm", files[2].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(6), files[2].last_modified());
EXPECT_EQ("2.mp4", files[3].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(2), files[3].last_modified());
}
TEST_F(RecentDiskSourceTest, GetDocumentFiles) {
// Oldest
ASSERT_TRUE(
CreateEmptyFile("1.jpg", base::Time::FromSecondsSinceUnixEpoch(1)));
ASSERT_TRUE(
CreateEmptyFile("2.mp4", base::Time::FromSecondsSinceUnixEpoch(2)));
ASSERT_TRUE(
CreateEmptyFile("3.png", base::Time::FromSecondsSinceUnixEpoch(3)));
ASSERT_TRUE(
CreateEmptyFile("4.doc", base::Time::FromSecondsSinceUnixEpoch(4)));
ASSERT_TRUE(
CreateEmptyFile("5.gif", base::Time::FromSecondsSinceUnixEpoch(5)));
ASSERT_TRUE(
CreateEmptyFile("6.txt", base::Time::FromSecondsSinceUnixEpoch(6)));
ASSERT_TRUE(
CreateEmptyFile("7.avi", base::Time::FromSecondsSinceUnixEpoch(7)));
ASSERT_TRUE(
CreateEmptyFile("8.gdoc", base::Time::FromSecondsSinceUnixEpoch(8)));
// Newest
std::vector<RecentFile> files =
GetRecentFiles(TestParams().FileType(RecentSource::FileType::kDocument));
ASSERT_EQ(3u, files.size());
EXPECT_EQ("8.gdoc", files[0].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(8), files[0].last_modified());
EXPECT_EQ("6.txt", files[1].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(6), files[1].last_modified());
EXPECT_EQ("4.doc", files[2].url().path().BaseName().value());
EXPECT_EQ(base::Time::FromSecondsSinceUnixEpoch(4), files[2].last_modified());
}
TEST_F(RecentDiskSourceTest, GetRecentFiles_UmaStats) {
base::HistogramTester histogram_tester;
GetRecentFiles(TestParams());
histogram_tester.ExpectTotalCount(uma_histogram_name_, 1);
}
} // namespace
} // namespace ash