summaryrefslogtreecommitdiff
path: root/spec/ruby/library/stringscanner
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/library/stringscanner')
-rw-r--r--spec/ruby/library/stringscanner/append_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/beginning_of_line_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/bol_spec.rb7
-rw-r--r--spec/ruby/library/stringscanner/captures_spec.rb36
-rw-r--r--spec/ruby/library/stringscanner/charpos_spec.rb18
-rw-r--r--spec/ruby/library/stringscanner/check_spec.rb93
-rw-r--r--spec/ruby/library/stringscanner/check_until_spec.rb129
-rw-r--r--spec/ruby/library/stringscanner/concat_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/dup_spec.rb39
-rw-r--r--spec/ruby/library/stringscanner/element_reference_spec.rb67
-rw-r--r--spec/ruby/library/stringscanner/eos_spec.rb20
-rw-r--r--spec/ruby/library/stringscanner/exist_spec.rb119
-rw-r--r--spec/ruby/library/stringscanner/fixed_anchor_spec.rb17
-rw-r--r--spec/ruby/library/stringscanner/get_byte_spec.rb84
-rw-r--r--spec/ruby/library/stringscanner/getch_spec.rb89
-rw-r--r--spec/ruby/library/stringscanner/initialize_spec.rb32
-rw-r--r--spec/ruby/library/stringscanner/inspect_spec.rb20
-rw-r--r--spec/ruby/library/stringscanner/match_spec.rb51
-rw-r--r--spec/ruby/library/stringscanner/matched_size_spec.rb24
-rw-r--r--spec/ruby/library/stringscanner/matched_spec.rb41
-rw-r--r--spec/ruby/library/stringscanner/must_C_version_spec.rb8
-rw-r--r--spec/ruby/library/stringscanner/named_captures_spec.rb30
-rw-r--r--spec/ruby/library/stringscanner/peek_byte_spec.rb35
-rw-r--r--spec/ruby/library/stringscanner/peek_spec.rb42
-rw-r--r--spec/ruby/library/stringscanner/pointer_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/pos_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/post_match_spec.rb28
-rw-r--r--spec/ruby/library/stringscanner/pre_match_spec.rb41
-rw-r--r--spec/ruby/library/stringscanner/reset_spec.rb15
-rw-r--r--spec/ruby/library/stringscanner/rest_size_spec.rb21
-rw-r--r--spec/ruby/library/stringscanner/rest_spec.rb48
-rw-r--r--spec/ruby/library/stringscanner/scan_byte_spec.rb98
-rw-r--r--spec/ruby/library/stringscanner/scan_full_spec.rb44
-rw-r--r--spec/ruby/library/stringscanner/scan_integer_spec.rb157
-rw-r--r--spec/ruby/library/stringscanner/scan_spec.rb101
-rw-r--r--spec/ruby/library/stringscanner/scan_until_spec.rb135
-rw-r--r--spec/ruby/library/stringscanner/search_full_spec.rb133
-rw-r--r--spec/ruby/library/stringscanner/shared/bol.rb25
-rw-r--r--spec/ruby/library/stringscanner/shared/concat.rb30
-rw-r--r--spec/ruby/library/stringscanner/shared/extract_range.rb11
-rw-r--r--spec/ruby/library/stringscanner/shared/extract_range_matched.rb13
-rw-r--r--spec/ruby/library/stringscanner/shared/pos.rb59
-rw-r--r--spec/ruby/library/stringscanner/size_spec.rb17
-rw-r--r--spec/ruby/library/stringscanner/skip_spec.rb32
-rw-r--r--spec/ruby/library/stringscanner/skip_until_spec.rb132
-rw-r--r--spec/ruby/library/stringscanner/string_spec.rb40
-rw-r--r--spec/ruby/library/stringscanner/terminate_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/unscan_spec.rb28
-rw-r--r--spec/ruby/library/stringscanner/values_at_spec.rb68
49 files changed, 2339 insertions, 0 deletions
diff --git a/spec/ruby/library/stringscanner/append_spec.rb b/spec/ruby/library/stringscanner/append_spec.rb
new file mode 100644
index 0000000000..fef5dcf2bd
--- /dev/null
+++ b/spec/ruby/library/stringscanner/append_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/concat'
+require 'strscan'
+
+describe "StringScanner#<<" do
+ it_behaves_like :strscan_concat, :<<
+end
+
+describe "StringScanner#<< when passed an Integer" do
+ it_behaves_like :strscan_concat_fixnum, :<<
+end
diff --git a/spec/ruby/library/stringscanner/beginning_of_line_spec.rb b/spec/ruby/library/stringscanner/beginning_of_line_spec.rb
new file mode 100644
index 0000000000..3f6f0da75f
--- /dev/null
+++ b/spec/ruby/library/stringscanner/beginning_of_line_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/bol'
+require 'strscan'
+
+describe "StringScanner#beginning_of_line?" do
+ it_behaves_like :strscan_bol, :beginning_of_line?
+end
diff --git a/spec/ruby/library/stringscanner/bol_spec.rb b/spec/ruby/library/stringscanner/bol_spec.rb
new file mode 100644
index 0000000000..d31766e0e2
--- /dev/null
+++ b/spec/ruby/library/stringscanner/bol_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+require_relative 'shared/bol'
+require 'strscan'
+
+describe "StringScanner#bol?" do
+ it_behaves_like :strscan_bol, :bol?
+end
diff --git a/spec/ruby/library/stringscanner/captures_spec.rb b/spec/ruby/library/stringscanner/captures_spec.rb
new file mode 100644
index 0000000000..bdfb0e0cc5
--- /dev/null
+++ b/spec/ruby/library/stringscanner/captures_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#captures" do
+ before do
+ @s = StringScanner.new('Fri Dec 12 1975 14:39')
+ end
+
+ it "returns the array of captured values of the most recent matching" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+ @s.captures.should == ["Fri", "Dec", "12"]
+ end
+
+ it "returns nil if the last match fails" do
+ @s.scan(/nope/)
+ @s.captures.should == nil
+ end
+
+ it "returns nil if there is no any match done" do
+ @s.captures.should == nil
+ end
+
+ version_is StringScanner::Version, ""..."3.0.8" do # ruby_version_is ""..."3.3.3"
+ it "returns '' for an optional capturing group if it doesn't match" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\s+)?/)
+ @s.captures.should == ["Fri", "Dec", ""]
+ end
+ end
+
+ version_is StringScanner::Version, "3.0.8" do # ruby_version_is "3.3.3"
+ it "returns nil for an optional capturing group if it doesn't match" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\s+)?/)
+ @s.captures.should == ["Fri", "Dec", nil]
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/charpos_spec.rb b/spec/ruby/library/stringscanner/charpos_spec.rb
new file mode 100644
index 0000000000..9aa5b00dd9
--- /dev/null
+++ b/spec/ruby/library/stringscanner/charpos_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#charpos" do
+ it "returns character index corresponding to the current position" do
+ s = StringScanner.new("abc")
+
+ s.scan_until(/b/)
+ s.charpos.should == 2
+ end
+
+ it "is multi-byte character sensitive" do
+ s = StringScanner.new("abcädeföghi")
+
+ s.scan_until(/ö/)
+ s.charpos.should == 8
+ end
+end
diff --git a/spec/ruby/library/stringscanner/check_spec.rb b/spec/ruby/library/stringscanner/check_spec.rb
new file mode 100644
index 0000000000..5e855e154a
--- /dev/null
+++ b/spec/ruby/library/stringscanner/check_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#check" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the value that scan would return, without advancing the scan pointer" do
+ @s.check(/This/).should == "This"
+ @s.matched.should == "This"
+ @s.pos.should == 0
+ @s.check(/is/).should == nil
+ @s.matched.should == nil
+ end
+
+ it "treats String as the pattern itself" do
+ @s.check("This").should == "This"
+ @s.matched.should == "This"
+ @s.pos.should == 0
+ @s.check(/is/).should == nil
+ @s.matched.should == nil
+ end
+
+ describe "#[] successive call with a capture group name" do
+ context "when #check was called with a Regexp pattern" do
+ it "returns matched substring when matching succeeded" do
+ @s.check(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.check(/(?<a>2008)/)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+
+ context "when #check was called with a String pattern" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil when matching succeeded" do
+ @s.check("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "raises IndexError when matching succeeded" do
+ @s.check("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns nil when matching failed" do
+ @s.check("2008")
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+
+ it "returns a matching substring when given Integer index" do
+ @s.check("This")
+ @s[0].should == "This"
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.check("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.check("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/check_until_spec.rb b/spec/ruby/library/stringscanner/check_until_spec.rb
new file mode 100644
index 0000000000..582da66b37
--- /dev/null
+++ b/spec/ruby/library/stringscanner/check_until_spec.rb
@@ -0,0 +1,129 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#check_until" do
+ before do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the same value of #scan_until, but don't advances the scan pointer" do
+ @s.check_until(/a/).should == "This is a"
+ @s.pos.should == 0
+ @s.check_until(/test/).should == "This is a test"
+ end
+
+ it "sets the last match result" do
+ @s.check_until(/a/)
+
+ @s.pre_match.should == "This is "
+ @s.matched.should == "a"
+ @s.post_match.should == " test"
+ end
+
+ version_is StringScanner::Version, ""..."3.1.1" do # ruby_version_is ""..."3.4"
+ it "raises TypeError if given a String" do
+ -> {
+ @s.check_until('T')
+ }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ it "searches a substring in the rest part of a string if given a String" do
+ @s.check_until("a").should == "This is a"
+ @s.pos.should == 0
+ end
+
+ # https://github.com/ruby/strscan/issues/131
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.1"
+ it "sets the last match result if given a String" do
+ @s.check_until("a")
+
+ @s.pre_match.should == ""
+ @s.matched.should == "This is a"
+ @s.post_match.should == " test"
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "sets the last match result if given a String" do
+ @s.check_until("a")
+
+ @s.pre_match.should == "This is "
+ @s.matched.should == "a"
+ @s.post_match.should == " test"
+ end
+ end
+ end
+
+ describe "#[] successive call with a capture group name" do
+ context "when #check_until was called with a Regexp pattern" do
+ it "returns matched substring when matching succeeded" do
+ @s.check_until(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.check_until(/(?<a>2008)/)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ context "when #check_until was called with a String pattern" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil when matching succeeded" do
+ @s.check_until("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "raises IndexError when matching succeeded" do
+ @s.check_until("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns nil when matching failed" do
+ @s.check_until("2008")
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+
+ it "returns a matching substring when given Integer index" do
+ @s.check_until("This")
+ @s[0].should == "This"
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.check_until("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.check_until("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/concat_spec.rb b/spec/ruby/library/stringscanner/concat_spec.rb
new file mode 100644
index 0000000000..4f790e2505
--- /dev/null
+++ b/spec/ruby/library/stringscanner/concat_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/concat'
+require 'strscan'
+
+describe "StringScanner#concat" do
+ it_behaves_like :strscan_concat, :concat
+end
+
+describe "StringScanner#concat when passed an Integer" do
+ it_behaves_like :strscan_concat_fixnum, :concat
+end
diff --git a/spec/ruby/library/stringscanner/dup_spec.rb b/spec/ruby/library/stringscanner/dup_spec.rb
new file mode 100644
index 0000000000..0fc52a1477
--- /dev/null
+++ b/spec/ruby/library/stringscanner/dup_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#dup" do
+ before :each do
+ @string = "this is a test"
+ @orig_s = StringScanner.new(@string)
+ end
+
+ it "copies the passed StringScanner's content to self" do
+ s = @orig_s.dup
+ s.string.should == @string
+ end
+
+ it "copies the passed StringScanner's position to self" do
+ @orig_s.pos = 5
+ s = @orig_s.dup
+ s.pos.should eql(5)
+ end
+
+ it "copies previous match state" do
+ @orig_s.scan(/\w+/)
+ @orig_s.scan(/\s/)
+
+ @orig_s.pre_match.should == "this"
+
+ s = @orig_s.dup
+ s.pre_match.should == "this"
+
+ s.unscan
+ s.scan(/\s/).should == " "
+ end
+
+ it "copies the passed StringScanner scan pointer to self" do
+ @orig_s.terminate
+ s = @orig_s.dup
+ s.eos?.should be_true
+ end
+end
diff --git a/spec/ruby/library/stringscanner/element_reference_spec.rb b/spec/ruby/library/stringscanner/element_reference_spec.rb
new file mode 100644
index 0000000000..91b6d86dc7
--- /dev/null
+++ b/spec/ruby/library/stringscanner/element_reference_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#[]" do
+ before :each do
+ @s = StringScanner.new("Fri Jun 13 2008 22:43")
+ end
+
+ it "returns nil if there is no current match" do
+ @s[0].should be_nil
+ @s[:wday].should be_nil
+ end
+
+ it "returns the n-th subgroup in the most recent match" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ @s[0].should == "Fri Jun 13 "
+ @s[1].should == "Fri"
+ @s[2].should == "Jun"
+ @s[3].should == "13"
+ @s[-3].should == "Fri"
+ @s[-2].should == "Jun"
+ @s[-1].should == "13"
+ end
+
+ it "returns nil if index is outside of self" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ @s[5].should == nil
+ @s[-5].should == nil
+ end
+
+ it "calls to_int on the given index" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ @s[0.5].should == "Fri Jun 13 "
+ end
+
+ it "raises a TypeError if the given index is nil" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ -> { @s[nil]}.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when a Range is as argument" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ -> { @s[0..2]}.should raise_error(TypeError)
+ end
+
+ it "raises a IndexError when there's no any named capture group in the regexp" do
+ @s.scan(/(\w+) (\w+) (\d+) /)
+ -> { @s["wday"]}.should raise_error(IndexError)
+ -> { @s[:wday]}.should raise_error(IndexError)
+ end
+
+ it "raises a IndexError when given a not existing capture group name" do
+ @s.scan(/(?<a>\w+) (?<b>\w+) (?<c>\d+) /)
+ -> { @s["wday"]}.should raise_error(IndexError)
+ -> { @s[:wday]}.should raise_error(IndexError)
+ end
+
+ it "returns named capture" do
+ @s.scan(/(?<wday>\w+) (?<month>\w+) (?<day>\d+) /)
+ @s["wday"].should == "Fri"
+ @s["month"].should == "Jun"
+ @s["day"].should == "13"
+ @s[:wday].should == "Fri"
+ @s[:month].should == "Jun"
+ @s[:day].should == "13"
+ end
+end
diff --git a/spec/ruby/library/stringscanner/eos_spec.rb b/spec/ruby/library/stringscanner/eos_spec.rb
new file mode 100644
index 0000000000..03c2804e5b
--- /dev/null
+++ b/spec/ruby/library/stringscanner/eos_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#eos?" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns true if the scan pointer is at the end of the string" do
+ @s.terminate
+ @s.should.eos?
+
+ s = StringScanner.new('')
+ s.should.eos?
+ end
+
+ it "returns false if the scan pointer is not at the end of the string" do
+ @s.should_not.eos?
+ end
+end
diff --git a/spec/ruby/library/stringscanner/exist_spec.rb b/spec/ruby/library/stringscanner/exist_spec.rb
new file mode 100644
index 0000000000..a408fd0b8d
--- /dev/null
+++ b/spec/ruby/library/stringscanner/exist_spec.rb
@@ -0,0 +1,119 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#exist?" do
+ before do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns distance in bytes between the current position and the end of the matched substring" do
+ @s.exist?(/s/).should == 4
+ @s.scan(/This is/)
+ @s.exist?(/s/).should == 6
+ end
+
+ it "returns 0 if the pattern is empty" do
+ @s.exist?(//).should == 0
+ end
+
+ it "returns nil if the pattern isn't found in the string" do
+ @s.exist?(/S/).should == nil
+ @s.scan(/This is/)
+ @s.exist?(/i/).should == nil
+ end
+
+ it "does not modify the current position" do
+ @s.pos.should == 0
+ @s.exist?(/s/).should == 4
+ @s.pos.should == 0
+ end
+
+ version_is StringScanner::Version, ""..."3.1.1" do # ruby_version_is ""..."3.4"
+ it "raises TypeError if given a String" do
+ -> {
+ @s.exist?('T')
+ }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ it "searches a substring in the rest part of a string if given a String" do
+ @s.exist?('a').should == 9
+ end
+
+ it "returns nil if the pattern isn't found in the string" do
+ @s.exist?("S").should == nil
+ end
+ end
+
+ describe "#[] successive call with a capture group name" do
+ context "when #exist? was called with a Regexp pattern" do
+ it "returns matched substring when matching succeeded" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.exist?(/(?<a>2008)/)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ context "when #exist? was called with a String pattern" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil when matching succeeded" do
+ @s.exist?("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "raises IndexError when matching succeeded" do
+ @s.exist?("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns nil when matching failed" do
+ @s.exist?("2008")
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+
+ it "returns a matching substring when given Integer index" do
+ @s.exist?("This")
+ @s[0].should == "This"
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.exist?("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.exist?("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/fixed_anchor_spec.rb b/spec/ruby/library/stringscanner/fixed_anchor_spec.rb
new file mode 100644
index 0000000000..ce0b714fa8
--- /dev/null
+++ b/spec/ruby/library/stringscanner/fixed_anchor_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#fixed_anchor?" do
+ it "returns whether the fixed-anchor property is set" do
+ s = StringScanner.new("foo", fixed_anchor: true)
+ s.should.fixed_anchor?
+
+ s = StringScanner.new("foo", fixed_anchor: false)
+ s.should_not.fixed_anchor?
+ end
+
+ it "is set to false by default" do
+ s = StringScanner.new("foo")
+ s.should_not.fixed_anchor?
+ end
+end
diff --git a/spec/ruby/library/stringscanner/get_byte_spec.rb b/spec/ruby/library/stringscanner/get_byte_spec.rb
new file mode 100644
index 0000000000..144859abc9
--- /dev/null
+++ b/spec/ruby/library/stringscanner/get_byte_spec.rb
@@ -0,0 +1,84 @@
+# encoding: binary
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#get_byte" do
+ it "scans one byte and returns it" do
+ s = StringScanner.new('abc5.')
+ s.get_byte.should == 'a'
+ s.get_byte.should == 'b'
+ s.get_byte.should == 'c'
+ s.get_byte.should == '5'
+ s.get_byte.should == '.'
+ end
+
+ it "is not multi-byte character sensitive" do
+ s = StringScanner.new("\244\242")
+ s.get_byte.should == "\244"
+ s.get_byte.should == "\242"
+ end
+
+ it "returns nil at the end of the string" do
+ # empty string case
+ s = StringScanner.new('')
+ s.get_byte.should == nil
+ s.get_byte.should == nil
+
+ # non-empty string case
+ s = StringScanner.new('a')
+ s.get_byte # skip one
+ s.get_byte.should == nil
+ end
+
+ describe "#[] successive call with a capture group name" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil" do
+ s = StringScanner.new("This is a test")
+ s.get_byte
+ s.should.matched?
+ s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "raises IndexError" do
+ s = StringScanner.new("This is a test")
+ s.get_byte
+ s.should.matched?
+ -> { s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns a matching character when given Integer index" do
+ s = StringScanner.new("This is a test")
+ s.get_byte
+ s[0].should == "T"
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ s = StringScanner.new("This is a test")
+ s.exist?(/(?<a>This)/)
+ s.should.matched?
+ s[:a].should == "This"
+
+ s.get_byte
+ s.should.matched?
+ s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "ignores the previous matching with Regexp" do
+ s = StringScanner.new("This is a test")
+ s.exist?(/(?<a>This)/)
+ s.should.matched?
+ s[:a].should == "This"
+
+ s.get_byte
+ s.should.matched?
+ -> { s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/getch_spec.rb b/spec/ruby/library/stringscanner/getch_spec.rb
new file mode 100644
index 0000000000..d369391b14
--- /dev/null
+++ b/spec/ruby/library/stringscanner/getch_spec.rb
@@ -0,0 +1,89 @@
+# encoding: binary
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range'
+require 'strscan'
+
+describe "StringScanner#getch" do
+ it "scans one character and returns it" do
+ s = StringScanner.new('abc')
+ s.getch.should == "a"
+ s.getch.should == "b"
+ s.getch.should == "c"
+ end
+
+ it "is multi-byte character sensitive" do
+ # Japanese hiragana "A" in EUC-JP
+ src = "\244\242".dup.force_encoding("euc-jp")
+
+ s = StringScanner.new(src)
+ s.getch.should == src
+ end
+
+ it "returns nil at the end of the string" do
+ # empty string case
+ s = StringScanner.new('')
+ s.getch.should == nil
+ s.getch.should == nil
+
+ # non-empty string case
+ s = StringScanner.new('a')
+ s.getch # skip one
+ s.getch.should == nil
+ end
+
+ describe "#[] successive call with a capture group name" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil" do
+ s = StringScanner.new("This is a test")
+ s.getch
+ s.should.matched?
+ s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "raises IndexError" do
+ s = StringScanner.new("This is a test")
+ s.getch
+ s.should.matched?
+ -> { s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns a matching character when given Integer index" do
+ s = StringScanner.new("This is a test")
+ s.getch
+ s[0].should == "T"
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ s = StringScanner.new("This is a test")
+
+ s.exist?(/(?<a>This)/)
+ s.should.matched?
+ s[:a].should == "This"
+
+ s.getch
+ s.should.matched?
+ s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ s = StringScanner.new("This is a test")
+
+ s.exist?(/(?<a>This)/)
+ s.should.matched?
+ s[:a].should == "This"
+
+ s.getch
+ s.should.matched?
+ -> { s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+
+ it_behaves_like :extract_range, :getch
+end
diff --git a/spec/ruby/library/stringscanner/initialize_spec.rb b/spec/ruby/library/stringscanner/initialize_spec.rb
new file mode 100644
index 0000000000..77f6084c1b
--- /dev/null
+++ b/spec/ruby/library/stringscanner/initialize_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#initialize" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "is a private method" do
+ StringScanner.should have_private_instance_method(:initialize)
+ end
+
+ it "returns an instance of StringScanner" do
+ @s.should be_kind_of(StringScanner)
+ @s.eos?.should be_false
+ end
+
+ it "converts the argument into a string using #to_str" do
+ m = mock(:str)
+
+ s = "test"
+ m.should_receive(:to_str).and_return(s)
+
+ scan = StringScanner.new(m)
+ scan.string.should == s
+ end
+
+ it "accepts a fixed_anchor keyword argument" do
+ s = StringScanner.new("foo", fixed_anchor: true)
+ s.should.fixed_anchor?
+ end
+end
diff --git a/spec/ruby/library/stringscanner/inspect_spec.rb b/spec/ruby/library/stringscanner/inspect_spec.rb
new file mode 100644
index 0000000000..ff6b97eb91
--- /dev/null
+++ b/spec/ruby/library/stringscanner/inspect_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#inspect" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns a String object" do
+ @s.inspect.should be_kind_of(String)
+ end
+
+ it "returns a string that represents the StringScanner object" do
+ @s.inspect.should == "#<StringScanner 0/14 @ \"This ...\">"
+ @s.scan_until(/is/)
+ @s.inspect.should == "#<StringScanner 4/14 \"This\" @ \" is a...\">"
+ @s.terminate
+ @s.inspect.should == "#<StringScanner fin>"
+ end
+end
diff --git a/spec/ruby/library/stringscanner/match_spec.rb b/spec/ruby/library/stringscanner/match_spec.rb
new file mode 100644
index 0000000000..a27bb51d72
--- /dev/null
+++ b/spec/ruby/library/stringscanner/match_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#match?" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the length of the match and the scan pointer is not advanced" do
+ @s.match?(/\w+/).should == 4
+ @s.match?(/\w+/).should == 4
+ @s.pos.should == 0
+ end
+
+ it "returns nil if there's no match" do
+ @s.match?(/\d+/).should == nil
+ @s.match?(/\s+/).should == nil
+ end
+
+ it "sets the last match result" do
+ @s.pos = 8
+ @s.match?(/a/)
+
+ @s.pre_match.should == "This is "
+ @s.matched.should == "a"
+ @s.post_match.should == " test"
+ end
+
+ it "effects pre_match" do
+ @s.scan(/\w+/)
+ @s.scan(/\s/)
+
+ @s.pre_match.should == "This"
+ @s.match?(/\w+/)
+ @s.pre_match.should == "This "
+ end
+
+ describe "#[] successive call with a capture group name" do
+ it "returns matched substring when matching succeeded" do
+ @s.match?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.match?(/(?<a>2008)/)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/matched_size_spec.rb b/spec/ruby/library/stringscanner/matched_size_spec.rb
new file mode 100644
index 0000000000..d9c338a07e
--- /dev/null
+++ b/spec/ruby/library/stringscanner/matched_size_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#matched_size" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the size of the most recent match" do
+ @s.check(/This/)
+ @s.matched_size.should == 4
+ @s.matched_size.should == 4
+ @s.scan(//)
+ @s.matched_size.should == 0
+ end
+
+ it "returns nil if there was no recent match" do
+ @s.matched_size.should == nil
+ @s.check(/\d+/)
+ @s.matched_size.should == nil
+ @s.terminate
+ @s.matched_size.should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/matched_spec.rb b/spec/ruby/library/stringscanner/matched_spec.rb
new file mode 100644
index 0000000000..c020bd3eae
--- /dev/null
+++ b/spec/ruby/library/stringscanner/matched_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range_matched'
+require 'strscan'
+
+describe "StringScanner#matched" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the last matched string" do
+ @s.match?(/\w+/)
+ @s.matched.should == "This"
+ @s.getch
+ @s.matched.should == "T"
+ @s.get_byte
+ @s.matched.should == "h"
+ end
+
+ it "returns nil if there's no match" do
+ @s.match?(/\d+/)
+ @s.matched.should == nil
+ end
+
+ it_behaves_like :extract_range_matched, :matched
+end
+
+describe "StringScanner#matched?" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns true if the last match was successful" do
+ @s.match?(/\w+/)
+ @s.matched?.should be_true
+ end
+
+ it "returns false if there's no match" do
+ @s.match?(/\d+/)
+ @s.matched?.should be_false
+ end
+end
diff --git a/spec/ruby/library/stringscanner/must_C_version_spec.rb b/spec/ruby/library/stringscanner/must_C_version_spec.rb
new file mode 100644
index 0000000000..9d6edfe7b6
--- /dev/null
+++ b/spec/ruby/library/stringscanner/must_C_version_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner.must_C_version" do
+ it "returns self" do
+ StringScanner.must_C_version.should == StringScanner
+ end
+end
diff --git a/spec/ruby/library/stringscanner/named_captures_spec.rb b/spec/ruby/library/stringscanner/named_captures_spec.rb
new file mode 100644
index 0000000000..a68d66c216
--- /dev/null
+++ b/spec/ruby/library/stringscanner/named_captures_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#named_captures" do
+ before do
+ @s = StringScanner.new('Fri Dec 12 1975 14:39')
+ end
+
+ it "returns a hash of names and matched substrings for named capturing groups in a regular expression of the most recent matching" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+ @s.named_captures.should == {"wday" => "Fri", "month" => "Dec", "day" => "12"}
+ end
+
+ it "returns {} if there are no named capturing groups" do
+ @s.exist?(/(\w+) (\w+) (\d+)/)
+ @s.named_captures.should == {}
+ end
+
+ # https://github.com/ruby/strscan/issues/132
+ ruby_bug "", ""..."3.3" do # fixed in strscan v3.0.7
+ it "returns {} if there is no any matching done" do
+ @s.named_captures.should == {}
+ end
+ end
+
+ it "returns nil for an optional named capturing group if it doesn't match" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\s+)?/)
+ @s.named_captures.should == {"wday" => "Fri", "month" => "Dec", "day" => nil}
+ end
+end
diff --git a/spec/ruby/library/stringscanner/peek_byte_spec.rb b/spec/ruby/library/stringscanner/peek_byte_spec.rb
new file mode 100644
index 0000000000..88ef4a2b7c
--- /dev/null
+++ b/spec/ruby/library/stringscanner/peek_byte_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ describe "StringScanner#peek_byte" do
+ it "returns a byte at the current position as an Integer" do
+ s = StringScanner.new('This is a test')
+ s.peek_byte.should == 84
+ end
+
+ it "returns nil at the end of the string" do
+ s = StringScanner.new('a')
+ s.getch # skip one
+ s.pos.should == 1
+ s.peek_byte.should == nil
+ end
+
+ it "is not multi-byte character sensitive" do
+ s = StringScanner.new("∂") # "∂".bytes => [226, 136, 130]
+ s.peek_byte.should == 226
+ s.pos = 1
+ s.peek_byte.should == 136
+ s.pos = 2
+ s.peek_byte.should == 130
+ end
+
+ it "doesn't change current position" do
+ s = StringScanner.new('This is a test')
+
+ s.pos.should == 0
+ s.peek_byte.should == 84
+ s.pos.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/peek_spec.rb b/spec/ruby/library/stringscanner/peek_spec.rb
new file mode 100644
index 0000000000..d490abecf9
--- /dev/null
+++ b/spec/ruby/library/stringscanner/peek_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#peek" do
+ before :each do
+ @s = StringScanner.new('This is a test')
+ end
+
+ it "returns at most the specified number of bytes from the current position" do
+ @s.peek(4).should == "This"
+ @s.pos.should == 0
+ @s.pos = 5
+ @s.peek(2).should == "is"
+ @s.peek(1000).should == "is a test"
+
+ s = StringScanner.new("été")
+ s.peek(2).should == "é"
+ end
+
+ it "returns an empty string when the passed argument is zero" do
+ @s.peek(0).should == ""
+ end
+
+ it "raises a ArgumentError when the passed argument is negative" do
+ -> { @s.peek(-2) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a RangeError when the passed argument is a Bignum" do
+ -> { @s.peek(bignum_value) }.should raise_error(RangeError)
+ end
+
+ it "returns an instance of String when passed a String subclass" do
+ cls = Class.new(String)
+ sub = cls.new("abc")
+
+ s = StringScanner.new(sub)
+
+ ch = s.peek(1)
+ ch.should_not be_kind_of(cls)
+ ch.should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/library/stringscanner/pointer_spec.rb b/spec/ruby/library/stringscanner/pointer_spec.rb
new file mode 100644
index 0000000000..bc0c0c50b7
--- /dev/null
+++ b/spec/ruby/library/stringscanner/pointer_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/pos'
+require 'strscan'
+
+describe "StringScanner#pointer" do
+ it_behaves_like :strscan_pos, :pointer
+end
+
+describe "StringScanner#pointer=" do
+ it_behaves_like :strscan_pos_set, :pointer=
+end
diff --git a/spec/ruby/library/stringscanner/pos_spec.rb b/spec/ruby/library/stringscanner/pos_spec.rb
new file mode 100644
index 0000000000..275fecf0f3
--- /dev/null
+++ b/spec/ruby/library/stringscanner/pos_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/pos'
+require 'strscan'
+
+describe "StringScanner#pos" do
+ it_behaves_like :strscan_pos, :pos
+end
+
+describe "StringScanner#pos=" do
+ it_behaves_like :strscan_pos_set, :pos=
+end
diff --git a/spec/ruby/library/stringscanner/post_match_spec.rb b/spec/ruby/library/stringscanner/post_match_spec.rb
new file mode 100644
index 0000000000..720dc3530d
--- /dev/null
+++ b/spec/ruby/library/stringscanner/post_match_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range_matched'
+require 'strscan'
+
+describe "StringScanner#post_match" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the post-match (in the regular expression sense) of the last scan" do
+ @s.post_match.should == nil
+ @s.scan(/\w+\s/)
+ @s.post_match.should == "is a test"
+ @s.getch
+ @s.post_match.should == "s a test"
+ @s.get_byte
+ @s.post_match.should == " a test"
+ @s.get_byte
+ @s.post_match.should == "a test"
+ end
+
+ it "returns nil if there's no match" do
+ @s.scan(/\s+/)
+ @s.post_match.should == nil
+ end
+
+ it_behaves_like :extract_range_matched, :post_match
+end
diff --git a/spec/ruby/library/stringscanner/pre_match_spec.rb b/spec/ruby/library/stringscanner/pre_match_spec.rb
new file mode 100644
index 0000000000..1c2c511d5c
--- /dev/null
+++ b/spec/ruby/library/stringscanner/pre_match_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range_matched'
+require 'strscan'
+
+describe "StringScanner#pre_match" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the pre-match (in the regular expression sense) of the last scan" do
+ @s.pre_match.should == nil
+ @s.scan(/\w+\s/)
+ @s.pre_match.should == ""
+ @s.getch
+ @s.pre_match.should == "This "
+ @s.get_byte
+ @s.pre_match.should == "This i"
+ @s.get_byte
+ @s.pre_match.should == "This is"
+ end
+
+ it "returns nil if there's no match" do
+ @s.scan(/\s+/)
+ @s.pre_match.should == nil
+ end
+
+ it "is more than just the data from the last match" do
+ @s.scan(/\w+/)
+ @s.scan_until(/a te/)
+ @s.pre_match.should == "This is "
+ end
+
+ it "is not changed when the scanner's position changes" do
+ @s.scan_until(/\s+/)
+ @s.pre_match.should == "This"
+ @s.pos -= 1
+ @s.pre_match.should == "This"
+ end
+
+ it_behaves_like :extract_range_matched, :pre_match
+end
diff --git a/spec/ruby/library/stringscanner/reset_spec.rb b/spec/ruby/library/stringscanner/reset_spec.rb
new file mode 100644
index 0000000000..a10ca2d838
--- /dev/null
+++ b/spec/ruby/library/stringscanner/reset_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#reset" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "reset the scan pointer and clear matching data" do
+ @s.scan(/This/)
+ @s.reset
+ @s.pos.should == 0
+ @s.matched.should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/rest_size_spec.rb b/spec/ruby/library/stringscanner/rest_size_spec.rb
new file mode 100644
index 0000000000..a5e971631a
--- /dev/null
+++ b/spec/ruby/library/stringscanner/rest_size_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#rest_size" do
+ before :each do
+ @s = StringScanner.new('This is a test')
+ end
+
+ it "returns the length of the rest of the string" do
+ @s.rest_size.should == 14
+ @s.scan(/This/)
+ @s.rest_size.should == 10
+ @s.terminate
+ @s.rest_size.should == 0
+ end
+
+ it "is equivalent to rest.size" do
+ @s.scan(/This/)
+ @s.rest_size.should == @s.rest.size
+ end
+end
diff --git a/spec/ruby/library/stringscanner/rest_spec.rb b/spec/ruby/library/stringscanner/rest_spec.rb
new file mode 100644
index 0000000000..67072f880d
--- /dev/null
+++ b/spec/ruby/library/stringscanner/rest_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'shared/extract_range_matched'
+require 'strscan'
+
+describe "StringScanner#rest" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the rest of the string" do
+ @s.scan(/This\s+/)
+ @s.rest.should == "is a test"
+ end
+
+ it "returns self in the reset position" do
+ @s.reset
+ @s.rest.should == @s.string
+ end
+
+ it "returns an empty string in the terminate position" do
+ @s.terminate
+ @s.rest.should == ""
+ end
+
+ it_behaves_like :extract_range_matched, :rest
+
+end
+
+describe "StringScanner#rest?" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns true if there is more data in the string" do
+ @s.rest?.should be_true
+ @s.scan(/This/)
+ @s.rest?.should be_true
+ end
+
+ it "returns false if there is no more data in the string" do
+ @s.terminate
+ @s.rest?.should be_false
+ end
+
+ it "is the opposite of eos?" do
+ @s.rest?.should_not == @s.eos?
+ end
+end
diff --git a/spec/ruby/library/stringscanner/scan_byte_spec.rb b/spec/ruby/library/stringscanner/scan_byte_spec.rb
new file mode 100644
index 0000000000..aa2decc8f7
--- /dev/null
+++ b/spec/ruby/library/stringscanner/scan_byte_spec.rb
@@ -0,0 +1,98 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ describe "StringScanner#scan_byte" do
+ it "scans one byte and returns it as on Integer" do
+ s = StringScanner.new('abc') # "abc".bytes => [97, 98, 99]
+ s.scan_byte.should == 97
+ s.scan_byte.should == 98
+ s.scan_byte.should == 99
+ end
+
+ it "is not multi-byte character sensitive" do
+ s = StringScanner.new("あ") # "あ".bytes => [227, 129, 130]
+ s.scan_byte.should == 227
+ s.scan_byte.should == 129
+ s.scan_byte.should == 130
+ end
+
+ it "returns nil at the end of the string" do
+ s = StringScanner.new('a')
+ s.scan_byte # skip one
+ s.scan_byte.should == nil
+ s.pos.should == 1
+ end
+
+ it "changes current position" do
+ s = StringScanner.new('abc')
+ s.pos.should == 0
+ s.scan_byte
+ s.pos.should == 1
+ end
+
+ it "sets the last match result" do
+ s = StringScanner.new('abc')
+ s.pos = 1
+ s.scan_byte
+
+ s.pre_match.should == "a"
+ s.matched.should == "b"
+ s.post_match.should == "c"
+ end
+
+ describe "#[] successive call with a capture group name" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil" do
+ s = StringScanner.new("abc")
+ s.scan_byte
+ s.should.matched?
+ s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "raises IndexError" do
+ s = StringScanner.new("abc")
+ s.scan_byte
+ s.should.matched?
+ -> { s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns a matching character when given Integer index" do
+ s = StringScanner.new("This is a test")
+ s.scan_byte
+ s[0].should == "T"
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ s = StringScanner.new("abc")
+
+ s.exist?(/(?<a>a)/)
+ s.should.matched?
+ s[:a].should == "a"
+
+ s.scan_byte
+ s.should.matched?
+ s[:a].should == nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ s = StringScanner.new("abc")
+
+ s.exist?(/(?<a>a)/)
+ s.should.matched?
+ s[:a].should == "a"
+
+ s.scan_byte
+ s.should.matched?
+ -> { s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/scan_full_spec.rb b/spec/ruby/library/stringscanner/scan_full_spec.rb
new file mode 100644
index 0000000000..967313f5ca
--- /dev/null
+++ b/spec/ruby/library/stringscanner/scan_full_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#scan_full" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the number of bytes advanced" do
+ orig_pos = @s.pos
+ @s.scan_full(/This/, false, false).should == 4
+ @s.pos.should == orig_pos
+ end
+
+ it "returns the number of bytes advanced and advances the scan pointer if the second argument is true" do
+ @s.scan_full(/This/, true, false).should == 4
+ @s.pos.should == 4
+ end
+
+ it "returns the matched string if the third argument is true" do
+ orig_pos = @s.pos
+ @s.scan_full(/This/, false, true).should == "This"
+ @s.pos.should == orig_pos
+ end
+
+ it "returns the matched string if the third argument is true and advances the scan pointer if the second argument is true" do
+ @s.scan_full(/This/, true, true).should == "This"
+ @s.pos.should == 4
+ end
+
+ describe "#[] successive call with a capture group name" do
+ it "returns matched substring when matching succeeded" do
+ @s.scan_full(/(?<a>This)/, false, false)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.scan_full(/(?<a>2008)/, false, false)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/scan_integer_spec.rb b/spec/ruby/library/stringscanner/scan_integer_spec.rb
new file mode 100644
index 0000000000..fe0d26f404
--- /dev/null
+++ b/spec/ruby/library/stringscanner/scan_integer_spec.rb
@@ -0,0 +1,157 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ describe "StringScanner#scan_integer" do
+ it "returns the matched Integer literal starting from the current position" do
+ s = StringScanner.new("42")
+ s.scan_integer.should == 42
+ end
+
+ it "returns nil when no Integer literal matched starting from the current position" do
+ s = StringScanner.new("a42")
+ s.scan_integer.should == nil
+ end
+
+ it "supports a sign +/-" do
+ StringScanner.new("+42").scan_integer.should == 42
+ StringScanner.new("-42").scan_integer.should == -42
+ end
+
+ it "changes the current position" do
+ s = StringScanner.new("42abc")
+ s.scan_integer
+ s.pos.should == 2
+ end
+
+ # https://github.com/ruby/strscan/issues/130
+ ruby_bug "", "3.4"..."4.0" do # introduced in strscan v3.1.1
+ it "sets the last match result" do
+ s = StringScanner.new("42abc")
+ s.scan_integer
+
+ s.should.matched?
+ s.matched.should == "42"
+ s.pre_match.should == ""
+ s.post_match.should == "abc"
+ s.matched_size.should == 2
+ end
+ end
+
+ it "raises Encoding::CompatibilityError when a scanned string not is ASCII-compatible encoding" do
+ string = "42".encode(Encoding::UTF_16BE)
+ s = StringScanner.new(string)
+
+ -> {
+ s.scan_integer
+ }.should raise_error(Encoding::CompatibilityError, 'ASCII incompatible encoding: UTF-16BE')
+ end
+
+ context "given base" do
+ it "supports base: 10" do
+ s = StringScanner.new("42")
+ s.scan_integer(base: 10).should == 42
+ end
+
+ it "supports base: 16" do
+ StringScanner.new("0xff").scan_integer(base: 16).should == 0xff
+ StringScanner.new("-0xff").scan_integer(base: 16).should == -0xff
+ StringScanner.new("0xFF").scan_integer(base: 16).should == 0xff
+ StringScanner.new("-0xFF").scan_integer(base: 16).should == -0xff
+ StringScanner.new("ff").scan_integer(base: 16).should == 0xff
+ StringScanner.new("-ff").scan_integer(base: 16).should == -0xff
+ end
+
+ it "raises ArgumentError when passed not supported base" do
+ -> {
+ StringScanner.new("42").scan_integer(base: 5)
+ }.should raise_error(ArgumentError, "Unsupported integer base: 5, expected 10 or 16")
+ end
+
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "does not match '0x' prefix on its own" do
+ StringScanner.new("0x").scan_integer(base: 16).should == nil
+ StringScanner.new("-0x").scan_integer(base: 16).should == nil
+ StringScanner.new("+0x").scan_integer(base: 16).should == nil
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "matches '0' in a '0x' that is followed by non-hex characters" do
+ StringScanner.new("0x!@#").scan_integer(base: 16).should == 0
+ StringScanner.new("-0x!@#").scan_integer(base: 16).should == 0
+ StringScanner.new("+0x!@#").scan_integer(base: 16).should == 0
+ end
+
+ it "matches '0' in a '0x' located in the end of a string" do
+ StringScanner.new("0x").scan_integer(base: 16).should == 0
+ StringScanner.new("-0x").scan_integer(base: 16).should == 0
+ StringScanner.new("+0x").scan_integer(base: 16).should == 0
+ end
+ end
+ end
+ end
+
+ describe "#[] successive call with a capture group name" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil substring when matching succeeded" do
+ s = StringScanner.new("42")
+ s.scan_integer
+ s.should.matched?
+ s[:a].should == nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "raises IndexError when matching succeeded" do
+ s = StringScanner.new("42")
+ s.scan_integer
+ s.should.matched?
+ -> { s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns nil when matching failed" do
+ s = StringScanner.new("a42")
+ s.scan_integer
+ s.should_not.matched?
+ s[:a].should be_nil
+ end
+
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "returns a matching substring when given Integer index" do
+ s = StringScanner.new("42")
+ s.scan_integer
+ s[0].should == "42"
+ end
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "does not ignore the previous matching with Regexp" do
+ s = StringScanner.new("42")
+
+ s.exist?(/(?<a>42)/)
+ s.should.matched?
+ s[:a].should == "42"
+
+ s.scan_integer
+ s.should.matched?
+ s[:a].should == "42"
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "ignores the previous matching with Regexp" do
+ s = StringScanner.new("42")
+
+ s.exist?(/(?<a>42)/)
+ s.should.matched?
+ s[:a].should == "42"
+
+ s.scan_integer
+ s.should.matched?
+ -> { s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/scan_spec.rb b/spec/ruby/library/stringscanner/scan_spec.rb
new file mode 100644
index 0000000000..088c3419fb
--- /dev/null
+++ b/spec/ruby/library/stringscanner/scan_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#scan" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the matched string" do
+ @s.scan(/\w+/).should == "This"
+ @s.scan(/.../).should == " is"
+ @s.scan(//).should == ""
+ @s.scan(/\s+/).should == " "
+ end
+
+ it "treats ^ as matching from the beginning of the current position" do
+ @s.scan(/\w+/).should == "This"
+ @s.scan(/^\d/).should be_nil
+ @s.scan(/^\s/).should == " "
+ end
+
+ it "treats ^ as matching from the beginning of the current position when it's not the first character in the regexp" do
+ @s.scan(/\w+/).should == "This"
+ @s.scan(/( is not|^ is a)/).should == " is a"
+ end
+
+ it "treats \\A as matching from the beginning of the current position" do
+ @s.scan(/\w+/).should == "This"
+ @s.scan(/\A\d/).should be_nil
+ @s.scan(/\A\s/).should == " "
+ end
+
+ it "treats \\A as matching from the beginning of the current position when it's not the first character in the regexp" do
+ @s.scan(/\w+/).should == "This"
+ @s.scan(/( is not|\A is a)/).should == " is a"
+ end
+
+ it "returns nil if there's no match" do
+ @s.scan(/\d/).should == nil
+ end
+
+ it "returns nil when there is no more to scan" do
+ @s.scan(/[\w\s]+/).should == "This is a test"
+ @s.scan(/\w+/).should be_nil
+ end
+
+ it "returns an empty string when the pattern matches empty" do
+ @s.scan(/.*/).should == "This is a test"
+ @s.scan(/.*/).should == ""
+ @s.scan(/./).should be_nil
+ end
+
+ it "treats String as the pattern itself" do
+ @s.scan("this").should be_nil
+ @s.scan("This").should == "This"
+ end
+
+ it "raises a TypeError if pattern isn't a Regexp nor String" do
+ -> { @s.scan(5) }.should raise_error(TypeError)
+ -> { @s.scan(:test) }.should raise_error(TypeError)
+ -> { @s.scan(mock('x')) }.should raise_error(TypeError)
+ end
+
+ describe "#[] successive call with a capture group name" do
+ it "returns matched substring when matching succeeded" do
+ @s.scan(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.scan(/(?<a>2008)/)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+end
+
+describe "StringScanner#scan with fixed_anchor: true" do
+ before :each do
+ @s = StringScanner.new("This\nis\na\ntest", fixed_anchor: true)
+ end
+
+ it "returns the matched string" do
+ @s.scan(/\w+/).should == "This"
+ @s.scan(/.../m).should == "\nis"
+ @s.scan(//).should == ""
+ @s.scan(/\s+/).should == "\n"
+ end
+
+ it "treats ^ as matching from the beginning of line" do
+ @s.scan(/\w+\n/).should == "This\n"
+ @s.scan(/^\w/).should == "i"
+ @s.scan(/^\w/).should be_nil
+ end
+
+ it "treats \\A as matching from the beginning of string" do
+ @s.scan(/\A\w/).should == "T"
+ @s.scan(/\A\w/).should be_nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/scan_until_spec.rb b/spec/ruby/library/stringscanner/scan_until_spec.rb
new file mode 100644
index 0000000000..610060d6f1
--- /dev/null
+++ b/spec/ruby/library/stringscanner/scan_until_spec.rb
@@ -0,0 +1,135 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#scan_until" do
+ before do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the substring up to and including the end of the match" do
+ @s.scan_until(/a/).should == "This is a"
+ end
+
+ it "sets the last match result" do
+ @s.scan_until(/a/)
+
+ @s.pre_match.should == "This is "
+ @s.matched.should == "a"
+ @s.post_match.should == " test"
+ end
+
+ it "returns nil if there's no match" do
+ @s.scan_until(/\d/).should == nil
+ end
+
+ it "can match anchors properly" do
+ @s.scan(/T/)
+ @s.scan_until(/^h/).should == "h"
+ end
+
+ version_is StringScanner::Version, ""..."3.1.1" do # ruby_version_is ""..."3.4"
+ it "raises TypeError if given a String" do
+ -> {
+ @s.scan_until('T')
+ }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ it "searches a substring in the rest part of a string if given a String" do
+ @s.scan_until("a").should == "This is a"
+ end
+
+ # https://github.com/ruby/strscan/issues/131
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.1"
+ it "sets the last match result if given a String" do
+ @s.scan_until("a")
+
+ @s.pre_match.should == ""
+ @s.matched.should == "This is a"
+ @s.post_match.should == " test"
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "sets the last match result if given a String" do
+ @s.scan_until("a")
+
+ @s.pre_match.should == "This is "
+ @s.matched.should == "a"
+ @s.post_match.should == " test"
+ end
+ end
+ end
+
+ describe "#[] successive call with a capture group name" do
+ context "when #scan_until was called with a Regexp pattern" do
+ it "returns matched substring when matching succeeded" do
+ @s.scan_until(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.scan_until(/(?<a>2008)/)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ context "when #scan_until was called with a String pattern" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil when matching succeeded" do
+ @s.scan_until("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "raises IndexError when matching succeeded" do
+ @s.scan_until("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns nil when matching failed" do
+ @s.scan_until("2008")
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+
+ it "returns a matching substring when given Integer index" do
+ @s.scan_until("This")
+ @s[0].should == "This"
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.scan_until("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.scan_until("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/search_full_spec.rb b/spec/ruby/library/stringscanner/search_full_spec.rb
new file mode 100644
index 0000000000..197adfda4d
--- /dev/null
+++ b/spec/ruby/library/stringscanner/search_full_spec.rb
@@ -0,0 +1,133 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#search_full" do
+ before do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the number of bytes advanced" do
+ orig_pos = @s.pos
+ @s.search_full(/This/, false, false).should == 4
+ @s.pos.should == orig_pos
+ end
+
+ it "returns the number of bytes advanced and advances the scan pointer if the second argument is true" do
+ @s.search_full(/This/, true, false).should == 4
+ @s.pos.should == 4
+ end
+
+ it "returns the matched string if the third argument is true" do
+ orig_pos = @s.pos
+ @s.search_full(/This/, false, true).should == "This"
+ @s.pos.should == orig_pos
+ end
+
+ it "returns the matched string if the third argument is true and advances the scan pointer if the second argument is true" do
+ @s.search_full(/This/, true, true).should == "This"
+ @s.pos.should == 4
+ end
+
+ it "sets the last match result" do
+ @s.search_full(/is a/, false, false)
+
+ @s.pre_match.should == "This "
+ @s.matched.should == "is a"
+ @s.post_match.should == " test"
+ end
+
+ version_is StringScanner::Version, ""..."3.1.1" do # ruby_version_is ""..."3.4"
+ it "raises TypeError if given a String" do
+ -> {
+ @s.search_full('T', true, true)
+ }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ it "searches a substring in the rest part of a string if given a String" do
+ @s.search_full("is a", false, false).should == 9
+ end
+
+ # https://github.com/ruby/strscan/issues/131
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.1"
+ it "sets the last match result if given a String" do
+ @s.search_full("is a", false, false)
+
+ @s.pre_match.should == ""
+ @s.matched.should == "This is a"
+ @s.post_match.should == " test"
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "sets the last match result if given a String" do
+ @s.search_full("is a", false, false)
+
+ @s.pre_match.should == "This "
+ @s.matched.should == "is a"
+ @s.post_match.should == " test"
+ end
+ end
+ end
+
+ describe "#[] successive call with a capture group name" do
+ context "when #search_full was called with a Regexp pattern" do
+ it "returns matched substring when matching succeeded" do
+ @s.search_full(/(?<a>This)/, false, false)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.search_full(/(?<a>2008)/, false, false)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ context "when #search_full was called with a String pattern" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil when matching succeeded" do
+ @s.search_full("This", false, false)
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "raises IndexError when matching succeeded" do
+ @s.search_full("This", false, false)
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns nil when matching failed" do
+ @s.search_full("2008", false, false)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+
+ it "returns a matching substring when given Integer index" do
+ @s.search_full("This", false, false)
+ @s[0].should == "This"
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.search_full("This", false, false)
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/bol.rb b/spec/ruby/library/stringscanner/shared/bol.rb
new file mode 100644
index 0000000000..ebcdd7938f
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/bol.rb
@@ -0,0 +1,25 @@
+describe :strscan_bol, shared: true do
+ it "returns true if the scan pointer is at the beginning of the line, false otherwise" do
+ s = StringScanner.new("This is a test")
+ s.send(@method).should be_true
+ s.scan(/This/)
+ s.send(@method).should be_false
+ s.terminate
+ s.send(@method).should be_false
+
+ s = StringScanner.new("hello\nworld")
+ s.bol?.should be_true
+ s.scan(/\w+/)
+ s.bol?.should be_false
+ s.scan(/\n/)
+ s.bol?.should be_true
+ s.unscan
+ s.bol?.should be_false
+ end
+
+ it "returns true if the scan pointer is at the end of the line of an empty string." do
+ s = StringScanner.new('')
+ s.terminate
+ s.send(@method).should be_true
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/concat.rb b/spec/ruby/library/stringscanner/shared/concat.rb
new file mode 100644
index 0000000000..1dbae11f7c
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/concat.rb
@@ -0,0 +1,30 @@
+describe :strscan_concat, shared: true do
+ it "concatenates the given argument to self and returns self" do
+ s = StringScanner.new(+"hello ")
+ s.send(@method, 'world').should == s
+ s.string.should == "hello world"
+ s.eos?.should be_false
+ end
+
+ it "raises a TypeError if the given argument can't be converted to a String" do
+ -> { StringScanner.new('hello').send(@method, :world) }.should raise_error(TypeError)
+ -> { StringScanner.new('hello').send(@method, mock('x')) }.should raise_error(TypeError)
+ end
+end
+
+describe :strscan_concat_fixnum, shared: true do
+ it "raises a TypeError" do
+ a = StringScanner.new("hello world")
+ -> { a.send(@method, 333) }.should raise_error(TypeError)
+ b = StringScanner.new("")
+ -> { b.send(@method, (256 * 3 + 64)) }.should raise_error(TypeError)
+ -> { b.send(@method, -200) }.should raise_error(TypeError)
+ end
+
+ it "doesn't call to_int on the argument" do
+ x = mock('x')
+ x.should_not_receive(:to_int)
+
+ -> { StringScanner.new("").send(@method, x) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/extract_range.rb b/spec/ruby/library/stringscanner/shared/extract_range.rb
new file mode 100644
index 0000000000..e7404fd0cb
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/extract_range.rb
@@ -0,0 +1,11 @@
+describe :extract_range, shared: true do
+ it "returns an instance of String when passed a String subclass" do
+ cls = Class.new(String)
+ sub = cls.new("abc")
+
+ s = StringScanner.new(sub)
+ ch = s.send(@method)
+ ch.should_not be_kind_of(cls)
+ ch.should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/extract_range_matched.rb b/spec/ruby/library/stringscanner/shared/extract_range_matched.rb
new file mode 100644
index 0000000000..070a132812
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/extract_range_matched.rb
@@ -0,0 +1,13 @@
+describe :extract_range_matched, shared: true do
+ it "returns an instance of String when passed a String subclass" do
+ cls = Class.new(String)
+ sub = cls.new("abc")
+
+ s = StringScanner.new(sub)
+ s.scan(/\w{1}/)
+
+ ch = s.send(@method)
+ ch.should_not be_kind_of(cls)
+ ch.should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/library/stringscanner/shared/pos.rb b/spec/ruby/library/stringscanner/shared/pos.rb
new file mode 100644
index 0000000000..eea7ead6b5
--- /dev/null
+++ b/spec/ruby/library/stringscanner/shared/pos.rb
@@ -0,0 +1,59 @@
+describe :strscan_pos, shared: true do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the position of the scan pointer" do
+ @s.send(@method).should == 0
+ @s.scan_until(/This is/)
+ @s.send(@method).should == 7
+ @s.get_byte
+ @s.send(@method).should == 8
+ @s.terminate
+ @s.send(@method).should == 14
+ end
+
+ it "returns 0 in the reset position" do
+ @s.reset
+ @s.send(@method).should == 0
+ end
+
+ it "returns the length of the string in the terminate position" do
+ @s.terminate
+ @s.send(@method).should == @s.string.length
+ end
+
+ it "is not multi-byte character sensitive" do
+ s = StringScanner.new("abcädeföghi")
+
+ s.scan_until(/ö/)
+ s.pos.should == 10
+ end
+end
+
+describe :strscan_pos_set, shared: true do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "modify the scan pointer" do
+ @s.send(@method, 5)
+ @s.rest.should == "is a test"
+ end
+
+ it "positions from the end if the argument is negative" do
+ @s.send(@method, -2)
+ @s.rest.should == "st"
+ @s.pos.should == 12
+ end
+
+ it "raises a RangeError if position too far backward" do
+ -> {
+ @s.send(@method, -20)
+ }.should raise_error(RangeError)
+ end
+
+ it "raises a RangeError when the passed argument is out of range" do
+ -> { @s.send(@method, 20) }.should raise_error(RangeError)
+ end
+end
diff --git a/spec/ruby/library/stringscanner/size_spec.rb b/spec/ruby/library/stringscanner/size_spec.rb
new file mode 100644
index 0000000000..3e475489e3
--- /dev/null
+++ b/spec/ruby/library/stringscanner/size_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#size" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the number of captures groups of the last match" do
+ @s.scan(/(.)(.)(.)/)
+ @s.size.should == 4
+ end
+
+ it "returns nil if there is no last match" do
+ @s.size.should == nil
+ end
+end
diff --git a/spec/ruby/library/stringscanner/skip_spec.rb b/spec/ruby/library/stringscanner/skip_spec.rb
new file mode 100644
index 0000000000..12f5b7781c
--- /dev/null
+++ b/spec/ruby/library/stringscanner/skip_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#skip" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns length of the match" do
+ @s.skip(/\w+/).should == 4
+ @s.skip(/\s+\w+/).should == 3
+ end
+
+ it "returns nil if there's no match" do
+ @s.skip(/\s+/).should == nil
+ @s.skip(/\d+/).should == nil
+ end
+
+ describe "#[] successive call with a capture group name" do
+ it "returns matched substring when matching succeeded" do
+ @s.skip(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.skip(/(?<a>2008)/)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/skip_until_spec.rb b/spec/ruby/library/stringscanner/skip_until_spec.rb
new file mode 100644
index 0000000000..5d73d8f0b9
--- /dev/null
+++ b/spec/ruby/library/stringscanner/skip_until_spec.rb
@@ -0,0 +1,132 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#skip_until" do
+ before do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "returns the number of bytes advanced and advances the scan pointer until pattern is matched and consumed" do
+ @s.skip_until(/a/).should == 9
+ @s.pos.should == 9
+ end
+
+ it "sets the last match result" do
+ @s.skip_until(/a/)
+
+ @s.pre_match.should == "This is "
+ @s.matched.should == "a"
+ @s.post_match.should == " test"
+ end
+
+ it "returns nil if no match was found" do
+ @s.skip_until(/d+/).should == nil
+ end
+
+ version_is StringScanner::Version, ""..."3.1.1" do # ruby_version_is ""..."3.4"
+ it "raises TypeError if given a String" do
+ -> {
+ @s.skip_until('T')
+ }.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ it "searches a substring in the rest part of a string if given a String" do
+ @s.skip_until("a").should == 9
+ @s.pos.should == 9
+ end
+
+ # https://github.com/ruby/strscan/issues/131
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.1"
+ it "sets the last match result if given a String" do
+ @s.skip_until("a")
+
+ @s.pre_match.should == ""
+ @s.matched.should == "This is a"
+ @s.post_match.should == " test"
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "sets the last match result if given a String" do
+ @s.skip_until("a")
+
+ @s.pre_match.should == "This is "
+ @s.matched.should == "a"
+ @s.post_match.should == " test"
+ end
+ end
+ end
+
+ describe "#[] successive call with a capture group name" do
+ context "when #scan_until was called with a Regexp pattern" do
+ it "returns matched substring when matching succeeded" do
+ @s.skip_until(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+ end
+
+ it "returns nil when matching failed" do
+ @s.skip_until(/(?<a>2008)/)
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+ end
+
+ version_is StringScanner::Version, "3.1.1" do # ruby_version_is "3.4"
+ context "when #skip_until was called with a String pattern" do
+ # https://github.com/ruby/strscan/issues/139
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "returns nil when matching succeeded" do
+ @s.skip_until("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4.3"
+ it "raises IndexError when matching succeeded" do
+ @s.skip_until("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+
+ it "returns nil when matching failed" do
+ @s.skip_until("2008")
+ @s.should_not.matched?
+ @s[:a].should be_nil
+ end
+
+ it "returns a matching substring when given Integer index" do
+ @s.skip_until("This")
+ @s[0].should == "This"
+ end
+
+ # https://github.com/ruby/strscan/issues/135
+ version_is StringScanner::Version, "3.1.1"..."3.1.3" do # ruby_version_is "3.4.0"..."3.4.3"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.skip_until("This")
+ @s.should.matched?
+ @s[:a].should be_nil
+ end
+ end
+ version_is StringScanner::Version, "3.1.3" do # ruby_version_is "3.4"
+ it "ignores the previous matching with Regexp" do
+ @s.exist?(/(?<a>This)/)
+ @s.should.matched?
+ @s[:a].should == "This"
+
+ @s.skip_until("This")
+ @s.should.matched?
+ -> { @s[:a] }.should raise_error(IndexError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/stringscanner/string_spec.rb b/spec/ruby/library/stringscanner/string_spec.rb
new file mode 100644
index 0000000000..cba6bd51dd
--- /dev/null
+++ b/spec/ruby/library/stringscanner/string_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#string" do
+ before :each do
+ @string = +"This is a test"
+ @s = StringScanner.new(@string)
+ end
+
+ it "returns the string being scanned" do
+ @s.string.should == "This is a test"
+ @s << " case"
+ @s.string.should == "This is a test case"
+ end
+
+ it "returns the identical object passed in" do
+ @s.string.equal?(@string).should be_true
+ end
+end
+
+describe "StringScanner#string=" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "changes the string being scanned to the argument and resets the scanner" do
+ @s.string = "Hello world"
+ @s.string.should == "Hello world"
+ end
+
+ it "converts the argument into a string using #to_str" do
+ m = mock(:str)
+
+ s = "test"
+ m.should_receive(:to_str).and_return(s)
+
+ @s.string = m
+ @s.string.should == s
+ end
+end
diff --git a/spec/ruby/library/stringscanner/terminate_spec.rb b/spec/ruby/library/stringscanner/terminate_spec.rb
new file mode 100644
index 0000000000..3cff5c010c
--- /dev/null
+++ b/spec/ruby/library/stringscanner/terminate_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#terminate" do
+ it "set the scan pointer to the end of the string and clear matching data." do
+ s = StringScanner.new('This is a test')
+ s.terminate
+ s.should_not.bol?
+ s.should.eos?
+ end
+end
diff --git a/spec/ruby/library/stringscanner/unscan_spec.rb b/spec/ruby/library/stringscanner/unscan_spec.rb
new file mode 100644
index 0000000000..b7b4876b8a
--- /dev/null
+++ b/spec/ruby/library/stringscanner/unscan_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#unscan" do
+ before :each do
+ @s = StringScanner.new("This is a test")
+ end
+
+ it "set the scan pointer to the previous position" do
+ @s.scan(/This/)
+ @s.unscan
+ @s.matched.should == nil
+ @s.pos.should == 0
+ end
+
+ it "remember only one previous position" do
+ @s.scan(/This/)
+ pos = @s.pos
+ @s.scan(/ is/)
+ @s.unscan
+ @s.pos.should == pos
+ end
+
+ it "raises a StringScanner::Error when the previous match had failed" do
+ -> { @s.unscan }.should raise_error(StringScanner::Error)
+ -> { @s.scan(/\d/); @s.unscan }.should raise_error(StringScanner::Error)
+ end
+end
diff --git a/spec/ruby/library/stringscanner/values_at_spec.rb b/spec/ruby/library/stringscanner/values_at_spec.rb
new file mode 100644
index 0000000000..14d4a5f6a7
--- /dev/null
+++ b/spec/ruby/library/stringscanner/values_at_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require 'strscan'
+
+describe "StringScanner#captures" do
+ before do
+ @s = StringScanner.new('Fri Dec 12 1975 14:39')
+ end
+
+ context "when passed a list of Integers" do
+ it "returns an array containing each value given by one of integers" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+ @s.values_at(0, 1, 2, 3).should == ["Fri Dec 12", "Fri", "Dec", "12"]
+ end
+
+ it "returns nil value for any integer that is out of range" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+ @s.values_at(4).should == [nil]
+ @s.values_at(-5).should == [nil]
+ end
+ end
+
+ context "when passed names" do
+ it 'slices captures with the given names' do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+ @s.values_at(:wday, :month, :day).should == ["Fri", "Dec", "12"]
+ end
+
+ it 'slices captures with the given String names' do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+ @s.values_at("wday", "month", "day").should == ["Fri", "Dec", "12"]
+ end
+
+ it "raises IndexError when given unknown name" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+
+ -> {
+ @s.values_at("foo")
+ }.should raise_error(IndexError, "undefined group name reference: foo")
+ end
+ end
+
+ it 'supports mixing of names and indices' do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+ @s.values_at(1, "wday", 2, "month", 3, "day").should == ["Fri", "Fri", "Dec", "Dec", "12", "12"]
+ end
+
+ it "returns a new empty Array if no arguments given" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+ @s.values_at().should == []
+ end
+
+ it "fails when passed arguments of unsupported types" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)
+
+ -> {
+ @s.values_at([])
+ }.should raise_error(TypeError, "no implicit conversion of Array into Integer")
+ end
+
+ it "returns nil if the most recent matching fails" do
+ @s.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\s+)/)
+ @s.values_at(1, 2, 3).should == nil
+ end
+
+ it "returns nil if there is no any matching done" do
+ @s.values_at(1, 2, 3).should == nil
+ end
+end