1 // Copyright 2010, Google Inc. All rights reserved.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
6 // 1. Redistributions of source code must retain the above copyright notice,
7 // this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright notice,
9 // this list of conditions and the following disclaimer in the documentation
10 // and/or other materials provided with the distribution.
11 // 3. Neither the name of Google Inc. nor the names of its contributors may be
12 // used to endorse or promote products derived from this software without
13 // specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26 // This file contains the unit tests for the XmlSerializer class and
27 // the SerializePretty and SerializeRaw public API functions.
28
29 #include "kml/dom/xml_serializer.h"
30 #include <sstream>
31 #include "boost/scoped_ptr.hpp"
32 #include "kml/dom/kml22.h"
33 #include "kml/dom/kml_factory.h"
34 #include "kml/dom/kml_funcs.h"
35 #include "kml/dom/kmldom.h"
36 #include "gtest/gtest.h"
37
38 using kmlbase::ToString;
39
40 namespace kmldom {
41
42 class XmlSerializerTest : public testing::Test {
43 protected:
SetUp()44 virtual void SetUp() {
45 string_adapter_.reset(new StringAdapter(&output_));
46 xml_serializer_.reset(
47 new XmlSerializer<StringAdapter>("", "",
48 string_adapter_.get()));
49 placemark_ = KmlFactory::GetFactory()->CreatePlacemark();
50 }
51
52 boost::scoped_ptr<XmlSerializer<StringAdapter> > xml_serializer_;
53 // If string were strictly std::string we could instead use
54 // std::ostringstream.
55 string output_;
56 boost::scoped_ptr<StringAdapter> string_adapter_;
57 PlacemarkPtr placemark_;
58 };
59
TEST_F(XmlSerializerTest,TestToString)60 TEST_F(XmlSerializerTest, TestToString) {
61 double pi = 3.14159;
62 unsigned int dna = 42;
63 ASSERT_EQ(string("3.14159"), ToString(pi));
64 ASSERT_EQ("42", ToString(dna));
65 }
66
TEST_F(XmlSerializerTest,TestSaveEmptyStringFieldById)67 TEST_F(XmlSerializerTest, TestSaveEmptyStringFieldById) {
68 // Assert that the <name/> field serializes as expected.
69 const int type_id = Type_name;
70 const string expected_result("<name/>");
71 const string empty;
72 xml_serializer_->SaveFieldById(type_id, empty);
73 ASSERT_EQ(expected_result, output_);
74 }
75
TEST_F(XmlSerializerTest,TestSaveStringFieldById)76 TEST_F(XmlSerializerTest, TestSaveStringFieldById) {
77 // Assert that the <name> field serializes as expected.
78 const int type_id = Type_name;
79 const string txt("some feature name");
80 const string expected_result("<name>some feature name</name>");
81 xml_serializer_->SaveFieldById(type_id, txt);
82 ASSERT_EQ(expected_result, output_);
83 }
84
TEST_F(XmlSerializerTest,TestCdataHandling)85 TEST_F(XmlSerializerTest, TestCdataHandling) {
86 // The underlying parser itself won't pass CDATA sections through to
87 // element char data, but it is possible that raw CDATA will be passed
88 // through directly by feature->set_name() or similar. If the serializer
89 // sees this, we entity-escape the entire string to prevent the output
90 // of invalid XML. Otherwise, if we see any invalid characters in the string,
91 // we wrap them with CDATA.
92 struct TestStruct {
93 const string chardata;
94 const string expected;
95 } testdata[] = {
96 {"simple text", "<name>simple text</name>\n"},
97 {"<![CDATA[...]]>", "<name><![CDATA[...]]></name>\n"},
98 {"invalid & char", "<name><![CDATA[invalid & char]]></name>\n"},
99 {"invalid ' char", "<name><![CDATA[invalid ' char]]></name>\n"},
100 {"invalid < char", "<name><![CDATA[invalid < char]]></name>\n"},
101 {"invalid > char", "<name><![CDATA[invalid > char]]></name>\n"},
102 {"invalid \" char", "<name><![CDATA[invalid \" char]]></name>\n"},
103 {"goo <![CDATA[goo]]> goo", "<name>goo <![CDATA[goo]]> goo</name>\n"},
104 {"<x><![CDATA[goo]]></x>",
105 "<name><x><![CDATA[goo]]></x></name>\n"}
106 };
107
108 const size_t size = sizeof(testdata) / sizeof(testdata[0]);
109
110 for (size_t i = 0; i < size; ++i) {
111 output_.clear();
112 XmlSerializer<StringAdapter> s_("\n","", string_adapter_.get());
113 s_.SaveFieldById(Type_name, testdata[i].chardata);
114 ASSERT_EQ(testdata[i].expected, output_);
115 }
116 }
117
TEST_F(XmlSerializerTest,TestCdataEscaping)118 TEST_F(XmlSerializerTest, TestCdataEscaping) {
119 // Assert that data that should be escaped in a CDATA is so quoted.
120 placemark_->set_name("<i>One</i> two");
121 string xml = SerializePretty(placemark_);
122 string expected("<Placemark>\n "
123 "<name><![CDATA[<i>One</i> two]]></name>\n"
124 "</Placemark>\n");
125 ASSERT_EQ(expected, xml);
126 }
127
TEST_F(XmlSerializerTest,TestCdataPassedBySetter)128 TEST_F(XmlSerializerTest, TestCdataPassedBySetter) {
129 string crazy_name("foo <b> goo <![CDATA[xxx<i>yyy</i>xxx]]> </b> goo");
130 placemark_->set_name(crazy_name);
131 string xml = SerializePretty(placemark_);
132 string expected("<Placemark>\n"
133 " <name>foo <b> goo <![CDATA["
134 "xxx<i>yyy</i>xxx]]> </b> goo</name>\n"
135 "</Placemark>\n");
136 ASSERT_EQ(expected, xml);
137 }
138
139
TEST_F(XmlSerializerTest,TestSaveBoolFieldByIdAsBool)140 TEST_F(XmlSerializerTest, TestSaveBoolFieldByIdAsBool) {
141 // Assert that <open> is serialized correctly.
142 const bool bool_state = true;
143 string expected_result("<open>1</open>");
144 // A parsed bool is serialized as an int:
145 xml_serializer_->SaveFieldById(Type_open, bool_state);
146 ASSERT_EQ(expected_result, output_);
147 }
148
TEST_F(XmlSerializerTest,TestSaveBoolFieldByIdAsInt)149 TEST_F(XmlSerializerTest, TestSaveBoolFieldByIdAsInt) {
150 // Assert that <open> is serialized correctly.
151 const unsigned int int_state = 1;
152 string expected_result("<open>1</open>");
153 // A parsed int is serialized as an int:
154 xml_serializer_->SaveFieldById(Type_open, int_state);
155 ASSERT_EQ(expected_result, output_);
156 }
157
TEST_F(XmlSerializerTest,TestSaveContent)158 TEST_F(XmlSerializerTest, TestSaveContent) {
159 // Ensure a simple string is serialized exactly.
160 const string s("tom, dick");
161 xml_serializer_->SaveContent(s, false);
162 ASSERT_EQ(s, output_);
163 // SaveContent will append continued calls.
164 string t(" and harry");
165 xml_serializer_->SaveContent(t, false);
166 string expected_result(s + t);
167 ASSERT_EQ(expected_result, output_);
168 }
169
TEST_F(XmlSerializerTest,TestSaveColor)170 TEST_F(XmlSerializerTest, TestSaveColor) {
171 const kmlbase::Color32 kRed(0xff0000ff);
172 const string kExpected("<color>ff0000ff</color>");
173 xml_serializer_->SaveColor(Type_color, kRed);
174 ASSERT_EQ(kExpected, output_);
175 }
176
TEST_F(XmlSerializerTest,TestPrecision)177 TEST_F(XmlSerializerTest, TestPrecision) {
178 double a = 1.0;
179 // Will round down to int:
180 string expected = "1";
181 ASSERT_EQ(expected, ToString(a));
182 double b = 1.1;
183 // Will preserve at current level of precision:
184 expected = "1.1";
185 ASSERT_EQ(expected, ToString(b));
186 double c = 1.2345678901234567890;
187 // Will round down to 15 decimals of precision:
188 expected = "1.23456789012346";
189 ASSERT_EQ(expected, ToString(c));
190 }
191
192 // Tests the internal Indent() method.
TEST_F(XmlSerializerTest,TestSerializePretty)193 TEST_F(XmlSerializerTest, TestSerializePretty) {
194 placemark_->set_name("hello");
195 string xml = SerializePretty(placemark_);
196 string expected("<Placemark>\n <name>hello</name>\n</Placemark>\n");
197 ASSERT_EQ(expected, xml);
198 }
199
200 // This tests the pretty serialization of an element with no content.
TEST_F(XmlSerializerTest,TestSerializePrettyNil)201 TEST_F(XmlSerializerTest, TestSerializePrettyNil) {
202 ASSERT_EQ(string("<Placemark/>\n"),
203 SerializePretty(placemark_));
204 }
205
206 // This tests the pretty serialization of an element with attributes but
207 // no content.
TEST_F(XmlSerializerTest,TestSerializePrettyNilWithAttrs)208 TEST_F(XmlSerializerTest, TestSerializePrettyNilWithAttrs) {
209 placemark_->set_id("hi"); // Adds the id= attribute.
210 ASSERT_EQ(string("<Placemark id=\"hi\"/>\n"),
211 SerializePretty(placemark_));
212 }
213
214 // This tests the raw serialization of an element a child element.
TEST_F(XmlSerializerTest,TestSerializeRaw)215 TEST_F(XmlSerializerTest, TestSerializeRaw) {
216 placemark_->set_name("hello");
217 string xml = SerializeRaw(placemark_);
218 string expected("<Placemark><name>hello</name></Placemark>");
219 ASSERT_EQ(expected, xml);
220 }
221
222 // This tests the raw serialization of an element with no content.
TEST_F(XmlSerializerTest,TestSerializeRawNil)223 TEST_F(XmlSerializerTest, TestSerializeRawNil) {
224 ASSERT_EQ(string("<Placemark/>"),
225 SerializeRaw(placemark_));
226 }
227
228 // This tests the raw serialization of an element with attributes but
229 // no content.
TEST_F(XmlSerializerTest,TestSerializeRawNilWithAttrs)230 TEST_F(XmlSerializerTest, TestSerializeRawNilWithAttrs) {
231 placemark_->set_id("hi"); // Adds the id= attribute.
232 ASSERT_EQ(string("<Placemark id=\"hi\"/>"),
233 SerializeRaw(placemark_));
234 }
235
TEST_F(XmlSerializerTest,TestSerializeUnknowns)236 TEST_F(XmlSerializerTest, TestSerializeUnknowns) {
237 // Unrecognised elements:
238 const string unknown1("<unknown>zzz<Foo/></unknown>");
239 const string unknown2("<unknownBar/>");
240 placemark_->AddUnknownElement(unknown1);
241 placemark_->AddUnknownElement(unknown2);
242 ASSERT_EQ(static_cast<size_t>(2),
243 placemark_->get_unknown_elements_array_size());
244 ASSERT_EQ(unknown1, placemark_->get_unknown_elements_array_at(0));
245 ASSERT_EQ(unknown2, placemark_->get_unknown_elements_array_at(1));
246 ASSERT_EQ(string("<Placemark>") + unknown1 + unknown2 + "</Placemark>",
247 SerializeRaw(placemark_));
248 }
249
TEST_F(XmlSerializerTest,TestSerializeNull)250 TEST_F(XmlSerializerTest, TestSerializeNull) {
251 const string empty;
252 ASSERT_EQ(empty, SerializePretty(NULL));
253 ASSERT_EQ(empty, SerializeRaw(NULL));
254 }
255
256 // This test verifies that SerializeRaw remains compatible with some slightly
257 // unfortunate and non-obvious behavior in libkml 1.2. In libkml 1.2 the
258 // serialization of <coordinates> _always_ emits lon,lat,alt and always
259 // uses "\n" to separate each tuple _even_ using SerializeRaw.
TEST_F(XmlSerializerTest,SerializeRawCoordinates)260 TEST_F(XmlSerializerTest, SerializeRawCoordinates) {
261 placemark_ = AsPlacemark(ParseKml(
262 "<Placemark>"
263 " <LineString>"
264 " <coordinates>1.2,3.4,5.6 9.8,7.6</coordinates>"
265 " </LineString>"
266 "</Placemark>"));
267 ASSERT_TRUE(placemark_);
268 const string want(
269 "<Placemark>"
270 "<LineString>"
271 "<coordinates>1.2,3.4,5.6\n"
272 "9.8,7.6,0\n"
273 "</coordinates>"
274 "</LineString>"
275 "</Placemark>");
276 ASSERT_EQ(want, SerializeRaw(placemark_));
277 }
278
TEST_F(XmlSerializerTest,BasicSerializePrettyToOstream)279 TEST_F(XmlSerializerTest, BasicSerializePrettyToOstream) {
280 kmldom::CoordinatesPtr coordinates =
281 kmldom::KmlFactory::GetFactory()->CreateCoordinates();
282 coordinates->add_latlng(1,2);
283 kmldom::PointPtr point = kmldom::KmlFactory::GetFactory()->CreatePoint();
284 point->set_coordinates(coordinates);
285 placemark_->set_geometry(point);
286 placemark_->set_id("pm123");
287 placemark_->set_name("placemark name");
288 XmlSerializer<StringAdapter>::Serialize(placemark_, "\r", " ",
289 string_adapter_.get());
290 const string want("<Placemark id=\"pm123\">\r"
291 " <name>placemark name</name>\r"
292 " <Point>\r"
293 " <coordinates>\r"
294 " 2,1,0\r"
295 " </coordinates>\r"
296 " </Point>\r"
297 "</Placemark>\r");
298 ASSERT_EQ(want, output_);
299 }
300
TEST_F(XmlSerializerTest,TestGetElementName)301 TEST_F(XmlSerializerTest, TestGetElementName) {
302 ASSERT_EQ(string(""), GetElementName(NULL));
303 ASSERT_EQ(string("Placemark"), GetElementName(placemark_));
304 ASSERT_EQ(string("atom:author"),
305 GetElementName(KmlFactory::GetFactory()->CreateAtomAuthor()));
306 ASSERT_EQ(string("gx:Tour"),
307 GetElementName(KmlFactory::GetFactory()->CreateGxTour()));
308 }
309
310 } // end namespace kmldom
311