blob: 26abbafe3c8f4ef9ec557094e9b524ee3bcd71f8 [file] [log] [blame]
// 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, DefaultActionIsRead) {
BufferReader reader = {
0x1A, 0x45, 0xDF, 0xA3, // ID = 0x1A45DFA3 (EBML).
0x80, // Size = 0.
};
MockCallback callback;
{
InSequence dummy;
const ElementMetadata metadata = {Id::kEbml, 5, 0, 0};
const Ebml ebml{};
// This intentionally does not set the action and relies on the parser using
// a default action value of kRead.
EXPECT_CALL(callback, OnElementBegin(metadata, NotNull()))
.WillOnce(Return(Status(Status::kOkCompleted)));
EXPECT_CALL(callback, OnEbml(metadata, ebml)).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