Initial port of BenchmarkState to androidx
Bug: 72877822
Test: ./gradlew benchmark:cC
Added minimal tests, no renaming at this point.
Change-Id: I509dd6e0d9104dc290bb3ee9bed1a78b36d0c8a1
diff --git a/benchmark/build.gradle b/benchmark/build.gradle
new file mode 100644
index 0000000..babffd4
--- /dev/null
+++ b/benchmark/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 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 static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+
+plugins {
+ id("SupportAndroidLibraryPlugin")
+ id("kotlin-android")
+}
+
+dependencies {
+ api(project(":annotation"))
+
+ implementation(TEST_RUNNER)
+
+ androidTestImplementation(JUNIT)
+ androidTestImplementation(KOTLIN_STDLIB)
+}
+
+supportLibrary {
+ name = "Android Benchmark"
+ publish = false
+ mavenVersion = LibraryVersions.BENCHMARK
+ mavenGroup = LibraryGroups.BENCHMARK
+ inceptionYear = "2018"
+ description = "Android Benchmark"
+ failOnUncheckedWarnings = true
+ failOnDeprecationWarnings = true
+}
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt b/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
new file mode 100644
index 0000000..fc9f4a5
--- /dev/null
+++ b/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 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.benchmark
+
+import android.support.test.filters.SmallTest
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.util.concurrent.TimeUnit
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BenchmarkStateTest {
+ fun ms2ns(ms: Long): Long = TimeUnit.MILLISECONDS.toNanos(ms)
+
+ @Test
+ fun simple() {
+ // would be better to mock the clock, but going with minimal changes for now
+ val state = BenchmarkState()
+ while (state.keepRunning()) {
+ Thread.sleep(3)
+ state.pauseTiming()
+ Thread.sleep(5)
+ state.resumeTiming()
+ }
+ val median = state.stats.median
+ assertTrue("median $median should be between 2ms and 4ms",
+ ms2ns(2) < median && median < ms2ns(4))
+ }
+}
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/StatsTest.kt b/benchmark/src/androidTest/java/androidx/benchmark/StatsTest.kt
new file mode 100644
index 0000000..5880a36
--- /dev/null
+++ b/benchmark/src/androidTest/java/androidx/benchmark/StatsTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 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.benchmark
+
+import android.support.test.filters.SmallTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class StatsTest {
+ @Test
+ fun simple() {
+ val stats = Stats(listOf(10, 10, 10, 10))
+ assertEquals(10.0, stats.mean, 0.0)
+ assertEquals(10, stats.median)
+ assertEquals(10, stats.max)
+ assertEquals(10, stats.min)
+ assertEquals(0.0, stats.standardDeviation, 0.0)
+ assertEquals(10, stats.percentile90)
+ assertEquals(10, stats.percentile95)
+ }
+
+ @Test
+ fun percentile() {
+ val stats = Stats((1L..100L).toList())
+ assertEquals(50.5, stats.mean, 0.0)
+ assertTrue(stats.median == 50L || stats.median == 51L)
+ assertEquals(100, stats.max)
+ assertEquals(1, stats.min)
+ assertEquals(29.01, stats.standardDeviation, 0.05)
+ assertEquals(90, stats.percentile90)
+ assertEquals(95, stats.percentile95)
+ }
+}
diff --git a/benchmark/src/main/AndroidManifest.xml b/benchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f4d89ba
--- /dev/null
+++ b/benchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.benchmark"/>
diff --git a/benchmark/src/main/java/androidx/benchmark/BenchmarkState.java b/benchmark/src/main/java/androidx/benchmark/BenchmarkState.java
new file mode 100644
index 0000000..f6ac275
--- /dev/null
+++ b/benchmark/src/main/java/androidx/benchmark/BenchmarkState.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2016 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.benchmark;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Debug;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provides a benchmark framework.
+ *
+ * Example usage:
+ * // Executes the code while keepRunning returning true.
+ *
+ * public void sampleMethod() {
+ * BenchmarkState state = new BenchmarkState();
+ *
+ * int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ * while (state.keepRunning()) {
+ * int[] dest = new int[src.length];
+ * System.arraycopy(src, 0, dest, 0, src.length);
+ * }
+ * System.out.println(state.summaryLine());
+ * }
+ */
+public final class BenchmarkState {
+
+ private static final String TAG = "BenchmarkState";
+ private static final boolean ENABLE_PROFILING = false;
+
+ private static final int NOT_STARTED = 0; // The benchmark has not started yet.
+ private static final int WARMUP = 1; // The benchmark is warming up.
+ private static final int RUNNING = 2; // The benchmark is running.
+ private static final int FINISHED = 3; // The benchmark has stopped.
+
+ private int mState = NOT_STARTED; // Current benchmark state.
+
+ private static final long WARMUP_DURATION_NS = ms2ns(250); // warm-up for at least 250ms
+ private static final int WARMUP_MIN_ITERATIONS = 16; // minimum iterations to warm-up for
+
+ // TODO: Tune these values.
+ private static final long TARGET_TEST_DURATION_NS = ms2ns(500); // target testing for 500 ms
+ private static final int MAX_TEST_ITERATIONS = 1000000;
+ private static final int MIN_TEST_ITERATIONS = 10;
+ private static final int REPEAT_COUNT = 5;
+
+ private long mStartTimeNs = 0; // Previously captured System.nanoTime().
+ private boolean mPaused;
+ private long mPausedTimeNs = 0; // The System.nanoTime() when the pauseTiming() is called.
+ private long mPausedDurationNs = 0; // The duration of paused state in nano sec.
+
+ private int mIteration = 0;
+ private int mMaxIterations = 0;
+
+ private int mRepeatCount = 0;
+
+ // Statistics. These values will be filled when the benchmark has finished.
+ // The computation needs double precision, but long int is fine for final reporting.
+ private Stats mStats;
+
+ // Individual duration in nano seconds.
+ private ArrayList<Long> mResults = new ArrayList<>();
+
+ private static long ms2ns(long ms) {
+ return TimeUnit.MILLISECONDS.toNanos(ms);
+ }
+
+ /**
+ * Stops the benchmark timer.
+ * <p>
+ * This method can be called only when the timer is running.
+ */
+ public void pauseTiming() {
+ if (mPaused) {
+ throw new IllegalStateException(
+ "Unable to pause the benchmark. The benchmark has already paused.");
+ }
+ mPausedTimeNs = System.nanoTime();
+ mPaused = true;
+ }
+
+ /**
+ * Starts the benchmark timer.
+ * <p>
+ * This method can be called only when the timer is stopped.
+ */
+ public void resumeTiming() {
+ if (!mPaused) {
+ throw new IllegalStateException(
+ "Unable to resume the benchmark. The benchmark is already running.");
+ }
+ mPausedDurationNs += System.nanoTime() - mPausedTimeNs;
+ mPausedTimeNs = 0;
+ mPaused = false;
+ }
+
+ private void beginWarmup() {
+ mStartTimeNs = System.nanoTime();
+ mIteration = 0;
+ mState = WARMUP;
+ }
+
+ private void beginBenchmark(long warmupDuration, int iterations) {
+ if (ENABLE_PROFILING && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ // TODO: support data dir for old platforms
+ File f = new File(InstrumentationRegistry.getContext().getDataDir(), "benchprof");
+ Log.d(TAG, "Tracing to: " + f.getAbsolutePath());
+ Debug.startMethodTracingSampling(f.getAbsolutePath(), 16 * 1024 * 1024, 100);
+ }
+ mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
+ mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
+ Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
+ mPausedDurationNs = 0;
+ mIteration = 0;
+ mRepeatCount = 0;
+ mState = RUNNING;
+ mStartTimeNs = System.nanoTime();
+ }
+
+ private boolean startNextTestRun() {
+ final long currentTime = System.nanoTime();
+ mResults.add((currentTime - mStartTimeNs - mPausedDurationNs) / mMaxIterations);
+ mRepeatCount++;
+ if (mRepeatCount >= REPEAT_COUNT) {
+ if (ENABLE_PROFILING) {
+ Debug.stopMethodTracing();
+ }
+ mStats = new Stats(mResults);
+ mState = FINISHED;
+ return false;
+ }
+ mPausedDurationNs = 0;
+ mIteration = 0;
+ mStartTimeNs = System.nanoTime();
+ return true;
+ }
+
+ /**
+ * Judges whether the benchmark needs more samples.
+ *
+ * For the usage, see class comment.
+ */
+ public boolean keepRunning() {
+ switch (mState) {
+ case NOT_STARTED:
+ beginWarmup();
+ return true;
+ case WARMUP:
+ mIteration++;
+ // Only check nanoTime on every iteration in WARMUP since we
+ // don't yet have a target iteration count.
+ final long duration = System.nanoTime() - mStartTimeNs;
+ if (mIteration >= WARMUP_MIN_ITERATIONS && duration >= WARMUP_DURATION_NS) {
+ beginBenchmark(duration, mIteration);
+ }
+ return true;
+ case RUNNING:
+ mIteration++;
+ if (mIteration >= mMaxIterations) {
+ return startNextTestRun();
+ }
+ if (mPaused) {
+ throw new IllegalStateException("Benchmark step finished with paused state. "
+ + "Resume the benchmark before finishing each step.");
+ }
+ return true;
+ case FINISHED:
+ throw new IllegalStateException("The benchmark has finished.");
+ default:
+ throw new IllegalStateException("The benchmark is in unknown state.");
+ }
+ }
+
+ /**
+ * Get the end of run benchmark statistics.
+ * <p>
+ * This method may only be called keepRunning() returns {@code false}.
+ *
+ * @return Stats from run.
+ */
+ @NonNull
+ public Stats getStats() {
+ if (mState != FINISHED) {
+ throw new IllegalStateException("The benchmark hasn't finished");
+ }
+ return mStats;
+ }
+
+ private long mean() {
+ return (long) getStats().getMean();
+ }
+
+ private long median() {
+ return getStats().getMedian();
+ }
+
+ private long min() {
+ return getStats().getMin();
+ }
+
+ private long standardDeviation() {
+ return (long) getStats().getStandardDeviation();
+ }
+
+ private String summaryLine() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Summary: ");
+ sb.append("median=").append(median()).append("ns, ");
+ sb.append("mean=").append(mean()).append("ns, ");
+ sb.append("min=").append(min()).append("ns, ");
+ sb.append("sigma=").append(standardDeviation()).append(", ");
+ sb.append("iteration=").append(mResults.size()).append(", ");
+ // print out the first few iterations' number for double checking.
+ int sampleNumber = Math.min(mResults.size(), 16);
+ for (int i = 0; i < sampleNumber; i++) {
+ sb.append("No ").append(i).append(" result is ").append(mResults.get(i)).append(", ");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Submit status report bundle as a RESULT_OK to the passed Instrumentation
+ *
+ * @param instrumentation Instrumentation used to signal result.
+ * @param key Run identifier, prepended to bundle properties.
+ */
+ @SuppressWarnings("WeakerAccess")
+ public void sendFullStatusReport(@NonNull Instrumentation instrumentation,
+ @NonNull String key) {
+ Log.i(TAG, key + summaryLine());
+ Bundle status = new Bundle();
+ status.putLong(key + "_median", median());
+ status.putLong(key + "_mean", mean());
+ status.putLong(key + "_min", min());
+ status.putLong(key + "_standardDeviation", standardDeviation());
+ instrumentation.sendStatus(Activity.RESULT_OK, status);
+ }
+}
diff --git a/benchmark/src/main/java/androidx/benchmark/PerfStatusReporter.java b/benchmark/src/main/java/androidx/benchmark/PerfStatusReporter.java
new file mode 100644
index 0000000..d508d00
--- /dev/null
+++ b/benchmark/src/main/java/androidx/benchmark/PerfStatusReporter.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2018 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.benchmark;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Use this rule to make sure we report the status after the test success.
+ *
+ * <code>
+ *
+ * @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ * @Test public void functionName() {
+ * ...
+ * BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ * while (state.keepRunning()) {
+ * // DO YOUR TEST HERE!
+ * }
+ * ...
+ * }
+ * </code>
+ *
+ * When test succeeded, the status report will use the key as
+ * "functionName[optional subTestName]_*"
+ *
+ * Notice that optional subTestName can't be just numbers, that means each sub test needs to have a
+ * name when using parameterization.
+ */
+
+public class PerfStatusReporter implements TestRule {
+ private static final String TAG = "PerfStatusReporter";
+ @SuppressWarnings("WeakerAccess") // synthetic access
+ final BenchmarkState mState = new BenchmarkState();
+
+ @NonNull
+ public BenchmarkState getBenchmarkState() {
+ return mState;
+ }
+
+ @NonNull
+ @Override
+ public Statement apply(@NonNull final Statement base, @NonNull final Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ String invokeMethodName = description.getMethodName();
+ Log.i(TAG, "Running " + description.getClassName() + "#" + invokeMethodName);
+
+ // validate and simplify the function name.
+ // First, remove the "test" prefix which normally comes from CTS test.
+ // Then make sure the [subTestName] is valid, not just numbers like [0].
+ if (invokeMethodName.startsWith("test")) {
+ assertTrue("The test name " + invokeMethodName + " is too short",
+ invokeMethodName.length() > 5);
+ invokeMethodName = invokeMethodName.substring(4, 5).toLowerCase()
+ + invokeMethodName.substring(5);
+ }
+
+ int index = invokeMethodName.lastIndexOf('[');
+ if (index > 0) {
+ boolean allDigits = true;
+ for (int i = index + 1; i < invokeMethodName.length() - 1; i++) {
+ if (!Character.isDigit(invokeMethodName.charAt(i))) {
+ allDigits = false;
+ break;
+ }
+ }
+ assertFalse("The name in [] can't contain only digits for " + invokeMethodName,
+ allDigits);
+ }
+
+ base.evaluate();
+
+ mState.sendFullStatusReport(InstrumentationRegistry.getInstrumentation(),
+ invokeMethodName);
+ }
+ };
+ }
+}
+
diff --git a/benchmark/src/main/java/androidx/benchmark/Stats.java b/benchmark/src/main/java/androidx/benchmark/Stats.java
new file mode 100644
index 0000000..28c2bbc
--- /dev/null
+++ b/benchmark/src/main/java/androidx/benchmark/Stats.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 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.benchmark;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Provides statistics such as mean, median, min, max, and percentiles, given a list of input
+ * values.
+ */
+public class Stats {
+ private long mMedian, mMin, mMax, mPercentile90, mPercentile95;
+ private double mMean, mStandardDeviation;
+
+ /* Calculate stats in constructor. */
+ public Stats(@NonNull List<Long> values) {
+ // make a copy since we're modifying it
+ values = new ArrayList<>(values);
+ final int size = values.size();
+ if (size < 2) {
+ throw new IllegalArgumentException("At least two results are necessary.");
+ }
+
+ Collections.sort(values);
+
+ mMin = values.get(0);
+ mMax = values.get(values.size() - 1);
+
+ mMedian = size % 2 == 0
+ ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2
+ : values.get(size / 2);
+ mPercentile90 = getPercentile(values, 90);
+ mPercentile95 = getPercentile(values, 95);
+
+ for (int i = 0; i < size; ++i) {
+ long result = values.get(i);
+ mMean += result;
+ }
+ mMean /= (double) size;
+
+ for (int i = 0; i < size; ++i) {
+ final double tmp = values.get(i) - mMean;
+ mStandardDeviation += tmp * tmp;
+ }
+ mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1));
+ }
+
+ public double getMean() {
+ return mMean;
+ }
+
+ public long getMedian() {
+ return mMedian;
+ }
+
+ public long getMax() {
+ return mMax;
+ }
+
+ public long getMin() {
+ return mMin;
+ }
+
+ public double getStandardDeviation() {
+ return mStandardDeviation;
+ }
+
+ public long getPercentile90() {
+ return mPercentile90;
+ }
+
+ public long getPercentile95() {
+ return mPercentile95;
+ }
+
+ private static long getPercentile(List<Long> values, int percentile) {
+ if (percentile < 0 || percentile > 100) {
+ throw new IllegalArgumentException(
+ "invalid percentile " + percentile + ", should be 0-100");
+ }
+ int idx = (values.size() - 1) * percentile / 100;
+ return values.get(idx);
+ }
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index f206a87..78e93f0 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -24,6 +24,7 @@
const val APPCOMPAT = "androidx.appcompat"
const val ASYNCLAYOUTINFLATER = "androidx.asynclayoutinflater"
const val BROWSER = "androidx.browser"
+ const val BENCHMARK = "androidx.benchmark"
const val CAR = "androidx.car"
const val CARDVIEW = "androidx.cardview"
const val COLLECTION = "androidx.collection"
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index b5be3c8..d66e216 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -112,6 +112,11 @@
*/
val MEDIA2 = Version("1.0.0-alpha01")
+ /*
+ * Version code for Benchmark
+ */
+ val BENCHMARK = Version("1.0.0-alpha01")
+
/**
* Version code for Leanback
*/
diff --git a/settings.gradle b/settings.gradle
index 507a94b..deb845d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -39,6 +39,7 @@
includeProject(":arch:core-testing", "app-toolkit/core-testing")
includeProject(":arch:core-runtime", "app-toolkit/runtime")
includeProject(":asynclayoutinflater", "asynclayoutinflater")
+includeProject(":benchmark", "benchmark")
includeProject(":browser", "browser")
includeProject(":car", "car")
includeProject(":cardview", "cardview")