[Tizen] Implement partial update
[platform/core/uifw/dali-core.git] / automated-tests / src / dali / utc-Dali-FrameCallbackInterface.cpp
index 694cb43..d854148 100644 (file)
@@ -45,20 +45,20 @@ class FrameCallbackBasic : public FrameCallbackInterface
 {
 public:
 
-  FrameCallbackBasic()
-  : mCalled( false )
-  {
-  }
+  FrameCallbackBasic() = default;
 
   virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds )
   {
     mCalled = true;
   }
 
-  bool mCalled;
-};
+  virtual void Reset()
+  {
+    mCalled = false;
+  }
 
-} // anon namespace
+  bool mCalled{ false };
+};
 
 class FrameCallbackOneActor : public FrameCallbackBasic
 {
@@ -69,14 +69,14 @@ public:
   {
   }
 
-  virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds )
+  virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds ) override
   {
     FrameCallbackBasic::Update( updateProxy, elapsedSeconds );
-    mSizeGetSizeCall = updateProxy.GetSize( mActorId );
-    mPositionGetPositionCall = updateProxy.GetPosition( mActorId );
+    updateProxy.GetSize( mActorId, mSizeGetSizeCall );
+    updateProxy.GetPosition( mActorId, mPositionGetPositionCall );
     updateProxy.GetPositionAndSize( mActorId, mPositionGetPositionAndSizeCall, mSizeGetPositionAndSizeCall );
-    mColor = updateProxy.GetColor( mActorId );
-    mScale = updateProxy.GetScale( mActorId );
+    updateProxy.GetColor( mActorId, mColor );
+    updateProxy.GetScale( mActorId, mScale );
   }
 
   const unsigned int mActorId;
@@ -107,17 +107,17 @@ public:
   {
   }
 
-  virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds )
+  virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds ) override
   {
     FrameCallbackBasic::Update( updateProxy, elapsedSeconds );
     updateProxy.SetSize( mActorId, mSizeToSet );
     updateProxy.SetPosition( mActorId, mPositionToSet );
     updateProxy.SetColor( mActorId, mColorToSet );
     updateProxy.SetScale( mActorId, mScaleToSet );
-    mSizeAfterSetting = updateProxy.GetSize( mActorId );
-    mPositionAfterSetting = updateProxy.GetPosition( mActorId );
-    mColorAfterSetting = updateProxy.GetColor( mActorId );
-    mScaleAfterSetting = updateProxy.GetScale( mActorId );
+    updateProxy.GetSize( mActorId, mSizeAfterSetting );
+    updateProxy.GetPosition( mActorId, mPositionAfterSetting );
+    updateProxy.GetColor( mActorId, mColorAfterSetting );
+    updateProxy.GetScale( mActorId, mScaleAfterSetting );
   }
 
   const unsigned int mActorId;
@@ -150,17 +150,17 @@ public:
   {
   }
 
-  virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds )
+  virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds ) override
   {
     FrameCallbackBasic::Update( updateProxy, elapsedSeconds );
     updateProxy.BakeSize( mActorId, mSizeToSet );
     updateProxy.BakePosition( mActorId, mPositionToSet );
     updateProxy.BakeColor( mActorId, mColorToSet );
     updateProxy.BakeScale( mActorId, mScaleToSet );
-    mSizeAfterSetting = updateProxy.GetSize( mActorId );
-    mPositionAfterSetting = updateProxy.GetPosition( mActorId );
-    mColorAfterSetting = updateProxy.GetColor( mActorId );
-    mScaleAfterSetting = updateProxy.GetScale( mActorId );
+    updateProxy.GetSize( mActorId, mSizeAfterSetting );
+    updateProxy.GetPosition( mActorId, mPositionAfterSetting );
+    updateProxy.GetColor( mActorId, mColorAfterSetting );
+    updateProxy.GetScale( mActorId, mScaleAfterSetting );
   }
 
   const unsigned int mActorId;
@@ -175,7 +175,6 @@ public:
   Vector3 mScaleAfterSetting;
 };
 
-
 class FrameCallbackMultipleActors : public FrameCallbackBasic
 {
 public:
@@ -184,7 +183,7 @@ public:
   {
   }
 
-  virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds )
+  virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds ) override
   {
     FrameCallbackBasic::Update( updateProxy, elapsedSeconds );
     for( auto&& i : mActorIds )
@@ -203,10 +202,80 @@ public:
   std::map< unsigned int, Vector3 > mSizes;
 };
 
+class FrameCallbackActorIdCheck : public FrameCallbackBasic
+{
+public:
+
+  FrameCallbackActorIdCheck( unsigned int actorId )
+  : mActorId( actorId )
+  {
+  }
+
+  virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds ) override
+  {
+    FrameCallbackBasic::Update( updateProxy, elapsedSeconds );
+    Vector3 vec3;
+    Vector4 vec4;
+
+    mGetSizeCallSuccess            = updateProxy.GetSize( mActorId, vec3 );
+    mGetPositionCallSuccess        = updateProxy.GetPosition( mActorId, vec3 );
+    mGetColorCallSuccess           = updateProxy.GetColor( mActorId, vec4 );
+    mGetScaleCallSuccess           = updateProxy.GetScale( mActorId, vec3 );
+    mGetPositionAndSizeCallSuccess = updateProxy.GetPositionAndSize( mActorId, vec3, vec3 );
+    mSetSizeCallSuccess            = updateProxy.SetSize( mActorId, vec3 );
+    mSetPositionCallSuccess        = updateProxy.SetPosition( mActorId, vec3 );
+    mSetColorCallSuccess           = updateProxy.SetColor( mActorId, vec4 );
+    mSetScaleCallSuccess           = updateProxy.SetScale( mActorId, vec3 );
+    mBakeSizeCallSuccess           = updateProxy.BakeSize( mActorId, vec3 );
+    mBakePositionCallSuccess       = updateProxy.BakePosition( mActorId, vec3 );
+    mBakeColorCallSuccess          = updateProxy.BakeColor( mActorId, vec4 );
+    mBakeScaleCallSuccess          = updateProxy.BakeScale( mActorId, vec3 );
+  }
+
+  virtual void Reset() override
+  {
+    // Up-call
+    FrameCallbackBasic::Reset();
+
+    mGetSizeCallSuccess = false;
+    mGetPositionCallSuccess = false;
+    mGetColorCallSuccess = false;
+    mGetScaleCallSuccess = false;
+    mGetPositionAndSizeCallSuccess = false;
+    mSetSizeCallSuccess = false;
+    mSetPositionCallSuccess = false;
+    mSetColorCallSuccess = false;
+    mSetScaleCallSuccess = false;
+    mBakeSizeCallSuccess = false;
+    mBakePositionCallSuccess = false;
+    mBakeColorCallSuccess = false;
+    mBakeScaleCallSuccess = false;
+  }
+
+  const uint32_t mActorId;
+  bool mGetSizeCallSuccess{ false };
+  bool mGetPositionCallSuccess{ false };
+  bool mGetColorCallSuccess{ false };
+  bool mGetScaleCallSuccess{ false };
+  bool mGetPositionAndSizeCallSuccess{ false };
+  bool mSetSizeCallSuccess{ false };
+  bool mSetPositionCallSuccess{ false };
+  bool mSetColorCallSuccess{ false };
+  bool mSetScaleCallSuccess{ false };
+  bool mBakeSizeCallSuccess{ false };
+  bool mBakePositionCallSuccess{ false };
+  bool mBakeColorCallSuccess{ false };
+  bool mBakeScaleCallSuccess{ false };
+};
+
+} // anon namespace
+
 ///////////////////////////////////////////////////////////////////////////////
 
 int UtcDaliFrameCallbackCheckInstallationAndRemoval(void)
 {
+  // Basic test to check that the frame-callback can be installed and removed correctly
+
   TestApplication application;
 
   FrameCallbackBasic frameCallback;
@@ -233,6 +302,8 @@ int UtcDaliFrameCallbackCheckInstallationAndRemoval(void)
 
 int UtcDaliFrameCallbackGetters(void)
 {
+  // Test to see that the Getters all return the expected values
+
   TestApplication application;
   Vector2 actorSize( 200, 300 );
   Vector4 color( 0.5f, 0.6f, 0.7f, 0.8f );
@@ -269,6 +340,8 @@ int UtcDaliFrameCallbackGetters(void)
 
 int UtcDaliFrameCallbackSetters(void)
 {
+  // Test to see that the setters set the values appropriately
+
   TestApplication application;
   Vector2 actorSize( 200, 300 );
 
@@ -308,11 +381,31 @@ int UtcDaliFrameCallbackSetters(void)
   DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::COLOR ).Get< Vector4 >(), Color::WHITE, TEST_LOCATION );
   DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::SCALE ).Get< Vector3 >(), Vector3::ONE, TEST_LOCATION );
 
+  // Render for a couple more frames to ensure the values are reset properly (some values are double-buffered)
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::POSITION ).Get< Vector3 >(), Vector3::ZERO, TEST_LOCATION );
+  DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::SIZE ).Get< Vector3 >(), Vector3( actorSize ), TEST_LOCATION );
+  DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::COLOR ).Get< Vector4 >(), Color::WHITE, TEST_LOCATION );
+  DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::SCALE ).Get< Vector3 >(), Vector3::ONE, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::POSITION ).Get< Vector3 >(), Vector3::ZERO, TEST_LOCATION );
+  DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::SIZE ).Get< Vector3 >(), Vector3( actorSize ), TEST_LOCATION );
+  DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::COLOR ).Get< Vector4 >(), Color::WHITE, TEST_LOCATION );
+  DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::SCALE ).Get< Vector3 >(), Vector3::ONE, TEST_LOCATION );
+
   END_TEST;
 }
 
 int UtcDaliFrameCallbackBake(void)
 {
+  // Test to see that the bake methods bake the values
+
   TestApplication application;
   Vector2 actorSize( 200, 300 );
 
@@ -359,6 +452,8 @@ int UtcDaliFrameCallbackBake(void)
 int UtcDaliFrameCallbackMultipleActors(void)
 {
   /**
+   * Test to check that the frame callback behaves appropriately with multiple actors
+   *
    * Tree:
    *              root-layer
    *              /        \
@@ -537,3 +632,291 @@ int UtcDaliFrameCallbackCheckActorNotAdded(void)
 
   END_TEST;
 }
+
+int UtcDaliFrameCallbackInvalidActorId(void)
+{
+  // Test to ensure that there are no issues when trying to use the update-proxy methods with an invalid actor ID.
+
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+
+  FrameCallbackActorIdCheck frameCallback( 10000 );
+  DevelStage::AddFrameCallback( stage, frameCallback, stage.GetRootLayer() );
+
+  application.SendNotification();
+  application.Render();
+
+  // Invalid Actor ID so all the methods should not return successfully.
+
+  DALI_TEST_EQUALS( frameCallback.mCalled,                        true,  TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetSizeCallSuccess,            false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetPositionCallSuccess,        false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetColorCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetScaleCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetPositionAndSizeCallSuccess, false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetSizeCallSuccess,            false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetPositionCallSuccess,        false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetColorCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetScaleCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeSizeCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakePositionCallSuccess,       false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeColorCallSuccess,          false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeScaleCallSuccess,          false, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliFrameCallbackActorRemovedAndAdded(void)
+{
+  // Test to ensure that we do not call methods on actors that have been removed on the stage
+  // and then re-start calling the required methods if that actor is re-added back to the stage
+
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+
+  Actor actor = Actor::New();
+  stage.Add( actor );
+
+  FrameCallbackActorIdCheck frameCallback( actor.GetId() );
+  DevelStage::AddFrameCallback( stage, frameCallback, stage.GetRootLayer() );
+
+  application.SendNotification();
+  application.Render();
+
+  // All methods should return successfully.
+
+  DALI_TEST_EQUALS( frameCallback.mCalled,                        true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetSizeCallSuccess,            true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetPositionCallSuccess,        true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetColorCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetScaleCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetPositionAndSizeCallSuccess, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetSizeCallSuccess,            true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetPositionCallSuccess,        true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetColorCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetScaleCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeSizeCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakePositionCallSuccess,       true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeColorCallSuccess,          true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeScaleCallSuccess,          true, TEST_LOCATION );
+  frameCallback.Reset();
+
+  // Remove the actor from stage, the methods should not return successfully.
+
+  stage.Remove( actor );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback.mCalled,                        true,  TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetSizeCallSuccess,            false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetPositionCallSuccess,        false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetColorCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetScaleCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetPositionAndSizeCallSuccess, false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetSizeCallSuccess,            false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetPositionCallSuccess,        false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetColorCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetScaleCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeSizeCallSuccess,           false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakePositionCallSuccess,       false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeColorCallSuccess,          false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeScaleCallSuccess,          false, TEST_LOCATION );
+  frameCallback.Reset();
+
+  // Re-add the actor back to the stage, all the methods should once again, return successfully.
+
+  stage.Add( actor );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback.mCalled,                        true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetSizeCallSuccess,            true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetPositionCallSuccess,        true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetColorCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetScaleCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mGetPositionAndSizeCallSuccess, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetSizeCallSuccess,            true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetPositionCallSuccess,        true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetColorCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mSetScaleCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeSizeCallSuccess,           true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakePositionCallSuccess,       true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeColorCallSuccess,          true, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback.mBakeScaleCallSuccess,          true, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliFrameCallbackMultipleCallbacks(void)
+{
+  // Test to ensure multiple frame-callbacks work as expected
+
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+
+  Actor actor = Actor::New();
+  stage.Add( actor );
+
+  FrameCallbackBasic frameCallback1;
+  FrameCallbackBasic frameCallback2;
+  DevelStage::AddFrameCallback( stage, frameCallback1, stage.GetRootLayer() );
+  DevelStage::AddFrameCallback( stage, frameCallback2, stage.GetRootLayer() );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback1.mCalled, true,  TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback2.mCalled, true,  TEST_LOCATION );
+  frameCallback1.Reset();
+  frameCallback2.Reset();
+
+  // Remove the second frame-callback, only the first should be called
+
+  DevelStage::RemoveFrameCallback( stage, frameCallback2 );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback1.mCalled, true,  TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback2.mCalled, false, TEST_LOCATION );
+  frameCallback1.Reset();
+  frameCallback2.Reset();
+
+  // Re-add the second frame-callback and remove the first, only the second should be called
+
+  DevelStage::AddFrameCallback( stage, frameCallback2, stage.GetRootLayer() );
+  DevelStage::RemoveFrameCallback( stage, frameCallback1 );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback1.mCalled, false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback2.mCalled, true,  TEST_LOCATION );
+  frameCallback1.Reset();
+  frameCallback2.Reset();
+
+  // Attempt removal of the first frame-callback again, should be a no-op and yield the exact same results as the last run
+  DevelStage::RemoveFrameCallback( stage, frameCallback1 );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback1.mCalled, false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback2.mCalled, true,  TEST_LOCATION );
+  frameCallback1.Reset();
+  frameCallback2.Reset();
+
+  // Remove the second frame-callback as well, neither should be called
+  DevelStage::RemoveFrameCallback( stage, frameCallback2 );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback1.mCalled, false, TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback2.mCalled, false, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliFrameCallbackActorDestroyed(void)
+{
+  // Test to ensure that the frame-callback behaves gracefully if the connected root-actor is destroyed
+
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+
+  Actor actor = Actor::New();
+  stage.Add( actor );
+
+  FrameCallbackBasic frameCallback1;
+  FrameCallbackBasic frameCallback2;
+  DevelStage::AddFrameCallback( stage, frameCallback1, actor );
+  DevelStage::AddFrameCallback( stage, frameCallback2, actor );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback1.mCalled, true,  TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback2.mCalled, true,  TEST_LOCATION );
+  frameCallback1.Reset();
+  frameCallback2.Reset();
+
+  // Remove the second frame-callback, only the first should be called
+
+  DevelStage::RemoveFrameCallback( stage, frameCallback2 );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback1.mCalled, true,  TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback2.mCalled, false, TEST_LOCATION );
+  frameCallback1.Reset();
+  frameCallback2.Reset();
+
+  // Remove and destroy the actor, the first one should not be called either
+  stage.Remove( actor );
+  actor.Reset();
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( frameCallback1.mCalled, false,  TEST_LOCATION );
+  DALI_TEST_EQUALS( frameCallback2.mCalled, false, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliFrameCallbackDestroyedBeforeRemoving(void)
+{
+  // Ensure there's no segmentation fault if the callback is deleted without being removed
+
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+
+  Actor actor = Actor::New();
+  stage.Add( actor );
+
+  {
+    FrameCallbackBasic frameCallback;
+    DevelStage::AddFrameCallback( stage, frameCallback, actor );
+
+    application.SendNotification();
+    application.Render();
+
+    DALI_TEST_EQUALS( frameCallback.mCalled, true,  TEST_LOCATION );
+    frameCallback.Reset();
+  }
+
+  // frameCallback has now been destroyed but not removed
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_CHECK( true ); // If it runs to here then there's no segmentation fault
+
+  END_TEST;
+}
+
+int UtcDaliFrameCallbackDoubleAddition(void)
+{
+  // Ensure we don't connect the same frame-callback twice
+
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+  Actor rootActor = stage.GetRootLayer();
+
+  FrameCallbackBasic frameCallback;
+  DevelStage::AddFrameCallback( stage, frameCallback, rootActor );
+
+  try
+  {
+    DevelStage::AddFrameCallback( stage, frameCallback, rootActor );
+  }
+  catch( ... )
+  {
+    DALI_TEST_CHECK( true );
+  }
+
+  END_TEST;
+}