blob: 9ec1761e43b19c74ab626909d29a0be6c4cc2682 [file] [log] [blame]
/*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AbstractExecutorServiceTest.h"
#include <string>
#include <decaf/lang/Runnable.h>
#include <decaf/lang/Pointer.h>
#include <decaf/lang/exceptions/NullPointerException.h>
#include <decaf/util/ArrayList.h>
#include <decaf/util/concurrent/LinkedBlockingQueue.h>
#include <decaf/util/concurrent/TimeUnit.h>
#include <decaf/util/concurrent/Future.h>
#include <decaf/util/concurrent/AbstractExecutorService.h>
#include <decaf/util/concurrent/ThreadPoolExecutor.h>
using namespace std;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
using namespace decaf::util;
using namespace decaf::util::concurrent;
////////////////////////////////////////////////////////////////////////////////
namespace {
/**
* A no-frills implementation of AbstractExecutorService, designed
* to test the submit methods only.
*/
class DirectExecutorService : public AbstractExecutorService {
private:
volatile bool isshutdown;
public:
DirectExecutorService() : AbstractExecutorService(), isshutdown(false) {
}
virtual ~DirectExecutorService() {}
virtual void execute(Runnable* r) {
this->execute(r, true);
}
virtual void execute(Runnable* r, bool own) {
if (r == NULL) {
throw NullPointerException(__FILE__, __LINE__, "Runnable was NULL");
}
try {
r->run();
} catch(Exception& e) {
if (own) {
delete r;
}
throw;
}
if (own) {
delete r;
}
}
virtual void shutdown() {
isshutdown = true;
}
virtual ArrayList<Runnable*> shutdownNow() {
isshutdown = true;
return ArrayList<Runnable*>();
}
virtual bool isShutdown() const {
return isshutdown;
}
virtual bool isTerminated() const {
return isShutdown();
}
virtual bool awaitTermination(long long timeout, const TimeUnit& unit) {
return isShutdown();
}
};
}
////////////////////////////////////////////////////////////////////////////////
AbstractExecutorServiceTest::AbstractExecutorServiceTest() {
}
////////////////////////////////////////////////////////////////////////////////
AbstractExecutorServiceTest::~AbstractExecutorServiceTest() {
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testExecuteRunnable() {
try {
bool done = false;
DirectExecutorService e;
TrackedShortRunnable task(&done);
CPPUNIT_ASSERT(!done);
Future<int>* future = e.submit<int>(&task, false);
future->get();
CPPUNIT_ASSERT(done);
delete future;
} catch (ExecutionException& ex) {
unexpectedException();
} catch (InterruptedException& ex) {
unexpectedException();
}
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testSubmitCallable() {
try {
DirectExecutorService e;
Future<string>* future = e.submit(new StringTask());
string result = future->get();
CPPUNIT_ASSERT_EQUAL(TEST_STRING, result);
delete future;
} catch (ExecutionException& ex) {
unexpectedException();
} catch (InterruptedException& ex) {
unexpectedException();
}
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testSubmitRunnable() {
try {
DirectExecutorService e;
Future<int>* future = e.submit<int>(new NoOpRunnable());
future->get();
CPPUNIT_ASSERT(future->isDone());
delete future;
} catch (ExecutionException& ex) {
unexpectedException();
} catch (InterruptedException& ex) {
unexpectedException();
}
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testSubmitRunnable2() {
try {
DirectExecutorService e;
Future<string>* future = e.submit(new NoOpRunnable(), TEST_STRING);
string result = future->get();
CPPUNIT_ASSERT_EQUAL(TEST_STRING, result);
delete future;
} catch (ExecutionException& ex) {
unexpectedException();
} catch (InterruptedException& ex) {
unexpectedException();
}
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testExecuteNullRunnable() {
try {
DirectExecutorService e;
TrackedShortRunnable* task = NULL;
e.submit<int>(task);
shouldThrow();
} catch (NullPointerException& success) {
} catch (Exception& ex) {
unexpectedException();
}
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testSubmitNullCallable() {
try {
DirectExecutorService e;
StringTask* t = NULL;
e.submit(t);
shouldThrow();
} catch (NullPointerException& success) {
} catch (Exception& ex) {
unexpectedException();
}
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testExecute1() {
ThreadPoolExecutor p(1, 1, 60, TimeUnit::SECONDS, new LinkedBlockingQueue<Runnable*>(1));
MediumRunnable task(this);
try {
for(int i = 0; i < 5; ++i) {
Pointer< Future<int> > future(p.submit<int>(&task, false));
}
shouldThrow();
} catch(RejectedExecutionException& success) {
}
joinPool(p);
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testExecute2() {
ThreadPoolExecutor p(1, 1, 60, TimeUnit::SECONDS, new LinkedBlockingQueue<Runnable*>(1));
try {
for(int i = 0; i < 5; ++i) {
delete p.submit(new SmallCallable<int>(this));
}
shouldThrow();
} catch(RejectedExecutionException& e) {
}
joinPool(p);
}
////////////////////////////////////////////////////////////////////////////////
namespace {
template<typename E>
class TestInterruptedSubmitCallable : public Callable<E> {
private:
AbstractExecutorServiceTest* parent;
private:
TestInterruptedSubmitCallable(const TestInterruptedSubmitCallable&);
TestInterruptedSubmitCallable operator= (const TestInterruptedSubmitCallable&);
public:
TestInterruptedSubmitCallable(AbstractExecutorServiceTest* parent) :
decaf::util::concurrent::Callable<E>(), parent(parent) {
}
virtual ~TestInterruptedSubmitCallable() {}
virtual E call() {
try {
Thread::sleep(AbstractExecutorServiceTest::LONG_DELAY_MS);
parent->threadShouldThrow();
} catch(InterruptedException& e){
}
return E();
}
};
class TestInterruptedSubmitRunnable : public Runnable {
private:
AbstractExecutorServiceTest* parent;
ThreadPoolExecutor* executor;
private:
TestInterruptedSubmitRunnable(const TestInterruptedSubmitRunnable&);
TestInterruptedSubmitRunnable operator= (const TestInterruptedSubmitRunnable&);
public:
TestInterruptedSubmitRunnable(AbstractExecutorServiceTest* parent, ThreadPoolExecutor* executor) :
Runnable(), parent(parent), executor(executor) {
}
virtual ~TestInterruptedSubmitRunnable() {}
virtual void run() {
try {
Pointer< Future<int> > future(executor->submit(new TestInterruptedSubmitCallable<int>(parent)));
future->get();
} catch(InterruptedException& success){
} catch(Exception& e) {
parent->unexpectedException();
}
}
};
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testInterruptedSubmit() {
// TODO
// ThreadPoolExecutor p(1, 1, 60, TimeUnit::SECONDS, new LinkedBlockingQueue<Runnable*>(10));
// TestInterruptedSubmitRunnable runner(this, &p);
// Thread t(&runner);
//
// try {
//
// t.start();
// Thread::sleep(SHORT_DELAY_MS);
// t.interrupt();
//
// } catch(Exception& e){
// unexpectedException();
// }
//
// joinPool(p);
}
////////////////////////////////////////////////////////////////////////////////
namespace {
template<typename E>
class CallingRunnable : public decaf::lang::Runnable {
private:
AbstractExecutorServiceTest* parent;
Callable<E>* target;
private:
CallingRunnable(const CallingRunnable&);
CallingRunnable operator= (const CallingRunnable&);
public:
CallingRunnable(AbstractExecutorServiceTest* parent, Callable<E>* target) :
Runnable(), parent(parent), target(target) {
}
virtual ~CallingRunnable() {
delete target;
}
virtual void run() {
try {
target->call();
} catch(Exception& e) {
}
}
};
template<typename E>
class TestSubmitIECallable : public Callable<E> {
private:
AbstractExecutorServiceTest* parent;
ThreadPoolExecutor* executor;
private:
TestSubmitIECallable(const TestSubmitIECallable&);
TestSubmitIECallable operator= (const TestSubmitIECallable&);
public:
TestSubmitIECallable(AbstractExecutorServiceTest* parent, ThreadPoolExecutor* executor) :
Callable<E>(), parent(parent), executor(executor) {
}
virtual ~TestSubmitIECallable() {}
virtual E call() {
try {
Pointer< Future<E> >(executor->submit(
new AbstractExecutorServiceTest::SmallCallable<E>(parent)))->get();
parent->threadShouldThrow();
} catch(InterruptedException& e){
} catch(RejectedExecutionException& e2){
} catch(ExecutionException& e3){
}
return E();
}
};
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testSubmitIE() {
ThreadPoolExecutor p(1, 1, 60, TimeUnit::SECONDS, new LinkedBlockingQueue<Runnable*>(10));
TestSubmitIECallable<int>* callable = new TestSubmitIECallable<int>(this, &p);
CallingRunnable<int> runner(this, callable);
Thread t(&runner);
try {
t.start();
Thread::sleep(SHORT_DELAY_MS);
t.interrupt();
t.join();
} catch(InterruptedException& e){
unexpectedException();
}
joinPool(p);
}
////////////////////////////////////////////////////////////////////////////////
namespace {
class testSubmitEECallable : public Callable<int> {
public:
virtual ~testSubmitEECallable() {}
virtual int call() {
throw NumberFormatException(__FILE__, __LINE__, "Throwing a common exception");
return 1;
}
};
}
////////////////////////////////////////////////////////////////////////////////
void AbstractExecutorServiceTest::testSubmitEE() {
ThreadPoolExecutor p(1, 1, 60, TimeUnit::SECONDS, new LinkedBlockingQueue<Runnable*>(10));
testSubmitEECallable c;
try {
for(int i = 0; i < 5; i++) {
Pointer< Future<int> >(p.submit(&c, false))->get();
}
shouldThrow();
} catch(ExecutionException& success) {
} catch(Exception& e) {
unexpectedException();
}
joinPool(p);
}