Java-如何对TimSort和“违反常规合同”进行单元测试

gen*_*ons 5 java unit-testing timsort

此问题与“比较方法违反其一般合同!”有关。-TimSort和GridLayout 以及其他一些类似的“违反通用合同”问题。我的问题特别与Ceekay在页面底部有关“如何测试TimSort实现的答案”有关。就我而言,我已修复了由于对称性违规而导致我出现此问题的应用程序错误,但是我无法创建一个单元测试来暴露该违规(如果将来对修补程序进行注释或未修正)。

public class TickNumber implements Comparable<TickNumber> {
    protected String zone;
    protected String track;
}
public class GisTickNumber extends TickNumber implements Comparable<TickNumber> {
    private String suffix;
}
Run Code Online (Sandbox Code Playgroud)

我省略了所有实现细节,但是基本上,刻度号是4位数字,其中前两位是区域,后两位是磁道。GisTickNumbers在“区域”或“跟踪”字段中可以包含字母字符,并且可以选择包含一个或两个字符的字母后缀。有效刻度是该范围内的所有整数[0000, 9999](即使表示为字符串)。所有有效的刻度线编号都是有效的Gis刻度线编号,但有效的Gis刻度线也可能类似于A912, R123, 0123G, A346*

我的对称性违反是在GisTick中compareTo,我考虑了可能的后缀,但在普通Tick中,compareTo我没有考虑它。因此,如果“ this”是一个0000Tick,而“ that”是一个0000*Gis Tick,0000.compareTo(0000*)则将返回0。而如果“ this”是一个0000*Gis Tick,并且“ that”是一个0000Tick,0000*.compareTo(0000)则将返回1。护罩拉回)

根据Ceekay在对链接问题的回答中,

  1. 创建包含32个或更多对象的列表。
  2. 在该列表中,需要[是]两个或多个运行。
  3. 每次运行必须包含3个或更多对象。

满足这三个条件后,就可以开始测试此失败了。

我相信我已经为单元测试设置了这样的TickNumber(和GisTickNumber)对象列表,但我似乎无法使测试失败。即使列表中有100个以上的对象,也有两个以上的运行,每个运行包含约10个对象。因此,我的问题是,Collections.sort(testList)由于“一般(对称)合同违约”而导致调用失败,被测对象列表还需要满足哪些其他特征?

  • 是的,在运行预期会失败的单元测试之前,我已注释掉该修复程序。

gen*_*ons -1

已解决:
我最终调试到一个断点,在该断点中我可以查看toString()列表中对象的表示形式并进行排序,然后能够从其余数据中提取 TickNumber 信息,并最终在单元测试中使用提取的数据。最后,我返回并删除了列表项,直到我制作了一个似乎满足触发对称性相关的“一般合同违规”的“最低要求”的列表。

我不确定如何将我的具体解决方案概括为列表必须满足的通用特征,以便触发 TimsSort 和这种“一般合同违规”。但这里...

  • 该列表必须包含 64 个元素 (49 + 1 + 12 + 1 + 1)
  • 该列表必须包含 50 个元素,其中 50 个元素中有 49 个元素的比较结果为 0(即比较匹配)
    • 在该“匹配运行”的前半部分中,必须有 1 个元素在该运行中的所有其他元素之前排序(比较时该运行中的所有其他元素都匹配),并且该单个奇数元素也必须“对称不匹配”该元素其他运行结束。
  • 该列表必须至少包含 2 个包含三个或更多元素的其他运行(我的测试列表有一个运行 8 个,后面跟着一个运行 4)
    • “对称不匹配”的另一半必须是 4 组中的最后一项(第二组其他组)。
  • 该列表必须在 (end - 1) 位置包含一个排序到已排序列表开头的元素
  • 该列表必须在(结束)位置包含一个元素,该元素在排序列表中间的某个位置进行排序

我很确定上面的项目符号并不是列表排序时必须满足的一般要求的详尽列表,以暴露对称性违规,但它们在一种特定情况下对我有用。

具体来说,我精心设计的测试列表以 49 个 TickNumber 对象开始,其中 Tick =“9999”,在 49 个 Tick 的前半部分有一个“9910”Tick,在此开放伪运行中总共有 50 个 Tick 数字。(伪因为“9910”分解了 49 个匹配“9999”刻度的未排序运行。)开始运行中的“9910”刻度是我正在测试的对称不匹配的一半。然后测试列表包含 12 个 GisTickNumber 对象,每组 8 个 ("9915*", "9920*", "9922*", "9931*", "9933*", "9934*", "9936*", " 9939*"),后跟 4 ("9907*"、"9908*"、"9909*"、"9910*")。请注意,这 4 个项目中的最后一项是我正在测试的“对称不匹配”的另一半。最后,列表以“9901”TickNumber 对象(将引导排序列表)和“9978*”GisTickNumber 对象(在中间某处排序)结束。我尝试删除和/或重新排列测试列表中的对象,但无济于事。例如,如果从测试列表中删除“9901”元素,单元测试将开始发出误报(成功)结果。(如果将“9901”移动到未排序列表的前面也会出现误报)

注意:我怀疑“9910”对称不匹配的普通 TickNumber 部分可能出现在第 MIN_RUN 个元素之前的开头运行中的任何位置。换句话说,如果 MIN_RUN 为 32,并且我的测试列表中的前导运行有 50 个元素与 49 个元素比较“相同”,则“9910”对称不匹配元素可以出现在运行中小于位置 32 的任何位置。假设尚未得到证实;但我凭经验确定,对称失配元素不能出现在引导运行结束附近,并且它可以出现在引导运行开始附近的多个位置。(每次测试运行一个不同的位置)

一般来说,如果这些条件中的任何一个不“完全正确”,即使您正在测试比较应违反合同的列表数据,也不会触发“一般合同违反”。

就我而言,测试列表中唯一匹配的 TickNumber 对象是 49 个“9999”刻度和 2 个(“9910”和“9910*”)在比较时违反对称性的刻度。