Description:
[grader] added fractional timelimit git-svn-id: http://theory.cpe.ku.ac.th/grader/judge/trunk/scripts@191 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r51:3618759afb50 - - 6 files changed: 66 inserted, 57 deleted

@@ -0,0 +1,38
1 + #include <stdio.h>
2 + #include <stdlib.h>
3 + #include <unistd.h>
4 + #include <sys/time.h>
5 + #include <time.h>
6 + #include <sys/resource.h>
7 +
8 + // run it for 1.5 s
9 +
10 + int main()
11 + {
12 + int a,b;
13 +
14 + int c=0;
15 +
16 + scanf("%d %d",&a,&b);
17 + printf("%d\n",a+b);
18 +
19 + struct rusage ru;
20 +
21 + while(1) {
22 + c++;
23 + b+=c;
24 + while(c<100000) {
25 + c++;
26 + b+=c;
27 + }
28 + getrusage(RUSAGE_SELF,&ru);
29 + double rtime = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
30 + rtime += (double)ru.ru_utime.tv_usec / 1000000.0;
31 + rtime += (double)ru.ru_stime.tv_usec / 1000000.0;
32 + if(rtime > 0.5)
33 + break;
34 + }
35 + printf("%d\n",b);
36 + exit(0);
37 + }
38 +
@@ -74,111 +74,116
74 elsif (ARGV.length==2) and (ARGV[1]=='all')
74 elsif (ARGV.length==2) and (ARGV[1]=='all')
75 stop_grader(:all)
75 stop_grader(:all)
76 puts "A global stop file ('stop.all') created."
76 puts "A global stop file ('stop.all') created."
77 puts "You should remove it manually later."
77 puts "You should remove it manually later."
78 else
78 else
79 (1..ARGV.length-1).each do |i|
79 (1..ARGV.length-1).each do |i|
80 stop_grader(ARGV[i])
80 stop_grader(ARGV[i])
81 end
81 end
82 puts "stop file(s) created"
82 puts "stop file(s) created"
83 end
83 end
84 exit(0)
84 exit(0)
85 end
85 end
86
86
87 if check_stopfile
87 if check_stopfile
88 puts "Stop file exists. Terminated."
88 puts "Stop file exists. Terminated."
89 clear_stopfile
89 clear_stopfile
90 exit(0)
90 exit(0)
91 end
91 end
92
92
93 grader_mode = 'queue'
93 grader_mode = 'queue'
94 if ARGV.length >= 1
94 if ARGV.length >= 1
95 GRADER_ENV = ARGV[0]
95 GRADER_ENV = ARGV[0]
96 if ARGV.length >=2
96 if ARGV.length >=2
97 grader_mode = ARGV[1]
97 grader_mode = ARGV[1]
98 end
98 end
99 else
99 else
100 GRADER_ENV = 'exam'
100 GRADER_ENV = 'exam'
101 end
101 end
102
102
103 puts "environment: #{GRADER_ENV}"
103 puts "environment: #{GRADER_ENV}"
104 require File.join(File.dirname(__FILE__),'config/environment')
104 require File.join(File.dirname(__FILE__),'config/environment')
105
105
106 # add grader_mode to config
106 # add grader_mode to config
107 # this is needed because method log needs it. TODO: clean this up
107 # this is needed because method log needs it. TODO: clean this up
108 class << config
108 class << config
109 attr_accessor :grader_mode
109 attr_accessor :grader_mode
110 end
110 end
111 config.grader_mode = grader_mode
111 config.grader_mode = grader_mode
112
112
113 # reading rails environment
113 # reading rails environment
114 log 'Reading rails environment'
114 log 'Reading rails environment'
115
115
116 RAILS_ENV = config.rails_env
116 RAILS_ENV = config.rails_env
117 require RAILS_ROOT + '/config/environment'
117 require RAILS_ROOT + '/config/environment'
118
118
119 # register grader process
119 # register grader process
120 if config.report_grader
120 if config.report_grader
121 grader_proc = GraderProcess.register(config.grader_hostname,
121 grader_proc = GraderProcess.register(config.grader_hostname,
122 Process.pid,
122 Process.pid,
123 grader_mode)
123 grader_mode)
124 else
124 else
125 grader_proc = nil
125 grader_proc = nil
126 end
126 end
127
127
128 #set loggin environment
128 #set loggin environment
129 ENV['GRADER_LOGGING'] = log_file_name
129 ENV['GRADER_LOGGING'] = log_file_name
130
130
131 #
131 #
132 # MAIN LOOP
132 # MAIN LOOP
133 #
133 #
134
134
135 case grader_mode
135 case grader_mode
136 when "queue", "test_request"
136 when "queue", "test_request"
137 log "Grader: #{grader_mode}"
137 log "Grader: #{grader_mode}"
138 if grader_mode=="queue"
138 if grader_mode=="queue"
139 engine = Grader::Engine.new
139 engine = Grader::Engine.new
140 else
140 else
141 engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new,
141 engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new,
142 Grader::TestRequestReporter.new)
142 Grader::TestRequestReporter.new)
143 end
143 end
144
144
145 runner = Grader::Runner.new(engine, grader_proc)
145 runner = Grader::Runner.new(engine, grader_proc)
146 while true
146 while true
147
147
148 if check_stopfile # created by calling grader stop
148 if check_stopfile # created by calling grader stop
149 clear_stopfile
149 clear_stopfile
150 log "stopped (with stop file)"
150 log "stopped (with stop file)"
151 break
151 break
152 end
152 end
153
153
154 if grader_mode=="queue"
154 if grader_mode=="queue"
155 task = runner.grade_oldest_task
155 task = runner.grade_oldest_task
156 else
156 else
157 task = runner.grade_oldest_test_request
157 task = runner.grade_oldest_test_request
158 end
158 end
159 if task==nil
159 if task==nil
160 sleep(1)
160 sleep(1)
161 end
161 end
162 end
162 end
163
163
164 when "prob"
164 when "prob"
165 engine = Grader::Engine.new
165 engine = Grader::Engine.new
166 runner = Grader::Runner.new(engine, grader_proc)
166 runner = Grader::Runner.new(engine, grader_proc)
167
167
168 grader_proc.report_active if grader_proc!=nil
168 grader_proc.report_active if grader_proc!=nil
169
169
170 - prob = Problem.find_by_name(ARGV[2])
170 + ARGV.shift
171 - if prob==nil
171 + ARGV.shift
172 - puts "cannot find problem: #{ARGV[2]}"
172 +
173 - else
173 + ARGV.each do |prob_name|
174 - runner.grade_problem(prob)
174 + prob = Problem.find_by_name(prob_name)
175 + if prob==nil
176 + puts "cannot find problem: #{prob_name}"
177 + else
178 + runner.grade_problem(prob)
179 + end
175 end
180 end
176
181
177 else
182 else
178 display_manual
183 display_manual
179 exit(0)
184 exit(0)
180 end
185 end
181
186
182 # report inactive
187 # report inactive
183 grader_proc.report_inactive if grader_proc!=nil
188 grader_proc.report_inactive if grader_proc!=nil
184
189
@@ -1,128 +1,128
1 /*
1 /*
2 * A Simple Testing Sandbox
2 * A Simple Testing Sandbox
3 *
3 *
4 * (c) 2001--2004 Martin Mares <mj@ucw.cz>
4 * (c) 2001--2004 Martin Mares <mj@ucw.cz>
5 */
5 */
6
6
7 #define _LARGEFILE64_SOURCE
7 #define _LARGEFILE64_SOURCE
8 //#define _GNU_SOURCE
8 //#define _GNU_SOURCE
9
9
10 #include <errno.h>
10 #include <errno.h>
11 #include <stdio.h>
11 #include <stdio.h>
12 #include <fcntl.h>
12 #include <fcntl.h>
13 #include <stdlib.h>
13 #include <stdlib.h>
14 #include <string.h>
14 #include <string.h>
15 #include <stdarg.h>
15 #include <stdarg.h>
16 #include <unistd.h>
16 #include <unistd.h>
17 #include <getopt.h>
17 #include <getopt.h>
18 #include <time.h>
18 #include <time.h>
19 #include <sys/wait.h>
19 #include <sys/wait.h>
20 #include <sys/user.h>
20 #include <sys/user.h>
21 #include <sys/time.h>
21 #include <sys/time.h>
22 #include <sys/ptrace.h>
22 #include <sys/ptrace.h>
23 #include <sys/signal.h>
23 #include <sys/signal.h>
24 #include <sys/sysinfo.h>
24 #include <sys/sysinfo.h>
25 #include <sys/syscall.h>
25 #include <sys/syscall.h>
26 #include <sys/resource.h>
26 #include <sys/resource.h>
27
27
28 #define NONRET __attribute__((noreturn))
28 #define NONRET __attribute__((noreturn))
29 #define UNUSED __attribute__((unused))
29 #define UNUSED __attribute__((unused))
30
30
31 static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
31 static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
32 - static int timeout;
32 + static double timeout;
33 static int pass_environ;
33 static int pass_environ;
34 static int use_wall_clock;
34 static int use_wall_clock;
35 static int file_access;
35 static int file_access;
36 static int verbose;
36 static int verbose;
37 static int memory_limit;
37 static int memory_limit;
38 static int allow_times;
38 static int allow_times;
39 static char *redir_stdin, *redir_stdout;
39 static char *redir_stdin, *redir_stdout;
40 static char *set_cwd;
40 static char *set_cwd;
41
41
42 static pid_t box_pid;
42 static pid_t box_pid;
43 static int is_ptraced;
43 static int is_ptraced;
44 static volatile int timer_tick;
44 static volatile int timer_tick;
45 static time_t start_time;
45 static time_t start_time;
46 static int ticks_per_sec;
46 static int ticks_per_sec;
47 static int page_size;
47 static int page_size;
48
48
49 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
49 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
50 /* glibc 2.1 or newer -> has lseek64 */
50 /* glibc 2.1 or newer -> has lseek64 */
51 #define long_seek(f,o,w) lseek64(f,o,w)
51 #define long_seek(f,o,w) lseek64(f,o,w)
52 #else
52 #else
53 /* Touching clandestine places in glibc */
53 /* Touching clandestine places in glibc */
54 extern loff_t llseek(int fd, loff_t pos, int whence);
54 extern loff_t llseek(int fd, loff_t pos, int whence);
55 #define long_seek(f,o,w) llseek(f,o,w)
55 #define long_seek(f,o,w) llseek(f,o,w)
56 #endif
56 #endif
57
57
58 int max_mem_used = 0;
58 int max_mem_used = 0;
59
59
60 void print_running_stat(double wall_time,
60 void print_running_stat(double wall_time,
61 double user_time,
61 double user_time,
62 double system_time,
62 double system_time,
63 int mem_usage)
63 int mem_usage)
64 {
64 {
65 fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
65 fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
66 wall_time, user_time, system_time, mem_usage);
66 wall_time, user_time, system_time, mem_usage);
67 }
67 }
68
68
69 static void NONRET
69 static void NONRET
70 box_exit(void)
70 box_exit(void)
71 {
71 {
72 if (box_pid > 0) {
72 if (box_pid > 0) {
73 if (is_ptraced)
73 if (is_ptraced)
74 ptrace(PTRACE_KILL, box_pid);
74 ptrace(PTRACE_KILL, box_pid);
75 kill(-box_pid, SIGKILL);
75 kill(-box_pid, SIGKILL);
76 kill(box_pid, SIGKILL);
76 kill(box_pid, SIGKILL);
77 }
77 }
78
78
79 struct timeval total;
79 struct timeval total;
80 int wall;
80 int wall;
81 struct rusage rus;
81 struct rusage rus;
82 int stat;
82 int stat;
83 pid_t p;
83 pid_t p;
84
84
85 // wait so that we can get information
85 // wait so that we can get information
86 p = wait4(box_pid, &stat, WUNTRACED, &rus);
86 p = wait4(box_pid, &stat, WUNTRACED, &rus);
87 if (p < 0) {
87 if (p < 0) {
88 fprintf(stderr,"wait4: error\n");
88 fprintf(stderr,"wait4: error\n");
89 print_running_stat(0,0,0,max_mem_used);
89 print_running_stat(0,0,0,max_mem_used);
90 } else if (p != box_pid) {
90 } else if (p != box_pid) {
91 fprintf(stderr,"wait4: unknown pid %d exited!\n", p);
91 fprintf(stderr,"wait4: unknown pid %d exited!\n", p);
92 print_running_stat(0,0,0,max_mem_used);
92 print_running_stat(0,0,0,max_mem_used);
93 } else {
93 } else {
94 if (!WIFEXITED(stat))
94 if (!WIFEXITED(stat))
95 fprintf(stderr,"wait4: unknown status\n");
95 fprintf(stderr,"wait4: unknown status\n");
96 struct timeval total;
96 struct timeval total;
97 int wall;
97 int wall;
98 wall = time(NULL) - start_time;
98 wall = time(NULL) - start_time;
99 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
99 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
100
100
101 print_running_stat((double)wall,
101 print_running_stat((double)wall,
102 (double) rus.ru_utime.tv_sec +
102 (double) rus.ru_utime.tv_sec +
103 ((double) rus.ru_utime.tv_usec/1000000.0),
103 ((double) rus.ru_utime.tv_usec/1000000.0),
104 (double) rus.ru_stime.tv_sec +
104 (double) rus.ru_stime.tv_sec +
105 ((double) rus.ru_stime.tv_usec/1000000.0),
105 ((double) rus.ru_stime.tv_usec/1000000.0),
106 max_mem_used);
106 max_mem_used);
107 }
107 }
108 exit(1);
108 exit(1);
109 }
109 }
110
110
111 static void NONRET __attribute__((format(printf,1,2)))
111 static void NONRET __attribute__((format(printf,1,2)))
112 die(char *msg, ...)
112 die(char *msg, ...)
113 {
113 {
114 va_list args;
114 va_list args;
115 va_start(args, msg);
115 va_start(args, msg);
116 vfprintf(stderr, msg, args);
116 vfprintf(stderr, msg, args);
117 fputc('\n', stderr);
117 fputc('\n', stderr);
118 box_exit();
118 box_exit();
119 }
119 }
120
120
121 static void __attribute__((format(printf,1,2)))
121 static void __attribute__((format(printf,1,2)))
122 log(char *msg, ...)
122 log(char *msg, ...)
123 {
123 {
124 va_list args;
124 va_list args;
125 va_start(args, msg);
125 va_start(args, msg);
126 if (verbose)
126 if (verbose)
127 {
127 {
128 vfprintf(stderr, msg, args);
128 vfprintf(stderr, msg, args);
@@ -234,444 +234,447
234 case __NR_getegid:
234 case __NR_getegid:
235 case __NR_dup2:
235 case __NR_dup2:
236 case __NR_ftruncate:
236 case __NR_ftruncate:
237 case __NR_fstat:
237 case __NR_fstat:
238 case __NR_personality:
238 case __NR_personality:
239 case __NR__llseek:
239 case __NR__llseek:
240 case __NR_readv:
240 case __NR_readv:
241 case __NR_writev:
241 case __NR_writev:
242 case __NR_getresuid:
242 case __NR_getresuid:
243 #ifdef __NR_pread64
243 #ifdef __NR_pread64
244 case __NR_pread64:
244 case __NR_pread64:
245 case __NR_pwrite64:
245 case __NR_pwrite64:
246 #else
246 #else
247 case __NR_pread:
247 case __NR_pread:
248 case __NR_pwrite:
248 case __NR_pwrite:
249 #endif
249 #endif
250 case __NR_ftruncate64:
250 case __NR_ftruncate64:
251 case __NR_fstat64:
251 case __NR_fstat64:
252 case __NR_fcntl:
252 case __NR_fcntl:
253 case __NR_fcntl64:
253 case __NR_fcntl64:
254 case __NR_mmap:
254 case __NR_mmap:
255 case __NR_munmap:
255 case __NR_munmap:
256 case __NR_ioctl:
256 case __NR_ioctl:
257 case __NR_uname:
257 case __NR_uname:
258 case 252:
258 case 252:
259 case 243:
259 case 243:
260 return 1;
260 return 1;
261 // case __NR_time:
261 // case __NR_time:
262 case __NR_alarm:
262 case __NR_alarm:
263 // case __NR_pause:
263 // case __NR_pause:
264 case __NR_signal:
264 case __NR_signal:
265 case __NR_fchmod:
265 case __NR_fchmod:
266 case __NR_sigaction:
266 case __NR_sigaction:
267 case __NR_sgetmask:
267 case __NR_sgetmask:
268 case __NR_ssetmask:
268 case __NR_ssetmask:
269 case __NR_sigsuspend:
269 case __NR_sigsuspend:
270 case __NR_sigpending:
270 case __NR_sigpending:
271 case __NR_getrlimit:
271 case __NR_getrlimit:
272 case __NR_getrusage:
272 case __NR_getrusage:
273 case __NR_gettimeofday:
273 case __NR_gettimeofday:
274 case __NR_select:
274 case __NR_select:
275 case __NR_readdir:
275 case __NR_readdir:
276 case __NR_setitimer:
276 case __NR_setitimer:
277 case __NR_getitimer:
277 case __NR_getitimer:
278 case __NR_sigreturn:
278 case __NR_sigreturn:
279 case __NR_mprotect:
279 case __NR_mprotect:
280 case __NR_sigprocmask:
280 case __NR_sigprocmask:
281 case __NR_getdents:
281 case __NR_getdents:
282 case __NR_getdents64:
282 case __NR_getdents64:
283 case __NR__newselect:
283 case __NR__newselect:
284 case __NR_fdatasync:
284 case __NR_fdatasync:
285 case __NR_mremap:
285 case __NR_mremap:
286 case __NR_poll:
286 case __NR_poll:
287 case __NR_getcwd:
287 case __NR_getcwd:
288 case __NR_nanosleep:
288 case __NR_nanosleep:
289 case __NR_rt_sigreturn:
289 case __NR_rt_sigreturn:
290 case __NR_rt_sigaction:
290 case __NR_rt_sigaction:
291 case __NR_rt_sigprocmask:
291 case __NR_rt_sigprocmask:
292 case __NR_rt_sigpending:
292 case __NR_rt_sigpending:
293 case __NR_rt_sigtimedwait:
293 case __NR_rt_sigtimedwait:
294 case __NR_rt_sigqueueinfo:
294 case __NR_rt_sigqueueinfo:
295 case __NR_rt_sigsuspend:
295 case __NR_rt_sigsuspend:
296 case __NR_mmap2:
296 case __NR_mmap2:
297 case __NR__sysctl:
297 case __NR__sysctl:
298 return (filter_syscalls == 1);
298 return (filter_syscalls == 1);
299 case __NR_times:
299 case __NR_times:
300 return allow_times;
300 return allow_times;
301 case __NR_kill:
301 case __NR_kill:
302 if (u->regs.ebx == box_pid)
302 if (u->regs.ebx == box_pid)
303 die("Commited suicide by signal %d.", (int)u->regs.ecx);
303 die("Commited suicide by signal %d.", (int)u->regs.ecx);
304 return 0;
304 return 0;
305 default:
305 default:
306 return 0;
306 return 0;
307 }
307 }
308 }
308 }
309
309
310 static void
310 static void
311 signal_alarm(int unused UNUSED)
311 signal_alarm(int unused UNUSED)
312 {
312 {
313 /* Time limit checks are synchronous, so we only schedule them there. */
313 /* Time limit checks are synchronous, so we only schedule them there. */
314 timer_tick = 1;
314 timer_tick = 1;
315
315
316 //NOTE: do not use alarm, changed to setitimer for precision
316 //NOTE: do not use alarm, changed to setitimer for precision
317 // alarm(1);
317 // alarm(1);
318 }
318 }
319
319
320 static void
320 static void
321 signal_int(int unused UNUSED)
321 signal_int(int unused UNUSED)
322 {
322 {
323 /* Interrupts are fatal, so no synchronization requirements. */
323 /* Interrupts are fatal, so no synchronization requirements. */
324 die("Interrupted.");
324 die("Interrupted.");
325 }
325 }
326
326
327 static void
327 static void
328 check_timeout(void)
328 check_timeout(void)
329 {
329 {
330 - int sec;
330 + double sec;
331
331
332 if (use_wall_clock)
332 if (use_wall_clock)
333 - sec = time(NULL) - start_time;
333 + sec = (double)(time(NULL) - start_time);
334 else
334 else
335 {
335 {
336 char buf[4096], *x;
336 char buf[4096], *x;
337 int c, utime, stime;
337 int c, utime, stime;
338 static int proc_status_fd;
338 static int proc_status_fd;
339 if (!proc_status_fd)
339 if (!proc_status_fd)
340 {
340 {
341 sprintf(buf, "/proc/%d/stat", (int) box_pid);
341 sprintf(buf, "/proc/%d/stat", (int) box_pid);
342 proc_status_fd = open(buf, O_RDONLY);
342 proc_status_fd = open(buf, O_RDONLY);
343 if (proc_status_fd < 0)
343 if (proc_status_fd < 0)
344 die("open(%s): %m", buf);
344 die("open(%s): %m", buf);
345 }
345 }
346 lseek(proc_status_fd, 0, SEEK_SET);
346 lseek(proc_status_fd, 0, SEEK_SET);
347 if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
347 if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
348 die("read on /proc/$pid/stat: %m");
348 die("read on /proc/$pid/stat: %m");
349 if (c >= (int) sizeof(buf) - 1)
349 if (c >= (int) sizeof(buf) - 1)
350 die("/proc/$pid/stat too long");
350 die("/proc/$pid/stat too long");
351 buf[c] = 0;
351 buf[c] = 0;
352 x = buf;
352 x = buf;
353 while (*x && *x != ' ')
353 while (*x && *x != ' ')
354 x++;
354 x++;
355 while (*x == ' ')
355 while (*x == ' ')
356 x++;
356 x++;
357 if (*x++ != '(')
357 if (*x++ != '(')
358 die("proc syntax error 1");
358 die("proc syntax error 1");
359 while (*x && (*x != ')' || x[1] != ' '))
359 while (*x && (*x != ')' || x[1] != ' '))
360 x++;
360 x++;
361 while (*x == ')' || *x == ' ')
361 while (*x == ')' || *x == ' ')
362 x++;
362 x++;
363 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
363 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
364 die("proc syntax error 2");
364 die("proc syntax error 2");
365 //printf("%s - %d\n",x,ticks_per_sec);
365 //printf("%s - %d\n",x,ticks_per_sec);
366 - sec = (utime + stime + ticks_per_sec-1)/ticks_per_sec;
366 + sec = ((double)(utime + stime))/(double)ticks_per_sec;
367 }
367 }
368 if (verbose > 1)
368 if (verbose > 1)
369 fprintf(stderr, "[timecheck: %d seconds]\n", sec);
369 fprintf(stderr, "[timecheck: %d seconds]\n", sec);
370 if (sec > timeout) {
370 if (sec > timeout) {
371 - die("Time limit exceeded.");
371 + die("Time limit exceeded.",sec,timeout);
372 }
372 }
373 }
373 }
374
374
375 static void
375 static void
376 check_memory_usage()
376 check_memory_usage()
377 {
377 {
378 char proc_fname[100];
378 char proc_fname[100];
379 sprintf(proc_fname,"/proc/%d/statm",box_pid);
379 sprintf(proc_fname,"/proc/%d/statm",box_pid);
380 //printf("proc fname: %s\n",proc_fname);
380 //printf("proc fname: %s\n",proc_fname);
381 FILE *fp = fopen(proc_fname,"r");
381 FILE *fp = fopen(proc_fname,"r");
382 if(fp!=NULL) {
382 if(fp!=NULL) {
383 char line[1000];
383 char line[1000];
384 fgets(line,999,fp);
384 fgets(line,999,fp);
385 //printf("%s\n",line);
385 //printf("%s\n",line);
386 int m;
386 int m;
387
387
388 if(sscanf(line,"%d",&m)==1) {
388 if(sscanf(line,"%d",&m)==1) {
389 m = (m*page_size+1023)/1024;
389 m = (m*page_size+1023)/1024;
390 if(m>max_mem_used)
390 if(m>max_mem_used)
391 max_mem_used = m;
391 max_mem_used = m;
392 }
392 }
393
393
394 fclose(fp);
394 fclose(fp);
395 }
395 }
396 }
396 }
397
397
398 static void
398 static void
399 boxkeeper(void)
399 boxkeeper(void)
400 {
400 {
401 int syscall_count = 0;
401 int syscall_count = 0;
402 struct sigaction sa;
402 struct sigaction sa;
403
403
404 is_ptraced = 1;
404 is_ptraced = 1;
405 bzero(&sa, sizeof(sa));
405 bzero(&sa, sizeof(sa));
406 sa.sa_handler = signal_int;
406 sa.sa_handler = signal_int;
407 sigaction(SIGINT, &sa, NULL);
407 sigaction(SIGINT, &sa, NULL);
408 start_time = time(NULL);
408 start_time = time(NULL);
409 ticks_per_sec = sysconf(_SC_CLK_TCK);
409 ticks_per_sec = sysconf(_SC_CLK_TCK);
410 page_size = getpagesize();
410 page_size = getpagesize();
411 if (ticks_per_sec <= 0)
411 if (ticks_per_sec <= 0)
412 die("Invalid ticks_per_sec!");
412 die("Invalid ticks_per_sec!");
413
413
414 check_memory_usage();
414 check_memory_usage();
415
415
416 sa.sa_handler = signal_alarm;
416 sa.sa_handler = signal_alarm;
417 sigaction(SIGALRM, &sa, NULL);
417 sigaction(SIGALRM, &sa, NULL);
418 //alarm(1);
418 //alarm(1);
419
419
420 struct itimerval val;
420 struct itimerval val;
421 val.it_interval.tv_sec = 0;
421 val.it_interval.tv_sec = 0;
422 val.it_interval.tv_usec = 50000;
422 val.it_interval.tv_usec = 50000;
423 val.it_value.tv_sec = 0;
423 val.it_value.tv_sec = 0;
424 val.it_value.tv_usec = 50000;
424 val.it_value.tv_usec = 50000;
425 setitimer(ITIMER_REAL,&val,NULL);
425 setitimer(ITIMER_REAL,&val,NULL);
426
426
427 /*
427 /*
428 --- add alarm handler no matter what..
428 --- add alarm handler no matter what..
429 if (timeout)
429 if (timeout)
430 {
430 {
431 sa.sa_handler = signal_alarm;
431 sa.sa_handler = signal_alarm;
432 sigaction(SIGALRM, &sa, NULL);
432 sigaction(SIGALRM, &sa, NULL);
433 alarm(1);
433 alarm(1);
434 }
434 }
435 */
435 */
436
436
437 for(;;)
437 for(;;)
438 {
438 {
439 struct rusage rus;
439 struct rusage rus;
440 int stat;
440 int stat;
441 pid_t p;
441 pid_t p;
442
442
443 if (timer_tick)
443 if (timer_tick)
444 {
444 {
445 check_timeout();
445 check_timeout();
446 check_memory_usage();
446 check_memory_usage();
447 timer_tick = 0;
447 timer_tick = 0;
448 }
448 }
449 p = wait4(box_pid, &stat, WUNTRACED, &rus);
449 p = wait4(box_pid, &stat, WUNTRACED, &rus);
450
450
451 if (p < 0)
451 if (p < 0)
452 {
452 {
453 if (errno == EINTR)
453 if (errno == EINTR)
454 continue;
454 continue;
455 die("wait4: %m");
455 die("wait4: %m");
456 }
456 }
457 if (p != box_pid)
457 if (p != box_pid)
458 die("wait4: unknown pid %d exited!", p);
458 die("wait4: unknown pid %d exited!", p);
459 if (WIFEXITED(stat))
459 if (WIFEXITED(stat))
460 {
460 {
461 struct timeval total;
461 struct timeval total;
462 int wall;
462 int wall;
463 wall = time(NULL) - start_time;
463 wall = time(NULL) - start_time;
464 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
464 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
465
465
466 box_pid = 0;
466 box_pid = 0;
467 if (WEXITSTATUS(stat))
467 if (WEXITSTATUS(stat))
468 fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat));
468 fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat));
469 - else if ((use_wall_clock ? wall : total.tv_sec) > timeout)
469 + else if ((use_wall_clock ?
470 + wall :
471 + (double) total.tv_sec +
472 + ((double) total.tv_usec/1000000.0)) > timeout)
470 fprintf(stderr,"Time limit exceeded.\n");
473 fprintf(stderr,"Time limit exceeded.\n");
471 else
474 else
472 // report OK and statistics
475 // report OK and statistics
473 fprintf(stderr,"OK\n");
476 fprintf(stderr,"OK\n");
474
477
475 print_running_stat((double) wall,
478 print_running_stat((double) wall,
476 (double) rus.ru_utime.tv_sec +
479 (double) rus.ru_utime.tv_sec +
477 ((double) rus.ru_utime.tv_usec/1000000.0),
480 ((double) rus.ru_utime.tv_usec/1000000.0),
478 (double) rus.ru_stime.tv_sec +
481 (double) rus.ru_stime.tv_sec +
479 ((double) rus.ru_stime.tv_usec/1000000.0),
482 ((double) rus.ru_stime.tv_usec/1000000.0),
480 max_mem_used);
483 max_mem_used);
481 /*
484 /*
482 (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
485 (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
483 (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
486 (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
484 (int) total.tv_usec,
487 (int) total.tv_usec,
485 wall,
488 wall,
486 syscall_count,
489 syscall_count,
487 max_mem_used);
490 max_mem_used);
488 */
491 */
489 exit(0);
492 exit(0);
490 }
493 }
491 if (WIFSIGNALED(stat))
494 if (WIFSIGNALED(stat))
492 {
495 {
493 box_pid = 0;
496 box_pid = 0;
494 fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat));
497 fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat));
495
498
496 struct timeval total;
499 struct timeval total;
497 int wall;
500 int wall;
498 wall = time(NULL) - start_time;
501 wall = time(NULL) - start_time;
499 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
502 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
500 print_running_stat((double) wall,
503 print_running_stat((double) wall,
501 (double) rus.ru_utime.tv_sec +
504 (double) rus.ru_utime.tv_sec +
502 ((double) rus.ru_utime.tv_usec/1000000.0),
505 ((double) rus.ru_utime.tv_usec/1000000.0),
503 (double) rus.ru_stime.tv_sec +
506 (double) rus.ru_stime.tv_sec +
504 ((double) rus.ru_stime.tv_usec/1000000.0),
507 ((double) rus.ru_stime.tv_usec/1000000.0),
505 max_mem_used);
508 max_mem_used);
506 exit(0);
509 exit(0);
507 }
510 }
508 if (WIFSTOPPED(stat))
511 if (WIFSTOPPED(stat))
509 {
512 {
510 int sig = WSTOPSIG(stat);
513 int sig = WSTOPSIG(stat);
511 if (sig == SIGTRAP)
514 if (sig == SIGTRAP)
512 {
515 {
513 struct user u;
516 struct user u;
514 static int stop_count = -1;
517 static int stop_count = -1;
515 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
518 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
516 die("ptrace(PTRACE_GETREGS): %m");
519 die("ptrace(PTRACE_GETREGS): %m");
517 stop_count++;
520 stop_count++;
518 if (!stop_count) /* Traceme request */
521 if (!stop_count) /* Traceme request */
519 log(">> Traceme request caught\n");
522 log(">> Traceme request caught\n");
520 else if (stop_count & 1) /* Syscall entry */
523 else if (stop_count & 1) /* Syscall entry */
521 {
524 {
522 log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx);
525 log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx);
523 syscall_count++;
526 syscall_count++;
524 if (!valid_syscall(&u))
527 if (!valid_syscall(&u))
525 {
528 {
526 /*
529 /*
527 * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
530 * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
528 * so we have to change it to something harmless (e.g., an undefined
531 * so we have to change it to something harmless (e.g., an undefined
529 * syscall) and make the program continue.
532 * syscall) and make the program continue.
530 */
533 */
531 unsigned int sys = u.regs.orig_eax;
534 unsigned int sys = u.regs.orig_eax;
532 u.regs.orig_eax = 0xffffffff;
535 u.regs.orig_eax = 0xffffffff;
533 if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0)
536 if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0)
534 die("ptrace(PTRACE_SETREGS): %m");
537 die("ptrace(PTRACE_SETREGS): %m");
535 die("Forbidden syscall %d.", sys);
538 die("Forbidden syscall %d.", sys);
536 }
539 }
537 }
540 }
538 else /* Syscall return */
541 else /* Syscall return */
539 log("= %ld\n", u.regs.eax);
542 log("= %ld\n", u.regs.eax);
540 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
543 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
541 }
544 }
542 else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ)
545 else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ)
543 {
546 {
544 log(">> Signal %d\n", sig);
547 log(">> Signal %d\n", sig);
545 ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
548 ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
546 }
549 }
547 else
550 else
548 die("Received signal %d.", sig);
551 die("Received signal %d.", sig);
549 }
552 }
550 else
553 else
551 die("wait4: unknown status %x, giving up!", stat);
554 die("wait4: unknown status %x, giving up!", stat);
552 }
555 }
553 }
556 }
554
557
555 static void
558 static void
556 box_inside(int argc, char **argv)
559 box_inside(int argc, char **argv)
557 {
560 {
558 struct rlimit rl;
561 struct rlimit rl;
559 char *args[argc+1];
562 char *args[argc+1];
560 char *env[1] = { NULL };
563 char *env[1] = { NULL };
561
564
562 memcpy(args, argv, argc * sizeof(char *));
565 memcpy(args, argv, argc * sizeof(char *));
563 args[argc] = NULL;
566 args[argc] = NULL;
564 if (set_cwd && chdir(set_cwd))
567 if (set_cwd && chdir(set_cwd))
565 die("chdir: %m");
568 die("chdir: %m");
566 if (redir_stdin)
569 if (redir_stdin)
567 {
570 {
568 close(0);
571 close(0);
569 if (open(redir_stdin, O_RDONLY) != 0)
572 if (open(redir_stdin, O_RDONLY) != 0)
570 die("open(\"%s\"): %m", redir_stdin);
573 die("open(\"%s\"): %m", redir_stdin);
571 }
574 }
572 if (redir_stdout)
575 if (redir_stdout)
573 {
576 {
574 close(1);
577 close(1);
575 if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
578 if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
576 die("open(\"%s\"): %m", redir_stdout);
579 die("open(\"%s\"): %m", redir_stdout);
577 }
580 }
578 dup2(1, 2);
581 dup2(1, 2);
579 setpgrp();
582 setpgrp();
580 if (memory_limit)
583 if (memory_limit)
581 {
584 {
582 rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
585 rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
583 if (setrlimit(RLIMIT_AS, &rl) < 0)
586 if (setrlimit(RLIMIT_AS, &rl) < 0)
584 die("setrlimit: %m");
587 die("setrlimit: %m");
585 }
588 }
586 rl.rlim_cur = rl.rlim_max = 64;
589 rl.rlim_cur = rl.rlim_max = 64;
587 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
590 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
588 die("setrlimit: %m");
591 die("setrlimit: %m");
589 if (filter_syscalls && ptrace(PTRACE_TRACEME) < 0)
592 if (filter_syscalls && ptrace(PTRACE_TRACEME) < 0)
590 die("ptrace(PTRACE_TRACEME): %m");
593 die("ptrace(PTRACE_TRACEME): %m");
591 execve(args[0], args, (pass_environ ? environ : env));
594 execve(args[0], args, (pass_environ ? environ : env));
592 die("execve(\"%s\"): %m", args[0]);
595 die("execve(\"%s\"): %m", args[0]);
593 }
596 }
594
597
595 static void
598 static void
596 usage(void)
599 usage(void)
597 {
600 {
598 fprintf(stderr, "Invalid arguments!\n");
601 fprintf(stderr, "Invalid arguments!\n");
599 printf("\
602 printf("\
600 Usage: box [<options>] -- <command> <arguments>\n\
603 Usage: box [<options>] -- <command> <arguments>\n\
601 \n\
604 \n\
602 Options:\n\
605 Options:\n\
603 -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
606 -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
604 -c <dir>\tChange directory to <dir> first\n\
607 -c <dir>\tChange directory to <dir> first\n\
605 -e\t\tPass full environment of parent process\n\
608 -e\t\tPass full environment of parent process\n\
606 -f\t\tFilter system calls (-ff=very restricted)\n\
609 -f\t\tFilter system calls (-ff=very restricted)\n\
607 -i <file>\tRedirect stdin from <file>\n\
610 -i <file>\tRedirect stdin from <file>\n\
608 -m <size>\tLimit address space to <size> KB\n\
611 -m <size>\tLimit address space to <size> KB\n\
609 -o <file>\tRedirect stdout to <file>\n\
612 -o <file>\tRedirect stdout to <file>\n\
610 -t <time>\tStop after <time> seconds\n\
613 -t <time>\tStop after <time> seconds\n\
611 -T\t\tAllow syscalls for measuring run time\n\
614 -T\t\tAllow syscalls for measuring run time\n\
612 -v\t\tBe verbose\n\
615 -v\t\tBe verbose\n\
613 -w\t\tMeasure wall clock time instead of run time\n\
616 -w\t\tMeasure wall clock time instead of run time\n\
614 ");
617 ");
615 exit(1);
618 exit(1);
616 }
619 }
617
620
618 int
621 int
619 main(int argc, char **argv)
622 main(int argc, char **argv)
620 {
623 {
621 int c;
624 int c;
622 uid_t uid;
625 uid_t uid;
623
626
624 while ((c = getopt(argc, argv, "a:c:efi:m:o:t:Tvw")) >= 0)
627 while ((c = getopt(argc, argv, "a:c:efi:m:o:t:Tvw")) >= 0)
625 switch (c)
628 switch (c)
626 {
629 {
627 case 'a':
630 case 'a':
628 file_access = atol(optarg);
631 file_access = atol(optarg);
629 break;
632 break;
630 case 'c':
633 case 'c':
631 set_cwd = optarg;
634 set_cwd = optarg;
632 break;
635 break;
633 case 'e':
636 case 'e':
634 pass_environ = 1;
637 pass_environ = 1;
635 break;
638 break;
636 case 'f':
639 case 'f':
637 filter_syscalls++;
640 filter_syscalls++;
638 break;
641 break;
639 case 'i':
642 case 'i':
640 redir_stdin = optarg;
643 redir_stdin = optarg;
641 break;
644 break;
642 case 'm':
645 case 'm':
643 memory_limit = atol(optarg);
646 memory_limit = atol(optarg);
644 break;
647 break;
645 case 'o':
648 case 'o':
646 redir_stdout = optarg;
649 redir_stdout = optarg;
647 break;
650 break;
648 case 't':
651 case 't':
649 - timeout = atol(optarg);
652 + timeout = atof(optarg);
650 break;
653 break;
651 case 'T':
654 case 'T':
652 allow_times++;
655 allow_times++;
653 break;
656 break;
654 case 'v':
657 case 'v':
655 verbose++;
658 verbose++;
656 break;
659 break;
657 case 'w':
660 case 'w':
658 use_wall_clock = 1;
661 use_wall_clock = 1;
659 break;
662 break;
660 default:
663 default:
661 usage();
664 usage();
662 }
665 }
663 if (optind >= argc)
666 if (optind >= argc)
664 usage();
667 usage();
665
668
666 uid = geteuid();
669 uid = geteuid();
667 if (setreuid(uid, uid) < 0)
670 if (setreuid(uid, uid) < 0)
668 die("setreuid: %m");
671 die("setreuid: %m");
669 box_pid = fork();
672 box_pid = fork();
670 if (box_pid < 0)
673 if (box_pid < 0)
671 die("fork: %m");
674 die("fork: %m");
672 if (!box_pid)
675 if (!box_pid)
673 box_inside(argc-optind, argv+optind);
676 box_inside(argc-optind, argv+optind);
674 else
677 else
675 boxkeeper();
678 boxkeeper();
676 die("Internal error: fell over edge of the world");
679 die("Internal error: fell over edge of the world");
677 }
680 }
@@ -1,20 +1,24
1 problem do
1 problem do
2 num_tests 2
2 num_tests 2
3 full_score 20
3 full_score 20
4 time_limit_each 1
4 time_limit_each 1
5 mem_limit_each 16
5 mem_limit_each 16
6 score_each 10
6 score_each 10
7
7
8 + test 1 do
9 + time_limit 0.5
10 + end
11 +
8 run 1 do
12 run 1 do
9 tests 1
13 tests 1
10 end
14 end
11
15
12 test 2 do
16 test 2 do
13 - time_limit 2
17 + time_limit 0.6
14 end
18 end
15
19
16 run 2 do
20 run 2 do
17 tests 2
21 tests 2
18 end
22 end
19
23
20 end
24 end
@@ -1,151 +1,151
1 require File.join(File.dirname(__FILE__),'spec_helper')
1 require File.join(File.dirname(__FILE__),'spec_helper')
2 require File.join(File.dirname(__FILE__),'engine_spec_helper')
2 require File.join(File.dirname(__FILE__),'engine_spec_helper')
3
3
4 describe "A grader engine, when grading submissions" do
4 describe "A grader engine, when grading submissions" do
5
5
6 include GraderEngineHelperMethods
6 include GraderEngineHelperMethods
7
7
8 before(:each) do
8 before(:each) do
9 @config = Grader::Configuration.get_instance
9 @config = Grader::Configuration.get_instance
10
10
11 # this test is from Pong
11 # this test is from Pong
12 @problem_test_normal = stub(Problem,
12 @problem_test_normal = stub(Problem,
13 :id => 1, :name => 'test_normal',
13 :id => 1, :name => 'test_normal',
14 :full_score => 135)
14 :full_score => 135)
15 @user_user1 = stub(User,
15 @user_user1 = stub(User,
16 :id => 1, :login => 'user1')
16 :id => 1, :login => 'user1')
17
17
18 @engine = Grader::Engine.new
18 @engine = Grader::Engine.new
19 init_sandbox
19 init_sandbox
20 end
20 end
21
21
22 it "should grade normal submission" do
22 it "should grade normal submission" do
23 grader_should(:grade => "test1_correct.c",
23 grader_should(:grade => "test1_correct.c",
24 :on => @problem_test_normal,
24 :on => @problem_test_normal,
25 :and_report => {
25 :and_report => {
26 :score => 135,
26 :score => 135,
27 :comment => /^PASSED/})
27 :comment => /^PASSED/})
28 end
28 end
29
29
30
30
31 it "should produce error message when submission cannot compile" do
31 it "should produce error message when submission cannot compile" do
32 grader_should(:grade => "test1_compile_error.c",
32 grader_should(:grade => "test1_compile_error.c",
33 :on => @problem_test_normal,
33 :on => @problem_test_normal,
34 :and_report => {
34 :and_report => {
35 :score => 0,
35 :score => 0,
36 :comment => 'FAILED: compilation error',
36 :comment => 'FAILED: compilation error',
37 :compiler_message => /[Ee]rror/})
37 :compiler_message => /[Ee]rror/})
38 end
38 end
39
39
40 it "should produce timeout error when submission runs forever" do
40 it "should produce timeout error when submission runs forever" do
41 @problem_test_timeout = stub(Problem,
41 @problem_test_timeout = stub(Problem,
42 :id => 1, :name => 'test_timeout',
42 :id => 1, :name => 'test_timeout',
43 :full_score => 10)
43 :full_score => 10)
44 grader_should(:grade => "test2_timeout.c",
44 grader_should(:grade => "test2_timeout.c",
45 :on => @problem_test_timeout,
45 :on => @problem_test_timeout,
46 :and_report => {
46 :and_report => {
47 :score => 0,
47 :score => 0,
48 :comment => 'FAILED: TT'})
48 :comment => 'FAILED: TT'})
49 end
49 end
50
50
51 - it "should produce timeout error correctly when submission runs slower than expected in less than a second" do
51 + it "should produce timeout error correctly with fractional running time and fractional time limits" do
52 - @problem_test_timeout = stub(Problem,
52 + @problem_test_timeout = stub(Problem,
53 :id => 1, :name => 'test_timeout',
53 :id => 1, :name => 'test_timeout',
54 :full_score => 20)
54 :full_score => 20)
55 - grader_should(:grade => "test2_1-5sec.c",
55 + grader_should(:grade => "test2_05sec.c",
56 :on => @problem_test_timeout,
56 :on => @problem_test_timeout,
57 :and_report => {
57 :and_report => {
58 :score => 10,
58 :score => 10,
59 :comment => 'FAILED: TP'})
59 :comment => 'FAILED: TP'})
60 end
60 end
61
61
62 it "should produce runtime error when submission uses too much static memory" do
62 it "should produce runtime error when submission uses too much static memory" do
63 @problem_test_memory = stub(Problem,
63 @problem_test_memory = stub(Problem,
64 :id => 1, :name => 'test_memory',
64 :id => 1, :name => 'test_memory',
65 :full_score => 20)
65 :full_score => 20)
66 grader_should(:grade => "add_too_much_memory_static.c",
66 grader_should(:grade => "add_too_much_memory_static.c",
67 :on => @problem_test_memory,
67 :on => @problem_test_memory,
68 :and_report => {
68 :and_report => {
69 :score => 10,
69 :score => 10,
70 :comment => /FAILED: [^P]P/})
70 :comment => /FAILED: [^P]P/})
71 end
71 end
72
72
73 it "should not allow submission to allocate too much dynamic memory" do
73 it "should not allow submission to allocate too much dynamic memory" do
74 @problem_test_memory = stub(Problem,
74 @problem_test_memory = stub(Problem,
75 :id => 1, :name => 'test_memory',
75 :id => 1, :name => 'test_memory',
76 :full_score => 20)
76 :full_score => 20)
77 grader_should(:grade => "add_too_much_memory_dynamic.c",
77 grader_should(:grade => "add_too_much_memory_dynamic.c",
78 :on => @problem_test_memory,
78 :on => @problem_test_memory,
79 :and_report => {
79 :and_report => {
80 :score => 10,
80 :score => 10,
81 :comment => /FAILED: [^P]P/})
81 :comment => /FAILED: [^P]P/})
82 end
82 end
83
83
84 it "should score test runs correctly when submission fails in some test case" do
84 it "should score test runs correctly when submission fails in some test case" do
85 grader_should(:grade => "add_fail_test_case_1.c",
85 grader_should(:grade => "add_fail_test_case_1.c",
86 :on => @problem_test_normal,
86 :on => @problem_test_normal,
87 :and_report => {
87 :and_report => {
88 :score => 105,
88 :score => 105,
89 :comment => /^FAILED:/})
89 :comment => /^FAILED:/})
90 end
90 end
91
91
92 it "should fail submission with non-zero exit status" do
92 it "should fail submission with non-zero exit status" do
93 grader_should(:grade => "add_nonzero_exit_status.c",
93 grader_should(:grade => "add_nonzero_exit_status.c",
94 :on => @problem_test_normal,
94 :on => @problem_test_normal,
95 :and_report => {
95 :and_report => {
96 :score => 0,
96 :score => 0,
97 :comment => /^FAILED:/})
97 :comment => /^FAILED:/})
98 end
98 end
99
99
100 it "should not allow malicious submission to see PROBLEM_HOME" do
100 it "should not allow malicious submission to see PROBLEM_HOME" do
101 problem_test_yesno = stub(Problem,
101 problem_test_yesno = stub(Problem,
102 :id => 1, :name => 'test_yesno',
102 :id => 1, :name => 'test_yesno',
103 :full_score => 10)
103 :full_score => 10)
104 grader_should(:grade => "yesno_access_problem_home.c",
104 grader_should(:grade => "yesno_access_problem_home.c",
105 :on => problem_test_yesno,
105 :on => problem_test_yesno,
106 :and_report => {
106 :and_report => {
107 :score => 0,
107 :score => 0,
108 :comment => /^FAILED:/})
108 :comment => /^FAILED:/})
109 end
109 end
110
110
111 it "should not allow malicious submission to open files" do
111 it "should not allow malicious submission to open files" do
112 problem_test_yesno = stub(Problem,
112 problem_test_yesno = stub(Problem,
113 :id => 1, :name => 'test_yesno',
113 :id => 1, :name => 'test_yesno',
114 :full_score => 10)
114 :full_score => 10)
115 grader_should(:grade => "yesno_open_file.c",
115 grader_should(:grade => "yesno_open_file.c",
116 :on => problem_test_yesno,
116 :on => problem_test_yesno,
117 :and_report => {
117 :and_report => {
118 :score => 0,
118 :score => 0,
119 :comment => /^FAILED:/})
119 :comment => /^FAILED:/})
120 end
120 end
121
121
122 def grader_should(args)
122 def grader_should(args)
123 @user1 = stub(User,
123 @user1 = stub(User,
124 :id => 1, :login => 'user1')
124 :id => 1, :login => 'user1')
125 submission =
125 submission =
126 create_submission_from_file(1, @user1, args[:on], args[:grade])
126 create_submission_from_file(1, @user1, args[:on], args[:grade])
127 submission.should_receive(:graded_at=)
127 submission.should_receive(:graded_at=)
128
128
129 expected_score = args[:and_report][:score]
129 expected_score = args[:and_report][:score]
130 expected_comment = args[:and_report][:comment]
130 expected_comment = args[:and_report][:comment]
131 if args[:and_report][:compiler_message]!=nil
131 if args[:and_report][:compiler_message]!=nil
132 expected_compiler_message = args[:and_report][:compiler_message]
132 expected_compiler_message = args[:and_report][:compiler_message]
133 else
133 else
134 expected_compiler_message = ''
134 expected_compiler_message = ''
135 end
135 end
136
136
137 submission.should_receive(:points=).with(expected_score)
137 submission.should_receive(:points=).with(expected_score)
138 submission.should_receive(:grader_comment=).with(expected_comment)
138 submission.should_receive(:grader_comment=).with(expected_comment)
139 submission.should_receive(:compiler_message=).with(expected_compiler_message)
139 submission.should_receive(:compiler_message=).with(expected_compiler_message)
140 submission.should_receive(:save)
140 submission.should_receive(:save)
141
141
142 @engine.grade(submission)
142 @engine.grade(submission)
143 end
143 end
144
144
145 protected
145 protected
146
146
147 def create_normal_submission_mock_from_file(source_fname)
147 def create_normal_submission_mock_from_file(source_fname)
148 create_submission_from_file(1, @user_user1, @problem_test_normal, source_fname)
148 create_submission_from_file(1, @user_user1, @problem_test_normal, source_fname)
149 end
149 end
150
150
151 end
151 end
deleted file
You need to be logged in to leave comments. Login now