1 /*
2  * Copyright 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  *      https://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 @file:Suppress("DEPRECATION")
18 
19 package com.google.accompanist.themeadapter.material3
20 
21 import androidx.appcompat.app.AppCompatActivity
22 import androidx.compose.foundation.shape.CornerSize
23 import androidx.compose.foundation.shape.CutCornerShape
24 import androidx.compose.foundation.shape.RoundedCornerShape
25 import androidx.compose.material3.LocalContentColor
26 import androidx.compose.material3.MaterialTheme
27 import androidx.compose.ui.geometry.Size
28 import androidx.compose.ui.platform.LocalDensity
29 import androidx.compose.ui.res.colorResource
30 import androidx.compose.ui.test.junit4.createAndroidComposeRule
31 import androidx.compose.ui.text.font.Font
32 import androidx.compose.ui.text.font.FontFamily
33 import androidx.compose.ui.text.font.FontWeight
34 import androidx.compose.ui.text.font.toFontFamily
35 import androidx.compose.ui.unit.Density
36 import androidx.compose.ui.unit.Dp
37 import androidx.compose.ui.unit.TextUnit
38 import androidx.compose.ui.unit.dp
39 import androidx.compose.ui.unit.em
40 import androidx.compose.ui.unit.sp
41 import com.google.accompanist.themeadapter.core.FontFamilyWithWeight
42 import com.google.accompanist.themeadapter.material3.test.R
43 import org.junit.Assert.assertEquals
44 import org.junit.Assert.assertNotNull
45 import org.junit.Assert.assertNull
46 import org.junit.Assert.assertTrue
47 import org.junit.Rule
48 import org.junit.Test
49 
50 /**
51  * Class which contains the majority of the tests. This class is extended
52  * in both the `androidTest` and `test` source sets for setup of the relevant
53  * test runner.
54  */
55 abstract class BaseMdc3ThemeTest<T : AppCompatActivity>(
56     activityClass: Class<T>
57 ) {
58     @get:Rule
59     val composeTestRule = createAndroidComposeRule(activityClass)
60 
61     @Test
<lambda>null62     fun colors() = composeTestRule.setContent {
63         Mdc3Theme {
64             val colorScheme = MaterialTheme.colorScheme
65 
66             assertEquals(colorResource(R.color.aquamarine), colorScheme.primary)
67             assertEquals(colorResource(R.color.pale_turquoise), colorScheme.onPrimary)
68             assertEquals(colorResource(R.color.midnight_blue), colorScheme.inversePrimary)
69             assertEquals(colorResource(R.color.royal_blue), colorScheme.primaryContainer)
70             assertEquals(colorResource(R.color.steel_blue), colorScheme.onPrimaryContainer)
71 
72             assertEquals(colorResource(R.color.dodger_blue), colorScheme.secondary)
73             assertEquals(colorResource(R.color.dark_golden_rod), colorScheme.onSecondary)
74             assertEquals(colorResource(R.color.peru), colorScheme.secondaryContainer)
75             assertEquals(colorResource(R.color.blue_violet), colorScheme.onSecondaryContainer)
76 
77             assertEquals(colorResource(R.color.dark_orchid), colorScheme.tertiary)
78             assertEquals(colorResource(R.color.slate_gray), colorScheme.onTertiary)
79             assertEquals(colorResource(R.color.gray), colorScheme.tertiaryContainer)
80             assertEquals(colorResource(R.color.spring_green), colorScheme.onTertiaryContainer)
81 
82             assertEquals(colorResource(R.color.medium_spring_green), colorScheme.background)
83             assertEquals(colorResource(R.color.navy), colorScheme.onBackground)
84 
85             assertEquals(colorResource(R.color.dark_blue), colorScheme.surface)
86             assertEquals(colorResource(R.color.light_coral), colorScheme.onSurface)
87             assertEquals(colorResource(R.color.salmon), colorScheme.surfaceVariant)
88             assertEquals(colorResource(R.color.dark_salmon), colorScheme.onSurfaceVariant)
89             assertEquals(colorResource(R.color.indian_red), colorScheme.surfaceTint)
90             assertEquals(colorResource(R.color.light_salmon), colorScheme.inverseSurface)
91             assertEquals(colorResource(R.color.orchid), colorScheme.inverseOnSurface)
92 
93             assertEquals(colorResource(R.color.violet), colorScheme.outline)
94             // TODO: MDC-Android doesn't include outlineVariant yet, add when available
95 
96             assertEquals(colorResource(R.color.beige), colorScheme.error)
97             assertEquals(colorResource(R.color.white_smoke), colorScheme.onError)
98             assertEquals(colorResource(R.color.olive), colorScheme.errorContainer)
99             assertEquals(colorResource(R.color.olive_drab), colorScheme.onErrorContainer)
100 
101             assertEquals(colorResource(R.color.crimson), colorScheme.scrim)
102 
103             // Mdc3Theme updates the LocalContentColor to match the calculated onBackground
104             assertEquals(colorResource(R.color.navy), LocalContentColor.current)
105         }
106     }
107 
108     @Test
<lambda>null109     fun shapes() = composeTestRule.setContent {
110         Mdc3Theme {
111             val shapes = MaterialTheme.shapes
112             val density = LocalDensity.current
113 
114             shapes.extraSmall.run {
115                 assertTrue(this is RoundedCornerShape)
116                 assertEquals(4f, topStart.toPx(density))
117                 assertEquals(9.dp.scaleToPx(density), topEnd.toPx(density))
118                 assertEquals(5f, bottomEnd.toPx(density))
119                 assertEquals(3.dp.scaleToPx(density), bottomStart.toPx(density))
120             }
121             shapes.small.run {
122                 assertTrue(this is CutCornerShape)
123                 assertEquals(4f, topStart.toPx(density))
124                 assertEquals(9.dp.scaleToPx(density), topEnd.toPx(density))
125                 assertEquals(5f, bottomEnd.toPx(density))
126                 assertEquals(3.dp.scaleToPx(density), bottomStart.toPx(density))
127             }
128             shapes.medium.run {
129                 assertTrue(this is RoundedCornerShape)
130                 assertEquals(12.dp.scaleToPx(density), topStart.toPx(density))
131                 assertEquals(12.dp.scaleToPx(density), topEnd.toPx(density))
132                 assertEquals(12.dp.scaleToPx(density), bottomEnd.toPx(density))
133                 assertEquals(12.dp.scaleToPx(density), bottomStart.toPx(density))
134             }
135             shapes.large.run {
136                 assertTrue(this is CutCornerShape)
137                 assertEquals(16.dp.scaleToPx(density), topStart.toPx(density))
138                 assertEquals(16.dp.scaleToPx(density), topEnd.toPx(density))
139                 assertEquals(16.dp.scaleToPx(density), bottomEnd.toPx(density))
140                 assertEquals(16.dp.scaleToPx(density), bottomStart.toPx(density))
141             }
142             shapes.extraLarge.run {
143                 assertTrue(this is RoundedCornerShape)
144                 assertEquals(28.dp.scaleToPx(density), topStart.toPx(density))
145                 assertEquals(28.dp.scaleToPx(density), topEnd.toPx(density))
146                 assertEquals(28.dp.scaleToPx(density), bottomEnd.toPx(density))
147                 assertEquals(28.dp.scaleToPx(density), bottomStart.toPx(density))
148             }
149         }
150     }
151 
152     @Test
<lambda>null153     fun type() = composeTestRule.setContent {
154         Mdc3Theme {
155             val typography = MaterialTheme.typography
156             val density = LocalDensity.current
157 
158             val rubik300 = Font(R.font.rubik_300).toFontFamily()
159             val rubik400 = Font(R.font.rubik_400).toFontFamily()
160             val rubik500 = Font(R.font.rubik_500).toFontFamily()
161             val rubik700 = Font(R.font.rubik_700).toFontFamily()
162             val sansSerif = FontFamilyWithWeight(FontFamily.SansSerif)
163             val sansSerifLight = FontFamilyWithWeight(FontFamily.SansSerif, FontWeight.Light)
164             val sansSerifBlack = FontFamilyWithWeight(FontFamily.SansSerif, FontWeight.Black)
165             val serif = FontFamilyWithWeight(FontFamily.Serif)
166             val cursive = FontFamilyWithWeight(FontFamily.Cursive)
167             val monospace = FontFamilyWithWeight(FontFamily.Monospace)
168 
169             typography.displayLarge.run {
170                 assertTextUnitEquals(97.54.sp, fontSize, density)
171                 assertTextUnitEquals((-0.0015).em, letterSpacing, density)
172                 assertEquals(rubik300, fontFamily)
173             }
174 
175             assertNotNull(typography.displayMedium.shadow)
176             typography.displayMedium.shadow!!.run {
177                 assertEquals(colorResource(R.color.olive_drab), color)
178                 assertEquals(4.43f, offset.x)
179                 assertEquals(8.19f, offset.y)
180                 assertEquals(2.13f, blurRadius)
181             }
182 
183             typography.displaySmall.run {
184                 assertEquals(sansSerif.fontFamily, fontFamily)
185                 assertEquals(sansSerif.weight, fontWeight)
186             }
187 
188             typography.headlineLarge.run {
189                 assertEquals(sansSerifLight.fontFamily, fontFamily)
190                 assertEquals(sansSerifLight.weight, fontWeight)
191             }
192 
193             typography.headlineMedium.run {
194                 assertEquals(sansSerifLight.fontFamily, fontFamily)
195                 assertEquals(sansSerifLight.weight, fontWeight)
196             }
197 
198             typography.headlineSmall.run {
199                 assertEquals(sansSerifBlack.fontFamily, fontFamily)
200                 assertEquals(sansSerifBlack.weight, fontWeight)
201             }
202 
203             typography.titleLarge.run {
204                 assertEquals(serif.fontFamily, fontFamily)
205                 assertEquals(serif.weight, fontWeight)
206             }
207 
208             typography.titleMedium.run {
209                 assertEquals(monospace.fontFamily, fontFamily)
210                 assertEquals(monospace.weight, fontWeight)
211                 assertTextUnitEquals(0.em, letterSpacing, density)
212             }
213 
214             typography.titleSmall.run {
215                 assertEquals(FontFamily.SansSerif, fontFamily)
216             }
217 
218             typography.bodyLarge.run {
219                 assertTextUnitEquals(16.26.sp, fontSize, density)
220                 assertTextUnitEquals(0.005.em, letterSpacing, density)
221                 assertEquals(rubik400, fontFamily)
222                 assertNull(shadow)
223             }
224 
225             typography.bodyMedium.run {
226                 assertEquals(cursive.fontFamily, fontFamily)
227                 assertEquals(cursive.weight, fontWeight)
228             }
229 
230             typography.bodySmall.run {
231                 assertEquals(FontFamily.SansSerif, fontFamily)
232                 assertTextUnitEquals(0.04.em, letterSpacing, density)
233             }
234 
235             typography.labelLarge.run {
236                 assertEquals(rubik500, fontFamily)
237             }
238 
239             typography.labelMedium.run {
240                 assertEquals(rubik700, fontFamily)
241             }
242 
243             typography.labelSmall.run {
244                 assertEquals(FontFamily.SansSerif, fontFamily)
245             }
246         }
247     }
248 }
249 
Dpnull250 private fun Dp.scaleToPx(density: Density): Float {
251     val dp = this
252     return with(density) { dp.toPx() }
253 }
254 
assertTextUnitEqualsnull255 private fun assertTextUnitEquals(expected: TextUnit, actual: TextUnit, density: Density) {
256     if (expected.javaClass == actual.javaClass) {
257         // If the expected and actual are the same type, compare the raw values with a
258         // delta to account for float inaccuracy
259         assertEquals(expected.value, actual.value, 0.001f)
260     } else {
261         // Otherwise we need to flatten to a px to compare the values. Again using a
262         // delta to account for float inaccuracy
263         with(density) { assertEquals(expected.toPx(), actual.toPx(), 0.001f) }
264     }
265 }
266 
toPxnull267 private fun CornerSize.toPx(density: Density) = toPx(Size.Unspecified, density)
268