zoukankan      html  css  js  c++  java
  • 【BUAA OO Unit3】史上最全OpenJML摸索实录

    史上最全OpenJML摸索实录

    本文为笔者OpenJML的安装配置及使用,以及用JunitNG进行自动测试的探索实录。OpenJML及JunitNG这两个工具非常古早,网上资料奇少,与IDEA配适度极差,难用程度令人发指。但是课程博客又不得不用,于是参考17级学长学姐们的教程,结合笔者自己的摸索经验,在这里整理成博客,以方便学弟学妹。

    一. OpenJML的安装

      OpenJML是一个检查JML语法,验证代码规格的工具。

      可以通过OpenJML官网(http://www.openjml.org/)进行下载。

    二. OpenJML的使用

     2.1 JML语法检查(JML type-checking)

      OpenJML最基础的功能就是对JML的语法进行检查。

    java -jar $OPENJML /openjml.jar <options> <files>
    

    较高的Java版本运行OpenJML会出现如下的报错。

      该报错出现的原因是OpenJML开发时的java版本较低,依赖类版本较低的包,因此和名称功能功能一样但是版本较高的包发生的冲突,通过将本地java版本换成JDK8就可以解决。

      每次都通过命令行调用openjml.jar来运行有点麻烦,我们可以通过自定义命令行指令来简化一下操作。

    MacOS

      命令行中输入如下指令:

    vim ~/.bash_profile
    

      在.bash_profile中输入:

    alias openjml='java -jar YOUR_OPENJML_PATH/openjml.jar'
    

      运用.bash_profile文件:

    source ~/.bash_profile
    

    Linux

    # filename : openjml
    #!/bin/bash
    java -jar YOUR_OPENJML_PATH/openjml.jar "$@"
    

    Windows

    ::openjml.bat
    @echo off
    
    set java=java -jar "%~dp0openjml.jar" -noPurityCheck
    set prove=-prover cvc4 -exec "%~dp0Solvers-windowscvc4-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
    

      Linux和Windos用户可以将上述代码拷贝到解压后的文件夹中,然后将该文件夹加入环境变量,参考博客https://www.cnblogs.com/lutingwang/p/openjml_basic.html

      这样,我们就可以通过openjml命令来运行OpenJML了。

    openjml <options> <files>
    

     2.2 路径参数

    • classpath:用于指定openjml.jar的路径,缺省时为当前路径。
    • sourcepath:用于指定外部引用文件的路径,缺省时为classpath。
    • specspath:用于指定规格文件(.jml文件)的路径,缺省时为sourcepath。

     2.3 静态检查(Extended Static Checking)

      Type-checking仅对JML的语法进行检查,对于规格内容和代码是否符合规格要求时不加检查的,如果需要检查规格内容,就需要用到静态检查(Extended Static Checking)和运行时检查(Runtime Assertion Checking)。

      SMT solver

      SMT是一款基于数理逻辑的用于自动化验证函数正确性的工具,其支持多平台的SMT解释器(SMT-Solver),其中应用最广泛的平台之一是微软所研发的 z3。

      从官方下载的OpenJML文件夹下已经为我们提供了不同平台不同版本的SMT solver。因为SMT solver并不属于OpenJML的一部分,因此在进行静态检查的时候我们需要告知OpenJML需要使用的solver名称以及路径。

    openjml -esc -prover provername -exec solver_path <options> <files>
    

      参数

    • -esc:对规格内容进行检查。
    • -prover:指定solver的名称。
    • -exec:指定solver的绝对路径。
    • -method <methodlist>:对指定的方法列表中的方法进行检查。
    • -exclude <methodlist>:不对指定的方法列表中的方法进行检查。
    • escMaxWarning int:限定每个方法抛出的最大警告数量。
    • -progress -verboseness:输出验证的过程信息。

     2.4 运行时检查(Runtime Assertion Checking)

      执行运行时检查的时候,首先会将被检查的代码编译成可RAC的.class文件,然后运行程序观察是否断言有冲突。

      MacOS

    openjml -rac <options> <files>
    java -cp FILE_PATH:$OPENJML/jmlruntime.jar <.class>
    

      Windows

      在windows系统中需要用“;”替换“:”

    openjml -rac <options> <files>
    java -cp FILE_PATH;$OPENJML/jmlruntime.jar <.class>
    

      参数

    • -racCheckAssumptions:运行时检查时,假设与断言都检查。在运行时检查时,对于一个方法,在其调用和被调用的时候都会进行检查。在默认情况下,会在调用的时候检查前置条件,在被调用的时候检查后置条件。当设置了该参数之后,不管调用还是被调用,都会对前置条件和后置条件进行检查。

      例如对于如下代码:

    public class Main {
        public static void main(String[] args) {
            int i=0;
            test1(i);
            test2(i);
        }
    
        //@ requires i == 1;
        //@ ensures 
    esult == 20;
        public static int test1(int i) {
            return 10;
        }
    
        //@ requires i == 0;
        //@ ensures 
    esult == 20;
        public static int test2(int i) {
            return 10;
        }
    }
    

      在不设置racCheckAssumptions参数时的输出如下:

    Main.java:4: 警告: JML precondition is false
            test1(i);
                 ^
    Main.java:10: 警告: Associated declaration: Main.java:4: 注: 
        public static int test1(int i) {
                          ^
    Main.java:16: 警告: JML postcondition is false
        public static int test2(int i) {
                          ^
    Main.java:15: 警告: Associated declaration: Main.java:16: 注: 
        //@ ensures 
    esult == 20;
            ^
    

      在设置了racCheckAssumptions参数之后的输出入下:

    Main.java:4: 警告: JML precondition is false
            test1(i);
                 ^
    Main.java:10: 警告: Associated declaration: Main.java:4: 注: 
        public static int test1(int i) {
                          ^
    Main.java:8: 警告: JML precondition is false
        //@ requires i == 1;
            ^
    Main.java:16: 警告: JML postcondition is false
        public static int test2(int i) {
                          ^
    Main.java:15: 警告: Associated declaration: Main.java:16: 注: 
        //@ ensures 
    esult == 20;
            ^
    Main.java:5: 警告: JML postcondition is false
            test2(i);
                 ^
    Main.java:15: 警告: Associated declaration: Main.java:5: 注: 
        //@ ensures 
    esult == 20;
            ^
    
    • -racJavaChecks:当设置了该参数之后,会检查java语句。

      例如如下代码:

    public class A {
    	public static void main(String ... args) {
        int i = args.length;
    		int j = i/(i-i);
    	} 
    }
    

      在设置了该参数之后,会输出除零报错:

    A.java:5: JML Division by zero 
      int j = i/(i-i);
    					 ^
    Exception in thread "main" java.lang.ArithmeticException: / by zero at A.main(A.java:5)
    

    2.5 在IDEA中配置OpenJML工具

    使用external tools教程(win/linux)

    https://www.jianshu.com/p/da29cc50eaf4

    使用external tools教程(mac)

    http://hyuga.top/2018/09/07/idea-external-tools/

    三. OpenJML与JunitNG联合进行自动单元测试

     3.1 JMLUnitNG的安装

      JMLUnitNG是基于OpenJML的以以JML语言规格描述作为指导的自动生成单元测试工具。

      下载传送门

    http://insttech.secretninjaformalmethods.org/software/jmlunitng/release_history.html

    3.2 一个简单的JMLUnitNG自动生成测试样例的例子

      先准备一个简单的测试代码

    public class Adder {
        /*@ public normal_behaviour
          @ ensures 
    esult == a + b;
        */
        public static int add(int a, int b) {
            return a + b;
        }
    }
    

      运行JMLUnitNG.jar

    java -jar jmlunitng-1_4.jar Adder.java
    

      编译当前目录下的所有.java文件

    javac -cp jmlunitng-1_4.jar *.java
    openjml -rac Adder.java
    

      运行生成的测试代码

    java -cp jmlunitng-1_4.jar Adder_JML_Test
    

      得到如下的结果:

    [TestNG] Running:
      Command line suite
    
    Passed: racEnabled()
    Passed: constructor Adder()
    Failed: static add(-2147483648, -2147483648)
    Passed: static add(0, -2147483648)
    Passed: static add(2147483647, -2147483648)
    Passed: static add(-2147483648, 0)
    Passed: static add(0, 0)
    Passed: static add(2147483647, 0)
    Passed: static add(-2147483648, 2147483647)
    Passed: static add(0, 2147483647)
    Failed: static add(2147483647, 2147483647)
    
    ===============================================
    Command line suite
    Total tests run: 11, Failures: 2, Skips: 0
    ===============================================
    

      可以看到检测出来算数溢出的问题。

     3.3 在本次作业中尝试使用JMLUnitNG

    • 在使用JMLUnitNG先用OpenJML对JML进行检查,确保能够进行运行时检查。
    • 如果出现HashMap ArrayList等报错,需要把代码补充完整
    ArrayList<Person> acquaintance = new ArrayList<Person>();
    

    从JMLUnitNG生成的测试样例来看,大多测试的都是边界数据。因此JMLUnitNG可以作为一个提醒程序员考虑数据边界的辅助测试工具,但是程序正确性的保证,还是需要JUnit进行单元测试以及考虑自动或手动构造更多的测试样例。

    四. OpenJML的webapp

      OpenJML的官网还提供了一个OpenJML WebApp(https://www.rise4fun.com/OpenJMLESC/),可以把代码提交上去进行检查。

  • 相关阅读:
    apk应用签名获取
    git强制推送命令
    Maven 将本地jar包添加到本地仓库
    关于Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-compiler-plugin的解决方案
    启动React-Native项目步骤
    Git初始化本地项目并提交远程仓库基础操作
    You have not accepted the license agreements of the following SDK components: [Android SDK Build-Tools 26.0.1, Android SDK Platform 26]
    kenkins安装
    Linux关闭防火墙和SELinux
    Linux下nginx安装配置
  • 原文地址:https://www.cnblogs.com/liujiahe0v0/p/ljh_BUAA_OO_OpenJML.html
Copyright © 2011-2022 走看看