1
+ /*
2
+ * 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 androidx.constraintlayout.compose
18
+
19
+ import androidx.compose.foundation.background
20
+ import androidx.compose.foundation.layout.Box
21
+ import androidx.compose.foundation.layout.size
22
+ import androidx.compose.runtime.Composable
23
+ import androidx.compose.ui.Modifier
24
+ import androidx.compose.ui.graphics.Color
25
+ import androidx.compose.ui.layout.layoutId
26
+ import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
27
+ import androidx.compose.ui.platform.testTag
28
+ import androidx.compose.ui.test.assertPositionInRootIsEqualTo
29
+ import androidx.compose.ui.test.junit4.createComposeRule
30
+ import androidx.compose.ui.test.onNodeWithTag
31
+ import androidx.compose.ui.unit.dp
32
+ import androidx.test.ext.junit.runners.AndroidJUnit4
33
+ import androidx.test.filters.MediumTest
34
+ import org.junit.After
35
+ import org.junit.Before
36
+ import org.junit.Rule
37
+ import org.junit.Test
38
+ import org.junit.runner.RunWith
39
+
40
+ /* *
41
+ * Tests for the Grid Helper (Row / Column)
42
+ */
43
+ @MediumTest
44
+ @RunWith(AndroidJUnit4 ::class )
45
+ class RowColumnDslTest {
46
+ @get:Rule
47
+ val rule = createComposeRule()
48
+
49
+ @Before
50
+ fun setup () {
51
+ isDebugInspectorInfoEnabled = true
52
+ }
53
+
54
+ @After
55
+ fun tearDown () {
56
+ isDebugInspectorInfoEnabled = false
57
+ }
58
+
59
+ @Test
60
+ fun testColumn () {
61
+ val rootSize = 200 .dp
62
+ val boxesCount = 4
63
+ rule.setContent {
64
+ ColumnComposableTest (
65
+ modifier = Modifier .size(rootSize),
66
+ gridSkips = arrayOf(),
67
+ gridSpans = arrayOf(),
68
+ boxesCount = boxesCount,
69
+ vGap = 0 ,
70
+ gridRowWeights = intArrayOf(),
71
+ )
72
+ }
73
+ var expectedX = 0 .dp
74
+ var expectedY = 0 .dp
75
+
76
+ // 10.dp is the size of a singular box
77
+ val hGapSize = (rootSize - 10 .dp) / 2f
78
+ val vGapSize = (rootSize - (10 .dp * 4f )) / (boxesCount * 2f )
79
+ rule.waitForIdle()
80
+ expectedX + = hGapSize
81
+ expectedY + = vGapSize
82
+ rule.onNodeWithTag(" box0" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
83
+ expectedY + = vGapSize + vGapSize + 10 .dp
84
+ rule.onNodeWithTag(" box1" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
85
+ expectedY + = vGapSize + vGapSize + 10 .dp
86
+ rule.onNodeWithTag(" box2" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
87
+ expectedY + = vGapSize + vGapSize + 10 .dp
88
+ rule.onNodeWithTag(" box3" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
89
+ }
90
+
91
+ @Test
92
+ fun testColumnSkips () {
93
+ val rootSize = 200 .dp
94
+ val boxesCount = 4
95
+ rule.setContent {
96
+ ColumnComposableTest (
97
+ modifier = Modifier .size(rootSize),
98
+ gridSkips = arrayOf(Skip (1 , 2 )),
99
+ gridSpans = arrayOf(),
100
+ boxesCount = boxesCount,
101
+ vGap = 0 ,
102
+ gridRowWeights = intArrayOf(),
103
+ )
104
+ }
105
+ var expectedX = 0 .dp
106
+ var expectedY = 0 .dp
107
+
108
+ // 10.dp is the size of a singular box
109
+ val hGapSize = (rootSize - 10 .dp) / 2f
110
+ val vGapSize = (rootSize - (10 .dp * 6f )) / ((boxesCount + 2 ) * 2f )
111
+ rule.waitForIdle()
112
+ expectedX + = hGapSize
113
+ expectedY + = vGapSize
114
+ rule.onNodeWithTag(" box0" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
115
+ expectedY + = vGapSize + vGapSize + 10 .dp
116
+ expectedY + = vGapSize + vGapSize + 10 .dp
117
+ expectedY + = vGapSize + vGapSize + 10 .dp
118
+ rule.onNodeWithTag(" box1" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
119
+ expectedY + = vGapSize + vGapSize + 10 .dp
120
+ rule.onNodeWithTag(" box2" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
121
+ expectedY + = vGapSize + vGapSize + 10 .dp
122
+ rule.onNodeWithTag(" box3" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
123
+ }
124
+
125
+ @Test
126
+ fun testColumnSpans () {
127
+ val rootSize = 200 .dp
128
+ val boxesCount = 4
129
+ rule.setContent {
130
+ ColumnComposableTest (
131
+ modifier = Modifier .size(rootSize),
132
+ gridSkips = arrayOf(),
133
+ gridSpans = arrayOf(Span (0 , 2 )),
134
+ boxesCount = boxesCount,
135
+ vGap = 0 ,
136
+ gridRowWeights = intArrayOf(),
137
+ )
138
+ }
139
+
140
+ // 10.dp is the size of a singular box
141
+ val hGapSize = (rootSize - 10 .dp) / 2f
142
+ val vGapSize = (rootSize - (10 .dp * 5f )) / ((boxesCount + 1 ) * 2f )
143
+ val rowSize = 10 .dp + vGapSize * 2
144
+ var expectedX = 0 .dp
145
+ var expectedY = rowSize - 5 .dp
146
+
147
+ expectedX + = hGapSize
148
+ rule.onNodeWithTag(" box0" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
149
+ expectedY + = expectedY + vGapSize + 10 .dp
150
+ rule.onNodeWithTag(" box1" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
151
+ expectedY + = vGapSize + vGapSize + 10 .dp
152
+ rule.onNodeWithTag(" box2" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
153
+ expectedY + = vGapSize + vGapSize + 10 .dp
154
+ rule.onNodeWithTag(" box3" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
155
+ }
156
+
157
+ @Test
158
+ fun testRow () {
159
+ val rootSize = 200 .dp
160
+ val boxesCount = 4
161
+ rule.setContent {
162
+ RowComposableTest (
163
+ modifier = Modifier .size(rootSize),
164
+ gridSkips = arrayOf(),
165
+ gridSpans = arrayOf(),
166
+ boxesCount = boxesCount,
167
+ hGap = 0 ,
168
+ gridColumnWeights = intArrayOf()
169
+ )
170
+ }
171
+ var expectedX = 0 .dp
172
+ var expectedY = 0 .dp
173
+
174
+ // 10.dp is the size of a singular box
175
+ val hGapSize = (rootSize - (10 .dp * 4f )) / (boxesCount * 2f )
176
+ val vGapSize = (rootSize - 10 .dp) / 2f
177
+ rule.waitForIdle()
178
+ expectedX + = hGapSize
179
+ expectedY + = vGapSize
180
+ rule.onNodeWithTag(" box0" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
181
+ expectedX + = hGapSize + hGapSize + 10 .dp
182
+ rule.onNodeWithTag(" box1" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
183
+ expectedX + = hGapSize + hGapSize + 10 .dp
184
+ rule.onNodeWithTag(" box2" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
185
+ expectedX + = hGapSize + hGapSize + 10 .dp
186
+ rule.onNodeWithTag(" box3" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
187
+ }
188
+
189
+ @Test
190
+ fun testRowSkips () {
191
+ val rootSize = 200 .dp
192
+ val boxesCount = 4
193
+ rule.setContent {
194
+ RowComposableTest (
195
+ modifier = Modifier .size(rootSize),
196
+ gridSkips = arrayOf(Skip (1 , 2 )),
197
+ gridSpans = arrayOf(),
198
+ boxesCount = boxesCount,
199
+ hGap = 0 ,
200
+ gridColumnWeights = intArrayOf()
201
+ )
202
+ }
203
+ var expectedX = 0 .dp
204
+ var expectedY = 0 .dp
205
+
206
+ // 10.dp is the size of a singular box
207
+ val hGapSize = (rootSize - (10 .dp * 6f )) / ((boxesCount + 2 ) * 2f )
208
+ val vGapSize = (rootSize - 10 .dp) / 2f
209
+ rule.waitForIdle()
210
+ expectedX + = hGapSize
211
+ expectedY + = vGapSize
212
+ rule.onNodeWithTag(" box0" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
213
+ expectedX + = hGapSize + hGapSize + 10 .dp
214
+ expectedX + = hGapSize + hGapSize + 10 .dp
215
+ expectedX + = hGapSize + hGapSize + 10 .dp
216
+ rule.onNodeWithTag(" box1" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
217
+ expectedX + = hGapSize + hGapSize + 10 .dp
218
+ rule.onNodeWithTag(" box2" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
219
+ expectedX + = hGapSize + hGapSize + 10 .dp
220
+ rule.onNodeWithTag(" box3" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
221
+ }
222
+
223
+ @Test
224
+ fun testRowSpans () {
225
+ val rootSize = 200 .dp
226
+ val boxesCount = 4
227
+ rule.setContent {
228
+ RowComposableTest (
229
+ modifier = Modifier .size(rootSize),
230
+ gridSkips = arrayOf(),
231
+ gridSpans = arrayOf(Span (0 , 2 )),
232
+ boxesCount = boxesCount,
233
+ hGap = 0 ,
234
+ gridColumnWeights = intArrayOf()
235
+ )
236
+ }
237
+
238
+ // 10.dp is the size of a singular box
239
+ val hGapSize = (rootSize - (10 .dp * 5f )) / ((boxesCount + 1 ) * 2f )
240
+ val vGapSize = (rootSize - 10 .dp) / 2f
241
+ val columnSize = 10 .dp + hGapSize * 2
242
+ var expectedX = columnSize - 5 .dp
243
+ var expectedY = 0 .dp
244
+
245
+ expectedY + = vGapSize
246
+
247
+ rule.onNodeWithTag(" box0" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
248
+ expectedX + = expectedX + hGapSize + 10 .dp
249
+ rule.onNodeWithTag(" box1" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
250
+ expectedX + = hGapSize + hGapSize + 10 .dp
251
+ rule.onNodeWithTag(" box2" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
252
+ expectedX + = hGapSize + hGapSize + 10 .dp
253
+ rule.onNodeWithTag(" box3" ).assertPositionInRootIsEqualTo(expectedX, expectedY)
254
+ }
255
+
256
+ @Composable
257
+ private fun ColumnComposableTest (
258
+ modifier : Modifier = Modifier ,
259
+ gridSkips : Array <Skip >,
260
+ gridSpans : Array <Span >,
261
+ gridRowWeights : IntArray ,
262
+ boxesCount : Int ,
263
+ vGap : Int ,
264
+ ) {
265
+ ConstraintLayout (
266
+ ConstraintSet {
267
+ val ids = (0 until boxesCount).map { " box$it " }.toTypedArray()
268
+ val elem = arrayListOf<LayoutReference >()
269
+ for (i in ids.indices) {
270
+ elem.add(createRefFor(ids[i]))
271
+ }
272
+
273
+ val g1 = createColumn(
274
+ elements = elem.toTypedArray(),
275
+ skips = gridSkips,
276
+ spans = gridSpans,
277
+ verticalGap = vGap.dp,
278
+ rowWeights = gridRowWeights,
279
+ )
280
+ constrain(g1) {
281
+ width = Dimension .matchParent
282
+ height = Dimension .matchParent
283
+ }
284
+ },
285
+ modifier = modifier
286
+ ) {
287
+ val ids = (0 until boxesCount).map { " box$it " }.toTypedArray()
288
+ ids.forEach { id ->
289
+ Box (
290
+ Modifier
291
+ .layoutId(id)
292
+ .background(Color .Red )
293
+ .testTag(id)
294
+ .size(10 .dp)
295
+ )
296
+ }
297
+ }
298
+ }
299
+
300
+ @Composable
301
+ private fun RowComposableTest (
302
+ modifier : Modifier = Modifier ,
303
+ gridSkips : Array <Skip >,
304
+ gridSpans : Array <Span >,
305
+ gridColumnWeights : IntArray ,
306
+ boxesCount : Int ,
307
+ hGap : Int ,
308
+ ) {
309
+ ConstraintLayout (
310
+ ConstraintSet {
311
+ val ids = (0 until boxesCount).map { " box$it " }.toTypedArray()
312
+ val elem = arrayListOf<LayoutReference >()
313
+ for (i in ids.indices) {
314
+ elem.add(createRefFor(ids[i]))
315
+ }
316
+
317
+ val g1 = createRow(
318
+ elements = elem.toTypedArray(),
319
+ horizontalGap = hGap.dp,
320
+ skips = gridSkips,
321
+ spans = gridSpans,
322
+ columnWeights = gridColumnWeights,
323
+ )
324
+ constrain(g1) {
325
+ width = Dimension .matchParent
326
+ height = Dimension .matchParent
327
+ }
328
+ },
329
+ modifier = modifier
330
+ ) {
331
+ val ids = (0 until boxesCount).map { " box$it " }.toTypedArray()
332
+ ids.forEach { id ->
333
+ Box (
334
+ Modifier
335
+ .layoutId(id)
336
+ .background(Color .Red )
337
+ .testTag(id)
338
+ .size(10 .dp)
339
+ )
340
+ }
341
+ }
342
+ }
343
+ }
0 commit comments