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

Commit d67b1f83 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

UinputRecordingIntegrationTests: Extend device lifetime to avoid racing

To avoid race conditions between the injection of the last event and the
processing of the device hangup, we will avoid terminating the uinput
shell command until the test successfully verifies that all events are
received.

To get around an unexpected behavior of the uinput command in
interactive mode, we inject an extra sync event at the end of the evemu
recording playback.

Bug: 366612213
Test: atest UinputRecordingIntegrationTest
Flag: TEST_ONLY
Change-Id: I021b26ec628711466ddaa3f1c21ff0caf315d1e1
parent 3c0b650a
Loading
Loading
Loading
Loading
+34 −16
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import com.android.cts.input.inputeventmatchers.withMotionAction
import com.android.cts.input.inputeventmatchers.withPressure
import com.android.cts.input.inputeventmatchers.withRawCoords
import com.android.cts.input.inputeventmatchers.withSource
import java.io.InputStream
import junit.framework.Assert.fail
import org.hamcrest.Matchers.allOf
import org.junit.Before
@@ -130,17 +129,18 @@ class UinputRecordingIntegrationTests {
                        scenario.virtualDisplay.display.uniqueId!!,
                    )

                    injectUinputEvents()

                    injectUinputEvents().use {
                        if (DEBUG_RECEIVED_EVENTS) {
                            printReceivedEventsToLogcat(scenario.activity)
                            fail("Test cannot pass in debug mode!")
                        }

                    val verifier =
                        EventVerifier(BatchedEventSplitter { scenario.activity.getInputEvent() })
                        val verifier = EventVerifier(
                            BatchedEventSplitter { scenario.activity.getInputEvent() }
                        )
                        verifyEvents(verifier)
                        scenario.activity.assertNoEvents()
                    }
                } finally {
                    inputManager.removeUniqueIdAssociationByPort(inputPort)
                }
@@ -162,14 +162,32 @@ class UinputRecordingIntegrationTests {
        }
    }

    private fun injectUinputEvents() {
    /**
     * Plays back the evemu recording associated with the current test case by injecting it via
     * the `uinput` shell command in interactive mode. The recording playback will begin
     * immediately, and the shell command (and the associated input device) will remain alive
     * until the returned [AutoCloseable] is closed.
     */
    private fun injectUinputEvents(): AutoCloseable {
        val fds = instrumentation.uiAutomation!!.executeShellCommandRw("uinput -")
        // We do not need to use stdout in this test.
        fds[0].close()

        ParcelFileDescriptor.AutoCloseOutputStream(fds[1]).use { stdIn ->
            val inputStream: InputStream = instrumentation.context.resources.openRawResource(
        return ParcelFileDescriptor.AutoCloseOutputStream(fds[1]).also { stdin ->
            instrumentation.context.resources.openRawResource(
                testData.uinputRecordingResource,
            )
            stdIn.write(inputStream.readBytes())
            ).use { inputStream ->
                stdin.write(inputStream.readBytes())

                // TODO(b/367419268): Remove extra event injection when uinput parsing is fixed.
                // Inject an extra sync event with an arbitrarily large timestamp, because the
                // uinput command will not process the last event until either the next event is
                // parsed, or fd is closed. Injecting this sync allows us complete injection of
                // the evemu recording and extend the lifetime of the input device by keeping this
                // fd open.
                stdin.write("\nE: 9999.99 0 0 0\n".toByteArray())
                stdin.flush()
            }
        }
    }