-
Notifications
You must be signed in to change notification settings - Fork 1
/
btest.c
336 lines (296 loc) · 9.48 KB
/
btest.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*
* 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> Limit number of errors to report for single function to N
* -f <Name> Check only the named function
* -g Print compact grading summary (implies -v 0 and -e 0)
* -h Print help message
* -a Don't check team structure
* -r <N> Give uniform weight of N for all problems
* -v <N> Set verbosity level to N
* N=0: Only give final scores
* N=1: Also report individual correctness scores (default)
*
* Each problem has a weight 1 to 4, which is defined in legallist.c.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "btest.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("Test %s() failed.\n Gives %d[0x%x]. Should be %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("Test %s(%d[0x%x]) failed.\n Gives %d[0x%x]. Should be %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(
"Test %s(%d[0x%x],%d[0x%x]) failed.\n Gives %d[0x%x]. Should be %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(
"Test %s(%d[0x%x],%d[0x%x],%d[0x%x]) failed.\n Gives %d[0x%x]. Should be %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("... %d total errors for function %s\n",
errors, t->name);
}
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("Score\tErrors\tFunction\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("Test %s score: %.2f/%.2f\n",
test_set[i].name, tpoints, (double) rating);
}
}
if (grade)
printf("Total points: %.2f/%.2f\n", points, max_points);
else
printf("Overall correctness score: %.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> Limit number of errors to report for single function to n\n");
printf(" -f <name> Check only the named function\n");
printf(" -g Print compact grading summary (implies -v 0 and -e 0)\n");
printf(" -h Print this message\n");
printf(" -a Omit check for valid team members\n");
printf(" -r <n> Give uniform weight of n for all problems\n");
printf(" -v <n> Set verbosity to level n\n");
printf(" n=0: Only give final scores\n");
printf(" n=1: Also report individual correctness scores (default)\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) {
/* Students must fill in their team information */
if (*team.teamname == '\0') {
printf("%s: ERROR. Please enter your team name in the team struct in bits.c.\n", argv[0]);
exit(1);
} else
printf("Team: %s\n", team.teamname);
if ((*team.name1 == '\0') || (*team.id1 == '\0')) {
printf("%s: ERROR. Please complete all team member 1 fields in the team struct.\n", argv[0]);
exit(1);
}
else
printf("Member 1:\t%s\t%s\n", team.name1, team.id1);
if (((*team.name2 != '\0') && (*team.id2 == '\0')) ||
((*team.name2 == '\0') && (*team.id2 != '\0'))) {
printf("%s: ERROR. You must fill in all or none of the team member 2 fields in the team struct.\n", argv[0]);
exit(1);
}
else if (*team.name2 != '\0')
printf("Member 2:\t%s\t%s\n", team.name2, team.id2);
printf("\n");
}
/* test each function */
errors = run_tests(verbose_level > 0);
if (!grade) {
if (errors > 0)
printf("%d errors encountered.\n", errors);
else {
printf("All tests passed.\n");
}
}
return 0;
}