#include "src/master_parser.h"
#include <cstdint>
#include <memory>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "src/byte_parser.h"
#include "test_utils/element_parser_test.h"
#include "webm/element.h"
#include "webm/id.h"
#include "webm/status.h"
using testing::_;
using testing::DoAll;
using testing::InSequence;
using testing::NotNull;
using testing::Return;
using testing::SetArgPointee;
using webm::Action;
using webm::BinaryParser;
using webm::ElementMetadata;
using webm::ElementParser;
using webm::ElementParserTest;
using webm::Id;
using webm::kUnknownElementSize;
using webm::LimitedReader;
using webm::MasterParser;
using webm::Status;
namespace {
std::pair<Id, std::unique_ptr<ElementParser>> ParserForId(
Id id, ElementParser* parser) {
return {id, std::unique_ptr<ElementParser>(parser)};
}
class MasterParserTest : public ElementParserTest<MasterParser> {};
TEST_F(MasterParserTest, BadId) {
SetReaderData({
0x00, 0x80, 0x80, });
EXPECT_CALL(callback_, OnElementBegin(_, _)).Times(0);
ParseAndExpectResult(Status::kInvalidElementId);
}
TEST_F(MasterParserTest, ChildInitFails) {
SetReaderData({
0xEC, 0xFF, });
const ElementMetadata metadata = {Id::kVoid, 2, kUnknownElementSize, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
ParseAndExpectResult(Status::kInvalidElementSize);
}
TEST_F(MasterParserTest, IndefiniteUnknownChild) {
SetReaderData({
0x80, 0xFF, 0x00, 0x00, });
EXPECT_CALL(callback_, OnElementBegin(_, _)).Times(0);
ParseAndExpectResult(Status::kIndefiniteUnknownElement);
}
TEST_F(MasterParserTest, ChildOverflow) {
SetReaderData({
0xEC, 0x82, 0x00, 0x00, });
EXPECT_CALL(callback_, OnElementBegin(_, _)).Times(0);
EXPECT_CALL(callback_, OnVoid(_, _, _)).Times(0);
ParseAndExpectResult(Status::kElementOverflow, reader_.size() - 1);
}
TEST_F(MasterParserTest, ChildOverflowWithUnknownSize) {
SetReaderData({
0xA1, 0xFF,
0xEC, 0x81, 0x12, });
{
InSequence dummy;
ElementMetadata metadata = {Id::kBlock, 2, kUnknownElementSize, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
}
ResetParser(ParserForId(Id::kBlock, new MasterParser));
ParseAndExpectResult(Status::kElementOverflow, 4);
}
TEST_F(MasterParserTest, ChildWithUnknownSizeBoundedByParentSize) {
SetReaderData({
0xA1, 0xFF,
0xEC, 0x81, 0x12,
0x00, });
{
InSequence dummy;
ElementMetadata metadata = {Id::kBlock, 2, kUnknownElementSize, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
metadata = {Id::kVoid, 2, 1, 2};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
EXPECT_CALL(callback_, OnVoid(metadata, NotNull(), NotNull())).Times(1);
}
ResetParser(ParserForId(Id::kBlock, new MasterParser));
ParseAndVerify(reader_.size() - 1);
}
TEST_F(MasterParserTest, Empty) {
EXPECT_CALL(callback_, OnElementBegin(_, _)).Times(0);
ParseAndVerify();
}
TEST_F(MasterParserTest, DefaultActionIsRead) {
SetReaderData({
0xEC, 0x80, });
{
InSequence dummy;
const ElementMetadata metadata = {Id::kVoid, 2, 0, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull()))
.WillOnce(Return(Status(Status::kOkCompleted)));
EXPECT_CALL(callback_, OnVoid(metadata, NotNull(), NotNull())).Times(1);
}
ParseAndVerify();
}
TEST_F(MasterParserTest, UnknownChildren) {
SetReaderData({
0x40, 0x00, 0x80,
0x80, 0x40, 0x00, });
EXPECT_CALL(callback_, OnVoid(_, _, _)).Times(0);
{
InSequence dummy;
ElementMetadata metadata = {static_cast<Id>(0x4000), 3, 0, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
metadata = {static_cast<Id>(0x80), 3, 0, 3};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
}
ParseAndVerify();
}
TEST_F(MasterParserTest, UnknownSize) {
SetReaderData({
0xEC, 0x81, 0x00,
0x80, 0x81, 0x12, });
{
InSequence dummy;
const ElementMetadata metadata = {Id::kVoid, 2, 1, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
EXPECT_CALL(callback_, OnVoid(metadata, NotNull(), NotNull())).Times(1);
}
ParseAndVerify(kUnknownElementSize);
EXPECT_EQ(static_cast<std::uint64_t>(5), reader_.Position());
}
TEST_F(MasterParserTest, MultipleUnknownChildSize) {
SetReaderData({
0xA1, 0xFF,
0xEC, 0x81, 0x12,
0xA1, 0xFF,
0xEC, 0x81, 0x13, });
{
InSequence dummy;
ElementMetadata metadata = {Id::kBlock, 2, kUnknownElementSize, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
metadata = {Id::kVoid, 2, 1, 2};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
EXPECT_CALL(callback_, OnVoid(metadata, NotNull(), NotNull())).Times(1);
metadata = {Id::kBlock, 2, kUnknownElementSize, 5};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
metadata = {Id::kVoid, 2, 1, 7};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
EXPECT_CALL(callback_, OnVoid(metadata, NotNull(), NotNull())).Times(1);
}
ResetParser(ParserForId(Id::kBlock, new MasterParser));
ParseAndVerify();
}
TEST_F(MasterParserTest, UnknownSizeToFileEnd) {
SetReaderData({
0xEC, 0x81, 0x00, });
{
InSequence dummy;
const ElementMetadata metadata = {Id::kVoid, 2, 1, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
EXPECT_CALL(callback_, OnVoid(metadata, NotNull(), NotNull())).Times(1);
}
ParseAndVerify();
}
TEST_F(MasterParserTest, IncrementalParse) {
SetReaderData({
0x1A, 0x45, 0xDF, 0xA3, 0x08, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, });
const ElementMetadata metadata = {Id::kEbml, 9, 6, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
BinaryParser* binary_parser = new BinaryParser;
ResetParser(ParserForId(Id::kEbml, binary_parser));
IncrementalParseAndVerify();
std::vector<std::uint8_t> expected = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
EXPECT_EQ(expected, binary_parser->value());
}
TEST_F(MasterParserTest, IncrementalSkipThenReadThenSkip) {
SetReaderData({
0xA1, 0x83,
0xEC, 0x81, 0x12,
0xA1, 0x83,
0xEC, 0x81, 0x13,
0xA1, 0xFF,
0xEC, 0x81, 0x14, });
{
InSequence dummy;
ElementMetadata metadata = {Id::kBlock, 2, 3, 0};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull()))
.WillOnce(Return(Status(Status::kOkPartial)))
.WillOnce(DoAll(SetArgPointee<1>(Action::kSkip),
Return(Status(Status::kOkCompleted))));
metadata = {Id::kBlock, 2, 3, 5};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
metadata = {Id::kVoid, 2, 1, 7};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull())).Times(1);
EXPECT_CALL(callback_, OnVoid(metadata, NotNull(), NotNull())).Times(2);
metadata = {Id::kBlock, 2, kUnknownElementSize, 10};
EXPECT_CALL(callback_, OnElementBegin(metadata, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(Action::kSkip),
Return(Status(Status::kOkCompleted))));
}
ResetParser(ParserForId(Id::kBlock, new MasterParser));
IncrementalParseAndVerify();
}
}