/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_svl.hxx" #include "sal/config.h" #include "sal/precppunit.hxx" #include #include #include #include #include #include #include "com/sun/star/lang/Locale.hpp" #include "com/sun/star/lang/XComponent.hpp" #include "com/sun/star/lang/XMultiComponentFactory.hpp" #include "com/sun/star/lang/XMultiServiceFactory.hpp" #include "com/sun/star/ucb/Command.hpp" #include "com/sun/star/ucb/CommandAbortedException.hpp" #include "com/sun/star/ucb/IllegalIdentifierException.hpp" #include "com/sun/star/ucb/XCommandProcessor.hpp" #include "com/sun/star/ucb/XContent.hpp" #include "com/sun/star/ucb/XContentIdentifier.hpp" #include "com/sun/star/ucb/XContentProvider.hpp" #include "com/sun/star/ucb/XContentProviderManager.hpp" #include "com/sun/star/uno/Any.hxx" #include "com/sun/star/uno/Exception.hpp" #include "com/sun/star/uno/Reference.hxx" #include "com/sun/star/uno/RuntimeException.hpp" #include "com/sun/star/uno/Sequence.hxx" #include "com/sun/star/uno/XComponentContext.hpp" #include "com/sun/star/uri/XUriReference.hpp" #include "cppuhelper/bootstrap.hxx" #include "cppuhelper/implbase1.hxx" #include "cppuhelper/implbase2.hxx" #include "osl/diagnose.h" #include "rtl/strbuf.hxx" #include "rtl/string.h" #include "rtl/string.hxx" #include "rtl/textenc.h" #include "rtl/ustring.h" #include "rtl/ustring.hxx" #include "sal/types.h" #include #include "tools/solar.h" #include "unotools/charclass.hxx" #include "urihelper.hxx" // This test needs a UNO component context that supports various services (the // UCB, an UriReferenceFactory, ...), so it is best executed within an OOo // installation. namespace com { namespace sun { namespace star { namespace ucb { class XCommandEnvironment; class XContentEventListener; } } } } namespace { namespace css = com::sun::star; // This class only implements that subset of functionality of a proper // css::ucb::Content that is known to be needed here: class Content: public cppu::WeakImplHelper2< css::ucb::XContent, css::ucb::XCommandProcessor > { public: explicit Content( css::uno::Reference< css::ucb::XContentIdentifier > const & identifier); virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL getIdentifier() throw (css::uno::RuntimeException) { return m_identifier; } virtual rtl::OUString SAL_CALL getContentType() throw (css::uno::RuntimeException) { return rtl::OUString(); } virtual void SAL_CALL addContentEventListener( css::uno::Reference< css::ucb::XContentEventListener > const &) throw (css::uno::RuntimeException) {} virtual void SAL_CALL removeContentEventListener( css::uno::Reference< css::ucb::XContentEventListener > const &) throw (css::uno::RuntimeException) {} virtual sal_Int32 SAL_CALL createCommandIdentifier() throw (css::uno::RuntimeException) { return 0; } virtual css::uno::Any SAL_CALL execute( css::ucb::Command const & command, sal_Int32 commandId, css::uno::Reference< css::ucb::XCommandEnvironment > const &) throw ( css::uno::Exception, css::ucb::CommandAbortedException, css::uno::RuntimeException); virtual void SAL_CALL abort(sal_Int32) throw (css::uno::RuntimeException) {} private: static char const m_prefix[]; css::uno::Reference< css::ucb::XContentIdentifier > m_identifier; }; char const Content::m_prefix[] = "test:"; Content::Content( css::uno::Reference< css::ucb::XContentIdentifier > const & identifier): m_identifier(identifier) { OSL_ASSERT(m_identifier.is()); rtl::OUString uri(m_identifier->getContentIdentifier()); if (!uri.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM(m_prefix)) || uri.indexOf('#', RTL_CONSTASCII_LENGTH(m_prefix)) != -1) { throw css::ucb::IllegalIdentifierException(); } } css::uno::Any Content::execute( css::ucb::Command const & command, sal_Int32, css::uno::Reference< css::ucb::XCommandEnvironment > const &) throw ( css::uno::Exception, css::ucb::CommandAbortedException, css::uno::RuntimeException) { if (!command.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("getCasePreservingURL"))) { throw css::uno::RuntimeException(); } // If any non-empty segment starts with anything but '0', '1', or '2', fail; // otherwise, if the last non-empty segment starts with '1', add a final // slash, and if the last non-empty segment starts with '2', remove a final // slash (if any); also, turn the given uri into all-lowercase: rtl::OUString uri(m_identifier->getContentIdentifier()); sal_Unicode c = '0'; for (sal_Int32 i = RTL_CONSTASCII_LENGTH(m_prefix); i != -1;) { rtl::OUString seg(uri.getToken(0, '/', i)); if (seg.getLength() > 0) { c = seg[0]; if (c < '0' || c > '2') { throw css::uno::Exception(); } } } switch (c) { case '1': uri += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/")); break; case '2': if (uri.getLength() > 0 && uri[uri.getLength() - 1] == '/') { uri = uri.copy(0, uri.getLength() -1); } break; } return css::uno::makeAny(uri.toAsciiLowerCase()); } class Provider: public cppu::WeakImplHelper1< css::ucb::XContentProvider > { public: virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent( css::uno::Reference< css::ucb::XContentIdentifier > const & identifier) throw (css::ucb::IllegalIdentifierException, css::uno::RuntimeException) { return new Content(identifier); } virtual sal_Int32 SAL_CALL compareContentIds( css::uno::Reference< css::ucb::XContentIdentifier > const & id1, css::uno::Reference< css::ucb::XContentIdentifier > const & id2) throw (css::uno::RuntimeException) { OSL_ASSERT(id1.is() && id2.is()); return id1->getContentIdentifier().compareTo(id2->getContentIdentifier()); } }; class Test: public CppUnit::TestFixture { public: virtual void setUp(); void finish(); void testNormalizedMakeRelative(); void testFindFirstURLInText(); CPPUNIT_TEST_SUITE(Test); CPPUNIT_TEST(testNormalizedMakeRelative); CPPUNIT_TEST(testFindFirstURLInText); CPPUNIT_TEST(finish); CPPUNIT_TEST_SUITE_END(); private: static css::uno::Reference< css::uno::XComponentContext > m_context; }; void Test::setUp() { // For whatever reason, on W32 it does not work to create/destroy a fresh // component context for each test in Test::setUp/tearDown; therefore, a // single component context is used for all tests and destroyed in the last // pseudo-test "finish": if (!m_context.is()) { m_context = cppu::defaultBootstrap_InitialComponentContext(); } } void Test::finish() { css::uno::Reference< css::lang::XComponent >( m_context, css::uno::UNO_QUERY_THROW)->dispose(); } void Test::testNormalizedMakeRelative() { css::uno::Sequence< css::uno::Any > args(2); args[0] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Local")); args[1] <<= rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Office")); css::uno::Reference< css::ucb::XContentProviderManager >( (css::uno::Reference< css::lang::XMultiComponentFactory >( m_context->getServiceManager(), css::uno::UNO_QUERY_THROW)-> createInstanceWithArgumentsAndContext( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ucb.UniversalContentBroker")), args, m_context)), css::uno::UNO_QUERY_THROW)->registerContentProvider( new Provider, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("test")), true); struct Test { char const * base; char const * absolute; char const * relative; }; static Test const tests[] = { { "hierarchical:/", "mailto:def@a.b.c.", "mailto:def@a.b.c." }, { "hierarchical:/", "a/b/c", "a/b/c" }, { "hierarchical:/a", "hierarchical:/a/b/c?d#e", "/a/b/c?d#e" }, { "hierarchical:/a/", "hierarchical:/a/b/c?d#e", "b/c?d#e" }, { "test:/0/0/a", "test:/0/b", "../b" }, { "test:/1/1/a", "test:/1/b", "../b" }, { "test:/2/2//a", "test:/2/b", "../../b" }, { "test:/0a/b", "test:/0A/c#f", "c#f" }, { "file:///usr/bin/nonex1/nonex2", "file:///usr/bin/nonex1/nonex3/nonex4", "nonex3/nonex4" }, { "file:///usr/bin/nonex1/nonex2#fragmentA", "file:///usr/bin/nonex1/nonex3/nonex4#fragmentB", "nonex3/nonex4#fragmentB" }, { "file:///usr/nonex1/nonex2", "file:///usr/nonex3", "../nonex3" }, { "file:///c:/windows/nonex1", "file:///c:/nonex2", "../nonex2" }, #if defined WNT { "file:///c:/nonex1/nonex2", "file:///C:/nonex1/nonex3/nonex4", "nonex3/nonex4" } #endif }; for (std::size_t i = 0; i < SAL_N_ELEMENTS(tests); ++i) { css::uno::Reference< css::uri::XUriReference > ref( URIHelper::normalizedMakeRelative( m_context, rtl::OUString::createFromAscii(tests[i].base), rtl::OUString::createFromAscii(tests[i].absolute))); bool ok = tests[i].relative == 0 ? !ref.is() : ref.is() && ref->getUriReference().equalsAscii(tests[i].relative); rtl::OString msg; if (!ok) { rtl::OStringBuffer buf; buf.append('<'); buf.append(tests[i].base); buf.append(RTL_CONSTASCII_STRINGPARAM(">, <")); buf.append(tests[i].absolute); buf.append(RTL_CONSTASCII_STRINGPARAM(">: ")); if (ref.is()) { buf.append('<'); buf.append( rtl::OUStringToOString( ref->getUriReference(), RTL_TEXTENCODING_UTF8)); buf.append('>'); } else { buf.append(RTL_CONSTASCII_STRINGPARAM("none")); } buf.append(RTL_CONSTASCII_STRINGPARAM(" instead of ")); if (tests[i].relative == 0) { buf.append(RTL_CONSTASCII_STRINGPARAM("none")); } else { buf.append('<'); buf.append(tests[i].relative); buf.append('>'); } msg = buf.makeStringAndClear(); } CPPUNIT_ASSERT_MESSAGE(msg.getStr(), ok); } } void Test::testFindFirstURLInText() { struct Test { char const * input; char const * result; xub_StrLen begin; xub_StrLen end; }; static Test const tests[] = { { "...ftp://bla.bla.bla/blubber/...", "ftp://bla.bla.bla/blubber/", 3, 29 }, { "..\\ftp://bla.bla.bla/blubber/...", 0, 0, 0 }, { "..\\ftp:\\\\bla.bla.bla\\blubber/...", //Sync with tools/source/fsys/urlobj.cxx and changeScheme #ifdef LINUX "smb://bla.bla.bla/blubber%2F", 7, 29 }, #endif #ifdef WNT "file://bla.bla.bla/blubber%2F", 7, 29 }, #endif { "http://sun.com", "http://sun.com/", 0, 14 }, { "http://sun.com/", "http://sun.com/", 0, 15 }, { "http://www.xerox.com@www.pcworld.com/go/3990332.htm", 0, 0, 0 }, { "ftp://www.xerox.com@www.pcworld.com/go/3990332.htm", "ftp://www.xerox.com@www.pcworld.com/go/3990332.htm", 0, 50 }, { "Version.1.2.3", 0, 0, 0 }, { "Version:1.2.3", 0, 0, 0 }, { "a.b.c", 0, 0, 0 }, { "file:///a|...", "file:///a:", 0, 10 }, { "file:///a||...", "file:///a%7C%7C", 0, 11 }, { "file:///a|/bc#...", "file:///a:/bc", 0, 13 }, { "file:///a|/bc#de...", "file:///a:/bc#de", 0, 16 }, { "abc.def.ghi,ftp.xxx.yyy/zzz...", "ftp://ftp.xxx.yyy/zzz", 12, 27 }, { "abc.def.ghi,Ftp.xxx.yyy/zzz...", "ftp://Ftp.xxx.yyy/zzz", 12, 27 }, { "abc.def.ghi,www.xxx.yyy...", "http://www.xxx.yyy/", 12, 23 }, { "abc.def.ghi,wwww.xxx.yyy...", 0, 0, 0 }, { "abc.def.ghi,wWW.xxx.yyy...", "http://wWW.xxx.yyy/", 12, 23 }, { "Bla {mailto.me@abc.def.g.h.i}...", "mailto:%7Bmailto.me@abc.def.g.h.i", 4, 28 }, { "abc@def@ghi", 0, 0, 0 }, { "lala@sun.com", "mailto:lala@sun.com", 0, 12 }, { "1lala@sun.com", "mailto:1lala@sun.com", 0, 13 }, { "aaa_bbb@xxx.yy", "mailto:aaa_bbb@xxx.yy", 0, 14 }, { "{a:\\bla/bla/bla...}", "file:///a:/bla/bla/bla", 1, 15 }, { "#b:/c/d#e#f#", "file:///b:/c/d", 1, 7 }, { "a:/", "file:///a:/", 0, 3 }, { ".component:", 0, 0, 0 }, { ".uno:", 0, 0, 0 }, { "cid:", 0, 0, 0 }, { "data:", 0, 0, 0 }, { "db:", 0, 0, 0 }, { "file:", 0, 0, 0 }, { "ftp:", 0, 0, 0 }, { "http:", 0, 0, 0 }, { "https:", 0, 0, 0 }, { "imap:", 0, 0, 0 }, { "javascript:", 0, 0, 0 }, { "ldap:", 0, 0, 0 }, { "macro:", 0, 0, 0 }, { "mailto:", 0, 0, 0 }, { "news:", 0, 0, 0 }, { "out:", 0, 0, 0 }, { "pop3:", 0, 0, 0 }, { "private:", 0, 0, 0 }, { "slot:", 0, 0, 0 }, { "staroffice.component:", 0, 0, 0 }, { "staroffice.db:", 0, 0, 0 }, { "staroffice.factory:", 0, 0, 0 }, { "staroffice.helpid:", 0, 0, 0 }, { "staroffice.java:", 0, 0, 0 }, { "staroffice.macro:", 0, 0, 0 }, { "staroffice.out:", 0, 0, 0 }, { "staroffice.pop3:", 0, 0, 0 }, { "staroffice.private:", 0, 0, 0 }, { "staroffice.searchfolder:", 0, 0, 0 }, { "staroffice.slot:", 0, 0, 0 }, { "staroffice.trashcan:", 0, 0, 0 }, { "staroffice.uno:", 0, 0, 0 }, { "staroffice.vim:", 0, 0, 0 }, { "staroffice:", 0, 0, 0 }, { "vim:", 0, 0, 0 }, { "vnd.sun.star.cmd:", 0, 0, 0 }, { "vnd.sun.star.help:", 0, 0, 0 }, { "vnd.sun.star.hier:", 0, 0, 0 }, { "vnd.sun.star.odma:", 0, 0, 0 }, { "vnd.sun.star.pkg:", 0, 0, 0 }, { "vnd.sun.star.script:", 0, 0, 0 }, { "vnd.sun.star.webdav:", 0, 0, 0 }, { "vnd.sun.star.wfs:", 0, 0, 0 }, { "generic:path", 0, 0, 0 }, { "wfs:", 0, 0, 0 } }; CharClass charClass( css::uno::Reference< css::lang::XMultiServiceFactory >( m_context->getServiceManager(), css::uno::UNO_QUERY_THROW), com::sun::star::lang::Locale( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en")), rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("US")), rtl::OUString())); for (std::size_t i = 0; i < SAL_N_ELEMENTS(tests); ++i) { rtl::OUString input(rtl::OUString::createFromAscii(tests[i].input)); xub_StrLen begin = 0; xub_StrLen end = static_cast< xub_StrLen >(input.getLength()); rtl::OUString result( URIHelper::FindFirstURLInText(input, begin, end, charClass)); bool ok = tests[i].result == 0 ? (result.getLength() == 0 && begin == input.getLength() && end == input.getLength()) : (result.equalsAscii(tests[i].result) && begin == tests[i].begin && end == tests[i].end); rtl::OString msg; if (!ok) { rtl::OStringBuffer buf; buf.append('"'); buf.append(tests[i].input); buf.append(RTL_CONSTASCII_STRINGPARAM("\" -> ")); buf.append(tests[i].result == 0 ? "none" : tests[i].result); buf.append(RTL_CONSTASCII_STRINGPARAM(" (")); buf.append(static_cast< sal_Int32 >(tests[i].begin)); buf.append(RTL_CONSTASCII_STRINGPARAM(", ")); buf.append(static_cast< sal_Int32 >(tests[i].end)); buf.append(')'); buf.append(RTL_CONSTASCII_STRINGPARAM(" != ")); buf.append(rtl::OUStringToOString(result, RTL_TEXTENCODING_UTF8)); buf.append(RTL_CONSTASCII_STRINGPARAM(" (")); buf.append(static_cast< sal_Int32 >(begin)); buf.append(RTL_CONSTASCII_STRINGPARAM(", ")); buf.append(static_cast< sal_Int32 >(end)); buf.append(')'); msg = buf.makeStringAndClear(); } CPPUNIT_ASSERT_MESSAGE(msg.getStr(), ok); } } css::uno::Reference< css::uno::XComponentContext > Test::m_context; CPPUNIT_TEST_SUITE_REGISTRATION(Test); } CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */