Lab-4 Report
文章目录
1 实验目标概述
本次实验重点训练学生面向健壮性和正确性的编程技能,利用错误和异常处
理、断言与防御式编程技术、日志/断点等调试技术、黑盒测试编程技术,使程序
可在不同的健壮性/正确性需求下能恰当的处理各种例外与错误情况,在出错后
可优雅的退出或继续执行,发现错误之后可有效的定位错误并做出修改。
实验针对Lab 3 中写好的ADT代码和基于该ADT的三个应用的代码,使用
以下技术进行改造,提高其健壮性和正确性:
错误处理
异常处理
Assertion 和防御式编程
日志
调试技术
黑盒测试及代码覆盖度
2 实验环境配置
在eclipse的marketplace安装了spotbugs,远程clone
3 实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 Error and Exception Handling
3.1.1 处理输入文本中的三类错误
在Exception下定义了13种错误,包括DifferentDateException, DifferentFlightException, DifferentPlaneException, ElementFormatException, FlightFormatException, MoreThanOneDayException, OneDecimalException, PlaneAgeException, PlaneNumberException, PlaneSeatException, SameAirportException, SameFlightException来处理不同的错误,用这些exception足够处理很多异常了。测试文本保存在txt下。
public class DifferentDateException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public DifferentDateException() {super();}
public DifferentDateException(String message) {super(message);}
}
在可能出现异常的地方直接throw,然后通过exception chain将其传递到App统一处理,处理时采用try-catch,catch到后记录上日志,并且提示用户重新读入文件。
3.1.2 处理客户端操作时产生的异常
用了自定义的LocalConflictException, LocalDeleteException, ResourceConflictException, ResourceDeleteException和系统自带的IllegalStateException对客户端操作的异常进行处理。catch到这些异常后,在控制台打印提示信息,并且恢复操作前的状态,即是忽略了用户的操作。这样可以保证即使用户做出一些违规操作系统仍然能够正常运行。
public class ResourceConflictException extends Exception{
/**
*
*/
private static final long serialVersionUID = 1L;
public ResourceConflictException() {super();}
public ResourceConflictException(String message) {super(message);}
}
3.2 Assertion and Defensive Programming
3.2.1 checkRep()检查rep invariants
rep详细请见源代码,此处讲几个例子:
- 航班的起飞时间必须要早于降落时间,起飞和降落的机场不相同;
- 高铁的起始站和各途经站不能重复,所分配的各个车厢不能有相同的编号
- 课程上课时间早于下课时间
private void checkRep() {
assert tle != null;
assert sre != null;
assert super.getTime().getStartTime().isBefore(super.getTime().getFinishTime());
assert getArrival() == null || getDeparture() == null || !getArrival().equals(getDeparture());
}
3.2.2 Assertion/异常机制来保障pre-/post-condition
对于很多pre-condition,采用assertion保障
- 在set时需要原来的属性为null
- 在change是需要原来的属性不为null
在状态改变时,对于非法的状态改变采用异常机制
@Override
public EntryState finish() {
throw new IllegalStateException("未启动");
}
@Override
public EntryState set() {
return instance;
}
@Override
public EntryState block() {
throw new IllegalStateException("未启动");
}
@Override
public EntryState unblock() {
throw new IllegalStateException("未启动");
}
3.2.3 你的代码的防御式策略概述
在Client和ADT中嵌入了不少的防御策略。对于client的输入,ADT会用assertion来保障前置条件的正确,而ADT本身又利用了异常处理机制来保障了程序的健壮性。不仅如此,对于Client端的一些输入我也采用了异常处理机制对齐进行处理,争取不需要某个模块的运行就已经发现并提示了错误。
3.3 Logging
3.3.1 异常处理的日志功能
利用log4j和commons-logging实现了日志功能。抛出异常时直接加上日志的log即可。采用WARN级别或者是ERROR级别进行记录。
FlightScheduleApp.log.log(Level.WARN, e.getMessage(), e);
3.3.2 应用层操作的日志功能
采用INFO级别进行记录。
FlightScheduleApp.log.info(“读取文件” + filename);
3.3.3 日志查询功能
根据配置文件,不同app的log都记录到了Log下的*.log文件当中。
配置文件:
log4j.rootLogger=INFO
log4j.logger.Flight=INFO,Flight
log4j.logger.Course=INFO,Course
log4j.logger.Train=INFO,Train
log4j.appender.Flight=org.apache.log4j.FileAppender
log4j.appender.Flight.File=src/Log/FlightLog.log
log4j.appender.Flight.Append=false
log4j.appender.Flight.layout=org.apache.log4j.PatternLayout
log4j.appender.Flight.layout.ConversionPattern=%d %p [%c] = %m%n
log4j.appender.Course=org.apache.log4j.FileAppender
log4j.appender.Course.File=src/Log/CourseLog.log
log4j.appender.Course.Append=false
log4j.appender.Course.layout=org.apache.log4j.PatternLayout
log4j.appender.Course.layout.ConversionPattern=%d %p [%c] = %m%n
log4j.appender.Train=org.apache.log4j.FileAppender
log4j.appender.Train.File=src/Log/TrainLog.log
log4j.appender.Train.Append=false
log4j.appender.Train.layout=org.apache.log4j.PatternLayout
log4j.appender.Train.layout.ConversionPattern=%d %p [%c] = %m%n
在日志查询时,采用正则表达式进行读入,用jtable将日志加以显示。查询时需要输入时间段。
3.4 Testing for Robustness and Correctness
3.4.1 Testing strategy
testing strategy主要是检测了一下null和普通的情况,将输入数据划分为了这两个等价类进行测试。
3.4.2 测试用例设计
对于App和Exception,我构造了一些数据和文件人为的进行了测试,主要是由于这部分我都是采用GUI实现的,Junit不太好处理这方面和Client端的操作。
而Junit中则使用了如下的测试
@Test
public void setLocationTest() {
fe.setLocation(loc);
assertEquals(loc, fe.getLocation());
}
@Test
public void changeLocationTest() {
fe.setLocation(loc);
fe.changeLocation(loc_1);
assertEquals(loc_1, fe.getLocation());
}
@Test
public void setTest() {
fe.set(plane);
assertEquals(plane, fe.get());
}
@Test
public void getLocationssTest() {
List<Location> s = new ArrayList<>();
s.add(loc);
fe.setLocation(loc);
assertEquals(s, fe.getLocationss());
}
@Test
public void getTimesTest() {
List<Timeslot> t = new ArrayList<>();
t.add(x);
assertEquals(t, fe.getTimes());
}
@Test
public void getResourceTest() {
fe.set(plane);
List<Teacher> pl = new ArrayList<>();
pl.add(plane);
assertEquals(pl, fe.getResource());
}
@Test
public void toStringTest() {
assertEquals("Course:1", fe.toString());
}
@Test
public void cancelTest() {
fe.set(plane);
fe.cancel();
}
3.5 SpotBugs tool
这部分没有发现什么错误,可能是防御策略比较得当的原因。
3.6 Debugging
3.6.1 EventManager程序
temp里保存的是该点的事件数的一个增量start+1,end-1,事件数为某点的start+end。所以在查询事件数时,直接将之前所有点的temp累加即可,从中找到最大值。
测试时发现不同天的时候会出问题,debug发现原来temp的键表示会出现重复,于是将其改为start += (day * 25); end += (day * 25);这样就避免了重复。
3.6.2 LowestPrice程序
LowestPrice实际上是采用了一个递归的方式实现。感觉有点贪心法的味道。大多数是读代码的时候就发现了有问题将其改了过来,因为代码的思想本身没有多大的错误,主要集中在语法和一些细节上。
3.6.3 FlightClient/Flight/Plane程序
程序本身根据一个Random对List中的计划项的飞机是否会有冲突进行了检测,但是可能会陷入一个没有可分配的飞机的死循环,为其加入一个flag条件,我用的是一个set保存对于该计划项已经查过的飞机,如果全都查过了就退出循环。还有错误是关于Calendar比较的错误,这个比较简单,eclipse都有提示了。
推荐阅读
-
Lab-4 Report
-
ucore lab1_report
-
ucore_os_lab lab1 report
-
Lab3 Report
-
Linux/Unix shell 自动发送AWR report
-
APEX 3.2上Interactive Report 的表头复制(1)(APEX4.1下运行通
-
如何使用awr_set_report_thresholds控制AWR报告里的sql语句数量
-
Oracle 10g AWR Report 分析
-
namespace warning : xmlns: URI Report is not absolute 怎么解决
-
PHP实现自动登入google play下载app report的方法,appreport