chromium/components/download/internal/common/download_path_reservation_tracker_unittest.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include <stddef.h>
#include <stdint.h>

#include <memory>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/task_environment.h"
#include "base/test/test_file_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/download/public/common/download_path_reservation_tracker.h"
#include "components/download/public/common/mock_download_item.h"
#include "net/base/filename_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_ANDROID)
#include "components/download/internal/common/android/download_collection_bridge.h"
#endif  // BUILDFLAG(IS_ANDROID)

AnyNumber;
Return;
ReturnRef;
ReturnRefOfCopy;

namespace download {

namespace {

class DownloadPathReservationTrackerTest : public testing::Test {};

DownloadPathReservationTrackerTest::DownloadPathReservationTrackerTest() =
    default;

void DownloadPathReservationTrackerTest::SetUp() {}

void DownloadPathReservationTrackerTest::TearDown() {}

std::unique_ptr<MockDownloadItem>
DownloadPathReservationTrackerTest::CreateDownloadItem(int32_t id) {}

base::FilePath DownloadPathReservationTrackerTest::GetPathInDownloadsDirectory(
    const base::FilePath::CharType* suffix) {}

bool DownloadPathReservationTrackerTest::IsPathInUse(
    const base::FilePath& path) {}

void DownloadPathReservationTrackerTest::RunUntilIdle() {}

void DownloadPathReservationTrackerTest::CallGetReservedPath(
    DownloadItem* download_item,
    const base::FilePath& target_path,
    bool create_directory,
    DownloadPathReservationTracker::FilenameConflictAction conflict_action,
    base::FilePath* return_path,
    PathValidationResult* return_result) {}

void DownloadPathReservationTrackerTest::TestReservedPathCallback(
    base::FilePath* return_path,
    PathValidationResult* return_result,
    PathValidationResult result,
    const base::FilePath& path) {}

base::FilePath
DownloadPathReservationTrackerTest::GetLongNamePathInDownloadsDirectory(
    size_t repeat,
    const base::FilePath::CharType* suffix) {}

void SetDownloadItemState(MockDownloadItem* download_item,
                          DownloadItem::DownloadState state) {}

void DownloadPathReservationTrackerTest::CreateReservation(
    MockDownloadItem* item,
    const base::FilePath& path,
    DownloadPathReservationTracker::FilenameConflictAction conflict_action,
    PathValidationResult expected_result,
    const base::FilePath& expected_reserved_path) {}

}  // namespace

// A basic reservation is acquired and committed.
TEST_F(DownloadPathReservationTrackerTest, BasicReservation) {}

// A download that is interrupted should lose its reservation.
TEST_F(DownloadPathReservationTrackerTest, InterruptedDownload) {}

// A completed download should also lose its reservation.
TEST_F(DownloadPathReservationTrackerTest, CompleteDownload) {}

// If there are files on the file system, a unique reservation should uniquify
// around it.
TEST_F(DownloadPathReservationTrackerTest, ConflictingFiles) {}

// If there are conflicting files on the file system, an overwriting reservation
// should succeed without altering the target path.
TEST_F(DownloadPathReservationTrackerTest, ConflictingFiles_Overwrite) {}

// If the source is a file:// URL that is in the download directory, then Chrome
// could download the file onto itself. Test that this is flagged by DPRT.
TEST_F(DownloadPathReservationTrackerTest, ConflictWithSource) {}

// Multiple reservations for the same path should uniquify around each other.
TEST_F(DownloadPathReservationTrackerTest, ConflictingReservations) {}

// An OVERWRITE reservation with the same path as an active reservation should
// return a CONFLICT result.
TEST_F(DownloadPathReservationTrackerTest, ConflictingReservation_Prevented) {}

// Two active downloads shouldn't be able to reserve paths that only differ by
// case.
TEST_F(DownloadPathReservationTrackerTest, ConflictingCaseReservations) {}

// If a unique path cannot be determined after trying kMaxUniqueFiles
// uniquifiers, then the callback should notified that verification failed, and
// the returned path should be set to the original requested path.
TEST_F(DownloadPathReservationTrackerTest, UnresolvedConflicts) {}

#if BUILDFLAG(IS_FUCHSIA)
// TODO(crbug.com/40221275): Re-enable when UnwriteableDirectory works on
// Fuchsia.
#define MAYBE_UnwriteableDirectory
#else
#define MAYBE_UnwriteableDirectory
#endif
// If the target directory is unwriteable, then callback should be notified that
// verification failed.
TEST_F(DownloadPathReservationTrackerTest, MAYBE_UnwriteableDirectory) {}

// If the default download directory doesn't exist, then it should be
// created. But only if we are actually going to create the download path there.
TEST_F(DownloadPathReservationTrackerTest, CreateDefaultDownloadPath) {}

// If the target path of the download item changes, the reservation should be
// updated to match.
TEST_F(DownloadPathReservationTrackerTest, UpdatesToTargetPath) {}

// Tests for long name truncation. On other platforms automatic truncation
// is not performed (yet).
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_CHROMEOS_ASH)

TEST_F(DownloadPathReservationTrackerTest, BasicTruncation) {
  int real_max_length =
      base::GetMaximumPathComponentLength(default_download_path());
  ASSERT_NE(-1, real_max_length);

#if BUILDFLAG(IS_WIN)
  const size_t max_length = real_max_length - strlen(":Zone.Identifier");
#else
  // TODO(kinaba): the current implementation leaves spaces for appending
  // ".crdownload". So take it into account. Should be removed in the future.
  const size_t max_length = real_max_length - 11;
#endif  // BUILDFLAG(IS_WIN)

  std::unique_ptr<MockDownloadItem> item = CreateDownloadItem(1);
  base::FilePath path(GetLongNamePathInDownloadsDirectory(
      max_length, FILE_PATH_LITERAL(".txt")));
  ASSERT_FALSE(IsPathInUse(path));

  base::FilePath reserved_path;
  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
  DownloadPathReservationTracker::FilenameConflictAction conflict_action =
      DownloadPathReservationTracker::OVERWRITE;
  bool create_directory = false;
  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
                      &reserved_path, &result);
  EXPECT_TRUE(IsPathInUse(reserved_path));
  EXPECT_EQ(PathValidationResult::SUCCESS, result);
  // The file name length is truncated to max_length.
  EXPECT_EQ(max_length, reserved_path.BaseName().value().size());
  // But the extension is kept unchanged.
  EXPECT_EQ(path.Extension(), reserved_path.Extension());
  SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
}

TEST_F(DownloadPathReservationTrackerTest, TruncationConflict) {
  int real_max_length =
      base::GetMaximumPathComponentLength(default_download_path());
  ASSERT_NE(-1, real_max_length);
#if BUILDFLAG(IS_WIN)
  const size_t max_length = real_max_length - strlen(":Zone.Identifier");
#else
  const size_t max_length = real_max_length - 11;
#endif  // BUILDFLAG(IS_WIN)

  std::unique_ptr<MockDownloadItem> item = CreateDownloadItem(1);
  base::FilePath path(GetLongNamePathInDownloadsDirectory(
      max_length, FILE_PATH_LITERAL(".txt")));
  base::FilePath path0(GetLongNamePathInDownloadsDirectory(
      max_length - 4, FILE_PATH_LITERAL(".txt")));
  base::FilePath path1(GetLongNamePathInDownloadsDirectory(
      max_length - 8, FILE_PATH_LITERAL(" (1).txt")));
  base::FilePath path2(GetLongNamePathInDownloadsDirectory(
      max_length - 8, FILE_PATH_LITERAL(" (2).txt")));
  ASSERT_FALSE(IsPathInUse(path));
  // "aaa...aaaaaaa.txt" (truncated path) and
  // "aaa...aaa (1).txt" (truncated and first uniquification try) exists.
  // "aaa...aaa (2).txt" should be used.
  ASSERT_TRUE(base::WriteFile(path0, ""));
  ASSERT_TRUE(base::WriteFile(path1, ""));

  base::FilePath reserved_path;
  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
  DownloadPathReservationTracker::FilenameConflictAction conflict_action =
      DownloadPathReservationTracker::UNIQUIFY;
  bool create_directory = false;
  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
                      &reserved_path, &result);
  EXPECT_TRUE(IsPathInUse(reserved_path));
  EXPECT_EQ(PathValidationResult::SUCCESS_RESOLVED_CONFLICT, result);
  EXPECT_EQ(path2, reserved_path);
  SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
}

TEST_F(DownloadPathReservationTrackerTest, TruncationFail) {
  int real_max_length =
      base::GetMaximumPathComponentLength(default_download_path());
  ASSERT_NE(-1, real_max_length);
#if BUILDFLAG(IS_WIN)
  const size_t max_length = real_max_length - strlen(":Zone.Identifier");
#else
  const size_t max_length = real_max_length - 11;
#endif  // BUILDFLAG(IS_WIN)

  std::unique_ptr<MockDownloadItem> item = CreateDownloadItem(1);
  base::FilePath path(GetPathInDownloadsDirectory(
      (FILE_PATH_LITERAL("a.") + base::FilePath::StringType(max_length, 'b'))
          .c_str()));
  ASSERT_FALSE(IsPathInUse(path));

  base::FilePath reserved_path;
  PathValidationResult result = PathValidationResult::SUCCESS;
  DownloadPathReservationTracker::FilenameConflictAction conflict_action =
      DownloadPathReservationTracker::OVERWRITE;
  bool create_directory = false;
  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
                      &reserved_path, &result);
  // We cannot truncate a path with very long extension.
  EXPECT_EQ(PathValidationResult::NAME_TOO_LONG, result);
  SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
}

#endif  // Platforms that support filename truncation.

}  // namespace download