xref: /trunk/main/svl/qa/test_URIHelper.cxx (revision 2958961e)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svl.hxx"
26 
27 #include "sal/config.h"
28 
29 #include <cstddef>
30 
31 #include "com/sun/star/lang/Locale.hpp"
32 #include "com/sun/star/lang/XComponent.hpp"
33 #include "com/sun/star/lang/XMultiComponentFactory.hpp"
34 #include "com/sun/star/lang/XMultiServiceFactory.hpp"
35 #include "com/sun/star/ucb/Command.hpp"
36 #include "com/sun/star/ucb/CommandAbortedException.hpp"
37 #include "com/sun/star/ucb/IllegalIdentifierException.hpp"
38 #include "com/sun/star/ucb/XCommandProcessor.hpp"
39 #include "com/sun/star/ucb/XContent.hpp"
40 #include "com/sun/star/ucb/XContentIdentifier.hpp"
41 #include "com/sun/star/ucb/XContentProvider.hpp"
42 #include "com/sun/star/ucb/XContentProviderManager.hpp"
43 #include "com/sun/star/uno/Any.hxx"
44 #include "com/sun/star/uno/Exception.hpp"
45 #include "com/sun/star/uno/Reference.hxx"
46 #include "com/sun/star/uno/RuntimeException.hpp"
47 #include "com/sun/star/uno/Sequence.hxx"
48 #include "com/sun/star/uno/XComponentContext.hpp"
49 #include "com/sun/star/uri/XUriReference.hpp"
50 #include "cppuhelper/bootstrap.hxx"
51 #include "cppuhelper/implbase1.hxx"
52 #include "cppuhelper/implbase2.hxx"
53 #include "osl/diagnose.h"
54 #include "rtl/strbuf.hxx"
55 #include "rtl/string.h"
56 #include "rtl/string.hxx"
57 #include "rtl/textenc.h"
58 #include "rtl/ustring.h"
59 #include "rtl/ustring.hxx"
60 #include "sal/types.h"
61 #include "tools/solar.h"
62 #include "unotools/charclass.hxx"
63 #include "gtest/gtest.h"
64 
65 #include "urihelper.hxx"
66 
67 // FIXME:
68 #define RUN_OLD_FAILING_TESTS 0
69 
70 // This test needs a UNO component context that supports various services (the
71 // UCB, an UriReferenceFactory, ...), so it is best executed within an OOo
72 // installation.
73 
74 namespace com { namespace sun { namespace star { namespace ucb {
75     class XCommandEnvironment;
76     class XContentEventListener;
77 } } } }
78 
79 namespace {
80 
81 namespace css = com::sun::star;
82 
83 // This class only implements that subset of functionality of a proper
84 // css::ucb::Content that is known to be needed here:
85 class Content:
86     public cppu::WeakImplHelper2<
87         css::ucb::XContent, css::ucb::XCommandProcessor >
88 {
89 public:
90     explicit Content(
91         css::uno::Reference< css::ucb::XContentIdentifier > const & identifier);
92 
93     virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL
getIdentifier()94     getIdentifier() throw (css::uno::RuntimeException) {
95         return m_identifier;
96     }
97 
getContentType()98     virtual rtl::OUString SAL_CALL getContentType()
99         throw (css::uno::RuntimeException)
100     {
101         return rtl::OUString();
102     }
103 
addContentEventListener(css::uno::Reference<css::ucb::XContentEventListener> const &)104     virtual void SAL_CALL addContentEventListener(
105         css::uno::Reference< css::ucb::XContentEventListener > const &)
106         throw (css::uno::RuntimeException)
107     {}
108 
removeContentEventListener(css::uno::Reference<css::ucb::XContentEventListener> const &)109     virtual void SAL_CALL removeContentEventListener(
110         css::uno::Reference< css::ucb::XContentEventListener > const &)
111         throw (css::uno::RuntimeException)
112     {}
113 
createCommandIdentifier()114     virtual sal_Int32 SAL_CALL createCommandIdentifier()
115         throw (css::uno::RuntimeException)
116     {
117         return 0;
118     }
119 
120     virtual css::uno::Any SAL_CALL execute(
121         css::ucb::Command const & command, sal_Int32 commandId,
122         css::uno::Reference< css::ucb::XCommandEnvironment > const &)
123         throw (
124             css::uno::Exception, css::ucb::CommandAbortedException,
125             css::uno::RuntimeException);
126 
abort(sal_Int32)127     virtual void SAL_CALL abort(sal_Int32) throw (css::uno::RuntimeException) {}
128 
129 private:
130     static char const m_prefix[];
131 
132     css::uno::Reference< css::ucb::XContentIdentifier > m_identifier;
133 };
134 
135 char const Content::m_prefix[] = "test:";
136 
Content(css::uno::Reference<css::ucb::XContentIdentifier> const & identifier)137 Content::Content(
138     css::uno::Reference< css::ucb::XContentIdentifier > const & identifier):
139     m_identifier(identifier)
140 {
141     OSL_ASSERT(m_identifier.is());
142     rtl::OUString uri(m_identifier->getContentIdentifier());
143     if (!uri.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM(m_prefix))
144         || uri.indexOf('#', RTL_CONSTASCII_LENGTH(m_prefix)) != -1)
145     {
146         throw css::ucb::IllegalIdentifierException();
147     }
148 }
149 
execute(css::ucb::Command const & command,sal_Int32,css::uno::Reference<css::ucb::XCommandEnvironment> const &)150 css::uno::Any Content::execute(
151     css::ucb::Command const & command, sal_Int32,
152     css::uno::Reference< css::ucb::XCommandEnvironment > const &)
153     throw (
154         css::uno::Exception, css::ucb::CommandAbortedException,
155         css::uno::RuntimeException)
156 {
157     if (!command.Name.equalsAsciiL(
158             RTL_CONSTASCII_STRINGPARAM("getCasePreservingURL")))
159     {
160         throw css::uno::RuntimeException();
161     }
162     // If any non-empty segment starts with anything but '0', '1', or '2', fail;
163     // otherwise, if the last non-empty segment starts with '1', add a final
164     // slash, and if the last non-empty segment starts with '2', remove a final
165     // slash (if any); also, turn the given uri into all-lowercase:
166     rtl::OUString uri(m_identifier->getContentIdentifier());
167     sal_Unicode c = '0';
168     for (sal_Int32 i = RTL_CONSTASCII_LENGTH(m_prefix); i != -1;) {
169         rtl::OUString seg(uri.getToken(0, '/', i));
170         if (seg.getLength() > 0) {
171             c = seg[0];
172             if (c < '0' || c > '2') {
173                 throw css::uno::Exception();
174             }
175         }
176     }
177     switch (c) {
178     case '1':
179         uri += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/"));
180         break;
181     case '2':
182         if (uri.getLength() > 0 && uri[uri.getLength() - 1] == '/') {
183             uri = uri.copy(0, uri.getLength() -1);
184         }
185         break;
186     }
187     return css::uno::makeAny(uri.toAsciiLowerCase());
188 }
189 
190 class Provider: public cppu::WeakImplHelper1< css::ucb::XContentProvider > {
191 public:
queryContent(css::uno::Reference<css::ucb::XContentIdentifier> const & identifier)192     virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent(
193         css::uno::Reference< css::ucb::XContentIdentifier > const & identifier)
194         throw (css::ucb::IllegalIdentifierException, css::uno::RuntimeException)
195     {
196         return new Content(identifier);
197     }
198 
compareContentIds(css::uno::Reference<css::ucb::XContentIdentifier> const & id1,css::uno::Reference<css::ucb::XContentIdentifier> const & id2)199     virtual sal_Int32 SAL_CALL compareContentIds(
200         css::uno::Reference< css::ucb::XContentIdentifier > const & id1,
201         css::uno::Reference< css::ucb::XContentIdentifier > const & id2)
202         throw (css::uno::RuntimeException)
203     {
204         OSL_ASSERT(id1.is() && id2.is());
205         return
206             id1->getContentIdentifier().compareTo(id2->getContentIdentifier());
207     }
208 };
209 
210 class Test: public ::testing::Test {
211 public:
212     virtual void SetUp();
213 
214     void finish();
215 
216 protected:
217     static css::uno::Reference< css::uno::XComponentContext > m_context;
218 };
219 
SetUp()220 void Test::SetUp() {
221     // For whatever reason, on W32 it does not work to create/destroy a fresh
222     // component context for each test in Test::setUp/tearDown; therefore, a
223     // single component context is used for all tests and destroyed in the last
224     // pseudo-test "finish":
225     if (!m_context.is()) {
226         m_context = cppu::defaultBootstrap_InitialComponentContext();
227     }
228 }
229 
finish()230 void Test::finish() {
231     css::uno::Reference< css::lang::XComponent >(
232         m_context, css::uno::UNO_QUERY_THROW)->dispose();
233 }
234 
235 #if RUN_OLD_FAILING_TESTS
236 
TEST_F(Test,testNormalizedMakeRelative)237 TEST_F(Test, testNormalizedMakeRelative) {
238     css::uno::Sequence< css::uno::Any > args(2);
239     args[0] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Local"));
240     args[1] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Office"));
241     css::uno::Reference< css::ucb::XContentProviderManager >(
242         (css::uno::Reference< css::lang::XMultiComponentFactory >(
243             m_context->getServiceManager(), css::uno::UNO_QUERY_THROW)->
244          createInstanceWithArgumentsAndContext(
245              rtl::OUString(
246                  RTL_CONSTASCII_USTRINGPARAM(
247                      "com.sun.star.ucb.UniversalContentBroker")),
248              args, m_context)),
249         css::uno::UNO_QUERY_THROW)->registerContentProvider(
250             new Provider, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("test")),
251             true);
252     struct Test {
253         char const * base;
254         char const * absolute;
255         char const * relative;
256     };
257     static Test const tests[] = {
258         { "hierarchical:/", "mailto:def@a.b.c.", "mailto:def@a.b.c." },
259         { "hierarchical:/", "a/b/c", "a/b/c" },
260         { "hierarchical:/a", "hierarchical:/a/b/c?d#e", "/a/b/c?d#e" },
261         { "hierarchical:/a/", "hierarchical:/a/b/c?d#e", "b/c?d#e" },
262         { "test:/0/0/a", "test:/0/b", "../b" },
263         { "test:/1/1/a", "test:/1/b", "../b" },
264         { "test:/2/2//a", "test:/2/b", "../../b" },
265         { "test:/0a/b", "test:/0A/c#f", "c#f" },
266         { "file:///usr/bin/nonex1/nonex2",
267           "file:///usr/bin/nonex1/nonex3/nonex4", "nonex3/nonex4" },
268         { "file:///usr/bin/nonex1/nonex2#fragmentA",
269           "file:///usr/bin/nonex1/nonex3/nonex4#fragmentB",
270           "nonex3/nonex4#fragmentB" },
271         { "file:///usr/nonex1/nonex2", "file:///usr/nonex3", "../nonex3" },
272         { "file:///c:/windows/nonex1", "file:///c:/nonex2", "../nonex2" },
273 #if defined WNT
274         { "file:///c:/nonex1/nonex2", "file:///C:/nonex1/nonex3/nonex4",
275           "nonex3/nonex4" }
276 #endif
277     };
278     for (std::size_t i = 0; i < sizeof tests / sizeof tests[0]; ++i) {
279         css::uno::Reference< css::uri::XUriReference > ref(
280             URIHelper::normalizedMakeRelative(
281                 m_context, rtl::OUString::createFromAscii(tests[i].base),
282                 rtl::OUString::createFromAscii(tests[i].absolute)));
283         bool ok = tests[i].relative == 0
284             ? !ref.is()
285             : ref.is() && ref->getUriReference().equalsAscii(tests[i].relative);
286         rtl::OString msg;
287         if (!ok) {
288             rtl::OStringBuffer buf;
289             buf.append('<');
290             buf.append(tests[i].base);
291             buf.append(RTL_CONSTASCII_STRINGPARAM(">, <"));
292             buf.append(tests[i].absolute);
293             buf.append(RTL_CONSTASCII_STRINGPARAM(">: "));
294             if (ref.is()) {
295                 buf.append('<');
296                 buf.append(
297                     rtl::OUStringToOString(
298                         ref->getUriReference(), RTL_TEXTENCODING_UTF8));
299                 buf.append('>');
300             } else {
301                 buf.append(RTL_CONSTASCII_STRINGPARAM("none"));
302             }
303             buf.append(RTL_CONSTASCII_STRINGPARAM(" instead of "));
304             if (tests[i].relative == 0) {
305                 buf.append(RTL_CONSTASCII_STRINGPARAM("none"));
306             } else {
307                 buf.append('<');
308                 buf.append(tests[i].relative);
309                 buf.append('>');
310             }
311             msg = buf.makeStringAndClear();
312         }
313         ASSERT_TRUE(ok) << msg.getStr();
314     }
315 }
316 
317 #endif
318 
TEST_F(Test,testFindFirstURLInText)319 TEST_F(Test, testFindFirstURLInText) {
320     struct Test {
321         char const * input;
322         char const * result;
323         xub_StrLen begin;
324         xub_StrLen end;
325     };
326     static Test const tests[] = {
327         { "...ftp://bla.bla.bla/blubber/...",
328           "ftp://bla.bla.bla/blubber/", 3, 29 },
329         { "..\\ftp://bla.bla.bla/blubber/...", 0, 0, 0 },
330         { "..\\ftp:\\\\bla.bla.bla\\blubber/...",
331           "file://bla.bla.bla/blubber%2F", 7, 29 },
332         { "http://sun.com", "http://sun.com/", 0, 14 },
333         { "http://sun.com/", "http://sun.com/", 0, 15 },
334         { "http://www.xerox.com@www.pcworld.com/go/3990332.htm", 0, 0, 0 },
335         { "ftp://www.xerox.com@www.pcworld.com/go/3990332.htm",
336           "ftp://www.xerox.com@www.pcworld.com/go/3990332.htm", 0, 50 },
337         { "Version.1.2.3", 0, 0, 0 },
338         { "Version:1.2.3", 0, 0, 0 },
339         { "a.b.c", 0, 0, 0 },
340         { "file:///a|...", "file:///a:", 0, 10 },
341         { "file:///a||...", "file:///a%7C%7C", 0, 11 },
342         { "file:///a|/bc#...", "file:///a:/bc", 0, 13 },
343         { "file:///a|/bc#de...", "file:///a:/bc#de", 0, 16 },
344         { "abc.def.ghi,ftp.xxx.yyy/zzz...", "ftp://ftp.xxx.yyy/zzz", 12, 27 },
345         { "abc.def.ghi,Ftp.xxx.yyy/zzz...", "ftp://Ftp.xxx.yyy/zzz", 12, 27 },
346         { "abc.def.ghi,www.xxx.yyy...", "http://www.xxx.yyy/", 12, 23 },
347         { "abc.def.ghi,wwww.xxx.yyy...", 0, 0, 0 },
348         { "abc.def.ghi,wWW.xxx.yyy...", "http://wWW.xxx.yyy/", 12, 23 },
349         { "Bla {mailto.me@abc.def.g.h.i}...",
350           "mailto:%7Bmailto.me@abc.def.g.h.i", 4, 28 },
351         { "abc@def@ghi", 0, 0, 0 },
352         { "lala@sun.com", "mailto:lala@sun.com", 0, 12 },
353         { "1lala@sun.com", "mailto:1lala@sun.com", 0, 13 },
354         { "aaa_bbb@xxx.yy", "mailto:aaa_bbb@xxx.yy", 0, 14 },
355         { "{a:\\bla/bla/bla...}", "file:///a:/bla/bla/bla", 1, 15 },
356         { "#b:/c/d#e#f#", "file:///b:/c/d", 1, 7 },
357         { "a:/", "file:///a:/", 0, 3 },
358         { ".component:", 0, 0, 0 },
359         { ".uno:", 0, 0, 0 },
360         { "cid:", 0, 0, 0 },
361         { "data:", 0, 0, 0 },
362         { "db:", 0, 0, 0 },
363         { "file:", 0, 0, 0 },
364         { "ftp:", 0, 0, 0 },
365         { "http:", 0, 0, 0 },
366         { "https:", 0, 0, 0 },
367         { "imap:", 0, 0, 0 },
368         { "javascript:", 0, 0, 0 },
369         { "ldap:", 0, 0, 0 },
370         { "macro:", 0, 0, 0 },
371         { "mailto:", 0, 0, 0 },
372         { "news:", 0, 0, 0 },
373         { "out:", 0, 0, 0 },
374         { "pop3:", 0, 0, 0 },
375         { "private:", 0, 0, 0 },
376         { "slot:", 0, 0, 0 },
377         { "staroffice.component:", 0, 0, 0 },
378         { "staroffice.db:", 0, 0, 0 },
379         { "staroffice.factory:", 0, 0, 0 },
380         { "staroffice.helpid:", 0, 0, 0 },
381         { "staroffice.java:", 0, 0, 0 },
382         { "staroffice.macro:", 0, 0, 0 },
383         { "staroffice.out:", 0, 0, 0 },
384         { "staroffice.pop3:", 0, 0, 0 },
385         { "staroffice.private:", 0, 0, 0 },
386         { "staroffice.searchfolder:", 0, 0, 0 },
387         { "staroffice.slot:", 0, 0, 0 },
388         { "staroffice.trashcan:", 0, 0, 0 },
389         { "staroffice.uno:", 0, 0, 0 },
390         { "staroffice.vim:", 0, 0, 0 },
391         { "staroffice:", 0, 0, 0 },
392         { "vim:", 0, 0, 0 },
393         { "vnd.sun.star.cmd:", 0, 0, 0 },
394         { "vnd.sun.star.help:", 0, 0, 0 },
395         { "vnd.sun.star.hier:", 0, 0, 0 },
396         { "vnd.sun.star.odma:", 0, 0, 0 },
397         { "vnd.sun.star.pkg:", 0, 0, 0 },
398         { "vnd.sun.star.script:", 0, 0, 0 },
399         { "vnd.sun.star.webdav:", 0, 0, 0 },
400         { "vnd.sun.star.wfs:", 0, 0, 0 },
401         { "generic:path", 0, 0, 0 },
402         { "wfs:", 0, 0, 0 }
403     };
404     CharClass charClass(
405         css::uno::Reference< css::lang::XMultiServiceFactory >(
406             m_context->getServiceManager(), css::uno::UNO_QUERY_THROW),
407         com::sun::star::lang::Locale(
408             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en")),
409             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("US")), rtl::OUString()));
410     for (std::size_t i = 0; i < sizeof tests / sizeof tests[0]; ++i) {
411         rtl::OUString input(rtl::OUString::createFromAscii(tests[i].input));
412         xub_StrLen begin = 0;
413         xub_StrLen end = static_cast< xub_StrLen >(input.getLength());
414         rtl::OUString result(
415             URIHelper::FindFirstURLInText(input, begin, end, charClass));
416         bool ok = tests[i].result == 0
417             ? (result.getLength() == 0 && begin == input.getLength()
418                && end == input.getLength())
419             : (result.equalsAscii(tests[i].result) && begin == tests[i].begin
420                && end == tests[i].end);
421         rtl::OString msg;
422         if (!ok) {
423             rtl::OStringBuffer buf;
424             buf.append('"');
425             buf.append(tests[i].input);
426             buf.append(RTL_CONSTASCII_STRINGPARAM("\" -> "));
427             buf.append(tests[i].result == 0 ? "none" : tests[i].result);
428             buf.append(RTL_CONSTASCII_STRINGPARAM(" ("));
429             buf.append(static_cast< sal_Int32 >(tests[i].begin));
430             buf.append(RTL_CONSTASCII_STRINGPARAM(", "));
431             buf.append(static_cast< sal_Int32 >(tests[i].end));
432             buf.append(')');
433             buf.append(RTL_CONSTASCII_STRINGPARAM(" != "));
434             buf.append(rtl::OUStringToOString(result, RTL_TEXTENCODING_UTF8));
435             buf.append(RTL_CONSTASCII_STRINGPARAM(" ("));
436             buf.append(static_cast< sal_Int32 >(begin));
437             buf.append(RTL_CONSTASCII_STRINGPARAM(", "));
438             buf.append(static_cast< sal_Int32 >(end));
439             buf.append(')');
440             msg = buf.makeStringAndClear();
441         }
442         ASSERT_TRUE(ok) << msg.getStr();
443     }
444 }
445 
446 css::uno::Reference< css::uno::XComponentContext > Test::m_context;
447 
448 
449 }
450 
main(int argc,char ** argv)451 int main(int argc, char **argv)
452 {
453     ::testing::InitGoogleTest(&argc, argv);
454     return RUN_ALL_TESTS();
455 }
456