1 /*
2  * Copyright 2023 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 androidx.compose.material3.benchmark
18 
19 import androidx.compose.material3.ExperimentalMaterial3Api
20 import androidx.compose.material3.MaterialTheme
21 import androidx.compose.material3.PlainTooltip
22 import androidx.compose.material3.RichTooltip
23 import androidx.compose.material3.Text
24 import androidx.compose.material3.TextButton
25 import androidx.compose.material3.TooltipBox
26 import androidx.compose.material3.TooltipDefaults
27 import androidx.compose.material3.TooltipScope
28 import androidx.compose.material3.TooltipState
29 import androidx.compose.material3.rememberTooltipState
30 import androidx.compose.runtime.Composable
31 import androidx.compose.runtime.rememberCoroutineScope
32 import androidx.compose.testutils.LayeredComposeTestCase
33 import androidx.compose.testutils.ToggleableTestCase
34 import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
35 import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
36 import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
37 import androidx.compose.ui.window.PopupPositionProvider
38 import kotlinx.coroutines.CoroutineScope
39 import kotlinx.coroutines.launch
40 import org.junit.Rule
41 import org.junit.Test
42 
43 class TooltipBenchmark {
44     @get:Rule val benchmarkRule = ComposeBenchmarkRule()
45 
<lambda>null46     private val plainTooltipTestCaseFactory = { TooltipTestCase(TooltipType.Plain) }
<lambda>null47     private val richTooltipTestCaseFactory = { TooltipTestCase(TooltipType.Rich) }
48 
49     @Test
plainTooltipFirstPixelnull50     fun plainTooltipFirstPixel() {
51         benchmarkRule.benchmarkToFirstPixel(plainTooltipTestCaseFactory)
52     }
53 
54     @Test
richTooltipFirstPixelnull55     fun richTooltipFirstPixel() {
56         benchmarkRule.benchmarkToFirstPixel(richTooltipTestCaseFactory)
57     }
58 
59     @Test
plainTooltipVisibilityTestnull60     fun plainTooltipVisibilityTest() {
61         benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
62             caseFactory = plainTooltipTestCaseFactory,
63             assertOneRecomposition = false
64         )
65     }
66 
67     @Test
richTooltipVisibilityTestnull68     fun richTooltipVisibilityTest() {
69         benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
70             caseFactory = richTooltipTestCaseFactory,
71             assertOneRecomposition = false
72         )
73     }
74 }
75 
76 @OptIn(ExperimentalMaterial3Api::class)
77 private class TooltipTestCase(val tooltipType: TooltipType) :
78     LayeredComposeTestCase(), ToggleableTestCase {
79     private lateinit var state: TooltipState
80     private lateinit var scope: CoroutineScope
81 
82     @Composable
MeasuredContentnull83     override fun MeasuredContent() {
84         state = rememberTooltipState()
85         scope = rememberCoroutineScope()
86 
87         val tooltip: @Composable TooltipScope.() -> Unit
88         val positionProvider: PopupPositionProvider
89         when (tooltipType) {
90             TooltipType.Plain -> {
91                 tooltip = { PlainTooltipTest() }
92                 positionProvider = TooltipDefaults.rememberTooltipPositionProvider()
93             }
94             TooltipType.Rich -> {
95                 tooltip = { RichTooltipTest() }
96                 positionProvider = TooltipDefaults.rememberTooltipPositionProvider()
97             }
98         }
99 
100         TooltipBox(positionProvider = positionProvider, tooltip = tooltip, state = state) {}
101     }
102 
103     @Composable
104     override fun ContentWrappers(content: @Composable () -> Unit) {
<lambda>null105         MaterialTheme { content() }
106     }
107 
toggleStatenull108     override fun toggleState() {
109         if (state.isVisible) {
110             state.dismiss()
111         } else {
112             scope.launch { state.show() }
113         }
114     }
115 
116     @Composable
TooltipScopenull117     private fun TooltipScope.PlainTooltipTest() {
118         PlainTooltip { Text("Text") }
119     }
120 
121     @Composable
TooltipScopenull122     private fun TooltipScope.RichTooltipTest() {
123         RichTooltip(
124             title = { Text("Subhead") },
125             action = { TextButton(onClick = {}) { Text(text = "Action") } }
126         ) {
127             Text(text = "Text")
128         }
129     }
130 }
131 
132 private enum class TooltipType {
133     Plain,
134     Rich
135 }
136