Add tests for SimpleMpdNotifier

- Add Mock{MpdBuilder, AdaptationSet, Representation}.

Change-Id: I7444e0fa955abedded83809fbb8b20ea6357e78a
This commit is contained in:
Rintaro Kuroiwa 2015-07-15 23:51:24 -07:00
parent fe851f692b
commit 817b2a3a19
7 changed files with 397 additions and 36 deletions

View File

@ -0,0 +1,34 @@
#include "packager/mpd/base/mock_mpd_builder.h"
#include "packager/mpd/base/media_info.pb.h"
namespace edash_packager {
namespace {
const uint32_t kAnyAdaptationSetId = 1;
const char kEmptyLang[] = "";
const MpdOptions kDefaultMpdOptions;
const MpdBuilder::MpdType kDefaultMpdType = MpdBuilder::kStatic;
} // namespace
// Doesn't matter what values get passed to the super class' constructor.
// All methods used for testing should be mocked.
MockMpdBuilder::MockMpdBuilder(MpdType type)
: MpdBuilder(type, kDefaultMpdOptions) {}
MockMpdBuilder::~MockMpdBuilder() {}
MockAdaptationSet::MockAdaptationSet()
: AdaptationSet(kAnyAdaptationSetId,
kEmptyLang,
kDefaultMpdOptions,
kDefaultMpdType,
&sequence_counter_) {}
MockAdaptationSet::~MockAdaptationSet() {}
MockRepresentation::MockRepresentation(uint32_t representation_id)
: Representation(MediaInfo(),
kDefaultMpdOptions,
representation_id,
scoped_ptr<RepresentationStateChangeListener>()) {}
MockRepresentation::~MockRepresentation() {}
} // namespace edash_packager

View File

@ -0,0 +1,56 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef MPD_BASE_MOCK_MPD_BUILDER_H_
#define MPD_BASE_MOCK_MPD_BUILDER_H_
#include <gmock/gmock.h>
#include "packager/base/compiler_specific.h"
#include "packager/mpd/base/content_protection_element.h"
#include "packager/mpd/base/mpd_builder.h"
namespace edash_packager {
class MockMpdBuilder : public MpdBuilder {
public:
// type indicates whether the MPD should be for VOD or live content (kStatic
// for VOD profile, or kDynamic for live profile).
explicit MockMpdBuilder(MpdType type);
virtual ~MockMpdBuilder() OVERRIDE;
MOCK_METHOD1(AddAdaptationSet, AdaptationSet*(const std::string& lang));
MOCK_METHOD1(ToString, bool(std::string* output));
};
class MockAdaptationSet : public AdaptationSet {
public:
MockAdaptationSet();
virtual ~MockAdaptationSet() OVERRIDE;
MOCK_METHOD1(AddRepresentation, Representation*(const MediaInfo& media_info));
private:
// Only for constructing the super class. Not used for testing.
base::AtomicSequenceNumber sequence_counter_;
};
class MockRepresentation : public Representation {
public:
// |representation_id| is the numeric ID for the <Representation>.
explicit MockRepresentation(uint32_t representation_id);
virtual ~MockRepresentation() OVERRIDE;
MOCK_METHOD1(AddContentProtectionElement,
void(const ContentProtectionElement& element));
MOCK_METHOD3(AddNewSegment,
void(uint64_t start_time, uint64_t duration, uint64_t size));
MOCK_METHOD1(SetSampleDuration, void(uint32_t sample_duration));
};
} // namespace edash_packager
#endif // MPD_BASE_MOCK_MPD_BUILDER_H_

View File

@ -391,7 +391,6 @@ bool MpdBuilder::ToString(std::string* output) {
DCHECK(output);
return WriteMpdToOutput(output);
}
template <typename OutputType>
bool MpdBuilder::WriteMpdToOutput(OutputType* output) {
static LibXmlInitializer lib_xml_initializer;

View File

@ -8,6 +8,7 @@
// declarations.
// http://goo.gl/UrsSlF
//
/// All the methods that are virtual are virtual for mocking.
/// NOTE: Inclusion of this module will cause xmlInitParser and xmlCleanupParser
/// to be called at static initialization / deinitialization time.
@ -64,7 +65,7 @@ class MpdBuilder {
/// @param type indicates whether the MPD should be for VOD or live content
/// (kStatic for VOD profile, or kDynamic for live profile).
MpdBuilder(MpdType type, const MpdOptions& mpd_options);
~MpdBuilder();
virtual ~MpdBuilder();
/// Add <BaseURL> entry to the MPD.
/// @param base_url URL for <BaseURL> entry.
@ -74,7 +75,7 @@ class MpdBuilder {
/// @param lang is the language of the AdaptationSet. This can be empty for
/// videos, for example.
/// @return The new adaptation set, which is owned by this instance.
AdaptationSet* AddAdaptationSet(const std::string& lang);
virtual AdaptationSet* AddAdaptationSet(const std::string& lang);
/// Write the MPD to specified file.
/// @param[out] output_file is MPD destination. output_file will be
@ -85,10 +86,10 @@ class MpdBuilder {
/// Writes the MPD to the given string.
/// @param[out] output is an output string where the MPD gets written.
/// @return true on success, false otherwise.
bool ToString(std::string* output);
virtual bool ToString(std::string* output);
/// @return The mpd type.
MpdType type() { return type_; }
MpdType type() const { return type_; }
/// Adjusts the fields of MediaInfo so that paths are relative to the
/// specified MPD path.
@ -168,14 +169,14 @@ class AdaptationSet {
kRoleDub
};
~AdaptationSet();
virtual ~AdaptationSet();
/// Create a Representation instance using @a media_info.
/// @param media_info is a MediaInfo object used to initialize the returned
/// Representation instance.
/// @return On success, returns a pointer to Representation. Otherwise returns
/// NULL. The returned pointer is owned by the AdaptationSet instance.
Representation* AddRepresentation(const MediaInfo& media_info);
virtual Representation* AddRepresentation(const MediaInfo& media_info);
/// Add a ContenProtection element to the adaptation set.
/// AdaptationSet does not add <ContentProtection> elements
@ -234,6 +235,20 @@ class AdaptationSet {
uint64_t start_time,
uint64_t duration);
protected:
/// @param adaptation_set_id is an ID number for this AdaptationSet.
/// @param lang is the language of this AdaptationSet. Mainly relevant for
/// audio.
/// @param mpd_options is the options for this MPD.
/// @param mpd_type is the type of this MPD.
/// @param representation_counter is a Counter for assigning ID numbers to
/// Representation. It can not be NULL.
AdaptationSet(uint32_t adaptation_set_id,
const std::string& lang,
const MpdOptions& mpd_options,
MpdBuilder::MpdType mpd_type,
base::AtomicSequenceNumber* representation_counter);
private:
// kSegmentAlignmentUnknown means that it is uncertain if the
// (sub)segments are aligned or not.
@ -257,6 +272,7 @@ class AdaptationSet {
typedef std::map<uint32_t, std::list<uint64_t> > RepresentationTimeline;
friend class MpdBuilder;
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckAdaptationSetId);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest,
CheckAdaptationSetVideoContentType);
@ -269,19 +285,6 @@ class AdaptationSet {
FRIEND_TEST_ALL_PREFIXES(StaticMpdBuilderTest, ForceSetSubSegmentAlignment);
FRIEND_TEST_ALL_PREFIXES(DynamicMpdBuilderTest, SegmentAlignment);
/// @param adaptation_set_id is an ID number for this AdaptationSet.
/// @param lang is the language of this AdaptationSet. Mainly relevant for
/// audio.
/// @param mpd_options is the options for this MPD.
/// @param mpd_type is the type of this MPD.
/// @param representation_counter is a Counter for assigning ID numbers to
/// Representation. It can not be NULL.
AdaptationSet(uint32_t adaptation_set_id,
const std::string& lang,
const MpdOptions& mpd_options,
MpdBuilder::MpdType mpd_type,
base::AtomicSequenceNumber* representation_counter);
// Gets the earliest, normalized segment timestamp. Returns true if
// successful, false otherwise.
bool GetEarliestTimestamp(double* timestamp_seconds);
@ -374,7 +377,7 @@ class RepresentationStateChangeListener {
/// well as optional ContentProtection elements for that stream.
class Representation {
public:
~Representation();
virtual ~Representation();
/// Tries to initialize the instance. If this does not succeed, the instance
/// should not be used.
@ -391,7 +394,8 @@ class Representation {
/// If @a element has {value, schemeIdUri} set and has
/// {“value”, “schemeIdUri”} as key for @a additional_attributes,
/// then the former is used.
void AddContentProtectionElement(const ContentProtectionElement& element);
virtual void AddContentProtectionElement(
const ContentProtectionElement& element);
/// Add a media (sub)segment to the representation.
/// AdaptationSet@{subSegmentAlignment,segmentAlignment} cannot be set
@ -401,14 +405,16 @@ class Representation {
/// @param duration is the duration of the segment, in units of the stream's
/// time scale.
/// @param size of the segment in bytes.
void AddNewSegment(uint64_t start_time, uint64_t duration, uint64_t size);
virtual void AddNewSegment(uint64_t start_time,
uint64_t duration,
uint64_t size);
/// Set the sample duration of this Representation.
/// In most cases, the sample duration is not available right away. This
/// allows setting the sample duration after the Representation has been
/// initialized.
/// @param sample_duration is the duration of a sample.
void SetSampleDuration(uint32_t sample_duration);
virtual void SetSampleDuration(uint32_t sample_duration);
/// @return Copy of <Representation>.
xml::ScopedXmlPtr<xmlNode>::type GetXml();
@ -416,18 +422,7 @@ class Representation {
/// @return ID number for <Representation>.
uint32_t id() const { return id_; }
private:
friend class AdaptationSet;
// TODO(rkuroiwa): Consider defining a public factory method that constructs
// and Init()s, at least for testing.
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, ValidMediaInfo);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, InvalidMediaInfo);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckRepresentationId);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, SetSampleDuration);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest,
RepresentationStateChangeListener);
protected:
/// @param media_info is a MediaInfo containing information on the media.
/// @a media_info.bandwidth is required for 'static' profile. If @a
/// media_info.bandwidth is not present in 'dynamic' profile, this
@ -442,6 +437,19 @@ class Representation {
uint32_t representation_id,
scoped_ptr<RepresentationStateChangeListener> state_change_listener);
private:
friend class AdaptationSet;
// TODO(rkuroiwa): Consider defining a public factory method that constructs
// and Init()s, at least for testing.
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, ValidMediaInfo);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, InvalidMediaInfo);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckRepresentationId);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, SetSampleDuration);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest,
RepresentationStateChangeListener);
bool AddLiveInfo(xml::RepresentationXmlNode* representation);
// Returns true if |media_info_| has required fields to generate a valid

View File

@ -11,6 +11,7 @@
#include <string>
#include <vector>
#include "packager/base/gtest_prod_util.h"
#include "packager/base/memory/scoped_ptr.h"
#include "packager/base/synchronization/lock.h"
#include "packager/mpd/base/mpd_notifier.h"
@ -20,6 +21,7 @@ namespace edash_packager {
class AdaptationSet;
class MpdBuilder;
class Representation;
class SimpleMpdNotifierTest;
struct MpdOptions;
@ -50,6 +52,18 @@ class SimpleMpdNotifier : public MpdNotifier {
/// @}
private:
friend SimpleMpdNotifierTest;
// Testing only method. Returns a pointer to MpdBuilder.
MpdBuilder* MpdBuilderForTesting() const {
return mpd_builder_.get();
}
// Testing only method. Sets mpd_builder_.
void SetMpdBuilderForTesting(scoped_ptr<MpdBuilder> mpd_builder) {
mpd_builder_ = mpd_builder.Pass();
}
enum ContentType {
kUnknown,
kVideo,

View File

@ -0,0 +1,247 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gtest/gtest.h>
#include "packager/base/file_util.h"
#include "packager/base/files/file_path.h"
#include "packager/mpd/base/mock_mpd_builder.h"
#include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/mpd_options.h"
#include "packager/mpd/base/simple_mpd_notifier.h"
#include "packager/mpd/test/mpd_builder_test_helper.h"
namespace edash_packager {
using ::testing::_;
using ::testing::Return;
namespace {
const char kValidMediaInfo[] =
"video_info {\n"
" codec: 'avc1'\n"
" width: 1280\n"
" height: 720\n"
" time_scale: 10\n"
" frame_duration: 10\n"
" pixel_width: 1\n"
" pixel_height: 1\n"
"}\n"
"container_type: 1\n";
} // namespace
class SimpleMpdNotifierTest
: public ::testing::TestWithParam<MpdBuilder::MpdType> {
protected:
SimpleMpdNotifierTest()
: default_mock_adaptation_set_(new MockAdaptationSet()) {}
virtual void SetUp() OVERRIDE {
ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_));
output_path_ = temp_file_path_.value();
}
virtual void TearDown() OVERRIDE {
base::DeleteFile(temp_file_path_, false /* non recursive, just 1 file */);
}
scoped_ptr<MockMpdBuilder> StaticMpdBuilderMock() {
return make_scoped_ptr(new MockMpdBuilder(MpdBuilder::kStatic));
}
scoped_ptr<MockMpdBuilder> DynamicMpdBuilderMock() {
return make_scoped_ptr(new MockMpdBuilder(MpdBuilder::kDynamic));
}
MpdBuilder::MpdType GetMpdBuilderType(const SimpleMpdNotifier& notifier) {
return notifier.MpdBuilderForTesting()->type();
}
void SetMpdBuilder(SimpleMpdNotifier* notifier,
scoped_ptr<MpdBuilder> mpd_builder) {
notifier->SetMpdBuilderForTesting(mpd_builder.Pass());
}
// Use output_path_ for specifying the MPD output path so that
// SimpleMpdNotifier::WriteMpdToFile() doesn't crash.
std::string output_path_;
const MpdOptions empty_mpd_option_;
const std::vector<std::string> empty_base_urls_;
// Default AdaptationSet mock.
scoped_ptr<MockAdaptationSet> default_mock_adaptation_set_;
private:
base::FilePath temp_file_path_;
};
// Verify that it creates the correct MpdBuilder type using DashProfile passed
// to the constructor.
TEST_F(SimpleMpdNotifierTest, CreateCorrectMpdBuilderType) {
SimpleMpdNotifier on_demand_notifier(kOnDemandProfile, empty_mpd_option_,
empty_base_urls_, output_path_);
EXPECT_TRUE(on_demand_notifier.Init());
EXPECT_EQ(MpdBuilder::kStatic,
GetMpdBuilderType(on_demand_notifier));
SimpleMpdNotifier live_notifier(kLiveProfile, empty_mpd_option_,
empty_base_urls_, output_path_);
EXPECT_TRUE(live_notifier.Init());
EXPECT_EQ(MpdBuilder::kDynamic, GetMpdBuilderType(live_notifier));
}
// Verify NotifyNewContainer() works as expected for VOD.
TEST_P(SimpleMpdNotifierTest, NotifyNewContainer) {
SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_,
empty_base_urls_, output_path_);
const uint32_t kRepresentationId = 1u;
const MpdBuilder::MpdType mpd_type = GetParam();
scoped_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder(mpd_type));
scoped_ptr<MockRepresentation> mock_representation(
new MockRepresentation(kRepresentationId));
EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_))
.WillOnce(Return(default_mock_adaptation_set_.get()));
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
.WillOnce(Return(mock_representation.get()));
if (mpd_type == MpdBuilder::kStatic)
EXPECT_CALL(*mock_mpd_builder, ToString(_)).WillOnce(Return(true));
uint32_t unused_container_id;
SetMpdBuilder(&notifier, mock_mpd_builder.PassAs<MpdBuilder>());
EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo),
&unused_container_id));
}
// Verify NotifySampleDuration() works as expected for Live.
TEST_F(SimpleMpdNotifierTest, LiveNotifySampleDuration) {
SimpleMpdNotifier notifier(kLiveProfile, empty_mpd_option_, empty_base_urls_,
output_path_);
const uint32_t kRepresentationId = 8u;
scoped_ptr<MockMpdBuilder> mock_mpd_builder(DynamicMpdBuilderMock());
scoped_ptr<MockRepresentation> mock_representation(
new MockRepresentation(kRepresentationId));
EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_))
.WillOnce(Return(default_mock_adaptation_set_.get()));
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
.WillOnce(Return(mock_representation.get()));
uint32_t container_id;
SetMpdBuilder(&notifier, mock_mpd_builder.PassAs<MpdBuilder>());
EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo),
&container_id));
EXPECT_EQ(kRepresentationId, container_id);
const uint32_t kSampleDuration = 100;
EXPECT_CALL(*mock_representation, SetSampleDuration(kSampleDuration));
EXPECT_TRUE(
notifier.NotifySampleDuration(kRepresentationId, kSampleDuration));
}
// Verify that NotifySampleDuration works for OnDemand profile.
// TODO(rkuroiwa): SimpleMpdNotifier returns a container ID but does not
// register it to its map for VOD. Must fix and enable this test.
// This test can be also parmeterized just like NotifyNewContainer() test, once
// it is fixed.
TEST_F(SimpleMpdNotifierTest, DISABLED_OnDemandNotifySampleDuration) {
SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_,
empty_base_urls_, output_path_);
const uint32_t kRepresentationId = 14u;
scoped_ptr<MockMpdBuilder> mock_mpd_builder(StaticMpdBuilderMock());
scoped_ptr<MockRepresentation> mock_representation(
new MockRepresentation(kRepresentationId));
EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_))
.WillOnce(Return(default_mock_adaptation_set_.get()));
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
.WillOnce(Return(mock_representation.get()));
EXPECT_CALL(*mock_mpd_builder, ToString(_)).WillOnce(Return(true));
uint32_t container_id;
SetMpdBuilder(&notifier, mock_mpd_builder.PassAs<MpdBuilder>());
EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo),
&container_id));
EXPECT_EQ(kRepresentationId, container_id);
const uint32_t kSampleDuration = 100;
EXPECT_CALL(*mock_representation, SetSampleDuration(kSampleDuration));
EXPECT_TRUE(
notifier.NotifySampleDuration(kRepresentationId, kSampleDuration));
}
// Verify that NotifyNewSegment() for live works.
TEST_F(SimpleMpdNotifierTest, LiveNotifyNewSegment) {
SimpleMpdNotifier notifier(kLiveProfile, empty_mpd_option_, empty_base_urls_,
output_path_);
const uint32_t kRepresentationId = 447834u;
scoped_ptr<MockMpdBuilder> mock_mpd_builder(DynamicMpdBuilderMock());
scoped_ptr<MockRepresentation> mock_representation(
new MockRepresentation(kRepresentationId));
EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_))
.WillOnce(Return(default_mock_adaptation_set_.get()));
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
.WillOnce(Return(mock_representation.get()));
// Expect call at NotifyNewSegment(). But putting expect call here because the
// next line passes the value to the notifier.
EXPECT_CALL(*mock_mpd_builder, ToString(_)).WillOnce(Return(true));
uint32_t container_id;
SetMpdBuilder(&notifier, mock_mpd_builder.PassAs<MpdBuilder>());
EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo),
&container_id));
EXPECT_EQ(kRepresentationId, container_id);
const uint64_t kStartTime = 0u;
const uint32_t kSegmentDuration = 100u;
const uint64_t kSegmentSize = 123456u;
EXPECT_CALL(*mock_representation,
AddNewSegment(kStartTime, kSegmentDuration, kSegmentSize));
EXPECT_TRUE(notifier.NotifyNewSegment(kRepresentationId, kStartTime,
kSegmentDuration, kSegmentSize));
}
// Verify AddContentProtectionElement() works. Profile doesn't matter.
// TODO(rkuroiwa): Not implemented yet, enable once it is implemented.
TEST_F(SimpleMpdNotifierTest, DISABLED_AddContentProtectionElement) {
SimpleMpdNotifier notifier(kOnDemandProfile, empty_mpd_option_,
empty_base_urls_, output_path_);
const uint32_t kRepresentationId = 0u;
scoped_ptr<MockMpdBuilder> mock_mpd_builder(StaticMpdBuilderMock());
scoped_ptr<MockRepresentation> mock_representation(
new MockRepresentation(kRepresentationId));
EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_))
.WillOnce(Return(default_mock_adaptation_set_.get()));
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
.WillOnce(Return(mock_representation.get()));
EXPECT_CALL(*mock_mpd_builder, ToString(_)).WillOnce(Return(true));
uint32_t container_id;
SetMpdBuilder(&notifier, mock_mpd_builder.PassAs<MpdBuilder>());
EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kValidMediaInfo),
&container_id));
EXPECT_EQ(kRepresentationId, container_id);
ContentProtectionElement element;
EXPECT_CALL(*mock_representation, AddContentProtectionElement(_));
EXPECT_TRUE(notifier.AddContentProtectionElement(kRepresentationId, element));
}
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
SimpleMpdNotifierTest,
::testing::Values(MpdBuilder::kStatic,
MpdBuilder::kDynamic));
} // namespace edash_packager

View File

@ -68,7 +68,10 @@
'type': '<(gtest_target_type)',
'sources': [
'base/bandwidth_estimator_unittest.cc',
'base/mock_mpd_builder.h',
'base/mock_mpd_builder.cc',
'base/mpd_builder_unittest.cc',
'base/simple_mpd_notifier_unittest.cc',
'base/xml/xml_node_unittest.cc',
'test/mpd_builder_test_helper.cc',
'test/mpd_builder_test_helper.h',