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 XsdFile class.
27 
28 #include "kml/xsd/xsd_file.h"
29 #include "boost/scoped_ptr.hpp"
30 #include "kml/base/attributes.h"
31 #include "gtest/gtest.h"
32 #include "kml/xsd/xsd_util.h"
33 
34 using kmlbase::Attributes;
35 
36 namespace kmlxsd {
37 
38 // This class is the unit test fixture for the XsdFile class.
39 class XsdFileTest : public testing::Test {
40  protected:
SetUp()41   virtual void SetUp() {
42     xsd_file_.reset(new XsdFile);
43   }
44 
45   void SetTestSchema();
46   void AddTestComplexTypes();
47   void AddTestElements();
48   void InitTestXsd();
49   boost::scoped_ptr<XsdFile> xsd_file_;
50 };
51 
TEST_F(XsdFileTest,TestConstructor)52 TEST_F(XsdFileTest, TestConstructor) {
53   ASSERT_TRUE(xsd_file_.get());
54 }
55 
56 // Verify CreateFromParse().
TEST_F(XsdFileTest,TestCreateFromParse)57 TEST_F(XsdFileTest, TestCreateFromParse) {
58   // Verify known bad XSD returns NULL.
59   string errors;
60   ASSERT_FALSE(XsdFile::CreateFromParse("not xsd", &errors));
61   ASSERT_FALSE(errors.empty());
62 
63   // TODO: more
64 }
65 
66 // Verify set_schema(),
TEST_F(XsdFileTest,TestSetSchema)67 TEST_F(XsdFileTest, TestSetSchema) {
68   const string kPrefix("myml");
69   const string kNamespace("my:own:namespace");
70   const XsdSchemaPtr xsd_schema = CreateXsdSchema(kPrefix, kNamespace);
71 
72   // Call the method under test
73   xsd_file_->set_schema(xsd_schema);
74 
75   // Setting the <xs:schema> sets the namespace and prefix for this file.
76   ASSERT_EQ(kPrefix, xsd_file_->get_target_namespace_prefix());
77   ASSERT_EQ(kNamespace, xsd_file_->get_target_namespace());
78 }
79 
80 // Verify add_type(), FindElement(), and FindElementType().
TEST_F(XsdFileTest,TestFindElementTypeByName)81 TEST_F(XsdFileTest, TestFindElementTypeByName) {
82   // Create the <xs:schema ... >
83   const string kPrefix("myml");
84   const string kNamespace("my:own:namespace");
85   xsd_file_->set_schema(CreateXsdSchema(kPrefix, kNamespace));
86   // Create an <xs:complexType name="MyCoolType"/>
87   const string kMyType("MyCoolType");
88   xsd_file_->add_type(CreateXsdComplexType(kMyType));
89   const string kMyElement("MyCoolness");
90   // <xs:element name="MyCoolness" type="myml:MyCoolType"/>
91   xsd_file_->add_element(CreateXsdElement(kMyElement, kPrefix + ":" + kMyType));
92 
93   const XsdElementPtr element = xsd_file_->FindElement(kMyElement);
94   ASSERT_TRUE(element);
95   const XsdTypePtr complex_type = xsd_file_->FindElementType(element);
96   ASSERT_TRUE(complex_type);
97   ASSERT_EQ(kMyType, complex_type->get_name());
98 }
99 
100 // Verify add_type() and GetChildElements().
TEST_F(XsdFileTest,TestAddComplexType)101 TEST_F(XsdFileTest, TestAddComplexType) {
102   // This is an instance document this XSD-let describes:
103   // <MyCoolness>
104   //   <lon>-120.123</lon>
105   //   <lat>37.37</lon>
106   // </MyCoolness>
107 
108   // This is the XSD for the type of MyCoolness:
109   // <xs:complexType name="MyCoolType">
110   //   <complexContent>
111   //     <sequence>
112   //       <element name="lon" type="double"/>
113   //       <element name="lat" type="double"/>
114   //     </sequence>
115   //   </complexContent>
116   // </xs:complexType">
117   const std:: string kMyElement("MyCoolness");
118   const std:: string kMyType("MyCoolType");
119   const string kLon("lon");
120   const string kLat("lat");
121   // Create the <xs:schema ... >
122   const string kPrefix("myml");
123   const string kNamespace("my:own:namespace");
124   xsd_file_->set_schema(CreateXsdSchema(kPrefix, kNamespace));
125   XsdComplexTypePtr xsd_complex_type = CreateXsdComplexType(kMyType);
126   xsd_complex_type->add_element(CreateXsdElement(kLon, "double"));
127   xsd_complex_type->add_element(CreateXsdElement(kLat, "double"));
128   xsd_file_->add_type(xsd_complex_type);
129 
130   // <xs:element name="MyCoolness" type="MyCoolType"/>
131   xsd_file_->add_element(CreateXsdElement(kMyElement, kPrefix + ":" + kMyType));
132 
133   XsdElementVector child_elements;
134   xsd_file_->GetChildElements(kMyElement, &child_elements);
135   ASSERT_EQ(static_cast<size_t>(2), child_elements.size());
136   ASSERT_EQ(kLon, child_elements[0]->get_name());
137   ASSERT_EQ(kLat, child_elements[1]->get_name());
138 }
139 
140 // Verify ResolveRef.
TEST_F(XsdFileTest,TestResolveRef)141 TEST_F(XsdFileTest, TestResolveRef) {
142   const string kPrefix("myml");
143   const string kNamespace("my:own:namespace");
144   const string kMyElement("MyCoolness");
145 
146   // Verify failure if no XsdSchema set for this XsdFile.
147   ASSERT_FALSE(xsd_file_->ResolveRef(kPrefix + ":" + kMyElement));
148 
149   // Build enough of an XsdFile to successfully use ResolveRef().
150   // Create the <xs:schema ... >
151   xsd_file_->set_schema(CreateXsdSchema(kPrefix, kNamespace));
152   // Create a global <xs:element name="myCoolness" type="string"/>
153   xsd_file_->add_element(CreateXsdElement(kMyElement, "string"));
154 
155   // Call the method under test for a good case.
156   XsdElementPtr element = xsd_file_->ResolveRef(kPrefix + ":" + kMyElement);
157   ASSERT_TRUE(element);
158   ASSERT_EQ(kMyElement, element->get_name());
159 
160   // Call the method under test for some failure cases.
161   ASSERT_FALSE(xsd_file_->ResolveRef("nosuchprefix:" + kMyElement));
162   ASSERT_FALSE(xsd_file_->ResolveRef(kPrefix + ":nosuchelement"));
163 }
164 
165 static const char* kTestPrefix = "myml";
166 static const char* kTestTargetNamespace = "my:own:namespace";
167 
168 static const struct {
169   const char* type_name;
170   const char* extension_base;  // NULL if no base type.
171 } kTestComplexTypes[] = {
172   { "FeatureType", "myml:ObjectType" },
173   { "GeometryType", "myml:ObjectType" },
174   { "LineStringType", "myml:GeometryType" },
175   { "ObjectType", NULL },
176   { "PlacemarkType", "myml:FeatureType" },
177   { "PointType", "myml:GeometryType" }
178 };
179 
180 static const struct {
181   const char* name;
182   const char* type;
183   const char* abstract;
184 } kTestElements[] = {
185   { "altitude", "double", "false" },
186   { "altitudeMode", "myml:altitudeModeEnum", "false" },
187   { "name", "string", "false" },
188   { "visibility", "boolean", "false" },
189   { "FeatureGroup", "myml:FeatureType", "true" },
190   { "GeometryGroup", "myml:GeometryType", "true" },
191   { "LineString", "myml:LineStringType", "false" },
192   { "ObjectGroup", "myml:ObjectType", "true" },
193   { "Placemark", "myml:PlacemarkType", "false" },
194   { "Point", "myml:PointType", "false" }
195 };
196 
SetTestSchema()197 void XsdFileTest::SetTestSchema() {
198   // Build enough of an XsdFile to successfull use ResolveRef(), and
199   // GetTypeHierarchy().
200   // Create the <xs:schema ... >
201   xsd_file_->set_schema(CreateXsdSchema(kTestPrefix, kTestTargetNamespace));
202 }
203 
204 // This is an internal utility to add the kTestComplexTypes entries to the
205 // complex types map in the kml_file_.
AddTestComplexTypes()206 void XsdFileTest::AddTestComplexTypes() {
207   // Create some <xs:complexTypes> which extend each other.
208   size_t size = sizeof(kTestComplexTypes)/sizeof(kTestComplexTypes[0]);
209   for (size_t i = 0; i < size; ++i) {
210     XsdComplexTypePtr complex_type =
211         CreateXsdComplexType(kTestComplexTypes[i].type_name);
212     if (kTestComplexTypes[i].extension_base) {
213       complex_type->set_extension_base(kTestComplexTypes[i].extension_base);
214     }
215     xsd_file_->add_type(complex_type);
216   }
217 }
218 
AddTestElements()219 void XsdFileTest::AddTestElements() {
220   size_t size = sizeof(kTestElements)/sizeof(kTestElements[0]);
221   for (size_t i = 0; i < size; ++i) {
222     Attributes attributes;
223     attributes.SetString(kName, kTestElements[i].name);
224     attributes.SetString(kType, kTestElements[i].type);
225     attributes.SetString(kAbstract, kTestElements[i].abstract);
226     XsdElementPtr element = XsdElement::Create(attributes);
227     ASSERT_TRUE(element);
228     xsd_file_->add_element(element);
229   }
230 }
231 
InitTestXsd()232 void XsdFileTest::InitTestXsd() {
233   SetTestSchema();
234   AddTestComplexTypes();
235   AddTestElements();
236 }
237 
238 // Verify GetTypeHierarchy().
TEST_F(XsdFileTest,TestGetTypeHierarchy)239 TEST_F(XsdFileTest, TestGetTypeHierarchy) {
240   InitTestXsd();
241 
242   std::vector<XsdComplexTypePtr> hier;
243   const XsdElementPtr element = xsd_file_->FindElement("Placemark");
244   XsdTypePtr derived = xsd_file_->FindElementType(element);
245   ASSERT_TRUE(derived);
246   XsdComplexTypePtr complex_type = XsdComplexType::AsComplexType(derived);
247   ASSERT_TRUE(complex_type);
248   ASSERT_TRUE(xsd_file_->GetTypeHierarchy(complex_type, &hier));
249   ASSERT_EQ(static_cast<size_t>(2), hier.size());
250   ASSERT_EQ(string("FeatureType"), hier[0]->get_name());
251   ASSERT_EQ(string("ObjectType"), hier[1]->get_name());
252 }
253 
254 // XXX Verify FindElementType().  ByName() we do above...
TEST_F(XsdFileTest,TestFindElementType)255 TEST_F(XsdFileTest, TestFindElementType) {
256 #if 0
257   SetTestSchema();
258   AddTestComplexTypes();
259   AddTestElements();
260   ASSERT_EQ(string("PlacemarkType"),
261                        xsd_file_->FindElementTypeByName("Placemark"));
262   ASSERT_EQ(string("PointType"),
263                        xsd_file_->FindElementTypeByName("Point"));
264 #endif
265 }
266 
267 // Verify GetAbstractElements().
TEST_F(XsdFileTest,TestGetAbstractElements)268 TEST_F(XsdFileTest, TestGetAbstractElements) {
269   XsdElementVector elements;
270   xsd_file_->GetAbstractElements(&elements);
271   // Empty XsdFile, no elements, no abstract elements, no nuthin.
272   ASSERT_TRUE(elements.empty());
273 
274   // Add the test elements to the XsdFile.
275   AddTestElements();
276   xsd_file_->GetAbstractElements(&elements);
277   ASSERT_EQ(static_cast<size_t>(3), elements.size());
278   ASSERT_EQ(string("FeatureGroup"), elements[0]->get_name());
279   ASSERT_EQ(string("GeometryGroup"), elements[1]->get_name());
280   ASSERT_EQ(string("ObjectGroup"), elements[2]->get_name());
281 
282 }
283 
284 // Verify GetComplexElements().
TEST_F(XsdFileTest,TestGetComplexElements)285 TEST_F(XsdFileTest, TestGetComplexElements) {
286   XsdElementVector element_names;
287   xsd_file_->GetComplexElements(&element_names);
288   // Empty XsdFile, no abstract elements.
289   ASSERT_TRUE(element_names.empty());
290 
291   // Add the test namespace, types, and elements to the XsdFile.
292   InitTestXsd();
293   xsd_file_->GetComplexElements(&element_names);
294   ASSERT_EQ(static_cast<size_t>(3), element_names.size());
295   ASSERT_EQ(string("LineString"), element_names[0]->get_name());
296   ASSERT_EQ(string("Placemark"), element_names[1]->get_name());
297   ASSERT_EQ(string("Point"), element_names[2]->get_name());
298 }
299 
300 // Verify GetSimpleElements().
TEST_F(XsdFileTest,TestGetSimpleElements)301 TEST_F(XsdFileTest, TestGetSimpleElements) {
302   XsdElementVector element_names;
303   xsd_file_->GetSimpleElements(&element_names);
304   // Empty XsdFile, no simple elements.
305   ASSERT_TRUE(element_names.empty());
306 
307   // Add the test namespace, types, and elements to the XsdFile.
308   InitTestXsd();
309   xsd_file_->GetSimpleElements(&element_names);
310   ASSERT_EQ(static_cast<size_t>(3), element_names.size());
311 }
312 
313 // Verify TestSetGetAlias();
TEST_F(XsdFileTest,TestSetGetAlias)314 TEST_F(XsdFileTest, TestSetGetAlias) {
315   const string kFeature("Feature");
316   const string kAbstractFeatureGroup("AbstractFeatureGroup");
317   const string kGeometry("Geometry");
318   const string kAbstractGeometryGroup("AbstractGeometryGroup");
319 
320   // An empty XsdFile has no aliases:
321   ASSERT_EQ(string(""),
322                        xsd_file_->get_alias(kAbstractFeatureGroup));
323 
324   // Add aliases using set_alias().
325   xsd_file_->set_alias(kAbstractFeatureGroup, kFeature);
326   xsd_file_->set_alias(kAbstractGeometryGroup, kGeometry);
327 
328   // Verify the proper result for get_alias().
329   ASSERT_EQ(kFeature, xsd_file_->get_alias(kAbstractFeatureGroup));
330   ASSERT_EQ(kGeometry, xsd_file_->get_alias(kAbstractGeometryGroup));
331 }
332 
333 // Verify GetAllTypes().
TEST_F(XsdFileTest,TestGetAllTypes)334 TEST_F(XsdFileTest, TestGetAllTypes) {
335   // Add the test namespace and types the XsdFile.
336   SetTestSchema();
337   AddTestComplexTypes();
338 
339   XsdTypeVector types;
340   xsd_file_->GetAllTypes(&types);
341   ASSERT_EQ(static_cast<size_t>(6), types.size());
342 }
343 
344 // Verify TestFindChildElements().
TEST_F(XsdFileTest,TestFindChildElements)345 TEST_F(XsdFileTest, TestFindChildElements) {
346   // Verify NULL conditions: NULL complex_type and nothing in XsdFile.
347   XsdComplexTypePtr complex_type;
348   XsdElementVector children;
349   xsd_file_->FindChildElements(complex_type, &children);
350   ASSERT_TRUE(children.empty());
351 
352   // Add the test namespace and types the XsdFile.
353   SetTestSchema();
354   AddTestComplexTypes();
355   xsd_file_->FindChildElements(complex_type, &children);
356   ASSERT_TRUE(children.empty());
357 
358   // Look for the given type.
359   const string kPlacemarkType("PlacemarkType");
360   complex_type = XsdComplexType::AsComplexType(
361       xsd_file_->FindType(kPlacemarkType));
362   ASSERT_TRUE(complex_type);
363   ASSERT_EQ(kPlacemarkType, complex_type->get_name());
364 
365   // This has no children.
366   xsd_file_->FindChildElements(complex_type, &children);
367   ASSERT_TRUE(children.empty());
368 
369   // Give it some children.
370   const string kLatitude("latitude");
371   const string kLongitude("longitude");
372   const string kDouble("double");
373   complex_type->add_element(CreateXsdElement(kLatitude, kDouble));
374   complex_type->add_element(CreateXsdElement(kLongitude, kDouble));
375 
376   // This now has some children.
377   xsd_file_->FindChildElements(complex_type, &children);
378   ASSERT_EQ(static_cast<size_t>(2), children.size());
379   ASSERT_EQ(kLatitude, children[0]->get_name());
380   ASSERT_EQ(kLongitude, children[1]->get_name());
381 }
382 
TEST_F(XsdFileTest,TestSearchTypeHierarchy)383 TEST_F(XsdFileTest, TestSearchTypeHierarchy) {
384   InitTestXsd();
385   const XsdComplexTypePtr& point_type =
386       XsdComplexType::AsComplexType(xsd_file_->FindType("PointType"));
387   ASSERT_TRUE(point_type);
388   const XsdComplexTypePtr& geometry_type =
389       XsdComplexType::AsComplexType(xsd_file_->FindType("GeometryType"));
390   ASSERT_TRUE(geometry_type);
391   const XsdComplexTypePtr& object_type =
392       XsdComplexType::AsComplexType(xsd_file_->FindType("ObjectType"));
393   ASSERT_TRUE(object_type);
394   ASSERT_TRUE(xsd_file_->SearchTypeHierarchy(point_type, geometry_type));
395   ASSERT_TRUE(xsd_file_->SearchTypeHierarchy(point_type, object_type));
396 }
397 
398 
TEST_F(XsdFileTest,TestGetElementsOfType)399 TEST_F(XsdFileTest, TestGetElementsOfType) {
400   // Add the test namespace, types, and elements to the XsdFile.
401   InitTestXsd();
402   const XsdComplexTypePtr geometry_type =
403       XsdComplexType::AsComplexType(xsd_file_->FindType("GeometryType"));
404   ASSERT_TRUE(geometry_type);
405   XsdElementVector geometry_elements;
406   xsd_file_->GetElementsOfType(geometry_type, &geometry_elements);
407   ASSERT_EQ(static_cast<size_t>(3), geometry_elements.size());
408   ASSERT_EQ(string("GeometryGroup"), geometry_elements[0]->get_name());
409   ASSERT_EQ(string("LineString"), geometry_elements[1]->get_name());
410   ASSERT_EQ(string("Point"), geometry_elements[2]->get_name());
411 
412   // Verify NULL element vector does not crash.
413   xsd_file_->GetElementsOfType(geometry_type, NULL);
414 }
415 
TEST_F(XsdFileTest,TestGetElementsOfTypeByName)416 TEST_F(XsdFileTest, TestGetElementsOfTypeByName) {
417   // Add the test namespace, types, and elements to the XsdFile.
418   InitTestXsd();
419   XsdElementVector object_elements;
420   xsd_file_->GetElementsOfTypeByName("ObjectType", &object_elements);
421   ASSERT_EQ(static_cast<size_t>(6), object_elements.size());
422   ASSERT_EQ(string("FeatureGroup"), object_elements[0]->get_name());
423   ASSERT_EQ(string("GeometryGroup"), object_elements[1]->get_name());
424   ASSERT_EQ(string("LineString"), object_elements[2]->get_name());
425   ASSERT_EQ(string("ObjectGroup"), object_elements[3]->get_name());
426   ASSERT_EQ(string("Placemark"), object_elements[4]->get_name());
427   ASSERT_EQ(string("Point"), object_elements[5]->get_name());
428 
429   object_elements.clear();
430   xsd_file_->GetElementsOfTypeByName("NoSuchType", &object_elements);
431   ASSERT_TRUE(object_elements.empty());
432 
433   // Verify NULL elements vector pointer doesn't crash.
434   xsd_file_->GetElementsOfTypeByName("ObjectType", NULL);
435   xsd_file_->GetElementsOfTypeByName("NoSuchType", NULL);
436 }
437 
438 }  // end namespace kmlxsd
439