如何为自定义SWT ColumnRatioLayout编写JUnit占比验证测试
问题修复与实现方案
一、现有问题排查
1. 自定义布局本身的bug
原ColumnRatioLayout里的calculatePercentAndConsumedPercent方法用了Java基本类型作为入参,基本类型是值传递,方法内部修改的参数不会同步到外部调用处,导致computeSize方法中的percent永远是0,计算逻辑完全失效。
2. 测试代码的问题
- 调用
_comp.computeSize时还没有添加子控件,空容器的计算结果自然是0 - 子控件是无内容的空
Composite,默认首选尺寸为0,没有参考意义 - 手动给子控件调用
setSize会被布局管理器的自动计算覆盖,属于无效操作 - 没有手动触发布局重算,SWT布局不会自动立刻生效
二、修复后的实现代码
1. 修复ColumnRatioLayout
public class ColumnRatioLayout extends Layout { int[] percentages; public ColumnRatioLayout(int... percentages) { this.percentages = percentages; } @Override protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { Control[] children = composite.getChildren(); int height = hHint; int width = wHint; int consumedPercent = 0; for (int i = 0; i < children.length; i++) { int percent; // 直接内联逻辑,避免值传递问题 if (i >= percentages.length) { percent = (100 - consumedPercent) / (children.length - percentages.length); } else { percent = percentages[i]; consumedPercent += percent; } Point childSize = children[i].computeSize(wHint == -1 ? -1 : wHint * percent / 100, hHint); if (wHint == SWT.DEFAULT) { width = Math.max(width, childSize.x * 100 / percent); } if (hHint == SWT.DEFAULT) { height = Math.max(height, childSize.y); } } return new Point(Math.max(width, 0), Math.max(height, 0)); } @Override protected void layout(Composite composite, boolean flushCache) { Control[] children = composite.getChildren(); Rectangle available = composite.getClientArea(); int x = available.x; int consumedPercent = 0; for (int i = 0; i < children.length - 1; i++) { int percent; if (i >= percentages.length) { percent = (100 - consumedPercent) / (children.length - percentages.length); } else { percent = percentages[i]; consumedPercent += percent; } int w = available.width * percent / 100; children[i].setBounds(x, available.y, w, available.height); x += w; } if (children.length > 0) { children[children.length - 1].setBounds(x, available.y, available.width - (x - available.x), available.height); } } }
2. 正确的JUnit测试代码
import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class ColumnRatioLayoutTest { private static Display _display; private static Shell _shell; private static Composite _comp; @BeforeAll public static void setUpAll() { _display = new Display(); _shell = new Shell(_display); _comp = new Composite(_shell, SWT.NONE); // 给父容器设置固定宽度1000px,方便按比例计算验证 _comp.setSize(1000, 200); } @Test public void setLayoutTest() { // 先添加子控件 Composite comp1 = new Composite(_comp, SWT.NONE); comp1.setLayout(new FillLayout()); Composite comp2 = new Composite(_comp, SWT.NONE); comp2.setLayout(new FillLayout()); // 设置布局后触发重算 int[] colRatio = {20, 80}; ColumnRatioLayout colLayout = new ColumnRatioLayout(colRatio); _comp.setLayout(colLayout); _comp.layout(); // 手动触发布局计算 // 获取子控件尺寸验证比例 Point size1 = comp1.getSize(); Point size2 = comp2.getSize(); System.out.println("Comp1宽度:" + size1.x); System.out.println("Comp2宽度:" + size2.x); // 1000px的20%是200,80%是800,允许1px的整数计算误差 assertEquals(200, size1.x, 1); assertEquals(800, size2.x, 1); // 验证宽度是4倍关系 assertEquals(4, size2.x / (double)size1.x, 0.1); } }
三、验证逻辑说明
测试用例给父容器设置了固定宽度1000px,按照20:80的比例,子控件宽度应该分别为200px和800px,刚好满足4倍的比例关系,通过JUnit断言直接验证结果即可。
内容的提问来源于stack exchange,提问作者Stefan Hristov
相关产品推荐
相关产品推荐

