代码覆盖
空中交通防撞系统 TCAS (Traffic Collision Avoidance System) 是飞机上搭载的一个重要软件系统,其会根据附近飞机的距离、高度、相对运动趋势判断碰撞风险,给出垂直方向的避让指令,即 Resolution Advisory (RA),例如 “爬升”、“下降”、“保持垂直速度” 等。
Software-artifact Infrastructure Repository (SIR)1 中提供了 TCAS 的一份 C 语言实现 (也可通过 SIR 提供的下载链接进行下载)。该程序接受 12 个输入参数,最终输出 UNRESOLVED、UPWARD_RA 和 DOWNWARD_RA 三种不同的避让指令:
/* -*- Last-Edit: Fri Jan 29 11:13:27 1993 by Tarak S. Goradia; -*- */
/* $Log: tcas.c,v $
* Revision 1.2 1993/03/12 19:29:50 foster
* Correct logic bug which didn't allow output of 2 - hf
* */
#include <stdio.h>
#include <stdlib.h>
#define OLEV 600 /* in feets/minute */
#define MAXALTDIFF 600 /* max altitude difference in feet */
#define MINSEP 300 /* min separation in feet */
#define NOZCROSS 100 /* in feet */
typedef int bool;
int Cur_Vertical_Sep;
bool High_Confidence;
bool Two_of_Three_Reports_Valid;
int Own_Tracked_Alt;
int Own_Tracked_Alt_Rate;
int Other_Tracked_Alt;
int Alt_Layer_Value; /* 0, 1, 2, 3 */
int Positive_RA_Alt_Thresh[4];
int Up_Separation;
int Down_Separation;
int Other_RAC; /* NO_INTENT, DO_NOT_CLIMB, DO_NOT_DESCEND */
#define NO_INTENT 0
#define DO_NOT_CLIMB 1
#define DO_NOT_DESCEND 2
int Other_Capability; /* TCAS_TA, OTHER */
#define TCAS_TA 1
#define OTHER 2
int Climb_Inhibit; /* true/false */
#define UNRESOLVED 0
#define UPWARD_RA 1
#define DOWNWARD_RA 2
void initialize(void);
int ALIM(void);
int Inhibit_Biased_Climb(void);
bool Non_Crossing_Biased_Climb(void);
bool Non_Crossing_Biased_Descend(void);
bool Own_Below_Threat(void);
bool Own_Above_Threat(void);
int alt_sep_test(void);
void initialize(void) {
Positive_RA_Alt_Thresh[0] = 400;
Positive_RA_Alt_Thresh[1] = 500;
Positive_RA_Alt_Thresh[2] = 640;
Positive_RA_Alt_Thresh[3] = 740;
}
int ALIM(void) {
return Positive_RA_Alt_Thresh[Alt_Layer_Value];
}
int Inhibit_Biased_Climb(void) {
return (Climb_Inhibit ? Up_Separation + NOZCROSS : Up_Separation);
}
bool Non_Crossing_Biased_Climb(void) {
int upward_preferred;
bool result;
upward_preferred = Inhibit_Biased_Climb() > Down_Separation;
if (upward_preferred) {
result = !(Own_Below_Threat()) || ((Own_Below_Threat()) && (!(Down_Separation >= ALIM())));
} else {
result = Own_Above_Threat() && (Cur_Vertical_Sep >= MINSEP) && (Up_Separation >= ALIM());
}
return result;
}
bool Non_Crossing_Biased_Descend(void) {
int upward_preferred;
bool result;
upward_preferred = Inhibit_Biased_Climb() > Down_Separation;
if (upward_preferred) {
result = Own_Below_Threat() && (Cur_Vertical_Sep >= MINSEP) && (Down_Separation >= ALIM());
} else {
result = !(Own_Above_Threat()) || ((Own_Above_Threat()) && (Up_Separation >= ALIM()));
}
return result;
}
bool Own_Below_Threat(void) {
return (Own_Tracked_Alt < Other_Tracked_Alt);
}
bool Own_Above_Threat(void) {
return (Other_Tracked_Alt < Own_Tracked_Alt);
}
int alt_sep_test(void) {
bool enabled, tcas_equipped, intent_not_known;
bool need_upward_RA, need_downward_RA;
int alt_sep;
enabled = High_Confidence && (Own_Tracked_Alt_Rate <= OLEV) && (Cur_Vertical_Sep > MAXALTDIFF);
tcas_equipped = Other_Capability == TCAS_TA;
intent_not_known = Two_of_Three_Reports_Valid && Other_RAC == NO_INTENT;
alt_sep = UNRESOLVED;
if (enabled && ((tcas_equipped && intent_not_known) || !tcas_equipped)) {
need_upward_RA = Non_Crossing_Biased_Climb() && Own_Below_Threat();
need_downward_RA = Non_Crossing_Biased_Descend() && Own_Above_Threat();
if (need_upward_RA && need_downward_RA)
alt_sep = UNRESOLVED;
else if (need_upward_RA)
alt_sep = UPWARD_RA;
else if (need_downward_RA)
alt_sep = DOWNWARD_RA;
else
alt_sep = UNRESOLVED;
}
return alt_sep;
}
int main(int argc, char *argv[]) {
if (argc < 13) {
fprintf(stdout, "Error: Command line arguments are\n");
fprintf(stdout, "Cur_Vertical_Sep, High_Confidence, Two_of_Three_Reports_Valid\n");
fprintf(stdout, "Own_Tracked_Alt, Own_Tracked_Alt_Rate, Other_Tracked_Alt\n");
fprintf(stdout, "Alt_Layer_Value, Up_Separation, Down_Separation\n");
fprintf(stdout, "Other_RAC, Other_Capability, Climb_Inhibit\n");
return EXIT_FAILURE;
}
initialize();
Cur_Vertical_Sep = atoi(argv[1]);
High_Confidence = atoi(argv[2]);
Two_of_Three_Reports_Valid = atoi(argv[3]);
Own_Tracked_Alt = atoi(argv[4]);
Own_Tracked_Alt_Rate = atoi(argv[5]);
Other_Tracked_Alt = atoi(argv[6]);
Alt_Layer_Value = atoi(argv[7]);
Up_Separation = atoi(argv[8]);
Down_Separation = atoi(argv[9]);
Other_RAC = atoi(argv[10]);
Other_Capability = atoi(argv[11]);
Climb_Inhibit = atoi(argv[12]);
fprintf(stdout, "%d\n", alt_sep_test());
return EXIT_SUCCESS;
}
Makefile 文件:
.PHONY: report clean
tcas: tcas.c
gcc -O0 --coverage tcas.c -o tcas
report:
rm -rf coverage-report
LC_ALL=C lcov --capture --directory . \
--output-file coverage.info \
--branch-coverage \
--ignore-errors unsupported
LC_ALL=C genhtml coverage.info \
--output-directory coverage-report \
--branch-coverage \
--title "TCAS Coverage"
clean:
rm -f tcas *.gcno *.gcda coverage.info
rm -rf coverage-report
为了执行和测试上述代码,需要安装 gcc 开发环境 (包括 gcov 覆盖率统计工具) 以及 lcov 工具 (用于可视化 gcov 结果)。 安装必要依赖后,执行下述命令进行编译 (加入 gcov 的 --coverage 选项):
make
执行一次测试:
# Confidence = 1, Alt Rate = 500, Vertical Sep = 700 (Upward RA)
./tcas 700 1 1 1000 500 1200 0 500 300 0 1 0
# Confidence = 0, Alt Rate = 500, Vertical Sep = 700 (UNRESOLVED)
./tcas 700 0 1 1000 500 1200 0 500 300 0 1 0
# Confidence = 1, Alt Rate = 700, Vertical Sep = 700 (UNRESOLVED)
./tcas 700 1 1 1000 700 1200 0 500 300 0 1 0
# Confidence = 1, Alt Rate = 500, Vertical Sep = 500 (UNRESOLVED)
./tcas 500 1 1 1000 500 1200 0 500 300 0 1 0
执行上述哪些测试数据能达到 100% 的 Branch Coverage?
统计代码覆盖结果,并利用 lcov 进行可视化:
make report
SIR 是一个主要用于学术研究的软件制品仓库,旨在支持程序分析和软件测试等领域的严格受控实验(例如,对比不同软件测试方法的检错能力)。 ↩