// Copyright (c) 2011 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. // Tests for the command parser. #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "gpu/command_buffer/service/cmd_parser.h" #include "gpu/command_buffer/service/mocks.h" #include "testing/gtest/include/gtest/gtest.h" namespace gpu { using testing::Return; using testing::Mock; using testing::Truly; using testing::Sequence; using testing::_; // Test fixture for CommandParser test - Creates a mock AsyncAPIInterface, and // a fixed size memory buffer. Also provides a simple API to create a // CommandParser. class CommandParserTest : public testing::Test { protected: virtual void SetUp() { api_mock_.reset(new AsyncAPIMock); buffer_entry_count_ = 20; buffer_.reset(new CommandBufferEntry[buffer_entry_count_]); } virtual void TearDown() {} // Adds a DoCommand expectation in the mock. void AddDoCommandExpect(error::Error _return, unsigned int command, unsigned int arg_count, CommandBufferEntry *args) { EXPECT_CALL(*api_mock(), DoCommand(command, arg_count, Truly(AsyncAPIMock::IsArgs(arg_count, args)))) .InSequence(sequence_) .WillOnce(Return(_return)); } // Creates a parser, with a buffer of the specified size (in entries). CommandParser *MakeParser(unsigned int entry_count) { size_t shm_size = buffer_entry_count_ * sizeof(CommandBufferEntry); // NOLINT size_t command_buffer_size = entry_count * sizeof(CommandBufferEntry); // NOLINT DCHECK_LE(command_buffer_size, shm_size); CommandParser* parser = new CommandParser(api_mock()); parser->SetBuffer(buffer(), shm_size, 0, command_buffer_size); return parser; } unsigned int buffer_entry_count() { return 20; } AsyncAPIMock *api_mock() { return api_mock_.get(); } CommandBufferEntry *buffer() { return buffer_.get(); } private: unsigned int buffer_entry_count_; scoped_ptr api_mock_; scoped_ptr buffer_; Sequence sequence_; }; // Tests initialization conditions. TEST_F(CommandParserTest, TestInit) { scoped_ptr parser(MakeParser(10)); EXPECT_EQ(0, parser->get()); EXPECT_EQ(0, parser->put()); EXPECT_TRUE(parser->IsEmpty()); } // Tests simple commands. TEST_F(CommandParserTest, TestSimple) { scoped_ptr parser(MakeParser(10)); CommandBufferOffset put = parser->put(); CommandHeader header; // add a single command, no args header.size = 1; header.command = 123; buffer()[put++].value_header = header; parser->set_put(put); EXPECT_EQ(put, parser->put()); AddDoCommandExpect(error::kNoError, 123, 0, NULL); EXPECT_EQ(error::kNoError, parser->ProcessCommand()); EXPECT_EQ(put, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); // add a single command, 2 args header.size = 3; header.command = 456; buffer()[put++].value_header = header; buffer()[put++].value_int32 = 2134; buffer()[put++].value_float = 1.f; parser->set_put(put); EXPECT_EQ(put, parser->put()); CommandBufferEntry param_array[2]; param_array[0].value_int32 = 2134; param_array[1].value_float = 1.f; AddDoCommandExpect(error::kNoError, 456, 2, param_array); EXPECT_EQ(error::kNoError, parser->ProcessCommand()); EXPECT_EQ(put, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); } // Tests having multiple commands in the buffer. TEST_F(CommandParserTest, TestMultipleCommands) { scoped_ptr parser(MakeParser(10)); CommandBufferOffset put = parser->put(); CommandHeader header; // add 2 commands, test with single ProcessCommand() header.size = 2; header.command = 789; buffer()[put++].value_header = header; buffer()[put++].value_int32 = 5151; CommandBufferOffset put_cmd2 = put; header.size = 2; header.command = 876; buffer()[put++].value_header = header; buffer()[put++].value_int32 = 3434; parser->set_put(put); EXPECT_EQ(put, parser->put()); CommandBufferEntry param_array[2]; param_array[0].value_int32 = 5151; AddDoCommandExpect(error::kNoError, 789, 1, param_array); param_array[1].value_int32 = 3434; AddDoCommandExpect(error::kNoError, 876, 1, param_array+1); EXPECT_EQ(error::kNoError, parser->ProcessCommand()); EXPECT_EQ(put_cmd2, parser->get()); EXPECT_EQ(error::kNoError, parser->ProcessCommand()); EXPECT_EQ(put, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); // add 2 commands again, test with ProcessAllCommands() header.size = 2; header.command = 123; buffer()[put++].value_header = header; buffer()[put++].value_int32 = 5656; header.size = 2; header.command = 321; buffer()[put++].value_header = header; buffer()[put++].value_int32 = 7878; parser->set_put(put); EXPECT_EQ(put, parser->put()); param_array[0].value_int32 = 5656; AddDoCommandExpect(error::kNoError, 123, 1, param_array); param_array[1].value_int32 = 7878; AddDoCommandExpect(error::kNoError, 321, 1, param_array+1); EXPECT_EQ(error::kNoError, parser->ProcessAllCommands()); EXPECT_EQ(put, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); } // Tests that the parser will wrap correctly at the end of the buffer. TEST_F(CommandParserTest, TestWrap) { scoped_ptr parser(MakeParser(5)); CommandBufferOffset put = parser->put(); CommandHeader header; // add 3 commands with no args (1 word each) for (unsigned int i = 0; i < 3; ++i) { header.size = 1; header.command = i; buffer()[put++].value_header = header; AddDoCommandExpect(error::kNoError, i, 0, NULL); } parser->set_put(put); EXPECT_EQ(put, parser->put()); EXPECT_EQ(error::kNoError, parser->ProcessAllCommands()); EXPECT_EQ(put, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); // add 1 command with 1 arg (2 words). That should put us at the end of the // buffer. header.size = 2; header.command = 3; buffer()[put++].value_header = header; buffer()[put++].value_int32 = 5; CommandBufferEntry param; param.value_int32 = 5; AddDoCommandExpect(error::kNoError, 3, 1, ¶m); DCHECK_EQ(5, put); put = 0; parser->set_put(put); EXPECT_EQ(put, parser->put()); EXPECT_EQ(error::kNoError, parser->ProcessAllCommands()); EXPECT_EQ(put, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); // add 1 command with 1 arg (2 words). header.size = 2; header.command = 4; buffer()[put++].value_header = header; buffer()[put++].value_int32 = 6; param.value_int32 = 6; AddDoCommandExpect(error::kNoError, 4, 1, ¶m); parser->set_put(put); EXPECT_EQ(put, parser->put()); EXPECT_EQ(error::kNoError, parser->ProcessAllCommands()); EXPECT_EQ(put, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); } // Tests error conditions. TEST_F(CommandParserTest, TestError) { const unsigned int kNumEntries = 5; scoped_ptr parser(MakeParser(kNumEntries)); CommandBufferOffset put = parser->put(); CommandHeader header; EXPECT_FALSE(parser->set_get(-1)); EXPECT_FALSE(parser->set_get(kNumEntries)); // Generate a command with size 0. header.size = 0; header.command = 3; buffer()[put++].value_header = header; parser->set_put(put); EXPECT_EQ(put, parser->put()); EXPECT_EQ(error::kInvalidSize, parser->ProcessAllCommands()); // check that no DoCommand call was made. Mock::VerifyAndClearExpectations(api_mock()); parser.reset(MakeParser(5)); put = parser->put(); // Generate a command with size 6, extends beyond the end of the buffer. header.size = 6; header.command = 3; buffer()[put++].value_header = header; parser->set_put(put); EXPECT_EQ(put, parser->put()); EXPECT_EQ(error::kOutOfBounds, parser->ProcessAllCommands()); // check that no DoCommand call was made. Mock::VerifyAndClearExpectations(api_mock()); parser.reset(MakeParser(5)); put = parser->put(); // Generates 2 commands. header.size = 1; header.command = 3; buffer()[put++].value_header = header; CommandBufferOffset put_post_fail = put; header.size = 1; header.command = 4; buffer()[put++].value_header = header; parser->set_put(put); EXPECT_EQ(put, parser->put()); // have the first command fail to parse. AddDoCommandExpect(error::kUnknownCommand, 3, 0, NULL); EXPECT_EQ(error::kUnknownCommand, parser->ProcessAllCommands()); // check that only one command was executed, and that get reflects that // correctly. EXPECT_EQ(put_post_fail, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); // make the second one succeed, and check that the parser recovered fine. AddDoCommandExpect(error::kNoError, 4, 0, NULL); EXPECT_EQ(error::kNoError, parser->ProcessAllCommands()); EXPECT_EQ(put, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); } TEST_F(CommandParserTest, SetBuffer) { scoped_ptr parser(MakeParser(3)); CommandBufferOffset put = parser->put(); CommandHeader header; // add a single command, no args header.size = 2; header.command = 123; buffer()[put++].value_header = header; buffer()[put++].value_int32 = 456; CommandBufferEntry param_array[1]; param_array[0].value_int32 = 456; parser->set_put(put); AddDoCommandExpect(error::kNoError, 123, 1, param_array); EXPECT_EQ(error::kNoError, parser->ProcessAllCommands()); // We should have advanced 2 entries EXPECT_EQ(2, parser->get()); Mock::VerifyAndClearExpectations(api_mock()); scoped_ptr buffer2(new CommandBufferEntry[2]); parser->SetBuffer( buffer2.get(), sizeof(CommandBufferEntry) * 2, 0, sizeof(CommandBufferEntry) * 2); // The put and get should have reset to 0. EXPECT_EQ(0, parser->get()); EXPECT_EQ(0, parser->put()); } } // namespace gpu