/* * 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(缺省该参数,将会无限制) * -f 检查特定名字的函数 * -g 打印出简要信息(等同于 -v 0 和 -e 0) * -h 显示本信息 * -a 不检查分组 * -r 对所有问题设置相同的权重N * -v 设置显示级别为 N * N=0: 仅仅给出最终得分 * N=1: N=1 另外还给出单项得分(缺省值) * * Each problem has a weight 1 to 4, which is defined in legallist.c. */ #include // #include #include "btest.h" #include "getopt.h" #include #include #include /* 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 ] [-e ]\n", cmd); printf(" -e 限制对某个函数的报错数目为n(缺省该参数,将会无限制)\n"); printf(" -f 检查特定名字的函数\n"); printf(" -g 打印出简要信息(等同于 -v 0 和 -e 0)\n"); printf(" -h 显示本信息\n"); printf(" -a 不检查分组\n"); printf(" -r 对所有问题设置相同的权重n\n"); printf(" -v 设置显示级别为 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; }