chromium/content/browser/file_system_access/file_system_access_observer_browsertest.cc

// Copyright 2023 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 "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/values_util.h"
#include "base/test/test_timeouts.h"
#include "build/buildflag.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/back_forward_cache_util.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/file_system_chooser_test_helpers.h"
#include "content/shell/browser/shell.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

namespace {

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_FUCHSIA) && \
    !BUILDFLAG(IS_MAC)
constexpr int kBFCacheTestTimeoutMs =;
#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
        // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_MAC)

enum class TestFileSystemType {};

}  // namespace

// Helpful macros to reduce the boilerplate script in the tests below.

// Resolves a promise with a serialized copy of the first `records` fired in the
// change callback.
//
// Notably, several FileSystemChangeRecord fields are not serializable, so tests
// which need to assert some behavior about the non-serializable fields may
// overwrite the `onChange` function.
#define CREATE_PROMISE_AND_RESOLVERS

#define GET_LOCAL_FILE
#define GET_BUCKET_FILE
#define GET_LOCAL_DIRECTORY
#define GET_BUCKET_DIRECTORY

#define GET_FILE(file_system_type)

#define GET_DIRECTORY(file_system_type)

#define START_OBSERVING_FILE(file_system_type)

#define START_OBSERVING_DIRECTORY(file_system_type, recursive)

#define WRITE_TO_FILE

#define SET_CHANGE_TIMEOUT

// TODO(crbug.com/341136316): Consider making these WPTs, and adding a
// lot more of them. For example:
//   - change types
//   - observing a handle without permission should fail
//   - changes should not be reported to swap files
//     (see https://crbug.com/321980149)
//   - changes should not be reported if permission to the handle is lost
//     (see https://crbug.com/321980366)
//   - moving an observed handle

class FileSystemAccessObserverBrowserTestBase : public ContentBrowserTest {};

class FileSystemAccessObserverDefaultBrowserTest
    : public FileSystemAccessObserverBrowserTestBase {};

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserverDefaultBrowserTest,
                       DisabledByDefault) {}

class FileSystemAccessObserveWithFlagBrowserTest
    : public FileSystemAccessObserverBrowserTestBase {};

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
                       UnobserveDisabledByDefault) {}

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
                       CreateObserver) {}

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
                       NothingToDisconnect) {}

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
                       DisconnectIsIdempotent) {}

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
                       ObserveSyncAccessHandleWrite) {}

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
                       ObserveSyncAccessHandleMultipleWrites) {}

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
                       ObserveSyncAccessHandleTruncate) {}

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
                       DoNotObserveSyncAccessHandleWithNoChanges) {}

class FileSystemAccessObserveWithUnobserveFlagBrowserTest
    : public FileSystemAccessObserveWithFlagBrowserTest {};

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithUnobserveFlagBrowserTest,
                       NothingToUnobserve) {}

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithUnobserveFlagBrowserTest,
                       UnobserveIsIdempotent) {}

class FileSystemAccessObserverBrowserTest
    : public FileSystemAccessObserverBrowserTestBase,
      public testing::WithParamInterface<TestFileSystemType> {};

// `base::FilePatchWatcher` is not implemented on Fuchsia. See
// https://crbug.com/851641. Instead, just check that attempting to observe
// a handle does not crash.
#if BUILDFLAG(IS_FUCHSIA)
IN_PROC_BROWSER_TEST_F(FileSystemAccessObserveWithFlagBrowserTest,
                       ObservingLocalFileIsNotSupportedOnFuchsia) {
  base::FilePath file_path = CreateFileToBePicked();

  const std::string script =
      // clang-format off
      "(async () => {"
         CREATE_PROMISE_AND_RESOLVERS
         START_OBSERVING_FILE(TestFileSystemType::kLocal)
         WRITE_TO_FILE
         SET_CHANGE_TIMEOUT
      "})()";
  // clang-format on
  auto result = EvalJs(shell(), script);
  EXPECT_TRUE(result.error.find("did not support") != std::string::npos)
      << result.error;
}
#endif  // BUILDFLAG(IS_FUCHSIA)

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest, CreateObserver) {}

// TODO(b/360153904): Disabled on Mac due to flakiness.
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest, ObserveFile) {}
#endif  // !BUILDFLAG(IS_MAC)

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest, ObserveFileRename) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest, ObserveDirectory) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveFailsWhenFileDoesNotExist) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveFailsWhenDirectoryDoesNotExist) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveDirectoryRecursively) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveThenUnobserve) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveThenUnobserveUnrelated) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       NoChangesAfterUnobserve) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveThenDisconnect) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       NoChangesAfterDisconnect) {}

// TODO(crbug.com/321980469): Add a ReObserveAfterUnobserve test once the
// unobserve() method is no longer racy. See https://crrev.com/c/4814709.
//
// TODO(b/360153904): Disabled on Mac due to flakiness.
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ReObserveAfterDisconnect) {}
#endif  // !BUILDFLAG(IS_MAC)

// TODO(crbug.com/343961295): Windows reports two events when a swap file
// is closed: a "disappeared" for the target file being overwritten, and a
// "appeared" for the swap file being moved to the target file.
//
// TODO(crbug.com/357134621): Like on Windows, FSEvents (Mac) also reports two
// events when the swap file is closed. This test fails due to a "disappear"
// event being reported.
#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveFileReportsType) {}
#endif  // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)

// TODO(b/360153904): Disabled on Mac due to flakiness.
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveFileReportsCorrectHandle) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveFileReportsCorrectRelativePathComponents) {}
#endif  // !BUILDFLAG(IS_MAC)

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveDirectoryReportsCorrectHandle) {}

// There is no way to know the correct handle type on Windows in this scenario.
//
// Window's content::FilePathWatcher uses base::GetFileInfo to figure out the
// file path type. Since `fileInDir` is deleted, there is nothing to call
// base::GetFileInfo on.
#if !BUILDFLAG(IS_WIN)
IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveDirectoryReportsCorrectHandleType) {}
#endif  // !BUILDFLAG(IS_WIN)

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveDirectoryReportsCorrectRelativePathComponents) {}

// TODO(b/321980270): Re-enable these tests on Mac, after fixing the failing
// expectations. It's possible that some of the failing expectations are due to
// historical create flags, which can affect the reported change type (reporting
// 'create' events when other change types should be reported). See b/357062364
// for more context.
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveDirectoryReportsMoveChangeInfo) {}
#endif  // !BUILDFLAG(IS_MAC)

// TODO(b/321980270) Re-enable these tests on Mac, which only fail when the
// modified path is reported.
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveDirectoryReportsAppearedOnMoveIntoScope) {}

IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       ObserveDirectoryReportsDisappearedOnMoveOutsideScope) {}

IN_PROC_BROWSER_TEST_P(
    FileSystemAccessObserverBrowserTest,
    NonRecursiveWatchReportsDisappearedWhenDirectDescendentMovedToNonDirectDescendent) {}
#endif  // !BUILDFLAG(IS_MAC)

// TODO(b/321980270) Re-enable this test on Mac, which only fails when the
// modified path is reported.
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(
    FileSystemAccessObserverBrowserTest,
    NonRecursiveWatchReportsAppearedWhenDirectDescendentMovedFromNonDirectDescendent) {}
#endif  // !BUILDFLAG(IS_MAC)

// TODO(crbug.com/343961295): Windows reports two events when a swap file
// is closed: a "disappeared" for the target file being overwritten, and a
// "appeared" for the swap file being moved to the target file.
//
// TODO(b/321980270): Filter out changes to swap files reported by FSEvents,
// and re-enable this test on Mac.
#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                       WritableReportsSingleModifiedEventOnClose) {}
#endif  // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)

INSTANTIATE_TEST_SUITE_P();

// Local file system access - including the open*Picker() methods used here
// - is not supported on Android or iOS. See https://crbug.com/1011535.
// Meanwhile, `FilePathWatcher` is not implemented on Fuchsia. See
// https://crbug.com/851641.
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_FUCHSIA)
class FileSystemAccessObserverWithBFCacheBrowserTest
    : public FileSystemAccessObserverBrowserTestBase {};

// TODO(b/360153904): This test is flaky on Mac, likely as a result of FSEvents
// reporting events later than expected, on occasion. Re-enable once flake is
// resolved.
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(FileSystemAccessObserverWithBFCacheBrowserTest,
                       ReceivesFileUpdatesAfterReturningFromBFCache) {}
#endif  // !BUILDFLAG(IS_MAC)

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserverWithBFCacheBrowserTest,
                       NotifyOnReturnFromBFCacheWhenFileUpdates) {}

IN_PROC_BROWSER_TEST_F(FileSystemAccessObserverWithBFCacheBrowserTest,
                       DoNotNotifyOnReturnFromBFCacheWhenNoFileUpdates) {}
#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
        // !BUILDFLAG(IS_FUCHSIA)

}  // namespace content