910e62b5创建于 1月15日历史提交
// Copyright 2022 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/feature_list.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "content/browser/network/http_cache_backend_file_operations_factory.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/network_service_util.h"
#include "content/public/browser/page.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_paths.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/network_service_test_helper.h"
#include "content/shell/browser/shell.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/features.h"
#include "sandbox/policy/features.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/network_service_test.mojom.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/android/android_info.h"
#endif

namespace content {
namespace {

using network::mojom::SimpleCache;
using network::mojom::SimpleCacheEntry;
using network::mojom::SimpleCacheEntryEnumerator;
using network::mojom::SimpleCacheOpenEntryResult;
using network::mojom::SimpleCacheOpenEntryResultPtr;

using FileEnumerationEntry =
    disk_cache::BackendFileOperations::FileEnumerationEntry;

// On Mac and Fuchsia, sandboxing is always enabled, so we don't need to test
// the non-sandboxing configuration.
#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA)

class NonSandboxedNetworkServiceBrowserTest : public ContentBrowserTest {
 public:
  NonSandboxedNetworkServiceBrowserTest() {
    std::vector<base::test::FeatureRef> kDisabledFeatures = {
        sandbox::policy::features::kNetworkServiceSandbox,
    };
    scoped_feature_list_.InitWithFeatures(
        /*enabled_features=*/{},
        /*disabled_features=*/kDisabledFeatures);
    ForceOutOfProcessNetworkService();
  }

  void SetUpOnMainThread() override {
    ASSERT_TRUE(IsOutOfProcessNetworkService());
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(NonSandboxedNetworkServiceBrowserTest,
                       OpeningFileIsAllowed) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::RunLoop run_loop;

  std::optional<bool> result;
  network_service_test().set_disconnect_handler(run_loop.QuitClosure());
  const base::FilePath path =
      temp_dir.GetPath().Append(FILE_PATH_LITERAL("blank.jpg"));
  network_service_test()->OpenFile(path,
                                   base::BindLambdaForTesting([&](bool ok) {
                                     result = ok;
                                     run_loop.Quit();
                                   }));
  run_loop.Run();

  EXPECT_EQ(result, std::make_optional(true));
}

#endif

class SandboxedHttpCacheBrowserTest : public ContentBrowserTest {
 public:
  SandboxedHttpCacheBrowserTest() {
    std::vector<base::test::FeatureRef> enabled_features = {
      features::kBrokerFileOperationsOnDiskCacheInNetworkService,
#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA)
      // Network Service Sandboxing is unconditionally enabled on these
      // platforms.
      sandbox::policy::features::kNetworkServiceSandbox,
#endif
    };
    scoped_feature_list_.InitWithFeatures(enabled_features, {});
    ForceOutOfProcessNetworkService();
  }

  void SetUp() override {
#if BUILDFLAG(IS_WIN)
    if (!sandbox::policy::features::IsNetworkSandboxSupported()) {
      // On *some* Windows, sandboxing cannot be enabled. We skip all the tests
      // on such platforms.
      GTEST_SKIP();
    }
#endif

#if BUILDFLAG(IS_ANDROID)
    {
      // On older android, we cannot use ftruncate in the network process.
      // See https://crbug.com/1315933 and https://crrev.com/c/3581676.
      // Let's skip the tests.
      const int sdk_version = base::android::android_info::sdk_int();
      if (sdk_version <=
          base::android::android_info::SdkVersion::SDK_VERSION_MARSHMALLOW) {
        DVLOG(0) << "Android is too old: " << sdk_version;
        GTEST_SKIP();
      }
    }
#endif

    // These assertions need to precede ContentBrowserTest::SetUp to prevent the
    // test body from running when one of the assertions fails.
    ASSERT_TRUE(IsOutOfProcessNetworkService());
    ASSERT_TRUE(sandbox::policy::features::IsNetworkSandboxEnabled());

    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());

    ContentBrowserTest::SetUp();
  }

  void SetUpOnMainThread() override {
    ASSERT_TRUE(IsOutOfProcessNetworkService());
  }

  mojo::Remote<SimpleCache> CreateSimpleCache() {
    base::RunLoop run_loop;

    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    const base::FilePath root_path = GetTempDirPath();
    const base::FilePath path = root_path.AppendASCII("foobar");
    mojo::Remote<SimpleCache> simple_cache;
    mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
        factory_remote;
    mojo::MakeSelfOwnedReceiver(
        std::make_unique<HttpCacheBackendFileOperationsFactory>(root_path),
        factory_remote.InitWithNewPipeAndPassReceiver());

    network_service_test()->CreateSimpleCache(
        std::move(factory_remote), path, /*reset=*/false,
        base::BindLambdaForTesting([&](mojo::PendingRemote<SimpleCache> cache) {
          if (cache) {
            simple_cache.Bind(std::move(cache));
          }
          run_loop.Quit();
        }));
    run_loop.Run();

    return simple_cache;
  }

  mojo::Remote<SimpleCacheEntry> CreateEntry(SimpleCache* cache,
                                             const std::string& key) {
    mojo::Remote<SimpleCacheEntry> entry;
    base::RunLoop run_loop;
    cache->CreateEntry(
        key, base::BindLambdaForTesting(
                 [&](mojo::PendingRemote<SimpleCacheEntry> pending_entry,
                     int net_error) {
                   if (pending_entry) {
                     entry.Bind(std::move(pending_entry));
                   }
                   run_loop.Quit();
                 }));
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    run_loop.Run();
    return entry;
  }

  mojo::Remote<SimpleCacheEntry> OpenEntry(SimpleCache* cache,
                                           const std::string& key) {
    mojo::Remote<SimpleCacheEntry> entry;
    base::RunLoop run_loop;
    cache->OpenEntry(
        key, base::BindLambdaForTesting(
                 [&](mojo::PendingRemote<SimpleCacheEntry> pending_entry,
                     int net_error) {
                   if (pending_entry) {
                     entry.Bind(std::move(pending_entry));
                   }
                   run_loop.Quit();
                 }));
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    run_loop.Run();
    return entry;
  }

  void Close(mojo::Remote<SimpleCacheEntry> entry) {
    base::RunLoop run_loop;
    entry->Close(run_loop.QuitClosure());
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    run_loop.Run();
  }

  void Detach(mojo::Remote<SimpleCache> cache) {
    base::RunLoop run_loop;
    cache->Detach(run_loop.QuitClosure());
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    run_loop.Run();
  }

  SimpleCacheOpenEntryResultPtr OpenNextEntry(
      SimpleCacheEntryEnumerator* enumerator) {
    base::RunLoop run_loop;

    SimpleCacheOpenEntryResultPtr result_to_pass;
    std::pair<mojo::Remote<SimpleCacheEntry>, int> result;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    enumerator->GetNext(
        base::BindLambdaForTesting([&](SimpleCacheOpenEntryResultPtr result) {
          DCHECK(result);
          result_to_pass = std::move(result);
          run_loop.Quit();
        }));
    run_loop.Run();

    return result_to_pass;
  }

  const base::FilePath& GetTempDirPath() const { return temp_dir_.GetPath(); }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  base::ScopedTempDir temp_dir_;
};

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, OpeningFileIsProhibited) {
  base::RunLoop run_loop;

  std::optional<bool> result;
  network_service_test().set_disconnect_handler(run_loop.QuitClosure());
  const base::FilePath path =
      GetTestDataFilePath().Append(FILE_PATH_LITERAL("blank.jpg"));
  network_service_test()->OpenFile(path,
                                   base::BindLambdaForTesting([&](bool b) {
                                     result = b;
                                     run_loop.Quit();
                                   }));
  run_loop.Run();

  EXPECT_EQ(result, std::make_optional(false));
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest,
                       EnumerateFilesOnNonExistingDirectory) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::RunLoop run_loop;

  base::FilePath root;
  base::PathService::Get(content::DIR_TEST_DATA, &root);
  root =
      root.Append(FILE_PATH_LITERAL("net")).Append(FILE_PATH_LITERAL("cache"));
  mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
      factory_remote;
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<HttpCacheBackendFileOperationsFactory>(root),
      factory_remote.InitWithNewPipeAndPassReceiver());

  network_service_test().set_disconnect_handler(run_loop.QuitClosure());
  const base::FilePath path = root.Append(FILE_PATH_LITERAL("not-found"));
  std::vector<FileEnumerationEntry> entries;
  bool has_error = false;

  network_service_test()->EnumerateFiles(
      path, std::move(factory_remote),
      base::BindLambdaForTesting(
          [&](const std::vector<FileEnumerationEntry>& in_entries,
              bool in_has_error) {
            entries = in_entries;
            has_error = in_has_error;
            run_loop.Quit();
          }));
  run_loop.Run();
  EXPECT_TRUE(entries.empty());
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, EnumerateFiles) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::RunLoop run_loop;

  base::FilePath root;
  base::PathService::Get(content::DIR_TEST_DATA, &root);
  root =
      root.Append(FILE_PATH_LITERAL("net")).Append(FILE_PATH_LITERAL("cache"));
  mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
      factory_remote;
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<HttpCacheBackendFileOperationsFactory>(root),
      factory_remote.InitWithNewPipeAndPassReceiver());

  network_service_test().set_disconnect_handler(run_loop.QuitClosure());
  const base::FilePath path = root.Append(FILE_PATH_LITERAL("file_enumerator"));
  std::vector<FileEnumerationEntry> entries;
  bool has_error = false;

  network_service_test()->EnumerateFiles(
      path, std::move(factory_remote),
      base::BindLambdaForTesting(
          [&](const std::vector<FileEnumerationEntry>& in_entries,
              bool in_has_error) {
            entries = in_entries;
            has_error = in_has_error;
            run_loop.Quit();
          }));
  run_loop.Run();
  ASSERT_EQ(1u, entries.size());
  EXPECT_FALSE(has_error);
  const FileEnumerationEntry entry = entries[0];
  EXPECT_EQ(entry.path, path.Append(FILE_PATH_LITERAL("test.txt")));
  EXPECT_EQ(entry.size, 13);
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, CreateSimpleCache) {
  base::RunLoop run_loop;

  network_service_test().set_disconnect_handler(run_loop.QuitClosure());
  const base::FilePath root_path = GetTempDirPath();
  mojo::Remote<SimpleCache> simple_cache;
  mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
      factory_remote;
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<HttpCacheBackendFileOperationsFactory>(root_path),
      factory_remote.InitWithNewPipeAndPassReceiver());

  network_service_test()->CreateSimpleCache(
      std::move(factory_remote), root_path, /*reset=*/false,
      base::BindLambdaForTesting([&](mojo::PendingRemote<SimpleCache> cache) {
        if (cache) {
          simple_cache.Bind(std::move(cache));
        }
        run_loop.Quit();
      }));
  run_loop.Run();

  ASSERT_TRUE(simple_cache.is_bound());
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest,
                       CreateSimpleCacheOnParentDirectory) {
  base::RunLoop run_loop;

  const base::FilePath path = GetTempDirPath();
  const base::FilePath root_path = path.AppendASCII("foobar");
  mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
      factory_remote;
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<HttpCacheBackendFileOperationsFactory>(root_path),
      factory_remote.InitWithNewPipeAndPassReceiver());

  // We expect the network service to crash due to a bad mojo message.
  network_service_test().set_disconnect_handler(run_loop.QuitClosure());
  network_service_test()->CreateSimpleCache(
      std::move(factory_remote), path, /*reset=*/false,
      base::BindOnce([](mojo::PendingRemote<SimpleCache> cache) {
        EXPECT_FALSE(cache.is_valid());
      }));
  run_loop.Run();
  IgnoreNetworkServiceCrashes();
}

// TODO(crbug.com/40919503): Flaky on at least Mac11.
#if BUILDFLAG(IS_MAC)
#define MAYBE_CreateSimpleCacheWithParentDirectoryTraversal \
  DISABLED_CreateSimpleCacheWithParentDirectoryTraversal
#else
#define MAYBE_CreateSimpleCacheWithParentDirectoryTraversal \
  CreateSimpleCacheWithParentDirectoryTraversal
#endif
IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest,
                       MAYBE_CreateSimpleCacheWithParentDirectoryTraversal) {
  base::RunLoop run_loop;

  const base::FilePath root_path = GetTempDirPath();
  const base::FilePath path = root_path.AppendASCII("foo")
                                  .Append(base::FilePath::kParentDirectory)
                                  .AppendASCII("bar");
  mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
      factory_remote;
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<HttpCacheBackendFileOperationsFactory>(root_path),
      factory_remote.InitWithNewPipeAndPassReceiver());

  // We expect the network service to crash due to a bad mojo message.
  network_service_test().set_disconnect_handler(run_loop.QuitClosure());
  network_service_test()->CreateSimpleCache(
      std::move(factory_remote), path, /*reset=*/false,
      base::BindOnce([](mojo::PendingRemote<SimpleCache> cache) {
        EXPECT_FALSE(cache.is_valid());
      }));
  run_loop.Run();
  IgnoreNetworkServiceCrashes();
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest,
                       CreateSimpleCacheWithReset) {
  base::RunLoop run_loop;

  const base::FilePath root_path = GetTempDirPath();
  const base::FilePath path = root_path.Append(FILE_PATH_LITERAL("cache-dir"));
  const base::FilePath child_path = path.Append(FILE_PATH_LITERAL("child"));
  mojo::Remote<SimpleCache> simple_cache;
  mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
      factory_remote;
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<HttpCacheBackendFileOperationsFactory>(root_path),
      factory_remote.InitWithNewPipeAndPassReceiver());

  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_TRUE(base::CreateDirectory(path));
    ASSERT_TRUE(base::CreateDirectory(child_path));
    ASSERT_TRUE(base::PathExists(child_path));
  }

  network_service_test().set_disconnect_handler(run_loop.QuitClosure());
  network_service_test()->CreateSimpleCache(
      std::move(factory_remote), path, /*reset=*/true,
      base::BindLambdaForTesting([&](mojo::PendingRemote<SimpleCache> cache) {
        if (cache) {
          simple_cache.Bind(std::move(cache));
        }
        run_loop.Quit();
      }));
  run_loop.Run();
  ASSERT_TRUE(simple_cache.is_bound());

  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    EXPECT_TRUE(base::PathExists(path));
    EXPECT_FALSE(base::PathExists(child_path));
  }
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, CreateEntry) {
  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();

  ASSERT_TRUE(simple_cache.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  mojo::Remote<SimpleCacheEntry> entry = CreateEntry(simple_cache.get(), "abc");
  ASSERT_TRUE(entry.is_bound());
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, OpenNonExistingEntry) {
  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();

  ASSERT_TRUE(simple_cache.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  mojo::Remote<SimpleCacheEntry> entry = OpenEntry(simple_cache.get(), "abc");
  ASSERT_FALSE(entry.is_bound());
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, CreateAndOpenEntry) {
  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();

  ASSERT_TRUE(simple_cache.is_bound());
  mojo::Remote<SimpleCacheEntry> entry = CreateEntry(simple_cache.get(), "abc");

  ASSERT_TRUE(entry.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  Close(std::move(entry));
  ASSERT_TRUE(network_service_test().is_connected());

  ASSERT_TRUE(simple_cache.is_bound());
  entry = OpenEntry(simple_cache.get(), "abc");

  ASSERT_TRUE(entry.is_bound());
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, WriteAndReadData) {
  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();
  const std::vector<uint8_t> kData = {'A', 'B', 'C', 'D', 'E'};
  const std::string kKey = "key";
  constexpr int kOffset = 4;
  constexpr int kIndex = 1;

  ASSERT_TRUE(simple_cache.is_bound());
  mojo::Remote<SimpleCacheEntry> entry = CreateEntry(simple_cache.get(), kKey);
  ASSERT_TRUE(entry.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->WriteData(kIndex, kOffset, kData, /*truncate=*/false,
                     base::BindLambdaForTesting([&](int result) {
                       EXPECT_EQ(result, static_cast<int>(kData.size()));
                       run_loop.Quit();
                     }));
    run_loop.Run();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  Close(std::move(entry));
  ASSERT_TRUE(network_service_test().is_connected());

  Detach(std::move(simple_cache));
  ASSERT_TRUE(network_service_test().is_connected());

  simple_cache = CreateSimpleCache();
  ASSERT_TRUE(simple_cache.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  entry = OpenEntry(simple_cache.get(), kKey);
  ASSERT_TRUE(entry);
  ASSERT_TRUE(network_service_test().is_connected());
  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->ReadData(kIndex, 0, kOffset + kData.size() + 5,
                    base::BindLambdaForTesting(
                        [&](const std::vector<uint8_t>& data, int result) {
                          std::vector<uint8_t> expected_data(kOffset, 0);
                          expected_data.insert(expected_data.end(),
                                               kData.cbegin(), kData.cend());
                          EXPECT_EQ(result,
                                    static_cast<int>(expected_data.size()));
                          EXPECT_EQ(data, expected_data);
                          run_loop.Quit();
                        }));
    run_loop.Run();
  }
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest,
                       WriteTruncateAndReadData) {
  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();
  const std::vector<uint8_t> kData = {'A', 'B', 'C', 'D', 'E'};
  const std::string kKey = "key";
  constexpr int kOffset = 4;
  constexpr int kIndex = 1;

  ASSERT_TRUE(simple_cache.is_bound());
  mojo::Remote<SimpleCacheEntry> entry = CreateEntry(simple_cache.get(), kKey);
  ASSERT_TRUE(entry.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->WriteData(kIndex, kOffset, kData, /*truncate=*/false,
                     base::BindLambdaForTesting([&](int result) {
                       EXPECT_EQ(result, static_cast<int>(kData.size()));
                       run_loop.Quit();
                     }));
    run_loop.Run();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->WriteData(kIndex, /*offset=*/0, kData, /*truncate=*/true,
                     base::BindLambdaForTesting([&](int result) {
                       EXPECT_EQ(result, static_cast<int>(kData.size()));
                       run_loop.Quit();
                     }));
    run_loop.Run();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->Close(run_loop.QuitClosure());
    entry.reset();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  Detach(std::move(simple_cache));
  ASSERT_TRUE(network_service_test().is_connected());

  simple_cache = CreateSimpleCache();
  ASSERT_TRUE(simple_cache.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  entry = OpenEntry(simple_cache.get(), kKey);
  ASSERT_TRUE(entry);
  ASSERT_TRUE(network_service_test().is_connected());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->ReadData(kIndex, 0, kOffset + kData.size() + 5,
                    base::BindLambdaForTesting(
                        [&](const std::vector<uint8_t>& data, int result) {
                          EXPECT_EQ(result, static_cast<int>(kData.size()));
                          EXPECT_EQ(data, kData);
                          run_loop.Quit();
                        }));
    run_loop.Run();
  }
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, WriteAndReadSparseData) {
  const std::string kKey = "key";
  constexpr int kOffset = 1024;
  std::vector<uint8_t> original_data;
  for (int i = 0; i < 2048; ++i) {
    original_data.push_back(static_cast<uint8_t>(i % 26 + 'A'));
  }

  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();
  ASSERT_TRUE(simple_cache.is_bound());
  mojo::Remote<SimpleCacheEntry> entry = CreateEntry(simple_cache.get(), kKey);
  ASSERT_TRUE(entry.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->WriteSparseData(
        kOffset, original_data, base::BindLambdaForTesting([&](int result) {
          EXPECT_EQ(result, static_cast<int>(original_data.size()));
          run_loop.Quit();
        }));
    run_loop.Run();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->Close(run_loop.QuitClosure());
    entry.reset();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  Detach(std::move(simple_cache));
  ASSERT_TRUE(network_service_test().is_connected());

  simple_cache = CreateSimpleCache();
  ASSERT_TRUE(simple_cache.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  entry = OpenEntry(simple_cache.get(), kKey);
  ASSERT_TRUE(entry);
  ASSERT_TRUE(network_service_test().is_connected());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->ReadSparseData(
        kOffset, original_data.size() + 1024,
        base::BindLambdaForTesting(
            [&](const std::vector<uint8_t>& data, int result) {
              EXPECT_EQ(result, static_cast<int>(original_data.size()));
              EXPECT_EQ(data, original_data);
              run_loop.Quit();
            }));
    run_loop.Run();
  }
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, DoomEntry) {
  const std::vector<uint8_t> kData = {'A', 'B', 'C'};
  const std::vector<uint8_t> kSparseData(1024, 'A');
  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();

  ASSERT_TRUE(simple_cache.is_bound());
  mojo::Remote<SimpleCacheEntry> entry = CreateEntry(simple_cache.get(), "abc");

  ASSERT_TRUE(entry.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  {
    // Write something, to open files.
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->WriteData(/*index=*/0, /*offset=*/0, kData, /*truncate=*/false,
                     base::BindLambdaForTesting([&](int result) {
                       EXPECT_EQ(result, static_cast<int>(kData.size()));
                       run_loop.Quit();
                     }));
    run_loop.Run();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  {
    // Write something, to open files.
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    entry->WriteSparseData(
        /*offset=*/0, kSparseData, base::BindLambdaForTesting([&](int result) {
          EXPECT_EQ(result, static_cast<int>(kSparseData.size()));
          run_loop.Quit();
        }));
    run_loop.Run();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    simple_cache->DoomEntry("abc",
                            base::BindLambdaForTesting([&](int32_t result) {
                              EXPECT_EQ(result, net::OK);
                              run_loop.Quit();
                            }));
    run_loop.Run();
  }
  entry.reset();
  Detach(std::move(simple_cache));
  ASSERT_TRUE(network_service_test().is_connected());

  simple_cache = CreateSimpleCache();
  ASSERT_TRUE(simple_cache.is_bound());
  ASSERT_TRUE(network_service_test().is_connected());

  ASSERT_TRUE(simple_cache.is_bound());
  entry = OpenEntry(simple_cache.get(), "abc");

  ASSERT_FALSE(entry.is_bound());
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, DoomEntryWithoutOpening) {
  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();

  ASSERT_TRUE(simple_cache.is_bound());
  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    simple_cache->DoomEntry("abc",
                            base::BindLambdaForTesting([&](int32_t result) {
                              EXPECT_EQ(result, net::OK);
                              run_loop.Quit();
                            }));
    run_loop.Run();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  Detach(std::move(simple_cache));
  ASSERT_TRUE(network_service_test().is_connected());
}

// TODO(crbug.com/40881636): Re-enable this test
IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest,
                       DISABLED_EnumerateEntries) {
  const std::string kKey1 = "abc";
  const std::string kKey2 = "def";
  const std::string kKey3 = "ghi";

  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();
  ASSERT_TRUE(simple_cache.is_bound());

  {
    mojo::Remote<SimpleCacheEntryEnumerator> enumerator;
    simple_cache->EnumerateEntries(enumerator.BindNewPipeAndPassReceiver());

    auto result = OpenNextEntry(enumerator.get());
    ASSERT_TRUE(network_service_test().is_connected());
    DCHECK(result);

    ASSERT_EQ(result->error, net::ERR_FAILED);
    ASSERT_FALSE(result->entry);
  }
  ASSERT_TRUE(network_service_test().is_connected());

  mojo::Remote<SimpleCacheEntry> entry1 =
      CreateEntry(simple_cache.get(), kKey1);
  ASSERT_TRUE(entry1.is_bound());

  mojo::Remote<SimpleCacheEntry> entry2 =
      CreateEntry(simple_cache.get(), kKey2);
  ASSERT_TRUE(entry2.is_bound());

  mojo::Remote<SimpleCacheEntry> entry3 =
      CreateEntry(simple_cache.get(), kKey3);
  ASSERT_TRUE(entry3.is_bound());

  Close(std::move(entry1));
  ASSERT_TRUE(network_service_test().is_connected());

  Close(std::move(entry2));
  ASSERT_TRUE(network_service_test().is_connected());

  Close(std::move(entry3));
  ASSERT_TRUE(network_service_test().is_connected());

  Detach(std::move(simple_cache));
  ASSERT_TRUE(network_service_test().is_connected());

  simple_cache = CreateSimpleCache();
  ASSERT_TRUE(simple_cache.is_bound());

  {
    mojo::Remote<SimpleCacheEntryEnumerator> enumerator;
    simple_cache->EnumerateEntries(enumerator.BindNewPipeAndPassReceiver());

    std::vector<std::string> keys;
    while (true) {
      auto result = OpenNextEntry(enumerator.get());
      ASSERT_TRUE(network_service_test().is_connected());
      DCHECK(result);

      if (result->error == net::ERR_FAILED) {
        EXPECT_FALSE(result->entry);
        break;
      }
      ASSERT_EQ(result->error, net::OK);
      ASSERT_TRUE(result->entry);
      keys.push_back(result->key);
    }
    std::sort(keys.begin(), keys.end());
    EXPECT_EQ(keys, (std::vector<std::string>{kKey1, kKey2, kKey3}));
  }
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, DoomAllEntries) {
  const std::string kKey1 = "abc";
  const std::string kKey2 = "def";
  const std::string kKey3 = "ghi";

  mojo::Remote<SimpleCache> simple_cache = CreateSimpleCache();
  ASSERT_TRUE(simple_cache.is_bound());

  {
    mojo::Remote<SimpleCacheEntryEnumerator> enumerator;
    simple_cache->EnumerateEntries(enumerator.BindNewPipeAndPassReceiver());

    auto result = OpenNextEntry(enumerator.get());
    ASSERT_TRUE(network_service_test().is_connected());
    ASSERT_TRUE(result);

    ASSERT_EQ(result->error, net::ERR_FAILED);
    ASSERT_FALSE(result->entry);
  }
  ASSERT_TRUE(network_service_test().is_connected());

  mojo::Remote<SimpleCacheEntry> entry1 =
      CreateEntry(simple_cache.get(), kKey1);
  ASSERT_TRUE(entry1.is_bound());

  mojo::Remote<SimpleCacheEntry> entry2 =
      CreateEntry(simple_cache.get(), kKey2);
  ASSERT_TRUE(entry2.is_bound());

  mojo::Remote<SimpleCacheEntry> entry3 =
      CreateEntry(simple_cache.get(), kKey3);
  ASSERT_TRUE(entry3.is_bound());

  {
    base::RunLoop run_loop;
    network_service_test().set_disconnect_handler(run_loop.QuitClosure());
    simple_cache->DoomAllEntries(
        base::BindLambdaForTesting([&](int32_t result) {
          EXPECT_EQ(result, net::OK);
          run_loop.Quit();
        }));
    run_loop.Run();
  }
  ASSERT_TRUE(network_service_test().is_connected());

  entry1.reset();
  entry2.reset();
  entry3.reset();

  Detach(std::move(simple_cache));
  ASSERT_TRUE(network_service_test().is_connected());

  simple_cache = CreateSimpleCache();
  ASSERT_TRUE(simple_cache.is_bound());

  {
    mojo::Remote<SimpleCacheEntryEnumerator> enumerator;
    simple_cache->EnumerateEntries(enumerator.BindNewPipeAndPassReceiver());

    std::vector<std::string> keys;
    auto result = OpenNextEntry(enumerator.get());
    EXPECT_EQ(result->error, net::ERR_FAILED);
    EXPECT_FALSE(result->entry);
  }
}

}  // namespace
}  // namespace content