Repository: firebase/firebase-admin-java
Pull request #256 (2019-02-26 22:00:35)

Speeding up the Project Management Unit Tests
Project management unit tests take several seconds due to the delay operations:

```
Running com.google.firebase.projectmanagement.FirebaseProjectManagementServiceImplTest
Tests run: 36, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.068 sec - in com.google.firebase.projectmanagement.FirebaseProjectManagementServiceImplTest
```

This PR makes it possible to inject a `MockSleeper` and a `MockScheduler` so the delays can be mocked during tests:

```
Running com.google.firebase.projectmanagement.FirebaseProjectManagementServiceImplTest
Tests run: 36, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.041 sec - in com.google.firebase.projectmanagement.FirebaseProjectManagementServiceImplTest
```


Patch:
diff --git a/src/main/java/com/google/firebase/projectmanagement/FirebaseProjectManagementServiceImpl.java b/src/main/java/com/google/firebase/projectmanagement/FirebaseProjectManagementServiceImpl.java
index 6ce7f35..72c570c 100644
--- a/src/main/java/com/google/firebase/projectmanagement/FirebaseProjectManagementServiceImpl.java
+++ b/src/main/java/com/google/firebase/projectmanagement/FirebaseProjectManagementServiceImpl.java
@@ -16,10 +16,12 @@
 package com.google.firebase.projectmanagement;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.api.client.http.HttpResponseInterceptor;
 import com.google.api.client.util.Base64;
 import com.google.api.client.util.Key;
+import com.google.api.client.util.Sleeper;
 import com.google.api.core.ApiAsyncFunction;
 import com.google.api.core.ApiFunction;
 import com.google.api.core.ApiFuture;
@@ -51,6 +53,8 @@ class FirebaseProjectManagementServiceImpl implements AndroidAppService, IosAppS
   private static final String IOS_NAMESPACE_PROPERTY = "bundle_id";
 
   private final FirebaseApp app;
+  private final Sleeper sleeper;
+  private final Scheduler scheduler;
   private final HttpHelper httpHelper;
 
   private final CreateAndroidAppFromAppIdFunction createAndroidAppFromAppIdFunction =
@@ -59,7 +63,13 @@ class FirebaseProjectManagementServiceImpl implements AndroidAppService, IosAppS
       new CreateIosAppFromAppIdFunction();
 
   FirebaseProjectManagementServiceImpl(FirebaseApp app) {
-    this.app = app;
+    this(app, Sleeper.DEFAULT, new FirebaseAppScheduler(app));
+  }
+
+  FirebaseProjectManagementServiceImpl(FirebaseApp app, Sleeper sleeper, Scheduler scheduler) {
+    this.app = checkNotNull(app);
+    this.sleeper = checkNotNull(sleeper);
+    this.scheduler = checkNotNull(scheduler);
     this.httpHelper = new HttpHelper(
         app.getOptions().getJsonFactory(),
         app.getOptions().getHttpTransport().createRequestFactory(
@@ -187,7 +197,7 @@ class FirebaseProjectManagementServiceImpl implements AndroidAppService, IosAppS
             projectId,
             platformResourceName,
             MAXIMUM_LIST_APPS_PAGE_SIZE);
-        ImmutableList.Builder<T> builder = ImmutableList.<T>builder();
+        ImmutableList.Builder<T> builder = ImmutableList.builder();
         ListAppsResponse parsedResponse;
         do {
           parsedResponse = new ListAppsResponse();
@@ -361,10 +371,9 @@ class FirebaseProjectManagementServiceImpl implements AndroidAppService, IosAppS
      * or an exception if an error occurred during polling.
      */
     @Override
-    public ApiFuture<String> apply(String operationName) throws FirebaseProjectManagementException {
-      SettableApiFuture<String> settableFuture = SettableApiFuture.<String>create();
-      ImplFirebaseTrampolines.schedule(
-          app,
+    public ApiFuture<String> apply(String operationName) {
+      SettableApiFuture<String> settableFuture = SettableApiFuture.create();
+      scheduler.schedule(
           new WaitOperationRunnable(
               /* numberOfPreviousPolls= */ 0,
               operationName,
@@ -417,8 +426,7 @@ class FirebaseProjectManagementServiceImpl implements AndroidAppService, IosAppS
           long delayMillis = (long) (
               POLL_BASE_WAIT_TIME_MILLIS
                   * Math.pow(POLL_EXPONENTIAL_BACKOFF_FACTOR, numberOfPreviousPolls + 1));
-          ImplFirebaseTrampolines.schedule(
-              app,
+          scheduler.schedule(
               new WaitOperationRunnable(
                   numberOfPreviousPolls + 1,
                   operationName,
@@ -725,12 +733,26 @@ class FirebaseProjectManagementServiceImpl implements AndroidAppService, IosAppS
     private String certType;
   }
 
+  private static class FirebaseAppScheduler implements Scheduler {
+
+    private final FirebaseApp app;
+
+    FirebaseAppScheduler(FirebaseApp app) {
+      this.app = checkNotNull(app);
+    }
+
+    @Override
+    public void schedule(Runnable runnable, long delayMillis) {
+      ImplFirebaseTrampolines.schedule(app, runnable, delayMillis);
+    }
+  }
+
   /* Helper methods. */
 
   private void sleepOrThrow(String projectId, long delayMillis)
       throws FirebaseProjectManagementException {
     try {
-      Thread.sleep(delayMillis);
+      sleeper.sleep(delayMillis);
     } catch (InterruptedException e) {
       throw HttpHelper.createFirebaseProjectManagementException(
           projectId,
diff --git a/src/main/java/com/google/firebase/projectmanagement/Scheduler.java b/src/main/java/com/google/firebase/projectmanagement/Scheduler.java
new file mode 100644
index 0000000..3df12a9
--- /dev/null
+++ b/src/main/java/com/google/firebase/projectmanagement/Scheduler.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Licensed 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.
+ */
+
+package com.google.firebase.projectmanagement;
+
+/**
+ * Schedules a task to be executed after a specified delay.
+ */
+interface Scheduler {
+
+  void schedule(Runnable runnable, long delayMillis);
+}


Test patch:
diff --git a/src/test/java/com/google/firebase/projectmanagement/FirebaseProjectManagementServiceImplTest.java b/src/test/java/com/google/firebase/projectmanagement/FirebaseProjectManagementServiceImplTest.java
index edf620c..a2fe872 100644
--- a/src/test/java/com/google/firebase/projectmanagement/FirebaseProjectManagementServiceImplTest.java
+++ b/src/test/java/com/google/firebase/projectmanagement/FirebaseProjectManagementServiceImplTest.java
@@ -34,6 +34,7 @@ import com.google.api.client.http.HttpResponseInterceptor;
 import com.google.api.client.json.JsonParser;
 import com.google.api.client.testing.http.MockHttpTransport;
 import com.google.api.client.testing.http.MockLowLevelHttpResponse;
+import com.google.api.client.testing.util.MockSleeper;
 import com.google.api.client.util.Base64;
 import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;
@@ -341,7 +342,7 @@ public class FirebaseProjectManagementServiceImplTest {
     MockLowLevelHttpResponse secondRpcResponse = new MockLowLevelHttpResponse();
     secondRpcResponse.setContent(LIST_IOS_APPS_PAGE_2_RESPONSE);
     serviceImpl = initServiceImpl(
-        ImmutableList.<MockLowLevelHttpResponse>of(firstRpcResponse, secondRpcResponse),
+        ImmutableList.of(firstRpcResponse, secondRpcResponse),
         interceptor);
 
     List<IosApp> iosAppList = serviceImpl.listIosApps(PROJECT_ID);
@@ -931,7 +932,7 @@ public class FirebaseProjectManagementServiceImplTest {
         .build();
     FirebaseApp app = FirebaseApp.initializeApp(options);
     FirebaseProjectManagementServiceImpl serviceImpl =
-        new FirebaseProjectManagementServiceImpl(app);
+        new FirebaseProjectManagementServiceImpl(app, new MockSleeper(), new MockScheduler());
     serviceImpl.setInterceptor(interceptor);
     return serviceImpl;
   }
@@ -985,7 +986,7 @@ public class FirebaseProjectManagementServiceImplTest {
   /**
    * Can be used to intercept multiple HTTP requests and responses made by the SDK during tests.
    */
-  private class MultiRequestTestResponseInterceptor implements HttpResponseInterceptor {
+  private static class MultiRequestTestResponseInterceptor implements HttpResponseInterceptor {
     private final List<HttpResponse> responsesList = new ArrayList<>();
 
     @Override
@@ -1001,4 +1002,11 @@ public class FirebaseProjectManagementServiceImplTest {
       return responsesList.get(index);
     }
   }
+
+  private static class MockScheduler implements Scheduler {
+    @Override
+    public void schedule(Runnable runnable, long delayMillis) {
+      runnable.run();
+    }
+  }
 }