1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "libipp/ipp.h"
6
7 #include <cstdint>
8 #include <string>
9 #include <vector>
10
11 #include <gtest/gtest.h>
12
13 // This file contains unit test based on examples from [rfc8010].
14
15 namespace ipp {
16
operator ==(const ipp::Resolution & a,const ipp::Resolution & b)17 static bool operator==(const ipp::Resolution& a, const ipp::Resolution& b) {
18 return (a.size1 == b.size1 && a.size2 == b.size2 && a.units == b.units);
19 }
20
operator ==(const ipp::RangeOfInteger & a,const ipp::RangeOfInteger & b)21 static bool operator==(const ipp::RangeOfInteger& a,
22 const ipp::RangeOfInteger& b) {
23 return (a.min_value == b.min_value && a.max_value == b.max_value);
24 }
25
operator ==(const ipp::DateTime & a,const ipp::DateTime & b)26 static bool operator==(const ipp::DateTime& a, const ipp::DateTime& b) {
27 return (a.UTC_direction == b.UTC_direction && a.UTC_hours == b.UTC_hours &&
28 a.UTC_minutes == b.UTC_minutes && a.day == b.day &&
29 a.hour == b.hour && a.deci_seconds == b.deci_seconds &&
30 a.minutes == b.minutes && a.month == b.month &&
31 a.seconds == b.seconds && a.year == b.year);
32 }
33
34 } // namespace ipp
35
36 namespace {
37
TwosComplementEncoding(int value)38 uint32_t TwosComplementEncoding(int value) {
39 if (value >= 0)
40 return value;
41 uint32_t binary = -static_cast<int64_t>(value);
42 binary = ~binary;
43 ++binary;
44 return binary;
45 }
46
47 // Helps build binary representation of frames from examples copied from
48 // [rfc8010].
49 struct BinaryContent {
50 // frame content
51 std::vector<uint8_t> data;
52 // add field with ASCII string
s__anon035c39180111::BinaryContent53 void s(std::string s) {
54 for (auto c : s) {
55 data.push_back(static_cast<uint8_t>(c));
56 }
57 }
58 // add 1 byte
u1__anon035c39180111::BinaryContent59 void u1(int v) {
60 const uint32_t b = TwosComplementEncoding(v);
61 data.push_back(b & 0xffu);
62 }
63 // add 2 bytes
u2__anon035c39180111::BinaryContent64 void u2(int v) {
65 const uint32_t b = TwosComplementEncoding(v);
66 data.push_back((b >> 8) & 0xffu);
67 data.push_back(b & 0xffu);
68 }
69 // add 4 bytes
u4__anon035c39180111::BinaryContent70 void u4(int v) {
71 const uint32_t b = TwosComplementEncoding(v);
72 data.push_back((b >> 24) & 0xffu);
73 data.push_back((b >> 16) & 0xffu);
74 data.push_back((b >> 8) & 0xffu);
75 data.push_back(b & 0xffu);
76 }
77 };
78
79 // function comparing if two collections have the same content
CompareCollections(ipp::Collection * c1,ipp::Collection * c2)80 void CompareCollections(ipp::Collection* c1, ipp::Collection* c2) {
81 std::vector<ipp::Attribute*> a1 = c1->GetAllAttributes();
82 std::vector<ipp::Attribute*> a2 = c2->GetAllAttributes();
83 ASSERT_EQ(a1.size(), a2.size());
84 for (size_t i = 0; i < a1.size(); ++i) {
85 EXPECT_EQ(a1[i]->GetName(), a2[i]->GetName());
86 EXPECT_EQ(a1[i]->GetType(), a2[i]->GetType());
87 EXPECT_EQ(a1[i]->IsASet(), a2[i]->IsASet());
88 EXPECT_EQ(a1[i]->GetState(), a2[i]->GetState());
89 ASSERT_EQ(a1[i]->GetSize(), a2[i]->GetSize());
90 for (size_t j = 0; j < a1[i]->GetSize(); ++j) {
91 std::string s1, s2;
92 ipp::DateTime d1, d2;
93 ipp::Resolution r1, r2;
94 ipp::RangeOfInteger i1, i2;
95 ipp::StringWithLanguage l1, l2;
96 switch (a1[i]->GetType()) {
97 case ipp::AttrType::text:
98 case ipp::AttrType::name:
99 a1[i]->GetValue(&l1, j);
100 a2[i]->GetValue(&l2, j);
101 EXPECT_EQ(l1.value, l2.value);
102 EXPECT_EQ(l1.language, l2.language);
103 break;
104 case ipp::AttrType::integer:
105 case ipp::AttrType::boolean:
106 case ipp::AttrType::enum_:
107 case ipp::AttrType::octetString:
108 case ipp::AttrType::keyword:
109 case ipp::AttrType::uri:
110 case ipp::AttrType::uriScheme:
111 case ipp::AttrType::charset:
112 case ipp::AttrType::naturalLanguage:
113 case ipp::AttrType::mimeMediaType:
114 a1[i]->GetValue(&s1, j);
115 a2[i]->GetValue(&s2, j);
116 EXPECT_EQ(s1, s2);
117 break;
118 case ipp::AttrType::dateTime:
119 a1[i]->GetValue(&d1, j);
120 a2[i]->GetValue(&d2, j);
121 EXPECT_EQ(d1, d2);
122 break;
123 case ipp::AttrType::resolution:
124 a1[i]->GetValue(&r1, j);
125 a2[i]->GetValue(&r2, j);
126 EXPECT_EQ(r1, r2);
127 break;
128 case ipp::AttrType::rangeOfInteger:
129 a1[i]->GetValue(&i1, j);
130 a2[i]->GetValue(&i2, j);
131 EXPECT_EQ(i1, i2);
132 break;
133 case ipp::AttrType::collection:
134 CompareCollections(a1[i]->GetCollection(j), a2[i]->GetCollection(j));
135 break;
136 }
137 }
138 }
139 }
140
141 // checks if given frame is a binary representation of given Request
CheckRequest(const BinaryContent & frame,ipp::Request * req,const int32_t request_no=1)142 void CheckRequest(const BinaryContent& frame,
143 ipp::Request* req,
144 const int32_t request_no = 1) {
145 // build output frame from Request and compare with the given frame
146 ipp::Client client(ipp::Version::_1_1, request_no - 1);
147 client.BuildRequestFrom(req);
148 EXPECT_TRUE(client.GetErrorLog().empty());
149 std::vector<uint8_t> bin_data;
150 ASSERT_TRUE(client.WriteRequestFrameTo(&bin_data));
151 EXPECT_TRUE(client.GetErrorLog().empty());
152 EXPECT_EQ(client.GetFrameLength(), bin_data.size());
153 EXPECT_EQ(bin_data, frame.data);
154 // parse the given frame and compare obtained object with the given Request
155 ipp::Server server(ipp::Version::_1_1, request_no);
156 auto req2 = ipp::Request::NewRequest(req->GetOperationId());
157 ASSERT_TRUE(server.ReadRequestFrameFrom(bin_data));
158 EXPECT_TRUE(server.GetErrorLog().empty());
159 ASSERT_TRUE(server.ParseRequestAndSaveTo(req2.get()));
160 EXPECT_TRUE(server.GetErrorLog().empty());
161 // ... compares two request objects
162 std::vector<ipp::Group*> groups1 = req->GetAllGroups();
163 std::vector<ipp::Group*> groups2 = req2->GetAllGroups();
164 ASSERT_EQ(groups1.size(), groups2.size());
165 for (size_t i = 0; i < groups1.size(); ++i) {
166 ipp::Group* g1 = groups1[i];
167 ipp::Group* g2 = groups2[i];
168 EXPECT_EQ(g1->GetName(), g2->GetName());
169 ASSERT_EQ(g1->GetSize(), g2->GetSize());
170 for (size_t j = 0; j < g1->GetSize(); ++j)
171 CompareCollections(g1->GetCollection(j), g2->GetCollection(j));
172 }
173 EXPECT_EQ(req->Data(), req2->Data());
174 }
175
176 // checks if given frame is a binary representation of given Response
CheckResponse(const BinaryContent & frame,ipp::Response * res,const int32_t request_no=1)177 void CheckResponse(const BinaryContent& frame,
178 ipp::Response* res,
179 const int32_t request_no = 1) {
180 // build output frame from Response and compare with the given frame
181 ipp::Server server(ipp::Version::_1_1, request_no);
182 server.BuildResponseFrom(res);
183 EXPECT_TRUE(server.GetErrorLog().empty());
184 std::vector<uint8_t> bin_data;
185 ASSERT_TRUE(server.WriteResponseFrameTo(&bin_data));
186 EXPECT_TRUE(server.GetErrorLog().empty());
187 EXPECT_EQ(server.GetFrameLength(), bin_data.size());
188 EXPECT_EQ(bin_data, frame.data);
189 // parse the given frame and compare obtained object with the given Response
190 ipp::Client client(ipp::Version::_1_1, request_no - 1);
191 auto res2 = ipp::Response::NewResponse(res->GetOperationId());
192 ASSERT_TRUE(client.ReadResponseFrameFrom(bin_data));
193 EXPECT_TRUE(client.GetErrorLog().empty());
194 ASSERT_TRUE(client.ParseResponseAndSaveTo(res2.get()));
195 EXPECT_TRUE(client.GetErrorLog().empty());
196 // ... compares two response objects
197 EXPECT_EQ(res->StatusCode(), res2->StatusCode());
198 std::vector<ipp::Group*> groups1 = res->GetAllGroups();
199 std::vector<ipp::Group*> groups2 = res2->GetAllGroups();
200 ASSERT_EQ(groups1.size(), groups2.size());
201 for (size_t i = 0; i < groups1.size(); ++i) {
202 ipp::Group* g1 = groups1[i];
203 ipp::Group* g2 = groups2[i];
204 EXPECT_EQ(g1->GetName(), g2->GetName());
205 ASSERT_EQ(g1->GetSize(), g2->GetSize());
206 for (size_t j = 0; j < g1->GetSize(); ++j)
207 CompareCollections(g1->GetCollection(j), g2->GetCollection(j));
208 }
209 EXPECT_EQ(res->Data(), res2->Data());
210 }
211
TEST(rfc8010,example1)212 TEST(rfc8010, example1) {
213 // A.1. Print-Job Request
214 // The following is an example of a Print-Job request with "job-name",
215 // "copies", and "sides" specified. The "ipp-attribute-fidelity"
216 // attribute is set to 'true' so that the print request will fail if the
217 // "copies" or the "sides" attribute is not supported or their values
218 // are not supported.
219 BinaryContent c;
220 // Octets Symbolic Value Protocol field
221 c.u2(0x0101u); // 1.1 version-number
222 c.u2(0x0002u); // Print-Job operation-id
223 c.u4(0x00000001u); // 1 request-id
224 c.u1(0x01u); // start operation- operation-
225 // attributes attributes-tag
226 c.u1(0x47u); // charset type value-tag
227 c.u2(0x0012u); // name-length
228 c.s("attributes-charset"); // attributes-charset name
229 c.u2(0x0005u); // value-length
230 c.s("utf-8"); // UTF-8 value
231 c.u1(0x48u); // natural-language value-tag
232 // type
233 c.u2(0x001bu); // name-length
234 c.s("attributes-natural-language"); // attributes-natural- name
235 // language
236 c.u2(0x0005u); // value-length
237 c.s("en-us"); // en-US value
238 c.u1(0x45u); // uri type value-tag
239 c.u2(0x000bu); // name-length
240 c.s("printer-uri"); // printer-uri name
241 c.u2(0x002cu); // value-length
242 c.s("ipp://printer.example.com/ipp/"); // printer pinetree value
243 c.s("print/pinetree");
244 c.u1(0x42u); // nameWithoutLanguage value-tag
245 // type
246 c.u2(0x0008u); // name-length
247 c.s("job-name"); // job-name name
248 c.u2(0x0006u); // value-length
249 c.s("foobar"); // foobar value
250 c.u1(0x22u); // boolean type value-tag
251 c.u2(0x0016u); // name-length
252 c.s("ipp-attribute-fidelity"); // ipp-attribute- name
253 // fidelity
254 c.u2(0x0001u); // value-length
255 c.u1(0x01u); // true value
256 c.u1(0x02u); // start job-attributes job-attributes-
257 // tag
258 c.u1(0x21u); // integer type value-tag
259 c.u2(0x0006u); // name-length
260 c.s("copies"); // copies name
261 c.u2(0x0004u); // value-length
262 c.u4(0x00000014u); // 20 value
263 c.u1(0x44u); // keyword type value-tag
264 c.u2(0x0005u); // name-length
265 c.s("sides"); // sides name
266 c.u2(0x0013u); // value-length
267 c.s("two-sided-long-edge"); // two-sided-long-edge value
268 c.u1(0x03u); // end-of-attributes end-of-
269 // attributes-tag
270 c.s("%!PDF..."); // <PDF Document> data
271
272 ipp::Request_Print_Job r;
273 r.operation_attributes.printer_uri.Set(
274 "ipp://printer.example.com/ipp/print/pinetree");
275 r.operation_attributes.job_name.Set("foobar");
276 r.operation_attributes.ipp_attribute_fidelity.Set(1);
277 r.job_attributes.copies.Set(20);
278 r.job_attributes.sides.Set(ipp::E_sides::two_sided_long_edge);
279 for (char c : std::string("%!PDF..."))
280 r.Data().push_back(static_cast<uint8_t>(c));
281
282 CheckRequest(c, &r);
283 }
284
TEST(rfc8010,example2)285 TEST(rfc8010, example2) {
286 // A.2. Print-Job Response (Successful)
287 // Here is an example of a successful Print-Job response to the previous
288 // Print-Job request. The Printer supported the "copies" and "sides"
289 // attributes and their supplied values. The status-code returned is
290 // 'successful-ok'.
291 BinaryContent c;
292 // Octets Symbolic Value Protocol field
293 c.u2(0x0101u); // 1.1 version-number
294 c.u2(0x0000u); // successful-ok status-code
295 c.u4(0x00000001u); // 1 request-id
296 c.u1(0x01u); // start operation- operation-
297 // attributes attributes-tag
298 c.u1(0x47u); // charset type value-tag
299 c.u2(0x0012u); // name-length
300 c.s("attributes-charset"); // attributes-charset name
301 c.u2(0x0005u); // value-length
302 c.s("utf-8"); // UTF-8 value
303 c.u1(0x48u); // natural-language value-tag
304 // type
305 c.u2(0x001bu); // name-length
306 c.s("attributes-natural-language"); // attributes- name
307 // natural-language
308 c.u2(0x0005u); // value-length
309 c.s("en-us"); // en-US value
310 c.u1(0x41u); // textWithoutLanguag value-tag
311 // e type
312 c.u2(0x000eu); // name-length
313 c.s("status-message"); // status-message name
314 c.u2(0x000du); // value-length
315 c.s("successful-ok"); // successful-ok value
316 c.u1(0x02u); // start job- job-attributes-
317 // attributes tag
318 c.u1(0x21u); // integer value-tag
319 c.u2(0x0006u); // name-length
320 c.s("job-id"); // job-id name
321 c.u2(0x0004u); // value-length
322 c.u4(147); // 147 value
323 c.u1(0x45u); // uri type value-tag
324 c.u2(0x0007u); // name-length
325 c.s("job-uri"); // job-uri name
326 c.u2(0x0030u); // value-length
327 c.s("ipp://printer.example.com/ipp/pr"); // job 147 on value
328 c.s("int/pinetree/147"); // pinetree
329 c.u1(0x23u); // enum type value-tag
330 c.u2(0x0009u); // name-length
331 c.s("job-state"); // job-state name
332 c.u2(0x0004u); // value-length
333 c.u4(0x0003u); // pending value
334 c.u1(0x03u); // end-of-attributes end-of-
335 // attributes-tag
336
337 ipp::Response_Print_Job r;
338 r.job_attributes.job_id.Set(147);
339 r.job_attributes.job_uri.Set(
340 "ipp://printer.example.com/ipp/print/pinetree/147");
341 r.job_attributes.job_state.Set(ipp::E_job_state::pending);
342
343 CheckResponse(c, &r);
344 }
345
TEST(rfc8010,example3)346 TEST(rfc8010, example3) {
347 // A.3. Print-Job Response (Failure)
348 // Here is an example of an unsuccessful Print-Job response to the
349 // previous Print-Job request. It fails because, in this case, the
350 // Printer does not support the "sides" attribute and because the value
351 // '20' for the "copies" attribute is not supported. Therefore, no Job
352 // is created, and neither a "job-id" nor a "job-uri" operation
353 // attribute is returned. The error code returned is 'client-error-
354 // attributes-or-values-not-supported' (0x040b).
355 BinaryContent c;
356 // Octets Symbolic Value Protocol
357 // field
358 c.u2(0x0101u); // 1.1 version-
359 // number
360 c.u2(0x040bu); // client-error-attributes-or- status-code
361 // values-not-supported
362 c.u4(0x00000001u); // 1 request-id
363 c.u1(0x01u); // start operation-attributes operation-
364 // attributes
365 // tag
366 c.u1(0x47u); // charset type value-tag
367 c.u2(0x0012u); // name-length
368 c.s("attributes-charset"); // attributes-charset name
369 c.u2(0x0005u); // value-length
370 c.s("utf-8"); // UTF-8 value
371 c.u1(0x48u); // natural-language type value-tag
372 c.u2(0x001bu); // name-length
373 c.s("attributes-natural-language"); // attributes-natural-language name
374 c.u2(0x0005u); // value-length
375 c.s("en-us"); // en-US value
376 c.u1(0x41u); // textWithoutLanguage type value-tag
377 c.u2(0x000eu); // name-length
378 c.s("status-message"); // status-message name
379 c.u2(0x002fu); // value-length
380 c.s("client-error-attributes-or-"); // client-error-attributes-or- value
381 c.s("values-not-supported"); // values-not-supported
382 c.u1(0x05u); // start unsupported- unsupported-
383 // attributes attributes
384 // tag
385 c.u1(0x21u); // integer type value-tag
386 c.u2(0x0006u); // name-length
387 c.s("copies"); // copies name
388 c.u2(0x0004u); // value-length
389 c.u4(0x00000014u); // 20 value
390 c.u1(0x10u); // unsupported (type) value-tag
391 c.u2(0x0005u); // name-length
392 c.s("sides"); // sides name
393 c.u2(0x0000u); // value-length
394 c.u1(0x03u); // end-of-attributes end-of-
395 // attributes-
396 // tag
397
398 ipp::Response_Print_Job r;
399 r.StatusCode() =
400 ipp::E_status_code::client_error_attributes_or_values_not_supported;
401 ipp::Attribute* a = r.unsupported_attributes.AddUnknownAttribute(
402 "copies", true, ipp::AttrType::integer);
403 a->SetValue(20);
404 a = r.unsupported_attributes.AddUnknownAttribute("sides", true,
405 ipp::AttrType::integer);
406 a->SetState(ipp::AttrState::unsupported);
407
408 CheckResponse(c, &r);
409 }
410
TEST(rfc8010,example4)411 TEST(rfc8010, example4) {
412 // A.4. Print-Job Response (Success with Attributes Ignored)
413 // Here is an example of a successful Print-Job response to a Print-Job
414 // request like the previous Print-Job request, except that the value of
415 // "ipp-attribute-fidelity" is 'false'. The print request succeeds,
416 // even though, in this case, the Printer supports neither the "sides"
417 // attribute nor the value '20' for the "copies" attribute. Therefore,
418 // a Job is created and both a "job-id" and a "job-uri" operation
419 // attribute are returned. The unsupported attributes are also returned
420 // in an Unsupported Attributes group. The error code returned is
421 // 'successful-ok-ignored-or-substituted-attributes' (0x0001).
422 BinaryContent c;
423 // Octets Symbolic Value Protocol
424 // field
425 c.u2(0x0101u); // 1.1 version-number
426 c.u2(0x0001u); // successful-ok-ignored-or- status-coder.StatusCode() =
427 // static_cast<uint16_t>(ipp::E_status_code::successful_ok);
428 // substituted-attributes
429 c.u4(0x00000001u); // 1 request-id
430 c.u1(0x01u); // start operation-attributes operation-
431 // attributes-tag
432 c.u1(0x47u); // charset type value-tag
433 c.u2(0x0012u); // name-length
434 c.s("attributes-charset"); // attributes-charset name
435 c.u2(0x0005u); // value-length
436 c.s("utf-8"); // UTF-8 value
437 c.u1(0x48u); // natural-language type value-tag
438 c.u2(0x001bu); // name-length
439 c.s("attributes-natural-"); // attributes-natural-language name
440 c.s("language");
441 c.u2(0x0005u); // value-length
442 c.s("en-us"); // en-US value
443 c.u1(0x41u); // textWithoutLanguage type value-tag
444 c.u2(0x000eu); // name-length
445 c.s("status-message"); // status-message name
446 c.u2(0x002fu); // value-length
447 c.s("successful-ok-ignored-or-"); // successful-ok-ignored-or- value
448 c.s("substituted-attributes"); // substituted-attributes
449 c.u1(0x05u); // start unsupported- unsupported-
450 // attributes attributes tag
451 c.u1(0x21u); // integer type value-tag
452 c.u2(0x0006u); // name-length
453 c.s("copies"); // copies name
454 c.u2(0x0004u); // value-length
455 c.u4(0x00000014u); // 20 value
456 c.u1(0x10u); // unsupported (type) value-tag
457 c.u2(0x0005u); // name-length
458 c.s("sides"); // sides name
459 c.u2(0x0000u); // value-length
460 c.u1(0x02u); // start job-attributes job-
461 // attributes-tag
462 c.u1(0x21u); // integer value-tag
463 c.u2(0x0006u); // name-length
464 c.s("job-id"); // job-id name
465 c.u2(0x0004u); // value-length
466 c.u4(147); // 147 value
467 c.u1(0x45u); // uri type value-tag
468 c.u2(0x0007u); // name-length
469 c.s("job-uri"); // job-uri name
470 c.u2(0x0030u); // value-length
471 c.s("ipp://printer.example.com/"); // job 147 on pinetree value
472 c.s("ipp/print/pinetree/147");
473 c.u1(0x23u); // enum type value-tag
474 c.u2(0x0009u); // name-length
475 c.s("job-state"); // job-state name
476 c.u2(0x0004u); // value-length
477 c.u4(0x0003u); // pending value
478 c.u1(0x03u); // end-of-attributes end-of-
479 // attributes-tag
480
481 ipp::Response_Print_Job r;
482 r.StatusCode() =
483 ipp::E_status_code::successful_ok_ignored_or_substituted_attributes;
484 ipp::Attribute* a = r.unsupported_attributes.AddUnknownAttribute(
485 "copies", true, ipp::AttrType::integer);
486 a->SetValue(20);
487 a = r.unsupported_attributes.AddUnknownAttribute("sides", true,
488 ipp::AttrType::integer);
489 a->SetState(ipp::AttrState::unsupported);
490 r.job_attributes.job_id.Set(147);
491 r.job_attributes.job_uri.Set(
492 "ipp://printer.example.com/ipp/print/pinetree/147");
493 r.job_attributes.job_state.Set(ipp::E_job_state::pending);
494
495 CheckResponse(c, &r);
496 }
497
TEST(rfc8010,example5)498 TEST(rfc8010, example5) {
499 // A.5. Print-URI Request
500 // The following is an example of Print-URI request with "copies" and
501 // "job-name" parameters:
502 BinaryContent c;
503 // Octets Symbolic Value Protocol field
504 c.u2(0x0101u); // 1.1 version-number
505 c.u2(0x0003u); // Print-URI operation-id
506 c.u4(0x00000001u); // 1 request-id
507 c.u1(0x01u); // start operation- operation-
508 // attributes attributes-tag
509 c.u1(0x47u); // charset type value-tag
510 c.u2(0x0012u); // name-length
511 c.s("attributes-charset"); // attributes-charset name
512 c.u2(0x0005u); // value-length
513 c.s("utf-8"); // UTF-8 value
514 c.u1(0x48u); // natural-language value-tag
515 // type
516 c.u2(0x001bu); // name-length
517 c.s("attributes-natural-language"); // attributes-natural- name
518 // language
519 c.u2(0x0005u); // value-length
520 c.s("en-us"); // en-US value
521 c.u1(0x45u); // uri type value-tag
522 c.u2(0x000bu); // name-length
523 c.s("printer-uri"); // printer-uri name
524 c.u2(0x002cu); // value-length
525 c.s("ipp://printer.example.com/ipp/"); // printer pinetree value
526 c.s("print/pinetree");
527 c.u1(0x45u); // uri type value-tag
528 c.u2(0x000cu); // name-length
529 c.s("document-uri"); // document-uri name
530 c.u2(0x0019u); // value-length
531 c.s("ftp://foo.example.com/foo"); // ftp://foo.example.co value
532 // m/foo
533 c.u1(0x42u); // nameWithoutLanguage value-tag
534 // type
535 c.u2(0x0008u); // name-length
536 c.s("job-name"); // job-name name
537 c.u2(0x0006u); // value-length
538 c.s("foobar"); // foobar value
539 c.u1(0x02u); // start job-attributes job-attributes-
540 // tag
541 c.u1(0x21u); // integer type value-tag
542 c.u2(0x0006u); // name-length
543 c.s("copies"); // copies name
544 c.u2(0x0004u); // value-length
545 c.u4(0x00000001u); // 1 value
546 c.u1(0x03u); // end-of-attributes end-of-
547 // attributes-tag
548
549 ipp::Request_Print_URI r;
550 r.operation_attributes.printer_uri.Set(
551 "ipp://printer.example.com/ipp/print/pinetree");
552 r.operation_attributes.document_uri.Set("ftp://foo.example.com/foo");
553 r.operation_attributes.job_name.Set("foobar");
554 r.job_attributes.copies.Set(1);
555
556 CheckRequest(c, &r);
557 }
558
TEST(rfc8010,example6)559 TEST(rfc8010, example6) {
560 // A.6. Create-Job Request
561 // The following is an example of Create-Job request with no parameters
562 // and no attributes:
563 BinaryContent c;
564 // Octets Symbolic Value Protocol field
565 c.u2(0x0101u); // 1.1 version-number
566 c.u2(0x0005u); // Create-Job operation-id
567 c.u4(0x00000001u); // 1 request-id
568 c.u1(0x01u); // start operation- operation-
569 // attributes attributes-tag
570 c.u1(0x47u); // charset type value-tag
571 c.u2(0x0012u); // name-length
572 c.s("attributes-charset"); // attributes-charset name
573 c.u2(0x0005u); // value-length
574 c.s("utf-8"); // UTF-8 value
575 c.u1(0x48u); // natural-language value-tag
576 // type
577 c.u2(0x001bu); // name-length
578 c.s("attributes-natural-language"); // attributes-natural- name
579 // language
580 c.u2(0x0005u); // value-length
581 c.s("en-us"); // en-US value
582 c.u1(0x45u); // uri type value-tag
583 c.u2(0x000bu); // name-length
584 c.s("printer-uri"); // printer-uri name
585 c.u2(0x002cu); // value-length
586 c.s("ipp://printer.example.com/ipp/"); // printer pinetree value
587 c.s("print/pinetree");
588 c.u1(0x03u); // end-of-attributes end-of-
589 // attributes-tag
590
591 ipp::Request_Create_Job r;
592 r.operation_attributes.printer_uri.Set(
593 "ipp://printer.example.com/ipp/print/pinetree");
594
595 CheckRequest(c, &r);
596 }
597
TEST(rfc8010,example7)598 TEST(rfc8010, example7) {
599 // A.7. Create-Job Request with Collection Attributes
600 // The following is an example of Create-Job request with the "media-
601 // col" collection attribute [PWG5100.3] with the value "media-
602 // size={x-dimension=21000 y-dimension=29700} media-type='stationery'":
603 BinaryContent c;
604 // Octets Symbolic Value Protocol field
605 c.u2(0x0101u); // 1.1 version-number
606 c.u2(0x0005u); // Create-Job operation-id
607 c.u4(0x00000001u); // 1 request-id
608 c.u1(0x01u); // start operation- operation-
609 // attributes attributes-tag
610 c.u1(0x47u); // charset type value-tag
611 c.u2(0x0012u); // name-length
612 c.s("attributes-charset"); // attributes-charset name
613 c.u2(0x0005u); // value-length
614 c.s("utf-8"); // UTF-8 value
615 c.u1(0x48u); // natural-language value-tag
616 // type
617 c.u2(0x001bu); // name-length
618 c.s("attributes-natural-language"); // attributes-natural- name
619 // language
620 c.u2(0x0005u); // value-length
621 c.s("en-us"); // en-US value
622 c.u1(0x45u); // uri type value-tag
623 c.u2(0x000bu); // name-length
624 c.s("printer-uri"); // printer-uri name
625 c.u2(0x002cu); // value-length
626 c.s("ipp://printer.example.com/ipp/"); // printer pinetree value
627 c.s("print/pinetree");
628 c.u1(static_cast<int>(ipp::GroupTag::job_attributes)); // missing byte ?
629 c.u1(0x34u); // begCollection value-tag
630 c.u2(0x0009u); // 9 name-length
631 c.s("media-col"); // media-col name
632 c.u2(0x0000u); // 0 value-length
633 c.u1(0x4au); // memberAttrName value-tag
634 c.u2(0x0000u); // 0 name-length
635 c.u2(0x000au); // 10 value-length
636 c.s("media-size"); // media-size value (member-
637 // name)
638 c.u1(0x34u); // begCollection member-value-tag
639 c.u2(0x0000u); // 0 name-length
640 c.u2(0x0000u); // 0 member-value-
641 // length
642 c.u1(0x4au); // memberAttrName value-tag
643 c.u2(0x0000u); // 0 name-length
644 c.u2(0x000bu); // 11 value-length
645 c.s("x-dimension"); // x-dimension value (member-
646 // name)
647 c.u1(0x21u); // integer member-value-tag
648 c.u2(0x0000u); // 0 name-length
649 c.u2(0x0004u); // 4 member-value-
650 // length
651 c.u4(0x00005208u); // 21000 member-value
652 c.u1(0x4au); // memberAttrName value-tag
653 c.u2(0x0000u); // 0 name-length
654 c.u2(0x000bu); // 11 value-length
655 c.s("y-dimension"); // y-dimension value (member-
656 // name)
657 c.u1(0x21u); // integer member-value-tag
658 c.u2(0x0000u); // 0 name-length
659 c.u2(0x0004u); // 4 member-value-
660 // length
661 c.u4(0x00007404u); // 29700 member-value
662 c.u1(0x37u); // endCollection end-value-tag
663 c.u2(0x0000u); // 0 end-name-length
664 c.u2(0x0000u); // 0 end-value-length
665 c.u1(0x4au); // memberAttrName value-tag
666 c.u2(0x0000u); // 0 name-length
667 c.u2(0x000au); // 10 value-length
668 c.s("media-type"); // media-type value (member-
669 // name)
670 c.u1(0x44u); // keyword member-value-tag
671 c.u2(0x0000u); // 0 name-length
672 c.u2(0x000au); // 10 member-value-
673 // length
674 c.s("stationery"); // stationery member-value
675 c.u1(0x37u); // endCollection end-value-tag
676 c.u2(0x0000u); // 0 end-name-length
677 c.u2(0x0000u); // 0 end-value-length
678 c.u1(0x03u); // end-of-attributes end-of-
679 // attributes-tag
680
681 ipp::Request_Create_Job r;
682 r.operation_attributes.printer_uri.Set(
683 "ipp://printer.example.com/ipp/print/pinetree");
684 r.job_attributes.media_col.media_size.x_dimension.Set(21000);
685 r.job_attributes.media_col.media_size.y_dimension.Set(29700);
686 r.job_attributes.media_col.media_type.Set(ipp::E_media_type::stationery);
687
688 CheckRequest(c, &r);
689 }
690
TEST(rfc8010,example8)691 TEST(rfc8010, example8) {
692 // A.8. Get-Jobs Request
693 // The following is an example of Get-Jobs request with parameters but
694 // no attributes:
695 BinaryContent c;
696 // Octets Symbolic Value Protocol field
697 c.u2(0x0101u); // 1.1 version-number
698 c.u2(0x000au); // Get-Jobs operation-id
699 c.u4(0x0000007bu); // 123 request-id
700 c.u1(0x01u); // start operation- operation-
701 // attributes attributes-tag
702 c.u1(0x47u); // charset type value-tag
703 c.u2(0x0012u); // name-length
704 c.s("attributes-charset"); // attributes-charset name
705 c.u2(0x0005u); // value-length
706 c.s("utf-8"); // UTF-8 value
707 c.u1(0x48u); // natural-language value-tag
708 // type
709 c.u2(0x001bu); // name-length
710 c.s("attributes-natural-language"); // attributes-natural- name
711 // language
712 c.u2(0x0005u); // value-length
713 c.s("en-us"); // en-US value
714 c.u1(0x45u); // uri type value-tag
715 c.u2(0x000bu); // name-length
716 c.s("printer-uri"); // printer-uri name
717 c.u2(0x002cu); // value-length
718 c.s("ipp://printer.example.com/ipp/"); // printer pinetree value
719 c.s("print/pinetree");
720 c.u1(0x21u); // integer type value-tag
721 c.u2(0x0005u); // name-length
722 c.s("limit"); // limit name
723 c.u2(0x0004u); // value-length
724 c.u4(0x00000032u); // 50 value
725 c.u1(0x44u); // keyword type value-tag
726 c.u2(0x0014u); // name-length
727 c.s("requested-attributes"); // requested-attributes name
728 c.u2(0x0006u); // value-length
729 c.s("job-id"); // job-id value
730 c.u1(0x44u); // keyword type value-tag
731 c.u2(0x0000u); // additional value name-length
732 c.u2(0x0008u); // value-length
733 c.s("job-name"); // job-name value
734 c.u1(0x44u); // keyword type value-tag
735 c.u2(0x0000u); // additional value name-length
736 c.u2(0x000fu); // value-length
737 c.s("document-format"); // document-format value
738 c.u1(0x03u); // end-of-attributes end-of-
739 // attributes-tag
740
741 ipp::Request_Get_Jobs r;
742 r.operation_attributes.printer_uri.Set(
743 "ipp://printer.example.com/ipp/print/pinetree");
744 r.operation_attributes.limit.Set(50);
745 r.operation_attributes.requested_attributes.Set(
746 {"job-id", "job-name", "document-format"});
747
748 CheckRequest(c, &r, 123);
749 }
750
TEST(rfc8010,example9)751 TEST(rfc8010, example9) {
752 // A.9. Get-Jobs Response
753 // The following is an example of a Get-Jobs response from a previous
754 // request with three Jobs. The Printer returns no information about
755 // the second Job (because of security reasons):
756 BinaryContent c;
757 // Octets Symbolic Value Protocol field
758 c.u2(0x0101u); // 1.1 version-number
759 c.u2(0x0000u); // successful-ok status-code
760 c.u4(0x0000007bu); // 123 request-id (echoed
761 // back)
762 c.u1(0x01u); // start operation- operation-attributes-
763 // attributes tag
764 c.u1(0x47u); // charset type value-tag
765 c.u2(0x0012u); // name-length
766 c.s("attributes-charset"); // attributes-charset name
767 c.u2(0x0005u); // value-length
768 c.s("utf-8"); // UTF-8 value
769 c.u1(0x48u); // natural-language type value-tag
770 c.u2(0x001bu); // name-length
771 c.s("attributes-natural-"); // attributes-natural- name
772 c.s("language"); // language
773 c.u2(0x0005u); // value-length
774 c.s("en-us"); // en-US value
775 c.u1(0x41u); // textWithoutLanguage value-tag
776 // type
777 c.u2(0x000eu); // name-length
778 c.s("status-message"); // status-message name
779 c.u2(0x000du); // value-length
780 c.s("successful-ok"); // successful-ok value
781 c.u1(0x02u); // start job-attributes job-attributes-tag
782 // (1st object)
783 c.u1(0x21u); // integer type value-tag
784 c.u2(0x0006u); // name-length
785 c.s("job-id"); // job-id name
786 c.u2(0x0004u); // value-length
787 c.u4(147); // 147 value
788 c.u1(0x36u); // nameWithLanguage value-tag
789 c.u2(0x0008u); // name-length
790 c.s("job-name"); // job-name name
791 c.u2(0x000cu); // value-length
792 c.u2(0x0005u); // sub-value-length
793 c.s("fr-ca"); // fr-CA value
794 c.u2(0x0003u); // sub-value-length
795 c.s("fou"); // fou name
796 c.u1(0x02u); // start job-attributes job-attributes-tag
797 // (2nd object)
798 c.u1(0x02u); // start job-attributes job-attributes-tag
799 // (3rd object)
800 c.u1(0x21u); // integer type value-tag
801 c.u2(0x0006u); // name-length
802 c.s("job-id"); // job-id name
803 c.u2(0x0004u); // value-length
804 c.u4(149); // 149 value
805 c.u1(0x36u); // nameWithLanguage value-tag
806 c.u2(0x0008u); // name-length
807 c.s("job-name"); // job-name name
808 c.u2(0x0012u); // value-length
809 c.u2(0x0005u); // sub-value-length
810 c.s("de-CH"); // de-CH value
811 c.u2(0x0009u); // sub-value-length
812 c.s("isch guet"); // isch guet name
813 c.u1(0x03u); // end-of-attributes end-of-attributes-tag
814
815 ipp::Response_Get_Jobs r;
816 r.job_attributes[0].job_id.Set(147);
817 r.job_attributes[0].job_name.Set(ipp::StringWithLanguage("fou", "fr-ca"));
818 r.job_attributes[2].job_id.Set(149);
819 r.job_attributes[2].job_name.Set(
820 ipp::StringWithLanguage("isch guet", "de-CH"));
821
822 CheckResponse(c, &r, 123);
823 }
824
825 } // namespace
826
main(int argc,char ** argv)827 int main(int argc, char** argv) {
828 ::testing::InitGoogleTest(&argc, argv);
829 return RUN_ALL_TESTS();
830 }
831