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