blob: 8bc5b20cf071c7f3e45f9e32bec55edad21cfc70 [file] [log] [blame]
/*
* Copyright (C) 2016 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 android.support.constraint.solver;
import android.support.constraint.solver.scout.Scout;
import android.support.constraint.solver.widgets.ConstraintWidget;
import android.support.constraint.solver.widgets.ConstraintWidgetContainer;
import android.support.constraint.solver.widgets.Guideline;
import org.testng.annotations.Test;
import java.awt.*;
import java.util.ArrayList;
import java.util.Random;
import static org.testng.Assert.assertTrue;
/**
* This test creates a random set of non overlapping rectangles uses the scout
* to add a sequence of constraints. Verify that the constraint engine will then layout the
* rectangles to within 12 pixels.
* It uses
*/
public class RandomLayoutTest {
private static final int ALLOWED_POSITION_ERROR = 12;
public static final int MIN_WIDTH = 100;
public static final int MIN_HEIGHT = 40;
public static final int MIN_GAP = 40;
public static final int MAX_TRIES = 100;
public static final int LAYOUT_WIDTH = 1024;
public static final int LAYOUT_HEIGHT = 512;
public static final int MAX_WIDGETS = 20;
public static final int PERCENT_BIG_WIDGETS = 70;
public static final int LOOP_FOR = 1000;
/**
* Create a collection of rectangles
*
* @param count the number of rectangles to try and generate
* @param sizeRatio 0 = all small ones, 100 = all big ones
* @param width the width of the bounding rectangle
* @param height the height of the bounding rectangle
* @return
*/
static ArrayList<Rectangle> random(long seed, int count, int sizeRatio, int width, int height) {
ArrayList<Rectangle> recs = new ArrayList<Rectangle>();
int minWidth = MIN_WIDTH;
int minHeight = MIN_HEIGHT;
int minGap = MIN_GAP;
int gapBy2 = MIN_GAP * 2;
Random rand = new Random(seed);
Rectangle test = new Rectangle();
for (int i = 0; i < count; i++) {
Rectangle rn = new Rectangle();
boolean found = false;
int attempt = 0;
while (!found) {
if (rand.nextInt(100) < sizeRatio) {
rn.x = rand.nextInt(width - minWidth - gapBy2) + minGap;
rn.y = rand.nextInt(height - minHeight - gapBy2) + minGap;
rn.width = minWidth + rand.nextInt(width - rn.x - minWidth - minGap);
rn.height = minHeight + rand.nextInt(height - rn.y - minHeight - minGap);
} else {
rn.x = rand.nextInt(width - minWidth - gapBy2) + minGap;
rn.y = rand.nextInt(height - minHeight - gapBy2) + minGap;
rn.width = minWidth;
rn.height = minHeight;
}
test.x = rn.x - minGap;
test.y = rn.y - minGap;
test.width = rn.width + gapBy2;
test.height = rn.height + gapBy2;
found = true;
int size = recs.size();
for (int j = 0; j < size; j++) {
if (recs.get(j).intersects(test)) {
found = false;
break;
}
}
attempt++;
if (attempt > MAX_TRIES) {
break;
}
}
if (found) {
recs.add(rn);
}
}
return recs;
}
@Test
void run() {
Random r = new Random(4567890);
for (int test = 0; test < LOOP_FOR; test++) {
Long seed = r.nextLong();
System.out.println("seed = " + seed);
ArrayList<Rectangle> list = random(seed, MAX_WIDGETS, PERCENT_BIG_WIDGETS, LAYOUT_WIDTH, LAYOUT_HEIGHT);
ConstraintWidgetContainer root = new ConstraintWidgetContainer(0, 0, LAYOUT_WIDTH, LAYOUT_HEIGHT);
root.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
root.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
root.setWidth(LAYOUT_WIDTH);
root.setHeight(LAYOUT_HEIGHT);
int k = 0;
for (Rectangle rec : list) {
ConstraintWidget widget = new ConstraintWidget();
widget.setType("TextView");
String text = ("TextView" + k++);
widget.setDebugName(text);
widget.setOrigin(rec.x, rec.y);
widget.setWidth(widget.getMinWidth());
widget.setHeight(widget.getMinHeight());
widget.setDrawWidth(widget.getWidth());
widget.setDrawHeight(widget.getHeight());
widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.WRAP_CONTENT);
widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.WRAP_CONTENT);
root.add(widget);
widget.setX(rec.x);
widget.setY(rec.y);
if (widget.getMinWidth() < rec.width) {
widget.setMinWidth(rec.width);
}
if (widget.getMinHeight() < rec.height) {
widget.setMinHeight(rec.height);
}
widget.setDimension(rec.width, rec.height);
widget.setWrapHeight(rec.height);
widget.setWrapHeight(rec.width);
}
ArrayList<ConstraintWidget> widgetList = root.getChildren();
Scout.inferConstraints(root);
for (ConstraintWidget widget : widgetList) {
widget.setDimension(10, 10);
widget.setOrigin(10, 10);
}
boolean allOk = true;
root.layout();
String layout = "\n";
boolean ok = true;
for (int i = 0; i < widgetList.size(); i++) {
ConstraintWidget widget = widgetList.get(i);
Rectangle rect = list.get(i);
allOk &= ok = isSame(dim(widget), dim(rect));
layout += rightPad(dim(widget), 15) + ((ok) ? " == " : " != ") + dim(rect) + "\n";
}
assertTrue(allOk, layout);
}
}
/**
* Compare two string containing comer separated integers
*
* @param a
* @param b
* @return
*/
private boolean isSame(String a, String b) {
if (a == null || b == null) {
return false;
}
String[] a_split = a.split(",");
String[] b_split = b.split(",");
if (a_split.length != b_split.length) {
return false;
}
for (int i = 0; i < a_split.length; i++) {
if (a_split[i].length() == 0) {
return false;
}
int error = ALLOWED_POSITION_ERROR;
if (b_split[i].startsWith("+")) {
error += 10;
}
int a_value = Integer.parseInt(a_split[i]);
int b_value = Integer.parseInt(b_split[i]);
if (Math.abs(a_value - b_value) > error) {
return false;
}
}
return true;
}
private static String rightPad(String s, int n) {
s = s + new String(new byte[n]).replace('\0', ' ');
return s.substring(0, n);
}
String dim(Rectangle r) {
return r.x + "," + r.y + "," + r.width + "," + r.height;
}
String dim(ConstraintWidget w) {
if (w instanceof Guideline) {
return w.getLeft() + "," + w.getTop() + "," + 0 + "," + 0;
}
if (w.getVisibility() == ConstraintWidget.GONE) {
return 0 + "," + 0 + "," + 0 + "," + 0;
}
return w.getLeft() + "," + w.getTop() + "," + w.getWidth() + "," + w.getHeight();
}
}