模板脚本测试语言(TSTL)

tstl的Python项目详细描述


tstl:模板脚本测试语言

tstl是一种领域特定语言(dsl)和一组工具,用于支持软件测试的自动生成。这个 实现以python为目标。您(在python中)定义了一组 用于构建测试的组件,以及您希望的任何属性 为测试的系统保留,tstl为您的系统生成测试。 tstl支持测试重放、测试缩减和代码覆盖率分析, 包括一些复杂的按钮支持 测试生成方法。换句话说,tstl是基于属性的 测试工具。

什么是基于属性的测试?基于属性的测试是依赖于 不是在开发人员为特定输入或调用序列指定结果时,而是在 通用行为规范,结合多个 测试以确保通用规范有效。更多关于 基于属性的测试请参见:

tstl已经被用来查找和修复实际代码中的实际错误,包括esri的arcpy(http://desktop.arcgis.com/en/arcmap/latest/analyze/arcpy/what is arcpy-.htm)、sortedcontainers(https://github.com/grantjenks/sorted_containers), GMPY2(https://github.com/aleaxit/gmpy)、Sympy(http://www.sympy.org/en/index.html)、pyfakefs(https://github.com/jmcgeheeiv/pyfakefs), python本身(https://bugs.python.org/issue27870" rel="nofollow">https://bugs.python.org/issue27870)、solidity编译器(https://github.com/ethereum/solidity" rel="nofollow">https://github.com/ethereum/solidity)、solidity静态分析工具,甚至os x。

安装

您可以使用pip最容易地获取最近的tstl。pip install tstl应该可以正常工作。如果您想要更新的内容,您可以做:

git clone https://github.com/agroce/tstl.git
cd tstl
python setup.py install

对于代码覆盖率,您还需要安装ned batchelder的coverage.py工具;pip install coverage就是所需的全部。

简而言之,tstl

为了了解tstl是如何操作的,让我们尝试一个玩具示例。我们将使用tstl来解决一个简单的"难题",看看是否有可能只用几行python代码,从0开始只用一小组操作(加4,减3,乘3,产生2的幂)来生成整数值510。

  1. 创建名为nutshell.tstl的文件,其内容如下:
@import math

# A line beginning with an @ is just python code.

pool: <int> 5

# A pool is a set of values we'll produce and use in testing.
# We need some integers, and we'll let TSTL produce up to 5 of them.
# The name is a variable name, basically, but often will be like a
# type name, showing how the value is used.

<int> := 0
<int> += 4
<int> -= 3
<int> *= 3
{OverflowError} <int> := int(math.pow(2,<int>))

# These are actions, basically single lines of Python code.
# The big changes from normal Python are:
# 1. := is like Python assignment with =, but also tells TSTL this
# assignment _initializes_ a value.
# 2. <int> is a placeholder meaning _any_ int value in the pool.
# 3. {OverflowError} means that we want to ignore if this line of
# Python produces an uncaught OverflowError exception.

# A test in TSTL is a sequence of actions.  So, given the above, one
# test would be:
#
# int3 = 0
# int4 = 0
# int3 *= 3
# int4 += 4
# int3 = 0
# int2 = int(math.pow(2,int4))
# int2 -= 3

# As you can see, the actions can appear in any order, but every
# pool variable is first initialized by some := assignment.
# Similarly, TSTL may use pool variables in an arbitrary order;
# thus we never see int0 or int1 used, here, by chance.

# The size of the int pool determines how many different ints can
# appear in such a test.  You can think of it as TSTL's "working
# memory."  If you have a pool size of 1, and an action like
# foo(<int>,<int>) you'll always call foo with the same value for both
# parameters -- like foo(int0,int0).  You should always have a pool
# size at least as large as the number of times you use a pool in a
# single action.  More is often better, to give TSTL more ability to
# bring back in earlier computed values.

property: <int> != 510

# property: expresses an invariant of what we are testing.  If the
# boolean expression evaluates to False, the test has failed.

与普通python一样,表示注释。注释行如下 正在描述的TSTL代码。

  1. 键入tstl nutshell.tstl
  2. 键入tstl_rt--normalize--output nutshell.test

这会在几秒钟内找到侵犯财产的方法 (产生值510),找到该值的最大简单版本 "失败的测试",并生成一个包含 测试。如果我们省略了{overflowerror}tstl 找到了生产的方法E 510,或者(不太可能)找到 在pow调用中产生溢出:任何一个都将被视为失败。

  1. 键入tstl_replay nutshell.test--verbose

这将重放刚才创建的测试。

  1. 注释掉(像在python代码中一样使用)行<;int>;-=3。现在尝试运行tstl_rt

tstl的核心思想是在测试中定义一组可能的步骤, 加上描述什么可以被认为是测试失败的属性,以及 让tstl找出是否存在将 产生测试失败。操作可以是函数或方法调用, 或组合输入数据的步骤(例如,构建字符串 传递给一个解析器),或者,实际上,任何可以用python做的事情。

使用TSTL

tstl安装一些标准工具:tstl编译器本身,tstl;随机测试生成器 tstl_rt;用于生成独立测试的工具,tstl_standalone; 用于重放tstl测试文件的工具,用于 tstl测试的增量调试和规范化,tstl_reduce;以及作为回归运行一组测试的工具,tstl_regression

只需使用命令tstltstl rTtstl rU replaytstl rU reduce即可完成所需的大部分工作。

  • tstl<;filename.tstl>;将一个.tstl文件编译成一个sut.py接口进行测试
  • tstl_rt对当前目录中的sut.py运行随机测试,并将发现的任何故障转储到文件中。test文件
  • tstl_replay<;filename.test>;运行保存的tstl测试,并告诉您它是否通过;使用--verbose可以提供测试执行的相当详细的跟踪信息
  • tstl_reduce<;filename.test>;<;newfilename.tstl>;接受<;filename.test>;对其运行reduce和normalization以生成更短、更易于理解的测试,并将输出保存为<;newfilename.tstl>;

所有这些工具都提供了大量的配置选项;--help将为所有tstl工具生成一个受支持的选项列表。

扩展示例

理解tstl最简单的方法可能是检查 examples/avl/avlnew.tstl(https://github.com/agroce/tstl/blob/master/examples/avl/avlnew.tstl),这是最新版本中的一个简单示例文件 语言格式。

avlnew.tstl为avl树类创建了一个功能非常全面的测试程序。你可以 只用几行字就写得很快而且相当有效 但是,代码:

@import avl
pool: <int> 3
pool: <avl> 2

property: <avl>.check_balanced()

<int> := <[1..20]>
<avl> := avl.AVLTree()

<avl>.insert(<int>)
<avl>.delete(<int>)
<avl>.find(<int>)
<avl>.display()	

这说明在我们 avl树实现测试:intavl。我们定义,在 python,如何创建这些东西,以及我们可以用它做什么 这些东西,然后tstl产生一系列的动作,即 符合我们定义的测试。tstl还检查所有avl树在任何时候 适当平衡。如果我们愿意,比如在avlnew.tstl中,我们也可以 确保我们的avl树"像"一个集合——当我们插入 有些东西,我们可以找到,当我们删除一些东西,我们 无法再找到它。

注意,我们从"raw python"开始导入avl模块sut。当TSTL时 支持在导入中使用from、别名和通配符,您应该始终 用一个简单的导入来导入测试中的模块。这允许tstl识别 要测试的代码自动提供覆盖率,静态分析辅助 测试方法和适当的模块管理。标准库中的实用程序代码, 另一方面,可以以任何方式导入。

如果我们测试这个(或avlnew.tstl)30秒,就会出现这样的情况:

~/tstl/examples/avl$tstl_rt—超时30

Random testing using config=Config(swarmSwitch=None, verbose=False, fastQuickAnalysis=False, failedLogging=None, maxtests=-1, greedyStutter=False, exploit=None, seed=None, generalize=False, localize=False, uncaught=False, speed='FAST', internal=False, normalize=False, highLowSwarm=None, replayable=False, essentials=False, quickTests=False, coverfile='coverage.out', uniqueValuesAnalysis=False, swarm=False, ignoreprops=False, total=False, swarmLength=None, noreassign=False, profile=False, full=False, multiple=False, relax=False, swarmP=0.5, stutter=None, running=False, compareFails=False, nocover=False, swarmProbs=None, gendepth=None, quickAnalysis=False, exploitCeiling=0.1, logging=None, html=None, keep=False, depth=100, throughput=False, timeout=30, output=None, markov=None, startExploit=0)
  12 [2:0]
-- < 2 [1:0]
---- < 1 [0:0] L
---- > 5 [0:0] L
-- > 13 [1:-1]
---- > 14 [0:0] L
set([1, 2, 5, 12, 13, 14])
...
  11 [2:0]
-- < 5 [1:0]
---- < 1 [0:0] L
---- > 9 [0:0] L
-- > 14 [1:-1]
---- > 18 [0:0] L
set([1, 5, 9, 11, 14, 18])
STOPPING TEST DUE TO TIMEOUT, TERMINATED AT LENGTH 17
STOPPING TESTING DUE TO TIMEOUT
80.8306709265 PERCENT COVERED
30.0417540073 TOTAL RUNTIME
236 EXECUTED
23517 TOTAL TEST OPERATIONS
10.3524413109 TIME SPENT EXECUTING TEST OPERATIONS
0.751145362854 TIME SPENT EVALUATING GUARDS AND CHOOSING ACTIONS
18.4323685169 TIME SPENT CHECKING PROPERTIES
28.7848098278 TOTAL TIME SPENT RUNNING SUT
0.179262161255 TIME SPENT RESTARTING
0.0 TIME SPENT REDUCING TEST CASES
224 BRANCHES COVERED
166 STATEMENTS COVERED

对很多人(但不是所有人!)程序,更强大的替代方案 简单的随机测试是使用群测试,它限制了 每个单独测试中的操作(例如,插入但不删除,或查找 但没有顺序遍历(请参见 http://agroce.github.io/issta12.pdf)。

~/tstl/examples/AVL$ tstl_rt --timeout 30 --swarm
Random testing using config=Config(swarmSwitch=None, verbose=False, fastQuickAnalysis=False, failedLogging=None, maxtests=-1, greedyStutter=False, exploit=None, seed=None, generalize=False, localize=False, uncaught=False, speed='FAST', internal=False, normalize=False, highLowSwarm=None, replayable=False, essentials=False, quickTests=False, coverfile='coverage.out', uniqueValuesAnalysis=False, swarm=True, ignoreprops=False, total=False, swarmLength=None, noreassign=False, profile=False, full=False, multiple=False, relax=False, swarmP=0.5, stutter=None, running=False, compareFails=False, nocover=False, swarmProbs=None, gendepth=None, quickAnalysis=False, exploitCeiling=0.1, logging=None, html=None, keep=False, depth=100, throughput=False, timeout=30, output=None, markov=None, startExploit=0)
  11 [2:0]
-- < 7 [1:0]
...
STOPPING TEST DUE TO TIMEOUT, TERMINATED AT LENGTH 94
224 BRANCHES COVERED
166 STATEMENTS COVERED

在这里,方法不是很重要;简单的随机测试 只需60秒就能覆盖AVL树代码。如果我们 通过删除第205行的self.rebalance()调用引入一个bug py,两种方法都会很快报告一个失败的测试用例, 自动减少。默认情况下,随机测试人员将运行测试 以详细模式显示执行过程中发生的情况 这会导致失败。

~/tstl/examples/AVL$ tstl_rt --timeout 30
Random testing using config=Config(swarmSwitch=None, verbose=False, fastQuickAnalysis=False, failedLogging=None, maxtests=-1, greedyStutter=False, exploit=None, seed=None, generalize=False, localize=False, uncaught=False, speed='FAST', uniqueValuesAnalysis=False, normalize=False, silentFail=False, noAlphaConvert=False, replayable=False, essentials=False, quickTests=False, coverfile='coverage.out', swarm=False, internal=False, total=False, progress=False, swarmLength=None, noreassign=False, profile=False, full=False, multiple=False, timedProgress=30, relax=False, swarmP=0.5, stutter=None, highLowSwarm=None, readQuick=False, verboseActions=False, running=False, ignoreProps=False, compareFails=False, nocover=False, swarmProbs=None, gendepth=None, quickAnalysis=False, exploitCeiling=0.1, computeFeatureStats=False, logging=None, html=None, keep=False, noExceptionMatch=False, depth=100, showActions=False, throughput=False, timeout=30, output='failure.26816.test', markov=None, startExploit=0)
  11 [2:0]
-- < 8 [1:0]
---- < 4 [0:0] L
---- > 9 [0:0] L
-- > 18 [1:1]
---- < 15 [0:0] L
set([4, 8, 9, 11, 15, 18])
PROPERLY VIOLATION
ERROR: (<type 'exceptions.AssertionError'>, AssertionError(), <traceback object at 0x1032bf4d0>)
TRACEBACK:
  File "/Users/alex/tstl/examples/AVL/sut.py", line 7960, in check
    assert self.p_avl[0].check_balanced()
Original test has 98 steps
REDUCING...
Failed to reduce, increasing granularity to 4
Reduced test length to 73
Failed to reduce, increasing granularity to 4
Reduced test length to 55
Failed to reduce, increasing granularity to 4
Reduced test length to 41
Failed to reduce, increasing granularity to 4
Reduced test length to 31
Failed to reduce, increasing granularity to 4
Reduced test length to 24
Failed to reduce, increasing granularity to 4
Failed to reduce, increasing granularity to 8
Reduced test length to 20
Failed to reduce, increasing granularity to 4
Failed to reduce, increasing granularity to 8
Reduced test length to 17
Failed to reduce, increasing granularity to 4
Failed to reduce, increasing granularity to 8
Reduced test length to 14
Failed to reduce, increasing granularity to 4
Failed to reduce, increasing granularity to 8
Reduced test length to 13
Failed to reduce, increasing granularity to 4
Failed to reduce, increasing granularity to 8
Reduced test length to 11
Failed to reduce, increasing granularity to 4
Failed to reduce, increasing granularity to 8
Failed to reduce, increasing granularity to 11
Reduced test has 11 steps
REDUCED IN 1.02356314659 SECONDS
Alpha converting test...
int0 = 1                                                                 # STEP 0
avl0 = avl.AVLTree()                                                     # STEP 1
avl0.insert(int0)                                                        # STEP 2
int0 = 6                                                                 # STEP 3
avl0.insert(int0)                                                        # STEP 4
int0 = 8                                                                 # STEP 5
avl0.insert(int0)                                                        # STEP 6
int1 = 20                                                                # STEP 7
avl0.insert(int1)                                                        # STEP 8
int1 = 1                                                                 # STEP 9
avl0.delete(int1)                                                       # STEP 10

SAVING TEST AS failure.26816.test
FINAL VERSION OF TEST, WITH LOGGED REPLAY:
int0 = 1                                                                 # STEP 0
ACTION: int0 = 1 
int0 = None : <type 'NoneType'>
=> int0 = 1 : <type 'int'>
==================================================
avl0 = avl.AVLTree()                                                     # STEP 1
ACTION: avl0 = avl.AVLTree() 
avl0 = None : <type 'NoneType'>
avl_REF0 = None : <type 'NoneType'>
=> avl0 = <avlbug2.AVLTree instance at 0x10311edd0> : <type 'instance'>
REFERENCE ACTION: avl_REF0 = set()
=> avl_REF0 = set([]) : <type 'set'>
==================================================
avl0.insert(int0)                                                        # STEP 2
ACTION: avl0.insert(int0) 
int0 = 1 : <type 'int'>
avl0 = <avlbug2.AVLTree instance at 0x10311edd0> : <type 'instance'>
avl_REF0 = set([]) : <type 'set'>
REFERENCE ACTION: avl_REF0.add(int0)
=> avl_REF0 = set([1]) : <type 'set'>
==================================================
int0 = 6                                                                 # STEP 3
ACTION: int0 = 6 
int0 = 1 : <type 'int'>
=> int0 = 6 : <type 'int'>
==================================================
avl0.insert(int0)                                                        # STEP 4
ACTION: avl0.insert(int0) 
int0 = 6 : <type 'int'>
avl0 = <avlbug2.AVLTree instance at 0x10311edd0> : <type 'instance'>
avl_REF0 = set([1]) : <type 'set'>
REFERENCE ACTION: avl_REF0.add(int0)
=> avl_REF0 = set([1, 6]) : <type 'set'>
==================================================
int0 = 8                                                                 # STEP 5
ACTION: int0 = 8 
int0 = 6 : <type 'int'>
=> int0 = 8 : <type 'int'>
==================================================
avl0.insert(int0)                                                        # STEP 6
ACTION: avl0.insert(int0) 
int0 = 8 : <type 'int'>
avl0 = <avlbug2.AVLTree instance at 0x10311edd0> : <type 'instance'>
avl_REF0 = set([1, 6]) : <type 'set'>
REFERENCE ACTION: avl_REF0.add(int0)
=> avl_REF0 = set([8, 1, 6]) : <type 'set'>
==================================================
int1 = 20                                                                # STEP 7
ACTION: int1 = 20 
int1 = None : <type 'NoneType'>
=> int1 = 20 : <type 'int'>
==================================================
avl0.insert(int1)                                                        # STEP 8
ACTION: avl0.insert(int1) 
int1 = 20 : <type 'int'>
avl0 = <avlbug2.AVLTree instance at 0x10311edd0> : <type 'instance'>
avl_REF0 = set([8, 1, 6]) : <type 'set'>
REFERENCE ACTION: avl_REF0.add(int1)
=> avl_REF0 = set([8, 1, 20, 6]) : <type 'set'>
==================================================
int1 = 1                                                                 # STEP 9
ACTION: int1 = 1 
int1 = 20 : <type 'int'>
=> int1 = 1 : <type 'int'>
==================================================
avl0.delete(int1)                                                       # STEP 10
ACTION: avl0.delete(int1) 
int1 = 1 : <type 'int'>
avl0 = <avlbug2.AVLTree instance at 0x10311edd0> : <type 'instance'>
avl_REF0 = set([8, 1, 20, 6]) : <type 'set'>
REFERENCE ACTION: avl_REF0.discard(int1)
=> avl_REF0 = set([8, 20, 6]) : <type 'set'>
==================================================
ERROR: (<type 'exceptions.AssertionError'>, AssertionError(), <traceback object at 0x10369c128>)
TRACEBACK:
  File "/Users/alex/tstl/examples/AVL/sut.py", line 7960, in check
    assert self.p_avl[0].check_balanced()
STOPPING TESTING DUE TO FAILED TEST
79.552715655 PERCENT COVERED
2.22598695755 TOTAL RUNTIME
15 EXECUTED
1498 TOTAL TEST OPERATIONS
0.408244371414 TIME SPENT EXECUTING TEST OPERATIONS
0.0258889198303 TIME SPENT EVALUATING GUARDS AND CHOOSING ACTIONS
0.706946611404 TIME SPENT CHECKING PROPERTIES
1.11519098282 TOTAL TIME SPENT RUNNING SUT
0.00753235816956 TIME SPENT RESTARTING
1.03021097183 TIME SPENT REDUCING TEST CASES
220 BRANCHES COVERED
164 STATEMENTS COVERED

使用--output,失败的测试可以保存到一个命名文件,并使用standalone.py 工具,转换成一个完全独立的测试用例 不需要TSTL本身。如果没有--output测试仍会保存,但名称基于tstl_rt的进程ID。在这两种情况下,您都可以使用tstl_replay<;testname>;轻松地重新运行保存的测试,甚至无需转换为独立测试,并使用tstl_reduce减少测试。--verbose标志对于重播很有用,因为它将准确地显示测试期间发生的情况。

~/tstl/examples/AVL$ tstl_rt --timeout 30 --output failure.test
Random testing using config=Config(swarmSwitch=None, verbose=False, fastQuickAnalysis=False, failedLogging=None, maxtests=-1, greedyStutter=False, exploit=None, seed=None, generalize=False, localize=False, uncaught=False, speed='FAST', internal=False, normalize=False, highLowSwarm=None, replayable=False, essentials=False, quickTests=False, coverfile='coverage.out', uniqueValuesAnalysis=False, swarm=False, ignoreprops=False, total=False, swarmLength=None, noreassign=False, profile=False, full=False, multiple=False, relax=False, swarmP=0.5, stutter=None, running=False, compareFails=False, nocover=False, swarmProbs=None, gendepth=None, quickAnalysis=False, exploitCeiling=0.1, logging=None, html=None, keep=False, depth=100, throughput=False, timeout=30, output=None, markov=None, startExploit=0)
...
~/tstl/examples/AVL$ tstl_reduce failure.test failure_norm.test
REDUCING...
...
NORMALIZING...
...
~/tstl/examples/AVL$ tstl_replay failure_norm.test --verbose
...
~/tstl/examples/AVL$ tstl_standalone failure_norm.test failure.py
~/tstl/examples/AVL$ python failure.py
Traceback (most recent call last):
  File "failure.py", line 98, in <module>
    check()
  File "failure.py", line 45, in check
    assert avl2.check_balanced()
AssertionError

开始的最后一个有用的提示是,有时您可能需要测试一些东西 (例如,用c实现的库),失败的测试会使python解释器崩溃。这是可能的, 但需要一些努力。首先,使用--replayable选项运行tstl_rt。这导致发电机 在运行测试的目录中保存一个文件currtest.test:该文件保存当前测试。如果随机测试仪崩溃,这将包括导致崩溃的操作。在少数情况下,过去测试的行为也与崩溃相关(重新加载模块并不能真正重置系统的状态——例如,与硬件交互)。对于这些情况,请使用--total并查看文件fulltest.test,其中包含随机测试仪执行过的所有操作。

currtest.testfulltest.test文件的工作方式与普通的tstl文件一样,可以用replay实用程序重放或转换为独立文件。但是,要使测试缩减和规范化正常工作,必须通过将--sandbox参数传递给tstl_reduce

通过进入无限循环而失败的测试呢?与用于碰撞的技术相同。但是,您需要在有时间限制的情况下运行tstl_rt(例如,在类unix系统上使用ulimit)。tstl_reduce实用程序提供了一个处理此类测试的--timeout参数,但目前这只适用于支持ulimit的系统。在极少数情况下,您可能会有测试执行锁定,因为例如,失败会导致从标准输入读取。如果您碰到这个,请与我联系。

最后,如何将tstl测试与更传统的方法(例如pytest)集成?examples目录中的文件test_tstl_regressions.py显示了一种方法。如果将所有感兴趣的tstl测试添加到sut.py所在目录下的tstl_tests目录中,则可以使pytest运行所有tstl测试。也许更有趣的是,这个文件还包装了一个简单的调用程序,强制pytest执行60秒的随机测试,作为一个健全性检查。你可以改变骗局随机测试的图形化很容易——通常,添加"-swarm"是一个好主意。

tstl和突变检测

使用 通用转换器工具, 你可以很容易地调查你的tstl测试工具的能力 漏洞。例如,如果我们想看看 使用我们的AVL线束进行随机测试,我们可以这样做:

~/tstl/examples/AVL$ pip install universalmutator
~/tstl/examples/AVL$ tstl avlnew.tstl
Generating harness core using config=Config(tstl='avlnew.tstl', stats=False, checkFailureDeterminism=False, pylib=False, defaultReplay=False, forceRefExceptionMatch=False, enumerateEnabled=False, forceStrongRefExceptionMatch=False, classname='sut', version=False, noCover=False, debug=False, output='sut.py', noReload=False, ignoreAngles=False, coverReload=False, coverInit=False)
~/tstl/examples/AVL$ mkdir mutants
~/tstl/examples/AVL$ mutate avl.py --mutantDir mutants
*** UNIVERSALMUTATOR ***
MUTATING WITH RULES: universal.rules, python.rules
1497 MUTANTS GENERATED BY RULES
...
768 VALID MUTANTS
591 INVALID MUTANTS
138 REDUNDANT MUTANTS
~/tstl/examples/AVL$ analyze_mutants avl.py "tstl_rt --timeout 10" --mutantDir mutants
ANALYZING avl.py
COMMAND: ** ['tstl_rt --timeout 10'] **
#1: [0.0s 0.0% DONE]
  mutants/avl.mutant.89.py NOT KILLED
  RUNNING SCORE: 0.0
#2: [10.43s 0.13% DONE]
  mutants/avl.mutant.507.py KILLED IN 0.689107179642
  RUNNING SCORE: 0.5
#3: [11.12s 0.26% DONE]
  mutants/avl.mutant.312.py KILLED IN 2.01887583733
  RUNNING SCORE: 0.666666666667
#4: [13.14s 0.39% DONE]
  mutants/avl.mutant.165.py NOT KILLED
  RUNNING SCORE: 0.5
...

这报告了变种人的得分,我们的测试甚至没有 掩护,如果我们只瞄准 应用程序编程接口。而且,坦白地说,有更简单的方法可以看出哪些代码没有被覆盖 比使用变异测试!所以我们可以把分析限制在 代码实际上包含在avl线束中,使用一个非常大的 超时:

~/tstl/examples/AVL$ tstl_rt --timeout 120 --internal >& avlnew.lines
~/tstl/examples/AVL$ check_covered avl.py avlnew.lines coveredmutants.txt --tstl --mutantDir mutants
~/tstl/examples/AVL$ analyze_mutants avl.py "tstl_rt --timeout 10" --mutantDir mutants --fromFile coveredmutants.txt
ANALYZING avl.py
COMMAND: ** ['tstl_rt --timeout 10'] **
#1: [0.0s 0.0% DONE]
  mutants/avl.mutant.17.py KILLED IN 0.574796915054
  RUNNING SCORE: 1.0
#2: [0.58s 0.14% DONE]
  mutants/avl.mutant.186.py KILLED IN 0.581020116806
  RUNNING SCORE: 1.0
#3: [1.16s 0.28% DONE]
  mutants/avl.mutant.692.py NOT KILLED
  RUNNING SCORE: 0.666666666667
...

更好测试的提示

有时仅仅做tstl_rt甚至tstl_rt--swarm是不够的。还有其他改进测试的方法。在许多情况下,一个特别强大的功能是根据代码行使用函数的大小来指导测试。为此,首先让tstl确定大小:

tstl_rt--generateloc sut.loc--timeout 120

然后使用生成的文件来指导测试:

tstl_rt--biasloc sut.loc

这也是一个好主意,为了更快的测试(因为随机的力量 测试的一部分是每分钟产生大量的测试, 使用--nocover关闭代码覆盖率收集。不是这样的 如果你想看看你的测试是否能很好地覆盖你的代码, 但对于踏板到金属虫子狩猎,这通常是一条路要走。

还有其他事情可以改进测试。--profileprobs 选项收集有关TSTL中每个操作的频率的信息 在测试过程中使用了安全带,并且线性偏置随机作用 选择较少采取的行动。这慢了 在大多数情况下,测试生成基本上是向下的,但是对于许多 程序(特别是复杂的程序)它也极大地改进了代码 覆盖率和故障检测,通过探索难以击中的动作,以及 与运行sut相比,生成输入数据的时间更少。在 在这些情况下,由于尝试 可能的残疾行为远不止补偿 因为测试质量的提高。因为两者都依赖于环境 动作概率,--profileprobs--biasloc是 很遗憾不兼容。

对于某些程序,线束文件的结构会减慢测试速度 生成和--usependencies选项可以通过 一个2-10倍的系数。但是,对于大多数程序,这个选项会减慢速度 测试生成大约是两倍。

由于这些选项的效用差别很大,最好是 只要试试就行了。对于--profileprobs您应该看到 如果对您的测试问题有效,则增加代码覆盖率 (可能伴随着生成的测试数量的大幅度下降) 对于--usependencies您应该会看到 执行的测试/测试操作数。

您还可以尝试一种以覆盖率为指导的"遗传算法"方法,它利用"高覆盖率"测试:

tstl_rt--利用0.8--pmutate 0.5

添加--reducepool有时也会提高此方法的性能。

您可以调整利用率和变异参数,以查看它们是否有所改进。 结果。你甚至可以合并代码偏差行或基于配置文件的行 利用方法和/或群测试的概率。不幸的是,使用--exploit确实意味着您无法摆脱--nocover来避免计算代码覆盖率的开销。

我们正在研究让TSTL做实验的方法 自动并通知您最佳配置用于测试A 提供安全带。

要获得一组非常快速的"回归测试",您可以运行 使用--quicktests选项在良好配置下运行很长时间, 生成一组代码覆盖率高的非常短的测试。

故障定位

tstl支持自动故障定位。如果你有找到 一个虫子,你可以通过 运行类似于:

tstl rt--本地化--多个

这将运行tstl一个小时,并生成许多失败 测试用例(如果你的bug在一小时内相对容易被发现的话)。 然后报告20个最有可能出错的语句和分支 在测试代码中。有些代码可能涉及到 类似于打印断言值或错误处理,但是 很有可能你会在本地化中找到错误代码 结果,根据我们的经验。事实上,跑五分钟就够了 如果五分钟足够找到 你的虫子几次了。注意,如果你有不止一个错误,结果会更糟!

基于swarm的工作流

有效使用群测试的一种方法是应用有向群 测试,一种关于swarm如何与代码交互的方法 覆盖率用于提高很少覆盖的语句的覆盖率 分支机构。

为此,请使用tstl_rt(一小时或更长时间)运行初始测试,选项如下:

  • --swarm
  • --saveswarmcoverage<;文件名1>;
  • --fullcoverage<;文件名2>;

这将测试您的程序,并生成两个感兴趣的文件。 <;filename2>;将包含一个带有分支和 涵盖的报表,按涵盖的次数排序。 这基本上是一种简化的输出 熟悉fromgcov或其他工具。你可以看看这个 文件,并标识外观有趣但很少涉及的代码。

然后,获取 有趣的代码,并使用这样的tstl_directedswarm工具:

tstl_directedswarm<;filename1>;"<;coverage target>;"<;probfile>;

<;filename1>;是您在Swarm的原始运行中生成的文件 测试。)这将试图找出哪些操作有帮助("触发器") 并妨碍(压制)对 目标代码,并生成一个文件(probfile),该文件的使用概率为 集中群体测试。如果工具没有识别任何触发器或 抑制器,使用--confidence选项和 数字小于0.95;所需的置信度越低,则 你可能会找到触发和抑制因素,但是 它们必须是有意义的——你可以试着慢慢降低,直到你得到 一些结果。然后再次运行测试,如下所示:

tstl_rt--swarm--swarmprobs<;probfile>;

通常,您应该能够很好地覆盖很少涉及的代码 方式。因为经常覆盖很少覆盖的代码 发现以前从未见过的有趣的新代码 一旦您研究了很少涉及的代码,请重复此过程 你的第一次跑步。当然,你可以,商店群 TSTL集中跑步的覆盖率和全覆盖率统计,以及 继续探索。

进行定向群测试的一个更系统的方法是尝试:

tstl_analyzeswarm<;文件名1>;<;前缀>;--截止0.5

为所有命中的覆盖目标生成触发器和抑制器 在运行期间,分组为等价类(具有相同 一组触发器和抑制器),并按中命中率最低的目标排列 每个等价类。输出将存储在开始的文件中 带有前缀:一组名为<;prefix>;.n.probs的文件,可以 与--swarmprobs和单个.class文件一起使用,与所有目标一起使用, 触发器、抑制器和类的最小频率,以及 指向相关概率文件的指针。只是在生成的 最稀有目标类的概率文件是一种很好的方法 进行定向群测试。上面的0.5可以是任何 截止点,在这个点上至少有一小部分测试击中目标 被认为是经过很好的测试和忽视的。将此值设置为0.01can 运行良好,初始运行产生大量测试。

tstl和美国模糊lop(afl)模糊器

您甚至可以使用afl(http://lcamtuf.coredump.cx/afl/)生成 TSTL测试。您需要安装afl本身和python afl 包(或从github获取代码,网址为https://github.com/jwilk/python-afl" rel="nofollow">https://github.com/jwilk/python afl)。然后可以在任何目录中使用afl模糊 TSTL线束:

tstl_afl_fuzz——输出<;输出目录>;——输入<;输入目录>;

这将使用一些(通常是好的)默认设置首先使用tstl 为afl生成一些良好的启动测试,然后运行afl 在SUT上呆一天。一天可能不够,所以 --timeout参数由TSTL随机测试仪支持。你 也可以通过添加--swarm来使用swarm测试。还有其他的,更少的 经常使用,也有选择。AFL产生的失败测试将 存储为当前目录中的aflfail.<;pid>;.test。一块 建议:<;outputdir>;应该是ramdisk,除非您 想真正锤炼你的固态硬盘(甚至不要考虑这样做 实际的硬盘驱动器)。

您还应该尝试--persist选项来tstl_afl_fuzz 通常会大幅度提高模糊速度 显著改善AFL结果(因为吞吐量非常关键);但是, 与非持久模式相比,这一点测试得不太好。用 更多的测试,这可能会成为默认设置,因此您可以 想要跳到前面,并且只有在 持久模式似乎会导致问题。

这是一个强大的测试选项,因为它可以让你使用afl 模糊事物的启发式方法 只是AFL。您可以设置复杂的tstl属性,混合语法 生成和api调用序列,并执行差异测试 tstl风格,但使用afl的调谐输入生成方法。主要 缺点是afl确实期望比tstl快得多的可执行文件。 所以你可能需要跑几天来改进 TSTL可以在一小时内完成,除非你的SUT异常。但它是 当然这是一个很有吸引力的选择,可以进行为期一周的重型测试 tstl_rt没有发现任何问题。

注意,如果不使用tstl_afl_fuzz而是直接调用 py afl fuzz您可能(除了在mac os上,内存限制 无论如何都不工作)需要一个大的-m才能使tstl工作。

在引擎盖下,tstl_afl命令接受一个字节文件,并解释每n个字节(n 取决于你的安全带有多少动作)。 使用sut.pytstl操作的索引(将操作数模化) 像往常一样。当tstl_afl检测到故障时 它还生成一个名为 aflfail.<;pid>;.测试。您甚至可以使用--swarm来解释前4个字节 作为种子来控制群体测试,从而允许afl使用群体测试;这有一个缺点,即 文件将被其他tstl工具错误地解释,除非您 把"密码"传给他们>选项。大多数tstl工具 --afl表示要读取的测试的选项是afl格式的, 以及表明它们是群测试的aflsarm

tstl_afl对于转动单个 使用--alwayssave选项将AFL字节文件转储到正常的TSTL测试文件中,该选项将从基于字节的输入创建TSTL测试文件转储到当前目录中。

还有一些工具可以将大量文件转换为afl格式或从afl格式转换为afl格式。 tstl_toafl只需获取现有的tstl测试文件 将它们转换为AFL字节输入,并且tstl_fromAFL是否符合预期 相反(并接受一个参数,表示文件是swarm格式的)。tstl_aflcurpus随机生成触发新sut的输入 覆盖以启动AFL,但通常更容易生成快速测试 tstl_rt--quicktests并用tstl_to afl>转换这些测试。 tstl_aflcurpus确实允许使用afl swarm格式,但是 用--swarm运行它。由于swarm格式的工作方式,它 不幸的是,目前无法提取swarm格式测试 来自标准TSTL测试。

TSTL的"小支票"

tstl_smallcheck是一个特殊用途的测试生成器,它使用 深度优先搜索以完全生成测试 深度限制。工具输出 覆盖率增加测试,如果遇到故障则停止。如果深度超过3至 最多10步,除非失败。如果你用完了 耐心点,你可以用ctrl-c中断进程,工具会 保存发现的测试。

深入进行"详尽"测试的一种方法 是使用--recursive选项从覆盖范围增加开始探索 使用相同的 作为原始运行的深度(以及较小的初始深度)。

如果你想收集所有失败的测试,而不仅仅是第一次就停下来, 您需要使用--multiple选项。因为它们体积小 对彻底探索的期望(你使用了这个工具, 毕竟,这个工具既不提供 覆盖测试或失败 避免滑倒的危险。

除了--recursive之外,您还可以使用--visited--visitedlist来避免再次访问 在dfs期间已经探索了状态;但是,这需要一些 关心。如果工具失败,或者测试似乎无效/不正确,您可能需要使用 --defaultreplay,因为基于状态的回溯不起作用。在 很多情况下,由于在这种情况下进行状态比较的成本很高, 跟踪访问过的州甚至可能没有多大帮助。

使用tstl_rt的随机测试可能总是更多 比这种方法有效,但是tstl_smallcheck可以提供 保证tstl_rt不能,例如 四步可以 导致任何失败。使用--from tests选项从现有的快速测试中启动一个小检查是增加测试信心的一种方法。

tstl和假设

你们中的一些人可能会问:"TSTL与假设有何不同?" https://hypothesis.readthedocs.io/en/latest/测试工具?有几个 答案。首先,tstl可能没有假设那么完美, 马上!更重要的是,假设和tstl 生成测试,但它们主要用于生成不同的 各种各样的测试。假设是我们认为的快速检查 family:如果有一个函数f将列表作为输入,则 弦,或者更复杂的东西,假设很可能是 想要使用。如果你有一组函数,fgh,以及 它们不仅返回对象,还修改不可见的系统状态(但是 也可以返回可能是其他函数输入的内容)。 需要TSTL。可以执行基于状态的方法调用序列测试 假设,但是tstl可能更容易,这就是tstl 建造的。所以,如果你在测试排序实现,假设 几乎肯定好多了。如果你在测试 文件系统,您可能需要查看tstl。如果你在测试 以字符串作为输入的解析器,这两种工具可能都很有用, 取决于你的情况。典型用户的另一个区别是,tstl对执行差分/引用测试有相当大的内置支持,在这里,sut与引用实现进行比较,可能使用一些代码来处理预期的差异(参见好好看看这有多强大)。最后,tstl是作为一个实用的测试工具而构建的,但是它的设计很大程度上受到了这样一个决定的影响:它将成为一个试验新的软件测试算法的平台。

相似之处在于tstl和假说看起来都不像 传统的单元测试。相反,他们让你定义 有效输入(一些数据值,或者在tstl中是一个方法序列 更像传统的 做一些事情,然后检查它的单元测试)并断言 关于有效输入下系统行为的属性。

处理大量错误的提示

如果你用一个好的工具测试真正的软件,你很可能会发现 问题。有几种方法可以解决这个问题。首先,使用 --normalize在执行--multiple时,使用tstl-rt可以帮助您。 在某些情况下(文件系统)规范化(甚至缩减)会 太远了。在美国宇航局的测试中,我们发现"最后一次行动"是 对不同的错误有很好的启发性。在测试中使用--keeplast 使用tstl_reduce)强制减少和规范化 最后一步。标准化仍然可以移动它,或者 改变它使用的游泳池,但要更小心地改变 实际执行的操作。还有一个工具tstl_triage 为一组测试获取一个全局表达式,运行它们,并报告 不同的(启发式)故障特征。特别是,它给你 每个签名的最短测试。记住,分类需要 glob表达式(用引号括起来)不是文件列表。这样就可以了 处理超出shell扩展限制的偶数组测试。 我们假设你不需要在回归中处理那么多测试, 但对于分类,谁知道呢?另一个工具,tstl_fpf tstl_triage的参数,但不将测试分组 很可能是同一个bug,它对所有测试进行排序,这样 使用 "最远点优先"(FPF)方法 Chen等人在PLDI 2013中。

更多详细信息

关于tstl的更多细节,最好的起点是 STTT期刊论文: http://agroce.github.io/sttt17.pdf。 也有美国宇航局的正式方法(NFM)和国际研讨会 2015年软件测试与分析论文 http://agrice.github.io/nfm15.pdfhttp://agrice.github.io/issta15.pdf,有一些实现 最新和 完成论文。尤其是NFM的论文"一种 "测试"有一个不推荐使用的语法和其他问题,但是 tstl核心思想的简明解释:dsl嵌入满 编程语言, 旨在使测试(和构建测试工具)变得简单。

最近有一篇文章描述了测试规范化,一个特性 TSTL独有的,更详细地说,http://agrice.github.io/issta17.pdf" rel="nofollow">http://agrice.github.io/issta17.pdf"以及 描述如何使用tstl的测试操作命令的工具文件 (http://agrace.github.io/issta17tool.pdf" rel="nofollow">http://agrace.github.io/issta17tool.pdf)。

nfm和issta论文使用了tstl语法的早期版本,它标记了 带有%符号的池和TSTL构造。"现代"TSTL使用"<;gt;由 默认值,但如果出于某种原因,您需要在代码中使用<;>;(以及 为将来的C++版本做准备)可以关闭,只支持%。

注意上面的文档是初步的。一旦您了解了基本工具(tstltstl_rttstl_replaytstl_reduce)之后,开始的最佳方法是检查示例目录并尝试真正的tstl测试 马具。对于勇敢者,阅读tstl/randomtester.py提供 如何(有效地)在通用 测试工具,其中tstl提供了到底层的接口 要测试的应用程序/库。

注意事项

请注意,tstl最初是为python 2.7编写的,大部分都是以这种方式开发/测试的,而且还没有用python 3.0+进行很好的测试。 不过,多亏了不来梅先生和特拉维斯 测试检查tstl在python 3.6上是否工作正常。早期3.0+版本 可能有些"问题"。

开发人员信息

目前还没有开发人员文档,将来可能会有所改变。 tstl的最佳安定性测试是编译并运行(使用tstl_t)avl 例子。删除对avl.py中平衡函数的任何调用 代码应该会导致tstl生成失败的测试用例。

学分

谁负责TSTL?

  • alex groce(agroce)编写了这个文件和大多数当前的代码库,并正在运行这个节目。如果TSTL有问题,那是我的错,不要责怪下面的任何人。

  • josie holmes(josieholmes)对核心语言设计的变化做出了贡献,并负责各种减滑策略的思想(和一些代码),加上loc偏差工作和markov事情。在乔西工作之前,tstl非常难读,而且效率要低得多。

  • jervis pinto是tstl的另一个原型,他在早期设计和代码的各个部分都有自己的指纹,这些部分构成了tstl的基础。

  • Pranjal Mittal贡献了许多关键元素,包括为PIP版本准备TSTL作为一个有用工具的最初努力,并帮助宣传TSTL。

  • pooria azimi添加了<;int,1>;表示法,这是最重要的更改之一,并消除了通过python函数和提交基于点的保护来处理绑定的非常笨拙的方式的需要。没有这个,你真的没有一个有用的tstl。

  • Kevin Kellar开发了一个(beta)Java版本的TSTL:

  • 我(Alex)的其他研究生(Amin Alipour、Rahul Gopinath、Arpit Christi、Chaoqiang Zhang、Shalini Shamasunder)和我的几乎所有研究生(Iftekhar Ahmed)对TSTL诞生的一般智力环境做出了贡献。

  • 北亚利桑那大学的cs 499和俄勒冈州立大学的cs 362 562和569的学生贡献了很多想法,以及一些具体的语言/工具更改或错误报告。这些太多了,我不记得有人问过"你为什么这么愚蠢?"在课堂上,得到了我认为这实际上是一种愚蠢的做事方式。

  • Ned Batchelder、David R.Maciver和John Regehr在TSTL中没有实际的代码,但他们都在不同的实现方面做出了重要贡献,这些贡献超出了TSTL从整个软件测试(研究)社区中自由窃取的一般免责声明。

  • Pyfakefs团队(Github上的Bean Bremen和JMCGeheeiv先生)真的 和我一起测试了pyfakefs,结果发现 对tstl的改进,特别是对差分测试的改进。 最近,Bean不来梅先生率先生产了TSTL 与Python3兼容,现在看来大部分都已经完成了!

  • jakub wilk帮助修改了python afl TSTL/AFL集成工作得更好。

  • Corey Kosak帮你把这个自述变成了 实际上喜欢读书,而且比 以前的版本。

*你还记得上面那个星号吗?脚注是tstl是一种小语言。然而,在另一个意义上,它嵌入了所有的python,这使得它相当大。这取决于你对它的看法。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java是通过internet与客户机/服务器应用程序交互的最佳方式吗?   awt为什么不推荐java getPeer调用?   java类的添加方法   java在启动tomcat时传递数据库身份验证详细信息   如何创建具有关联值(如Swift enum)的Java枚举?   如何清理这个Java示例内存   visualvm如何在Java Visual VM中解释大型自时结果?   当实例变量的名称与参数变量相同时,java调用实例变量   eclipse缺少工件组织。硒。硒:seleniumjava:jar:3.14.59   java如何在Android Studio中Expandablelistview的子布局中使用Listview   从Guava 19升级到20时出现java编译错误   java在Maven 2中,我如何知道哪个依赖项来自于可传递依赖项?   需要javascript简单数据分级应用程序支持   接受特定对象或其子类型的java通用方法   在Java中剥离HTML   错误的Java字符串连接   Mybatis,Mysql中重复更新查询的java语法错误