git @ Cat's Eye Technologies Falderal / 1b2a4fc
Merge pull request #7 from catseye/python-3-support Support Python 3. Chris Pressey authored 1 year, 9 months ago GitHub committed 1 year, 9 months ago
4 changed file(s) with 461 addition(s) and 351 deletion(s). Raw diff Collapse all Expand all
2626 parser.add_option("-d", "--dump",
2727 action="store_true", default=False,
2828 help="print out info about parsed tests, don't run them")
29 parser.add_option("-t", "--test",
30 action="store_true", default=False,
31 help="run internal tests only and exit")
3229 parser.add_option("-v", "--verbose",
3330 action="store_true", default=False,
3431 help="print out info about each test as it is run")
3532
3633 (options, args) = parser.parse_args(args[1:])
37
38 if options.test:
39 import doctest
40 import falderal.objects
41 (failure_count, test_count) = \
42 doctest.testmod(falderal.objects,
43 optionflags=doctest.NORMALIZE_WHITESPACE)
44 if failure_count > 0:
45 return 1
46 else:
47 return 0
4834
4935 # load Documents and create Falderal Tests from them
5036 documents = []
8167 return 1
8268
8369 if options.dump:
84 print "Functionalities:"
70 print("Functionalities:")
8571 for name in functionalities:
86 print " " + name
72 print(" " + name)
8773 for implementation in functionalities[name].implementations:
88 print " +-" + str(implementation)
89 print "Tests:"
74 print(" +-" + str(implementation))
75 print("Tests:")
9076 for test in tests:
91 print " " + str(test)
77 print(" " + str(test))
9278 return 0
9379
9480 # run tests
9884 try:
9985 for test in tests:
10086 if options.verbose:
101 print str(test)
87 print(str(test))
10288 these_results = test.run(options=options)
10389 if options.verbose:
10490 for result in these_results:
115101 if options.verbose:
116102 for key in dup_check:
117103 if len(dup_check[key]) != 1:
118 print "WARNING: test/impl combination %s was run %d times %r" % (
104 print("WARNING: test/impl combination %s was run %d times %r" % (
119105 key, len(dup_check[key]), dup_check[key]
120 )
106 ))
121107
122108 # report on results
123109 for result in results:
125111 num_results = len(results)
126112 num_failures = len([x for x in results if not x.is_successful()])
127113 if not all_ran:
128 print '**************************************************************'
129 print '** TESTING TERMINATED PREMATURELY -- NOT ALL TESTS WERE RUN **'
130 print '**************************************************************'
114 print('**************************************************************')
115 print('** TESTING TERMINATED PREMATURELY -- NOT ALL TESTS WERE RUN **')
116 print('**************************************************************')
131117
132 print '--------------------------------'
133 print 'Total test runs: %d, failures: %d' % (num_results, num_failures)
134 print '--------------------------------'
118 print('--------------------------------')
119 print('Total test runs: %d, failures: %d' % (num_results, num_failures))
120 print('--------------------------------')
135121
136122 if num_failures == 0:
137123 return 0
33 from subprocess import Popen, PIPE
44 from tempfile import mkstemp
55
6 # Note: the __unicode__ method of all the classes defined herein should
6 # Python 2/3
7 try:
8 unicode = unicode
9 except NameError:
10 unicode = str
11
12 # Note: the __str__ method of all the classes defined herein should
713 # produce a short, human-readable summary of the contents of the object,
814 # suitable for displaying in the test results but not necessarily
915 # complete. __repr__ should produce something complete, when it is
4955 def __repr__(self):
5056 return '%s(%r)' % (self.__class__.__name__, self.text)
5157
58 def __eq__(self, other):
59 return self.__class__ == other.__class__ and self.text == other.text
60
5261
5362 class OutputOutcome(Outcome):
54 def __unicode__(self):
63 def __str__(self):
5564 return u'output:\n' + self.text
5665
5766
5867 class ErrorOutcome(Outcome):
59 def __unicode__(self):
68 def __str__(self):
6069 return u'error:\n' + self.text
6170
6271
118127 def short_description(self):
119128 return 'expected %r, got %r' % (self.test.expectation, self.actual)
120129
130 def fmt(self, field, contents):
131 if str == unicode: # Python 3
132 if isinstance(contents, bytes):
133 contents = contents.decode('utf-8')
134 s = field + contents
135 print(s)
136 else: # Python 2
137 s = field + contents
138 print(s)
139
121140 def report(self):
122 print "FAILED : " + self.format_text_block(self.test.description)
123 print "Location: " + self.test.body_block.location()
124 print "Function: " + self.format_text_block(self.test.functionality.name)
125 print "Impl : " + self.format_text_block(self.implementation)
126 print "Body : " + self.format_text_block(self.test.body)
127 #if input is not None:
128 #print "Input : " + self.format_text_block(self.test.input)
129 print "Expected: " + self.format_text_block(self.test.expectation)
130 print "Actual : " + self.format_text_block(self.actual)
131 print
141 self.fmt("FAILED : ", self.format_text_block(self.test.description))
142 self.fmt("Location: ", self.test.body_block.location())
143 self.fmt("Function: ", self.format_text_block(self.test.functionality.name))
144 self.fmt("Impl : ", self.format_text_block(self.implementation))
145 self.fmt("Body : ", self.format_text_block(self.test.body))
146 self.fmt("Expected: ", self.format_text_block(self.test.expectation))
147 self.fmt("Actual : ", self.format_text_block(self.actual))
148 print("")
132149
133150 def is_successful(self):
134151 return False
142159
143160 class Block(object):
144161 """A segment of a Falderal-formatted file.
145
146 >>> b = Block()
147 >>> b.append(u'line 1')
148 >>> b.append(u'line 2')
149 >>> print b.text()
150 line 1
151 line 2
152 >>> print b.text(seperator='')
153 line 1line 2
154 >>> print b.deconstruct()
155 [('', [u'line 1', u'line 2'])]
156
157 >>> b = Block()
158 >>> b.append(u'-> This is a pragma.')
159 >>> b.append(u"| This is some test input.")
160 >>> b.append(u"| It extends over two lines.")
161 >>> b.append(u'? Expected Error')
162 >>> b.append(u'Plain text')
163 >>> b.append(u'More plain text')
164 >>> b.append(u'| Test with input')
165 >>> b.append(u'+ input-for-test')
166 >>> b.append(u'= Expected result on output')
167 >>> b.append(u'= which extends over two lines')
168 >>> print [pair[0] for pair in b.deconstruct()]
169 [u'->', u'| ', u'? ', '', u'| ', u'+ ', u'= ']
170
171 >>> b = Block()
172 >>> b.append(u'-> This is a pragma.')
173 >>> b.append(u'-> which extends over two lines')
174 >>> print b.classify(ParseState())
175 Pragma(line_num=1)
176
177 >>> f = Functionality('foo')
178 >>> b = Block()
179 >>> b.append(u'| Test body here.')
180 >>> b.append(u'= Expected result here.')
181 >>> print b.classify(ParseState(current_functionality=f))
182 Test(body_block=Block(line_num=1), input_block=None,
183 expectation=OutputOutcome(u'Expected result here.'),
184 functionality=Functionality('foo'), desc_block=None,
185 body=u'Test body here.', input=None)
186
187 >>> b = Block()
188 >>> b.append(u'| Test body here.')
189 >>> b.append(u'? Expected error here.')
190 >>> print b.classify(ParseState(current_functionality=f))
191 Test(body_block=Block(line_num=1), input_block=None,
192 expectation=ErrorOutcome(u'Expected error here.'),
193 functionality=Functionality('foo'), desc_block=None,
194 body=u'Test body here.', input=None)
195162
196163 """
197164
206173 u'??> ': u'? ',
207174 u'???> ': u'? ',
208175 }
209 FREESTYLE_PREFIXES = FREESTYLE_MAP.keys()
176 FREESTYLE_PREFIXES = list(FREESTYLE_MAP.keys())
210177 PREFIXES = FREESTYLE_PREFIXES + [
211178 u'| ',
212179 u'+ ',
238205 self.__class__.__name__, self.line_num, filename_repr
239206 )
240207
241 def __unicode__(self):
208 def __str__(self):
242209 return unicode(repr(self))
243210
244211 def location(self):
440407 self.lines.append(line)
441408
442409 def parse_lines_to_blocks(self):
443 r"""Parse the lines of the Document into Blocks.
444
445 >>> d = Document()
446 >>> d.append(u'This is a test file.')
447 >>> d.append(u' -> This is a pragma.')
448 >>> d.append(u'')
449 >>> d.append(u" | This is some test input.\n")
450 >>> d.append(u" | It extends over two lines.")
451 >>> d.append(u' ? Expected Error')
452 >>> d.append(u'')
453 >>> d.append(u' | Test with input')
454 >>> d.append(u' + input-for-test')
455 >>> d.append(u' = Expected result on output')
456 >>> blocks = d.parse_lines_to_blocks()
457 >>> [block.lines for block in blocks if isinstance(block, InterveningText)]
458 [[u'This is a test file.'], [u''], [u'']]
459 >>> [b.__class__.__name__ for b in blocks]
460 ['InterveningText', 'Block', 'InterveningText', 'Block', 'InterveningText', 'Block']
461 >>> [b.line_num for b in blocks]
462 [1, 2, 3, 4, 7, 8]
410 """Parse the lines of the Document into Blocks.
463411
464412 """
465413 indent = None
531479 return tests
532480
533481 def extract_tests(self, functionalities):
534 r"""Extract all Tests from this Document.
535
536 >>> functionalities = {}
537 >>> d = Document()
538 >>> d.append(u"This is a text file.")
539 >>> d.append(u'It contains NO tests.')
540 >>> d.extract_tests(functionalities)
541 []
542
543 >>> d = Document()
544 >>> d.append(u'This is a test file.')
545 >>> d.append(u' -> Tests for functionality "Parse Thing"')
546 >>> d.append(u'')
547 >>> d.append(u" | This is some test body.")
548 >>> d.append(u' = Expected result')
549 >>> d.extract_tests(functionalities)
550 [Test(body_block=Block(line_num=4), input_block=None,
551 expectation=OutputOutcome(u'Expected result'),
552 functionality=Functionality(u'Parse Thing'),
553 desc_block=InterveningText(line_num=1),
554 body=u'This is some test body.', input=None)]
555
556 >>> d = Document()
557 >>> d.append(u'This is a test file.')
558 >>> d.append(u' -> Tests for functionality "Parse Thing"')
559 >>> d.append(u'')
560 >>> d.append(u" | This is some test body.")
561 >>> d.append(u" | It extends over two lines.")
562 >>> d.append(u' ? Expected Error')
563 >>> d.append(u'')
564 >>> d.append(u' | Test with input')
565 >>> d.append(u' + input-for-test')
566 >>> d.append(u' = Expected result on output')
567 >>> d.append(u'')
568 >>> d.append(u' + Other input-for-test')
569 >>> d.append(u' = Other Expected result on output')
570 >>> d.append(u'')
571 >>> d.append(u' -> Tests for functionality "Run Thing"')
572 >>> d.append(u'')
573 >>> d.append(u" | Thing")
574 >>> d.append(u' ? Oops')
575 >>> tests = d.extract_tests(functionalities)
576 >>> [t.body for t in tests]
577 [u'This is some test body.\nIt extends over two lines.',
578 u'Test with input', u'Test with input', u'Thing']
579 >>> [t.input_block for t in tests]
580 [None, Block(line_num=8), Block(line_num=12), None]
581 >>> tests[1].input_block.text()
582 u'input-for-test'
583 >>> tests[2].input_block.text()
584 u'Other input-for-test'
585 >>> [t.expectation for t in tests]
586 [ErrorOutcome(u'Expected Error'),
587 OutputOutcome(u'Expected result on output'),
588 OutputOutcome(u'Other Expected result on output'),
589 ErrorOutcome(u'Oops')]
590 >>> [t.functionality.name for t in tests]
591 [u'Parse Thing', u'Parse Thing', u'Parse Thing', u'Run Thing']
592 >>> sorted(functionalities.keys())
593 [u'Parse Thing', u'Run Thing']
594
595 >>> d = Document()
596 >>> d.append(u" | This is some test body.")
597 >>> d.append(u' = Expected')
598 >>> d.extract_tests({})
599 Traceback (most recent call last):
600 ...
601 FalderalSyntaxError: line 1: functionality under test not specified
602
603 >>> d = Document()
604 >>> d.append(u'This is a test file.')
605 >>> d.append(u' ? Expected Error')
606 >>> d.extract_tests({})
607 Traceback (most recent call last):
608 ...
609 FalderalSyntaxError: line 2: expectation must be preceded by test body or test input
610
611 >>> d = Document()
612 >>> d.append(u' -> Hello, this is pragma')
613 >>> d.append(u' = Expected')
614 >>> d.extract_tests({})
615 Traceback (most recent call last):
616 ...
617 FalderalSyntaxError: line 1: incorrectly formatted test block
618
619 >>> d = Document()
620 >>> d.append(u' | This is test')
621 >>> d.append(u'This is text')
622 >>> d.extract_tests({})
623 Traceback (most recent call last):
624 ...
625 FalderalSyntaxError: line 1: test body must be followed by expectation or test input
626
627 >>> d = Document()
628 >>> d.append(u' -> Hello, this is pragma')
629 >>> d.append(u' + Input to where exactly?')
630 >>> d.extract_tests({})
631 Traceback (most recent call last):
632 ...
633 FalderalSyntaxError: line 1: incorrectly formatted test block
634
635 >>> d = Document()
636 >>> funs = {}
637 >>> d.append(u' -> Functionality "Parse Stuff" is implemented by '
638 ... u'shell command "parse"')
639 >>> d.append(u'')
640 >>> d.append(u' -> Functionality "Parse Stuff" is')
641 >>> d.append(u' -> implemented by shell command "pxxxy"')
642 >>> tests = d.extract_tests(funs)
643 >>> len(funs.keys())
644 1
645 >>> [i for i in funs["Parse Stuff"].implementations]
646 [ShellImplementation(u'parse'), ShellImplementation(u'pxxxy')]
482 """Extract all Tests from this Document.
647483
648484 """
649485 blocks = self.parse_lines_to_blocks()
668504 def __repr__(self):
669505 return "Functionality(%r)" % self.name
670506
671 def __unicode__(self):
507 def __str__(self):
672508 return unicode(repr(self))
673509
674510 def add_implementation(self, implementation):
703539 def __repr__(self):
704540 return '%s(%r)' % (self.__class__.__name__, self.callable)
705541
706 def __unicode__(self):
542 def __str__(self):
707543 return u'callable "%r"' % self.callable
708544
709545 def run(self, body=None, input=None):
721557 def __repr__(self):
722558 return '%s(%r)' % (self.__class__.__name__, self.command)
723559
724 def __unicode__(self):
560 def __str__(self):
725561 return u'shell command "%s"' % self.command
726562
563 def __eq__(self, other):
564 return self.__class__ == other.__class__ and self.command == other.command
565
727566 def run(self, body=None, input=None):
728 r"""
729 >>> i = ShellImplementation('cat')
730 >>> i.run(body=u'text')
731 OutputOutcome(u'text')
732
733 >>> i = ShellImplementation('cat fhofhofhf')
734 >>> i.run(body=u'text')
735 ErrorOutcome(u'cat: fhofhofhf: No such file or directory')
736
737 >>> i = ShellImplementation('cat %(test-body-file)')
738 >>> i.run(body=u'text')
739 OutputOutcome(u'text')
740
741 >>> i = ShellImplementation("echo '%(test-body-text)'")
742 >>> i.run(body=u'text')
743 OutputOutcome(u'text')
744
745 >>> i = ShellImplementation('cat >%(output-file)')
746 >>> i.run(body=u'text')
747 OutputOutcome(u'text')
748
749 >>> i = ShellImplementation("echo '%(test-body-text)' '%(test-input-text)'")
750 >>> i.run(body=u'text', input=u'zzrk')
751 OutputOutcome(u'text zzrk')
752
753 Here the body is sent to cat's stdin, but cat ignores it.
754
755 >>> i = ShellImplementation('cat >%(output-file) <%(test-input-file)')
756 >>> i.run(body=u'text', input=u'zzrk')
757 OutputOutcome(u'zzrk')
758
759 """
760567 # expand variables in the command
761568 test_filename = None
762569 output_filename = None
848655 return result
849656
850657 def normalize_output(self, text):
851 text = text.decode('UTF-8', errors='ignore')
658 try:
659 text = text.decode('UTF-8', errors='ignore')
660 except AttributeError:
661 pass
852662 text = re.sub(r'\r\n', '\n', text)
853663 return text.strip('\r\n')
854664
865675 a body and/or input may be passed alone.
866676
867677 TODO: maybe write a helper function for that instead.
868
869 >>> b = Block()
870 >>> b.append(u'foo')
871 >>> b.append(u'bar')
872 >>> i = Block()
873 >>> i.append(u'green')
874 >>> t = Test(body_block=b, input_block=i)
875 >>> print t.body
876 foo
877 bar
878 >>> print t.input
879 green
880678
881679 """
882680 def __init__(self, body_block=None, input_block=None, expectation=None,
910708 """Returns a list of Results, one for each implementation of
911709 the functionality being tested.
912710
913 >>> f = Functionality('Cat File')
914 >>> f.add_implementation(CallableImplementation(lambda x, y: x))
915 >>> t = Test(body=u'foo', expectation=OutputOutcome(u'foo'),
916 ... functionality=f)
917 >>> [r.short_description() for r in t.run()]
918 ['success']
919
920 >>> f = Functionality('Cat File')
921 >>> f.add_implementation(CallableImplementation(lambda x, y: x))
922 >>> t = Test(body=u'foo', expectation=OutputOutcome(u'bar'),
923 ... functionality=f)
924 >>> [r.short_description() for r in t.run()]
925 ["expected OutputOutcome(u'bar'), got OutputOutcome(u'foo')"]
926
927 >>> f = Functionality('Cat File')
928 >>> f.add_implementation(CallableImplementation(lambda x, y: x))
929 >>> t = Test(body=u'foo', expectation=ErrorOutcome(u'foo'),
930 ... functionality=f)
931 >>> [r.short_description() for r in t.run()]
932 ["expected ErrorOutcome(u'foo'), got OutputOutcome(u'foo')"]
933
934 >>> f = Functionality('Cat File')
935 >>> def e(x, y):
936 ... raise ValueError(x)
937 >>> f.add_implementation(CallableImplementation(e))
938 >>> t = Test(body=u'foo', expectation=ErrorOutcome(u'foo'),
939 ... functionality=f)
940 >>> [r.short_description() for r in t.run()]
941 ['success']
942
943 >>> f = Functionality('Cat File')
944 >>> def e(x, y):
945 ... raise ValueError(x)
946 >>> f.add_implementation(CallableImplementation(e))
947 >>> t = Test(body=u'foo', expectation=ErrorOutcome(u'bar'),
948 ... functionality=f)
949 >>> [r.short_description() for r in t.run()]
950 ["expected ErrorOutcome(u'bar'), got ErrorOutcome(u'foo')"]
951
952 >>> f = Functionality('Cat File')
953 >>> def e(x, y):
954 ... raise ValueError(x)
955 >>> f.add_implementation(CallableImplementation(e))
956 >>> t = Test(body=u'foo', expectation=OutputOutcome(u'foo'),
957 ... functionality=f)
958 >>> [r.short_description() for r in t.run()]
959 ["expected OutputOutcome(u'foo'), got ErrorOutcome(u'foo')"]
960
961 >>> f = Functionality('Cat File with Input')
962 >>> f.add_implementation(CallableImplementation(lambda x, y: x + y))
963 >>> t = Test(body=u'foo', input=u'bar', expectation=OutputOutcome(u'foobar'),
964 ... functionality=f)
965 >>> [r.short_description() for r in t.run()]
966 ['success']
967
968 A functionality can have multiple implementations. We test them all.
969
970 >>> f = Functionality('Cat File')
971 >>> def c1(body, input):
972 ... return body
973 >>> def c2(body, input):
974 ... return body + '...'
975 >>> def c3(body, input):
976 ... raise ValueError(body)
977 >>> for c in (c1, c2, c3):
978 ... f.add_implementation(CallableImplementation(c))
979 >>> t = Test(body=u'foo', expectation=OutputOutcome(u'foo'),
980 ... functionality=f)
981 >>> [r.short_description() for r in t.run()]
982 ['success', "expected OutputOutcome(u'foo'), got OutputOutcome(u'foo...')",
983 "expected OutputOutcome(u'foo'), got ErrorOutcome(u'foo')"]
984
985711 """
986712 results = []
987713 for implementation in self.functionality.implementations:
0 # Note: these are unit tests for py-falderal itself,
1 # not tests that Falderal can understand.
2
3 import unittest
4 from unittest import TestCase
5
6 from falderal.objects import (
7 Block, Pragma,
8 ParseState, InterveningText,
9 Document,
10 Functionality, ShellImplementation,
11 Test, OutputOutcome, ErrorOutcome,
12 FalderalSyntaxError,
13 )
14
15
16 class BlockTestCase(TestCase):
17 def test_block(self):
18 b = Block()
19 b.append(u'line 1')
20 b.append(u'line 2')
21 self.assertEqual(b.text(), "line 1\nline 2")
22 self.assertEqual(b.text(seperator=''), "line 1line 2")
23 self.assertEqual(
24 b.deconstruct(),
25 [('', [u'line 1', u'line 2'])]
26 )
27
28 def test_deconstruct_block(self):
29 b = Block()
30 b.append(u'-> This is a pragma.')
31 b.append(u"| This is some test input.")
32 b.append(u"| It extends over two lines.")
33 b.append(u'? Expected Error')
34 b.append(u'Plain text')
35 b.append(u'More plain text')
36 b.append(u'| Test with input')
37 b.append(u'+ input-for-test')
38 b.append(u'= Expected result on output')
39 b.append(u'= which extends over two lines')
40 self.assertEqual(
41 [pair[0] for pair in b.deconstruct()],
42 [u'->', u'| ', u'? ', '', u'| ', u'+ ', u'= ']
43 )
44
45 def test_classify_block_pragma(self):
46 b = Block()
47 b.append(u'-> This is a pragma.')
48 b.append(u'-> which extends over two lines')
49 result = b.classify(ParseState())
50 self.assertIsInstance(result, Pragma)
51 self.assertEqual(result.lines, [u' This is a pragma.', u' which extends over two lines'])
52 self.assertEqual(result.line_num, 1)
53
54 def test_classify_block_success_test(self):
55 f = Functionality('foo')
56 b = Block()
57 b.append(u'| Test body here.')
58 b.append(u'= Expected result here.')
59 result = b.classify(ParseState(current_functionality=f))
60 self.assertIsInstance(result, Test)
61 self.assertEqual(result.body_block.lines, [u'Test body here.'])
62 self.assertEqual(result.body_block.line_num, 1)
63 self.assertEqual(result.body_block.filename, None)
64 self.assertEqual(result.input_block, None)
65 self.assertEqual(result.expectation, OutputOutcome(u'Expected result here.'))
66 self.assertEqual(result.functionality, f)
67 self.assertEqual(result.desc_block, None)
68 self.assertEqual(result.body, u'Test body here.')
69 self.assertEqual(result.input, None)
70
71 def test_classify_block_error_test(self):
72 f = Functionality('foo')
73 b = Block()
74 b.append(u'| Test body here.')
75 b.append(u'? Expected error here.')
76 result = b.classify(ParseState(current_functionality=f))
77 self.assertIsInstance(result, Test)
78 self.assertEqual(result.body_block.lines, [u'Test body here.'])
79 self.assertEqual(result.body_block.line_num, 1)
80 self.assertEqual(result.body_block.filename, None)
81 self.assertEqual(result.input_block, None)
82 self.assertEqual(result.expectation, ErrorOutcome(u'Expected error here.'))
83 self.assertEqual(result.functionality, f)
84 self.assertEqual(result.desc_block, None)
85 self.assertEqual(result.body, u'Test body here.')
86 self.assertEqual(result.input, None)
87
88
89 class DocumentTestCase(TestCase):
90 def test_document(self):
91 d = Document()
92 d.append(u'This is a test file.')
93 d.append(u' -> This is a pragma.')
94 d.append(u'')
95 d.append(u" | This is some test input.\n")
96 d.append(u" | It extends over two lines.")
97 d.append(u' ? Expected Error')
98 d.append(u'')
99 d.append(u' | Test with input')
100 d.append(u' + input-for-test')
101 d.append(u' = Expected result on output')
102 blocks = d.parse_lines_to_blocks()
103 self.assertEqual(
104 [block.lines for block in blocks if isinstance(block, InterveningText)],
105 [[u'This is a test file.'], [u''], [u'']]
106 )
107 self.assertEqual(
108 [b.__class__.__name__ for b in blocks],
109 ['InterveningText', 'Block', 'InterveningText', 'Block', 'InterveningText', 'Block']
110 )
111 self.assertEqual(
112 [b.line_num for b in blocks],
113 [1, 2, 3, 4, 7, 8]
114 )
115
116 def test_extract_tests_empty(self):
117 d = Document()
118 d.append(u"This is a text file.")
119 d.append(u'It contains NO tests.')
120 functionalities = {}
121 self.assertEqual(d.extract_tests(functionalities), [])
122
123 def test_extract_tests_basic(self):
124 d = Document()
125 d.append(u'This is a test file.')
126 d.append(u' -> Tests for functionality "Parse Thing"')
127 d.append(u'')
128 d.append(u" | This is some test body.")
129 d.append(u' = Expected result')
130 functionalities = {}
131 tests = d.extract_tests(functionalities)
132 self.assertEqual(len(tests), 1)
133 result = tests[0]
134 self.assertIsInstance(result, Test)
135 self.assertEqual(result.body_block.lines, [u'This is some test body.'])
136 self.assertEqual(result.body_block.line_num, 4)
137 self.assertEqual(result.body_block.filename, None)
138 self.assertEqual(result.input_block, None)
139 self.assertEqual(result.expectation, OutputOutcome(u'Expected result'))
140 self.assertEqual(result.functionality, functionalities['Parse Thing'])
141 self.assertEqual(result.desc_block.__class__, InterveningText)
142 self.assertEqual(result.desc_block.lines, [u'This is a test file.'])
143 self.assertEqual(result.body, u'This is some test body.')
144 self.assertEqual(result.input, None)
145
146 def test_extract_tests_more(self):
147 d = Document()
148 d.append(u'This is a test file.')
149 d.append(u' -> Tests for functionality "Parse Thing"')
150 d.append(u'')
151 d.append(u" | This is some test body.")
152 d.append(u" | It extends over two lines.")
153 d.append(u' ? Expected Error')
154 d.append(u'')
155 d.append(u' | Test with input')
156 d.append(u' + input-for-test')
157 d.append(u' = Expected result on output')
158 d.append(u'')
159 d.append(u' + Other input-for-test')
160 d.append(u' = Other Expected result on output')
161 d.append(u'')
162 d.append(u' -> Tests for functionality "Run Thing"')
163 d.append(u'')
164 d.append(u" | Thing")
165 d.append(u' ? Oops')
166 functionalities = {}
167 tests = d.extract_tests(functionalities)
168 self.assertEqual(
169 [t.body for t in tests],
170 [u'This is some test body.\nIt extends over two lines.',
171 u'Test with input', u'Test with input', u'Thing']
172 )
173 self.assertEqual(
174 [t.input_block.__class__ for t in tests],
175 [None.__class__, Block, Block, None.__class__]
176 )
177 self.assertEqual(
178 [t.input_block.text() for t in tests if t.input_block is not None],
179 [u'input-for-test', u'Other input-for-test']
180 )
181 self.assertEqual(
182 [t.expectation for t in tests],
183 [ErrorOutcome(u'Expected Error'),
184 OutputOutcome(u'Expected result on output'),
185 OutputOutcome(u'Other Expected result on output'),
186 ErrorOutcome(u'Oops')]
187 )
188 self.assertEqual(
189 [t.functionality.name for t in tests],
190 [u'Parse Thing', u'Parse Thing', u'Parse Thing', u'Run Thing']
191 )
192 self.assertEqual(
193 sorted(functionalities.keys()),
194 [u'Parse Thing', u'Run Thing']
195 )
196
197 def test_no_functionality_under_test(self):
198 d = Document()
199 d.append(u" | This is some test body.")
200 d.append(u' = Expected')
201 with self.assertRaises(FalderalSyntaxError) as ar:
202 d.extract_tests({})
203 self.assertEqual(str(ar.exception), "line 1: functionality under test not specified")
204
205 def test_expectation_in_bad_place(self):
206 d = Document()
207 d.append(u'This is a test file.')
208 d.append(u' ? Expected Error')
209 with self.assertRaises(FalderalSyntaxError) as ar:
210 d.extract_tests({})
211 self.assertEqual(str(ar.exception), "line 2: expectation must be preceded by test body or test input")
212
213 def test_badly_formatted_test_block(self):
214 d = Document()
215 d.append(u' -> Hello, this is pragma')
216 d.append(u' = Expected')
217 with self.assertRaises(FalderalSyntaxError) as ar:
218 d.extract_tests({})
219 self.assertEqual(str(ar.exception), "line 1: incorrectly formatted test block")
220
221 def test_body_not_followed_by_anything_sensible(self):
222 d = Document()
223 d.append(u' | This is test')
224 d.append(u'This is text')
225 with self.assertRaises(FalderalSyntaxError) as ar:
226 d.extract_tests({})
227 self.assertEqual(str(ar.exception), "line 1: test body must be followed by expectation or test input")
228
229 def test_another_badly_formatted_block(self):
230 d = Document()
231 d.append(u' -> Hello, this is pragma')
232 d.append(u' + Input to where exactly?')
233 with self.assertRaises(FalderalSyntaxError) as ar:
234 d.extract_tests({})
235 self.assertEqual(str(ar.exception), "line 1: incorrectly formatted test block")
236
237 def test_parse_functionalities(self):
238 d = Document()
239 funs = {}
240 d.append(u' -> Functionality "Parse Stuff" is implemented by '
241 u'shell command "parse"')
242 d.append(u'')
243 d.append(u' -> Functionality "Parse Stuff" is')
244 d.append(u' -> implemented by shell command "pxxxy"')
245 tests = d.extract_tests(funs)
246 self.assertEqual(list(funs.keys()), ['Parse Stuff'])
247 self.assertEqual(
248 [i for i in funs["Parse Stuff"].implementations],
249 [ShellImplementation(u'parse'), ShellImplementation(u'pxxxy')]
250 )
251
252
253 class ShellImplementationTestCase(TestCase):
254 def test_cat(self):
255 i = ShellImplementation('cat')
256 self.assertEqual(i.run(body=u'text'), OutputOutcome(u'text'))
257
258 def test_cat_file(self):
259 i = ShellImplementation('cat fhofhofhf')
260 self.assertEqual(i.run(body=u'text'), ErrorOutcome(u'cat: fhofhofhf: No such file or directory'))
261
262 def test_cat_test_body_file(self):
263 i = ShellImplementation('cat %(test-body-file)')
264 self.assertEqual(i.run(body=u'text'), OutputOutcome(u'text'))
265
266 def test_cat_test_body_text(self):
267 i = ShellImplementation("echo '%(test-body-text)'")
268 self.assertEqual(i.run(body=u'text'), OutputOutcome(u'text'))
269
270 def test_cat_output_file(self):
271 i = ShellImplementation('cat >%(output-file)')
272 self.assertEqual(i.run(body=u'text'), OutputOutcome(u'text'))
273
274 def test_echo(self):
275 i = ShellImplementation("echo '%(test-body-text)' '%(test-input-text)'")
276 self.assertEqual(i.run(body=u'text', input=u'zzrk'), OutputOutcome(u'text zzrk'))
277
278 def test_cat_stdin(self):
279 # Here the body is sent to cat's stdin, but cat ignores it.
280 i = ShellImplementation('cat >%(output-file) <%(test-input-file)')
281 self.assertEqual(i.run(body=u'text', input=u'zzrk'), OutputOutcome(u'zzrk'))
282
283
284 def TestTestCase(TestCase):
285 def test_test_contents(self):
286 b = Block()
287 b.append(u'foo')
288 b.append(u'bar')
289 i = Block()
290 i.append(u'green')
291 t = Test(body_block=b, input_block=i)
292 self.assertEqual(t.body, "foo\nbar")
293 self.assertEqual(t.input, "green")
294
295 def test_tests_1(self):
296 f = Functionality('Cat File')
297 f.add_implementation(CallableImplementation(lambda x, y: x))
298 t = Test(body=u'foo', expectation=OutputOutcome(u'foo'), functionality=f)
299 self.assertEqual(
300 [r.short_description() for r in t.run()]
301 ['success']
302 )
303
304 def test_tests_2(self):
305 f = Functionality('Cat File')
306 f.add_implementation(CallableImplementation(lambda x, y: x))
307 t = Test(body=u'foo', expectation=OutputOutcome(u'bar'),
308 functionality=f)
309 self.assertEqual(
310 [r.short_description() for r in t.run()],
311 ["expected OutputOutcome(u'bar'), got OutputOutcome(u'foo')"]
312 )
313
314 def test_tests_3(self):
315 f = Functionality('Cat File')
316 f.add_implementation(CallableImplementation(lambda x, y: x))
317 t = Test(body=u'foo', expectation=ErrorOutcome(u'foo'),
318 functionality=f)
319 self.assertEqual(
320 [r.short_description() for r in t.run()],
321 ["expected ErrorOutcome(u'foo'), got OutputOutcome(u'foo')"]
322 )
323
324 def test_tests_4(self):
325 f = Functionality('Cat File')
326 def e(x, y):
327 raise ValueError(x)
328 f.add_implementation(CallableImplementation(e))
329 t = Test(body=u'foo', expectation=ErrorOutcome(u'foo'),
330 functionality=f)
331 self.assertEqual(
332 [r.short_description() for r in t.run()],
333 ['success']
334 )
335
336 def test_tests_5(self):
337 f = Functionality('Cat File')
338 def e(x, y):
339 raise ValueError(x)
340 f.add_implementation(CallableImplementation(e))
341 t = Test(body=u'foo', expectation=ErrorOutcome(u'bar'),
342 functionality=f)
343 self.assertEqual(
344 [r.short_description() for r in t.run()],
345 ["expected ErrorOutcome(u'bar'), got ErrorOutcome(u'foo')"]
346 )
347
348 def test_tests_6(self):
349 f = Functionality('Cat File')
350 def e(x, y):
351 raise ValueError(x)
352 f.add_implementation(CallableImplementation(e))
353 t = Test(body=u'foo', expectation=OutputOutcome(u'foo'),
354 functionality=f)
355 self.assertEqual(
356 [r.short_description() for r in t.run()],
357 ["expected OutputOutcome(u'foo'), got ErrorOutcome(u'foo')"]
358 )
359
360 def test_tests_7(self):
361 f = Functionality('Cat File with Input')
362 f.add_implementation(CallableImplementation(lambda x, y: x + y))
363 t = Test(body=u'foo', input=u'bar', expectation=OutputOutcome(u'foobar'),
364 functionality=f)
365 self.assertEqual(
366 [r.short_description() for r in t.run()],
367 ['success']
368 )
369
370 def test_functionality_with_multiple_implementations(self):
371 # A functionality can have multiple implementations. We test them all.
372
373 f = Functionality('Cat File')
374 def c1(body, input):
375 return body
376 def c2(body, input):
377 return body + '...'
378 def c3(body, input):
379 raise ValueError(body)
380 for c in (c1, c2, c3):
381 f.add_implementation(CallableImplementation(c))
382 t = Test(body=u'foo', expectation=OutputOutcome(u'foo'),
383 functionality=f)
384 self.assertEqual(
385 [r.short_description() for r in t.run()],
386 ['success', "expected OutputOutcome(u'foo'), got OutputOutcome(u'foo...')",
387 "expected OutputOutcome(u'foo'), got ErrorOutcome(u'foo')"]
388 )
389
390
391 if __name__ == '__main__':
392 unittest.main()
11
22 # Really crude test harness for py-falderal itself...
33
4 bin/falderal -v -t || exit 1
4 if [ "x$PYTHON" = "x" ]; then
5 PYTHON="python3"
6 fi
7 FALDERAL="$PYTHON ../bin/falderal"
8
9 PYTHONPATH=src $PYTHON src/falderal/tests.py -v || exit 1
510
611 cd tests
712
1621 "
1722 for TEST in ${FIRST_TESTS}; do
1823 echo ${TEST}...
19 ../bin/falderal --cavalier ${TEST}.markdown > ${TEST}.actual 2>&1
24 $FALDERAL --cavalier ${TEST}.markdown > ${TEST}.actual 2>&1
2025 diff -u ${TEST}.expected ${TEST}.actual || exit 1
2126 done
2227
2429 LINTING_TESTS="test-no-tests"
2530 for TEST in ${LINTING_TESTS}; do
2631 echo ${TEST}...
27 ../bin/falderal ${TEST}.markdown > ${TEST}.actual 2>&1
32 $FALDERAL ${TEST}.markdown > ${TEST}.actual 2>&1
2833 diff -u ${TEST}.expected ${TEST}.actual || exit 1
2934 done
3035
3338 "
3439 for TEST in ${TWO_PART_TESTS}; do
3540 echo ${TEST}...
36 ../bin/falderal ${TEST}-a.markdown ${TEST}-b.markdown > ${TEST}.actual 2>&1
41 $FALDERAL ${TEST}-a.markdown ${TEST}-b.markdown > ${TEST}.actual 2>&1
3742 diff -u ${TEST}.expected ${TEST}.actual || exit 1
3843 done
3944
4045 # special tests: -b
4146 TEST=test-substring-error
4247 echo ${TEST}...
43 ../bin/falderal -b ${TEST}.markdown > ${TEST}.actual 2>&1
48 $FALDERAL -b ${TEST}.markdown > ${TEST}.actual 2>&1
4449 diff -u ${TEST}.expected ${TEST}.actual || exit 1
4550
4651 rm -f *.actual