1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "printing/printing_context_chromeos.h"
9 #include "base/memory/raw_ptr.h"
10 #include "base/strings/string_split.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "printing/backend/cups_ipp_constants.h"
13 #include "printing/backend/mock_cups_printer.h"
14 #include "printing/mojom/print.mojom.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
23 using ::testing::ByMove;
24 using ::testing::DoAll;
25 using ::testing::NiceMock;
26 using ::testing::Return;
27 using ::testing::SaveArg;
28 using ::testing::SetArgPointee;
30 constexpr char kPrinterName[] = "printer";
31 constexpr char16_t kPrinterName16[] = u"printer";
33 constexpr char kUsername[] = "test user";
35 constexpr char kDocumentName[] = "document name";
36 constexpr char16_t kDocumentName16[] = u"document name";
38 constexpr gfx::Size kDefaultPaperSize = {215900, 279400};
39 constexpr char kDefaultPaperName[] = "some_vendor_id";
41 class MockCupsConnection : public CupsConnection {
43 MOCK_METHOD1(GetDests, bool(std::vector<std::unique_ptr<CupsPrinter>>&));
45 bool(const std::vector<std::string>& printer_ids,
46 std::vector<QueueStatus>* jobs));
47 MOCK_METHOD2(GetPrinterStatus,
48 bool(const std::string& printer_id,
49 PrinterStatus* printer_status));
50 MOCK_CONST_METHOD0(server_name, std::string());
51 MOCK_CONST_METHOD0(last_error, int());
52 MOCK_CONST_METHOD0(last_error_message, std::string());
54 MOCK_METHOD1(GetPrinter,
55 std::unique_ptr<CupsPrinter>(const std::string& printer_name));
58 class TestPrintSettings : public PrintSettings {
60 TestPrintSettings() { set_duplex_mode(mojom::DuplexMode::kSimplex); }
63 class PrintingContextTest : public testing::Test,
64 public PrintingContext::Delegate {
66 void SetDefaultSettings(bool send_user_info, const std::string& uri) {
67 auto unique_connection = std::make_unique<MockCupsConnection>();
68 auto* connection = unique_connection.get();
69 auto unique_printer = std::make_unique<NiceMock<MockCupsPrinter>>();
70 printer_ = unique_printer.get();
71 EXPECT_CALL(*printer_, GetUri()).WillRepeatedly(Return(uri));
72 EXPECT_CALL(*connection, GetPrinter(kPrinterName))
73 .WillOnce(Return(ByMove(std::move(unique_printer))));
74 printing_context_ = PrintingContextChromeos::CreateForTesting(
75 this, std::move(unique_connection));
76 auto settings = std::make_unique<PrintSettings>();
77 settings->set_device_name(kPrinterName16);
78 settings->set_send_user_info(send_user_info);
79 settings->set_duplex_mode(mojom::DuplexMode::kLongEdge);
80 settings->set_username(kUsername);
81 printing_context_->UpdatePrintSettingsFromPOD(std::move(settings));
82 settings_.set_requested_media({kDefaultPaperSize, kDefaultPaperName});
85 ipp_attribute_t* GetAttribute(ipp_t* attributes,
86 const char* attr_name) const {
88 ipp_attribute_t* ret = nullptr;
89 for (ipp_attribute_t* attr = ippFirstAttribute(attributes); attr;
90 attr = ippNextAttribute(attributes)) {
91 const char* name = ippGetName(attr);
92 if (name && !strcmp(attr_name, name)) {
93 EXPECT_EQ(nullptr, ret)
94 << "Multiple attributes with name " << attr_name << " found.";
102 void TestStringOptionValue(const char* attr_name,
103 const char* expected_value) const {
104 auto attributes = SettingsToIPPOptions(settings_, printable_area_);
105 auto* attr = GetAttribute(attributes.get(), attr_name);
106 EXPECT_STREQ(expected_value, ippGetString(attr, 0, nullptr));
109 void TestIntegerOptionValue(const char* attr_name, int expected_value) const {
110 auto attributes = SettingsToIPPOptions(settings_, printable_area_);
111 auto* attr = GetAttribute(attributes.get(), attr_name);
112 EXPECT_EQ(expected_value, ippGetInteger(attr, 0));
115 void TestOctetStringOptionValue(const char* attr_name,
116 base::span<const char> expected_value) const {
117 auto attributes = SettingsToIPPOptions(settings_, printable_area_);
118 auto* attr = GetAttribute(attributes.get(), attr_name);
120 void* value = ippGetOctetString(attr, 0, &length);
121 ASSERT_EQ(expected_value.size(), static_cast<size_t>(length));
123 EXPECT_EQ(0, memcmp(expected_value.data(), value, expected_value.size()));
126 void TestResolutionOptionValue(const char* attr_name,
128 int expected_y_res) const {
129 auto attributes = SettingsToIPPOptions(settings_, printable_area_);
130 auto* attr = GetAttribute(attributes.get(), attr_name);
133 int x_res = ippGetResolution(attr, 0, &y_res, &unit);
134 EXPECT_EQ(unit, IPP_RES_PER_INCH);
135 EXPECT_EQ(expected_x_res, x_res);
136 EXPECT_EQ(expected_y_res, y_res);
139 void TestMediaColValue(const gfx::Size& expected_size,
140 int expected_bottom_margin,
141 int expected_left_margin,
142 int expected_right_margin,
143 int expected_top_margin) {
144 auto attributes = SettingsToIPPOptions(settings_, printable_area_);
146 ippGetCollection(GetAttribute(attributes.get(), kIppMediaCol), 0);
148 ippGetCollection(GetAttribute(media_col, kIppMediaSize), 0);
150 int width = ippGetInteger(GetAttribute(media_size, kIppXDimension), 0);
151 int height = ippGetInteger(GetAttribute(media_size, kIppYDimension), 0);
152 EXPECT_EQ(expected_size.width(), width);
153 EXPECT_EQ(expected_size.height(), height);
156 ippGetInteger(GetAttribute(media_col, kIppMediaBottomMargin), 0);
157 int left = ippGetInteger(GetAttribute(media_col, kIppMediaLeftMargin), 0);
158 int right = ippGetInteger(GetAttribute(media_col, kIppMediaRightMargin), 0);
159 int top = ippGetInteger(GetAttribute(media_col, kIppMediaTopMargin), 0);
161 EXPECT_EQ(expected_bottom_margin, bottom);
162 EXPECT_EQ(expected_left_margin, left);
163 EXPECT_EQ(expected_right_margin, right);
164 EXPECT_EQ(expected_top_margin, top);
167 bool HasAttribute(const char* attr_name) const {
168 auto attributes = SettingsToIPPOptions(settings_, printable_area_);
169 return !!ippFindAttribute(attributes.get(), attr_name, IPP_TAG_ZERO);
172 int GetAttrValueCount(const char* attr_name) const {
173 auto attributes = SettingsToIPPOptions(settings_, printable_area_);
174 auto* attr = GetAttribute(attributes.get(), attr_name);
175 return ippGetCount(attr);
178 TestPrintSettings settings_;
179 gfx::Rect printable_area_;
181 // PrintingContext::Delegate methods.
182 gfx::NativeView GetParentView() override { return gfx::NativeView(); }
183 std::string GetAppLocale() override { return std::string(); }
185 std::unique_ptr<PrintingContextChromeos> printing_context_;
186 raw_ptr<MockCupsPrinter> printer_;
189 TEST_F(PrintingContextTest, SettingsToIPPOptions_Color) {
190 settings_.set_color(mojom::ColorModel::kGray);
191 TestStringOptionValue(kIppColor, "monochrome");
192 settings_.set_color(mojom::ColorModel::kColor);
193 TestStringOptionValue(kIppColor, "color");
196 TEST_F(PrintingContextTest, SettingsToIPPOptions_Duplex) {
197 settings_.set_duplex_mode(mojom::DuplexMode::kSimplex);
198 TestStringOptionValue(kIppDuplex, "one-sided");
199 settings_.set_duplex_mode(mojom::DuplexMode::kLongEdge);
200 TestStringOptionValue(kIppDuplex, "two-sided-long-edge");
201 settings_.set_duplex_mode(mojom::DuplexMode::kShortEdge);
202 TestStringOptionValue(kIppDuplex, "two-sided-short-edge");
205 TEST_F(PrintingContextTest, SettingsToIPPOptions_MediaCol) {
206 settings_.set_requested_media(
207 {gfx::Size(297000, 420000), "iso_a3_297x420mm"});
209 gfx::Rect(2000, 1000, 297000 - (2000 + 3000), 420000 - (1000 + 4000));
210 TestMediaColValue(gfx::Size(29700, 42000), 100, 200, 300, 400);
213 TEST_F(PrintingContextTest, SettingsToIPPOptions_Copies) {
214 settings_.set_copies(3);
215 TestIntegerOptionValue(kIppCopies, 3);
218 TEST_F(PrintingContextTest, SettingsToIPPOptions_Collate) {
219 TestStringOptionValue(kIppCollate, "separate-documents-uncollated-copies");
220 settings_.set_collate(true);
221 TestStringOptionValue(kIppCollate, "separate-documents-collated-copies");
224 TEST_F(PrintingContextTest, SettingsToIPPOptions_Pin) {
225 EXPECT_FALSE(HasAttribute(kIppPin));
226 settings_.set_pin_value("1234");
227 TestOctetStringOptionValue(kIppPin, base::make_span("1234", 4u));
230 TEST_F(PrintingContextTest, SettingsToIPPOptions_Resolution) {
231 EXPECT_FALSE(HasAttribute(kIppResolution));
232 settings_.set_dpi_xy(0, 300);
233 EXPECT_FALSE(HasAttribute(kIppResolution));
234 settings_.set_dpi_xy(300, 0);
235 EXPECT_FALSE(HasAttribute(kIppResolution));
236 settings_.set_dpi(600);
237 TestResolutionOptionValue(kIppResolution, 600, 600);
238 settings_.set_dpi_xy(600, 1200);
239 TestResolutionOptionValue(kIppResolution, 600, 1200);
242 TEST_F(PrintingContextTest, SettingsToIPPOptions_SendUserInfo_Secure) {
243 ipp_status_t status = ipp_status_t::IPP_STATUS_OK;
244 std::u16string document_name = kDocumentName16;
245 SetDefaultSettings(/*send_user_info=*/true, "ipps://test-uri");
246 std::string create_job_document_name;
247 std::string create_job_username;
248 std::string start_document_document_name;
249 std::string start_document_username;
250 EXPECT_CALL(*printer_, CreateJob)
251 .WillOnce(DoAll(SetArgPointee<0>(/*job_id=*/1),
252 SaveArg<1>(&create_job_document_name),
253 SaveArg<2>(&create_job_username), Return(status)));
254 EXPECT_CALL(*printer_, StartDocument)
255 .WillOnce(DoAll(SaveArg<1>(&start_document_document_name),
256 SaveArg<3>(&start_document_username), Return(true)));
258 printing_context_->NewDocument(document_name);
260 EXPECT_EQ(create_job_document_name, kDocumentName);
261 EXPECT_EQ(start_document_document_name, kDocumentName);
262 EXPECT_EQ(create_job_username, kUsername);
263 EXPECT_EQ(start_document_username, kUsername);
266 TEST_F(PrintingContextTest, SettingsToIPPOptions_SendUserInfo_Insecure) {
267 ipp_status_t status = ipp_status_t::IPP_STATUS_OK;
268 std::u16string document_name = kDocumentName16;
269 std::string default_username = "chronos";
270 std::string default_document_name = "-";
271 SetDefaultSettings(/*send_user_info=*/true, "ipp://test-uri");
272 std::string create_job_document_name;
273 std::string create_job_username;
274 std::string start_document_document_name;
275 std::string start_document_username;
276 EXPECT_CALL(*printer_, CreateJob)
277 .WillOnce(DoAll(SetArgPointee<0>(/*job_id=*/1),
278 SaveArg<1>(&create_job_document_name),
279 SaveArg<2>(&create_job_username), Return(status)));
280 EXPECT_CALL(*printer_, StartDocument)
281 .WillOnce(DoAll(SaveArg<1>(&start_document_document_name),
282 SaveArg<3>(&start_document_username), Return(true)));
284 printing_context_->NewDocument(document_name);
286 EXPECT_EQ(create_job_document_name, default_document_name);
287 EXPECT_EQ(start_document_document_name, default_document_name);
288 EXPECT_EQ(create_job_username, default_username);
289 EXPECT_EQ(start_document_username, default_username);
292 TEST_F(PrintingContextTest, SettingsToIPPOptions_DoNotSendUserInfo) {
293 ipp_status_t status = ipp_status_t::IPP_STATUS_OK;
294 std::u16string document_name = kDocumentName16;
295 SetDefaultSettings(/*send_user_info=*/false, "ipps://test-uri");
296 std::string create_job_document_name;
297 std::string create_job_username;
298 std::string start_document_document_name;
299 std::string start_document_username;
300 EXPECT_CALL(*printer_, CreateJob)
301 .WillOnce(DoAll(SetArgPointee<0>(/*job_id=*/1),
302 SaveArg<1>(&create_job_document_name),
303 SaveArg<2>(&create_job_username), Return(status)));
304 EXPECT_CALL(*printer_, StartDocument)
305 .WillOnce(DoAll(SaveArg<1>(&start_document_document_name),
306 SaveArg<3>(&start_document_username), Return(true)));
308 printing_context_->NewDocument(document_name);
310 EXPECT_EQ(create_job_document_name, "");
311 EXPECT_EQ(start_document_document_name, "");
312 EXPECT_EQ(create_job_username, "");
313 EXPECT_EQ(start_document_username, "");
316 TEST_F(PrintingContextTest, SettingsToIPPOptionsClientInfo) {
317 mojom::IppClientInfo client_info(
318 mojom::IppClientInfo::ClientType::kOperatingSystem, "a-", "B_", "1.",
320 settings_.set_client_infos({client_info});
322 auto attributes = SettingsToIPPOptions(settings_, printable_area_);
323 auto* attr = ippFindAttribute(attributes.get(), kIppClientInfo,
324 IPP_TAG_BEGIN_COLLECTION);
325 auto* client_info_collection = ippGetCollection(attr, 0);
327 attr = ippFindAttribute(client_info_collection, kIppClientName, IPP_TAG_NAME);
328 EXPECT_STREQ("a-", ippGetString(attr, 0, nullptr));
330 attr = ippFindAttribute(client_info_collection, kIppClientType, IPP_TAG_ENUM);
331 EXPECT_EQ(4, ippGetInteger(attr, 0));
334 ippFindAttribute(client_info_collection, kIppClientPatches, IPP_TAG_TEXT);
335 EXPECT_STREQ("B_", ippGetString(attr, 0, nullptr));
337 attr = ippFindAttribute(client_info_collection, kIppClientStringVersion,
339 EXPECT_STREQ("1.", ippGetString(attr, 0, nullptr));
341 attr = ippFindAttribute(client_info_collection, kIppClientVersion,
344 void* version = ippGetOctetString(attr, 0, &length);
345 ASSERT_TRUE(version);
346 EXPECT_EQ(6, length);
347 EXPECT_EQ(0, memcmp("a.1-B_", version, 6));
350 TEST_F(PrintingContextTest, SettingsToIPPOptionsClientInfoSomeValid) {
351 mojom::IppClientInfo valid_client_info(
352 mojom::IppClientInfo::ClientType::kOperatingSystem, "aB.1-_", "aB.1-_",
354 mojom::IppClientInfo invalid_client_info(
355 mojom::IppClientInfo::ClientType::kOperatingSystem, "{}", "aB.1-_",
357 settings_.set_client_infos(
358 {valid_client_info, invalid_client_info, valid_client_info});
360 // Check that the invalid item is skipped in the client-info collection.
361 EXPECT_EQ(GetAttrValueCount(kIppClientInfo), 2);
364 TEST_F(PrintingContextTest, SettingsToIPPOptionsClientInfoEmpty) {
365 settings_.set_client_infos({});
366 EXPECT_FALSE(HasAttribute(kIppClientInfo));
368 mojom::IppClientInfo invalid_client_info(
369 mojom::IppClientInfo::ClientType::kOther, "$", " ", "{}", absl::nullopt);
371 settings_.set_client_infos({invalid_client_info});
372 EXPECT_FALSE(HasAttribute(kIppClientInfo));
377 } // namespace printing