JML(Java Modeling Language)是用于对Java程序进行规格化设计、行为接口规格语言(Behavior Interface Specification Language,BISL)。通过JML及其支持工具,不仅可以基于规格自动构造测试用例,并整合了SMT Solver等工具以静态方式来检查代码实现对规格的满足情况。
//@annotation 行注释
/* @ annotation @*/ 块注释
原子表达式
\result
表达式:表示一个非void
类型的方法执行所获得的结果,即方法执行后的返回值。\old( expr )
表达式:用来表示一个表达式 expr 在相应方法执行前的取值。 \not_assigned(x,y,...)
表达式:用来表示括号中的变量是否在方法执行过程中被赋值。\not_modified(x,y,...)
表达式:与上面的\not_assigned表达式类似,该表达式限制括号中的变量在方法执行期间的取值未发生变化。\nonnullelements( container )
表达式:表示 container
对象中存储的对象不会有 null
\type(type)
表达式:返回类型type
对应的类型(Class)
\typeof(expr)
表达式:该表达式返回expr
对应的准确类型。量化表达式
\forall
表达式:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。\exists
表达式:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束。\sum
表达式:返回给定范围内的表达式的和。\product
表达式:返回给定范围内的表达式的连乘结果。\max
表达式:返回给定范围内的表达式的最大值。\min
表达式:返回给定范围内的表达式的最小值。\num_of
表达式:返回指定变量中满足相应条件的取值个数。操作符
b_expr1<==>b_expr2
或者 b_expr1<=!=>b_expr2
,其中b_expr1
和b_expr2
都是布尔表达式,这两个表达式的意思是 b_expr1==b_expr2
或者 b_expr1!=b_expr2
。b_expr1==>b_expr2
或者 b_expr2<==b_expr1
。\nothing
\everything
requires P
; 意为“要求调用者确保P为 真”。ensures P
;意为“方法实现者确保方法执行返回结果一定满足谓词P的要求,即确保P为真”。assignable
或者modifiable
;指方法在执行过程中会修改对象的属性数据或者类的静态成员数据,从而给后续方法的执行带来影响。pure
关键词signals (***Exception e) b_expr
invariant P
,其中 invariant
为关键词,P
为谓词。constraint
:对象的状态在变化时往往也许满足一些约束,这种约束本质上也是一种不变式。用constraint
来对前序可见状态和当前可见状态的关系进行约束。::openjml.bat
@echo off
set java=java -jar "%~dp0openjml.jar" -noPurityCheck
set prove=-prover cvc4 -exec "%~dp0Solvers-windows\cvc4-1.6.exe"
set runtime=-cp %~dp0jmlruntime.jar;
set allparam=
set rac=
:param
set str=%1
if "%str%"=="" (
goto end
)
if "%str%"=="-rac" (
set rac=rac
)
if "%str%"=="-prove" (
set str=%prove%
)
set allparam=%allparam% %str%
shift /0
goto param
:end
if "%allparam%"=="" (
goto eof
)
rem remove left right blank
:intercept_left
if "%allparam:~0,1%"==" " set "allparam=%allparam:~1%"&goto intercept_left
:intercept_right
if "%allparam:~-1%"==" " set "allparam=%allparam:~0,-1%"&goto intercept_right
:eof
%java% %allparam%
set filename=
if "%rac%"=="rac" (
:input
set /p filename=Input file name:
if "%filename%"=="" (
goto input
)
java %runtime% %filename%
)
pause
将上述代码放在下载有JML的文件夹中,再将该文件夹加入环境变量。理论上就可以通过命令行使用openjml
命令了。
但由于一些原因,我未能成功配置,报错图就不放了。但查阅多处博客发现OpenJML的局限,比如规格不能存在exist
,forall
。
未能成功配置,但查阅多处博客发现JMLUnitNG测试主要侧重于边界条件,比如对传入数据的可行域取边界进行测试。
这次测试主要依赖的还是数据生成器以及输出对拍来完成,有些规格或是测试规定的数据范围让这种测试方法更加便捷有效。而且本次作业针对一些代码实际实现可以手动构造一些特殊样例,来进行满载的测试。在类中的实现有时也不能完全按照JML规格完成,比如加入缓存之类的考量,这些使得类变为一个整体,不能单单测试某一个方法。
Network
和Person
的JML规格,选取适合的容器来实现,因为社交关系系统的指令偏重查询,所以我在MyPerson
中的acquaintance``value
,MyNetwork
中的people
均采取hashmap
的容器来存储,利用空间换时间,提高查询效率。在Network
中查询两人是否处于同一连通块时采取bfs
的算法来完成。Group
类,因为数据测试量足够大,所以需要在保证正确性的基础上思考如何提高效率。在Group
类中采取缓存中间变量的方式,每一次往组内添加人时修改相应中间变量,将操作分散到每一次addtoGroup
中,减少了不必要的遍历。HashMap
的遍历速度在大数据量下慢于ArrayList
,所以在涉及到遍历的地方均增添了ArrayList
的成员变量用来遍历操作;还对HashMap
的初始化影响速率问题做了调整,以测试量的满载按规则计算初始化值。这次作业难度提升巨大,主要是两个算法的考量,最短路和点双分量的求解。
最短路径的求解一开始采用迪杰斯特拉和弗洛伊德这两种常用的方法,但在特意构造的数据上均会被卡死,思考很久也未能想到解决办法,最后看到群里的讨论,再去搜索了相关的资料,采取了优先队列的迪杰斯特拉算法来求解路径,也不需要缓存,因为增添缓存路径会十分麻烦,而且优化后的复杂度也能够符合题目要求。
点双分量的求解理解起来也较为困难,仿照博客的写法构造了相关算法函数。
连通块数量的计算被第二次作业中的并查集很好的包含,在加人时连通块数量加1,加关系时若不联通则减1。
总的来说,还是在按照JML规格来实现相关内容,最终也出现像老师所说network类复杂程度较高的结果。可以将一些图相关的操作提取出来,单独成类,这样结构层次更加清晰明了。
原文:https://www.cnblogs.com/xys2020/p/12922205.html