Files
csapp2025/datalab/src/btest.c
2025-03-06 19:56:12 +08:00

319 lines
9.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* CS:APP Data Lab
*
* btest.c - A test harness that checks a student's solution
* in bits.c for correctness.
*
* Copyright (c) 2001, R. Bryant and D. O'Hallaron, All rights reserved.
* May not be used, modified, or copied without permission.
*
* Usage:
* -e <N> 限制对某个函数的报错数目为N缺省该参数将会无限制
* -f <Name> 检查特定名字的函数
* -g 打印出简要信息(等同于 -v 0 和 -e 0)
* -h 显示本信息
* -a 不检查分组
* -r <N> 对所有问题设置相同的权重N
* -v <N> 设置显示级别为 N
* N=0: 仅仅给出最终得分
* N=1: N=1 另外还给出单项得分(缺省值)
*
* Each problem has a weight 1 to 4, which is defined in legallist.c.
*/
#include <stdio.h>
// #include <unistd.h>
#include "btest.h"
#include "getopt.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
/* Globals defined in other modules */
extern team_struct team; /* defined in bits.c */
extern test_rec test_set[]; /* defined in decl.c */
/* and generated from templates in ./puzzles */
/* Generate test values near "corner cases" */
#define TEST_RANGE 5
#define TEST_COUNT 33
/* Print only compact grading summary if set (-g) */
static int grade = 0;
/* Max errors reported per function (-e) */
static int error_limit = 1000;
/* If non-NULL, test only one function (-f) */
static char *test_fname = NULL;
/* Should I used fixed weight for rating, and if so, what should it be? (-r)*/
static int global_rating = 0;
/* Return random value between min and max */
static int random_val(int min, int max) {
double weight = rand() / (double)RAND_MAX;
int result = min * (1 - weight) + max * weight;
return result;
}
/* Generate the integer values we'll use to test a function */
static int gen_vals(int test_vals[], int min, int max) {
int i;
int test_count = 0;
/* If range small enough, then do exhaustively */
if (max - 32 <= min) {
for (i = min; i <= max; i++)
test_vals[test_count++] = i;
return test_count;
}
/* Otherwise, need to sample.
Do so near the boundaries and for a few random cases */
for (i = 0; i < TEST_RANGE; i++) {
test_vals[test_count++] = min + i;
test_vals[test_count++] = max - i;
test_vals[test_count++] = (max + min - TEST_RANGE) / 2 + i;
test_vals[test_count++] = random_val(min, max);
}
return test_count;
}
/* Test a function with zero arguments */
static int test_0_arg(funct_t f, funct_t ft, char *name, int report) {
int r = f();
int rt = ft();
int error = (r != rt);
if (error && report)
;
// printf("测试 %s() 失败!\n 计算得到 %d[0x%x]. 应该是 %d[0x%x]\n",
// name, r, r, rt, rt);
return error;
}
/* Test a function with one argument */
static int test_1_arg(funct_t f, funct_t ft, int arg1, char *name, int report) {
funct1_t f1 = (funct1_t)f;
funct1_t f1t = (funct1_t)ft;
int r, rt, error;
r = f1(arg1);
rt = f1t(arg1);
error = (r != rt);
if (error && report)
// printf("测试 %s(%d[0x%x]) 失败!\n 计算得到 %d[0x%x]. 应该是
// %d[0x%x]\n",
// name, arg1, arg1, r, r, rt, rt);
;
return error;
}
/* Test a function with two arguments */
static int test_2_arg(funct_t f, funct_t ft, int arg1, int arg2, char *name,
int report) {
funct2_t f2 = (funct2_t)f;
funct2_t f2t = (funct2_t)ft;
int r = f2(arg1, arg2);
int rt = f2t(arg1, arg2);
int error = (r != rt);
if (error && report)
// printf("测试 %s(%d[0x%x],%d[0x%x]) 失败!\n 计算得到 %d[0x%x]. 应该是 "
// "%d[0x%x]\n",
// name, arg1, arg1, arg2, arg2, r, r, rt, rt);
;
return error;
}
/* Test a function with three arguments */
static int test_3_arg(funct_t f, funct_t ft, int arg1, int arg2, int arg3,
char *name, int report) {
funct3_t f3 = (funct3_t)f;
funct3_t f3t = (funct3_t)ft;
int r = f3(arg1, arg2, arg3);
int rt = f3t(arg1, arg2, arg3);
int error = (r != rt);
if (error && report)
// printf("测试 %s(%d[0x%x],%d[0x%x],%d[0x%x]) 失败!\n 计算得到 %d[0x%x].
// "
// "应该是 %d[0x%x]\n",
// name, arg1, arg1, arg2, arg2, arg3, arg3, r, r, rt, rt);
;
return error;
}
/* Test a function. Return number of errors */
static int test_function(test_ptr t, int report) {
int test_vals[3][TEST_COUNT];
int test_counts[3];
int errors = 0;
int i;
int a1, a2, a3;
int args = t->args;
/* Create test set */
for (i = 0; i < 3; i++)
test_counts[i] =
gen_vals(test_vals[i], t->arg_ranges[i][0], t->arg_ranges[i][1]);
if (args == 0) {
errors += test_0_arg(t->solution_funct, t->test_funct, t->name,
report && errors < error_limit);
} else
for (a1 = 0; a1 < test_counts[0]; a1++) {
if (args == 1) {
errors += test_1_arg(t->solution_funct, t->test_funct, test_vals[0][a1],
t->name, report && errors < error_limit);
} else
for (a2 = 0; a2 < test_counts[1]; a2++) {
if (args == 2) {
errors += test_2_arg(t->solution_funct, t->test_funct,
test_vals[0][a1], test_vals[1][a2], t->name,
report && errors < error_limit);
} else
for (a3 = 0; a3 < test_counts[2]; a3++) {
errors +=
test_3_arg(t->solution_funct, t->test_funct, test_vals[0][a1],
test_vals[1][a2], test_vals[2][a3], t->name,
report && errors < error_limit);
}
}
}
if (!grade) {
if (report && errors > error_limit)
printf("函数 %s 总共有%d个错误\n", t->name, errors);
}
return errors;
}
/* Run series of tests. Return number of errors */
static int run_tests(int report) {
int i;
int errors = 0;
double points = 0.0;
double max_points = 0.0;
if (grade)
printf("得分\t错误数\t函数\n");
for (i = 0; test_set[i].solution_funct; i++) {
int terrors;
double tscore;
double tpoints;
if (!test_fname || strcmp(test_set[i].name, test_fname) == 0) {
int rating = global_rating ? global_rating : test_set[i].rating;
terrors = test_function(&test_set[i], report);
errors += terrors;
if (test_set[i].args == 0)
tscore = terrors == 0 ? 1.0 : 0.0;
else
tscore = terrors == 0 ? 1.0 : terrors == 1 ? 0.5 : 0.0;
tpoints = rating * tscore;
points += tpoints;
max_points += rating;
if (grade)
printf(" %.1f\t%d\t%s\n", tpoints, terrors, test_set[i].name);
if (report)
printf("测试 %s 得分: %.2f/%.2f\n", test_set[i].name, tpoints,
(double)rating);
}
}
if (grade)
printf("总分: %.2f/%.2f\n", points, max_points);
else
printf("总共正确得分: %.2f/%.2f\n", points, max_points);
return errors;
}
static void usage(char *cmd) {
printf("Usage: %s [-v 0|1] [-hag] [-f <func name>] [-e <max errors>]\n", cmd);
printf(" -e <n> 限制对某个函数的报错数目为n缺省该参数将会无限制\n");
printf(" -f <name> 检查特定名字的函数\n");
printf(" -g 打印出简要信息(等同于 -v 0 和 -e 0)\n");
printf(" -h 显示本信息\n");
printf(" -a 不检查分组\n");
printf(" -r <n> 对所有问题设置相同的权重n\n");
printf(" -v <n> 设置显示级别为 n\n");
printf(" n=0: 仅仅给出最终得分\n");
printf(" n=1: 另外还给出单项得分(缺省值)\n");
exit(1);
}
/*
* main routine
*/
int main(int argc, char *argv[]) {
int verbose_level = 1;
int errors;
int team_check = 1;
char c;
/* parse command line args */
while ((c = getopt(argc, argv, "hagv:f:e:r:")) != -1)
switch (c) {
case 'h': /* help */
usage(argv[0]);
break;
case 'a': /* Don't check team structure */
team_check = 0;
break;
case 'g': /* grading summary */
grade = 1;
break;
case 'v': /* set verbosity level */
verbose_level = atoi(optarg);
if (verbose_level < 0 || verbose_level > 1)
usage(argv[0]);
break;
case 'f': /* test only one function */
test_fname = strdup(optarg);
break;
case 'e': /* set error limit */
error_limit = atoi(optarg);
if (error_limit < 0)
usage(argv[0]);
break;
case 'r': /* set global rating for each problem */
global_rating = atoi(optarg);
if (global_rating < 0)
usage(argv[0]);
break;
default:
usage(argv[0]);
}
if (grade) {
error_limit = 0;
verbose_level = 0;
}
if (team_check) {
if ((*team.name1 == '\0') || (*team.id1 == '\0')) {
printf("%s: 错误!你必须填写所有小组成员!\n", argv[0]);
exit(1);
} else
printf("学生:\t%s\t%s\n", team.name1, team.id1);
printf("\n");
}
/* test each function */
errors = run_tests(verbose_level > 0);
if (!grade) {
if (errors > 0)
printf("发现%d个错误\n", errors);
else {
printf("成功通过所有测试!\n");
}
}
return 0;
}