| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/webm/cluster_builder.h" |
| #include "media/webm/webm_constants.h" |
| #include "media/webm/webm_parser.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::InSequence; |
| using ::testing::Return; |
| using ::testing::ReturnNull; |
| using ::testing::StrictMock; |
| using ::testing::_; |
| |
| namespace media { |
| |
| enum { kBlockCount = 5 }; |
| |
| class MockWebMParserClient : public WebMParserClient { |
| public: |
| virtual ~MockWebMParserClient() {} |
| |
| // WebMParserClient methods. |
| MOCK_METHOD1(OnListStart, WebMParserClient*(int)); |
| MOCK_METHOD1(OnListEnd, bool(int)); |
| MOCK_METHOD2(OnUInt, bool(int, int64)); |
| MOCK_METHOD2(OnFloat, bool(int, double)); |
| MOCK_METHOD3(OnBinary, bool(int, const uint8*, int)); |
| MOCK_METHOD2(OnString, bool(int, const std::string&)); |
| }; |
| |
| class WebMParserTest : public testing::Test { |
| protected: |
| StrictMock<MockWebMParserClient> client_; |
| }; |
| |
| static scoped_ptr<Cluster> CreateCluster(int block_count) { |
| ClusterBuilder cb; |
| cb.SetClusterTimecode(0); |
| |
| for (int i = 0; i < block_count; i++) { |
| uint8 data[] = { 0x00 }; |
| cb.AddSimpleBlock(0, i, 0, data, sizeof(data)); |
| } |
| |
| return cb.Finish(); |
| } |
| |
| static void CreateClusterExpectations(int block_count, |
| bool is_complete_cluster, |
| MockWebMParserClient* client) { |
| |
| InSequence s; |
| EXPECT_CALL(*client, OnListStart(kWebMIdCluster)).WillOnce(Return(client)); |
| EXPECT_CALL(*client, OnUInt(kWebMIdTimecode, 0)) |
| .WillOnce(Return(true)); |
| |
| for (int i = 0; i < block_count; i++) { |
| EXPECT_CALL(*client, OnBinary(kWebMIdSimpleBlock, _, _)) |
| .WillOnce(Return(true)); |
| } |
| |
| if (is_complete_cluster) |
| EXPECT_CALL(*client, OnListEnd(kWebMIdCluster)).WillOnce(Return(true)); |
| } |
| |
| TEST_F(WebMParserTest, EmptyCluster) { |
| const uint8 kEmptyCluster[] = { |
| 0x1F, 0x43, 0xB6, 0x75, 0x80 // CLUSTER (size = 0) |
| }; |
| int size = sizeof(kEmptyCluster); |
| |
| InSequence s; |
| EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(&client_)); |
| EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true)); |
| |
| WebMListParser parser(kWebMIdCluster, &client_); |
| int result = parser.Parse(kEmptyCluster, size); |
| EXPECT_EQ(size, result); |
| EXPECT_TRUE(parser.IsParsingComplete()); |
| } |
| |
| TEST_F(WebMParserTest, EmptyClusterInSegment) { |
| const uint8 kBuffer[] = { |
| 0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 5) |
| 0x1F, 0x43, 0xB6, 0x75, 0x80, // CLUSTER (size = 0) |
| }; |
| int size = sizeof(kBuffer); |
| |
| InSequence s; |
| EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&client_)); |
| EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(&client_)); |
| EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true)); |
| EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true)); |
| |
| WebMListParser parser(kWebMIdSegment, &client_); |
| int result = parser.Parse(kBuffer, size); |
| EXPECT_EQ(size, result); |
| EXPECT_TRUE(parser.IsParsingComplete()); |
| } |
| |
| // Test the case where a non-list child element has a size |
| // that is beyond the end of the parent. |
| TEST_F(WebMParserTest, ChildNonListLargerThanParent) { |
| const uint8 kBuffer[] = { |
| 0x1F, 0x43, 0xB6, 0x75, 0x81, // CLUSTER (size = 1) |
| 0xE7, 0x81, 0x01, // Timecode (size=1, value=1) |
| }; |
| int size = sizeof(kBuffer); |
| |
| InSequence s; |
| EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(&client_)); |
| |
| WebMListParser parser(kWebMIdCluster, &client_); |
| int result = parser.Parse(kBuffer, size); |
| EXPECT_EQ(-1, result); |
| EXPECT_FALSE(parser.IsParsingComplete()); |
| } |
| |
| // Test the case where a list child element has a size |
| // that is beyond the end of the parent. |
| TEST_F(WebMParserTest, ChildListLargerThanParent) { |
| const uint8 kBuffer[] = { |
| 0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 5) |
| 0x1F, 0x43, 0xB6, 0x75, 0x81, 0x11 // CLUSTER (size = 1) |
| }; |
| int size = sizeof(kBuffer); |
| |
| InSequence s; |
| EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&client_)); |
| |
| WebMListParser parser(kWebMIdSegment, &client_); |
| int result = parser.Parse(kBuffer, size); |
| EXPECT_EQ(-1, result); |
| EXPECT_FALSE(parser.IsParsingComplete()); |
| } |
| |
| // Expecting to parse a Cluster, but get a Segment. |
| TEST_F(WebMParserTest, ListIdDoesNotMatch) { |
| const uint8 kBuffer[] = { |
| 0x18, 0x53, 0x80, 0x67, 0x80, // SEGMENT (size = 0) |
| }; |
| int size = sizeof(kBuffer); |
| |
| WebMListParser parser(kWebMIdCluster, &client_); |
| int result = parser.Parse(kBuffer, size); |
| EXPECT_EQ(-1, result); |
| EXPECT_FALSE(parser.IsParsingComplete()); |
| } |
| |
| TEST_F(WebMParserTest, InvalidElementInList) { |
| const uint8 kBuffer[] = { |
| 0x18, 0x53, 0x80, 0x67, 0x82, // SEGMENT (size = 2) |
| 0xAE, 0x80, // TrackEntry (size = 0) |
| }; |
| int size = sizeof(kBuffer); |
| |
| InSequence s; |
| EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&client_)); |
| |
| WebMListParser parser(kWebMIdSegment, &client_); |
| int result = parser.Parse(kBuffer, size); |
| EXPECT_EQ(-1, result); |
| EXPECT_FALSE(parser.IsParsingComplete()); |
| } |
| |
| TEST_F(WebMParserTest, VoidAndCRC32InList) { |
| const uint8 kBuffer[] = { |
| 0x18, 0x53, 0x80, 0x67, 0x99, // SEGMENT (size = 25) |
| 0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3) |
| 0xBF, 0x83, 0x00, 0x00, 0x00, // CRC32 (size = 3) |
| 0x1F, 0x43, 0xB6, 0x75, 0x8A, // CLUSTER (size = 10) |
| 0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3) |
| 0xBF, 0x83, 0x00, 0x00, 0x00, // CRC32 (size = 3) |
| }; |
| int size = sizeof(kBuffer); |
| |
| InSequence s; |
| EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&client_)); |
| EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(&client_)); |
| EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true)); |
| EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true)); |
| |
| WebMListParser parser(kWebMIdSegment, &client_); |
| int result = parser.Parse(kBuffer, size); |
| EXPECT_EQ(size, result); |
| EXPECT_TRUE(parser.IsParsingComplete()); |
| } |
| |
| |
| TEST_F(WebMParserTest, ParseListElementWithSingleCall) { |
| scoped_ptr<Cluster> cluster(CreateCluster(kBlockCount)); |
| CreateClusterExpectations(kBlockCount, true, &client_); |
| |
| WebMListParser parser(kWebMIdCluster, &client_); |
| int result = parser.Parse(cluster->data(), cluster->size()); |
| EXPECT_EQ(cluster->size(), result); |
| EXPECT_TRUE(parser.IsParsingComplete()); |
| } |
| |
| TEST_F(WebMParserTest, ParseListElementWithMultipleCalls) { |
| scoped_ptr<Cluster> cluster(CreateCluster(kBlockCount)); |
| CreateClusterExpectations(kBlockCount, true, &client_); |
| |
| const uint8* data = cluster->data(); |
| int size = cluster->size(); |
| int default_parse_size = 3; |
| WebMListParser parser(kWebMIdCluster, &client_); |
| int parse_size = std::min(default_parse_size, size); |
| |
| while (size > 0) { |
| int result = parser.Parse(data, parse_size); |
| ASSERT_GE(result, 0); |
| ASSERT_LE(result, parse_size); |
| |
| if (result == 0) { |
| // The parser needs more data so increase the parse_size a little. |
| EXPECT_FALSE(parser.IsParsingComplete()); |
| parse_size += default_parse_size; |
| parse_size = std::min(parse_size, size); |
| continue; |
| } |
| |
| parse_size = default_parse_size; |
| |
| data += result; |
| size -= result; |
| |
| EXPECT_EQ((size == 0), parser.IsParsingComplete()); |
| } |
| EXPECT_TRUE(parser.IsParsingComplete()); |
| } |
| |
| TEST_F(WebMParserTest, TestReset) { |
| InSequence s; |
| scoped_ptr<Cluster> cluster(CreateCluster(kBlockCount)); |
| |
| // First expect all but the last block. |
| CreateClusterExpectations(kBlockCount - 1, false, &client_); |
| |
| // Now expect all blocks. |
| CreateClusterExpectations(kBlockCount, true, &client_); |
| |
| WebMListParser parser(kWebMIdCluster, &client_); |
| |
| // Send slightly less than the full cluster so all but the last block is |
| // parsed. |
| int result = parser.Parse(cluster->data(), cluster->size() - 1); |
| EXPECT_GT(result, 0); |
| EXPECT_LT(result, cluster->size()); |
| EXPECT_FALSE(parser.IsParsingComplete()); |
| |
| parser.Reset(); |
| |
| // Now parse a whole cluster to verify that all the blocks will get parsed. |
| result = parser.Parse(cluster->data(), cluster->size()); |
| EXPECT_EQ(result, cluster->size()); |
| EXPECT_TRUE(parser.IsParsingComplete()); |
| } |
| |
| // Test the case where multiple clients are used for different lists. |
| TEST_F(WebMParserTest, MultipleClients) { |
| const uint8 kBuffer[] = { |
| 0x18, 0x53, 0x80, 0x67, 0x94, // SEGMENT (size = 20) |
| 0x16, 0x54, 0xAE, 0x6B, 0x85, // TRACKS (size = 5) |
| 0xAE, 0x83, // TRACKENTRY (size = 3) |
| 0xD7, 0x81, 0x01, // TRACKNUMBER (size = 1) |
| 0x1F, 0x43, 0xB6, 0x75, 0x85, // CLUSTER (size = 5) |
| 0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3) |
| }; |
| int size = sizeof(kBuffer); |
| |
| StrictMock<MockWebMParserClient> c1_; |
| StrictMock<MockWebMParserClient> c2_; |
| StrictMock<MockWebMParserClient> c3_; |
| |
| InSequence s; |
| EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&c1_)); |
| EXPECT_CALL(c1_, OnListStart(kWebMIdTracks)).WillOnce(Return(&c2_)); |
| EXPECT_CALL(c2_, OnListStart(kWebMIdTrackEntry)).WillOnce(Return(&c3_)); |
| EXPECT_CALL(c3_, OnUInt(kWebMIdTrackNumber, 1)).WillOnce(Return(true)); |
| EXPECT_CALL(c2_, OnListEnd(kWebMIdTrackEntry)).WillOnce(Return(true)); |
| EXPECT_CALL(c1_, OnListEnd(kWebMIdTracks)).WillOnce(Return(true)); |
| EXPECT_CALL(c1_, OnListStart(kWebMIdCluster)).WillOnce(Return(&c2_)); |
| EXPECT_CALL(c1_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true)); |
| EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true)); |
| |
| WebMListParser parser(kWebMIdSegment, &client_); |
| int result = parser.Parse(kBuffer, size); |
| EXPECT_EQ(size, result); |
| EXPECT_TRUE(parser.IsParsingComplete()); |
| } |
| |
| // Test the case where multiple clients are used for different lists. |
| TEST_F(WebMParserTest, InvalidClient) { |
| const uint8 kBuffer[] = { |
| 0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 20) |
| 0x16, 0x54, 0xAE, 0x6B, 0x80, // TRACKS (size = 5) |
| }; |
| int size = sizeof(kBuffer); |
| |
| InSequence s; |
| EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(ReturnNull()); |
| |
| WebMListParser parser(kWebMIdSegment, &client_); |
| int result = parser.Parse(kBuffer, size); |
| EXPECT_EQ(-1, result); |
| EXPECT_FALSE(parser.IsParsingComplete()); |
| } |
| |
| TEST_F(WebMParserTest, ReservedIds) { |
| const uint8 k1ByteReservedId[] = { 0xFF, 0x81 }; |
| const uint8 k2ByteReservedId[] = { 0x7F, 0xFF, 0x81 }; |
| const uint8 k3ByteReservedId[] = { 0x3F, 0xFF, 0xFF, 0x81 }; |
| const uint8 k4ByteReservedId[] = { 0x1F, 0xFF, 0xFF, 0xFF, 0x81 }; |
| const uint8* kBuffers[] = { |
| k1ByteReservedId, |
| k2ByteReservedId, |
| k3ByteReservedId, |
| k4ByteReservedId |
| }; |
| |
| for (size_t i = 0; i < arraysize(kBuffers); i++) { |
| int id; |
| int64 element_size; |
| int buffer_size = 2 + i; |
| EXPECT_EQ(buffer_size, WebMParseElementHeader(kBuffers[i], buffer_size, |
| &id, &element_size)); |
| EXPECT_EQ(id, kWebMReservedId); |
| EXPECT_EQ(element_size, 1); |
| } |
| } |
| |
| TEST_F(WebMParserTest, ReservedSizes) { |
| const uint8 k1ByteReservedSize[] = { 0xA3, 0xFF }; |
| const uint8 k2ByteReservedSize[] = { 0xA3, 0x7F, 0xFF }; |
| const uint8 k3ByteReservedSize[] = { 0xA3, 0x3F, 0xFF, 0xFF }; |
| const uint8 k4ByteReservedSize[] = { 0xA3, 0x1F, 0xFF, 0xFF, 0xFF }; |
| const uint8 k5ByteReservedSize[] = { 0xA3, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF }; |
| const uint8 k6ByteReservedSize[] = { 0xA3, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, |
| 0xFF }; |
| const uint8 k7ByteReservedSize[] = { 0xA3, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
| 0xFF }; |
| const uint8 k8ByteReservedSize[] = { 0xA3, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
| 0xFF, 0xFF }; |
| const uint8* kBuffers[] = { |
| k1ByteReservedSize, |
| k2ByteReservedSize, |
| k3ByteReservedSize, |
| k4ByteReservedSize, |
| k5ByteReservedSize, |
| k6ByteReservedSize, |
| k7ByteReservedSize, |
| k8ByteReservedSize |
| }; |
| |
| for (size_t i = 0; i < arraysize(kBuffers); i++) { |
| int id; |
| int64 element_size; |
| int buffer_size = 2 + i; |
| EXPECT_EQ(buffer_size, WebMParseElementHeader(kBuffers[i], buffer_size, |
| &id, &element_size)); |
| EXPECT_EQ(id, 0xA3); |
| EXPECT_EQ(element_size, kWebMUnknownSize); |
| } |
| } |
| |
| } // namespace media |