| // 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/strings/string_number_conversions.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <chromeos/variant_dictionary.h> |
| #include <chromeos/process_mock.h> |
| #include <gtest/gtest.h> |
| |
| using base::ScopedFD; |
| using chromeos::VariantDictionary; |
| using std::map; |
| using std::string; |
| using testing::_; |
| using testing::InSequence; |
| using testing::Return; |
| |
| namespace lorgnette { |
| |
| class ManagerTest : public testing::Test { |
| public: |
| ManagerTest() |
| : input_scoped_fd_(kInputPipeFd), |
| output_scoped_fd_(kOutputPipeFd), |
| 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. |
| int fd = input_scoped_fd_.release(); |
| CHECK(fd == kInvalidFd || fd == kInputPipeFd); |
| fd = output_scoped_fd_.release(); |
| CHECK(fd == kInvalidFd || fd == kOutputPipeFd); |
| } |
| |
| protected: |
| static const char kDeviceName[]; |
| static const char kMode[]; |
| static const int kInvalidFd; |
| 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, |
| base::ScopedFD *input_scoped_fd, |
| base::ScopedFD *output_scoped_fd, |
| const VariantDictionary &scan_properties, |
| chromeos::Process *scan_process, |
| chromeos::Process *convert_process, |
| chromeos::ErrorPtr *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); |
| } |
| |
| static std::string GetScanConverterPath() { |
| return Manager::kScanConverterPath; |
| } |
| static std::string GetScanImagePath() { return Manager::kScanImagePath; } |
| static std::string GetScanImageFromattedDeviceListCmd() { |
| return Manager::kScanImageFormattedDeviceListCmd; |
| } |
| |
| ScopedFD input_scoped_fd_; |
| ScopedFD output_scoped_fd_; |
| Manager manager_; |
| }; |
| |
| // kInvalidFd must equal to base::internal::ScopedFDCloseTraits::InvalidValue(). |
| const int ManagerTest::kInvalidFd = -1; |
| 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"; |
| |
| MATCHER_P(IsDbusErrorStartingWith, message, "") { |
| return arg != nullptr && |
| arg->GetDomain() == chromeos::errors::dbus::kDomain && |
| arg->GetCode() == kManagerServiceError && |
| StartsWithASCII(arg->GetMessage(), message, false); |
| } |
| |
| 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) { |
| VariantDictionary props{ |
| {"Mode", string{kMode}}, |
| {"Resolution", uint32_t{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)); |
| chromeos::ErrorPtr error; |
| RunScanImageProcess(kDeviceName, |
| kOutputFd, |
| &input_scoped_fd_, |
| &output_scoped_fd_, |
| props, |
| &scan_process, |
| &convert_process, |
| &error); |
| EXPECT_EQ(kInvalidFd, input_scoped_fd_.get()); |
| EXPECT_EQ(kInvalidFd, output_scoped_fd_.get()); |
| EXPECT_EQ(nullptr, error.get()); |
| } |
| |
| TEST_F(ManagerTest, RunScanImageProcessInvalidArgument) { |
| const char kInvalidArgument[] = "InvalidArgument"; |
| VariantDictionary props{{kInvalidArgument, ""}}; |
| 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); |
| chromeos::ErrorPtr error; |
| RunScanImageProcess("", 0, nullptr, nullptr, props, &scan_process, |
| &convert_process, &error); |
| |
| // Expect that the pipe fds have not been released. |
| EXPECT_EQ(kInputPipeFd, input_scoped_fd_.get()); |
| EXPECT_EQ(kOutputPipeFd, output_scoped_fd_.get()); |
| |
| EXPECT_THAT(error, IsDbusErrorStartingWith( |
| base::StringPrintf("Invalid scan parameter %s", kInvalidArgument))); |
| } |
| |
| TEST_F(ManagerTest, RunScanImageInvalidModeArgument) { |
| const char kBadMode[] = "Raytrace"; |
| VariantDictionary props{{"Mode", 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); |
| chromeos::ErrorPtr 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(kInputPipeFd, input_scoped_fd_.get()); |
| EXPECT_EQ(kOutputPipeFd, output_scoped_fd_.get()); |
| |
| EXPECT_THAT(error, IsDbusErrorStartingWith( |
| base::StringPrintf("Invalid mode parameter %s", kBadMode))); |
| } |
| |
| TEST_F(ManagerTest, RunScanImageProcessCaptureFailure) { |
| VariantDictionary props{ |
| {"Mode", string{kMode}}, |
| {"Resolution", uint32_t{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); |
| chromeos::ErrorPtr error; |
| RunScanImageProcess(kDeviceName, |
| kOutputFd, |
| &input_scoped_fd_, |
| &output_scoped_fd_, |
| props, |
| &scan_process, |
| &convert_process, |
| &error); |
| EXPECT_EQ(kInvalidFd, input_scoped_fd_.get()); |
| EXPECT_EQ(kInvalidFd, output_scoped_fd_.get()); |
| EXPECT_THAT(error, IsDbusErrorStartingWith( |
| base::StringPrintf("Scan process exited with result %d", kErrorResult))); |
| } |
| |
| TEST_F(ManagerTest, RunScanImageProcessConvertFailure) { |
| VariantDictionary props{ |
| {"Mode", string{kMode}}, |
| {"Resolution", uint32_t{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)); |
| chromeos::ErrorPtr error; |
| RunScanImageProcess(kDeviceName, |
| kOutputFd, |
| &input_scoped_fd_, |
| &output_scoped_fd_, |
| props, |
| &scan_process, |
| &convert_process, |
| &error); |
| EXPECT_EQ(kInvalidFd, input_scoped_fd_.get()); |
| EXPECT_EQ(kInvalidFd, output_scoped_fd_.get()); |
| EXPECT_THAT(error, IsDbusErrorStartingWith( |
| base::StringPrintf("Image converter process failed with result %d", |
| kErrorResult))); |
| } |
| |
| 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 |