1 // Copyright 2019 The Chromium 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 "discovery/mdns/mdns_writer.h"
6
7 #include <memory>
8 #include <vector>
9
10 #include "discovery/mdns/testing/mdns_test_util.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
13
14 namespace openscreen {
15 namespace discovery {
16
17 using testing::ElementsAreArray;
18
19 namespace {
20
21 constexpr std::chrono::seconds kTtl{120};
22
23 template <class T>
TestWriteEntrySucceeds(const T & entry,const uint8_t * expected_data,size_t expected_size)24 void TestWriteEntrySucceeds(const T& entry,
25 const uint8_t* expected_data,
26 size_t expected_size) {
27 std::vector<uint8_t> buffer(expected_size);
28 MdnsWriter writer(buffer.data(), buffer.size());
29 EXPECT_TRUE(writer.Write(entry));
30 EXPECT_EQ(writer.remaining(), UINT64_C(0));
31 EXPECT_THAT(buffer, ElementsAreArray(expected_data, expected_size));
32 }
33
34 template <class T>
TestWriteEntryInsufficientBuffer(const T & entry)35 void TestWriteEntryInsufficientBuffer(const T& entry) {
36 std::vector<uint8_t> buffer(entry.MaxWireSize() - 1);
37 MdnsWriter writer(buffer.data(), buffer.size());
38 EXPECT_FALSE(writer.Write(entry));
39 // There should be no side effects for failing to write an entry. The
40 // underlying pointer should not have changed.
41 EXPECT_EQ(writer.offset(), UINT64_C(0));
42 }
43
44 } // namespace
45
TEST(MdnsWriterTest,WriteDomainName)46 TEST(MdnsWriterTest, WriteDomainName) {
47 // clang-format off
48 constexpr uint8_t kExpectedResult[] = {
49 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
50 0x05, 'l', 'o', 'c', 'a', 'l',
51 0x00
52 };
53 // clang-format on
54 uint8_t result[sizeof(kExpectedResult)];
55 MdnsWriter writer(result, sizeof(kExpectedResult));
56 ASSERT_TRUE(writer.Write(DomainName{"testing", "local"}));
57 EXPECT_EQ(0UL, writer.remaining());
58 EXPECT_EQ(0, memcmp(kExpectedResult, result, sizeof(result)));
59 }
60
TEST(MdnsWriterTest,WriteDomainName_CompressedMessage)61 TEST(MdnsWriterTest, WriteDomainName_CompressedMessage) {
62 // clang-format off
63 constexpr uint8_t kExpectedResultCompressed[] = {
64 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
65 0x05, 'l', 'o', 'c', 'a', 'l',
66 0x00,
67 0x06, 'p', 'r', 'e', 'f', 'i', 'x',
68 0xC0, 0x08, // byte 8
69 0x03, 'n', 'e', 'w',
70 0xC0, 0x0F, // byte 15
71 0xC0, 0x0F, // byte 15
72 };
73 // clang-format on
74 uint8_t result[sizeof(kExpectedResultCompressed)];
75 MdnsWriter writer(result, sizeof(kExpectedResultCompressed));
76 ASSERT_TRUE(writer.Write(DomainName{"testing", "local"}));
77 ASSERT_TRUE(writer.Write(DomainName{"prefix", "local"}));
78 ASSERT_TRUE(writer.Write(DomainName{"new", "prefix", "local"}));
79 ASSERT_TRUE(writer.Write(DomainName{"prefix", "local"}));
80 EXPECT_EQ(0UL, writer.remaining());
81 EXPECT_THAT(std::vector<uint8_t>(result, result + sizeof(result)),
82 ElementsAreArray(kExpectedResultCompressed));
83 }
84
TEST(MdnsWriterTest,WriteDomainName_NotEnoughSpace)85 TEST(MdnsWriterTest, WriteDomainName_NotEnoughSpace) {
86 // clang-format off
87 constexpr uint8_t kExpectedResultCompressed[] = {
88 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
89 0x05, 'l', 'o', 'c', 'a', 'l',
90 0x00,
91 0x09, 'd', 'i', 'f', 'f', 'e', 'r', 'e', 'n', 't',
92 0x06, 'd', 'o', 'm', 'a', 'i', 'n',
93 0x00
94 };
95 // clang-format on
96 uint8_t result[sizeof(kExpectedResultCompressed)];
97 MdnsWriter writer(result, sizeof(kExpectedResultCompressed));
98 ASSERT_TRUE(writer.Write(DomainName{"testing", "local"}));
99 // Not enough space to write this domain name. Failure to write it must not
100 // affect correct successful write of the next domain name.
101 ASSERT_FALSE(writer.Write(DomainName{"a", "different", "domain"}));
102 ASSERT_TRUE(writer.Write(DomainName{"different", "domain"}));
103 EXPECT_EQ(0UL, writer.remaining());
104 EXPECT_THAT(std::vector<uint8_t>(result, result + sizeof(result)),
105 ElementsAreArray(kExpectedResultCompressed));
106 }
107
TEST(MdnsWriterTest,WriteDomainName_Long)108 TEST(MdnsWriterTest, WriteDomainName_Long) {
109 constexpr char kLongLabel[] =
110 "12345678901234567890123456789012345678901234567890";
111 // clang-format off
112 constexpr uint8_t kExpectedResult[] = {
113 0x32, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
114 '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
115 '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
116 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
117 0x32, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
118 '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
119 '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
120 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
121 0x32, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
122 '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
123 '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
124 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
125 0x32, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
126 '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
127 '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
128 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
129 0x00,
130 };
131 // clang-format on
132 DomainName name{kLongLabel, kLongLabel, kLongLabel, kLongLabel};
133 uint8_t result[sizeof(kExpectedResult)];
134 MdnsWriter writer(result, sizeof(kExpectedResult));
135 ASSERT_TRUE(writer.Write(name));
136 EXPECT_EQ(0UL, writer.remaining());
137 EXPECT_EQ(0, memcmp(kExpectedResult, result, sizeof(result)));
138 }
139
TEST(MdnsWriterTest,WriteDomainName_Empty)140 TEST(MdnsWriterTest, WriteDomainName_Empty) {
141 DomainName name;
142 uint8_t result[256];
143 MdnsWriter writer(result, sizeof(result));
144 EXPECT_FALSE(writer.Write(name));
145 // The writer should not have moved its internal pointer when it fails to
146 // write. It should fail without any side effects.
147 EXPECT_EQ(0u, writer.offset());
148 }
149
TEST(MdnsWriterTest,WriteDomainName_NoCompressionForBigOffsets)150 TEST(MdnsWriterTest, WriteDomainName_NoCompressionForBigOffsets) {
151 // clang-format off
152 constexpr uint8_t kExpectedResultCompressed[] = {
153 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
154 0x05, 'l', 'o', 'c', 'a', 'l',
155 0x00,
156 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
157 0x05, 'l', 'o', 'c', 'a', 'l',
158 0x00,
159 };
160 // clang-format on
161
162 DomainName name{"testing", "local"};
163 // Maximum supported value for label pointer offset is 0x3FFF.
164 // Labels written into a buffer at greater offsets must not
165 // produce compression label pointers.
166 std::vector<uint8_t> buffer(0x4000 + sizeof(kExpectedResultCompressed));
167 {
168 MdnsWriter writer(buffer.data(), buffer.size());
169 writer.Skip(0x4000);
170 ASSERT_TRUE(writer.Write(name));
171 ASSERT_TRUE(writer.Write(name));
172 EXPECT_EQ(0UL, writer.remaining());
173 }
174 buffer.erase(buffer.begin(), buffer.begin() + 0x4000);
175 EXPECT_THAT(buffer, ElementsAreArray(kExpectedResultCompressed));
176 }
177
TEST(MdnsWriterTest,WriteRawRecordRdata)178 TEST(MdnsWriterTest, WriteRawRecordRdata) {
179 // clang-format off
180 constexpr uint8_t kExpectedRdata[] = {
181 0x00, 0x08, // RDLENGTH = 8 bytes
182 0x05, 'c', 'n', 'a', 'm', 'e', 0xc0, 0x00,
183 };
184 // clang-format on
185 TestWriteEntrySucceeds(
186 RawRecordRdata(kExpectedRdata + 2, sizeof(kExpectedRdata) - 2),
187 kExpectedRdata, sizeof(kExpectedRdata));
188 }
189
TEST(MdnsWriterTest,WriteRawRecordRdata_InsufficientBuffer)190 TEST(MdnsWriterTest, WriteRawRecordRdata_InsufficientBuffer) {
191 // clang-format off
192 constexpr uint8_t kRawRdata[] = {
193 0x05, 'c', 'n', 'a', 'm', 'e', 0xc0, 0x00,
194 };
195 // clang-format on
196 TestWriteEntryInsufficientBuffer(
197 RawRecordRdata(kRawRdata, sizeof(kRawRdata)));
198 }
199
TEST(MdnsWriterTest,WriteSrvRecordRdata)200 TEST(MdnsWriterTest, WriteSrvRecordRdata) {
201 constexpr uint8_t kExpectedRdata[] = {
202 0x00, 0x15, // RDLENGTH = 21
203 0x00, 0x05, // PRIORITY = 5
204 0x00, 0x06, // WEIGHT = 6
205 0x1f, 0x49, // PORT = 8009
206 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
207 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
208 };
209 TestWriteEntrySucceeds(
210 SrvRecordRdata(5, 6, 8009, DomainName{"testing", "local"}),
211 kExpectedRdata, sizeof(kExpectedRdata));
212 }
213
TEST(MdnsWriterTest,WriteSrvRecordRdata_InsufficientBuffer)214 TEST(MdnsWriterTest, WriteSrvRecordRdata_InsufficientBuffer) {
215 TestWriteEntryInsufficientBuffer(
216 SrvRecordRdata(5, 6, 8009, DomainName{"testing", "local"}));
217 }
218
TEST(MdnsWriterTest,WriteARecordRdata)219 TEST(MdnsWriterTest, WriteARecordRdata) {
220 constexpr uint8_t kExpectedRdata[] = {
221 0x00, 0x4, // RDLENGTH = 4
222 0x08, 0x08, 0x08, 0x08, // ADDRESS = 8.8.8.8
223 };
224 TestWriteEntrySucceeds(ARecordRdata(IPAddress{8, 8, 8, 8}), kExpectedRdata,
225 sizeof(kExpectedRdata));
226 }
227
TEST(MdnsWriterTest,WriteARecordRdata_InsufficientBuffer)228 TEST(MdnsWriterTest, WriteARecordRdata_InsufficientBuffer) {
229 TestWriteEntryInsufficientBuffer(ARecordRdata(IPAddress{8, 8, 8, 8}));
230 }
231
TEST(MdnsWriterTest,WriteAAAARecordRdata)232 TEST(MdnsWriterTest, WriteAAAARecordRdata) {
233 // clang-format off
234 constexpr uint8_t kExpectedRdata[] = {
235 0x00, 0x10, // RDLENGTH = 16
236 // ADDRESS = FE80:0000:0000:0000:0202:B3FF:FE1E:8329
237 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x02, 0x02, 0xb3, 0xff, 0xfe, 0x1e, 0x83, 0x29,
239 };
240 // clang-format on
241 TestWriteEntrySucceeds(
242 AAAARecordRdata(IPAddress(IPAddress::Version::kV6, kExpectedRdata + 2)),
243 kExpectedRdata, sizeof(kExpectedRdata));
244 }
245
TEST(MdnsWriterTest,WriteAAAARecordRdata_InsufficientBuffer)246 TEST(MdnsWriterTest, WriteAAAARecordRdata_InsufficientBuffer) {
247 // clang-format off
248 constexpr uint16_t kAAAARdata[] = {
249 // ADDRESS = FE80:0000:0000:0000:0202:B3FF:FE1E:8329
250 0xfe80, 0x0000, 0x0000, 0x0000,
251 0x0202, 0xb3ff, 0xfe1e, 0x8329,
252 };
253 // clang-format on
254 TestWriteEntryInsufficientBuffer(AAAARecordRdata(IPAddress(kAAAARdata)));
255 }
256
TEST(MdnsWriterTest,WriteNSECRecordRdata)257 TEST(MdnsWriterTest, WriteNSECRecordRdata) {
258 const DomainName domain{"testing", "local"};
259 NsecRecordRdata(DomainName{"mydevice", "testing", "local"}, DnsType::kA,
260 DnsType::kTXT, DnsType::kSRV, DnsType::kNSEC);
261
262 // clang-format off
263 constexpr uint8_t kExpectedRdata[] = {
264 0x00, 0x20, // RDLENGTH = 32
265 0x08, 'm', 'y', 'd', 'e', 'v', 'i', 'c', 'e',
266 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
267 0x05, 'l', 'o', 'c', 'a', 'l',
268 0x00,
269 // It takes 8 bytes to encode the kA and kSRV records because:
270 // - Both record types have value less than 256, so they are both in window
271 // block 1.
272 // - The bitmap length for this block is always a single byte
273 // - DnsTypes have the following values:
274 // - kA = 1 (encoded in byte 1)
275 // kTXT = 16 (encoded in byte 3)
276 // - kSRV = 33 (encoded in byte 5)
277 // - kNSEC = 47 (encoded in 6 bytes)
278 // - The largest of these is 47, so 6 bytes are needed to encode this data.
279 // So the full encoded version is:
280 // 00000000 00000110 01000000 00000000 10000000 00000000 0100000 00000001
281 // |window| | size | | 0-7 | | 8-15 | |16-23 | |24-31 | |32-39 | |40-47 |
282 0x00, 0x06, 0x40, 0x00, 0x80, 0x00, 0x40, 0x01
283 };
284 // clang-format on
285 TestWriteEntrySucceeds(
286 NsecRecordRdata(DomainName{"mydevice", "testing", "local"}, DnsType::kA,
287 DnsType::kTXT, DnsType::kSRV, DnsType::kNSEC),
288 kExpectedRdata, sizeof(kExpectedRdata));
289 }
290
TEST(MdnsWriterTest,WriteNSECRecordRdata_InsufficientBuffer)291 TEST(MdnsWriterTest, WriteNSECRecordRdata_InsufficientBuffer) {
292 TestWriteEntryInsufficientBuffer(
293 NsecRecordRdata(DomainName{"mydevice", "testing", "local"}, DnsType::kA,
294 DnsType::kTXT, DnsType::kSRV, DnsType::kNSEC));
295 }
296
TEST(MdnsWriterTest,WritePtrRecordRdata)297 TEST(MdnsWriterTest, WritePtrRecordRdata) {
298 // clang-format off
299 constexpr uint8_t kExpectedRdata[] = {
300 0x00, 0x18, // RDLENGTH = 24
301 0x08, 'm', 'y', 'd', 'e', 'v', 'i', 'c', 'e',
302 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
303 0x05, 'l', 'o', 'c', 'a', 'l',
304 0x00,
305 };
306 // clang-format on
307 TestWriteEntrySucceeds(
308 PtrRecordRdata(DomainName{"mydevice", "testing", "local"}),
309 kExpectedRdata, sizeof(kExpectedRdata));
310 }
311
TEST(MdnsWriterTest,WritePtrRecordRdata_InsufficientBuffer)312 TEST(MdnsWriterTest, WritePtrRecordRdata_InsufficientBuffer) {
313 TestWriteEntryInsufficientBuffer(
314 PtrRecordRdata(DomainName{"mydevice", "testing", "local"}));
315 }
316
TEST(MdnsWriterTest,WriteTxtRecordRdata)317 TEST(MdnsWriterTest, WriteTxtRecordRdata) {
318 // clang-format off
319 constexpr uint8_t kExpectedRdata[] = {
320 0x00, 0x0C, // RDLENGTH = 12
321 0x05, 'f', 'o', 'o', '=', '1',
322 0x05, 'b', 'a', 'r', '=', '2',
323 };
324 // clang-format on
325 TestWriteEntrySucceeds(MakeTxtRecord({"foo=1", "bar=2"}), kExpectedRdata,
326 sizeof(kExpectedRdata));
327 }
328
TEST(MdnsWriterTest,WriteTxtRecordRdata_Empty)329 TEST(MdnsWriterTest, WriteTxtRecordRdata_Empty) {
330 constexpr uint8_t kExpectedRdata[] = {
331 0x00, 0x01, // RDLENGTH = 1
332 0x00, // empty string
333 };
334 TestWriteEntrySucceeds(TxtRecordRdata(), kExpectedRdata,
335 sizeof(kExpectedRdata));
336 }
337
TEST(MdnsWriterTest,WriteTxtRecordRdata_InsufficientBuffer)338 TEST(MdnsWriterTest, WriteTxtRecordRdata_InsufficientBuffer) {
339 TestWriteEntryInsufficientBuffer(MakeTxtRecord({"foo=1", "bar=2"}));
340 }
341
TEST(MdnsWriterTest,WriteMdnsRecord_ARecordRdata)342 TEST(MdnsWriterTest, WriteMdnsRecord_ARecordRdata) {
343 // clang-format off
344 constexpr uint8_t kExpectedResult[] = {
345 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
346 0x05, 'l', 'o', 'c', 'a', 'l',
347 0x00,
348 0x00, 0x01, // TYPE = A (1)
349 0x80, 0x01, // CLASS = IN (1) | CACHE_FLUSH_BIT
350 0x00, 0x00, 0x00, 0x78, // TTL = 120 seconds
351 0x00, 0x04, // RDLENGTH = 4 bytes
352 0xac, 0x00, 0x00, 0x01, // 172.0.0.1
353 };
354 // clang-format on
355 TestWriteEntrySucceeds(MdnsRecord(DomainName{"testing", "local"}, DnsType::kA,
356 DnsClass::kIN, RecordType::kUnique, kTtl,
357 ARecordRdata(IPAddress{172, 0, 0, 1})),
358 kExpectedResult, sizeof(kExpectedResult));
359 }
360
TEST(MdnsWriterTest,WriteMdnsRecord_PtrRecordRdata)361 TEST(MdnsWriterTest, WriteMdnsRecord_PtrRecordRdata) {
362 // clang-format off
363 constexpr uint8_t kExpectedResult[] = {
364 0x08, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e',
365 0x07, 't', 'e', 's', 't', 'i', 'n', 'g',
366 0x05, 'l', 'o', 'c', 'a', 'l',
367 0x00,
368 0x00, 0x0c, // TYPE = PTR (12)
369 0x00, 0x01, // CLASS = IN (1)
370 0x00, 0x00, 0x00, 0x78, // TTL = 120 seconds
371 0x00, 0x02, // RDLENGTH = 2 bytes
372 0xc0, 0x09, // Domain name label pointer to byte
373 };
374 // clang-format on
375 TestWriteEntrySucceeds(
376 MdnsRecord(DomainName{"_service", "testing", "local"}, DnsType::kPTR,
377 DnsClass::kIN, RecordType::kShared, kTtl,
378 PtrRecordRdata(DomainName{"testing", "local"})),
379 kExpectedResult, sizeof(kExpectedResult));
380 }
381
TEST(MdnsWriterTest,WriteMdnsRecord_InsufficientBuffer)382 TEST(MdnsWriterTest, WriteMdnsRecord_InsufficientBuffer) {
383 TestWriteEntryInsufficientBuffer(MdnsRecord(
384 DomainName{"testing", "local"}, DnsType::kA, DnsClass::kIN,
385 RecordType::kUnique, kTtl, ARecordRdata(IPAddress{172, 0, 0, 1})));
386 }
387
TEST(MdnsWriterTest,WriteMdnsQuestion)388 TEST(MdnsWriterTest, WriteMdnsQuestion) {
389 // clang-format off
390 constexpr uint8_t kExpectedResult[] = {
391 0x04, 'w', 'i', 'r', 'e',
392 0x06, 'f', 'o', 'r', 'm', 'a', 't',
393 0x05, 'l', 'o', 'c', 'a', 'l',
394 0x00,
395 0x00, 0x0c, // TYPE = PTR (12)
396 0x80, 0x01, // CLASS = IN (1) | UNICAST_BIT
397 };
398 // clang-format on
399 TestWriteEntrySucceeds(
400 MdnsQuestion(DomainName{"wire", "format", "local"}, DnsType::kPTR,
401 DnsClass::kIN, ResponseType::kUnicast),
402 kExpectedResult, sizeof(kExpectedResult));
403 }
404
TEST(MdnsWriterTest,WriteMdnsQuestion_InsufficientBuffer)405 TEST(MdnsWriterTest, WriteMdnsQuestion_InsufficientBuffer) {
406 TestWriteEntryInsufficientBuffer(
407 MdnsQuestion(DomainName{"wire", "format", "local"}, DnsType::kPTR,
408 DnsClass::kIN, ResponseType::kUnicast));
409 }
410
TEST(MdnsWriterTest,WriteMdnsMessage)411 TEST(MdnsWriterTest, WriteMdnsMessage) {
412 // clang-format off
413 constexpr uint8_t kExpectedMessage[] = {
414 0x00, 0x01, // ID = 1
415 0x00, 0x00, // FLAGS = None
416 0x00, 0x01, // Question count
417 0x00, 0x00, // Answer count
418 0x00, 0x01, // Authority count
419 0x00, 0x00, // Additional count
420 // Question
421 0x08, 'q', 'u', 'e', 's', 't', 'i', 'o', 'n',
422 0x00,
423 0x00, 0x0c, // TYPE = PTR (12)
424 0x00, 0x01, // CLASS = IN (1)
425 // Authority Record
426 0x04, 'a', 'u', 't', 'h',
427 0x00,
428 0x00, 0x10, // TYPE = TXT (16)
429 0x00, 0x01, // CLASS = IN (1)
430 0x00, 0x00, 0x00, 0x78, // TTL = 120 seconds
431 0x00, 0x0c, // RDLENGTH = 12 bytes
432 0x05, 'f', 'o', 'o', '=', '1',
433 0x05, 'b', 'a', 'r', '=', '2',
434 };
435 // clang-format on
436 MdnsQuestion question(DomainName{"question"}, DnsType::kPTR, DnsClass::kIN,
437 ResponseType::kMulticast);
438
439 MdnsRecord auth_record(DomainName{"auth"}, DnsType::kTXT, DnsClass::kIN,
440 RecordType::kShared, kTtl,
441 MakeTxtRecord({"foo=1", "bar=2"}));
442
443 MdnsMessage message(1, MessageType::Query);
444 message.AddQuestion(question);
445 message.AddAuthorityRecord(auth_record);
446
447 std::vector<uint8_t> buffer(sizeof(kExpectedMessage));
448 MdnsWriter writer(buffer.data(), buffer.size());
449 EXPECT_TRUE(writer.Write(message));
450 EXPECT_EQ(writer.remaining(), UINT64_C(0));
451 EXPECT_THAT(buffer, ElementsAreArray(kExpectedMessage));
452 }
453
TEST(MdnsWriterTest,WriteMdnsMessage_InsufficientBuffer)454 TEST(MdnsWriterTest, WriteMdnsMessage_InsufficientBuffer) {
455 MdnsQuestion question(DomainName{"question"}, DnsType::kPTR, DnsClass::kIN,
456 ResponseType::kMulticast);
457
458 MdnsRecord auth_record(DomainName{"auth"}, DnsType::kTXT, DnsClass::kIN,
459 RecordType::kShared, kTtl,
460 MakeTxtRecord({"foo=1", "bar=2"}));
461
462 MdnsMessage message(1, MessageType::Query);
463 message.AddQuestion(question);
464 message.AddAuthorityRecord(auth_record);
465 TestWriteEntryInsufficientBuffer(message);
466 }
467
468 } // namespace discovery
469 } // namespace openscreen
470