| // 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 |