1 // Copyright (c) 2016 The WebM project authors. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 #include "webm/webm_parser.h"
9
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
12
13 #include "test_utils/mock_callback.h"
14 #include "webm/buffer_reader.h"
15 #include "webm/status.h"
16
17 using testing::_;
18 using testing::DoDefault;
19 using testing::InSequence;
20 using testing::NotNull;
21 using testing::Return;
22
23 using webm::BufferReader;
24 using webm::Ebml;
25 using webm::ElementMetadata;
26 using webm::Id;
27 using webm::Info;
28 using webm::kUnknownElementPosition;
29 using webm::kUnknownElementSize;
30 using webm::kUnknownHeaderSize;
31 using webm::MockCallback;
32 using webm::Status;
33 using webm::WebmParser;
34
35 namespace {
36
37 class WebmParserTest : public testing::Test {};
38
TEST_F(WebmParserTest,InvalidId)39 TEST_F(WebmParserTest, InvalidId) {
40 BufferReader reader = {
41 0x00, // IDs cannot start with 0x00.
42 };
43
44 MockCallback callback;
45 {
46 InSequence dummy;
47
48 EXPECT_CALL(callback, OnEbml(_, _)).Times(0);
49
50 EXPECT_CALL(callback, OnSegmentBegin(_, NotNull())).Times(0);
51 EXPECT_CALL(callback, OnSegmentEnd(_)).Times(0);
52 }
53
54 WebmParser parser;
55 Status status = parser.Feed(&callback, &reader);
56 EXPECT_EQ(Status::kInvalidElementId, status.code);
57 }
58
TEST_F(WebmParserTest,InvalidSize)59 TEST_F(WebmParserTest, InvalidSize) {
60 BufferReader reader = {
61 0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
62 0x00, // Size must have 1+ bits set in the first byte.
63 };
64
65 MockCallback callback;
66 {
67 InSequence dummy;
68
69 EXPECT_CALL(callback, OnEbml(_, _)).Times(0);
70
71 EXPECT_CALL(callback, OnSegmentBegin(_, NotNull())).Times(0);
72 EXPECT_CALL(callback, OnSegmentEnd(_)).Times(0);
73 }
74
75 WebmParser parser;
76 Status status = parser.Feed(&callback, &reader);
77 EXPECT_EQ(Status::kInvalidElementSize, status.code);
78 }
79
TEST_F(WebmParserTest,DefaultParse)80 TEST_F(WebmParserTest, DefaultParse) {
81 BufferReader reader = {
82 0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
83 0x80, // Size = 0.
84
85 0x18, 0x53, 0x80, 0x67, // ID = 0x18538067 (Segment).
86 0x80, // Size = 0.
87 };
88
89 MockCallback callback;
90 {
91 InSequence dummy;
92
93 ElementMetadata metadata = {Id::kEbml, 5, 0, 0};
94 const Ebml ebml{};
95 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
96 EXPECT_CALL(callback, OnEbml(metadata, ebml)).Times(1);
97
98 metadata = {Id::kSegment, 5, 0, 5};
99 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
100 EXPECT_CALL(callback, OnSegmentBegin(metadata, NotNull())).Times(1);
101 EXPECT_CALL(callback, OnSegmentEnd(metadata)).Times(1);
102 }
103
104 WebmParser parser;
105 Status status = parser.Feed(&callback, &reader);
106 EXPECT_EQ(Status::kOkCompleted, status.code);
107 }
108
TEST_F(WebmParserTest,DefaultActionIsRead)109 TEST_F(WebmParserTest, DefaultActionIsRead) {
110 BufferReader reader = {
111 0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
112 0x80, // Size = 0.
113 };
114
115 MockCallback callback;
116 {
117 InSequence dummy;
118
119 const ElementMetadata metadata = {Id::kEbml, 5, 0, 0};
120 const Ebml ebml{};
121
122 // This intentionally does not set the action and relies on the parser using
123 // a default action value of kRead.
124 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull()))
125 .WillOnce(Return(Status(Status::kOkCompleted)));
126 EXPECT_CALL(callback, OnEbml(metadata, ebml)).Times(1);
127 }
128
129 WebmParser parser;
130 Status status = parser.Feed(&callback, &reader);
131 EXPECT_EQ(Status::kOkCompleted, status.code);
132 }
133
TEST_F(WebmParserTest,UnknownElement)134 TEST_F(WebmParserTest, UnknownElement) {
135 BufferReader reader = {
136 0x80, // ID = 0x80.
137 0x80, // Size = 0.
138 };
139
140 MockCallback callback;
141 {
142 InSequence dummy;
143
144 ElementMetadata metadata = {static_cast<Id>(0x80), 2, 0, 0};
145 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
146 EXPECT_CALL(callback, OnUnknownElement(metadata, NotNull(), NotNull()))
147 .Times(1);
148 }
149
150 WebmParser parser;
151 Status status = parser.Feed(&callback, &reader);
152 EXPECT_EQ(Status::kOkCompleted, status.code);
153 }
154
TEST_F(WebmParserTest,SeekEbml)155 TEST_F(WebmParserTest, SeekEbml) {
156 BufferReader reader = {
157 0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
158 0x87, // Size = 7.
159
160 0x42, 0x86, // ID = 0x4286 (EBMLVersion).
161 0x10, 0x00, 0x00, 0x01, // Size = 1.
162 0x02, // Body (value = 2).
163 };
164 std::uint64_t num_to_skip = 5; // Skip the starting EBML element metadata.
165 std::uint64_t num_actually_skipped = 0;
166 reader.Skip(num_to_skip, &num_actually_skipped);
167 EXPECT_EQ(num_to_skip, num_actually_skipped);
168
169 MockCallback callback;
170 {
171 InSequence dummy;
172
173 ElementMetadata metadata = {Id::kEbmlVersion, 6, 1, num_to_skip};
174 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
175
176 metadata = {Id::kEbml, kUnknownHeaderSize, kUnknownElementSize,
177 kUnknownElementPosition};
178 Ebml ebml{};
179 ebml.ebml_version.Set(2, true);
180 EXPECT_CALL(callback, OnEbml(metadata, ebml)).Times(1);
181 }
182
183 WebmParser parser;
184 parser.DidSeek();
185 Status status = parser.Feed(&callback, &reader);
186 EXPECT_EQ(Status::kOkCompleted, status.code);
187 EXPECT_EQ(reader.size(), reader.Position());
188 }
189
TEST_F(WebmParserTest,SeekSegment)190 TEST_F(WebmParserTest, SeekSegment) {
191 BufferReader reader = {
192 0x18, 0x53, 0x80, 0x67, // ID = 0x18538067 (Segment).
193 0x85, // Size = 5.
194
195 0x15, 0x49, 0xA9, 0x66, // ID = 0x1549A966 (Info).
196 0x80, // Size = 0.
197 };
198 std::uint64_t num_to_skip = 5; // Skip the starting Segment element metadata.
199 std::uint64_t num_actually_skipped = 0;
200 reader.Skip(num_to_skip, &num_actually_skipped);
201 EXPECT_EQ(num_to_skip, num_actually_skipped);
202
203 MockCallback callback;
204 {
205 InSequence dummy;
206
207 ElementMetadata metadata = {Id::kInfo, 5, 0, num_to_skip};
208 const Info info{};
209 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
210 EXPECT_CALL(callback, OnInfo(metadata, info)).Times(1);
211
212 metadata = {Id::kSegment, kUnknownHeaderSize, kUnknownElementSize,
213 kUnknownElementPosition};
214 EXPECT_CALL(callback, OnSegmentEnd(metadata)).Times(1);
215 }
216
217 WebmParser parser;
218 parser.DidSeek();
219 Status status = parser.Feed(&callback, &reader);
220 EXPECT_EQ(Status::kOkCompleted, status.code);
221 EXPECT_EQ(reader.size(), reader.Position());
222 }
223
TEST_F(WebmParserTest,SeekVoid)224 TEST_F(WebmParserTest, SeekVoid) {
225 BufferReader reader = {
226 0xEC, // ID = 0xEC (Void).
227 0x81, // Size = 0.
228
229 0xEC, // ID = 0xEC (Void).
230 0x81, // Size = 1.
231 0x00, // Body.
232
233 0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
234 0x80, // Size = 0.
235 };
236 std::uint64_t num_to_skip = 2; // Skip the first Void element.
237 std::uint64_t num_actually_skipped = 0;
238 reader.Skip(num_to_skip, &num_actually_skipped);
239 EXPECT_EQ(num_to_skip, num_actually_skipped);
240
241 MockCallback callback;
242 {
243 InSequence dummy;
244
245 ElementMetadata metadata = {Id::kVoid, 2, 1, num_to_skip};
246 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
247 EXPECT_CALL(callback, OnVoid(metadata, &reader, NotNull())).Times(1);
248
249 metadata = {Id::kEbml, 5, 0, 5};
250 const Ebml ebml{};
251 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
252 EXPECT_CALL(callback, OnEbml(metadata, ebml)).Times(1);
253 }
254
255 WebmParser parser;
256 parser.DidSeek();
257 Status status = parser.Feed(&callback, &reader);
258 EXPECT_EQ(Status::kOkCompleted, status.code);
259 EXPECT_EQ(reader.size(), reader.Position());
260 }
261
TEST_F(WebmParserTest,SwapAfterFailedParse)262 TEST_F(WebmParserTest, SwapAfterFailedParse) {
263 BufferReader reader = {
264 0x00, // Invalid ID.
265
266 0xEC, // ID = 0xEC (Void).
267 0x81, // Size = 1.
268 0x00, // Body.
269 };
270
271 MockCallback expect_nothing;
272 EXPECT_CALL(expect_nothing, OnElementBegin(_, _)).Times(0);
273
274 MockCallback expect_void;
275 {
276 InSequence dummy;
277
278 ElementMetadata metadata = {Id::kVoid, 2, 1, 1};
279 EXPECT_CALL(expect_void, OnElementBegin(metadata, NotNull())).Times(1);
280 EXPECT_CALL(expect_void, OnVoid(metadata, &reader, NotNull())).Times(1);
281 }
282
283 WebmParser parser1;
284 Status status = parser1.Feed(&expect_nothing, &reader);
285 EXPECT_EQ(Status::kInvalidElementId, status.code);
286 EXPECT_EQ(static_cast<std::uint64_t>(1), reader.Position());
287
288 // After swapping, the parser should retain its failed state and not consume
289 // more data.
290 WebmParser parser2;
291 parser2.Swap(&parser1);
292 status = parser2.Feed(&expect_nothing, &reader);
293 EXPECT_EQ(Status::kInvalidElementId, status.code);
294 EXPECT_EQ(static_cast<std::uint64_t>(1), reader.Position());
295
296 // parser1 should be a fresh/new parser after the swap, so parsing should
297 // succeed.
298 status = parser1.Feed(&expect_void, &reader);
299 EXPECT_EQ(Status::kOkCompleted, status.code);
300 EXPECT_EQ(reader.size(), reader.Position());
301 }
302
TEST_F(WebmParserTest,Swap)303 TEST_F(WebmParserTest, Swap) {
304 BufferReader reader = {
305 0xEC, // ID = 0xEC (Void).
306 0x81, // Size = 1.
307 0x00, // Body.
308
309 0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
310 0x80, // Size = 0.
311 };
312
313 MockCallback callback;
314 {
315 InSequence dummy;
316
317 ElementMetadata metadata = {Id::kVoid, 2, 1, 0};
318 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
319 EXPECT_CALL(callback, OnVoid(metadata, &reader, NotNull()))
320 .WillOnce(Return(Status(Status::kOkPartial)))
321 .WillOnce(DoDefault());
322
323 metadata = {Id::kEbml, 5, 0, 3};
324 EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
325 EXPECT_CALL(callback, OnEbml(metadata, Ebml{})).Times(1);
326 }
327
328 WebmParser parser1;
329 Status status = parser1.Feed(&callback, &reader);
330 EXPECT_EQ(Status::kOkPartial, status.code);
331
332 WebmParser parser2;
333 swap(parser1, parser2);
334 status = parser2.Feed(&callback, &reader);
335 EXPECT_EQ(Status::kOkCompleted, status.code);
336 EXPECT_EQ(reader.size(), reader.Position());
337 }
338
339 } // namespace
340