re2 2.9.0 → 2.27.0

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.
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe RE2::Scanner do
4
4
  describe "#regexp" do
@@ -8,16 +8,102 @@ RSpec.describe RE2::Scanner do
8
8
 
9
9
  expect(scanner.regexp).to equal(re)
10
10
  end
11
+
12
+ it "raises an error when called on an uninitialized object" do
13
+ expect { described_class.allocate.regexp }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
14
+ end
15
+ end
16
+
17
+ describe "#dup" do
18
+ it "returns a usable copy of the scanner at the same position" do
19
+ scanner = RE2::Regexp.new('(\w+)').scan("foo bar baz")
20
+ scanner.scan
21
+
22
+ copy = scanner.dup
23
+
24
+ expect(copy.scan).to eq(["bar"])
25
+ end
26
+
27
+ it "creates an independent copy" do
28
+ scanner = RE2::Regexp.new('(\w+)').scan("foo bar baz")
29
+ scanner.scan
30
+
31
+ copy = scanner.dup
32
+ copy.scan
33
+
34
+ expect(scanner.scan).to eq(["bar"])
35
+ end
36
+
37
+ it "copies the EOF state of the scanner" do
38
+ scanner = RE2::Regexp.new('(\w+)').scan("foo")
39
+ scanner.scan
40
+ scanner.scan
41
+
42
+ copy = scanner.dup
43
+
44
+ expect(copy).to be_eof
45
+ end
46
+
47
+ it "raises an error when called on an uninitialized object" do
48
+ expect { described_class.allocate.dup }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
49
+ end
50
+ end
51
+
52
+ describe "#clone" do
53
+ it "returns a usable copy of the scanner at the same position" do
54
+ scanner = RE2::Regexp.new('(\w+)').scan("foo bar baz")
55
+ scanner.scan
56
+
57
+ copy = scanner.clone
58
+
59
+ expect(copy.scan).to eq(["bar"])
60
+ end
11
61
  end
12
62
 
13
63
  describe "#string" do
14
- it "returns the original text for the scanner" do
64
+ it "returns the text for the scanner" do
65
+ re = RE2::Regexp.new('(\w+)')
66
+ text = "It is a truth"
67
+ scanner = re.scan(text)
68
+
69
+ expect(scanner.string).to eq("It is a truth")
70
+ end
71
+
72
+ it "returns a frozen string" do
73
+ re = RE2::Regexp.new('(\w+)')
74
+ text = "It is a truth"
75
+ scanner = re.scan(text)
76
+
77
+ expect(scanner.string).to be_frozen
78
+ end
79
+
80
+ it "freezes unfrozen strings" do
81
+ re = RE2::Regexp.new('(\w+)')
82
+ text = +"It is a truth"
83
+ scanner = re.scan(text)
84
+
85
+ expect(scanner.string).to be_frozen
86
+ end
87
+
88
+ it "copies unfrozen strings" do
89
+ re = RE2::Regexp.new('(\w+)')
90
+ text = +"It is a truth"
91
+ scanner = re.scan(text)
92
+
93
+ expect(scanner.string).to_not equal(text)
94
+ end
95
+
96
+ it "does not copy the string if it was already frozen" do
15
97
  re = RE2::Regexp.new('(\w+)')
16
98
  text = "It is a truth"
17
99
  scanner = re.scan(text)
18
100
 
19
101
  expect(scanner.string).to equal(text)
20
102
  end
103
+
104
+ it "raises an error when called on an uninitialized object" do
105
+ expect { described_class.allocate.string }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
106
+ end
21
107
  end
22
108
 
23
109
  describe "#scan" do
@@ -99,11 +185,11 @@ RSpec.describe RE2::Scanner do
99
185
  expect(scanner.scan).to be_nil
100
186
  end
101
187
 
102
- it "returns an array of nil with an empty input and capture", :aggregate_failures do
188
+ it "returns an array of empty strings with an empty input and capture", :aggregate_failures do
103
189
  r = RE2::Regexp.new("()")
104
190
  scanner = r.scan("")
105
191
 
106
- expect(scanner.scan).to eq([nil])
192
+ expect(scanner.scan).to eq([""])
107
193
  expect(scanner.scan).to be_nil
108
194
  end
109
195
 
@@ -118,25 +204,34 @@ RSpec.describe RE2::Scanner do
118
204
  expect(scanner.scan).to be_nil
119
205
  end
120
206
 
121
- it "returns an array of nil if the pattern is an empty capturing group", :aggregate_failures do
207
+ it "returns an array of empty strings if the pattern is an empty capturing group", :aggregate_failures do
122
208
  r = RE2::Regexp.new("()")
123
209
  scanner = r.scan("Foo")
124
210
 
125
- expect(scanner.scan).to eq([nil])
126
- expect(scanner.scan).to eq([nil])
127
- expect(scanner.scan).to eq([nil])
128
- expect(scanner.scan).to eq([nil])
211
+ expect(scanner.scan).to eq([""])
212
+ expect(scanner.scan).to eq([""])
213
+ expect(scanner.scan).to eq([""])
214
+ expect(scanner.scan).to eq([""])
129
215
  expect(scanner.scan).to be_nil
130
216
  end
131
217
 
132
- it "returns array of nils with multiple empty capturing groups", :aggregate_failures do
218
+ it "returns array of empty strings with multiple empty capturing groups", :aggregate_failures do
133
219
  r = RE2::Regexp.new("()()()")
134
220
  scanner = r.scan("Foo")
135
221
 
136
- expect(scanner.scan).to eq([nil, nil, nil])
137
- expect(scanner.scan).to eq([nil, nil, nil])
138
- expect(scanner.scan).to eq([nil, nil, nil])
139
- expect(scanner.scan).to eq([nil, nil, nil])
222
+ expect(scanner.scan).to eq(["", "", ""])
223
+ expect(scanner.scan).to eq(["", "", ""])
224
+ expect(scanner.scan).to eq(["", "", ""])
225
+ expect(scanner.scan).to eq(["", "", ""])
226
+ expect(scanner.scan).to be_nil
227
+ end
228
+
229
+ it "distinguishes zero-length matches from unmatched groups", :aggregate_failures do
230
+ r = RE2::Regexp.new("()(a)?")
231
+ scanner = r.scan("b")
232
+
233
+ expect(scanner.scan).to eq(["", nil])
234
+ expect(scanner.scan).to eq(["", nil])
140
235
  expect(scanner.scan).to be_nil
141
236
  end
142
237
 
@@ -144,7 +239,53 @@ RSpec.describe RE2::Scanner do
144
239
  r = RE2::Regexp.new("()€")
145
240
  scanner = r.scan("€")
146
241
 
147
- expect(scanner.scan).to eq([nil])
242
+ expect(scanner.scan).to eq([""])
243
+ expect(scanner.scan).to be_nil
244
+ end
245
+
246
+ it "advances by whole characters with zero-width matches on 2-byte UTF-8 input", :aggregate_failures do
247
+ r = RE2::Regexp.new("")
248
+ scanner = r.scan("à")
249
+
250
+ expect(scanner.scan).to eq([])
251
+ expect(scanner.scan).to eq([])
252
+ expect(scanner.scan).to be_nil
253
+ end
254
+
255
+ it "advances by whole characters with zero-width matches on 3-byte UTF-8 input", :aggregate_failures do
256
+ r = RE2::Regexp.new("")
257
+ scanner = r.scan("\u20AC")
258
+
259
+ expect(scanner.scan).to eq([])
260
+ expect(scanner.scan).to eq([])
261
+ expect(scanner.scan).to be_nil
262
+ end
263
+
264
+ it "advances by whole characters with zero-width matches on 4-byte UTF-8 input", :aggregate_failures do
265
+ r = RE2::Regexp.new("")
266
+ scanner = r.scan("\u{1F600}")
267
+
268
+ expect(scanner.scan).to eq([])
269
+ expect(scanner.scan).to eq([])
270
+ expect(scanner.scan).to be_nil
271
+ end
272
+
273
+ it "advances by single bytes with zero-width matches on Latin-1 input", :aggregate_failures do
274
+ r = RE2::Regexp.new("", utf8: false)
275
+ scanner = r.scan("\xC3\xA0")
276
+
277
+ expect(scanner.scan).to eq([])
278
+ expect(scanner.scan).to eq([])
279
+ expect(scanner.scan).to eq([])
280
+ expect(scanner.scan).to be_nil
281
+ end
282
+
283
+ it "handles truncated multi-byte sequences at the end of input", :aggregate_failures do
284
+ r = RE2::Regexp.new("")
285
+ scanner = r.scan("\xC3")
286
+
287
+ expect(scanner.scan).to eq([])
288
+ expect(scanner.scan).to eq([])
148
289
  expect(scanner.scan).to be_nil
149
290
  end
150
291
 
@@ -162,6 +303,27 @@ RSpec.describe RE2::Scanner do
162
303
  expect(scanner.scan).to eq(["world"])
163
304
  expect(scanner.scan).to be_nil
164
305
  end
306
+
307
+ it "supports GC compaction" do
308
+ r = RE2::Regexp.new('(\w+)')
309
+ scanner = r.scan("Hello world" * 2)
310
+ GC.compact
311
+
312
+ expect(scanner.scan).to eq(["Hello"])
313
+ end
314
+
315
+ it "works even if the original input is mutated" do
316
+ r = RE2::Regexp.new('(\w+)')
317
+ text = +"It is a truth universally acknowledged"
318
+ scanner = r.scan(text)
319
+ text.upcase!
320
+
321
+ expect(scanner.scan).to eq(["It"])
322
+ end
323
+
324
+ it "raises an error when called on an uninitialized object" do
325
+ expect { described_class.allocate.scan }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
326
+ end
165
327
  end
166
328
 
167
329
  it "is enumerable" do
@@ -223,6 +385,10 @@ RSpec.describe RE2::Scanner do
223
385
 
224
386
  expect(scanner).not_to be_eof
225
387
  end
388
+
389
+ it "raises an error when called on an uninitialized object" do
390
+ expect { described_class.allocate.rewind }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
391
+ end
226
392
  end
227
393
 
228
394
  describe "#eof?" do
@@ -271,5 +437,9 @@ RSpec.describe RE2::Scanner do
271
437
 
272
438
  expect(scanner).to be_eof
273
439
  end
440
+
441
+ it "raises an error when called on an uninitialized object" do
442
+ expect { described_class.allocate.eof? }.to raise_error(TypeError, /uninitialized RE2::Scanner/)
443
+ end
274
444
  end
275
445
  end
data/spec/re2/set_spec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe RE2::Set do
2
4
  describe "#initialize" do
3
5
  it "returns an instance given no args" do
@@ -49,6 +51,18 @@ RSpec.describe RE2::Set do
49
51
  end
50
52
  end
51
53
 
54
+ describe "#dup" do
55
+ it "raises a TypeError" do
56
+ expect { described_class.new.dup }.to raise_error(TypeError, /cannot copy RE2::Set/)
57
+ end
58
+ end
59
+
60
+ describe "#clone" do
61
+ it "raises a TypeError" do
62
+ expect { described_class.new.clone }.to raise_error(TypeError, /cannot copy RE2::Set/)
63
+ end
64
+ end
65
+
52
66
  describe "#add" do
53
67
  it "allows multiple patterns to be added", :aggregate_failures do
54
68
  set = RE2::Set.new
@@ -64,20 +78,18 @@ RSpec.describe RE2::Set do
64
78
  expect { set.add("???") }.to raise_error(ArgumentError, /str rejected by RE2::Set->Add\(\)/)
65
79
  end
66
80
 
67
- it "truncates error messages to 100 characters" do
81
+ it "includes the full error message" do
68
82
  set = RE2::Set.new(:unanchored, log_errors: false)
69
83
 
70
- expect { set.add("(?P<#{'o' * 200}") }.to raise_error(ArgumentError, "str rejected by RE2::Set->Add(): invalid named capture group: (?P<oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")
84
+ expect { set.add("(?P<#{'o' * 200}") }.to raise_error(ArgumentError, "str rejected by RE2::Set->Add(): invalid named capture group: (?P<oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")
71
85
  end
72
86
 
73
- it "raises an error if called after #compile" do
87
+ it "raises a FrozenError if called after #compile" do
74
88
  set = RE2::Set.new(:unanchored, log_errors: false)
75
89
  set.add("abc")
76
90
  set.compile
77
91
 
78
- silence_stderr do
79
- expect { set.add("def") }.to raise_error(ArgumentError)
80
- end
92
+ expect { set.add("def") }.to raise_error(FrozenError)
81
93
  end
82
94
 
83
95
  it "raises an error if given a pattern that can't be coerced to a String" do
@@ -91,6 +103,10 @@ RSpec.describe RE2::Set do
91
103
 
92
104
  expect(set.add(StringLike.new("abc"))).to eq(0)
93
105
  end
106
+
107
+ it "raises an error when called on an uninitialized object" do
108
+ expect { described_class.allocate.add("foo") }.to raise_error(TypeError, /uninitialized RE2::Set/)
109
+ end
94
110
  end
95
111
 
96
112
  describe "#compile" do
@@ -102,6 +118,33 @@ RSpec.describe RE2::Set do
102
118
 
103
119
  expect(set.compile).to be_truthy
104
120
  end
121
+
122
+ it "freezes the set on successful compilation" do
123
+ set = RE2::Set.new
124
+ set.add("abc")
125
+ set.compile
126
+
127
+ expect(set).to be_frozen
128
+ end
129
+
130
+ it "is not frozen before compilation" do
131
+ set = RE2::Set.new
132
+ set.add("abc")
133
+
134
+ expect(set).to_not be_frozen
135
+ end
136
+
137
+ it "cannot be re-initialized after compilation" do
138
+ set = RE2::Set.new
139
+ set.add("abc")
140
+ set.compile
141
+
142
+ expect { set.send(:initialize) }.to raise_error(FrozenError)
143
+ end
144
+
145
+ it "raises an error when called on an uninitialized object" do
146
+ expect { described_class.allocate.compile }.to raise_error(TypeError, /uninitialized RE2::Set/)
147
+ end
105
148
  end
106
149
 
107
150
  describe "#match" do
@@ -200,6 +243,80 @@ RSpec.describe RE2::Set do
200
243
 
201
244
  expect(set.match(StringLike.new("abcdef"), exception: false)).to contain_exactly(0)
202
245
  end
246
+
247
+ it "raises an error when called on an uninitialized object" do
248
+ expect { described_class.allocate.match("foo") }.to raise_error(TypeError, /uninitialized RE2::Set/)
249
+ end
250
+
251
+ it "can be run concurrently" do
252
+ set = RE2::Set.new
253
+ set.add("abc")
254
+ set.add("def")
255
+ set.add("ghi")
256
+ set.compile
257
+
258
+ threads = 10.times.map do
259
+ Thread.new { set.match("abcdefghi", exception: false) }
260
+ end
261
+
262
+ expect(threads.map(&:value)).to all(eq([0, 1, 2]))
263
+ end
264
+ end
265
+
266
+ describe "#size" do
267
+ it "returns the number of patterns added to the set", :aggregate_failures do
268
+ skip "Underlying RE2::Set has no Size method" unless RE2::Set.size?
269
+
270
+ set = RE2::Set.new
271
+
272
+ expect(set.size).to eq(0)
273
+
274
+ set.add("abc")
275
+
276
+ expect(set.size).to eq(1)
277
+
278
+ set.add("def")
279
+
280
+ expect(set.size).to eq(2)
281
+ end
282
+
283
+ it "raises an error if RE2 does not support Set::Size" do
284
+ skip "Underlying RE2::Set has a Size method" if RE2::Set.size?
285
+
286
+ set = RE2::Set.new
287
+
288
+ expect { set.size }.to raise_error(RE2::Set::UnsupportedError)
289
+ end
290
+
291
+ it "raises an error when called on an uninitialized object" do
292
+ skip "Underlying RE2::Set has no Size method" unless RE2::Set.size?
293
+
294
+ expect { described_class.allocate.size }.to raise_error(TypeError, /uninitialized RE2::Set/)
295
+ end
296
+ end
297
+
298
+ describe "#length" do
299
+ it "is an alias for size" do
300
+ skip "Underlying RE2::Set has no Size method" unless RE2::Set.size?
301
+
302
+ set = RE2::Set.new
303
+
304
+ expect(set.length).to eq(0)
305
+
306
+ set.add("abc")
307
+
308
+ expect(set.length).to eq(1)
309
+
310
+ set.add("def")
311
+
312
+ expect(set.length).to eq(2)
313
+ end
314
+
315
+ it "raises an error when called on an uninitialized object" do
316
+ skip "Underlying RE2::Set has no Size method" unless RE2::Set.size?
317
+
318
+ expect { described_class.allocate.length }.to raise_error(TypeError, /uninitialized RE2::Set/)
319
+ end
203
320
  end
204
321
 
205
322
  def silence_stderr
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "re2/string"
2
4
 
3
5
  class String