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

Commit 757e60e0 authored by Sergey Nikolaienkov's avatar Sergey Nikolaienkov
Browse files

Introduce .wm.flicker.pip.tv.TvPipNotificationTests

Move first test out of android.systemui.cts.tv.PipNotificationTests to
com.android.wm.shell.flicker.pip.tv.TvPipNotification tests.

Bug: 171520419
Test: atest WMShellFlickerTests:TvPipNotificationTests
Change-Id: I6a3e2fb96a7688814ce909f28d6d904356fad3fd
parent 58dc55b6
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -36,6 +36,15 @@
    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
    <application>
        <uses-library android:name="android.test.runner"/>

        <service android:name=".NotificationListener"
                 android:exported="true"
                 android:label="WMShellTestsNotificationListenerService"
                 android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
            <intent-filter>
                <action android:name="android.service.notification.NotificationListenerService" />
            </intent-filter>
        </service>
    </application>

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+4 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.flicker

import android.content.pm.PackageManager
import android.os.RemoteException
import android.os.SystemClock
import android.platform.helpers.IAppHelper
@@ -41,6 +42,9 @@ abstract class FlickerTestBase {
    val uiDevice by lazy {
        UiDevice.getInstance(instrumentation)
    }
    val packageManager: PackageManager by lazy {
        instrumentation.context.getPackageManager()
    }

    /**
     * Build a test tag for the test
+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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 com.android.wm.shell.flicker

import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.Log
import com.android.compatibility.common.util.SystemUtil.runShellCommand

class NotificationListener : NotificationListenerService() {
    private val notifications: MutableMap<Any, StatusBarNotification> = mutableMapOf()

    override fun onNotificationPosted(sbn: StatusBarNotification) {
        if (DEBUG) Log.d(TAG, "onNotificationPosted: $sbn")
        notifications[sbn.key] = sbn
    }

    override fun onNotificationRemoved(sbn: StatusBarNotification) {
        if (DEBUG) Log.d(TAG, "onNotificationRemoved: $sbn")
        notifications.remove(sbn.key)
    }

    override fun onListenerConnected() {
        if (DEBUG) Log.d(TAG, "onListenerConnected")
        instance = this
    }

    override fun onListenerDisconnected() {
        if (DEBUG) Log.d(TAG, "onListenerDisconnected")
        instance = null
        notifications.clear()
    }

    companion object {
        private const val DEBUG = false
        private const val TAG = "WMShellFlickerTests_NotificationListener"

        private const val CMD_NOTIFICATION_ALLOW_LISTENER = "cmd notification allow_listener %s"
        private const val CMD_NOTIFICATION_DISALLOW_LISTENER =
                "cmd notification disallow_listener %s"
        private const val COMPONENT_NAME = "com.android.wm.shell.flicker/.NotificationListener"

        private var instance: NotificationListener? = null

        fun startNotificationListener(): Boolean {
            if (instance != null) {
                return true
            }

            runShellCommand(CMD_NOTIFICATION_ALLOW_LISTENER.format(COMPONENT_NAME))
            return wait { instance != null }
        }

        fun stopNotificationListener(): Boolean {
            if (instance == null) {
                return true
            }

            runShellCommand(CMD_NOTIFICATION_DISALLOW_LISTENER.format(COMPONENT_NAME))
            return wait { instance == null }
        }

        fun waitForNotificationToAppear(predicate: (StatusBarNotification) -> Boolean): Boolean {
            return instance?.let {
                wait { it.notifications.values.any(predicate) }
            } ?: throw IllegalStateException("NotificationListenerService is not connected")
        }

        fun waitForNotificationToDisappear(predicate: (StatusBarNotification) -> Boolean): Boolean {
            return instance?.let {
                wait { it.notifications.values.none(predicate) }
            } ?: throw IllegalStateException("NotificationListenerService is not connected")
        }
    }
}
 No newline at end of file
+45 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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 com.android.wm.shell.flicker

import android.os.SystemClock

private const val DEFAULT_TIMEOUT = 10000L
private const val DEFAULT_POLL_INTERVAL = 1000L

fun wait(condition: () -> Boolean): Boolean {
    val (success, _) = waitForResult(extractor = condition, validator = { it })
    return success
}

fun <R> waitForResult(
    timeout: Long = DEFAULT_TIMEOUT,
    interval: Long = DEFAULT_POLL_INTERVAL,
    extractor: () -> R,
    validator: (R) -> Boolean = { it != null }
): Pair<Boolean, R?> {
    val startTime = SystemClock.uptimeMillis()
    do {
        val result = extractor()
        if (validator(result)) {
            return (true to result)
        }
        SystemClock.sleep(interval)
    } while (SystemClock.uptimeMillis() - startTime < timeout)

    return (false to null)
}
 No newline at end of file
+92 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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 com.android.wm.shell.flicker.pip.tv

import android.app.Notification
import android.service.notification.StatusBarNotification
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.NotificationListener.Companion.startNotificationListener
import com.android.wm.shell.flicker.NotificationListener.Companion.stopNotificationListener
import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToAppear
import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToDisappear
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized

/**
 * Test Pip Notifications on TV.
 * To run this test: `atest WMShellFlickerTests:TvPipNotificationTests`
 */
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class TvPipNotificationTests(rotationName: String, rotation: Int)
    : TvPipTestBase(rotationName, rotation) {

    @Before
    override fun setUp() {
        super.setUp()
        val started = startNotificationListener()
        if (!started) {
            error("NotificationListener hasn't started")
        }
    }

    @After
    override fun tearDown() {
        stopNotificationListener()
        testApp.forceStop()
        super.tearDown()
    }

    @Test
    fun pipNotification_postedAndDismissed() {
        testApp.launchViaIntent()
        testApp.clickEnterPipButton()

        assertTrue("Pip notification should have been posted",
                waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) })

        testApp.closePipWindow()

        assertTrue("Pip notification should have been dismissed",
                waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.label) })
    }

    companion object {
        @Parameterized.Parameters(name = "{0}")
        @JvmStatic
        fun getParams(): Collection<Array<Any>> {
            val supportedRotations = intArrayOf(Surface.ROTATION_0)
            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
        }
    }
}

private const val PIP_NOTIFICATION_TAG = "PipNotification"

private val StatusBarNotification.title: String
    get() = notification?.extras?.getString(Notification.EXTRA_TITLE) ?: ""

private fun StatusBarNotification.isPipNotificationWithTitle(expectedTitle: String): Boolean =
    tag == PIP_NOTIFICATION_TAG && title == expectedTitle
 No newline at end of file
Loading