Loading tests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -176,6 +176,7 @@ android_library { name: "launcher-testing-shared", srcs: [ "multivalentTests/shared/com/android/launcher3/testing/shared/**/*.java", "multivalentTests/shared/com/android/launcher3/testing/shared/**/*.kt" ], resource_dirs: [], manifest: "multivalentTests/shared/AndroidManifest.xml", Loading tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java +3 −3 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } public CellLayoutBoard(int width, int height) { mWidget = new char[width + 1][height + 1]; mWidget = new char[width][height]; this.mWidth = width; this.mHeight = height; for (int x = 0; x < mWidget.length; x++) { Loading Loading @@ -371,8 +371,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { s.append("\n"); maxX = Math.min(maxX, mWidget.length); maxY = Math.min(maxY, mWidget[0].length); for (int y = 0; y <= maxY; y++) { for (int x = 0; x <= maxX; x++) { for (int y = 0; y < maxY; y++) { for (int x = 0; x < maxX; x++) { s.append(mWidget[x][y]); } s.append('\n'); Loading tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt +3 −3 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ open class RandomBoardGenerator(generator: Random) : DeterministicRandomGenerato * usually less than 100. * @return a randomly generated board filled with icons and widgets. */ open fun generateBoard(width: Int, height: Int, remainingEmptySpaces: Int): CellLayoutBoard? { open fun generateBoard(width: Int, height: Int, remainingEmptySpaces: Int): CellLayoutBoard { val cellLayoutBoard = CellLayoutBoard(width, height) return fillBoard(cellLayoutBoard, Rect(0, 0, width, height), remainingEmptySpaces) } Loading @@ -39,8 +39,8 @@ open class RandomBoardGenerator(generator: Random) : DeterministicRandomGenerato ): CellLayoutBoard { var remainingEmptySpaces = remainingEmptySpacesArg if (area.height() * area.width() <= 0) return board val width = getRandom(1, area.width() - 1) val height = getRandom(1, area.height() - 1) val width = getRandom(1, area.width()) val height = getRandom(1, area.height()) val x = area.left + getRandom(0, area.width() - width) val y = area.top + getRandom(0, area.height() - height) if (remainingEmptySpaces > 0) { Loading tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt 0 → 100644 +179 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.celllayout import android.content.Context import android.graphics.Point import android.util.Log import android.view.View import androidx.core.view.get import androidx.test.core.app.ApplicationProvider import com.android.launcher3.CellLayout import com.android.launcher3.celllayout.board.CellLayoutBoard import com.android.launcher3.celllayout.board.IconPoint import com.android.launcher3.celllayout.board.PermutedBoardComparator import com.android.launcher3.celllayout.board.WidgetRect import com.android.launcher3.celllayout.testgenerator.RandomBoardGenerator import com.android.launcher3.util.ActivityContextWrapper import com.android.launcher3.views.DoubleShadowBubbleTextView import java.util.Random import org.junit.Assert import org.junit.Rule import org.junit.Test private class HotseatReorderTestCase( val startBoard: CellLayoutBoard, val endBoard: CellLayoutBoard ) { override fun toString(): String { return "$startBoard#endBoard:\n$endBoard" } } class HotseatReorderUnitTest { private val applicationContext: Context = ActivityContextWrapper(ApplicationProvider.getApplicationContext()) @JvmField @Rule var cellLayoutBuilder = UnitTestCellLayoutBuilderRule() /** * This test generates random CellLayout configurations and then try to reorder it and makes * sure the result is a valid board meaning it didn't remove any widget or icon. */ @Test fun generateValidTests() { val generator = Random(Companion.SEED.toLong()) for (i in 0 until Companion.TOTAL_OF_CASES_GENERATED) { // Using a new seed so that we can replicate the same test cases. val seed = generator.nextInt() Log.d(Companion.TAG, "Seed = $seed") val testCase: HotseatReorderTestCase = generateRandomTestCase(RandomBoardGenerator(Random(seed.toLong()))) Log.d(Companion.TAG, "testCase = $testCase") Assert.assertTrue( "invalid case $i", PermutedBoardComparator().compare(testCase.startBoard, testCase.endBoard) == 0 ) } } private fun addViewInCellLayout( cellLayout: CellLayout, cellX: Int, cellY: Int, spanX: Int, spanY: Int, isWidget: Boolean ) { val cell = if (isWidget) View(applicationContext) else DoubleShadowBubbleTextView(applicationContext) cell.layoutParams = CellLayoutLayoutParams(cellX, cellY, spanX, spanY) cellLayout.addViewToCellLayout( cell, -1, cell.id, cell.layoutParams as CellLayoutLayoutParams, true ) } private fun solve(board: CellLayoutBoard): CellLayout { val cl = cellLayoutBuilder.createCellLayout(board.width, board.height, false) // The views have to be sorted or the result can vary board.icons .map(IconPoint::getCoord) .sortedWith( Comparator.comparing { p: Any -> (p as Point).x } .thenComparing { p: Any -> (p as Point).y } ) .forEach { p -> addViewInCellLayout( cellLayout = cl, cellX = p.x, cellY = p.y, spanX = 1, spanY = 1, isWidget = false ) } board.widgets .sortedWith( Comparator.comparing(WidgetRect::getCellX).thenComparing(WidgetRect::getCellY) ) .forEach { widget -> addViewInCellLayout( cl, widget.cellX, widget.cellY, widget.spanX, widget.spanY, isWidget = true ) } if (cl.makeSpaceForHotseatMigration(true)) { commitTempPosition(cl) } return cl } private fun commitTempPosition(cellLayout: CellLayout) { val count = cellLayout.shortcutsAndWidgets.childCount for (i in 0 until count) { val params = cellLayout.shortcutsAndWidgets[i].layoutParams as CellLayoutLayoutParams params.cellX = params.tmpCellX params.cellY = params.tmpCellY } } private fun boardFromCellLayout(cellLayout: CellLayout): CellLayoutBoard { val views = mutableListOf<View>() for (i in 0 until cellLayout.shortcutsAndWidgets.childCount) { views.add(cellLayout.shortcutsAndWidgets.getChildAt(i)) } return CellLayoutTestUtils.viewsToBoard(views, cellLayout.countX, cellLayout.countY) } private fun generateRandomTestCase( boardGenerator: RandomBoardGenerator ): HotseatReorderTestCase { val width: Int = boardGenerator.getRandom(3, Companion.MAX_BOARD_SIZE) val height: Int = boardGenerator.getRandom(3, Companion.MAX_BOARD_SIZE) val targetWidth: Int = boardGenerator.getRandom(1, width - 2) val targetHeight: Int = boardGenerator.getRandom(1, height - 2) val board: CellLayoutBoard = boardGenerator.generateBoard(width, height, targetWidth * targetHeight) val finishBoard: CellLayoutBoard = boardFromCellLayout(solve(board)) return HotseatReorderTestCase(board, finishBoard) } companion object { private const val MAX_BOARD_SIZE = 13 /** * There is nothing special about this numbers, the random seed is just to be able to * reproduce the test cases and the height and width is a random number similar to what * users expect on their devices */ private const val SEED = -194162315 private const val TOTAL_OF_CASES_GENERATED = 300 private const val TAG = "HotseatReorderUnitTest" } } Loading
tests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -176,6 +176,7 @@ android_library { name: "launcher-testing-shared", srcs: [ "multivalentTests/shared/com/android/launcher3/testing/shared/**/*.java", "multivalentTests/shared/com/android/launcher3/testing/shared/**/*.kt" ], resource_dirs: [], manifest: "multivalentTests/shared/AndroidManifest.xml", Loading
tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java +3 −3 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } public CellLayoutBoard(int width, int height) { mWidget = new char[width + 1][height + 1]; mWidget = new char[width][height]; this.mWidth = width; this.mHeight = height; for (int x = 0; x < mWidget.length; x++) { Loading Loading @@ -371,8 +371,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { s.append("\n"); maxX = Math.min(maxX, mWidget.length); maxY = Math.min(maxY, mWidget[0].length); for (int y = 0; y <= maxY; y++) { for (int x = 0; x <= maxX; x++) { for (int y = 0; y < maxY; y++) { for (int x = 0; x < maxX; x++) { s.append(mWidget[x][y]); } s.append('\n'); Loading
tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt +3 −3 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ open class RandomBoardGenerator(generator: Random) : DeterministicRandomGenerato * usually less than 100. * @return a randomly generated board filled with icons and widgets. */ open fun generateBoard(width: Int, height: Int, remainingEmptySpaces: Int): CellLayoutBoard? { open fun generateBoard(width: Int, height: Int, remainingEmptySpaces: Int): CellLayoutBoard { val cellLayoutBoard = CellLayoutBoard(width, height) return fillBoard(cellLayoutBoard, Rect(0, 0, width, height), remainingEmptySpaces) } Loading @@ -39,8 +39,8 @@ open class RandomBoardGenerator(generator: Random) : DeterministicRandomGenerato ): CellLayoutBoard { var remainingEmptySpaces = remainingEmptySpacesArg if (area.height() * area.width() <= 0) return board val width = getRandom(1, area.width() - 1) val height = getRandom(1, area.height() - 1) val width = getRandom(1, area.width()) val height = getRandom(1, area.height()) val x = area.left + getRandom(0, area.width() - width) val y = area.top + getRandom(0, area.height() - height) if (remainingEmptySpaces > 0) { Loading
tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt 0 → 100644 +179 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.celllayout import android.content.Context import android.graphics.Point import android.util.Log import android.view.View import androidx.core.view.get import androidx.test.core.app.ApplicationProvider import com.android.launcher3.CellLayout import com.android.launcher3.celllayout.board.CellLayoutBoard import com.android.launcher3.celllayout.board.IconPoint import com.android.launcher3.celllayout.board.PermutedBoardComparator import com.android.launcher3.celllayout.board.WidgetRect import com.android.launcher3.celllayout.testgenerator.RandomBoardGenerator import com.android.launcher3.util.ActivityContextWrapper import com.android.launcher3.views.DoubleShadowBubbleTextView import java.util.Random import org.junit.Assert import org.junit.Rule import org.junit.Test private class HotseatReorderTestCase( val startBoard: CellLayoutBoard, val endBoard: CellLayoutBoard ) { override fun toString(): String { return "$startBoard#endBoard:\n$endBoard" } } class HotseatReorderUnitTest { private val applicationContext: Context = ActivityContextWrapper(ApplicationProvider.getApplicationContext()) @JvmField @Rule var cellLayoutBuilder = UnitTestCellLayoutBuilderRule() /** * This test generates random CellLayout configurations and then try to reorder it and makes * sure the result is a valid board meaning it didn't remove any widget or icon. */ @Test fun generateValidTests() { val generator = Random(Companion.SEED.toLong()) for (i in 0 until Companion.TOTAL_OF_CASES_GENERATED) { // Using a new seed so that we can replicate the same test cases. val seed = generator.nextInt() Log.d(Companion.TAG, "Seed = $seed") val testCase: HotseatReorderTestCase = generateRandomTestCase(RandomBoardGenerator(Random(seed.toLong()))) Log.d(Companion.TAG, "testCase = $testCase") Assert.assertTrue( "invalid case $i", PermutedBoardComparator().compare(testCase.startBoard, testCase.endBoard) == 0 ) } } private fun addViewInCellLayout( cellLayout: CellLayout, cellX: Int, cellY: Int, spanX: Int, spanY: Int, isWidget: Boolean ) { val cell = if (isWidget) View(applicationContext) else DoubleShadowBubbleTextView(applicationContext) cell.layoutParams = CellLayoutLayoutParams(cellX, cellY, spanX, spanY) cellLayout.addViewToCellLayout( cell, -1, cell.id, cell.layoutParams as CellLayoutLayoutParams, true ) } private fun solve(board: CellLayoutBoard): CellLayout { val cl = cellLayoutBuilder.createCellLayout(board.width, board.height, false) // The views have to be sorted or the result can vary board.icons .map(IconPoint::getCoord) .sortedWith( Comparator.comparing { p: Any -> (p as Point).x } .thenComparing { p: Any -> (p as Point).y } ) .forEach { p -> addViewInCellLayout( cellLayout = cl, cellX = p.x, cellY = p.y, spanX = 1, spanY = 1, isWidget = false ) } board.widgets .sortedWith( Comparator.comparing(WidgetRect::getCellX).thenComparing(WidgetRect::getCellY) ) .forEach { widget -> addViewInCellLayout( cl, widget.cellX, widget.cellY, widget.spanX, widget.spanY, isWidget = true ) } if (cl.makeSpaceForHotseatMigration(true)) { commitTempPosition(cl) } return cl } private fun commitTempPosition(cellLayout: CellLayout) { val count = cellLayout.shortcutsAndWidgets.childCount for (i in 0 until count) { val params = cellLayout.shortcutsAndWidgets[i].layoutParams as CellLayoutLayoutParams params.cellX = params.tmpCellX params.cellY = params.tmpCellY } } private fun boardFromCellLayout(cellLayout: CellLayout): CellLayoutBoard { val views = mutableListOf<View>() for (i in 0 until cellLayout.shortcutsAndWidgets.childCount) { views.add(cellLayout.shortcutsAndWidgets.getChildAt(i)) } return CellLayoutTestUtils.viewsToBoard(views, cellLayout.countX, cellLayout.countY) } private fun generateRandomTestCase( boardGenerator: RandomBoardGenerator ): HotseatReorderTestCase { val width: Int = boardGenerator.getRandom(3, Companion.MAX_BOARD_SIZE) val height: Int = boardGenerator.getRandom(3, Companion.MAX_BOARD_SIZE) val targetWidth: Int = boardGenerator.getRandom(1, width - 2) val targetHeight: Int = boardGenerator.getRandom(1, height - 2) val board: CellLayoutBoard = boardGenerator.generateBoard(width, height, targetWidth * targetHeight) val finishBoard: CellLayoutBoard = boardFromCellLayout(solve(board)) return HotseatReorderTestCase(board, finishBoard) } companion object { private const val MAX_BOARD_SIZE = 13 /** * There is nothing special about this numbers, the random seed is just to be able to * reproduce the test cases and the height and width is a random number similar to what * users expect on their devices */ private const val SEED = -194162315 private const val TOTAL_OF_CASES_GENERATED = 300 private const val TAG = "HotseatReorderUnitTest" } }