root/trunk/airspeed_test.py

Revision 56, 27.6 kB (checked in by steve, 3 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
4from unittest import TestCase, main
5import airspeed
6
7###############################################################################
8# Compatibility for old Pythons & Jython
9###############################################################################
10try: True
11except NameError:
12    False, True = 0, 1
13
14
15class 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('''\
222foo
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
562if __name__ == '__main__':
563    reload(airspeed)
564    try: main()
565    except SystemExit: pass
Note: See TracBrowser for help on using the browser.