Add WorkManager Benchmarks.

* The first benchmark measures the cost to create an instance of WorkManager with no outstanding work left.
* The second benchmark measures the cost to instantitate WorkManager with pending work which needs to be executed.

Test: Added benchmarks.
Fixes: b/126406798

Change-Id: Iab86a1ee2a1a72ac7c5f7f3b7233713feb087701
diff --git a/settings.gradle b/settings.gradle
index 8e9171e..5dcbc7b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -233,6 +233,7 @@
 includeProject(":work:work-runtime-ktx", "work/workmanager-ktx")
 includeProject(":work:work-rxjava2", "work/workmanager-rxjava2")
 includeProject(":work:work-testing", "work/workmanager-testing")
+includeProject(":work:work-benchmark", "work/workmanager-benchmark")
 includeProject(":work:integration-tests:testapp", "work/integration-tests/testapp")
 
 /////////////////////////////
diff --git a/work/workmanager-benchmark/build.gradle b/work/workmanager-benchmark/build.gradle
new file mode 100644
index 0000000..e64d2a7
--- /dev/null
+++ b/work/workmanager-benchmark/build.gradle
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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.
+ */
+
+
+import androidx.build.AndroidXExtension
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+    id("androidx.benchmark")
+}
+
+dependencies {
+    androidTestImplementation(project(':work:work-runtime-ktx'))
+    androidTestImplementation(project(":benchmark:benchmark-junit4"))
+    androidTestImplementation(WORK_ARCH_ROOM_RUNTIME)
+    androidTestImplementation(JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(KOTLIN_STDLIB)
+    androidTestImplementation(KOTLIN_COROUTINES)
+}
+
+androidx {
+    name = "Android WorkManager Benchmarks"
+     publish = Publish.NONE
+    mavenVersion = LibraryVersions.WORK
+    mavenGroup = LibraryGroups.WORK
+    inceptionYear = "2019"
+    description = "Android WorkManager Benchmark Library"
+    url = AndroidXExtension.ARCHITECTURE_URL
+}
diff --git a/work/workmanager-benchmark/src/androidTest/AndroidManifest.xml b/work/workmanager-benchmark/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..9ad6fef
--- /dev/null
+++ b/work/workmanager-benchmark/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ 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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.work.benchmark.test">
+
+    <!-- Important: disable debuggable for accurate performance results -->
+    <application
+        android:debuggable="false"
+        tools:replace="android:debuggable">
+    </application>
+</manifest>
diff --git a/work/workmanager-benchmark/src/androidTest/java/androidx/work/benchmark/DispatchingExecutor.kt b/work/workmanager-benchmark/src/androidTest/java/androidx/work/benchmark/DispatchingExecutor.kt
new file mode 100644
index 0000000..5b6b635
--- /dev/null
+++ b/work/workmanager-benchmark/src/androidTest/java/androidx/work/benchmark/DispatchingExecutor.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.work.benchmark
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import java.util.concurrent.Executor
+
+/**
+ * An [Executor] where we can await termination of all commands.
+ */
+class DispatchingExecutor : Executor {
+    val job = Job()
+    private val scope = CoroutineScope(Dispatchers.Default + job)
+    override fun execute(command: Runnable) {
+        scope.launch {
+            command.run()
+        }
+    }
+}
diff --git a/work/workmanager-benchmark/src/androidTest/java/androidx/work/benchmark/InitializeBenchmark.kt b/work/workmanager-benchmark/src/androidTest/java/androidx/work/benchmark/InitializeBenchmark.kt
new file mode 100644
index 0000000..3d33749
--- /dev/null
+++ b/work/workmanager-benchmark/src/androidTest/java/androidx/work/benchmark/InitializeBenchmark.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.work.benchmark
+
+import android.content.Context
+import android.util.Log
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.work.Configuration
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkInfo
+import androidx.work.impl.WorkDatabase
+import androidx.work.impl.WorkManagerImpl
+import androidx.work.impl.utils.SerialExecutor
+import androidx.work.impl.utils.taskexecutor.TaskExecutor
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.Executor
+
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class InitializeBenchmark {
+
+    @get:Rule
+    val benchmarkRule = BenchmarkRule()
+    private lateinit var context: Context
+    private lateinit var executor: DispatchingExecutor
+    private lateinit var taskExecutor: TaskExecutor
+    private lateinit var configuration: Configuration
+
+    @Before
+    fun setUp() {
+        context = ApplicationProvider.getApplicationContext()
+
+        // Use a DispatchingExecutor to avoid having to wait for all the tasks to be done
+        // in the actual benchmark.
+        executor = DispatchingExecutor()
+        val serialExecutor = SerialExecutor(executor)
+
+        taskExecutor = object : TaskExecutor {
+            override fun postToMainThread(runnable: Runnable) {
+                serialExecutor.execute(runnable)
+            }
+
+            override fun getMainThreadExecutor(): Executor {
+                return serialExecutor
+            }
+
+            override fun executeOnBackgroundThread(runnable: Runnable) {
+                serialExecutor.execute(runnable)
+            }
+
+            override fun getBackgroundExecutor(): SerialExecutor {
+                return serialExecutor
+            }
+        }
+
+        configuration = Configuration.Builder()
+            .setTaskExecutor(executor)
+            .setExecutor(executor)
+            .setMinimumLoggingLevel(Log.DEBUG)
+            .build()
+    }
+
+    @Test
+    fun initializeSimple() {
+        benchmarkRule.measureRepeated {
+            // Runs ForceStopRunnable
+            val database = WorkDatabase.create(context, configuration.taskExecutor, false)
+            WorkManagerImpl(context, configuration, taskExecutor, database)
+            runWithTimingDisabled {
+                runBlocking {
+                    executor.job.children.forEach {
+                        it.join()
+                    }
+                }
+                database.close()
+            }
+        }
+    }
+
+    @Test
+    fun initializeWithWorkLeft() {
+        val count = 20
+        benchmarkRule.measureRepeated {
+            val database = WorkDatabase.create(context, configuration.taskExecutor, false)
+            runWithTimingDisabled {
+                for (i in 0 until count) {
+                    val request = OneTimeWorkRequestBuilder<NoOpWorker>()
+                    val state =
+                        if (i <= count - 2) WorkInfo.State.SUCCEEDED else WorkInfo.State.RUNNING
+                    request.setInitialState(state)
+                    database.workSpecDao().insertWorkSpec(request.build().workSpec)
+                }
+            }
+            // Runs ForceStopRunnable
+            WorkManagerImpl(context, configuration, taskExecutor, database)
+            // Prune records for the next run.
+            runWithTimingDisabled {
+                runBlocking {
+                    executor.job.children.forEach {
+                        it.join()
+                    }
+                }
+                database.workSpecDao().pruneFinishedWorkWithZeroDependentsIgnoringKeepForAtLeast()
+                database.close()
+            }
+        }
+    }
+}
diff --git a/work/workmanager-benchmark/src/androidTest/java/androidx/work/benchmark/NoOpWorker.kt b/work/workmanager-benchmark/src/androidTest/java/androidx/work/benchmark/NoOpWorker.kt
new file mode 100644
index 0000000..760ffd9
--- /dev/null
+++ b/work/workmanager-benchmark/src/androidTest/java/androidx/work/benchmark/NoOpWorker.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.work.benchmark
+
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+
+/**
+ * Does not do anything useful except returning a [androidx.work.ListenableWorker.Result.success].
+ */
+class NoOpWorker(context: Context, parameters: WorkerParameters) :
+    CoroutineWorker(context, parameters) {
+    override suspend fun doWork(): Result {
+        return Result.success()
+    }
+}
\ No newline at end of file
diff --git a/work/workmanager-benchmark/src/main/AndroidManifest.xml b/work/workmanager-benchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4eb2ff3
--- /dev/null
+++ b/work/workmanager-benchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ 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.
+  -->
+<manifest package="androidx.work.benchmark" />
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java b/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
index e5120d2..9c52835b 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
@@ -93,7 +93,7 @@
      * @hide
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public static void setDelegate(WorkManagerImpl delegate) {
+    public static void setDelegate(@Nullable WorkManagerImpl delegate) {
         synchronized (sLock) {
             sDelegatedInstance = delegate;
         }
@@ -220,10 +220,33 @@
             @NonNull Configuration configuration,
             @NonNull TaskExecutor workTaskExecutor,
             boolean useTestDatabase) {
+        this(context,
+                configuration,
+                workTaskExecutor,
+                WorkDatabase.create(
+                        context.getApplicationContext(),
+                        configuration.getTaskExecutor(),
+                        useTestDatabase)
+        );
+    }
 
+    /**
+     * Create an instance of {@link WorkManagerImpl}.
+     *
+     * @param context          The application {@link Context}
+     * @param configuration    The {@link Configuration} configuration
+     * @param workTaskExecutor The {@link TaskExecutor} for running "processing" jobs, such as
+     *                         enqueueing, scheduling, cancellation, etc.
+     * @param database         The {@link WorkDatabase}
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public WorkManagerImpl(
+            @NonNull Context context,
+            @NonNull Configuration configuration,
+            @NonNull TaskExecutor workTaskExecutor,
+            @NonNull WorkDatabase database) {
         Context applicationContext = context.getApplicationContext();
-        WorkDatabase database = WorkDatabase.create(
-                applicationContext, configuration.getTaskExecutor(), useTestDatabase);
         Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
         List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
         Processor processor = new Processor(