80685d3865
OnUnknownElement() should be called to handle them. Change-Id: Iebd99631f094e95d0c3e75952930962e15826bac
315 lines
9.0 KiB
C++
315 lines
9.0 KiB
C++
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file in the root of the source
|
|
// tree. An additional intellectual property rights grant can be found
|
|
// in the file PATENTS. All contributing project authors may
|
|
// be found in the AUTHORS file in the root of the source tree.
|
|
#include "webm/webm_parser.h"
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "test_utils/mock_callback.h"
|
|
#include "webm/buffer_reader.h"
|
|
#include "webm/status.h"
|
|
|
|
using testing::_;
|
|
using testing::DoDefault;
|
|
using testing::InSequence;
|
|
using testing::NotNull;
|
|
using testing::Return;
|
|
|
|
using webm::BufferReader;
|
|
using webm::Ebml;
|
|
using webm::ElementMetadata;
|
|
using webm::Id;
|
|
using webm::Info;
|
|
using webm::kUnknownElementPosition;
|
|
using webm::kUnknownElementSize;
|
|
using webm::kUnknownHeaderSize;
|
|
using webm::MockCallback;
|
|
using webm::Status;
|
|
using webm::WebmParser;
|
|
|
|
namespace {
|
|
|
|
class WebmParserTest : public testing::Test {};
|
|
|
|
TEST_F(WebmParserTest, InvalidId) {
|
|
BufferReader reader = {
|
|
0x00, // IDs cannot start with 0x00.
|
|
};
|
|
|
|
MockCallback callback;
|
|
{
|
|
InSequence dummy;
|
|
|
|
EXPECT_CALL(callback, OnEbml(_, _)).Times(0);
|
|
|
|
EXPECT_CALL(callback, OnSegmentBegin(_, NotNull())).Times(0);
|
|
EXPECT_CALL(callback, OnSegmentEnd(_)).Times(0);
|
|
}
|
|
|
|
WebmParser parser;
|
|
Status status = parser.Feed(&callback, &reader);
|
|
EXPECT_EQ(Status::kInvalidElementId, status.code);
|
|
}
|
|
|
|
TEST_F(WebmParserTest, InvalidSize) {
|
|
BufferReader reader = {
|
|
0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
|
|
0x00, // Size must have 1+ bits set in the first byte.
|
|
};
|
|
|
|
MockCallback callback;
|
|
{
|
|
InSequence dummy;
|
|
|
|
EXPECT_CALL(callback, OnEbml(_, _)).Times(0);
|
|
|
|
EXPECT_CALL(callback, OnSegmentBegin(_, NotNull())).Times(0);
|
|
EXPECT_CALL(callback, OnSegmentEnd(_)).Times(0);
|
|
}
|
|
|
|
WebmParser parser;
|
|
Status status = parser.Feed(&callback, &reader);
|
|
EXPECT_EQ(Status::kInvalidElementSize, status.code);
|
|
}
|
|
|
|
TEST_F(WebmParserTest, DefaultParse) {
|
|
BufferReader reader = {
|
|
0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
|
|
0x80, // Size = 0.
|
|
|
|
0x18, 0x53, 0x80, 0x67, // ID = 0x18538067 (Segment).
|
|
0x80, // Size = 0.
|
|
};
|
|
|
|
MockCallback callback;
|
|
{
|
|
InSequence dummy;
|
|
|
|
ElementMetadata metadata = {Id::kEbml, 5, 0, 0};
|
|
const Ebml ebml{};
|
|
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(callback, OnEbml(metadata, ebml)).Times(1);
|
|
|
|
metadata = {Id::kSegment, 5, 0, 5};
|
|
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(callback, OnSegmentBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(callback, OnSegmentEnd(metadata)).Times(1);
|
|
}
|
|
|
|
WebmParser parser;
|
|
Status status = parser.Feed(&callback, &reader);
|
|
EXPECT_EQ(Status::kOkCompleted, status.code);
|
|
}
|
|
|
|
TEST_F(WebmParserTest, UnknownElement) {
|
|
BufferReader reader = {
|
|
0x80, // ID = 0x80.
|
|
0x80, // Size = 0.
|
|
};
|
|
|
|
MockCallback callback;
|
|
{
|
|
InSequence dummy;
|
|
|
|
ElementMetadata metadata = {static_cast<Id>(0x80), 2, 0, 0};
|
|
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(callback, OnUnknownElement(metadata, NotNull(), NotNull()))
|
|
.Times(1);
|
|
}
|
|
|
|
WebmParser parser;
|
|
Status status = parser.Feed(&callback, &reader);
|
|
EXPECT_EQ(Status::kOkCompleted, status.code);
|
|
}
|
|
|
|
TEST_F(WebmParserTest, SeekEbml) {
|
|
BufferReader reader = {
|
|
0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
|
|
0x87, // Size = 7.
|
|
|
|
0x42, 0x86, // ID = 0x4286 (EBMLVersion).
|
|
0x10, 0x00, 0x00, 0x01, // Size = 1.
|
|
0x02, // Body (value = 2).
|
|
};
|
|
std::uint64_t num_to_skip = 5; // Skip the starting EBML element metadata.
|
|
std::uint64_t num_actually_skipped = 0;
|
|
reader.Skip(num_to_skip, &num_actually_skipped);
|
|
EXPECT_EQ(num_to_skip, num_actually_skipped);
|
|
|
|
MockCallback callback;
|
|
{
|
|
InSequence dummy;
|
|
|
|
ElementMetadata metadata = {Id::kEbmlVersion, 6, 1, num_to_skip};
|
|
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
|
|
|
|
metadata = {Id::kEbml, kUnknownHeaderSize, kUnknownElementSize,
|
|
kUnknownElementPosition};
|
|
Ebml ebml{};
|
|
ebml.ebml_version.Set(2, true);
|
|
EXPECT_CALL(callback, OnEbml(metadata, ebml)).Times(1);
|
|
}
|
|
|
|
WebmParser parser;
|
|
parser.DidSeek();
|
|
Status status = parser.Feed(&callback, &reader);
|
|
EXPECT_EQ(Status::kOkCompleted, status.code);
|
|
EXPECT_EQ(reader.size(), reader.Position());
|
|
}
|
|
|
|
TEST_F(WebmParserTest, SeekSegment) {
|
|
BufferReader reader = {
|
|
0x18, 0x53, 0x80, 0x67, // ID = 0x18538067 (Segment).
|
|
0x85, // Size = 5.
|
|
|
|
0x15, 0x49, 0xA9, 0x66, // ID = 0x1549A966 (Info).
|
|
0x80, // Size = 0.
|
|
};
|
|
std::uint64_t num_to_skip = 5; // Skip the starting Segment element metadata.
|
|
std::uint64_t num_actually_skipped = 0;
|
|
reader.Skip(num_to_skip, &num_actually_skipped);
|
|
EXPECT_EQ(num_to_skip, num_actually_skipped);
|
|
|
|
MockCallback callback;
|
|
{
|
|
InSequence dummy;
|
|
|
|
ElementMetadata metadata = {Id::kInfo, 5, 0, num_to_skip};
|
|
const Info info{};
|
|
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(callback, OnInfo(metadata, info)).Times(1);
|
|
|
|
metadata = {Id::kSegment, kUnknownHeaderSize, kUnknownElementSize,
|
|
kUnknownElementPosition};
|
|
EXPECT_CALL(callback, OnSegmentEnd(metadata)).Times(1);
|
|
}
|
|
|
|
WebmParser parser;
|
|
parser.DidSeek();
|
|
Status status = parser.Feed(&callback, &reader);
|
|
EXPECT_EQ(Status::kOkCompleted, status.code);
|
|
EXPECT_EQ(reader.size(), reader.Position());
|
|
}
|
|
|
|
TEST_F(WebmParserTest, SeekVoid) {
|
|
BufferReader reader = {
|
|
0xEC, // ID = 0xEC (Void).
|
|
0x81, // Size = 0.
|
|
|
|
0xEC, // ID = 0xEC (Void).
|
|
0x81, // Size = 1.
|
|
0x00, // Body.
|
|
|
|
0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
|
|
0x80, // Size = 0.
|
|
};
|
|
std::uint64_t num_to_skip = 2; // Skip the first Void element.
|
|
std::uint64_t num_actually_skipped = 0;
|
|
reader.Skip(num_to_skip, &num_actually_skipped);
|
|
EXPECT_EQ(num_to_skip, num_actually_skipped);
|
|
|
|
MockCallback callback;
|
|
{
|
|
InSequence dummy;
|
|
|
|
ElementMetadata metadata = {Id::kVoid, 2, 1, num_to_skip};
|
|
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(callback, OnVoid(metadata, &reader, NotNull())).Times(1);
|
|
|
|
metadata = {Id::kEbml, 5, 0, 5};
|
|
const Ebml ebml{};
|
|
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(callback, OnEbml(metadata, ebml)).Times(1);
|
|
}
|
|
|
|
WebmParser parser;
|
|
parser.DidSeek();
|
|
Status status = parser.Feed(&callback, &reader);
|
|
EXPECT_EQ(Status::kOkCompleted, status.code);
|
|
EXPECT_EQ(reader.size(), reader.Position());
|
|
}
|
|
|
|
TEST_F(WebmParserTest, SwapAfterFailedParse) {
|
|
BufferReader reader = {
|
|
0x00, // Invalid ID.
|
|
|
|
0xEC, // ID = 0xEC (Void).
|
|
0x81, // Size = 1.
|
|
0x00, // Body.
|
|
};
|
|
|
|
MockCallback expect_nothing;
|
|
EXPECT_CALL(expect_nothing, OnElementBegin(_, _)).Times(0);
|
|
|
|
MockCallback expect_void;
|
|
{
|
|
InSequence dummy;
|
|
|
|
ElementMetadata metadata = {Id::kVoid, 2, 1, 1};
|
|
EXPECT_CALL(expect_void, OnElementBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(expect_void, OnVoid(metadata, &reader, NotNull())).Times(1);
|
|
}
|
|
|
|
WebmParser parser1;
|
|
Status status = parser1.Feed(&expect_nothing, &reader);
|
|
EXPECT_EQ(Status::kInvalidElementId, status.code);
|
|
EXPECT_EQ(static_cast<std::uint64_t>(1), reader.Position());
|
|
|
|
// After swapping, the parser should retain its failed state and not consume
|
|
// more data.
|
|
WebmParser parser2;
|
|
parser2.Swap(&parser1);
|
|
status = parser2.Feed(&expect_nothing, &reader);
|
|
EXPECT_EQ(Status::kInvalidElementId, status.code);
|
|
EXPECT_EQ(static_cast<std::uint64_t>(1), reader.Position());
|
|
|
|
// parser1 should be a fresh/new parser after the swap, so parsing should
|
|
// succeed.
|
|
status = parser1.Feed(&expect_void, &reader);
|
|
EXPECT_EQ(Status::kOkCompleted, status.code);
|
|
EXPECT_EQ(reader.size(), reader.Position());
|
|
}
|
|
|
|
TEST_F(WebmParserTest, Swap) {
|
|
BufferReader reader = {
|
|
0xEC, // ID = 0xEC (Void).
|
|
0x81, // Size = 1.
|
|
0x00, // Body.
|
|
|
|
0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
|
|
0x80, // Size = 0.
|
|
};
|
|
|
|
MockCallback callback;
|
|
{
|
|
InSequence dummy;
|
|
|
|
ElementMetadata metadata = {Id::kVoid, 2, 1, 0};
|
|
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(callback, OnVoid(metadata, &reader, NotNull()))
|
|
.WillOnce(Return(Status(Status::kOkPartial)))
|
|
.WillOnce(DoDefault());
|
|
|
|
metadata = {Id::kEbml, 5, 0, 3};
|
|
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull())).Times(1);
|
|
EXPECT_CALL(callback, OnEbml(metadata, Ebml{})).Times(1);
|
|
}
|
|
|
|
WebmParser parser1;
|
|
Status status = parser1.Feed(&callback, &reader);
|
|
EXPECT_EQ(Status::kOkPartial, status.code);
|
|
|
|
WebmParser parser2;
|
|
swap(parser1, parser2);
|
|
status = parser2.Feed(&callback, &reader);
|
|
EXPECT_EQ(Status::kOkCompleted, status.code);
|
|
EXPECT_EQ(reader.size(), reader.Position());
|
|
}
|
|
|
|
} // namespace
|