1 /* <lambda>null2 * Copyright (C) 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 com.android.launcher3.responsive 18 19 import android.content.Context 20 import androidx.test.ext.junit.runners.AndroidJUnit4 21 import androidx.test.filters.SmallTest 22 import androidx.test.platform.app.InstrumentationRegistry 23 import com.android.launcher3.AbstractDeviceProfileTest 24 import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType 25 import com.android.launcher3.responsive.ResponsiveSpec.DimensionType 26 import com.android.launcher3.util.TestResourceHelper 27 import com.google.common.truth.Truth.assertThat 28 import org.junit.Before 29 import org.junit.Test 30 import org.junit.runner.RunWith 31 32 @SmallTest 33 @RunWith(AndroidJUnit4::class) 34 class ResponsiveSpecsProviderTest : AbstractDeviceProfileTest() { 35 override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context 36 val deviceSpec = deviceSpecs["tablet"]!! 37 var aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second 38 39 @Before 40 fun setup() { 41 initializeVarsForPhone(deviceSpec) 42 } 43 44 @Test 45 fun parseValidFile() { 46 val resourceHelper = TestResourceHelper(context, "valid_responsive_spec_unsorted".xmlToId()) 47 val provider = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) 48 49 // Validate Portrait 50 val aspectRatioPortrait = 1.0f 51 val portraitSpecs = provider.getSpecsByAspectRatio(aspectRatioPortrait) 52 53 val (expectedPortWidthSpecs, expectedPortHeightSpecs) = expectedPortraitSpecs() 54 validateSpecs( 55 portraitSpecs, 56 aspectRatioPortrait, 57 expectedPortWidthSpecs, 58 expectedPortHeightSpecs 59 ) 60 61 // Validate Landscape 62 val aspectRatioLandscape = 1.051f 63 val landscapeSpecs = provider.getSpecsByAspectRatio(aspectRatioLandscape) 64 65 val (expectedLandWidthSpecs, expectedLandHeightSpecs) = expectedLandscapeSpecs() 66 validateSpecs( 67 landscapeSpecs, 68 aspectRatioLandscape, 69 expectedLandWidthSpecs, 70 expectedLandHeightSpecs 71 ) 72 73 // Validate Extra Spec 74 val aspectRatioExtra = 10.1f 75 val extraSpecs = provider.getSpecsByAspectRatio(aspectRatioExtra) 76 77 val expectedOtherWidthSpecs = 78 listOf( 79 ResponsiveSpec( 80 maxAvailableSize = 9999.dpToPx(), 81 dimensionType = DimensionType.WIDTH, 82 specType = ResponsiveSpecType.Workspace, 83 startPadding = SizeSpec(1f.dpToPx()), 84 endPadding = SizeSpec(1f.dpToPx()), 85 gutter = SizeSpec(8f.dpToPx()), 86 cellSize = SizeSpec(ofRemainderSpace = 1f) 87 ) 88 ) 89 90 val expectedOtherHeightSpecs = 91 listOf( 92 ResponsiveSpec( 93 maxAvailableSize = 9999.dpToPx(), 94 dimensionType = DimensionType.HEIGHT, 95 specType = ResponsiveSpecType.Workspace, 96 startPadding = SizeSpec(2f.dpToPx()), 97 endPadding = SizeSpec(2f.dpToPx()), 98 gutter = SizeSpec(8f.dpToPx()), 99 cellSize = SizeSpec(ofRemainderSpace = 1f) 100 ), 101 ) 102 103 validateSpecs( 104 extraSpecs, 105 aspectRatioExtra, 106 expectedOtherWidthSpecs, 107 expectedOtherHeightSpecs 108 ) 109 } 110 111 @Test(expected = IllegalStateException::class) 112 fun parseValidFile_invalidAspectRatio_throwsError() { 113 val resourceHelper = TestResourceHelper(context, "valid_responsive_spec_unsorted".xmlToId()) 114 val provider = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) 115 provider.getSpecsByAspectRatio(0f) 116 } 117 118 @Test(expected = InvalidResponsiveGridSpec::class) 119 fun parseInvalidFile_missingGroups_throwsError() { 120 val resourceHelper = TestResourceHelper(context, "invalid_responsive_spec_1".xmlToId()) 121 ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) 122 } 123 124 @Test(expected = InvalidResponsiveGridSpec::class) 125 fun parseInvalidFile_partialGroups_throwsError() { 126 val resourceHelper = TestResourceHelper(context, "invalid_responsive_spec_2".xmlToId()) 127 ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) 128 } 129 130 @Test(expected = IllegalStateException::class) 131 fun parseInvalidFile_invalidAspectRatio_throwsError() { 132 val resourceHelper = TestResourceHelper(context, "invalid_responsive_spec_3".xmlToId()) 133 ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) 134 } 135 136 @Test(expected = IllegalStateException::class) 137 fun parseInvalidFile_invalidRemainderSpace_throwsError() { 138 val resourceHelper = TestResourceHelper(context, "invalid_responsive_spec_4".xmlToId()) 139 ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) 140 } 141 142 @Test(expected = IllegalStateException::class) 143 fun parseInvalidFile_invalidAvailableSpace_throwsError() { 144 val resourceHelper = TestResourceHelper(context, "invalid_responsive_spec_5".xmlToId()) 145 ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) 146 } 147 148 @Test(expected = IllegalStateException::class) 149 fun parseInvalidFile_invalidFixedSize_throwsError() { 150 val resourceHelper = TestResourceHelper(context, "invalid_responsive_spec_6".xmlToId()) 151 ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) 152 } 153 154 private fun validateSpecs( 155 specs: ResponsiveSpecGroup<ResponsiveSpec>, 156 expectedAspectRatio: Float, 157 expectedWidthSpecs: List<ResponsiveSpec>, 158 expectedHeightSpecs: List<ResponsiveSpec> 159 ) { 160 assertThat(specs.aspectRatio).isAtLeast(expectedAspectRatio) 161 162 assertThat(specs.widthSpecs.size).isEqualTo(expectedWidthSpecs.size) 163 specs.widthSpecs.forEachIndexed { index, responsiveSpec -> 164 assertThat(responsiveSpec).isEqualTo(expectedWidthSpecs[index]) 165 } 166 167 assertThat(specs.heightSpecs.size).isEqualTo(expectedHeightSpecs.size) 168 specs.heightSpecs.forEachIndexed { index, responsiveSpec -> 169 assertThat(responsiveSpec).isEqualTo(expectedHeightSpecs[index]) 170 } 171 } 172 173 private fun expectedPortraitSpecs(): Pair<List<ResponsiveSpec>, List<ResponsiveSpec>> { 174 val sizeSpec16 = SizeSpec(16f.dpToPx()) 175 val expectedWidthSpecs = 176 listOf( 177 ResponsiveSpec( 178 maxAvailableSize = 9999.dpToPx(), 179 dimensionType = DimensionType.WIDTH, 180 specType = ResponsiveSpecType.Workspace, 181 startPadding = SizeSpec(22f.dpToPx()), 182 endPadding = SizeSpec(22f.dpToPx()), 183 gutter = sizeSpec16, 184 cellSize = SizeSpec(ofRemainderSpace = 1f) 185 ) 186 ) 187 188 val expectedHeightSpecs = 189 listOf( 190 ResponsiveSpec( 191 maxAvailableSize = 584.dpToPx(), 192 dimensionType = DimensionType.HEIGHT, 193 specType = ResponsiveSpecType.Workspace, 194 startPadding = SizeSpec(0f), 195 endPadding = SizeSpec(32f.dpToPx()), 196 gutter = sizeSpec16, 197 cellSize = SizeSpec(ofAvailableSpace = .15808f) 198 ), 199 ResponsiveSpec( 200 maxAvailableSize = 612.dpToPx(), 201 dimensionType = DimensionType.HEIGHT, 202 specType = ResponsiveSpecType.Workspace, 203 startPadding = SizeSpec(0f), 204 endPadding = SizeSpec(ofRemainderSpace = 1f), 205 gutter = sizeSpec16, 206 cellSize = SizeSpec(104f.dpToPx()) 207 ), 208 ResponsiveSpec( 209 maxAvailableSize = 9999.dpToPx(), 210 dimensionType = DimensionType.HEIGHT, 211 specType = ResponsiveSpecType.Workspace, 212 startPadding = SizeSpec(8f.dpToPx()), 213 endPadding = SizeSpec(ofRemainderSpace = 1f), 214 gutter = sizeSpec16, 215 cellSize = SizeSpec(104f.dpToPx()) 216 ), 217 ) 218 219 return Pair(expectedWidthSpecs, expectedHeightSpecs) 220 } 221 222 private fun expectedLandscapeSpecs(): Pair<List<ResponsiveSpec>, List<ResponsiveSpec>> { 223 val sizeSpec12 = SizeSpec(12f.dpToPx()) 224 val expectedWidthSpecs = 225 listOf( 226 ResponsiveSpec( 227 maxAvailableSize = 602.dpToPx(), 228 dimensionType = DimensionType.WIDTH, 229 specType = ResponsiveSpecType.Workspace, 230 startPadding = SizeSpec(0f.dpToPx()), 231 endPadding = SizeSpec(36f.dpToPx()), 232 gutter = sizeSpec12, 233 cellSize = SizeSpec(ofRemainderSpace = 1f) 234 ), 235 ResponsiveSpec( 236 maxAvailableSize = 716.dpToPx(), 237 dimensionType = DimensionType.WIDTH, 238 specType = ResponsiveSpecType.Workspace, 239 startPadding = SizeSpec(16f.dpToPx()), 240 endPadding = SizeSpec(64f.dpToPx()), 241 gutter = sizeSpec12, 242 cellSize = SizeSpec(ofRemainderSpace = 1f) 243 ), 244 ResponsiveSpec( 245 maxAvailableSize = 9999.dpToPx(), 246 dimensionType = DimensionType.WIDTH, 247 specType = ResponsiveSpecType.Workspace, 248 startPadding = SizeSpec(36f.dpToPx()), 249 endPadding = SizeSpec(80f.dpToPx()), 250 gutter = sizeSpec12, 251 cellSize = SizeSpec(ofRemainderSpace = 1f) 252 ) 253 ) 254 255 val expectedHeightSpecs = 256 listOf( 257 ResponsiveSpec( 258 maxAvailableSize = 371.dpToPx(), 259 dimensionType = DimensionType.HEIGHT, 260 specType = ResponsiveSpecType.Workspace, 261 startPadding = SizeSpec(0f), 262 endPadding = SizeSpec(24f.dpToPx()), 263 gutter = sizeSpec12, 264 cellSize = SizeSpec(ofRemainderSpace = 1f) 265 ), 266 ResponsiveSpec( 267 maxAvailableSize = 9999.dpToPx(), 268 dimensionType = DimensionType.HEIGHT, 269 specType = ResponsiveSpecType.Workspace, 270 startPadding = SizeSpec(0f), 271 endPadding = SizeSpec(34f.dpToPx()), 272 gutter = sizeSpec12, 273 cellSize = SizeSpec(ofRemainderSpace = 1f) 274 ), 275 ) 276 277 return Pair(expectedWidthSpecs, expectedHeightSpecs) 278 } 279 } 280