Merge "Adding unit test to ReorderAlgorithm" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
db982003b2
@@ -85,6 +85,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
public class CellLayout extends ViewGroup {
|
public class CellLayout extends ViewGroup {
|
||||||
@@ -891,7 +892,7 @@ public class CellLayout extends ViewGroup {
|
|||||||
*
|
*
|
||||||
* @param result Array of 2 ints to hold the x and y coordinate of the point
|
* @param result Array of 2 ints to hold the x and y coordinate of the point
|
||||||
*/
|
*/
|
||||||
void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
|
public void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
|
||||||
cellToRect(cellX, cellY, spanX, spanY, mTempRect);
|
cellToRect(cellX, cellY, spanX, spanY, mTempRect);
|
||||||
result[0] = mTempRect.centerX();
|
result[0] = mTempRect.centerX();
|
||||||
result[1] = mTempRect.centerY();
|
result[1] = mTempRect.centerY();
|
||||||
@@ -2340,7 +2341,16 @@ public class CellLayout extends ViewGroup {
|
|||||||
}
|
}
|
||||||
Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
|
Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
|
||||||
Rect r1 = new Rect();
|
Rect r1 = new Rect();
|
||||||
for (View child: solution.map.keySet()) {
|
// The views need to be sorted so that the results are deterministic on the views positions
|
||||||
|
// and not by the views hash which is "random".
|
||||||
|
// The views are sorted twice, once for the X position and a second time for the Y position
|
||||||
|
// to ensure same order everytime.
|
||||||
|
Comparator comparator = Comparator.comparing(view ->
|
||||||
|
((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX())
|
||||||
|
.thenComparing(view ->
|
||||||
|
((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY());
|
||||||
|
List<View> views = solution.map.keySet().stream().sorted(comparator).toList();
|
||||||
|
for (View child : views) {
|
||||||
if (child == ignoreView) continue;
|
if (child == ignoreView) continue;
|
||||||
CellAndSpan c = solution.map.get(child);
|
CellAndSpan c = solution.map.get(child);
|
||||||
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
|
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -171,6 +171,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HashSet<Character> mUsedWidgetTypes = new HashSet<>();
|
||||||
|
|
||||||
static final int INFINITE = 99999;
|
static final int INFINITE = 99999;
|
||||||
|
|
||||||
char[][] mWidget = new char[30][30];
|
char[][] mWidget = new char[30][30];
|
||||||
@@ -182,6 +184,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
|
|||||||
|
|
||||||
WidgetRect mMain = null;
|
WidgetRect mMain = null;
|
||||||
|
|
||||||
|
int mWidth, mHeight;
|
||||||
|
|
||||||
CellLayoutBoard() {
|
CellLayoutBoard() {
|
||||||
for (int x = 0; x < mWidget.length; x++) {
|
for (int x = 0; x < mWidget.length; x++) {
|
||||||
for (int y = 0; y < mWidget[0].length; y++) {
|
for (int y = 0; y < mWidget[0].length; y++) {
|
||||||
@@ -190,6 +194,17 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CellLayoutBoard(int width, int height) {
|
||||||
|
mWidget = new char[width][height];
|
||||||
|
this.mWidth = width;
|
||||||
|
this.mHeight = height;
|
||||||
|
for (int x = 0; x < mWidget.length; x++) {
|
||||||
|
for (int y = 0; y < mWidget[0].length; y++) {
|
||||||
|
mWidget[x][y] = CellType.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<WidgetRect> getWidgets() {
|
public List<WidgetRect> getWidgets() {
|
||||||
return mWidgetsRects;
|
return mWidgetsRects;
|
||||||
}
|
}
|
||||||
@@ -256,6 +271,16 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
|
|||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private char getNextWidgetType() {
|
||||||
|
for (char type = 'a'; type <= 'z'; type++) {
|
||||||
|
if (type == 'i') continue;
|
||||||
|
if (mUsedWidgetTypes.contains(type)) continue;
|
||||||
|
mUsedWidgetTypes.add(type);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
return 'z';
|
||||||
|
}
|
||||||
|
|
||||||
public void addWidget(int x, int y, int spanX, int spanY, char type) {
|
public void addWidget(int x, int y, int spanX, int spanY, char type) {
|
||||||
Rect rect = new Rect(x, y + spanY - 1, x + spanX - 1, y);
|
Rect rect = new Rect(x, y + spanY - 1, x + spanX - 1, y);
|
||||||
removeOverlappingItems(rect);
|
removeOverlappingItems(rect);
|
||||||
@@ -268,6 +293,10 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addWidget(int x, int y, int spanX, int spanY) {
|
||||||
|
addWidget(x, y, spanX, spanY, getNextWidgetType());
|
||||||
|
}
|
||||||
|
|
||||||
public void addIcon(int x, int y) {
|
public void addIcon(int x, int y) {
|
||||||
Point iconCoord = new Point(x, y);
|
Point iconCoord = new Point(x, y);
|
||||||
removeOverlappingItems(iconCoord);
|
removeOverlappingItems(iconCoord);
|
||||||
@@ -367,6 +396,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
board.mHeight = lines.length;
|
||||||
|
board.mWidth = lines[0].length();
|
||||||
board.mWidgetsRects = getRects(board.mWidget);
|
board.mWidgetsRects = getRects(board.mWidget);
|
||||||
board.mWidgetsRects.forEach(widgetRect -> {
|
board.mWidgetsRects.forEach(widgetRect -> {
|
||||||
if (widgetRect.mType == CellType.MAIN_WIDGET) {
|
if (widgetRect.mType == CellType.MAIN_WIDGET) {
|
||||||
@@ -380,6 +411,11 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
|
|||||||
|
|
||||||
public String toString(int maxX, int maxY) {
|
public String toString(int maxX, int maxY) {
|
||||||
StringBuilder s = new StringBuilder();
|
StringBuilder s = new StringBuilder();
|
||||||
|
s.append("board: ");
|
||||||
|
s.append(maxX);
|
||||||
|
s.append("x");
|
||||||
|
s.append(maxY);
|
||||||
|
s.append("\n");
|
||||||
maxX = Math.min(maxX, mWidget.length);
|
maxX = Math.min(maxX, mWidget.length);
|
||||||
maxY = Math.min(maxY, mWidget[0].length);
|
maxY = Math.min(maxY, mWidget[0].length);
|
||||||
for (int y = 0; y < maxY; y++) {
|
for (int y = 0; y < maxY; y++) {
|
||||||
@@ -391,6 +427,11 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
|
|||||||
return s.toString();
|
return s.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toString(mWidth, mHeight);
|
||||||
|
}
|
||||||
|
|
||||||
public static List<CellLayoutBoard> boardListFromString(String boardsStr) {
|
public static List<CellLayoutBoard> boardListFromString(String boardsStr) {
|
||||||
String[] lines = boardsStr.split("\n");
|
String[] lines = boardsStr.split("\n");
|
||||||
ArrayList<String> individualBoards = new ArrayList<>();
|
ArrayList<String> individualBoards = new ArrayList<>();
|
||||||
@@ -410,4 +451,12 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
|
|||||||
}
|
}
|
||||||
return boards;
|
return boards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return mWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return mHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ import com.android.launcher3.Launcher;
|
|||||||
import com.android.launcher3.views.DoubleShadowBubbleTextView;
|
import com.android.launcher3.views.DoubleShadowBubbleTextView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class CellLayoutTestUtils {
|
public class CellLayoutTestUtils {
|
||||||
|
|
||||||
public static ArrayList<CellLayoutBoard> workspaceToBoards(Launcher launcher) {
|
public static ArrayList<CellLayoutBoard> workspaceToBoards(Launcher launcher) {
|
||||||
ArrayList<CellLayoutBoard> boards = new ArrayList<>();
|
ArrayList<CellLayoutBoard> boards = new ArrayList<>();
|
||||||
int widgetCount = 0;
|
|
||||||
for (CellLayout cellLayout : launcher.getWorkspace().mWorkspaceScreens) {
|
for (CellLayout cellLayout : launcher.getWorkspace().mWorkspaceScreens) {
|
||||||
|
|
||||||
int count = cellLayout.getShortcutsAndWidgets().getChildCount();
|
int count = cellLayout.getShortcutsAndWidgets().getChildCount();
|
||||||
@@ -52,11 +52,29 @@ public class CellLayoutTestUtils {
|
|||||||
} else {
|
} else {
|
||||||
// is widget
|
// is widget
|
||||||
board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan,
|
board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan,
|
||||||
params.cellVSpan, (char) ('a' + widgetCount));
|
params.cellVSpan);
|
||||||
widgetCount++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return boards;
|
return boards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CellLayoutBoard viewsToBoard(List<View> views, int width, int height) {
|
||||||
|
CellLayoutBoard board = new CellLayoutBoard();
|
||||||
|
board.mWidth = width;
|
||||||
|
board.mHeight = height;
|
||||||
|
|
||||||
|
for (View callView : views) {
|
||||||
|
CellLayoutLayoutParams params = (CellLayoutLayoutParams) callView.getLayoutParams();
|
||||||
|
// is icon
|
||||||
|
if (callView instanceof DoubleShadowBubbleTextView) {
|
||||||
|
board.addIcon(params.getCellX(), params.getCellY());
|
||||||
|
} else {
|
||||||
|
// is widget
|
||||||
|
board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan,
|
||||||
|
params.cellVSpan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return board;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.launcher3.CellLayout;
|
||||||
|
import com.android.launcher3.DeviceProfile;
|
||||||
|
import com.android.launcher3.InvariantDeviceProfile;
|
||||||
|
import com.android.launcher3.util.ActivityContextWrapper;
|
||||||
|
import com.android.launcher3.views.DoubleShadowBubbleTextView;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ReorderAlgorithmUnitTest {
|
||||||
|
private Context mApplicationContext;
|
||||||
|
|
||||||
|
private int mPrevNumColumns, mPrevNumRows;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllCases() throws IOException {
|
||||||
|
List<ReorderAlgorithmUnitTestCase> testCases = getTestCases(
|
||||||
|
"ReorderAlgorithmUnitTest/reorder_algorithm_test_cases");
|
||||||
|
mApplicationContext = new ActivityContextWrapper(getApplicationContext());
|
||||||
|
List<Integer> failingCases = new ArrayList<>();
|
||||||
|
for (int i = 0; i < testCases.size(); i++) {
|
||||||
|
try {
|
||||||
|
evaluateTestCase(testCases.get(i));
|
||||||
|
} catch (AssertionError e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
failingCases.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals("Some test cases failed " + Arrays.toString(failingCases.toArray()), 0,
|
||||||
|
failingCases.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addViewInCellLayout(CellLayout cellLayout, int cellX, int cellY, int spanX,
|
||||||
|
int spanY, boolean isWidget) {
|
||||||
|
View cell = isWidget ? new View(mApplicationContext) : new DoubleShadowBubbleTextView(
|
||||||
|
mApplicationContext);
|
||||||
|
cell.setLayoutParams(new CellLayoutLayoutParams(cellX, cellY, spanX, spanY));
|
||||||
|
cellLayout.addViewToCellLayout(cell, -1, cell.getId(),
|
||||||
|
(CellLayoutLayoutParams) cell.getLayoutParams(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CellLayout createCellLayout(int width, int height) {
|
||||||
|
Context c = mApplicationContext;
|
||||||
|
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c);
|
||||||
|
// modify the device profile.
|
||||||
|
dp.inv.numColumns = width;
|
||||||
|
dp.inv.numRows = height;
|
||||||
|
|
||||||
|
CellLayout cl = new CellLayout(getWrappedContext(c, dp));
|
||||||
|
// I put a very large number for width and height so that all the items can fit, it doesn't
|
||||||
|
// need to be exact, just bigger than the sum of cell border
|
||||||
|
cl.measure(View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY),
|
||||||
|
View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY));
|
||||||
|
|
||||||
|
cl.measure(View.MeasureSpec.makeMeasureSpec(cl.getDesiredWidth(), View.MeasureSpec.EXACTLY),
|
||||||
|
View.MeasureSpec.makeMeasureSpec(cl.getDesiredHeight(), View.MeasureSpec.EXACTLY));
|
||||||
|
return cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Context getWrappedContext(Context context, DeviceProfile dp) {
|
||||||
|
return new ActivityContextWrapper(context) {
|
||||||
|
public DeviceProfile getDeviceProfile() {
|
||||||
|
return dp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public CellLayout.ItemConfiguration solve(CellLayoutBoard board, int x, int y, int spanX,
|
||||||
|
int spanY, int minSpanX, int minSpanY) {
|
||||||
|
CellLayout cl = createCellLayout(board.getWidth(), board.getHeight());
|
||||||
|
|
||||||
|
// The views have to be sorted or the result can vary
|
||||||
|
board.getIcons()
|
||||||
|
.stream()
|
||||||
|
.map(CellLayoutBoard.IconPoint::getCoord)
|
||||||
|
.sorted(Comparator.comparing(p -> ((Point) p).x).thenComparing(p -> ((Point) p).y))
|
||||||
|
.forEach(p -> addViewInCellLayout(cl, p.x, p.y, 1, 1, false));
|
||||||
|
board.getWidgets().stream()
|
||||||
|
.sorted(Comparator.comparing(CellLayoutBoard.WidgetRect::getCellX)
|
||||||
|
.thenComparing(CellLayoutBoard.WidgetRect::getCellY))
|
||||||
|
.forEach(widget -> addViewInCellLayout(cl, widget.getCellX(), widget.getCellY(),
|
||||||
|
widget.getSpanX(), widget.getSpanY(), true));
|
||||||
|
|
||||||
|
int[] testCaseXYinPixels = new int[2];
|
||||||
|
cl.regionToCenterPoint(x, y, spanX, spanY, testCaseXYinPixels);
|
||||||
|
CellLayout.ItemConfiguration solution = cl.createReorderAlgorithm().calculateReorder(
|
||||||
|
testCaseXYinPixels[0], testCaseXYinPixels[1], minSpanX, minSpanY, spanX, spanY,
|
||||||
|
null);
|
||||||
|
if (solution == null) {
|
||||||
|
solution = new CellLayout.ItemConfiguration();
|
||||||
|
solution.isSolution = false;
|
||||||
|
}
|
||||||
|
return solution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CellLayoutBoard boardFromSolution(CellLayout.ItemConfiguration solution, int width,
|
||||||
|
int height) {
|
||||||
|
// Update the views with solution value
|
||||||
|
solution.map.forEach((key, val) -> key.setLayoutParams(
|
||||||
|
new CellLayoutLayoutParams(val.cellX, val.cellY, val.spanX, val.spanY)));
|
||||||
|
CellLayoutBoard board = CellLayoutTestUtils.viewsToBoard(
|
||||||
|
new ArrayList<>(solution.map.keySet()), width, height);
|
||||||
|
board.addWidget(solution.cellX, solution.cellY, solution.spanX, solution.spanY,
|
||||||
|
'z');
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void evaluateTestCase(ReorderAlgorithmUnitTestCase testCase) {
|
||||||
|
CellLayout.ItemConfiguration solution = solve(testCase.startBoard, testCase.x,
|
||||||
|
testCase.y, testCase.spanX, testCase.spanY, testCase.minSpanX,
|
||||||
|
testCase.minSpanY);
|
||||||
|
assertEquals("should be a valid solution", solution.isSolution,
|
||||||
|
testCase.isValidSolution);
|
||||||
|
if (testCase.isValidSolution) {
|
||||||
|
CellLayoutBoard finishBoard = boardFromSolution(solution,
|
||||||
|
testCase.startBoard.getWidth(), testCase.startBoard.getHeight());
|
||||||
|
assertTrue("End result and test case result board doesn't match ",
|
||||||
|
finishBoard.compareTo(testCase.endBoard) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void storePreviousValues() {
|
||||||
|
Context c = new ActivityContextWrapper(getApplicationContext());
|
||||||
|
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c);
|
||||||
|
mPrevNumColumns = dp.inv.numColumns;
|
||||||
|
mPrevNumRows = dp.inv.numColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void restorePreviousValues() {
|
||||||
|
Context c = new ActivityContextWrapper(getApplicationContext());
|
||||||
|
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c);
|
||||||
|
dp.inv.numColumns = mPrevNumColumns;
|
||||||
|
dp.inv.numRows = mPrevNumRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedMethod")
|
||||||
|
/**
|
||||||
|
* Utility function used to generate all the test cases
|
||||||
|
*/
|
||||||
|
private ReorderAlgorithmUnitTestCase generateRandomTestCase() {
|
||||||
|
ReorderAlgorithmUnitTestCase testCase = new ReorderAlgorithmUnitTestCase();
|
||||||
|
|
||||||
|
int width = getRandom(3, 8);
|
||||||
|
int height = getRandom(3, 8);
|
||||||
|
|
||||||
|
int targetWidth = getRandom(1, width - 2);
|
||||||
|
int targetHeight = getRandom(1, height - 2);
|
||||||
|
|
||||||
|
int minTargetWidth = getRandom(1, targetWidth);
|
||||||
|
int minTargetHeight = getRandom(1, targetHeight);
|
||||||
|
|
||||||
|
int x = getRandom(0, width - targetWidth);
|
||||||
|
int y = getRandom(0, height - targetHeight);
|
||||||
|
|
||||||
|
CellLayoutBoard board = generateBoard(new CellLayoutBoard(width, height),
|
||||||
|
new Rect(0, 0, width, height), targetWidth * targetHeight);
|
||||||
|
|
||||||
|
CellLayout.ItemConfiguration solution = solve(board, x, y, targetWidth, targetHeight,
|
||||||
|
minTargetWidth, minTargetHeight);
|
||||||
|
|
||||||
|
CellLayoutBoard finishBoard = solution.isSolution ? boardFromSolution(solution,
|
||||||
|
board.getWidth(), board.getHeight()) : new CellLayoutBoard(board.getWidth(),
|
||||||
|
board.getHeight());
|
||||||
|
|
||||||
|
|
||||||
|
testCase.startBoard = board;
|
||||||
|
testCase.endBoard = finishBoard;
|
||||||
|
testCase.isValidSolution = solution.isSolution;
|
||||||
|
testCase.x = x;
|
||||||
|
testCase.y = y;
|
||||||
|
testCase.spanX = targetWidth;
|
||||||
|
testCase.spanY = targetHeight;
|
||||||
|
testCase.minSpanX = minTargetWidth;
|
||||||
|
testCase.minSpanY = minTargetHeight;
|
||||||
|
testCase.type = solution.area() == 1 ? "icon" : "widget";
|
||||||
|
|
||||||
|
return testCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getRandom(int start, int end) {
|
||||||
|
int random = end == 0 ? 0 : new Random().nextInt(end);
|
||||||
|
return start + random;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CellLayoutBoard generateBoard(CellLayoutBoard board, Rect area,
|
||||||
|
int emptySpaces) {
|
||||||
|
if (area.height() * area.width() <= 0) return board;
|
||||||
|
|
||||||
|
int width = getRandom(1, area.width() - 1);
|
||||||
|
int height = getRandom(1, area.height() - 1);
|
||||||
|
|
||||||
|
int x = area.left + getRandom(0, area.width() - width);
|
||||||
|
int y = area.top + getRandom(0, area.height() - height);
|
||||||
|
|
||||||
|
if (emptySpaces > 0) {
|
||||||
|
emptySpaces -= width * height;
|
||||||
|
} else if (width * height > 1) {
|
||||||
|
board.addWidget(x, y, width, height);
|
||||||
|
} else {
|
||||||
|
board.addIcon(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateBoard(board,
|
||||||
|
new Rect(area.left, area.top, area.right, y), emptySpaces);
|
||||||
|
generateBoard(board,
|
||||||
|
new Rect(area.left, y, x, area.bottom), emptySpaces);
|
||||||
|
generateBoard(board,
|
||||||
|
new Rect(x, y + height, area.right, area.bottom), emptySpaces);
|
||||||
|
generateBoard(board,
|
||||||
|
new Rect(x + width, y, area.right, y + height), emptySpaces);
|
||||||
|
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ReorderAlgorithmUnitTestCase> getTestCases(String testPath)
|
||||||
|
throws IOException {
|
||||||
|
List<ReorderAlgorithmUnitTestCase> cases = new ArrayList<>();
|
||||||
|
Iterator<CellLayoutTestCaseReader.TestSection> iterableSection =
|
||||||
|
CellLayoutTestCaseReader.readFromFile(testPath).parse().iterator();
|
||||||
|
while (iterableSection.hasNext()) {
|
||||||
|
cases.add(ReorderAlgorithmUnitTestCase.readNextCase(iterableSection));
|
||||||
|
}
|
||||||
|
return cases;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a test case for {@code ReorderAlgorithmUnitTest}. The test cases are generated from
|
||||||
|
* text, an example of a test is the following:
|
||||||
|
*
|
||||||
|
* board: 10x8
|
||||||
|
* aaaaaaaaai
|
||||||
|
* bbbbbcciii
|
||||||
|
* ---------f
|
||||||
|
* ---------f
|
||||||
|
* ---------f
|
||||||
|
* ---------i
|
||||||
|
* iiddddddii
|
||||||
|
* iieeiiiiii
|
||||||
|
* arguments: 2 5 7 1 3 1 widget valid
|
||||||
|
* board: 10x8
|
||||||
|
* bbbbbbbbbi
|
||||||
|
* eeeeecciii
|
||||||
|
* ---------a
|
||||||
|
* ---------a
|
||||||
|
* ---------a
|
||||||
|
* --zzzzzzzi
|
||||||
|
* iiddddddii
|
||||||
|
* iiffiiiiii
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This represents a Workspace boards and a dragged widget that wants to be dropped on the
|
||||||
|
* workspace. The endBoard represents the result from such drag
|
||||||
|
* The first board is the startBoard, the arguments are as follow: cellX, cellY, widget spanX,
|
||||||
|
* widget spanY, minimum spanX, minimum spanX, type of object being drag (icon, widget, folder ),
|
||||||
|
* if the resulting board is a valid solution or not reorder was found.
|
||||||
|
*
|
||||||
|
* For more information on how to read the board please go to the text file
|
||||||
|
* reorder_algorithm_test_cases
|
||||||
|
*/
|
||||||
|
public class ReorderAlgorithmUnitTestCase {
|
||||||
|
|
||||||
|
CellLayoutBoard startBoard;
|
||||||
|
|
||||||
|
int x, y, spanX, spanY, minSpanX, minSpanY;
|
||||||
|
String type;
|
||||||
|
boolean isValidSolution;
|
||||||
|
CellLayoutBoard endBoard;
|
||||||
|
|
||||||
|
public static ReorderAlgorithmUnitTestCase readNextCase(
|
||||||
|
Iterator<CellLayoutTestCaseReader.TestSection> sections) {
|
||||||
|
ReorderAlgorithmUnitTestCase testCase = new ReorderAlgorithmUnitTestCase();
|
||||||
|
CellLayoutTestCaseReader.Board startBoard =
|
||||||
|
(CellLayoutTestCaseReader.Board) sections.next();
|
||||||
|
testCase.startBoard = CellLayoutBoard.boardFromString(startBoard.board);
|
||||||
|
CellLayoutTestCaseReader.Arguments arguments =
|
||||||
|
(CellLayoutTestCaseReader.Arguments) sections.next();
|
||||||
|
testCase.x = Integer.parseInt(arguments.arguments[0]);
|
||||||
|
testCase.y = Integer.parseInt(arguments.arguments[1]);
|
||||||
|
testCase.spanX = Integer.parseInt(arguments.arguments[2]);
|
||||||
|
testCase.spanY = Integer.parseInt(arguments.arguments[3]);
|
||||||
|
testCase.minSpanX = Integer.parseInt(arguments.arguments[4]);
|
||||||
|
testCase.minSpanY = Integer.parseInt(arguments.arguments[5]);
|
||||||
|
testCase.type = arguments.arguments[6];
|
||||||
|
testCase.isValidSolution = arguments.arguments[7].compareTo("valid") == 0;
|
||||||
|
|
||||||
|
CellLayoutTestCaseReader.Board endBoard = (CellLayoutTestCaseReader.Board) sections.next();
|
||||||
|
testCase.endBoard = CellLayoutBoard.boardFromString(endBoard.board);
|
||||||
|
return testCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CellLayoutBoard getStartBoard() {
|
||||||
|
return startBoard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(int x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(int y) {
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSpanX() {
|
||||||
|
return spanX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpanX(int spanX) {
|
||||||
|
this.spanX = spanX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSpanY() {
|
||||||
|
return spanY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpanY(int spanY) {
|
||||||
|
this.spanY = spanY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidSolution() {
|
||||||
|
return isValidSolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidSolution(boolean validSolution) {
|
||||||
|
isValidSolution = validSolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CellLayoutBoard getEndBoard() {
|
||||||
|
return endBoard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndBoard(CellLayoutBoard endBoard) {
|
||||||
|
this.endBoard = endBoard;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String valid = isValidSolution ? "valid" : "invalid";
|
||||||
|
return startBoard + "arguments: " + x + " " + y + " " + spanX + " " + spanY + " " + minSpanX
|
||||||
|
+ " " + minSpanY + " " + type + " " + valid + "\n" + endBoard;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user