20373222李世昱第三单元总结
数据生成
常规的数据生成思路就是大量随机数据,理论上数据足够多也有足够的覆盖性,实际测试时覆盖率也比较理想。
但是既然本单元学了JML了,我就尝试根据JML写了几乎全覆盖的数据。理论上可以检查到每一行代码,对每一种异常类型每一种情况都有检查。这个数据生成器写了近200行,相比随机数据而言麻烦了很多,但是它毕竟还是为我和同学找出了几个bug,也是值得一试的。下面简述一下构造思路
对于如AP AG(添加人添加组)这种简单但是基本的指令,直接根据JML对其所有情况遍历(塞合法ID 非法ID,相同两次ID),并基于参数数量写出基于这些基本操作的全覆盖ID模拟器
在上层的一些函数比如AR(添加关系)等,直接利用第一层的覆盖器给函数指针即可。具体而言,AP在双参数的时候有多种情况,(合法,非法)(合法,合法)(非法,合法)(合法同,合法同)(非法同,非法同)在内部构造出这种环境(当然就是添加人使得某些ID合法),然后取调用传进来的函数指针,就实现了AR的遍历并且其他的一些函数可以复用
对于更复杂的函数,如生成树,最短路等,策略除了覆盖率以外还顺带生成适量随机数据来对正确性有一定的检查
图模型构建和维护策略,以及可能的性能问题
图模型就是每个人作为一个点,人认识别人作为一条边,每个点维护自己有关的所有边,我代码中并没有单独把边提取出来管理,需要的时候直接去拿,除了并查集也没什么需要维护的。
QCI:这个函数我在第一次就是写了个简单的bfs 最不理想情况所有人查一次,N次查询N^2复杂度,没有被卡。第二次看到了讨论区的并查集后改为并查集,复杂度无限接近O1,很理想
QBS:也就是最大联通子图的数量,这个如果用并查集的话直接把所有人的祖宗塞到hashset然后查个数即可,复杂度N,如果在线维护可以达到O1。可惜我第一次没有用并查集,也是写了个简单的bfs,N^2没有被卡
最小生成树我的算法是拿去所有的边,然后对边进行排序,复杂度O(nlogn),这里选择边是否合适我另开了一个并查集用于(无限接近于)O(1)查询,接近NlogNlogN复杂度,不会被卡
最短路:按照一个循环 安放一个点,拿走它所有边去更新周围点的距离,然后选一个短的安防,我当时这里被卡T了,没有看讨论区没有做取最短的堆优化,复杂度从logn涨到n,被T了一个点
NETWORK拓展
发送广告
添加新的message的子类 广告类adv,就和第二次的sendmessage函数很像
/@ public normal_behavior
@ requires containsMessage(id) && (getMessage(id) instanceof Adv);
@ assignable messages;
@ assignable people[].messages;
@ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 &&
@ (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id;
@ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
@ ensures (\forall int i; 0 <= i && i < people.length && !getMessage(id).getPerson1().isLinked(people[i]);
@ people[i].getMessages().equals(\old(people[i].getMessages()));
@ ensures (\forall int i; 0 <= i && i < people.length && getMessage(id).getPerson1().isLinked(people[i]);
@ (\forall int j; 0 <= j && j < \old(people[i].getMessages().size());
@ people[i].getMessages().get(j+1) == \old(people[i].getMessages().get(j))) &&
@ people[i].getMessages().get(0).equals(\old(getMessage(id)))
@ also
@ public exceptional_behavior
@ signals (MessageIdNotFoundException e) !containsMessage(id);
@ signals (NotAdvertisementException e) containsMessage(id)&&(getMessage(id) instanceof Adv);
@*/
public void send(int id) throws
MessageIdNotFoundException, NotAdvertisementException;
生产
/@ public normal_behavior
@ requires contains(PersonId) && (getPerson(producerId) instanceof Producer);
@ assignable getProducer(PersonId).products;
@ ensures getProducer(PersonId).getProducts(PersonId).size() ==\old(getProducer(producerId).getProducts(productId).size()) + 1;
@ ensures containsProduce(produceId) &&
(\forall int i; 0 <= i && i < \old(products.length);
(\exists int j; 0 <= j && j < products.length; products[j].equals(\old(products[i]))));
@ also
@ public exceptional_behavior
@ signals (PersonIdNotFoundException e) !contains(PersonId);
@ signals (NotProducerException e) contains(PersonId)&&!(getPerson(PersonId) instanceof Producer);
@/
public void produce(int PersonId ,Produce produceId) throws
PersonIdNotFoundException, NotProducerException;
购买
设置为消息的子类消息
@ public normal_behavior
@ requires containsMessage(id) && (getMessage(id) instanceof BuyMessage);
@ requires (getMessage(id).getPerson1() instanceof Customer) && (getMessage(id).getPerson2() instanceof Advertiser);
@ assignable messages;
@ assignable getMessage(id).getPerson1().products;
@ assignable getMessage(id).getPerson2().products;
@ assignable getMessage(id).getPerson1().money;
@ assignable getMessage(id).getPerson2().money;
@ assignable getMessage(id).getPerson1().messages
@ assignable getMessage(id).getPerson2().messages
@ ensures !containsMessage(id) && messages.length == \old(messages.length) - 1 &&
@ (\forall int i; 0 <= i && i < \old(messages.length) && \old(messages[i].getId()) != id;
@ (\exists int j; 0 <= j && j < messages.length; messages[j].equals(\old(messages[i]))));
@ ensures (\forall int i; 0 <= i && i < \old(getMessage(id).getPerson2().getMessages().size());
@ \old(getMessage(id)).getPerson2().getMessages().get(i+1) == \old(getMessage(id).getPerson2().getMessages().get(i)));
@ ensures \old(getMessage(id)).getPerson2().getMessages().get(0).equals(\old(getMessage(id)));
@ ensures \old(getMessage(id)).getPerson2().getMessages().size() == \old(getMessage(id).getPerson2().getMessages().size()) + 1;
@ ensures !getMessage(id).getPeoson1().containsProduce(produceId) && getMessage(id).getPeoson1().getProduces().length == \old(getMessage(id).getPeoson1().getProduces().length) - 1 &&
(\forall int i; 0 <= i && i < \old(getMessage(id).getPeoson1().getProduces().length);
(\exists int j; 0 <= j && j < messages.length; getMessage(id).getPeoson1().getProduce(j)
.equals(\old(getMessage(id).getPeoson1().getProduces(i)))));
@ ensures getMessage(id).getPeoson2().containsProduce(produceId) && getMessage(id).getPeoson2().getProduces().length == \old(getMessage(id).getPeoson1().getProduces().length) + 1 &&
(\forall int i; 0 <= i && i < \old(getMessage(id).getPeoson2().getProduces().length);
(\exists int j; 0 <= j && j < messages.length; getMessage(id).getPeoson2().getProduce(j)
.equals(\old(getMessage(id).getPeoson2().getProduces(i)))));
@ ensures (\old(getMessage(id)).getPerson1().getMoney() ==
@ \old(getMessage(id).getPerson1().getMoney()) - ((BuyMessage)\old(getMessage(id))).getMoney() &&
@ \old(getMessage(id)).getPerson2().getMoney() ==
@ \old(getMessage(id).getPerson2().getMoney()) + ((BuyMessage)\old(getMessage(id))).getMoney());
@ also
@ public exceptional_behavior
@ signals (MessageIdNotFoundException e) !containsMessage(id);
@ signals (NotBuyMessageException e) containsMessage(id)&&!(getMessage(id) instanceof BuyMessage);
@ signals (NotCustomerException e) containsMessage(id)&&(getMessage(id) instanceof BuyMessage)&&!(getMessage(id).getPerson1() instanceof Customer);
@ signals (NotAdvertiserException e) (NotCustomerException e) containsMessage(id)&&(getMessage(id) instanceof BuyMessage)&&(getMessage(id).getPerson1() instanceof Customer)&&!(getMessage(id).getPerson2() instanceof Advertiser);
@/
public void Buy(int id,int produceId) throws
MessageIdNotFoundException, NotBuyMessageException, NotCustomerException, NotAdvertiserException;
学习体会
本单元相比12单元而言压力小了很多,主要目的是认识JML
JML个人感觉是一个离散化的语言,这种语言好处是显而易见的,程序的正确性不用尝试大量数据测试,可以直接检查规格来判断正确性,理论上规格设计的正确,代码的每一块都符合规格程序就是正确的,但是对是否满足规格的测试能否自动化,规格能否自动化生成这些领域还未知,希望以后能见识到JML的进一步发展