/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the Collabora Office project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <sal/config.h>

#include <cstdlib>
#include <iostream>
#include <source_location>

#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/task/XJob.hpp>
#include <com/sun/star/task/XJobExecutor.hpp>
#include <com/sun/star/testuno/Enum.hpp>
#include <com/sun/star/testuno/Exception.hpp>
#include <com/sun/star/testuno/Struct.hpp>
#include <com/sun/star/testuno/StructLong.hpp>
#include <com/sun/star/testuno/StructString.hpp>
#include <com/sun/star/testuno/Template.hpp>
#include <com/sun/star/testuno/Test.hpp>
#include <com/sun/star/testuno/XTest.hpp>
#include <cpo/uno/Any.hxx>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <cpo/uno/Sequence.hxx>
#include <cpo/uno/Type.hxx>
#include <com/sun/star/uno/XInterface.hpp>
#include <cppu/unotype.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <cppuhelper/weak.hxx>
#include <o3tl/any.hxx>
#include <rtl/ref.hxx>
#include <rtl/ustring.hxx>
#include <sal/types.h>
#include <salhelper/thread.hxx>
#include <tools/debug.hxx>
#include <uno/dispatcher.hxx>
#include <uno/environment.h>
#include <uno/environment.hxx>
#include <uno/mapping.hxx>
#include <vcl/svapp.hxx>

namespace com::sun::star::uno
{
class XComponentContext;
}

namespace
{
void verify(bool value, std::source_location const& location = std::source_location::current())
{
    if (!value)
    {
        std::cerr << "Verification failed in " << location.function_name() << " at "
                  << location.file_name() << ":" << location.line() << ":" << location.column()
                  << "\n";
        std::abort();
    }
}

bool checkAnyVoid(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<void>::get();
}

bool checkAnyBoolean(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<bool>::get() && *o3tl::forceAccess<bool>(value);
}

bool checkAnyByte(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<sal_Int8>::get()
           && *o3tl::forceAccess<sal_Int8>(value) == -12;
}

bool checkAnyShort(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<sal_Int16>::get()
           && *o3tl::forceAccess<sal_Int16>(value) == -1234;
}

bool checkAnyUnsignedShort(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<sal_uInt16>::get()
           && *o3tl::forceAccess<sal_uInt16>(value) == 54321;
}

bool checkAnyLong(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<sal_Int32>::get()
           && *o3tl::forceAccess<sal_Int32>(value) == -123456;
}

bool checkAnyUnsignedLong(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<sal_uInt32>::get()
           && *o3tl::forceAccess<sal_uInt32>(value) == 3456789012;
}

bool checkAnyHyper(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<sal_Int64>::get()
           && *o3tl::forceAccess<sal_Int64>(value) == -123456789;
}

bool checkAnyUnsignedHyper(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<sal_uInt64>::get()
           && *o3tl::forceAccess<sal_uInt64>(value) == 9876543210;
}

bool checkAnyFloat(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<float>::get()
           && *o3tl::forceAccess<float>(value) == -10.25;
}

bool checkAnyDouble(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<double>::get()
           && *o3tl::forceAccess<double>(value) == 100.5;
}

bool checkAnyChar(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<sal_Unicode>::get()
           && *o3tl::forceAccess<sal_Unicode>(value) == u'Ö';
}

bool checkAnyString(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<OUString>::get()
           && *o3tl::forceAccess<OUString>(value) == u"hä";
}

bool checkAnyType(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<cpo::uno::Type>::get()
           && *o3tl::forceAccess<cpo::uno::Type>(value) == cppu::UnoType<sal_Int32>::get();
}

bool checkAnySequence(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<cpo::uno::Sequence<OUString>>::get()
           && *o3tl::forceAccess<cpo::uno::Sequence<OUString>>(value)
                  == cpo::uno::Sequence<OUString>{ u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr };
}

bool checkAnyEnum(cpo::uno::Any const& value)
{
    return value.getValueType() == cppu::UnoType<css::testuno::Enum>::get()
           && *o3tl::forceAccess<css::testuno::Enum>(value) == css::testuno::Enum_E_2;
}

bool checkAnyStruct(cpo::uno::Any const& value,
                    css::uno::Reference<css::uno::XInterface> const& object)
{
    return value.getValueType() == cppu::UnoType<css::testuno::Struct>::get()
           && *o3tl::forceAccess<css::testuno::Struct>(value)
                  == css::testuno::Struct{ true,
                                           -12,
                                           -1234,
                                           54321,
                                           -123456,
                                           3456789012,
                                           -123456789,
                                           9876543210,
                                           -10.25,
                                           100.5,
                                           u'Ö',
                                           u"hä"_ustr,
                                           cppu::UnoType<sal_Int32>::get(),
                                           cpo::uno::Any(sal_Int32(-123456)),
                                           { u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr },
                                           css::testuno::Enum_E_2,
                                           { -123456 },
                                           { { u"foo"_ustr },
                                             -123456,
                                             cpo::uno::Any(sal_Int32(-123456)),
                                             { u"barr"_ustr } },
                                           object };
}

bool checkAnyException(cpo::uno::Any const& value)
{
    if (value.getValueType() != cppu::UnoType<css::testuno::Exception>::get())
    {
        return false;
    }
    auto const& e = *o3tl::forceAccess<css::testuno::Exception>(value);
    return e.Message.startsWith("error") && !e.Context.is() && e.m1 == -123456 && e.m2 == 100.5
           && e.m3 == u"hä";
}

bool checkAnyInterface(cpo::uno::Any const& value,
                       css::uno::Reference<css::testuno::XTest> const& object)
{
    return value.getValueType() == cppu::UnoType<css::testuno::XTest>::get()
           && *o3tl::forceAccess<css::uno::Reference<css::testuno::XTest>>(value) == object;
}

void doExecuteTest(css::uno::Reference<css::testuno::XTest> const& test)
{
    {
        bool const val = test->getBoolean();
        verify(val);
        bool const ok = test->isBoolean(val);
        verify(ok);
    }
    {
        auto const val = test->getByte();
        verify(val == -12);
        bool const ok = test->isByte(val);
        verify(ok);
    }
    {
        auto const val = test->getShort();
        verify(val == -1234);
        bool const ok = test->isShort(val);
        verify(ok);
    }
    {
        auto const val = test->getUnsignedShort();
        verify(val == 54321);
        bool const ok = test->isUnsignedShort(val);
        verify(ok);
    }
    {
        auto const val = test->getLong();
        verify(val == -123456);
        bool const ok = test->isLong(val);
        verify(ok);
    }
    {
        auto const val = test->getUnsignedLong();
        verify(val == 3456789012);
        bool const ok = test->isUnsignedLong(val);
        verify(ok);
    }
    {
        auto const val = test->getHyper();
        verify(val == -123456789);
        bool const ok = test->isHyper(val);
        verify(ok);
    }
    {
        auto const val = test->getUnsignedHyper();
        verify(val == 9876543210);
        bool const ok = test->isUnsignedHyper(val);
        verify(ok);
    }
    {
        auto const val = test->getFloat();
        verify(val == -10.25);
        bool const ok = test->isFloat(val);
        verify(ok);
    }
    {
        auto const val = test->getDouble();
        verify(val == 100.5);
        bool const ok = test->isDouble(val);
        verify(ok);
    }
    {
        auto const val = test->getChar();
        verify(val == u'Ö');
        bool const ok = test->isChar(val);
        verify(ok);
    }
    {
        auto const val = test->getString();
        verify(val == u"hä"_ustr);
        bool const ok = test->isString(val);
        verify(ok);
    }
    {
        auto const val = test->getType();
        verify(val == cppu::UnoType<sal_Int32>::get());
        bool const ok = test->isType(val);
        verify(ok);
    }
    {
        auto const val = test->getEnum();
        verify(val == css::testuno::Enum_E_2);
        bool const ok = test->isEnum(val);
        verify(ok);
    }
    {
        auto const val = test->getStruct();
        verify(val
               == css::testuno::Struct{ true,
                                        -12,
                                        -1234,
                                        54321,
                                        -123456,
                                        3456789012,
                                        -123456789,
                                        9876543210,
                                        -10.25,
                                        100.5,
                                        u'Ö',
                                        u"hä"_ustr,
                                        cppu::UnoType<sal_Int32>::get(),
                                        cpo::uno::Any(sal_Int32(-123456)),
                                        { u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr },
                                        css::testuno::Enum_E_2,
                                        { -123456 },
                                        { { u"foo"_ustr },
                                          -123456,
                                          cpo::uno::Any(sal_Int32(-123456)),
                                          { u"barr"_ustr } },
                                        test });
        bool const ok = test->isStruct(val);
        verify(ok);
    }
    {
        auto const val = test->getStructLong();
        verify(val == css::testuno::StructLong{ -123456 });
        bool const ok = test->isStructLong(val);
        verify(ok);
    }
    {
        auto const val = test->getStructString();
        verify(val == css::testuno::StructString{ u"hä"_ustr });
        bool const ok = test->isStructString(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyVoid();
        verify(checkAnyVoid(val));
        bool const ok = test->isAnyVoid(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyBoolean();
        verify(checkAnyBoolean(val));
        bool const ok = test->isAnyBoolean(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyByte();
        verify(checkAnyByte(val));
        bool const ok = test->isAnyByte(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyShort();
        verify(checkAnyShort(val));
        bool const ok = test->isAnyShort(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyUnsignedShort();
        verify(checkAnyUnsignedShort(val));
        bool const ok = test->isAnyUnsignedShort(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyLong();
        verify(checkAnyLong(val));
        bool const ok = test->isAnyLong(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyUnsignedLong();
        verify(checkAnyUnsignedLong(val));
        bool const ok = test->isAnyUnsignedLong(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyHyper();
        verify(checkAnyHyper(val));
        bool const ok = test->isAnyHyper(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyUnsignedHyper();
        verify(checkAnyUnsignedHyper(val));
        bool const ok = test->isAnyUnsignedHyper(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyFloat();
        verify(checkAnyFloat(val));
        bool const ok = test->isAnyFloat(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyDouble();
        verify(checkAnyDouble(val));
        bool const ok = test->isAnyDouble(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyChar();
        verify(checkAnyChar(val));
        bool const ok = test->isAnyChar(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyString();
        verify(checkAnyString(val));
        bool const ok = test->isAnyString(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyType();
        verify(checkAnyType(val));
        bool const ok = test->isAnyType(val);
        verify(ok);
    }
    {
        auto const val = test->getAnySequence();
        verify(checkAnySequence(val));
        bool const ok = test->isAnySequence(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyEnum();
        verify(checkAnyEnum(val));
        bool const ok = test->isAnyEnum(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyStruct();
        verify(checkAnyStruct(val, test));
        bool const ok = test->isAnyStruct(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyException();
        verify(checkAnyException(val));
        bool const ok = test->isAnyException(val);
        verify(ok);
    }
    {
        auto const val = test->getAnyInterface();
        verify(checkAnyInterface(val, test));
        bool const ok = test->isAnyInterface(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceBoolean();
        verify(val == cpo::uno::Sequence<bool>{ true, true, false });
        bool const ok = test->isSequenceBoolean(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceByte();
        verify(val == cpo::uno::Sequence<sal_Int8>{ -12, 1, 12 });
        bool const ok = test->isSequenceByte(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceShort();
        verify(val == cpo::uno::Sequence<sal_Int16>{ -1234, 1, 1234 });
        bool const ok = test->isSequenceShort(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceUnsignedShort();
        verify(val == cpo::uno::Sequence<sal_uInt16>{ 1, 10, 54321 });
        bool const ok = test->isSequenceUnsignedShort(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceLong();
        verify(val == cpo::uno::Sequence<sal_Int32>{ -123456, 1, 123456 });
        bool const ok = test->isSequenceLong(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceUnsignedLong();
        verify(val == cpo::uno::Sequence<sal_uInt32>{ 1, 10, 3456789012 });
        bool const ok = test->isSequenceUnsignedLong(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceHyper();
        verify(val == cpo::uno::Sequence<sal_Int64>{ -123456789, 1, 123456789 });
        bool const ok = test->isSequenceHyper(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceUnsignedHyper();
        verify(val == cpo::uno::Sequence<sal_uInt64>{ 1, 10, 9876543210 });
        bool const ok = test->isSequenceUnsignedHyper(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceFloat();
        verify(val == cpo::uno::Sequence<float>{ -10.25, 1.5, 10.75 });
        bool const ok = test->isSequenceFloat(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceDouble();
        verify(val == cpo::uno::Sequence<double>{ -100.5, 1.25, 100.75 });
        bool const ok = test->isSequenceDouble(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceChar();
        verify(val == cpo::uno::Sequence<sal_Unicode>{ 'a', 'B', u'Ö' });
        bool const ok = test->isSequenceChar(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceString();
        verify(val == cpo::uno::Sequence<OUString>{ u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr });
        bool const ok = test->isSequenceString(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceType();
        verify(val
               == cpo::uno::Sequence<cpo::uno::Type>{
                      cppu::UnoType<sal_Int32>::get(), cppu::UnoType<void>::get(),
                      cppu::UnoType<cpo::uno::Sequence<css::testuno::Enum>>::get() });
        bool const ok = test->isSequenceType(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceAny();
        verify(
            val
            == cpo::uno::Sequence<cpo::uno::Any>{
                   cpo::uno::Any(sal_Int32(-123456)), cpo::uno::Any(),
                   cpo::uno::Any(cpo::uno::Sequence<css::testuno::Enum>{
                       css::testuno::Enum_E_2, css::testuno::Enum_E3, css::testuno::Enum_E_10 }) });
        bool const ok = test->isSequenceAny(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceSequenceString();
        verify(val
               == cpo::uno::Sequence<cpo::uno::Sequence<OUString>>{
                      {}, { u"foo"_ustr, u"barr"_ustr }, { u"baz"_ustr } });
        bool const ok = test->isSequenceSequenceString(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceEnum();
        verify(val
               == cpo::uno::Sequence<css::testuno::Enum>{
                      css::testuno::Enum_E_2, css::testuno::Enum_E3, css::testuno::Enum_E_10 });
        bool const ok = test->isSequenceEnum(val);
        verify(ok);
    }
    {
        auto const val = test->getSequenceStruct();
        verify(
            val
            == cpo::uno::Sequence<css::testuno::Struct>{
                   { true,
                     -12,
                     -1234,
                     1,
                     -123456,
                     1,
                     -123456789,
                     1,
                     -10.25,
                     -100.5,
                     'a',
                     u"hä"_ustr,
                     cppu::UnoType<sal_Int32>::get(),
                     cpo::uno::Any(sal_Int32(-123456)),
                     {},
                     css::testuno::Enum_E_2,
                     { -123456 },
                     { { u"foo"_ustr },
                       -123456,
                       cpo::uno::Any(sal_Int32(-123456)),
                       { u"barr"_ustr } },
                     test },
                   { true,
                     1,
                     1,
                     10,
                     1,
                     10,
                     1,
                     10,
                     1.5,
                     1.25,
                     'B',
                     u"barr"_ustr,
                     cppu::UnoType<void>::get(),
                     cpo::uno::Any(),
                     { u"foo"_ustr, u"barr"_ustr },
                     css::testuno::Enum_E3,
                     { 1 },
                     { { u"baz"_ustr }, 1, cpo::uno::Any(), { u"foo"_ustr } },
                     nullptr },
                   { false,
                     12,
                     1234,
                     54321,
                     123456,
                     3456789012,
                     123456789,
                     9876543210,
                     10.75,
                     100.75,
                     u'Ö',
                     u"bazzz"_ustr,
                     cppu::UnoType<cpo::uno::Sequence<css::testuno::Enum>>::get(),
                     cpo::uno::Any(cpo::uno::Sequence<css::testuno::Enum>{
                         css::testuno::Enum_E_2, css::testuno::Enum_E3, css::testuno::Enum_E_10 }),
                     { u"baz"_ustr },
                     css::testuno::Enum_E_10,
                     { 123456 },
                     { { u"barr"_ustr },
                       123456,
                       cpo::uno::Any(cpo::uno::Sequence<css::testuno::Enum>{
                           css::testuno::Enum_E_2, css::testuno::Enum_E3,
                           css::testuno::Enum_E_10 }),
                       { u"bazz"_ustr } },
                     test } });
        bool const ok = test->isSequenceStruct(val);
        verify(ok);
    }
    {
        auto const val = test->getNull();
        verify(val == css::uno::Reference<css::testuno::XTest>());
        bool const ok = test->isNull(val);
        verify(ok);
    }
    {
        bool value1;
        sal_Int8 value2;
        sal_Int16 value3;
        sal_uInt16 value4;
        sal_Int32 value5;
        sal_uInt32 value6;
        sal_Int64 value7;
        sal_uInt64 value8;
        float value9;
        double value10;
        sal_Unicode value11;
        OUString value12;
        cpo::uno::Type value13;
        cpo::uno::Any value14;
        cpo::uno::Sequence<OUString> value15;
        css::testuno::Enum value16;
        css::testuno::Struct value17;
        css::uno::Reference<css::testuno::XTest> value18;
        test->getOut(value1, value2, value3, value4, value5, value6, value7, value8, value9,
                     value10, value11, value12, value13, value14, value15, value16, value17,
                     value18);
        verify(value1);
        verify(value2 == -12);
        verify(value3 == -1234);
        verify(value4 == 54321);
        verify(value5 == -123456);
        verify(value6 == 3456789012);
        verify(value7 == -123456789);
        verify(value8 == 9876543210);
        verify(value9 == -10.25);
        verify(value10 == 100.5);
        verify(value11 == u'Ö');
        verify(value12 == u"hä"_ustr);
        verify(value13 == cppu::UnoType<sal_Int32>::get());
        verify(value14 == cpo::uno::Any(sal_Int32(-123456)));
        verify(value15 == cpo::uno::Sequence<OUString>{ u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr });
        verify(value16 == css::testuno::Enum_E_2);
        verify(value17
               == css::testuno::Struct{ true,
                                        -12,
                                        -1234,
                                        54321,
                                        -123456,
                                        3456789012,
                                        -123456789,
                                        9876543210,
                                        -10.25,
                                        100.5,
                                        u'Ö',
                                        u"hä"_ustr,
                                        cppu::UnoType<sal_Int32>::get(),
                                        cpo::uno::Any(sal_Int32(-123456)),
                                        { u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr },
                                        css::testuno::Enum_E_2,
                                        { -123456 },
                                        { { u"foo"_ustr },
                                          -123456,
                                          cpo::uno::Any(sal_Int32(-123456)),
                                          { u"barr"_ustr } },
                                        test });
        verify(value18 == test);
    }
    try
    {
        test->throwRuntimeException();
        verify(false);
    }
    catch (css::uno::RuntimeException& e)
    {
        verify(e.Message.startsWith("test"));
    }
}

class TestThread : public salhelper::Thread
{
public:
    TestThread()
        : Thread("testuno")
    {
    }

    bool value = false;

private:
    void execute() override
    {
        SolarMutexGuard g;
        value = true;
    }
};

class JobExecutorThread : public salhelper::Thread
{
public:
    JobExecutorThread(css::uno::Reference<css::task::XJobExecutor> const& object)
        : Thread("jobexecutor")
        , object_(object)
    {
    }

private:
    void execute() override { object_->trigger(u"executor thread"_ustr); }

    css::uno::Reference<css::task::XJobExecutor> object_;
};

class Test : public cppu::WeakImplHelper<css::lang::XServiceInfo, css::testuno::XTest>
{
    OUString SAL_CALL getImplementationName() override
    {
        return u"com.sun.star.comp.testuno.Test"_ustr;
    }

    bool SAL_CALL supportsService(OUString const& ServiceName) override
    {
        return cppu::supportsService(this, ServiceName);
    }

    cpo::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
    {
        return { u"com.sun.star.testuno.Test"_ustr };
    }

    bool SAL_CALL getBoolean() override { return true; }

    bool SAL_CALL isBoolean(bool value) override { return value; }

    sal_Int8 SAL_CALL getByte() override { return -12; }

    bool SAL_CALL isByte(sal_Int8 value) override { return value == -12; }

    sal_Int16 SAL_CALL getShort() override { return -1234; }

    bool SAL_CALL isShort(sal_Int16 value) override { return value == -1234; }

    sal_uInt16 SAL_CALL getUnsignedShort() override { return 54321; }

    bool SAL_CALL isUnsignedShort(sal_uInt16 value) override { return value == 54321; }

    sal_Int32 SAL_CALL getLong() override { return -123456; }

    bool SAL_CALL isLong(sal_Int32 value) override { return value == -123456; }

    sal_uInt32 SAL_CALL getUnsignedLong() override { return 3456789012; }

    bool SAL_CALL isUnsignedLong(sal_uInt32 value) override { return value == 3456789012; }

    sal_Int64 SAL_CALL getHyper() override { return -123456789; }

    bool SAL_CALL isHyper(sal_Int64 value) override { return value == -123456789; }

    sal_uInt64 SAL_CALL getUnsignedHyper() override { return 9876543210; }

    bool SAL_CALL isUnsignedHyper(sal_uInt64 value) override { return value == 9876543210; }

    float SAL_CALL getFloat() override { return -10.25; }

    bool SAL_CALL isFloat(float value) override { return value == -10.25; }

    double SAL_CALL getDouble() override { return 100.5; }

    bool SAL_CALL isDouble(double value) override { return value == 100.5; }

    sal_Unicode SAL_CALL getChar() override { return u'Ö'; }

    bool SAL_CALL isChar(sal_Unicode value) override { return value == u'Ö'; }

    OUString SAL_CALL getString() override { return u"hä"_ustr; }

    bool SAL_CALL isString(OUString const& value) override { return value == u"hä"; }

    cpo::uno::Type SAL_CALL getType() override { return cppu::UnoType<sal_Int32>::get(); }

    bool SAL_CALL isType(cpo::uno::Type const& value) override
    {
        return value == cppu::UnoType<sal_Int32>::get();
    }

    css::testuno::Enum SAL_CALL getEnum() override { return css::testuno::Enum_E_2; }

    bool SAL_CALL isEnum(css::testuno::Enum value) override
    {
        return value == css::testuno::Enum_E_2;
    }

    css::testuno::Struct SAL_CALL getStruct() override
    {
        return { true,
                 -12,
                 -1234,
                 54321,
                 -123456,
                 3456789012,
                 -123456789,
                 9876543210,
                 -10.25,
                 100.5,
                 u'Ö',
                 u"hä"_ustr,
                 cppu::UnoType<sal_Int32>::get(),
                 cpo::uno::Any(sal_Int32(-123456)),
                 { u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr },
                 css::testuno::Enum_E_2,
                 { -123456 },
                 { { u"foo"_ustr }, -123456, cpo::uno::Any(sal_Int32(-123456)), { u"barr"_ustr } },
                 static_cast<OWeakObject*>(this) };
    }

    bool SAL_CALL isStruct(css::testuno::Struct const& value) override
    {
        return value
               == css::testuno::Struct{ true,
                                        -12,
                                        -1234,
                                        54321,
                                        -123456,
                                        3456789012,
                                        -123456789,
                                        9876543210,
                                        -10.25,
                                        100.5,
                                        u'Ö',
                                        u"hä"_ustr,
                                        cppu::UnoType<sal_Int32>::get(),
                                        cpo::uno::Any(sal_Int32(-123456)),
                                        { u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr },
                                        css::testuno::Enum_E_2,
                                        { -123456 },
                                        { { u"foo"_ustr },
                                          -123456,
                                          cpo::uno::Any(sal_Int32(-123456)),
                                          { u"barr"_ustr } },
                                        static_cast<OWeakObject*>(this) };
    }

    css::testuno::StructLong SAL_CALL getStructLong() override { return { -123456 }; }

    bool SAL_CALL isStructLong(css::testuno::StructLong const& value) override
    {
        return value.m == -123456;
    }

    css::testuno::StructString SAL_CALL getStructString() override { return { u"hä"_ustr }; }

    bool SAL_CALL isStructString(css::testuno::StructString const& value) override
    {
        return value.m == u"hä";
    }

    css::testuno::Template<cpo::uno::Any, css::testuno::StructString>
        SAL_CALL getTemplate() override
    {
        return { { u"foo"_ustr }, -123456, cpo::uno::Any(sal_Int32(-123456)), { u"barr"_ustr } };
    }

    bool SAL_CALL isTemplate(
        css::testuno::Template<cpo::uno::Any, css::testuno::StructString> const& value) override
    {
        return value
               == css::testuno::Template<cpo::uno::Any, css::testuno::StructString>{
                      { u"foo"_ustr }, -123456, cpo::uno::Any(sal_Int32(-123456)), { u"barr"_ustr }
                  };
    }

    cpo::uno::Any SAL_CALL getAnyVoid() override { return {}; }

    bool SAL_CALL isAnyVoid(cpo::uno::Any const& value) override { return checkAnyVoid(value); }

    cpo::uno::Any SAL_CALL getAnyBoolean() override { return cpo::uno::Any(true); }

    bool SAL_CALL isAnyBoolean(cpo::uno::Any const& value) override
    {
        return checkAnyBoolean(value);
    }

    cpo::uno::Any SAL_CALL getAnyByte() override { return cpo::uno::Any(sal_Int8(-12)); }

    bool SAL_CALL isAnyByte(cpo::uno::Any const& value) override { return checkAnyByte(value); }

    cpo::uno::Any SAL_CALL getAnyShort() override { return cpo::uno::Any(sal_Int16(-1234)); }

    bool SAL_CALL isAnyShort(cpo::uno::Any const& value) override { return checkAnyShort(value); }

    cpo::uno::Any SAL_CALL getAnyUnsignedShort() override
    {
        return cpo::uno::Any(sal_uInt16(54321));
    }

    bool SAL_CALL isAnyUnsignedShort(cpo::uno::Any const& value) override
    {
        return checkAnyUnsignedShort(value);
    }

    cpo::uno::Any SAL_CALL getAnyLong() override { return cpo::uno::Any(sal_Int32(-123456)); }

    bool SAL_CALL isAnyLong(cpo::uno::Any const& value) override { return checkAnyLong(value); }

    cpo::uno::Any SAL_CALL getAnyUnsignedLong() override
    {
        return cpo::uno::Any(sal_uInt32(3456789012));
    }

    bool SAL_CALL isAnyUnsignedLong(cpo::uno::Any const& value) override
    {
        return checkAnyUnsignedLong(value);
    }

    cpo::uno::Any SAL_CALL getAnyHyper() override { return cpo::uno::Any(sal_Int64(-123456789)); }

    bool SAL_CALL isAnyHyper(cpo::uno::Any const& value) override { return checkAnyHyper(value); }

    cpo::uno::Any SAL_CALL getAnyUnsignedHyper() override
    {
        return cpo::uno::Any(sal_uInt64(9876543210));
    }

    bool SAL_CALL isAnyUnsignedHyper(cpo::uno::Any const& value) override
    {
        return checkAnyUnsignedHyper(value);
    }

    cpo::uno::Any SAL_CALL getAnyFloat() override { return cpo::uno::Any(-10.25f); }

    bool SAL_CALL isAnyFloat(cpo::uno::Any const& value) override { return checkAnyFloat(value); }

    cpo::uno::Any SAL_CALL getAnyDouble() override { return cpo::uno::Any(100.5); }

    bool SAL_CALL isAnyDouble(cpo::uno::Any const& value) override { return checkAnyDouble(value); }

    cpo::uno::Any SAL_CALL getAnyChar() override { return cpo::uno::Any(u'Ö'); }

    bool SAL_CALL isAnyChar(cpo::uno::Any const& value) override { return checkAnyChar(value); }

    cpo::uno::Any SAL_CALL getAnyString() override { return cpo::uno::Any(u"hä"_ustr); }

    bool SAL_CALL isAnyString(cpo::uno::Any const& value) override { return checkAnyString(value); }

    cpo::uno::Any SAL_CALL getAnyType() override
    {
        return cpo::uno::Any(cppu::UnoType<sal_Int32>::get());
    }

    bool SAL_CALL isAnyType(cpo::uno::Any const& value) override { return checkAnyType(value); }

    cpo::uno::Any SAL_CALL getAnySequence() override
    {
        return cpo::uno::Any(cpo::uno::Sequence{ u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr });
    }

    bool SAL_CALL isAnySequence(cpo::uno::Any const& value) override
    {
        return checkAnySequence(value);
    }

    cpo::uno::Any SAL_CALL getAnyEnum() override { return cpo::uno::Any(css::testuno::Enum_E_2); }

    bool SAL_CALL isAnyEnum(cpo::uno::Any const& value) override { return checkAnyEnum(value); }

    cpo::uno::Any SAL_CALL getAnyStruct() override
    {
        return cpo::uno::Any(css::testuno::Struct{
            true,
            -12,
            -1234,
            54321,
            -123456,
            3456789012,
            -123456789,
            9876543210,
            -10.25,
            100.5,
            u'Ö',
            u"hä"_ustr,
            cppu::UnoType<sal_Int32>::get(),
            cpo::uno::Any(sal_Int32(-123456)),
            { u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr },
            css::testuno::Enum_E_2,
            { -123456 },
            { { u"foo"_ustr }, -123456, cpo::uno::Any(sal_Int32(-123456)), { u"barr"_ustr } },
            static_cast<OWeakObject*>(this) });
    }

    bool SAL_CALL isAnyStruct(cpo::uno::Any const& value) override
    {
        return checkAnyStruct(value, static_cast<OWeakObject*>(this));
    }

    cpo::uno::Any SAL_CALL getAnyException() override
    {
        return cpo::uno::Any(
            css::testuno::Exception{ u"error"_ustr, {}, -123456, 100.5, u"hä"_ustr });
    }

    bool SAL_CALL isAnyException(cpo::uno::Any const& value) override
    {
        return checkAnyException(value);
    }

    cpo::uno::Any SAL_CALL getAnyInterface() override
    {
        return cpo::uno::Any(css::uno::Reference<css::testuno::XTest>(this));
    }

    bool SAL_CALL isAnyInterface(cpo::uno::Any const& value) override
    {
        return checkAnyInterface(value, this);
    }

    cpo::uno::Sequence<bool> SAL_CALL getSequenceBoolean() override
    {
        return { true, true, false };
    }

    bool SAL_CALL isSequenceBoolean(cpo::uno::Sequence<bool> const& value) override
    {
        return value == cpo::uno::Sequence<bool>{ true, true, false };
    }

    cpo::uno::Sequence<sal_Int8> SAL_CALL getSequenceByte() override { return { -12, 1, 12 }; }

    bool SAL_CALL isSequenceByte(cpo::uno::Sequence<sal_Int8> const& value) override
    {
        return value == cpo::uno::Sequence<sal_Int8>{ -12, 1, 12 };
    }

    cpo::uno::Sequence<sal_Int16> SAL_CALL getSequenceShort() override
    {
        return { -1234, 1, 1234 };
    }

    bool SAL_CALL isSequenceShort(cpo::uno::Sequence<sal_Int16> const& value) override
    {
        return value == cpo::uno::Sequence<sal_Int16>{ -1234, 1, 1234 };
    }

    cpo::uno::Sequence<sal_uInt16> SAL_CALL getSequenceUnsignedShort() override
    {
        return { 1, 10, 54321 };
    }

    bool SAL_CALL isSequenceUnsignedShort(cpo::uno::Sequence<sal_uInt16> const& value) override
    {
        return value == cpo::uno::Sequence<sal_uInt16>{ 1, 10, 54321 };
    }

    cpo::uno::Sequence<sal_Int32> SAL_CALL getSequenceLong() override
    {
        return { -123456, 1, 123456 };
    }

    bool SAL_CALL isSequenceLong(cpo::uno::Sequence<sal_Int32> const& value) override
    {
        return value == cpo::uno::Sequence<sal_Int32>{ -123456, 1, 123456 };
    }

    cpo::uno::Sequence<sal_uInt32> SAL_CALL getSequenceUnsignedLong() override
    {
        return { 1, 10, 3456789012 };
    }

    bool SAL_CALL isSequenceUnsignedLong(cpo::uno::Sequence<sal_uInt32> const& value) override
    {
        return value == cpo::uno::Sequence<sal_uInt32>{ 1, 10, 3456789012 };
    }

    cpo::uno::Sequence<sal_Int64> SAL_CALL getSequenceHyper() override
    {
        return { -123456789, 1, 123456789 };
    }

    bool SAL_CALL isSequenceHyper(cpo::uno::Sequence<sal_Int64> const& value) override
    {
        return value == cpo::uno::Sequence<sal_Int64>{ -123456789, 1, 123456789 };
    }

    cpo::uno::Sequence<sal_uInt64> SAL_CALL getSequenceUnsignedHyper() override
    {
        return { 1, 10, 9876543210 };
    }

    bool SAL_CALL isSequenceUnsignedHyper(cpo::uno::Sequence<sal_uInt64> const& value) override
    {
        return value == cpo::uno::Sequence<sal_uInt64>{ 1, 10, 9876543210 };
    }

    cpo::uno::Sequence<float> SAL_CALL getSequenceFloat() override
    {
        return { -10.25, 1.5, 10.75 };
    }

    bool SAL_CALL isSequenceFloat(cpo::uno::Sequence<float> const& value) override
    {
        return value == cpo::uno::Sequence<float>{ -10.25, 1.5, 10.75 };
    }

    cpo::uno::Sequence<double> SAL_CALL getSequenceDouble() override
    {
        return { -100.5, 1.25, 100.75 };
    }

    bool SAL_CALL isSequenceDouble(cpo::uno::Sequence<double> const& value) override
    {
        return value == cpo::uno::Sequence<double>{ -100.5, 1.25, 100.75 };
    }

    cpo::uno::Sequence<sal_Unicode> SAL_CALL getSequenceChar() override
    {
        return { 'a', 'B', u'Ö' };
    }

    bool SAL_CALL isSequenceChar(cpo::uno::Sequence<sal_Unicode> const& value) override
    {
        return value == cpo::uno::Sequence<sal_Unicode>{ 'a', 'B', u'Ö' };
    }

    cpo::uno::Sequence<OUString> SAL_CALL getSequenceString() override
    {
        return { u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr };
    }

    bool SAL_CALL isSequenceString(cpo::uno::Sequence<OUString> const& value) override
    {
        return value == cpo::uno::Sequence<OUString>{ u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr };
    }

    cpo::uno::Sequence<cpo::uno::Type> SAL_CALL getSequenceType() override
    {
        return { cppu::UnoType<sal_Int32>::get(), cppu::UnoType<void>::get(),
                 cppu::UnoType<cpo::uno::Sequence<css::testuno::Enum>>::get() };
    }

    bool SAL_CALL isSequenceType(cpo::uno::Sequence<cpo::uno::Type> const& value) override
    {
        return value
               == cpo::uno::Sequence<cpo::uno::Type>{
                      cppu::UnoType<sal_Int32>::get(), cppu::UnoType<void>::get(),
                      cppu::UnoType<cpo::uno::Sequence<css::testuno::Enum>>::get()
                  };
    }

    cpo::uno::Sequence<cpo::uno::Any> SAL_CALL getSequenceAny() override
    {
        return { cpo::uno::Any(sal_Int32(-123456)), cpo::uno::Any(),
                 cpo::uno::Any(cpo::uno::Sequence<css::testuno::Enum>{
                     css::testuno::Enum_E_2, css::testuno::Enum_E3, css::testuno::Enum_E_10 }) };
    }

    bool SAL_CALL isSequenceAny(cpo::uno::Sequence<cpo::uno::Any> const& value) override
    {
        return value
               == cpo::uno::Sequence<cpo::uno::Any>{
                      cpo::uno::Any(sal_Int32(-123456)), cpo::uno::Any(),
                      cpo::uno::Any(cpo::uno::Sequence<css::testuno::Enum>{
                          css::testuno::Enum_E_2, css::testuno::Enum_E3, css::testuno::Enum_E_10 })
                  };
    }

    cpo::uno::Sequence<cpo::uno::Sequence<OUString>> SAL_CALL getSequenceSequenceString() override
    {
        return { {}, { u"foo"_ustr, u"barr"_ustr }, { u"baz"_ustr } };
    }

    bool SAL_CALL
    isSequenceSequenceString(cpo::uno::Sequence<cpo::uno::Sequence<OUString>> const& value) override
    {
        return value
               == cpo::uno::Sequence<cpo::uno::Sequence<OUString>>{ {},
                                                                    { u"foo"_ustr, u"barr"_ustr },
                                                                    { u"baz"_ustr } };
    }

    cpo::uno::Sequence<css::testuno::Enum> SAL_CALL getSequenceEnum() override
    {
        return { css::testuno::Enum_E_2, css::testuno::Enum_E3, css::testuno::Enum_E_10 };
    }

    bool SAL_CALL isSequenceEnum(cpo::uno::Sequence<css::testuno::Enum> const& value) override
    {
        return value
               == cpo::uno::Sequence<css::testuno::Enum>{ css::testuno::Enum_E_2,
                                                          css::testuno::Enum_E3,
                                                          css::testuno::Enum_E_10 };
    }

    cpo::uno::Sequence<css::testuno::Struct> SAL_CALL getSequenceStruct() override
    {
        return {
            { true,
              -12,
              -1234,
              1,
              -123456,
              1,
              -123456789,
              1,
              -10.25,
              -100.5,
              'a',
              u"hä"_ustr,
              cppu::UnoType<sal_Int32>::get(),
              cpo::uno::Any(sal_Int32(-123456)),
              {},
              css::testuno::Enum_E_2,
              { -123456 },
              { { u"foo"_ustr }, -123456, cpo::uno::Any(sal_Int32(-123456)), { u"barr"_ustr } },
              static_cast<OWeakObject*>(this) },
            { true,
              1,
              1,
              10,
              1,
              10,
              1,
              10,
              1.5,
              1.25,
              'B',
              u"barr"_ustr,
              cppu::UnoType<void>::get(),
              cpo::uno::Any(),
              { u"foo"_ustr, u"barr"_ustr },
              css::testuno::Enum_E3,
              { 1 },
              { { u"baz"_ustr }, 1, cpo::uno::Any(), { u"foo"_ustr } },
              nullptr },
            { false,
              12,
              1234,
              54321,
              123456,
              3456789012,
              123456789,
              9876543210,
              10.75,
              100.75,
              u'Ö',
              u"bazzz"_ustr,
              cppu::UnoType<cpo::uno::Sequence<css::testuno::Enum>>::get(),
              cpo::uno::Any(cpo::uno::Sequence<css::testuno::Enum>{
                  css::testuno::Enum_E_2, css::testuno::Enum_E3, css::testuno::Enum_E_10 }),
              { u"baz"_ustr },
              css::testuno::Enum_E_10,
              { 123456 },
              { { u"barr"_ustr },
                123456,
                cpo::uno::Any(cpo::uno::Sequence<css::testuno::Enum>{
                    css::testuno::Enum_E_2, css::testuno::Enum_E3, css::testuno::Enum_E_10 }),
                { u"bazz"_ustr } },
              static_cast<OWeakObject*>(this) }
        };
    }

    bool SAL_CALL isSequenceStruct(cpo::uno::Sequence<css::testuno::Struct> const& value) override
    {
        return value
               == cpo::uno::Sequence<css::testuno::Struct>{
                      { true,
                        -12,
                        -1234,
                        1,
                        -123456,
                        1,
                        -123456789,
                        1,
                        -10.25,
                        -100.5,
                        'a',
                        u"hä"_ustr,
                        cppu::UnoType<sal_Int32>::get(),
                        cpo::uno::Any(sal_Int32(-123456)),
                        {},
                        css::testuno::Enum_E_2,
                        { -123456 },
                        { { u"foo"_ustr },
                          -123456,
                          cpo::uno::Any(sal_Int32(-123456)),
                          { u"barr"_ustr } },
                        static_cast<OWeakObject*>(this) },
                      { true,
                        1,
                        1,
                        10,
                        1,
                        10,
                        1,
                        10,
                        1.5,
                        1.25,
                        'B',
                        u"barr"_ustr,
                        cppu::UnoType<void>::get(),
                        cpo::uno::Any(),
                        { u"foo"_ustr, u"barr"_ustr },
                        css::testuno::Enum_E3,
                        { 1 },
                        { { u"baz"_ustr }, 1, cpo::uno::Any(), { u"foo"_ustr } },
                        nullptr },
                      { false,
                        12,
                        1234,
                        54321,
                        123456,
                        3456789012,
                        123456789,
                        9876543210,
                        10.75,
                        100.75,
                        u'Ö',
                        u"bazzz"_ustr,
                        cppu::UnoType<cpo::uno::Sequence<css::testuno::Enum>>::get(),
                        cpo::uno::Any(cpo::uno::Sequence<css::testuno::Enum>{
                            css::testuno::Enum_E_2, css::testuno::Enum_E3,
                            css::testuno::Enum_E_10 }),
                        { u"baz"_ustr },
                        css::testuno::Enum_E_10,
                        { 123456 },
                        { { u"barr"_ustr },
                          123456,
                          cpo::uno::Any(cpo::uno::Sequence<css::testuno::Enum>{
                              css::testuno::Enum_E_2, css::testuno::Enum_E3,
                              css::testuno::Enum_E_10 }),
                          { u"bazz"_ustr } },
                        static_cast<OWeakObject*>(this) }
                  };
    }

    css::uno::Reference<css::testuno::XTest> SAL_CALL getNull() override { return {}; }

    bool SAL_CALL isNull(css::uno::Reference<css::testuno::XTest> const& value) override
    {
        return !value;
    }

    void SAL_CALL getOut(bool& value1, sal_Int8& value2, sal_Int16& value3, sal_uInt16& value4,
                         sal_Int32& value5, sal_uInt32& value6, sal_Int64& value7,
                         sal_uInt64& value8, float& value9, double& value10, sal_Unicode& value11,
                         OUString& value12, cpo::uno::Type& value13, cpo::uno::Any& value14,
                         cpo::uno::Sequence<OUString>& value15, css::testuno::Enum& value16,
                         css::testuno::Struct& value17,
                         css::uno::Reference<css::testuno::XTest>& value18) override
    {
        value1 = true;
        value2 = -12;
        value3 = -1234;
        value4 = 54321;
        value5 = -123456;
        value6 = 3456789012;
        value7 = -123456789;
        value8 = 9876543210;
        value9 = -10.25;
        value10 = 100.5;
        value11 = u'Ö';
        value12 = u"hä"_ustr;
        value13 = cppu::UnoType<sal_Int32>::get();
        value14 <<= sal_Int32(-123456);
        value15 = { u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr };
        value16 = css::testuno::Enum_E_2;
        value17
            = { true,
                -12,
                -1234,
                54321,
                -123456,
                3456789012,
                -123456789,
                9876543210,
                -10.25,
                100.5,
                u'Ö',
                u"hä"_ustr,
                cppu::UnoType<sal_Int32>::get(),
                cpo::uno::Any(sal_Int32(-123456)),
                { u"foo"_ustr, u"barr"_ustr, u"bazzz"_ustr },
                css::testuno::Enum_E_2,
                { -123456 },
                { { u"foo"_ustr }, -123456, cpo::uno::Any(sal_Int32(-123456)), { u"barr"_ustr } },
                static_cast<OWeakObject*>(this) };
        value18 = this;
    }

    void SAL_CALL throwRuntimeException() override
    {
        throw css::uno::RuntimeException(u"test"_ustr);
    }

    void SAL_CALL passJob(css::uno::Reference<css::task::XJob> const& object) override
    {
        try
        {
            object->execute({});
        }
        catch (css::uno::RuntimeException& e)
        {
            object->execute({ { u"name"_ustr, cpo::uno::Any(e.Message) } });
        }
    }

    void SAL_CALL passJobExecutor(css::uno::Reference<css::task::XJobExecutor> const& object,
                                  bool newThread) override
    {
        if (newThread)
        {
            rtl::Reference<JobExecutorThread> t(new JobExecutorThread(object));
            t->launch();
            t->join();
        }
        else
        {
            object->trigger(u"executor"_ustr);
        }
    }

    void SAL_CALL passInterface(css::uno::Reference<css::uno::XInterface> const& object) override
    {
        css::uno::Reference<css::task::XJob>(object, css::uno::UNO_QUERY_THROW)
            ->execute({ { u"name"_ustr, cpo::uno::Any(u"queried job"_ustr) } });
        css::uno::Reference<css::task::XJobExecutor>(object, css::uno::UNO_QUERY_THROW)
            ->trigger(u"queried executor"_ustr);
    }

    bool SAL_CALL
    checkAttributes(css::uno::Reference<css::testuno::XAttributes> const& object) override
    {
        auto const ok1 = object->getLongAttribute() == 789;
        verify(ok1);
        auto const ok2 = object->getStringAttribute() == u"foo"_ustr;
        verify(ok2);
        bool const ok3 = object->getReadOnlyAttribute();
        verify(ok3);
        return ok1 && ok2 && ok3;
    }

    OUString SAL_CALL getStringAttribute() override { return stringAttribute_; }

    void SAL_CALL setStringAttribute(OUString const& value) override { stringAttribute_ = value; }

    bool SAL_CALL testSolarMutex() override
    {
        DBG_TESTNOTSOLARMUTEX();
        rtl::Reference t(new TestThread);
        t->launch();
        t->join();
        SolarMutexGuard g;
        return t->value;
    }

    void SAL_CALL executeTest(css::uno::Reference<css::testuno::XTest> const& test) override
    {
        doExecuteTest(test);
    }

    OUString stringAttribute_ = u"hä"_ustr;
};

class BridgeTest : public cppu::WeakImplHelper<css::task::XJob>
{
public:
    explicit BridgeTest(css::uno::Reference<css::uno::XComponentContext> const& context)
        : context_(context)
    {
    }

private:
    cpo::uno::Any SAL_CALL
    execute(cpo::uno::Sequence<css::beans::NamedValue> const& Arguments) override
    {
        if (Arguments.hasElements())
        {
            throw css::lang::IllegalArgumentException(u"BridgeTest execute args not empty"_ustr, {},
                                                      0);
        }
        auto const envCppOrig = css::uno::Environment::getCurrent();
        css::uno::Environment envUno;
        uno_createEnvironment(reinterpret_cast<uno_Environment**>(&envUno),
                              u"" UNO_LB_UNO ""_ustr.pData, nullptr);
        css::uno::Mapping cpp2uno(envCppOrig.get(), envUno.get());
        css::uno::Environment envCpp;
        if (!cpp2uno.is())
        {
            throw css::uno::RuntimeException(u"cannot get C++ to UNO mapping"_ustr);
        }
        uno_createEnvironment(reinterpret_cast<uno_Environment**>(&envCpp),
                              envCppOrig.getTypeName().pData, nullptr);
        css::uno::Mapping uno2cpp(envUno.get(), envCpp.get());
        if (!uno2cpp.is())
        {
            throw css::uno::RuntimeException(u"cannot get UNO to C++ mapping"_ustr);
        }
        css::uno::UnoInterfaceReference ifcUno;
        cpp2uno.mapInterface(reinterpret_cast<void**>(&ifcUno.m_pUnoI),
                             css::testuno::Test::create(context_).get(),
                             cppu::UnoType<css::testuno::XTest>::get());
        if (!ifcUno.is())
        {
            throw css::uno::RuntimeException(u"cannot map from C++ to UNO"_ustr);
        }
        css::uno::Reference<css::testuno::XTest> ifcCpp;
        uno2cpp.mapInterface(reinterpret_cast<void**>(&ifcCpp), ifcUno.get(),
                             cppu::UnoType<css::testuno::XTest>::get());
        if (!ifcCpp.is())
        {
            throw css::uno::RuntimeException(u"cannot map from UNO to C++"_ustr);
        }
        doExecuteTest(ifcCpp);
        {
            auto const val1 = ifcCpp->getStringAttribute();
            verify(val1 == u"hä"_ustr);
            ifcCpp->setStringAttribute(u"foo"_ustr);
            auto const val2 = ifcCpp->getStringAttribute();
            verify(val2 == u"foo"_ustr);
        }
        return cpo::uno::Any(true);
    }

    css::uno::Reference<css::uno::XComponentContext> context_;
};
}

extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
com_sun_star_comp_testuno_BridgeTest_get_implementation(css::uno::XComponentContext* context,
                                                        cpo::uno::Sequence<cpo::uno::Any> const&)
{
    return cppu::acquire(new BridgeTest(context));
}

extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
com_sun_star_comp_testuno_Test_get_implementation(css::uno::XComponentContext*,
                                                  cpo::uno::Sequence<cpo::uno::Any> const&)
{
    return cppu::acquire(new Test);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
