| 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 |
|---|