summaryrefslogtreecommitdiffstats
path: root/sal/qa/osl/process
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sal/qa/osl/process
parentInitial commit. (diff)
downloadlibreoffice-upstream/4%7.4.7.tar.xz
libreoffice-upstream/4%7.4.7.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sal/qa/osl/process')
-rwxr-xr-xsal/qa/osl/process/batch.bat19
-rwxr-xr-xsal/qa/osl/process/batch.sh2
-rw-r--r--sal/qa/osl/process/osl_Thread.cxx2005
-rw-r--r--sal/qa/osl/process/osl_Thread.xsce18
-rw-r--r--sal/qa/osl/process/osl_process.cxx450
-rw-r--r--sal/qa/osl/process/osl_process_child.cxx101
6 files changed, 2595 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..3f62c6ea3
--- /dev/null
+++ b/sal/qa/osl/process/osl_Thread.cxx
@@ -0,0 +1,2005 @@
+/* -*- 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 );
+ 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";
+ 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
+ );
+ 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: */
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..c396a06b5
--- /dev/null
+++ b/sal/qa/osl/process/osl_process.cxx
@@ -0,0 +1,450 @@
+/* -*- 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 = OUString::Concat(dirPath.subView( 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
+ {
+ ENV1,
+ ENV2,
+ 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: */