| /* |
| * Copyright 2004 The WebRTC 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 <iostream> |
| #include <sstream> |
| #include <string> |
| #include "webrtc/libjingle/xmllite/xmlelement.h" |
| #include "webrtc/base/common.h" |
| #include "webrtc/base/gunit.h" |
| #include "webrtc/base/thread.h" |
| |
| using buzz::QName; |
| using buzz::XmlAttr; |
| using buzz::XmlChild; |
| using buzz::XmlElement; |
| |
| std::ostream& operator<<(std::ostream& os, const QName& name) { |
| os << name.Namespace() << ":" << name.LocalPart(); |
| return os; |
| } |
| |
| TEST(XmlElementTest, TestConstructors) { |
| XmlElement elt(QName("google:test", "first")); |
| EXPECT_EQ("<test:first xmlns:test=\"google:test\"/>", elt.Str()); |
| |
| XmlElement elt2(QName("google:test", "first"), true); |
| EXPECT_EQ("<first xmlns=\"google:test\"/>", elt2.Str()); |
| } |
| |
| TEST(XmlElementTest, TestAdd) { |
| XmlElement elt(QName("google:test", "root"), true); |
| elt.AddElement(new XmlElement(QName("google:test", "first"))); |
| elt.AddElement(new XmlElement(QName("google:test", "nested")), 1); |
| elt.AddText("nested-value", 2); |
| elt.AddText("between-", 1); |
| elt.AddText("value", 1); |
| elt.AddElement(new XmlElement(QName("google:test", "nested2")), 1); |
| elt.AddElement(new XmlElement(QName("google:test", "second"))); |
| elt.AddText("init-value", 1); |
| elt.AddElement(new XmlElement(QName("google:test", "nested3")), 1); |
| elt.AddText("trailing-value", 1); |
| |
| // make sure it looks ok overall |
| EXPECT_EQ("<root xmlns=\"google:test\">" |
| "<first><nested>nested-value</nested>between-value<nested2/></first>" |
| "<second>init-value<nested3/>trailing-value</second></root>", |
| elt.Str()); |
| |
| // make sure text was concatenated |
| XmlChild * pchild = |
| elt.FirstChild()->AsElement()->FirstChild()->NextChild(); |
| EXPECT_TRUE(pchild->IsText()); |
| EXPECT_EQ("between-value", pchild->AsText()->Text()); |
| } |
| |
| TEST(XmlElementTest, TestAttrs) { |
| XmlElement elt(QName("", "root")); |
| elt.SetAttr(QName("", "a"), "avalue"); |
| EXPECT_EQ("<root a=\"avalue\"/>", elt.Str()); |
| |
| elt.SetAttr(QName("", "b"), "bvalue"); |
| EXPECT_EQ("<root a=\"avalue\" b=\"bvalue\"/>", elt.Str()); |
| |
| elt.SetAttr(QName("", "a"), "avalue2"); |
| EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue\"/>", elt.Str()); |
| |
| elt.SetAttr(QName("", "b"), "bvalue2"); |
| EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\"/>", elt.Str()); |
| |
| elt.SetAttr(QName("", "c"), "cvalue"); |
| EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\" c=\"cvalue\"/>", elt.Str()); |
| |
| XmlAttr * patt = elt.FirstAttr(); |
| EXPECT_EQ(QName("", "a"), patt->Name()); |
| EXPECT_EQ("avalue2", patt->Value()); |
| |
| patt = patt->NextAttr(); |
| EXPECT_EQ(QName("", "b"), patt->Name()); |
| EXPECT_EQ("bvalue2", patt->Value()); |
| |
| patt = patt->NextAttr(); |
| EXPECT_EQ(QName("", "c"), patt->Name()); |
| EXPECT_EQ("cvalue", patt->Value()); |
| |
| patt = patt->NextAttr(); |
| EXPECT_TRUE(NULL == patt); |
| |
| EXPECT_TRUE(elt.HasAttr(QName("", "a"))); |
| EXPECT_TRUE(elt.HasAttr(QName("", "b"))); |
| EXPECT_TRUE(elt.HasAttr(QName("", "c"))); |
| EXPECT_FALSE(elt.HasAttr(QName("", "d"))); |
| |
| elt.SetAttr(QName("", "d"), "dvalue"); |
| EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\" c=\"cvalue\" d=\"dvalue\"/>", |
| elt.Str()); |
| EXPECT_TRUE(elt.HasAttr(QName("", "d"))); |
| |
| elt.ClearAttr(QName("", "z")); // not found, no effect |
| EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\" c=\"cvalue\" d=\"dvalue\"/>", |
| elt.Str()); |
| |
| elt.ClearAttr(QName("", "b")); |
| EXPECT_EQ("<root a=\"avalue2\" c=\"cvalue\" d=\"dvalue\"/>", elt.Str()); |
| |
| elt.ClearAttr(QName("", "a")); |
| EXPECT_EQ("<root c=\"cvalue\" d=\"dvalue\"/>", elt.Str()); |
| |
| elt.ClearAttr(QName("", "d")); |
| EXPECT_EQ("<root c=\"cvalue\"/>", elt.Str()); |
| |
| elt.ClearAttr(QName("", "c")); |
| EXPECT_EQ("<root/>", elt.Str()); |
| } |
| |
| TEST(XmlElementTest, TestBodyText) { |
| XmlElement elt(QName("", "root")); |
| EXPECT_EQ("", elt.BodyText()); |
| |
| elt.AddText("body value text"); |
| |
| EXPECT_EQ("body value text", elt.BodyText()); |
| |
| elt.ClearChildren(); |
| elt.AddText("more value "); |
| elt.AddText("text"); |
| |
| EXPECT_EQ("more value text", elt.BodyText()); |
| |
| elt.ClearChildren(); |
| elt.AddText("decoy"); |
| elt.AddElement(new XmlElement(QName("", "dummy"))); |
| EXPECT_EQ("", elt.BodyText()); |
| |
| elt.SetBodyText("replacement"); |
| EXPECT_EQ("replacement", elt.BodyText()); |
| |
| elt.SetBodyText(""); |
| EXPECT_TRUE(NULL == elt.FirstChild()); |
| |
| elt.SetBodyText("goodbye"); |
| EXPECT_EQ("goodbye", elt.FirstChild()->AsText()->Text()); |
| EXPECT_EQ("goodbye", elt.BodyText()); |
| } |
| |
| TEST(XmlElementTest, TestCopyConstructor) { |
| XmlElement * element = XmlElement::ForStr( |
| "<root xmlns='test-foo'>This is a <em a='avalue' b='bvalue'>" |
| "little <b>little</b></em> test</root>"); |
| |
| XmlElement * pelCopy = new XmlElement(*element); |
| EXPECT_EQ("<root xmlns=\"test-foo\">This is a <em a=\"avalue\" b=\"bvalue\">" |
| "little <b>little</b></em> test</root>", pelCopy->Str()); |
| delete pelCopy; |
| |
| pelCopy = new XmlElement(*(element->FirstChild()->NextChild()->AsElement())); |
| EXPECT_EQ("<foo:em a=\"avalue\" b=\"bvalue\" xmlns:foo=\"test-foo\">" |
| "little <foo:b>little</foo:b></foo:em>", pelCopy->Str()); |
| |
| XmlAttr * patt = pelCopy->FirstAttr(); |
| EXPECT_EQ(QName("", "a"), patt->Name()); |
| EXPECT_EQ("avalue", patt->Value()); |
| |
| patt = patt->NextAttr(); |
| EXPECT_EQ(QName("", "b"), patt->Name()); |
| EXPECT_EQ("bvalue", patt->Value()); |
| |
| patt = patt->NextAttr(); |
| EXPECT_TRUE(NULL == patt); |
| delete pelCopy; |
| delete element; |
| } |
| |
| TEST(XmlElementTest, TestNameSearch) { |
| XmlElement * element = XmlElement::ForStr( |
| "<root xmlns='test-foo'>" |
| "<firstname>George</firstname>" |
| "<middlename>X.</middlename>" |
| "some text" |
| "<lastname>Harrison</lastname>" |
| "<firstname>John</firstname>" |
| "<middlename>Y.</middlename>" |
| "<lastname>Lennon</lastname>" |
| "</root>"); |
| EXPECT_TRUE(NULL == |
| element->FirstNamed(QName("", "firstname"))); |
| EXPECT_EQ(element->FirstChild(), |
| element->FirstNamed(QName("test-foo", "firstname"))); |
| EXPECT_EQ(element->FirstChild()->NextChild(), |
| element->FirstNamed(QName("test-foo", "middlename"))); |
| EXPECT_EQ(element->FirstElement()->NextElement(), |
| element->FirstNamed(QName("test-foo", "middlename"))); |
| EXPECT_EQ("Harrison", |
| element->TextNamed(QName("test-foo", "lastname"))); |
| EXPECT_EQ(element->FirstElement()->NextElement()->NextElement(), |
| element->FirstNamed(QName("test-foo", "lastname"))); |
| EXPECT_EQ("John", element->FirstNamed(QName("test-foo", "firstname"))-> |
| NextNamed(QName("test-foo", "firstname"))->BodyText()); |
| EXPECT_EQ("Y.", element->FirstNamed(QName("test-foo", "middlename"))-> |
| NextNamed(QName("test-foo", "middlename"))->BodyText()); |
| EXPECT_EQ("Lennon", element->FirstNamed(QName("test-foo", "lastname"))-> |
| NextNamed(QName("test-foo", "lastname"))->BodyText()); |
| EXPECT_TRUE(NULL == element->FirstNamed(QName("test-foo", "firstname"))-> |
| NextNamed(QName("test-foo", "firstname"))-> |
| NextNamed(QName("test-foo", "firstname"))); |
| |
| delete element; |
| } |
| |
| class XmlElementCreatorThread : public rtc::Thread { |
| public: |
| XmlElementCreatorThread(int count, buzz::QName qname) : |
| count_(count), qname_(qname) {} |
| |
| virtual ~XmlElementCreatorThread() { |
| Stop(); |
| } |
| |
| virtual void Run() { |
| std::vector<buzz::XmlElement*> elems; |
| for (int i = 0; i < count_; i++) { |
| elems.push_back(new XmlElement(qname_)); |
| } |
| for (int i = 0; i < count_; i++) { |
| delete elems[i]; |
| } |
| } |
| |
| private: |
| int count_; |
| buzz::QName qname_; |
| }; |
| |
| // If XmlElement creation and destruction isn't thread safe, |
| // this test should crash. |
| TEST(XmlElementTest, TestMultithread) { |
| int thread_count = 2; // Was 100, but that's too slow. |
| int elem_count = 100; // Was 100000, but that's too slow. |
| buzz::QName qname("foo", "bar"); |
| |
| std::vector<rtc::Thread*> threads; |
| for (int i = 0; i < thread_count; i++) { |
| threads.push_back( |
| new XmlElementCreatorThread(elem_count, qname)); |
| threads[i]->Start(); |
| } |
| |
| for (int i = 0; i < thread_count; i++) { |
| threads[i]->Stop(); |
| delete threads[i]; |
| } |
| } |