Add grading system.
[pintos-anon] / src / tests / make-grade
1 #! /usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 @ARGV == 3 || die;
7 my ($src_dir, $results_file, $grading_file) = @ARGV;
8
9 # Read pass/file verdicts from $results_file.
10 open (RESULTS, '<', $results_file) || die "$results_file: open: $!\n";
11 my (%verdicts, %verdict_counts);
12 while (<RESULTS>) {
13     my ($verdict, $test) = /^(pass|FAIL) (.*)$/ or die;
14     $verdicts{$test} = $verdict eq 'pass';
15 }
16 close RESULTS;
17
18 my (@failures);
19 my (@overall, @rubrics, @summary);
20 my ($pct_actual, $pct_possible) = (0, 0);
21
22 # Read grading file.
23 my (@items);
24 open (GRADING, '<', $grading_file) || die "$grading_file: open: $!\n";
25 while (<GRADING>) {
26     s/#.*//;
27     next if /^\s*$/;
28     my ($max_pct, $rubric_suffix) = /^\s*(\d+)%\t(.*)/ or die;
29     my ($dir) = $rubric_suffix =~ /^(.*)\//;
30     my ($rubric_file) = "$src_dir/$rubric_suffix";
31     open (RUBRIC, '<', $rubric_file) or die "$rubric_file: open: $!\n";
32
33     # Rubric file must begin with title line.
34     my $title = <RUBRIC>;
35     chomp $title;
36     $title =~ s/:$// or die;
37     $title .= " ($rubric_suffix):";
38     push (@rubrics, $title);
39
40     my ($score, $possible) = (0, 0);
41     my ($cnt, $passed) = (0, 0);
42     my ($was_score) = 0;
43     while (<RUBRIC>) {
44         chomp;
45         push (@rubrics, "\t$_"), next if /^-/;
46         push (@rubrics, ""), next if /^\s*$/;
47         my ($poss, $name) = /^(\d+)\t(.*)$/ or die;
48         my ($test) = "$dir/$name";
49         my ($points) = 0;
50         if (!defined $verdicts{$test}) {
51             push (@overall, "warning: $test not tested, assuming failure");
52         } elsif ($verdicts{$test}) {
53             $points = $poss;
54             $passed++;
55         }
56         push (@failures, $test) if !$points;
57         $verdict_counts{$test}++;
58         push (@rubrics, sprintf ("\t%4s%2d/%2d %s",
59                                  $points ? '' : '**', $points, $poss, $test));
60         $score += $points;
61         $possible += $poss;
62         $cnt++;
63     }
64     close (RUBRIC);
65
66     push (@rubrics, "");
67     push (@rubrics, "\t- Section summary.");
68     push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
69                              '', $passed, $cnt, 'tests passed'));
70     push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
71                              '', $score, $possible, 'points subtotal'));
72     push (@rubrics, '');
73
74     my ($pct) = ($score / $possible) * $max_pct;
75     push (@summary, sprintf ("%-40s %3d/%3d %5.1f%%/%5.1f%%",
76                              $rubric_suffix,
77                              $score, $possible,
78                              $pct, $max_pct));
79     $pct_actual += $pct;
80     $pct_possible += $max_pct;
81 }
82 close GRADING;
83
84 my ($sum_line)
85   = "---------------------------------------- --- --- ------ ------";
86 unshift (@summary,
87          "SUMMARY BY TEST SET",
88          '',
89          sprintf ("%-40s %3s %3s %6s %6s",
90                   "Test Set", "Pts", "Max", "% Ttl", "% Max"),
91          $sum_line);
92 push (@summary,
93       $sum_line,
94       sprintf ("%-40s %3s %3s %5.1f%%/%5.1f%%",
95                'Total', '', '', $pct_actual, $pct_possible));
96
97 unshift (@rubrics,
98          "SUMMARY OF INDIVIDUAL TESTS",
99          '');
100
101 foreach my $name (keys (%verdicts)) {
102     my ($count) = $verdict_counts{$name};
103     if (!defined ($count) || $count != 1) {
104         if (!defined ($count) || !$count) {
105             push (@overall, "warning: test $name doesn't count for grading");
106         } else {
107             push (@overall,
108                   "warning: test $name counted $count times in grading");
109         }
110     }
111 }
112 push (@overall, sprintf ("TOTAL TESTING SCORE: %.1f%%", $pct_actual));
113
114 my (@divider) = ('', '- ' x 38, '');
115
116 print map ("$_\n", @overall, @divider, @summary, @divider, @rubrics);
117
118 for my $test (@failures) {
119     open (RESULT, '<', "$test.result") or next;
120     print map ("$_\n", @divider);
121     print "DETAILS OF $test FAILURE:\n\n";
122     my $first_line = <RESULT>;
123     my ($cnt) = 0;
124     while (<RESULT>) {
125         print;
126         $cnt++;
127     }
128     close (RESULT);
129
130     if ($cnt == 0) {
131         open (OUTPUT, '<', "$test.output") or next;
132         my ($panics) = 0;
133         while (<OUTPUT>) {
134             if (/PANIC/ && ++$panics > 2) {
135                 print "[...details of additional panic(s) omitted...]\n";
136                 last;
137             }
138             print;
139         }
140         close (OUTPUT);
141     }
142 }
143