Identifying Patch Correctness in Test-Based Program Repair--基于测试的程序修复中补丁正确性的识别

Identifying Patch Correctness in Test-Based Program Repair–基于测试的程序修复中补丁正确性的识别
在这里插入图片描述

摘要

近年来,基于测试的程序自动修复引起了广泛关注。然而,实践中的测试套件往往太弱,无法保证正确性,现有的方法通常会生成大量不正确的补丁。为了减少生成的错误补丁的数量,我们提出了一种新的方法,启发式地确定生成的补丁的正确性。其核心思想是利用测试用例执行的行为相似性。在原始程序和修补程序上通过测试的行为可能类似,而在原始程序和修补程序上失败的测试的行为可能不同。此外,如果两个测试表现出相似的运行时行为,那么这两个测试可能具有相同的测试结果
ps:就像俄国作家列夫·托尔斯泰在《安娜·卡列尼娜》里面中说的:“幸福的家庭都是相似的,不幸的家庭各有各的不幸” 有异曲同工之妙?

基于这些观察,我们生成新的测试输入来增强测试套件,并使用它们的行为相似性来确定补丁的正确性。我们的方法是在一个由139个补丁组成的数据集上进行评估的,这些补丁来自现有的程序修复系统,包括jGenProg、Nopol、jKali、ACS和HDRepair。我们的方法成功地阻止了56.3%的错误补丁的生成,而没有阻止任何正确的补丁

在过去几十年中,已经提出了大量的自动化程序修复方法[7-9、12、13、16-19、25、26、28、43、44],其中许多属于基于测试的程序修复。在基于测试的程序修复中,修复工具将故障程序和测试套件(包括至少一个显示故障的失败测试)作为输入,然后生成一个补丁,使所有测试都通过。然而,现实项目中的测试套件往往很薄弱[34],通过所有测试的修补程序可能仍然存在故障。如果补丁版本通过测试套件中的所有测试,我们称补丁是合理的,并且如果补丁修复,只修复补丁,我们认为补丁是正确的。正如Long等人[20]所研究的那样,现实世界系统中的测试套件通常很弱,以至于大多数看似合理的补丁都不正确,这使得基于测试的程序修复系统很难确保补丁的正确性。正如现有研究[24,34,37]所示,多个自动程序修复系统产生的错误补丁比真实世界缺陷上的正确补丁多得多,导致生成的补丁的精度较低

现有程序修复系统的低精度严重影响了这些系统的可用性。由于测试套件无法保证补丁的正确性,开发人员必须手动验证补丁。当程序修复系统的精度较低时,开发人员必须验证大量不正确的补丁,而且目前尚不清楚这种验证过程是否比开发人员直接修复缺陷的成本更高。一项现有的研究[39]还表明,当开发人员获得低质量的补丁时,与没有提供补丁的情况相比,他们的性能会下降。因此,我们认为提高程序修复系统的精度至关重要,即使有丢失一些正确补丁的风险

由于较弱的测试套件不足以过滤掉程序修复系统产生的不正确补丁,因此一个直接的想法是增强测试套件。事实上,现有的研究[42,46]试图生成新的测试用例来识别错误的补丁。然而,虽然可以生成测试输入,但通常无法自动生成测试预言,称为预言问题[1,31]。因此,现有的方法要么需要人工确定测试结果[42],这在许多情况下过于昂贵,要么依赖固有的预言,如无崩溃[46],它只能识别违反预言的某些类型的错误补丁。我们的目标是在不知道完整预言的情况下,对补丁进行启发式分类。给定一组看似合理的补丁,我们尝试确定每个补丁是否可能正确,并拒绝可能不正确的补丁。我们的方法基于两个关键观察。
PATCH-SIM。应用正确的补丁后,通过测试的行为通常与之前类似,而失败测试的行为通常不同
TEST-SIM。当两个测试执行相似时,它们可能具有相同的测试结果,即,两个测试都触发相同的故障,或者都是正常执行

PATCH-SIM允许我们在没有oracles的情况下测试补丁,也就是说,我们在修补系统之前和之后运行测试,并检查行为变化的程度。正如我们稍后的评估所示,仅PATCH-SIM就已经识别出了大量不正确的补丁。然而 我们只能使用原始测试,而不能使用新生成的测试输入,因为我们不知道它们是通过还是失败TEST-SIM通过确定新生成测试输入的测试结果来补充PATCH-SIM
基于这两个关键观察,我们的方法包括以下步骤。首先,我们生成一组新的测试输入。其次,我们通过将新生成的测试输入与现有测试输入进行比较,将其分类为通过测试或失败测试。第三,我们通过比较每个测试(包括原始测试和生成的测试)的补丁前后的执行情况来确定补丁的正确性
我们通过设计具体的公式来比较执行情况,实现了我们的方法,并在一个由139个补丁组成的数据集上评估了我们的方法,这些补丁来自以前的程序修复系统,包括jGenProg[24]、Nopol[24]、jKali[24]、HDRepair[13]和ACS[43]。我们的方法成功地过滤掉了56.3%的错误补丁,而没有丢失任何正确的补丁。结果表明,我们的方法提高了程序修复方法的精确度,但对召回的负面影响有限。

综上所述,本文的主要贡献如下。
•我们提出了两种启发式方法PATCH-SIM和TEST-SIM,它们提供了补丁正确性的指标
•我们设计了一种具体的方法,根据这两种启发式方法自动对补丁进行分类
•我们在大量补丁上对该方法进行了评估,结果表明我们的方法是有用的

论文的其余部分组织如下。第2节首先讨论了相关工作。第3节用例子激励我们的方法,第4节详细介绍了我们的方法第5节介绍了我们的实现第6节描述了我们对139个补丁数据集的评估。第7节讨论了对有效性的威胁。最后,第8节对本文进行了总结。

2.相关工作

基于测试的程序修复。基于测试的程序修复通常被视为一个搜索问题,通过定义补丁的搜索空间,通常是通过一组预定义的修复模板,目标是在搜索空间中找到正确的补丁。定位补丁的典型方法包括以下几种。
•搜索算法。有些方法使用元启发式搜索[16,18]或随机搜索[33]来定位补丁
•统计数据。一些方法基于各种信息源(如现有补丁[12,13,19]和现有源代码[43])建立统计模型,以选择可能修复缺陷的补丁
•约束解决。一些方法[3,14,25,26,28,36]将搜索问题转化为可满足性或优化问题,并使用约束求解器定位补丁
虽然生成补丁的具体方法不同,但弱测试套件问题仍然是基于测试的程序修复的一个挑战,可能会导致生成错误的补丁。正如我们的评估所示,我们的方法可以有效地增强这些现有方法,以提高其精度。

补丁分类。面对弱测试套件的挑战,一些研究人员还提出了确定补丁正确性的方法。一些研究人员寻求确定性方法。Xin和Reiss[42]假设存在一个完美的oracle(通常是手动的),用于对测试结果进行分类,并生成新的测试输入,以识别oracle违规行为Yang等人[46]生成测试输入,并监控对固有预言的违反,包括崩溃和内存安全问题。与它们相比,我们的方法不需要一个完美的oracle,并且可以潜在地识别不违反固有预言的错误补丁,但存在错误分类正确补丁的风险

其他方法也使用启发式方法对补丁进行分类Tan等人[38]提出了反模式,以捕获落入特定静态结构的典型错误补丁我们的方法主要依赖于动态信息评估将显示,这两种方法可能会结合起来。Yu等人[47]研究了通过最小化对生成测试的行为影响来过滤补丁的方法,发现这种方法无法提高现有程序修复方法的精度。与他们的方法相比,我们的方法对生成的测试进行分类,并对不同的类提出不同的行为需求。最后,Weimer等人[40]强调了识别补丁正确性的可能方向

补丁排名。许多修复方法使用一个内部排序组件,根据补丁正确的概率对补丁进行排序。补丁排序是一个与补丁分类非常相关但又不同的问题。一方面,我们可以通过设置合适的阈值来区分正确和错误的补丁,将补丁分类问题转化为补丁排序问题另一方面,一个完美的补丁排序方法并不一定导致一个完美的补丁分类方法,因为阈值可能因缺陷而异
补丁排序技术主要有三类。第一类是根据通过测试的次数对补丁进行排名。然而,这一类别不能对看似合理的补丁进行排序。第二类使用原始程序的句法距离[3,14,25]和语义距离[3,14]对补丁进行排序。正如我们稍后的评估所示,我们的方法可以显著优于这两种距离。第三类[13,19,43]从现有规则中学习概率模型,对补丁进行排序。我们的方法可以补充这些方法:正如我们稍后的评估所示,我们的方法能够识别由ACS生成的50%的不正确补丁,这是该类别中最新的方法。

解决预言问题的方法。缺乏测试oracle是软件测试中一个长期存在的问题,关于这个问题的研究总结可以在现有的调查中找到[1,31]。其中,一些研究侧重于自动生成启发式测试预言。例如,不变量挖掘可能会从通过测试执行中挖掘不变量[4,5],从而对新的测试输入进行分类。然而,就我们所知,这样的应用程序对补丁正确性识别的影响仍然是未知的,仍有待于未来的工作。
其他相关工作。Marinescu和Cadar[22]建议使用KATCH生成覆盖补丁的测试我们的方法可能与KATCH相结合,以提高生成测试的质量。这是一个有待探索的未来方向。

基于突变的故障定位,如Metallaxis[30]和MUSE[27]与PATCH-SIM有一个类似的观察结果:当突变故障位置时,通过测试的行为变化比失败测试的行为变化小得多。

3.补丁正确性和行为相似性

在本节中,我们将分析补丁正确性和行为相似性之间的关系,以激励我们的方法。我们将补丁定义为一对程序版本,即原始错误版本和修补版本。为了简化讨论,我们假设程序只包含一个错误。因此,失败的测试执行必须触发故障并产生错误的输出
Weak Oracles and PATCH-SIM。如前所述,一个测试套件可能在输入或预言方面很弱,或者两者都很弱,从而错过了这样一个错误的补丁。为了看看Oracle有多么脆弱,可能会错过一个不正确的补丁,让我们考虑图1中的例子。图1(a)显示了jKali[34]为缺陷基准Defects4J[11]中的缺陷Chart15生成的错误补丁。在本例中,如果接收方对象使用空数据集初始化,则调用draw将导致意外异常。图1(b)显示了一个测试用例,它通过创建这样一个对象并检查异常来检测这个缺陷。图1(c)显示了一个通过的测试,检查数据集中具有空值的图形不会导致异常。这个补丁只是跳过了可能引发异常的整个方法。在通过测试和失败测试中,oracle只检查没有引发异常,但不检查程序的输出是否正确。因此,由于补丁阻止了异常,两个测试都通过了。
在这里插入图片描述正如引言中提到的,我们依靠观测PATCH-SIM来验证补丁。给定一个补丁,我们希望原始程序和补丁程序在通过测试执行时表现相似,而在失败测试执行时表现不同
在本例中,通过测试的人会在原始程序的图表上绘制一些东西,但在修补程序中会完全跳过绘制方法,从而导致显著的行为差异。基于这种差异,我们可以确定补丁不正确。

Weak Inputs and TEST-SIM。要了解弱测试输入是如何导致错误补丁的错误,让我们考虑图2中的示例。该示例演示了Nopol[45]生产的Defects4J中Lang-39的错误补丁。当replacementList或searchList中的元素为null时,原始程序会抛出不希望出现的异常。为了防止这种异常,正确的方法是跳过这些元素,如图2(b)所示。然而,图2(a)中生成的补丁基于repeat的值阻塞了整个循环,repeat是该方法的一个参数。有趣的是,所有现有的测试,不管是之前通过的还是失败的,都会在补丁程序中产生正确的输出。这是因为(1)repeat的值恰好在所有通过的测试中为真,而在所有失败的测试中为假,以及(2)searchList和replacementList中的任何元素都不满足大于0的条件。然而,通过PATCH-SIM增强测试预言器是没有用的,因为通过测试时的行为几乎与原始程序相同,而失败测试时的行为变化很大
在这里插入图片描述为了捕获弱测试输入遗漏的不正确补丁,我们需要新的测试输入。为了使用PATCH-SIM和新的测试输入,我们需要知道测试的输出是否正确。为了解决这个问题,我们使用了observation TEST-SIM。我们假设,当新测试输入的执行与失败测试的执行类似时,新测试输入可能会导致不正确的输出。类似地,当新测试输入的执行与通过测试的执行类似时,新测试输入可能会导致正确的输出。基于这个假设,我们可以通过比较新测试输入与现有测试输入的执行情况来对其进行分类。
在图2中的示例中,对于触发此错误的任何新的测试输入,将在循环中间抛出异常,这与失败测试的执行类似。对于任何不会触发此错误的测试输入,循环将正常完成,这与现有通过测试的执行类似。

Measuring Execution Similarity.实现该方法的一个重要问题是如何度量两个测试执行的相似性。在我们的方法中,我们测量两次执行之间的完整路径谱[10]的相似性。完整路径谱,简称CPS,是程序执行期间执行的语句ID序列。几项现有研究[2,10,35]表明,光谱在区分正确和失败的测试执行中很有用,Harrold等人[10]发现CPS是整体性能最好的光谱之一
例如,让我们考虑图1和图2中的两个例子。在这两个示例中,缺陷将导致异常,这进一步导致传递和失败测试的不同频谱:通过测试将执行到方法的结束,而失败的测试将在中间停止。此外,修补系统后,失败的测试执行将变得不同:不会引发异常,测试将执行到方法的末尾。

Multiple Faults。在一个程序中出现多个故障的情况下,这两种启发式方法仍然适用。当存在多个故障时,失败的测试可能只能识别其中的一些故障。我们只是将已识别的故障视为一个故障,其余故障视为正确的程序,上述讨论仍然适用。

4.方法

4.1综述

图3显示了我们方法的整个过程。我们将原始的buggy程序、一组测试用例和一个补丁作为输入,并生成一个分类结果来判断补丁是否正确
在这里插入图片描述我们的方法由五部分组成,分为三类:测试生成(包括测试输入生成器)、距离测量(包括测试距离测量器和补丁距离测量器)和结果分类(包括测试分类器和补丁分类器)。
首先,测试输入生成器生成一组测试输入。然后,我们在原始buggy程序上运行生成的测试。在测试执行期间,我们动态地收集关于测试执行的运行时信息基于运行时信息测试距离度量器计算每个新生成的测试输入的执行与每个原始测试用例之间的距离。距离是一个实数,表示两次测试执行的不同程度。结果是测试距离的向量。然后,该向量被传递给测试分类器,该分类器通过比较其与通过测试的距离以及与失败测试的距离根据test-SIM将测试分类为通过或失败
现在我们有了一组增强的测试输入,它们被分类为通过或失败,我们可以使用它们来确定补丁的正确性。给定一个补丁,patch distance measurer在原始程序和补丁程序上运行每个测试,并测量两次执行之间的距离。结果是一个补丁距离向量。最后,基于观测patch-SIM,将该向量引入补丁分类器,通过距离确定补丁的正确性
在本节的其余部分中,我们将分别介绍这三个类别的组件。

4.2测试生成

给定一个程序,测试生成器生成该程序的测试输入。此外,由于我们的目标是确定补丁的正确性,因此我们需要生成的测试来覆盖补丁方法如果一个补丁修改了多个方法,生成的测试应该至少覆盖其中一个。理论上,我们的方法可以使用任何测试输入生成技术。我们可以利用符号执行技术来涵盖特定的方法,尤其是那些为测试补丁而设计的方法[22]。我们还可以采用随机测试技术[6,21,29],过滤掉那些不包括任何修改方法的测试

4.3距离测量

4.3.1距离测量

如前所述,我们通过比较两次执行的完整路径谱来测量两次执行之间的距离。因此,问题归结为测量两个序列之间的距离。通常,有许多不同的度量标准来度量序列距离,例如最长公共子序列、Levenshtein距离、Hamming距离
作为基于序列距离对补丁进行分类的首次尝试,我们使用最长公共子序列(LCS)作为距离度量,并将其他距离度量留待以后的工作。两个序列a和b的LCS是仅通过删除元素即可从a和b获得的最长序列。然后,我们使用以下公式将LCS的长度标准化为0到1之间的值,其中a和b是两个序列。
在这里插入图片描述

4.3.2 测试距离测量器

组件测试距离测量器接受生成的测试,并计算其与每个原始测试的距离结果是一个距离向量,其中每个元素表示生成的测试和原始测试之间的距离。为了关注故障,我们只考虑修补方法的调用上下文中执行的语句。也就是说,我们在运行时跟踪上定位成对的位置:(a) 从一个方法调用中输入一个修补过的方法,(b)从同一个调用中退出该方法,并且只保留两个位置之间的语句
这一步可以帮助我们过滤噪音:在补丁方法的调用上下文之外的语句执行中,两个测试可能不同,但这种差异通常与故障无关。如果原始测试不包括任何修补方法,我们也将测试排除在距离测量之外。

4.3.3Patch Distance Measurer

组件补丁距离测量器对生成的或原始的每个测试进行测量,并计算其在原始程序和补丁程序上执行之间的距离。结果是一个距离向量,其中每个元素表示测试的距离。
与测试距离测量器不同,这里我们考虑执行语句的全序列。这是因为比较的执行来自同一个测试,它们不太可能是修补方法之外的噪音

4.4 分类

根据距离,我们可以对生成的测试和补丁进行分类。我们逐一描述这两个组件。

4.4.1Test Classifier.

测试分类器将生成测试的测试结果分类为通过或失败。一些生成的测试很难精确分类,我们放弃了这些测试。Let result(t)表示原始测试t的测试结果,即通过、失败或放弃。让distance(t,t’)表示在原始程序上执行t和t’之间的距离。给定生成的测试t’,我们使用以下公式确定其分类结果。公式将最近邻的结果分配给生成的测试
在这里插入图片描述
请注意,上述公式仅适用于至少通过测试的情况。如果没有通过测试,我们将距离与阈值为Kt的所有失败测试进行比较,如果基于原始程序在大多数输入上正常工作的假设,测试执行与所有失败测试显著不同,则认为测试通过。请注意,总是有至少一个失败的测试暴露了缺陷。
在这里插入图片描述

4.4.2Patch classifier.

补丁 分类器根据计算的距离将补丁分类为正确或不正确。设distancep(t)表示在应用补丁p之前和之后执行测试t之间的距离。我们使用以下公式确定补丁p的正确性。
在这里插入图片描述
这个公式检查了观察中的两个条件
首先,通过测试的人应该表现得类似。为了检查这种情况,我们将通过测试的最大距离与阈值Kp进行比较,如果行为变化太大,则确定补丁不正确。
其次,失败的测试应该有不同的表现。然而,由于不同的缺陷需要不同的修复方法,因此很难设置固定的阈值。因此,我们检查失败测试中的平均行为变化是否仍然大于所有通过测试。如果不是,则认为修补程序不正确。
我们使用最大距离表示通过测试,而使用平均距离表示失败测试。一个不正确的补丁可能只会影响一些通过的测试,我们使用最大距离来关注这些测试。另一方面,修补后,所有失败测试的行为都应该改变,所以我们使用平均距离。请注意,这个公式要求我们至少有一个通过测试,无论是原始的还是生成的。如果没有通过测试,我们只需将补丁视为正确。

5.实现

我们已经将我们的方法实现为Java上的补丁分类工具。给定一个带有测试套件和程序补丁的Java程序,我们的工具会将补丁分类为正确与否。在我们的实现中,我们选择随机测试工具Randoop[29]作为测试生成工具。由于我们的目标是覆盖补丁方法,因此旨在覆盖特定位置的测试生成工具似乎更合适,例如基于符号执行的工具[32]或基于搜索的测试[6]。然而,我们没有使用这些工具,因为它们旨在用更少的测试覆盖程序。例如,Evosuite[6]在我们的评估对象中,每个buggy程序最多生成三个测试用例。如此少量的测试不足以进行统计分析。

6.评估

实现和评估数据可在线获取。https://github.com/Ultimanecat/DefectRepairing

6.1研究问题

•RQ1:TEST-SIM和PATCH-SIM的可靠性在多大程度上
•RQ2:我们识别补丁正确性的方法有多有效?
•RQ3:我们的方法与现有方法(即反模式、Opad、句法相似性和语义相似性)相比如何
•RQ4:测试生成如何影响整体性能
•RQ5:参数Kt和Kp如何影响整体性能
•RQ6:假阳性和假阴性的原因是什么
•RQ7:我们的工具对开发人员的正确补丁进行分类的效果如何?

RQ1检查TEST-SIM卡和PATCH-SIM卡的总容量
RQ2关注我们方法的整体有效性。特别是,我们担心我们过滤掉了多少不正确和正确的补丁。
RQ3将我们的方法与现有的四种识别补丁正确性的方法进行了比较。反模式[38]通过将不正确的补丁与预定义的模式匹配来捕获它们。Opad[46]基于固有的预言,即修补程序不应引入新的崩溃或内存安全问题。句法相似性[3,14,25]和语义相似性[3,14]是补丁排序技术,通过测量补丁在程序中的语法或语义变化程度来对补丁进行排序,可以通过设置适当的阈值来确定补丁的正确性。
RQ4和RQ5探讨了我们方法的不同配置如何影响整体性能。
RQ6调查错误结果的原因,以指导未来的研究。最后,正如将在下一小节中看到的,尽管我们已经尽了最大努力收集Java上生成的补丁,但与不正确的补丁相比,正确的补丁数量仍然很少。为了弥补这一点
RQ7进一步研究了我们的方法在开发人员的正确补丁上的性能

6.2数据集

我们从现有的论文中收集了生成补丁的数据集。表1显示了数据集的统计信息。我们的数据集由六个程序修复工具生成的补丁组成。在这些工具中,jGenProg是GenProg[15,16,41]在Java上的重新实现,是一种基于遗传算法的修复工具;jKali是Kali[34]在Java上的重新实现,这是一个只删除功能的修复工具;Nopol[45]是一个依赖约束求解来修复错误条件的工具,我们的实验中使用了两个版本,2015[23]和2017[45];HDRepair[13]使用历史错误修复中的信息来指导搜索过程;ACS[43]是一种基于多个信息源的工具,用于统计和启发式地修复错误条件。选定的工具包括三种类型的面片生成方法:搜索算法(jGenProg,jKali)、约束求解(Nopol)和统计(HDRepair,ACS)。有关这三种类型的更多详细信息,请参见相关工作部分。
jGenProg、jKali和Nopol2015生成的补丁来自Martinez等人对Defects4J的实验[23]。Nopol2017生成的补丁来自Nopol的最新报告[45]。HDRepair生成的补丁来自Xin和Reiss的补丁分类实验[42]。ACS生成的补丁来自ACS评估[43]。
在这里插入图片描述所有补丁都是为Defects4J[11]中的缺陷生成的,这是Java上广泛使用的真实缺陷基准。Defects4J由六个项目组成:Chart是一个用于显示图表的库;数学是科学计算的图书馆;时间是用于日期\时间处理的库;Lang是一组用于操作JDK类的额外方法;Closure是针对Javascript的优化编译器;Mockito是用于单元测试的模拟框架。
我们的实现不支持某些补丁,主要是因为Randoop无法为这些补丁生成任何测试。尤其是,Randoop无法生成任何Closure和Mockito测试。我们删除了这些不受支持的补丁。
Martinez等人的实验、ACS评估和Qi等人的实验中的补丁包含标识补丁正确性的标签,这些标签将补丁标记为正确、不正确或未知。Nopol2017的补丁不包含此类标签。我们手动检查未标记的补丁和一些标记的补丁是否在语义上等同于人工编写的补丁。由于正确性未知的补丁无法用于评估我们的方法,因此我们删除这些补丁。
最后,我们有一个由自动程序修复工具生成的139个补丁的数据集,其中110个是不正确的补丁,29个是正确的补丁。为了回答RQ6问题,我们还将Defects4J上的所有开发者补丁添加到了我们的数据集中。和生成的补丁一样,我们删除了不受支持的补丁,包括Close和Mockito上的所有补丁。最终,我们有194个开发者补丁。请注意,开发人员补丁仅在RQ6中使用,因为它们与生成的补丁具有不同的特性。

6.3实验设置

测试生成。我们让Randoop在原始程序上运行了3分钟,并收集了涵盖修补方法的测试。我们在3分钟内停止,因为对于大多数缺陷,Randoop在3分钟内完成了足够的测试,对于剩余的没有足够测试的缺陷,延长时间不会导致更多的测试。然后,我们为每个补丁随机选择20个测试。如果少于20个测试,我们选择所有测试。最后,我们平均每个补丁有7.1个测试,至少有0个测试。根据TEST-SIM的分类,生成的测试中有71%通过了测试。
RQ1。为了评估PATCH-SIM,我们测量了补丁和未补丁版本的测试执行之间的平均距离,并检查在正确和不正确补丁上通过测试和失败测试之间是否存在显著差异。为了评估TEST-SIM,我们测量了测试之间的距离,并分析了距离越近是否表明测试结果相似。
RQ2。我们将我们的方法应用于数据集中的补丁,并检查分类结果是否与正确性标签一致
RQ3。我们将现有的四种方法应用于数据集,并将它们的结果与我们的结果进行了比较。反模式最初是在C中实现的。为了在Java数据集上应用反模式,我们采用了Tan等人[38]定义的七种反模式,并手动检查数据集中的补丁是否属于这些模式。Opad[46]使用了固有的预言,即补丁不应引入新的崩溃或内存安全问题。Opad最初是为C设计的,我们需要将其改编为Java。一方面 ,在Java中,崩溃被表示为运行时异常。另一方面,内存安全问题要么被Java基础设施阻止,要么被检测为运行时异常。因此,我们统一检测补丁是否在测试运行时引入任何新的运行时异常。如果是这样,则认为修补程序不正确。
关于句法和语义距离,不同的论文[3,14,25]提出了不同的度量标准来衡量句法和语义距离,总结见表2。然而,正如表中所分析的,许多指标是为特定类别的补丁定义的,不能应用于一般补丁。特别是,许多指标仅为表达式替换而设计,它们对其他类型更改的定义不明确。因此,我们选择了表2中标记为“一般”的两个指标进行比较:一个通过比较AST来测量句法距离,另一个通过比较完整路径谱来测量语义距离。基于AST的语法距离定义为将一个程序更改为另一个程序所需删除或插入的AST节点的最小数量。例如,将表达式a>b+1更改为c<d+1需要至少删除三个AST节点(a,b,>)并插入三个AST节点(c,d,<),从而使句法距离为6。基于程序p的完整路径谱的语义距离使用以下公式定义,其中To是所有原始测试的集合,包括至少一种修改的方法,distancep在第4.4.2节中定义 。
在这里插入图片描述
句法/语义距离给出了补丁的排序列表。然后我们检查是否能找到一个最佳阈值,将列表分为正确和错误的补丁。
在这里插入图片描述RQ4。我们考虑了两种不同的测试生成策略,并将其结果与RQ1的结果进行了比较没有一代人。这种策略根本不会生成任何测试输入。该策略用作评估新生成的测试输入对整体性能的贡献程度的基线Randoop重复运行。由于Randoop是一个随机测试生成工具,对Randoop的不同调用可能会导致不同的测试套件。这种策略只是重新调用Randoop来生成一组可能不同的测试,比较有助于我们理解测试生成中的随机性对我们方法整体性能的影响。由于第二种策略涉及重新生成测试,且执行成本较高,因此我们在随机选择的50个补丁子集上评估了该策略。为了观察随机性的影响,我们重复了5次实验。
RQ5。在前三个研究问题的实验中,我们将我们方法的参数设置为:Kp=0.25,Kt=0.4。这些参数值是通过在一小组面片上进行几次尝试来确定的。为了回答RQ4,我们系统地为参数Kp和Kt设置了不同的值,然后分析了这些参数对整个数据集结果的影响。
RQ6。我们手动分析所有误报和漏报,以了解错误分类的原因并总结原因。
RQ7。我们将我们的方法应用于Defects4J benchmark提供的人工编写的补丁,并检查我们的方法是否将它们误分类为不正确。硬件平台。该实验是在一台配备Intel Xeon E3 CPU和32GB内存的服务器上进行的。

6.4RQ1的结果:启发式的可靠性

表3显示了评估PATCH-SIM的结果,即补丁版本和未补丁版本上测试执行之间的距离。从表中可以看出,对于正确的补丁,通过测试的距离非常接近于零,而失败测试的距离要大得多,是通过测试的9.5倍。结果表明,PATCH-SIM普遍适用。另一方面,通过测试和失败测试在不正确的补丁上并没有表现出如此强大的特性。虽然失败测试的距离仍然大于通过测试的距离,但这一比率仅为1.32倍,而不是9.5倍。这一结果表明,PATCH-SIM可以用来区分正确和错误的补丁。

图4显示了评估TEST-SIM的结果。X轴显示距离的间隔,而Y轴显示落入间隔的测试百分比。从图中可以看出,当两个测试距离较短时,它们更有可能得到相同的测试结果,而不是不同的测试结果。这一结果表明,TEST-SIM通常适用。另一方面,当两个测试距离较长时,它们更有可能得到不同的测试结果,而不是相同的测试结果。
在这里插入图片描述
在这里插入图片描述

6.5RQ2的结果:总体有效性

表4和表5分别显示了我们的方法在每个工具和每个项目的数据集上的性能。如表所示,我们的方法成功筛选出110个不正确的合理补丁中的62个,并且没有筛选出正确的补丁。此外,我们的该方法在不同的工具和不同的项目上表现出相似的性能,表明我们的结果可能适用于不同类型的项目和不同类型的工具。请注意,虽然我们的方法没有过滤掉数据集上任何正确的补丁,但理论上仍然可以过滤掉正确的补丁。例如,补丁可能会显著改变通过测试的控制流, 例如,通过使用新算法或调用一组不同的API,但测试执行可能会产生相同的结果。然而,考虑到当前程序修复方法的现状,这样的补丁可能很少。将我们的方法应用于人工编写的补丁时,会过滤掉一些正确的补丁。RQ6中讨论了HumanWrite补丁有效性的更多细节。在大多数情况下,我们的方法需要5到10分钟来确定补丁的正确性,而有些补丁可能需要30分钟。大部分时间都花在生成测试输入和记录运行时跟踪上。
在这里插入图片描述在这里插入图片描述

6.6 RQ3的结果:与其他人比较

反模式。在所有139个补丁中,反模式过滤出28个补丁,其中27个不正确,1个正确。结果表明,我们的方法明显优于反模式方法。此外,反模式过滤掉的27个错误补丁中,有13个也被我们的方法过滤掉,而其余14个补丁没有被我们的方法过滤掉。这一结果表明,我们可能会将这两种方法结合起来,以获得更好的性能。
Opad。当使用与我们的方法相同的测试输入集时,Opad无法识别任何不正确的补丁。为了进一步了解更强大的测试套件是否可以获得更好的结果,我们进一步选择了多达50个测试,而不是每个补丁20个测试。这一次,Opad过滤掉了3个不正确的补丁。这一结果表明,固有的预言对Java修补程序的分类可能影响有限,因为Java基础设施已经防止了许多崩溃和内存安全问题,而且修补程序可能不太容易破坏这样的预言。
句法和语义距离。图5显示了错误补丁和正确补丁在句法距离上的分布。x轴显示距离的间隔,Y轴显示间隔内的面片数。从图中可以看出,不正确的补丁和正确的补丁在所有间隔中都会出现,并且分布没有显示出特定的特征。如果我们想使用句法距离排除56.3%的错误补丁,我们需要至少排除66.7%的正确补丁。这一结果表明,句法距离不能直接用来决定补丁的正确性。
在这里插入图片描述图6显示了语义距离上错误和正确补丁的分布。从图中我们可以看到,当距离较小时,这两种类型的斑块往往出现得更频繁。 当距离变大时,这两种类型都会减少,但正确的面片减少得更快。如果我们想使用语义距离排除56.3%的错误补丁,我们需要排除43.3%的正确补丁。这一结果表明,语义距离可以比句法距离更好地衡量补丁的正确性,但我们的方法仍然显著优于语义距离。
请注意,上述结果并不意味着句法和语义距离不适合对补丁进行排序。虽然很难找到一个阈值来区分一组缺陷的正确修补程序和错误修补程序,但在大多数单个缺陷中,正确修补程序的排名仍然可能更高。
在这里插入图片描述

6.7RQ4的结果:测试生成的影响

表6显示了不生成测试输入的结果。在没有生成的测试输入的情况下,我们的方法过滤掉了8个不正确的补丁,仍然过滤出了0个正确的补丁。这一结果表明,PATCH-SIM本身已经是一种有效的方法,但测试生成和测试-SIM可以进一步提高性能。
关于随机性,我们在选定的50个贴片上重复了5次实验,只在3个不正确的贴片上得到了不同的结果。最好的情况比最坏的情况多了一个不正确的补丁。结果表明,随机性确实会影响结果,但影响有限。
在这里插入图片描述

6.8RQ5的结果:参数

我们的方法涉及两个参数,Kt和Kp,都在0到1之间。我们采用不同参数的方法的结果如表7和表8所示,其中每列都显示了带有表头参数值的结果。从表中可以看出,将参数设置为不同的值对整体结果的影响有限,大范围的参数值可以实现最佳性能。结果表明,我们的方法不需要精确调整参数

6.9 RQ6结果:错误结果的原因

我们的方法在47个不正确的补丁上给出了错误的结果。我们手动分析了这些补丁,并确定了错误分类的三个主要原因,如下所示。
太弱的测试套件。通常情况下(48个测试中有21个)只有一个失败的测试覆盖了修补的方法。在没有通过测试的情况下,我们的方法只依赖于阈值Kt来对测试进行分类,有时很难生成通过阈值的测试。因此,我们可能没有或只有少数通过测试来执行补丁分类,从而导致性能低下。
不满意的测试生成。另一种常见情况(48个案例中有27个,与前一个案例有9个重叠)是测试生成工具无法生成令人满意的测试。Randoop可能无法提供覆盖修补方法的测试,或者无法生成可能暴露错误行为的测试。如果我们使用更强大的测试生成工具,这类补丁有可能被正确识别。
不满意的分类公式。最后一个案例(48个案例中的8个)是由一些失败的测试执行中的大型行为变化引起的。因为我们计算了补丁分类中所有失败测试执行的平均距离,如果有一个非常大的值,那么即使所有其他失败测试都有小的行为变化,平均值也可能变大。因此,补丁可能会被错误分类。这个问题可以通过生成更多失败的测试用例来解决,以降低平均值,或者找到更好的公式来对补丁进行分类。

6.10 RQ7的结果:开发者补丁

在194个正确的开发人员补丁中,我们的工具将16个(8.25%)补丁归类为不正确。我们进一步分析了16个补丁被错误分类的原因,发现所有16个补丁都非平凡地改变了控制流,并在通过测试的执行中导致了CPS的显著差异。特别是,在6个补丁中,通过测试的行为发生了显著变化,而在其余10个补丁中,行为几乎保持不变,但执行的语句发生了显著变化(例如,使用相同的功能调用不同的方法)。结果表明:(1)人类斑块确实比当前自动化技术生成的斑块更复杂;(2) 当补丁的复杂性增加时,我们的方法可能仍然有效,因为只有一小部分正确的补丁被排除在外;(3) 为了进一步提高性能,我们需要增强PATCH-SIM和CPS以应对此类情况。

7. 对虚拟现实的威胁和限制

对内部有效性的主要威胁是,我们从数据集中丢弃了一些补丁,要么是因为无法确定它们的正确性,要么是因为我们实现中使用的基础设施工具无法支持这些补丁。因此,可能会引入选择偏差。然而,我们认为这种威胁并不严重,因为与整个数据集相比,移除的补丁数量较少,而且这些补丁的结果不太可能显著改变整体结果。外部有效性的主要威胁是我们的方法是否可以推广到不同类型的程序修复工具。虽然我们已经从所有主要类别的程序修复工具中选择了修复工具,包括基于搜索算法、约束求解和统计的工具,但仍不清楚未来的工具是否具有与当前工具显著不同的特征。为了最大限度地减少这种威胁,我们添加了RQ7来测试开发者补丁,这可以被视为自动生成补丁的最终目标。结果表明,我们的方法在开发人员补丁和生成的补丁上可能有不同的性能,但差异是有限的。构造有效性的主要威胁是人工评估补丁的正确性,并且分类可能是错误的。为了减少这种威胁,所有困难的补丁都会通过前两位作者进行讨论,以便共同做出决定。此外,部分分类来自Martinez等人的实验[23],其结果已在网上发表了几年,据我们所知,没有报告质疑分类质量。在设计公式时可以有许多不同的选择。例如,我们可以使用不同的序列距离,甚至不同的光谱来测量两次执行的距离。我们可以使用不同的统计方法对测试和补丁进行分类。目前的论文没有也不可能探索所有的可能性,并将其作为未来的工作。

8.总结

在本文中,我们提出了一种基于程序执行之间的行为相似性自动确定补丁正确性的方法。我们的评估表明,我们的方法可以有效地过滤掉56.3%的错误补丁,而不会丢失任何正确的补丁。结果表明,测量行为相似性可能是解决oracle问题的一种很有前途的方法,并呼吁对这一主题进行更多的研究。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值