TEST_F(OwnedImplTest, LinearizeDrainTracking) {
  constexpr uint32_t SmallChunk = 200;
  constexpr uint32_t LargeChunk = 16384 - SmallChunk;
  constexpr uint32_t LinearizeSize = SmallChunk + LargeChunk;

  // Create a buffer with a eclectic combination of buffer OwnedSlice and UnownedSlices that will
  // help us explore the properties of linearize.
  Buffer::OwnedImpl buffer;

  // Large add below the target linearize size.
  testing::MockFunction<void()> tracker1;
  buffer.add(std::string(LargeChunk, 'a'));
  buffer.addDrainTracker(tracker1.AsStdFunction());

  // Unowned slice which causes some fragmentation.
  testing::MockFunction<void()> tracker2;
  testing::MockFunction<void(const void*, size_t, const BufferFragmentImpl*)>
      release_callback_tracker;
  std::string frag_input(2 * SmallChunk, 'b');
  BufferFragmentImpl frag(frag_input.c_str(), frag_input.size(),
                          release_callback_tracker.AsStdFunction());
  buffer.addBufferFragment(frag);
  buffer.addDrainTracker(tracker2.AsStdFunction());

  // And an unowned slice with 0 size, because.
  testing::MockFunction<void()> tracker3;
  testing::MockFunction<void(const void*, size_t, const BufferFragmentImpl*)>
      release_callback_tracker2;
  BufferFragmentImpl frag2(nullptr, 0, release_callback_tracker2.AsStdFunction());
  buffer.addBufferFragment(frag2);
  buffer.addDrainTracker(tracker3.AsStdFunction());

  // Add a very large chunk
  testing::MockFunction<void()> tracker4;
  buffer.add(std::string(LargeChunk + LinearizeSize, 'c'));
  buffer.addDrainTracker(tracker4.AsStdFunction());

  // Small adds that create no gaps.
  testing::MockFunction<void()> tracker5;
  for (int i = 0; i < 105; ++i) {
    buffer.add(std::string(SmallChunk, 'd'));
  }
  buffer.addDrainTracker(tracker5.AsStdFunction());

  expectSlices({{16184, 136, 16320},
                {400, 0, 400},
                {0, 0, 0},
                {32704, 0, 32704},
                {4032, 0, 4032},
                {4032, 0, 4032},
                {4032, 0, 4032},
                {4032, 0, 4032},
                {4032, 0, 4032},
                {704, 3328, 4032}},
               buffer);

  testing::InSequence s;
  testing::MockFunction<void(int, int)> drain_tracker;
  testing::MockFunction<void()> done_tracker;
  EXPECT_CALL(tracker1, Call());
  EXPECT_CALL(release_callback_tracker, Call(_, _, _));
  EXPECT_CALL(tracker2, Call());
  EXPECT_CALL(drain_tracker, Call(3 * LargeChunk + 108 * SmallChunk, 16384));
  EXPECT_CALL(release_callback_tracker2, Call(_, _, _));
  EXPECT_CALL(tracker3, Call());
  EXPECT_CALL(tracker4, Call());
  EXPECT_CALL(drain_tracker, Call(2 * LargeChunk + 107 * SmallChunk, 16384));
  EXPECT_CALL(drain_tracker, Call(LargeChunk + 106 * SmallChunk, 16384));
  EXPECT_CALL(drain_tracker, Call(105 * SmallChunk, 16384));
  EXPECT_CALL(tracker5, Call());
  EXPECT_CALL(drain_tracker, Call(4616, 4616));
  EXPECT_CALL(done_tracker, Call());
  for (auto& expected_first_slice : std::vector<std::vector<int>>{{16584, 3832, 20416},
                                                                  {32904, 3896, 36800},
                                                                  {16520, 3896, 36800},
                                                                  {20296, 120, 20416},
                                                                  {4616, 3512, 8128}}) {
    const uint32_t write_size = std::min<uint32_t>(LinearizeSize, buffer.length());
    buffer.linearize(write_size);
    expectFirstSlice(expected_first_slice, buffer);
    drain_tracker.Call(buffer.length(), write_size);
    buffer.drain(write_size);
  }
  done_tracker.Call();

  expectSlices({}, buffer);
}