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>&lt;![CDATA[...]]&gt;</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 &lt;![CDATA[goo]]&gt; goo</name>\n"},
104     {"<x><![CDATA[goo]]></x>",
105      "<name>&lt;x&gt;&lt;![CDATA[goo]]&gt;&lt;/x&gt;</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 &lt;b&gt; goo &lt;![CDATA["
134                   "xxx&lt;i&gt;yyy&lt;/i&gt;xxx]]&gt; &lt;/b&gt; 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