tox does not test with unsupported python versions.
[docutils.git] / docutils / test / test_statemachine.py
blobfa62720aa474726379b9eb51043496eb1427eb1f
1 #! /usr/bin/env python3
3 # $Id$
4 # Author: David Goodger <goodger@python.org>
5 # Copyright: This module has been placed in the public domain.
7 """
8 Test module for statemachine.py.
9 """
11 from pathlib import Path
12 import re
13 import sys
14 import unittest
16 if __name__ == '__main__':
17 # prepend the "docutils root" to the Python library path
18 # so we import the local `docutils` package.
19 sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
21 from docutils import statemachine
24 debug = False
25 testtext = statemachine.string2lines("""\
26 First paragraph.
28 - This is a bullet list. First list item.
29 Second line of first para.
31 Second para.
33 block quote
35 - Second list item. Example::
38 literal
39 block
41 Last paragraph.""")
42 expected = ('StateMachine1 text1 blank1 bullet1 known_indent1 '
43 'StateMachine2 text2 text2 blank2 text2 blank2 indent2 '
44 'StateMachine3 text3 blank3 finished3 finished2 '
45 'bullet1 known_indent1 '
46 'StateMachine2 text2 blank2 literalblock2(4) finished2 '
47 'text1 finished1').split()
48 para1 = testtext[:2]
49 item1 = [line[2:] for line in testtext[2:9]]
50 item2 = [line[2:] for line in testtext[9:-1]]
51 lbindent = 6
52 literalblock = [line[lbindent:] for line in testtext[11:-1]]
53 para2 = testtext[-1]
56 class MockState(statemachine.StateWS):
58 patterns = {'bullet': re.compile(r'- '),
59 'text': ''}
60 initial_transitions = ['bullet', ['text']]
61 levelholder = [0]
63 def bof(self, context):
64 self.levelholder[0] += 1
65 self.level = self.levelholder[0]
66 if self.debug:
67 print('StateMachine%s' % self.level, file=sys.stderr)
68 return [], ['StateMachine%s' % self.level]
70 def blank(self, match, context, next_state):
71 result = ['blank%s' % self.level]
72 if self.debug:
73 print('blank%s' % self.level, file=sys.stderr)
74 if context and context[-1] and context[-1][-2:] == '::':
75 result.extend(self.literalblock())
76 return [], None, result
78 def indent(self, match, context, next_state):
79 if self.debug:
80 print('indent%s' % self.level, file=sys.stderr)
81 context, next_state, result = statemachine.StateWS.indent(
82 self, match, context, next_state)
83 return context, next_state, ['indent%s' % self.level] + result
85 def known_indent(self, match, context, next_state):
86 if self.debug:
87 print('known_indent%s' % self.level, file=sys.stderr)
88 context, next_state, result = statemachine.StateWS.known_indent(
89 self, match, context, next_state)
90 return context, next_state, ['known_indent%s' % self.level] + result
92 def bullet(self, match, context, next_state):
93 if self.debug:
94 print('bullet%s' % self.level, file=sys.stderr)
95 context, next_state, result \
96 = self.known_indent(match, context, next_state)
97 return [], next_state, ['bullet%s' % self.level] + result
99 def text(self, match, context, next_state):
100 if self.debug:
101 print('text%s' % self.level, file=sys.stderr)
102 return [match.string], next_state, ['text%s' % self.level]
104 def literalblock(self):
105 indented, indent, offset, good = self.state_machine.get_indented()
106 if self.debug:
107 print('literalblock%s(%s)' % (self.level, indent), file=sys.stderr)
108 return ['literalblock%s(%s)' % (self.level, indent)]
110 def eof(self, context):
111 self.levelholder[0] -= 1
112 if self.debug:
113 print('finished%s' % self.level, file=sys.stderr)
114 return ['finished%s' % self.level]
117 class EmptySMTests(unittest.TestCase):
119 def setUp(self):
120 self.sm = statemachine.StateMachine(
121 state_classes=[], initial_state='State')
122 self.sm.debug = debug
124 def test_add_state(self):
125 self.sm.add_state(statemachine.State)
126 self.assertTrue(len(self.sm.states) == 1)
127 with self.assertRaises(statemachine.DuplicateStateError):
128 self.sm.add_state(statemachine.State)
129 self.sm.add_state(statemachine.StateWS)
130 self.assertTrue(len(self.sm.states) == 2)
132 def test_add_states(self):
133 self.sm.add_states((statemachine.State, statemachine.StateWS))
134 self.assertEqual(len(self.sm.states), 2)
136 def test_get_state(self):
137 self.assertRaises(statemachine.UnknownStateError, self.sm.get_state)
138 self.sm.add_states((statemachine.State, statemachine.StateWS))
139 with self.assertRaises(statemachine.UnknownStateError):
140 self.sm.get_state('unknownState')
141 self.assertTrue(isinstance(self.sm.get_state('State'),
142 statemachine.State))
143 self.assertTrue(isinstance(self.sm.get_state('StateWS'),
144 statemachine.State))
145 self.assertEqual(self.sm.current_state, 'StateWS')
148 class EmptySMWSTests(EmptySMTests):
150 def setUp(self):
151 self.sm = statemachine.StateMachineWS(
152 state_classes=[], initial_state='State')
153 self.sm.debug = debug
156 class SMWSTests(unittest.TestCase):
158 def setUp(self):
159 self.sm = statemachine.StateMachineWS([MockState], 'MockState',
160 debug=debug)
161 self.sm.debug = debug
162 self.sm.states['MockState'].levelholder[0] = 0
164 def tearDown(self):
165 self.sm.unlink()
167 def test___init__(self):
168 self.assertEqual(list(self.sm.states.keys()), ['MockState'])
169 self.assertEqual(len(self.sm.states['MockState'].transitions), 4)
171 def test_get_indented(self):
172 self.sm.input_lines = statemachine.StringList(testtext)
173 self.sm.line_offset = -1
174 self.sm.next_line(3)
175 indented, offset, good = self.sm.get_known_indented(2)
176 self.assertEqual(indented, item1)
177 self.assertEqual(offset, len(para1))
178 self.assertTrue(good)
179 self.sm.next_line()
180 indented, offset, good = self.sm.get_known_indented(2)
181 self.assertEqual(indented, item2)
182 self.assertEqual(offset, len(para1) + len(item1))
183 self.assertTrue(good)
184 self.sm.previous_line(3)
185 if self.sm.debug:
186 print('\ntest_get_indented: self.sm.line:\n', self.sm.line)
187 indented, indent, offset, good = self.sm.get_indented()
188 if self.sm.debug:
189 print('\ntest_get_indented: indented:\n', indented)
190 self.assertEqual(indent, lbindent)
191 self.assertEqual(indented, literalblock)
192 self.assertEqual(offset, (len(para1) + len(item1) + len(item2)
193 - len(literalblock)))
194 self.assertTrue(good)
196 def test_get_text_block(self):
197 self.sm.input_lines = statemachine.StringList(testtext)
198 self.sm.line_offset = -1
199 self.sm.next_line()
200 textblock = self.sm.get_text_block()
201 self.assertEqual(textblock, testtext[:1])
202 self.sm.next_line(2)
203 textblock = self.sm.get_text_block()
204 self.assertEqual(textblock, testtext[2:4])
206 def test_get_text_block_flush_left(self):
207 self.sm.input_lines = statemachine.StringList(testtext)
208 self.sm.line_offset = -1
209 self.sm.next_line()
210 textblock = self.sm.get_text_block(flush_left=1)
211 self.assertEqual(textblock, testtext[:1])
212 self.sm.next_line(2)
213 with self.assertRaises(statemachine.UnexpectedIndentationError):
214 self.sm.get_text_block(flush_left=1)
216 def test_run(self):
217 self.assertEqual(self.sm.run(testtext), expected)
220 class EmptyClass:
221 pass
224 class EmptyStateTests(unittest.TestCase):
226 def setUp(self):
227 self.state = statemachine.State(EmptyClass(), debug=debug)
228 self.state.patterns = {'nop': 'dummy',
229 'nop2': 'dummy',
230 'nop3': 'dummy',
231 'bogus': 'dummy'}
232 self.state.nop2 = self.state.nop3 = self.state.nop
234 def test_add_transitions(self):
235 self.assertEqual(len(self.state.transitions), 0)
236 self.state.add_transitions(['None'], {'None': None})
237 self.assertEqual(len(self.state.transitions), 1)
238 with self.assertRaises(statemachine.UnknownTransitionError):
239 self.state.add_transitions(['bogus'], {})
240 with self.assertRaises(statemachine.DuplicateTransitionError):
241 self.state.add_transitions(['None'], {'None': None})
243 def test_add_transition(self):
244 self.assertEqual(len(self.state.transitions), 0)
245 self.state.add_transition('None', None)
246 self.assertEqual(len(self.state.transitions), 1)
247 with self.assertRaises(statemachine.DuplicateTransitionError):
248 self.state.add_transition('None', None)
250 def test_remove_transition(self):
251 self.assertEqual(len(self.state.transitions), 0)
252 self.state.add_transition('None', None)
253 self.assertEqual(len(self.state.transitions), 1)
254 self.state.remove_transition('None')
255 self.assertEqual(len(self.state.transitions), 0)
256 with self.assertRaises(statemachine.UnknownTransitionError):
257 self.state.remove_transition('None')
259 def test_make_transition(self):
260 dummy = re.compile('dummy')
261 self.assertEqual(self.state.make_transition('nop', 'bogus'),
262 (dummy, self.state.nop, 'bogus'))
263 self.assertEqual(self.state.make_transition('nop'),
264 (dummy, self.state.nop,
265 self.state.__class__.__name__))
266 with self.assertRaises(statemachine.TransitionPatternNotFound):
267 self.state.make_transition('None')
268 with self.assertRaises(statemachine.TransitionMethodNotFound):
269 self.state.make_transition('bogus')
271 def test_make_transitions(self):
272 dummy = re.compile('dummy')
273 self.assertEqual(self.state.make_transitions(('nop', ['nop2'],
274 ('nop3', 'bogus'))),
275 (['nop', 'nop2', 'nop3'],
276 {'nop': (dummy, self.state.nop,
277 self.state.__class__.__name__),
278 'nop2': (dummy, self.state.nop2,
279 self.state.__class__.__name__),
280 'nop3': (dummy, self.state.nop3, 'bogus')}))
283 class MiscTests(unittest.TestCase):
285 s2l_string = "hello\tthere\thow are\tyou?\n\tI'm fine\tthanks.\n"
286 s2l_expected = ['hello there how are you?',
287 " I'm fine thanks."]
289 def test_string2lines(self):
290 self.assertEqual(statemachine.string2lines(self.s2l_string),
291 self.s2l_expected)
294 if __name__ == '__main__':
295 unittest.main()