root/trunk/airspeed_test.py

Revision 56, 27.6 kB (checked in by steve, 2 years ago)

Fixed #15, a problem with the regex for range literals

  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1 #!/usr/bin/env python
2 # -*- coding: latin1 -*-
3
4 from unittest import TestCase, main
5 import airspeed
6
7 ###############################################################################
8 # Compatibility for old Pythons & Jython
9 ###############################################################################
10 try: True
11 except NameError:
12     False, True = 0, 1
13
14
15 class TemplateTestCase(TestCase):
16
17     def test_parser_returns_input_when_there_is_nothing_to_substitute(self):
18         template = airspeed.Template("<html></html>")
19         self.assertEquals("<html></html>", template.merge({}))
20
21     def test_parser_substitutes_string_added_to_the_context(self):
22         template = airspeed.Template("Hello $name")
23         self.assertEquals("Hello Chris", template.merge({"name": "Chris"}))
24
25     def test_dollar_left_untouched(self):
26         template = airspeed.Template("Hello $ ")
27         self.assertEquals("Hello $ ", template.merge({}))
28         template = airspeed.Template("Hello $")
29         self.assertEquals("Hello $", template.merge({}))
30
31     def test_unmatched_name_does_not_get_substituted(self):
32         template = airspeed.Template("Hello $name")
33         self.assertEquals("Hello $name", template.merge({}))
34
35     def test_silent_substitution_for_unmatched_values(self):
36         template = airspeed.Template("Hello $!name")
37         self.assertEquals("Hello world", template.merge({"name": "world"}))
38         self.assertEquals("Hello ", template.merge({}))
39
40     def test_embed_substitution_value_in_braces_gets_handled(self):
41         template = airspeed.Template("Hello ${name}.")
42         self.assertEquals("Hello World.", template.merge({"name": "World"}))
43
44     def test_unmatched_braces_raises_exception(self):
45         template = airspeed.Template("Hello ${name.")
46         self.assertRaises(airspeed.TemplateSyntaxError, template.merge, {})
47
48     def test_unmatched_trailing_brace_preserved(self):
49         template = airspeed.Template("Hello $name}.")
50         self.assertEquals("Hello World}.", template.merge({"name": "World"}))
51
52     def test_can_return_value_from_an_attribute_of_a_context_object(self):
53         template = airspeed.Template("Hello $name.first_name")
54         class MyObj: pass
55         o = MyObj()
56         o.first_name = 'Chris'
57         self.assertEquals("Hello Chris", template.merge({"name": o}))
58
59     def test_can_return_value_from_an_attribute_of_a_context_object(self):
60         template = airspeed.Template("Hello $name.first_name")
61         class MyObj: pass
62         o = MyObj()
63         o.first_name = 'Chris'
64         self.assertEquals("Hello Chris", template.merge({"name": o}))
65
66     def test_can_return_value_from_a_method_of_a_context_object(self):
67         template = airspeed.Template("Hello $name.first_name()")
68         class MyObj:
69             def first_name(self): return "Chris"
70         self.assertEquals("Hello Chris", template.merge({"name": MyObj()}))
71
72     def test_when_if_statement_resolves_to_true_the_content_is_returned(self):
73         template = airspeed.Template("Hello #if ($name)your name is ${name}#end Good to see you")
74         self.assertEquals("Hello your name is Steve Good to see you", template.merge({"name": "Steve"}))
75
76     def test_when_if_statement_resolves_to_false_the_content_is_skipped(self):
77         template = airspeed.Template("Hello #if ($show_greeting)your name is ${name}#end Good to see you")
78         self.assertEquals("Hello  Good to see you", template.merge({"name": "Steve", "show_greeting": False}))
79
80     def test_when_if_statement_is_nested_inside_a_successful_enclosing_if_it_gets_evaluated(self):
81         template = airspeed.Template("Hello #if ($show_greeting)your name is ${name}.#if ($is_birthday) Happy Birthday.#end#end Good to see you")
82         namespace = {"name": "Steve", "show_greeting": False}
83         self.assertEquals("Hello  Good to see you", template.merge(namespace))
84         namespace["show_greeting"] = True
85         self.assertEquals("Hello your name is Steve. Good to see you", template.merge(namespace))
86         namespace["is_birthday"] = True
87         self.assertEquals("Hello your name is Steve. Happy Birthday. Good to see you", template.merge(namespace))
88
89     def test_if_statement_considers_None_to_be_false(self):
90         template = airspeed.Template("#if ($some_value)hide me#end")
91         self.assertEquals('', template.merge({}))
92         self.assertEquals('', template.merge({'some_value': None}))
93
94     def test_if_statement_honours_custom_truth_value_of_objects(self):
95         class BooleanValue:
96             def __init__(self, value): self.value = value
97             def __nonzero__(self): return self.value
98         template = airspeed.Template("#if ($v)yes#end")
99         self.assertEquals('', template.merge({'v': BooleanValue(False)}))
100         self.assertEquals('yes', template.merge({'v': BooleanValue(True)}))
101
102
103     def test_new_lines_in_templates_are_permitted(self):
104         template = airspeed.Template("hello #if ($show_greeting)${name}.\n#if($is_birthday)Happy Birthday\n#end.\n#endOff out later?")
105         namespace = {"name": "Steve", "show_greeting": True, "is_birthday": True}
106         self.assertEquals("hello Steve.\nHappy Birthday\n.\nOff out later?", template.merge(namespace))
107
108     def test_foreach_with_plain_content_loops_correctly(self):
109         template = airspeed.Template("#foreach ($name in $names)Hello you. #end")
110         self.assertEquals("Hello you. Hello you. ", template.merge({"names": ["Chris", "Steve"]}))
111
112     def test_foreach_skipped_when_nested_in_a_failing_if(self):
113         template = airspeed.Template("#if ($false_value)#foreach ($name in $names)Hello you. #end#end")
114         self.assertEquals("", template.merge({"false_value": False, "names": ["Chris", "Steve"]}))
115
116     def test_foreach_with_expression_content_loops_correctly(self):
117         template = airspeed.Template("#foreach ($name in $names)Hello $you. #end")
118         self.assertEquals("Hello You. Hello You. ", template.merge({"you": "You", "names": ["Chris", "Steve"]}))
119
120     def test_foreach_makes_loop_variable_accessible(self):
121         template = airspeed.Template("#foreach ($name in $names)Hello $name. #end")
122         self.assertEquals("Hello Chris. Hello Steve. ", template.merge({"names": ["Chris", "Steve"]}))
123
124     def test_loop_variable_not_accessible_after_loop(self):
125         template = airspeed.Template("#foreach ($name in $names)Hello $name. #end$name")
126         self.assertEquals("Hello Chris. Hello Steve. $name", template.merge({"names": ["Chris", "Steve"]}))
127
128     def test_loop_variables_do_not_clash_in_nested_loops(self):
129         template = airspeed.Template("#foreach ($word in $greetings)$word to#foreach ($word in $names) $word#end. #end")
130         namespace = {"greetings": ["Hello", "Goodbye"], "names": ["Chris", "Steve"]}
131         self.assertEquals("Hello to Chris Steve. Goodbye to Chris Steve. ", template.merge(namespace))
132
133     def test_loop_counter_variable_available_in_loops(self):
134         template = airspeed.Template("#foreach ($word in $greetings)$velocityCount,#end")
135         namespace = {"greetings": ["Hello", "Goodbye"]}
136         self.assertEquals("1,2,", template.merge(namespace))
137
138     def test_loop_counter_variables_do_not_clash_in_nested_loops(self):
139         template = airspeed.Template("#foreach ($word in $greetings)Outer $velocityCount#foreach ($word in $names), inner $velocityCount#end. #end")
140         namespace = {"greetings": ["Hello", "Goodbye"], "names": ["Chris", "Steve"]}
141         self.assertEquals("Outer 1, inner 1, inner 2. Outer 2, inner 1, inner 2. ", template.merge(namespace))
142
143     def test_can_use_an_integer_variable_defined_in_template(self):
144         template = airspeed.Template("#set ($value = 10)$value")
145         self.assertEquals("10", template.merge({}))
146
147     def test_passed_in_namespace_not_modified_by_set(self):
148         template = airspeed.Template("#set ($value = 10)$value")
149         namespace = {}
150         template.merge(namespace)
151         self.assertEquals({}, namespace)
152
153     def test_can_use_a_string_variable_defined_in_template(self):
154         template = airspeed.Template('#set ($value = "Steve")$value')
155         self.assertEquals("Steve", template.merge({}))
156
157     def test_can_use_a_single_quoted_string_variable_defined_in_template(self):
158         template = airspeed.Template("#set ($value = 'Steve')$value")
159         self.assertEquals("Steve", template.merge({}))
160
161     def test_single_line_comments_skipped(self):
162         template = airspeed.Template('## comment\nStuff\nMore stuff## more comments $blah')
163         self.assertEquals("Stuff\nMore stuff", template.merge({}))
164
165     def test_multi_line_comments_skipped(self):
166         template = airspeed.Template('Stuff#*\n more comments *#\n and more stuff')
167         self.assertEquals("Stuff and more stuff", template.merge({}))
168
169     def test_merge_to_stream(self):
170         template = airspeed.Template('Hello $name!')
171         from cStringIO import StringIO
172         output = StringIO()
173         template.merge_to({"name": "Chris"}, output)
174         self.assertEquals('Hello Chris!', output.getvalue())
175
176     def test_string_literal_can_contain_embedded_escaped_quotes(self):
177         template = airspeed.Template('#set ($name = "\\"batman\\"")$name')
178         self.assertEquals('"batman"', template.merge({}))
179
180     def test_string_literal_can_contain_embedded_escaped_newlines(self):
181         template = airspeed.Template('#set ($name = "\\\\batman\\nand robin")$name')
182         self.assertEquals('\\batman\nand robin', template.merge({}))
183
184     def test_else_block_evaluated_when_if_expression_false(self):
185         template = airspeed.Template('#if ($value) true #else false #end')
186         self.assertEquals(" false ", template.merge({}))
187
188     def test_too_many_end_clauses_trigger_error(self):
189         template = airspeed.Template('#if (1)true!#end #end ')
190         self.assertRaises(airspeed.TemplateSyntaxError, template.merge, {})
191
192     def test_can_call_function_with_one_parameter(self):
193         def squared(number):
194             return number * number
195         template = airspeed.Template('$squared(8)')
196         self.assertEquals("64", template.merge(locals()))
197         some_var = 6
198         template = airspeed.Template('$squared($some_var)')
199         self.assertEquals("36", template.merge(locals()))
200         template = airspeed.Template('$squared($squared($some_var))')
201         self.assertEquals("1296", template.merge(locals()))
202
203     def test_can_call_function_with_two_parameters(self):
204         def multiply(number1, number2):
205             return number1 * number2
206         template = airspeed.Template('$multiply(2, 4)')
207         self.assertEquals("8", template.merge(locals()))
208         template = airspeed.Template('$multiply( 2 , 4 )')
209         self.assertEquals("8", template.merge(locals()))
210         value1, value2 = 4, 12
211         template = airspeed.Template('$multiply($value1,$value2)')
212         self.assertEquals("48", template.merge(locals()))
213
214     def test_velocity_style_escaping(self): # example from Velocity docs
215         template = airspeed.Template('''\
216 #set( $email = "foo" )
217 $email
218 \\$email
219 \\\\$email
220 \\\\\\$email''')
221         self.assertEquals('''\
222 foo
223 $email
224 \\foo
225 \\$email''', template.merge({}))
226
227 #    def test_velocity_style_escaping_when_var_unset(self): # example from Velocity docs
228 #        template = airspeed.Template('''\
229 #$email
230 #\$email
231 #\\$email
232 #\\\$email''')
233 #        self.assertEquals('''\
234 #$email
235 #\$email
236 #\\$email
237 #\\\$email''', template.merge({}))
238
239     def test_true_elseif_evaluated_when_if_is_false(self):
240         template = airspeed.Template('#if ($value1) one #elseif ($value2) two #end')
241         value1, value2 = False, True
242         self.assertEquals(' two ', template.merge(locals()))
243
244     def test_false_elseif_skipped_when_if_is_true(self):
245         template = airspeed.Template('#if ($value1) one #elseif ($value2) two #end')
246         value1, value2 = True, False
247         self.assertEquals(' one ', template.merge(locals()))
248
249     def test_first_true_elseif_evaluated_when_if_is_false(self):
250         template = airspeed.Template('#if ($value1) one #elseif ($value2) two #elseif($value3) three #end')
251         value1, value2, value3 = False, True, True
252         self.assertEquals(' two ', template.merge(locals()))
253
254     def test_illegal_to_have_elseif_after_else(self):
255         template = airspeed.Template('#if ($value1) one #else two #elseif($value3) three #end')
256         self.assertRaises(airspeed.TemplateSyntaxError, template.merge, {})
257
258     def test_else_evaluated_when_if_and_elseif_are_false(self):
259         template = airspeed.Template('#if ($value1) one #elseif ($value2) two #else three #end')
260         value1, value2 = False, False
261         self.assertEquals(' three ', template.merge(locals()))
262
263     def test_syntax_error_contains_line_and_column_pos(self):
264         try: airspeed.Template('#if ( $hello )\n\n#elseif blah').merge({})
265         except airspeed.TemplateSyntaxError, e:
266             self.assertEquals((3, 9), (e.line, e.column))
267         else: self.fail('expected error')
268         try: airspeed.Template('#else blah').merge({})
269         except airspeed.TemplateSyntaxError, e:
270             self.assertEquals((1, 1), (e.line, e.column))
271         else: self.fail('expected error')
272
273     def test_get_position_strings_in_syntax_error(self):
274         try: airspeed.Template('#else whatever').merge({})
275         except airspeed.TemplateSyntaxError, e:
276             self.assertEquals(['#else whatever',
277                                '^'], e.get_position_strings())
278         else: self.fail('expected error')
279
280     def test_get_position_strings_in_syntax_error_when_newline_after_error(self):
281         try: airspeed.Template('#else whatever\n').merge({})
282         except airspeed.TemplateSyntaxError, e:
283             self.assertEquals(['#else whatever',
284                                '^'], e.get_position_strings())
285         else: self.fail('expected error')
286
287     def test_get_position_strings_in_syntax_error_when_newline_before_error(self):
288         try: airspeed.Template('foobar\n  #else whatever\n').merge({})
289         except airspeed.TemplateSyntaxError, e:
290             self.assertEquals(['  #else whatever',
291                                '  ^'], e.get_position_strings())
292         else: self.fail('expected error')
293
294     def test_compare_greater_than_operator(self):
295         template = airspeed.Template('#if ( $value > 1 )yes#end')
296         self.assertEquals('', template.merge({'value': 0}))
297         self.assertEquals('', template.merge({'value': 1}))
298         self.assertEquals('yes', template.merge({'value': 2}))
299
300     def test_compare_greater_than_or_equal_operator(self):
301         template = airspeed.Template('#if ( $value >= 1 )yes#end')
302         self.assertEquals('', template.merge({'value': 0}))
303         self.assertEquals('yes', template.merge({'value': 1}))
304         self.assertEquals('yes', template.merge({'value': 2}))
305
306     def test_compare_less_than_operator(self):
307         template = airspeed.Template('#if ( $value < 1 )yes#end')
308         self.assertEquals('yes', template.merge({'value': 0}))
309         self.assertEquals('', template.merge({'value': 1}))
310         self.assertEquals('', template.merge({'value': 2}))
311
312     def test_compare_less_than_or_equal_operator(self):
313         template = airspeed.Template('#if ( $value <= 1 )yes#end')
314         self.assertEquals('yes', template.merge({'value': 0}))
315         self.assertEquals('yes', template.merge({'value': 1}))
316         self.assertEquals('', template.merge({'value': 2}))
317
318     def test_compare_equality_operator(self):
319         template = airspeed.Template('#if ( $value == 1 )yes#end')
320         self.assertEquals('', template.merge({'value': 0}))
321         self.assertEquals('yes', template.merge({'value': 1}))
322         self.assertEquals('', template.merge({'value': 2}))
323
324     def test_or_operator(self):
325         template = airspeed.Template('#if ( $value1 || $value2 )yes#end')
326         self.assertEquals('', template.merge({'value1': False, 'value2': False}))
327         self.assertEquals('yes', template.merge({'value1': True, 'value2': False}))
328         self.assertEquals('yes', template.merge({'value1': False, 'value2': True}))
329
330     def test_or_operator_considers_not_None_values_true(self):
331         class SomeClass: pass
332         template = airspeed.Template('#if ( $value1 || $value2 )yes#end')
333         self.assertEquals('', template.merge({'value1': None, 'value2': None}))
334         self.assertEquals('yes', template.merge({'value1': SomeClass(), 'value2': False}))
335         self.assertEquals('yes', template.merge({'value1': False, 'value2': SomeClass()}))
336
337     def test_and_operator(self):
338         template = airspeed.Template('#if ( $value1 && $value2 )yes#end')
339         self.assertEquals('', template.merge({'value1': False, 'value2': False}))
340         self.assertEquals('', template.merge({'value1': True, 'value2': False}))
341         self.assertEquals('', template.merge({'value1': False, 'value2': True}))
342         self.assertEquals('yes', template.merge({'value1': True, 'value2': True}))
343
344     def test_and_operator_considers_not_None_values_true(self):
345         class SomeClass: pass
346         template = airspeed.Template('#if ( $value1 && $value2 )yes#end')
347         self.assertEquals('', template.merge({'value1': None, 'value2': None}))
348         self.assertEquals('yes', template.merge({'value1': SomeClass(), 'value2': True}))
349         self.assertEquals('yes', template.merge({'value1': True, 'value2': SomeClass()}))
350
351     def test_parenthesised_value(self):
352         template = airspeed.Template('#if ( ($value1 == 1) && ($value2 == 2) )yes#end')
353         self.assertEquals('', template.merge({'value1': 0, 'value2': 1}))
354         self.assertEquals('', template.merge({'value1': 1, 'value2': 1}))
355         self.assertEquals('', template.merge({'value1': 0, 'value2': 2}))
356         self.assertEquals('yes', template.merge({'value1': 1, 'value2': 2}))
357
358     def test_compound_condition(self):
359         template = airspeed.Template('#if ( ($value) )yes#end')
360         self.assertEquals('', template.merge({'value': False}))
361         self.assertEquals('yes', template.merge({'value': True}))
362
363     def test_logical_negation_operator(self):
364         template = airspeed.Template('#if ( !$value )yes#end')
365         self.assertEquals('yes', template.merge({'value': False}))
366         self.assertEquals('', template.merge({'value': True}))
367
368     def test_logical_negation_operator_yields_true_for_None(self):
369         template = airspeed.Template('#if ( !$value )yes#end')
370         self.assertEquals('yes', template.merge({'value': None}))
371
372     def test_logical_negation_operator_honours_custom_truth_values(self):
373         class BooleanValue:
374             def __init__(self, value): self.value = value
375             def __nonzero__(self): return self.value
376         template = airspeed.Template('#if ( !$v)yes#end')
377         self.assertEquals('yes', template.merge({'v': BooleanValue(False)}))
378         self.assertEquals('', template.merge({'v': BooleanValue(True)}))
379
380     def test_compound_binary_and_unary_operators(self):
381         template = airspeed.Template('#if ( !$value1 && !$value2 )yes#end')
382         self.assertEquals('', template.merge({'value1': False, 'value2': True}))
383         self.assertEquals('', template.merge({'value1': True, 'value2': False}))
384         self.assertEquals('', template.merge({'value1': True, 'value2': True}))
385         self.assertEquals('yes', template.merge({'value1': False, 'value2': False}))
386
387     def test_cannot_define_macro_to_override_reserved_statements(self):
388         for reserved in ('if', 'else', 'elseif', 'set', 'macro', 'foreach', 'parse', 'include', 'stop', 'end'):
389             template = airspeed.Template('#macro ( %s $value) $value #end' % reserved)
390             self.assertRaises(airspeed.TemplateSyntaxError, template.merge, {})
391
392     def test_cannot_call_undefined_macro(self):
393         template = airspeed.Template('#undefined()')
394         self.assertRaises(Exception, template.merge, {})
395
396     def test_define_and_use_macro_with_no_parameters(self):
397         template = airspeed.Template('#macro ( hello)hi#end#hello ()#hello()')
398         self.assertEquals('hihi', template.merge({'text': 'hello'}))
399
400     def test_define_and_use_macro_with_one_parameter(self):
401         template = airspeed.Template('#macro ( bold $value)<strong>$value</strong>#end#bold ($text)')
402         self.assertEquals('<strong>hello</strong>', template.merge({'text': 'hello'}))
403
404     def test_use_of_macro_name_is_case_insensitive(self):
405         template = airspeed.Template('#macro ( bold $value)<strong>$value</strong>#end#BoLd ($text)')
406         self.assertEquals('<strong>hello</strong>', template.merge({'text': 'hello'}))
407
408     def test_define_and_use_macro_with_two_parameter(self):
409         template = airspeed.Template('#macro (addition $value1 $value2 )$value1+$value2#end#addition (1 2)')
410         self.assertEquals('1+2', template.merge({}))
411         template = airspeed.Template('#macro (addition $value1 $value2 )$value1+$value2#end#addition( $one   $two )')
412         self.assertEquals('ONE+TWO', template.merge({'one': 'ONE', 'two': 'TWO'}))
413
414     def test_cannot_redefine_macro(self):
415         template = airspeed.Template('#macro ( hello)hi#end#macro(hello)again#end')
416         self.assertRaises(Exception, template.merge, {}) ## Should this be TemplateSyntaxError?
417
418     def test_include_directive_gives_error_if_no_loader_provided(self):
419         template = airspeed.Template('#include ("foo.tmpl")')
420         self.assertRaises(airspeed.TemplateError, template.merge, {})
421
422     def test_include_directive_yields_loader_error_if_included_content_not_found(self):
423         class BrokenLoader:
424             def load_text(self, name):
425                 raise IOError(name)
426         template = airspeed.Template('#include ("foo.tmpl")')
427         self.assertRaises(IOError, template.merge, {}, loader=BrokenLoader())
428
429     def test_valid_include_directive_include_content(self):
430         class WorkingLoader:
431             def load_text(self, name):
432                 if name == 'foo.tmpl':
433                     return "howdy"
434         template = airspeed.Template('Message is: #include ("foo.tmpl")!')
435         self.assertEquals('Message is: howdy!', template.merge({}, loader=WorkingLoader()))
436
437     def test_parse_directive_gives_error_if_no_loader_provided(self):
438         template = airspeed.Template('#parse ("foo.tmpl")')
439         self.assertRaises(airspeed.TemplateError, template.merge, {})
440
441     def test_parse_directive_yields_loader_error_if_parsed_content_not_found(self):
442         class BrokenLoader:
443             def load_template(self, name):
444                 raise IOError(name)
445         template = airspeed.Template('#parse ("foo.tmpl")')
446         self.assertRaises(IOError, template.merge, {}, loader=BrokenLoader())
447
448     def test_valid_parse_directive_outputs_parsed_content(self):
449         class WorkingLoader:
450             def load_template(self, name):
451                 if name == 'foo.tmpl':
452                     return airspeed.Template("$message")
453         template = airspeed.Template('Message is: #parse ("foo.tmpl")!')
454         self.assertEquals('Message is: hola!', template.merge({'message': 'hola'}, loader=WorkingLoader()))
455         template = airspeed.Template('Message is: #parse ($foo)!')
456         self.assertEquals('Message is: hola!', template.merge({'foo': 'foo.tmpl', 'message': 'hola'}, loader=WorkingLoader()))
457
458     def test_assign_range_literal(self):
459         template = airspeed.Template('#set($values = [1..5])#foreach($value in $values)$value,#end')
460         self.assertEquals('1,2,3,4,5,', template.merge({}))
461         template = airspeed.Template('#set($values = [2..-2])#foreach($value in $values)$value,#end')
462         self.assertEquals('2,1,0,-1,-2,', template.merge({}))
463
464     def test_local_namespace_methods_are_not_available_in_context(self):
465         template = airspeed.Template('#macro(tryme)$values#end#tryme()')
466         self.assertEquals('$values', template.merge({}))
467
468     def test_array_literal(self):
469         template = airspeed.Template('blah\n#set($valuesInList = ["Hello ", $person, ", your lucky number is ", 7])\n#foreach($value in $valuesInList)$value#end\nblah')
470         self.assertEquals('blah\nHello Chris, your lucky number is 7\nblah', template.merge({'person': 'Chris'}))
471
472     def test_nested_array_literals(self):
473         template = airspeed.Template('#set($values = [["Hello ", "Steve"], ["Hello", " Chris"]])#foreach($pair in $values)#foreach($word in $pair)$word#end. #end')
474         self.assertEquals('Hello Steve. Hello Chris. ', template.merge({}))
475
476     def test_when_dictionary_does_not_contain_referenced_attribute_no_substitution_occurs(self):
477         template = airspeed.Template(" $user.name ")
478         self.assertEquals(" $user.name ", template.merge({'user':self}))
479
480     def test_when_non_dictionary_object_does_not_contain_referenced_attribute_no_substitution_occurs(self):
481         class MyObject: pass
482         template = airspeed.Template(" $user.name ")
483         self.assertEquals(" $user.name ", template.merge({'user':MyObject()}))
484
485     def test_variables_expanded_in_double_quoted_strings(self):
486         template = airspeed.Template('#set($hello="hello, $name is my name")$hello')
487         self.assertEquals("hello, Steve is my name", template.merge({'name':'Steve'}))
488
489     def test_escaped_variable_references_not_expanded_in_double_quoted_strings(self):
490         template = airspeed.Template('#set($hello="hello, \\$name is my name")$hello')
491         self.assertEquals("hello, $name is my name", template.merge({'name':'Steve'}))
492
493     def test_macros_expanded_in_double_quoted_strings(self):
494         template = airspeed.Template('#macro(hi $person)$person says hello#end#set($hello="#hi($name)")$hello')
495         self.assertEquals("Steve says hello", template.merge({'name':'Steve'}))
496
497     def test_large_areas_of_text_handled_without_error(self):
498         text = "qwerty uiop asdfgh jkl zxcvbnm. 1234" * 300
499         template = airspeed.Template(text)
500         self.assertEquals(text, template.merge({}))
501
502     def test_foreach_with_unset_variable_expands_to_nothing(self):
503         template = airspeed.Template('#foreach($value in $values)foo#end')
504         self.assertEquals('', template.merge({}))
505
506     def test_foreach_with_non_iterable_variable_raises_error(self):
507         template = airspeed.Template('#foreach($value in $values)foo#end')
508         self.assertRaises(ValueError, template.merge, {'values': 1})
509
510     def test_correct_scope_for_parameters_of_method_calls(self):
511         template = airspeed.Template('$obj.get_self().method($param)')
512         class C:
513             def get_self(self):
514                 return self
515             def method(self, p):
516                 if p == 'bat': return 'monkey'
517         value = template.merge({'obj': C(), 'param':'bat'})
518         self.assertEquals('monkey', value)
519
520     def test_preserves_unicode_strings(self):
521         template = airspeed.Template('$value')
522         value = unicode('Grüße', 'latin1')
523         self.assertEquals(value, template.merge(locals()))
524
525     def test_can_define_macros_in_parsed_files(self):
526         class Loader:
527             def load_template(self, name):
528                 if name == 'foo.tmpl':
529                     return airspeed.Template('#macro(themacro)works#end')
530         template = airspeed.Template('#parse("foo.tmpl")#themacro()')
531         self.assertEquals('works', template.merge({}, loader=Loader()))
532
533     def test_modulus_operator(self):
534         template = airspeed.Template('#set( $modulus = ($value % 2) )$modulus')
535         self.assertEquals('1', template.merge({'value': 3}))
536
537     def test_can_assign_empty_string(self):
538         template = airspeed.Template('#set( $v = "" )#set( $y = \'\' ).$v.$y.')
539         self.assertEquals('...', template.merge({}))
540
541     def test_can_loop_over_numeric_ranges(self):
542         ## Test for bug #15
543         template = airspeed.Template('#foreach( $v in [1..5] )$v\n#end')
544         self.assertEquals('1\n2\n3\n4\n5\n', template.merge({}))
545
546
547 #
548 # TODO:
549 #
550 #  Report locations for template errors in strings
551 #  Math expressions (requires operator precedence)
552 #  Gobbling up whitespace (tricky!)
553 #  Bind #macro calls at compile time?
554 #  #stop ?
555 #  map literals
556 #  Sub-object assignment:  #set( $customer.Behavior = $primate )
557 #  Q. What is scope of #set ($customer.Name = 'john')  ???
558 #  Scope of #set across if/elseif/else?
559 #
560
561
562 if __name__ == '__main__':
563     reload(airspeed)
564     try: main()
565     except SystemExit: pass
Note: See TracBrowser for help on using the browser.