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