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