diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sal/qa/osl/process | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sal/qa/osl/process')
-rwxr-xr-x | sal/qa/osl/process/batch.bat | 19 | ||||
-rwxr-xr-x | sal/qa/osl/process/batch.sh | 2 | ||||
-rw-r--r-- | sal/qa/osl/process/osl_Thread.cxx | 1989 | ||||
-rw-r--r-- | sal/qa/osl/process/osl_Thread.xsce | 18 | ||||
-rw-r--r-- | sal/qa/osl/process/osl_process.cxx | 448 | ||||
-rw-r--r-- | sal/qa/osl/process/osl_process_child.cxx | 101 |
6 files changed, 2577 insertions, 0 deletions
diff --git a/sal/qa/osl/process/batch.bat b/sal/qa/osl/process/batch.bat new file mode 100755 index 000000000..e27f7191b --- /dev/null +++ b/sal/qa/osl/process/batch.bat @@ -0,0 +1,19 @@ +rem +rem This file is part of the LibreOffice project. +rem +rem This Source Code Form is subject to the terms of the Mozilla Public +rem License, v. 2.0. If a copy of the MPL was not distributed with this +rem file, You can obtain one at http://mozilla.org/MPL/2.0/. +rem +rem This file incorporates work covered by the following license notice: +rem +rem Licensed to the Apache Software Foundation (ASF) under one or more +rem contributor license agreements. See the NOTICE file distributed +rem with this work for additional information regarding copyright +rem ownership. The ASF licenses this file to you under the Apache +rem License, Version 2.0 (the "License"); you may not use this file +rem except in compliance with the License. You may obtain a copy of +rem the License at http://www.apache.org/licenses/LICENSE-2.0 . +rem +@echo off +echo "Hello world"
\ No newline at end of file diff --git a/sal/qa/osl/process/batch.sh b/sal/qa/osl/process/batch.sh new file mode 100755 index 000000000..fd3828c6a --- /dev/null +++ b/sal/qa/osl/process/batch.sh @@ -0,0 +1,2 @@ +#!/bin/sh +echo "Hello world"
\ No newline at end of file diff --git a/sal/qa/osl/process/osl_Thread.cxx b/sal/qa/osl/process/osl_Thread.cxx new file mode 100644 index 000000000..d551f471d --- /dev/null +++ b/sal/qa/osl/process/osl_Thread.cxx @@ -0,0 +1,1989 @@ +/* -*- 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> +#else +#include <unistd.h> +#include <time.h> +#endif + +// include files + +#include <sal/types.h> +#include <rtl/string.hxx> +#include <rtl/strbuf.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> +#include <cppunit/plugin/TestPlugIn.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 && 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"; + break; + + case osl_Thread_PriorityAboveNormal: + sPrioStr = "AboveNormal"; + break; + + case osl_Thread_PriorityNormal: + sPrioStr = "Normal"; + break; + + case osl_Thread_PriorityBelowNormal: + sPrioStr = "BelowNormal"; + break; + + case osl_Thread_PriorityLowest: + sPrioStr = "Lowest"; + break; + default: + sPrioStr = "unknown"; + } + 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 && + nValueAboveNormal > 0 && + 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 && + 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_MESSAGE( + "ThreadData setData: ", + cData1 == 'a' && cData2 == 'b' + ); + 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: */ diff --git a/sal/qa/osl/process/osl_Thread.xsce b/sal/qa/osl/process/osl_Thread.xsce new file mode 100644 index 000000000..11d0c5da1 --- /dev/null +++ b/sal/qa/osl/process/osl_Thread.xsce @@ -0,0 +1,18 @@ +# +# 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 . +# +osl_Thread.setPriority.setPriority_001_1 unxsols4 diff --git a/sal/qa/osl/process/osl_process.cxx b/sal/qa/osl/process/osl_process.cxx new file mode 100644 index 000000000..4555a0d83 --- /dev/null +++ b/sal/qa/osl/process/osl_process.cxx @@ -0,0 +1,448 @@ +/* -*- 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 . + */ + +#ifdef IOS +#define CPPUNIT_PLUGIN_EXPORTED_NAME cppunitTest_osl_process +#endif + +#include <sal/types.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +#include <osl/process.h> +#include <osl/file.hxx> +#include <osl/thread.h> +#include <rtl/ustring.hxx> +#include <signal.h> + +#include <stdio.h> +#include <stdlib.h> +#include <osl/module.hxx> +#include <sal/macros.h> + +#if defined HAVE_VALGRIND_HEADERS +#include <valgrind/valgrind.h> +#elif !defined _WIN32 +#define RUNNING_ON_VALGRIND false +#endif + +#if !defined(_WIN32) // Windows +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include <vector> +#include <algorithm> +#include <iterator> +#include <string> + +#ifdef UNX +#if defined( MACOSX ) +# include <crt_externs.h> +# define environ (*_NSGetEnviron()) +# else + extern char** environ; +# endif +#endif + +using namespace osl; + +/** get binary Path. +*/ +static OUString getExecutablePath() +{ + OUString dirPath; + osl::Module::getUrlFromAddress( + reinterpret_cast<oslGenericFunction>(&getExecutablePath), dirPath); + dirPath = dirPath.copy( 0, dirPath.lastIndexOf('/') ); + dirPath = dirPath.copy( 0, dirPath.lastIndexOf('/') + 1) + + "Executable"; + return dirPath; +} + +#if !defined _WIN32 + +namespace { + +class exclude +{ +public: + + explicit exclude(const std::vector<OString>& exclude_list) + { + for (auto& exclude_list_item : exclude_list) + exclude_list_.push_back(env_var_name(exclude_list_item)); + } + + bool operator() (const OString& env_var) const + { + return (exclude_list_.end() != + std::find( + exclude_list_.begin(), + exclude_list_.end(), + env_var_name(env_var))); + } + +private: + + // extract the name from an environment variable + // that is given in the form "NAME=VALUE" + static OString env_var_name(const OString& env_var) + { + sal_Int32 pos_equal_sign = + env_var.indexOf('='); + + if (pos_equal_sign != -1) + return env_var.copy(0, pos_equal_sign); + + return OString(); + } + +private: + std::vector<OString> exclude_list_; +}; + + void tidy_container(std::vector<OString> &env_container) + { + //sort them because there are no guarantees to ordering + std::sort(env_container.begin(), env_container.end()); + if (RUNNING_ON_VALGRIND) + { + env_container.erase( + std::remove_if( + env_container.begin(), env_container.end(), + [](OString const & s) { + return s.startsWith("LD_PRELOAD=") + || s.startsWith("VALGRIND_LIB="); }), + env_container.end()); + } + } +} + + static void read_parent_environment(std::vector<OString>* env_container) + { + for (int i = 0; environ[i] != nullptr; i++) + env_container->push_back(OString(environ[i])); + tidy_container(*env_container); + } + +#endif + +class Test_osl_executeProcess : public CppUnit::TestFixture +{ + const OUString env_param_; + + OUString temp_file_url_; + OUString temp_file_path_; + rtl_uString* parameters_[2]; + static const int parameters_count_ = 2; + OUString suCWD; + OUString suExecutableFileURL; + +public: + + // ctor + Test_osl_executeProcess() : + env_param_(OUString("-env")), suCWD(getExecutablePath()) + { + parameters_[0] = env_param_.pData; + +#if defined(_WIN32) + suExecutableFileURL = suCWD + "/" "osl_process_child.exe"; +#else + suExecutableFileURL = suCWD + "/" "osl_process_child"; +#endif + } + + virtual void setUp() override + { + temp_file_path_ = create_temp_file(temp_file_url_); + parameters_[1] = temp_file_path_.pData; + } + + virtual void tearDown() override + { + osl::File::remove(temp_file_url_); + } + + OUString create_temp_file(OUString &temp_file_url) + { + FileBase::RC rc = FileBase::createTempFile(nullptr, nullptr, &temp_file_url); + CPPUNIT_ASSERT_EQUAL_MESSAGE("createTempFile failed", FileBase::E_None, rc); + + OUString temp_file_path; + rc = FileBase::getSystemPathFromFileURL(temp_file_url, temp_file_path); + CPPUNIT_ASSERT_EQUAL_MESSAGE("getSystemPathFromFileURL failed", FileBase::E_None, rc); + + return temp_file_path; + } + +#if !defined _WIN32 + + void read_child_environment(std::vector<OString>* env_container) + { + OString temp_file_name = OUStringToOString(OUString( + parameters_[1]), osl_getThreadTextEncoding()); + std::ifstream file(temp_file_name.getStr()); + + CPPUNIT_ASSERT_MESSAGE + ( + "I/O error, cannot open child environment file", + file.is_open() + ); + + std::string line; + line.reserve(1024); + while (std::getline(file, line, '\0')) + env_container->push_back(OString(line.c_str())); + tidy_container(*env_container); + } + + // environment of the child process that was + // started. The child process writes his + // environment into a file + void compare_environments() + { + std::vector<OString> parent_env; + read_parent_environment(&parent_env); + + std::vector<OString> child_env; + read_child_environment(&child_env); + + OString msg( + OString::number(parent_env.size()) + "/" + + OString::number(child_env.size())); + auto min = std::min(parent_env.size(), child_env.size()); + for (decltype(min) i = 0; i != min; ++i) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + msg.getStr(), parent_env[i], child_env[i]); + } + if (parent_env.size() != child_env.size()) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + (parent_env.size() >= child_env.size() + ? parent_env.back() : child_env.back()).getStr(), + parent_env.size(), child_env.size()); + } + } + + // compare the equal environment parts and the + // different part of the child environment + bool compare_merged_environments(const std::vector<OString>& different_env_vars) + { + std::vector<OString> parent_env; + read_parent_environment(&parent_env); + + for (auto& env : parent_env) + std::cout << "initially parent env: " << env << "\n"; + + //remove the environment variables that we have changed + //in the child environment from the read parent environment + parent_env.erase( + std::remove_if(parent_env.begin(), parent_env.end(), exclude(different_env_vars)), + parent_env.end()); + + for (auto& env : parent_env) + std::cout << "stripped parent env: " << env << "\n"; + + //read the child environment and exclude the variables that + //are different + std::vector<OString> child_env; + read_child_environment(&child_env); + + for (auto& env : child_env) + std::cout << "initial child env: " << env << "\n"; + //partition the child environment into the variables that + //are different to the parent environment (they come first) + //and the variables that should be equal between parent + //and child environment + auto iter_logical_end = + std::stable_partition(child_env.begin(), child_env.end(), exclude(different_env_vars)); + + std::vector<OString> different_child_env_vars(child_env.begin(), iter_logical_end); + child_env.erase(child_env.begin(), iter_logical_end); + + for (auto& env : child_env) + std::cout << "stripped child env: " << env << "\n"; + + bool common_env_size_equals = (parent_env.size() == child_env.size()); + bool common_env_content_equals = std::equal(child_env.begin(), child_env.end(), parent_env.begin()); + + for (auto& env_var : different_env_vars) + std::cout << "different should be: " << env_var << "\n"; + + for (auto& env_var : different_child_env_vars) + std::cout << "different are: " << env_var << "\n"; + + bool different_env_size_equals = (different_child_env_vars.size() == different_env_vars.size()); + bool different_env_content_equals = + std::equal(different_env_vars.begin(), different_env_vars.end(), different_child_env_vars.begin()); + + return (common_env_size_equals && common_env_content_equals && + different_env_size_equals && different_env_content_equals); + } + + // test that parent and child process have the + // same environment when osl_executeProcess will + // be called without setting new environment + // variables + void osl_execProc_parent_equals_child_environment() + { + oslProcess process; + oslProcessError osl_error = osl_executeProcess( + suExecutableFileURL.pData, + parameters_, + parameters_count_, + osl_Process_NORMAL, + nullptr, + suCWD.pData, + nullptr, + 0, + &process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_createProcess failed", + osl_Process_E_None, osl_error + ); + + osl_error = ::osl_joinProcess(process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_joinProcess returned with failure", + osl_Process_E_None, osl_error + ); + + osl_freeProcessHandle(process); + + compare_environments(); + } + + #define ENV1 "PAT=a:\\" + #define ENV2 "PATHb=b:\\" + #define ENV3 "Patha=c:\\" + #define ENV4 "Patha=d:\\" + + void osl_execProc_merged_child_environment() + { + rtl_uString* child_env[4]; + OUString env1(ENV1); + OUString env2(ENV2); + OUString env3(ENV3); + OUString env4(ENV4); + + child_env[0] = env1.pData; + child_env[1] = env2.pData; + child_env[2] = env3.pData; + child_env[3] = env4.pData; + + oslProcess process; + oslProcessError osl_error = osl_executeProcess( + suExecutableFileURL.pData, + parameters_, + parameters_count_, + osl_Process_NORMAL, + nullptr, + suCWD.pData, + child_env, + SAL_N_ELEMENTS(child_env), + &process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_createProcess failed", + osl_Process_E_None, osl_error + ); + + osl_error = ::osl_joinProcess(process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_joinProcess returned with failure", + osl_Process_E_None, osl_error + ); + + osl_freeProcessHandle(process); + + std::vector<OString> different_child_env_vars; + different_child_env_vars.push_back(ENV1); + different_child_env_vars.push_back(ENV2); + different_child_env_vars.push_back(ENV4); + + CPPUNIT_ASSERT_MESSAGE + ( + "osl_execProc_merged_child_environment", + compare_merged_environments(different_child_env_vars) + ); + } + +#endif + + void osl_execProc_test_batch() + { + oslProcess process; +#if defined(_WIN32) + OUString suBatch = suCWD + "/batch.bat"; +#else + OUString suBatch = suCWD + "/batch.sh"; +#endif + oslProcessError osl_error = osl_executeProcess( + suBatch.pData, + nullptr, + 0, + osl_Process_NORMAL, + nullptr, + suCWD.pData, + nullptr, + 0, + &process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_createProcess failed", + osl_Process_E_None, osl_error + ); + + osl_error = ::osl_joinProcess(process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_joinProcess returned with failure", + osl_Process_E_None, osl_error + ); + + osl_freeProcessHandle(process); + } + + CPPUNIT_TEST_SUITE(Test_osl_executeProcess); + //TODO: Repair these (at least under Windows) +#if !defined(_WIN32) + CPPUNIT_TEST(osl_execProc_parent_equals_child_environment); + CPPUNIT_TEST(osl_execProc_merged_child_environment); +#endif + CPPUNIT_TEST(osl_execProc_test_batch); + ///TODO: Repair test (or tested function ;-) - test fails. + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(Test_osl_executeProcess); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/qa/osl/process/osl_process_child.cxx b/sal/qa/osl/process/osl_process_child.cxx new file mode 100644 index 000000000..17f749bc2 --- /dev/null +++ b/sal/qa/osl/process/osl_process_child.cxx @@ -0,0 +1,101 @@ +/* -*- 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 . + */ + +#if defined(_WIN32) // Windows +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#else +# include <unistd.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <fstream> +#include <string.h> + + +#ifdef UNX +#if defined( MACOSX ) +# include <crt_externs.h> +# define environ (*_NSGetEnviron()) +# else + extern char** environ; +# endif +#endif + +#ifdef _WIN32 +# define SLEEP(t) (Sleep((t)*1000)) +#else +# define SLEEP(t) (sleep((t))) +#endif + +static void wait_for_seconds(char* time) +{ + SLEEP(atoi(time)); +} + +#ifdef _WIN32 + +static void w_to_a(LPCWSTR strW, LPSTR strA, DWORD size) +{ + WideCharToMultiByte(CP_ACP, 0, strW, -1, strA, size, nullptr, nullptr); +} + + static void dump_env(char* file_path) + { + LPWSTR env = GetEnvironmentStringsW(); + LPWSTR p = env; + + std::ofstream file(file_path); + + char buffer[32767]; + while (size_t l = wcslen(p)) + { + w_to_a(p, buffer, sizeof(buffer)); + file << buffer << '\0'; + p += l + 1; + } + FreeEnvironmentStringsW(env); + } +#else + static void dump_env(char* file_path) + { + std::ofstream file(file_path); + for (int i = 0; environ[i] != nullptr; ++i) + file << environ[i] << '\0'; + } +#endif + +int main(int argc, char* argv[]) +{ + if (argc > 2) + { + if (strcmp("-join", argv[1]) == 0) + { + // coverity[tainted_data] - this is a build-time only test tool + wait_for_seconds(argv[2]); + } + else if (strcmp("-env", argv[1]) == 0) + dump_env(argv[2]); + } + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |