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 MergeFields() and MergeElements()
27 // functions.
28
29 #include "kml/engine/merge.h"
30 #include "kml/dom/kml_funcs.h"
31 #include "kml/dom/kml_factory.h"
32 #include "kml/dom/kml22.h"
33 #include "kml/dom/kmldom.h"
34 #include "gtest/gtest.h"
35
36 using kmlbase::Vec3;
37 using kmldom::CoordinatesPtr;
38 using kmldom::FolderPtr;
39 using kmldom::LineStringPtr;
40 using kmldom::IconStylePtr;
41 using kmldom::IconStyleIconPtr;
42 using kmldom::KmlFactory;
43 using kmldom::PlacemarkPtr;
44 using kmldom::PointPtr;
45 using kmldom::StylePtr;
46
47 namespace kmlengine {
48
49 class MergeTest : public testing::Test {
50 protected:
SetUp()51 virtual void SetUp() {
52 factory_ = KmlFactory::GetFactory();
53 linestring_ = factory_->CreateLineString();
54 source_placemark_ = factory_->CreatePlacemark();
55 target_placemark_ = factory_->CreatePlacemark();
56 point_ = factory_->CreatePoint();
57 source_style_ = factory_->CreateStyle();
58 target_style_ = factory_->CreateStyle();
59 }
60
61 StylePtr SetSubStyles(StylePtr style, double icon_style_scale,
62 const string& label_style_color,
63 double line_style_width, bool poly_style_fill,
64 const string& balloon_style_text,
65 kmldom::ListItemTypeEnum liststyle_style_listitemtype);
66 void VerifySubStyles(StylePtr style, double icon_style_scale,
67 const string& label_style_color,
68 double line_style_width, bool poly_style_fill,
69 const string& balloon_style_text,
70 kmldom::ListItemTypeEnum liststyle_style_listitemtype);
71 void SetPointPlacemark(PlacemarkPtr placemark, const string& name,
72 double lat, double lon);
73 void VerifyPointPlacemark(PlacemarkPtr placemark, const string& name,
74 double lat, double lon);
75 KmlFactory *factory_;
76 LineStringPtr linestring_;
77 PlacemarkPtr source_placemark_;
78 PlacemarkPtr target_placemark_;
79 StylePtr source_style_;
80 StylePtr target_style_;
81 PointPtr point_;
82 };
83
84 // Verify MergeFields() does not crash on NULL args.
TEST_F(MergeTest,TestMergeFieldsNull)85 TEST_F(MergeTest, TestMergeFieldsNull) {
86 MergeFields(source_placemark_, NULL);
87 MergeFields(NULL, target_placemark_);
88 MergeFields(NULL, NULL);
89 }
90
91 // Verify MergeFields does not crash or harm source if source and target
92 // are the same element.
TEST_F(MergeTest,TestMergeFieldsSame)93 TEST_F(MergeTest, TestMergeFieldsSame) {
94 // Verify that passing the same element for source and target does not crash.
95 MergeFields(source_placemark_, source_placemark_);
96 const string kDescription("description");
97 source_placemark_->set_description(kDescription);
98 MergeFields(source_placemark_, source_placemark_);
99 // Placemark is still sane. Placemark had a description before and still
100 // does and had no other fields and still does.
101 ASSERT_EQ(kDescription, source_placemark_->get_description());
102 ASSERT_FALSE(source_placemark_->has_name());
103 ASSERT_FALSE(source_placemark_->has_region());
104 ASSERT_FALSE(source_placemark_->has_geometry());
105 MergeFields(point_, point_);
106 // Point is still sane. Point had no coordinates before and none after.
107 ASSERT_FALSE(point_->has_coordinates());
108 }
109
110 // Verify normal usage of MergeFields().
TEST_F(MergeTest,TestSimpleMergeFields)111 TEST_F(MergeTest, TestSimpleMergeFields) {
112 // Set one field in the source.
113 const string kSourceName("source name");
114 source_placemark_->set_name(kSourceName);
115 // Merge fields in source to target.
116 MergeFields(source_placemark_, target_placemark_);
117 // Verify target now has the expected value for the given field.
118 ASSERT_EQ(kSourceName, target_placemark_->get_name());
119
120 // Set the target's value for the given field.
121 const string kTargetName("target name");
122 target_placemark_->set_name(kTargetName);
123 // Verify that the source still has the expected value.
124 ASSERT_EQ(kSourceName, source_placemark_->get_name());
125 // Merge source to target.
126 MergeFields(source_placemark_, target_placemark_);
127 // Verify that target now has the expected value.
128 ASSERT_EQ(kSourceName, target_placemark_->get_name());
129 }
130
131 // Verify that several fields are properly set in the target.
TEST_F(MergeTest,TestMergeFieldsMany)132 TEST_F(MergeTest, TestMergeFieldsMany) {
133 const string kName("name");
134 const bool kVisibility(false);
135 const bool kOpen(false);
136 const string kDescription("name");
137 source_placemark_->set_name(kName);
138 source_placemark_->set_description(kDescription);
139 source_placemark_->set_visibility(kVisibility);
140 source_placemark_->set_open(kOpen);
141 MergeFields(source_placemark_, target_placemark_);
142 ASSERT_TRUE(target_placemark_->has_name());
143 ASSERT_EQ(kName, target_placemark_->get_name());
144 ASSERT_TRUE(target_placemark_->has_description());
145 ASSERT_EQ(kDescription, target_placemark_->get_description());
146 ASSERT_TRUE(target_placemark_->has_visibility());
147 ASSERT_EQ(kVisibility, target_placemark_->get_visibility());
148 ASSERT_TRUE(target_placemark_->has_open());
149 ASSERT_EQ(kOpen, target_placemark_->get_open());
150 }
151
152 // Verify that MergeFields() does not effect complex children.
TEST_F(MergeTest,TestDontMergeComplexChildren)153 TEST_F(MergeTest, TestDontMergeComplexChildren) {
154 // Give the source element a complex child.
155 source_placemark_->set_geometry(point_);
156 // Give the target a different type for the same complex child.
157 target_placemark_->set_geometry(linestring_);
158 // Set some simple element children in the source.
159 const string kName("hi there");
160 source_placemark_->set_name(kName);
161 source_placemark_->set_visibility(false);
162 // Merge fields from source to target.
163 MergeFields(source_placemark_, target_placemark_);
164 // Verify simple fields were copied.
165 ASSERT_EQ(kName, target_placemark_->get_name());
166 ASSERT_FALSE(target_placemark_->get_visibility());
167 // Verify complex child of target was not touched.
168 ASSERT_EQ(kmldom::Type_LineString,
169 target_placemark_->get_geometry()->Type());
170 }
171
172 // Verify MergeElements() does not crash on NULL args.
TEST_F(MergeTest,TestMergeElementsNull)173 TEST_F(MergeTest, TestMergeElementsNull) {
174 MergeElements(source_placemark_, NULL);
175 MergeElements(NULL, target_placemark_);
176 MergeElements(NULL, NULL);
177 }
178
TEST_F(MergeTest,TestBasicMergeIconStyle)179 TEST_F(MergeTest, TestBasicMergeIconStyle) {
180 const string kHref("icon.png");
181 IconStylePtr source = KmlFactory::GetFactory()->CreateIconStyle();
182 IconStyleIconPtr icon = KmlFactory::GetFactory()->CreateIconStyleIcon();
183 icon->set_href(kHref);
184 source->set_icon(icon);
185
186 IconStylePtr target = KmlFactory::GetFactory()->CreateIconStyle();
187 MergeElements(source, target);
188 ASSERT_TRUE(target->has_icon());
189 ASSERT_TRUE(target->get_icon()->has_href());
190 ASSERT_EQ(kHref, target->get_icon()->get_href());
191 }
192
193 // This verifies that an element hierarchy is properly merged from
194 // target to source. The test of IconStyle/Icon in particular excercises
195 // some special handling of this element.
TEST_F(MergeTest,TestMergeIconStyle)196 TEST_F(MergeTest, TestMergeIconStyle) {
197 const double kScale(1.3);
198 const double kHeading(123);
199 const string kHref("cool.jpeg");
200
201 // This is what we are merging in:
202 // <Style>
203 // <IconStyle>
204 // <scale>1.3</scale>
205 // <Icon>
206 // <href>cool.jpeg</href>
207 // </Icon>
208 // </IconStyle>
209 // </Style>
210 kmldom::IconStylePtr iconstyle = factory_->CreateIconStyle();
211 iconstyle->set_scale(kScale);
212 kmldom::IconStyleIconPtr iconstyleicon = factory_->CreateIconStyleIcon();
213 iconstyleicon->set_href(kHref);
214 iconstyle->set_icon(iconstyleicon);
215 source_style_->set_iconstyle(iconstyle);
216
217 // This is what we are merging onto:
218 // <Style>
219 // <IconStyle>
220 // <scale>1.5</scale>
221 // <heading>123</heading>
222 // </IconStyle>
223 // </Style>
224 iconstyle = factory_->CreateIconStyle();
225 iconstyle->set_scale(1.5); // Something that is not kScale.
226 iconstyle->set_heading(kHeading);
227 target_style_->set_iconstyle(iconstyle);
228
229 // Since target_style_ _has_ an IconStyle, this _merges_ IconStyle from src.
230 MergeElements(source_style_, target_style_);
231
232 // Verify that the result is this:
233 // <Style>
234 // <IconStyle>
235 // <scale>1.3</scale> <!-- over-written -->
236 // <heading>123</heading> <!-- preserved -->
237 // <Icon> <!-- added -->
238 // <href>cool.jpeg</href>
239 // </Icon>
240 // </IconStyle>
241 // </Style>
242 ASSERT_TRUE(target_style_->has_iconstyle());
243 ASSERT_EQ(kScale, source_style_->get_iconstyle()->get_scale());
244 ASSERT_EQ(kScale, target_style_->get_iconstyle()->get_scale());
245 ASSERT_EQ(kHeading, target_style_->get_iconstyle()->get_heading());
246 ASSERT_TRUE(source_style_->get_iconstyle()->has_icon());
247 ASSERT_TRUE(target_style_->get_iconstyle()->has_icon());
248 ASSERT_TRUE(source_style_->get_iconstyle()->get_icon()->has_href());
249 ASSERT_TRUE(target_style_->get_iconstyle()->get_icon()->has_href());
250 ASSERT_EQ(kHref, source_style_->get_iconstyle()->get_icon()->get_href());
251 ASSERT_EQ(kHref, target_style_->get_iconstyle()->get_icon()->get_href());
252 }
253
254 // This is a utility function to create each substyle and set the given
255 // field to the given value.
SetSubStyles(StylePtr style,double icon_style_scale,const string & label_style_color,double line_style_width,bool poly_style_fill,const string & balloon_style_text,kmldom::ListItemTypeEnum list_style_listitemtype)256 StylePtr MergeTest::SetSubStyles(StylePtr style,
257 double icon_style_scale,
258 const string& label_style_color,
259 double line_style_width,
260 bool poly_style_fill,
261 const string& balloon_style_text,
262 kmldom::ListItemTypeEnum
263 list_style_listitemtype) {
264 // <IconStyle><scale>
265 kmldom::IconStylePtr iconstyle = factory_->CreateIconStyle();
266 iconstyle->set_scale(icon_style_scale);
267 style->set_iconstyle(iconstyle);
268
269 // <LabelStyle><color>
270 kmldom::LabelStylePtr labelstyle = factory_->CreateLabelStyle();
271 labelstyle->set_color(label_style_color);
272 style->set_labelstyle(labelstyle);
273
274 // <LineStyle><width>
275 kmldom::LineStylePtr linestyle = factory_->CreateLineStyle();
276 linestyle->set_width(line_style_width);
277 style->set_linestyle(linestyle);
278
279 // <PolyStyle><fill>
280 kmldom::PolyStylePtr polystyle = factory_->CreatePolyStyle();
281 polystyle->set_fill(poly_style_fill);
282 style->set_polystyle(polystyle);
283
284 // <BalloonStyle><text>
285 kmldom::BalloonStylePtr balloonstyle = factory_->CreateBalloonStyle();
286 balloonstyle->set_text(balloon_style_text);
287 style->set_balloonstyle(balloonstyle);
288
289 // <ListStyle><listItemType>
290 kmldom::ListStylePtr liststyle = factory_->CreateListStyle();
291 liststyle->set_listitemtype(list_style_listitemtype);
292 style->set_liststyle(liststyle);
293
294 return style;
295 }
296
297 // This is a utility function to verify the given SubStyle fields.
VerifySubStyles(StylePtr style,double icon_style_scale,const string & label_style_color,double line_style_width,bool poly_style_fill,const string & balloon_style_text,kmldom::ListItemTypeEnum list_style_listitemtype)298 void MergeTest::VerifySubStyles(StylePtr style,
299 double icon_style_scale,
300 const string& label_style_color,
301 double line_style_width,
302 bool poly_style_fill,
303 const string& balloon_style_text,
304 kmldom::ListItemTypeEnum
305 list_style_listitemtype) {
306 ASSERT_TRUE(style->has_iconstyle());
307 ASSERT_TRUE(style->get_iconstyle()->has_scale());
308 ASSERT_EQ(icon_style_scale, style->get_iconstyle()->get_scale());
309 ASSERT_TRUE(style->has_labelstyle());
310 ASSERT_TRUE(style->get_labelstyle()->has_color());
311 ASSERT_EQ(label_style_color,
312 style->get_labelstyle()->get_color().to_string_abgr());
313 ASSERT_TRUE(style->has_linestyle());
314 ASSERT_TRUE(style->get_linestyle()->has_width());
315 ASSERT_EQ(line_style_width, style->get_linestyle()->get_width());
316 ASSERT_TRUE(style->has_polystyle());
317 ASSERT_TRUE(style->get_polystyle()->has_fill());
318 ASSERT_EQ(poly_style_fill, style->get_polystyle()->get_fill());
319 ASSERT_TRUE(style->has_balloonstyle());
320 ASSERT_TRUE(style->get_balloonstyle()->has_text());
321 ASSERT_EQ(balloon_style_text,
322 style->get_balloonstyle()->get_text());
323 ASSERT_TRUE(style->has_liststyle());
324 ASSERT_TRUE(style->get_liststyle()->has_listitemtype());
325 ASSERT_EQ(static_cast<int>(list_style_listitemtype),
326 style->get_liststyle()->get_listitemtype());
327 }
328
329 // This verifies that all SubStyles are merged from a source to target Style.
330 // Note that most field types are represented in this test: double, string,
331 // bool and enum.
TEST_F(MergeTest,TestMergeFullStyle)332 TEST_F(MergeTest, TestMergeFullStyle) {
333 // Create a Style with the following SubStyle fields.
334 const double kScale(2.2);
335 const string kColor("ff112233");
336 const double kWidth(3.3);
337 const bool kFill(false);
338 const string kText("This is a <b>bold $[name]</b>");
339 const kmldom::ListItemTypeEnum
340 kListItemType(kmldom::LISTITEMTYPE_CHECKHIDECHILDREN);
341 SetSubStyles(source_style_, kScale, kColor, kWidth, kFill, kText,
342 kListItemType);
343
344 // For good measure verify everything was set.
345 VerifySubStyles(source_style_, kScale, kColor, kWidth, kFill, kText,
346 kListItemType);
347
348 // Make an empty Style and merge in everything from source.
349 MergeElements(source_style_, target_style_);
350
351 // Verify everything was merged:
352 VerifySubStyles(target_style_, kScale, kColor, kWidth, kFill, kText,
353 kListItemType);
354
355 // Set source sub style fields to different values.
356 SetSubStyles(source_style_, 1.1, "00110011", 0.2, true, "xxx",
357 kmldom::LISTITEMTYPE_RADIOFOLDER);
358
359 // Verify target is unscathed.
360 VerifySubStyles(target_style_, kScale, kColor, kWidth, kFill, kText,
361 kListItemType);
362 }
363
364 // This is a utility method to create a Placemark with a Point.
SetPointPlacemark(PlacemarkPtr placemark,const string & name,double lat,double lon)365 void MergeTest::SetPointPlacemark(PlacemarkPtr placemark,
366 const string& name,
367 double lat, double lon) {
368 placemark->set_name(name);
369 PointPtr point = factory_->CreatePoint();
370 CoordinatesPtr coordinates = factory_->CreateCoordinates();
371 coordinates->add_latlng(lat, lon);
372 point->set_coordinates(coordinates);
373 placemark->set_geometry(point);
374 }
375
376 // This is a utility method to verify the given fields in a Point Placemark.
VerifyPointPlacemark(PlacemarkPtr placemark,const string & name,double lat,double lon)377 void MergeTest::VerifyPointPlacemark(PlacemarkPtr placemark,
378 const string& name,
379 double lat, double lon) {
380 ASSERT_TRUE(placemark->has_name());
381 ASSERT_EQ(name, placemark->get_name());
382 ASSERT_TRUE(placemark->has_geometry());
383 PointPtr point = kmldom::AsPoint(placemark->get_geometry());
384 ASSERT_TRUE(point);
385 ASSERT_TRUE(point->has_coordinates());
386 CoordinatesPtr coordinates = point->get_coordinates();
387 ASSERT_EQ(static_cast<size_t>(1),
388 coordinates->get_coordinates_array_size());
389 Vec3 vec3 = coordinates->get_coordinates_array_at(0);
390 ASSERT_EQ(lat, vec3.get_latitude());
391 ASSERT_EQ(lon, vec3.get_longitude());
392 }
393
394 // This verifies the merge of a Placemark Point element hierarchy.
TEST_F(MergeTest,TestMergePointPlacemark)395 TEST_F(MergeTest, TestMergePointPlacemark) {
396 const string kName("source");
397 const double kLat(1.1);
398 const double kLon(-1.1);
399 SetPointPlacemark(source_placemark_, kName, kLat, kLon);
400 SetPointPlacemark(target_placemark_, "not source at all", -2.2, 2.2);
401
402 // Merge the source onto the target.
403 MergeElements(source_placemark_, target_placemark_);
404
405 // Verify that the target has the expected values.
406 VerifyPointPlacemark(target_placemark_, kName, kLat, kLon);
407 }
408
409 // This verifies that a group match causes a replacement in the target.
410 // In this case a LineString replaces the Point because both are in
411 // single-value substitution group: Placemark's Geometry.
TEST_F(MergeTest,TestMergeSubstitutionGroup)412 TEST_F(MergeTest, TestMergeSubstitutionGroup) {
413 target_placemark_->set_geometry(point_);
414 source_placemark_->set_geometry(linestring_);
415 MergeElements(source_placemark_, target_placemark_);
416 ASSERT_EQ(kmldom::Type_LineString,
417 target_placemark_->get_geometry()->Type());
418 }
419
420 // This verifies that the merge does not introduce any extraneous elements.
TEST_F(MergeTest,TestMergeFieldsSerialize)421 TEST_F(MergeTest, TestMergeFieldsSerialize) {
422 kmldom::ListStylePtr liststyle = factory_->CreateListStyle();
423 liststyle->set_listitemtype(kmldom::LISTITEMTYPE_CHECKHIDECHILDREN);
424 source_style_->set_liststyle(liststyle);
425 MergeElements(source_style_, target_style_);
426 ASSERT_EQ(kmldom::SerializeRaw(target_style_),
427 kmldom::SerializeRaw(source_style_));
428 }
429
430 // An early version of MergeElements did not properly preserve the state of
431 // previously set attributes.
TEST_F(MergeTest,TestMergeAttributes)432 TEST_F(MergeTest, TestMergeAttributes) {
433 const string kId("style-id");
434 const string kTargetId("style-target-id");
435 source_style_->set_targetid(kTargetId);
436 target_style_->set_id(kId);
437 MergeElements(source_style_, target_style_);
438 ASSERT_TRUE(target_style_->has_id());
439 ASSERT_EQ(kId, target_style_->get_id());
440 ASSERT_TRUE(target_style_->has_targetid());
441 ASSERT_EQ(kTargetId, target_style_->get_targetid());
442 }
443
444 } // end namespace kmlengine
445