blob: 68951858c0aa347b02e3f022369314cbe50ce68c [file] [log] [blame]
// 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 "chrome/browser/ash/crosapi/document_scan_ash.h"
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <vector>
#include "base/containers/contains.h"
#include "base/test/bind.h"
#include "base/test/test_future.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/scanning/fake_lorgnette_scanner_manager.h"
#include "chrome/browser/ash/scanning/lorgnette_scanner_manager_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/crosapi/mojom/document_scan.mojom-forward.h"
#include "chromeos/crosapi/mojom/document_scan.mojom-shared.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/test_helper.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 {
namespace {
using testing::ElementsAre;
// Creates a new FakeLorgnetteScannerManager for the given `context`.
std::unique_ptr<KeyedService> BuildLorgnetteScannerManager(
content::BrowserContext* context) {
return std::make_unique<ash::FakeLorgnetteScannerManager>();
}
} // namespace
class DocumentScanAshTest : public testing::Test {
public:
void SetUp() override {
fake_user_manager_.Reset(std::make_unique<ash::FakeChromeUserManager>());
ash::LorgnetteScannerManagerFactory::GetInstance()->SetTestingFactory(
&profile_, base::BindRepeating(&BuildLorgnetteScannerManager));
const AccountId account_id =
AccountId::FromUserEmail(profile_.GetProfileUserName());
fake_user_manager_->AddUserWithAffiliationAndTypeAndProfile(
account_id,
/*is_affiliated=*/false, user_manager::UserType::kRegular, &profile_);
fake_user_manager_->UserLoggedIn(
account_id, user_manager::TestHelper::GetFakeUsernameHash(account_id));
fake_user_manager_->SimulateUserProfileLoad(account_id);
}
void TearDown() override {
fake_user_manager_.Reset();
}
ash::FakeLorgnetteScannerManager* GetLorgnetteScannerManager() {
return static_cast<ash::FakeLorgnetteScannerManager*>(
ash::LorgnetteScannerManagerFactory::GetForBrowserContext(&profile_));
}
DocumentScanAsh& document_scan_ash() { return document_scan_ash_; }
mojom::OpenScannerResponsePtr OpenScanner(const std::string& client_id,
const std::string& scanner_id) {
base::test::TestFuture<mojom::OpenScannerResponsePtr> future;
document_scan_ash().OpenScanner(client_id, scanner_id,
future.GetCallback());
return future.Take();
}
mojom::CloseScannerResponsePtr CloseScanner(
const std::string& scanner_handle) {
base::test::TestFuture<mojom::CloseScannerResponsePtr> future;
document_scan_ash().CloseScanner(scanner_handle, future.GetCallback());
return future.Take();
}
mojom::StartPreparedScanResponsePtr StartPreparedScan(
const std::string& scanner_handle,
mojom::StartScanOptionsPtr options) {
base::test::TestFuture<mojom::StartPreparedScanResponsePtr> future;
document_scan_ash().StartPreparedScan("scanner-handle", std::move(options),
future.GetCallback());
return future.Take();
}
mojom::ReadScanDataResponsePtr ReadScanData(const std::string& job_handle) {
base::test::TestFuture<mojom::ReadScanDataResponsePtr> future;
document_scan_ash().ReadScanData(job_handle, future.GetCallback());
return future.Take();
}
mojom::SetOptionsResponsePtr SetOptions(
const std::string& scanner_handle,
std::vector<mojom::OptionSettingPtr> options) {
base::test::TestFuture<mojom::SetOptionsResponsePtr> future;
document_scan_ash().SetOptions(scanner_handle, std::move(options),
future.GetCallback());
return future.Take();
}
mojom::GetOptionGroupsResponsePtr GetOptionGroups(
const std::string& scanner_handle) {
base::test::TestFuture<mojom::GetOptionGroupsResponsePtr> future;
document_scan_ash().GetOptionGroups(scanner_handle, future.GetCallback());
return future.Take();
}
mojom::CancelScanResponsePtr CancelScan(const std::string& job_handle) {
base::test::TestFuture<mojom::CancelScanResponsePtr> future;
document_scan_ash().CancelScan(job_handle, future.GetCallback());
return future.Take();
}
private:
// Must outlive `profile_`.
content::BrowserTaskEnvironment task_environment_;
user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
fake_user_manager_;
// Must outlive `document_scan_ash_`.
TestingProfile profile_;
DocumentScanAsh document_scan_ash_;
};
TEST_F(DocumentScanAshTest, OpenScanner_BadResponse) {
GetLorgnetteScannerManager()->SetOpenScannerResponse(std::nullopt);
const mojom::OpenScannerResponsePtr response =
OpenScanner("client-id", "scanner-id");
EXPECT_EQ(response->scanner_id, "scanner-id");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kInternalError);
EXPECT_FALSE(response->scanner_handle.has_value());
EXPECT_FALSE(response->options.has_value());
}
TEST_F(DocumentScanAshTest, OpenScanner_GoodResponse) {
lorgnette::OpenScannerResponse fake_response;
fake_response.mutable_scanner_id()->set_connection_string("scanner-id");
fake_response.set_result(lorgnette::OPERATION_RESULT_DEVICE_BUSY);
lorgnette::ScannerConfig* fake_config = fake_response.mutable_config();
fake_config->mutable_scanner()->set_token("12345");
(*fake_config->mutable_options())["option1-name"] = {};
(*fake_config->mutable_options())["option2-name"] = {};
GetLorgnetteScannerManager()->SetOpenScannerResponse(
std::move(fake_response));
const mojom::OpenScannerResponsePtr response =
OpenScanner("client-id", "scanner-id");
EXPECT_EQ(response->scanner_id, "scanner-id");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kDeviceBusy);
ASSERT_TRUE(response->scanner_handle.has_value());
EXPECT_EQ(response->scanner_handle.value(), "12345");
ASSERT_TRUE(response->options.has_value());
EXPECT_TRUE(base::Contains(response->options.value(), "option1-name"));
EXPECT_TRUE(base::Contains(response->options.value(), "option2-name"));
}
TEST_F(DocumentScanAshTest, CloseScanner_BadResponse) {
GetLorgnetteScannerManager()->SetCloseScannerResponse(std::nullopt);
const mojom::CloseScannerResponsePtr response =
CloseScanner("scanner-handle");
EXPECT_EQ(response->scanner_handle, "scanner-handle");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kInternalError);
}
TEST_F(DocumentScanAshTest, CloseScanner_GoodResponse) {
lorgnette::CloseScannerResponse fake_response;
fake_response.mutable_scanner()->set_token("scanner-handle");
fake_response.set_result(lorgnette::OPERATION_RESULT_MISSING);
GetLorgnetteScannerManager()->SetCloseScannerResponse(
std::move(fake_response));
const mojom::CloseScannerResponsePtr response =
CloseScanner("scanner-handle");
EXPECT_EQ(response->scanner_handle, "scanner-handle");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kDeviceMissing);
}
TEST_F(DocumentScanAshTest, StartPreparedScan_BadResponse) {
GetLorgnetteScannerManager()->SetStartPreparedScanResponse(std::nullopt);
const mojom::StartPreparedScanResponsePtr response =
StartPreparedScan("scanner-handle", mojom::StartScanOptions::New());
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kInternalError);
EXPECT_EQ(response->scanner_handle, "scanner-handle");
EXPECT_FALSE(response->job_handle.has_value());
}
TEST_F(DocumentScanAshTest, StartPreparedScan_GoodResponse) {
lorgnette::StartPreparedScanResponse fake_response;
fake_response.set_result(lorgnette::OPERATION_RESULT_SUCCESS);
fake_response.mutable_scanner()->set_token("scanner-handle");
fake_response.mutable_job_handle()->set_token("job-handle");
GetLorgnetteScannerManager()->SetStartPreparedScanResponse(
std::move(fake_response));
auto options = mojom::StartScanOptions::New();
options->format = "image/png";
options->max_read_size = 32768;
const mojom::StartPreparedScanResponsePtr response =
StartPreparedScan("scanner-handle", std::move(options));
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kSuccess);
EXPECT_EQ(response->scanner_handle, "scanner-handle");
ASSERT_TRUE(response->job_handle.has_value());
EXPECT_EQ(response->job_handle.value(), "job-handle");
}
TEST_F(DocumentScanAshTest, ReadScanData_BadResponse) {
GetLorgnetteScannerManager()->SetReadScanDataResponse(std::nullopt);
const mojom::ReadScanDataResponsePtr response = ReadScanData("job-handle");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kInternalError);
EXPECT_EQ(response->job_handle, "job-handle");
EXPECT_FALSE(response->data.has_value());
}
TEST_F(DocumentScanAshTest, ReadScanData_GoodResponse) {
lorgnette::ReadScanDataResponse fake_response;
fake_response.set_result(lorgnette::OPERATION_RESULT_SUCCESS);
fake_response.mutable_job_handle()->set_token("job-handle");
fake_response.set_data("data");
fake_response.set_estimated_completion(24);
GetLorgnetteScannerManager()->SetReadScanDataResponse(
std::move(fake_response));
const mojom::ReadScanDataResponsePtr response = ReadScanData("job-handle");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kSuccess);
EXPECT_EQ(response->job_handle, "job-handle");
ASSERT_TRUE(response->data.has_value());
EXPECT_THAT(response->data.value(), ElementsAre('d', 'a', 't', 'a'));
ASSERT_TRUE(response->estimated_completion.has_value());
EXPECT_EQ(response->estimated_completion.value(), 24U);
}
TEST_F(DocumentScanAshTest, SetOptions_BadResponse) {
auto option = mojom::OptionSetting::New();
option->name = "option-name";
std::vector<mojom::OptionSettingPtr> options;
options.emplace_back(std::move(option));
GetLorgnetteScannerManager()->SetSetOptionsResponse(std::nullopt);
const mojom::SetOptionsResponsePtr response =
SetOptions("scanner-handle", std::move(options));
EXPECT_EQ(response->scanner_handle, "scanner-handle");
EXPECT_FALSE(response->options.has_value());
ASSERT_EQ(response->results.size(), 1U);
EXPECT_EQ(response->results[0]->name, "option-name");
EXPECT_EQ(response->results[0]->result,
mojom::ScannerOperationResult::kInternalError);
}
TEST_F(DocumentScanAshTest, SetOptions_BadValue) {
// Insert an option whose value type is different than the option type.
auto option = mojom::OptionSetting::New();
option->name = "bad-option";
option->type = mojom::OptionType::kString;
option->value = crosapi::mojom::OptionValue::NewBoolValue(false);
std::vector<mojom::OptionSettingPtr> options;
options.emplace_back(std::move(option));
// Don't add any results to our fake response. The result in our response
// should come from our DocumentScanAsh object itself, not from the backend.
lorgnette::SetOptionsResponse fake_response;
fake_response.mutable_scanner()->set_token("scanner-handle");
GetLorgnetteScannerManager()->SetSetOptionsResponse(std::move(fake_response));
const mojom::SetOptionsResponsePtr response =
SetOptions("scanner-handle", std::move(options));
EXPECT_EQ(response->scanner_handle, "scanner-handle");
ASSERT_EQ(response->results.size(), 1U);
EXPECT_EQ(response->results[0]->name, "bad-option");
EXPECT_EQ(response->results[0]->result,
mojom::ScannerOperationResult::kWrongType);
}
TEST_F(DocumentScanAshTest, SetOptions_GoodResponse) {
// The options we put in here don't really matter since we are setting the
// response manually in the fake lorgnette scanner manager.
std::vector<mojom::OptionSettingPtr> options;
lorgnette::SetOptionsResponse fake_response;
fake_response.mutable_scanner()->set_token("scanner-handle");
(*fake_response.mutable_results())["option-name"] =
lorgnette::OPERATION_RESULT_SUCCESS;
// Config
lorgnette::ScannerConfig config;
lorgnette::ScannerOption scanner_option;
scanner_option.set_name("scanner-option");
(*config.mutable_options())["config-option"] = std::move(scanner_option);
*fake_response.mutable_config() = std::move(config);
GetLorgnetteScannerManager()->SetSetOptionsResponse(std::move(fake_response));
const mojom::SetOptionsResponsePtr response =
SetOptions("scanner-handle", std::move(options));
EXPECT_EQ(response->scanner_handle, "scanner-handle");
ASSERT_EQ(response->results.size(), 1U);
EXPECT_EQ(response->results[0]->name, "option-name");
EXPECT_EQ(response->results[0]->result,
mojom::ScannerOperationResult::kSuccess);
ASSERT_TRUE(response->options.has_value());
const auto& it = response->options.value().find("config-option");
ASSERT_TRUE(it != response->options.value().end());
EXPECT_EQ(it->second->name, "scanner-option");
}
TEST_F(DocumentScanAshTest, GetOptionGroups_BadResponse) {
GetLorgnetteScannerManager()->SetGetCurrentConfigResponse(std::nullopt);
const mojom::GetOptionGroupsResponsePtr response =
GetOptionGroups("scanner-handle");
EXPECT_EQ(response->scanner_handle, "scanner-handle");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kInternalError);
EXPECT_FALSE(response->groups.has_value());
}
TEST_F(DocumentScanAshTest, GetOptionGroups_GoodResponse) {
lorgnette::GetCurrentConfigResponse fake_response;
fake_response.mutable_scanner()->set_token("scanner-handle");
fake_response.set_result(lorgnette::OPERATION_RESULT_SUCCESS);
// Config
lorgnette::ScannerConfig config;
lorgnette::OptionGroup group;
group.set_title("group-title");
group.add_members("group-member");
*config.add_option_groups() = std::move(group);
*fake_response.mutable_config() = std::move(config);
GetLorgnetteScannerManager()->SetGetCurrentConfigResponse(
std::move(fake_response));
const mojom::GetOptionGroupsResponsePtr response =
GetOptionGroups("scanner-handle");
EXPECT_EQ(response->scanner_handle, "scanner-handle");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kSuccess);
EXPECT_TRUE(response->groups.has_value());
ASSERT_EQ(response->groups.value().size(), 1U);
EXPECT_EQ(response->groups.value()[0]->title, "group-title");
EXPECT_THAT(response->groups.value()[0]->members,
ElementsAre("group-member"));
}
TEST_F(DocumentScanAshTest, CancelScan_BadResponse) {
GetLorgnetteScannerManager()->SetCancelScanResponse(std::nullopt);
const mojom::CancelScanResponsePtr response = CancelScan("job-handle");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kInternalError);
EXPECT_EQ(response->job_handle, "job-handle");
}
TEST_F(DocumentScanAshTest, CancelScan_GoodResponse) {
lorgnette::CancelScanResponse fake_response;
fake_response.set_result(lorgnette::OPERATION_RESULT_SUCCESS);
fake_response.mutable_job_handle()->set_token("job-handle");
GetLorgnetteScannerManager()->SetCancelScanResponse(std::move(fake_response));
const mojom::CancelScanResponsePtr response = CancelScan("job-handle");
EXPECT_EQ(response->result, mojom::ScannerOperationResult::kSuccess);
EXPECT_EQ(response->job_handle, "job-handle");
}
} // namespace crosapi