blob: 573fb4b7999e3d561cc541080531a63c40383ea7 [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 "PointerTest.h"
#include <decaf/lang/Pointer.h>
#include <decaf/lang/Thread.h>
#include <decaf/lang/Runnable.h>
#include <decaf/lang/exceptions/ClassCastException.h>
#include <decaf/util/concurrent/CountDownLatch.h>
#include <map>
#include <string>
#include <vector>
using namespace std;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
using namespace decaf::util::concurrent;
////////////////////////////////////////////////////////////////////////////////
class TestClassBase {
protected:
std::vector<int> content;
public:
TestClassBase() : content() {}
virtual ~TestClassBase() {}
virtual std::string returnHello() = 0;
int getSize() const {
return (int)content.size();
}
};
////////////////////////////////////////////////////////////////////////////////
class TestClassA : public TestClassBase {
public:
TestClassA() : TestClassBase() {
this->content.resize(1);
}
virtual ~TestClassA() {
}
std::string returnHello() {
return "Hello";
}
};
////////////////////////////////////////////////////////////////////////////////
class TestClassB : public TestClassBase {
public:
TestClassB() : TestClassBase() {
this->content.resize(2);
}
virtual ~TestClassB() {
}
std::string returnHello() {
return "GoodBye";
}
};
////////////////////////////////////////////////////////////////////////////////
class SomeOtherClass {
public:
};
////////////////////////////////////////////////////////////////////////////////
class ExceptionThrowingClass {
public:
ExceptionThrowingClass() {
throw std::bad_alloc();
}
};
////////////////////////////////////////////////////////////////////////////////
struct X {
Pointer<X> next;
X() : next() {}
};
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testBasics() {
TestClassA* thePointer = new TestClassA();
// Test Null Initialize
Pointer<TestClassA> nullPointer;
CPPUNIT_ASSERT( nullPointer.get() == NULL );
CPPUNIT_ASSERT( nullPointer == NULL );
CPPUNIT_ASSERT_NO_THROW( nullPointer.reset( NULL ) );
// Test Value Constructor
Pointer<TestClassA> pointer( thePointer );
CPPUNIT_ASSERT( pointer.get() == thePointer );
CPPUNIT_ASSERT( pointer.get() != NULL );
// Test Copy Constructor
Pointer<TestClassA> ctorCopy( pointer );
CPPUNIT_ASSERT( ctorCopy.get() == thePointer );
// Test Assignment
Pointer<TestClassA> copy = pointer;
CPPUNIT_ASSERT( copy.get() == thePointer );
CPPUNIT_ASSERT( ( *pointer ).returnHello() == "Hello" );
CPPUNIT_ASSERT( pointer->returnHello() == "Hello" );
copy.reset( NULL );
CPPUNIT_ASSERT( copy.get() == NULL );
Pointer<X> p( new X );
p->next = Pointer<X>( new X );
p = p->next;
CPPUNIT_ASSERT( !p->next );
try{
Pointer<ExceptionThrowingClass> ex( new ExceptionThrowingClass() );
CPPUNIT_FAIL( "Should Have Thrown." );
} catch(...) {}
}
////////////////////////////////////////////////////////////////////////////////
template<typename T>
void ConstReferenceMethod( const Pointer<T>& pointer ) {
Pointer<T> copy = pointer;
CPPUNIT_ASSERT( copy.get() != NULL );
}
////////////////////////////////////////////////////////////////////////////////
template<typename T>
void ReferenceMethod( Pointer<T>& pointer ) {
pointer.reset( NULL );
CPPUNIT_ASSERT( pointer.get() == NULL );
}
////////////////////////////////////////////////////////////////////////////////
Pointer<TestClassA> ReturnByValue() {
Pointer<TestClassA> pointer( new TestClassA );
CPPUNIT_ASSERT( pointer.get() != NULL );
return pointer;
}
////////////////////////////////////////////////////////////////////////////////
const Pointer<TestClassA>& ReturnByConstReference() {
static Pointer<TestClassA> pointer( new TestClassA );
CPPUNIT_ASSERT( pointer.get() != NULL );
return pointer;
}
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testAssignment() {
TestClassA* thePointerA = new TestClassA();
TestClassB* thePointerB = new TestClassB();
Pointer<TestClassBase> pointer;
CPPUNIT_ASSERT( pointer.get() == NULL );
pointer.reset( thePointerA );
CPPUNIT_ASSERT( pointer.get() == thePointerA );
pointer.reset( thePointerB );
CPPUNIT_ASSERT( pointer.get() == thePointerB );
// Doing this however won't compile.
// SomeOtherClass other;
// pointer.reset( &other );
Pointer<TestClassA> pointer1( new TestClassA() );
Pointer<TestClassA> pointer2 = pointer1;
Pointer<TestClassA> pointer3 = pointer2;
CPPUNIT_ASSERT( pointer1.get() == pointer2.get() );
CPPUNIT_ASSERT( pointer2.get() == pointer3.get() );
pointer3.reset( NULL );
CPPUNIT_ASSERT( pointer1.get() != NULL );
CPPUNIT_ASSERT( pointer2.get() != NULL );
CPPUNIT_ASSERT( pointer3.get() == NULL );
ConstReferenceMethod( pointer1 );
ReferenceMethod( pointer2 );
CPPUNIT_ASSERT( pointer2.get() == NULL );
ReturnByValue();
{
Pointer<TestClassA> copy = ReturnByValue();
CPPUNIT_ASSERT( copy.get() != NULL );
}
{
Pointer<TestClassA> copy = ReturnByConstReference();
CPPUNIT_ASSERT( copy.get() != NULL );
}
ReturnByConstReference();
}
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testComparisons() {
Pointer<TestClassBase> pointer1( new TestClassA );
Pointer<TestClassBase> pointer2( new TestClassB );
TestClassA* raw1 = new TestClassA;
CPPUNIT_ASSERT( ( pointer1 == pointer2 ) == false );
CPPUNIT_ASSERT( ( pointer1 != pointer2 ) == true );
CPPUNIT_ASSERT( ( pointer1 == raw1 ) == false );
CPPUNIT_ASSERT( ( pointer1 != raw1 ) == true );
CPPUNIT_ASSERT( ( raw1 == pointer2 ) == false );
CPPUNIT_ASSERT( ( raw1 != pointer2 ) == true );
delete raw1;
Pointer<TestClassBase> pointer3( new TestClassA );
Pointer<TestClassA> pointer4( new TestClassA );
CPPUNIT_ASSERT( ( pointer3 == pointer4 ) == false );
CPPUNIT_ASSERT( ( pointer3 != pointer4 ) == true );
CPPUNIT_ASSERT( pointer1 != NULL );
CPPUNIT_ASSERT( !pointer1 == false );
CPPUNIT_ASSERT( !!pointer1 == true );
// This won't compile which is correct.
//Pointer<TestClassB> pointer5( new TestClassB );
//Pointer<TestClassA> pointer6( new TestClassA );
//CPPUNIT_ASSERT( pointer5 != pointer6 );
}
////////////////////////////////////////////////////////////////////////////////
class PointerTestRunnable : public decaf::lang::Runnable {
private:
Pointer<TestClassA> mine;
public:
PointerTestRunnable( const Pointer<TestClassA>& value ) : mine( value ) {}
void run() {
for( int i = 0; i < 999; ++i ) {
Pointer<TestClassBase> copy = this->mine;
CPPUNIT_ASSERT( copy->returnHello() == "Hello" );
copy.reset( new TestClassB() );
CPPUNIT_ASSERT( copy->returnHello() == "GoodBye" );
}
}
};
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testThreaded1() {
Pointer<TestClassA> pointer( new TestClassA() );
PointerTestRunnable runnable( pointer );
Thread testThread( &runnable );
testThread.start();
for( int i = 0; i < 999; ++i ) {
Pointer<TestClassBase> copy = pointer;
CPPUNIT_ASSERT( copy->returnHello() == "Hello" );
Thread::yield();
copy.reset( new TestClassB() );
CPPUNIT_ASSERT( copy->returnHello() == "GoodBye" );
}
testThread.join();
}
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testThreaded2() {
}
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testOperators() {
Pointer<TestClassBase> pointer1( new TestClassA );
Pointer<TestClassBase> pointer2( new TestClassB );
Pointer<TestClassBase> pointer3;
CPPUNIT_ASSERT( pointer1->returnHello() == "Hello" );
CPPUNIT_ASSERT( pointer2->returnHello() == "GoodBye" );
CPPUNIT_ASSERT( ( *pointer1 ).returnHello() == "Hello" );
CPPUNIT_ASSERT( ( *pointer2 ).returnHello() == "GoodBye" );
CPPUNIT_ASSERT_THROW_MESSAGE(
"operator* on a NULL Should Throw a NullPointerException",
( *pointer3 ).returnHello(),
decaf::lang::exceptions::NullPointerException );
CPPUNIT_ASSERT_THROW_MESSAGE(
"operator-> on a NULL Should Throw a NullPointerException",
pointer3->returnHello(),
decaf::lang::exceptions::NullPointerException );
pointer2.reset( NULL );
CPPUNIT_ASSERT_THROW_MESSAGE(
"operator* on a NULL Should Throw a NullPointerException",
( *pointer2 ).returnHello(),
decaf::lang::exceptions::NullPointerException );
CPPUNIT_ASSERT_THROW_MESSAGE(
"operator-> on a NULL Should Throw a NullPointerException",
pointer2->returnHello(),
decaf::lang::exceptions::NullPointerException );
}
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testSTLContainers() {
Pointer<TestClassBase> pointer1( new TestClassA );
Pointer<TestClassBase> pointer2( new TestClassB );
Pointer<TestClassBase> pointer3( pointer2 );
CPPUNIT_ASSERT( pointer1.get() != NULL );
CPPUNIT_ASSERT( pointer2.get() != NULL );
std::map< Pointer<TestClassBase>, std::string > testMap;
testMap.insert( std::make_pair( pointer1, std::string("Bob") ) );
testMap.insert( std::make_pair( pointer2, std::string("Steve") ) );
testMap.insert( std::make_pair( pointer3, std::string("Steve") ) );
// Two and Three should be equivalent (not equal) but in this case
// equivalent is what matters. So pointer2 should be bumped out of the map.
CPPUNIT_ASSERT( testMap.size() == 2 );
testMap.insert( std::make_pair( Pointer<TestClassBase>( new TestClassA ), "Fred" ) );
CPPUNIT_ASSERT( testMap.find( pointer1 ) != testMap.end() );
CPPUNIT_ASSERT( testMap.find( pointer2 ) != testMap.end() );
Pointer< int > one( new int );
Pointer< int > two( new int );
Pointer< int > three( new int );
*one = 1;
*two = 2;
*three = 3;
std::map< Pointer<int>, int, PointerComparator<int> > testMap2;
CPPUNIT_ASSERT( testMap2.size() == 0 );
testMap2.insert( std::make_pair( three, 3 ) );
testMap2.insert( std::make_pair( two, 2 ) );
testMap2.insert( std::make_pair( one, 1 ) );
CPPUNIT_ASSERT( testMap2.size() == 3 );
CPPUNIT_ASSERT( *( testMap2.begin()->first ) == 1 );
CPPUNIT_ASSERT( *( testMap2.rbegin()->first ) == 3 );
}
////////////////////////////////////////////////////////////////////////////////
TestClassBase* methodReturnRawPointer() {
return new TestClassA;
}
////////////////////////////////////////////////////////////////////////////////
Pointer<TestClassBase> methodReturnPointer() {
return Pointer<TestClassBase>( methodReturnRawPointer() );
}
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testReturnByValue() {
Pointer<TestClassBase> result = methodReturnPointer();
}
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testDynamicCast() {
Pointer<TestClassBase> pointer1( new TestClassA );
Pointer<TestClassBase> pointer2( new TestClassB );
Pointer<TestClassA> ptrTestClassA;
CPPUNIT_ASSERT_NO_THROW(
ptrTestClassA = pointer1.dynamicCast<TestClassA>() );
CPPUNIT_ASSERT( ptrTestClassA != NULL );
CPPUNIT_ASSERT( ptrTestClassA->getSize() == 1 );
Pointer<TestClassB> ptrTestClassB;
CPPUNIT_ASSERT_NO_THROW(
ptrTestClassB = pointer2.dynamicCast<TestClassB>() );
CPPUNIT_ASSERT( ptrTestClassB != NULL );
CPPUNIT_ASSERT( ptrTestClassB->getSize() == 2 );
Pointer<TestClassA> ptrTestClassA2;
CPPUNIT_ASSERT_THROW_MESSAGE(
"Should Throw a ClassCastException",
ptrTestClassA2 = pointer2.dynamicCast<TestClassA>(),
ClassCastException );
Pointer<TestClassBase> nullPointer;
CPPUNIT_ASSERT_THROW_MESSAGE(
"Should Throw a ClassCastException",
ptrTestClassA2 = nullPointer.dynamicCast<TestClassA>(),
ClassCastException );
Pointer<TestClassBase> basePointer = ptrTestClassA.dynamicCast<TestClassBase>();
CPPUNIT_ASSERT( basePointer->getSize() == 1 );
basePointer = ptrTestClassB.dynamicCast<TestClassBase>();
CPPUNIT_ASSERT( basePointer->getSize() == 2 );
}
////////////////////////////////////////////////////////////////////////////////
class Gate {
private:
CountDownLatch* enterLatch;
CountDownLatch* leaveLatch;
Mutex mutex;
bool closed;
private:
Gate(const Gate&);
Gate& operator= (const Gate&);
public:
Gate() : enterLatch(), leaveLatch(), mutex(), closed(true) {}
virtual ~Gate() {}
void open(int count) {
leaveLatch = new CountDownLatch(count);
enterLatch = new CountDownLatch(count);
mutex.lock();
closed = false;
mutex.notifyAll();
mutex.unlock();
}
void enter() {
mutex.lock();
while (closed)
mutex.wait();
enterLatch->countDown();
if (enterLatch->getCount() == 0) {
closed = true;
}
mutex.unlock();
}
void leave() {
leaveLatch->countDown();
}
void close() {
leaveLatch->await();
delete leaveLatch;
delete enterLatch;
}
};
////////////////////////////////////////////////////////////////////////////////
class PointerTestThread: public Thread {
private:
Gate *gate;
Pointer<std::string> s;
private:
PointerTestThread(const PointerTestThread&);
PointerTestThread& operator= (const PointerTestThread&);
public:
PointerTestThread(Gate *gate) : gate(gate), s() {}
virtual ~PointerTestThread() {}
void setString(Pointer<std::string> s) {
this->s = s;
}
virtual void run() {
for (int j = 0; j < 1000; j++) {
gate->enter();
s.reset(NULL);
gate->leave();
}
}
};
////////////////////////////////////////////////////////////////////////////////
void PointerTest::testThreadSafety() {
const int NUM_THREADS = 1;
Pointer<PointerTestThread> thread[NUM_THREADS];
Gate gate;
for( int i = 0; i < NUM_THREADS; i++ ) {
thread[i].reset( new PointerTestThread( &gate ) );
thread[i]->start();
}
for( int j = 0; j < 1000; j++ ) {
// Put this in its own scope so that the main thread frees the string
// before the threads.
{
Pointer<std::string> s( new std::string() );
for( int i = 0; i < NUM_THREADS; i++ )
thread[i]->setString( s );
}
// Signal the threads to free the string.
gate.open( NUM_THREADS );
gate.close();
}
for( int i = 0; i < NUM_THREADS; i++ ) {
thread[i]->join();
}
}