sexp_processor 4.9.0 → 4.10.0b1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/unique.rb CHANGED
@@ -2,10 +2,16 @@
2
2
  # Unique creates unique variable names.
3
3
 
4
4
  class Unique
5
- def self.reset # mostly for testing
5
+ ##
6
+ # Reset current count back to zero. Mainly used for testing.
7
+
8
+ def self.reset
6
9
  @@curr = 0
7
10
  end
8
11
 
12
+ ##
13
+ # Get the next unique variable name.
14
+
9
15
  def self.next
10
16
  @@curr += 1
11
17
  "temp_#{@@curr}".intern
@@ -1,7 +1,7 @@
1
1
  $TESTING = true
2
2
 
3
- require 'composite_sexp_processor'
4
- require 'minitest/autorun'
3
+ require "composite_sexp_processor"
4
+ require "minitest/autorun"
5
5
 
6
6
  class FakeProcessor1 < SexpProcessor # ZenTest SKIP
7
7
 
@@ -9,16 +9,13 @@ class FakeProcessor1 < SexpProcessor # ZenTest SKIP
9
9
  super
10
10
  self.warn_on_default = false
11
11
  self.default_method = :default_processor
12
+ self.require_empty = false
12
13
  self.expected = Array
13
14
  end
14
15
 
15
- def default_processor(exp)
16
- result = []
17
- result << exp.shift
18
- until exp.empty? do
19
- result << exp.shift.to_s + " woot"
20
- end
21
- result
16
+ def default_processor exp
17
+ t, *rest = exp
18
+ s(t, *rest.map { |s| "#{s} woot" })
22
19
  end
23
20
  end
24
21
 
@@ -29,20 +26,20 @@ class TestCompositeSexpProcessor < Minitest::Test
29
26
  end
30
27
 
31
28
  def test_process_default
32
- data = [1, 2, 3]
29
+ data = s(1, 2, 3)
33
30
  result = @p.process(data.dup)
34
31
  assert_equal(data.dup, result)
35
32
  end
36
33
 
37
34
  def test_process_fake1
38
- data = [:x, 1, 2, 3]
35
+ data = s(:x, 1, 2, 3)
39
36
  @p << FakeProcessor1.new
40
37
  result = @p.process(data.dup)
41
38
  assert_equal [:x, "1 woot", "2 woot", "3 woot"], result
42
39
  end
43
40
 
44
41
  def test_process_fake1_twice
45
- data = [:x, 1, 2, 3]
42
+ data = s(:x, 1, 2, 3)
46
43
  @p << FakeProcessor1.new
47
44
  @p << FakeProcessor1.new
48
45
  result = @p.process(data.dup)
@@ -1,7 +1,7 @@
1
1
  $TESTING = true
2
2
 
3
- require 'minitest/autorun'
4
- require 'sexp_processor'
3
+ require "minitest/autorun"
4
+ require "sexp_processor"
5
5
 
6
6
  class TestEnvironment < Minitest::Test
7
7
 
data/test/test_sexp.rb CHANGED
@@ -1,10 +1,19 @@
1
1
  $TESTING = true
2
2
 
3
- require 'minitest/autorun'
3
+ if ENV["COV"]
4
+ require "simplecov"
5
+ SimpleCov.start do
6
+ add_filter "lib/sexp_processor"
7
+ add_filter "test"
8
+ end
9
+ warn "Running simplecov"
10
+ end
11
+
12
+ require "minitest/autorun"
13
+ require "minitest/hell" # beat these up
4
14
  require "minitest/benchmark" if ENV["BENCH"]
5
- require 'sexp_processor'
6
- require 'stringio'
7
- require 'pp'
15
+ require "sexp_processor" # for deep_clone (TODO: why is that on SP and not S?)
16
+ require "sexp"
8
17
 
9
18
  def pyramid_sexp max
10
19
  # s(:array,
@@ -20,49 +29,119 @@ def pyramid_sexp max
20
29
  end
21
30
 
22
31
  class SexpTestCase < Minitest::Test
32
+ M = Sexp::Matcher
33
+ MC = Sexp::MatchCollection
34
+ MR = Sexp # ::MatchResult
35
+
36
+ CLASS_SEXP = s(:class, :cake, nil,
37
+ s(:defn, :foo, s(:args), s(:add, :a, :b)),
38
+ s(:defn, :bar, s(:args), s(:sub, :a, :b)))
39
+
40
+ def skip_if_strict n = 1
41
+ strict = ENV["STRICT_SEXP"].to_i
42
+
43
+ skip "Can't pass on STRICT_SEXP mode" if strict >= n
44
+ end
45
+
23
46
  # KEY for regex tests
24
47
  # :a == no change
25
48
  # :b == will change (but sometimes ONLY once)
26
49
  # :c == change to
27
50
 
28
- include SexpMatchSpecials
51
+ def assert_equal3 x, y
52
+ skip_if_strict
29
53
 
30
- def util_equals(x, y)
31
- result = x == y
32
- refute_nil result, "#{x.inspect} does not === #{y.inspect}"
54
+ assert_operator x, :===, y
33
55
  end
34
56
 
35
- def util_equals3(x, y)
36
- result = x === y
37
- refute_nil result, "#{x.inspect} does not === #{y.inspect}"
57
+ def refute_equal3 x, y
58
+ refute_operator x, :===, y
38
59
  end
39
60
 
40
- def setup
41
- @any = ANY()
61
+ def assert_pretty_print expect, input
62
+ assert_equal expect, input.pretty_inspect.chomp
42
63
  end
43
- end
44
64
 
45
- class TestSexp < SexpTestCase # ZenTest FULL
65
+ def assert_inspect expect, input
66
+ assert_equal expect, input.inspect
67
+ end
46
68
 
47
- class SexpFor
48
- def method
49
- 1
50
- end
69
+ def assert_search count, sexp, pattern
70
+ assert_equal count, sexp.search_each(pattern).count
71
+ end
72
+
73
+ def assert_satisfy pattern, sexp
74
+ assert_operator pattern, :satisfy?, sexp
75
+ end
76
+
77
+ def refute_satisfy pattern, sexp
78
+ refute_operator pattern, :satisfy?, sexp
79
+ end
80
+ end # class SexpTestCase
81
+
82
+ class MatcherTestCase < SexpTestCase
83
+ def self.abstract_test_case! klass = self # REFACTOR: push this up to minitest
84
+ extend Module.new {
85
+ define_method :run do |*args|
86
+ super(*args) unless self == klass
87
+ end
88
+ }
89
+ end
90
+
91
+ abstract_test_case!
92
+
93
+ def matcher
94
+ raise "Subclass responsibility"
95
+ end
96
+
97
+ def inspect_str
98
+ raise "Subclass responsibility"
99
+ end
100
+
101
+ def pretty_str
102
+ inspect_str
103
+ end
104
+
105
+ def sexp
106
+ s(:a)
107
+ end
108
+
109
+ def bad_sexp
110
+ s(:b)
111
+ end
112
+
113
+ def test_satisfy_eh
114
+ assert_equal3 matcher, sexp
115
+ end
116
+
117
+ def test_satisfy_eh_fail
118
+ skip "not applicable" unless bad_sexp
119
+ refute_equal3 matcher, bad_sexp
120
+ end
121
+
122
+ def test_greedy
123
+ refute_operator matcher, :greedy?
51
124
  end
52
125
 
53
- def util_pretty_print(expect, input)
54
- io = StringIO.new
55
- PP.pp(input, io)
56
- io.rewind
57
- assert_equal(expect, io.read.chomp)
126
+ def test_inspect
127
+ assert_inspect inspect_str, matcher
58
128
  end
59
129
 
130
+ def test_pretty_print
131
+ assert_pretty_print pretty_str, matcher
132
+ end
133
+ end # class MatcherTestCase
134
+
135
+ class TestSexp < SexpTestCase # ZenTest FULL
60
136
  def setup
61
137
  super
62
138
  @sexp_class = Object.const_get(self.class.name[4..-1])
63
- @processor = SexpProcessor.new
64
139
  @sexp = @sexp_class.new(1, 2, 3)
65
140
  @basic_sexp = s(:lasgn, :var, s(:lit, 42).line(1)).line(1)
141
+ @basic_sexp.each_sexp do |s|
142
+ s.file = "file.rb"
143
+ end
144
+
66
145
  @complex_sexp = s(:block,
67
146
  s(:lasgn, :foo, s(:str, "foo").line(1)).line(1),
68
147
  s(:if, s(:and, s(:true).line(2), s(:lit, 1).line(2)).line(2),
@@ -76,38 +155,50 @@ class TestSexp < SexpTestCase # ZenTest FULL
76
155
  @bad1 = s(:blah, 42)
77
156
  end
78
157
 
158
+ def assert_from_array exp, input
159
+ assert_equal exp, Sexp.from_array(input)
160
+ end
161
+
79
162
  def test_class_from_array
80
- skip 'Need to write test_class_from_array'
163
+ assert_from_array s(), []
164
+ assert_from_array s(:s), [:s]
165
+ assert_from_array s(:s, s(:m)), [:s, [:m]]
166
+ assert_from_array s(:s, s(:m)), [:s, s(:m)]
167
+ assert_from_array s(:s, s(:m, [:not_converted])), [:s, s(:m, [:not_converted])]
81
168
  end
82
169
 
83
- def test_class_index
84
- skip 'Need to write test_class_index'
170
+ def test_compact
171
+ input = s(:a, nil, :b)
172
+
173
+ actual = input.compact
174
+
175
+ assert_equal s(:a, :b), actual
176
+ assert_same input, actual # returns mutated result
85
177
  end
86
178
 
87
179
  def test_array_type_eh
88
- assert_equal false, @sexp.array_type?
89
- @sexp.unshift :array
90
- assert_equal true, @sexp.array_type?
180
+ capture_io do # HACK
181
+ assert_equal false, s(:lit, 42).array_type?
182
+ assert_equal true, s(:array, 42).array_type?
183
+ end
91
184
  end
92
185
 
93
186
  def test_each_of_type
94
187
  # TODO: huh... this tests fails if top level sexp :b is removed
95
188
  @sexp = s(:b, s(:a, s(:b, s(:a), :a, s(:b, :a), s(:b, s(:a)))))
96
189
  count = 0
97
- @sexp.each_of_type(:a) do |exp|
190
+ @sexp.each_of_type :a do
98
191
  count += 1
99
192
  end
100
193
  assert_equal(3, count, "must find 3 a's in #{@sexp.inspect}")
101
194
  end
102
195
 
103
196
  def test_equals2_array
104
- # can't use assert_equals because it uses array as receiver
105
- refute_equal(@sexp, [1, 2, 3],
106
- "Sexp must not be equal to equivalent array")
107
- # both directions just in case
108
- # HACK - this seems to be a bug in ruby as far as I'm concerned
109
- # assert_not_equal([1, 2, 3], @sexp,
110
- # "Sexp must not be equal to equivalent array")
197
+ refute_equal @sexp, [1, 2, 3] # Sexp == Array
198
+ assert_raises Minitest::Assertion do # Array == Sexp.
199
+ refute_equal [1, 2, 3], @sexp # This is a bug in ruby:
200
+ end
201
+ # TODO: test if it is calling to_ary first? seems not to
111
202
  end
112
203
 
113
204
  def test_equals2_not_body
@@ -124,81 +215,107 @@ class TestSexp < SexpTestCase # ZenTest FULL
124
215
  end
125
216
  end
126
217
 
127
- def test_equals3_any
128
- util_equals3 @any, s()
129
- util_equals3 @any, s(:a)
130
- util_equals3 @any, s(:a, :b, s(:c))
218
+ def test_equal3_full_match
219
+ assert_equal3 s(), s() # 0
220
+ assert_equal3 s(:blah), s(:blah) # 1
221
+ assert_equal3 s(:a, :b), s(:a, :b) # 2
222
+ assert_equal3 @basic_sexp, @basic_sexp.dup # deeper structure
131
223
  end
132
224
 
133
- def test_equals3_full_match
134
- util_equals3 s(), s() # 0
135
- util_equals3 s(:blah), s(:blah) # 1
136
- util_equals3 s(:a, :b), s(:a, :b) # 2
137
- util_equals3 @basic_sexp, @basic_sexp.dup # deeper structure
225
+ def test_equal3_mismatch
226
+ refute_equal3 s(), s(:a)
227
+ refute_equal3 s(:a), s()
228
+ refute_equal3 s(:blah1), s(:blah2)
229
+ refute_equal3 s(:a), s(:a, :b)
230
+ refute_equal3 s(:a, :b), s(:a)
231
+ refute_equal3 s(:a1, :b), s(:a2, :b)
232
+ refute_equal3 s(:a, :b1), s(:a, :b2)
138
233
  end
139
234
 
140
- def test_equals3_mismatch
141
- assert_nil s() === s(:a)
142
- assert_nil s(:a) === s()
143
- assert_nil s(:blah1) === s(:blah2)
144
- assert_nil s(:a) === s(:a, :b)
145
- assert_nil s(:a, :b) === s(:a)
146
- assert_nil s(:a1, :b) === s(:a2, :b)
147
- assert_nil s(:a, :b1) === s(:a, :b2)
148
- assert_nil @basic_sexp === @basic_sexp.dup.push(42)
149
- assert_nil @basic_sexp.dup.push(42) === @basic_sexp
235
+ def test_equal3_subset_match
236
+ assert_match s{s(:a)}, s(s(:a), s(:b)) # left - =~
237
+ assert_equal3 s{s(:a)}, s(s(:a), s(:b)) # left - ===
238
+ assert_equal3 s{s(:a)}, s(:blah, s(:a ), s(:b)) # mid 1
239
+ assert_equal3 s{s(:a, 1)}, s(:blah, s(:a, 1), s(:b)) # mid 2
240
+ assert_equal3 s{s(:a)}, s(:blah, s(:blah, s(:a))) # left deeper
150
241
  end
151
242
 
152
- def test_equals3_subset_match
153
- util_equals3 s(:a), s(s(:a), s(:b)) # left
154
- util_equals3 s(:a), s(:blah, s(:a ), s(:b)) # mid 1
155
- util_equals3 s(:a, 1), s(:blah, s(:a, 1), s(:b)) # mid 2
156
- util_equals3 @basic_sexp, s(:blah, @basic_sexp.dup, s(:b)) # mid deeper
157
- util_equals3 @basic_sexp, s(@basic_sexp.dup, s(:a), s(:b)) # left deeper
158
-
159
- util_equals3 s(:a), s(:blah, s(:blah, s(:a))) # left deeper
160
- end
243
+ def test_equalstilde_fancy
244
+ assert_match s{ s(:b) }, s(:a, s(:b), :c)
245
+ assert_match s(:a, s(:b), :c), s{ s(:b) }
161
246
 
162
- # def test_equalstilde_any
163
- # result = @basic_sexp =~ s(:lit, ANY())
164
- # p result
165
- # assert result
166
- # end
247
+ e = assert_raises ArgumentError do
248
+ s(:b) =~ s(:a, s(:b), :c)
249
+ end
250
+ assert_equal "Not a pattern: s(:a, s(:b), :c)", e.message
167
251
 
168
- def test_equalstilde_fancy
169
- assert_nil s(:b) =~ s(:a, s(:b), :c)
170
- refute_nil s(:a, s(:b), :c) =~ s(:b)
252
+ e = assert_raises ArgumentError do
253
+ s(:a, s(:b), :c) =~ s(:b)
254
+ end
255
+ assert_equal "Not a pattern: s(:b)", e.message
171
256
  end
172
257
 
173
258
  def test_equalstilde_plain
174
- result = @basic_sexp =~ @re
175
- assert result
259
+ s{ s(:re) } =~ s(:data) # pattern on LHS
260
+ s(:data) =~ s{ s(:re) } # pattern on RHS
261
+
262
+ e = assert_raises ArgumentError do
263
+ s(:re) =~ s(:data) # no pattern
264
+ end
265
+
266
+ assert_equal "Not a pattern: s(:data)", e.message
176
267
  end
177
268
 
178
269
  def test_find_and_replace_all
179
- @sexp = s(:a, s(:b, s(:a), s(:b), s(:b, s(:a))))
180
- expected = s(:a, s(:a, s(:a), s(:a), s(:a, s(:a))))
270
+ skip_if_strict 2
271
+
272
+ @sexp = s(:a, s(:a, :b, s(:a, :b), s(:a), :b, s(:a, s(:a))))
273
+ expected = s(:a, s(:a, :a, s(:a, :a), s(:a), :a, s(:a, s(:a))))
181
274
 
182
275
  @sexp.find_and_replace_all(:b, :a)
183
276
 
184
277
  assert_equal(expected, @sexp)
185
278
  end
186
279
 
280
+ def assert_gsub exp, sexp, from, to
281
+ assert_equal exp, sexp.gsub(from, to)
282
+ end
283
+
187
284
  def test_gsub
188
- assert_equal s(:c), s(:b). gsub(s(:b), s(:c))
189
- assert_equal s(:a), s(:a). gsub(s(:b), s(:c))
190
- assert_equal s(:a, s(:c)), s(:a, s(:b)).gsub(s(:b), s(:c))
285
+ assert_gsub s(:c), s(:b), s(:b), s(:c)
286
+ assert_gsub s(:a), s(:a), s(:b), s(:c)
287
+ assert_gsub s(:a, s(:c)), s(:a, s(:b)), s(:b), s(:c)
191
288
  end
192
289
 
193
290
  def test_gsub_empty
194
- assert_equal s(:c), s().gsub(s(), s(:c))
291
+ assert_gsub s(:c), s(), s(), s(:c)
195
292
  end
196
293
 
197
294
  def test_gsub_multiple
198
- assert_equal(s(:a, s(:c), s(:c)),
199
- s(:a, s(:b), s(:b)). gsub(s(:b), s(:c)))
200
- assert_equal(s(:a, s(:c), s(:a, s(:c))),
201
- s(:a, s(:b), s(:a, s(:b))). gsub(s(:b), s(:c)))
295
+ assert_gsub s(:a, s(:c), s(:c)), s(:a, s(:b), s(:b)), s(:b), s(:c)
296
+ assert_gsub s(:a, s(:c), s(:a, s(:c))), s(:a, s(:b), s(:a, s(:b))), s(:b), s(:c)
297
+ end
298
+
299
+ def test_gsub_matcher
300
+ assert_gsub s(:a, :b, :c), s(:a, s(:b, 42), :c), s{ s(:b, _) }, :b
301
+ assert_gsub s(:a, s(:b), :c), s(:a, s(:b), :c), s{ s(:b, _) }, :b
302
+ assert_gsub s(:a, s(:c, :b), :d), s(:a, s(:c, s(:b, 42)), :d), s{ s(:b, _) }, :b
303
+ assert_gsub s(:a, s(:q), :c), s(:a, s(:q), :c), s{ s(:b, _) }, :b
304
+ end
305
+
306
+ def with_env key
307
+ old_val, ENV[key] = ENV[key], "1"
308
+ yield
309
+ ensure
310
+ ENV[key] = old_val
311
+ end
312
+
313
+ def with_verbose &block
314
+ with_env "VERBOSE", &block
315
+ end
316
+
317
+ def with_debug &block
318
+ with_env "DEBUG", &block
202
319
  end
203
320
 
204
321
  def test_inspect
@@ -212,6 +329,17 @@ class TestSexp < SexpTestCase # ZenTest FULL
212
329
  k.new(:a, :b).inspect)
213
330
  assert_equal("#{n}(:a, #{n}(:b))",
214
331
  k.new(:a, k.new(:b)).inspect)
332
+
333
+ with_verbose do
334
+ assert_equal("#{n}().line(42)",
335
+ k.new().line(42).inspect)
336
+ assert_equal("#{n}(:a).line(42)",
337
+ k.new(:a).line(42).inspect)
338
+ assert_equal("#{n}(:a, :b).line(42)",
339
+ k.new(:a, :b).line(42).inspect)
340
+ assert_equal("#{n}(:a, #{n}(:b).line(43)).line(42)",
341
+ k.new(:a, k.new(:b).line(43)).line(42).inspect)
342
+ end
215
343
  end
216
344
 
217
345
  def test_line
@@ -226,6 +354,54 @@ class TestSexp < SexpTestCase # ZenTest FULL
226
354
  assert_equal 5, @complex_sexp.line_max
227
355
  end
228
356
 
357
+ def test_new
358
+ file = "file.rb"
359
+
360
+ old = s(:lasgn, :var, s(:lit, 42).line(2)).line(1)
361
+ old.file = file
362
+ old.each_sexp do |x|
363
+ x.file = file
364
+ end
365
+ old.comments = "do the thing"
366
+
367
+ assert_same file, old.file
368
+ assert_equal 1, old.line
369
+ assert_same file, old.last.file
370
+ assert_equal 2, old.last.line
371
+
372
+ new = old.new(1, 2, 3)
373
+
374
+ assert_equal s(1, 2, 3), new
375
+
376
+ assert_same file, new.file
377
+ assert_equal 1, new.line
378
+ assert_same old.comments, new.comments
379
+ end
380
+
381
+ def test_map
382
+ file = "file.rb"
383
+
384
+ old = s(:lasgn, :var, s(:lit, 42).line(2)).line(1)
385
+ old.file = file
386
+ old.each_sexp do |x|
387
+ x.file = file
388
+ end
389
+ old.comments = "do the thing"
390
+
391
+ assert_same file, old.file
392
+ assert_equal 1, old.line
393
+ assert_same file, old.last.file
394
+ assert_equal 2, old.last.line
395
+
396
+ new = old.map { |x| x }
397
+
398
+ assert_same file, new.file
399
+ assert_equal 1, new.line
400
+ assert_same file, new.last.file
401
+ assert_equal 2, new.last.line
402
+ assert_same old.comments, new.comments
403
+ end
404
+
229
405
  def test_mass
230
406
  assert_equal 1, s(:a).mass
231
407
  assert_equal 3, s(:a, s(:b), s(:c)).mass
@@ -259,11 +435,52 @@ class TestSexp < SexpTestCase # ZenTest FULL
259
435
  end
260
436
 
261
437
  def test_method_missing
262
- assert_nil @sexp.not_there
263
- assert_equal s(:lit, 42), @basic_sexp.lit
438
+ skip_if_strict 3
439
+
440
+ capture_io do
441
+ assert_nil @sexp.not_there
442
+ assert_equal s(:lit, 42), @basic_sexp.lit
443
+ end
444
+ end
445
+
446
+ def test_method_missing_missing
447
+ skip_if_strict 3
448
+ skip "debugging for now" if ENV["DEBUG"]
449
+
450
+ assert_silent do
451
+ assert_nil @basic_sexp.missing
452
+ end
453
+ end
454
+
455
+ def test_method_missing_missing_debug
456
+ skip_if_strict 3
457
+
458
+ exp = /#{Regexp.escape @basic_sexp.to_s}.method_missing\(:missing\) => nil from/
459
+
460
+ with_debug do
461
+ assert_output "", exp do
462
+ assert_nil @basic_sexp.missing
463
+ end
464
+ end
465
+ end
466
+
467
+ def test_method_missing_hit_debug_verbose
468
+ skip_if_strict 3
469
+
470
+ with_debug do
471
+ with_verbose do
472
+ exp = /#{Regexp.escape @basic_sexp.to_s}.method_missing\(:lit\) from/
473
+
474
+ assert_output "", exp do
475
+ assert_equal s(:lit, 42), @basic_sexp.lit
476
+ end
477
+ end
478
+ end
264
479
  end
265
480
 
266
481
  def test_method_missing_ambigious
482
+ skip_if_strict 3
483
+
267
484
  assert_raises NoMethodError do
268
485
  pirate = s(:says, s(:arrr!), s(:arrr!), s(:arrr!))
269
486
  pirate.arrr!
@@ -271,26 +488,34 @@ class TestSexp < SexpTestCase # ZenTest FULL
271
488
  end
272
489
 
273
490
  def test_method_missing_deep
274
- sexp = s(:blah, s(:a, s(:b, s(:c, :yay!))))
275
- assert_equal(s(:c, :yay!), sexp.a.b.c)
491
+ skip_if_strict 3
492
+
493
+ capture_io do
494
+ sexp = s(:blah, s(:a, s(:b, s(:c, :yay!))))
495
+ assert_equal(s(:c, :yay!), sexp.a.b.c)
496
+ end
276
497
  end
277
498
 
278
499
  def test_method_missing_delete
500
+ skip_if_strict 3
501
+
279
502
  sexp = s(:blah, s(:a, s(:b, s(:c, :yay!))))
280
503
 
281
- assert_equal(s(:c, :yay!), sexp.a.b.c(true))
282
- assert_equal(s(:blah, s(:a, s(:b))), sexp)
504
+ capture_io do
505
+ assert_equal(s(:c, :yay!), sexp.a.b.c(true))
506
+ assert_equal(s(:blah, s(:a, s(:b))), sexp)
507
+ end
283
508
  end
284
509
 
285
510
  def test_pretty_print
286
- util_pretty_print("s()",
287
- s())
288
- util_pretty_print("s(:a)",
289
- s(:a))
290
- util_pretty_print("s(:a, :b)",
291
- s(:a, :b))
292
- util_pretty_print("s(:a, s(:b))",
293
- s(:a, s(:b)))
511
+ assert_pretty_print("s()",
512
+ s())
513
+ assert_pretty_print("s(:a)",
514
+ s(:a))
515
+ assert_pretty_print("s(:a, :b)",
516
+ s(:a, :b))
517
+ assert_pretty_print("s(:a, s(:b))",
518
+ s(:a, s(:b)))
294
519
  end
295
520
 
296
521
  def test_sexp_body
@@ -298,6 +523,8 @@ class TestSexp < SexpTestCase # ZenTest FULL
298
523
  end
299
524
 
300
525
  def test_shift
526
+ skip_if_strict 5
527
+
301
528
  skip "https://github.com/MagLev/maglev/issues/250" if maglev?
302
529
 
303
530
  assert_equal(1, @sexp.shift)
@@ -327,6 +554,17 @@ class TestSexp < SexpTestCase # ZenTest FULL
327
554
  assert_equal(backup, @sexp)
328
555
  end
329
556
 
557
+ def test_structure_deprecated
558
+ exp_err = "NOTE: form s(s(:subsexp)).structure is deprecated. Removing in 5.0\n"
559
+
560
+ assert_output "", exp_err do
561
+ sexp = s(s(:subsexp))
562
+ act = sexp.structure
563
+
564
+ assert_equal s(:bogus, s(:subsexp)), act
565
+ end
566
+ end
567
+
330
568
  def test_sub
331
569
  assert_equal s(:c), s(:b). sub(s(:b), s(:c))
332
570
  assert_equal s(:a, s(:c), s(:b)), s(:a, s(:b), s(:b)). sub(s(:b), s(:c))
@@ -342,8 +580,39 @@ class TestSexp < SexpTestCase # ZenTest FULL
342
580
  assert_equal s(:c), s(). sub(s(), s(:c))
343
581
  end
344
582
 
583
+ def assert_sub exp, sexp, from, to
584
+ assert_equal exp, sexp.sub(from, to)
585
+ end
586
+
587
+ def test_sub_matcher
588
+ assert_sub s(:c), s(:b), s{ s(:b) }, s(:c)
589
+ assert_sub s(:a, s(:c), s(:b)), s(:a, s(:b), s(:b)), s{ s(:b) }, s(:c)
590
+ assert_sub s(:a, s(:c), s(:a)), s(:a, s(:b), s(:a)), s{ s(:b) }, s(:c)
591
+
592
+ assert_sub s(:a, :b, :c), s(:a, s(:b, 42), :c), s{ s(:b, _) }, :b
593
+ assert_sub s(:a, s(:b), :c), s(:a, s(:b), :c), s{ s(:b, _) }, :b
594
+ assert_sub s(:a, s(:c, :b), :d), s(:a, s(:c, s(:b, 42)), :d), s{ s(:b, _) }, :b
595
+ assert_sub s(:a, s(:q), :c), s(:a, s(:q), :c), s{ s(:b, _) }, :b
596
+ end
597
+
345
598
  def test_sub_structure
346
- assert_equal(s(:a, s(:c, s(:b))), s(:a, s(:b)). sub(s(:b), s(:c, s(:b))))
599
+ assert_sub s(:a, s(:c, s(:b))), s(:a, s(:b)), s(:b), s(:c, s(:b))
600
+ end
601
+
602
+ def test_sexp_type_eq
603
+ sexp = s(:bad_type, 42)
604
+
605
+ sexp.sexp_type = :good_type
606
+
607
+ assert_equal s(:good_type, 42), sexp
608
+ end
609
+
610
+ def test_sexp_body_eq
611
+ sexp = s(:type, 42)
612
+
613
+ sexp.sexp_body = [1, 2, 3]
614
+
615
+ assert_equal s(:type, 1, 2, 3), sexp
347
616
  end
348
617
 
349
618
  def test_to_a
@@ -374,7 +643,7 @@ class TestSexp < SexpTestCase # ZenTest FULL
374
643
 
375
644
  def test_deep_each
376
645
  result = []
377
- @complex_sexp.deep_each { |s| result << s if s.first == :if }
646
+ @complex_sexp.deep_each { |s| result << s if s.sexp_type == :if }
378
647
  assert_equal [:if, :if], result.map { |k, _| k }
379
648
  end
380
649
 
@@ -382,28 +651,896 @@ class TestSexp < SexpTestCase # ZenTest FULL
382
651
  assert_kind_of Enumerator, @complex_sexp.deep_each
383
652
  assert_equal [:if, :if], @complex_sexp.deep_each.select { |s, _| s == :if }.map { |k, _| k }
384
653
  end
654
+
655
+ def test_unary_not
656
+ skip "TODO?"
657
+ assert_equal M::Not.new(M.q(:a)), s{ !s(:a) }
658
+ end
659
+
660
+ def test_unary_not_outside
661
+ skip "TODO?"
662
+ assert_equal M::Not.new(s(:a)), !s(:a)
663
+ end
664
+ end # TestSexp
665
+
666
+ class TestSexpMatcher < SexpTestCase
667
+ def test_cls_s
668
+ assert_equal M.s(:x), s{ s(:x) }
669
+ end
670
+
671
+ def test_cls_underscore
672
+ assert_equal M::Wild.new, s{ _ }
673
+ end
674
+
675
+ def test_cls_underscore3
676
+ assert_equal M::Remaining.new, s{ ___ }
677
+ end
678
+
679
+ def test_cls_include
680
+ assert_equal M::Include.new(:a), s{ include(:a) }
681
+ end
682
+
683
+ def test_cls_atom
684
+ assert_equal M::Atom.new, s{ atom }
685
+ end
686
+
687
+ def test_cls_any
688
+ assert_equal M::Any.new(M.s(:a), M.s(:b)), s{ any(s(:a), s(:b)) }
689
+ end
690
+
691
+ def test_cls_all
692
+ assert_equal M::All.new(M.s(:a), M.s(:b)), s{ all(s(:a), s(:b)) }
693
+ end
694
+
695
+ def test_cls_not_eh
696
+ assert_equal M::Not.new(M.s(:a)), s{ not?(s(:a)) }
697
+ end
698
+
699
+ def test_cls_child
700
+ assert_equal M::Child.new(M.s(:a)), s{ child(s(:a)) }
701
+ end
702
+
703
+ def test_cls_t
704
+ assert_equal M::Type.new(:a), s{ t(:a) }
705
+ end
706
+
707
+ def test_cls_m
708
+ assert_equal M::Pattern.new(/a/), s{ m(/a/) }
709
+ assert_equal M::Pattern.new(/\Aa\Z/), s{ m(:a) }
710
+ assert_equal M::Pattern.new(/test_\w/), s{ m(/test_\w/) }
711
+ re = Regexp.union [/\w/, /\d/]
712
+ assert_equal M::Pattern.new(re), s{ m(/\w/,/\d/) }
713
+ end
714
+
715
+ def test_amp
716
+ m = s{ s(:a) & s(:b) }
717
+ e = M::All.new(M.s(:a), M.s(:b))
718
+
719
+ assert_equal e, m
720
+ end
721
+
722
+ def test_pipe
723
+ m = s{ s(:a) | s(:b) }
724
+ e = M::Any.new(M.s(:a), M.s(:b))
725
+
726
+ assert_equal e, m
727
+ end
728
+
729
+ def test_unary_minus
730
+ assert_equal M::Not.new(M.s(:a)), s{ -s(:a) }
731
+ end
732
+
733
+ def test_rchevron
734
+ assert_equal M::Sibling.new(M.s(:a), M.s(:b)), s{ s(:a) >> s(:b) }
735
+ end
736
+
737
+ def test_greedy_eh
738
+ refute_operator s{ s(:a) }, :greedy?
739
+ end
740
+
741
+ def test_inspect
742
+ assert_inspect "q(:a)", s{ s(:a) }
743
+ end
744
+
745
+ def test_pretty_print
746
+ assert_pretty_print "q(:a)", s{ s(:a) }
747
+ end
748
+ end # class TestSexpMatcher
749
+
750
+ class TestWild < MatcherTestCase
751
+ def matcher
752
+ s{ _ }
753
+ end
754
+
755
+ def bad_sexp
756
+ nil
757
+ end
758
+
759
+ def inspect_str
760
+ "_"
761
+ end
762
+
763
+ def test_wild_satisfy_eh # TODO: possibly remove
764
+ w = Sexp::Wild.new
765
+
766
+ assert_satisfy w, :a
767
+ assert_satisfy w, 1
768
+ assert_satisfy w, nil
769
+ assert_satisfy w, []
770
+ assert_satisfy w, s()
771
+ end
772
+
773
+ def test_wild_search # TODO: possibly remove
774
+ sexp = CLASS_SEXP.dup
775
+
776
+ assert_search 1, s(:add, :a, :b), s{ s(:add, _, :b) }
777
+ assert_search 1, sexp, s{ s(:defn, :bar, _, _) }
778
+ assert_search 2, sexp, s{ s(:defn, _, _, s(_, :a, :b) ) }
779
+ assert_search 1, s(:a, s()), s{ s(:a, _) }
780
+ assert_search 1, s(:a, :b, :c), s{ s(_, _, _) }
781
+ assert_search 7, sexp, s{ _ }
782
+ end
783
+ end
784
+
785
+ class TestRemaining < MatcherTestCase
786
+ def matcher
787
+ s{ ___ }
788
+ end
789
+
790
+ def bad_sexp
791
+ nil
792
+ end
793
+
794
+ def inspect_str
795
+ "___"
796
+ end
797
+
798
+ def test_remaining_satisfy_eh # TODO: possibly remove
799
+ assert_satisfy s{ ___ }, s(:a)
800
+ assert_satisfy s{ ___ }, s(:a, :b, :c)
801
+ assert_satisfy s{ s(:x, ___ ) }, s(:x, :y)
802
+ refute_satisfy s{ s(:y, ___ ) }, s(:x, :y)
803
+ end
804
+
805
+ def test_greedy
806
+ assert_operator matcher, :greedy?
807
+ end
808
+ end
809
+
810
+ class TestAny < MatcherTestCase
811
+ def matcher
812
+ s{ s(:a) | s(:c) }
813
+ end
814
+
815
+ def inspect_str
816
+ "q(:a) | q(:c)"
817
+ end
818
+
819
+ def pretty_str
820
+ "any(q(:a), q(:c))"
821
+ end
822
+
823
+ def test_any_search # TODO: possibly remove
824
+ assert_search 2, s(:foo, s(:a), s(:b)), s{ s(any(:a, :b)) }
825
+ assert_search 1, s(:foo, s(:a), s(:b)), s{ any( s(:a), s(:c)) }
826
+ end
827
+
828
+ def test_or_satisfy_eh # TODO: possibly remove
829
+ assert_satisfy s{ s(:a) | s(:b) }, s(:a)
830
+ refute_satisfy s{ s(:a) | s(:b) }, s(:c)
831
+ end
832
+
833
+ def test_or_search # TODO: possibly remove
834
+ sexp = CLASS_SEXP.dup
835
+
836
+ assert_search 2, s(:a, s(:b, :c), s(:b, :d)), s{ s(:b, :c) | s(:b, :d) }
837
+ assert_search 2, sexp, s{ s(:add, :a, :b) | s(:defn, :bar, _, _) }
838
+ end
839
+ end
840
+
841
+ class TestAll < MatcherTestCase
842
+ def matcher
843
+ s{ s(:a) & s(:a) }
844
+ end
845
+
846
+ def inspect_str
847
+ "q(:a) & q(:a)"
848
+ end
849
+
850
+ def pretty_str
851
+ "all(q(:a), q(:a))"
852
+ end
853
+
854
+ def test_and_satisfy_eh # TODO: possibly remove
855
+ refute_satisfy s{ s(:a) & s(:b) }, s(:a)
856
+ assert_satisfy s{ s(:a) & s(atom) }, s(:a)
857
+ end
858
+ end
859
+
860
+ class TestNot < MatcherTestCase
861
+ def matcher
862
+ s{ not? s(:b) } # TODO: test unary minus
863
+ end
864
+
865
+ def inspect_str
866
+ "not?(q(:b))" # TODO: change?
867
+ end
868
+
869
+ def pretty_str
870
+ "not?(q(:b))" # TODO: change?
871
+ end
872
+
873
+ def test_not_satisfy_eh # TODO: possibly remove
874
+ refute_satisfy s{ -_ }, s(:a)
875
+ assert_satisfy s{ -s(:b) }, s(:a)
876
+ assert_satisfy s{ not?(s(:b)) }, s(:a)
877
+ refute_satisfy s{ -s(atom) }, s(:a)
878
+ assert_satisfy s{ s(not?(:b)) }, s(:a)
879
+ end
880
+ end
881
+
882
+ class TestChild < MatcherTestCase
883
+ def matcher
884
+ s{ child(s(:a)) }
885
+ end
886
+
887
+ def sexp
888
+ s(:x, s(:a))
889
+ end
890
+
891
+ def bad_sexp
892
+ s(:x, s(:b))
893
+ end
894
+
895
+ def inspect_str
896
+ "child(q(:a))"
897
+ end
898
+
899
+ def test_child_search # TODO: possibly remove
900
+ sexp = CLASS_SEXP.dup
901
+
902
+ assert_search 1, sexp, s{ s(:class, :cake, _, _, child( s(:sub, :a, :b) ) ) }
903
+ assert_search 1, sexp, s{ s(:class, :cake, _, _, child(include(:a))) }
904
+ end
905
+
906
+ def test_satisfy_eh_by_child
907
+ assert_satisfy matcher, s(:a)
908
+ end
909
+ end
910
+
911
+ class TestAtom < MatcherTestCase
912
+ def matcher
913
+ s{ atom }
914
+ end
915
+
916
+ def sexp
917
+ 42
918
+ end
919
+
920
+ def bad_sexp
921
+ s(:a)
922
+ end
923
+
924
+ def inspect_str
925
+ "atom"
926
+ end
927
+
928
+ def test_atom_satisfy_eh # TODO: possibly remove
929
+ a = s{ atom }
930
+
931
+ assert_satisfy a, :a
932
+ assert_satisfy a, 1
933
+ assert_satisfy a, nil
934
+ refute_satisfy a, s()
935
+ end
936
+
937
+ def test_atom_search # TODO: possibly remove
938
+ sexp = CLASS_SEXP.dup
939
+
940
+ assert_search 1, s(:add, :a, :b), s{ s(:add, atom, :b) }
941
+ assert_search 2, sexp, s{ s(:defn, atom, _, s(atom, :a, :b) ) }
942
+ assert_search 0, s(:a, s()), s{ s(:a, atom) }
943
+ end
944
+ end
945
+
946
+ class TestPattern < MatcherTestCase
947
+ def matcher
948
+ s{ s(:a, m(/a/)) }
949
+ end
950
+
951
+ def sexp
952
+ s(:a, :blah)
953
+ end
954
+
955
+ def inspect_str
956
+ "q(:a, m(/a/))"
957
+ end
958
+
959
+ def test_pattern_satisfy_eh # TODO: possibly remove
960
+ assert_satisfy s{ m(/a/) }, :a
961
+ assert_satisfy s{ m(/^test/) }, :test_case
962
+ assert_satisfy s{ m("test") }, :test
963
+ refute_satisfy s{ m("test") }, :test_case
964
+ refute_satisfy s{ m(/a/) }, s(:a)
965
+ end
966
+
967
+ def test_pattern_search # TODO: possibly remove
968
+ sexp = CLASS_SEXP.dup
969
+
970
+ assert_search 2, sexp, s{ s(m(/\w{3}/), :a, :b) }
971
+
972
+ assert_search 0, s(:a), s{ m(/\w/) }
973
+ assert_search 1, s(:a), s{ s(m(/\w/)) }
974
+ assert_search 0, s(:a), s{ m(/\w/,/\d/) }
975
+ assert_search 1, s(:a), s{ s(m(/\w/,/\d/)) }
976
+
977
+ assert_search 0, s(:tests, s(s(:test_a), s(:test_b))), s{ m(/test_\w/) }
978
+ assert_search 2, s(:tests, s(s(:test_a), s(:test_b))), s{ s(m(/test_\w/)) }
979
+ end
980
+ end
981
+
982
+ class TestType < MatcherTestCase
983
+ def matcher
984
+ s{ t(:a) }
985
+ end
986
+
987
+ def test_type_satisfy_eh # TODO: possibly remove
988
+ assert_satisfy s{ t(:a) }, s(:a)
989
+ assert_satisfy s{ t(:a) }, s(:a, :b, s(:oh_hai), :d)
990
+ end
991
+
992
+ def test_type_search
993
+ assert_search 2, CLASS_SEXP.dup, s{ t(:defn) }
994
+ end
995
+
996
+ def inspect_str
997
+ "t(:a)"
998
+ end
999
+ end
1000
+
1001
+ class TestInclude < MatcherTestCase
1002
+ def sexp
1003
+ s(:x, s(:a))
1004
+ end
1005
+
1006
+ def matcher
1007
+ s{ include(s(:a)) }
1008
+ end
1009
+
1010
+ def inspect_str
1011
+ "include(q(:a))"
1012
+ end
1013
+
1014
+ def test_include_search # TODO: possibly remove
1015
+ sexp = CLASS_SEXP.dup
1016
+
1017
+ assert_search 1, s(:add, :a, :b), s{ include(:a) }
1018
+ assert_search 1, sexp, s{ include(:bar) }
1019
+ assert_search 2, sexp, s{ s(:defn, atom, _, include(:a)) }
1020
+ assert_search 2, sexp, s{ include(:a) }
1021
+ assert_search 0, s(:a, s(:b, s(:c))), s{ s(:a, include(:c)) }
1022
+ end
385
1023
  end
386
1024
 
387
- class TestSexpAny < SexpTestCase
1025
+ class TestSibling < MatcherTestCase
1026
+ def sexp
1027
+ s(:x, s(:a), s(:x), s(:b))
1028
+ end
1029
+
1030
+ def matcher
1031
+ s{ s(:a) >> s(:b) }
1032
+ end
1033
+
1034
+ def inspect_str
1035
+ "q(:a) >> q(:b)"
1036
+ end
1037
+
1038
+ def test_pretty_print_distance
1039
+ m = s{ M::Sibling.new(s(:a), s(:b), 3) } # maybe s(:a) << s(:b) << 3 ?
1040
+ assert_pretty_print "sibling(q(:a), q(:b), 3)", m
1041
+ end
1042
+
1043
+ def test_sibling_satisfy_eh # TODO: possibly remove
1044
+ a_a = s{ s(:a) >> s(:a) }
1045
+ a_b = s{ s(:a) >> s(:b) }
1046
+ a_c = s{ s(:a) >> s(:c) }
1047
+ c_a = s{ s(:c) >> s(:a) }
1048
+
1049
+ assert_satisfy a_b, s(s(:a), s(:b))
1050
+ assert_satisfy a_b, s(s(:a), s(:b), s(:c))
1051
+ assert_satisfy a_c, s(s(:a), s(:b), s(:c))
1052
+ refute_satisfy c_a, s(s(:a), s(:b), s(:c))
1053
+ refute_satisfy a_a, s(s(:a))
1054
+ assert_satisfy a_a, s(s(:a), s(:b), s(:a))
1055
+ end
1056
+
1057
+ def test_sibling_search # TODO: possibly remove
1058
+ sexp = CLASS_SEXP.dup
1059
+
1060
+ assert_search 1, sexp, s{ t(:defn) >> t(:defn) }
1061
+ end
1062
+ end
1063
+
1064
+ class TestMatchCollection < SexpTestCase
1065
+ attr_accessor :sexp, :pat, :act
388
1066
 
389
1067
  def setup
390
- super
1068
+ self.sexp = s(:a, :b, :c)
1069
+ self.pat = s{ _ }
1070
+ self.act = sexp / pat
1071
+ end
1072
+
1073
+ def test_slash
1074
+ self.sexp =
1075
+ s(:class, :cake, nil,
1076
+ s(:defn, :foo, s(:args), s(:add, :a, :b)),
1077
+ s(:defn, :bar, s(:args), s(:sub, :a, :b)))
1078
+
1079
+ res = sexp / s{ s(:class, atom, _, ___) } # sexp / pat => MC
1080
+ act = res / s{ s(:defn, atom, ___) } # MC / pat => MC
1081
+
1082
+ _, _, _, defn1, defn2 = sexp
1083
+
1084
+ exp = MC.new
1085
+ exp << defn1.deep_clone
1086
+ exp << defn2.deep_clone
1087
+
1088
+ assert_equal exp, act
1089
+ end
1090
+
1091
+ def test_sanity
1092
+ act = sexp / pat
1093
+ exp = MC.new << sexp
1094
+
1095
+ assert_equal exp, act
1096
+ end
1097
+
1098
+ STR = "MatchCollection.new(s(:a, :b, :c))"
1099
+
1100
+ def test_to_s
1101
+ assert_equal STR, act.to_s
1102
+ end
1103
+
1104
+ def test_inspect
1105
+ assert_inspect STR, act
1106
+ end
1107
+
1108
+ def test_pretty_print
1109
+ assert_pretty_print STR, act
1110
+ end
1111
+ end
1112
+
1113
+ class TestSexpSearch < SexpTestCase
1114
+ attr_accessor :sexp
1115
+
1116
+ make_my_diffs_pretty!
1117
+
1118
+ def setup
1119
+ self.sexp = CLASS_SEXP.dup
1120
+ end
1121
+
1122
+ def coll *args
1123
+ exp = MC.new
1124
+
1125
+ args.each_slice 2 do |sexp, hash|
1126
+ exp << res(sexp, hash)
1127
+ end
1128
+
1129
+ exp
1130
+ end
1131
+
1132
+ def res sexp, hash
1133
+ MR.new sexp.deep_clone, hash
1134
+ end
1135
+
1136
+ def test_slash_simple
1137
+ act = sexp / s{ s(:class, atom, _, ___) }
1138
+
1139
+ exp = MC.new
1140
+ exp << sexp.deep_clone
1141
+
1142
+ assert_equal exp, act
1143
+ end
1144
+
1145
+ def test_slash_subsexp
1146
+ act = sexp / s{ s(:defn, atom, ___) }
1147
+
1148
+ exp = MC.new
1149
+ exp << s(:defn, :foo, s(:args), s(:add, :a, :b))
1150
+ exp << s(:defn, :bar, s(:args), s(:sub, :a, :b))
1151
+
1152
+ assert_equal exp, act
1153
+ end
1154
+
1155
+ def test_slash_data
1156
+ pat = s{ s(:defn, m(/^test_.+/), ___ ) }
1157
+
1158
+ _, _, (_klass, _, _, _setup, t1, t2, t3) = TestUseCase.sexp.deep_clone
1159
+ exp = [t1, t2, t3]
1160
+
1161
+ assert_equal exp, TestUseCase.sexp.deep_clone / pat
1162
+ end
1163
+
1164
+ def test_search_each_no_block
1165
+ assert_kind_of Enumerator, sexp.search_each(s{_})
1166
+ assert_equal 7, sexp.search_each(s{_}).count
1167
+ assert_equal 2, sexp.search_each(s{t(:defn)}).count
1168
+ assert_search 7, sexp, s{_}
1169
+ assert_search 2, sexp, s{t(:defn)}
1170
+
1171
+ _, _, _, defn1, defn2 = sexp
1172
+
1173
+ mc = []
1174
+ mc << defn1.deep_clone
1175
+ mc << defn2.deep_clone
1176
+
1177
+ assert_equal mc, sexp.search_each(s{t(:defn)}).map { |x| x }
1178
+ end
1179
+
1180
+ def test_searching_simple_examples # TODO: possibly remove
1181
+ assert_raises ArgumentError do
1182
+ assert_search 0, sexp, :class # non-pattern should raise
1183
+ end
1184
+
1185
+ assert_search 0, sexp, s{ s(:class) }
1186
+ assert_search 1, sexp, s{ s(:add, :a, :b) }
1187
+ assert_search 1, s(:a, s(:b, s(:c))), s{ s(:b, s(:c)) }
1188
+ assert_search 0, s(:a, s(:b, s(:c))), s{ s(:a, s(:c)) }
1189
+ assert_search 1, sexp, s{ s(:defn, :bar, _, s(:sub, :a, :b)) }
391
1190
  end
392
1191
 
393
- def test_equals
394
- util_equals @any, s()
395
- util_equals @any, s(:a)
396
- util_equals @any, s(:a, :b, s(:c))
1192
+ def test_satisfy_eh_any_capture # TODO: remove
1193
+ sexp = s(:add, :a, :b)
1194
+ assert_satisfy s{ any(s(:add, :a, :b), s(:sub, :a, :b)) }, sexp
1195
+
1196
+ assert_satisfy s{ any(s(atom, :a, :b), s(:sub, :a, :b)) }, sexp
1197
+ end
1198
+
1199
+ def test_satisfy_eh_all_capture # TODO: remove
1200
+ sexp = s(:add, :a, :b)
1201
+ assert_satisfy s{ all(s(_, :a, :b), s(atom, :a, :b)) }, sexp
1202
+
1203
+ assert_satisfy s{ all(s(_, :a, :b), s(atom, :a, :b)) }, sexp
1204
+
1205
+ assert_search 1, sexp, s{ all(s(_, :a, :b), s(atom, :a, :b)) }
1206
+ end
1207
+ end
1208
+
1209
+ class TestSexpPath < Minitest::Test
1210
+ def test_global_s_block
1211
+ sexp = s(:a, :b, :c) # s called outside block
1212
+
1213
+ assert_instance_of Sexp, s{ sexp.deep_clone }
1214
+ assert_instance_of Sexp::Matcher, s{ s(:a, :b, :c) }
1215
+ assert_instance_of Sexp::Matcher, s{ s(:a, atom, :c) }
1216
+ end
1217
+ end
1218
+
1219
+ class TestSexpReplaceSexp < SexpTestCase
1220
+ def test_replace_sexp
1221
+ sexp = s(:a, s(:b), :c)
1222
+ actual = sexp.replace_sexp(s{ s(:b) }) { :b }
1223
+
1224
+ assert_equal s(:a, :b, :c), actual
1225
+ end
1226
+
1227
+ def test_replace_sexp_root
1228
+ sexp = s(:a, s(:b), :c)
1229
+ actual = sexp.replace_sexp(s{ t(:a) }) { s(:new) }
1230
+
1231
+ assert_equal s(:new), actual
1232
+ end
1233
+
1234
+ def test_replace_sexp_yields_match_result
1235
+ sexp = s(:a, s(:b), :c)
1236
+
1237
+ exp = sexp.deep_clone
1238
+
1239
+ sexp.replace_sexp(s{ t(:a) }) { |x|
1240
+ assert_equal exp, x
1241
+ }
397
1242
  end
398
1243
 
399
- def test_equals3
400
- util_equals3 @any, s()
401
- util_equals3 @any, s(:a)
402
- util_equals3 @any, s(:a, :b, s(:c))
1244
+ def test_replace_sexp_non_matcher
1245
+ e = assert_raises ArgumentError do
1246
+ s(:a, s(:b), :c).replace_sexp(42) { :b }
1247
+ end
1248
+
1249
+ assert_equal "Needs a pattern", e.message
403
1250
  end
404
1251
 
1252
+ def test_search_each_yields_match_result
1253
+ sexp = s(:a, s(:b), :c)
1254
+
1255
+ exp = sexp.deep_clone
1256
+
1257
+ sexp.search_each(s{ t(:a) }) { |x|
1258
+ assert_equal exp, x
1259
+ }
1260
+ end
1261
+
1262
+ def test_search_each_no_pattern
1263
+ e = assert_raises ArgumentError do
1264
+ s(:a, s(:b), :c).search_each(42) { :b }
1265
+ end
1266
+
1267
+ assert_equal "Needs a pattern", e.message
1268
+ end
405
1269
  end
406
1270
 
1271
+ # Here's a crazy idea, these tests actually use sexp_path on some "real"
1272
+ # code to see if it can satisfy my requirements.
1273
+ #
1274
+ # These tests are two fold:
1275
+ # 1. Make sure it works
1276
+ # 2. Make sure it's not painful to use
1277
+
1278
+ class TestUseCase < SexpTestCase
1279
+ @@sexp = eval File.read(__FILE__).split(/^__END__/).last
1280
+
1281
+ def self.sexp
1282
+ @@sexp
1283
+ end
1284
+
1285
+ def setup
1286
+ @sexp = @@sexp.deep_clone
1287
+ end
1288
+
1289
+ def test_finding_methods
1290
+ methods = @sexp / s{ t(:defn) }
1291
+ assert_equal 5, methods.length
1292
+ end
1293
+
1294
+ def test_finding_classes_and_methods
1295
+ res = @sexp / s{ s(:class, atom, ___ ) }
1296
+
1297
+ _klass, name, * = res.first
1298
+
1299
+ assert_equal 1, res.length
1300
+ assert_equal :ExampleTest, name
1301
+
1302
+ methods = res / s{ t(:defn) }
1303
+ assert_equal 5, methods.length
1304
+ end
1305
+
1306
+ def test_finding_empty_test_methods
1307
+ empty_test = s{ s(:defn, m(/^test_.+/), s(:args), s(:nil)) }
1308
+ res = @sexp / empty_test
1309
+
1310
+ _, _, (_klass, _, _, _setup, _t1, t2, _t3) = TestUseCase.sexp.deep_clone
1311
+
1312
+ assert_equal [t2], res
1313
+ end
1314
+
1315
+ def test_search_each_finding_duplicate_test_names
1316
+ pat = s{ s(:defn, m(/^test_.+/), ___ ) }
1317
+ counts = Hash.new { |h, k| h[k] = 0 }
1318
+
1319
+ @sexp.search_each pat do |x|
1320
+ _, name, * = x
1321
+ counts[name] += 1
1322
+ end
1323
+
1324
+ assert_equal 1, counts[:test_b], "Should have seen test_b once"
1325
+ assert_equal 2, counts[:test_a], "Should have caught test_a being repeated"
1326
+ end
1327
+
1328
+ def test_finding_duplicate_test_names_via_res
1329
+ pat = s{ s(:defn, m(/^test_.+/), ___ ) }
1330
+ res = @sexp / pat
1331
+ counts = Hash.new { |h, k| h[k] = 0 }
1332
+
1333
+ _, _, (_klass, _, _, _setup, t1, t2, t3) = TestUseCase.sexp.deep_clone
1334
+ exp = [t1, t2, t3]
1335
+
1336
+ assert_equal exp, res
1337
+
1338
+ res.each do |m|
1339
+ _, name, * = m
1340
+ counts[name] += 1
1341
+ end
1342
+
1343
+ assert_equal 1, counts[:test_b], "Should have seen test_b once"
1344
+ assert_equal 2, counts[:test_a], "Should have caught test_a being repeated"
1345
+ end
1346
+
1347
+ def test_rewriting_colon2s
1348
+ colon2 = s{ s(:colon2, s(:const, atom), atom) }
1349
+ expected = s{ s(:const, "Minitest::Test") }
1350
+
1351
+ new_sexp = @sexp.replace_sexp(colon2) { |r|
1352
+ (_, (_, a), b) = r
1353
+ s(:const, "%s::%s" % [a, b])
1354
+ }
1355
+
1356
+ assert_search 1, new_sexp, expected
1357
+ assert_search 0, @sexp, expected
1358
+ end
1359
+ end
1360
+
1361
+ ##
1362
+ # NOTE: this entire class is now redundant, but it illustrates usage
1363
+ # and edge cases well.
1364
+
1365
+ class TestSexpMatchers < SexpTestCase
1366
+ CLASS_LIT = s(:class, :X, nil,
1367
+ s(:lasgn, :x, s(:lit, 42)),
1368
+ s(:cdecl, :Y,
1369
+ s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2))))
1370
+
1371
+ SEXP = s(:class, :X, nil, s(:defn, :x, s(:args)))
1372
+
1373
+ def test_match_subset
1374
+ assert_match s{ child(s(:a)) }, s(:blah, s(:blah, s(:a)))
1375
+ assert_match s{ child(s(:a)) }, s(:a)
1376
+ end
1377
+
1378
+ def test_match_simple
1379
+ assert_match s{ s(:lit, _) }, s(:lit, 42)
1380
+ end
1381
+
1382
+ def test_match_mismatch_type
1383
+ refute_match s{ s(:xxx, 42) }, s(:lit, 42)
1384
+ end
1385
+
1386
+ def test_match_mismatch_data
1387
+ refute_match s{ s(:lit, 24) }, s(:lit, 42)
1388
+ end
1389
+
1390
+ def test_match_mismatch_length_shorter
1391
+ refute_match s{ s(:a, :b) }, s(:a, :b, :c)
1392
+ end
1393
+
1394
+ def test_match_mismatch_length_longer
1395
+ refute_match s{ s(:a, :b, :c) }, s(:a, :b)
1396
+ end
1397
+
1398
+ def test_match_wild
1399
+ assert_match s{ s(:class, _, _, _) }, SEXP
1400
+ end
1401
+
1402
+ def test_match_rest_same_length
1403
+ assert_match s{ s(:class, _, _, ___) }, SEXP
1404
+ end
1405
+
1406
+ def test_match_rest_diff_length
1407
+ skip_if_strict
1408
+
1409
+ assert_match s{ s(:class, ___) }, SEXP
1410
+ end
1411
+
1412
+ def test_match_reversed
1413
+ assert_match SEXP, s{ s(:class, _, _, ___) }
1414
+ end
1415
+
1416
+ def assert_match_case pat, data
1417
+ case data
1418
+ when pat then
1419
+ assert true
1420
+ else
1421
+ flunk "Expected %p to match %p" % [pat, data]
1422
+ end
1423
+ end
1424
+
1425
+ def test_match_case
1426
+ assert_match_case s{ s(:class, _, _, ___) }, SEXP
1427
+ end
1428
+
1429
+ # NOTE: eqt is =~ (equal-tilde)
1430
+
1431
+ # cmt = create_match_test
1432
+ def self.cmt e1, e2, e3, e4, lit, pat
1433
+ Class.new SexpTestCase do
1434
+ attr_accessor :lit, :pat
1435
+
1436
+ define_method :setup do
1437
+ self.lit = lit
1438
+ self.pat = pat
1439
+ end
1440
+
1441
+ define_method :test_match_lit_eqt_pat do
1442
+ skip_if_strict
1443
+
1444
+ if e1 then
1445
+ assert_match lit, pat
1446
+ else
1447
+ refute_match lit, pat
1448
+ end
1449
+ end
1450
+
1451
+ define_method :test_match_pat_eqt_lit do
1452
+ skip_if_strict
1453
+
1454
+ if e2 then
1455
+ assert_match pat, lit
1456
+ else
1457
+ refute_match pat, lit
1458
+ end
1459
+ end
1460
+
1461
+ define_method :test_match_lit_eq3_pat do
1462
+ if e3 then
1463
+ assert_equal3 lit, pat
1464
+ else
1465
+ refute_equal3 lit, pat
1466
+ end
1467
+ end
1468
+
1469
+ define_method :test_match_pat_eq3_lit do
1470
+ if e4 then
1471
+ assert_equal3 pat, lit
1472
+ else
1473
+ refute_equal3 pat, lit
1474
+ end
1475
+ end
1476
+ end
1477
+ end
1478
+
1479
+ l_a = s(:a)
1480
+ l_abc = s(:a, s(:b, s(:c)))
1481
+ l_cls = s(:class, :X, nil,
1482
+ s(:something_in_between),
1483
+ s(:cdecl, :Y, s(:hash, s(:lit, :a), s(:lit, 1))))
1484
+ p_cls1 = s{ s(:class, ___) & include(s(:cdecl, _, s(:hash, ___))) }
1485
+ p_cls2 = s{ s(:class, _, _, s(:cdecl, _, s(:hash, ___))) }
1486
+
1487
+ x, o = true, false
1488
+ TestMatcherDirectMatch = cmt x, x, o, x, l_a, s{ s(:a) }
1489
+ TestMatcherSubtree = cmt x, x, o, x, l_abc, s{ s(:c) }
1490
+ TestMatcherSubtreeType = cmt x, x, o, x, l_abc, s{ t(:c) }
1491
+ TestMatcherDisparateSubtree = cmt x, x, o, x, l_cls, p_cls1
1492
+ TestMatcherDisparateSubtree2 = cmt o, o, o, o, l_cls, p_cls2 # TODO: make pass
1493
+ end
1494
+
1495
+ class TestSexpMatcherParser < Minitest::Test
1496
+ def assert_parse exp, str
1497
+ act = Sexp::Matcher.parse str
1498
+
1499
+ if exp.nil? then
1500
+ assert_nil act
1501
+ else
1502
+ assert_equal exp, act
1503
+ end
1504
+ end
1505
+
1506
+ def self.test_parse name, exp_lambda, str
1507
+ define_method "test_parse_#{name}" do
1508
+ exp = exp_lambda && exp_lambda.call
1509
+ assert_parse exp, str
1510
+ end
1511
+ end
1512
+
1513
+ def self.test_bad_parse name, str
1514
+ define_method "test_parse_bad_#{name}" do
1515
+ assert_raises SyntaxError do
1516
+ assert_parse :whatever, str
1517
+ end
1518
+ end
1519
+ end
1520
+
1521
+ def self.delay &b
1522
+ lambda { s(&b) }
1523
+ end
1524
+
1525
+ test_parse "nothing", nil, ""
1526
+ test_parse "nil", delay{ nil }, "nil"
1527
+ test_parse "empty", delay{ s() }, "()"
1528
+ test_parse "simple", delay{ s(:a) }, "(a)"
1529
+ test_parse "number", delay{ s(:a, 42) }, "(a 42)"
1530
+ test_parse "string", delay{ s(:a, "s") }, "(a \"s\")"
1531
+ test_parse "compound", delay{ s(:b) }, "(a) (b)"
1532
+ test_parse "complex", delay{ s(:a, _, s(:b, :cde), ___) }, "(a _ (b cde) ___)"
1533
+ test_parse "type", delay{ s(:a, t(:b)) }, "(a [t b])"
1534
+ test_parse "match", delay{ s(:a, m(/b/)) }, "(a [m /b/])"
1535
+ test_parse "not_atom", delay{ s(:atom) }, "(atom)"
1536
+ test_parse "atom", delay{ atom }, "[atom]"
1537
+
1538
+ test_bad_parse "open_sexp", "(a"
1539
+ test_bad_parse "closed_sexp", "a)"
1540
+ test_bad_parse "open_cmd", "[a"
1541
+ test_bad_parse "closed_cmd", "a]"
1542
+ end # class TestSexpMatcherParser
1543
+
407
1544
  class BenchSexp < Minitest::Benchmark
408
1545
  def run
409
1546
  GC.disable
@@ -430,3 +1567,64 @@ class BenchSexp < Minitest::Benchmark
430
1567
  end
431
1568
  end
432
1569
  end if ENV["BENCH"]
1570
+
1571
+ # class ExampleTest < Minitest::Test
1572
+ # def setup
1573
+ # 1 + 2
1574
+ # end
1575
+ #
1576
+ # def test_a
1577
+ # assert_equal 1+2, 4
1578
+ # end
1579
+ #
1580
+ # def test_b
1581
+ # # assert 1+1
1582
+ # end
1583
+ #
1584
+ # def test_a
1585
+ # assert_equal 1+2, 3
1586
+ # end
1587
+ #
1588
+ # private
1589
+ #
1590
+ # def helper_method apples, oranges, cakes = nil
1591
+ # [apples, oranges, cakes].compact.map { |food| food.to_s.upcase }
1592
+ # end
1593
+ # end
1594
+
1595
+ __END__
1596
+ s(:block,
1597
+ s(:call, nil, :require, s(:str, "minitest/autorun")),
1598
+ s(:class,
1599
+ :ExampleTest,
1600
+ s(:colon2, s(:const, :Minitest), :Test),
1601
+ s(:defn, :setup, s(:args), s(:call, s(:lit, 1), :+, s(:lit, 2))),
1602
+ s(:defn,
1603
+ :test_a,
1604
+ s(:args),
1605
+ s(:call,
1606
+ nil,
1607
+ :assert_equal,
1608
+ s(:call, s(:lit, 1), :+, s(:lit, 2)),
1609
+ s(:lit, 4))),
1610
+ s(:defn, :test_b, s(:args), s(:nil)),
1611
+ s(:defn,
1612
+ :test_a,
1613
+ s(:args),
1614
+ s(:call,
1615
+ nil,
1616
+ :assert_equal,
1617
+ s(:call, s(:lit, 1), :+, s(:lit, 2)),
1618
+ s(:lit, 3))),
1619
+ s(:call, nil, :private),
1620
+ s(:defn,
1621
+ :helper_method,
1622
+ s(:args, :apples, :oranges, s(:lasgn, :cakes, s(:nil))),
1623
+ s(:iter,
1624
+ s(:call,
1625
+ s(:call,
1626
+ s(:array, s(:lvar, :apples), s(:lvar, :oranges), s(:lvar, :cakes)),
1627
+ :compact),
1628
+ :map),
1629
+ s(:args, :food),
1630
+ s(:call, s(:call, s(:lvar, :food), :to_s), :upcase)))))