fluid 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,469 @@
1
+ # Fluid variables for Ruby.
2
+ # Originally 2004/03/08 20:16:13
3
+ # Gemified 2007/07
4
+
5
+
6
+ $:.unshift("../lib")
7
+ require 'test/unit'
8
+ require 'fluid'
9
+ require 's4t-utils/more-assertions'
10
+
11
+ class TestFluid < Test::Unit::TestCase
12
+ ### Tests
13
+ def test_one_variable_uninitialized
14
+ Fluid.let(:a) {
15
+ assert_equal(nil, Fluid.a)
16
+ }
17
+ end
18
+
19
+ def test_one_variable_initialized
20
+ Fluid.let(:a, 1) {
21
+ assert_equal(1, Fluid.a)
22
+ }
23
+ end
24
+
25
+ # Couple of notes about this test.
26
+ # - It checks that fluid variable names can be strings or symbols.
27
+ # - It checks that the values can be complex objects or references.
28
+ def test_multiple_variables
29
+ dval = ["dval"]
30
+ Fluid.let([:a, 1],
31
+ [:b],
32
+ ["c", [1, 2]],
33
+ [:d, dval]) {
34
+ assert_equal(1, Fluid.a)
35
+ assert_equal(nil, Fluid.b)
36
+ assert_equal([1, 2], Fluid.c)
37
+ assert_equal(dval, Fluid.d)
38
+ }
39
+ end
40
+
41
+
42
+ def test_fluid_variable_assignment
43
+ Fluid.let("a", 1) {
44
+ assert_equal(1, Fluid.a)
45
+ Fluid.a = 2
46
+ assert_equal(2, Fluid.a)
47
+ Fluid.let("a") {
48
+ assert_equal(nil, Fluid.a)
49
+ }
50
+ assert_equal(2, Fluid.a)
51
+ }
52
+ end
53
+
54
+ # Assignment is parallel, as in Lisp let, not let*
55
+ # This is inherent in Ruby arg evaluation, but thought I would
56
+ # document it here. let* is impossible, I think.
57
+ def test_assignment_is_parallel
58
+ Fluid.let(:dawn, "best beloved") {
59
+ assert_equal("best beloved", Fluid.dawn)
60
+ Fluid.let([:dawn, "wife"],
61
+ [:paul, "child of #{Fluid.dawn}"]) {
62
+ assert_equal("wife", Fluid.dawn)
63
+ assert_equal("child of best beloved", Fluid.paul)
64
+ }
65
+ }
66
+ end
67
+
68
+ # Test that pushing and popping of values works correctly.
69
+ # Note that this also checks whether the no-op form (no vars)
70
+ # in fact has no effect.
71
+ def test_nesting_behavior_simple_within_method
72
+ Fluid.let(:a, 1) {
73
+ assert_equal(1, Fluid.a)
74
+ Fluid.let {
75
+ assert_equal(1, Fluid.a)
76
+ Fluid.let(:a, 2) {
77
+ assert_equal(2, Fluid.a)
78
+ }
79
+ assert_equal(1, Fluid.a)
80
+ }
81
+ assert_equal(1, Fluid.a)
82
+ }
83
+ end
84
+
85
+ # Check that multiple variables are pushed and popped appropriately.
86
+ def test_nesting_behavior_complex_within_method
87
+ Fluid.let([:a, 1], [:b]) {
88
+ assert_equal(1, Fluid.a)
89
+ assert_equal(nil, Fluid.b)
90
+ Fluid.let(:a) {
91
+ assert_equal(nil, Fluid.a)
92
+ assert_equal(nil, Fluid.b)
93
+ Fluid.let([:b, 'b'],
94
+ [:a, [1, 2]]) {
95
+ assert_equal([1,2], Fluid.a)
96
+ assert_equal('b', Fluid.b)
97
+ }
98
+ assert_equal(nil, Fluid.a)
99
+ assert_equal(nil, Fluid.b)
100
+ }
101
+ assert_equal(1, Fluid.a)
102
+ assert_equal(nil, Fluid.b)
103
+ }
104
+ end
105
+
106
+ # UNWINDING
107
+ # This is an example that shows how bindings are unwound or not
108
+ # unwound as control passes out of a block in various ways
109
+ # that involve calls to other methods. Shows how variable bindings
110
+ # are independent of the call stack.
111
+ def test_nesting_behavior_across_methods
112
+ Fluid.let([:paul, 6],
113
+ [:sophie, 5]) {
114
+ ordinary_subfunction # add one to age.
115
+ assert_equal(7, Fluid.paul)
116
+ assert_equal(6, Fluid.sophie)
117
+
118
+ # Values are unwound in presence of catch.
119
+ catch_value = catch(:catcher) {
120
+ Fluid.let(:paul, 66) {
121
+ assert_equal(66, Fluid.paul)
122
+ assert_equal(6, Fluid.sophie)
123
+ throwing_subfunction
124
+ # This changes both variables, but only
125
+ # the change to Sophie will be visible outside the let block
126
+ }
127
+ }
128
+ assert_equal(55600, catch_value)
129
+ assert_equal(7, Fluid.paul)
130
+ assert_equal("sophster", Fluid.sophie)
131
+
132
+ # Values are unwound with exceptions as well
133
+ begin
134
+ Fluid.let(:sophie, "leewit") {
135
+ assert_equal(7, Fluid.paul)
136
+ assert_equal("leewit", Fluid.sophie)
137
+ raising_subfunction
138
+ # This changes both variables, but only
139
+ # the change to Paul will be visible outside the let block
140
+ }
141
+ rescue RuntimeError
142
+ assert_equal(nil, Fluid.paul)
143
+ assert_equal("sophster", Fluid.sophie)
144
+ return
145
+ end
146
+ fail("Should not reach here.")
147
+ }
148
+ end
149
+
150
+ def ordinary_subfunction
151
+ assert_equal(6, Fluid.paul)
152
+ assert_equal(5, Fluid.sophie)
153
+ Fluid.paul += 1
154
+ Fluid.sophie += 1
155
+ end
156
+
157
+ def throwing_subfunction
158
+ assert_equal(66, Fluid.paul)
159
+ assert_equal(6, Fluid.sophie)
160
+ # This change will be unwound as computation passes
161
+ # outside the Let block, which binds Paul.
162
+ Fluid.paul += 55534
163
+ # This will not, since the let block does not bind
164
+ # sophie.
165
+ Fluid.sophie = "sophster"
166
+ throw :catcher, Fluid.paul
167
+ end
168
+
169
+ def raising_subfunction
170
+ assert_equal(7, Fluid.paul)
171
+ assert_equal("leewit", Fluid.sophie)
172
+ # This change will be unowund as computation passes
173
+ # outside the Let block, which binds sophie.
174
+ Fluid.sophie = nil
175
+
176
+ # This change will be visible outside the let block, which
177
+ # does not bind paul.
178
+ Fluid.paul = nil
179
+ raise "Here comes the exception"
180
+ end
181
+
182
+ # END UNWINDING
183
+
184
+
185
+ def test_unbound_reference
186
+ assert_raises_with_matching_message(NameError,
187
+ "'unbound' has not been defined with Fluid.let or Fluid.defvar.") {
188
+ Fluid.let(:a) {
189
+ puts(Fluid.unbound)
190
+ }
191
+ }
192
+ end
193
+
194
+ def test_unbound_assignment
195
+ assert_raises_with_matching_message(NameError,
196
+ "'also_unbound' has not been defined with Fluid.let or Fluid.defvar.") {
197
+ Fluid.also_unbound = 1
198
+ }
199
+ end
200
+
201
+ def test_bindings_vanish
202
+ Fluid.let(:establish_binding) {
203
+ assert_equal(nil, Fluid.establish_binding)
204
+ }
205
+ assert_raises_with_matching_message(NameError,
206
+ "'establish_binding' has not been defined with Fluid.let or Fluid.defvar.") {
207
+ Fluid.establish_binding = 1
208
+ }
209
+ end
210
+
211
+ def test_block_yields_last_value
212
+ result = Fluid.let([:a,1], [:b, 2]) {
213
+ Fluid.a + Fluid.b
214
+ }
215
+ assert_equal(3, result)
216
+ end
217
+
218
+ def test_some_names_are_not_allowed
219
+ assert_raises_with_matching_message(NameError,
220
+ "'let' cannot be a fluid variable. It's already a method of Fluid's.") {
221
+ Fluid.let(:let) {fail("How'd I get here?")}
222
+ }
223
+
224
+ assert_raises_with_matching_message(NameError,
225
+ "'send' cannot be a fluid variable. It's already a method of Fluid's.") {
226
+ Fluid.let(:send) {fail("How'd I get here?")}
227
+ }
228
+ end
229
+
230
+ def test_duplicates_in_let
231
+ assert_raises_with_matching_message(NameError,
232
+ "'duplicate' is defined twice in the same Fluid.let.") {
233
+ Fluid.let([:duplicate, 1], [:unique], ["duplicate", 1]) {
234
+ fail("How'd I get here?")
235
+ }
236
+ }
237
+ end
238
+
239
+ def test_defvar_simple
240
+ Fluid.defvar(:defvar_simple_a)
241
+ Fluid.defvar("defvar_simple_b", 1)
242
+ Fluid.defvar(:defvar_simple_c) { [1, 2] }
243
+
244
+ assert_equal(nil, Fluid.defvar_simple_a)
245
+ assert_equal(1, Fluid.defvar_simple_b)
246
+ assert_equal([1, 2], Fluid.defvar_simple_c)
247
+
248
+ Fluid.let([:defvar_simple_a, 99],
249
+ [:defvar_simple_b, 999],
250
+ [:defvar_simple_c, "000"]) {
251
+ assert_equal(99, Fluid.defvar_simple_a)
252
+ assert_equal(999, Fluid.defvar_simple_b)
253
+ assert_equal("000", Fluid.defvar_simple_c)
254
+ }
255
+
256
+ assert_equal(nil, Fluid.defvar_simple_a)
257
+ assert_equal(1, Fluid.defvar_simple_b)
258
+ assert_equal([1, 2], Fluid.defvar_simple_c)
259
+ end
260
+
261
+ def test_defvar_once_only
262
+ Fluid.defvar("defvar_once_hello_5", "first value")
263
+ Fluid.defvar(:defvar_once_hello_5, "second value")
264
+ assert_equal("first value", Fluid.defvar_once_hello_5)
265
+
266
+ # Same is true of implicit nil.
267
+ Fluid.defvar(:defvar_once_hello_5)
268
+ assert_equal("first value", Fluid.defvar_once_hello_5)
269
+
270
+ # Moreover, blocks are not evaluated at all the second time.
271
+ Fluid.defvar(:defvar_once_hello_5) { fail "Should not be reached" }
272
+ assert_equal("first value", Fluid.defvar_once_hello_5)
273
+ end
274
+
275
+ def test_defvar_within_let # has no effect
276
+ assert_raises_with_matching_message(NameError,
277
+ "'a' has not been defined with Fluid.let or Fluid.defvar.") {
278
+ Fluid.a
279
+ }
280
+
281
+ Fluid.let(:a, 1) {
282
+ assert_equal(1, Fluid.a)
283
+ Fluid.defvar(:a, 999)
284
+ assert_equal(1, Fluid.a)
285
+ }
286
+
287
+ assert_raises_with_matching_message(NameError,
288
+ "'a' has not been defined with Fluid.let or Fluid.defvar.") {
289
+ Fluid.a
290
+ }
291
+ end
292
+
293
+ def test_variable_names_are_method_names
294
+ assert_raises_with_matching_message(NameError,
295
+ "'blank name' is not a good fluid variable name. It can't be used as a method name.") {
296
+ Fluid.let("blank name") {}
297
+ }
298
+
299
+ assert_raises_with_matching_message(NameError,
300
+ "'9foo' is not a good fluid variable name. It can't be used as a method name.") {
301
+ Fluid.let("9foo".intern) {}
302
+ }
303
+
304
+ assert_raises_with_matching_message(NameError,
305
+ "'a=b' is not a good fluid variable name. It can't be used as a method name.") {
306
+ Fluid.defvar("a=b") {}
307
+ }
308
+
309
+ # Underscores are legal method names, though:
310
+ Fluid.defvar("_", 5)
311
+ assert_equal(5, Fluid._)
312
+ Fluid.defvar("_t", 7)
313
+ assert_equal(7, Fluid._t)
314
+ end
315
+
316
+ def incrementor
317
+ Fluid.counter += 1
318
+ end
319
+
320
+ def test_destructor_called
321
+ Fluid.let([:counter, 0]) do
322
+ Fluid.let(:unused, self, :incrementor) do
323
+ assert_equal(0, Fluid.counter)
324
+ end
325
+ assert_equal(1, Fluid.counter)
326
+ end
327
+ end
328
+
329
+ def test_block_destructor
330
+ value = "destructor_called"
331
+ destructor_called = false
332
+ p = proc { | x | destructor_called = x }
333
+
334
+ assert_equal(value * 2,
335
+ Fluid.let([:_t_d_, value, p]) {
336
+ Fluid._t_d_ * 2
337
+ })
338
+ assert_equal(value, destructor_called)
339
+ end
340
+
341
+ def test_var_checking
342
+ assert_equal(false, Fluid.has?(:log))
343
+ assert_equal(false, Fluid.has?('log'))
344
+ Fluid.defvar(:log)
345
+ assert_equal(true, Fluid.has?(:log))
346
+ assert_equal(true, Fluid.has?('log'))
347
+
348
+ assert_equal(false, Fluid.has?(:another_log))
349
+ assert_equal(false, Fluid.has?('another_log'))
350
+ Fluid.let(:another_log, 5) {
351
+ assert_equal(true, Fluid.has?(:another_log))
352
+ assert_equal(true, Fluid.has?('another_log'))
353
+ }
354
+ assert_equal(false, Fluid.has?(:another_log))
355
+ assert_equal(false, Fluid.has?('another_log'))
356
+ end
357
+
358
+
359
+ #### GLOBALS
360
+
361
+ def test_bound_global
362
+ $global = 'start value'
363
+ assert_equal($global, 'start value')
364
+ Fluid.let("$global", 5) {
365
+ assert_equal(5, $global)
366
+ Fluid.let([:local, 'local'], # mix fluid and globals, just in case.
367
+ ["$global", 'global']) {
368
+ assert_equal('global', $global)
369
+ assert_equal('local', Fluid.local)
370
+ $global = 2334
371
+ }
372
+ assert_equal(5, $global)
373
+ $global = 3434
374
+ }
375
+ assert_equal($global, 'start value')
376
+
377
+ # Binding a predefined global variable has an effect.
378
+ assert_equal(nil, "UP" =~ /up/)
379
+ Fluid.let(["$,", "+"]) {
380
+ assert_equal("1+2", [1, 2].join)
381
+ }
382
+ assert_equal("12", [1, 2].join)
383
+ end
384
+
385
+ def test_has_global
386
+ # Global variables are always considered bound.
387
+ assert_equal(true, Fluid.has?("$has_global_undef"))
388
+ $has_global = 5
389
+ assert_equal(true, Fluid.has?("$has_global"))
390
+ Fluid.let( [ "$has_global", 99 ] ) {
391
+ assert_equal(true, Fluid.has?("$has_global"))
392
+ }
393
+ assert_equal(true, Fluid.has?("$has_global"))
394
+ end
395
+
396
+ def test_exception_global
397
+ # Assigning $! causes odd behavior.
398
+ # Disallow it.
399
+
400
+ assert_raises_with_matching_message(NameError, /'\$\!' is not allowed/) {
401
+ Fluid.let("$!", Exception.new("overridden")) {
402
+ flunk
403
+ }
404
+ }
405
+ end
406
+
407
+ def test_stdin_global
408
+ # Stdin is buffered in such a way that text from the inner binding
409
+ # is visible after a let unbinds. See globals/stdin.rb (Ruby 1.6)
410
+
411
+ assert_raises_with_matching_message(NameError, /'\$stdin' is not allowed/) {
412
+ Fluid.let(["$stdin", nil]) {
413
+ flunk
414
+ }
415
+ }
416
+ end
417
+
418
+ def test_stdout_global
419
+ # $stdout can be bound, but when it exits from the let, it will be
420
+ # buffered differently than $defout. If both are used, lines will be
421
+ # out of order. See globals/stdout.rb. (Ruby 1.6)
422
+
423
+ assert_raises_with_matching_message(NameError, /'\$stdout' is not allowed/) {
424
+ Fluid.let(["$stdout", nil]) {
425
+ flunk
426
+ }
427
+ }
428
+ end
429
+
430
+ def test_global_parallel_assignment
431
+ $g1 = 1
432
+ $g2 = 2
433
+ Fluid.let(["$g1", 11],
434
+ ["$g2", $g1 + 1]) {
435
+ assert_equal(11, $g1)
436
+ assert_equal(2, $g2) # NOT 12
437
+ }
438
+ assert_equal(1, $g1)
439
+ assert_equal(2, $g2)
440
+ end
441
+
442
+ def test_sequential_globals
443
+ $g1 = 1
444
+ $g2 = 2
445
+ Fluid.let(["$g1", $g2],
446
+ ["$g2", $g1]) {
447
+ assert_equal(2, $g1)
448
+ assert_equal(1, $g2)
449
+ }
450
+ assert_equal(1, $g1)
451
+ assert_equal(2, $g2)
452
+
453
+ Fluid.let(["$g1", $g2],
454
+ ["$g2", $g1]) {
455
+ assert_equal(2, $g1)
456
+ assert_equal(1, $g2)
457
+ }
458
+ assert_equal(1, $g1)
459
+ assert_equal(2, $g2)
460
+ end
461
+
462
+ def test_global_defvar
463
+ assert_raises_with_matching_message(NameError,
464
+ "Fluid.defvar of a global can never have an effect, so it's not allowed.") {
465
+ Fluid.defvar("$defvar", 33)
466
+ }
467
+ end
468
+
469
+ end