blob: 0b62e45c4fdf2f37fe0b4e7bcefcd8a2af45ebb0 [file] [log] [blame]
// 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 "chrome/browser/ash/crosapi/download_status_updater_ash.h"
#include <list>
#include <string>
#include "base/functional/callback.h"
#include "base/test/gmock_move_support.h"
#include "base/test/test_future.h"
#include "chrome/browser/ash/crosapi/mock_download_status_updater_client.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/crosapi/mojom/download_status_updater.mojom.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace crosapi {
// Aliases.
using ::crosapi::mojom::DownloadStatusUpdater;
using ::crosapi::mojom::DownloadStatusUpdaterClient;
using ::testing::_;
using ::testing::Action;
using ::testing::Eq;
using ::testing::Matcher;
using ::testing::NiceMock;
using ::testing::TestWithParam;
using ::testing::Values;
// DownloadStatusUpdaterAshCommandTest -----------------------------------------
// Enumeration of `DownloadStatusUpdaterAsh` commands to test.
enum class Command {
kCancel, // See `DownloadStatusUpdaterAsh::Cancel()`.
kPause, // See `DownloadStatusUpdaterAsh::Pause()`.
kResume, // See `DownloadStatusUpdaterAsh::Resume()`.
kShowInBrowser, // See `DownloadStatusUpdaterAsh::ShowInBrowser()`.
};
// Base class for tests of `DownloadStatusUpdaterAsh` which are parameterized by
// which `Command` to run for testing.
class DownloadStatusUpdaterAshCommandTest : public TestWithParam<Command> {
public:
// Callback which accepts whether a `Command` has been `handled` successfully.
using CommandHandledCallback = base::OnceCallback<void(bool handled)>;
// Binds a new client to the download status updater under test, with default
// fulfillment for the `command()` under test given parameterization.
void BindClient(
Matcher<const std::string&> default_guid_matcher,
Action<void(const std::string& guid, CommandHandledCallback callback)>
default_action) {
// Create client.
clients_.emplace_back();
// Add default fulfillment for `command()`.
switch (command()) {
case Command::kCancel:
ON_CALL(clients_.back(), Cancel(default_guid_matcher, _))
.WillByDefault(default_action);
break;
case Command::kPause:
ON_CALL(clients_.back(), Pause(default_guid_matcher, _))
.WillByDefault(default_action);
break;
case Command::kResume:
ON_CALL(clients_.back(), Resume(default_guid_matcher, _))
.WillByDefault(default_action);
break;
case Command::kShowInBrowser:
ON_CALL(clients_.back(), ShowInBrowser(default_guid_matcher, _))
.WillByDefault(default_action);
break;
}
// Bind client.
client_receivers_.emplace_back(&clients_.back());
static_cast<DownloadStatusUpdater*>(download_status_updater_ash())
->BindClient(client_receivers_.back().BindNewPipeAndPassRemote());
}
// Runs the current message loop until a no-op message has been received on
// each client interface's message pipe. This effectively ensures that any
// messages in transit are received before returning.
void FlushClientsForTesting() {
for (auto& client_receiver : client_receivers_) {
client_receiver.FlushForTesting();
}
}
// Runs the `command()` under test given parameterization. Note that
// `callback` may be run asynchronously.
void RunCommand(const std::string& guid, CommandHandledCallback callback) {
switch (command()) {
case Command::kCancel:
download_status_updater_ash()->Cancel(guid, std::move(callback));
break;
case Command::kPause:
download_status_updater_ash()->Pause(guid, std::move(callback));
break;
case Command::kResume:
download_status_updater_ash()->Resume(guid, std::move(callback));
break;
case Command::kShowInBrowser:
download_status_updater_ash()->ShowInBrowser(guid, std::move(callback));
break;
}
}
// Returns the `Command` under test given parameterization.
Command command() const { return GetParam(); }
// Returns the `DownloadStatusUpdaterAsh` instance under test.
DownloadStatusUpdaterAsh* download_status_updater_ash() {
return &download_status_updater_ash_;
}
private:
// `RunLoop`s require a task environment. Though not used explicitly,
// this test suite has a number of `RunLoop` dependencies.
content::BrowserTaskEnvironment task_environment_;
// The download status updater instance under test with a testing profile,
// which requires a browser thread environment.
TestingProfile profile_;
DownloadStatusUpdaterAsh download_status_updater_ash_{&profile_};
// The collection of clients which are bound to the download status updater
// under test. Note that clients must be explicitly bound via `BindClient()`.
std::list<NiceMock<MockDownloadStatusUpdaterClient>> clients_;
std::list<mojo::Receiver<DownloadStatusUpdaterClient>> client_receivers_;
};
INSTANTIATE_TEST_SUITE_P(All,
DownloadStatusUpdaterAshCommandTest,
Values(Command::kCancel,
Command::kPause,
Command::kResume,
Command::kShowInBrowser),
[](auto& info) {
switch (info.param) {
case Command::kCancel:
return "Cancel";
case Command::kPause:
return "Pause";
case Command::kResume:
return "Resume";
case Command::kShowInBrowser:
return "ShowInBrowser";
}
});
// Tests -----------------------------------------------------------------------
// Verifies that `DownloadStatusUpdaterAsh` commands are working as intended.
TEST_P(DownloadStatusUpdaterAshCommandTest, Command) {
constexpr char kGuid[] = "guid";
// Case: No bound clients, command unhandled.
{
// When no clients are bound, running a download status updater command
// should synchronously return that the command was unhandled.
base::test::TestFuture<bool> run_command_future;
RunCommand(kGuid, run_command_future.GetCallback());
EXPECT_TRUE(run_command_future.IsReady());
EXPECT_FALSE(run_command_future.Get());
}
// Bind multiple clients that (a) verify the `kGuid` associated with the
// command is being propagated properly, and (b) cache the callback to run to
// indicate whether the client handled the command successfully.
CommandHandledCallback handled_by_client1_callback;
BindClient(Eq(kGuid), MoveArg<1>(&handled_by_client1_callback));
CommandHandledCallback handled_by_client2_callback;
BindClient(Eq(kGuid), MoveArg<1>(&handled_by_client2_callback));
// Case: Multiple bound clients, command unhandled.
{
// When clients are bound, running a download status updater command
// should return whether the command was handled asynchronously.
base::test::TestFuture<bool> run_command_future;
RunCommand(kGuid, run_command_future.GetCallback());
FlushClientsForTesting();
EXPECT_FALSE(run_command_future.IsReady());
// The command should have been dispatched to all clients.
EXPECT_TRUE(handled_by_client1_callback);
EXPECT_TRUE(handled_by_client2_callback);
// All clients should indicate whether the command was handled before
// the download status updater returns its response asynchronously.
std::move(handled_by_client1_callback).Run(/*handled=*/false);
FlushClientsForTesting();
EXPECT_FALSE(run_command_future.IsReady());
std::move(handled_by_client2_callback).Run(/*handled=*/false);
FlushClientsForTesting();
EXPECT_TRUE(run_command_future.IsReady());
EXPECT_FALSE(run_command_future.Get());
}
// Case: Multiple bound clients, command handled.
{
// When clients are bound, running a download status updater command
// should return whether the command was handled asynchronously.
base::test::TestFuture<bool> run_command_future;
RunCommand(kGuid, run_command_future.GetCallback());
FlushClientsForTesting();
// The command should have been dispatched to all clients.
EXPECT_FALSE(run_command_future.IsReady());
EXPECT_TRUE(handled_by_client1_callback);
EXPECT_TRUE(handled_by_client2_callback);
// All clients should indicate whether the command was handled before
// the download status updater returns its response asynchronously.
std::move(handled_by_client1_callback).Run(/*handled=*/true);
FlushClientsForTesting();
EXPECT_FALSE(run_command_future.IsReady());
std::move(handled_by_client2_callback).Run(/*handled=*/false);
FlushClientsForTesting();
EXPECT_TRUE(run_command_future.IsReady());
EXPECT_TRUE(run_command_future.Get());
}
}
} // namespace crosapi