334
338
def set_stdin(self, stdin):
335
339
""" Set the given string as the stdin for this test case"""
340
# TODO: Set stdin of console
336
341
self._stdin = stdin
338
343
def add_file(self, filename, data):
339
344
""" Insert the given filename-data pair into the filespace for this test case"""
345
# TODO: Add the file to the console
340
346
self._filespace.add_file(filename, data)
342
348
def add_variable(self, variable, value):
343
349
""" Add the given varibale-value pair to the initial global environment
345
Throw and exception if the value cannot be paresed.
350
for this test case. The value is the string repr() of an actual value.
351
Throw an exception if the value cannot be paresed.
395
401
# if we are just testing a function
396
402
if not self._function == None:
397
if self._function not in global_space_copy:
403
if self._function not in solution_data['globals']:
398
404
raise FunctionNotFoundError(self._function)
399
func_to_exec = lambda: global_space_copy[self._function](
400
*self._list_args, **self._keyword_args)
401
solution_data = self._run_function(func_to_exec, solution)
405
solution_data = self._run_function(self._function,
406
self._list_args, self._keyword_args, solution)
404
409
raise ScriptExecutionError(sys.exc_info())
411
416
# if we are just testing a function
412
417
if not self._function == None:
413
if self._function not in global_space_copy:
418
if self._function not in attempt_data['globals']:
414
419
raise FunctionNotFoundError(self._function)
415
func_to_exec = lambda: global_space_copy[self._function](
416
*self._list_args, **self._keyword_args)
417
attempt_data = self._run_function(func_to_exec, attempt_code)
420
attempt_data = self._run_function(self._function,
421
self._list_args, self._keyword_args, attempt_code)
419
423
case_dict['exception'] = ScriptExecutionError(sys.exc_info()).to_dict()
420
424
case_dict['passed'] = False
459
463
def _execstring(self, string, global_space):
460
""" Execute the given string in global_space, and return the outputs. """
464
""" Execute the given string in global_space, and return the outputs.
461
466
self._initialise_global_space(global_space)
464
exec string in global_space
466
data = self._run_function(f, code=string)
468
inspection = self._console.inspect(string)
470
exception_name = None
471
if 'exception' in inspection:
472
exception = inspection['exception']['except']
473
exception_name = type(exception).__name__
476
return {'code': string,
478
'globals': inspection['globals'],
479
'exception': exception_name, # Hmmm... odd? Is this right?
480
'stdout': inspection['stdout'],
481
'stderr': inspection['stderr'],
482
'modified_files': None}
469
484
def _initialise_global_space(self, global_space):
470
485
""" Modify the provided global_space so that file, open and raw_input are redefined
471
486
to use our methods instead.
488
self._console.flush(global_space)
473
489
self._current_filespace_copy = self._filespace.copy()
474
490
global_space['file'] = lambda filename, mode='r', bufsize=-1: self._current_filespace_copy.openfile(filename, mode)
475
491
global_space['open'] = global_space['file']
476
492
global_space['raw_input'] = lambda x=None: raw_input()
477
493
return global_space
479
def _run_function(self, function, code):
495
def _run_function(self, function, args, kwargs, code):
480
496
""" Run the provided function with the provided stdin, capturing stdout and stderr
481
497
and the return value.
482
498
Return all the output data.
483
499
code: The full text of the code, which needs to be stored as part of
484
500
the returned dictionary.
487
sys_stdout, sys_stdin, sys_stderr = sys.stdout, sys.stdin, sys.stderr
489
output_stream, input_stream, error_stream = StringIO.StringIO(), StringIO.StringIO(self._stdin), StringIO.StringIO()
490
sys.stdout, sys.stdin, sys.stderr = output_stream, input_stream, error_stream
502
s_args = map(repr, args)
503
s_kwargs = dict(zip(kwargs.keys(), map(repr, kwargs.values())))
504
call = self._console.call(function, *s_args, **s_kwargs)
493
506
exception_name = None
498
sys.stdout, sys.stdin, sys.stderr = sys_stdout, sys_stdin, sys_stderr
499
exception_name = sys.exc_info()[0].__name__
500
if exception_name not in self._allowed_exceptions:
503
sys.stdout, sys.stdin, sys.stderr = sys_stdout, sys_stdin, sys_stderr
507
if 'exception' in call:
508
exception = call['exception']['except']
509
exception_name = type(exception).__name__
505
self._current_filespace_copy.flush_all()
507
512
return {'code': code,
513
'result': call['result'],
509
514
'exception': exception_name,
510
'stdout': output_stream.getvalue(),
511
'stderr': output_stream.getvalue(),
512
'modified_files': self._current_filespace_copy.get_modified_files()}
515
'stdout': call['stdout'],
516
'stderr': call['stderr'],
517
'modified_files': None}
516
521
The complete collection of test cases for a given exercise
518
def __init__(self, name, solution=None):
523
def __init__(self, name, console, solution=None):
519
524
"""Initialise with the name of the test suite (the exercise name) and the solution.
520
525
The solution may be specified later.
522
527
self._solution = solution
523
528
self._name = name
530
self._console = console
525
531
self.add_include_code("")
527
533
def add_solution(self, solution):