|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | // This file contains the tests for the RingBuffer class. | 
|  |  | 
|  | #include "gpu/command_buffer/client/ring_buffer.h" | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "gpu/command_buffer/client/cmd_buffer_helper.h" | 
|  | #include "gpu/command_buffer/service/command_buffer_direct.h" | 
|  | #include "gpu/command_buffer/service/mocks.h" | 
|  | #include "gpu/command_buffer/service/transfer_buffer_manager.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace gpu { | 
|  |  | 
|  | using testing::Return; | 
|  | using testing::Mock; | 
|  | using testing::Truly; | 
|  | using testing::Sequence; | 
|  | using testing::DoAll; | 
|  | using testing::Invoke; | 
|  | using testing::_; | 
|  |  | 
|  | class BaseRingBufferTest : public testing::Test { | 
|  | protected: | 
|  | static const unsigned int kBaseOffset = 128; | 
|  | static const unsigned int kBufferSize = 1024; | 
|  | static const unsigned int kAlignment = 4; | 
|  |  | 
|  | void RunPendingSetToken() { | 
|  | for (std::vector<const volatile void*>::iterator it = | 
|  | set_token_arguments_.begin(); | 
|  | it != set_token_arguments_.end(); ++it) { | 
|  | api_mock_->SetToken(cmd::kSetToken, 1, *it); | 
|  | } | 
|  | set_token_arguments_.clear(); | 
|  | delay_set_token_ = false; | 
|  | } | 
|  |  | 
|  | void SetToken(unsigned int command, | 
|  | unsigned int arg_count, | 
|  | const volatile void* _args) { | 
|  | EXPECT_EQ(static_cast<unsigned int>(cmd::kSetToken), command); | 
|  | EXPECT_EQ(1u, arg_count); | 
|  | if (delay_set_token_) | 
|  | set_token_arguments_.push_back(_args); | 
|  | else | 
|  | api_mock_->SetToken(cmd::kSetToken, 1, _args); | 
|  | } | 
|  |  | 
|  | void SetUp() override { | 
|  | delay_set_token_ = false; | 
|  | transfer_buffer_manager_ = std::make_unique<TransferBufferManager>(nullptr); | 
|  | command_buffer_.reset( | 
|  | new CommandBufferDirect(transfer_buffer_manager_.get())); | 
|  | api_mock_.reset(new AsyncAPIMock(true, command_buffer_->service())); | 
|  | command_buffer_->set_handler(api_mock_.get()); | 
|  |  | 
|  | // ignore noops in the mock - we don't want to inspect the internals of the | 
|  | // helper. | 
|  | EXPECT_CALL(*api_mock_, DoCommand(cmd::kNoop, 0, _)) | 
|  | .WillRepeatedly(Return(error::kNoError)); | 
|  | // Forward the SetToken calls to the engine | 
|  | EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) | 
|  | .WillRepeatedly(DoAll(Invoke(this, &BaseRingBufferTest::SetToken), | 
|  | Return(error::kNoError))); | 
|  |  | 
|  | helper_.reset(new CommandBufferHelper(command_buffer_.get())); | 
|  | helper_->Initialize(kBufferSize); | 
|  | } | 
|  |  | 
|  | int32_t GetToken() { return command_buffer_->GetLastState().token; } | 
|  |  | 
|  | std::unique_ptr<TransferBufferManager> transfer_buffer_manager_; | 
|  | std::unique_ptr<CommandBufferDirect> command_buffer_; | 
|  | std::unique_ptr<AsyncAPIMock> api_mock_; | 
|  | std::unique_ptr<CommandBufferHelper> helper_; | 
|  | std::vector<const volatile void*> set_token_arguments_; | 
|  | bool delay_set_token_; | 
|  |  | 
|  | std::unique_ptr<int8_t[]> buffer_; | 
|  | int8_t* buffer_start_; | 
|  | base::MessageLoop message_loop_; | 
|  | }; | 
|  |  | 
|  | #ifndef _MSC_VER | 
|  | const unsigned int BaseRingBufferTest::kBaseOffset; | 
|  | const unsigned int BaseRingBufferTest::kBufferSize; | 
|  | #endif | 
|  |  | 
|  | // Test fixture for RingBuffer test - Creates a RingBuffer, using a | 
|  | // CommandBufferHelper with a mock AsyncAPIInterface for its interface (calling | 
|  | // it directly, not through the RPC mechanism), making sure Noops are ignored | 
|  | // and SetToken are properly forwarded to the engine. | 
|  | class RingBufferTest : public BaseRingBufferTest { | 
|  | protected: | 
|  | void SetUp() override { | 
|  | BaseRingBufferTest::SetUp(); | 
|  |  | 
|  | buffer_.reset(new int8_t[kBufferSize + kBaseOffset]); | 
|  | buffer_start_ = buffer_.get() + kBaseOffset; | 
|  | allocator_.reset(new RingBuffer(kAlignment, kBaseOffset, kBufferSize, | 
|  | helper_.get(), buffer_start_)); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | // If the CommandExecutor posts any tasks, this forces them to run. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  |  | 
|  | BaseRingBufferTest::TearDown(); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<RingBuffer> allocator_; | 
|  | }; | 
|  |  | 
|  | // Checks basic alloc and free. | 
|  | TEST_F(RingBufferTest, TestBasic) { | 
|  | const unsigned int kSize = 16; | 
|  | EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); | 
|  | EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | void* pointer = allocator_->Alloc(kSize); | 
|  | EXPECT_GE(kBufferSize, allocator_->GetOffset(pointer) - kBaseOffset + kSize); | 
|  | EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); | 
|  | EXPECT_EQ(kBufferSize - kSize, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | int32_t token = helper_->InsertToken(); | 
|  | allocator_->FreePendingToken(pointer, token); | 
|  | } | 
|  |  | 
|  | // Checks the free-pending-token mechanism. | 
|  | TEST_F(RingBufferTest, TestFreePendingToken) { | 
|  | const unsigned int kSize = 16; | 
|  | const unsigned int kAllocCount = kBufferSize / kSize; | 
|  | CHECK(kAllocCount * kSize == kBufferSize); | 
|  |  | 
|  | delay_set_token_ = true; | 
|  | // Allocate several buffers to fill in the memory. | 
|  | int32_t tokens[kAllocCount]; | 
|  | for (unsigned int ii = 0; ii < kAllocCount; ++ii) { | 
|  | void* pointer = allocator_->Alloc(kSize); | 
|  | EXPECT_GE(kBufferSize, | 
|  | allocator_->GetOffset(pointer) - kBaseOffset + kSize); | 
|  | tokens[ii] = helper_->InsertToken(); | 
|  | allocator_->FreePendingToken(pointer, tokens[ii]); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(kBufferSize - (kSize * kAllocCount), | 
|  | allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | RunPendingSetToken(); | 
|  |  | 
|  | // This allocation will need to reclaim the space freed above, so that should | 
|  | // process the commands until a token is passed. | 
|  | void* pointer1 = allocator_->Alloc(kSize); | 
|  | EXPECT_EQ(kBaseOffset, allocator_->GetOffset(pointer1)); | 
|  |  | 
|  | // Check that the token has indeed passed. | 
|  | EXPECT_LE(tokens[0], GetToken()); | 
|  |  | 
|  | allocator_->FreePendingToken(pointer1, helper_->InsertToken()); | 
|  | } | 
|  |  | 
|  | // Tests GetLargestFreeSizeNoWaiting | 
|  | TEST_F(RingBufferTest, TestGetLargestFreeSizeNoWaiting) { | 
|  | EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | void* pointer = allocator_->Alloc(kBufferSize); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(pointer, helper_->InsertToken()); | 
|  | } | 
|  |  | 
|  | TEST_F(RingBufferTest, TestFreeBug) { | 
|  | // The first and second allocations must not match. | 
|  | const unsigned int kAlloc1 = 3*kAlignment; | 
|  | const unsigned int kAlloc2 = 20; | 
|  | void* pointer = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(pointer, helper_.get()->InsertToken()); | 
|  | pointer = allocator_->Alloc(kAlloc2); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, | 
|  | allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(pointer, helper_.get()->InsertToken()); | 
|  | pointer = allocator_->Alloc(kBufferSize); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(pointer, helper_.get()->InsertToken()); | 
|  | } | 
|  |  | 
|  | // Test that discarding a single allocation clears the block. | 
|  | TEST_F(RingBufferTest, DiscardTest) { | 
|  | const unsigned int kAlloc1 = 3*kAlignment; | 
|  | void* ptr = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->DiscardBlock(ptr); | 
|  | EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | } | 
|  |  | 
|  | // Test that discarding front of the buffer effectively frees the block. | 
|  | TEST_F(RingBufferTest, DiscardFrontTest) { | 
|  | const unsigned int kAlloc1 = 3*kAlignment; | 
|  | const unsigned int kAlloc2 = 2*kAlignment; | 
|  | const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2; | 
|  | void* ptr1 = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr2 = allocator_->Alloc(kAlloc2); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1, | 
|  | static_cast<uint8_t*>(ptr2)); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, | 
|  | allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr3 = allocator_->Alloc(kAlloc3); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2, | 
|  | static_cast<uint8_t*>(ptr3)); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | // Discard first block should free it up upon GetLargestFreeSizeNoWaiting(). | 
|  | allocator_->DiscardBlock(ptr1); | 
|  | EXPECT_EQ(kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken()); | 
|  | } | 
|  |  | 
|  | // Test that discarding middle of the buffer merely marks it as padding. | 
|  | TEST_F(RingBufferTest, DiscardMiddleTest) { | 
|  | const unsigned int kAlloc1 = 3*kAlignment; | 
|  | const unsigned int kAlloc2 = 2*kAlignment; | 
|  | const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2; | 
|  | void* ptr1 = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr2 = allocator_->Alloc(kAlloc2); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1, | 
|  | static_cast<uint8_t*>(ptr2)); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, | 
|  | allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr3 = allocator_->Alloc(kAlloc3); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2, | 
|  | static_cast<uint8_t*>(ptr3)); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | // Discard middle block should just set it as padding. | 
|  | allocator_->DiscardBlock(ptr2); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken()); | 
|  | } | 
|  |  | 
|  | // Test that discarding end of the buffer frees it for no waiting. | 
|  | TEST_F(RingBufferTest, DiscardEndTest) { | 
|  | const unsigned int kAlloc1 = 3*kAlignment; | 
|  | const unsigned int kAlloc2 = 2*kAlignment; | 
|  | const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2; | 
|  | void* ptr1 = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr2 = allocator_->Alloc(kAlloc2); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1, | 
|  | static_cast<uint8_t*>(ptr2)); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, | 
|  | allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr3 = allocator_->Alloc(kAlloc3); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2, | 
|  | static_cast<uint8_t*>(ptr3)); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | // Discard end block should discard it. | 
|  | allocator_->DiscardBlock(ptr3); | 
|  | EXPECT_EQ(kAlloc3, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | } | 
|  |  | 
|  | // Test discard end of the buffer that has looped around. | 
|  | TEST_F(RingBufferTest, DiscardLoopedEndTest) { | 
|  | const unsigned int kAlloc1 = 3*kAlignment; | 
|  | const unsigned int kAlloc2 = 2*kAlignment; | 
|  | const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2; | 
|  | void* ptr1 = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr2 = allocator_->Alloc(kAlloc2); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1, | 
|  | static_cast<uint8_t*>(ptr2)); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, | 
|  | allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr3 = allocator_->Alloc(kAlloc3); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2, | 
|  | static_cast<uint8_t*>(ptr3)); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken()); | 
|  |  | 
|  | // This allocation should be at the beginning again, we need to utilize | 
|  | // DiscardBlock here to discard the first item so that we can allocate | 
|  | // at the beginning without the FreeOldestBlock() getting called and freeing | 
|  | // the whole ring buffer. | 
|  | allocator_->DiscardBlock(ptr1); | 
|  | void* ptr4 = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(ptr1, ptr4); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | // Discard end block should work properly still. | 
|  | allocator_->DiscardBlock(ptr4); | 
|  | EXPECT_EQ(kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | } | 
|  |  | 
|  | // Test discard end of the buffer that has looped around with padding. | 
|  | TEST_F(RingBufferTest, DiscardEndWithPaddingTest) { | 
|  | const unsigned int kAlloc1 = 3*kAlignment; | 
|  | const unsigned int kAlloc2 = 2*kAlignment; | 
|  | const unsigned int kPadding = kAlignment; | 
|  | const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2 - kPadding; | 
|  | void* ptr1 = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr2 = allocator_->Alloc(kAlloc2); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1, | 
|  | static_cast<uint8_t*>(ptr2)); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, | 
|  | allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr3 = allocator_->Alloc(kAlloc3); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2, | 
|  | static_cast<uint8_t*>(ptr3)); | 
|  | EXPECT_EQ(kPadding, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken()); | 
|  |  | 
|  | // Cause it to loop around with padding at the end of ptr3. | 
|  | allocator_->DiscardBlock(ptr1); | 
|  | void* ptr4 = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(ptr1, ptr4); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | // Discard end block should also discard the padding. | 
|  | allocator_->DiscardBlock(ptr4); | 
|  | EXPECT_EQ(kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | // We can test that there is padding by attempting to allocate the padding. | 
|  | void* padding = allocator_->Alloc(kPadding); | 
|  | EXPECT_EQ(kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(padding, helper_.get()->InsertToken()); | 
|  | } | 
|  |  | 
|  | // Test that discard will effectively remove all padding at the end. | 
|  | TEST_F(RingBufferTest, DiscardAllPaddingFromEndTest) { | 
|  | const unsigned int kAlloc1 = 3*kAlignment; | 
|  | const unsigned int kAlloc2 = 2*kAlignment; | 
|  | const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2; | 
|  | void* ptr1 = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr2 = allocator_->Alloc(kAlloc2); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1, | 
|  | static_cast<uint8_t*>(ptr2)); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, | 
|  | allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr3 = allocator_->Alloc(kAlloc3); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2, | 
|  | static_cast<uint8_t*>(ptr3)); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | // Discarding the middle allocation should turn it into padding. | 
|  | allocator_->DiscardBlock(ptr2); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | // Discarding the last allocation should discard the middle padding as well. | 
|  | allocator_->DiscardBlock(ptr3); | 
|  | EXPECT_EQ(kAlloc2 + kAlloc3, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | } | 
|  |  | 
|  | // Test that discard will effectively remove all padding from the beginning. | 
|  | TEST_F(RingBufferTest, DiscardAllPaddingFromBeginningTest) { | 
|  | const unsigned int kAlloc1 = 3*kAlignment; | 
|  | const unsigned int kAlloc2 = 2*kAlignment; | 
|  | const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2; | 
|  | void* ptr1 = allocator_->Alloc(kAlloc1); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr2 = allocator_->Alloc(kAlloc2); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1, | 
|  | static_cast<uint8_t*>(ptr2)); | 
|  | EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, | 
|  | allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken()); | 
|  |  | 
|  | void* ptr3 = allocator_->Alloc(kAlloc3); | 
|  | EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2, | 
|  | static_cast<uint8_t*>(ptr3)); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken()); | 
|  |  | 
|  | // Discarding the middle allocation should turn it into padding. | 
|  | allocator_->DiscardBlock(ptr2); | 
|  | EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  |  | 
|  | // Discarding the first allocation should discard the middle padding as well. | 
|  | allocator_->DiscardBlock(ptr1); | 
|  | EXPECT_EQ(kAlloc1 + kAlloc2, allocator_->GetLargestFreeSizeNoWaiting()); | 
|  | } | 
|  |  | 
|  | }  // namespace gpu |