1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Tests for the command parser.
6
7#include "base/logging.h"
8#include "base/memory/scoped_ptr.h"
9#include "gpu/command_buffer/service/cmd_parser.h"
10#include "gpu/command_buffer/service/mocks.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13namespace gpu {
14
15using testing::Return;
16using testing::Mock;
17using testing::Truly;
18using testing::Sequence;
19using testing::_;
20
21// Test fixture for CommandParser test - Creates a mock AsyncAPIInterface, and
22// a fixed size memory buffer. Also provides a simple API to create a
23// CommandParser.
24class CommandParserTest : public testing::Test {
25 protected:
26  virtual void SetUp() {
27    api_mock_.reset(new AsyncAPIMock);
28    buffer_entry_count_ = 20;
29    buffer_.reset(new CommandBufferEntry[buffer_entry_count_]);
30  }
31  virtual void TearDown() {}
32
33  // Adds a DoCommand expectation in the mock.
34  void AddDoCommandExpect(error::Error _return,
35                          unsigned int command,
36                          unsigned int arg_count,
37                          CommandBufferEntry *args) {
38    EXPECT_CALL(*api_mock(), DoCommand(command, arg_count,
39        Truly(AsyncAPIMock::IsArgs(arg_count, args))))
40        .InSequence(sequence_)
41        .WillOnce(Return(_return));
42  }
43
44  // Creates a parser, with a buffer of the specified size (in entries).
45  CommandParser *MakeParser(unsigned int entry_count) {
46    size_t shm_size = buffer_entry_count_ *
47                      sizeof(CommandBufferEntry);  // NOLINT
48    size_t command_buffer_size = entry_count *
49                                 sizeof(CommandBufferEntry);  // NOLINT
50    DCHECK_LE(command_buffer_size, shm_size);
51    CommandParser* parser = new CommandParser(api_mock());
52
53    parser->SetBuffer(buffer(), shm_size, 0, command_buffer_size);
54    return parser;
55  }
56
57  unsigned int buffer_entry_count() { return 20; }
58  AsyncAPIMock *api_mock() { return api_mock_.get(); }
59  CommandBufferEntry *buffer() { return buffer_.get(); }
60 private:
61  unsigned int buffer_entry_count_;
62  scoped_ptr<AsyncAPIMock> api_mock_;
63  scoped_ptr<CommandBufferEntry[]> buffer_;
64  Sequence sequence_;
65};
66
67// Tests initialization conditions.
68TEST_F(CommandParserTest, TestInit) {
69  scoped_ptr<CommandParser> parser(MakeParser(10));
70  EXPECT_EQ(0, parser->get());
71  EXPECT_EQ(0, parser->put());
72  EXPECT_TRUE(parser->IsEmpty());
73}
74
75// Tests simple commands.
76TEST_F(CommandParserTest, TestSimple) {
77  scoped_ptr<CommandParser> parser(MakeParser(10));
78  CommandBufferOffset put = parser->put();
79  CommandHeader header;
80
81  // add a single command, no args
82  header.size = 1;
83  header.command = 123;
84  buffer()[put++].value_header = header;
85
86  parser->set_put(put);
87  EXPECT_EQ(put, parser->put());
88
89  AddDoCommandExpect(error::kNoError, 123, 0, NULL);
90  EXPECT_EQ(error::kNoError, parser->ProcessCommand());
91  EXPECT_EQ(put, parser->get());
92  Mock::VerifyAndClearExpectations(api_mock());
93
94  // add a single command, 2 args
95  header.size = 3;
96  header.command = 456;
97  buffer()[put++].value_header = header;
98  buffer()[put++].value_int32 = 2134;
99  buffer()[put++].value_float = 1.f;
100
101  parser->set_put(put);
102  EXPECT_EQ(put, parser->put());
103
104  CommandBufferEntry param_array[2];
105  param_array[0].value_int32 = 2134;
106  param_array[1].value_float = 1.f;
107  AddDoCommandExpect(error::kNoError, 456, 2, param_array);
108  EXPECT_EQ(error::kNoError, parser->ProcessCommand());
109  EXPECT_EQ(put, parser->get());
110  Mock::VerifyAndClearExpectations(api_mock());
111}
112
113// Tests having multiple commands in the buffer.
114TEST_F(CommandParserTest, TestMultipleCommands) {
115  scoped_ptr<CommandParser> parser(MakeParser(10));
116  CommandBufferOffset put = parser->put();
117  CommandHeader header;
118
119  // add 2 commands, test with single ProcessCommand()
120  header.size = 2;
121  header.command = 789;
122  buffer()[put++].value_header = header;
123  buffer()[put++].value_int32 = 5151;
124
125  CommandBufferOffset put_cmd2 = put;
126  header.size = 2;
127  header.command = 876;
128  buffer()[put++].value_header = header;
129  buffer()[put++].value_int32 = 3434;
130
131  parser->set_put(put);
132  EXPECT_EQ(put, parser->put());
133
134  CommandBufferEntry param_array[2];
135  param_array[0].value_int32 = 5151;
136  AddDoCommandExpect(error::kNoError, 789, 1, param_array);
137  param_array[1].value_int32 = 3434;
138  AddDoCommandExpect(error::kNoError, 876, 1,
139                     param_array+1);
140
141  EXPECT_EQ(error::kNoError, parser->ProcessCommand());
142  EXPECT_EQ(put_cmd2, parser->get());
143  EXPECT_EQ(error::kNoError, parser->ProcessCommand());
144  EXPECT_EQ(put, parser->get());
145  Mock::VerifyAndClearExpectations(api_mock());
146
147  // add 2 commands again, test with ProcessAllCommands()
148  header.size = 2;
149  header.command = 123;
150  buffer()[put++].value_header = header;
151  buffer()[put++].value_int32 = 5656;
152
153  header.size = 2;
154  header.command = 321;
155  buffer()[put++].value_header = header;
156  buffer()[put++].value_int32 = 7878;
157
158  parser->set_put(put);
159  EXPECT_EQ(put, parser->put());
160
161  param_array[0].value_int32 = 5656;
162  AddDoCommandExpect(error::kNoError, 123, 1, param_array);
163  param_array[1].value_int32 = 7878;
164  AddDoCommandExpect(error::kNoError, 321, 1,
165                     param_array+1);
166
167  EXPECT_EQ(error::kNoError, parser->ProcessAllCommands());
168  EXPECT_EQ(put, parser->get());
169  Mock::VerifyAndClearExpectations(api_mock());
170}
171
172// Tests that the parser will wrap correctly at the end of the buffer.
173TEST_F(CommandParserTest, TestWrap) {
174  scoped_ptr<CommandParser> parser(MakeParser(5));
175  CommandBufferOffset put = parser->put();
176  CommandHeader header;
177
178  // add 3 commands with no args (1 word each)
179  for (unsigned int i = 0; i < 3; ++i) {
180    header.size = 1;
181    header.command = i;
182    buffer()[put++].value_header = header;
183    AddDoCommandExpect(error::kNoError, i, 0, NULL);
184  }
185  parser->set_put(put);
186  EXPECT_EQ(put, parser->put());
187  EXPECT_EQ(error::kNoError, parser->ProcessAllCommands());
188  EXPECT_EQ(put, parser->get());
189  Mock::VerifyAndClearExpectations(api_mock());
190
191  // add 1 command with 1 arg (2 words). That should put us at the end of the
192  // buffer.
193  header.size = 2;
194  header.command = 3;
195  buffer()[put++].value_header = header;
196  buffer()[put++].value_int32 = 5;
197  CommandBufferEntry param;
198  param.value_int32 = 5;
199  AddDoCommandExpect(error::kNoError, 3, 1, &param);
200
201  DCHECK_EQ(5, put);
202  put = 0;
203  parser->set_put(put);
204  EXPECT_EQ(put, parser->put());
205  EXPECT_EQ(error::kNoError, parser->ProcessAllCommands());
206  EXPECT_EQ(put, parser->get());
207  Mock::VerifyAndClearExpectations(api_mock());
208
209  // add 1 command with 1 arg (2 words).
210  header.size = 2;
211  header.command = 4;
212  buffer()[put++].value_header = header;
213  buffer()[put++].value_int32 = 6;
214  param.value_int32 = 6;
215  AddDoCommandExpect(error::kNoError, 4, 1, &param);
216  parser->set_put(put);
217  EXPECT_EQ(put, parser->put());
218  EXPECT_EQ(error::kNoError, parser->ProcessAllCommands());
219  EXPECT_EQ(put, parser->get());
220  Mock::VerifyAndClearExpectations(api_mock());
221}
222
223// Tests error conditions.
224TEST_F(CommandParserTest, TestError) {
225  const unsigned int kNumEntries = 5;
226  scoped_ptr<CommandParser> parser(MakeParser(kNumEntries));
227  CommandBufferOffset put = parser->put();
228  CommandHeader header;
229
230  EXPECT_FALSE(parser->set_get(-1));
231  EXPECT_FALSE(parser->set_get(kNumEntries));
232
233  // Generate a command with size 0.
234  header.size = 0;
235  header.command = 3;
236  buffer()[put++].value_header = header;
237
238  parser->set_put(put);
239  EXPECT_EQ(put, parser->put());
240  EXPECT_EQ(error::kInvalidSize,
241            parser->ProcessAllCommands());
242  // check that no DoCommand call was made.
243  Mock::VerifyAndClearExpectations(api_mock());
244
245  parser.reset(MakeParser(5));
246  put = parser->put();
247
248  // Generate a command with size 6, extends beyond the end of the buffer.
249  header.size = 6;
250  header.command = 3;
251  buffer()[put++].value_header = header;
252
253  parser->set_put(put);
254  EXPECT_EQ(put, parser->put());
255  EXPECT_EQ(error::kOutOfBounds,
256            parser->ProcessAllCommands());
257  // check that no DoCommand call was made.
258  Mock::VerifyAndClearExpectations(api_mock());
259
260  parser.reset(MakeParser(5));
261  put = parser->put();
262
263  // Generates 2 commands.
264  header.size = 1;
265  header.command = 3;
266  buffer()[put++].value_header = header;
267  CommandBufferOffset put_post_fail = put;
268  header.size = 1;
269  header.command = 4;
270  buffer()[put++].value_header = header;
271
272  parser->set_put(put);
273  EXPECT_EQ(put, parser->put());
274  // have the first command fail to parse.
275  AddDoCommandExpect(error::kUnknownCommand, 3, 0, NULL);
276  EXPECT_EQ(error::kUnknownCommand,
277            parser->ProcessAllCommands());
278  // check that only one command was executed, and that get reflects that
279  // correctly.
280  EXPECT_EQ(put_post_fail, parser->get());
281  Mock::VerifyAndClearExpectations(api_mock());
282  // make the second one succeed, and check that the parser recovered fine.
283  AddDoCommandExpect(error::kNoError, 4, 0, NULL);
284  EXPECT_EQ(error::kNoError, parser->ProcessAllCommands());
285  EXPECT_EQ(put, parser->get());
286  Mock::VerifyAndClearExpectations(api_mock());
287}
288
289TEST_F(CommandParserTest, SetBuffer) {
290  scoped_ptr<CommandParser> parser(MakeParser(3));
291  CommandBufferOffset put = parser->put();
292  CommandHeader header;
293
294  // add a single command, no args
295  header.size = 2;
296  header.command = 123;
297  buffer()[put++].value_header = header;
298  buffer()[put++].value_int32 = 456;
299
300  CommandBufferEntry param_array[1];
301  param_array[0].value_int32 = 456;
302
303  parser->set_put(put);
304  AddDoCommandExpect(error::kNoError, 123, 1, param_array);
305  EXPECT_EQ(error::kNoError, parser->ProcessAllCommands());
306  // We should have advanced 2 entries
307  EXPECT_EQ(2, parser->get());
308  Mock::VerifyAndClearExpectations(api_mock());
309
310  scoped_ptr<CommandBufferEntry[]> buffer2(new CommandBufferEntry[2]);
311  parser->SetBuffer(
312      buffer2.get(), sizeof(CommandBufferEntry) * 2, 0,
313      sizeof(CommandBufferEntry) * 2);
314  // The put and get should have reset to 0.
315  EXPECT_EQ(0, parser->get());
316  EXPECT_EQ(0, parser->put());
317}
318
319}  // namespace gpu
320