blob: 3fe10a93eacdada37036b04df9bbe648ff6fa3ce [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lorgnette/manager.h"
#include <map>
#include <string>
#include <base/memory/scoped_ptr.h>
#include <base/stl_util.h>
#include <base/string_number_conversions.h>
#include <base/string_util.h>
#include <base/stringprintf.h>
#include <chromeos/process_mock.h>
#include <gtest/gtest.h>
using file_util::ScopedFD;
using std::map;
using std::string;
using testing::_;
using testing::InSequence;
using testing::Return;
namespace lorgnette {
class ManagerTest : public testing::Test {
public:
ManagerTest()
: input_pipe_fd_(kInputPipeFd),
output_pipe_fd_(kOutputPipeFd),
input_scoped_fd_(&input_pipe_fd_),
output_scoped_fd_(&output_pipe_fd_),
manager_(base::Callback<void()>()) {}
virtual void TearDown() {
// The fds that we have handed to these ScopedFD are not real, so we
// must prevent our scoped fds from calling close() on them.
input_scoped_fd_.release();
output_scoped_fd_.release();
}
protected:
static const char kDeviceName[];
static const char kMode[];
static const int kInputPipeFd;
static const int kOutputFd;
static const int kOutputPipeFd;
static const int kResolution;
static void RunListScannersProcess(int fd, chromeos::Process *process) {
Manager::RunListScannersProcess(fd, process);
}
static void RunScanImageProcess(
const string &device_name,
int out_fd,
file_util::ScopedFD *input_scoped_fd,
file_util::ScopedFD *output_scoped_fd,
const map<string, ::DBus::Variant> &scan_properties,
chromeos::Process *scan_process,
chromeos::Process *convert_process,
::DBus::Error *error) {
Manager::RunScanImageProcess(device_name,
out_fd,
input_scoped_fd,
output_scoped_fd,
scan_properties,
scan_process,
convert_process,
error);
}
static void ExpectStartScan(
const char *mode,
int resolution,
chromeos::ProcessMock *scan_process,
chromeos::ProcessMock *convert_process) {
EXPECT_CALL(*scan_process, AddArg(GetScanImagePath()));
EXPECT_CALL(*scan_process, AddArg("-d"));
EXPECT_CALL(*scan_process, AddArg(kDeviceName));
if (mode) {
EXPECT_CALL(*scan_process, AddArg("--mode"));
EXPECT_CALL(*scan_process, AddArg(mode));
}
if (resolution) {
const string kResolutionString(base::IntToString(resolution));
EXPECT_CALL(*scan_process, AddArg("--resolution"));
EXPECT_CALL(*scan_process, AddArg(kResolutionString));
}
EXPECT_CALL(*scan_process, BindFd(kOutputPipeFd, STDOUT_FILENO));
EXPECT_CALL(*convert_process, AddArg(GetScanConverterPath()));
EXPECT_CALL(*convert_process, BindFd(kInputPipeFd, STDIN_FILENO));
EXPECT_CALL(*convert_process, BindFd(kOutputFd, STDOUT_FILENO));
EXPECT_CALL(*convert_process, Start());
EXPECT_CALL(*scan_process, Start());
}
static Manager::ScannerInfo ScannerInfoFromString(
const std::string &scanner_info_string) {
return Manager::ScannerInfoFromString(scanner_info_string);
}
Manager::DBusAdaptor *GetDBusAdaptor() {
return manager_.dbus_adaptor_.get();
}
static std::string GetScanConverterPath() {
return Manager::kScanConverterPath;
}
static std::string GetScanImagePath() { return Manager::kScanImagePath; }
static std::string GetScanImageFromattedDeviceListCmd() {
return Manager::kScanImageFormattedDeviceListCmd;
}
int input_pipe_fd_;
int output_pipe_fd_;
ScopedFD input_scoped_fd_;
ScopedFD output_scoped_fd_;
Manager manager_;
};
const int ManagerTest::kInputPipeFd = 123;
const int ManagerTest::kOutputFd = 456;
const int ManagerTest::kOutputPipeFd = 789;
const char ManagerTest::kDeviceName[] = "scanner";
const int ManagerTest::kResolution = 300;
const char ManagerTest::kMode[] = "Color";
TEST_F(ManagerTest, Construct) {
EXPECT_EQ(NULL, GetDBusAdaptor());
}
TEST_F(ManagerTest, RunListScannersProcess) {
chromeos::ProcessMock process;
const int kFd = 123;
InSequence seq;
EXPECT_CALL(process, AddArg(GetScanImagePath()));
EXPECT_CALL(process, AddArg(GetScanImageFromattedDeviceListCmd()));
EXPECT_CALL(process, BindFd(kFd, STDOUT_FILENO));
EXPECT_CALL(process, Run());
RunListScannersProcess(kFd, &process);
}
TEST_F(ManagerTest, RunScanImageProcessSuccess) {
map<string, ::DBus::Variant> props;
props["Mode"].writer().append_string(kMode);
props["Resolution"].writer().append_uint32(kResolution);
chromeos::ProcessMock scan_process;
chromeos::ProcessMock convert_process;
InSequence seq;
ExpectStartScan(kMode,
kResolution,
&scan_process,
&convert_process);
EXPECT_CALL(scan_process, Wait()).WillOnce(Return(0));
EXPECT_CALL(convert_process, Wait()).WillOnce(Return(0));
DBus::Error error;
RunScanImageProcess(kDeviceName,
kOutputFd,
&input_scoped_fd_,
&output_scoped_fd_,
props,
&scan_process,
&convert_process,
&error);
EXPECT_EQ(NULL, input_scoped_fd_.get());
EXPECT_EQ(NULL, output_scoped_fd_.get());
EXPECT_FALSE(error.is_set());
}
TEST_F(ManagerTest, RunScanImageProcessInvalidArgument) {
map<string, ::DBus::Variant> props;
const char kInvalidArgument[] = "InvalidArgument";
props[kInvalidArgument].writer().append_string("");
chromeos::ProcessMock scan_process;
chromeos::ProcessMock convert_process;
// For "scanimage", "-d", "<device name>".
EXPECT_CALL(scan_process, AddArg(_)).Times(3);
EXPECT_CALL(convert_process, AddArg(_)).Times(0);
EXPECT_CALL(convert_process, Start()).Times(0);
EXPECT_CALL(scan_process, Start()).Times(0);
DBus::Error error;
RunScanImageProcess("", 0, NULL, NULL, props, &scan_process,
&convert_process, &error);
// Expect that the pipe fds have not been released.
EXPECT_EQ(&input_pipe_fd_, input_scoped_fd_.get());
EXPECT_EQ(&output_pipe_fd_, output_scoped_fd_.get());
EXPECT_TRUE(error.is_set());
EXPECT_TRUE(StartsWithASCII(error.message(),
base::StringPrintf("Invalid scan parameter %s",
kInvalidArgument),
false));
}
TEST_F(ManagerTest, RunScanImageInvalidModeArgument) {
map<string, ::DBus::Variant> props;
const char kBadMode[] = "Raytrace";
props["Mode"].writer().append_string(kBadMode);
chromeos::ProcessMock scan_process;
chromeos::ProcessMock convert_process;
// For "scanimage", "-d", "<device name>".
EXPECT_CALL(scan_process, AddArg(_)).Times(3);
EXPECT_CALL(convert_process, AddArg(_)).Times(0);
EXPECT_CALL(convert_process, Start()).Times(0);
EXPECT_CALL(scan_process, Start()).Times(0);
DBus::Error error;
RunScanImageProcess(kDeviceName,
kOutputFd,
&input_scoped_fd_,
&output_scoped_fd_,
props,
&scan_process,
&convert_process,
&error);
// Expect that the pipe fds have not been released.
EXPECT_EQ(&input_pipe_fd_, input_scoped_fd_.get());
EXPECT_EQ(&output_pipe_fd_, output_scoped_fd_.get());
EXPECT_TRUE(error.is_set());
EXPECT_TRUE(StartsWithASCII(error.message(),
base::StringPrintf("Invalid mode parameter %s",
kBadMode),
false));
}
TEST_F(ManagerTest, RunScanImageProcessCaptureFailure) {
map<string, ::DBus::Variant> props;
props["Mode"].writer().append_string(kMode);
props["Resolution"].writer().append_uint32(kResolution);
chromeos::ProcessMock scan_process;
chromeos::ProcessMock convert_process;
InSequence seq;
ExpectStartScan(kMode,
kResolution,
&scan_process,
&convert_process);
const int kErrorResult = 999;
EXPECT_CALL(scan_process, Wait()).WillOnce(Return(kErrorResult));
EXPECT_CALL(convert_process, Kill(SIGKILL, 1));
EXPECT_CALL(convert_process, Wait()).Times(0);
DBus::Error error;
RunScanImageProcess(kDeviceName,
kOutputFd,
&input_scoped_fd_,
&output_scoped_fd_,
props,
&scan_process,
&convert_process,
&error);
EXPECT_EQ(NULL, input_scoped_fd_.get());
EXPECT_EQ(NULL, output_scoped_fd_.get());
EXPECT_TRUE(error.is_set());
EXPECT_STREQ(
base::StringPrintf("Scan process exited with result %d",
kErrorResult).c_str(),
error.message());
}
TEST_F(ManagerTest, RunScanImageProcessConvertFailure) {
map<string, ::DBus::Variant> props;
props["Mode"].writer().append_string(kMode);
props["Resolution"].writer().append_uint32(kResolution);
chromeos::ProcessMock scan_process;
chromeos::ProcessMock convert_process;
InSequence seq;
ExpectStartScan(kMode,
kResolution,
&scan_process,
&convert_process);
EXPECT_CALL(scan_process, Wait()).WillOnce(Return(0));
const int kErrorResult = 111;
EXPECT_CALL(convert_process, Wait()).WillOnce(Return(kErrorResult));
DBus::Error error;
RunScanImageProcess(kDeviceName,
kOutputFd,
&input_scoped_fd_,
&output_scoped_fd_,
props,
&scan_process,
&convert_process,
&error);
EXPECT_EQ(NULL, input_scoped_fd_.get());
EXPECT_EQ(NULL, output_scoped_fd_.get());
EXPECT_TRUE(error.is_set());
EXPECT_STREQ(
base::StringPrintf("Image converter process failed with result %d",
kErrorResult).c_str(),
error.message());
}
TEST_F(ManagerTest, ScannerInfoFromString) {
EXPECT_TRUE(ScannerInfoFromString("").empty());
EXPECT_TRUE(ScannerInfoFromString("one").empty());
EXPECT_TRUE(ScannerInfoFromString("one%two").empty());
EXPECT_TRUE(ScannerInfoFromString("one%two%three").empty());
const char kDevice0[] = "device0";
const char kDevice1[] = "device1";
const char kManufacturer0[] = "rayban";
const char kManufacturer1[] = "oakley";
const char kModel0[] = "model0";
const char kModel1[] = "model1";
const char kType0[] = "type0";
const char kType1[] = "type1";
const string kInputString(base::StringPrintf(
"one\n"
"%s%%%s%%%s%%%s\n"
"one%%two\n"
"%s%%%s%%%s%%%s\n"
"one%%two%%three\n"
"one%%two%%three%%four%%five\n",
kDevice0, kManufacturer0, kModel0, kType0,
kDevice1, kManufacturer1, kModel1, kType1));
Manager::ScannerInfo scan_info = ScannerInfoFromString(kInputString);
EXPECT_EQ(2, scan_info.size());
EXPECT_TRUE(ContainsKey(scan_info, kDevice0));
EXPECT_EQ(3, scan_info[kDevice0].size());
EXPECT_STREQ(kManufacturer0, scan_info[kDevice0]["Manufacturer"].c_str());
EXPECT_STREQ(kModel0, scan_info[kDevice0]["Model"].c_str());
EXPECT_STREQ(kType0, scan_info[kDevice0]["Type"].c_str());
EXPECT_TRUE(ContainsKey(scan_info, kDevice1));
EXPECT_EQ(3, scan_info[kDevice1].size());
EXPECT_STREQ(kManufacturer1, scan_info[kDevice1]["Manufacturer"].c_str());
EXPECT_STREQ(kModel1, scan_info[kDevice1]["Model"].c_str());
EXPECT_STREQ(kType1, scan_info[kDevice1]["Type"].c_str());
}
} // namespace lorgnette