Loading packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt 0 → 100644 +124 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 com.android.systemui import androidx.test.filters.SmallTest import com.google.common.truth.Truth.assertThat import org.junit.Assert.fail import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.JUnitCore @Suppress("JUnitMalformedDeclaration") @SmallTest class OnTeardownRuleTest : SysuiTestCase() { // None of these inner classes should be run except as part of this utilities-testing test class HasTeardown { @get:Rule val teardownRule = OnTeardownRule() @Before fun setUp() { teardownWasRun = false teardownRule.onTeardown { teardownWasRun = true } } @Test fun doTest() {} companion object { var teardownWasRun = false } } @Test fun teardownRuns() { val result = JUnitCore().run(HasTeardown::class.java) assertThat(result.failures).isEmpty() assertThat(HasTeardown.teardownWasRun).isTrue() } class FirstTeardownFails { @get:Rule val teardownRule = OnTeardownRule() @Before fun setUp() { teardownWasRun = false teardownRule.onTeardown { fail("One fails") } teardownRule.onTeardown { teardownWasRun = true } } @Test fun doTest() {} companion object { var teardownWasRun = false } } @Test fun allTeardownsRun() { val result = JUnitCore().run(FirstTeardownFails::class.java) assertThat(result.failures.map { it.message }).isEqualTo(listOf("One fails")) assertThat(FirstTeardownFails.teardownWasRun).isTrue() } class ThreeTeardowns { @get:Rule val teardownRule = OnTeardownRule() @Before fun setUp() { messages.clear() } @Test fun doTest() { teardownRule.onTeardown { messages.add("A") } teardownRule.onTeardown { messages.add("B") } teardownRule.onTeardown { messages.add("C") } } companion object { val messages = mutableListOf<String>() } } @Test fun reverseOrder() { val result = JUnitCore().run(ThreeTeardowns::class.java) assertThat(result.failures).isEmpty() assertThat(ThreeTeardowns.messages).isEqualTo(listOf("C", "B", "A")) } class TryToDoABadThing { @get:Rule val teardownRule = OnTeardownRule() @Test fun doTest() { teardownRule.onTeardown { teardownRule.onTeardown { // do nothing } } } } @Test fun prohibitTeardownDuringTeardown() { val result = JUnitCore().run(TryToDoABadThing::class.java) assertThat(result.failures.map { it.message }) .isEqualTo(listOf("Cannot add new teardown routines after test complete.")) } } packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt +5 −11 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R Loading @@ -58,7 +57,6 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading Loading @@ -144,6 +142,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { controller.start() controller.addCallback(mockOngoingCallListener) controller.setChipView(chipView) onTeardown { controller.tearDownChipView() } val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java) verify(notificationCollection).addCollectionListener(collectionListenerCaptor.capture()) Loading @@ -153,11 +152,6 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { .thenReturn(PROC_STATE_INVISIBLE) } @After fun tearDown() { controller.tearDownChipView() } @Test fun onEntryUpdated_isOngoingCallNotif_listenerAndRepoNotified() { val notification = NotificationEntryBuilder(createOngoingCallNotifEntry()) Loading Loading @@ -224,7 +218,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { notifCollectionListener.onEntryUpdated(notification.build()) chipView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), ) assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) Loading @@ -241,7 +235,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { notifCollectionListener.onEntryUpdated(notification.build()) chipView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), ) assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) Loading @@ -257,7 +251,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { notifCollectionListener.onEntryUpdated(notification.build()) chipView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), ) assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) Loading Loading @@ -668,7 +662,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { private fun createCallNotifEntry( callStyle: Notification.CallStyle, nullContentIntent: Boolean = false nullContentIntent: Boolean = false, ): NotificationEntry { val notificationEntryBuilder = NotificationEntryBuilder() notificationEntryBuilder.modifyNotification(context).style = callStyle Loading packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -56,7 +56,6 @@ import java.util.Collections; @RunWith(AndroidTestingRunner.class) @SmallTest public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestCase { private static final String TAG = "AAA++VerifyTest"; private static final Class[] BASE_CLS_TO_INCLUDE = { Loading Loading @@ -149,6 +148,9 @@ public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestC */ private boolean isTestClass(Class<?> loadedClass) { try { if (loadedClass.getAnnotation(SkipSysuiVerification.class) != null) { return false; } if (Modifier.isAbstract(loadedClass.getModifiers())) { logDebug(String.format("Skipping abstract class %s: not a test", loadedClass.getName())); Loading packages/SystemUI/tests/utils/src/com/android/systemui/OnTeardownRule.kt 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 com.android.systemui import org.junit.rules.TestWatcher import org.junit.runner.Description import org.junit.runners.model.MultipleFailureException /** * Rule that allows teardown steps to be added right next to the places where it becomes clear they * are needed. This can avoid the need for complicated or conditional logic in a single teardown * method. Examples: * ``` * @get:Rule teardownRule = OnTeardownRule() * * // setup and teardown right next to each other * @Before * fun setUp() { * val oldTimeout = getGlobalTimeout() * teardownRule.onTeardown { setGlobalTimeout(oldTimeout) } * overrideGlobalTimeout(5000) * } * * // add teardown logic for fixtures that aren't used in every test * fun addCustomer() { * val id = globalDatabase.addCustomer(TEST_NAME, TEST_ADDRESS, ...) * teardownRule.onTeardown { globalDatabase.deleteCustomer(id) } * } * ``` */ class OnTeardownRule : TestWatcher() { private var canAdd = true private val teardowns = mutableListOf<() -> Unit>() fun onTeardown(teardownRunnable: () -> Unit) { if (!canAdd) { throw IllegalStateException("Cannot add new teardown routines after test complete.") } teardowns.add(teardownRunnable) } fun onTeardown(teardownRunnable: Runnable) { if (!canAdd) { throw IllegalStateException("Cannot add new teardown routines after test complete.") } teardowns.add { teardownRunnable.run() } } override fun finished(description: Description?) { canAdd = false val errors = mutableListOf<Throwable>() teardowns.reversed().forEach { try { it() } catch (e: Throwable) { errors.add(e) } } MultipleFailureException.assertEmpty(errors) } } packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +22 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ import org.mockito.Mockito; import java.io.FileInputStream; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Future; Loading @@ -69,6 +71,17 @@ import java.util.concurrent.Future; // background on Ravenwood is available at go/ravenwood-docs @DisabledOnRavenwood public abstract class SysuiTestCase { /** * Especially when self-testing test utilities, we may have classes that look like test * classes, but we don't expect to ever actually run as a top-level test. * For example, {@link com.android.systemui.TryToDoABadThing}. * Verifying properties on these as a part of structural tests like * AAAPlusPlusVerifySysuiRequiredTestPropertiesTest is a waste of our time, and makes things * look more confusing, so this lets us skip when appropriate. */ @Retention(RetentionPolicy.RUNTIME) public @interface SkipSysuiVerification { } private static final String TAG = "SysuiTestCase"; Loading Loading @@ -172,6 +185,15 @@ public abstract class SysuiTestCase { public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); @Rule public final OnTeardownRule mTearDownRule = new OnTeardownRule(); /** * Schedule a cleanup routine to happen when the test state is torn down. */ protected void onTeardown(Runnable tearDownRunnable) { mTearDownRule.onTeardown(tearDownRunnable); } // set the highest order so it's the innermost rule @Rule(order = Integer.MAX_VALUE) public TestWithLooperRule mlooperRule = new TestWithLooperRule(); Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt 0 → 100644 +124 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 com.android.systemui import androidx.test.filters.SmallTest import com.google.common.truth.Truth.assertThat import org.junit.Assert.fail import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.JUnitCore @Suppress("JUnitMalformedDeclaration") @SmallTest class OnTeardownRuleTest : SysuiTestCase() { // None of these inner classes should be run except as part of this utilities-testing test class HasTeardown { @get:Rule val teardownRule = OnTeardownRule() @Before fun setUp() { teardownWasRun = false teardownRule.onTeardown { teardownWasRun = true } } @Test fun doTest() {} companion object { var teardownWasRun = false } } @Test fun teardownRuns() { val result = JUnitCore().run(HasTeardown::class.java) assertThat(result.failures).isEmpty() assertThat(HasTeardown.teardownWasRun).isTrue() } class FirstTeardownFails { @get:Rule val teardownRule = OnTeardownRule() @Before fun setUp() { teardownWasRun = false teardownRule.onTeardown { fail("One fails") } teardownRule.onTeardown { teardownWasRun = true } } @Test fun doTest() {} companion object { var teardownWasRun = false } } @Test fun allTeardownsRun() { val result = JUnitCore().run(FirstTeardownFails::class.java) assertThat(result.failures.map { it.message }).isEqualTo(listOf("One fails")) assertThat(FirstTeardownFails.teardownWasRun).isTrue() } class ThreeTeardowns { @get:Rule val teardownRule = OnTeardownRule() @Before fun setUp() { messages.clear() } @Test fun doTest() { teardownRule.onTeardown { messages.add("A") } teardownRule.onTeardown { messages.add("B") } teardownRule.onTeardown { messages.add("C") } } companion object { val messages = mutableListOf<String>() } } @Test fun reverseOrder() { val result = JUnitCore().run(ThreeTeardowns::class.java) assertThat(result.failures).isEmpty() assertThat(ThreeTeardowns.messages).isEqualTo(listOf("C", "B", "A")) } class TryToDoABadThing { @get:Rule val teardownRule = OnTeardownRule() @Test fun doTest() { teardownRule.onTeardown { teardownRule.onTeardown { // do nothing } } } } @Test fun prohibitTeardownDuringTeardown() { val result = JUnitCore().run(TryToDoABadThing::class.java) assertThat(result.failures.map { it.message }) .isEqualTo(listOf("Cannot add new teardown routines after test complete.")) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt +5 −11 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R Loading @@ -58,7 +57,6 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading Loading @@ -144,6 +142,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { controller.start() controller.addCallback(mockOngoingCallListener) controller.setChipView(chipView) onTeardown { controller.tearDownChipView() } val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java) verify(notificationCollection).addCollectionListener(collectionListenerCaptor.capture()) Loading @@ -153,11 +152,6 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { .thenReturn(PROC_STATE_INVISIBLE) } @After fun tearDown() { controller.tearDownChipView() } @Test fun onEntryUpdated_isOngoingCallNotif_listenerAndRepoNotified() { val notification = NotificationEntryBuilder(createOngoingCallNotifEntry()) Loading Loading @@ -224,7 +218,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { notifCollectionListener.onEntryUpdated(notification.build()) chipView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), ) assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) Loading @@ -241,7 +235,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { notifCollectionListener.onEntryUpdated(notification.build()) chipView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), ) assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) Loading @@ -257,7 +251,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { notifCollectionListener.onEntryUpdated(notification.build()) chipView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), ) assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) Loading Loading @@ -668,7 +662,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { private fun createCallNotifEntry( callStyle: Notification.CallStyle, nullContentIntent: Boolean = false nullContentIntent: Boolean = false, ): NotificationEntry { val notificationEntryBuilder = NotificationEntryBuilder() notificationEntryBuilder.modifyNotification(context).style = callStyle Loading
packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -56,7 +56,6 @@ import java.util.Collections; @RunWith(AndroidTestingRunner.class) @SmallTest public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestCase { private static final String TAG = "AAA++VerifyTest"; private static final Class[] BASE_CLS_TO_INCLUDE = { Loading Loading @@ -149,6 +148,9 @@ public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestC */ private boolean isTestClass(Class<?> loadedClass) { try { if (loadedClass.getAnnotation(SkipSysuiVerification.class) != null) { return false; } if (Modifier.isAbstract(loadedClass.getModifiers())) { logDebug(String.format("Skipping abstract class %s: not a test", loadedClass.getName())); Loading
packages/SystemUI/tests/utils/src/com/android/systemui/OnTeardownRule.kt 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 com.android.systemui import org.junit.rules.TestWatcher import org.junit.runner.Description import org.junit.runners.model.MultipleFailureException /** * Rule that allows teardown steps to be added right next to the places where it becomes clear they * are needed. This can avoid the need for complicated or conditional logic in a single teardown * method. Examples: * ``` * @get:Rule teardownRule = OnTeardownRule() * * // setup and teardown right next to each other * @Before * fun setUp() { * val oldTimeout = getGlobalTimeout() * teardownRule.onTeardown { setGlobalTimeout(oldTimeout) } * overrideGlobalTimeout(5000) * } * * // add teardown logic for fixtures that aren't used in every test * fun addCustomer() { * val id = globalDatabase.addCustomer(TEST_NAME, TEST_ADDRESS, ...) * teardownRule.onTeardown { globalDatabase.deleteCustomer(id) } * } * ``` */ class OnTeardownRule : TestWatcher() { private var canAdd = true private val teardowns = mutableListOf<() -> Unit>() fun onTeardown(teardownRunnable: () -> Unit) { if (!canAdd) { throw IllegalStateException("Cannot add new teardown routines after test complete.") } teardowns.add(teardownRunnable) } fun onTeardown(teardownRunnable: Runnable) { if (!canAdd) { throw IllegalStateException("Cannot add new teardown routines after test complete.") } teardowns.add { teardownRunnable.run() } } override fun finished(description: Description?) { canAdd = false val errors = mutableListOf<Throwable>() teardowns.reversed().forEach { try { it() } catch (e: Throwable) { errors.add(e) } } MultipleFailureException.assertEmpty(errors) } }
packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +22 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ import org.mockito.Mockito; import java.io.FileInputStream; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Future; Loading @@ -69,6 +71,17 @@ import java.util.concurrent.Future; // background on Ravenwood is available at go/ravenwood-docs @DisabledOnRavenwood public abstract class SysuiTestCase { /** * Especially when self-testing test utilities, we may have classes that look like test * classes, but we don't expect to ever actually run as a top-level test. * For example, {@link com.android.systemui.TryToDoABadThing}. * Verifying properties on these as a part of structural tests like * AAAPlusPlusVerifySysuiRequiredTestPropertiesTest is a waste of our time, and makes things * look more confusing, so this lets us skip when appropriate. */ @Retention(RetentionPolicy.RUNTIME) public @interface SkipSysuiVerification { } private static final String TAG = "SysuiTestCase"; Loading Loading @@ -172,6 +185,15 @@ public abstract class SysuiTestCase { public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); @Rule public final OnTeardownRule mTearDownRule = new OnTeardownRule(); /** * Schedule a cleanup routine to happen when the test state is torn down. */ protected void onTeardown(Runnable tearDownRunnable) { mTearDownRule.onTeardown(tearDownRunnable); } // set the highest order so it's the innermost rule @Rule(order = Integer.MAX_VALUE) public TestWithLooperRule mlooperRule = new TestWithLooperRule(); Loading