# -*- encoding: utf-8 -*-
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

describe :string_match_escaped_literal, shared: true do
  not_supported_on :opal do
    it "matches a literal Regexp that uses ASCII-only UTF-8 escape sequences" do
      "a b".match(/([\u{20}-\u{7e}])/)[0].should == "a"
    end
  end
end

describe "String#=~" do
  it "behaves the same way as index() when given a regexp" do
    ("rudder" =~ /udder/).should == "rudder".index(/udder/)
    ("boat" =~ /[^fl]oat/).should == "boat".index(/[^fl]oat/)
    ("bean" =~ /bag/).should == "bean".index(/bag/)
    ("true" =~ /false/).should == "true".index(/false/)
  end

  it "raises a TypeError if a obj is a string" do
    -> { "some string" =~ "another string" }.should.raise(TypeError)
    -> { "a" =~ StringSpecs::MyString.new("b")          }.should.raise(TypeError)
  end

  it "invokes obj.=~ with self if obj is neither a string nor regexp" do
    str = "w00t"
    obj = mock('x')

    obj.should_receive(:=~).with(str).any_number_of_times.and_return(true)
    str.should =~ obj

    obj = mock('y')
    obj.should_receive(:=~).with(str).any_number_of_times.and_return(false)
    str.should_not =~ obj
  end

  it "sets $~ to MatchData when there is a match and nil when there's none" do
    'hello' =~ /./
    $~[0].should == 'h'

    'hello' =~ /not/
    $~.should == nil
  end

  it "returns the character index of a found match" do
    ("こにちわ" =~ /に/).should == 1
  end

end

describe "String#match" do
  it "matches the pattern against self" do
    'hello'.match(/(.)\1/)[0].should == 'll'
  end

  it_behaves_like :string_match_escaped_literal, :match

  describe "with [pattern, position]" do
    describe "when given a positive position" do
      it "matches the pattern against self starting at an optional index" do
        "01234".match(/(.).(.)/, 1).captures.should == ["1", "3"]
      end

      it "uses the start as a character offset" do
        "零一二三四".match(/(.).(.)/, 1).captures.should == ["一", "三"]
      end
    end

    describe "when given a negative position" do
      it "matches the pattern against self starting at an optional index" do
        "01234".match(/(.).(.)/, -4).captures.should == ["1", "3"]
      end

      it "uses the start as a character offset" do
        "零一二三四".match(/(.).(.)/, -4).captures.should == ["一", "三"]
      end
    end
  end

  describe "when passed a block" do
    it "yields the MatchData" do
      "abc".match(/./) {|m| ScratchPad.record m }
      ScratchPad.recorded.should.is_a?(MatchData)
    end

    it "returns the block result" do
      "abc".match(/./) { :result }.should == :result
    end

    it "does not yield if there is no match" do
      ScratchPad.record []
      "b".match(/a/) {|m| ScratchPad << m }
      ScratchPad.recorded.should == []
    end
  end

  it "tries to convert pattern to a string via to_str" do
    obj = mock('.')
    def obj.to_str() "." end
    "hello".match(obj)[0].should == "h"

    obj = mock('.')
    def obj.respond_to?(type, *) true end
    def obj.method_missing(*args) "." end
    "hello".match(obj)[0].should == "h"
  end

  it "raises a TypeError if pattern is not a regexp or a string" do
    -> { 'hello'.match(10)   }.should.raise(TypeError)
    not_supported_on :opal do
      -> { 'hello'.match(:ell) }.should.raise(TypeError)
    end
  end

  it "converts string patterns to regexps without escaping" do
    'hello'.match('(.)\1')[0].should == 'll'
  end

  it "returns nil if there's no match" do
    'hello'.match('xx').should == nil
  end

  it "matches \\G at the start of the string" do
    'hello'.match(/\Gh/)[0].should == 'h'
    'hello'.match(/\Go/).should == nil
  end

  it "sets $~ to MatchData of match or nil when there is none" do
    'hello'.match(/./)
    $~[0].should == 'h'
    Regexp.last_match[0].should == 'h'

    'hello'.match(/X/)
    $~.should == nil
    Regexp.last_match.should == nil
  end

  it "calls match on the regular expression" do
    # Can't use regexp.should_receive(:match).and_return(:foo) since regexps are frozen
    ScratchPad.clear
    regexp = Class.new(Regexp) {
      def match(*args)
        ScratchPad.record [:match, *args]
        super(*args)
      end
    }.new('.')

    'hello'.match(regexp)
    ScratchPad.recorded.should == [:match, 'hello']
  end
end

describe "String#match?" do
  before :each do
    # Resetting Regexp.last_match
    /DONTMATCH/.match ''
  end

  context "when matches the given regex" do
    it "returns true but does not set Regexp.last_match" do
      'string'.match?(/string/i).should == true
      Regexp.last_match.should == nil
    end
  end

  it "returns false when does not match the given regex" do
    'string'.match?(/STRING/).should == false
  end

  it "takes matching position as the 2nd argument" do
    'string'.match?(/str/i, 0).should == true
    'string'.match?(/str/i, 1).should == false
  end
end
