1 /* 2 * libjingle 3 * Copyright 2004, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <iostream> 29 #include <sstream> 30 #include <string> 31 #include "talk/xmllite/xmlelement.h" 32 #include "webrtc/base/common.h" 33 #include "webrtc/base/gunit.h" 34 #include "webrtc/base/thread.h" 35 36 using buzz::QName; 37 using buzz::XmlAttr; 38 using buzz::XmlChild; 39 using buzz::XmlElement; 40 41 std::ostream& operator<<(std::ostream& os, const QName& name) { 42 os << name.Namespace() << ":" << name.LocalPart(); 43 return os; 44 } 45 46 TEST(XmlElementTest, TestConstructors) { 47 XmlElement elt(QName("google:test", "first")); 48 EXPECT_EQ("<test:first xmlns:test=\"google:test\"/>", elt.Str()); 49 50 XmlElement elt2(QName("google:test", "first"), true); 51 EXPECT_EQ("<first xmlns=\"google:test\"/>", elt2.Str()); 52 } 53 54 TEST(XmlElementTest, TestAdd) { 55 XmlElement elt(QName("google:test", "root"), true); 56 elt.AddElement(new XmlElement(QName("google:test", "first"))); 57 elt.AddElement(new XmlElement(QName("google:test", "nested")), 1); 58 elt.AddText("nested-value", 2); 59 elt.AddText("between-", 1); 60 elt.AddText("value", 1); 61 elt.AddElement(new XmlElement(QName("google:test", "nested2")), 1); 62 elt.AddElement(new XmlElement(QName("google:test", "second"))); 63 elt.AddText("init-value", 1); 64 elt.AddElement(new XmlElement(QName("google:test", "nested3")), 1); 65 elt.AddText("trailing-value", 1); 66 67 // make sure it looks ok overall 68 EXPECT_EQ("<root xmlns=\"google:test\">" 69 "<first><nested>nested-value</nested>between-value<nested2/></first>" 70 "<second>init-value<nested3/>trailing-value</second></root>", 71 elt.Str()); 72 73 // make sure text was concatenated 74 XmlChild * pchild = 75 elt.FirstChild()->AsElement()->FirstChild()->NextChild(); 76 EXPECT_TRUE(pchild->IsText()); 77 EXPECT_EQ("between-value", pchild->AsText()->Text()); 78 } 79 80 TEST(XmlElementTest, TestAttrs) { 81 XmlElement elt(QName("", "root")); 82 elt.SetAttr(QName("", "a"), "avalue"); 83 EXPECT_EQ("<root a=\"avalue\"/>", elt.Str()); 84 85 elt.SetAttr(QName("", "b"), "bvalue"); 86 EXPECT_EQ("<root a=\"avalue\" b=\"bvalue\"/>", elt.Str()); 87 88 elt.SetAttr(QName("", "a"), "avalue2"); 89 EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue\"/>", elt.Str()); 90 91 elt.SetAttr(QName("", "b"), "bvalue2"); 92 EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\"/>", elt.Str()); 93 94 elt.SetAttr(QName("", "c"), "cvalue"); 95 EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\" c=\"cvalue\"/>", elt.Str()); 96 97 XmlAttr * patt = elt.FirstAttr(); 98 EXPECT_EQ(QName("", "a"), patt->Name()); 99 EXPECT_EQ("avalue2", patt->Value()); 100 101 patt = patt->NextAttr(); 102 EXPECT_EQ(QName("", "b"), patt->Name()); 103 EXPECT_EQ("bvalue2", patt->Value()); 104 105 patt = patt->NextAttr(); 106 EXPECT_EQ(QName("", "c"), patt->Name()); 107 EXPECT_EQ("cvalue", patt->Value()); 108 109 patt = patt->NextAttr(); 110 EXPECT_TRUE(NULL == patt); 111 112 EXPECT_TRUE(elt.HasAttr(QName("", "a"))); 113 EXPECT_TRUE(elt.HasAttr(QName("", "b"))); 114 EXPECT_TRUE(elt.HasAttr(QName("", "c"))); 115 EXPECT_FALSE(elt.HasAttr(QName("", "d"))); 116 117 elt.SetAttr(QName("", "d"), "dvalue"); 118 EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\" c=\"cvalue\" d=\"dvalue\"/>", 119 elt.Str()); 120 EXPECT_TRUE(elt.HasAttr(QName("", "d"))); 121 122 elt.ClearAttr(QName("", "z")); // not found, no effect 123 EXPECT_EQ("<root a=\"avalue2\" b=\"bvalue2\" c=\"cvalue\" d=\"dvalue\"/>", 124 elt.Str()); 125 126 elt.ClearAttr(QName("", "b")); 127 EXPECT_EQ("<root a=\"avalue2\" c=\"cvalue\" d=\"dvalue\"/>", elt.Str()); 128 129 elt.ClearAttr(QName("", "a")); 130 EXPECT_EQ("<root c=\"cvalue\" d=\"dvalue\"/>", elt.Str()); 131 132 elt.ClearAttr(QName("", "d")); 133 EXPECT_EQ("<root c=\"cvalue\"/>", elt.Str()); 134 135 elt.ClearAttr(QName("", "c")); 136 EXPECT_EQ("<root/>", elt.Str()); 137 } 138 139 TEST(XmlElementTest, TestBodyText) { 140 XmlElement elt(QName("", "root")); 141 EXPECT_EQ("", elt.BodyText()); 142 143 elt.AddText("body value text"); 144 145 EXPECT_EQ("body value text", elt.BodyText()); 146 147 elt.ClearChildren(); 148 elt.AddText("more value "); 149 elt.AddText("text"); 150 151 EXPECT_EQ("more value text", elt.BodyText()); 152 153 elt.ClearChildren(); 154 elt.AddText("decoy"); 155 elt.AddElement(new XmlElement(QName("", "dummy"))); 156 EXPECT_EQ("", elt.BodyText()); 157 158 elt.SetBodyText("replacement"); 159 EXPECT_EQ("replacement", elt.BodyText()); 160 161 elt.SetBodyText(""); 162 EXPECT_TRUE(NULL == elt.FirstChild()); 163 164 elt.SetBodyText("goodbye"); 165 EXPECT_EQ("goodbye", elt.FirstChild()->AsText()->Text()); 166 EXPECT_EQ("goodbye", elt.BodyText()); 167 } 168 169 TEST(XmlElementTest, TestCopyConstructor) { 170 XmlElement * element = XmlElement::ForStr( 171 "<root xmlns='test-foo'>This is a <em a='avalue' b='bvalue'>" 172 "little <b>little</b></em> test</root>"); 173 174 XmlElement * pelCopy = new XmlElement(*element); 175 EXPECT_EQ("<root xmlns=\"test-foo\">This is a <em a=\"avalue\" b=\"bvalue\">" 176 "little <b>little</b></em> test</root>", pelCopy->Str()); 177 delete pelCopy; 178 179 pelCopy = new XmlElement(*(element->FirstChild()->NextChild()->AsElement())); 180 EXPECT_EQ("<foo:em a=\"avalue\" b=\"bvalue\" xmlns:foo=\"test-foo\">" 181 "little <foo:b>little</foo:b></foo:em>", pelCopy->Str()); 182 183 XmlAttr * patt = pelCopy->FirstAttr(); 184 EXPECT_EQ(QName("", "a"), patt->Name()); 185 EXPECT_EQ("avalue", patt->Value()); 186 187 patt = patt->NextAttr(); 188 EXPECT_EQ(QName("", "b"), patt->Name()); 189 EXPECT_EQ("bvalue", patt->Value()); 190 191 patt = patt->NextAttr(); 192 EXPECT_TRUE(NULL == patt); 193 delete pelCopy; 194 delete element; 195 } 196 197 TEST(XmlElementTest, TestNameSearch) { 198 XmlElement * element = XmlElement::ForStr( 199 "<root xmlns='test-foo'>" 200 "<firstname>George</firstname>" 201 "<middlename>X.</middlename>" 202 "some text" 203 "<lastname>Harrison</lastname>" 204 "<firstname>John</firstname>" 205 "<middlename>Y.</middlename>" 206 "<lastname>Lennon</lastname>" 207 "</root>"); 208 EXPECT_TRUE(NULL == 209 element->FirstNamed(QName("", "firstname"))); 210 EXPECT_EQ(element->FirstChild(), 211 element->FirstNamed(QName("test-foo", "firstname"))); 212 EXPECT_EQ(element->FirstChild()->NextChild(), 213 element->FirstNamed(QName("test-foo", "middlename"))); 214 EXPECT_EQ(element->FirstElement()->NextElement(), 215 element->FirstNamed(QName("test-foo", "middlename"))); 216 EXPECT_EQ("Harrison", 217 element->TextNamed(QName("test-foo", "lastname"))); 218 EXPECT_EQ(element->FirstElement()->NextElement()->NextElement(), 219 element->FirstNamed(QName("test-foo", "lastname"))); 220 EXPECT_EQ("John", element->FirstNamed(QName("test-foo", "firstname"))-> 221 NextNamed(QName("test-foo", "firstname"))->BodyText()); 222 EXPECT_EQ("Y.", element->FirstNamed(QName("test-foo", "middlename"))-> 223 NextNamed(QName("test-foo", "middlename"))->BodyText()); 224 EXPECT_EQ("Lennon", element->FirstNamed(QName("test-foo", "lastname"))-> 225 NextNamed(QName("test-foo", "lastname"))->BodyText()); 226 EXPECT_TRUE(NULL == element->FirstNamed(QName("test-foo", "firstname"))-> 227 NextNamed(QName("test-foo", "firstname"))-> 228 NextNamed(QName("test-foo", "firstname"))); 229 230 delete element; 231 } 232 233 class XmlElementCreatorThread : public rtc::Thread { 234 public: 235 XmlElementCreatorThread(int count, buzz::QName qname) : 236 count_(count), qname_(qname) {} 237 238 virtual ~XmlElementCreatorThread() { 239 Stop(); 240 } 241 242 virtual void Run() { 243 std::vector<buzz::XmlElement*> elems; 244 for (int i = 0; i < count_; i++) { 245 elems.push_back(new XmlElement(qname_)); 246 } 247 for (int i = 0; i < count_; i++) { 248 delete elems[i]; 249 } 250 } 251 252 private: 253 int count_; 254 buzz::QName qname_; 255 }; 256 257 // If XmlElement creation and destruction isn't thread safe, 258 // this test should crash. 259 TEST(XmlElementTest, TestMultithread) { 260 int thread_count = 2; // Was 100, but that's too slow. 261 int elem_count = 100; // Was 100000, but that's too slow. 262 buzz::QName qname("foo", "bar"); 263 264 std::vector<rtc::Thread*> threads; 265 for (int i = 0; i < thread_count; i++) { 266 threads.push_back( 267 new XmlElementCreatorThread(elem_count, qname)); 268 threads[i]->Start(); 269 } 270 271 for (int i = 0; i < thread_count; i++) { 272 threads[i]->Stop(); 273 delete threads[i]; 274 } 275 } 276