代码覆盖

空中交通防撞系统 TCAS (Traffic Collision Avoidance System) 是飞机上搭载的一个重要软件系统,其会根据附近飞机的距离、高度、相对运动趋势判断碰撞风险,给出垂直方向的避让指令,即 Resolution Advisory (RA),例如 “爬升”、“下降”、“保持垂直速度” 等。

Software-artifact Infrastructure Repository (SIR)1 中提供了 TCAS 的一份 C 语言实现 (也可通过 SIR 提供的下载链接进行下载)。该程序接受 12 个输入参数,最终输出 UNRESOLVEDUPWARD_RADOWNWARD_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
  1. SIR 是一个主要用于学术研究的软件制品仓库,旨在支持程序分析和软件测试等领域的严格受控实验(例如,对比不同软件测试方法的检错能力)。