1 /*
2 * Copyright 2024 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.foundation.layout.Spacer
20 import androidx.compose.foundation.layout.size
21 import androidx.compose.material.icons.Icons
22 import androidx.compose.material.icons.outlined.Edit
23 import androidx.compose.material.icons.outlined.KeyboardArrowDown
24 import androidx.compose.material3.ButtonDefaults
25 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
26 import androidx.compose.material3.Icon
27 import androidx.compose.material3.MaterialTheme
28 import androidx.compose.material3.SplitButtonDefaults
29 import androidx.compose.material3.SplitButtonLayout
30 import androidx.compose.material3.Text
31 import androidx.compose.runtime.Composable
32 import androidx.compose.runtime.mutableStateOf
33 import androidx.compose.testutils.LayeredComposeTestCase
34 import androidx.compose.testutils.ToggleableTestCase
35 import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
36 import androidx.compose.testutils.benchmark.benchmarkFirstCompose
37 import androidx.compose.testutils.benchmark.benchmarkFirstDraw
38 import androidx.compose.testutils.benchmark.benchmarkFirstLayout
39 import androidx.compose.testutils.benchmark.benchmarkFirstMeasure
40 import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
41 import androidx.compose.ui.Modifier
42 import androidx.test.filters.LargeTest
43 import org.junit.Ignore
44 import org.junit.Rule
45 import org.junit.Test
46 import org.junit.runner.RunWith
47 import org.junit.runners.Parameterized
48
49 @LargeTest
50 @RunWith(Parameterized::class)
51 class SplitButtonBenchmark(private val type: SplitButtonType) {
52 companion object {
53 @Parameterized.Parameters(name = "{0}")
54 @JvmStatic
parametersnull55 fun parameters() = SplitButtonType.values()
56 }
57
58 @get:Rule val benchmarkRule = ComposeBenchmarkRule()
59
60 private val splitButtonTestCaseFactory = { SplitButtonTestCase(type) }
61
62 @Ignore
63 @Test
splitButton_first_composenull64 fun splitButton_first_compose() {
65 benchmarkRule.benchmarkFirstCompose(splitButtonTestCaseFactory)
66 }
67
68 @Ignore
69 @Test
splitButton_measurenull70 fun splitButton_measure() {
71 benchmarkRule.benchmarkFirstMeasure(splitButtonTestCaseFactory)
72 }
73
74 @Ignore
75 @Test
splitButton_layoutnull76 fun splitButton_layout() {
77 benchmarkRule.benchmarkFirstLayout(splitButtonTestCaseFactory)
78 }
79
80 @Ignore
81 @Test
splitButton_drawnull82 fun splitButton_draw() {
83 benchmarkRule.benchmarkFirstDraw(splitButtonTestCaseFactory)
84 }
85
86 @Test
splitButton_firstPixelnull87 fun splitButton_firstPixel() {
88 benchmarkRule.benchmarkToFirstPixel(splitButtonTestCaseFactory)
89 }
90 }
91
92 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
93 internal class SplitButtonTestCase(private val type: SplitButtonType) :
94 LayeredComposeTestCase(), ToggleableTestCase {
95 private var trailingButtonChecked = mutableStateOf(false)
96
97 @Composable
MeasuredContentnull98 override fun MeasuredContent() {
99 when (type) {
100 SplitButtonType.Filled ->
101 SplitButtonLayout(
102 leadingButton = {
103 SplitButtonDefaults.LeadingButton(
104 onClick = { /* Do Nothing */ },
105 ) {
106 leadingContent()
107 }
108 },
109 trailingButton = {
110 SplitButtonDefaults.TrailingButton(
111 checked = trailingButtonChecked.value,
112 onCheckedChange = { /* Do Nothing */ },
113 ) {
114 trailingContent()
115 }
116 }
117 )
118 SplitButtonType.Tonal ->
119 SplitButtonLayout(
120 leadingButton = {
121 SplitButtonDefaults.TonalLeadingButton(
122 onClick = { /* Do Nothing */ },
123 ) {
124 leadingContent()
125 }
126 },
127 trailingButton = {
128 SplitButtonDefaults.TonalTrailingButton(
129 checked = trailingButtonChecked.value,
130 onCheckedChange = { /* Do Nothing */ },
131 ) {
132 trailingContent()
133 }
134 }
135 )
136 SplitButtonType.Elevated ->
137 SplitButtonLayout(
138 leadingButton = {
139 SplitButtonDefaults.ElevatedLeadingButton(
140 onClick = { /* Do Nothing */ },
141 ) {
142 leadingContent()
143 }
144 },
145 trailingButton = {
146 SplitButtonDefaults.ElevatedTrailingButton(
147 checked = trailingButtonChecked.value,
148 onCheckedChange = { /* Do Nothing */ },
149 ) {
150 trailingContent()
151 }
152 }
153 )
154 SplitButtonType.Outlined ->
155 SplitButtonLayout(
156 leadingButton = {
157 SplitButtonDefaults.OutlinedLeadingButton(
158 onClick = { /* Do Nothing */ },
159 ) {
160 leadingContent()
161 }
162 },
163 trailingButton = {
164 SplitButtonDefaults.OutlinedTrailingButton(
165 checked = trailingButtonChecked.value,
166 onCheckedChange = { /* Do Nothing */ },
167 ) {
168 trailingContent()
169 }
170 }
171 )
172 }
173 }
174
175 @Composable
176 override fun ContentWrappers(content: @Composable () -> Unit) {
<lambda>null177 MaterialTheme { content() }
178 }
179
toggleStatenull180 override fun toggleState() {
181 trailingButtonChecked.value = !trailingButtonChecked.value
182 }
183 }
184
185 @Composable
leadingContentnull186 private fun leadingContent() {
187 Icon(
188 Icons.Outlined.Edit,
189 contentDescription = "Localized description",
190 )
191 Spacer(Modifier.size(ButtonDefaults.IconSpacing))
192 Text("My Button")
193 }
194
195 @Composable
trailingContentnull196 private fun trailingContent() {
197 Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Localized description")
198 }
199
200 enum class SplitButtonType {
201 Filled,
202 Tonal,
203 Elevated,
204 Outlined,
205 }
206