第三单元OO总结博客

1 梳理JML语言的理论基础、应用工具链情况

由于篇幅原因,这里只梳理几个在本单元常用的

注释结构

  1. 行注释://@annotation

  2. 块注释:/* @ annotation @*/

    例如:纯粹查询方法/*@ pure @ */,即方法的执行不会有任何副作用

JML表达式

原子表达式

  • \result:表示一个非 void 类型的方法执行所获得的结果,即方法执行后的返回值

  • \old(expr): 用来表示一个表达式 expr 在相应方法执行前的取值。作为一般规则,任何情况下,都应该使用\old把关心的表达式取值整体括起来。

量化表达式

  • \forall:全称量词修饰的表达式,表示对于给定范围内的元素,每个元素都满足相应的约束。

    作业中的例子:

     (\forall int i;  <= i && (i < npath.length - ); containsEdge(npath[i], npath[i + ])));

    保证isConnected 的前提之一是,这两个点之间有连续的边将他们相连(否则不通)

  • \exist:存在量词修饰的表达式,表示对于给定范围内的元素,存在某个元素满足相应的约束

    作业中的例子:

     requires (\exists Path path; path.isValid() && containsPath(path); path.containsNode(fromNodeId)) &&
    (\exists Path path; path.isValid() && containsPath(path); path.containsNode(toNodeId));

    保证isConnected 的前提之一,要存在某个Path,这个Path上包含这个点(否则就Not Found Exception)

操作符

  • 子类型关系操作符: E1<:E2

  • 等价关系操作符: b_expr1<==>b_expr2或者 b_expr1<=!=>b_expr2

  • 推理操作符: b_expr1==>b_expr2 或者b_expr2<==b_expr1。对于表达式b_expr1==>b_expr2 而言,当 b_expr1==false ,或者b_expr1==trueb_expr2==true时,整个表达式的值为 true 。

  • 变量引用操作符

    • \nothing指示一个空集:

      例如:assignable \nothing表示当前作用域下每个变量都不可以在方法执行过程中被赋值

    • \everything指示一个全集

方法规格

正常行为规格normal_behavior

前置条件
  • 通过requires子句来表示

    • 例如增加一条路径的前提条件:增加的这条路径必须合法有效

       requires path != null && path.isValid();
后置条件
  • 通过ensures子句来表示

    • getPathId必须确保返回的ID确实属于当前存在的路径

    •  ensures (\exists int i; 0 <= i && i < pList.length; pList[i].equals(path) && pidList[i] == \result);
副作用
  • 使用关键词 assignable 或者 modifiable,这个函数要改什么变量,就在后面加那个变量的名字

    • 例如addPath

        /*@ normal_behavior
      @ requires path != null && path.isValid();
      @ assignable pList, pidList; 需要修改这两个List
      省略一部分
      @ also
      省略一部分
      @ assignable \nothing; 如果Path不合法,那么返回0,没有需要修改的东西
      @ ensures \result == 0;
      @*/
      public int addPath(Path path);

异常行为规格expcetional_behavior

  • also: 有两种场景使用

    1. 除了正常功能规格外,还有一个异常功能规格,需要使用also来分隔。

    2. 父类中对相应方法定义了规格,子类重写了该方法,需要补充规格,这时应该在补充的规格之前使用also;

  • signals子句

    • 结构为signals (***Exception e) b_expr,意思是当b_expr为true时,方法会抛出括号中给出 的相应异常e。

      例子:

       /*@ public normal_behavior
      省略
      @ also 下面是异常行为
      @ public exceptional_behavior
      @ assignable \nothing;
      @ signals (PathNotFoundException e) path == null;
      @ signals (PathNotFoundException e) path.isValid() == false;
      @ signals (PathNotFoundException e) !containsPath(path);
      @*/
      public int removePath(Path path) throws PathNotFoundException;
      • 当 path == null条件成立,抛出异常

      • 当 path.isValid条件成立,抛出异常

      • 当容器内不含 path,抛出异常

在线OpenJML链接

2 部署SMT Solver,至少选择3个主要方法来尝试进行验证,报告结果

跳过

3 部署JMLUnitNG/JMLUnit,针对Graph接口的实现自动生成测试用例, 并结合规格对生成的测试用例和数据进行简要分析

生成数据

我研究了好久貌似OpenJML对MacOS不是很友好……好像也没有搞清楚TestNG怎么生成测试样例

写了一个python程序来生成测试指令

 import random
SEQUENCE = 0;
ID = 1;
DOUBLE_ID = 2;
NONE = 3;
ID_NODE = 4;
queries = [("PATH_ADD", SEQUENCE),
("PATH_REMOVE", SEQUENCE),
("PATH_REMOVE_BY_ID", ID),
("PATH_GET_ID", SEQUENCE),
("PATH_GET_BY_ID", ID),
("PATH_COUNT", NONE),
("PATH_SIZE", ID),
("PATH_DISTINCT_NODE_COUNT", ID),
("CONTAINS_PATH", SEQUENCE),
("CONTAINS_PATH_ID", ID),
("DISTINCT_NODE_COUNT", NONE),
("COMPARE_PATHS", DOUBLE_ID),
("PATH_CONTAINS_NODE", ID_NODE)
] NUMBER = 100
PATH_LENGTH = 500
query_count = len(queries)
path_count = 0 def gen_node():
"""generate random node"""
return random.randint(-2**31, 2**31 -1) def gen_none():
""""""
return [] def gen_sequence():
"""generate id sequence"""
len = random.randint(2, PATH_LENGTH)
sequence = [] for i in range(len):
sequence.append(gen_node()) path_count + 1;
return sequence def gen_id():
"""generate id"""
return [random.randint(1, path_count * 2 + 1)] def gen_doubel_id():
"""generate two ids"""
return [random.randint(1, path_count + 1), random.randint(1, path_count + 1)] def gen_id_node():
return [random.randint(1, path_count + 1), gen_node()] # print(gen_sequence()) generators = {SEQUENCE : gen_sequence,
ID: gen_id,
DOUBLE_ID: gen_doubel_id,
NONE: gen_none,
ID_NODE: gen_id_node } for i in range(13):
(op, arg) = queries[0]
print(op + ' ' + ' '.join(map(lambda x: str(x), generators[arg]())))
for i in range(NUMBER):
(op, arg) = queries[random.randint(0, query_count - 1)]
print(op + ' ' + ' '.join(map(lambda x: str(x), generators[arg]())))

IDEA TestMe插件和 TestNG和OpenJML的安装历程

  1. 安装TestMe插件,可以直接在Plugin 的Market里面下载,TestNG失败

  2. 下载TestNG

(TestNG下载及安装教程链接)

(TestNG6.8.7JAR包的下载链接)

Build TestNG from source code

TestNG is also hosted on GitHub, where you can download the source and build the distribution yourself:

 

然后把Jar包导入新创建的Maven项目当中,课程组的Jar包导入项目当中

在右侧边栏的Maven,点击加号,把课程组给的pom.xml导入

后来弄出了形如这样的一堆令人头疼的代码,遂放弃

     @Test
public void testGetConnectedBlockCount() {
when(bfs.getBlockSize()).thenReturn(0);
when(bfs.updateGraph(any())).thenReturn(new HashMap<Integer, HashMap<Integer, Integer>>() {{
put(Integer.valueOf(0), new HashMap<Integer, Integer>() {{
put(Integer.valueOf(0), Integer.valueOf(0));
}});
}}); int result = myRailwaySystem.getConnectedBlockCount();
Assert.assertEquals(result, 0);
}

(错误示范)

正确使用OpenJML

在许多大佬的帖子中已经把如何生成测试代码讲得很清楚了

示例主程序在Demo.java里面

java -jar jmlunitng.jar src/Demo.java
javac -cp jmlunitng.jar src/*.java
 

然后会出现很多名字奇怪的.java文件,其中有一个叫Demo_JML_Test.java,也就是测试时要运行的主程序

我把生成的.java测试文件都加入到IDEA工程的src文件夹下面,需要导入的包有openjml.jar,jmlunitng.jar

但是我在运行Demo_JML_Test的时候有一些奇怪的报错 “org.jmlspecs.utils.JmlAssertionError 中是 protected访问控制”

于是把报错代码Catch 的 Exception删了

在艰苦卓绝的研究下,和修改了一堆版本不兼容的问题后,测试代码终于运行成功了!

(racEnabled尚未解决)

Junit 测试代码

创建单元测试可以直接在IDEA里面自动生成一个测试代码的框架

Junit中集中基本注解,是必须掌握的:
  • @BeforeClass – 表示在类中的任意public static void方法执行之前执行

  • @AfterClass – 表示在类中的任意public static void方法执行之后执行

  • @Before – 表示在任意使用@Test注解标注的public void方法执行之前执行

  • @After – 表示在任意使用@Test注解标注的public void方法执行之后执行

  • @Test – 使用该注解标注的public void方法会表示为一个测试方法

junit中的assert方法全部放在Assert类中,总结一下junit类中assert方法的分类。

Junit中部分Assert方法总结:(来自这个博客)
  1. assertTrue/False([String message,]boolean condition); 判断一个条件是true还是false。感觉这个最好用了,不用记下来那么多的方法名。

  1. fail([String message,]); 失败,可以有消息,也可以没有消息。

  1. assertEquals([String message,]Object expected,Object actual); 判断是否相等,可以指定输出错误信息。 第一个参数是期望值,第二个参数是实际的值。 这个方法对各个变量有多种实现。在JDK1.5中基本一样。 但是需要主意的是float和double最后面多一个delta的值。

  1. assertNotEquals([String message,]Object expected,Object actual); 判断是否不相等。 第一个参数是期望值,第二个参数是实际的值。

  1. assertArrayEquals([java.lang.String message,] java.lang.Object[] expecteds, java.lang.Object[] actuals) ;

  1. assertNotNull/Null([String message,]Object obj); 判读一个对象是否非空(非空)。

  1. assertSame/NotSame([String message,]Object expected,Object actual); 判断两个对象是否指向同一个对象。看内存地址。

  1. failNotSame/failNotEquals(String message, Object expected, Object actual) 当不指向同一个内存地址或者不相等的时候,输出错误信息。 注意信息是必须的,而且这个输出是格式化过的。

下面仅写出部分函数的测试代码

 public class MyGraphTest {
MyGraph graph = new MyGraph();
int[] nodelist1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13};
int[] nodelist2 = {-1, 6, -2, -3, -4, -5, -6, -7, -8};
int[] nodelist3 = {0,1205, 999, 888, 777, 666};
@Before
public void before() throws Exception { // 每个方法检测之前,都另这个图里面有这三条路径
System.out.println("Ready to test " );
//MyGraph graph = new MyGraph();
graph.addPath(new MyPath(nodelist1));
graph.addPath(new MyPath(nodelist2));
graph.addPath(new MyPath(nodelist3));
} @After
public void after() throws Exception { // 每个函数检测完毕都会输出“test successfully!”
System.out.println("test successfully!");
} @Test
public void testContainsPath() throws Exception {
//TODO: Test goes here...
System.out.println("Testing ContainsPath!");
Assert.assertTrue(graph.containsPath(new MyPath(nodelist1)));
Assert.assertTrue(graph.containsPath(new MyPath(nodelist2)));
Assert.assertTrue(graph.containsPath(new MyPath(nodelist3)));
int[] nodelist4 = {9,9,9,9};
Assert.assertFalse(graph.containsPath(new MyPath(nodelist4)));
} @Test
public void testContainsPathId() throws Exception {
System.out.println("Testing contains Path id!");
Assert.assertTrue(graph.containsPathId(1));
Assert.assertTrue(graph.containsPathId(2));
Assert.assertTrue(graph.containsPathId(3));
Assert.assertFalse(graph.containsPathId(100));
} @Test
public void testRemovePath() throws Exception {
//TODO: Test goes here...
graph.removePath(new MyPath(nodelist1));
Assert.assertFalse(graph.containsPathId(1));
} @Test
public void testRemovePathById() throws Exception {
//TODO: Test goes here...
graph.removePathById(2);
Assert.assertFalse(graph.containsPath(new MyPath(nodelist2)));
} @Test
public void testContainsNode() throws Exception {
//TODO: Test goes here...
Assert.assertTrue(graph.containsNode(999));
Assert.assertFalse(graph.containsNode(14)); @Test
public void testIsConnected() throws Exception {
//TODO: Test goes here...
System.out.println("Testing is Connected@");
Assert.assertTrue(graph.isConnected(2,-8)); // 判断这个连接是对的
Assert.assertTrue(graph.isConnected(13,-8));
Assert.assertFalse(graph.isConnected(0, 1));
// 把这个文件拖到src文件夹里面即可运行 } }

愉快的Tests passed!

面向对象OO第三单元总结的更多相关文章

  1. 北航面向对象OO第三单元——JML

    简介 本单元借助JML(Java Modeling Language),训练了我们关于的"规格(specification)"的意识和思想 本单元代码难度较低,简单来讲就是给你规定 ...

  2. OO第三单元作业总结

    OO第三单元作业总结--JML 第三单元的主题是JML规格的学习,其中的三次作业也是围绕JML规格的实现所展开的(虽然感觉作业中最难的还是如何正确适用数据结构以及如何正确地对于时间复杂度进行优化). ...

  3. 规格化设计——OO第三单元总结

    规格化设计--OO第三单元总结 一.JML语言理论基础.应用工具链 1.1 JML语言 ​ JML(java modeling language)是一种描述代码行为的语言,包括前置条件.副作用等等.J ...

  4. OO第三单元——基于JML的社交网络总结

    OO第三单元--基于JML的社交网络总结 一.JML知识梳理 1)JML的语言基础以及基本语法 JML是用于java程序进行规格化设计的一种表示语言,是一种行为接口规格语言.其为严格的程序设计提供了一 ...

  5. 【OO学习】OO第三单元作业总结

    [OO学习]OO第三单元作业总结 第三单元,我们学习了JML语言,用来进行形式化设计.本单元包括三次作业,通过给定的JML来实行了一个对路径的管理系统,最后完成了一个地铁系统,来管理不同的线路,求得关 ...

  6. OO第三单元(地铁,JML)单元总结

    OO第三单元(地铁,JML)单元总结 这是我们OO课程的第二个单元,这个单元的主要目的是让我们熟悉并了解JML来是我们具有规格化编程架构的思想.这个单元的主题一开始并不明了,从第一次作业的路径到第二次 ...

  7. OO第三单元作业——魔教规格

    OO第三单元作业--魔教规格 JML的理论基础和相关工具   JML(Java Modeling Language,Java建模语言),在Java代码种增加了一些符号,这些符号用来标志一个方法是干什么 ...

  8. OO第三单元个人总结

    OO第三单元个人总结 JML理论与基础与应用工具链 JML是什么? Java建模语言(JML)是一种行为接口规范语言,可用于指定Java模块的行为 .它结合了Eiffel的契约设计方法 和Larch ...

  9. 2020 OO 第三单元总结 JML语言

    title: 2020 OO 第三单元总结 date: 2020-05-21 10:10:06 tags: OO categories: 学习 第三单元终于结束了,这是我目前为止最惨的一单元,第十次作 ...

随机推荐

  1. Python基础-random模块及随机生成11位手机号

    import random # print(random.random()) # 随机浮点数,默认取0-1,不能指定范围# print(random.randint(1, 20)) # 随机整数,顾头 ...

  2. Windows 下GitHub 安装和使用

    一.官网注册和设置 1.登录官网,注册账号,其中用户名以后会用到. 2.创建仓库.使用公开仓库方式创建,公开仓库免费.(右上角->加号->new repository) 第一行:仓库名字. ...

  3. P2P流媒体开源项目介绍

    P2P流媒体开源项目介绍1. PeerCast 2002年成立,最早的开源P2P流媒体项目.PeerCast把节点按树结构组织起来, 每个频道都是一个树, 直播源是根节点,父节点只给子节点提供数据.节 ...

  4. node好用的东东

    supervisor 可参考: http://www.cnblogs.com/pigtail/archive/2013/01/08/2851056.html http://www.cnblogs.co ...

  5. IntelliJ手记

    1. 配置JDK:File - Project Structure - SDKs,点击“+”即可: 2. 配置远程调试,对于azkaban的远程调试,在azkaban-solo-start.sh里面的 ...

  6. 洛谷【P1619】 解一元二次方程的烦恼

    我对模拟的理解:https://www.cnblogs.com/AKMer/p/9064018.html 题目传送门:https://www.luogu.org/problemnew/show/P16 ...

  7. BZOJ1720:[Usaco2006 Jan]Corral the Cows 奶牛围栏

    我对二分的理解:https://www.cnblogs.com/AKMer/p/9737477.html 题目传送门:https://www.lydsy.com/JudgeOnline/problem ...

  8. DSP/BIOS程序启动顺序

    基于TI的DSP芯片的应用程序分为两种:一般应用程序:DSP/BIOS应用程序. 为简化编程,TI提供了一套C的编程接口,它以API和宏的形式封装了TI的所有硬件模块,这套接口统称DSP/BIOS.D ...

  9. ubantu在登录界面一致循环的问题

    1.进入非图形化界面:在登录界面同时按下ctrl+alt+f1(有的需要同时按下ctrl+alt+f1+fn) 2.:输入你的账户名回车     *注意;这里是帐户名,而不是密码 3.:输入你的密码回 ...

  10. Java常见设计模式之工厂模式

    工厂模式在我们日常的应用中应当算是比较广泛的一种设计模式了.今天让我们一起来学习一下,工厂的设计模式. 工厂模式在<Java与模式>中分为三类:     1)简单工厂模式(Simple F ...