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 internal UriParser class.
27 // This is more to test the UriParser interface as the uriparser library has
28 // its own internal unit test suite for its functionality (including example
29 // cases from RFC 3986 itself).
30
31 // TODO photooverlay $[x]
32 // TODO ;flyTo ,etc
33
34 #include "kml/base/uri_parser.h"
35 #include "boost/scoped_ptr.hpp"
36 #include "gtest/gtest.h"
37
38 namespace kmlbase {
39
40 // This test fixture is for the unit test cases of the UriParser class.
41 class UriParserTest : public testing::Test {
42 protected:
43 boost::scoped_ptr<UriParser> uri_parser_;
44 void VerifyUriResolution(const char* base, const char* relative,
45 const char* want_result);
46 };
47
48 // Verify basic usage of the CreateFromParse() static method.
TEST_F(UriParserTest,TestBasicCreateFromParse)49 TEST_F(UriParserTest, TestBasicCreateFromParse) {
50 // Note: since UriParse does not actually generate any networking traffic
51 // the hostnames and TLDs used here generally do not conform to RFC 2606.
52 const string kUrl("http://host.com/path/to/some.kml#id");
53 uri_parser_.reset(UriParser::CreateFromParse(kUrl.c_str()));
54 ASSERT_TRUE(uri_parser_.get());
55 string url;
56 ASSERT_TRUE(uri_parser_->ToString(&url));
57 ASSERT_EQ(kUrl, url);
58 }
59
60 // Verify basic usage of the CreateResolvedUri() static method.
TEST_F(UriParserTest,TestBasicCreateResolvedUri)61 TEST_F(UriParserTest, TestBasicCreateResolvedUri) {
62 const string kHost("http://hi.there.com/");
63 const string kPath("blah.kml");
64 const string kBase = kHost + kPath;
65 const string kRelative("images/pretty.jpg");
66 uri_parser_.reset(UriParser::CreateResolvedUri(kBase.c_str(),
67 kRelative.c_str()));
68 ASSERT_TRUE(uri_parser_.get());
69 string url;
70 ASSERT_TRUE(uri_parser_->ToString(&url));
71 ASSERT_EQ(kHost + kRelative, url);
72 }
73
74 // Verify basic usage of the Parse() method.
TEST_F(UriParserTest,TestBasicParse)75 TEST_F(UriParserTest, TestBasicParse) {
76 const string kUrl("this/is/a/relative/url.kmz/some/file.kml#id");
77 uri_parser_.reset(new UriParser);
78 ASSERT_TRUE(uri_parser_->Parse(kUrl.c_str()));
79 string url;
80 ASSERT_TRUE(uri_parser_->ToString(&url));
81 ASSERT_EQ(kUrl, url);
82 }
83
84 // Verify basic usage of the Normalize() method.
TEST_F(UriParserTest,TestBasicNormalize)85 TEST_F(UriParserTest, TestBasicNormalize) {
86 const string kUrl("this/../a/relative/url.kmz/../file.kml#id");
87 const string kNormalized("a/relative/file.kml#id");
88 uri_parser_.reset(new UriParser);
89 // Parse() does not normalize.
90 ASSERT_TRUE(uri_parser_->Parse(kUrl.c_str()));
91 string url;
92 ASSERT_TRUE(uri_parser_->ToString(&url));
93 ASSERT_EQ(kUrl, url);
94 // Normalize() normalizes.
95 ASSERT_TRUE(uri_parser_->Normalize());
96 ASSERT_TRUE(uri_parser_->ToString(&url));
97 ASSERT_EQ(kNormalized, url);
98 }
99
100 // Verify basic usage of the Resolve() method.
TEST_F(UriParserTest,TestBasicResolve)101 TEST_F(UriParserTest, TestBasicResolve) {
102 const string kBase("http://foo.com/hello/");
103 const string kRelative("../hi");
104 const string kResult("http://foo.com/hi");
105 boost::scoped_ptr<UriParser> base_uri(
106 UriParser::CreateFromParse(kBase.c_str()));
107 boost::scoped_ptr<UriParser> relative_uri(
108 UriParser::CreateFromParse(kRelative.c_str()));
109 uri_parser_.reset(new UriParser);
110 ASSERT_TRUE(uri_parser_->Resolve(*base_uri.get(), *relative_uri.get()));
111 string url;
112 ASSERT_TRUE(uri_parser_->ToString(&url));
113 ASSERT_EQ(kResult, url);
114 }
115
116 // Verify basic usage of the ToString() method.
TEST_F(UriParserTest,TestBasicToString)117 TEST_F(UriParserTest, TestBasicToString) {
118 uri_parser_.reset(new UriParser);
119 ASSERT_FALSE(uri_parser_->ToString(NULL));
120 const string kUrl("a/b/c/d");
121 ASSERT_TRUE(uri_parser_->Parse(kUrl.c_str()));
122 string url;
123 ASSERT_TRUE(uri_parser_->ToString(&url));
124 ASSERT_EQ(kUrl, url);
125 }
126
127 // Verify basic usage of the GetScheme(), GetHost(), GetPort(), GetPath(),
128 // GetQuery(), and GetFragment() methods.
TEST_F(UriParserTest,TestBasicGetComponents)129 TEST_F(UriParserTest, TestBasicGetComponents) {
130 uri_parser_.reset(new UriParser);
131 // Verify NULL uri returns false for all components.
132 ASSERT_FALSE(uri_parser_->GetScheme(NULL));
133 ASSERT_FALSE(uri_parser_->GetHost(NULL));
134 ASSERT_FALSE(uri_parser_->GetPort(NULL));
135 ASSERT_FALSE(uri_parser_->GetPath(NULL));
136 ASSERT_FALSE(uri_parser_->GetQuery(NULL));
137 ASSERT_FALSE(uri_parser_->GetFragment(NULL));
138 // Verify initial state returns false with non-NULL string output arg.
139 string output;
140 ASSERT_FALSE(uri_parser_->GetScheme(&output));
141 ASSERT_TRUE(output.empty());
142 ASSERT_FALSE(uri_parser_->GetHost(&output));
143 ASSERT_TRUE(output.empty());
144 ASSERT_FALSE(uri_parser_->GetPort(&output));
145 ASSERT_TRUE(output.empty());
146 ASSERT_FALSE(uri_parser_->GetPath(&output));
147 ASSERT_TRUE(output.empty());
148 ASSERT_FALSE(uri_parser_->GetQuery(&output));
149 ASSERT_TRUE(output.empty());
150 ASSERT_FALSE(uri_parser_->GetFragment(&output));
151 ASSERT_TRUE(output.empty());
152
153 // Verify a typical URI.
154 const string kScheme("http");
155 const string kHost("www.somehost.com");
156 const string kPath("path/to/some.kml");
157 const string kUrlNoFragment(kScheme + "://" + kHost + "/" + kPath);
158 const string kFragment("id");
159 uri_parser_.reset(UriParser::CreateFromParse(kUrlNoFragment.c_str()));
160 // Verify NULL output string returns proper status of component's existence.
161 ASSERT_TRUE(uri_parser_->GetScheme(NULL));
162 ASSERT_TRUE(uri_parser_->GetHost(NULL));
163 ASSERT_FALSE(uri_parser_->GetPort(NULL));
164 ASSERT_TRUE(uri_parser_->GetPath(NULL));
165 ASSERT_FALSE(uri_parser_->GetQuery(NULL));
166 ASSERT_FALSE(uri_parser_->GetFragment(NULL));
167 // Verify output string gets proper result.
168 ASSERT_TRUE(uri_parser_->GetScheme(&output));
169 ASSERT_EQ(kScheme, output);
170 ASSERT_TRUE(uri_parser_->GetHost(&output));
171 ASSERT_EQ(kHost, output);
172 ASSERT_TRUE(uri_parser_->GetPath(&output));
173 ASSERT_EQ(kPath, output);
174 output.clear();
175 ASSERT_FALSE(uri_parser_->GetFragment(&output));
176 ASSERT_TRUE(output.empty());
177
178 // Verify a URI with fragment.
179 const string kUrlWithFragment(kUrlNoFragment + "#" + kFragment);
180 uri_parser_.reset(UriParser::CreateFromParse(kUrlWithFragment.c_str()));
181 // Verify NULL output string returns proper status of component's existence.
182 ASSERT_TRUE(uri_parser_->GetScheme(NULL));
183 ASSERT_TRUE(uri_parser_->GetHost(NULL));
184 ASSERT_FALSE(uri_parser_->GetPort(NULL));
185 ASSERT_TRUE(uri_parser_->GetPath(NULL));
186 ASSERT_FALSE(uri_parser_->GetQuery(NULL));
187 ASSERT_TRUE(uri_parser_->GetFragment(NULL));
188 // Verify output string gets proper result.
189 ASSERT_TRUE(uri_parser_->GetScheme(&output));
190 ASSERT_EQ(kScheme, output);
191 ASSERT_TRUE(uri_parser_->GetHost(&output));
192 ASSERT_EQ(kHost, output);
193 ASSERT_TRUE(uri_parser_->GetPath(&output));
194 ASSERT_EQ(kPath, output);
195 ASSERT_TRUE(uri_parser_->GetFragment(&output));
196 ASSERT_EQ(kFragment, output);
197 }
198
199 // This is a table of URI resolution test cases. The given result is the
200 // proper resolution of the given base with the given relative URI. A "base"
201 // in the context of KML is the URI of the KML file and the "relative" is
202 // something that might appear in an <href>. The unit test suite within
203 // the uriparser library is intended to cover the broad functionality of
204 // that library against the RFC 3986 standard. These unit test cases are
205 // essentially KML-specific examples which all still follow RFC 3986
206 // (including the ones using KMZ pathnames which is opaque to the RFC 3986
207 // standard).
208 // TODO: This table does NOT contain some special KMZ handling performed by GE
209 // (which may potentially be left out of libkml).
210 static const struct {
211 const char* base;
212 const char* relative;
213 const char* result;
214 } kUriTestCases[] = {
215 // TODO these first NULL result test cases indicate a need for KML-specific
216 // handling likely elsewhere in libkml. The intention is to keep
217 // UriParser true to the uriparser library. That is, while it is not
218 // directly RFC 3986 valid to resolve against a non-absolute path this
219 // _is_ commonplace and valid in KML.
220 {
221 "x/a.kml",
222 "b.kml",
223 NULL // NOT: "x/b.kml"
224 },
225 {
226 "a.kml",
227 "b.kml",
228 NULL // NOT: "b.kml"
229 },
230 { // Note that uriparser requires the base to be absolute (has a scheme).
231 "file://x",
232 "y",
233 "file://x/y"
234 },
235 {
236 "file:///a.kml",
237 "b.kml",
238 "file:///b.kml"
239 },
240 {
241 "file://dir/a.kml",
242 "b.kml",
243 "file://dir/b.kml"
244 },
245 {
246 "http://somehost.net/DIR/path.kml",
247 "/path/starts/with/slash",
248 "http://somehost.net/path/starts/with/slash"
249 },
250 {
251 "http://somehost.net/DIR/path.kml",
252 "path/does/not/with/slash",
253 "http://somehost.net/DIR/path/does/not/with/slash"
254 },
255 {
256 "http://web.gaggle.com/cine/me3b/me3b.kml",
257 "../me3b_daily/00.kmz",
258 "http://web.gaggle.com/cine/me3b_daily/00.kmz"
259 },
260 { // An absolute relative path will just be the result.
261 "http://web.gaggle.com/veho/philly/root/en/philly.kml",
262 "http://web.gaggle.com/veho/philly/philly0/en/mapfinder.kml",
263 "http://web.gaggle.com/veho/philly/philly0/en/mapfinder.kml"
264 },
265 {
266 "http://web.gaggle.com/veho/philly/root/en/philly.kml",
267 "http://web.gaggle.com/veho/philly/philly0/kart/sat/kml/0.kmz",
268 "http://web.gaggle.com/veho/philly/philly0/kart/sat/kml/0.kmz"
269 },
270 {
271 "http://web.gaggle.com/vaer/gnrl/loopy/loopster.kml",
272 "loopster/root.kmz",
273 "http://web.gaggle.com/vaer/gnrl/loopy/loopster/root.kmz"
274 },
275 {
276 "http://web.gaggle.com/cine/me3b_daily/00.kmz",
277 "05.kmz",
278 "http://web.gaggle.com/cine/me3b_daily/05.kmz"
279 },
280 { // Up outside the KMZ to a normal file.
281 "http://web.gaggle.com/veho/philly/philly0/kart/sat/kml/0.kmz",
282 "../imagery/0_10.jpg",
283 "http://web.gaggle.com/veho/philly/philly0/kart/sat/imagery/0_10.jpg"
284 },
285 {
286 "http://web.gaggle.com/veho/philly/philly0/kart/sat/kml/0.kmz",
287 "00.kmz",
288 "http://web.gaggle.com/veho/philly/philly0/kart/sat/kml/00.kmz"
289 },
290 {
291 "http://web.gaggle.com/veho/philly/philly0/kart/sat/kml/0.kmz",
292 "../imagery/0_10.jpg",
293 "http://web.gaggle.com/veho/philly/philly0/kart/sat/imagery/0_10.jpg"
294 },
295 {
296 "http://web.gaggle.com/vaer/gnrl/loopy/loopster/root.kmz/doc.kml",
297 "level00/0.kml",
298 "http://web.gaggle.com/vaer/gnrl/loopy/loopster/root.kmz/level00/0.kml"
299 },
300 {
301 "http://web.gaggle.com/vaer/gnrl/loopy/loopster/root.kmz/doc.kml",
302 "level00/0.kml",
303 "http://web.gaggle.com/vaer/gnrl/loopy/loopster/root.kmz/level00/0.kml"
304 },
305 {
306 "http://web.gaggle.com/vaer/gnrl/loopy/loopster/root.kmz/level00/0.kml",
307 "../level01/02.kml",
308 "http://web.gaggle.com/vaer/gnrl/loopy/loopster/root.kmz/level01/02.kml"
309 },
310 {
311 "http://web.gaggle.com/vaer/gnrl/loopy/loopster/root.kmz/level01/02.kml",
312 "../level02/020.kml",
313 "http://web.gaggle.com/vaer/gnrl/loopy/loopster/root.kmz/level02/020.kml"
314 },
315 { // Relative within the same KMZ.
316 "http://web.gaggle.com/vaer/nimbus-now/nrl/loopy/loopster/root.kmz/level02/"
317 "020.kml",
318 "../level03/0202.kml",
319 "http://web.gaggle.com/vaer/nimbus-now/nrl/loopy/loopster/root.kmz/level03/"
320 "0202.kml"
321 },
322 { // Relative up and over into another KMZ.
323 "http://web.gaggle.com/vaer/nimbus-now/nrl/loopy/loopster/root.kmz/level03/"
324 "0202.kml",
325 "../../images0018.kmz/images/0202.png",
326 "http://web.gaggle.com/vaer/nimbus-now/nrl/loopy/loopster/images0018.kmz/"
327 "images/0202.png"
328 }
329 };
330
331 // This is an internal utility to use UriParser::CreateResolvedUri()
332 // to resolve and verify a given test case.
VerifyUriResolution(const char * base,const char * relative,const char * want_result)333 void UriParserTest::VerifyUriResolution(const char* base, const char* relative,
334 const char* want_result) {
335 string got_result;
336 if (want_result == NULL) { // We're expecting resolution to fail.
337 ASSERT_EQ(NULL, UriParser::CreateResolvedUri(base, relative));
338 return;
339 }
340 UriParser *parser = UriParser::CreateResolvedUri(base, relative);
341 parser->ToString(&got_result);
342 delete parser;
343 parser = NULL;
344
345 ASSERT_EQ(string(want_result), got_result);
346 }
347
348 // Verify all expected URI resolution test cases.
TEST_F(UriParserTest,TestBasicUriResolutionTestCases)349 TEST_F(UriParserTest, TestBasicUriResolutionTestCases) {
350 const size_t size = sizeof(kUriTestCases)/sizeof(kUriTestCases[0]);
351 for (size_t i = 0; i < size; ++i) {
352 VerifyUriResolution(kUriTestCases[i].base,
353 kUriTestCases[i].relative,
354 kUriTestCases[i].result);
355 }
356 }
357
358 static const struct {
359 const char* unix_filename;
360 const char* unix_uri;
361 const char* windows_filename;
362 const char* windows_uri;
363 } kUriFilenameCases[] = {
364 {
365 "/home/libkml/foo.bar",
366 "file:///home/libkml/foo.bar",
367 "C:\\home\\libkml\\foo.bar",
368 "file:///C:/home/libkml/foo.bar"
369 },
370 {
371 "/this/path has/some spaces in/it",
372 "file:///this/path%20has/some%20spaces%20in/it",
373 "C:\\this\\path has\\some spaces in\\it",
374 "file:///C:/this/path%20has/some%20spaces%20in/it"
375 },
376 {
377 "some/relative path/to a.file",
378 "some/relative%20path/to%20a.file",
379 "some\\relative path\\to a.file",
380 "some/relative%20path/to%20a.file"
381 }
382 };
383
TEST_F(UriParserTest,TestUriFilenameConversions)384 TEST_F(UriParserTest, TestUriFilenameConversions) {
385 const size_t size = sizeof(kUriFilenameCases)/sizeof(kUriFilenameCases[0]);
386 for (size_t i = 0; i < size; ++i) {
387 // Unix filename to URI.
388 string unix_uri;
389 ASSERT_TRUE(
390 UriParser::UnixFilenameToUri(kUriFilenameCases[i].unix_filename,
391 &unix_uri));
392 ASSERT_EQ(string(kUriFilenameCases[i].unix_uri), unix_uri);
393 // Windows filename to URI.
394 string windows_uri;
395 ASSERT_TRUE(
396 UriParser::WindowsFilenameToUri(kUriFilenameCases[i].windows_filename,
397 &windows_uri));
398 ASSERT_EQ(string(kUriFilenameCases[i].windows_uri), windows_uri);
399 // URI to unix filename.
400 string unix_filename;
401 ASSERT_TRUE(UriParser::UriToUnixFilename(kUriFilenameCases[i].unix_uri,
402 &unix_filename));
403 ASSERT_EQ(string(kUriFilenameCases[i].unix_filename), unix_filename);
404 // URI to windows filename.
405 string windows_filename;
406 ASSERT_TRUE(
407 UriParser::UriToWindowsFilename(kUriFilenameCases[i].windows_uri,
408 &windows_filename));
409 ASSERT_EQ(string(kUriFilenameCases[i].windows_filename),
410 windows_filename);
411 }
412 }
413
TEST_F(UriParserTest,TestUriToFilename)414 TEST_F(UriParserTest, TestUriToFilename) {
415 // This simply tests that the ifdef works as expected.
416 string uri;
417 string expected_filename;
418 #ifdef WIN32
419 uri = "file:///C:/home/libkml/foo.bar";
420 expected_filename = "C:\\home\\libkml\\foo.bar";
421 #else
422 uri = "file:///home/libkml/foo.bar";
423 expected_filename = "/home/libkml/foo.bar";
424 #endif
425 string filename;
426 ASSERT_TRUE(UriParser::UriToFilename(uri, &filename));
427 ASSERT_EQ(expected_filename, filename);
428 }
429
TEST_F(UriParserTest,TestFilenameToUri)430 TEST_F(UriParserTest, TestFilenameToUri) {
431 // This simply tests that the ifdef works as expected.
432 string filename;
433 string expected_uri;
434 #ifdef WIN32
435 filename = "C:\\home\\libkml\\foo.bar";
436 expected_uri = "file:///C:/home/libkml/foo.bar";
437 #else
438 filename = "/home/libkml/foo.bar";
439 expected_uri = "file:///home/libkml/foo.bar";
440 #endif
441 string uri;
442 ASSERT_TRUE(UriParser::FilenameToUri(filename, &uri));
443 ASSERT_EQ(expected_uri, uri);
444 }
445
446 } // end namespace kmlbase
447