/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <algorithm>
#ifdef _WIN32
#if !defined WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif

// include files

#include <sal/types.h>
#include <rtl/string.hxx>
#include <osl/thread.hxx>
#include <osl/mutex.hxx>
#include <osl/time.h>

#include <string.h>
#include <memory>

#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

#define t_print printf

using namespace osl;

namespace {

// Small stopwatch
class StopWatch {
    TimeValue t1,t2;                                // Start and stoptime

protected:
    sal_Int32 m_nNanoSec;
    sal_Int32 m_nSeconds;

    bool m_bIsValid;                                   // TRUE, when started and stopped
    bool m_bIsRunning;                                 // TRUE, when started

public:
    StopWatch();

    void start();                                 // Starts time
    void stop();                                  // Stops time

    double getSeconds() const;
    double getTenthSec() const;
};

}

// ================================= Stop Watch =================================

// A small stopwatch for internal use
// (c) Lars Langhans 29.12.1996 22:10

StopWatch::StopWatch()
    : m_nNanoSec(0)
    , m_nSeconds(0)
    , m_bIsValid(false)
    , m_bIsRunning(false)
{
    t1.Seconds = 0;
    t1.Nanosec = 0;
    t2.Seconds = 0;
    t2.Nanosec = 0;
}

void StopWatch::start()
{
// pre: %
// post: Start Timer

    m_bIsValid = false;
    m_bIsRunning = true;
    osl_getSystemTime( &t1 );
    t_print("# %u %u nsecs\n", static_cast<unsigned>(t1.Seconds), static_cast<unsigned>(t1.Nanosec));
    // gettimeofday(&t1, 0);
}

void StopWatch::stop()
{
// pre: Timer should be started
// post: Timer will stopped

    osl_getSystemTime( &t2 );
    t_print("# %u %u nsecs\n", static_cast<unsigned>(t2.Seconds), static_cast<unsigned>(t2.Nanosec));

    if (m_bIsRunning)
    {                                // check if started.
        m_nSeconds = static_cast<sal_Int32>(t2.Seconds) - static_cast<sal_Int32>(t1.Seconds);
        if ( t2.Nanosec > t1.Nanosec )
            m_nNanoSec = static_cast<sal_Int32>(t2.Nanosec) - static_cast<sal_Int32>(t1.Nanosec);
        else
        {
            m_nNanoSec = 1000000000 + static_cast<sal_Int32>(t2.Nanosec) - static_cast<sal_Int32>(t1.Nanosec);
            m_nSeconds -= 1;
        }
        t_print("# %u %u nsecs\n", static_cast<unsigned>(m_nSeconds), static_cast<unsigned>(m_nNanoSec) );
        m_bIsValid = true;
        m_bIsRunning = false;
    }
}

double StopWatch::getSeconds() const
{
// pre: valid = TRUE
// BACK: time in seconds

    double nValue = 0.0;
    if (m_bIsValid)
    {
        nValue = double(m_nNanoSec) / 1000000000.0 + m_nSeconds; // milli micro nano
    }
    return nValue;
}

double StopWatch::getTenthSec() const
{
    double nValue = 0.0;
    if (m_bIsValid)
    {
        nValue = double(m_nNanoSec) / 100000000.0 + m_nSeconds * 10;
    }
    return nValue ;
}

namespace {

template <class T>
class ThreadSafeValue
{
    T   m_nFlag;
    Mutex   m_aMutex;
public:
    explicit ThreadSafeValue(T n = 0): m_nFlag(n) {}
    T getValue()
        {
            //block if already acquired by another thread.
            osl::MutexGuard g(m_aMutex);
            return m_nFlag;
        }
    void incValue()
        {
            //only one thread operate on the flag.
            osl::MutexGuard g(m_aMutex);
            m_nFlag++;
        }
    void acquire() {m_aMutex.acquire();}
    void release() {m_aMutex.release();}
};

}

namespace ThreadHelper
{
    static void thread_sleep_tenth_sec(sal_Int32 _nTenthSec)
    {
        osl::Thread::wait(std::chrono::milliseconds(_nTenthSec * 100));
    }

    static void outputPriority(oslThreadPriority const& _aPriority)
    {
        // LLA: output the priority
        if (_aPriority == osl_Thread_PriorityHighest)
        {
            t_print("Prio is High\n");
        }
        else if (_aPriority == osl_Thread_PriorityAboveNormal)
        {
            t_print("Prio is above normal\n");
        }
        else if (_aPriority == osl_Thread_PriorityNormal)
        {
            t_print("Prio is normal\n");
        }
        else if (_aPriority == osl_Thread_PriorityBelowNormal)
        {
            t_print("Prio is below normal\n");
        }
        else if (_aPriority == osl_Thread_PriorityLowest)
        {
            t_print("Prio is lowest\n");
        }
        else
        {
            t_print("Prio is unknown\n");
        }
    }
}

namespace {

/** Simple thread for testing Thread-create.

    Just add 1 of value 0, and after running, result is 1.
 */
class myThread : public Thread
{
    ThreadSafeValue<sal_Int32> m_aFlag;
public:
    sal_Int32 getValue() { return m_aFlag.getValue(); }
protected:
    /** guarded value which initialized 0

        @see ThreadSafeValue
    */
    void SAL_CALL run() override
        {
            while(schedule())
            {
                m_aFlag.incValue();
                ThreadHelper::thread_sleep_tenth_sec(1);
            }
        }

public:

    virtual void SAL_CALL suspend() override
        {
            m_aFlag.acquire();
            ::osl::Thread::suspend();
            m_aFlag.release();
        }

    virtual ~myThread() override
        {
            if (isRunning())
            {
                t_print("error: not terminated.\n");
            }
        }

};

/** Thread which has a flag add 1 every second until 20
 */
class OCountThread : public Thread
{
    ThreadSafeValue<sal_Int32> m_aFlag;
public:
    OCountThread() : m_nWaitSec(0)
        {
            t_print("new OCountThread thread %u!\n", static_cast<unsigned>(getIdentifier()));
        }
    sal_Int32 getValue() { return m_aFlag.getValue(); }

    void setWait(sal_Int32 nSec)
        {
            m_nWaitSec = nSec;
            //m_bWait = sal_True;
        }

    virtual void SAL_CALL suspend() override
        {
            m_aFlag.acquire();
            ::osl::Thread::suspend();
            m_aFlag.release();
        }

protected:
    //sal_Bool m_bWait;
    sal_Int32 m_nWaitSec;

    void SAL_CALL run() override
        {
            /// if the thread should terminate, schedule return false
            while (m_aFlag.getValue() < 20 && schedule())
            {
                m_aFlag.incValue();
                ThreadHelper::thread_sleep_tenth_sec(1);

                if (m_nWaitSec != 0)
                {
                    TimeValue nTV;
                    nTV.Seconds = m_nWaitSec / 10 ;
                    nTV.Nanosec = ( m_nWaitSec%10 ) * 100000000 ;
                    wait( nTV );
                    m_nWaitSec = 0;
                }
            }
        }
    void SAL_CALL onTerminated() override
        {
            t_print("normally terminate this thread %u!\n", static_cast<unsigned>(getIdentifier()));
        }
public:

    virtual ~OCountThread() override
        {
            if (isRunning())
            {
                t_print("error: not terminated.\n");
            }
        }

};

/** no call schedule in the run method
*/
class ONoScheduleThread : public Thread
{
    ThreadSafeValue<sal_Int32> m_aFlag;
public:
    sal_Int32 getValue() { return m_aFlag.getValue(); }

    virtual void SAL_CALL suspend() override
        {
            m_aFlag.acquire();
            ::osl::Thread::suspend();
            m_aFlag.release();
        }
protected:
    void SAL_CALL run() override
        {
            while (m_aFlag.getValue() < 10)
            {
                m_aFlag.incValue();
                ThreadHelper::thread_sleep_tenth_sec(1);
            }
        }
    void SAL_CALL onTerminated() override
        {
            t_print("normally terminate this thread %u!\n", static_cast<unsigned>(getIdentifier()));
        }
public:
    ONoScheduleThread()
        {
                t_print("new thread id %u!\n", static_cast<unsigned>(getIdentifier()));
        }
    virtual ~ONoScheduleThread() override
        {
            if (isRunning())
            {
                t_print("error: not terminated.\n");
            }
        }

};


class OAddThread : public Thread
{
    ThreadSafeValue<sal_Int32> m_aFlag;
public:
    //oslThreadIdentifier m_id, m_CurId;
    OAddThread(){}
    sal_Int32 getValue() { return m_aFlag.getValue(); }

    virtual void SAL_CALL suspend() override
        {
            m_aFlag.acquire();
            ::osl::Thread::suspend();
            m_aFlag.release();
        }
protected:
    void SAL_CALL run() override
        {
            //if the thread should terminate, schedule return false
            while (schedule())
            {
                m_aFlag.incValue();
            }
        }
    void SAL_CALL onTerminated() override
        {
            // t_print("normally terminate this thread %d!\n", getIdentifier());
        }
public:

    virtual ~OAddThread() override
        {
            if (isRunning())
            {
                // t_print("error: not terminated.\n");
            }
        }

};

}

namespace osl_Thread
{

    static void resumeAndWaitThread(Thread* _pThread)
    {
        // This function starts a thread, wait a second and suspends the thread
        // Due to the fact, that a suspend and never run thread never really exists.

        // Note: on UNX, after createSuspended, and then terminate the thread, it performs well;
        // while on Windows, after createSuspended, the thread can not terminate, wait endlessly,
        // so here call resume at first, then call terminate.
#ifdef _WIN32
        t_print("resumeAndWaitThread\n");
        _pThread->resume();
        ThreadHelper::thread_sleep_tenth_sec(1);
#else
        _pThread->resume();
#endif
    }

    // kill a running thread and join it, if it has terminated, do nothing
    static void termAndJoinThread(Thread* _pThread)
    {
        _pThread->terminate();

// LLA: Windows feature???, a suspended thread can not terminated, so we have to weak it up
#ifdef _WIN32
        _pThread->resume();
        ThreadHelper::thread_sleep_tenth_sec(1);
#endif
        t_print("#wait for join.\n");
        _pThread->join();
    }
/** Test of the osl::Thread::create method
 */

    class create : public CppUnit::TestFixture
    {
    public:
        /** Simple create a thread.

            Create a simple thread, it just does add 1 to value(which initialized 0),
            if the thread run, the value should be 1.
        */
        void create_001()
            {
                myThread* newthread = new myThread;
                bool bRes = newthread->create();
                CPPUNIT_ASSERT_MESSAGE("Can not creates a new thread!\n", bRes);

                ThreadHelper::thread_sleep_tenth_sec(1);        // wait short
                bool isRunning = newthread->isRunning();    // check if thread is running
                /// wait for the new thread to assure it has run
                ThreadHelper::thread_sleep_tenth_sec(3);
                sal_Int32 nValue = newthread->getValue();
                /// to assure the new thread has terminated
                termAndJoinThread(newthread);
                delete newthread;

                t_print("   nValue = %d\n", static_cast<int>(nValue));
                t_print("isRunning = %s\n", isRunning ? "true" : "false");

                CPPUNIT_ASSERT_MESSAGE(
                    "Creates a new thread",
                    nValue >= 1
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "Creates a new thread",
                    isRunning
                    );

            }

        /** only one running thread per instance, return false if create secondly
         */
        void create_002()
            {
                myThread* newthread = new myThread;
                bool res1 = newthread->create();
                bool res2 = newthread->create();
                t_print("In non pro, an assertion should occurred. This behaviour is right.\n");
                termAndJoinThread(newthread);
                delete newthread;

                CPPUNIT_ASSERT_MESSAGE(
                    "Creates a new thread: can not create two threads per instance",
                    res1
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "Creates a new thread: can not create two threads per instance",
                    !res2
                    );

            }

        CPPUNIT_TEST_SUITE(create);
        CPPUNIT_TEST(create_001);
        CPPUNIT_TEST(create_002);
        CPPUNIT_TEST_SUITE_END();
    }; // class create

    /** Test of the osl::Thread::createSuspended method
    */
    class createSuspended : public CppUnit::TestFixture
    {
    public:
        /** Create a suspended thread, use the same class as create_001

            after create, wait enough time, check the value, if it's still the initial value, pass
        */
        void createSuspended_001()
            {
                myThread* newthread = new myThread;
                bool bRes = newthread->createSuspended();
                CPPUNIT_ASSERT_MESSAGE("Can not creates a new thread!", bRes);

                ThreadHelper::thread_sleep_tenth_sec(1);
                bool isRunning = newthread->isRunning();
                ThreadHelper::thread_sleep_tenth_sec(3);
                sal_Int32 nValue = newthread->getValue();

                resumeAndWaitThread(newthread);

                termAndJoinThread(newthread);
                delete newthread;

                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "Creates a new suspended thread",
                    sal_Int32(0), nValue
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "Creates a new suspended thread",
                    isRunning
                    );
            }

        void createSuspended_002()
            {
                myThread* newthread = new myThread;
                bool res1 = newthread->createSuspended();
                bool res2 = newthread->createSuspended();

                resumeAndWaitThread(newthread);

                termAndJoinThread(newthread);

                delete newthread;

                CPPUNIT_ASSERT_MESSAGE(
                    "Creates a new thread: can not create two threads per instance",
                    res1
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "Creates a new thread: can not create two threads per instance",
                    !res2
                    );
            }

        CPPUNIT_TEST_SUITE(createSuspended);
        CPPUNIT_TEST(createSuspended_001);
        // LLA: Deadlocked!!!
        CPPUNIT_TEST(createSuspended_002);
        CPPUNIT_TEST_SUITE_END();
    }; // class createSuspended

    /** when the count value equal to or more than 3, suspend the thread.
    */
    static void suspendCountThread(OCountThread* _pCountThread)
    {
        sal_Int32 nValue = 0;
        while (true)
        {
            nValue = _pCountThread->getValue();
            if (nValue >= 3)
            {
                _pCountThread->suspend();
                break;
            }
        }
    }

    /** Test of the osl::Thread::suspend method
    */
    class suspend : public CppUnit::TestFixture
    {
    public:
        /** Use a thread which has a flag added 1 every second

            ALGORITHM:
            create the thread, after running special time, record value of flag, then suspend it,
            wait a long time, check the flag, if it remains unchanged during suspending
        */
        void suspend_001()
            {
                OCountThread* aCountThread = new OCountThread();
                bool bRes = aCountThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );
                // the thread run for some seconds, but not terminate
                suspendCountThread( aCountThread );

                // the value just after calling suspend
                sal_Int32 nValue = aCountThread->getValue();       // (2)

                ThreadHelper::thread_sleep_tenth_sec(3);

                // the value after waiting 3 seconds
                sal_Int32 nLaterValue = aCountThread->getValue();    // (3)

                resumeAndWaitThread(aCountThread);
                termAndJoinThread(aCountThread);
                delete aCountThread;

                CPPUNIT_ASSERT_MESSAGE(
                    "Suspend the thread",
                    bRes
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "Suspend the thread",
                    nValue, nLaterValue
                    );

            }

        CPPUNIT_TEST_SUITE(suspend);
        CPPUNIT_TEST(suspend_001);
        // LLA: Deadlocked!!!
        // CPPUNIT_TEST(createSuspended_002);
        CPPUNIT_TEST_SUITE_END();
    }; // class suspend

    /** Test of the osl::Thread::resume method
    */
    class resume : public CppUnit::TestFixture
    {
    public:
        /** check if the thread run samely as usual after suspend and resume

            ALGORITHM:
            compare the values before and after suspend, they should be same,
            then compare values before and after resume, the difference should be same as the sleep seconds number
        */
        void resume_001()
            {
                OCountThread* pCountThread = new OCountThread();
                bool bRes = pCountThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                suspendCountThread(pCountThread);

                sal_Int32 nSuspendValue = pCountThread->getValue();  // (2)
                // suspend for 3 seconds
                ThreadHelper::thread_sleep_tenth_sec(3);
                pCountThread->resume();

                ThreadHelper::thread_sleep_tenth_sec(3);
                sal_Int32 nResumeValue = pCountThread->getValue();

                ThreadHelper::thread_sleep_tenth_sec(3);
                sal_Int32 nLaterValue = pCountThread->getValue();

                termAndJoinThread(pCountThread);
                delete pCountThread;

                t_print("SuspendValue: %d\n", static_cast<int>(nSuspendValue));
                t_print("ResumeValue:  %d\n", static_cast<int>(nResumeValue));
                t_print("LaterValue:   %d\n", static_cast<int>(nLaterValue));

                /* LLA: this assumption is no longer relevant: nResumeValue ==  nSuspendValue && */
                CPPUNIT_ASSERT_MESSAGE(
                    "Suspend then resume the thread",
                    nLaterValue >= 9
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "Suspend then resume the thread",
                    nResumeValue > nSuspendValue
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "Suspend then resume the thread",
                    nLaterValue > nResumeValue
                    );

            }

        /** Create a suspended thread then resume, check if the thread has run
         */
        void resume_002()
            {
                myThread* newthread = new myThread;
                bool bRes = newthread->createSuspended();
                CPPUNIT_ASSERT_MESSAGE ( "Can't create thread!", bRes );

                newthread->resume();
                ThreadHelper::thread_sleep_tenth_sec(2);
                sal_Int32 nValue = newthread->getValue();

                termAndJoinThread(newthread);
                delete newthread;

                t_print("   nValue = %d\n", static_cast<int>(nValue));

                CPPUNIT_ASSERT_MESSAGE(
                    "Creates a suspended thread, then resume",
                    nValue >= 1
                    );
            }

        CPPUNIT_TEST_SUITE(resume);
        CPPUNIT_TEST(resume_001);
        CPPUNIT_TEST(resume_002);
        CPPUNIT_TEST_SUITE_END();
    }; // class resume

    /** Test of the osl::Thread::terminate method
    */
    class terminate : public CppUnit::TestFixture
    {
    public:
        /** Check after call terminate if the running thread running go on executing

            ALGORITHM:
            before and after call terminate, the values should be the same
        */
        void terminate_001()
            {
                OCountThread* aCountThread = new OCountThread();
                bool bRes = aCountThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                ThreadHelper::thread_sleep_tenth_sec(2);
                sal_Int32 nValue = aCountThread->getValue();
                aCountThread->terminate();
                ThreadHelper::thread_sleep_tenth_sec(2);
                sal_Int32 nLaterValue = aCountThread->getValue();

                // isRunning should be false after terminate
                bool isRunning = aCountThread->isRunning();
                aCountThread->join();
                delete aCountThread;

                t_print("     nValue = %d\n", static_cast<int>(nValue));
                t_print("nLaterValue = %d\n", static_cast<int>(nLaterValue));

                CPPUNIT_ASSERT_MESSAGE(
                    "Terminate the thread",
                    !isRunning
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "Terminate the thread",
                    nLaterValue >= nValue
                    );
            }
        /** Check if a suspended thread will terminate after call terminate, different on w32 and on UNX
         */
        void terminate_002()
            {
                OCountThread* aCountThread = new OCountThread();
                bool bRes = aCountThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                ThreadHelper::thread_sleep_tenth_sec(1);
                suspendCountThread(aCountThread);
                sal_Int32 nValue = aCountThread->getValue();

                // seems a suspended thread can not be terminated on W32, while on Solaris can
                resumeAndWaitThread(aCountThread);

                ThreadHelper::thread_sleep_tenth_sec(2);

                termAndJoinThread(aCountThread);
                sal_Int32 nLaterValue = aCountThread->getValue();
                delete aCountThread;

                t_print("     nValue = %d\n", static_cast<int>(nValue));
                t_print("nLaterValue = %d\n", static_cast<int>(nLaterValue));

                CPPUNIT_ASSERT_MESSAGE(
                    "Suspend then resume the thread",
                    nLaterValue > nValue );
            }

        CPPUNIT_TEST_SUITE(terminate);
        CPPUNIT_TEST(terminate_001);
        CPPUNIT_TEST(terminate_002);
        CPPUNIT_TEST_SUITE_END();
    }; // class terminate

    /** Test of the osl::Thread::join method
    */
    class join : public CppUnit::TestFixture
    {
    public:
        /** Check after call terminate if the thread running function will not go on executing

            the next statement after join will not exec before the thread terminates
            ALGORITHM:
            recode system time at the beginning of the thread run, call join, then record system time again,
            the difference of the two times should be equal or more than 20 seconds, the CountThread normally terminates
        */
        void join_001()
            {
                OCountThread *aCountThread = new OCountThread();
                bool bRes = aCountThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                StopWatch aStopWatch;
                aStopWatch.start();
                // TimeValue aTimeVal_befor;
                // osl_getSystemTime( &aTimeVal_befor );
                //t_print("#join:the system time is %d,%d\n", pTimeVal_befor->Seconds,pTimeVal_befor->Nanosec);

                aCountThread->join();

                //the below line will be executed after aCountThread terminate
                // TimeValue aTimeVal_after;
                // osl_getSystemTime( &aTimeVal_after );
                aStopWatch.stop();
                // sal_uInt32 nSec  = aTimeVal_after.Seconds - aTimeVal_befor.Seconds;
                double nSec = aStopWatch.getSeconds();
                t_print("join_001 nSec=%f\n", nSec);
                delete aCountThread;

                CPPUNIT_ASSERT_MESSAGE(
                    "Join the thread: after the thread terminate",
                    nSec >= 2
                    );

            }
        /** after terminated by another thread, join exited immediately

            ALGORITHM:
            terminate the thread when value>=3, call join, check the beginning time and time after join,
            the difference should be 3 seconds, join costs little time
        */
        void join_002()
            {
                OCountThread *aCountThread = new OCountThread();
                bool bRes = aCountThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                //record the time when the running begin
                // TimeValue aTimeVal_befor;
                // osl_getSystemTime( &aTimeVal_befor );
                StopWatch aStopWatch;
                aStopWatch.start();

                ThreadHelper::thread_sleep_tenth_sec(10);
                termAndJoinThread(aCountThread);

                //the below line will be executed after aCountThread terminate
                // TimeValue aTimeVal_after;
                // osl_getSystemTime( &aTimeVal_after );
                // sal_uInt32 nSec  = aTimeVal_after.Seconds - aTimeVal_befor.Seconds;
                aStopWatch.stop();
                double nSec = aStopWatch.getSeconds();
                t_print("join_002 nSec=%f\n", nSec);

                delete aCountThread;
                CPPUNIT_ASSERT_MESSAGE(
                    "Join the thread: after thread terminate by another thread",
                    nSec >= 1
                    );
            }

        CPPUNIT_TEST_SUITE(join);
        CPPUNIT_TEST(join_001);
        CPPUNIT_TEST(join_002);
        CPPUNIT_TEST_SUITE_END();
    }; // class join

    /** Test of the osl::Thread::isRunning method
    */
    class isRunning : public CppUnit::TestFixture
    {
    public:
        void isRunning_001()
            {
                OCountThread *aCountThread = new OCountThread();
                bool bRes = aCountThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                bool bRun = aCountThread->isRunning();

                ThreadHelper::thread_sleep_tenth_sec(2);
                termAndJoinThread(aCountThread);
                bool bTer = aCountThread->isRunning();
                delete aCountThread;

                CPPUNIT_ASSERT_MESSAGE(
                    "Test isRunning",
                    bRun
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "Test isRunning",
                    !bTer
                    );
            }
        /** check the value of isRunning when suspending and after resume
         */
        void isRunning_002()
            {
                OCountThread *aCountThread = new OCountThread();
                bool bRes = aCountThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                // sal_Bool bRunning = aCountThread->isRunning();
                // sal_Int32 nValue = 0;
                suspendCountThread(aCountThread);

                bool bRunning_sup = aCountThread->isRunning();
                ThreadHelper::thread_sleep_tenth_sec(2);
                aCountThread->resume();
                ThreadHelper::thread_sleep_tenth_sec(2);
                bool bRunning_res = aCountThread->isRunning();
                termAndJoinThread(aCountThread);
                bool bRunning_ter = aCountThread->isRunning();
                delete aCountThread;

                CPPUNIT_ASSERT_MESSAGE(
                    "Test isRunning",
                    bRes );
                CPPUNIT_ASSERT_MESSAGE(
                    "Test isRunning",
                    bRunning_sup );
                CPPUNIT_ASSERT_MESSAGE(
                    "Test isRunning",
                    bRunning_res );
                CPPUNIT_ASSERT_MESSAGE(
                    "Test isRunning",
                    !bRunning_ter );

            }

        CPPUNIT_TEST_SUITE(isRunning);
        CPPUNIT_TEST(isRunning_001);
        CPPUNIT_TEST(isRunning_002);
        CPPUNIT_TEST_SUITE_END();
    }; // class isRunning

    /// check osl::Thread::setPriority
    class setPriority : public CppUnit::TestFixture
    {
    public:
        // insert your test code here.
        OString getPrioName(oslThreadPriority _aPriority)
            {
                OString sPrioStr;
                switch (_aPriority)
                {
                case osl_Thread_PriorityHighest:
                    sPrioStr = "Highest"_ostr;
                    break;

                case osl_Thread_PriorityAboveNormal:
                    sPrioStr = "AboveNormal"_ostr;
                    break;

                case osl_Thread_PriorityNormal:
                    sPrioStr = "Normal"_ostr;
                    break;

                case osl_Thread_PriorityBelowNormal:
                    sPrioStr = "BelowNormal"_ostr;
                    break;

                case osl_Thread_PriorityLowest:
                    sPrioStr = "Lowest"_ostr;
                    break;
                default:
                    sPrioStr = "unknown"_ostr;
                }
                return sPrioStr;
            }

        /** check 2 threads.

            ALGORITHM:
            Here the function should show, that 2 different threads,
            which only increase a value, should run at the same time with same prio.
            The test fails, if the difference between the two values is more than 5%
            but IMHO this isn't a failure, it's only a feature of the OS.
        */

        void check2Threads(oslThreadPriority _aPriority)
            {
                // initial 5 threads with different priorities
                OAddThread* pThread = new OAddThread();
                OAddThread* p2Thread = new OAddThread();

                //Create them and start running at the same time
                pThread->create();
                pThread->setPriority(_aPriority);
                p2Thread->create();
                p2Thread->setPriority(_aPriority);

                ThreadHelper::thread_sleep_tenth_sec(5);

                pThread->terminate();
                p2Thread->terminate();

                sal_Int32 nValueNormal = pThread->getValue();

                sal_Int32 nValueNormal2 = p2Thread->getValue();

                OString sPrio = getPrioName(_aPriority);
                t_print("After 10 tenth seconds\n");

                t_print("nValue in %s Prio Thread is  %d\n",sPrio.getStr(), static_cast<int>(nValueNormal));
                t_print("nValue in %s Prio Thread is %d\n", sPrio.getStr(), static_cast<int>(nValueNormal2));

                // ThreadHelper::thread_sleep_tenth_sec(1);
                pThread->join();
                p2Thread->join();

                delete pThread;
                delete p2Thread;

                sal_Int32 nDelta = abs(nValueNormal - nValueNormal2);
                double nQuotient = std::max(nValueNormal, nValueNormal2);
                CPPUNIT_ASSERT_MESSAGE(
                    "Quotient is zero, which means, there exist no right values.",
                    nQuotient != 0
                    );
                double nDeltaPercent = nDelta / nQuotient * 100;

                t_print("Delta value %d, percent %f\n", static_cast<int>(nDelta), nDeltaPercent);

                // LLA: it's not a bug if the current OS is not able to handle thread scheduling right and good.
                // like Windows XP
                // LLA: CPPUNIT_ASSERT_MESSAGE(
                // LLA:     "Run 2 normal threads, the count diff more than 5 percent.",
                // LLA:     nDeltaPercent <= 5
                // LLA:     );
            }

        void setPriority_001_1()
            {
                check2Threads(osl_Thread_PriorityHighest);
            }
        void setPriority_001_2()
            {
                check2Threads(osl_Thread_PriorityAboveNormal);
            }
        void setPriority_001_3()
            {
                check2Threads(osl_Thread_PriorityNormal);
            }
        void setPriority_001_4()
            {
                check2Threads(osl_Thread_PriorityBelowNormal);
            }
        void setPriority_001_5()
            {
                check2Threads(osl_Thread_PriorityLowest);
            }

        void setPriority_002()
            {
                // initial 5 threads with different priorities

                OAddThread aHighestThread;
                OAddThread aAboveNormalThread;
                OAddThread aNormalThread;
                //OAddThread *aBelowNormalThread = new OAddThread();
                //OAddThread *aLowestThread = new OAddThread();

                //Create them and start running at the same time
                aHighestThread.createSuspended();
                aHighestThread.setPriority(osl_Thread_PriorityHighest);

                aAboveNormalThread.createSuspended();
                aAboveNormalThread.setPriority(osl_Thread_PriorityAboveNormal);

                aNormalThread.createSuspended();
                aNormalThread.setPriority(osl_Thread_PriorityNormal);
                /*aBelowNormalThread->create();
                  aBelowNormalThread->setPriority(osl_Thread_PriorityBelowNormal);
                  aLowestThread->create();
                  aLowestThread->setPriority(osl_Thread_PriorityLowest);
                */

                aHighestThread.resume();
                aAboveNormalThread.resume();
                aNormalThread.resume();

                ThreadHelper::thread_sleep_tenth_sec(5);

                aHighestThread.suspend();
                aAboveNormalThread.suspend();
                aNormalThread.suspend();

                termAndJoinThread(&aNormalThread);
                termAndJoinThread(&aAboveNormalThread);
                termAndJoinThread(&aHighestThread);
                //aBelowNormalThread->terminate();
                //aLowestThread->terminate();

                sal_Int32 nValueHighest = aHighestThread.getValue();

                sal_Int32 nValueAboveNormal = aAboveNormalThread.getValue();

                sal_Int32 nValueNormal = aNormalThread.getValue();

                t_print("After 10 tenth seconds\n");
                t_print("nValue in Highest Prio Thread is %d\n", static_cast<int>(nValueHighest));
                t_print("nValue in AboveNormal Prio Thread is %d\n", static_cast<int>(nValueAboveNormal));
                t_print("nValue in Normal Prio Thread is %d\n", static_cast<int>(nValueNormal));

#ifndef _WIN32
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueHighest     > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueAboveNormal > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueNormal > 0
                    );
#endif
            }

        void setPriority_003()
            {
                // initial 5 threads with different priorities
                OAddThread pHighestThread;
                OAddThread pAboveNormalThread;
                OAddThread pNormalThread;
                OAddThread pBelowNormalThread;
                OAddThread pLowestThread;

                //Create them and start running at the same time
                pHighestThread.createSuspended();
                pHighestThread.setPriority(osl_Thread_PriorityHighest);

                pAboveNormalThread.createSuspended();
                pAboveNormalThread.setPriority(osl_Thread_PriorityAboveNormal);

                pNormalThread.createSuspended();
                pNormalThread.setPriority(osl_Thread_PriorityNormal);

                pBelowNormalThread.createSuspended();
                pBelowNormalThread.setPriority(osl_Thread_PriorityBelowNormal);

                pLowestThread.createSuspended();
                pLowestThread.setPriority(osl_Thread_PriorityLowest);

                pHighestThread.resume();
                pAboveNormalThread.resume();
                pNormalThread.resume();
                pBelowNormalThread.resume();
                pLowestThread.resume();

                ThreadHelper::thread_sleep_tenth_sec(5);

                pHighestThread.suspend();
                pAboveNormalThread.suspend();
                pNormalThread.suspend();
                pBelowNormalThread.suspend();
                pLowestThread.suspend();

                termAndJoinThread(&pHighestThread);
                termAndJoinThread(&pAboveNormalThread);
                termAndJoinThread(&pNormalThread);
                termAndJoinThread(&pBelowNormalThread);
                termAndJoinThread(&pLowestThread);

                sal_Int32 nValueHighest = pHighestThread.getValue();

                sal_Int32 nValueAboveNormal = pAboveNormalThread.getValue();

                sal_Int32 nValueNormal = pNormalThread.getValue();

                sal_Int32 nValueBelowNormal = pBelowNormalThread.getValue();

                sal_Int32 nValueLowest = pLowestThread.getValue();

                t_print("After 10 tenth seconds\n");
                t_print("nValue in Highest Prio Thread is     %d\n", static_cast<int>(nValueHighest));
                t_print("nValue in AboveNormal Prio Thread is %d\n", static_cast<int>(nValueAboveNormal));
                t_print("nValue in Normal Prio Thread is      %d\n", static_cast<int>(nValueNormal));
                t_print("nValue in BelowNormal Prio Thread is %d\n", static_cast<int>(nValueBelowNormal));
                t_print("nValue in Lowest Prio Thread is      %d\n", static_cast<int>(nValueLowest));

#ifndef _WIN32
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueHighest     > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueAboveNormal > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueNormal      > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueBelowNormal > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueLowest      > 0
                    );
#endif
            }

        void setPriority_004()
            {
                // initial 5 threads with different priorities
                // OAddThread *pHighestThread = new OAddThread();
                OAddThread pAboveNormalThread;
                OAddThread pNormalThread;
                OAddThread pBelowNormalThread;
                OAddThread pLowestThread;

                //Create them and start running at the same time
                // pHighestThread->createSuspended();
                // pHighestThread->setPriority(osl_Thread_PriorityHighest);

                pAboveNormalThread.createSuspended();
                pAboveNormalThread.setPriority(osl_Thread_PriorityAboveNormal);

                pNormalThread.createSuspended();
                pNormalThread.setPriority(osl_Thread_PriorityNormal);

                pBelowNormalThread.createSuspended();
                pBelowNormalThread.setPriority(osl_Thread_PriorityBelowNormal);

                pLowestThread.createSuspended();
                pLowestThread.setPriority(osl_Thread_PriorityLowest);

                // pHighestThread->resume();
                pAboveNormalThread.resume();
                pNormalThread.resume();
                pBelowNormalThread.resume();
                pLowestThread.resume();

                ThreadHelper::thread_sleep_tenth_sec(5);

                // pHighestThread->suspend();
                pAboveNormalThread.suspend();
                pNormalThread.suspend();
                pBelowNormalThread.suspend();
                pLowestThread.suspend();

                // termAndJoinThread(pHighestThread);
                termAndJoinThread(&pAboveNormalThread);
                termAndJoinThread(&pNormalThread);
                termAndJoinThread(&pBelowNormalThread);
                termAndJoinThread(&pLowestThread);

                // sal_Int32 nValueHighest  = 0;
                // nValueHighest =  pHighestThread->getValue();

                sal_Int32 nValueAboveNormal = pAboveNormalThread.getValue();

                sal_Int32 nValueNormal = pNormalThread.getValue();

                sal_Int32 nValueBelowNormal = pBelowNormalThread.getValue();

                sal_Int32 nValueLowest = pLowestThread.getValue();

                t_print("After 5 tenth seconds\n");
                t_print("nValue in AboveNormal Prio Thread is %d\n", static_cast<int>(nValueAboveNormal));
                t_print("nValue in Normal Prio Thread is      %d\n", static_cast<int>(nValueNormal));
                t_print("nValue in BelowNormal Prio Thread is %d\n", static_cast<int>(nValueBelowNormal));
                t_print("nValue in Lowest Prio Thread is      %d\n", static_cast<int>(nValueLowest));

                // delete pHighestThread;

#ifndef _WIN32
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    /* nValueHighest     > 0 &&  */
                    nValueAboveNormal > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueNormal      > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueBelowNormal > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueLowest      > 0
                    );
#endif
            }
        void setPriority_005()
            {
                // initial 5 threads with different priorities
                // OAddThread *pHighestThread = new OAddThread();
                // OAddThread *pAboveNormalThread = new OAddThread();
                OAddThread pNormalThread;
                OAddThread pBelowNormalThread;
                OAddThread pLowestThread;

                //Create them and start running at the same time
                // pHighestThread->createSuspended();
                // pHighestThread->setPriority(osl_Thread_PriorityHighest);

                // pAboveNormalThread->createSuspended();
                // pAboveNormalThread->setPriority(osl_Thread_PriorityAboveNormal);

                pNormalThread.createSuspended();
                pNormalThread.setPriority(osl_Thread_PriorityNormal);

                pBelowNormalThread.createSuspended();
                pBelowNormalThread.setPriority(osl_Thread_PriorityBelowNormal);

                pLowestThread.createSuspended();
                pLowestThread.setPriority(osl_Thread_PriorityLowest);

                // pHighestThread->resume();
                // pAboveNormalThread->resume();
                pNormalThread.resume();
                pBelowNormalThread.resume();
                pLowestThread.resume();

                ThreadHelper::thread_sleep_tenth_sec(5);

                // pHighestThread->suspend();
                // pAboveNormalThread->suspend();
                pNormalThread.suspend();
                pBelowNormalThread.suspend();
                pLowestThread.suspend();

                // termAndJoinThread(pHighestThread);
                // termAndJoinThread(pAboveNormalThread);
                termAndJoinThread(&pNormalThread);
                termAndJoinThread(&pBelowNormalThread);
                termAndJoinThread(&pLowestThread);

                // sal_Int32 nValueHighest  = 0;
                // nValueHighest =  pHighestThread->getValue();

                // sal_Int32 nValueAboveNormal = 0;
                // nValueAboveNormal = pAboveNormalThread->getValue();

                sal_Int32 nValueNormal = pNormalThread.getValue();

                sal_Int32 nValueBelowNormal = pBelowNormalThread.getValue();

                sal_Int32 nValueLowest = pLowestThread.getValue();

                t_print("After 5 tenth seconds\n");
                t_print("nValue in Normal Prio Thread is      %d\n", static_cast<int>(nValueNormal));
                t_print("nValue in BelowNormal Prio Thread is %d\n", static_cast<int>(nValueBelowNormal));
                t_print("nValue in Lowest Prio Thread is      %d\n", static_cast<int>(nValueLowest));

#ifndef _WIN32
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    /* nValueHighest     > 0 &&  */
                    /* nValueAboveNormal > 0 &&  */
                    nValueNormal      > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueBelowNormal > 0
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "SetPriority",
                    nValueLowest      > 0
                    );
#endif
            }

        CPPUNIT_TEST_SUITE(setPriority);
#ifndef __sun
        CPPUNIT_TEST(setPriority_002);
        CPPUNIT_TEST(setPriority_003);
        CPPUNIT_TEST(setPriority_004);
        CPPUNIT_TEST(setPriority_005);
#endif
        CPPUNIT_TEST(setPriority_001_1);
        CPPUNIT_TEST(setPriority_001_2);
        CPPUNIT_TEST(setPriority_001_3);
        CPPUNIT_TEST(setPriority_001_4);
        CPPUNIT_TEST(setPriority_001_5);
        CPPUNIT_TEST_SUITE_END();
    }; // class setPriority

    /** Test of the osl::Thread::getPriority method
    */
    class getPriority : public CppUnit::TestFixture
    {
    public:
        // insert your test code here.
        void getPriority_001()
            {
                OAddThread *pHighestThread = new OAddThread();

                //Create them and start running at the same time
                pHighestThread->create();
                pHighestThread->setPriority(osl_Thread_PriorityHighest);

                oslThreadPriority aPriority = pHighestThread->getPriority();
                termAndJoinThread(pHighestThread);
                delete pHighestThread;

                ThreadHelper::outputPriority(aPriority);

// LLA: Priority settings may not work within some OS versions.
#if defined(_WIN32) || defined(__sun)
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "getPriority",
                    osl_Thread_PriorityHighest, aPriority
                    );
#else
// LLA: Linux
// NO_PTHREAD_PRIORITY ???
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "getPriority",
                    osl_Thread_PriorityNormal, aPriority
                    );
#endif
            }

        CPPUNIT_TEST_SUITE(getPriority);
        CPPUNIT_TEST(getPriority_001);
        CPPUNIT_TEST_SUITE_END();
    }; // class getPriority

    class getIdentifier : public CppUnit::TestFixture
    {
    public:
        // initialise your test code values here.

        void getIdentifier_001()
        {
            // insert your test code here.
        }

        CPPUNIT_TEST_SUITE(getIdentifier);
        CPPUNIT_TEST(getIdentifier_001);
        CPPUNIT_TEST_SUITE_END();
    }; // class getIdentifier

    /** Test of the osl::Thread::getCurrentIdentifier method
    */
    class getCurrentIdentifier : public CppUnit::TestFixture
    {
    public:
        void getCurrentIdentifier_001()
        {
            oslThreadIdentifier oId;
            OCountThread* pCountThread = new OCountThread;
            pCountThread->create();
            pCountThread->setWait(3);
            oId = Thread::getCurrentIdentifier();
            oslThreadIdentifier oIdChild = pCountThread->getIdentifier();
            termAndJoinThread(pCountThread);
            delete pCountThread;

            CPPUNIT_ASSERT_MESSAGE(
                "Get the identifier for the current active thread.",
                oId != oIdChild);
        }

        CPPUNIT_TEST_SUITE(getCurrentIdentifier);
        CPPUNIT_TEST(getCurrentIdentifier_001);
        CPPUNIT_TEST_SUITE_END();
    }; // class getCurrentIdentifier

    /** Test of the osl::Thread::wait method
    */
    class waittest : public CppUnit::TestFixture
    {
    public:
        /** call wait in the run method

            ALGORITHM:
            tested thread wait nWaitSec seconds, main thread sleep (2) seconds,
            then terminate the tested thread, due to the fact that the thread do a sleep(1) + wait(5)
            it's finish after 6 seconds.
        */
        void wait_001()
            {
                OCountThread *aCountThread = new OCountThread();
                sal_Int32 nWaitSec = 5;
                aCountThread->setWait(nWaitSec);
                // thread runs at least 5 seconds.
                bool bRes = aCountThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                //record the time when the running begin
                StopWatch aStopWatch;
                aStopWatch.start();

                // wait a little bit, to let the thread the time, to start
                ThreadHelper::thread_sleep_tenth_sec( 4 );

                // if wait works,
                // this function returns, after 4 sec. later
                termAndJoinThread(aCountThread);

                // value should be one.
                sal_Int32 nValue = aCountThread->getValue();

                aStopWatch.stop();

                // sal_uInt32 nSec  = aTimeVal_after.Seconds - aTimeVal_befor.Seconds;
                double nTenthSec = aStopWatch.getTenthSec();
                double nSec = aStopWatch.getSeconds();
                delete aCountThread;
                t_print("nTenthSec = %f \n", nTenthSec);
                t_print("nSec = %f \n", nSec);
                t_print("nValue = %d \n",  static_cast<int>(nValue));

                CPPUNIT_ASSERT_MESSAGE(
                    "Wait: Blocks the calling thread for the given number of time.",
                    nTenthSec >= 5
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "Wait: Blocks the calling thread for the given number of time.",
                    sal_Int32(1), nValue
                    );

            }

        CPPUNIT_TEST_SUITE(waittest);
        CPPUNIT_TEST(wait_001);
        CPPUNIT_TEST_SUITE_END();
    }; // class waittest

    /** osl::Thread::yield method: can not design good test scenario to test up to now
    */
    class yield : public CppUnit::TestFixture
    {
    public:
        void yield_001()
        {
            // insert your test code here.
        }

        CPPUNIT_TEST_SUITE(yield);
        CPPUNIT_TEST(yield_001);
        CPPUNIT_TEST_SUITE_END();
    }; // class yield

    /** Test of the osl::Thread::schedule method
    */
    class schedule : public CppUnit::TestFixture
    {
    public:

        /** The requested thread will get terminate the next time schedule() is called.

            Note: on UNX, if call suspend thread is not the to be suspended thread, the to be
            suspended   thread will get suspended the next time schedule() is called,
            while on w32, it's nothing with schedule.

            check if suspend and terminate work well via schedule
        */
        void schedule_001()
            {
                OAddThread* aThread = new OAddThread();
                bool bRes = aThread->create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                ThreadHelper::thread_sleep_tenth_sec(2);
                aThread->suspend();
                ThreadHelper::thread_sleep_tenth_sec(1);
                sal_Int32 nValue = aThread->getValue();
                ThreadHelper::thread_sleep_tenth_sec(3);
                sal_Int32 nLaterValue = aThread->getValue();
                // resumeAndWaitThread(aThread);
                t_print("      value = %d\n", static_cast<int>(nValue));
                t_print("later value = %d\n", static_cast<int>(nLaterValue));
                // if value and latervalue not equal, then the thread would not suspended

                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "Schedule: suspend works.",
                    nValue, nLaterValue
                    );

                aThread->resume();
                ThreadHelper::thread_sleep_tenth_sec(2);

                aThread->terminate();
                sal_Int32 nValue_term = aThread->getValue();

                aThread->join();
                sal_Int32 nValue_join = aThread->getValue();

                t_print("value after term = %d\n", static_cast<int>(nValue_term));
                t_print("value after join = %d\n", static_cast<int>(nValue_join));

                // nValue_term and nValue_join should be the same
                // but should be differ from nValue

                delete aThread;
                //check if thread really terminate after call terminate, if join immediately return
                CPPUNIT_ASSERT_MESSAGE(
                    "Schedule: Returns False if the thread should terminate.",
                    nValue_join -  nValue_term <= 1
                    );
                CPPUNIT_ASSERT_MESSAGE(
                    "Schedule: Returns False if the thread should terminate.",
                    nValue_join -  nValue_term >= 0
                    );

            }

        /** design a thread that has not call schedule in the workfunction--run method
         */
        void schedule_002()
            {
                ONoScheduleThread aThread; // this thread runs 10 sec. (no schedule() used)
                bool bRes = aThread.create();
                CPPUNIT_ASSERT_MESSAGE ( "Can't start thread!", bRes );

                ThreadHelper::thread_sleep_tenth_sec(2);
                aThread.suspend();
                sal_Int32 nValue = aThread.getValue();

                ThreadHelper::thread_sleep_tenth_sec(3);
                sal_Int32 nLaterValue = aThread.getValue();
                ThreadHelper::thread_sleep_tenth_sec(5);

                resumeAndWaitThread(&aThread);

                t_print("      value = %d\n", static_cast<int>(nValue));
                t_print("later value = %d\n", static_cast<int>(nLaterValue));

                //On windows, suspend works, so the values are same
#ifdef _WIN32
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "Schedule: don't schedule in thread run method, suspend works.",
                    nValue, nLaterValue
                    );
#endif

                //On UNX, suspend does not work, so the difference of the values equals to sleep seconds number
#ifdef UNX
                aThread.resume();
                CPPUNIT_ASSERT_MESSAGE(
                    "Schedule: don't schedule in thread run method, suspend does not work too.",
                    nLaterValue > nValue
                    );
#endif

                // terminate will not work if no schedule in thread's work function
                termAndJoinThread(&aThread);
                sal_Int32 nValue_term = aThread.getValue();

                t_print(" value term = %d\n", static_cast<int>(nValue_term));

                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "Schedule: don't schedule in thread run method, terminate failed.",
                    static_cast<sal_Int32>(10), nValue_term
                    );
            }

        CPPUNIT_TEST_SUITE(schedule);
        CPPUNIT_TEST(schedule_001);
        CPPUNIT_TEST(schedule_002);
        CPPUNIT_TEST_SUITE_END();
    }; // class schedule

    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::create, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::createSuspended, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::suspend, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::resume, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::terminate, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::join, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::isRunning, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::setPriority, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::getPriority, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::getIdentifier, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::getCurrentIdentifier, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::waittest, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::yield, "osl_Thread");
    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Thread::schedule, "osl_Thread");
} // namespace osl_Thread

// destroy function when the binding thread terminate
static void destroyCallback(void * data)
{
    delete[] static_cast<char *>(data);
}

static ThreadData myThreadData(destroyCallback);

namespace {

class myKeyThread : public Thread
{
public:
    // a public char member for test result checking
    char m_Char_Test;
    // for pass thread-special data to thread
    explicit myKeyThread(const char cData)
        : m_Char_Test(0)
        {
            m_nData = cData;
        }
private:
    char m_nData;

    void SAL_CALL run() override
        {
            char * pc = new char[2];
//      strcpy(pc, &m_nData);
            memcpy(pc, &m_nData, 1);
            pc[1] = '\0';

            myThreadData.setData(pc);
            char* pData = static_cast<char*>(myThreadData.getData());
            m_Char_Test = *pData;
            // wait for long time to check the data value in main thread
            ThreadHelper::thread_sleep_tenth_sec(3);
        }
public:
    virtual ~myKeyThread() override
        {
            if (isRunning())
            {
                t_print("error: not terminated.\n");
            }
        }
};

}

static ThreadData idData;

namespace {

class idThread: public Thread
{
public:
    oslThreadIdentifier m_Id;
private:
    void SAL_CALL run() override
        {
            std::unique_ptr<oslThreadIdentifier> pId( new oslThreadIdentifier );
            *pId = getIdentifier();
            idData.setData(pId.get());
            oslThreadIdentifier* pIdData = static_cast<oslThreadIdentifier*>(idData.getData());
            //t_print("Thread %d has Data %d\n", getIdentifier(), *pIdData);
            m_Id = *pIdData;
        }

public:
    virtual ~idThread() override
        {
            if (isRunning())
            {
                t_print("error: not terminated.\n");
            }
        }
};

}

namespace osl_ThreadData
{

    class ctors : public CppUnit::TestFixture
    {
    public:

        // insert your test code here.
        void ctor_001()
            {

            }

        CPPUNIT_TEST_SUITE(ctors);
        CPPUNIT_TEST(ctor_001);
        CPPUNIT_TEST_SUITE_END();
    }; // class ctors

    class setData : public CppUnit::TestFixture
    {
    public:

        /** the same instance of the class can have different values in different threads
         */
        void setData_001()
            {
                idThread aThread1;
                aThread1.create();
                idThread aThread2;
                aThread2.create();

                aThread1.join();
                aThread2.join();

                oslThreadIdentifier aThreadId1 = aThread1.getIdentifier();
                oslThreadIdentifier aThreadId2 = aThread2.getIdentifier();

                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    aThread1.m_Id, aThreadId1
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    aThread2.m_Id, aThreadId2
                    );

            }

        void setData_002()
            {
                // at first, set the data a value
                char* pc = new char[2];
                char nData = 'm';
                pc[0] = nData;
                pc[1] = '\0';

                myThreadData.setData(pc);

                myKeyThread aThread1('a');
                aThread1.create();
                myKeyThread aThread2('b');
                aThread2.create();
                // aThread1 and aThread2 should have not terminated yet, check current data, not 'a' 'b'
                char* pChar = static_cast<char*>(myThreadData.getData());
                char aChar = *pChar;

                aThread1.join();
                aThread2.join();

                // the saved thread data of aThread1 & aThread2, different
                char cData1 = aThread1.m_Char_Test;
                char cData2 = aThread2.m_Char_Test;

                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'a', cData1
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'b', cData2
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'm', aChar
                    );

            }
        /** setData the second time, and then getData
         */
        void setData_003()
            {
                // at first, set the data a value
                char* pc = new char[2];
                char nData = 'm';
                memcpy(pc, &nData, 1);
                pc[1] = '\0';
                myThreadData.setData(pc);

                myKeyThread aThread1('a');
                aThread1.create();
                myKeyThread aThread2('b');
                aThread2.create();
                // aThread1 and aThread2 should have not terminated yet
                // setData the second time
                char* pc2 = new char[2];
                nData = 'o';
                memcpy(pc2, &nData, 1);
                pc2[1] = '\0';

                myThreadData.setData(pc2);
                char* pChar = static_cast<char*>(myThreadData.getData());
                char aChar = *pChar;

                aThread1.join();
                aThread2.join();

                // the saved thread data of aThread1 & aThread2, different
                char cData1 = aThread1.m_Char_Test;
                char cData2 = aThread2.m_Char_Test;

                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'a', cData1
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'b', cData2
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'o', aChar
                    );
            }

        CPPUNIT_TEST_SUITE(setData);
        CPPUNIT_TEST(setData_001);
        CPPUNIT_TEST(setData_002);
        CPPUNIT_TEST(setData_003);
        CPPUNIT_TEST_SUITE_END();
    }; // class setData

    class getData : public CppUnit::TestFixture
    {
    public:

        // After setData in child threads, get Data in the main thread, should be independent
        void getData_001()
            {
                char* pc = new char[2];
                strcpy(pc, "i");
                myThreadData.setData(pc);

                myKeyThread aThread1('c');
                aThread1.create();
                myKeyThread aThread2('d');
                aThread2.create();

                aThread1.join();
                aThread2.join();

                char cData1 = aThread1.m_Char_Test;
                char cData2 = aThread2.m_Char_Test;

                char* pChar = static_cast<char*>(myThreadData.getData());
                char aChar = *pChar;

                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'c', cData1
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'd', cData2
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'i', aChar
                    );
            }

        // setData then change the value in the address data pointer points,
        // and then getData, should get the new value
        void getData_002()
            {
                char* pc = new char[2];
                char nData = 'i';
                memcpy(pc, &nData, 1);
                pc[1] = '\0';

                myThreadData.setData(pc);

                myKeyThread aThread1('a');
                aThread1.create();
                myKeyThread aThread2('b');
                aThread2.create();

                // change the value which pc points
                char nData2 = 'j';
                memcpy(pc, &nData2, 1);
                pc[1] = '\0';

                void* pChar = myThreadData.getData();
                char aChar = *static_cast<char*>(pChar);

                aThread1.join();
                aThread2.join();

                char cData1 = aThread1.m_Char_Test;
                char cData2 = aThread2.m_Char_Test;

                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'a', cData1
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'b', cData2
                    );
                CPPUNIT_ASSERT_EQUAL_MESSAGE(
                    "ThreadData setData: ",
                    'j', aChar
                    );

            }

        CPPUNIT_TEST_SUITE(getData);
        CPPUNIT_TEST(getData_001);
        CPPUNIT_TEST(getData_002);
        CPPUNIT_TEST_SUITE_END();
    }; // class getData

    CPPUNIT_TEST_SUITE_REGISTRATION(osl_ThreadData::ctors);
    CPPUNIT_TEST_SUITE_REGISTRATION(osl_ThreadData::setData);
    CPPUNIT_TEST_SUITE_REGISTRATION(osl_ThreadData::getData);
} // namespace osl_ThreadData

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
