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.viewcapture
18 
19 import android.content.Intent
20 import android.media.permission.SafeCloseable
21 import android.testing.AndroidTestingRunner
22 import android.view.View
23 import android.widget.LinearLayout
24 import android.widget.TextView
25 import androidx.test.ext.junit.rules.ActivityScenarioRule
26 import androidx.test.filters.SmallTest
27 import androidx.test.platform.app.InstrumentationRegistry
28 import com.android.app.viewcapture.TestActivity.Companion.TEXT_VIEW_COUNT
29 import com.android.app.viewcapture.data.MotionWindowData
30 import junit.framework.Assert.assertEquals
31 import org.junit.Rule
32 import org.junit.Test
33 import org.junit.runner.RunWith
34 
35 @SmallTest
36 @RunWith(AndroidTestingRunner::class)
37 class ViewCaptureTest {
38 
39     private val memorySize = 100
40     private val initPoolSize = 15
41     private val viewCapture by lazy {
42         object :
43             ViewCapture(memorySize, initPoolSize, MAIN_EXECUTOR) {}
44     }
45 
46     private val activityIntent =
47         Intent(InstrumentationRegistry.getInstrumentation().context, TestActivity::class.java)
48 
49     @get:Rule val activityScenarioRule = ActivityScenarioRule<TestActivity>(activityIntent)
50 
51     @Test
52     fun testWindowListenerDumpsOneFrameAfterInvalidate() {
53         activityScenarioRule.scenario.onActivity { activity ->
54             val closeable = startViewCaptureAndInvalidateNTimes(1, activity)
55             val rootView = activity.requireViewById<View>(android.R.id.content)
56             val data = viewCapture.getDumpTask(rootView).get().get()
57 
58             assertEquals(1, data.frameDataList.size)
59             verifyTestActivityViewHierarchy(data)
60             closeable.close()
61         }
62     }
63 
64     @Test
65     fun testWindowListenerDumpsCorrectlyAfterRecyclingStarted() {
66         activityScenarioRule.scenario.onActivity { activity ->
67             val closeable = startViewCaptureAndInvalidateNTimes(memorySize + 5, activity)
68             val rootView = activity.requireViewById<View>(android.R.id.content)
69             val data = viewCapture.getDumpTask(rootView).get().get()
70 
71             // since ViewCapture MEMORY_SIZE is [viewCaptureMemorySize], only
72             // [viewCaptureMemorySize] frames are exported, although the view is invalidated
73             // [viewCaptureMemorySize + 5] times
74             assertEquals(memorySize, data.frameDataList.size)
75             verifyTestActivityViewHierarchy(data)
76             closeable.close()
77         }
78     }
79 
80     private fun startViewCaptureAndInvalidateNTimes(n: Int, activity: TestActivity): SafeCloseable {
81         val rootView: View = activity.requireViewById(android.R.id.content)
82         val closeable: SafeCloseable = viewCapture.startCapture(rootView, "rootViewId")
83         dispatchOnDraw(rootView, times = n)
84         return closeable
85     }
86 
87     private fun dispatchOnDraw(view: View, times: Int) {
88         if (times > 0) {
89             view.viewTreeObserver.dispatchOnDraw()
90             dispatchOnDraw(view, times - 1)
91         }
92     }
93 
94     private fun verifyTestActivityViewHierarchy(exportedData: MotionWindowData) {
95         for (frame in exportedData.frameDataList) {
96             val testActivityRoot =
97                 frame.node // FrameLayout (android.R.id.content)
98                     .childrenList
99                     .first() // LinearLayout (set by setContentView())
100             assertEquals(TEXT_VIEW_COUNT, testActivityRoot.childrenList.size)
101             assertEquals(
102                 LinearLayout::class.qualifiedName,
103                 exportedData.getClassname(testActivityRoot.classnameIndex)
104             )
105             assertEquals(
106                 TextView::class.qualifiedName,
107                 exportedData.getClassname(testActivityRoot.childrenList.first().classnameIndex)
108             )
109         }
110     }
111 }
112