Merge "[GH] Use projectOrArtifact for appcompat-lint's core dependency" into androidx-main
diff --git a/appcompat/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
index d128244..8176114 100644
--- a/appcompat/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
@@ -242,6 +242,10 @@
android:configChanges="orientation|screenSize|keyboardHidden"/>
<activity
+ android:name="androidx.appcompat.app.g3.FilternatorActivityWithCustomDefault"
+ android:configChanges="orientation|screenSize|keyboardHidden"/>
+
+ <activity
android:name="androidx.appcompat.app.g3.OldTranslateActivity"
android:configChanges="screenSize|keyboardHidden|orientation|smallestScreenSize|screenLayout"
android:recreateOnConfigChanges="mcc|mnc"
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorActivityWithCustomDefault.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorActivityWithCustomDefault.java
new file mode 100644
index 0000000..f8763e9
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorActivityWithCustomDefault.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 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.appcompat.app.g3;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.app.AppCompatDelegate;
+
+import java.util.concurrent.CountDownLatch;
+
+public class FilternatorActivityWithCustomDefault extends AppCompatActivity {
+ public static CountDownLatch configurationLatch = new CountDownLatch(1);
+ public static Exception configurationException = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Configuration initialConfig =
+ new Configuration(getApplication().getResources().getConfiguration());
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
+
+ super.onCreate(savedInstanceState);
+
+ if (!initialConfig.equals(getApplication().getResources().getConfiguration())) {
+ throw new IllegalStateException("Base configuration got messed up");
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ Configuration actualConfig = getResources().getConfiguration();
+ if (actualConfig.equals(newConfig)) {
+ int diff = actualConfig.diff(newConfig);
+ configurationException = new RuntimeException("Configuration changes not correctly "
+ + "reflected in getResources().getConfiguration(), diff is " + diff + ", "
+ + "actual config is " + actualConfig + ", new config is " + newConfig);
+ }
+
+ if (configurationLatch.getCount() > 0) {
+ configurationLatch.countDown();
+ }
+ }
+}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTestWithCustomDefault.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTestWithCustomDefault.kt
new file mode 100644
index 0000000..45357c5
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/g3/FilternatorTestWithCustomDefault.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2022 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.appcompat.app.g3
+
+import androidx.appcompat.Orientation
+import androidx.appcompat.app.AppCompatDelegate
+import androidx.appcompat.withOrientation
+import androidx.lifecycle.Lifecycle
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import androidx.testutils.LifecycleOwnerUtils.waitUntilState
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.TimeUnit
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Regression test for b/236394773, adapted from GmsCore's own tests. The activity used for this
+ * test has been modified to always call AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES).
+ *
+ * The primary purpose of this test is to ensure that the application configuration is not
+ * accidentally modified when we modify the activity configuration.
+ */
+@Suppress("SameParameterValue")
+@LargeTest
+@SdkSuppress(minSdkVersion = 18) // UiDevice
+@RunWith(AndroidJUnit4::class)
+class FilternatorTestWithCustomDefault {
+ @get:Rule
+ val activityRule = ActivityScenarioRule(FilternatorActivityWithCustomDefault::class.java)
+
+ private lateinit var uiDevice: UiDevice
+
+ @Before
+ fun setup() {
+ uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
+ }
+ }
+
+ @After
+ fun teardown() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
+ }
+ }
+
+ @Test
+ fun testConfigurationUpdatedOnLandscapeMode() {
+ // Wait for the activity to fully start before rotating,
+ // otherwise we won't receive onConfigurationChanged.
+ val activity = activityRule.withActivity { this }
+ waitUntilState(activity, Lifecycle.State.RESUMED)
+
+ // Rotate and wait for the activity to check that
+ // configuration has been properly updated.
+ uiDevice.withOrientation(Orientation.LEFT) {
+ FilternatorActivity.configurationLatch.await(5000, TimeUnit.MILLISECONDS)
+ assertThat(FilternatorActivity.configurationException).isNull()
+ }
+ }
+}
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatActivity.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatActivity.java
index 981c192..5ce91f2 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatActivity.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatActivity.java
@@ -225,14 +225,16 @@
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (mResources != null) {
- // The real (and thus managed) resources object was already updated
- // by ResourcesManager, so pull the current metrics from there.
- final DisplayMetrics newMetrics = super.getResources().getDisplayMetrics();
- mResources.updateConfiguration(newConfig, newMetrics);
- }
-
+ // The delegate may modify the real resources object or the config param to implement its
+ // desired configuration overrides. Let it do it's thing and then use the resulting state.
getDelegate().onConfigurationChanged(newConfig);
+
+ // Manually propagate configuration changes to our unmanaged resources object.
+ if (mResources != null) {
+ final Configuration currConfig = super.getResources().getConfiguration();
+ final DisplayMetrics currMetrics = super.getResources().getDisplayMetrics();
+ mResources.updateConfiguration(currConfig, currMetrics);
+ }
}
@Override
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
index 8219742..09c00d772 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -366,7 +366,7 @@
if (sCanApplyOverrideConfiguration
&& baseContext instanceof android.view.ContextThemeWrapper) {
final Configuration config = createOverrideConfigurationForDayNight(
- baseContext, modeToApply, null);
+ baseContext, modeToApply, null, false);
if (DEBUG) {
Log.d(TAG, String.format("Attempting to apply config to base context: %s",
config.toString()));
@@ -386,7 +386,7 @@
// Again, but using the AppCompat version of ContextThemeWrapper.
if (baseContext instanceof ContextThemeWrapper) {
final Configuration config = createOverrideConfigurationForDayNight(
- baseContext, modeToApply, null);
+ baseContext, modeToApply, null, false);
if (DEBUG) {
Log.d(TAG, String.format("Attempting to apply config to base context: %s",
config.toString()));
@@ -443,7 +443,7 @@
}
final Configuration config = createOverrideConfigurationForDayNight(
- baseContext, modeToApply, configOverlay);
+ baseContext, modeToApply, configOverlay, true);
if (DEBUG) {
Log.d(TAG, String.format("Applying night mode using ContextThemeWrapper and "
+ "applyOverrideConfiguration(). Config: %s", config.toString()));
@@ -664,6 +664,10 @@
// Re-apply Day/Night with the new configuration but disable recreations. Since this
// configuration change has only just happened we can safely just update the resources now
applyDayNight(false);
+
+ // We may have just changed the resource configuration. Make sure that everyone after us
+ // sees the same configuration by modifying the parameter's internal state.
+ newConfig.updateFrom(mContext.getResources().getConfiguration());
}
@Override
@@ -2464,7 +2468,7 @@
@NonNull
private Configuration createOverrideConfigurationForDayNight(
@NonNull Context context, @ApplyableNightMode final int mode,
- @Nullable Configuration configOverlay) {
+ @Nullable Configuration configOverlay, boolean ignoreFollowSystem) {
int newNightMode;
switch (mode) {
case MODE_NIGHT_YES:
@@ -2475,11 +2479,17 @@
break;
default:
case MODE_NIGHT_FOLLOW_SYSTEM:
- // If we're following the system, we just use the system default from the
- // application context
- final Configuration appConfig =
- context.getApplicationContext().getResources().getConfiguration();
- newNightMode = appConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ if (ignoreFollowSystem) {
+ // We're generating an overlay to be used on top of the system configuration,
+ // so use whatever's already there.
+ newNightMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
+ } else {
+ // If we're following the system, we just use the system default from the
+ // application context
+ final Configuration appConfig =
+ context.getApplicationContext().getResources().getConfiguration();
+ newNightMode = appConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ }
break;
}
@@ -2508,7 +2518,7 @@
boolean handled = false;
final Configuration overrideConfig =
- createOverrideConfigurationForDayNight(mContext, mode, null);
+ createOverrideConfigurationForDayNight(mContext, mode, null, false);
final boolean activityHandlingUiMode = isActivityManifestHandlingUiMode(mContext);
final Configuration currentConfiguration = mEffectiveConfiguration == null
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java
index 56d9ec7..b8c582d 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java
@@ -33,6 +33,11 @@
* A context wrapper that allows you to modify or replace the theme of the wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
+ /**
+ * Lazily-populated configuration object representing an empty, default configuration.
+ */
+ private static Configuration sEmptyConfig;
+
private int mThemeResource;
private Resources.Theme mTheme;
private LayoutInflater mInflater;
@@ -113,7 +118,10 @@
private Resources getResourcesInternal() {
if (mResources == null) {
- if (mOverrideConfiguration == null) {
+ if (isEmptyConfiguration(mOverrideConfiguration)) {
+ // If we're not applying any overrides, use the base context's resources. On API
+ // 26+, this will avoid pulling in resources that share a backing implementation
+ // with the application context.
mResources = super.getResources();
} else if (Build.VERSION.SDK_INT >= 17) {
final Context resContext =
@@ -203,6 +211,26 @@
return getResources().getAssets();
}
+ /**
+ * @return {@code true} if the specified configuration is {@code null} or is a no-op when
+ * used as a configuration overlay
+ */
+ private static boolean isEmptyConfiguration(Configuration overrideConfiguration) {
+ if (overrideConfiguration == null) {
+ return true;
+ }
+
+ if (sEmptyConfig == null) {
+ Configuration emptyConfig = new Configuration();
+ // Workaround for incorrect default fontScale on earlier SDKs (b/29924927). Note
+ // that Configuration.setToDefaults() is *not* a no-op configuration overlay.
+ emptyConfig.fontScale = 0.0f;
+ sEmptyConfig = emptyConfig;
+ }
+
+ return overrideConfiguration.equals(sEmptyConfig);
+ }
+
@RequiresApi(17)
static class Api17Impl {
private Api17Impl() {
diff --git a/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt b/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt
index 8b7dffc..0e381a6 100644
--- a/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt
+++ b/benchmark/benchmark-macro-junit4/api/public_plus_experimental_current.txt
@@ -4,6 +4,7 @@
@RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
ctor public BaselineProfileRule();
method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ method public void collectBaselineProfile(String packageName, optional java.util.List<java.lang.String> packageFilters, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
}
diff --git a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
index 63d2072..a65f294 100644
--- a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
+++ b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
@@ -80,15 +80,20 @@
/**
* Collects baseline profiles for a critical user journey.
* @param packageName Package name of the app for which profiles are to be generated.
+ * @param packageFilters List of package names to use as a filter for the generated profiles.
+ * By default no filters are applied. Note that this works only when the code is not obfuscated.
* @param [profileBlock] defines the critical user journey.
*/
+ @JvmOverloads
public fun collectBaselineProfile(
packageName: String,
+ packageFilters: List<String> = emptyList(),
profileBlock: MacrobenchmarkScope.() -> Unit
) {
collectBaselineProfile(
- currentDescription.toUniqueName(),
+ uniqueName = currentDescription.toUniqueName(),
packageName = packageName,
+ packageFilters = packageFilters,
profileBlock = profileBlock
)
}
diff --git a/benchmark/benchmark-macro/api/restricted_current.txt b/benchmark/benchmark-macro/api/restricted_current.txt
index ed523f7..53fcd5a 100644
--- a/benchmark/benchmark-macro/api/restricted_current.txt
+++ b/benchmark/benchmark-macro/api/restricted_current.txt
@@ -13,6 +13,7 @@
}
public final class BaselineProfilesKt {
+ method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void collectBaselineProfile(String uniqueName, String packageName, optional java.util.List<java.lang.String> packageFilters, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void collectBaselineProfile(String uniqueName, String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
index f5ad12a..e11d2c8 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
@@ -33,9 +33,11 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
@RequiresApi(28)
+@JvmOverloads
fun collectBaselineProfile(
uniqueName: String,
packageName: String,
+ packageFilters: List<String> = emptyList(),
profileBlock: MacrobenchmarkScope.() -> Unit
) {
require(Build.VERSION.SDK_INT >= 28) {
@@ -79,17 +81,32 @@
Log.d(TAG, "Converting to human readable profile format")
// Look at reference profile first, and then fallback to current profile
val profile = profile(apkPath, listOf(referenceProfile, currentProfile))
+
+ // Filters the profile output with the given set or rules.
+ // Note that the filter rules are package name but the profile file lines contain
+ // jvm method signature, ex: `HSPLandroidx/room/RoomDatabase;-><init>()V`.
+ // In order to simplify this for developers we transform the filters from regular package names.
+ val filteredProfile = if (packageFilters.isEmpty()) profile else {
+ // Ensure that the package name ends with `/`
+ val fixedPackageFilters = packageFilters
+ .map { "${it.replace(".", "/")}${if (it.endsWith(".")) "" else "/"}" }
+ profile
+ .lines()
+ .filter { line -> fixedPackageFilters.any { line.contains(it) } }
+ .joinToString(System.lineSeparator())
+ }
+
InstrumentationResults.instrumentationReport {
val fileName = "$uniqueName-baseline-prof.txt"
val absolutePath = Outputs.writeFile(fileName, "baseline-profile") {
- it.writeText(profile)
+ it.writeText(filteredProfile)
}
// Write a file with a timestamp to be able to disambiguate between runs with the same
// unique name.
val tsFileName = "$uniqueName-baseline-prof-${Outputs.dateToFileName()}.txt"
val tsAbsolutePath = Outputs.writeFile(tsFileName, "baseline-profile-ts") {
Log.d(TAG, "Pull Baseline Profile with: `adb pull \"${it.absolutePath}\" .`")
- it.writeText(profile)
+ it.writeText(filteredProfile)
}
val totalRunTime = System.nanoTime() - startTime
val summary = summaryRecord(totalRunTime, absolutePath, tsAbsolutePath)
diff --git a/benchmark/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/benchmark/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
index b42a78b..25e882a 100644
--- a/benchmark/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/benchmark/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -53,6 +53,15 @@
</activity>
<activity
+ android:name=".EmptyActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="androidx.benchmark.integration.macrobenchmark.target.EMPTY_ACTIVITY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name=".RecyclerViewActivity"
android:exported="true">
<intent-filter>
diff --git a/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/EmptyActivity.kt b/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/EmptyActivity.kt
new file mode 100644
index 0000000..3c30af3
--- /dev/null
+++ b/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/EmptyActivity.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 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.integration.macrobenchmark.target
+
+import android.os.Bundle
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+
+class EmptyActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ findViewById<TextView>(R.id.txtNotice).setText(R.string.app_notice)
+ }
+}
diff --git a/benchmark/integration-tests/macrobenchmark/build.gradle b/benchmark/integration-tests/macrobenchmark/build.gradle
index 0aee23f..7bfa4bb 100644
--- a/benchmark/integration-tests/macrobenchmark/build.gradle
+++ b/benchmark/integration-tests/macrobenchmark/build.gradle
@@ -38,6 +38,7 @@
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.testUiautomator)
+ androidTestImplementation(libs.testExtTruth)
}
// Allow usage of Kotlin's @OptIn.
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileFilterTest.kt b/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileFilterTest.kt
new file mode 100644
index 0000000..c47cb7c
--- /dev/null
+++ b/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileFilterTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2022 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.integration.macrobenchmark
+
+import android.content.Intent
+import androidx.benchmark.DeviceInfo
+import com.google.common.truth.Truth.assertThat
+import androidx.benchmark.Outputs
+import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
+import androidx.benchmark.macro.junit4.BaselineProfileRule
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import java.io.File
+import org.junit.Assume
+import org.junit.Rule
+import org.junit.Test
+
+@LargeTest
+@SdkSuppress(minSdkVersion = 29)
+@OptIn(ExperimentalBaselineProfilesApi::class)
+class BaselineProfileFilterTest {
+
+ @get:Rule
+ val baselineRule = BaselineProfileRule()
+
+ @Test
+ fun baselineProfilesFilter() {
+ Assume.assumeTrue(DeviceInfo.isRooted)
+
+ // Collects the baseline profile
+ baselineRule.collectBaselineProfile(
+ packageName = PACKAGE_NAME,
+ packageFilters = listOf(PACKAGE_NAME),
+ profileBlock = {
+ startActivityAndWait(Intent(ACTION))
+ device.waitForIdle()
+ }
+ )
+
+ // Asserts the output of the baseline profile
+ val lines = File(Outputs.outputDirectory, BASELINE_PROFILE_OUTPUT_FILE_NAME).readLines()
+ assertThat(lines).containsExactly(
+ "HSPLandroidx/benchmark/integration/macrobenchmark/target/EmptyActivity;" +
+ "-><init>()V",
+ "HSPLandroidx/benchmark/integration/macrobenchmark/target/EmptyActivity;" +
+ "->onCreate(Landroid/os/Bundle;)V",
+ "Landroidx/benchmark/integration/macrobenchmark/target/EmptyActivity;",
+ )
+ }
+
+ companion object {
+ private const val PACKAGE_NAME =
+ "androidx.benchmark.integration.macrobenchmark.target"
+ private const val ACTION =
+ "androidx.benchmark.integration.macrobenchmark.target.EMPTY_ACTIVITY"
+
+ // Note: this name is automatically generated starting from class and method name,
+ // according to the patter `<class>_<method>-baseline-prof.txt`. Changes for class and
+ // method names should be reflected here in order for the test to succeed.
+ private const val BASELINE_PROFILE_OUTPUT_FILE_NAME =
+ "BaselineProfileFilterTest_baselineProfilesFilter-baseline-prof.txt"
+ }
+}
diff --git a/busytown/androidx.sh b/busytown/androidx.sh
index 50396d2..7813bc8 100755
--- a/busytown/androidx.sh
+++ b/busytown/androidx.sh
@@ -21,6 +21,7 @@
if ! impl/build.sh buildOnServer checkExternalLicenses listTaskOutputs validateProperties \
-Pandroidx.enableComposeCompilerMetrics=true \
-Pandroidx.enableComposeCompilerReports=true \
+ --no-daemon \
--profile "$@"; then
EXIT_VALUE=1
fi
diff --git a/busytown/impl/build-metalava-and-androidx.sh b/busytown/impl/build-metalava-and-androidx.sh
index 5023d43..d24f86c 100755
--- a/busytown/impl/build-metalava-and-androidx.sh
+++ b/busytown/impl/build-metalava-and-androidx.sh
@@ -5,9 +5,9 @@
androidxArguments="$*"
-WORKING_DIR="$(pwd)"
SCRIPTS_DIR="$(cd $(dirname $0)/.. && pwd)"
cd "$SCRIPTS_DIR/../../.."
+CHECKOUT_ROOT="$(pwd)"
echo "Script running from $(pwd)"
# resolve dirs
@@ -24,17 +24,11 @@
export GRADLE_USER_HOME="$OUT_DIR/gradle"
mkdir -p "$GRADLE_USER_HOME"
-if [ "$ROOT_DIR" == "" ]; then
- ROOT_DIR="$WORKING_DIR"
-else
- ROOT_DIR="$(cd $ROOT_DIR && pwd)"
-fi
-
-METALAVA_DIR=$ROOT_DIR/tools/metalava
+METALAVA_DIR=$CHECKOUT_ROOT/tools/metalava
gw="$METALAVA_DIR/gradlew -Dorg.gradle.jvmargs=-Xmx24g"
# Use androidx prebuilt since we don't have metalava prebuilts
-export ANDROID_HOME="$WORKING_DIR/../../prebuilts/fullsdk-linux/"
+export ANDROID_HOME="$CHECKOUT_ROOT/prebuilts/fullsdk-linux/"
function buildMetalava() {
METALAVA_BUILD_LOG="$OUT_DIR/metalava.log"
@@ -52,7 +46,7 @@
# Mac grep doesn't support -P, so use perl version of `grep -oP "(?<=metalavaVersion=).*"`
export METALAVA_VERSION=`perl -nle'print $& while m{(?<=metalavaVersion=).*}g' $METALAVA_DIR/src/main/resources/version.properties`
-export METALAVA_REPO="$ROOT_DIR/out/dist/repo/m2repository"
+export METALAVA_REPO="$CHECKOUT_ROOT/out/dist/repo/m2repository"
function buildAndroidx() {
./frameworks/support/busytown/impl/build.sh $androidxArguments \
diff --git a/car/app/app-projected/src/test/java/androidx/car/app/media/ProjectedCarAudioRecordTest.java b/car/app/app-projected/src/test/java/androidx/car/app/media/ProjectedCarAudioRecordTest.java
index 329f07f..d60e6df 100644
--- a/car/app/app-projected/src/test/java/androidx/car/app/media/ProjectedCarAudioRecordTest.java
+++ b/car/app/app-projected/src/test/java/androidx/car/app/media/ProjectedCarAudioRecordTest.java
@@ -27,6 +27,7 @@
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -48,6 +49,7 @@
mCarContext.getFakeHost().setMicrophoneInputData(new ByteArrayInputStream(mArr));
}
+ @Ignore // b/234034123
@Test
public void readNotStarted_throws() {
byte[] arr = new byte[AUDIO_CONTENT_BUFFER_SIZE];
@@ -55,6 +57,7 @@
AUDIO_CONTENT_BUFFER_SIZE));
}
+ @Ignore // b/234034123
@Test
public void readAfterStop_throws() {
mCarAudioRecord.startRecording();
@@ -65,6 +68,7 @@
AUDIO_CONTENT_BUFFER_SIZE));
}
+ @Ignore // b/234034123
@Test
public void read() {
mCarAudioRecord.startRecording();
@@ -75,6 +79,7 @@
mCarAudioRecord.stopRecording();
}
+ @Ignore // b/234034123
@Test
public void readAfterAllRead_returns_negative_1() {
mCarAudioRecord.startRecording();
@@ -87,6 +92,7 @@
mCarAudioRecord.stopRecording();
}
+ @Ignore // b/234034123
@Test
public void stopRecording_tellsHostToStop() {
mCarAudioRecord.startRecording();
@@ -98,6 +104,7 @@
assertThat(mCarContext.getFakeHost().hasToldHostToStopRecording()).isTrue();
}
+ @Ignore // b/234034123
@Test
public void hostTellsToStop_noLongerReadsBytes() {
mCarAudioRecord.startRecording();
diff --git a/datastore/datastore-core-okio/build.gradle b/datastore/datastore-core-okio/build.gradle
index 1e6cc67..11f42b4 100644
--- a/datastore/datastore-core-okio/build.gradle
+++ b/datastore/datastore-core-okio/build.gradle
@@ -58,6 +58,8 @@
api(libs.kotlinTestCommon)
api(libs.kotlinTestAnnotationsCommon)
api(libs.kotlinCoroutinesTest)
+ api(project(":internal-testutils-datastore"))
+ api(project(":internal-testutils-kmp"))
}
}
jvmTest {
@@ -65,8 +67,11 @@
implementation(libs.kotlinCoroutinesTest)
implementation(libs.kotlinTest)
implementation(libs.kotlinTestAnnotationsCommon)
+ api(project(":internal-testutils-datastore"))
+ api(project(":internal-testutils-kmp"))
}
}
+
if (enableNative) {
nativeMain {
dependencies {
diff --git a/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/OkioStorageTest.kt b/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/OkioStorageTest.kt
index 3a326cd..7ae42ea 100644
--- a/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/OkioStorageTest.kt
+++ b/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/OkioStorageTest.kt
@@ -16,6 +16,7 @@
package androidx.datastore.core.okio
+import androidx.datastore.OkioTestIO
import androidx.datastore.core.ReadScope
import androidx.datastore.core.Storage
import androidx.datastore.core.StorageConnection
@@ -23,6 +24,8 @@
import androidx.datastore.core.readData
import androidx.datastore.core.use
import androidx.datastore.core.writeData
+import androidx.datastore.TestingOkioSerializer
+import androidx.datastore.TestingSerializerConfig
import androidx.kruth.assertThat
import androidx.kruth.assertThrows
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -43,21 +46,21 @@
class OkioStorageTest {
private lateinit var testPath: Path
- private lateinit var testingSerializer: TestingSerializer
+ private lateinit var testingSerializerConfig: TestingSerializerConfig
+ private lateinit var testingSerializer: TestingOkioSerializer
private lateinit var testStorage: Storage<Byte>
private lateinit var testConnection: StorageConnection<Byte>
private lateinit var testScope: TestScope
- private lateinit var fileScope: TestScope
- private lateinit var testIO: TestIO
+ private lateinit var testIO: OkioTestIO
private var fileSystem: FileSystem = FileSystem.SYSTEM
@BeforeTest
fun setUp() {
- testIO = TestIO()
- testingSerializer = TestingSerializer()
- testPath = testIO.tempDir() / "temp-file.tmpo"
+ testIO = OkioTestIO()
+ testingSerializerConfig = TestingSerializerConfig()
+ testingSerializer = TestingOkioSerializer(testingSerializerConfig)
+ testPath = testIO.newTempFile().path
testScope = TestScope(UnconfinedTestDispatcher())
- fileScope = TestScope(UnconfinedTestDispatcher())
testStorage = OkioStorage(fileSystem, testingSerializer) { testPath }
testConnection = testStorage.createConnection()
}
@@ -67,7 +70,7 @@
val data = testConnection.readData()
- assertThat(data).isEqualTo(0)
+ assertThat(data).isEqualTo(0.toByte())
}
@Test
@@ -179,7 +182,7 @@
coroutineScope {
testConnection.writeData(1)
- testingSerializer.failingWrite = true
+ testingSerializerConfig.failingWrite = true
assertThrows<IOException> { testConnection.writeData(1) }
}
@@ -275,7 +278,8 @@
@Test
fun testWriteToNonExistentDir() = testScope.runTest {
- val fileInNonExistentDir = testIO.tempDir() / "this/does/not/exist/foo.tst"
+ val fileInNonExistentDir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY /
+ "this/does/not/exist/foo.tst"
coroutineScope {
val storage = OkioStorage(fileSystem, testingSerializer) { fileInNonExistentDir }
storage.createConnection().use { connection ->
@@ -294,7 +298,7 @@
@Test
fun writeToDirFails() = testScope.runTest {
- val directoryFile = testIO.tempDir() / "this/is/a/directory"
+ val directoryFile = FileSystem.SYSTEM_TEMPORARY_DIRECTORY / "this/is/a/directory"
fileSystem.createDirectories(directoryFile)
val storage = OkioStorage(fileSystem, testingSerializer) { directoryFile }
@@ -310,7 +314,7 @@
if (failFileProducer) {
throw IOException("Exception when producing file")
}
- testIO.tempDir() / "new-temp-file.tmp"
+ FileSystem.SYSTEM_TEMPORARY_DIRECTORY / "new-temp-file.tmp"
}
val storage = OkioStorage(fileSystem, testingSerializer, fileProducer)
@@ -327,13 +331,13 @@
@Test
fun writeAfterTransientBadRead() = testScope.runTest {
- testingSerializer.failingRead = true
+ testingSerializerConfig.failingRead = true
testConnection.writeData(1)
assertThrows<IOException> { testConnection.readData() }
- testingSerializer.failingRead = false
+ testingSerializerConfig.failingRead = false
testConnection.writeData(1)
assertThat(testConnection.readData()).isEqualTo(1)
diff --git a/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/TestIO.kt b/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/TestIO.kt
deleted file mode 100644
index 9bcb6c8..0000000
--- a/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/TestIO.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2022 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.datastore.core.okio
-
-import androidx.datastore.core.Storage
-import kotlin.random.Random
-import okio.FileSystem
-import okio.Path
-
-// TODO: move to its own module
-class TestIO(private val dirName: String = "datastore-test-dir") {
- var onProduceFileCallback: () -> Unit = {}
- private val fileSystem = FileSystem.SYSTEM
- private fun randomFileName( // LAME :)
- prefix: String = "test-file"
- ): String {
- return prefix + (0 until 15).joinToString(separator = "") {
- ('a' + Random.nextInt(from = 0, until = 26)).toString()
- }
- }
- private val tmpDir = tempDir()
-
- fun tempDir(): Path {
- return FileSystem.SYSTEM_TEMPORARY_DIRECTORY / randomFileName(dirName)
- }
-
- fun <T> newFileStorage(
- serializer: OkioSerializer<T>,
- prefix: String = "test-file"
- ): Storage<T> {
- val storage = OkioStorage(
- fileSystem = fileSystem,
- serializer = serializer
- ) {
- onProduceFileCallback()
- tmpDir / randomFileName(prefix)
- }
- return storage
- }
-
- fun <T> newFileStorage(
- serializer: OkioSerializer<T>,
- testFile: Path
- ): Storage<T> {
- return OkioStorage(fileSystem, serializer) { testFile }
- }
-}
\ No newline at end of file
diff --git a/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/TestingSerializer.kt b/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/TestingSerializer.kt
deleted file mode 100644
index a120078..0000000
--- a/datastore/datastore-core-okio/src/commonTest/kotlin/androidx/datastore/core/okio/TestingSerializer.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2022 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.datastore.core.okio
-
-import androidx.datastore.core.CorruptionException
-import okio.BufferedSink
-import okio.BufferedSource
-import okio.EOFException
-import okio.IOException
-import okio.use
-
-internal class TestingSerializer(
- var failReadWithCorruptionException: Boolean = false,
- var failingRead: Boolean = false,
- var failingWrite: Boolean = false,
- override val defaultValue: Byte = 0
-) : OkioSerializer<Byte> {
-
- override suspend fun readFrom(source: BufferedSource): Byte {
- if (failReadWithCorruptionException) {
- throw CorruptionException(
- "CorruptionException",
- IOException("I was asked to fail with corruption on reads")
- )
- }
-
- if (failingRead) {
- throw IOException("I was asked to fail on reads")
- }
-
- val read = try {
- source.use {
- it.readInt()
- }
- } catch (eof: EOFException) {
- return 0
- }
- return read.toByte()
- }
-
- override suspend fun writeTo(t: Byte, sink: BufferedSink) {
- if (failingWrite) {
- throw IOException("I was asked to fail on writes")
- }
- sink.use {
- it.writeInt(t.toInt())
- }
- }
-}
\ No newline at end of file
diff --git a/datastore/datastore-core/build.gradle b/datastore/datastore-core/build.gradle
index baaf645..4355020 100644
--- a/datastore/datastore-core/build.gradle
+++ b/datastore/datastore-core/build.gradle
@@ -55,6 +55,7 @@
implementation(libs.okio)
api(project(":datastore:datastore-core-okio"))
implementation(project(":internal-testutils-kmp"))
+ implementation(project(":internal-testutils-datastore"))
}
}
@@ -63,6 +64,7 @@
implementation(libs.junit)
implementation(libs.kotlinTest)
implementation(project(":internal-testutils-kmp"))
+ implementation(project(":internal-testutils-datastore"))
}
}
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CommonTests.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CommonTests.kt
index 1c63e8c..3fc0429 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CommonTests.kt
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CommonTests.kt
@@ -16,6 +16,8 @@
package androidx.datastore.core
+import androidx.datastore.OkioPath
+import androidx.datastore.OkioTestIO
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.InternalCoroutinesApi
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt
index d63ecc5..de8f95a 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt
@@ -16,6 +16,9 @@
package androidx.datastore.core
+import androidx.datastore.TestFile
+import androidx.datastore.TestIO
+import androidx.datastore.TestingSerializerConfig
import androidx.kruth.assertThat
import androidx.kruth.assertThrows
import kotlin.test.BeforeTest
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
index 18cd4a0..420ab81 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
@@ -18,6 +18,9 @@
package androidx.datastore.core
+import androidx.datastore.TestFile
+import androidx.datastore.TestIO
+import androidx.datastore.TestingSerializerConfig
import androidx.datastore.core.handlers.NoOpCorruptionHandler
import androidx.kruth.assertThat
import androidx.kruth.assertThrows
diff --git a/datastore/datastore-core/src/jvmMain/java/androidx/datastore/core/FileStorage.kt b/datastore/datastore-core/src/jvmMain/java/androidx/datastore/core/FileStorage.kt
index 64f1d8a..76523eb 100644
--- a/datastore/datastore-core/src/jvmMain/java/androidx/datastore/core/FileStorage.kt
+++ b/datastore/datastore-core/src/jvmMain/java/androidx/datastore/core/FileStorage.kt
@@ -17,6 +17,7 @@
package androidx.datastore.core
import androidx.annotation.GuardedBy
+import androidx.annotation.RestrictTo
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
@@ -27,7 +28,15 @@
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
-internal class FileStorage<T>(
+/**
+ * The Java IO File version of the Storage<T> interface. Is able to read and write T to a given
+ * file location.
+ *
+ * @param serializer The serializer that can write <T> to and from a byte array.
+ * @param produceFile The file producer that returns the file that will be read and written.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class FileStorage<T>(
private val serializer: Serializer<T>,
private val produceFile: () -> File
) : Storage<T> {
diff --git a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/DataStoreFactoryTest.kt b/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/DataStoreFactoryTest.kt
index 7037e6d..f6dcfbc 100644
--- a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/DataStoreFactoryTest.kt
+++ b/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/DataStoreFactoryTest.kt
@@ -16,6 +16,7 @@
package androidx.datastore.core
+import androidx.datastore.TestingSerializerConfig
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.kruth.assertThat
import java.io.File
@@ -71,7 +72,8 @@
val store = DataStoreFactory.create(
serializer = TestingSerializer(
- TestingSerializerConfig(failReadWithCorruptionException = true)),
+ TestingSerializerConfig(failReadWithCorruptionException = true)
+ ),
corruptionHandler = ReplaceFileCorruptionHandler<Byte> {
valueToReplace
},
diff --git a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/FileStorageTest.kt b/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/FileStorageTest.kt
index 72cf3fa..4cc1399 100644
--- a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/FileStorageTest.kt
+++ b/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/FileStorageTest.kt
@@ -16,6 +16,7 @@
package androidx.datastore.core
+import androidx.datastore.TestingSerializerConfig
import androidx.kruth.assertThat
import androidx.kruth.assertThrows
import java.io.File
diff --git a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/JvmTests.kt b/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/JvmTests.kt
index e38dc1b..2910895 100644
--- a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/JvmTests.kt
+++ b/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/JvmTests.kt
@@ -16,6 +16,8 @@
package androidx.datastore.core
+import androidx.datastore.FileTestIO
+import androidx.datastore.JavaIOFile
import androidx.kruth.assertThat
import androidx.kruth.assertThrows
import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/handlers/ReplaceFileCorruptionHandlerTest.kt b/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/handlers/ReplaceFileCorruptionHandlerTest.kt
index 0e18d7f..abb75db 100644
--- a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/handlers/ReplaceFileCorruptionHandlerTest.kt
+++ b/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/handlers/ReplaceFileCorruptionHandlerTest.kt
@@ -16,10 +16,10 @@
package androidx.datastore.core.handlers
+import androidx.datastore.TestingSerializerConfig
import androidx.datastore.core.FileStorage
import androidx.datastore.core.SingleProcessDataStore
import androidx.datastore.core.TestingSerializer
-import androidx.datastore.core.TestingSerializerConfig
import androidx.kruth.assertThrows
import androidx.kruth.assertThat
import java.io.File
diff --git a/development/build_log_simplifier/message-flakes.ignore b/development/build_log_simplifier/message-flakes.ignore
index 0767997..f259723 100644
--- a/development/build_log_simplifier/message-flakes.ignore
+++ b/development/build_log_simplifier/message-flakes.ignore
@@ -22,11 +22,7 @@
[0-9]+ test.*failed.*
There were failing tests\. See the report at: .*.html
# Some status messages
-Starting a Gradle Daemon, [0-9]+ busy and [0-9]+ incompatible Daemons could not be reused, use \-\-status for details
-Starting a Gradle Daemon, [0-9]+ busy and [0-9]+ incompatible and [0-9]+ stopped Daemons could not be reused, use \-\-status for details
-Starting a Gradle Daemon, [0-9]+ incompatible Daemon could not be reused, use \-\-status for details
-Starting a Gradle Daemon, [0-9]+ stopped Daemon could not be reused, use \-\-status for details
-Starting a Gradle Daemon \(subsequent builds will be faster\)
+Starting a Gradle Daemon.*
Remote build cache is disabled when running with \-\-offline\.
[0-9]+ actionable tasks: [0-9]+ up\-to\-date
[0-9]+ actionable tasks: [0-9]+ executed
diff --git a/gradlew b/gradlew
index 2ba936b..3c2eea5 100755
--- a/gradlew
+++ b/gradlew
@@ -269,8 +269,7 @@
if [ "$compact" == "--strict" ]; then
expanded="-Pandroidx.validateNoUnrecognizedMessages\
-Pandroidx.verifyUpToDate\
- --no-watch-fs\
- --no-daemon"
+ --no-watch-fs"
if [ "$USE_ANDROIDX_REMOTE_BUILD_CACHE" == "" ]; then
expanded="$expanded --offline"
fi
diff --git a/metrics/integration-tests/janktest/src/main/AndroidManifest.xml b/metrics/integration-tests/janktest/src/main/AndroidManifest.xml
index 5a9eabd..3250f3b 100644
--- a/metrics/integration-tests/janktest/src/main/AndroidManifest.xml
+++ b/metrics/integration-tests/janktest/src/main/AndroidManifest.xml
@@ -34,6 +34,17 @@
</intent-filter>
</activity>
<activity
+ android:name=".SimpleLoggingActivity"
+ android:label="@string/app_name"
+ android:exported="true"
+ android:theme="@style/Theme.Androidx.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
android:name=".JankAggregatorActivity"
android:label="@string/app_name"
android:exported="true"
diff --git a/metrics/integration-tests/janktest/src/main/java/androidx/metrics/performance/janktest/SimpleLoggingActivity.kt b/metrics/integration-tests/janktest/src/main/java/androidx/metrics/performance/janktest/SimpleLoggingActivity.kt
new file mode 100644
index 0000000..ae5d51f
--- /dev/null
+++ b/metrics/integration-tests/janktest/src/main/java/androidx/metrics/performance/janktest/SimpleLoggingActivity.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2022 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.metrics.performance.janktest
+
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.metrics.performance.FrameData
+import androidx.metrics.performance.JankStats
+import androidx.metrics.performance.PerformanceMetricsState
+import java.util.Date
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+
+class SimpleLoggingActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.simple_layout)
+
+ JankStats.createAndTrack(window, Dispatchers.Default.asExecutor(),
+ object : JankStats.OnFrameListener {
+ override fun onFrame(frameData: FrameData) {
+ Log.d("MainActivity", frameData.toString())
+ }
+ })
+
+ findViewById<View>(R.id.button).setOnClickListener {
+ val stateHolder = PerformanceMetricsState.getForHierarchy(it).state!!
+ stateHolder.addSingleFrameState("stateKey1", "stateValue")
+ stateHolder.addState("stateKey2", "${Date()}")
+ }
+ }
+}
\ No newline at end of file
diff --git a/metrics/integration-tests/janktest/src/main/res/layout/simple_layout.xml b/metrics/integration-tests/janktest/src/main/res/layout/simple_layout.xml
new file mode 100644
index 0000000..fd53e82
--- /dev/null
+++ b/metrics/integration-tests/janktest/src/main/res/layout/simple_layout.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2022 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Click Me"
+ android:id="@+id/button"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/metrics/metrics-performance/src/main/java/androidx/metrics/performance/JankStatsApi16Impl.kt b/metrics/metrics-performance/src/main/java/androidx/metrics/performance/JankStatsApi16Impl.kt
index e0774cc..88be2de 100644
--- a/metrics/metrics-performance/src/main/java/androidx/metrics/performance/JankStatsApi16Impl.kt
+++ b/metrics/metrics-performance/src/main/java/androidx/metrics/performance/JankStatsApi16Impl.kt
@@ -175,7 +175,7 @@
decorView?.let {
val frameStart = getFrameStartTime()
with(decorView) {
- handler.sendMessage(Message.obtain(handler) {
+ handler.sendMessageAtFrontOfQueue(Message.obtain(handler) {
val now = System.nanoTime()
val expectedDuration = getExpectedFrameDuration(decorView)
// prevent concurrent modification of delegates list by synchronizing on
diff --git a/paging/paging-common/src/main/kotlin/androidx/paging/Logger.kt b/paging/paging-common/src/main/kotlin/androidx/paging/Logger.kt
new file mode 100644
index 0000000..38cf905
--- /dev/null
+++ b/paging/paging-common/src/main/kotlin/androidx/paging/Logger.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 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.
+ */
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+
+package androidx.paging
+import androidx.annotation.IntRange
+import androidx.annotation.RestrictTo
+
+/**
+ * Stores an instance of the Logger interface implementation which is to be injected by
+ * paging-runtime during runtime. This allows paging-common to add logs with
+ * android.util.log as a non-android artifact
+ */
+public var LOGGER: Logger? = null
+
+public const val LOG_TAG: String = "Paging"
+
+/**
+ * @hide
+ */
+public interface Logger {
+ public fun isLoggable(level: Int): Boolean
+ public fun log(level: Int, message: String, tr: Throwable? = null)
+}
+
+/**
+ * @hide
+ */
+public inline fun log(
+ @IntRange(from = VERBOSE.toLong(), to = DEBUG.toLong()) level: Int,
+ tr: Throwable? = null,
+ block: () -> String
+) {
+ val logger = LOGGER
+ if (logger?.isLoggable(level) == true) {
+ logger.log(level, block(), tr)
+ }
+}
+
+/**
+ * @hide
+ */
+public const val VERBOSE: Int = 2
+
+/**
+ * @hide
+ */
+public const val DEBUG: Int = 3
diff --git a/paging/paging-runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt b/paging/paging-runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
index 2217df4..205bc66 100644
--- a/paging/paging-runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
+++ b/paging/paging-runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
@@ -16,6 +16,7 @@
package androidx.paging
+import android.util.Log
import androidx.annotation.IntRange
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
@@ -413,4 +414,36 @@
fun removeLoadStateListener(listener: (CombinedLoadStates) -> Unit) {
differBase.removeLoadStateListener(listener)
}
+
+ private companion object {
+ init {
+ /**
+ * Implements the Logger interface from paging-common and injects it into the LOGGER
+ * global var stored within Pager.
+ *
+ * Checks for null LOGGER because paging-compose can also inject a Logger
+ * with the same implementation
+ */
+ LOGGER = LOGGER ?: object : Logger {
+ override fun isLoggable(level: Int): Boolean {
+ return Log.isLoggable(LOG_TAG, level)
+ }
+
+ override fun log(level: Int, message: String, tr: Throwable?) {
+ when {
+ tr != null && level == Log.DEBUG -> Log.d(LOG_TAG, message, tr)
+ tr != null && level == Log.VERBOSE -> Log.v(LOG_TAG, message, tr)
+ level == Log.DEBUG -> Log.d(LOG_TAG, message)
+ level == Log.VERBOSE -> Log.v(LOG_TAG, message)
+ else -> {
+ throw IllegalArgumentException(
+ "debug level $level is requested but Paging only supports " +
+ "default logging for level 2 (DEBUG) or level 3 (VERBOSE)"
+ )
+ }
+ }
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/room/room-common/src/main/java/androidx/room/Junction.kt b/room/room-common/src/main/java/androidx/room/Junction.kt
index 1f114b7..5ea5ef4 100644
--- a/room/room-common/src/main/java/androidx/room/Junction.kt
+++ b/room/room-common/src/main/java/androidx/room/Junction.kt
@@ -53,7 +53,7 @@
* }
* ```
*
- * In the above example the many-to-many relationship between `Song` and `Playlist` has
+ * In the above example the many-to-many relationship between a `Song` and a `Playlist` has
* an associative table defined by the entity `PlaylistSongXRef`.
*
* @see [Relation]
diff --git a/settings.gradle b/settings.gradle
index 58fa787..23ede6a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -951,7 +951,7 @@
/////////////////////////////
includeProject(":internal-testutils-common", "testutils/testutils-common", [BuildType.MAIN, BuildType.COMPOSE])
-includeProject(":internal-testutils-datastore", "testutils/testutils-datastore", [BuildType.MAIN])
+includeProject(":internal-testutils-datastore", "testutils/testutils-datastore", [BuildType.MAIN, BuildType.KMP])
includeProject(":internal-testutils-runtime", "testutils/testutils-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.MEDIA, BuildType.WEAR, BuildType.CAMERA])
includeProject(":internal-testutils-appcompat", "testutils/testutils-appcompat", [BuildType.MAIN])
includeProject(":internal-testutils-espresso", "testutils/testutils-espresso", [BuildType.MAIN, BuildType.COMPOSE])
diff --git a/testutils/testutils-datastore/build.gradle b/testutils/testutils-datastore/build.gradle
index 6582467..0888962 100644
--- a/testutils/testutils-datastore/build.gradle
+++ b/testutils/testutils-datastore/build.gradle
@@ -18,11 +18,32 @@
plugins {
id("AndroidXPlugin")
- id("kotlin")
}
+androidXMultiplatform {
+ jvm {}
+ macosX64()
+ linuxX64()
+ macosArm64()
-dependencies {
- implementation(libs.kotlinStdlib)
+ sourceSets {
+ commonMain {
+ dependencies {
+ api(project(":datastore:datastore-core"))
+ api(project(":datastore:datastore-core-okio"))
+ api(libs.kotlinStdlibCommon)
+ api(libs.kotlinTestCommon)
+ api(libs.kotlinCoroutinesCore)
+ api(libs.okio)
+ }
+ }
+
+ jvmMain {
+ dependencies {
+ api(libs.kotlinStdlib)
+ api(libs.kotlinTest)
+ }
+ }
+ }
}
androidx {
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/OkioTestIO.kt b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt
similarity index 97%
rename from datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/OkioTestIO.kt
rename to testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt
index 3244e85..afde3af 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/OkioTestIO.kt
+++ b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package androidx.datastore.core
+package androidx.datastore
+import androidx.datastore.core.Storage
import androidx.datastore.core.okio.OkioStorage
import kotlin.random.Random
import kotlin.reflect.KClass
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/TestIO.kt b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt
similarity index 86%
rename from datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/TestIO.kt
rename to testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt
index 94de2b6..772f901 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/TestIO.kt
+++ b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-package androidx.datastore.core
+package androidx.datastore
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.Storage
+import androidx.datastore.core.DataStoreFactory.create
import kotlin.reflect.KClass
import kotlinx.coroutines.CoroutineScope
-// TODO(b/237677833): move this class when datastore test utils is created
abstract class TestIO<F : TestFile, IOE : Throwable>(
protected val dirName: String = "datastore-test-dir"
) {
@@ -29,7 +31,7 @@
scope: CoroutineScope,
futureFile: () -> TestFile
): DataStore<Byte> {
- return SingleProcessDataStore(getStorage(serializerConfig, futureFile), scope = scope)
+ return create(getStorage(serializerConfig, futureFile), scope = scope)
}
abstract fun getStorage(
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/TestingOkioSerializer.kt b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingOkioSerializer.kt
similarity index 91%
rename from datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/TestingOkioSerializer.kt
rename to testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingOkioSerializer.kt
index 82885ac..cdfff76 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/TestingOkioSerializer.kt
+++ b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingOkioSerializer.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package androidx.datastore.core
+package androidx.datastore
+import androidx.datastore.core.CorruptionException
import androidx.datastore.core.okio.OkioSerializer
import okio.BufferedSink
import okio.BufferedSource
@@ -23,8 +24,7 @@
import okio.IOException
import okio.use
-// TODO(b/237677833): remove this class when datastore test utils is created
-internal class TestingOkioSerializer(
+class TestingOkioSerializer(
val config: TestingSerializerConfig
) : OkioSerializer<Byte> {
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/TestingSerializerConfig.kt b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingSerializerConfig.kt
similarity index 96%
rename from datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/TestingSerializerConfig.kt
rename to testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingSerializerConfig.kt
index 3bd6754..2ea35c5 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/TestingSerializerConfig.kt
+++ b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestingSerializerConfig.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.datastore.core
+package androidx.datastore
import kotlin.jvm.Volatile
diff --git a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/FileTestIO.kt b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt
similarity index 88%
rename from datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/FileTestIO.kt
rename to testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt
index 00ca26b6..3a2c5d0 100644
--- a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/FileTestIO.kt
+++ b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-package androidx.datastore.core
+package androidx.datastore
+import androidx.datastore.core.FileStorage
+import androidx.datastore.core.Storage
+import androidx.datastore.core.TestingSerializer
import java.io.File
import java.io.IOException
import kotlin.reflect.KClass
@@ -43,9 +46,9 @@
serializerConfig: TestingSerializerConfig,
futureFile: () -> TestFile
): Storage<Byte> {
- return FileStorage(
- TestingSerializer(serializerConfig)
- ) { (futureFile() as JavaIOFile).file }
+ return FileStorage(TestingSerializer(serializerConfig)) {
+ (futureFile() as JavaIOFile).file
+ }
}
override fun isDirectory(file: JavaIOFile): Boolean {
diff --git a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/TestingSerializer.kt b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/TestingSerializer.kt
similarity index 95%
rename from datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/TestingSerializer.kt
rename to testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/TestingSerializer.kt
index 9069e0c..fcfc44b 100644
--- a/datastore/datastore-core/src/jvmTest/java/androidx/datastore/core/TestingSerializer.kt
+++ b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/TestingSerializer.kt
@@ -16,11 +16,12 @@
package androidx.datastore.core
+import androidx.datastore.TestingSerializerConfig
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
-internal class TestingSerializer(
+class TestingSerializer(
val config: TestingSerializerConfig = TestingSerializerConfig(),
) : Serializer<Byte> {
override suspend fun readFrom(input: InputStream): Byte {