177 | 177 |
|
178 | 178 |
>>> b = Block()
|
179 | 179 |
>>> b.append(u'| Test body here.')
|
180 | |
>>> b.append(u'= Expected output here')
|
|
180 |
>>> b.append(u'= Expected result here.')
|
181 | 181 |
>>> print b.classify()
|
182 | 182 |
[TestBody(line_num=1), ExpectedResult(line_num=1)]
|
|
183 |
|
|
184 |
>>> b = Block()
|
|
185 |
>>> b.append(u'| Test body here.')
|
|
186 |
>>> b.append(u'? Expected error here.')
|
|
187 |
>>> print b.classify()
|
|
188 |
[TestBody(line_num=1), ExpectedError(line_num=1)]
|
183 | 189 |
|
184 | 190 |
"""
|
185 | 191 |
|
|
221 | 227 |
def text(self, seperator='\n'):
|
222 | 228 |
return seperator.join(self.lines)
|
223 | 229 |
|
|
230 |
def is_empty(self):
|
|
231 |
return all([(not line or line.isspace()) for line in self.lines])
|
|
232 |
|
224 | 233 |
def deconstruct(self):
|
225 | 234 |
"""Return a list of pairs of (prefix, list of lines) representing
|
226 | 235 |
the contents of this Block. The pairs are in the order the runs
|
|
256 | 265 |
|
257 | 266 |
pattern = self.deconstruct()
|
258 | 267 |
pattern_prefixes = [p[0] for p in pattern]
|
259 | |
if len(pattern_prefixes) == 1:
|
260 | |
return [self.PREFIX_MAP[pattern_prefixes[0]](lines=self.lines)]
|
261 | |
|
262 | |
if pattern_prefixes == [u'| ', u'= ']:
|
263 | |
return [TestBody(lines=pattern[0][1]), ExpectedResult(lines=pattern[1][1])]
|
264 | |
|
265 | |
return []
|
|
268 |
|
|
269 |
if '' in pattern_prefixes:
|
|
270 |
# There is plain, non-prefixed text embedded somewhere in this Block.
|
|
271 |
# TODO: interpret this according to the new, not-yet-written rules.
|
|
272 |
return []
|
|
273 |
else:
|
|
274 |
valid_patterns = [
|
|
275 |
[u'->'],
|
|
276 |
[u'> '],
|
|
277 |
[u'| ', u'= '],
|
|
278 |
[u'| ', u'? '],
|
|
279 |
[u'| ', u'+ ', u'= '],
|
|
280 |
[u'| ', u'+ ', u'? '],
|
|
281 |
[u'+ ', u'= '],
|
|
282 |
[u'+ ', u'? '],
|
|
283 |
]
|
|
284 |
if pattern_prefixes in valid_patterns:
|
|
285 |
return [self.PREFIX_MAP[prefix](line_num=self.line_num, filename=self.filename, lines=lines) for (prefix, lines) in pattern]
|
|
286 |
raise FalderalSyntaxError(
|
|
287 |
("line %d: " % self.line_num) +
|
|
288 |
"test block must consist of test body and/or test input, then expectation, in that order")
|
266 | 289 |
|
267 | 290 |
|
268 | 291 |
class LiterateCode(Block):
|
|
323 | 346 |
def parse_lines_to_blocks(self):
|
324 | 347 |
r"""Parse the lines of the Document into Blocks.
|
325 | 348 |
|
326 | |
| >>> d = Document()
|
327 | |
| >>> d.append(u'This is a test file.')
|
328 | |
| >>> d.append(u' -> This is a pragma.')
|
329 | |
| >>> d.append(u'')
|
330 | |
| >>> d.append(u" | This is some test input.\n")
|
331 | |
| >>> d.append(u" | It extends over two lines.")
|
332 | |
| >>> d.append(u' ? Expected Error')
|
333 | |
| >>> d.append(u'')
|
334 | |
| >>> d.append(u' | Test with input')
|
335 | |
| >>> d.append(u' + input-for-test')
|
336 | |
| >>> d.append(u' = Expected result on output')
|
337 | |
| >>> d.parse_lines_to_blocks()
|
338 | |
| >>> [b.__class__.__name__ for b in d.blocks]
|
339 | |
| ['InterveningText', 'Pragma', 'TestBody', 'ExpectedError',
|
340 | |
| 'TestBody', 'TestInput', 'ExpectedResult']
|
341 | |
| >>> [b.line_num for b in d.blocks]
|
342 | |
| [1, 2, 3, 5, 6, 7, 8]
|
|
349 |
>>> d = Document()
|
|
350 |
>>> d.append(u'This is a test file.')
|
|
351 |
>>> d.append(u' -> This is a pragma.')
|
|
352 |
>>> d.append(u'')
|
|
353 |
>>> d.append(u" | This is some test input.\n")
|
|
354 |
>>> d.append(u" | It extends over two lines.")
|
|
355 |
>>> d.append(u' ? Expected Error')
|
|
356 |
>>> d.append(u'')
|
|
357 |
>>> d.append(u' | Test with input')
|
|
358 |
>>> d.append(u' + input-for-test')
|
|
359 |
>>> d.append(u' = Expected result on output')
|
|
360 |
>>> d.parse_lines_to_blocks()
|
|
361 |
>>> [block.lines for block in d.blocks if isinstance(block, InterveningText)]
|
|
362 |
[[u'This is a test file.']]
|
|
363 |
>>> [b.__class__.__name__ for b in d.blocks]
|
|
364 |
['InterveningText', 'Pragma', 'TestBody', 'ExpectedError',
|
|
365 |
'TestBody', 'TestInput', 'ExpectedResult']
|
|
366 |
>>> [b.line_num for b in d.blocks]
|
|
367 |
[1, 2, 4, 4, 8, 8, 8]
|
343 | 368 |
|
344 | 369 |
"""
|
345 | 370 |
indent = None
|
|
386 | 411 |
new_blocks = []
|
387 | 412 |
for block in blocks:
|
388 | 413 |
if isinstance(block, InterveningText):
|
|
414 |
if block.is_empty():
|
|
415 |
continue
|
389 | 416 |
new_blocks.append(block)
|
390 | 417 |
else:
|
391 | 418 |
new_blocks.extend(block.classify())
|
|
394 | 421 |
def parse_blocks_to_tests(self, functionalities):
|
395 | 422 |
r"""Assemble a list of Tests from the blocks in this Document.
|
396 | 423 |
|
397 | |
| >>> funs = {}
|
398 | |
| >>> d = Document()
|
399 | |
| >>> d.append(u"This is a text file.")
|
400 | |
| >>> d.append(u'It contains NO tests.')
|
401 | |
| >>> d.parse_blocks_to_tests(funs)
|
402 | |
| []
|
403 | |
|
|
404 | |
| >>> d = Document()
|
405 | |
| >>> d.append(u'This is a test file.')
|
406 | |
| >>> d.append(u' -> Tests for functionality "Parse Thing"')
|
407 | |
| >>> d.append(u" | This is some test body.")
|
408 | |
| >>> d.append(u" | It extends over two lines.")
|
409 | |
| >>> d.append(u' ? Expected Error')
|
410 | |
| >>> d.append(u' | Test with input')
|
411 | |
| >>> d.append(u' + input-for-test')
|
412 | |
| >>> d.append(u' = Expected result on output')
|
413 | |
| >>> d.append(u' + Other input-for-test')
|
414 | |
| >>> d.append(u' = Other Expected result on output')
|
415 | |
| >>> d.append(u' -> Tests for functionality "Run Thing"')
|
416 | |
| >>> d.append(u" | Thing")
|
417 | |
| >>> d.append(u' ? Oops')
|
418 | |
| >>> d.parse_lines_to_blocks()
|
419 | |
| >>> [b.__class__.__name__ for b in d.blocks]
|
420 | |
| ['InterveningText', 'Pragma', 'TestBody', 'ExpectedError',
|
421 | |
| 'TestBody', 'TestInput', 'ExpectedResult',
|
422 | |
| 'TestInput', 'ExpectedResult', 'Pragma', 'TestBody', 'ExpectedError']
|
423 | |
| >>> tests = d.parse_blocks_to_tests(funs)
|
424 | |
| >>> [t.body for t in tests]
|
425 | |
| [u'This is some test body.\nIt extends over two lines.',
|
426 | |
| u'Test with input', u'Test with input', u'Thing']
|
427 | |
| >>> [t.input_block for t in tests]
|
428 | |
| [None, TestInput(u' + ', line_num=7, filename=None),
|
429 | |
| TestInput(u' + ', line_num=9, filename=None), None]
|
430 | |
| >>> tests[1].input_block.text()
|
431 | |
| u'input-for-test'
|
432 | |
| >>> tests[2].input_block.text()
|
433 | |
| u'Other input-for-test'
|
434 | |
| >>> [t.expectation for t in tests]
|
435 | |
| [ErrorOutcome(u'Expected Error'),
|
436 | |
| OutputOutcome(u'Expected result on output'),
|
437 | |
| OutputOutcome(u'Other Expected result on output'),
|
438 | |
| ErrorOutcome(u'Oops')]
|
439 | |
| >>> [t.functionality.name for t in tests]
|
440 | |
| [u'Parse Thing', u'Parse Thing', u'Parse Thing', u'Run Thing']
|
441 | |
| >>> sorted(funs.keys())
|
442 | |
| [u'Parse Thing', u'Run Thing']
|
443 | |
|
|
444 | |
| >>> d = Document()
|
445 | |
| >>> d.append(u" | This is some test body.")
|
446 | |
| >>> d.append(u' = Expected')
|
447 | |
| >>> d.parse_blocks_to_tests({})
|
448 | |
| Traceback (most recent call last):
|
449 | |
| ...
|
450 | |
| FalderalSyntaxError: line 2: functionality under test not specified
|
451 | |
|
|
452 | |
| >>> d = Document()
|
453 | |
| >>> d.append(u'This is a test file.')
|
454 | |
| >>> d.append(u' ? Expected Error')
|
455 | |
| >>> d.parse_blocks_to_tests({})
|
456 | |
| Traceback (most recent call last):
|
457 | |
| ...
|
458 | |
| FalderalSyntaxError: line 2: expectation must be preceded by test body or test input
|
459 | |
|
|
460 | |
| >>> d = Document()
|
461 | |
| >>> d.append(u' -> Hello, this is pragma')
|
462 | |
| >>> d.append(u' = Expected')
|
463 | |
| >>> d.parse_blocks_to_tests({})
|
464 | |
| Traceback (most recent call last):
|
465 | |
| ...
|
466 | |
| FalderalSyntaxError: line 2: expectation must be preceded by test body or test input
|
467 | |
|
|
468 | |
| >>> d = Document()
|
469 | |
| >>> d.append(u' | This is test')
|
470 | |
| >>> d.append(u'This is text')
|
471 | |
| >>> d.parse_blocks_to_tests({})
|
472 | |
| Traceback (most recent call last):
|
473 | |
| ...
|
474 | |
| FalderalSyntaxError: line 2: test body must be followed by expectation or test input
|
475 | |
|
|
476 | |
| >>> d = Document()
|
477 | |
| >>> d.append(u' -> Hello, this is pragma')
|
478 | |
| >>> d.append(u' + Input to where exactly?')
|
479 | |
| >>> d.parse_blocks_to_tests({})
|
480 | |
| Traceback (most recent call last):
|
481 | |
| ...
|
482 | |
| FalderalSyntaxError: line 2: test input must be preceded by test body
|
483 | |
|
|
484 | |
| >>> d = Document()
|
485 | |
| >>> funs = {}
|
486 | |
| >>> d.append(u' -> Functionality "Parse Stuff" is implemented by '
|
487 | |
| ... u'shell command "parse"')
|
488 | |
| >>> d.append(u'')
|
489 | |
| >>> d.append(u' -> Functionality "Parse Stuff" is')
|
490 | |
| >>> d.append(u' -> implemented by shell command "pxxxy"')
|
491 | |
| >>> tests = d.parse_blocks_to_tests(funs)
|
492 | |
| >>> len(funs.keys())
|
493 | |
| 1
|
494 | |
| >>> [i for i in funs["Parse Stuff"].implementations]
|
495 | |
| [ShellImplementation(u'parse'), ShellImplementation(u'pxxxy')]
|
|
424 |
>>> funs = {}
|
|
425 |
>>> d = Document()
|
|
426 |
>>> d.append(u"This is a text file.")
|
|
427 |
>>> d.append(u'It contains NO tests.')
|
|
428 |
>>> d.parse_blocks_to_tests(funs)
|
|
429 |
[]
|
|
430 |
|
|
431 |
>>> d = Document()
|
|
432 |
>>> d.append(u'This is a test file.')
|
|
433 |
>>> d.append(u' -> Tests for functionality "Parse Thing"')
|
|
434 |
>>> d.append(u" | This is some test body.")
|
|
435 |
>>> d.append(u" | It extends over two lines.")
|
|
436 |
>>> d.append(u' ? Expected Error')
|
|
437 |
>>> d.append(u' | Test with input')
|
|
438 |
>>> d.append(u' + input-for-test')
|
|
439 |
>>> d.append(u' = Expected result on output')
|
|
440 |
>>> d.append(u' + Other input-for-test')
|
|
441 |
>>> d.append(u' = Other Expected result on output')
|
|
442 |
>>> d.append(u' -> Tests for functionality "Run Thing"')
|
|
443 |
>>> d.append(u" | Thing")
|
|
444 |
>>> d.append(u' ? Oops')
|
|
445 |
>>> d.parse_lines_to_blocks()
|
|
446 |
>>> [b.__class__.__name__ for b in d.blocks]
|
|
447 |
['InterveningText', 'Pragma', 'TestBody', 'ExpectedError',
|
|
448 |
'TestBody', 'TestInput', 'ExpectedResult',
|
|
449 |
'TestInput', 'ExpectedResult', 'Pragma', 'TestBody', 'ExpectedError']
|
|
450 |
>>> tests = d.parse_blocks_to_tests(funs)
|
|
451 |
>>> [t.body for t in tests]
|
|
452 |
[u'This is some test body.\nIt extends over two lines.',
|
|
453 |
u'Test with input', u'Test with input', u'Thing']
|
|
454 |
>>> [t.input_block for t in tests]
|
|
455 |
[None, TestInput(u' + ', line_num=7, filename=None),
|
|
456 |
TestInput(u' + ', line_num=9, filename=None), None]
|
|
457 |
>>> tests[1].input_block.text()
|
|
458 |
u'input-for-test'
|
|
459 |
>>> tests[2].input_block.text()
|
|
460 |
u'Other input-for-test'
|
|
461 |
>>> [t.expectation for t in tests]
|
|
462 |
[ErrorOutcome(u'Expected Error'),
|
|
463 |
OutputOutcome(u'Expected result on output'),
|
|
464 |
OutputOutcome(u'Other Expected result on output'),
|
|
465 |
ErrorOutcome(u'Oops')]
|
|
466 |
>>> [t.functionality.name for t in tests]
|
|
467 |
[u'Parse Thing', u'Parse Thing', u'Parse Thing', u'Run Thing']
|
|
468 |
>>> sorted(funs.keys())
|
|
469 |
[u'Parse Thing', u'Run Thing']
|
|
470 |
|
|
471 |
>>> d = Document()
|
|
472 |
>>> d.append(u" | This is some test body.")
|
|
473 |
>>> d.append(u' = Expected')
|
|
474 |
>>> d.parse_blocks_to_tests({})
|
|
475 |
Traceback (most recent call last):
|
|
476 |
...
|
|
477 |
FalderalSyntaxError: line 2: functionality under test not specified
|
|
478 |
|
|
479 |
>>> d = Document()
|
|
480 |
>>> d.append(u'This is a test file.')
|
|
481 |
>>> d.append(u' ? Expected Error')
|
|
482 |
>>> d.parse_blocks_to_tests({})
|
|
483 |
Traceback (most recent call last):
|
|
484 |
...
|
|
485 |
FalderalSyntaxError: line 2: expectation must be preceded by test body or test input
|
|
486 |
|
|
487 |
>>> d = Document()
|
|
488 |
>>> d.append(u' -> Hello, this is pragma')
|
|
489 |
>>> d.append(u' = Expected')
|
|
490 |
>>> d.parse_blocks_to_tests({})
|
|
491 |
Traceback (most recent call last):
|
|
492 |
...
|
|
493 |
FalderalSyntaxError: line 2: expectation must be preceded by test body or test input
|
|
494 |
|
|
495 |
>>> d = Document()
|
|
496 |
>>> d.append(u' | This is test')
|
|
497 |
>>> d.append(u'This is text')
|
|
498 |
>>> d.parse_blocks_to_tests({})
|
|
499 |
Traceback (most recent call last):
|
|
500 |
...
|
|
501 |
FalderalSyntaxError: line 2: test body must be followed by expectation or test input
|
|
502 |
|
|
503 |
>>> d = Document()
|
|
504 |
>>> d.append(u' -> Hello, this is pragma')
|
|
505 |
>>> d.append(u' + Input to where exactly?')
|
|
506 |
>>> d.parse_blocks_to_tests({})
|
|
507 |
Traceback (most recent call last):
|
|
508 |
...
|
|
509 |
FalderalSyntaxError: line 2: test input must be preceded by test body
|
|
510 |
|
|
511 |
>>> d = Document()
|
|
512 |
>>> funs = {}
|
|
513 |
>>> d.append(u' -> Functionality "Parse Stuff" is implemented by '
|
|
514 |
... u'shell command "parse"')
|
|
515 |
>>> d.append(u'')
|
|
516 |
>>> d.append(u' -> Functionality "Parse Stuff" is')
|
|
517 |
>>> d.append(u' -> implemented by shell command "pxxxy"')
|
|
518 |
>>> tests = d.parse_blocks_to_tests(funs)
|
|
519 |
>>> len(funs.keys())
|
|
520 |
1
|
|
521 |
>>> [i for i in funs["Parse Stuff"].implementations]
|
|
522 |
[ShellImplementation(u'parse'), ShellImplementation(u'pxxxy')]
|
496 | 523 |
|
497 | 524 |
"""
|
498 | 525 |
if self.blocks is None:
|
|
798 | 825 |
|
799 | 826 |
TODO: maybe write a helper function for that instead.
|
800 | 827 |
|
801 | |
| >>> b = TestBody(u' | ')
|
802 | |
| >>> b.append(u' | foo')
|
803 | |
| >>> b.append(u' | bar')
|
804 | |
| >>> i = TestInput(u' + ')
|
805 | |
| >>> i.append(u' + green')
|
806 | |
| >>> t = Test(body_block=b, input_block=i)
|
807 | |
| >>> print t.body
|
808 | |
| foo
|
809 | |
| bar
|
810 | |
| >>> print t.input
|
811 | |
| green
|
|
828 |
>>> b = TestBody(u' | ')
|
|
829 |
>>> b.append(u' | foo')
|
|
830 |
>>> b.append(u' | bar')
|
|
831 |
>>> i = TestInput(u' + ')
|
|
832 |
>>> i.append(u' + green')
|
|
833 |
>>> t = Test(body_block=b, input_block=i)
|
|
834 |
>>> print t.body
|
|
835 |
foo
|
|
836 |
bar
|
|
837 |
>>> print t.input
|
|
838 |
green
|
812 | 839 |
|
813 | 840 |
"""
|
814 | 841 |
def __init__(self, body_block=None, input_block=None, expectation=None,
|