1 // Copyright 2009, 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 StyleInliner class.
27
28 #include "kml/engine/style_inliner.h"
29 #include "kml/engine/style_inliner_internal.h"
30 #include "gtest/gtest.h"
31 #include "kml/base/file.h"
32
33 // The following define is a convenience for testing inside Google.
34 #ifdef GOOGLE_INTERNAL
35 #include "kml/base/google_internal_test.h"
36 #endif
37
38 #ifndef DATADIR
39 #error *** DATADIR must be defined! ***
40 #endif
41
42 using kmlbase::File;
43 using kmldom::DocumentPtr;
44 using kmldom::ElementPtr;
45 using kmldom::FieldPtr;
46 using kmldom::KmlFactory;
47 using kmldom::PlacemarkPtr;
48 using kmldom::StylePtr;
49 using kmldom::Type_styleUrl;
50 using kmldom::UpdatePtr;
51
52 namespace kmlengine {
53
54 class StyleInlinerTest : public testing::Test {
55 protected:
SetUp()56 virtual void SetUp() {
57 kml_factory_ = KmlFactory::GetFactory();
58 style_inliner_.reset(new StyleInliner);
59 }
60
61 KmlFactory* kml_factory_;
62 boost::scoped_ptr<StyleInliner> style_inliner_;
63 };
64
TEST_F(StyleInlinerTest,CallStyleInlinerMethodsInTypicalUsage)65 TEST_F(StyleInlinerTest, CallStyleInlinerMethodsInTypicalUsage) {
66 // Call the StyleInliner methods involved in parsing this:
67 // <Document>
68 // <Style id="_0"/>
69 // <Placemark>
70 // <styleUrl>#_0</styleUrl>
71 // </Placemark>
72 // </Document>
73 // ...to generate this:
74 const string kExpectedKml(
75 "<Document>\n"
76 " <Placemark>\n"
77 " <StyleMap>\n"
78 " <Pair>\n"
79 " <key>normal</key>\n"
80 " <Style/>\n"
81 " </Pair>\n"
82 " <Pair>\n"
83 " <key>highlight</key>\n"
84 " <Style/>\n"
85 " </Pair>\n"
86 " </StyleMap>\n"
87 " </Placemark>\n"
88 "</Document>\n");
89
90 // 0) Initial conditions.
91 ASSERT_FALSE(style_inliner_->get_document());
92 ASSERT_EQ(static_cast<size_t>(0),
93 style_inliner_->get_shared_styles().size());
94 ASSERT_FALSE(style_inliner_->in_update());
95
96 // 1) <Document>
97 DocumentPtr document = kml_factory_->CreateDocument();
98 ASSERT_TRUE(style_inliner_->NewElement(document));
99 ASSERT_TRUE(style_inliner_->get_document());
100
101 // 2) <Style id="_0">
102 StylePtr style = kml_factory_->CreateStyle();
103 const string kStyleId("_0");
104 style->set_id(kStyleId);
105 ASSERT_TRUE(style_inliner_->NewElement(style));
106
107 // 3) </Style>
108 ASSERT_FALSE(style_inliner_->EndElement(document, style));
109
110 // 4) <Placemark>
111 PlacemarkPtr placemark = kml_factory_->CreatePlacemark();
112 ASSERT_TRUE(style_inliner_->NewElement(placemark));
113
114 // 5) <styleUrl>#_0</styleUrl>
115 FieldPtr styleurl = kml_factory_->CreateFieldById(Type_styleUrl);
116 styleurl->set_char_data(string("#") + kStyleId);
117 ASSERT_TRUE(style_inliner_->NewElement(styleurl));
118 ASSERT_FALSE(style_inliner_->EndElement(placemark, styleurl));
119 // StyleInliner::EndElement() gives the placemark a style selector
120 // representing that which the styleUrl pointed to.
121 ASSERT_TRUE(placemark->has_styleselector());
122 // The local style should have no ids.
123 ASSERT_FALSE(placemark->get_styleselector()->has_id());
124
125 // 6) </Placemark>
126 ASSERT_TRUE(style_inliner_->EndElement(document, placemark));
127 document->add_feature(placemark);
128
129 // Verify the shared style was captured.
130 const SharedStyleMap& shared_styles = style_inliner_->get_shared_styles();
131 ASSERT_EQ(static_cast<size_t>(1), shared_styles.size());
132 ASSERT_EQ(kStyleId, shared_styles.find(kStyleId)->second->get_id());
133
134 // Serialize the document and verify a proper overall result.
135 ASSERT_EQ(kExpectedKml, SerializePretty(document));
136 }
137
TEST_F(StyleInlinerTest,VerifyRemoteStyleUrlNotInlined)138 TEST_F(StyleInlinerTest, VerifyRemoteStyleUrlNotInlined) {
139 PlacemarkPtr placemark = kml_factory_->CreatePlacemark();
140 FieldPtr styleurl = kml_factory_->CreateFieldById(Type_styleUrl);
141 styleurl->set_char_data("http://example.com/style.kml#cool-style");
142 // true == "proceed to add this styleurl to its feature"
143 ASSERT_TRUE(style_inliner_->EndElement(placemark, styleurl));
144
145 // true == "proceed to add this styleurl to its feature"
146 styleurl->set_char_data("#non-existent-local-reference");
147 ASSERT_TRUE(style_inliner_->EndElement(placemark, styleurl));
148 }
149
TEST_F(StyleInlinerTest,VerifyNoInliningWithinUpdate)150 TEST_F(StyleInlinerTest, VerifyNoInliningWithinUpdate) {
151 // <Update>
152 UpdatePtr update = kml_factory_->CreateUpdate();
153 ASSERT_TRUE(style_inliner_->NewElement(update));
154 ASSERT_TRUE(style_inliner_->in_update());
155
156 // Since this is somewhere inside an <Update> the <Style> will be added
157 // to the <Document> as normal.
158 DocumentPtr document = kml_factory_->CreateDocument();
159 StylePtr style = kml_factory_->CreateStyle();
160 const string kStyleId("style0");
161 style->set_id(kStyleId);
162 ASSERT_TRUE(style_inliner_->EndElement(document, style));
163
164 // </Update>
165 ASSERT_TRUE(style_inliner_->AddChild(kml_factory_->CreateNetworkLinkControl(),
166 update));
167 ASSERT_FALSE(style_inliner_->in_update());
168
169 // Now that we're no longer within an <Update> a <Style> with an id _will_
170 // be captured into the SharedStyleMap and _not_ parented to a <Document>.
171 ASSERT_FALSE(style_inliner_->EndElement(document, style));
172 ASSERT_EQ(static_cast<size_t>(1), style_inliner_->get_shared_styles().size());
173 SharedStyleMap::const_iterator find =
174 style_inliner_->get_shared_styles().find(kStyleId);
175 ASSERT_EQ(kStyleId, find->second->get_id());
176 }
177
178 // This is a utility function to read a file relative to the testdata directory.
ReadDataDirFileToString(const string & dir,const string & filename,string * content)179 static bool ReadDataDirFileToString(const string& dir,
180 const string& filename,
181 string* content) {
182 const string pathname =
183 File::JoinPaths(File::JoinPaths(string(DATADIR), dir), filename);
184 return File::ReadFileToString(pathname, content);
185 }
186
187 // This is a table of test cases of input files with shared styles checked
188 // against the expected inline result.
189 static const struct {
190 const char* source_subdir_;
191 const char* source_file_;
192 const char* check_subdir_;
193 const char* check_file_;
194 } kTestCases [] = {
195 { "kml", "kmlsamples.kml", "style", "kmlsamples-inline-style-check.kml" },
196 { "kml", "ge-point.kml", "style", "ge-point-inline-style-check.kml" }
197 };
198
TEST_F(StyleInlinerTest,TestFiles)199 TEST_F(StyleInlinerTest, TestFiles) {
200 const size_t size = sizeof(kTestCases)/sizeof(kTestCases[0]);
201 for (size_t i = 0; i < size; ++i) {
202 string input;
203 ASSERT_TRUE(ReadDataDirFileToString(kTestCases[i].source_subdir_,
204 kTestCases[i].source_file_, &input))
205 << kTestCases[i].source_subdir_ << "/" << kTestCases[i].source_file_;
206 string errors;
207
208 // Call the function under test.
209 ElementPtr root = InlineStyles(input, &errors);
210 ASSERT_TRUE(root);
211 ASSERT_TRUE(errors.empty());
212 string check;
213 ASSERT_TRUE(ReadDataDirFileToString(kTestCases[i].check_subdir_,
214 kTestCases[i].check_file_, &check))
215 << kTestCases[i].check_subdir_ << "/" << kTestCases[i].check_file_;
216 ASSERT_EQ(check, kmldom::SerializePretty(root));
217 }
218 }
219
220 } // end namespace kmlengine
221