summaryrefslogtreecommitdiff
path: root/spec/ruby/language/if_spec.rb
blob: bdb2d1e6ac83ad8a3e2617e993eb3b9c91b89cdd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
require_relative '../spec_helper'

describe "The if expression" do
  ruby_version_is '2.4' do
    describe "accepts multiple assignments in conditional expression" do
      before(:each) { ScratchPad.record([]) }
      after(:each)  { ScratchPad.clear }

      it 'with non-nil values' do
        ary = [1, 2]
        eval "if (a, b = ary); ScratchPad.record [a, b]; end"
        ScratchPad.recorded.should == [1, 2]
      end

      it 'with nil values' do
        ary = nil
        eval "if (a, b = ary); else; ScratchPad.record [a, b]; end"
        ScratchPad.recorded.should == [nil, nil]
      end
    end
  end

  it "evaluates body if expression is true" do
    a = []
    if true
      a << 123
    end
    a.should == [123]
  end

  it "does not evaluate body if expression is false" do
    a = []
    if false
      a << 123
    end
    a.should == []
  end

  it "does not evaluate body if expression is empty" do
    a = []
    if ()
      a << 123
    end
    a.should == []
  end

  it "does not evaluate else-body if expression is true" do
    a = []
    if true
      a << 123
    else
      a << 456
    end
    a.should == [123]
  end

  it "evaluates only else-body if expression is false" do
    a = []
    if false
      a << 123
    else
      a << 456
    end
    a.should == [456]
  end

  it "returns result of then-body evaluation if expression is true" do
    if true
      123
    end.should == 123
  end

  it "returns result of last statement in then-body if expression is true" do
    if true
      'foo'
      'bar'
      'baz'
    end.should == 'baz'
  end

  it "returns result of then-body evaluation if expression is true and else part is present" do
    if true
      123
    else
      456
    end.should == 123
  end

  it "returns result of else-body evaluation if expression is false" do
    if false
      123
    else
      456
    end.should == 456
  end

  it "returns nil if then-body is empty and expression is true" do
    if true
    end.should == nil
  end

  it "returns nil if then-body is empty, expression is true and else part is present" do
    if true
    else
      456
    end.should == nil
  end

  it "returns nil if then-body is empty, expression is true and else part is empty" do
    if true
    else
    end.should == nil
  end

  it "returns nil if else-body is empty and expression is false" do
    if false
      123
    else
    end.should == nil
  end

  it "returns nil if else-body is empty, expression is false and then-body is empty" do
    if false
    else
    end.should == nil
  end

  it "considers an expression with nil result as false" do
    if nil
      123
    else
      456
    end.should == 456
  end

  it "considers a non-nil and non-boolean object in expression result as true" do
    if mock('x')
      123
    else
      456
    end.should == 123
  end

  it "considers a zero integer in expression result as true" do
    if 0
      123
    else
      456
    end.should == 123
  end

  it "allows starting else-body on the same line" do
    if false
      123
    else 456
    end.should == 456
  end

  it "evaluates subsequent elsif statements and execute body of first matching" do
    if false
      123
    elsif false
      234
    elsif true
      345
    elsif true
      456
    end.should == 345
  end

  it "evaluates else-body if no if/elsif statements match" do
    if false
      123
    elsif false
      234
    elsif false
      345
    else
      456
    end.should == 456
  end

  it "allows 'then' after expression when then-body is on the next line" do
    if true then
      123
    end.should == 123

    if true then ; 123; end.should == 123
  end

  it "allows then-body on the same line separated with 'then'" do
    if true then 123
    end.should == 123

    if true then 123; end.should == 123
  end

  it "returns nil when then-body on the same line separated with 'then' and expression is false" do
    if false then 123
    end.should == nil

    if false then 123; end.should == nil
  end

  it "returns nil when then-body separated by 'then' is empty and expression is true" do
    if true then
    end.should == nil

    if true then ; end.should == nil
  end

  it "returns nil when then-body separated by 'then', expression is false and no else part" do
    if false then
    end.should == nil

    if false then ; end.should == nil
  end

  it "evaluates then-body when then-body separated by 'then', expression is true and else part is present" do
    if true then 123
    else 456
    end.should == 123

    if true then 123; else 456; end.should == 123
  end

  it "evaluates else-body when then-body separated by 'then' and expression is false" do
    if false then 123
    else 456
    end.should == 456

    if false then 123; else 456; end.should == 456
  end

  describe "with a boolean range ('flip-flop' operator)" do
    before :each do
      ScratchPad.record []
      @verbose = $VERBOSE
      $VERBOSE = nil
    end

    after :each do
      ScratchPad.clear
      $VERBOSE = @verbose
    end

    it "mimics an awk conditional with a single-element inclusive-end range" do
      eval "10.times { |i| ScratchPad << i if (i == 4)..(i == 4) }"
      ScratchPad.recorded.should == [4]
    end

    it "mimics an awk conditional with a many-element inclusive-end range" do
      eval "10.times { |i| ScratchPad << i if (i == 4)..(i == 7) }"
      ScratchPad.recorded.should == [4, 5, 6, 7]
    end

    it "mimics a sed conditional with a zero-element exclusive-end range" do
      eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 4) }"
      ScratchPad.recorded.should == [4, 5, 6, 7, 8, 9]
    end

    it "mimics a sed conditional with a many-element exclusive-end range" do
      eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 5) }"
      ScratchPad.recorded.should == [4, 5]
    end

    it "allows combining two flip-flops" do
      eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 5) or (i == 7)...(i == 8) }"
      ScratchPad.recorded.should == [4, 5, 7, 8]
    end

    it "evaluates the first conditions lazily with inclusive-end range" do
      collector = proc { |i| ScratchPad << i }
      eval "10.times { |i| i if collector[i]...false }"
      ScratchPad.recorded.should == [0]
    end

    it "evaluates the first conditions lazily with exclusive-end range" do
      collector = proc { |i| ScratchPad << i }
      eval "10.times { |i| i if collector[i]..false }"
      ScratchPad.recorded.should == [0]
    end

    it "evaluates the second conditions lazily with inclusive-end range" do
      collector = proc { |i| ScratchPad << i }
      eval "10.times { |i| i if (i == 4)...collector[i] }"
      ScratchPad.recorded.should == [5]
    end

    it "evaluates the second conditions lazily with exclusive-end range" do
      collector = proc { |i| ScratchPad << i }
      eval "10.times { |i| i if (i == 4)..collector[i] }"
      ScratchPad.recorded.should == [4]
    end

    it "scopes state by flip-flop" do
      store_me = eval("proc { |i| ScratchPad << i if (i == 4)..(i == 7) }")
      store_me[1]
      store_me[4]
      proc { store_me[1] }.call
      store_me[7]
      store_me[5]
      ScratchPad.recorded.should == [4, 1, 7]
    end

    it "keeps flip-flops from interfering" do
      a = eval "proc { |i| ScratchPad << i if (i == 4)..(i == 7) }"
      b = eval "proc { |i| ScratchPad << i if (i == 4)..(i == 7) }"
      6.times(&a)
      6.times(&b)
      ScratchPad.recorded.should == [4, 5, 4, 5]
    end

    ruby_version_is "2.6" do
      it 'is deprecated' do
        i = 4

        -> do
          eval "ScratchPad << 'it works' if (i == 4)..(i == 7)"
        end.should complain(/flip-flop is deprecated/)

        ScratchPad.recorded.should == ['it works']
      end
    end
  end
end

describe "The postfix if form" do
  it "evaluates statement if expression is true" do
    a = []
    a << 123 if true
    a.should == [123]
  end

  it "does not evaluate statement if expression is false" do
    a = []
    a << 123 if false
    a.should == []
  end

  it "returns result of expression if value is true" do
    (123 if true).should == 123
  end

  it "returns nil if expression is false" do
    (123 if false).should == nil
  end

  it "considers a nil expression as false" do
    (123 if nil).should == nil
  end

  it "considers a non-nil object as true" do
    (123 if mock('x')).should == 123
  end

  it "evaluates then-body in containing scope" do
    a = 123
    if true
      b = a+1
    end
    b.should == 124
  end

  it "evaluates else-body in containing scope" do
    a = 123
    if false
      b = a+1
    else
      b = a+2
    end
    b.should == 125
  end

  it "evaluates elsif-body in containing scope" do
    a = 123
    if false
      b = a+1
    elsif false
      b = a+2
    elsif true
      b = a+3
    else
      b = a+4
    end
    b.should == 126
  end
end