~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/webapp/tutorial/test/TestFramework.py

Updated the exercises to be loaded from the database, not a local file.

This updated fixes some things broken in my previous commit, and now
it should leave the worksheets in a viewable (not working) state.

I have also updated ivle-addexercise and the database schema to reflect
the schema of an exercise.

Show diffs side-by-side

added added

removed removed

Lines of Context:
118
118
    true = classmethod(lambda *x: True)
119
119
    false = classmethod(lambda *x: False)
120
120
 
121
 
    def __init__(self, pass_msg, fail_msg, default='match'):
 
121
    def __init__(self, test_case):
122
122
        """Initialise with descriptions (pass,fail) and a default behavior for output
123
123
        If default is match, unspecified files are matched exactly
124
124
        If default is ignore, unspecified files are ignored
125
125
        The default default is match.
126
126
        """
127
 
        self._pass_msg = pass_msg
128
 
        self._fail_msg = fail_msg
129
 
        self._default = default
130
 
        if default == 'ignore':
 
127
        self._pass_msg = test_case.passmsg
 
128
        self._fail_msg = test_case.failmsg
 
129
        self._default = test_case.test_default
 
130
        if self._default == 'ignore':
131
131
            self._default_func = self.true
132
132
        else:
133
133
            self._default_func = self.match
138
138
        self._exception_test = ('check', self._default_func)
139
139
        self._result_test = ('check', self._default_func)
140
140
        self._code_test = ('check', self._default_func)
 
141
        
 
142
        for part in test_case.parts:
 
143
            if part.part_type =="file":
 
144
                add_file_test(part)
 
145
            elif part.part_type =="stdout":
 
146
                add_stdout_test(part)
 
147
            elif part.part_type =="stderr":
 
148
                add_stderr_test(part)
 
149
            elif part.part_type =="result":
 
150
                add_result_test(part)
 
151
            elif part.part_type =="exception":
 
152
                add_exception_test(part)
 
153
            elif part.part_type =="code":
 
154
                add_code_test(part)
141
155
 
142
156
    def _set_default_function(self, function, test_type):
143
157
        """"Ensure test type is valid and set function to a default
191
205
        for filename, (test_type, function) in self._file_tests.items():
192
206
            self._file_tests[filename] = (test_type, self._validate_function(function, included_code))
193
207
            
194
 
    def add_result_test(self, function, test_type='norm'):
 
208
    def add_result_test(self, part):
195
209
        "Test part that compares function return values"
196
 
        function = self._set_default_function(function, test_type)
 
210
        function = self._set_default_function(part.data, part.test_type)
197
211
        self._result_test = (test_type, function)
198
212
            
199
 
    def add_stdout_test(self, function, test_type='norm'):
 
213
    def add_stdout_test(self, part):
200
214
        "Test part that compares stdout"
201
 
        function = self._set_default_function(function, test_type)
 
215
        function = self._set_default_function(part.data, part.test_type)
202
216
        self._stdout_test = (test_type, function)
203
217
 
204
 
    def add_stderr_test(self, function, test_type='norm'):
 
218
    def add_stderr_test(self, part):
205
219
        "Test part that compares stderr"
206
 
        function = self._set_default_function(function, test_type)
 
220
        function = self._set_default_function(part.data, part.test_type)
207
221
        self._stderr_test = (test_type, function)
208
222
 
209
 
    def add_exception_test(self, function, test_type='norm'):
 
223
    def add_exception_test(self, part):
210
224
        "Test part that compares stderr"
211
 
        function = self._set_default_function(function, test_type)
 
225
        function = self._set_default_function(part.data, part.test_type)
212
226
        self._exception_test = (test_type, function)
213
227
 
214
 
    def add_file_test(self, filename, function, test_type='norm'):
 
228
    def add_file_test(self, part):
215
229
        "Test part that compares the contents of a specified file"
216
 
        function = self._set_default_function(function, test_type)
217
 
        self._file_tests[filename] = (test_type, function)
 
230
        function = self._set_default_function(part.data, part.test_type)
 
231
        self._file_tests[part.filename] = (test_type, function)
218
232
 
219
 
    def add_code_test(self, function, test_type='norm'):
 
233
    def add_code_test(self, part):
220
234
        "Test part that examines the supplied code"
221
 
        function = self._set_default_function(function, test_type)
 
235
        function = self._set_default_function(part.data, part.test_type)
222
236
        self._code_test = (test_type, function)
223
237
 
224
238
    def _check_output(self, solution_output, attempt_output, test_type, f):
307
321
    """
308
322
    A set of tests with a common inputs
309
323
    """
310
 
    def __init__(self, console, name='', function=None, stdin='', filespace=None, global_space=None):
 
324
    def __init__(self, console, suite):
311
325
        """Initialise with name and optionally, a function to test (instead of the entire script)
312
326
        The inputs stdin, the filespace and global variables can also be specified at
313
327
        initialisation, but may also be set later.
314
328
        """
315
329
        self._console = console
316
 
 
317
 
        if global_space == None:
318
 
            global_space = {}
319
 
        if filespace == None:
320
 
            filespace = {}
321
 
        
322
 
        self._name = name
323
 
        
 
330
        self._name = suite.description
 
331
        
 
332
        function = suite.function
324
333
        if function == '': function = None
325
334
        self._function = function
326
335
        self._list_args = []
327
336
        self._keyword_args = {}
328
337
        
329
 
        self.set_stdin(stdin)
330
 
        self._filespace = testfilespace.TestFilespace(filespace)
331
 
        self._global_space = global_space
 
338
        self.set_stdin(suite.stdin)
 
339
        self._filespace = testfilespace.TestFilespace(None)
 
340
        self._global_space = {}
332
341
        self._parts = []
333
342
        self._allowed_exceptions = set()
 
343
        
 
344
        for var in suite.variables:
 
345
            if var.var_type == "file":
 
346
                self.add_file(var)
 
347
            elif var.var_type == "var":
 
348
                self.add_variable(var)
 
349
            elif var.var_type == "arg":
 
350
                self.add_arg(var)
 
351
            elif var.var_type == "exception":
 
352
                self.add_exception(var)
 
353
        
 
354
        for test_case in suite.test_cases:
 
355
            self.add_part(TestCasePart(test_case))
334
356
 
335
357
    def set_stdin(self, stdin):
336
358
        """ Set the given string as the stdin for this test case"""
337
359
        # stdin must have a newline at the end for raw_input to work properly
338
 
        if stdin[-1:] != '\n':
339
 
            stdin += '\n'
 
360
        if stdin is not None:
 
361
            if stdin[-1:] != '\n':
 
362
                stdin += '\n'
 
363
        else:
 
364
            stdin = ""
340
365
        self._stdin = stdin
341
366
 
342
 
    def add_file(self, filename, data):
 
367
    def add_file(self, filevar):
343
368
        """ Insert the given filename-data pair into the filespace for this test case"""
344
369
        # TODO: Add the file to the console
345
 
        self._filespace.add_file(filename, data)
 
370
        self._filespace.add_file(filevar.var_name, "")
346
371
        
347
 
    def add_variable(self, variable, value):
 
372
    def add_variable(self, var):
348
373
        """ Add the given varibale-value pair to the initial global environment
349
374
        for this test case. The value is the string repr() of an actual value.
350
375
        Throw an exception if the value cannot be paresed.
351
376
        """
352
377
        
353
378
        try:
354
 
            self._global_space[variable] = eval(value)
 
379
            self._global_space[var.var_name] = eval(var.var_value)
355
380
        except:
356
 
            raise TestCreationError("Invalid value for variable %s: %s" %(variable, value))
 
381
            raise TestCreationError("Invalid value for variable %s: %s" 
 
382
                                    %(var.var_name, var.var_value))
357
383
 
358
 
    def add_arg(self, value, name=None):
 
384
    def add_arg(self, var):
359
385
        """ Add a value to the argument list. This only applies when testing functions.
360
386
        By default arguments are not named, but if they are, they become keyword arguments.
361
387
        """
362
388
        try:
363
 
            if name == None or name == '':
364
 
                self._list_args.append(eval(value))
 
389
            if var.var_name == None or var.var_name == '':
 
390
                self._list_args.append(eval(var.var_value))
365
391
            else:
366
 
                self._keyword_args[name] = value
 
392
                self._keyword_args[var.var_name] = var.var_value
367
393
        except:
368
 
            raise TestCreationError("Invalid value for function argument: %s" %value)
 
394
            raise TestCreationError("Invalid value for function argument: %s" %var.var_value)
369
395
 
370
396
    def add_exception(self, exception_name):
371
 
        self._allowed_exceptions.add(exception_name)
 
397
        self._allowed_exceptions.add(var.var_name)
372
398
        
373
399
    def add_part(self, test_part):
374
400
        """ Add a TestPart to this test case"""
406
432
                solution_data = self._run_function(self._function,
407
433
                    self._list_args, self._keyword_args, solution)
408
434
                
409
 
        except:
410
 
            raise ScriptExecutionError(sys.exc_info())
 
435
        except Exception, e:
 
436
            raise e #ScriptExecutionError(sys.exc_info())
411
437
 
412
438
        # Run student attempt
413
439
        try:
523
549
    """
524
550
    The complete collection of test cases for a given exercise
525
551
    """
526
 
    def __init__(self, name, console, solution=None):
 
552
    def __init__(self, exercise, console):
527
553
        """Initialise with the name of the test suite (the exercise name) and the solution.
528
554
        The solution may be specified later.
529
555
        """
530
 
        self._solution = solution
531
 
        self._name = name
 
556
        self._solution = exercise.solution
 
557
        self._name = exercise.id
 
558
        self._exercise = exercise
532
559
        self._tests = []
533
560
        self._console = console
534
 
        self.add_include_code("")
535
 
 
536
 
    def add_solution(self, solution):
537
 
        " Specify the solution script for this exercise "
538
 
        self._solution = solution
 
561
        self.add_include_code(exercise.include)
 
562
        
 
563
        for test_case in exercise.test_suites:
 
564
            new_case = TestCase(console, test_case)
 
565
            self.add_case(new_case)
539
566
 
540
567
    def has_solution(self):
541
568
        " Returns true if a solution has been provided "
595
622
        return exercise_dict
596
623
 
597
624
    def get_name(self):
598
 
        return self._name
599
 
 
600
 
##def get_function(filename, function_name):
601
 
##      import compiler
602
 
##      mod = compiler.parseFile(filename)
603
 
##      for node in mod.node.nodes:
604
 
##              if isinstance(node, compiler.ast.Function) and node.name == function_name:
605
 
##                      return node
606
 
##  
 
625
        return self._names