Clean up mpd_unittest
Added gMock matchers for matching xmlNodePtr and its attributes. Clean up mpd_unittests to make it easier to separate adaptation set tests and representation tests. Change-Id: I31816b06e9c76f92d4a82656c659f3b9acae8cb5
This commit is contained in:
parent
86d960bea6
commit
327537df36
File diff suppressed because it is too large
Load Diff
|
@ -4,6 +4,7 @@
|
||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://developers.google.com/open-source/licenses/bsd
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
|
@ -32,59 +33,12 @@ void AddAttribute(const std::string& name,
|
||||||
attribute->set_value(value);
|
attribute->set_value(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetDocAsFlatString(xmlDocPtr doc) {
|
|
||||||
static const int kFlatFormat = 0;
|
|
||||||
int doc_str_size = 0;
|
|
||||||
xmlChar* doc_str = NULL;
|
|
||||||
xmlDocDumpFormatMemoryEnc(doc, &doc_str, &doc_str_size, "UTF-8", kFlatFormat);
|
|
||||||
DCHECK(doc_str);
|
|
||||||
|
|
||||||
std::string output(doc_str, doc_str + doc_str_size);
|
|
||||||
xmlFree(doc_str);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_xml_ptr<xmlDoc> MakeDoc(scoped_xml_ptr<xmlNode> node) {
|
|
||||||
xml::scoped_xml_ptr<xmlDoc> doc(xmlNewDoc(BAD_CAST ""));
|
|
||||||
xmlDocSetRootElement(doc.get(), node.release());
|
|
||||||
return doc;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class RepresentationTest : public ::testing::Test {
|
|
||||||
public:
|
|
||||||
RepresentationTest() {}
|
|
||||||
~RepresentationTest() override {}
|
|
||||||
|
|
||||||
// Ownership transfers, IOW this function will release the resource for
|
|
||||||
// |node|. Returns |node| in string format.
|
|
||||||
// You should not call this function multiple times.
|
|
||||||
std::string GetStringFormat() {
|
|
||||||
xml::scoped_xml_ptr<xmlDoc> doc(xmlNewDoc(BAD_CAST ""));
|
|
||||||
|
|
||||||
// Because you cannot easily get the string format of a xmlNodePtr, it gets
|
|
||||||
// attached to a temporary xml doc.
|
|
||||||
xmlDocSetRootElement(doc.get(), representation_.Release());
|
|
||||||
std::string doc_str = GetDocAsFlatString(doc.get());
|
|
||||||
|
|
||||||
// GetDocAsFlatString() adds
|
|
||||||
// <?xml version="" encoding="UTF-8"?>
|
|
||||||
// to the first line. So this removes the first line.
|
|
||||||
const size_t first_newline_char_pos = doc_str.find('\n');
|
|
||||||
DCHECK_NE(first_newline_char_pos, std::string::npos);
|
|
||||||
return doc_str.substr(first_newline_char_pos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
RepresentationXmlNode representation_;
|
|
||||||
std::list<SegmentInfo> segment_infos_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure XmlEqual() is functioning correctly.
|
// Make sure XmlEqual() is functioning correctly.
|
||||||
// TODO(rkuroiwa): Move this to a separate file. This requires it to be TEST_F
|
// TODO(rkuroiwa): Move this to a separate file. This requires it to be TEST
|
||||||
// due to gtest /test
|
// due to gtest /test
|
||||||
TEST_F(RepresentationTest, MetaTestXmlElementsEqual) {
|
TEST(XmlNodeTest, MetaTestXmlElementsEqual) {
|
||||||
static const char kXml1[] =
|
static const char kXml1[] =
|
||||||
"<A>\n"
|
"<A>\n"
|
||||||
" <B\n"
|
" <B\n"
|
||||||
|
@ -173,7 +127,7 @@ TEST_F(RepresentationTest, MetaTestXmlElementsEqual) {
|
||||||
// xmlNodeGetContent(<A>) (for both <A>s) will return "content1content2".
|
// xmlNodeGetContent(<A>) (for both <A>s) will return "content1content2".
|
||||||
// But if it is run on <B> for the first XML, it will return "content1", but
|
// But if it is run on <B> for the first XML, it will return "content1", but
|
||||||
// for second XML will return "c".
|
// for second XML will return "c".
|
||||||
TEST_F(RepresentationTest, MetaTestXmlEqualDifferentContent) {
|
TEST(XmlNodeTest, MetaTestXmlEqualDifferentContent) {
|
||||||
ASSERT_FALSE(XmlEqual(
|
ASSERT_FALSE(XmlEqual(
|
||||||
"<A><B>content1</B><B>content2</B></A>",
|
"<A><B>content1</B><B>content2</B></A>",
|
||||||
"<A><B>c</B><B>ontent1content2</B></A>"));
|
"<A><B>c</B><B>ontent1content2</B></A>"));
|
||||||
|
@ -184,7 +138,7 @@ TEST_F(RepresentationTest, MetaTestXmlEqualDifferentContent) {
|
||||||
// namespaces without context, e.g. <cenc:pssh> element.
|
// namespaces without context, e.g. <cenc:pssh> element.
|
||||||
// The MpdBuilderTests work because the MPD element has xmlns:cenc attribute.
|
// The MpdBuilderTests work because the MPD element has xmlns:cenc attribute.
|
||||||
// Tests that have <cenc:pssh> is in mpd_builder_unittest.
|
// Tests that have <cenc:pssh> is in mpd_builder_unittest.
|
||||||
TEST_F(RepresentationTest, AddContentProtectionElements) {
|
TEST(XmlNodeTest, AddContentProtectionElements) {
|
||||||
std::list<ContentProtectionElement> content_protections;
|
std::list<ContentProtectionElement> content_protections;
|
||||||
ContentProtectionElement content_protection_widevine;
|
ContentProtectionElement content_protection_widevine;
|
||||||
content_protection_widevine.scheme_id_uri =
|
content_protection_widevine.scheme_id_uri =
|
||||||
|
@ -201,9 +155,11 @@ TEST_F(RepresentationTest, AddContentProtectionElements) {
|
||||||
"urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b";
|
"urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b";
|
||||||
content_protections.push_back(content_protection_clearkey);
|
content_protections.push_back(content_protection_clearkey);
|
||||||
|
|
||||||
representation_.AddContentProtectionElements(content_protections);
|
RepresentationXmlNode representation;
|
||||||
scoped_xml_ptr<xmlDoc> doc(MakeDoc(representation_.PassScopedPtr()));
|
representation.AddContentProtectionElements(content_protections);
|
||||||
ASSERT_TRUE(XmlEqual(
|
EXPECT_THAT(
|
||||||
|
representation.GetRawPtr(),
|
||||||
|
XmlNodeEqual(
|
||||||
"<Representation>\n"
|
"<Representation>\n"
|
||||||
" <ContentProtection\n"
|
" <ContentProtection\n"
|
||||||
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\"\n"
|
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\"\n"
|
||||||
|
@ -213,47 +169,49 @@ TEST_F(RepresentationTest, AddContentProtectionElements) {
|
||||||
" <ContentProtection\n"
|
" <ContentProtection\n"
|
||||||
" schemeIdUri=\"urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b\">"
|
" schemeIdUri=\"urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b\">"
|
||||||
" </ContentProtection>\n"
|
" </ContentProtection>\n"
|
||||||
"</Representation>",
|
"</Representation>"));
|
||||||
doc.get()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RepresentationTest, AddEC3AudioInfo) {
|
TEST(XmlNodeTest, AddEC3AudioInfo) {
|
||||||
MediaInfo::AudioInfo audio_info;
|
MediaInfo::AudioInfo audio_info;
|
||||||
audio_info.set_codec("ec-3");
|
audio_info.set_codec("ec-3");
|
||||||
audio_info.set_sampling_frequency(44100);
|
audio_info.set_sampling_frequency(44100);
|
||||||
audio_info.mutable_codec_specific_data()->set_ec3_channel_map(0xF801);
|
audio_info.mutable_codec_specific_data()->set_ec3_channel_map(0xF801);
|
||||||
representation_.AddAudioInfo(audio_info);
|
|
||||||
scoped_xml_ptr<xmlDoc> doc(MakeDoc(representation_.PassScopedPtr()));
|
|
||||||
|
|
||||||
ASSERT_TRUE(XmlEqual(
|
RepresentationXmlNode representation;
|
||||||
|
representation.AddAudioInfo(audio_info);
|
||||||
|
EXPECT_THAT(
|
||||||
|
representation.GetRawPtr(),
|
||||||
|
XmlNodeEqual(
|
||||||
"<Representation audioSamplingRate=\"44100\">\n"
|
"<Representation audioSamplingRate=\"44100\">\n"
|
||||||
" <AudioChannelConfiguration\n"
|
" <AudioChannelConfiguration\n"
|
||||||
" schemeIdUri=\n"
|
" schemeIdUri=\n"
|
||||||
" \"tag:dolby.com,2014:dash:audio_channel_configuration:2011\"\n"
|
" \"tag:dolby.com,2014:dash:audio_channel_configuration:2011\"\n"
|
||||||
" value=\"F801\"/>\n"
|
" value=\"F801\"/>\n"
|
||||||
"</Representation>\n",
|
"</Representation>\n"));
|
||||||
doc.get()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some template names cannot be used for init segment name.
|
// Some template names cannot be used for init segment name.
|
||||||
TEST_F(RepresentationTest, InvalidLiveInitSegmentName) {
|
TEST(XmlNodeTest, InvalidLiveInitSegmentName) {
|
||||||
MediaInfo media_info;
|
MediaInfo media_info;
|
||||||
const uint32_t kDefaultStartNumber = 1;
|
const uint32_t kDefaultStartNumber = 1;
|
||||||
|
std::list<SegmentInfo> segment_infos;
|
||||||
|
RepresentationXmlNode representation;
|
||||||
|
|
||||||
// $Number$ cannot be used for segment name.
|
// $Number$ cannot be used for segment name.
|
||||||
media_info.set_init_segment_name("$Number$.mp4");
|
media_info.set_init_segment_name("$Number$.mp4");
|
||||||
ASSERT_FALSE(representation_.AddLiveOnlyInfo(
|
ASSERT_FALSE(representation.AddLiveOnlyInfo(media_info, segment_infos,
|
||||||
media_info, segment_infos_, kDefaultStartNumber));
|
kDefaultStartNumber));
|
||||||
|
|
||||||
// $Time$ as well.
|
// $Time$ as well.
|
||||||
media_info.set_init_segment_name("$Time$.mp4");
|
media_info.set_init_segment_name("$Time$.mp4");
|
||||||
ASSERT_FALSE(representation_.AddLiveOnlyInfo(
|
ASSERT_FALSE(representation.AddLiveOnlyInfo(media_info, segment_infos,
|
||||||
media_info, segment_infos_, kDefaultStartNumber));
|
kDefaultStartNumber));
|
||||||
|
|
||||||
// This should be valid.
|
// This should be valid.
|
||||||
media_info.set_init_segment_name("some_non_template_name.mp4");
|
media_info.set_init_segment_name("some_non_template_name.mp4");
|
||||||
ASSERT_TRUE(representation_.AddLiveOnlyInfo(
|
ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info, segment_infos,
|
||||||
media_info, segment_infos_, kDefaultStartNumber));
|
kDefaultStartNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xml
|
} // namespace xml
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "packager/base/files/file_path.h"
|
#include "packager/base/files/file_path.h"
|
||||||
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/strings/string_util.h"
|
#include "packager/base/strings/string_util.h"
|
||||||
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
|
@ -167,4 +166,38 @@ bool XmlEqual(xmlDocPtr xml1, xmlDocPtr xml2) {
|
||||||
return CompareNodes(xml1_root_element, xml2_root_element);
|
return CompareNodes(xml1_root_element, xml2_root_element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool XmlEqual(const std::string& xml1, xmlNodePtr xml2) {
|
||||||
|
xml::scoped_xml_ptr<xmlDoc> xml1_doc(GetDocFromString(xml1));
|
||||||
|
if (!xml1_doc) {
|
||||||
|
LOG(ERROR) << "xml1 are not valid XML.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
xmlNodePtr xml1_root_element = xmlDocGetRootElement(xml1_doc.get());
|
||||||
|
if (!xml1_root_element)
|
||||||
|
return false;
|
||||||
|
return CompareNodes(xml1_root_element, xml2);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string XmlNodeToString(xmlNodePtr xml_node) {
|
||||||
|
// Create an xmlDoc from xmlNodePtr. The node is copied so ownership does not
|
||||||
|
// transfer.
|
||||||
|
xml::scoped_xml_ptr<xmlDoc> doc(xmlNewDoc(BAD_CAST ""));
|
||||||
|
xmlDocSetRootElement(doc.get(), xmlCopyNode(xml_node, true));
|
||||||
|
|
||||||
|
// Format the xmlDoc to string.
|
||||||
|
static const int kNiceFormat = 1;
|
||||||
|
int doc_str_size = 0;
|
||||||
|
xmlChar* doc_str = nullptr;
|
||||||
|
xmlDocDumpFormatMemoryEnc(doc.get(), &doc_str, &doc_str_size, "UTF-8",
|
||||||
|
kNiceFormat);
|
||||||
|
std::string output(doc_str, doc_str + doc_str_size);
|
||||||
|
xmlFree(doc_str);
|
||||||
|
|
||||||
|
// Remove the first line from the formatted string:
|
||||||
|
// <?xml version="" encoding="UTF-8"?>
|
||||||
|
const size_t first_newline_char_pos = output.find('\n');
|
||||||
|
DCHECK_NE(first_newline_char_pos, std::string::npos);
|
||||||
|
return output.substr(first_newline_char_pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
#ifndef MPD_TEST_XML_COMPARE_H_
|
#ifndef MPD_TEST_XML_COMPARE_H_
|
||||||
#define MPD_TEST_XML_COMPARE_H_
|
#define MPD_TEST_XML_COMPARE_H_
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
/// Checks whether the XMLs are equivalent. An XML schema can specify strict
|
/// Checks whether the XMLs are equivalent. An XML schema can specify strict
|
||||||
|
@ -18,7 +21,40 @@ namespace shaka {
|
||||||
bool XmlEqual(const std::string& xml1, const std::string& xml2);
|
bool XmlEqual(const std::string& xml1, const std::string& xml2);
|
||||||
bool XmlEqual(const std::string& xml1, xmlDocPtr xml2);
|
bool XmlEqual(const std::string& xml1, xmlDocPtr xml2);
|
||||||
bool XmlEqual(xmlDocPtr xml1, xmlDocPtr xml2);
|
bool XmlEqual(xmlDocPtr xml1, xmlDocPtr xml2);
|
||||||
|
bool XmlEqual(const std::string& xml1, xmlNodePtr xml2);
|
||||||
|
|
||||||
|
/// Get string representation of the xml node.
|
||||||
|
/// Note that the ownership is not transferred.
|
||||||
|
std::string XmlNodeToString(xmlNodePtr xml_node);
|
||||||
|
|
||||||
|
/// Match an xmlNodePtr with an xml in string representation.
|
||||||
|
MATCHER_P(XmlNodeEqual, xml, "") {
|
||||||
|
*result_listener << "\n" << XmlNodeToString(arg);
|
||||||
|
return XmlEqual(xml, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Match the attribute of an xmlNodePtr with expected value.
|
||||||
|
/// Note that the ownership is not transferred.
|
||||||
|
MATCHER_P2(AttributeEqual, attribute, expected_value, "") {
|
||||||
|
xml::scoped_xml_ptr<xmlChar> attribute_xml_str(
|
||||||
|
xmlGetProp(arg, BAD_CAST attribute));
|
||||||
|
if (!attribute_xml_str) {
|
||||||
|
*result_listener << "no attribute '" << attribute << "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string actual_value =
|
||||||
|
reinterpret_cast<const char*>(attribute_xml_str.get());
|
||||||
|
*result_listener << actual_value;
|
||||||
|
return expected_value == actual_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the attribute is set in an xmlNodePtr.
|
||||||
|
/// Note that the ownership is not transferred.
|
||||||
|
MATCHER_P(AttributeSet, attribute, "") {
|
||||||
|
xml::scoped_xml_ptr<xmlChar> attribute_xml_str(
|
||||||
|
xmlGetProp(arg, BAD_CAST attribute));
|
||||||
|
return attribute_xml_str != nullptr;
|
||||||
|
}
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
||||||
#endif // MPD_TEST_XML_COMPARE_H_
|
#endif // MPD_TEST_XML_COMPARE_H_
|
||||||
|
|
Loading…
Reference in New Issue