• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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