serialize-packed-test.c++ 18.2 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
21 22

#include "serialize-packed.h"
Kenton Varda's avatar
Kenton Varda committed
23
#include <kj/debug.h>
24
#include <kj/compat/gtest.h>
25 26 27 28
#include <string>
#include <stdlib.h>
#include "test-util.h"

29
namespace capnp {
30
namespace _ {  // private
31 32
namespace {

33
class TestPipe: public kj::BufferedInputStream, public kj::OutputStream {
34 35
public:
  TestPipe()
36
      : preferredReadSize(kj::maxValue), readPos(0) {}
37 38 39 40 41
  explicit TestPipe(size_t preferredReadSize)
      : preferredReadSize(preferredReadSize), readPos(0) {}
  ~TestPipe() {}

  const std::string& getData() { return data; }
42 43 44 45 46

  kj::ArrayPtr<const byte> getArray() {
    return kj::arrayPtr(reinterpret_cast<const byte*>(data.data()), data.size());
  }

47
  void resetRead(size_t preferredReadSize = kj::maxValue) {
48 49 50 51 52 53 54 55
    readPos = 0;
    this->preferredReadSize = preferredReadSize;
  }

  bool allRead() {
    return readPos == data.size();
  }

56
  void clear(size_t preferredReadSize = kj::maxValue) {
57 58 59 60 61 62 63 64
    resetRead(preferredReadSize);
    data.clear();
  }

  void write(const void* buffer, size_t size) override {
    data.append(reinterpret_cast<const char*>(buffer), size);
  }

65
  size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override {
66
    KJ_ASSERT(maxBytes <= data.size() - readPos, "Overran end of stream.");
67
    size_t amount = kj::min(maxBytes, kj::max(minBytes, preferredReadSize));
68 69 70 71 72 73
    memcpy(buffer, data.data() + readPos, amount);
    readPos += amount;
    return amount;
  }

  void skip(size_t bytes) override {
74
    KJ_ASSERT(bytes <= data.size() - readPos, "Overran end of stream.");
75 76 77
    readPos += bytes;
  }

78
  kj::ArrayPtr<const byte> tryGetReadBuffer() override {
79
    size_t amount = kj::min(data.size() - readPos, preferredReadSize);
80
    return kj::arrayPtr(reinterpret_cast<const byte*>(data.data() + readPos), amount);
81 82 83 84 85 86 87 88
  }

private:
  size_t preferredReadSize;
  std::string data;
  std::string::size_type readPos;
};

89
void expectPacksTo(kj::ArrayPtr<const byte> unpackedUnaligned, kj::ArrayPtr<const byte> packed) {
90 91
  TestPipe pipe;

92 93 94 95 96
  auto unpackedSizeInWords = computeUnpackedSizeInWords(packed);
  EXPECT_EQ(unpackedUnaligned.size(), unpackedSizeInWords * sizeof(word));

  // Make a guaranteed-to-be-aligned copy of the unpacked buffer.
  kj::Array<word> unpackedWords = kj::heapArray<word>(unpackedSizeInWords);
97 98 99
  if (unpackedUnaligned.size() != 0u) {
    memcpy(unpackedWords.begin(), unpackedUnaligned.begin(), unpackedUnaligned.size());
  }
100
  kj::ArrayPtr<const byte> unpacked = unpackedWords.asBytes();
101

102 103 104 105
  // -----------------------------------------------------------------
  // write

  {
106
    kj::BufferedOutputStreamWrapper bufferedOut(pipe);
107 108 109 110
    PackedOutputStream packedOut(bufferedOut);
    packedOut.write(unpacked.begin(), unpacked.size());
  }

111
  if (pipe.getData() != std::string(packed.asChars().begin(), packed.asChars().size())) {
112 113
    KJ_FAIL_ASSERT("Tried to pack `unpacked`, expected `packed`, got `pipe.getData()`",
                   unpacked, packed, pipe.getData());
114 115 116 117 118 119
    return;
  }

  // -----------------------------------------------------------------
  // read

120
  kj::Array<byte> roundTrip = kj::heapArray<byte>(unpacked.size());
121 122 123

  {
    PackedInputStream packedIn(pipe);
124
    packedIn.InputStream::read(roundTrip.begin(), roundTrip.size());
125 126 127
    EXPECT_TRUE(pipe.allRead());
  }

128
  if (memcmp(roundTrip.begin(), unpacked.begin(), unpacked.size()) != 0) {
129 130
    KJ_FAIL_ASSERT("Tried to unpack `packed`, expected `unpacked`, got `roundTrip`",
                   packed, unpacked, roundTrip);
131 132 133 134 135 136 137 138
    return;
  }

  for (uint blockSize = 1; blockSize < packed.size(); blockSize <<= 1) {
    pipe.resetRead(blockSize);

    {
      PackedInputStream packedIn(pipe);
139
      packedIn.InputStream::read(roundTrip.begin(), roundTrip.size());
140 141 142
      EXPECT_TRUE(pipe.allRead());
    }

143
    if (memcmp(roundTrip.begin(), unpacked.begin(), unpacked.size()) != 0) {
144 145
      KJ_FAIL_ASSERT("Tried to unpack `packed`, expected `unpacked`, got `roundTrip`",
                     packed, blockSize, unpacked, roundTrip);
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    }
  }

  // -----------------------------------------------------------------
  // skip

  pipe.resetRead();

  {
    PackedInputStream packedIn(pipe);
    packedIn.skip(unpacked.size());
    EXPECT_TRUE(pipe.allRead());
  }

  for (uint blockSize = 1; blockSize < packed.size(); blockSize <<= 1) {
    pipe.resetRead(blockSize);

    {
      PackedInputStream packedIn(pipe);
      packedIn.skip(unpacked.size());
      EXPECT_TRUE(pipe.allRead());
    }
  }

  pipe.clear();

  // -----------------------------------------------------------------
  // write / read multiple

  {
176
    kj::BufferedOutputStreamWrapper bufferedOut(pipe);
177 178 179 180 181 182 183 184 185 186
    PackedOutputStream packedOut(bufferedOut);
    for (uint i = 0; i < 5; i++) {
      packedOut.write(unpacked.begin(), unpacked.size());
    }
  }

  for (uint i = 0; i < 5; i++) {
    PackedInputStream packedIn(pipe);
    packedIn.InputStream::read(&*roundTrip.begin(), roundTrip.size());

187
    if (memcmp(roundTrip.begin(), unpacked.begin(), unpacked.size()) != 0) {
188 189
      KJ_FAIL_ASSERT("Tried to unpack `packed`, expected `unpacked`, got `roundTrip`",
                     packed, i, unpacked, roundTrip);
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
    }
  }

  EXPECT_TRUE(pipe.allRead());
}

#ifdef __CDT_PARSER__
// CDT doesn't seem to understand these initializer lists.
#define expectPacksTo(...)
#endif

TEST(Packed, SimplePacking) {
  expectPacksTo({}, {});
  expectPacksTo({0,0,0,0,0,0,0,0}, {0,0});
  expectPacksTo({0,0,12,0,0,34,0,0}, {0x24,12,34});
  expectPacksTo({1,3,2,4,5,7,6,8}, {0xff,1,3,2,4,5,7,6,8,0});
  expectPacksTo({0,0,0,0,0,0,0,0,1,3,2,4,5,7,6,8}, {0,0,0xff,1,3,2,4,5,7,6,8,0});
  expectPacksTo({0,0,12,0,0,34,0,0,1,3,2,4,5,7,6,8}, {0x24,12,34,0xff,1,3,2,4,5,7,6,8,0});
  expectPacksTo({1,3,2,4,5,7,6,8,8,6,7,4,5,2,3,1}, {0xff,1,3,2,4,5,7,6,8,1,8,6,7,4,5,2,3,1});

  expectPacksTo(
      {1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 0,2,4,0,9,0,5,1},
      {0xff,1,2,3,4,5,6,7,8, 3, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 0xd6,2,4,9,5,1});
  expectPacksTo(
      {1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 6,2,4,3,9,0,5,1, 1,2,3,4,5,6,7,8, 0,2,4,0,9,0,5,1},
      {0xff,1,2,3,4,5,6,7,8, 3, 1,2,3,4,5,6,7,8, 6,2,4,3,9,0,5,1, 1,2,3,4,5,6,7,8, 0xd6,2,4,9,5,1});

  expectPacksTo(
      {8,0,100,6,0,1,1,2, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,1,0,2,0,3,1},
      {0xed,8,100,6,1,1,2, 0,2, 0xd4,1,2,3,1});
}

// =======================================================================================

class TestMessageBuilder: public MallocMessageBuilder {
  // A MessageBuilder that tries to allocate an exact number of total segments, by allocating
  // minimum-size segments until it reaches the number, then allocating one large segment to
  // finish.

public:
  explicit TestMessageBuilder(uint desiredSegmentCount)
      : MallocMessageBuilder(0, AllocationStrategy::FIXED_SIZE),
        desiredSegmentCount(desiredSegmentCount) {}
  ~TestMessageBuilder() {
    EXPECT_EQ(0u, desiredSegmentCount);
  }

237
  kj::ArrayPtr<word> allocateSegment(uint minimumSize) override {
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    if (desiredSegmentCount <= 1) {
      if (desiredSegmentCount < 1) {
        ADD_FAILURE() << "Allocated more segments than desired.";
      } else {
        --desiredSegmentCount;
      }
      return MallocMessageBuilder::allocateSegment(SUGGESTED_FIRST_SEGMENT_WORDS);
    } else {
      --desiredSegmentCount;
      return MallocMessageBuilder::allocateSegment(minimumSize);
    }
  }

private:
  uint desiredSegmentCount;
};

TEST(Packed, RoundTrip) {
  TestMessageBuilder builder(1);
  initTestMessage(builder.initRoot<TestAllTypes>());

  TestPipe pipe;
  writePackedMessage(pipe, builder);

262 263
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

264 265 266 267 268 269 270 271 272 273 274
  PackedMessageReader reader(pipe);
  checkTestMessage(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripScratchSpace) {
  TestMessageBuilder builder(1);
  initTestMessage(builder.initRoot<TestAllTypes>());

  TestPipe pipe;
  writePackedMessage(pipe, builder);

275 276
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

277
  word scratch[1024];
278
  PackedMessageReader reader(pipe, ReaderOptions(), kj::ArrayPtr<word>(scratch, 1024));
279 280 281 282 283 284 285 286 287 288
  checkTestMessage(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripLazy) {
  TestMessageBuilder builder(1);
  initTestMessage(builder.initRoot<TestAllTypes>());

  TestPipe pipe(1);
  writePackedMessage(pipe, builder);

289 290
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

291 292 293 294 295 296 297 298 299 300 301
  PackedMessageReader reader(pipe);
  checkTestMessage(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripOddSegmentCount) {
  TestMessageBuilder builder(7);
  initTestMessage(builder.initRoot<TestAllTypes>());

  TestPipe pipe;
  writePackedMessage(pipe, builder);

302 303
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

304 305 306 307 308 309 310 311 312 313 314
  PackedMessageReader reader(pipe);
  checkTestMessage(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripOddSegmentCountLazy) {
  TestMessageBuilder builder(7);
  initTestMessage(builder.initRoot<TestAllTypes>());

  TestPipe pipe(1);
  writePackedMessage(pipe, builder);

315 316
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

317 318 319 320 321 322 323 324 325 326 327
  PackedMessageReader reader(pipe);
  checkTestMessage(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripEvenSegmentCount) {
  TestMessageBuilder builder(10);
  initTestMessage(builder.initRoot<TestAllTypes>());

  TestPipe pipe;
  writePackedMessage(pipe, builder);

328 329
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

330 331 332 333 334 335 336 337 338 339 340
  PackedMessageReader reader(pipe);
  checkTestMessage(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripEvenSegmentCountLazy) {
  TestMessageBuilder builder(10);
  initTestMessage(builder.initRoot<TestAllTypes>());

  TestPipe pipe(1);
  writePackedMessage(pipe, builder);

341 342
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
  PackedMessageReader reader(pipe);
  checkTestMessage(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripTwoMessages) {
  TestMessageBuilder builder(1);
  initTestMessage(builder.initRoot<TestAllTypes>());

  TestMessageBuilder builder2(1);
  builder2.initRoot<TestAllTypes>().setTextField("Second message.");

  TestPipe pipe;
  writePackedMessage(pipe, builder);
  writePackedMessage(pipe, builder2);

358 359 360
  EXPECT_EQ(computeSerializedSizeInWords(builder) + computeSerializedSizeInWords(builder2),
            computeUnpackedSizeInWords(pipe.getArray()));

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
  {
    PackedMessageReader reader(pipe);
    checkTestMessage(reader.getRoot<TestAllTypes>());
  }

  {
    PackedMessageReader reader(pipe);
    EXPECT_EQ("Second message.", reader.getRoot<TestAllTypes>().getTextField());
  }
}

// =======================================================================================

TEST(Packed, RoundTripAllZero) {
  TestMessageBuilder builder(1);
  builder.initRoot<TestAllTypes>();

  TestPipe pipe;
  writePackedMessage(pipe, builder);

381 382
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

383 384 385
  PackedMessageReader reader(pipe);
  checkTestMessageAllZero(reader.getRoot<TestAllTypes>());

386
  // Segment table packs to 2 bytes.
387
  // Root pointer packs to 3 bytes.
388 389
  // Content packs to 2 bytes (zero span).
  EXPECT_LE(pipe.getData().size(), 7u);
390 391 392 393 394 395 396 397 398
}

TEST(Packed, RoundTripAllZeroScratchSpace) {
  TestMessageBuilder builder(1);
  builder.initRoot<TestAllTypes>();

  TestPipe pipe;
  writePackedMessage(pipe, builder);

399 400
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

401
  word scratch[1024];
402
  PackedMessageReader reader(pipe, ReaderOptions(), kj::ArrayPtr<word>(scratch, 1024));
403 404 405 406 407 408 409 410 411 412
  checkTestMessageAllZero(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripAllZeroLazy) {
  TestMessageBuilder builder(1);
  builder.initRoot<TestAllTypes>();

  TestPipe pipe(1);
  writePackedMessage(pipe, builder);

413 414
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

415 416 417 418 419 420 421 422 423 424 425
  PackedMessageReader reader(pipe);
  checkTestMessageAllZero(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripAllZeroOddSegmentCount) {
  TestMessageBuilder builder(3);
  builder.initRoot<TestAllTypes>().initStructField().initStructField();

  TestPipe pipe;
  writePackedMessage(pipe, builder);

426 427
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

428 429 430 431 432 433 434 435 436 437 438
  PackedMessageReader reader(pipe);
  checkTestMessageAllZero(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripAllZeroOddSegmentCountLazy) {
  TestMessageBuilder builder(3);
  builder.initRoot<TestAllTypes>().initStructField().initStructField();

  TestPipe pipe(1);
  writePackedMessage(pipe, builder);

439 440
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

441 442 443 444 445 446 447 448 449 450 451
  PackedMessageReader reader(pipe);
  checkTestMessageAllZero(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripAllZeroEvenSegmentCount) {
  TestMessageBuilder builder(2);
  builder.initRoot<TestAllTypes>().initStructField().initStructField();

  TestPipe pipe;
  writePackedMessage(pipe, builder);

452 453
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

454 455 456 457 458 459 460 461 462 463 464
  PackedMessageReader reader(pipe);
  checkTestMessageAllZero(reader.getRoot<TestAllTypes>());
}

TEST(Packed, RoundTripAllZeroEvenSegmentCountLazy) {
  TestMessageBuilder builder(2);
  builder.initRoot<TestAllTypes>().initStructField().initStructField();

  TestPipe pipe(1);
  writePackedMessage(pipe, builder);

465 466
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

467 468 469 470 471 472 473
  PackedMessageReader reader(pipe);
  checkTestMessageAllZero(reader.getRoot<TestAllTypes>());
}

// =======================================================================================

TEST(Packed, RoundTripHugeString) {
474 475 476
  kj::String huge = kj::heapString(5023);
  memset(huge.begin(), 'x', 5023);

477
  TestMessageBuilder builder(1);
478
  builder.initRoot<TestAllTypes>().setTextField(huge);
479 480 481 482

  TestPipe pipe;
  writePackedMessage(pipe, builder);

483 484
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

485
  PackedMessageReader reader(pipe);
486
  EXPECT_TRUE(reader.getRoot<TestAllTypes>().getTextField() == huge);
487 488 489
}

TEST(Packed, RoundTripHugeStringScratchSpace) {
490 491 492
  kj::String huge = kj::heapString(5023);
  memset(huge.begin(), 'x', 5023);

493
  TestMessageBuilder builder(1);
494
  builder.initRoot<TestAllTypes>().setTextField(huge);
495 496 497 498

  TestPipe pipe;
  writePackedMessage(pipe, builder);

499 500
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

501
  word scratch[1024];
502
  PackedMessageReader reader(pipe, ReaderOptions(), kj::ArrayPtr<word>(scratch, 1024));
503
  EXPECT_TRUE(reader.getRoot<TestAllTypes>().getTextField() == huge);
504 505 506
}

TEST(Packed, RoundTripHugeStringLazy) {
507 508 509
  kj::String huge = kj::heapString(5023);
  memset(huge.begin(), 'x', 5023);

510
  TestMessageBuilder builder(1);
511
  builder.initRoot<TestAllTypes>().setTextField(huge);
512 513 514 515

  TestPipe pipe(1);
  writePackedMessage(pipe, builder);

516 517
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

518
  PackedMessageReader reader(pipe);
519
  EXPECT_TRUE(reader.getRoot<TestAllTypes>().getTextField() == huge);
520 521 522
}

TEST(Packed, RoundTripHugeStringOddSegmentCount) {
523 524 525
  kj::String huge = kj::heapString(5023);
  memset(huge.begin(), 'x', 5023);

526
  TestMessageBuilder builder(3);
527
  builder.initRoot<TestAllTypes>().setTextField(huge);
528 529 530 531

  TestPipe pipe;
  writePackedMessage(pipe, builder);

532 533
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

534
  PackedMessageReader reader(pipe);
535
  EXPECT_TRUE(reader.getRoot<TestAllTypes>().getTextField() == huge);
536 537 538
}

TEST(Packed, RoundTripHugeStringOddSegmentCountLazy) {
539 540 541
  kj::String huge = kj::heapString(5023);
  memset(huge.begin(), 'x', 5023);

542
  TestMessageBuilder builder(3);
543
  builder.initRoot<TestAllTypes>().setTextField(huge);
544 545 546 547

  TestPipe pipe(1);
  writePackedMessage(pipe, builder);

548 549
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

550
  PackedMessageReader reader(pipe);
551
  EXPECT_TRUE(reader.getRoot<TestAllTypes>().getTextField() == huge);
552 553 554
}

TEST(Packed, RoundTripHugeStringEvenSegmentCount) {
555 556 557
  kj::String huge = kj::heapString(5023);
  memset(huge.begin(), 'x', 5023);

558
  TestMessageBuilder builder(2);
559
  builder.initRoot<TestAllTypes>().setTextField(huge);
560 561 562 563

  TestPipe pipe;
  writePackedMessage(pipe, builder);

564 565
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

566
  PackedMessageReader reader(pipe);
567
  EXPECT_TRUE(reader.getRoot<TestAllTypes>().getTextField() == huge);
568 569 570
}

TEST(Packed, RoundTripHugeStringEvenSegmentCountLazy) {
571 572 573
  kj::String huge = kj::heapString(5023);
  memset(huge.begin(), 'x', 5023);

574
  TestMessageBuilder builder(2);
575
  builder.initRoot<TestAllTypes>().setTextField(huge);
576 577 578 579

  TestPipe pipe(1);
  writePackedMessage(pipe, builder);

580 581
  EXPECT_EQ(computeSerializedSizeInWords(builder), computeUnpackedSizeInWords(pipe.getArray()));

582
  PackedMessageReader reader(pipe);
583
  EXPECT_TRUE(reader.getRoot<TestAllTypes>().getTextField() == huge);
584 585
}

586
// TODO(test):  Test error cases.
587 588

}  // namespace
589
}  // namespace _ (private)
590
}  // namespace capnp