chromium/chrome/updater/test/unit_test_util.h

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

#ifndef CHROME_UPDATER_TEST_UNIT_TEST_UTIL_H_
#define CHROME_UPDATER_TEST_UNIT_TEST_UTIL_H_

#include <optional>
#include <string>

#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/functional/function_ref.h"
#include "base/memory/scoped_refptr.h"
#include "base/process/process_iterator.h"
#include "base/synchronization/waitable_event.h"
#include "chrome/updater/tag.h"

namespace base {
class TimeDelta;
class Version;
}  // namespace base

namespace updater {
class PolicyService;
enum class UpdaterScope;
}  // namespace updater

namespace updater::test {

extern const char kChromeAppId[];

// Returns true if a process based on the named executable and optional filter
// is running.
bool IsProcessRunning(const base::FilePath::StringType& executable_name,
                      const base::ProcessFilter* filter = nullptr);

// Returns true if all processes based on the named executable and optional
// filter have exited. Otherwise, returns false if the time delta has expired.
bool WaitForProcessesToExit(const base::FilePath::StringType& executable_name,
                            base::TimeDelta wait,
                            const base::ProcessFilter* filter = nullptr);

// Terminates all the processes on the current machine that were launched
// from the given executable name and optional filter, ending them with the
// given exit code. Returns true if all processes were able to be killed off.
bool KillProcesses(const base::FilePath::StringType& executable_name,
                   int exit_code,
                   const base::ProcessFilter* filter = nullptr);

// A policy service with default values.
scoped_refptr<PolicyService> CreateTestPolicyService();

// Returns the current test name in the format "TestSuiteName.TestName" or "?.?"
// if the test name is not available.
std::string GetTestName();

// Deletes the file and its parent directories, if the parent directories are
// empty. Returns true if:
// - the file and the directories are deleted.
// - the file does not exist.
// - the directory is not empty.
bool DeleteFileAndEmptyParentDirectories(
    const std::optional<base::FilePath>& file_path);

// Fetches the path to the ${ISOLATED_OUTDIR} env var.
// ResultDB reads logs and test artifacts info from there.
base::FilePath GetLogDestinationDir();

// Initializes the logging for the unit test and redirects the log output to
// ${ISOLATED_OUTDIR} if the directory is available. `log_base_path` is the base
// name of the log file in the above directory. The unit tests can't log into
// the updater directory because that directory is touched by the integration
// tests. This function must be called after the `base::TestSuite` instance is
// created, because `base::TestSuite` initializes logging too.
void InitLoggingForUnitTest(const base::FilePath& log_base_path);

#if BUILDFLAG(IS_WIN)
// Change Windows Defender settings to skip scanning the paths used by the
// updater if test runs with the flag `exclude-paths-from-win-defender`.
void MaybeExcludePathsFromWindowsDefender();

// Starts procmon logging if admin and procmon exists at
// `C:\\tools\\Procmon.exe`. Returns the path to the PML file if procmon could
// be successfully started.
base::FilePath StartProcmonLogging();

// Stops procmon logging and exports the PML file to a CSV file at the same
// location as `pml_file`. Caller needs to be admin, procmon needs to exist at
// `C:\\tools\\Procmon.exe`, and `pml_file` needs to be a valid path to a
// procmon PML file returned from `StartProcmonLogging`.
void StopProcmonLogging(const base::FilePath& pml_file);
#endif

// Returns a list of processes matching `executable_name` and optional `filter`.
const base::ProcessIterator::ProcessEntries FindProcesses(
    const base::FilePath::StringType& executable_name,
    const base::ProcessFilter* filter = nullptr);

// Returns a formatted string of processes matching `executable_name` and
// optional `filter`.
std::string PrintProcesses(const base::FilePath::StringType& executable_name,
                           const base::ProcessFilter* filter = nullptr);

// Waits for a given `predicate` to become true. Invokes `still_waiting`
// periodically to provide a indication of progress. Returns true if the
// predicate becomes true before a timeout, otherwise returns false.
[[nodiscard]] bool WaitFor(
    base::FunctionRef<bool()> predicate,
    base::FunctionRef<void()> still_waiting = [] {});

struct EventHolder {};

// Creates a waitable event with default attributes for the current process,
// test, and test scope.
EventHolder CreateWaitableEventForTest();

// Returns the absolute path to a test file used by update client unit tests.
// These test files exist in the source tree and are available to tests in
// `//chrome/updater/test/data.` `file_name` is the relative name of the
// file in that directory.
[[nodiscard]] base::FilePath GetTestFilePath(const char* file_name);

// Sets up the official updater directory with global prefs, the versioned
// install folder (with a version of `base_version + major_version_offset`), and
// optionally, an empty updater executable in the versioned folder.
void SetupFakeUpdaterVersion(UpdaterScope scope,
                             const base::Version& base_version,
                             int major_version_offset,
                             bool should_create_updater_executable);

// Set up a mock `mock_updater_path`, and the following mock directories under
// `mock_updater_path.DirName()`: `Download`, `Install`, and a versioned
// `1.2.3.4` directory.
void SetupMockUpdater(const base::FilePath& mock_updater_path);

// Expect only a single file `mock_updater_path` and nothing else under
// `mock_updater_path.DirName()`.
void ExpectOnlyMockUpdater(const base::FilePath& mock_updater_path);

// Expects that all members of `actual` and `expected` are the same.
void ExpectTagArgsEqual(const updater::tagging::TagArgs& actual,
                        const updater::tagging::TagArgs& expected);

// Wait for the process to exit up to the action timeout and returns the exit
// code. The function expects the process to exit in time.
int WaitForProcess(base::Process& process);

}  // namespace updater::test

#endif  // CHROME_UPDATER_TEST_UNIT_TEST_UTIL_H_