1 // Copyright 2008, 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 KmlHandler class.
27 
28 #include "kml/dom/kml_handler.h"
29 #include <stdlib.h>  // For calloc() and free().
30 #include "boost/scoped_ptr.hpp"
31 #include "kml/base/file.h"
32 #include "kml/dom/element.h"
33 #include "kml/dom/kml_cast.h"
34 #include "kml/dom/kml_funcs.h"
35 #include "kml/dom/kml_ptr.h"
36 #include "kml/dom/placemark.h"
37 #include "kml/dom/parser.h"
38 #include "kml/dom/parser_observer.h"
39 #include "gtest/gtest.h"
40 
41 // The following define is a convenience for testing inside Google.
42 #ifdef GOOGLE_INTERNAL
43 #include "kml/base/google_internal_test.h"
44 #endif
45 
46 #ifndef DATADIR
47 #error *** DATADIR must be defined! ***
48 #endif
49 
50 namespace kmldom {
51 
52 typedef std::vector<ElementPtr> element_vector_t;
53 
54 // This class is the unit test fixture for the KmlHandler class.
55 class KmlHandlerTest : public testing::Test {
56  protected:
SetUp()57   virtual void SetUp() {
58     kml_handler_.reset(new KmlHandler(observers_));
59   }
60 
TearDown()61   virtual void TearDown() {
62   }
63 
64   kmlbase::StringVector atts_;
65   parser_observer_vector_t observers_;
66   boost::scoped_ptr<KmlHandler> kml_handler_;
67   void VerifyFolderParse(const ElementPtr& root) const;
68   void VerifyElementTypes(const KmlDomType* types_array,
69                           const element_vector_t& element_vector) const;
70   void MultipleObserverTestCommon(size_t max_elements,
71                                   size_t expected_elements,
72                                   size_t expected_pairs) const;
73 };
74 
75 // This verifies the initial state of a freshly constructed KmlHandler.
TEST_F(KmlHandlerTest,TestInitialState)76 TEST_F(KmlHandlerTest, TestInitialState) {
77   // No elements have been processed, but the PopRoot() method should
78   // be well behaved.
79   ASSERT_TRUE(NULL == kml_handler_->PopRoot());
80 }
81 
82 // This is a test of the StartElement() method for a known simple element.
TEST_F(KmlHandlerTest,TestStartSimpleElement)83 TEST_F(KmlHandlerTest, TestStartSimpleElement) {
84   // This is what expat sends to StartElement() on "<name>".
85   kml_handler_->StartElement("name", atts_);
86 
87   // Since "name" is known we will find it as the root element.
88   ElementPtr root = kml_handler_->PopRoot();
89   ASSERT_EQ(root->Type(), Type_name);
90 
91   // PopRoot() is destructive so now there is nothing.
92   ASSERT_TRUE(NULL == kml_handler_->PopRoot());
93 }
94 
95 // This is a test of the EndElement() method for a known simple element.
TEST_F(KmlHandlerTest,TestEndSimpleElement)96 TEST_F(KmlHandlerTest, TestEndSimpleElement) {
97   // This is what expat sends to KmlHandler on "<name/>":
98   kml_handler_->StartElement("name", atts_);
99   kml_handler_->EndElement("name");
100 
101   ElementPtr root = kml_handler_->PopRoot();
102   ASSERT_EQ(root->Type(), Type_name);
103 
104   // PopRoot() is destructive so now there is nothing.
105   ASSERT_TRUE(NULL == kml_handler_->PopRoot());
106 }
107 
108 // This is a test of the CharData() method for a known simple element.
TEST_F(KmlHandlerTest,TestBasicCharData)109 TEST_F(KmlHandlerTest, TestBasicCharData) {
110   // This is what expat sends to KmlHandler on "<name>what is in a</name>":
111   const char* kTagName = "name";
112   const char* kContent = "what is in a name";
113 
114   kml_handler_->StartElement(kTagName, atts_);
115   kml_handler_->CharData(kContent);
116   kml_handler_->EndElement(kTagName);
117 
118   ElementPtr root = kml_handler_->PopRoot();
119   ASSERT_EQ(root->Type(), Type_name);
120   ASSERT_TRUE(NULL == kml_handler_->PopRoot());
121   ASSERT_EQ(kContent, root->get_char_data());
122 }
123 
124 // This is a test of the StartElement() method for a known complex element.
TEST_F(KmlHandlerTest,TestStartComplexElement)125 TEST_F(KmlHandlerTest, TestStartComplexElement) {
126   kml_handler_->StartElement("Placemark", atts_);
127   ElementPtr root = kml_handler_->PopRoot();
128   ASSERT_EQ(root->Type(), Type_Placemark);
129   ASSERT_TRUE(NULL == kml_handler_->PopRoot());
130 }
131 
132 // This is a test of the EndElement() method for a known complex element.
TEST_F(KmlHandlerTest,TestEndComplexElement)133 TEST_F(KmlHandlerTest, TestEndComplexElement) {
134   // This is what expat does for "<Placemark/>".
135   kml_handler_->StartElement("Placemark", atts_);
136   kml_handler_->EndElement("Placemark");
137   ElementPtr root = kml_handler_->PopRoot();
138   ASSERT_EQ(root->Type(), Type_Placemark);
139   ASSERT_TRUE(NULL == kml_handler_->PopRoot());
140 }
141 
142 // This is a test of StartElement() for a known complex element with known
143 // attributes.
TEST_F(KmlHandlerTest,TestStartComplexElementWithAtts)144 TEST_F(KmlHandlerTest, TestStartComplexElementWithAtts) {
145   const string kAttrName("id");
146   const string kAttrVal("foo");
147   atts_.push_back(kAttrName);
148   atts_.push_back(kAttrVal);
149   kml_handler_->StartElement("Placemark", atts_);
150   ElementPtr root = kml_handler_->PopRoot();
151   ASSERT_EQ(root->Type(), Type_Placemark);
152   ASSERT_TRUE(NULL == kml_handler_->PopRoot());
153   PlacemarkPtr placemark = AsPlacemark(root);
154   ASSERT_EQ(kAttrVal, placemark->get_id());
155 }
156 
157 // This ParserObserver simply appends each Element passed to its NewElement
158 // to the vector passed to the constructor.  The max_elements constructor arg
159 // terminates the parse if the specified number of elements have been parsed.
160 class SimpleNewElementObserver : public ParserObserver {
161  public:
SimpleNewElementObserver(element_vector_t * new_element_vector,size_t max_elements)162   SimpleNewElementObserver(element_vector_t* new_element_vector,
163                            size_t max_elements)
164     : new_element_vector_(new_element_vector), max_elements_(max_elements) {
165   }
166 
167   // ParserObserver::NewElement().  Append the new element to our vector.
NewElement(const ElementPtr & element)168   virtual bool NewElement(const ElementPtr& element) {
169     if (new_element_vector_->size() == max_elements_) {
170       return false;  // Terminates parse.
171     }
172     new_element_vector_->push_back(element);
173     return true;  // Keep parsing.
174   }
175 
176   // Default implementation of AddChild() returns true.
177 
178  private:
179   element_vector_t* new_element_vector_;
180   size_t max_elements_;
181 };
182 
183 // This ParserObserver appends each parent-child pair to the supplied vectors.
184 // Plain vectors are used to simplify testing which is based on
185 // VerifyElementTypes().  The max_elements constructor arg specifies to
186 // terminate the parse if the specified number of pairs have been parsed.
187 class SimpleAddChildObserver : public ParserObserver {
188  public:
SimpleAddChildObserver(element_vector_t * parent_vector,element_vector_t * child_vector,size_t max_elements)189   SimpleAddChildObserver(element_vector_t* parent_vector,
190                          element_vector_t* child_vector, size_t max_elements)
191     : parent_vector_(parent_vector),
192       child_vector_(child_vector),
193       max_elements_(max_elements) {
194   }
195 
196   // Default implementation of NewElement() returns true.
197 
AddChild(const ElementPtr & parent,const ElementPtr & child)198   virtual bool AddChild(const ElementPtr& parent,
199                         const ElementPtr& child) {
200     if (parent_vector_->size() == max_elements_) {
201       return false;  // Terminate parse.
202     }
203     parent_vector_->push_back(parent);
204     child_vector_->push_back(child);
205     return true;  // Keep parsing.
206   }
207 
208  private:
209   element_vector_t* parent_vector_;
210   element_vector_t* child_vector_;
211   size_t max_elements_;
212 };
213 
214 // This KML document and test are kept here together.
215 static const char kKmlFolder[] =
216   "<kml>"
217   "<Folder><name/><description/><Region/>"
218   "<Placemark><Point/></Placemark>"
219   "</Folder>"
220   "</kml>";
221 
222 static const size_t kNumElements = 7;  // Number of elements in kKmlFolder.
223 
224 // This is the order of the elements from kKmlFolder see in NewElement().
225 static const KmlDomType kKmlFolderNewElementOrder[] = {
226   Type_kml, Type_Folder, Type_name, Type_description, Type_Region,
227   Type_Placemark, Type_Point };
228 
229 // This is the order of the elements from kKmlFolder seen in AddChild().
230 static const KmlDomType kKmlFolderParentOrder[] = {
231   Type_Folder, Type_Folder, Type_Folder, Type_Placemark, Type_Folder, Type_kml
232 };
233 static const KmlDomType kKmlFolderChildOrder[] = {
234   Type_name, Type_description, Type_Region, Type_Point, Type_Placemark,
235   Type_Folder
236 };
237 
238 // Verify that each element in the vector is of the corresponding type
239 // in the types_array.
VerifyElementTypes(const KmlDomType * types_array,const element_vector_t & element_vector) const240 void KmlHandlerTest::VerifyElementTypes(
241     const KmlDomType* types_array,
242     const element_vector_t& element_vector) const {
243   for (size_t i = 0; i < element_vector.size(); ++i) {
244     ASSERT_EQ(*(types_array+i), element_vector[i]->Type());
245   }
246 }
247 
248 // This helper function verifies the proper state of kKmlFolder's DOM.
VerifyFolderParse(const ElementPtr & root) const249 void KmlHandlerTest::VerifyFolderParse(const ElementPtr& root) const {
250   KmlPtr kml = AsKml(root);
251   ASSERT_TRUE(kml);
252   FolderPtr folder = AsFolder(kml->get_feature());
253   ASSERT_TRUE(folder);
254   ASSERT_TRUE(folder->has_name());
255   ASSERT_FALSE(folder->has_visibility());
256   ASSERT_FALSE(folder->has_open());
257   ASSERT_TRUE(folder->has_description());
258   ASSERT_TRUE(folder->has_region());
259   ASSERT_EQ(static_cast<size_t>(1), folder->get_feature_array_size());
260   PlacemarkPtr placemark = AsPlacemark(folder->get_feature_array_at(0));
261   PointPtr point = AsPoint(placemark->get_geometry());
262   ASSERT_FALSE(point->has_coordinates());
263 }
264 
265 // This is a simple test of the NewElement() for an observer which does not
266 // terminate the parse.
TEST_F(KmlHandlerTest,SimpleNewElementObserverTest)267 TEST_F(KmlHandlerTest, SimpleNewElementObserverTest) {
268   Parser parser;
269   element_vector_t element_vector;
270   // This specifies to let the parse complete all 7 elements which are saved
271   // in the order encountered to element_vector.
272   SimpleNewElementObserver simple_new_element_observer(&element_vector,
273                                                        kNumElements);
274   parser.AddObserver(&simple_new_element_observer);
275   ElementPtr root = parser.Parse(kKmlFolder, NULL);
276 
277   // Verify that the entire document parsed properly.
278   VerifyFolderParse(root);
279 
280   // Verify that the observer's NewElement() saw the expected elements in
281   // the expected order.
282   VerifyElementTypes(kKmlFolderNewElementOrder, element_vector);
283 }
284 
285 // This verifies that an observer returning false from NewElement() terminates
286 // the parse.
TEST_F(KmlHandlerTest,NewElementObserverTerminationTest)287 TEST_F(KmlHandlerTest, NewElementObserverTerminationTest) {
288   Parser parser;
289   element_vector_t element_vector;
290   // This specifies to stop parsing after 2 elements.
291   SimpleNewElementObserver simple_new_element_observer(&element_vector, 2);
292   parser.AddObserver(&simple_new_element_observer);
293   string errors;
294   ElementPtr root = parser.Parse(kKmlFolder, &errors);
295 
296   // Verify that the parse was terminated.
297   ASSERT_FALSE(root);
298   ASSERT_FALSE(errors.empty());
299 
300   // Verify that exactly the first 2 elements were gathered.
301   ASSERT_EQ(static_cast<size_t>(2), element_vector.size());
302   ASSERT_EQ(Type_kml, element_vector[0]->Type());
303   ASSERT_EQ(Type_Folder, element_vector[1]->Type());
304 }
305 
306 // This is a simple test of the AddChild() for an observer which does not
307 // terminate the parse.
TEST_F(KmlHandlerTest,SimpleAddChildObserverTest)308 TEST_F(KmlHandlerTest, SimpleAddChildObserverTest) {
309   Parser parser;
310   element_vector_t parent_vector;
311   element_vector_t child_vector;
312   // This specifies to let the parse complete all 7 elements which are saved
313   // in the order encountered to element_vector.
314   SimpleAddChildObserver simple_add_child_observer(&parent_vector,
315                                                    &child_vector,
316                                                    kNumElements);
317   parser.AddObserver(&simple_add_child_observer);
318   ElementPtr root = parser.Parse(kKmlFolder, NULL);
319 
320   // Verify that the observer did not interfere with the parse as normal.
321   VerifyFolderParse(root);
322 
323   // Verify that the observer's AddChild() saw the expected elements in
324   // the expected order.
325   VerifyElementTypes(kKmlFolderParentOrder, parent_vector);
326   VerifyElementTypes(kKmlFolderChildOrder, child_vector);
327 }
328 
329 // This verifies that an observer returning false from AddChild() terminates
330 // the parse.
TEST_F(KmlHandlerTest,AddChildObserverTerminationTest)331 TEST_F(KmlHandlerTest, AddChildObserverTerminationTest) {
332   Parser parser;
333   element_vector_t parent_vector;
334   element_vector_t child_vector;
335   // This specifies to stop parsing after 4 parent-child pairs.
336   SimpleAddChildObserver simple_add_child_observer(&parent_vector,
337                                                    &child_vector,
338                                                    4);
339   parser.AddObserver(&simple_add_child_observer);
340   string errors;
341   ElementPtr root = parser.Parse(kKmlFolder, &errors);
342 
343   // Verify that the parse was terminated.
344   ASSERT_FALSE(root);
345   ASSERT_FALSE(errors.empty());
346 
347   // Verify that exactly the first 4 parent-child pairs were gathered.
348   ASSERT_EQ(static_cast<size_t>(4),parent_vector.size());
349   ASSERT_EQ(static_cast<size_t>(4), child_vector.size());
350   VerifyElementTypes(kKmlFolderParentOrder, parent_vector);
351   VerifyElementTypes(kKmlFolderChildOrder, child_vector);
352 }
353 
354 // This verifies that multiple ParserObservers function properly and that
355 // the expected number of new elements and element pairs are seen for the
356 // given value of max_elements.
MultipleObserverTestCommon(size_t max_elements,size_t expected_element_count,size_t expected_pair_count) const357 void KmlHandlerTest::MultipleObserverTestCommon(size_t max_elements,
358                                                 size_t expected_element_count,
359                                                 size_t expected_pair_count)
360                                                 const {
361   element_vector_t element_vector;
362   SimpleNewElementObserver simple_new_element_observer(&element_vector,
363                                                        max_elements);
364   element_vector_t parent_vector;
365   element_vector_t child_vector;
366   SimpleAddChildObserver simple_parent_child_observer(&parent_vector,
367                                                       &child_vector,
368                                                       max_elements);
369   ParserObserver null_observer;
370 
371   Parser parser;
372   parser.AddObserver(&null_observer);
373   parser.AddObserver(&simple_new_element_observer);
374   parser.AddObserver(&simple_parent_child_observer);
375   string errors;
376   ElementPtr root = parser.Parse(kKmlFolder, &errors);
377 
378   if (expected_element_count >= kNumElements) {
379     // Verify that the observers did not interfere with the parse as normal.
380     ASSERT_TRUE(errors.empty());
381     VerifyFolderParse(root);
382   } else {
383     // Verify that an observer teminated the parse.
384     ASSERT_FALSE(root);
385     ASSERT_FALSE(errors.empty());
386   }
387 
388   // Verify that the observers functioned properly.
389   ASSERT_TRUE(expected_element_count == element_vector.size());
390   ASSERT_TRUE(expected_pair_count == parent_vector.size());
391   ASSERT_TRUE(expected_pair_count == child_vector.size());
392   VerifyElementTypes(kKmlFolderNewElementOrder, element_vector);
393   VerifyElementTypes(kKmlFolderParentOrder, parent_vector);
394   VerifyElementTypes(kKmlFolderChildOrder, child_vector);
395 }
396 
397 // Verify proper operation with multiple ParseObservers when no observer
398 // terminates the parse.
TEST_F(KmlHandlerTest,MultipleObserverNormalTest)399 TEST_F(KmlHandlerTest, MultipleObserverNormalTest) {
400   KmlHandlerTest::MultipleObserverTestCommon(kNumElements, kNumElements,
401                                              kNumElements-1);
402 }
403 
404 // Verify proper operation with multiple ParseObservers when an observer
405 // terminates the parse.
TEST_F(KmlHandlerTest,MultipleObserverTerminationTest)406 TEST_F(KmlHandlerTest, MultipleObserverTerminationTest) {
407   KmlHandlerTest::MultipleObserverTestCommon(0, 0, 0);
408   // Accepting just one element results in seeing no pairs.
409   KmlHandlerTest::MultipleObserverTestCommon(1, 1, 0);
410   // These are highly dependent on the exact form of kKmlFolder!
411   KmlHandlerTest::MultipleObserverTestCommon(2, 2, 1);
412   KmlHandlerTest::MultipleObserverTestCommon(6, 6, 4);
413 }
414 
415 // This ParserObserver collects all Features in the parse.
416 class FeatureCollector : public ParserObserver {
417  public:
FeatureCollector(element_vector_t * element_vector)418   FeatureCollector(element_vector_t* element_vector)
419     : element_vector_(element_vector) {
420   }
421   // This EndElement saves each non-Container Feature and returns false to
422   // request that the parser not give this feature to the given parent.
423   // All other parent-child relationships are preserved (such as all children
424   // of the collected feature).
EndElement(const ElementPtr & parent,const ElementPtr & child)425   virtual bool EndElement(const ElementPtr& parent,
426                           const ElementPtr& child) {
427     if (child->IsA(Type_Feature) && !child->IsA(Type_Container)) {
428       element_vector_->push_back(child);
429       return false;
430     }
431     return true;
432   }
433  private:
434   element_vector_t* element_vector_;
435 };
436 
TEST_F(KmlHandlerTest,InhibitingEndElement)437 TEST_F(KmlHandlerTest, InhibitingEndElement) {
438   element_vector_t features;
439   FeatureCollector feature_collector(&features);
440   observers_.push_back(&feature_collector);
441   KmlHandler kml_handler(observers_);
442   kml_handler.StartElement("kml", atts_);
443   kml_handler.StartElement("Document", atts_);
444   kml_handler.StartElement("Placemark", atts_);
445   kml_handler.StartElement("name", atts_);
446   kml_handler.EndElement("name");
447   kml_handler.StartElement("Point", atts_);
448   kml_handler.StartElement("coordinates", atts_);
449   kml_handler.EndElement("coordinates");
450   kml_handler.EndElement("Point");
451   kml_handler.EndElement("Placemark");
452   kml_handler.EndElement("Document");
453   kml_handler.StartElement("NetworkLinkControl", atts_);
454   kml_handler.EndElement("NetworkLinkControl");
455   kml_handler.EndElement("kml");
456   ElementPtr root = kml_handler.PopRoot();
457   ASSERT_TRUE(root);
458   KmlPtr kml = AsKml(root);
459   ASSERT_TRUE(kml);
460   // Document is a Container and is not collected.
461   ASSERT_TRUE(kml->has_feature());
462   ASSERT_TRUE(AsDocument(kml->get_feature()));
463   // NetworkLinkControl is not a Feature is not collected.
464   ASSERT_TRUE(kml->has_networklinkcontrol());
465   // One non-Container Feature is collected.
466   ASSERT_EQ(static_cast<size_t>(1), features.size());
467   PlacemarkPtr placemark = AsPlacemark(features[0]);
468   ASSERT_TRUE(placemark);
469   // Verify the collected feature has all expected children.
470   ASSERT_TRUE(placemark->has_name());
471   ASSERT_TRUE(placemark->has_geometry());
472   PointPtr point = AsPoint(placemark->get_geometry());
473   ASSERT_TRUE(point);
474   ASSERT_TRUE(point->has_coordinates());
475 }
476 
TEST_F(KmlHandlerTest,TestParserHandlesGrossDescriptions)477 TEST_F(KmlHandlerTest, TestParserHandlesGrossDescriptions) {
478   // HTML markup in <description> MUST be wrapped with CDATA elements like so:
479   // <description><![CDATA[<h1>title</h1>]]></description>
480   // However, the web has files with markup like this:
481   // <description><table><tr>...</tr><table></description>
482   // Historically, Google Earth has preserved the author's intent with this
483   // type of invalid markup. And hence, we try to as well.
484   const string kInvalidDescriptions(
485       kmlbase::File::JoinPaths(DATADIR, kmlbase::File::JoinPaths(
486           "kml", "invalid_descriptions.kml")));
487   string data;
488   ASSERT_TRUE(kmlbase::File::ReadFileToString(kInvalidDescriptions, &data));
489   ElementPtr root = Parse(data, NULL);
490   ASSERT_TRUE(root);
491   KmlPtr kml = AsKml(root);
492   ASSERT_TRUE(kml);
493   DocumentPtr document = AsDocument(kml->get_feature());
494   ASSERT_TRUE(document);
495   ASSERT_EQ(static_cast<size_t>(3), document->get_feature_array_size());
496 
497   PlacemarkPtr placemark0 = AsPlacemark(document->get_feature_array_at(0));
498   const string kExpected0("<b>bold</b>");
499   ASSERT_EQ(kExpected0, placemark0->get_description());
500 
501   PlacemarkPtr placemark1 = AsPlacemark(document->get_feature_array_at(1));
502   const string kExpected1("foo<b>bold</b>bar");
503   ASSERT_EQ(kExpected1, placemark1->get_description());
504 
505   PlacemarkPtr placemark2 = AsPlacemark(document->get_feature_array_at(2));
506   const string kExpected2("<description>foo<b>bold</b>bar</description>");
507   ASSERT_EQ(kExpected2, placemark2->get_description());
508 }
509 
TEST_F(KmlHandlerTest,TestParserHandlesBoolWhitespace)510 TEST_F(KmlHandlerTest, TestParserHandlesBoolWhitespace) {
511   const string kOutlineSpace(
512       kmlbase::File::JoinPaths(DATADIR, kmlbase::File::JoinPaths(
513           "kml", "outline_space.kml")));
514   string data;
515   ASSERT_TRUE(kmlbase::File::ReadFileToString(kOutlineSpace, &data));
516   ElementPtr root = Parse(data, NULL);
517   ASSERT_TRUE(root);
518   DocumentPtr document = AsDocument(AsKml(root)->get_feature());
519   StylePtr style = AsStyle(document->get_styleselector_array_at(0));
520   PolyStylePtr polystyle = style->get_polystyle();
521   ASSERT_FALSE(polystyle->get_fill());
522   ASSERT_TRUE(polystyle->get_outline());
523   PlacemarkPtr placemark = AsPlacemark(document->get_feature_array_at(0));
524   polystyle = AsStyle(placemark->get_styleselector())->get_polystyle();
525   ASSERT_FALSE(polystyle->get_fill());
526   ASSERT_TRUE(polystyle->get_outline());
527 }
528 
529 // 100 nested folders is equal to our default nesting limit.
TEST_F(KmlHandlerTest,TestMaxNestingOf100Folders)530 TEST_F(KmlHandlerTest, TestMaxNestingOf100Folders) {
531   const string k100Folders(
532       kmlbase::File::JoinPaths(DATADIR, kmlbase::File::JoinPaths(
533           "kml", "100_nested_folders.kml")));
534   string data;
535   ASSERT_TRUE(kmlbase::File::ReadFileToString(k100Folders, &data));
536   ElementPtr root = Parse(data, NULL);
537   ASSERT_TRUE(root);  // Parse succeeded.
538 }
539 
540 // 101 nested folders exceeds our default nesting limit of 100.
TEST_F(KmlHandlerTest,TestMaxNestingOf101Folders)541 TEST_F(KmlHandlerTest, TestMaxNestingOf101Folders) {
542   const string k101Folders(
543       kmlbase::File::JoinPaths(DATADIR, kmlbase::File::JoinPaths(
544           "kml", "101_nested_folders.kml")));
545   string data;
546   ASSERT_TRUE(kmlbase::File::ReadFileToString(k101Folders, &data));
547   ElementPtr root = Parse(data, NULL);
548   ASSERT_FALSE(root);  // Parse was stopped.
549 }
550 
551 // 101 nested elements exceeds our default nesting limit of 100.
TEST_F(KmlHandlerTest,TestMaxNestingOf101Elements)552 TEST_F(KmlHandlerTest, TestMaxNestingOf101Elements) {
553   const string k101Elements(
554       kmlbase::File::JoinPaths(DATADIR, kmlbase::File::JoinPaths(
555           "kml", "101_nested_elements.kml")));
556   string data;
557   ASSERT_TRUE(kmlbase::File::ReadFileToString(k101Elements, &data));
558   ElementPtr root = Parse(data, NULL);
559   ASSERT_FALSE(root);  // Parse was stopped.
560 }
561 
562 // KML 2.0 and 2.1 permitted the extension of Placemark by defining a
563 // substitution element and possible children. This didn't make it to
564 // OGC KML 2.2, but these files exist in surprising numbers, so we try our
565 // best to parse it sanely into standard KML.
566 //
567 // This is a test of turning testdata/kml/old_schema_example.kml into this:
568 //
569 // <?xml version="1.0" encoding="utf-8"?>
570 // <kml xmlns="http://www.opengis.net/kml/2.2">
571 //   <Document>
572 //     <Schema id="S_521_525_SSSSS_id" name="S_521_525_SSSSS">
573 //       <SimpleField name="Foo" type="string"/>
574 //       <SimpleField name="Bar" type="string"/>
575 //     </Schema>
576 //     <Placemark>
577 //       <name>1</name>
578 //       <ExtendedData>
579 //         <SchemaData schemaUrl="S_521_525_SSSSS_id">
580 //           <SimpleData name="Foo">foo 1</SimpleData>
581 //           <SimpleData name="Bar">bar 1</SimpleData>
582 //         </SchemaData>
583 //       </ExtendedData>
584 //       <Point>
585 //         <coordinates>
586 //           -122,37,0
587 //         </coordinates>
588 //       </Point>
589 //     </Placemark>
590 //     <Placemark>
591 //       <name>2</name>
592 //       <ExtendedData>
593 //         <SchemaData schemaUrl="S_521_525_SSSSS_id">
594 //           <SimpleData name="Foo">foo 2</SimpleData>
595 //           <SimpleData name="Bar">bar 2</SimpleData>
596 //         </SchemaData>
597 //       </ExtendedData>
598 //     </Placemark>
599 //   </Document>
600 // </kml>
TEST_F(KmlHandlerTest,TestHandlesOldSchemaUsage)601 TEST_F(KmlHandlerTest, TestHandlesOldSchemaUsage) {
602   const string kOldSchemaKml(
603       kmlbase::File::JoinPaths(DATADIR, kmlbase::File::JoinPaths(
604           "kml", "old_schema_example.kml")));
605   string data;
606   ASSERT_TRUE(kmlbase::File::ReadFileToString(kOldSchemaKml, &data));
607   string errors;
608   ElementPtr root = Parse(data, &errors);
609   ASSERT_TRUE(root);
610   ASSERT_TRUE(errors.empty());
611   const KmlPtr kml = AsKml(root);
612   ASSERT_TRUE(kml);
613   ASSERT_TRUE(kml->has_feature());
614   const DocumentPtr document = AsDocument(kml->get_feature());
615   ASSERT_TRUE(document);
616   ASSERT_EQ(static_cast<size_t>(1), document->get_schema_array_size());
617   const SchemaPtr schema = AsSchema(document->get_schema_array_at(0));
618   ASSERT_TRUE(schema);
619   ASSERT_EQ("S_521_525_SSSSS_id", schema->get_id());
620   ASSERT_EQ("S_521_525_SSSSS", schema->get_name());
621   ASSERT_EQ(static_cast<size_t>(2), schema->get_simplefield_array_size());
622   const SimpleFieldPtr simplefield0 =
623     AsSimpleField(schema->get_simplefield_array_at(0));
624   ASSERT_TRUE(simplefield0);
625   ASSERT_EQ("Foo", simplefield0->get_name());
626   ASSERT_EQ("string", simplefield0->get_type());
627   const SimpleFieldPtr simplefield1 =
628     AsSimpleField(schema->get_simplefield_array_at(1));
629   ASSERT_TRUE(simplefield1);
630   ASSERT_EQ("Bar", simplefield1->get_name());
631   ASSERT_EQ("string", simplefield1->get_type());
632   ASSERT_EQ(static_cast<size_t>(2), document->get_feature_array_size());
633 
634   const PlacemarkPtr placemark0 =
635     AsPlacemark(document->get_feature_array_at(0));
636   ASSERT_EQ("1", placemark0->get_name());
637   ASSERT_TRUE(placemark0->has_extendeddata());
638   const ExtendedDataPtr extendeddata0 =
639     AsExtendedData(placemark0->get_extendeddata());
640   ASSERT_TRUE(extendeddata0);
641   ASSERT_EQ(static_cast<size_t>(1), extendeddata0->get_schemadata_array_size());
642   const SchemaDataPtr schemadata0 =
643     AsSchemaData(extendeddata0->get_schemadata_array_at(0));
644   ASSERT_TRUE(schemadata0);
645   ASSERT_EQ("S_521_525_SSSSS_id", schemadata0->get_schemaurl());
646   ASSERT_EQ(static_cast<size_t>(2), schemadata0->get_simpledata_array_size());
647   const SimpleDataPtr simpledata00 =
648     AsSimpleData(schemadata0->get_simpledata_array_at(0));
649   ASSERT_TRUE(simpledata00);
650   ASSERT_EQ("Foo", simpledata00->get_name());
651   ASSERT_EQ("foo 1", simpledata00->get_text());
652   const SimpleDataPtr simpledata01 =
653     AsSimpleData(schemadata0->get_simpledata_array_at(1));
654   ASSERT_TRUE(simpledata01);
655   ASSERT_EQ("Bar", simpledata01->get_name());
656   ASSERT_EQ("bar 1", simpledata01->get_text());
657 
658   const PlacemarkPtr placemark1 =
659     AsPlacemark(document->get_feature_array_at(1));
660   ASSERT_EQ("2", placemark1->get_name());
661   ASSERT_TRUE(placemark1->has_extendeddata());
662   const ExtendedDataPtr extendeddata1 =
663     AsExtendedData(placemark1->get_extendeddata());
664   ASSERT_TRUE(extendeddata1);
665   ASSERT_EQ(static_cast<size_t>(1), extendeddata1->get_schemadata_array_size());
666   const SchemaDataPtr schemadata1 =
667     AsSchemaData(extendeddata1->get_schemadata_array_at(0));
668   ASSERT_TRUE(schemadata1);
669   ASSERT_EQ("S_521_525_SSSSS_id", schemadata1->get_schemaurl());
670   ASSERT_EQ(static_cast<size_t>(2), schemadata1->get_simpledata_array_size());
671   const SimpleDataPtr simpledata10 =
672     AsSimpleData(schemadata1->get_simpledata_array_at(0));
673   ASSERT_TRUE(simpledata10);
674   ASSERT_EQ("Foo", simpledata10->get_name());
675   ASSERT_EQ("foo 2", simpledata10->get_text());
676   const SimpleDataPtr simpledata11 =
677     AsSimpleData(schemadata1->get_simpledata_array_at(1));
678   ASSERT_TRUE(simpledata11);
679   ASSERT_EQ("Bar", simpledata11->get_name());
680   ASSERT_EQ("bar 2", simpledata11->get_text());
681 }
682 
683 // This verifies that a ParserObsever sees a <Placemark> when old
684 // KML 2.0/2.1 <Schema> usages is parsed.
TEST_F(KmlHandlerTest,TestOldSchemaParserObserver)685 TEST_F(KmlHandlerTest, TestOldSchemaParserObserver) {
686   const string kOldSchemaKml = (
687     "<Document>"
688     "<Schema parent=\"Placemark\" name=\"S_521_525_SSSSS\">"
689     "<SimpleField type=\"string\" name=\"Foo\"></SimpleField>"
690     "</Schema>"
691     "<S_521_525_SSSSS>"
692     "<Foo>foo 1</Foo>"
693     "</S_521_525_SSSSS>"
694     "</Document>");
695 
696   element_vector_t element_vector;
697   size_t max_elements = 100;
698   SimpleNewElementObserver simple_new_element_observer(&element_vector,
699                                                        max_elements);
700   Parser parser;
701   parser.AddObserver(&simple_new_element_observer);
702   string errors;
703   ElementPtr root = parser.Parse(kOldSchemaKml, &errors);
704   ASSERT_TRUE(root);
705   ASSERT_TRUE(errors.empty());
706   // NewElement() is called only 4 times; The logic that handles the old
707   // <Schema> knows to look for <Foo> as a child, and the handing there is
708   // special-cased; StartElement() returns before the observer for <Foo> is
709   // called.
710   ASSERT_EQ(static_cast<size_t>(4), element_vector.size());
711   ASSERT_EQ(Type_Placemark, element_vector.at(3)->Type());
712 }
713 
714 // Verify the handling of old-style <Schema> parsing directly in
715 // StartElement and EndElement.
TEST_F(KmlHandlerTest,TestOldSchemaHandling)716 TEST_F(KmlHandlerTest, TestOldSchemaHandling) {
717   kml_handler_->StartElement("Document", atts_);
718   atts_.push_back("parent");
719   atts_.push_back("Placemark");
720   atts_.push_back("name");
721   const string kOldStyleSchemaName("OldStyleSchemaName");
722   atts_.push_back(kOldStyleSchemaName);
723   kml_handler_->StartElement("Schema", atts_);
724   atts_.clear();
725   atts_.push_back("type");
726   atts_.push_back("string");
727   atts_.push_back("name");
728   const string kOldStyleSchemaChild("OldStyleSchemaChild");
729   atts_.push_back(kOldStyleSchemaChild);
730   kml_handler_->StartElement("SimpleField", atts_);
731   kml_handler_->EndElement("SimpleField");
732   kml_handler_->EndElement("Schema");
733   atts_.clear();
734   kml_handler_->StartElement(kOldStyleSchemaName, atts_);
735   kml_handler_->StartElement(kOldStyleSchemaChild, atts_);
736   const string kOldStyleSchemaChildCharData("char data");
737   kml_handler_->CharData(kOldStyleSchemaChildCharData);
738   kml_handler_->EndElement(kOldStyleSchemaChild);
739   kml_handler_->EndElement(kOldStyleSchemaName);
740   ElementPtr root = kml_handler_->PopRoot();
741   ASSERT_TRUE(root);
742   ASSERT_EQ(Type_Document, root->Type());
743   // A Placemark was created from OldStyleSchemaName.
744   ASSERT_EQ(Type_Placemark, AsDocument(root)->get_feature_array_at(0)->Type());
745   PlacemarkPtr placemark =
746     AsPlacemark(AsDocument(root)->get_feature_array_at(0));
747   // The OldStyleSchemaChild was converted into an ExtendedData structure.
748   ASSERT_TRUE(placemark->has_extendeddata());
749   ExtendedDataPtr extendeddata = AsExtendedData(placemark->get_extendeddata());
750   ASSERT_EQ(static_cast<size_t>(1), extendeddata->get_schemadata_array_size());
751   SchemaDataPtr schemadata = extendeddata->get_schemadata_array_at(0);
752   ASSERT_EQ(kOldStyleSchemaName + "_id", schemadata->get_schemaurl());
753   ASSERT_EQ(static_cast<size_t>(1), schemadata->get_simpledata_array_size());
754   SimpleDataPtr simpledata = schemadata->get_simpledata_array_at(0);
755   ASSERT_EQ(kOldStyleSchemaChild, simpledata->get_name());
756   ASSERT_EQ(kOldStyleSchemaChildCharData, simpledata->get_text());
757 }
758 
759 }  // end namespace kmldom
760