OO第三单元作业

OO第三单元作业

BearHuchao 2020年5月23日

JML这个东西,放眼全国,北航独此一家。

——诸彤宇

前言

本单元的主题是形式化设计与验证。软件测试无法证明系统不存在缺陷,也不能证明它符合一定的属性。只有形式化验证过程可以证明一个系统不存在某个缺陷或符合某个或某些属性。本单元作业采用了JML这一形式化语言来进行描述。

JML语言的理论基础及应用工具链

The Java Modeling Language (JML) is a specification language for Java programs, using Hoare style pre- and postconditions and invariants, that follows the design by contract paradigm. Specifications are written as Java annotation comments to the source files, which hence can be compiled with any Java compiler.

——Java Modeling Language - Wikipedia

理论基础

JML使用类似Javadoc的形式将对方法的形式化描述以特定语法呈现给开发测试者。在使用中,以表达式为基本单位,组合使用表达式对方法规格和类型规格进行描述。其基本结构如下表所示:

JML新增表达式一览
类型 名称 备注
原子表达式 \result 该非void方法返回结果
\old(expr) expr在该方法执行前的值
\not_assigned(elems, …) elems是否在方法中被赋值
\not_assigned(elems, …) 同上
\nonnullelemets(container) container中不包含null
\type(type) 返回type对应的Class
\typeof(expr) 返返回expr对应的Class
量化表达式 \forall(vars; range; expr) 全称量词
\exists(vars; range; expr) 存在量词
\sum(vars; range; expr) 求和
\product(vars; range; expr) 求积
\max(vars; range; expr) 求最大值
\min(vars; range; expr) 求最小值
\num_of(vars; range; expr) 求可满足的元素个数
集合表达式 JMLObjectSet {T x | modify} 抽象集合构造器
操作符 <: 子类型关系
<==> 等价关系
==> and <== 推理操作符
\nothing 变量引用:空集
\everything 变量引用:全集

此外,在进行方法规格描述的时候,还需要用到前置条件(requires)、后置条件(ensures)和副作用范围限定(assignable, modifiable)相关的关键词来进行进一步的描述。除此之外,还有normal_behavioralsoexceptional_behaviorsignalssignals_only等描述方法正常与异常的判断标准和返回值。

Full documentation of JML syntax is available in the JML Reference Manual.

工具链

A variety of tools provide functionality based on JML annotations. The Iowa State JML tools provide an assertion checking compiler jmlc which converts JML annotations into runtime assertions, a documentation generator jmldoc which produces Javadoc documentation augmented with extra information from JML annotations, and a unit test generator jmlunit which generates JUnit test code from JML annotations.

Independent groups are working on tools that make use of JML annotations. These include:

  • ESC/Java2 [1], an extended static checker which uses JML annotations to perform more rigorous static checking than is otherwise possible.
  • OpenJML declares itself the successor of ESC/Java2.
  • Daikon, a dynamic invariant generator.
  • KeY, which provides an open source theorem prover with a JML front-end and an Eclipse plug-in (JML Editing) with support for syntax highlighting of JML.
  • Krakatoa, a static verification tool based on the Why verification platform and using the Coq proof assistant.
  • JMLEclipse, a plugin for the Eclipse integrated development environment with support for JML syntax and interfaces to various tools that make use of JML annotations.
  • Sireum/Kiasan, a symbolic execution based static analyzer which supports JML as a contract language.
  • JMLUnit, a tool to generate files for running JUnit tests on JML annotated Java files.
  • TACO, an open source program analysis tool that statically checks the compliance of a Java program against its Java Modeling Language specification.
  • VerCors verifier

由上述描述可知,JML有丰富的应用工具链。其主要的工具包括编译器jmlc,文档生成器 jmldoc,单元测试程序jmlunit

JMLUnitNG

工具的使用

下载相关工具(jmlunitng.jaropenjml.jarjmlruntime.jarjmlspecs.jar),使用命令行工具生成测试文件。

使用结果

非常遗憾,在本人的作业中,由于使用了lambda表达式等Java 8语言特性,这使得使用老旧的工具进行验证不现实。

JML_Error

另外,由于该工具不能对接口类以及抽象方法类进行基于JML的形式化验证,遂放弃自行生成测试代码的想法。

JML_Interface

补救措施

由于本人无法在本次作业的基础上对JMLUnitNG进行测试,故参照其他同学的测试结果,结合规格对生成的测试用例和数据进行简要分析。

JML_Example_by_pgh

可以看出,测试工具所使用的测试数据和用例都是基于一些极端值的,例如,对int类型,分别使用Integer.MAX_VALUE, Interger_MIN_VALUE以及0;对String类型,则使用""(空字符串)和null(空引用);对于其他更一般的对象,则仅使用null这一种值进行测试。(下图)

JML_Example-1_by_pgh

因此,仅仅使用本工具并不能进行完全的形式化验证工作,更准确的方法是使用SMT Solver等工具进行真正的形式化验证。

设计策略

基本模型

使用HashMap管理数据,在此基础上,使用图论的相关方法对JML规格中的方法进行实现。更具体的请参照下列各图:

第9次作业类图:

U3HW9_UML

第10次作业类图:

U3HW10_UML

第11次作业类图:

U3HW11_UML

主要困难及解决方案

主要的困难是关于图的相关内容。由于Person类中使用ArrayList来保存了相关的人员关系情况,故可以将其视为图中的结点,并将该ArrayList视为邻接矩阵,进行图的相关计算。

在进行最短路径的计算过程中,使用了优先队列进行了优化。

另外,在Network的规格要求中出现了queryGroupPeopleSum, queryGroupRelationSum, queryGroupValueSum, queryGroupConflictSum, queryGroupAgeMean, queryGroupAgeVar等较多的查询方法。如果使用遍历计算相关结果,会造成极大的资源浪费,进而超时。故本人在Group类中维护了与这些查询方法相关的变量。当Group修改时,更新相关变量,在查询时直接返回即可,节省了大量计算资源。

本次作业还使用了并查集算法。

Bug分析

目前最重大的一次Bug是在第10次作业中,由于看漏了JML中对group.length的约束(对最大值1111的特判),导致在强测中失分。

另外,在第11次作业中遇到了CTLE的问题,正在排查有关情况。

心得和体会

  • 在规格撰写方面,需要时刻将方法的变与不变放在心上,灵活运用离散数学学科中的相关内容对程序运行的前置条件和后置条件进行思考。在此基础上,需要对JML的语法熟练掌握。另外,需要注意方法在正常情况和异常情况的相关触发条件和返回值。
  • 在规格理解方面,首先需要静下心来,对方法规格进行概览,不要漏看、错看。在此基础上,对方法规格的理解需要结合类型规格。另外,在理解出现困难时,不妨看看方法的名称,结合文字描述再去看形式化语言描述往往会事半功倍。

猜你喜欢

转载自www.cnblogs.com/bearhuchao/p/OOUnit3.html