Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c36dad82 authored by David Saff's avatar David Saff Committed by Android (Google) Code Review
Browse files

Merge "Create onTeardown and use it." into main

parents 33397475 adc9d6e7
Loading
Loading
Loading
Loading
+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."))
    }
}
+5 −11
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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())
@@ -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())
@@ -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)
@@ -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)
@@ -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)
@@ -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
+3 −1
Original line number Diff line number Diff line
@@ -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 = {
@@ -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()));
+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)
    }
}
+22 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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";

@@ -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();