blob: 10fedda3bec8bcbdc4b8b3fcd48af5d0390450f8 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lorgnette/device_tracker.h"
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <base/files/file.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/files/scoped_temp_dir.h>
#include <base/notimplemented.h>
#include <base/run_loop.h>
#include <base/strings/stringprintf.h>
#include <base/test/bind.h>
#include <base/test/task_environment.h>
#include <brillo/files/file_util.h>
#include <chromeos/constants/lorgnette_dlc.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "lorgnette/dlc_client_fake.h"
#include "lorgnette/firewall_manager.h"
#include "lorgnette/sane_client_fake.h"
#include "lorgnette/test_util.h"
#include "lorgnette/usb/libusb_wrapper_fake.h"
#include "lorgnette/usb/usb_device.h"
#include "lorgnette/usb/usb_device_fake.h"
using ::testing::_;
using ::testing::Each;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::Not;
using ::testing::Pointee;
using ::testing::Property;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedPointwise;
namespace lorgnette {
namespace {
constexpr char kEpson2Name[] = "epson2:net:127.0.0.1";
constexpr char kEpsonDsName[] = "epsonds:libusb:003:002";
enum class DlcType {
kNoDlc,
kSingleDlc,
kMultipleDlcs,
};
ScanParameters MakeScanParameters(int width, int height) {
return {
.format = FrameFormat::kRGB,
.bytes_per_line = 3 * width,
.pixels_per_line = width,
.lines = height,
.depth = 8,
};
}
// libusb structures contain various pointers to separately allocated
// memory and other resources. This struct bundles RAII objects for these
// allocations along with the device that uses them so they can all be deleted
// when the whole struct goes out of scope.
struct UsbDeviceBundle {
std::unique_ptr<libusb_interface_descriptor> altsetting;
std::unique_ptr<libusb_interface> interface;
std::unique_ptr<UsbDeviceFake> device;
std::string connection_string;
std::string ippusb_string;
base::File ippusb_socket;
};
UsbDeviceBundle MakeIPPUSBDevice(const std::string& model) {
static size_t num = 1; // Incremented on each call so returned devices are
// unique.
UsbDeviceBundle result;
result.device = std::make_unique<UsbDeviceFake>();
// Device descriptor containing basic device info.
libusb_device_descriptor device_desc = MakeMinimalDeviceDescriptor();
device_desc.bDeviceClass = LIBUSB_CLASS_PER_INTERFACE;
device_desc.bNumConfigurations = 1;
device_desc.iManufacturer = 1;
device_desc.iProduct = 2;
device_desc.iSerialNumber = 3;
result.device->SetStringDescriptors(
{"", "GoogleTest", model, base::StringPrintf("ABC123%02zu", num)});
result.device->SetDeviceDescriptor(device_desc);
// One altsetting with a printer class and the IPP-USB protocol.
result.altsetting = MakeIppUsbInterfaceDescriptor();
// One interface containing the altsetting.
result.interface = std::make_unique<libusb_interface>();
result.interface->num_altsetting = 1;
result.interface->altsetting = result.altsetting.get();
// One config descriptor containing the interface.
libusb_config_descriptor config_desc;
memset(&config_desc, 0, sizeof(config_desc));
config_desc.bLength = sizeof(config_desc);
config_desc.bDescriptorType = LIBUSB_DT_CONFIG;
config_desc.wTotalLength = sizeof(config_desc);
config_desc.bNumInterfaces = 1;
config_desc.interface = result.interface.get();
result.device->SetConfigDescriptors({config_desc});
result.device->SetBusNumber(1);
result.device->SetDeviceAddress(num);
result.device->Init();
++num;
return result;
}
class DlcDeviceTrackerTest : public testing::Test {
protected:
std::unique_ptr<DeviceTracker> DlcMinimalDiscoverySetup(
base::RunLoop* run_loop, DlcType dlc_type = DlcType::kNoDlc) {
std::vector<std::unique_ptr<UsbDevice>> device_list;
// Scanners that need DLC backend
VidPid device1 = {0x832, 0x231};
VidPid device2 = {0x832, 0x342};
VidPid device3 = {0x432, 0x342};
dlc_backend_scanners_ = {{device1, kSaneBackendsPfuDlcId},
{device2, kSaneBackendsPfuDlcId},
{device3, kSaneBackendsExtrasDlcId}};
device_desc_.bDeviceClass = LIBUSB_CLASS_PER_INTERFACE;
device_desc_.bNumConfigurations = 1;
device_desc_.iManufacturer = 1;
device_desc_.iProduct = 2;
usb_printer_->SetStringDescriptors({"", "GoogleTest", "USB Scanner 3000"});
printer_altsetting_->bInterfaceProtocol = 0;
printer_interface_->num_altsetting = 1;
printer_interface_->altsetting = printer_altsetting_.get();
memset(&descriptor_, 0, sizeof(descriptor_));
descriptor_.bLength = sizeof(descriptor_);
descriptor_.bDescriptorType = LIBUSB_DT_CONFIG;
descriptor_.wTotalLength = sizeof(descriptor_);
descriptor_.bNumInterfaces = 1;
descriptor_.interface = interface_.get();
usb_printer_->SetDlcBackendScanners(&dlc_backend_scanners_);
usb_printer_->SetDeviceDescriptor(device_desc_);
usb_printer_->SetConfigDescriptors({descriptor_});
usb_printer_->SetBusNumber(1);
usb_printer_->SetDeviceAddress(1);
usb_printer_->Init();
// USB printer with no DLC download required (wrong vid pid)
auto no_dlc_usb_printer = UsbDeviceFake::Clone(*usb_printer_.get());
no_dlc_usb_printer->MutableConfigDescriptor(0).interface =
printer_interface_.get();
no_dlc_usb_printer->SetStringDescriptors(
{"", "GoogleTest", "USB Printer 1000"});
no_dlc_usb_printer->MutableDeviceDescriptor().idProduct = 0x987;
no_dlc_usb_printer->MutableDeviceDescriptor().idVendor = 0x123;
// USB printer with no DLC download required (wrong vid, right pid)
auto no_dlc_usb_printer2 = UsbDeviceFake::Clone(*usb_printer_.get());
no_dlc_usb_printer2->MutableConfigDescriptor(0).interface =
printer_interface_.get();
no_dlc_usb_printer2->SetStringDescriptors(
{"", "GoogleTest", "USB Printer 1500"});
no_dlc_usb_printer2->MutableDeviceDescriptor().idProduct = 0x231;
no_dlc_usb_printer2->MutableDeviceDescriptor().idVendor = 0x432;
// USB printer with DLC required (Correct vid pid)
auto dlc_usb_printer = UsbDeviceFake::Clone(*usb_printer_.get());
dlc_usb_printer->MutableConfigDescriptor(0).interface =
printer_interface_.get();
dlc_usb_printer->SetStringDescriptors(
{"", "GoogleTest", "USB Printer 2000"});
dlc_usb_printer->MutableDeviceDescriptor().idProduct = 0x231;
dlc_usb_printer->MutableDeviceDescriptor().idVendor = 0x832;
// Second USB printer with different DLC required (Correct vid pid)
auto second_dlc_usb_printer = UsbDeviceFake::Clone(*usb_printer_.get());
second_dlc_usb_printer->MutableConfigDescriptor(0).interface =
printer_interface_.get();
second_dlc_usb_printer->SetStringDescriptors(
{"", "GoogleTest", "USB Printer 3000"});
second_dlc_usb_printer->MutableDeviceDescriptor().idProduct = 0x342;
second_dlc_usb_printer->MutableDeviceDescriptor().idVendor = 0x432;
device_list.emplace_back(std::move(usb_printer_));
device_list.emplace_back(std::move(no_dlc_usb_printer));
device_list.emplace_back(std::move(no_dlc_usb_printer2));
if (dlc_type == DlcType::kSingleDlc) {
device_list.emplace_back(std::move(dlc_usb_printer));
} else if (dlc_type == DlcType::kMultipleDlcs) {
device_list.emplace_back(std::move(dlc_usb_printer));
device_list.emplace_back(std::move(second_dlc_usb_printer));
}
libusb_->SetDevices(std::move(device_list));
sane_client_->SetListDevicesResult(true);
auto tracker =
std::make_unique<DeviceTracker>(sane_client_.get(), libusb_.get());
// Signal handler that tracks all the events of interest.
std::set<std::unique_ptr<ScannerInfo>> scanners;
// Set signal handler
auto signal_handler =
base::BindLambdaForTesting([run_loop, &tracker, &scanners](
const ScannerListChangedSignal& signal) {
if (signal.event_type() == ScannerListChangedSignal::ENUM_COMPLETE) {
StopScannerDiscoveryRequest stop_request;
stop_request.set_session_id(signal.session_id());
tracker->StopScannerDiscovery(stop_request);
}
if (signal.event_type() == ScannerListChangedSignal::SESSION_ENDING) {
run_loop->Quit();
}
if (signal.event_type() == ScannerListChangedSignal::SCANNER_ADDED) {
std::unique_ptr<ScannerInfo> info(signal.scanner().New());
info->CopyFrom(signal.scanner());
scanners.insert(std::move(info));
}
});
tracker->SetScannerListChangedSignalSender(signal_handler);
return tracker;
}
std::unique_ptr<LibusbWrapperFake> libusb_ =
std::make_unique<LibusbWrapperFake>();
// Scanner that supports eSCL over IPP-USB.
std::unique_ptr<UsbDeviceFake> usb_printer_ =
std::make_unique<UsbDeviceFake>();
// One config descriptor containing the interface.
libusb_config_descriptor descriptor_;
std::unique_ptr<libusb_interface_descriptor> printer_altsetting_ =
MakeIppUsbInterfaceDescriptor();
std::unique_ptr<libusb_interface> printer_interface_ =
std::make_unique<libusb_interface>();
libusb_device_descriptor device_desc_ = MakeMinimalDeviceDescriptor();
// One interface containing the altsetting.
std::unique_ptr<libusb_interface> interface_ =
std::make_unique<libusb_interface>();
std::unique_ptr<SaneClientFake> sane_client_ =
std::make_unique<SaneClientFake>();
std::map<VidPid, std::string> dlc_backend_scanners_;
// Test root path set in dlc_client_fake.cc
const base::FilePath root_path_ = base::FilePath("/test/path/to/dlc");
};
class MockFirewallManager : public FirewallManager {
public:
explicit MockFirewallManager(const std::string& interface)
: FirewallManager(interface) {}
MOCK_METHOD(std::vector<PortToken>, RequestPortsForDiscovery, (), (override));
MOCK_METHOD(PortToken, RequestUdpPortAccess, (uint16_t), (override));
MOCK_METHOD(void, ReleaseUdpPortAccess, (uint16_t), (override));
};
class DeviceTrackerTest : public testing::Test {
public:
// Moves discovery sessions along while recording the events that happen.
// The caller should first start one or more sessions and record their session
// IDs into `open_sessions_`. This will then use the run loop to advance the
// sessions normally:
// 1. When a scanner is added, it will be recorded in `discovered_scanners_`.
// 2. When enumeration finishes, that session will be closed.
// 3. When a session closes, it is recorded into `closed_sessions_`.
// 4. When all sessions originally present in `open_sessions_` are closed, the
// entire run loop is terminated.
void RecordingSignalSender(const ScannerListChangedSignal& signal) {
switch (signal.event_type()) {
case ScannerListChangedSignal::ENUM_COMPLETE: {
StopScannerDiscoveryRequest stop_request;
stop_request.set_session_id(signal.session_id());
tracker_->StopScannerDiscovery(stop_request);
break;
}
case ScannerListChangedSignal::SESSION_ENDING:
closed_sessions_.push_back(signal.session_id());
open_sessions_.erase(signal.session_id());
if (open_sessions_.empty()) {
run_loop_quit_.Run();
}
break;
case ScannerListChangedSignal::SCANNER_ADDED: {
std::unique_ptr<ScannerInfo> info(signal.scanner().New());
info->CopyFrom(signal.scanner());
discovered_scanners_[signal.session_id()].insert(std::move(info));
break;
}
case ScannerListChangedSignal::SCANNER_REMOVED:
LOG(ERROR) << "Scanner " << signal.scanner().name()
<< " removed from session " << signal.session_id();
NOTIMPLEMENTED();
break;
default:
// Do nothing for the internal proto enum values.
break;
}
}
protected:
void SetUp() override {
run_loop_quit_ = run_loop_.QuitClosure();
sane_client_ = std::make_unique<SaneClientFake>();
libusb_ = std::make_unique<LibusbWrapperFake>();
tracker_ =
std::make_unique<DeviceTracker>(sane_client_.get(), libusb_.get());
CHECK(socket_dir_.CreateUniqueTempDir());
sane_client_->SetIppUsbSocketDir(socket_dir_.GetPath());
// Using base::Unretained is safe here because the signal sender is removed
// in TearDown() while this object is still alive.
tracker_->SetScannerListChangedSignalSender(base::BindRepeating(
&DeviceTrackerTest::RecordingSignalSender, base::Unretained(this)));
firewall_manager_ =
std::make_unique<MockFirewallManager>(/*interface=*/"test");
tracker_->SetFirewallManager(firewall_manager_.get());
dlc_client_ = std::make_unique<DlcClientFake>();
tracker_->SetDlcClient(dlc_client_.get());
}
void TearDown() override {
tracker_->SetScannerListChangedSignalSender(base::DoNothing());
}
// Create the appropriate "socket" in the filesystem to allow an IPP-USB
// device to pass the SaneDevice checks and fill in the connection string
// pointing to the socket.
void CreateIPPUSBSocket(UsbDeviceBundle& device) {
std::string socket_name = base::StringPrintf(
"%04x-%04x.sock", device.device->GetVid(), device.device->GetPid());
base::FilePath socket_path = socket_dir_.GetPath().Append(socket_name);
device.ippusb_socket = base::File(
socket_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
device.connection_string = base::StringPrintf(
"airscan:escl:%s %s:unix://%s/eSCL/",
device.device
->GetStringDescriptor(
device.device->GetDeviceDescriptor()->iManufacturer)
->c_str(),
device.device
->GetStringDescriptor(
device.device->GetDeviceDescriptor()->iProduct)
->c_str(),
socket_name.c_str());
device.ippusb_string = base::StringPrintf(
"ippusb:escl:%s %s:%04x_%04x/eSCL/",
device.device
->GetStringDescriptor(
device.device->GetDeviceDescriptor()->iManufacturer)
->c_str(),
device.device
->GetStringDescriptor(
device.device->GetDeviceDescriptor()->iProduct)
->c_str(),
device.device->GetVid(), device.device->GetPid());
}
SaneDeviceFake* CreateFakeScanner(const std::string& name) {
auto scanner = std::make_unique<SaneDeviceFake>();
SaneDeviceFake* raw_scanner = scanner.get();
sane_client_->SetDeviceForName(name, std::move(scanner));
return raw_scanner;
}
SaneDeviceFake* CreateFakeScanner(const std::string& name,
const std::string& manufacturer,
const std::string& model,
const std::string& type) {
sane_client_->AddDeviceListing(name, manufacturer, model, type);
return CreateFakeScanner(name);
}
void SetQuitClosure(base::RepeatingClosure closure) {
run_loop_quit_ = std::move(closure);
}
base::test::SingleThreadTaskEnvironment task_environment_;
base::RunLoop run_loop_;
base::RepeatingClosure run_loop_quit_;
std::unique_ptr<SaneClientFake> sane_client_;
std::unique_ptr<LibusbWrapperFake> libusb_;
std::unique_ptr<MockFirewallManager> firewall_manager_;
std::unique_ptr<DlcClientFake> dlc_client_;
std::unique_ptr<DeviceTracker> tracker_; // Must come after all the mocks.
base::ScopedTempDir socket_dir_;
// Set of open session ids that will be tracked by `RecordingSignalSender`.
std::set<std::string> open_sessions_;
// List of sessions that were closed while running the discovery session loop.
std::vector<std::string> closed_sessions_;
// For each session ID that has at least one scanner discovered, a set of all
// the discovered scanners.
std::map<std::string, std::set<std::unique_ptr<ScannerInfo>>>
discovered_scanners_;
};
TEST_F(DeviceTrackerTest, CreateMultipleSessions) {
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 0);
StartScannerDiscoveryRequest start_request;
start_request.set_client_id("client_1");
StartScannerDiscoveryResponse response1 =
tracker_->StartScannerDiscovery(start_request);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 1);
start_request.set_client_id("client_2");
StartScannerDiscoveryResponse response2 =
tracker_->StartScannerDiscovery(start_request);
EXPECT_TRUE(response2.started());
EXPECT_FALSE(response2.session_id().empty());
EXPECT_NE(response1.session_id(), response2.session_id());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 2);
StopScannerDiscoveryRequest stop_request;
stop_request.set_session_id(response1.session_id());
StopScannerDiscoveryResponse stop1 =
tracker_->StopScannerDiscovery(stop_request);
EXPECT_TRUE(stop1.stopped());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 1);
stop_request.set_session_id(response2.session_id());
StopScannerDiscoveryResponse stop2 =
tracker_->StopScannerDiscovery(stop_request);
EXPECT_TRUE(stop2.stopped());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 0);
EXPECT_THAT(closed_sessions_,
ElementsAre(response1.session_id(), response2.session_id()));
}
TEST_F(DeviceTrackerTest, CreateDuplicateSessions) {
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 0);
StartScannerDiscoveryRequest start_request;
start_request.set_client_id("client_1");
StartScannerDiscoveryResponse response1 =
tracker_->StartScannerDiscovery(start_request);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 1);
start_request.set_client_id("client_1");
StartScannerDiscoveryResponse response2 =
tracker_->StartScannerDiscovery(start_request);
EXPECT_TRUE(response2.started());
EXPECT_FALSE(response2.session_id().empty());
EXPECT_EQ(response1.session_id(), response2.session_id());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 1);
StopScannerDiscoveryRequest stop_request;
stop_request.set_session_id(response1.session_id());
StopScannerDiscoveryResponse stop1 =
tracker_->StopScannerDiscovery(stop_request);
EXPECT_TRUE(stop1.stopped());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 0);
stop_request.set_session_id(response2.session_id());
StopScannerDiscoveryResponse stop2 =
tracker_->StopScannerDiscovery(stop_request);
EXPECT_TRUE(stop2.stopped());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 0);
// Session ID should get closed twice even though it doesn't exist the second
// time.
EXPECT_THAT(closed_sessions_,
ElementsAre(response1.session_id(), response1.session_id()));
}
TEST_F(DeviceTrackerTest, StartSessionMissingClient) {
StartScannerDiscoveryRequest start_request;
start_request.set_client_id("");
StartScannerDiscoveryResponse response =
tracker_->StartScannerDiscovery(start_request);
EXPECT_FALSE(response.started());
EXPECT_TRUE(response.session_id().empty());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 0);
}
TEST_F(DeviceTrackerTest, StopSessionMissingID) {
StopScannerDiscoveryRequest stop_request;
stop_request.set_session_id("");
StopScannerDiscoveryResponse response =
tracker_->StopScannerDiscovery(stop_request);
EXPECT_FALSE(response.stopped());
EXPECT_TRUE(closed_sessions_.empty());
EXPECT_EQ(tracker_->NumActiveDiscoverySessions(), 0);
}
// Policy: Download never
// Will not install, even though DLC requiring devices are found.
TEST_F(DlcDeviceTrackerTest, TestNeverDownloadDlcPolicy) {
base::test::SingleThreadTaskEnvironment task_environment;
base::RunLoop run_loop;
// Add dlc requiring devices
auto tracker = DlcMinimalDiscoverySetup(&run_loop, DlcType::kSingleDlc);
MockFirewallManager firewall_manager(/*interface=*/"test");
tracker->SetFirewallManager(&firewall_manager);
EXPECT_CALL(firewall_manager, RequestPortsForDiscovery()).WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// Set fake DLC client
auto dlc_client = std::make_unique<DlcClientFake>();
tracker->SetDlcClient(dlc_client.get());
StartScannerDiscoveryRequest start_request;
// DOWNLOAD_NEVER Policy
start_request.set_client_id("dlc_client");
start_request.set_download_policy(BackendDownloadPolicy::DOWNLOAD_NEVER);
StartScannerDiscoveryResponse response1 =
tracker->StartScannerDiscovery(start_request);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
run_loop.Run();
EXPECT_EQ(tracker->GetDlcRootPath(kSaneBackendsPfuDlcId), std::nullopt);
}
// Policy: Download if needed
// Will not install DLC because no DLC requiring devices detected.
TEST_F(DlcDeviceTrackerTest, TestDownloadIfNeededDlcPolicyWithoutDlcDevices) {
base::test::SingleThreadTaskEnvironment task_environment;
base::RunLoop run_loop;
auto tracker = DlcMinimalDiscoverySetup(&run_loop);
MockFirewallManager firewall_manager(/*interface=*/"test");
tracker->SetFirewallManager(&firewall_manager);
EXPECT_CALL(firewall_manager, RequestPortsForDiscovery()).WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// Set fake DLC client
auto dlc_client = std::make_unique<DlcClientFake>();
tracker->SetDlcClient(dlc_client.get());
StartScannerDiscoveryRequest start_request;
// DOWNLOAD_IF_NEEDED Policy
start_request.set_client_id("dlc_client");
start_request.set_download_policy(BackendDownloadPolicy::DOWNLOAD_IF_NEEDED);
StartScannerDiscoveryResponse response1 =
tracker->StartScannerDiscovery(start_request);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
run_loop.Run();
// DLC not installed because no devices needed it.
EXPECT_EQ(tracker->GetDlcRootPath(kSaneBackendsPfuDlcId), std::nullopt);
}
// Policy: Download if needed
// Will install DLC since DLC devices are detected.
TEST_F(DlcDeviceTrackerTest, TestDownloadIfNeededDlcPolicyWithDlcDevices) {
base::test::SingleThreadTaskEnvironment task_environment;
base::RunLoop run_loop;
// Add DLC requiring devices
auto tracker = DlcMinimalDiscoverySetup(&run_loop, DlcType::kSingleDlc);
MockFirewallManager firewall_manager(/*interface=*/"test");
tracker->SetFirewallManager(&firewall_manager);
EXPECT_CALL(firewall_manager, RequestPortsForDiscovery()).WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// Set fake DLC client
auto dlc_client = std::make_unique<DlcClientFake>();
tracker->SetDlcClient(dlc_client.get());
StartScannerDiscoveryRequest start_request;
// DOWNLOAD_IF_NEEDED Policy
start_request.set_client_id("dlc_client");
start_request.set_download_policy(BackendDownloadPolicy::DOWNLOAD_IF_NEEDED);
StartScannerDiscoveryResponse response1 =
tracker->StartScannerDiscovery(start_request);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
run_loop.Run();
// DLC installed because 1 device needed it.
ASSERT_NE(tracker->GetDlcRootPath(kSaneBackendsPfuDlcId), std::nullopt);
EXPECT_EQ(*tracker->GetDlcRootPath(kSaneBackendsPfuDlcId),
root_path_.Append(kSaneBackendsPfuDlcId));
}
// Policy: Always download
// Should install DLC even if DLC requiring devices aren't present/detected.
TEST_F(DlcDeviceTrackerTest, TestAlwaysDownloadDlcPolicy) {
base::test::SingleThreadTaskEnvironment task_environment;
base::RunLoop run_loop;
// DLC devices shouldn't be required to prompt download in this policy
auto tracker = DlcMinimalDiscoverySetup(&run_loop);
MockFirewallManager firewall_manager(/*interface=*/"test");
tracker->SetFirewallManager(&firewall_manager);
EXPECT_CALL(firewall_manager, RequestPortsForDiscovery()).WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// Set fake DLC client
auto dlc_client = std::make_unique<DlcClientFake>();
tracker->SetDlcClient(dlc_client.get());
StartScannerDiscoveryRequest start_request;
// DOWNLOAD_ALWAYS Policy
start_request.set_client_id("dlc_client");
start_request.set_download_policy(BackendDownloadPolicy::DOWNLOAD_ALWAYS);
StartScannerDiscoveryResponse response1 =
tracker->StartScannerDiscovery(start_request);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
run_loop.Run();
ASSERT_NE(tracker->GetDlcRootPath(kSaneBackendsPfuDlcId), std::nullopt);
EXPECT_EQ(*tracker->GetDlcRootPath(kSaneBackendsPfuDlcId),
root_path_.Append(kSaneBackendsPfuDlcId));
}
// Policy: Download if needed
// Will install multiple DLCs since DLC devices are detected.
TEST_F(DlcDeviceTrackerTest,
TestDownloadIfNeededDlcPolicyWithMultipleDlcDevices) {
base::test::TaskEnvironment task_environment;
base::RunLoop run_loop;
// Add DLC requiring devices
auto tracker = DlcMinimalDiscoverySetup(&run_loop, DlcType::kMultipleDlcs);
MockFirewallManager firewall_manager(/*interface=*/"test");
tracker->SetFirewallManager(&firewall_manager);
EXPECT_CALL(firewall_manager, RequestPortsForDiscovery()).WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// Set fake DLC client
auto dlc_client = std::make_unique<DlcClientFake>();
tracker->SetDlcClient(dlc_client.get());
StartScannerDiscoveryRequest start_request;
// DOWNLOAD_IF_NEEDED Policy
start_request.set_client_id("dlc_client");
start_request.set_download_policy(BackendDownloadPolicy::DOWNLOAD_IF_NEEDED);
StartScannerDiscoveryResponse response1 =
tracker->StartScannerDiscovery(start_request);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
run_loop.Run();
// DLC installed because 2 devices needed it.
ASSERT_NE(tracker->GetDlcRootPath(kSaneBackendsPfuDlcId), std::nullopt);
EXPECT_EQ(*tracker->GetDlcRootPath(kSaneBackendsPfuDlcId),
root_path_.Append(kSaneBackendsPfuDlcId));
ASSERT_NE(tracker->GetDlcRootPath(kSaneBackendsExtrasDlcId), std::nullopt);
EXPECT_EQ(*tracker->GetDlcRootPath(kSaneBackendsExtrasDlcId),
root_path_.Append(kSaneBackendsExtrasDlcId));
}
// Test the whole flow with several fake USB devices. Confirm that
// exactly and only the devices that fully match the checks and have a SANE
// backend have a signal emitted before shutting down the session.
TEST_F(DeviceTrackerTest, CompleteDiscoverySession) {
// Scanner that supports eSCL over IPP-USB.
auto ippusb_escl_device = MakeIPPUSBDevice("eSCL Scanner 3000");
ippusb_escl_device.device->MutableDeviceDescriptor().idVendor = 0x04a9;
ippusb_escl_device.device->Init();
CreateIPPUSBSocket(ippusb_escl_device);
CreateFakeScanner(ippusb_escl_device.connection_string);
// Printer that supports IPP-USB but not eSCL.
auto ippusb_printer = MakeIPPUSBDevice("IPP-USB Printer 2000");
ippusb_printer.device->MutableDeviceDescriptor().idProduct = 0x6543;
ippusb_printer.device->Init();
CreateIPPUSBSocket(ippusb_printer);
// Printer that doesn't support IPP-USB.
auto printer_altsetting = MakeIppUsbInterfaceDescriptor();
printer_altsetting->bInterfaceProtocol = 0;
auto printer_interface = std::make_unique<libusb_interface>();
printer_interface->num_altsetting = 1;
printer_interface->altsetting = printer_altsetting.get();
auto usb_printer = MakeIPPUSBDevice("USB Printer 1000");
usb_printer.device->MutableDeviceDescriptor().idProduct = 0x7654;
usb_printer.device->MutableConfigDescriptor(0).interface =
printer_interface.get();
usb_printer.device->SetStringDescriptors(
{"", "GoogleTest", "USB Printer 1000"});
usb_printer.device->Init();
// Not a printer at all.
auto non_printer = UsbDeviceFake::Clone(*usb_printer.device.get());
non_printer->MutableDeviceDescriptor().idProduct = 0x7654;
non_printer->MutableDeviceDescriptor().bDeviceClass = LIBUSB_DT_HUB;
non_printer->SetStringDescriptors({"", "GoogleTest", "USB Gadget 500"});
non_printer->Init();
sane_client_->SetListDevicesResult(true);
// Duplicates of eSCL over ippusb that are filtered out.
std::string dup_by_vidpid =
base::StringPrintf("pixma:04A94321_%s",
ippusb_escl_device.device->GetSerialNumber().c_str());
CreateFakeScanner(dup_by_vidpid, "GoogleTest", "eSCL Scanner 3001", "eSCL");
std::string dup_by_busdev = base::StringPrintf(
"epson2:libusb:%03d:%03d", ippusb_escl_device.device->GetBusNumber(),
ippusb_escl_device.device->GetDeviceAddress());
CreateFakeScanner(dup_by_busdev, "GoogleTest", "eSCL Scanner 3002", "eSCL");
// Unique USB device without ippusb support that is added during SANE probing.
CreateFakeScanner(kEpsonDsName, "GoogleTest", "SANE Scanner 4000", "USB");
// Unique non-eSCL network device that is added during SANE probing.
CreateFakeScanner(kEpson2Name, "GoogleTest", "GoogleTest SANE NetScan 4200",
"Network");
std::vector<std::unique_ptr<UsbDevice>> device_list;
device_list.emplace_back(std::move(non_printer));
device_list.emplace_back(std::move(ippusb_escl_device.device));
device_list.emplace_back(std::move(ippusb_printer.device));
device_list.emplace_back(std::move(usb_printer.device));
libusb_->SetDevices(std::move(device_list));
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery()).WillOnce([] {
std::vector<PortToken> retval;
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
StartScannerDiscoveryRequest start_request;
start_request.set_client_id("CompleteDiscoverySession");
start_request.set_preferred_only(true);
StartScannerDiscoveryResponse response =
tracker_->StartScannerDiscovery(start_request);
EXPECT_TRUE(response.started());
EXPECT_FALSE(response.session_id().empty());
open_sessions_.insert(response.session_id());
run_loop_.Run();
ScannerInfo escl3000;
escl3000.set_manufacturer("GoogleTest");
escl3000.set_model("eSCL Scanner 3000");
escl3000.set_connection_type(lorgnette::CONNECTION_USB);
escl3000.set_display_name("GoogleTest eSCL Scanner 3000 (USB)");
escl3000.set_protocol_type("Mopria");
escl3000.set_secure(true);
ScannerInfo sane4000;
sane4000.set_manufacturer("GoogleTest");
sane4000.set_model("SANE Scanner 4000");
sane4000.set_display_name("GoogleTest SANE Scanner 4000 (USB)");
sane4000.set_connection_type(lorgnette::CONNECTION_USB);
sane4000.set_secure(true);
sane4000.set_protocol_type("epsonds");
ScannerInfo sane4200;
sane4200.set_manufacturer("GoogleTest");
sane4200.set_model("GoogleTest SANE NetScan 4200");
sane4200.set_display_name("GoogleTest SANE NetScan 4200");
sane4200.set_connection_type(lorgnette::CONNECTION_NETWORK);
sane4200.set_secure(false);
sane4200.set_protocol_type("epson2");
EXPECT_THAT(closed_sessions_, ElementsAre(response.session_id()));
EXPECT_THAT(discovered_scanners_[response.session_id()],
UnorderedElementsAre(MatchesScannerInfo(escl3000),
MatchesScannerInfo(sane4000),
MatchesScannerInfo(sane4200)));
}
TEST_F(DeviceTrackerTest, DiscoverySessionCachingUnpluggedDeviceRemoved) {
sane_client_->SetListDevicesResult(true);
CreateFakeScanner("epson2:libusb:001:001", "GoogleTest", "Scanner 1", "USB");
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery())
.WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// First discovery session finds the device.
base::RunLoop run_loop1;
SetQuitClosure(run_loop1.QuitClosure());
StartScannerDiscoveryRequest start_request1;
start_request1.set_client_id("DiscoverySessionCaching1");
StartScannerDiscoveryResponse response1 =
tracker_->StartScannerDiscovery(start_request1);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
run_loop1.Run();
EXPECT_THAT(closed_sessions_, ElementsAre(response1.session_id()));
EXPECT_EQ(discovered_scanners_[response1.session_id()].size(), 1);
sane_client_->RemoveDeviceListing("epson2:libusb:001:001");
// Second discovery session does not find the device.
base::RunLoop run_loop2;
SetQuitClosure(run_loop2.QuitClosure());
StartScannerDiscoveryRequest start_request2;
start_request2.set_client_id("DiscoverySessionCaching2");
StartScannerDiscoveryResponse response2 =
tracker_->StartScannerDiscovery(start_request2);
EXPECT_TRUE(response2.started());
EXPECT_FALSE(response2.session_id().empty());
run_loop2.Run();
EXPECT_THAT(closed_sessions_,
ElementsAre(response1.session_id(), response2.session_id()));
EXPECT_EQ(discovered_scanners_[response2.session_id()].size(), 0);
}
TEST_F(DeviceTrackerTest, DiscoverySessionCachingBlockedIPPUSBDeviceIncluded) {
auto usb_device = MakeIPPUSBDevice("Scanner 1");
CreateIPPUSBSocket(usb_device);
CreateFakeScanner(usb_device.connection_string);
std::vector<std::unique_ptr<UsbDevice>> device_list;
device_list.emplace_back(std::move(usb_device.device));
libusb_->SetDevices(std::move(device_list));
sane_client_->SetListDevicesResult(true);
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery())
.WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// First discovery session finds the device.
base::RunLoop run_loop1;
SetQuitClosure(run_loop1.QuitClosure());
StartScannerDiscoveryRequest start_request1;
start_request1.set_client_id("DiscoverySessionCaching1");
StartScannerDiscoveryResponse response1 =
tracker_->StartScannerDiscovery(start_request1);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
run_loop1.Run();
EXPECT_THAT(closed_sessions_, ElementsAre(response1.session_id()));
EXPECT_EQ(discovered_scanners_[response1.session_id()].size(), 1);
// After removing the SANE device, it can no longer be opened for probing, but
// it is still included in the listing. This is similar to having a device
// opened by another client.
sane_client_->SetDeviceForName(usb_device.connection_string, nullptr);
// Second discovery session finds the device because it reads from the cache.
base::RunLoop run_loop2;
SetQuitClosure(run_loop2.QuitClosure());
StartScannerDiscoveryRequest start_request2;
start_request2.set_client_id("DiscoverySessionCaching2");
StartScannerDiscoveryResponse response2 =
tracker_->StartScannerDiscovery(start_request2);
EXPECT_TRUE(response2.started());
EXPECT_FALSE(response2.session_id().empty());
run_loop2.Run();
EXPECT_THAT(closed_sessions_,
ElementsAre(response1.session_id(), response2.session_id()));
EXPECT_EQ(discovered_scanners_[response2.session_id()].size(), 1);
}
TEST_F(DeviceTrackerTest, DiscoverySessionCachingBlockedSANEDeviceIncluded) {
sane_client_->SetListDevicesResult(true);
CreateFakeScanner("epson2:libusb:001:001", "GoogleTest", "Scanner 1", "USB");
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery())
.WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// First discovery session finds the device.
base::RunLoop run_loop1;
SetQuitClosure(run_loop1.QuitClosure());
StartScannerDiscoveryRequest start_request1;
start_request1.set_client_id("DiscoverySessionCaching1");
StartScannerDiscoveryResponse response1 =
tracker_->StartScannerDiscovery(start_request1);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
run_loop1.Run();
EXPECT_THAT(closed_sessions_, ElementsAre(response1.session_id()));
EXPECT_EQ(discovered_scanners_[response1.session_id()].size(), 1);
// After removing the SANE device, it can no longer be opened for probing, but
// it is still included in the listing. This is similar to having a device
// opened by another client.
sane_client_->SetDeviceForName("epson2:libusb:001:001", nullptr);
// Second discovery session finds the device because it reads from the cache.
base::RunLoop run_loop2;
SetQuitClosure(run_loop2.QuitClosure());
StartScannerDiscoveryRequest start_request2;
start_request2.set_client_id("DiscoverySessionCaching2");
StartScannerDiscoveryResponse response2 =
tracker_->StartScannerDiscovery(start_request2);
EXPECT_TRUE(response2.started());
EXPECT_FALSE(response2.session_id().empty());
run_loop2.Run();
EXPECT_THAT(closed_sessions_,
ElementsAre(response1.session_id(), response2.session_id()));
EXPECT_EQ(discovered_scanners_[response2.session_id()].size(), 1);
}
TEST_F(DeviceTrackerTest, DiscoverySessionClosesOpenScanners) {
sane_client_->SetListDevicesResult(true);
SaneDeviceFake* scanner = CreateFakeScanner("epson2:libusb:001:001",
"GoogleTest", "Scanner 1", "USB");
scanner->SetScanParameters(MakeScanParameters(10, 10));
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery())
.WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// First discovery session finds the device.
base::RunLoop run_loop1;
SetQuitClosure(run_loop1.QuitClosure());
StartScannerDiscoveryRequest start_request;
start_request.set_client_id("DiscoverySessionClosesOpenScanners");
StartScannerDiscoveryResponse discovery_response1 =
tracker_->StartScannerDiscovery(start_request);
EXPECT_TRUE(discovery_response1.started());
EXPECT_FALSE(discovery_response1.session_id().empty());
open_sessions_.insert(discovery_response1.session_id());
run_loop1.Run();
EXPECT_THAT(closed_sessions_, ElementsAre(discovery_response1.session_id()));
EXPECT_EQ(discovered_scanners_[discovery_response1.session_id()].size(), 1);
// Opening the discovered device succeeds.
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string(
"epson2:libusb:001:001");
open_request.set_client_id("DiscoverySessionClosesOpenScanners");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
EXPECT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
ASSERT_TRUE(open_response.has_config());
auto& handle = open_response.config().scanner();
EXPECT_FALSE(handle.token().empty());
// Start a scan. This job should get closed when the second discovery session
// invalidates the handle.
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
EXPECT_THAT(sps_response.scanner(), EqualsProto(sps_request.scanner()));
EXPECT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_TRUE(sps_response.has_job_handle());
// Second discovery session succeeds and invalidates the handle.
base::RunLoop run_loop2;
SetQuitClosure(run_loop2.QuitClosure());
StartScannerDiscoveryResponse discovery_response2 =
tracker_->StartScannerDiscovery(start_request);
EXPECT_TRUE(discovery_response2.started());
EXPECT_FALSE(discovery_response2.session_id().empty());
open_sessions_.insert(discovery_response2.session_id());
run_loop2.Run();
EXPECT_THAT(closed_sessions_, ElementsAre(discovery_response1.session_id(),
discovery_response2.session_id()));
EXPECT_EQ(discovered_scanners_[discovery_response2.session_id()].size(), 1);
// Handle is no longer valid.
GetCurrentConfigRequest config_request;
*config_request.mutable_scanner() = handle;
GetCurrentConfigResponse config_response =
tracker_->GetCurrentConfig(config_request);
EXPECT_THAT(config_response.scanner(), EqualsProto(handle));
EXPECT_NE(config_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_FALSE(config_response.has_config());
// Job is no longer valid.
CancelScanRequest cancel_request;
*cancel_request.mutable_job_handle() = sps_response.job_handle();
CancelScanResponse cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_FALSE(cancel_response.success());
EXPECT_NE(cancel_response.failure_reason(), "");
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_INVALID);
EXPECT_THAT(cancel_response.job_handle(),
EqualsProto(cancel_request.job_handle()));
}
TEST_F(DeviceTrackerTest, DiscoverySessionLocalDevices) {
sane_client_->SetListDevicesResult(true);
// Unique USB device without ippusb support that is added during SANE probing.
CreateFakeScanner(kEpsonDsName, "GoogleTest", "SANE Scanner 4000", "USB");
// Unique non-eSCL network device that is added during SANE probing.
CreateFakeScanner(kEpson2Name, "GoogleTest", "GoogleTest SANE NetScan 4200",
"Network");
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery())
.WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// Session that should get both local and net devices.
StartScannerDiscoveryRequest full_start_request;
full_start_request.set_client_id("full_discovery");
StartScannerDiscoveryResponse full_response =
tracker_->StartScannerDiscovery(full_start_request);
EXPECT_TRUE(full_response.started());
EXPECT_FALSE(full_response.session_id().empty());
open_sessions_.insert(full_response.session_id());
// Session that only gets local devices.
StartScannerDiscoveryRequest local_start_request;
local_start_request.set_client_id("local_discovery");
local_start_request.set_local_only(true);
StartScannerDiscoveryResponse local_response =
tracker_->StartScannerDiscovery(local_start_request);
EXPECT_TRUE(local_response.started());
EXPECT_FALSE(local_response.session_id().empty());
open_sessions_.insert(local_response.session_id());
run_loop_.Run();
ScannerInfo usb_device;
usb_device.set_manufacturer("GoogleTest");
usb_device.set_model("SANE Scanner 4000");
usb_device.set_display_name("GoogleTest SANE Scanner 4000 (USB)");
usb_device.set_connection_type(lorgnette::CONNECTION_USB);
usb_device.set_protocol_type("epsonds");
usb_device.set_secure(true);
ScannerInfo net_device;
net_device.set_manufacturer("GoogleTest");
net_device.set_model("GoogleTest SANE NetScan 4200");
net_device.set_display_name("GoogleTest SANE NetScan 4200");
net_device.set_connection_type(lorgnette::CONNECTION_NETWORK);
net_device.set_protocol_type("epson2");
net_device.set_secure(false);
EXPECT_THAT(discovered_scanners_[full_response.session_id()],
UnorderedElementsAre(MatchesScannerInfo(usb_device),
MatchesScannerInfo(net_device)));
EXPECT_THAT(discovered_scanners_[local_response.session_id()],
ElementsAre(MatchesScannerInfo(usb_device)));
}
TEST_F(DeviceTrackerTest, DiscoverySessionEpsonBackend) {
sane_client_->SetListDevicesResult(true);
// Test the case where a device responds to the epson2 backend during SANE
// discovery but the device really uses the epsonds backend.
sane_client_->AddDeviceListing("epson2:net:127.0.0.1", "GoogleTest",
"GoogleTest SANE NetScan 4200", "Network");
sane_client_->SetDeviceForName("epsonds:net:127.0.0.1",
std::make_unique<SaneDeviceFake>());
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery()).WillOnce([] {
std::vector<PortToken> retval;
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
StartScannerDiscoveryRequest request;
request.set_client_id("discovery");
StartScannerDiscoveryResponse response =
tracker_->StartScannerDiscovery(request);
EXPECT_TRUE(response.started());
EXPECT_FALSE(response.session_id().empty());
open_sessions_.insert(response.session_id());
run_loop_.Run();
ScannerInfo scanner;
// The epsonds should be the name of the device found, not epson2.
scanner.set_name("epsonds:net:127.0.0.1");
scanner.set_manufacturer("GoogleTest");
scanner.set_model("GoogleTest SANE NetScan 4200");
scanner.set_display_name("GoogleTest SANE NetScan 4200");
scanner.set_connection_type(lorgnette::CONNECTION_NETWORK);
scanner.set_protocol_type("epsonds");
scanner.set_secure(false);
EXPECT_THAT(discovered_scanners_[response.session_id()],
UnorderedElementsAre(MatchesScannerInfo(scanner)));
}
// Test that two discovery sessions finding the same devices return the same
// device IDs.
TEST_F(DeviceTrackerTest, DiscoverySessionStableDeviceIds) {
auto ippusb_device1 = MakeIPPUSBDevice("Scanner 1");
ippusb_device1.device->MutableDeviceDescriptor().idProduct = 0x1234;
ippusb_device1.device->Init();
CreateIPPUSBSocket(ippusb_device1);
CreateFakeScanner(ippusb_device1.connection_string);
auto ippusb_device2 = MakeIPPUSBDevice("Scanner 2");
ippusb_device2.device->MutableDeviceDescriptor().idProduct = 0x2345;
ippusb_device2.device->Init();
CreateIPPUSBSocket(ippusb_device2);
CreateFakeScanner(ippusb_device2.connection_string);
std::vector<std::unique_ptr<UsbDevice>> device_list;
device_list.emplace_back(std::move(ippusb_device1.device));
device_list.emplace_back(std::move(ippusb_device2.device));
libusb_->SetDevices(std::move(device_list));
sane_client_->SetListDevicesResult(true);
// Unique device by VID:PID.
CreateFakeScanner("pixma:04A91234_5555", "GoogleTest",
"Unique VIDPID Scanner", "USB");
// Unique device by BUS:DEV.
CreateFakeScanner("epson2:libusb:002:003", "GoogleTest",
"Unique BUSDEV Scanner", "USB");
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery())
.WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// First session finds the scanners. They should have different deviceUuid
// values.
base::RunLoop run_loop1;
SetQuitClosure(run_loop1.QuitClosure());
StartScannerDiscoveryRequest start_request1;
start_request1.set_client_id("DiscoverySessionStableDevicesIds");
StartScannerDiscoveryResponse response1 =
tracker_->StartScannerDiscovery(start_request1);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
open_sessions_.insert(response1.session_id());
run_loop1.Run();
EXPECT_EQ(discovered_scanners_[response1.session_id()].size(), 4);
std::map<std::string, ScannerInfo*>
device_ids; // Device ID to original info.
for (const auto& scanner : discovered_scanners_[response1.session_id()]) {
EXPECT_FALSE(scanner->device_uuid().empty());
EXPECT_FALSE(device_ids.contains(scanner->device_uuid()));
// It's safe to store these raw pointers because the original unique_ptr in
// discovered_scanners_ won't be deleted until after the test ends.
device_ids.emplace(scanner->device_uuid(), scanner.get());
}
// Second session for the same client finds the same scanners with the
// same deviceUuid values.
base::RunLoop run_loop2;
SetQuitClosure(run_loop2.QuitClosure());
StartScannerDiscoveryRequest start_request2;
start_request2.set_client_id("DiscoverySessionStableDevicesIds");
StartScannerDiscoveryResponse response2 =
tracker_->StartScannerDiscovery(start_request2);
EXPECT_TRUE(response2.started());
EXPECT_FALSE(response2.session_id().empty());
open_sessions_.insert(response2.session_id());
run_loop2.Run();
EXPECT_EQ(discovered_scanners_[response2.session_id()].size(), 4);
// Every returned device_uuid should match one of the previously returned
// devices.
for (const auto& scanner : discovered_scanners_[response2.session_id()]) {
EXPECT_FALSE(scanner->device_uuid().empty());
ASSERT_TRUE(device_ids.contains(scanner->device_uuid()));
EXPECT_THAT(scanner,
MatchesScannerInfo(*device_ids.at(scanner->device_uuid())));
}
// Session for a different client finds the same scanners with the same
// deviceUuid values.
base::RunLoop run_loop3;
SetQuitClosure(run_loop3.QuitClosure());
StartScannerDiscoveryRequest start_request3;
start_request3.set_client_id("DiscoverySessionStableDevicesIds2");
StartScannerDiscoveryResponse response3 =
tracker_->StartScannerDiscovery(start_request3);
EXPECT_TRUE(response3.started());
EXPECT_FALSE(response3.session_id().empty());
open_sessions_.insert(response3.session_id());
run_loop3.Run();
EXPECT_EQ(discovered_scanners_[response3.session_id()].size(), 4);
// Every returned device_uuid should match one of the previously returned
// devices.
for (const auto& scanner : discovered_scanners_[response3.session_id()]) {
EXPECT_FALSE(scanner->device_uuid().empty());
ASSERT_TRUE(device_ids.contains(scanner->device_uuid()));
EXPECT_THAT(scanner,
MatchesScannerInfo(*device_ids.at(scanner->device_uuid())));
}
}
// If one client opens a scanner and a second client performs a discovery while
// that scanner is open, the discovery session should return all SANE-discovered
// devices from the cache instead of querying SANE. libusb devices will still
// be discovered as usual.
TEST_F(DeviceTrackerTest, DiscoverySessionWithOpenScanner) {
// This scanner will be discovered by libusb.
auto ippusb_device1 = MakeIPPUSBDevice("Scanner 1");
ippusb_device1.device->MutableDeviceDescriptor().idProduct = 0x1234;
ippusb_device1.device->Init();
CreateIPPUSBSocket(ippusb_device1);
CreateFakeScanner(ippusb_device1.connection_string);
std::vector<std::unique_ptr<UsbDevice>> device_list;
device_list.emplace_back(std::move(ippusb_device1.device));
libusb_->SetDevices(std::move(device_list));
sane_client_->SetListDevicesResult(true);
// This scanner will be discovered by SANE.
CreateFakeScanner("pixma:04A91234_5555", "GoogleTest",
"Unique VIDPID Scanner", "USB");
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery())
.WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// First session finds the scanners.
base::RunLoop run_loop1;
SetQuitClosure(run_loop1.QuitClosure());
StartScannerDiscoveryRequest start_request1;
start_request1.set_client_id("Client1");
StartScannerDiscoveryResponse response1 =
tracker_->StartScannerDiscovery(start_request1);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
open_sessions_.insert(response1.session_id());
run_loop1.Run();
std::set<std::string> device_names1;
for (const auto& scanner : discovered_scanners_[response1.session_id()]) {
device_names1.insert(scanner->name());
}
EXPECT_THAT(device_names1,
UnorderedElementsAre("pixma:04A91234_5555",
ippusb_device1.ippusb_string));
// Open a discovered device.
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string(
ippusb_device1.ippusb_string);
open_request.set_client_id("Client1");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
EXPECT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
ASSERT_TRUE(open_response.has_config());
auto& handle = open_response.config().scanner();
EXPECT_FALSE(handle.token().empty());
// Remove the existing SANE-discovered scanner and add a new one. The second
// discovery will still return the first one (from the cache) and will not
// find the new one.
sane_client_->RemoveDeviceListing("pixma:04A91234_5555");
CreateFakeScanner("epson2:libusb:002:003", "GoogleTest",
"Unique BUSDEV Scanner", "USB");
// Add a new libusb scanner. The second discovery will find this.
auto ippusb_device2 = MakeIPPUSBDevice("Scanner 2");
ippusb_device2.device->MutableDeviceDescriptor().idProduct = 0x2345;
ippusb_device2.device->Init();
CreateIPPUSBSocket(ippusb_device2);
CreateFakeScanner(ippusb_device2.connection_string);
libusb_->AppendDevice(std::move(ippusb_device2.device));
// A discovery session started by a second client.
base::RunLoop run_loop2;
SetQuitClosure(run_loop2.QuitClosure());
StartScannerDiscoveryRequest start_request2;
start_request2.set_client_id("Client2");
StartScannerDiscoveryResponse response2 =
tracker_->StartScannerDiscovery(start_request2);
EXPECT_TRUE(response2.started());
EXPECT_FALSE(response2.session_id().empty());
open_sessions_.insert(response2.session_id());
run_loop2.Run();
std::set<std::string> device_names2;
for (const auto& scanner : discovered_scanners_[response2.session_id()]) {
device_names2.insert(scanner->name());
}
EXPECT_THAT(
device_names2,
UnorderedElementsAre("pixma:04A91234_5555", ippusb_device1.ippusb_string,
ippusb_device2.ippusb_string));
// Close the scanner that was open. After this is closed, the final discovery
// session should return the new SANE-discovered scanners and all the libusb
// scanners.
CloseScannerRequest close_request;
*close_request.mutable_scanner() = handle;
CloseScannerResponse close_response = tracker_->CloseScanner(close_request);
EXPECT_THAT(close_request.scanner(), EqualsProto(handle));
EXPECT_EQ(close_response.result(), OPERATION_RESULT_SUCCESS);
base::RunLoop run_loop3;
SetQuitClosure(run_loop3.QuitClosure());
StartScannerDiscoveryRequest start_request3;
start_request3.set_client_id("Client2");
StartScannerDiscoveryResponse response3 =
tracker_->StartScannerDiscovery(start_request3);
EXPECT_TRUE(response3.started());
EXPECT_FALSE(response3.session_id().empty());
open_sessions_.insert(response3.session_id());
run_loop3.Run();
std::set<std::string> device_names3;
for (const auto& scanner : discovered_scanners_[response3.session_id()]) {
device_names3.insert(scanner->name());
}
EXPECT_THAT(device_names3,
UnorderedElementsAre("epson2:libusb:002:003",
ippusb_device1.ippusb_string,
ippusb_device2.ippusb_string));
}
TEST_F(DeviceTrackerTest, DiscoverySessionCanonicalDeviceIds) {
auto ippusb_device1 = MakeIPPUSBDevice("Scanner 1");
ippusb_device1.device->MutableDeviceDescriptor().idProduct = 0x1234;
ippusb_device1.device->Init();
CreateIPPUSBSocket(ippusb_device1);
CreateFakeScanner(ippusb_device1.connection_string);
auto ippusb_device2 = MakeIPPUSBDevice("Scanner 2");
ippusb_device2.device->MutableDeviceDescriptor().idProduct = 0x2345;
ippusb_device2.device->Init();
CreateIPPUSBSocket(ippusb_device2);
CreateFakeScanner(ippusb_device2.connection_string);
// Duplicate of device 1 by VID:PID. Should have the same ID.
std::string dup_by_vidpid =
base::StringPrintf("pixma:%04X%04X_%s", ippusb_device1.device->GetVid(),
ippusb_device1.device->GetPid(),
ippusb_device1.device->GetSerialNumber().c_str());
CreateFakeScanner(dup_by_vidpid, "GoogleTest", "eSCL Scanner 3001", "eSCL");
// Duplicate of device 1 by BUS:DEV. Should have the same ID.
std::string dup_by_busdev = base::StringPrintf(
"epson2:libusb:%03d:%03d", ippusb_device1.device->GetBusNumber(),
ippusb_device1.device->GetDeviceAddress());
CreateFakeScanner(dup_by_busdev, "GoogleTest", "eSCL Scanner 3002", "eSCL");
// Unique device by VID:PID.
CreateFakeScanner("pixma:04A91234_5555", "GoogleTest",
"Unique VIDPID Scanner", "USB");
// Unique device by BUS:DEV.
CreateFakeScanner("epson2:libusb:002:003", "GoogleTest",
"Unique BUSDEV Scanner", "USB");
std::vector<std::unique_ptr<UsbDevice>> device_list;
device_list.emplace_back(std::move(ippusb_device1.device));
device_list.emplace_back(std::move(ippusb_device2.device));
libusb_->SetDevices(std::move(device_list));
sane_client_->SetListDevicesResult(true);
tracker_->SetCacheDirectoryForTesting(socket_dir_.GetPath());
base::FilePath cache_file = socket_dir_.GetPath().Append("known_devices");
brillo::DeleteFile(cache_file); // Start off with no saved devices.
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery())
.WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// Session finds all the scanners. Three of them should have the same
// device_uuid as device1 and the others should be unique.
base::RunLoop run_loop1;
SetQuitClosure(run_loop1.QuitClosure());
StartScannerDiscoveryRequest start_request1;
start_request1.set_client_id("DiscoverySessionStableDevicesIds");
StartScannerDiscoveryResponse response1 =
tracker_->StartScannerDiscovery(start_request1);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
open_sessions_.insert(response1.session_id());
run_loop1.Run();
std::map<std::string, std::set<std::string>> device_ids1;
std::string ippusb_device_id;
for (const auto& scanner : discovered_scanners_[response1.session_id()]) {
EXPECT_FALSE(scanner->device_uuid().empty());
device_ids1[scanner->device_uuid()].insert(scanner->name());
if (scanner->name() == ippusb_device1.ippusb_string) {
ippusb_device_id = scanner->device_uuid();
}
}
EXPECT_EQ(device_ids1.size(), 4);
EXPECT_THAT(device_ids1[ippusb_device_id],
UnorderedElementsAre(ippusb_device1.ippusb_string, dup_by_vidpid,
dup_by_busdev));
// Saved devices file is non-empty after session.
std::optional<int64_t> file_size = base::GetFileSize(cache_file);
ASSERT_TRUE(file_size.has_value());
EXPECT_GT(file_size.value(), 0);
// Clear saved devices to simulate a lorgnette shutdown. Then the second
// session should produce the same set of devices and IDs because it reloads
// from the cache.
tracker_->ClearKnownDevicesForTesting();
base::RunLoop run_loop2;
SetQuitClosure(run_loop2.QuitClosure());
StartScannerDiscoveryRequest start_request2;
start_request2.set_client_id("DiscoverySessionStableDevicesIds");
StartScannerDiscoveryResponse response2 =
tracker_->StartScannerDiscovery(start_request2);
EXPECT_TRUE(response2.started());
EXPECT_FALSE(response2.session_id().empty());
open_sessions_.insert(response2.session_id());
run_loop2.Run();
std::map<std::string, std::set<std::string>> device_ids2;
for (const auto& scanner : discovered_scanners_[response2.session_id()]) {
EXPECT_FALSE(scanner->device_uuid().empty());
device_ids2[scanner->device_uuid()].insert(scanner->name());
if (scanner->name() == ippusb_device1.ippusb_string) {
EXPECT_EQ(scanner->device_uuid(), ippusb_device_id);
}
}
EXPECT_EQ(device_ids2.size(), 4);
EXPECT_EQ(device_ids2[ippusb_device_id].size(), 3);
for (const auto& [id, names] : device_ids2) {
EXPECT_THAT(device_ids1[id], UnorderedPointwise(Eq(), names));
}
// Clear saved devices to simulate a lorgnette shutdown and remove the cache.
// Then the third session should produce a set of results with the same
// structure as the first session, but new IDs.
tracker_->ClearKnownDevicesForTesting();
brillo::DeleteFile(cache_file);
base::RunLoop run_loop3;
SetQuitClosure(run_loop3.QuitClosure());
StartScannerDiscoveryRequest start_request3;
start_request3.set_client_id("DiscoverySessionStableDevicesIds");
StartScannerDiscoveryResponse response3 =
tracker_->StartScannerDiscovery(start_request3);
EXPECT_TRUE(response3.started());
EXPECT_FALSE(response3.session_id().empty());
open_sessions_.insert(response3.session_id());
run_loop3.Run();
std::map<std::string, std::set<std::string>> device_ids3;
for (const auto& scanner : discovered_scanners_[response3.session_id()]) {
EXPECT_FALSE(scanner->device_uuid().empty());
device_ids3[scanner->device_uuid()].insert(scanner->name());
if (scanner->name() == ippusb_device1.ippusb_string) {
ippusb_device_id = scanner->device_uuid();
}
}
EXPECT_EQ(device_ids3.size(), 4);
EXPECT_THAT(device_ids3[ippusb_device_id],
UnorderedElementsAre(ippusb_device1.ippusb_string, dup_by_vidpid,
dup_by_busdev));
for (const auto& [id, names] : device_ids3) {
EXPECT_FALSE(base::Contains(device_ids1, id));
}
}
TEST_F(DeviceTrackerTest, DiscoverySessionCachedDevicePreferred) {
// Test the case with one unique device and a duplicate device. The first
// discovery session will return both devices. The second discovery session
// sets `preferred_only` to true so should just return the ippusb device (from
// the cache).
auto ippusb_device1 = MakeIPPUSBDevice("Scanner 1");
ippusb_device1.device->MutableDeviceDescriptor().idProduct = 0x1234;
ippusb_device1.device->Init();
CreateIPPUSBSocket(ippusb_device1);
CreateFakeScanner(ippusb_device1.connection_string);
// Duplicate of device 1 by VID:PID. Should have the same ID.
std::string dup_by_vidpid =
base::StringPrintf("pixma:%04X%04X_%s", ippusb_device1.device->GetVid(),
ippusb_device1.device->GetPid(),
ippusb_device1.device->GetSerialNumber().c_str());
CreateFakeScanner(dup_by_vidpid, "GoogleTest", "eSCL Scanner 3001", "eSCL");
std::vector<std::unique_ptr<UsbDevice>> device_list;
device_list.emplace_back(std::move(ippusb_device1.device));
libusb_->SetDevices(std::move(device_list));
sane_client_->SetListDevicesResult(true);
tracker_->SetCacheDirectoryForTesting(socket_dir_.GetPath());
base::FilePath cache_file = socket_dir_.GetPath().Append("known_devices");
brillo::DeleteFile(cache_file); // Start off with no saved devices.
EXPECT_CALL(*firewall_manager_, RequestPortsForDiscovery())
.WillRepeatedly([] {
std::vector<PortToken> retval;
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/8612));
retval.emplace_back(
PortToken(/*firewall_manager=*/nullptr, /*port=*/1865));
return retval;
});
// Session finds both scanners.
base::RunLoop run_loop1;
SetQuitClosure(run_loop1.QuitClosure());
StartScannerDiscoveryRequest start_request1;
start_request1.set_preferred_only(false);
start_request1.set_client_id("discovery");
StartScannerDiscoveryResponse response1 =
tracker_->StartScannerDiscovery(start_request1);
EXPECT_TRUE(response1.started());
EXPECT_FALSE(response1.session_id().empty());
open_sessions_.insert(response1.session_id());
run_loop1.Run();
std::map<std::string, std::set<std::string>> device_ids1;
std::string ippusb_device_id;
for (const auto& scanner : discovered_scanners_[response1.session_id()]) {
EXPECT_FALSE(scanner->device_uuid().empty());
device_ids1[scanner->device_uuid()].insert(scanner->name());
if (scanner->name() == ippusb_device1.ippusb_string) {
ippusb_device_id = scanner->device_uuid();
}
}
ASSERT_EQ(device_ids1.count(ippusb_device_id), 1);
EXPECT_THAT(
device_ids1[ippusb_device_id],
UnorderedElementsAre(ippusb_device1.ippusb_string, dup_by_vidpid));
// Cache file is non-empty after session.
std::optional<int64_t> file_size = base::GetFileSize(cache_file);
ASSERT_TRUE(file_size.has_value());
EXPECT_GT(file_size.value(), 0);
// Clear saved devices to simulate a lorgnette shutdown. The second session
// returns the device from the cache and only returns the ippusb device since
// `preferred_only` is set to true.
tracker_->ClearKnownDevicesForTesting();
base::RunLoop run_loop2;
SetQuitClosure(run_loop2.QuitClosure());
StartScannerDiscoveryRequest start_request2;
start_request2.set_preferred_only(true);
start_request2.set_client_id("discovery");
StartScannerDiscoveryResponse response2 =
tracker_->StartScannerDiscovery(start_request2);
EXPECT_TRUE(response2.started());
EXPECT_FALSE(response2.session_id().empty());
open_sessions_.insert(response2.session_id());
run_loop2.Run();
ASSERT_EQ(discovered_scanners_[response2.session_id()].size(), 1);
const std::unique_ptr<ScannerInfo>& scanner_info =
*discovered_scanners_[response2.session_id()].begin();
EXPECT_EQ(scanner_info->device_uuid(), ippusb_device_id);
EXPECT_EQ(scanner_info->name(), ippusb_device1.ippusb_string);
}
TEST_F(DeviceTrackerTest, OpenScannerEmptyDevice) {
OpenScannerRequest request;
request.set_client_id("DeviceTrackerTest");
auto response = tracker_->OpenScanner(request);
EXPECT_THAT(response.scanner_id(), EqualsProto(request.scanner_id()));
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
}
TEST_F(DeviceTrackerTest, OpenScannerEmptyString) {
OpenScannerRequest request;
request.mutable_scanner_id()->set_connection_string("Test");
OpenScannerResponse response = tracker_->OpenScanner(request);
EXPECT_THAT(response.scanner_id(), EqualsProto(request.scanner_id()));
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
}
TEST_F(DeviceTrackerTest, OpenScannerNoDevice) {
OpenScannerRequest request;
request.mutable_scanner_id()->set_connection_string("Test");
request.set_client_id("DeviceTrackerTest");
OpenScannerResponse response = tracker_->OpenScanner(request);
EXPECT_THAT(response.scanner_id(), EqualsProto(request.scanner_id()));
EXPECT_NE(response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
}
TEST_F(DeviceTrackerTest, OpenScannerFirstClientSucceeds) {
CreateFakeScanner("Test");
OpenScannerRequest request;
request.mutable_scanner_id()->set_connection_string("Test");
request.set_client_id("DeviceTrackerTest");
OpenScannerResponse response = tracker_->OpenScanner(request);
EXPECT_THAT(response.scanner_id(), EqualsProto(request.scanner_id()));
EXPECT_EQ(response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(response.config().scanner(), Not(EqualsProto(ScannerHandle())));
EXPECT_EQ(tracker_->NumOpenScanners(), 1);
}
TEST_F(DeviceTrackerTest, OpenScannerSameClientSucceedsTwice) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
OpenScannerRequest request;
request.mutable_scanner_id()->set_connection_string("Test");
request.set_client_id("DeviceTrackerTest");
OpenScannerResponse response1 = tracker_->OpenScanner(request);
// Start a scan on the first handle to check that the job gets closed when the
// scanner gets opened a second time.
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = response1.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
EXPECT_THAT(sps_response.scanner(), EqualsProto(sps_request.scanner()));
EXPECT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_TRUE(sps_response.has_job_handle());
OpenScannerResponse response2 = tracker_->OpenScanner(request);
// Cancelling this job should fail since it was implicitly cancelled in the
// second open request.
CancelScanRequest cancel_request;
*cancel_request.mutable_job_handle() = sps_response.job_handle();
CancelScanResponse cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_FALSE(cancel_response.success());
EXPECT_NE(cancel_response.failure_reason(), "");
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_INVALID);
EXPECT_THAT(cancel_response.job_handle(),
EqualsProto(cancel_request.job_handle()));
EXPECT_THAT(response1.scanner_id(), EqualsProto(request.scanner_id()));
EXPECT_EQ(response1.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(response1.config().scanner(), Not(EqualsProto(ScannerHandle())));
EXPECT_THAT(response2.scanner_id(), EqualsProto(request.scanner_id()));
EXPECT_EQ(response2.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(response2.config().scanner(), Not(EqualsProto(ScannerHandle())));
EXPECT_THAT(response2.config().scanner(),
Not(EqualsProto(response1.config().scanner())));
EXPECT_EQ(tracker_->NumOpenScanners(), 1);
}
TEST_F(DeviceTrackerTest, OpenScannerSecondClientFails) {
CreateFakeScanner("Test");
OpenScannerRequest request;
request.mutable_scanner_id()->set_connection_string("Test");
request.set_client_id("DeviceTrackerTest");
OpenScannerResponse response1 = tracker_->OpenScanner(request);
// Re-insert the test device because the fake SANE client deletes it after one
// connection.
auto scanner2 = std::make_unique<SaneDeviceFake>();
sane_client_->SetDeviceForName("Test", std::move(scanner2));
request.set_client_id("DeviceTrackerTest2");
OpenScannerResponse response2 = tracker_->OpenScanner(request);
EXPECT_THAT(response1.scanner_id(), EqualsProto(request.scanner_id()));
EXPECT_EQ(response1.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(response1.config().scanner(), Not(EqualsProto(ScannerHandle())));
EXPECT_THAT(response2.scanner_id(), EqualsProto(request.scanner_id()));
EXPECT_EQ(response2.result(), OPERATION_RESULT_DEVICE_BUSY);
EXPECT_THAT(response2.config().scanner(), EqualsProto(ScannerHandle()));
EXPECT_EQ(tracker_->NumOpenScanners(), 1);
}
TEST_F(DeviceTrackerTest, OpenScannerThatRequresFirewall) {
const std::string connection_string = "pixma:MF2600_1.2.3.4";
CreateFakeScanner(connection_string, "GoogleTest", "Unique VIDPID Scanner",
"USB");
// Since this is a pixma scanner, the port should get requested when the
// scanner is opened.
EXPECT_CALL(*firewall_manager_, RequestUdpPortAccess(8612))
.WillOnce(testing::Return(
PortToken(firewall_manager_->GetWeakPtrForTesting(), /*port=*/8612)));
OpenScannerRequest request1;
request1.mutable_scanner_id()->set_connection_string(connection_string);
request1.set_client_id("DeviceTrackerTest");
OpenScannerResponse response1 = tracker_->OpenScanner(request1);
EXPECT_EQ(tracker_->NumOpenScanners(), 1);
// The request to close the port should happen when the scanner is closed.
EXPECT_CALL(*firewall_manager_, ReleaseUdpPortAccess(8612))
.WillOnce(testing::Return());
CloseScannerRequest request2;
*request2.mutable_scanner() = response1.config().scanner();
CloseScannerResponse response2 = tracker_->CloseScanner(request2);
EXPECT_THAT(request2.scanner(), EqualsProto(response2.scanner()));
EXPECT_EQ(response2.result(), OPERATION_RESULT_SUCCESS);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
}
TEST_F(DeviceTrackerTest, OpenScannerThatRequresFirewallFails) {
// Test the case where the scanner to be opened requires an open firewall
// port, but connecting to the scanner fails. Make sure the port gets
// released even though CloseScanner is not called.
const std::string connection_string = "pixma:MF2600_1.2.3.4";
EXPECT_CALL(*firewall_manager_, RequestUdpPortAccess(8612))
.WillOnce(testing::Return(
PortToken(firewall_manager_->GetWeakPtrForTesting(), /*port=*/8612)));
EXPECT_CALL(*firewall_manager_, ReleaseUdpPortAccess(8612))
.WillOnce(testing::Return());
OpenScannerRequest request1;
request1.mutable_scanner_id()->set_connection_string(connection_string);
request1.set_client_id("DeviceTrackerTest");
OpenScannerResponse response1 = tracker_->OpenScanner(request1);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
}
TEST_F(DeviceTrackerTest, CloseScannerMissingHandle) {
CloseScannerRequest request;
CloseScannerResponse response = tracker_->CloseScanner(request);
EXPECT_THAT(request.scanner(), EqualsProto(response.scanner()));
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
}
TEST_F(DeviceTrackerTest, CloseScannerInvalidHandle) {
CloseScannerRequest request;
request.mutable_scanner()->set_token("NoSuchScanner");
CloseScannerResponse response = tracker_->CloseScanner(request);
EXPECT_THAT(request.scanner(), EqualsProto(response.scanner()));
EXPECT_EQ(response.result(), OPERATION_RESULT_MISSING);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
}
TEST_F(DeviceTrackerTest, CloseScannerSuccess) {
CreateFakeScanner("Test");
OpenScannerRequest request1;
request1.mutable_scanner_id()->set_connection_string("Test");
request1.set_client_id("DeviceTrackerTest");
OpenScannerResponse response1 = tracker_->OpenScanner(request1);
EXPECT_EQ(tracker_->NumOpenScanners(), 1);
CloseScannerRequest request2;
*request2.mutable_scanner() = response1.config().scanner();
CloseScannerResponse response2 = tracker_->CloseScanner(request2);
EXPECT_THAT(request2.scanner(), EqualsProto(response2.scanner()));
EXPECT_EQ(response2.result(), OPERATION_RESULT_SUCCESS);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
}
TEST_F(DeviceTrackerTest, CloseScannerTwiceFails) {
CreateFakeScanner("Test");
OpenScannerRequest request1;
request1.mutable_scanner_id()->set_connection_string("Test");
request1.set_client_id("DeviceTrackerTest");
OpenScannerResponse response1 = tracker_->OpenScanner(request1);
EXPECT_EQ(tracker_->NumOpenScanners(), 1);
CloseScannerRequest request2;
*request2.mutable_scanner() = response1.config().scanner();
CloseScannerResponse response2 = tracker_->CloseScanner(request2);
CloseScannerResponse response3 = tracker_->CloseScanner(request2);
EXPECT_THAT(request2.scanner(), EqualsProto(response2.scanner()));
EXPECT_EQ(response2.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(request2.scanner(), EqualsProto(response3.scanner()));
EXPECT_EQ(response3.result(), OPERATION_RESULT_MISSING);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
}
TEST_F(DeviceTrackerTest, CloseScannerFreesDevice) {
CreateFakeScanner("Test");
// First client succeeds.
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse response1 = tracker_->OpenScanner(open_request);
EXPECT_EQ(tracker_->NumOpenScanners(), 1);
// This will fail because the device is still open.
open_request.set_client_id("DeviceTrackerTest2");
OpenScannerResponse response2 = tracker_->OpenScanner(open_request);
EXPECT_EQ(tracker_->NumOpenScanners(), 1);
// Close first client's handle to free up the device.
CloseScannerRequest close_request;
*close_request.mutable_scanner() = response1.config().scanner();
CloseScannerResponse response3 = tracker_->CloseScanner(close_request);
EXPECT_EQ(tracker_->NumOpenScanners(), 0);
// Now the second client can open the device.
OpenScannerResponse response4 = tracker_->OpenScanner(open_request);
EXPECT_EQ(tracker_->NumOpenScanners(), 1);
EXPECT_THAT(response1.scanner_id(), EqualsProto(open_request.scanner_id()));
EXPECT_EQ(response1.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(response1.config().scanner(), Not(EqualsProto(ScannerHandle())));
EXPECT_THAT(response2.scanner_id(), EqualsProto(open_request.scanner_id()));
EXPECT_EQ(response2.result(), OPERATION_RESULT_DEVICE_BUSY);
EXPECT_THAT(response2.config().scanner(), EqualsProto(ScannerHandle()));
EXPECT_THAT(response3.scanner(), EqualsProto(close_request.scanner()));
EXPECT_EQ(response3.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(response4.scanner_id(), EqualsProto(open_request.scanner_id()));
EXPECT_EQ(response4.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(response4.config().scanner(), Not(EqualsProto(ScannerHandle())));
EXPECT_THAT(response4.config().scanner(),
Not(EqualsProto(response1.config().scanner())));
}
TEST_F(DeviceTrackerTest, SetOptionsMissingHandleFails) {
SetOptionsRequest request;
request.add_options()->set_name("option");
SetOptionsResponse response = tracker_->SetOptions(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
ASSERT_TRUE(response.results().contains("option"));
EXPECT_EQ(response.results().at("option"), OPERATION_RESULT_INVALID);
EXPECT_FALSE(response.has_config());
}
TEST_F(DeviceTrackerTest, SetOptionsEmptyHandleFails) {
SetOptionsRequest request;
request.mutable_scanner()->set_token("");
request.add_options()->set_name("option");
SetOptionsResponse response = tracker_->SetOptions(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
ASSERT_TRUE(response.results().contains("option"));
EXPECT_EQ(response.results().at("option"), OPERATION_RESULT_INVALID);
EXPECT_FALSE(response.has_config());
}
TEST_F(DeviceTrackerTest, SetOptionsInvalidHandleFails) {
SetOptionsRequest request;
request.mutable_scanner()->set_token("NoSuchScanner");
request.add_options()->set_name("option");
SetOptionsResponse response = tracker_->SetOptions(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
ASSERT_TRUE(response.results().contains("option"));
EXPECT_EQ(response.results().at("option"), OPERATION_RESULT_MISSING);
EXPECT_FALSE(response.has_config());
}
TEST_F(DeviceTrackerTest, SetOptionsScannerConfigFails) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
scanner->SetScannerConfig(std::nullopt);
SetOptionsRequest set_request;
*set_request.mutable_scanner() = open_response.config().scanner();
set_request.add_options()->set_name("option");
SetOptionsResponse set_response = tracker_->SetOptions(set_request);
EXPECT_THAT(set_response.scanner(), EqualsProto(set_request.scanner()));
ASSERT_TRUE(set_response.results().contains("option"));
EXPECT_EQ(set_response.results().at("option"),
OPERATION_RESULT_INTERNAL_ERROR);
EXPECT_FALSE(set_response.has_config());
}
TEST_F(DeviceTrackerTest, SetOptionsAllOptionsAttempted) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetOptionStatus("option1", SANE_STATUS_INVAL);
scanner->SetOptionStatus("option2", SANE_STATUS_GOOD);
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
SetOptionsRequest set_request;
*set_request.mutable_scanner() = open_response.config().scanner();
set_request.add_options()->set_name("option1");
set_request.add_options()->set_name("option2");
SetOptionsResponse set_response = tracker_->SetOptions(set_request);
EXPECT_THAT(set_response.scanner(), EqualsProto(set_request.scanner()));
ASSERT_TRUE(set_response.results().contains("option1"));
EXPECT_EQ(set_response.results().at("option1"), OPERATION_RESULT_INVALID);
ASSERT_TRUE(set_response.results().contains("option2"));
EXPECT_EQ(set_response.results().at("option2"), OPERATION_RESULT_SUCCESS);
EXPECT_TRUE(set_response.has_config());
EXPECT_THAT(set_response.config().scanner(),
EqualsProto(set_request.scanner()));
}
TEST_F(DeviceTrackerTest, GetCurrentConfigMissingHandleFails) {
GetCurrentConfigRequest request;
GetCurrentConfigResponse response = tracker_->GetCurrentConfig(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_FALSE(response.has_config());
}
TEST_F(DeviceTrackerTest, GetCurrentConfigEmptyHandleFails) {
GetCurrentConfigRequest request;
request.mutable_scanner()->set_token("");
GetCurrentConfigResponse response = tracker_->GetCurrentConfig(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_FALSE(response.has_config());
}
TEST_F(DeviceTrackerTest, GetCurrentConfigInvalidHandleFails) {
GetCurrentConfigRequest request;
request.mutable_scanner()->set_token("NoSuchScanner");
GetCurrentConfigResponse response = tracker_->GetCurrentConfig(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
EXPECT_EQ(response.result(), OPERATION_RESULT_MISSING);
EXPECT_FALSE(response.has_config());
}
TEST_F(DeviceTrackerTest, GetCurrentConfigScannerConfigFails) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
scanner->SetScannerConfig(std::nullopt);
GetCurrentConfigRequest request;
*request.mutable_scanner() = open_response.config().scanner();
GetCurrentConfigResponse response = tracker_->GetCurrentConfig(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
EXPECT_EQ(response.result(), OPERATION_RESULT_INTERNAL_ERROR);
EXPECT_FALSE(response.has_config());
}
TEST_F(DeviceTrackerTest, GetCurrentConfig) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
ScannerConfig config;
*config.mutable_scanner() = open_response.config().scanner();
ScannerOption option;
option.set_name("int-option");
option.set_option_type(OptionType::TYPE_INT);
option.mutable_int_value()->add_value(24);
(*config.mutable_options())["name"] = option;
OptionGroup* group = config.add_option_groups();
group->set_title("option-1");
group->add_members("value-1");
group->add_members("value-2");
group = config.add_option_groups();
group->set_title("option-2");
group->add_members("another-one");
scanner->SetScannerConfig(config);
GetCurrentConfigRequest get_request;
*get_request.mutable_scanner() = open_response.config().scanner();
GetCurrentConfigResponse get_response =
tracker_->GetCurrentConfig(get_request);
EXPECT_THAT(get_response.scanner(), EqualsProto(get_request.scanner()));
EXPECT_EQ(get_response.result(), OPERATION_RESULT_SUCCESS);
ASSERT_TRUE(get_response.has_config());
EXPECT_THAT(get_response.config().scanner(), EqualsProto(config.scanner()));
auto it = get_response.config().options().find("name");
ASSERT_TRUE(it != get_response.config().options().end());
EXPECT_THAT(it->second, EqualsProto(option));
ASSERT_EQ(get_response.config().option_groups_size(),
config.option_groups_size());
for (int i = 0; i < get_response.config().option_groups_size(); ++i) {
EXPECT_THAT(get_response.config().option_groups(i),
EqualsProto(config.option_groups(i)));
}
}
TEST_F(DeviceTrackerTest, StartPreparedScanMissingHandleFails) {
StartPreparedScanRequest request;
StartPreparedScanResponse response = tracker_->StartPreparedScan(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_FALSE(response.has_job_handle());
}
TEST_F(DeviceTrackerTest, StartPreparedScanEmptyHandleFails) {
StartPreparedScanRequest request;
request.mutable_scanner()->set_token("");
StartPreparedScanResponse response = tracker_->StartPreparedScan(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_FALSE(response.has_job_handle());
}
TEST_F(DeviceTrackerTest, StartPreparedScanInvalidHandleFails) {
StartPreparedScanRequest request;
request.mutable_scanner()->set_token("NoSuchScanner");
StartPreparedScanResponse response = tracker_->StartPreparedScan(request);
EXPECT_THAT(response.scanner(), EqualsProto(request.scanner()));
EXPECT_EQ(response.result(), OPERATION_RESULT_MISSING);
EXPECT_FALSE(response.has_job_handle());
}
TEST_F(DeviceTrackerTest, StartPreparedScanMissingImageFormatFails) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
EXPECT_THAT(sps_response.scanner(), EqualsProto(sps_request.scanner()));
EXPECT_EQ(sps_response.result(), OPERATION_RESULT_INVALID);
EXPECT_FALSE(sps_response.has_job_handle());
}
TEST_F(DeviceTrackerTest, StartPreparedScanInsufficientMaxReadFails) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
// Zero-size buffer explicitly not allowed.
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
sps_request.set_max_read_size(0);
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
EXPECT_THAT(sps_response.scanner(), EqualsProto(sps_request.scanner()));
EXPECT_EQ(sps_response.result(), OPERATION_RESULT_INVALID);
EXPECT_FALSE(sps_response.has_job_handle());
// Anything smaller than 32KB not allowed.
sps_request.set_max_read_size(32 * 1024 - 1);
sps_response = tracker_->StartPreparedScan(sps_request);
EXPECT_THAT(sps_response.scanner(), EqualsProto(sps_request.scanner()));
EXPECT_EQ(sps_response.result(), OPERATION_RESULT_INVALID);
EXPECT_FALSE(sps_response.has_job_handle());
}
TEST_F(DeviceTrackerTest, StartPreparedScanDeviceStartFails) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
scanner->SetStartScanResult(SANE_STATUS_JAMMED);
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
EXPECT_THAT(sps_response.scanner(), EqualsProto(sps_request.scanner()));
EXPECT_EQ(sps_response.result(), OPERATION_RESULT_ADF_JAMMED);
EXPECT_FALSE(sps_response.has_job_handle());
}
TEST_F(DeviceTrackerTest, StartPreparedScanDeviceMissingJob) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
scanner->SetCallStartJob(false);
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
EXPECT_THAT(sps_response.scanner(), EqualsProto(sps_request.scanner()));
EXPECT_EQ(sps_response.result(), OPERATION_RESULT_INTERNAL_ERROR);
EXPECT_FALSE(sps_response.has_job_handle());
}
TEST_F(DeviceTrackerTest, StartPreparedScanFailsWithoutImageReader) {
CreateFakeScanner("Test");
// No parameters set, so image reader creation will fail.
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
EXPECT_THAT(sps_response.scanner(), EqualsProto(sps_request.scanner()));
EXPECT_NE(sps_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_FALSE(sps_response.has_job_handle());
}
TEST_F(DeviceTrackerTest, StartPreparedScanCreatesJob) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
EXPECT_THAT(sps_response.scanner(), EqualsProto(sps_request.scanner()));
EXPECT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_TRUE(sps_response.has_job_handle());
}
// CancelScan with a scan_uuid is supposed to be handled by Manager::CancelScan,
// not DeviceTracker::CancelScan.
TEST_F(DeviceTrackerTest, CancelScanByUuidIsBlocked) {
CancelScanRequest request;
request.set_scan_uuid("12345");
ASSERT_DEATH(tracker_->CancelScan(std::move(request)), "Manager::CancelScan");
}
TEST_F(DeviceTrackerTest, CancelScanRequiresJobHandle) {
CancelScanRequest request;
request.mutable_job_handle()->set_token("");
CancelScanResponse response = tracker_->CancelScan(request);
EXPECT_FALSE(response.success());
EXPECT_NE(response.failure_reason(), "");
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_THAT(response.job_handle(), EqualsProto(request.job_handle()));
}
TEST_F(DeviceTrackerTest, CancelScanInvalidJobHandle) {
CancelScanRequest request;
request.mutable_job_handle()->set_token("bad_handle");
request.set_scan_uuid("bad_uuid");
CancelScanResponse response = tracker_->CancelScan(request);
EXPECT_FALSE(response.success());
EXPECT_THAT(response.failure_reason(), HasSubstr("bad_handle"));
EXPECT_THAT(response.failure_reason(), Not(HasSubstr("bad_uuid")));
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_THAT(response.job_handle(), EqualsProto(request.job_handle()));
}
TEST_F(DeviceTrackerTest, CloseScannerCancelsJob) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
ASSERT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_TRUE(sps_response.has_job_handle());
// Close device, which should also cancel the job.
CloseScannerRequest close_request;
*close_request.mutable_scanner() = open_response.config().scanner();
CloseScannerResponse close_response = tracker_->CloseScanner(close_request);
ASSERT_EQ(close_response.result(), OPERATION_RESULT_SUCCESS);
CancelScanRequest cancel_request;
*cancel_request.mutable_job_handle() = sps_response.job_handle();
CancelScanResponse cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_FALSE(cancel_response.success());
EXPECT_NE(cancel_response.failure_reason(), "");
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_INVALID);
EXPECT_THAT(cancel_response.job_handle(),
EqualsProto(cancel_request.job_handle()));
}
TEST_F(DeviceTrackerTest, CancelScanOnCompletedJobSucceeds) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request1;
*sps_request1.mutable_scanner() = open_response.config().scanner();
sps_request1.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response1 =
tracker_->StartPreparedScan(sps_request1);
ASSERT_EQ(sps_response1.result(), OPERATION_RESULT_SUCCESS);
// Simulate finishing the first job by clearing it out.
scanner->ClearScanJob();
// Cancelling original job should succeed because no new job has started.
CancelScanRequest cancel_request;
*cancel_request.mutable_job_handle() = sps_response1.job_handle();
CancelScanResponse cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_TRUE(cancel_response.success());
EXPECT_EQ(cancel_response.failure_reason(), "");
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(cancel_response.job_handle(),
EqualsProto(cancel_request.job_handle()));
}
TEST_F(DeviceTrackerTest, CancelScanNotCurrentJobFails) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request1;
*sps_request1.mutable_scanner() = open_response.config().scanner();
sps_request1.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response1 =
tracker_->StartPreparedScan(sps_request1);
ASSERT_EQ(sps_response1.result(), OPERATION_RESULT_SUCCESS);
// Simulate finishing the first job by clearing it out.
scanner->ClearScanJob();
StartPreparedScanRequest sps_request2;
*sps_request2.mutable_scanner() = open_response.config().scanner();
sps_request2.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response2 =
tracker_->StartPreparedScan(sps_request2);
ASSERT_EQ(sps_response2.result(), OPERATION_RESULT_SUCCESS);
// Original job handle is not valid because a new job has started.
CancelScanRequest cancel_request;
*cancel_request.mutable_job_handle() = sps_response1.job_handle();
CancelScanResponse cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_FALSE(cancel_response.success());
EXPECT_NE(cancel_response.failure_reason(), "");
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_INVALID);
EXPECT_THAT(cancel_response.job_handle(),
EqualsProto(cancel_request.job_handle()));
// Cancelling second/current job should still succeed.
*cancel_request.mutable_job_handle() = sps_response2.job_handle();
cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_TRUE(cancel_response.success());
EXPECT_EQ(cancel_response.failure_reason(), "");
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(cancel_response.job_handle(),
EqualsProto(cancel_request.job_handle()));
}
TEST_F(DeviceTrackerTest, CancelScanDeviceCancelFails) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
scanner->SetCancelScanResult(false);
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
ASSERT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
CancelScanRequest cancel_request;
*cancel_request.mutable_job_handle() = sps_response.job_handle();
CancelScanResponse cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_FALSE(cancel_response.success());
EXPECT_NE(cancel_response.failure_reason(), "");
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_INTERNAL_ERROR);
EXPECT_THAT(cancel_response.job_handle(),
EqualsProto(cancel_request.job_handle()));
}
TEST_F(DeviceTrackerTest, CancelScanNoErrors) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
ASSERT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
CancelScanRequest cancel_request;
*cancel_request.mutable_job_handle() = sps_response.job_handle();
CancelScanResponse cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_TRUE(cancel_response.success());
EXPECT_EQ(cancel_response.failure_reason(), "");
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(cancel_response.job_handle(),
EqualsProto(cancel_request.job_handle()));
// Reading from the job handle reports cancellation.
ReadScanDataRequest read_request;
*read_request.mutable_job_handle() = sps_response.job_handle();
ReadScanDataResponse read_response = tracker_->ReadScanData(read_request);
EXPECT_EQ(read_response.result(), OPERATION_RESULT_CANCELLED);
// Trying to cancel again succeeds.
cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_TRUE(cancel_response.success());
EXPECT_EQ(cancel_response.failure_reason(), "");
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(cancel_response.job_handle(),
EqualsProto(cancel_request.job_handle()));
}
TEST_F(DeviceTrackerTest, ReadScanDataRequiresJobHandle) {
ReadScanDataRequest request;
request.mutable_job_handle()->set_token("");
ReadScanDataResponse response = tracker_->ReadScanData(request);
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_THAT(response.job_handle(), EqualsProto(request.job_handle()));
EXPECT_FALSE(response.has_data());
EXPECT_FALSE(response.has_estimated_completion());
}
TEST_F(DeviceTrackerTest, ReadScanDataFailsForInvalidJob) {
ReadScanDataRequest request;
request.mutable_job_handle()->set_token("no_job");
ReadScanDataResponse response = tracker_->ReadScanData(request);
EXPECT_EQ(response.result(), OPERATION_RESULT_INVALID);
EXPECT_THAT(response.job_handle(), EqualsProto(request.job_handle()));
EXPECT_FALSE(response.has_data());
EXPECT_FALSE(response.has_estimated_completion());
}
TEST_F(DeviceTrackerTest, ReadScanDataFailsForClosedScanner) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
ASSERT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
// Close device, which cancels the job.
CloseScannerRequest close_request;
*close_request.mutable_scanner() = open_response.config().scanner();
CloseScannerResponse close_response = tracker_->CloseScanner(close_request);
ASSERT_EQ(close_response.result(), OPERATION_RESULT_SUCCESS);
ReadScanDataRequest rsd_request;
*rsd_request.mutable_job_handle() = sps_response.job_handle();
ReadScanDataResponse rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_INVALID);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_FALSE(rsd_response.has_data());
EXPECT_FALSE(rsd_response.has_estimated_completion());
}
TEST_F(DeviceTrackerTest, ReadScanDataFailsForBadRead) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(10, 10));
scanner->SetReadScanDataResult(SANE_STATUS_NO_DOCS);
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/jpeg");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
ASSERT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
ReadScanDataRequest rsd_request;
*rsd_request.mutable_job_handle() = sps_response.job_handle();
ReadScanDataResponse rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_ADF_EMPTY);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_FALSE(rsd_response.has_data());
EXPECT_FALSE(rsd_response.has_estimated_completion());
}
TEST_F(DeviceTrackerTest, ReadScanDataSuccess) {
const size_t kExpectedImageSize = 130;
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(100, 100));
std::vector<uint8_t> page1(3 * 100 * 100, 0);
scanner->SetScanData({page1});
scanner->SetMaxReadSize(3 * 100 * 60 + 5); // 60 lines plus leftover.
scanner->SetInitialEmptyReads(2);
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/png");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
ASSERT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
size_t total_read = 0;
// First request will read nothing, but header data is available.
ReadScanDataRequest rsd_request;
*rsd_request.mutable_job_handle() = sps_response.job_handle();
ReadScanDataResponse rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_EQ(rsd_response.data().length(), 54); // Magic + header chunks.
EXPECT_EQ(rsd_response.estimated_completion(), 0);
total_read += rsd_response.data().size();
// Second request will read nothing.
rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_EQ(rsd_response.data().size(), 0);
EXPECT_EQ(rsd_response.estimated_completion(), 0);
total_read += rsd_response.data().size();
// Next request will read 60 full lines, but the encoder might not write them
// yet.
rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_TRUE(rsd_response.has_data());
EXPECT_EQ(rsd_response.estimated_completion(), 60);
total_read += rsd_response.data().size();
// Next request will read the remaining data.
rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_GT(rsd_response.data().length(), 9); // IDAT + data.
EXPECT_EQ(rsd_response.estimated_completion(), 100);
total_read += rsd_response.data().size();
// Last request will get EOF plus the IEND chunk.
rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_EOF);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_EQ(rsd_response.data().length(), 12); // IEND.
EXPECT_EQ(rsd_response.estimated_completion(), 100);
total_read += rsd_response.data().size();
EXPECT_EQ(total_read, kExpectedImageSize);
}
TEST_F(DeviceTrackerTest, ReadScanDataSmallReadsSuccess) {
const size_t kExpectedImageSize = 130;
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(100, 100));
std::vector<uint8_t> page1(3 * 100 * 100, 0);
scanner->SetScanData({page1});
scanner->SetMaxReadSize(3 * 100 * 60 + 5); // 60 lines plus leftover.
scanner->SetInitialEmptyReads(2);
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/png");
sps_request.set_max_read_size(1);
tracker_->SetSmallestMaxReadSizeForTesting(1);
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
ASSERT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
// Read one byte at a time until reaching the expected last byte.
size_t completion = 0;
size_t total_read = 0;
while (total_read < kExpectedImageSize - 1) {
ReadScanDataRequest rsd_request;
*rsd_request.mutable_job_handle() = sps_response.job_handle();
ReadScanDataResponse rsd_response = tracker_->ReadScanData(rsd_request);
ASSERT_EQ(rsd_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(rsd_response.job_handle(),
EqualsProto(rsd_request.job_handle()));
EXPECT_LE(rsd_response.data().length(), 1);
EXPECT_GE(rsd_response.estimated_completion(), completion);
total_read += rsd_response.data().size();
completion = rsd_response.estimated_completion();
}
// Last request will get EOF plus the final byte.
ReadScanDataRequest rsd_request;
*rsd_request.mutable_job_handle() = sps_response.job_handle();
ReadScanDataResponse rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_EOF);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_EQ(rsd_response.data().length(), 1);
EXPECT_EQ(rsd_response.estimated_completion(), 100);
total_read += rsd_response.data().size();
EXPECT_EQ(total_read, kExpectedImageSize);
}
TEST_F(DeviceTrackerTest, ReadScanDataSmallReadsCancel) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(1000, 100));
std::vector<uint8_t> page1(3 * 1000 * 100, 0);
scanner->SetScanData({page1});
scanner->SetMaxReadSize(3 * 1000 * 30 + 5); // 30 lines plus leftover.
scanner->SetInitialEmptyReads(2);
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/png");
sps_request.set_max_read_size(7);
tracker_->SetSmallestMaxReadSizeForTesting(1);
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
ASSERT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
// Read some of the data a few bytes at a time.
size_t completion = 0;
size_t total_read = 0;
while (total_read < 100) {
ReadScanDataRequest rsd_request;
*rsd_request.mutable_job_handle() = sps_response.job_handle();
ReadScanDataResponse rsd_response = tracker_->ReadScanData(rsd_request);
ASSERT_EQ(rsd_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(rsd_response.job_handle(),
EqualsProto(rsd_request.job_handle()));
EXPECT_LE(rsd_response.data().length(), 7);
EXPECT_GE(rsd_response.estimated_completion(), completion);
total_read += rsd_response.data().size();
completion = rsd_response.estimated_completion();
}
// CancelScan internally discards the remaining data.
CancelScanRequest cancel_request;
*cancel_request.mutable_job_handle() = sps_response.job_handle();
CancelScanResponse cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_TRUE(cancel_response.success());
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_SUCCESS);
// Last request will get EOF with no payload because the cancel discarded it.
ReadScanDataRequest rsd_request;
*rsd_request.mutable_job_handle() = sps_response.job_handle();
ReadScanDataResponse rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_CANCELLED);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_FALSE(rsd_response.has_data());
EXPECT_FALSE(rsd_response.has_estimated_completion());
EXPECT_LE(total_read, 106); // Up to one read past the loop ending at 100.
}
TEST_F(DeviceTrackerTest, ReadScanDataAfterCancel) {
SaneDeviceFake* scanner = CreateFakeScanner("Test");
scanner->SetScanParameters(MakeScanParameters(100, 100));
std::vector<uint8_t> page1(3 * 100 * 100, 0);
scanner->SetScanData({page1});
scanner->SetMaxReadSize(3 * 100 * 60 + 5); // 60 lines plus leftover.
scanner->SetInitialEmptyReads(2);
OpenScannerRequest open_request;
open_request.mutable_scanner_id()->set_connection_string("Test");
open_request.set_client_id("DeviceTrackerTest");
OpenScannerResponse open_response = tracker_->OpenScanner(open_request);
ASSERT_EQ(open_response.result(), OPERATION_RESULT_SUCCESS);
StartPreparedScanRequest sps_request;
*sps_request.mutable_scanner() = open_response.config().scanner();
sps_request.set_image_format("image/png");
StartPreparedScanResponse sps_response =
tracker_->StartPreparedScan(sps_request);
ASSERT_EQ(sps_response.result(), OPERATION_RESULT_SUCCESS);
// First request will read nothing, but header data is available.
ReadScanDataRequest rsd_request;
*rsd_request.mutable_job_handle() = sps_response.job_handle();
ReadScanDataResponse rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_SUCCESS);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_EQ(rsd_response.data().length(), 54); // Magic + header chunks.
EXPECT_EQ(rsd_response.estimated_completion(), 0);
// CancelScan internally discards the remaining data.
CancelScanRequest cancel_request;
*cancel_request.mutable_job_handle() = sps_response.job_handle();
CancelScanResponse cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_TRUE(cancel_response.success());
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_SUCCESS);
// Next read sees the cancelled status.
rsd_response = tracker_->ReadScanData(rsd_request);
EXPECT_EQ(rsd_response.result(), OPERATION_RESULT_CANCELLED);
EXPECT_THAT(rsd_response.job_handle(), EqualsProto(rsd_request.job_handle()));
EXPECT_FALSE(rsd_response.has_data());
EXPECT_EQ(rsd_response.estimated_completion(), 0);
// Cancelling again doesn't do anything.
cancel_response = tracker_->CancelScan(cancel_request);
EXPECT_TRUE(cancel_response.success());
EXPECT_EQ(cancel_response.result(), OPERATION_RESULT_SUCCESS);
}
} // namespace
} // namespace lorgnette