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