@@ -112,7 +112,6 @@ TEST(TimestampIsNear, Matcher) {
112112 EXPECT_THAT (firebase::Variant::EmptyString (), Not (TimestampIsNear (0 )));
113113}
114114
115-
116115class FirebaseDatabaseTest : public FirebaseTest {
117116 public:
118117 FirebaseDatabaseTest ();
@@ -151,7 +150,7 @@ class FirebaseDatabaseTest : public FirebaseTest {
151150
152151 static firebase::App* shared_app_;
153152 static firebase::auth::Auth* shared_auth_;
154-
153+
155154 static bool first_time_;
156155
157156 bool initialized_;
@@ -169,15 +168,13 @@ class FirebaseDatabaseTest : public FirebaseTest {
169168// - TearDown: Shut down Database.
170169// - Once, after all tests are finished:
171170// - TearDownTestSuite: Sign out. Shut down Auth and App.
172-
171+
173172firebase::App* FirebaseDatabaseTest::shared_app_;
174173firebase::auth::Auth* FirebaseDatabaseTest::shared_auth_;
175174
176175bool FirebaseDatabaseTest::first_time_ = true ;
177176
178- void FirebaseDatabaseTest::SetUpTestSuite () {
179- InitializeAppAndAuth ();
180- }
177+ void FirebaseDatabaseTest::SetUpTestSuite () { InitializeAppAndAuth (); }
181178
182179void FirebaseDatabaseTest::InitializeAppAndAuth () {
183180 LogDebug (" Initialize Firebase App." );
@@ -197,14 +194,14 @@ void FirebaseDatabaseTest::InitializeAppAndAuth() {
197194
198195 // Initialize Firebase Auth.
199196 ::firebase::ModuleInitializer initializer;
200- initializer.Initialize (
201- shared_app_, &shared_auth_, [](::firebase::App* app, void * target) {
202- LogDebug (" Attempting to initialize Firebase Auth." );
203- ::firebase::InitResult result;
204- *reinterpret_cast <firebase::auth::Auth**>(target) =
205- ::firebase::auth::Auth::GetAuth (app, &result);
206- return result;
207- });
197+ initializer.Initialize (shared_app_, &shared_auth_,
198+ [](::firebase::App* app, void * target) {
199+ LogDebug (" Attempting to initialize Firebase Auth." );
200+ ::firebase::InitResult result;
201+ *reinterpret_cast <firebase::auth::Auth**>(target) =
202+ ::firebase::auth::Auth::GetAuth (app, &result);
203+ return result;
204+ });
208205
209206 WaitForCompletion (initializer.InitializeLastResult (), " InitializeAuth" );
210207
@@ -219,9 +216,7 @@ void FirebaseDatabaseTest::InitializeAppAndAuth() {
219216 SignIn ();
220217}
221218
222- void FirebaseDatabaseTest::TearDownTestSuite () {
223- TerminateAppAndAuth ();
224- }
219+ void FirebaseDatabaseTest::TearDownTestSuite () { TerminateAppAndAuth (); }
225220
226221void FirebaseDatabaseTest::TerminateAppAndAuth () {
227222 if (shared_auth_) {
@@ -283,7 +278,7 @@ void FirebaseDatabaseTest::InitializeDatabase() {
283278 LogDebug (" Attempting to initialize Firebase Database." );
284279 ::firebase::InitResult result;
285280 *reinterpret_cast <firebase::database::Database**>(target) =
286- firebase::database::Database::GetInstance (app, &result);
281+ firebase::database::Database::GetInstance (app, &result);
287282 return result;
288283 });
289284
@@ -345,9 +340,9 @@ void FirebaseDatabaseTest::SignOut() {
345340 }
346341 if (shared_auth_->current_user ()->is_anonymous ()) {
347342 // If signed in anonymously, delete the anonymous user.
348- WaitForCompletion (shared_auth_->current_user ()->Delete (), " DeleteAnonymousUser " );
349- }
350- else {
343+ WaitForCompletion (shared_auth_->current_user ()->Delete (),
344+ " DeleteAnonymousUser " );
345+ } else {
351346 // If not signed in anonymously (e.g. if the tests were modified to sign in
352347 // as an actual user), just sign out normally.
353348 shared_auth_->SignOut ();
@@ -771,7 +766,7 @@ TEST_F(FirebaseDatabaseTest, TestQueryFiltering) {
771766 UnorderedElementsAre (Key (" fig" )));
772767 }
773768}
774- #endif // !defined(ANDROID)
769+ #endif // !defined(ANDROID)
775770
776771// A simple ValueListener that logs the values it sees.
777772class LoggingValueListener : public firebase ::database::ValueListener {
@@ -804,6 +799,72 @@ class LoggingValueListener : public firebase::database::ValueListener {
804799 bool got_error_;
805800};
806801
802+ TEST_F (FirebaseDatabaseTest, TestAddAndRemoveListenerRace) {
803+ const char * test_name = test_info_->name ();
804+
805+ SignIn ();
806+
807+ firebase::database::DatabaseReference ref = CreateWorkingPath ();
808+ WaitForCompletion (ref.Child (test_name).SetValue (0 ), " SetValue" );
809+
810+ const int kTestIterations = 100 ;
811+
812+ // Ensure adding, removing and deleting a listener in rapid succession is safe
813+ // from race conditions.
814+ for (int i = 0 ; i < kTestIterations ; i++) {
815+ LoggingValueListener* listener = new LoggingValueListener ();
816+ ref.Child (test_name).AddValueListener (listener);
817+ ref.Child (test_name).RemoveValueListener (listener);
818+ delete listener;
819+ }
820+
821+ // Ensure adding, removing and deleting the same listener twice in rapid
822+ // succession is safe from race conditions.
823+ for (int i = 0 ; i < kTestIterations ; i++) {
824+ LoggingValueListener* listener = new LoggingValueListener ();
825+ ref.Child (test_name).AddValueListener (listener);
826+ ref.Child (test_name).RemoveValueListener (listener);
827+ ref.Child (test_name).AddValueListener (listener);
828+ ref.Child (test_name).RemoveValueListener (listener);
829+ delete listener;
830+ }
831+
832+ // Ensure adding, removing and deleting the same listener twice in rapid
833+ // succession is safe from race conditions.
834+ for (int i = 0 ; i < kTestIterations ; i++) {
835+ LoggingValueListener* listener = new LoggingValueListener ();
836+ ref.Child (test_name).AddValueListener (listener);
837+ ref.Child (test_name).AddValueListener (listener);
838+ ref.Child (test_name).RemoveValueListener (listener);
839+ ref.Child (test_name).RemoveValueListener (listener);
840+ delete listener;
841+ }
842+
843+ // Ensure removing a listener too many times is benign.
844+ for (int i = 0 ; i < kTestIterations ; i++) {
845+ LoggingValueListener* listener = new LoggingValueListener ();
846+ ref.Child (test_name).AddValueListener (listener);
847+ ref.Child (test_name).AddValueListener (listener);
848+ ref.Child (test_name).RemoveValueListener (listener);
849+ ref.Child (test_name).RemoveValueListener (listener);
850+ ref.Child (test_name).RemoveValueListener (listener);
851+ delete listener;
852+ }
853+
854+ // Ensure adding, removing and deleting difference listeners in rapid
855+ // succession is safe from race conditions.
856+ for (int i = 0 ; i < kTestIterations ; i++) {
857+ LoggingValueListener* listener1 = new LoggingValueListener ();
858+ LoggingValueListener* listener2 = new LoggingValueListener ();
859+ ref.Child (test_name).AddValueListener (listener1);
860+ ref.Child (test_name).AddValueListener (listener2);
861+ ref.Child (test_name).RemoveValueListener (listener1);
862+ ref.Child (test_name).RemoveValueListener (listener2);
863+ delete listener1;
864+ delete listener2;
865+ }
866+ }
867+
807868TEST_F (FirebaseDatabaseTest, TestValueListener) {
808869 const char * test_name = test_info_->name ();
809870
0 commit comments