1 /* <lambda>null2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.app.motiontool 18 19 import android.content.Intent 20 import android.testing.AndroidTestingRunner 21 import android.view.Choreographer 22 import android.view.View 23 import android.view.WindowManagerGlobal 24 import androidx.test.ext.junit.rules.ActivityScenarioRule 25 import androidx.test.filters.SmallTest 26 import androidx.test.platform.app.InstrumentationRegistry 27 import com.android.app.motiontool.DdmHandleMotionTool.Companion.CHUNK_MOTO 28 import com.android.app.motiontool.util.TestActivity 29 import junit.framework.Assert 30 import junit.framework.Assert.assertEquals 31 import org.apache.harmony.dalvik.ddmc.Chunk 32 import org.apache.harmony.dalvik.ddmc.ChunkHandler.wrapChunk 33 import org.junit.After 34 import org.junit.Before 35 import org.junit.Rule 36 import org.junit.Test 37 import org.junit.runner.RunWith 38 39 @SmallTest 40 @RunWith(AndroidTestingRunner::class) 41 class DdmHandleMotionToolTest { 42 43 private val windowManagerGlobal = WindowManagerGlobal.getInstance() 44 private val motionToolManager = MotionToolManager.getInstance(windowManagerGlobal) 45 private val ddmHandleMotionTool = DdmHandleMotionTool.getInstance(motionToolManager) 46 private val CLIENT_VERSION = 1 47 48 private val activityIntent = 49 Intent(InstrumentationRegistry.getInstrumentation().context, TestActivity::class.java) 50 51 @get:Rule 52 val activityScenarioRule = ActivityScenarioRule<TestActivity>(activityIntent) 53 54 @Before 55 fun setup() { 56 ddmHandleMotionTool.register() 57 } 58 59 @After 60 fun cleanup() { 61 ddmHandleMotionTool.unregister() 62 } 63 64 @Test 65 fun testHandshakeErrorWithInvalidWindowId() { 66 val handshakeResponse = performHandshakeRequest("InvalidWindowId") 67 assertEquals(HandshakeResponse.Status.WINDOW_NOT_FOUND, handshakeResponse.handshake.status) 68 } 69 70 @Test 71 fun testHandshakeOkWithValidWindowId() { 72 val handshakeResponse = performHandshakeRequest(getActivityViewRootId()) 73 assertEquals(HandshakeResponse.Status.OK, handshakeResponse.handshake.status) 74 } 75 76 @Test 77 fun testBeginFailsWithInvalidWindowId() { 78 val errorResponse = performBeginTraceRequest("InvalidWindowId") 79 assertEquals(ErrorResponse.Code.WINDOW_NOT_FOUND, errorResponse.error.code) 80 } 81 82 @Test 83 fun testEndTraceFailsWithoutPrecedingBeginTrace() { 84 val errorResponse = performEndTraceRequest(0) 85 assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, errorResponse.error.code) 86 } 87 88 @Test 89 fun testPollTraceFailsWithoutPrecedingBeginTrace() { 90 val errorResponse = performPollTraceRequest(0) 91 assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, errorResponse.error.code) 92 } 93 94 @Test 95 fun testEndTraceFailsWithInvalidTraceId() { 96 val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) 97 val endTraceResponse = performEndTraceRequest(beginTraceResponse.beginTrace.traceId + 1) 98 assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, endTraceResponse.error.code) 99 } 100 101 @Test 102 fun testPollTraceFailsWithInvalidTraceId() { 103 val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) 104 val endTraceResponse = performPollTraceRequest(beginTraceResponse.beginTrace.traceId + 1) 105 assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, endTraceResponse.error.code) 106 } 107 108 @Test 109 fun testMalformedRequestFails() { 110 val requestBytes = ByteArray(9) 111 val requestChunk = Chunk(CHUNK_MOTO, requestBytes, 0, requestBytes.size) 112 val responseChunk = ddmHandleMotionTool.handleChunk(requestChunk) 113 val response = MotionToolsResponse.parseFrom(wrapChunk(responseChunk).array()).error 114 assertEquals(ErrorResponse.Code.INVALID_REQUEST, response.code) 115 } 116 117 @Test 118 fun testNoOnDrawCallReturnsEmptyTrace() { 119 activityScenarioRule.scenario.onActivity { 120 val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) 121 val endTraceResponse = performEndTraceRequest(beginTraceResponse.beginTrace.traceId) 122 Assert.assertTrue(endTraceResponse.endTrace.exportedData.frameDataList.isEmpty()) 123 } 124 } 125 126 @Test 127 fun testOneOnDrawCallReturnsOneFrameResponse() { 128 activityScenarioRule.scenario.onActivity { activity -> 129 val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) 130 val traceId = beginTraceResponse.beginTrace.traceId 131 132 Choreographer.getInstance().postFrameCallback { 133 activity.findViewById<View>(android.R.id.content).viewTreeObserver.dispatchOnDraw() 134 135 val pollTraceResponse = performPollTraceRequest(traceId) 136 assertEquals(1, pollTraceResponse.pollTrace.exportedData.frameDataList.size) 137 138 // Verify that frameData is only included once and is not returned again 139 val endTraceResponse = performEndTraceRequest(traceId) 140 assertEquals(0, endTraceResponse.endTrace.exportedData.frameDataList.size) 141 } 142 } 143 } 144 145 private fun performPollTraceRequest(requestTraceId: Int): MotionToolsResponse { 146 val pollTraceRequest = MotionToolsRequest.newBuilder() 147 .setPollTrace(PollTraceRequest.newBuilder() 148 .setTraceId(requestTraceId)) 149 .build() 150 return performRequest(pollTraceRequest) 151 } 152 153 private fun performEndTraceRequest(requestTraceId: Int): MotionToolsResponse { 154 val endTraceRequest = MotionToolsRequest.newBuilder() 155 .setEndTrace(EndTraceRequest.newBuilder() 156 .setTraceId(requestTraceId)) 157 .build() 158 return performRequest(endTraceRequest) 159 } 160 161 private fun performBeginTraceRequest(windowId: String): MotionToolsResponse { 162 val beginTraceRequest = MotionToolsRequest.newBuilder() 163 .setBeginTrace(BeginTraceRequest.newBuilder() 164 .setWindow(WindowIdentifier.newBuilder() 165 .setRootWindow(windowId))) 166 .build() 167 return performRequest(beginTraceRequest) 168 } 169 170 private fun performHandshakeRequest(windowId: String): MotionToolsResponse { 171 val handshakeRequest = MotionToolsRequest.newBuilder() 172 .setHandshake(HandshakeRequest.newBuilder() 173 .setWindow(WindowIdentifier.newBuilder() 174 .setRootWindow(windowId)) 175 .setClientVersion(CLIENT_VERSION)) 176 .build() 177 return performRequest(handshakeRequest) 178 } 179 180 private fun performRequest(motionToolsRequest: MotionToolsRequest): MotionToolsResponse { 181 val requestBytes = motionToolsRequest.toByteArray() 182 val requestChunk = Chunk(CHUNK_MOTO, requestBytes, 0, requestBytes.size) 183 val responseChunk = ddmHandleMotionTool.handleChunk(requestChunk) 184 return MotionToolsResponse.parseFrom(wrapChunk(responseChunk).array()) 185 } 186 187 private fun getActivityViewRootId(): String { 188 var activityViewRootId = "" 189 activityScenarioRule.scenario.onActivity { 190 activityViewRootId = WindowManagerGlobal.getInstance().viewRootNames.first() 191 } 192 return activityViewRootId 193 } 194 195 } 196