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