/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010 University of Bonn. All rights reserved. * Please see the LICENSE file or "Copyright notice" in builder.cpp for details. */ /* * ObserverTest.cpp * * Created on: Jan 19, 2010 * Author: crueger */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "CodePatterns/Assert.hpp" #include #include "stubs/ObserverStub.hpp" #include "CodePatterns/Observer/Notification.hpp" #include "CodePatterns/Observer/ObservedContainer.hpp" #include "CodePatterns/Observer/ObservedContainer_impl.hpp" #include "CodePatterns/Observer/ObserverLog.hpp" #include "ObserverUnitTest.hpp" #ifdef HAVE_TESTRUNNER #include "UnitTestMain.hpp" #endif /*HAVE_TESTRUNNER*/ // Registers the fixture into the 'registry' CPPUNIT_TEST_SUITE_REGISTRATION( ObserverTest ); /******************* Test stubs ************************/ /******************* actuall tests ***************/ void ObserverTest::setUp() { ASSERT_DO(Assert::Throw); simpleObservable1 = new SimpleObservable(); simpleObservable2 = new SimpleObservable(); callObservable = new CallObservable(); superObservable = new SuperObservable(); blockObservable = new BlockObservable(); notificationObservable = new NotificationObservable(); obsset = new ObservableSet(5); obsmap = new ObservableMap(5); observer1 = new UpdateCountObserver(); observer2 = new UpdateCountObserver(); observer3 = new UpdateCountObserver(); observer4 = new UpdateCountObserver(); notificationObserver1 = new NotificationObserver( notificationObservable->getChannel(NotificationObservable::Operation1Notify)); notificationObserver2 = new NotificationObserver( notificationObservable->getChannel(NotificationObservable::Operation2Notify)); RelayObservable = new RelayTest; RelayObserver = new RelayCountObserver(RelayObservable); RelayNotifier = new RelayNotification; RelayNotified = new RelayNotificationObserver(RelayObservable); } void ObserverTest::tearDown() { delete simpleObservable1; delete simpleObservable2; delete callObservable; delete superObservable; delete blockObservable; delete notificationObservable; delete obsset; delete obsmap; delete RelayObservable; delete RelayObserver; delete RelayNotifier; delete RelayNotified; delete observer1; delete observer2; delete observer3; delete observer4; delete notificationObserver1; delete notificationObserver2; #ifdef LOG_OBSERVER ObserverLog::purgeInstance(); #endif } void ObserverTest::doesUpdateTest() { simpleObservable1->signOn(observer1); simpleObservable1->signOn(observer2); simpleObservable1->signOn(observer3); simpleObservable2->signOn(observer2); simpleObservable2->signOn(observer4); CPPUNIT_ASSERT_EQUAL( 0, observer1->updates ); CPPUNIT_ASSERT_EQUAL( 0, observer2->updates ); CPPUNIT_ASSERT_EQUAL( 0, observer3->updates ); CPPUNIT_ASSERT_EQUAL( 0, observer4->updates ); simpleObservable1->changeMethod(); CPPUNIT_ASSERT_EQUAL( 1, observer1->updates ); CPPUNIT_ASSERT_EQUAL( 1, observer2->updates ); CPPUNIT_ASSERT_EQUAL( 1, observer3->updates ); CPPUNIT_ASSERT_EQUAL( 0, observer4->updates ); simpleObservable1->signOff(observer3); simpleObservable1->changeMethod(); CPPUNIT_ASSERT_EQUAL( 2, observer1->updates ); CPPUNIT_ASSERT_EQUAL( 2, observer2->updates ); CPPUNIT_ASSERT_EQUAL( 1, observer3->updates ); CPPUNIT_ASSERT_EQUAL( 0, observer4->updates ); simpleObservable2->changeMethod(); CPPUNIT_ASSERT_EQUAL( 2, observer1->updates ); CPPUNIT_ASSERT_EQUAL( 3, observer2->updates ); CPPUNIT_ASSERT_EQUAL( 1, observer3->updates ); CPPUNIT_ASSERT_EQUAL( 1, observer4->updates ); } void ObserverTest::doesBlockUpdateTest() { callObservable->signOn(observer1); CPPUNIT_ASSERT_EQUAL( 0, observer1->updates ); callObservable->changeMethod1(); CPPUNIT_ASSERT_EQUAL( 1, observer1->updates ); callObservable->changeMethod2(); CPPUNIT_ASSERT_EQUAL( 2, observer1->updates ); } void ObserverTest::doesSubObservableTest() { superObservable->signOn(observer1); superObservable->subObservable->signOn(observer2); superObservable->subObservable->changeMethod(); CPPUNIT_ASSERT_EQUAL( 1, observer1->updates ); CPPUNIT_ASSERT_EQUAL( 1, observer2->updates ); superObservable->changeMethod(); CPPUNIT_ASSERT_EQUAL( 2, observer1->updates ); CPPUNIT_ASSERT_EQUAL( 2, observer2->updates ); } void ObserverTest::outsideLockTest(){ callObservable->signOn(observer1); CPPUNIT_ASSERT_EQUAL( 0, observer1->updates ); { LOCK_OBSERVABLE(*callObservable); CPPUNIT_ASSERT_EQUAL( 0, observer1->updates ); } // lock is gone now, observer should have notified CPPUNIT_ASSERT_EQUAL( 1, observer1->updates ); } void ObserverTest::doesNotifyTest(){ notificationObservable->signOn(notificationObserver1, NotificationObservable::Operation1Notify); notificationObservable->signOn(notificationObserver2, NotificationObservable::Operation2Notify); notificationObservable->operation1(); CPPUNIT_ASSERT(notificationObserver1->wasNotified); CPPUNIT_ASSERT(!notificationObserver2->wasNotified); notificationObserver1->wasNotified=false; notificationObservable->operation2(); CPPUNIT_ASSERT(!notificationObserver1->wasNotified); CPPUNIT_ASSERT(notificationObserver2->wasNotified); } void ObserverTest::doesReportTest(){ // Actual checks are in the Stub-methods for this blockObservable->changeMethod1(); blockObservable->changeMethod2(); blockObservable->noChangeMethod(); } void ObserverTest::iteratorTest(){ int i = 0; // test the general iterator methods for(ObservableSet::iterator iter=obsset->begin(); iter!=obsset->end();++iter){ CPPUNIT_ASSERT(i< obsset->num); i++; } i=0; for(ObservableSet::const_iterator iter=obsset->begin(); iter!=obsset->end();++iter){ CPPUNIT_ASSERT(inum); i++; } obsset->signOn(observer1); { // we construct this out of the loop, so the iterator dies at the end of // the scope and not the end of the loop (allows more testing) ObservableSet::iterator iter; for(iter=obsset->begin(); iter!=obsset->end(); ++iter){ (*iter)->changeMethod(); } // At this point no change should have been propagated CPPUNIT_ASSERT_EQUAL( 0, observer1->updates); } // After the Iterator has died the propagation should take place CPPUNIT_ASSERT_EQUAL( 1, observer1->updates); // when using a const_iterator no changes should be propagated for(ObservableSet::const_iterator iter = obsset->begin(); iter!=obsset->end();++iter); CPPUNIT_ASSERT_EQUAL( 1, observer1->updates); // we need to test the operator-> as well obsmap->signOn(observer2); { // we construct this out of the loop, so the iterator dies at the end of // the scope and not the end of the loop (allows more testing) ObservableMap::iterator iter; for(iter=obsmap->begin(); iter!=obsmap->end(); ++iter){ iter->second->changeMethod(); } // At this point no change should have been propagated CPPUNIT_ASSERT_EQUAL( 0, observer2->updates); } // After the Iterator has died the propagation should take place CPPUNIT_ASSERT_EQUAL( 1, observer2->updates); obsset->signOff(observer1); obsmap->signOff(observer2); } void ObserverTest::relayTest() { // sign on some observables to the relay simpleObservable1->signOn(RelayObservable); simpleObservable2->signOn(RelayObservable); // sign on an observer to the relay RelayObservable->signOn(RelayObserver); simpleObservable1->signOn(observer1); // check that all is zero CPPUNIT_ASSERT_EQUAL( 0, RelayObserver->updates ); CPPUNIT_ASSERT_EQUAL( 0, observer1->updates ); // signal update simpleObservable1->changeMethod(); // check that both the change // (and RelayObserver checks being called correctly) CPPUNIT_ASSERT_EQUAL( 1, RelayObserver->updates ); CPPUNIT_ASSERT_EQUAL( 1, observer1->updates ); // signal update through relay only simpleObservable2->changeMethod(); // check that only one got the change CPPUNIT_ASSERT_EQUAL( 2, RelayObserver->updates ); CPPUNIT_ASSERT_EQUAL( 1, observer1->updates ); // check on signOff simpleObservable1->signOff(RelayObservable); simpleObservable1->signOff(observer1); } void ObserverTest::relayNotificationTest() { observerLog().enableLogging(); // sign on some observables to the relay notificationObservable->signOn(RelayNotifier, NotificationObservable::Operation1Notify); notificationObservable->signOn(RelayNotifier, NotificationObservable::Operation2Notify); notificationObservable->signOn(notificationObserver1, NotificationObservable::Operation1Notify); RelayNotifier->signOn(RelayNotified, NotificationObservable::Operation1Notify); // operation1 notificationObservable->operation1(); CPPUNIT_ASSERT(RelayNotified->wasNotified); CPPUNIT_ASSERT(notificationObserver1->wasNotified); RelayNotified->wasNotified=false; notificationObserver1->wasNotified=false; // operation2 notificationObservable->operation2(); CPPUNIT_ASSERT(!RelayNotified->wasNotified); CPPUNIT_ASSERT(!notificationObserver1->wasNotified); // signOff relay from 1 and operation1 notificationObserver1->wasNotified=false; notificationObservable->signOff(RelayNotifier, NotificationObservable::Operation1Notify); notificationObservable->operation1(); CPPUNIT_ASSERT(!RelayNotified->wasNotified); CPPUNIT_ASSERT(notificationObserver1->wasNotified); // test kill subject RelayNotifier->signOff(RelayNotified, NotificationObservable::Operation1Notify); delete RelayNotified; RelayNotified = NULL; // delete in tearDown is allowed for NULL notificationObservable->operation1(); notificationObservable->signOff(RelayNotifier, NotificationObservable::Operation2Notify); delete notificationObservable; notificationObservable = NULL; // delete in tearDown is allowed for NULL observerLog().disableLogging(); } void ObserverTest::CircleDetectionTest() { std::cout << std::endl << "Warning: the next test involved methods that can produce infinite loops." << std::endl; std::cout << "Errors in this methods can not be checked using the CPPUNIT_ASSERT Macros." << std::endl; std::cout << "Instead tests are run on these methods to see if termination is assured" << std::endl << std::endl; std::cout << "If this test does not complete in a few seconds, kill the test-suite and fix the Error in the circle detection mechanism" << std::endl; std::cout << std::endl << std::endl << "The following errors displayed by the observer framework can be ignored" << std::endl; // make this Observable its own subject. NEVER DO THIS IN ACTUAL CODE simpleObservable1->signOn(simpleObservable1); #ifndef NDEBUG CPPUNIT_ASSERT_THROW(simpleObservable1->changeMethod(),Assert::AssertionFailure); #else simpleObservable1->changeMethod(); #endif // more complex test simpleObservable1->signOff(simpleObservable1); simpleObservable1->signOn(simpleObservable2); simpleObservable2->signOn(simpleObservable1); #ifndef NDEBUG CPPUNIT_ASSERT_THROW(simpleObservable1->changeMethod(),Assert::AssertionFailure); #else simpleObservable1->changeMethod(); #endif simpleObservable1->signOff(simpleObservable2); simpleObservable2->signOff(simpleObservable1); // when we reach this line, although we broke the DAG assumption the circle check works fine CPPUNIT_ASSERT(true); }