| // 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<AsyncAPIMock> api_mock_; |
| scoped_ptr<CommandBufferEntry[]> buffer_; |
| Sequence sequence_; |
| }; |
| |
| // Tests initialization conditions. |
| TEST_F(CommandParserTest, TestInit) { |
| scoped_ptr<CommandParser> 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<CommandParser> 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<CommandParser> 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<CommandParser> 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<CommandParser> 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<CommandParser> 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<CommandBufferEntry[]> 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 |