require 'rubygems/test_case'
require 'rubygems/request_set'
require 'rubygems/request_set/lockfile'
require 'rubygems/request_set/lockfile/tokenizer'
require 'rubygems/request_set/lockfile/parser'

class TestGemRequestSetLockfileTokenizer < Gem::TestCase
  def setup
    super

    @gem_deps_file = 'gem.deps.rb'
    @lock_file = File.expand_path "#{@gem_deps_file}.lock"
  end

  def test_peek
    tokenizer = Gem::RequestSet::Lockfile::Tokenizer.new "\n"

    assert_equal :newline, tokenizer.peek.first

    assert_equal :newline, tokenizer.next_token.first

    assert_equal [:EOF], tokenizer.peek
  end

  def test_skip
    tokenizer = Gem::RequestSet::Lockfile::Tokenizer.new "\n"

    refute_predicate tokenizer, :empty?

    tokenizer.skip :newline

    assert_empty tokenizer
  end

  def test_token_pos
    tokenizer = Gem::RequestSet::Lockfile::Tokenizer.new ''
    assert_equal [5, 0], tokenizer.token_pos(5)

    tokenizer = Gem::RequestSet::Lockfile::Tokenizer.new '', nil, 1, 2
    assert_equal [3, 1], tokenizer.token_pos(5)
  end

  def test_tokenize
    write_lockfile <<-LOCKFILE
GEM
  remote: #{@gem_repo}
  specs:
    a (2)
      b (= 2)
      c (!= 3)
      d (> 4)
      e (< 5)
      f (>= 6)
      g (<= 7)
      h (~> 8)

PLATFORMS
  #{Gem::Platform::RUBY}

DEPENDENCIES
  a
    LOCKFILE

    expected = [
      [:section,     'GEM',                0,  0],
      [:newline,     nil,                  3,  0],

      [:entry,       'remote',             2,  1],
      [:text,        @gem_repo,           10,  1],
      [:newline,     nil,                 34,  1],

      [:entry,       'specs',              2,  2],
      [:newline,     nil,                  8,  2],

      [:text,        'a',                  4,  3],
      [:l_paren,     nil,                  6,  3],
      [:text,        '2',                  7,  3],
      [:r_paren,     nil,                  8,  3],
      [:newline,     nil,                  9,  3],

      [:text,        'b',                  6,  4],
      [:l_paren,     nil,                  8,  4],
      [:requirement, '=',                  9,  4],
      [:text,        '2',                 11,  4],
      [:r_paren,     nil,                 12,  4],
      [:newline,     nil,                 13,  4],

      [:text,        'c',                  6,  5],
      [:l_paren,     nil,                  8,  5],
      [:requirement, '!=',                 9,  5],
      [:text,        '3',                 12,  5],
      [:r_paren,     nil,                 13,  5],
      [:newline,     nil,                 14,  5],

      [:text,        'd',                  6,  6],
      [:l_paren,     nil,                  8,  6],
      [:requirement, '>',                  9,  6],
      [:text,        '4',                 11,  6],
      [:r_paren,     nil,                 12,  6],
      [:newline,     nil,                 13,  6],

      [:text,        'e',                  6,  7],
      [:l_paren,     nil,                  8,  7],
      [:requirement, '<',                  9,  7],
      [:text,        '5',                 11,  7],
      [:r_paren,     nil,                 12,  7],
      [:newline,     nil,                 13,  7],

      [:text,        'f',                  6,  8],
      [:l_paren,     nil,                  8,  8],
      [:requirement, '>=',                 9,  8],
      [:text,        '6',                 12,  8],
      [:r_paren,     nil,                 13,  8],
      [:newline,     nil,                 14,  8],

      [:text,        'g',                  6,  9],
      [:l_paren,     nil,                  8,  9],
      [:requirement, '<=',                 9,  9],
      [:text,        '7',                 12,  9],
      [:r_paren,     nil,                 13,  9],
      [:newline,     nil,                 14,  9],

      [:text,        'h',                  6, 10],
      [:l_paren,     nil,                  8, 10],
      [:requirement, '~>',                 9, 10],
      [:text,        '8',                 12, 10],
      [:r_paren,     nil,                 13, 10],
      [:newline,     nil,                 14, 10],

      [:newline,     nil,                  0, 11],

      [:section,     'PLATFORMS',          0, 12],
      [:newline,     nil,                  9, 12],

      [:text,        Gem::Platform::RUBY,  2, 13],
      [:newline,     nil,                  6, 13],

      [:newline,     nil,                  0, 14],

      [:section,     'DEPENDENCIES',       0, 15],
      [:newline,     nil,                 12, 15],

      [:text,        'a',                  2, 16],
      [:newline,     nil,                  3, 16],
    ]

    assert_equal expected, tokenize_lockfile
  end

  def test_tokenize_capitals
    write_lockfile <<-LOCKFILE
GEM
  remote: #{@gem_repo}
  specs:
    Ab (2)

PLATFORMS
  #{Gem::Platform::RUBY}

DEPENDENCIES
  Ab
    LOCKFILE

    expected = [
      [:section, 'GEM',                0, 0],
      [:newline, nil,                  3, 0],
      [:entry,   'remote',             2, 1],
      [:text,    @gem_repo,           10, 1],
      [:newline, nil,                 34, 1],
      [:entry,   'specs',              2, 2],
      [:newline, nil,                  8, 2],
      [:text,    'Ab',                 4, 3],
      [:l_paren, nil,                  7, 3],
      [:text,    '2',                  8, 3],
      [:r_paren, nil,                  9, 3],
      [:newline, nil,                 10, 3],
      [:newline, nil,                  0, 4],
      [:section, 'PLATFORMS',          0, 5],
      [:newline, nil,                  9, 5],
      [:text,    Gem::Platform::RUBY,  2, 6],
      [:newline, nil,                  6, 6],
      [:newline, nil,                  0, 7],
      [:section, 'DEPENDENCIES',       0, 8],
      [:newline, nil,                 12, 8],
      [:text,    'Ab',                 2, 9],
      [:newline, nil,                  4, 9],
    ]

    assert_equal expected, tokenize_lockfile
  end

  def test_tokenize_conflict_markers
    write_lockfile '<<<<<<<'

    e = assert_raises Gem::RequestSet::Lockfile::ParseError do
      tokenize_lockfile
    end

    assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)",
                 e.message

    write_lockfile '|||||||'

    e = assert_raises Gem::RequestSet::Lockfile::ParseError do
      tokenize_lockfile
    end

    assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)",
                 e.message

    write_lockfile '======='

    e = assert_raises Gem::RequestSet::Lockfile::ParseError do
      tokenize_lockfile
    end

    assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)",
                 e.message

    write_lockfile '>>>>>>>'

    e = assert_raises Gem::RequestSet::Lockfile::ParseError do
      tokenize_lockfile
    end

    assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)",
                 e.message
  end

  def test_tokenize_git
    write_lockfile <<-LOCKFILE
DEPENDENCIES
  a!
    LOCKFILE

    expected = [
      [:section, 'DEPENDENCIES',  0,  0],
      [:newline, nil,            12,  0],

      [:text,    'a',             2,  1],
      [:bang,    nil,             3,  1],
      [:newline, nil,             4,  1],
    ]

    assert_equal expected, tokenize_lockfile
  end

  def test_tokenize_multiple
    write_lockfile <<-LOCKFILE
GEM
  remote: #{@gem_repo}
  specs:
    a (2)
      b (~> 3.0, >= 3.0.1)
    LOCKFILE

    expected = [
      [:section,     'GEM',      0,  0],
      [:newline,     nil,        3,  0],

      [:entry,       'remote',   2,  1],
      [:text,        @gem_repo, 10,  1],
      [:newline,     nil,       34,  1],

      [:entry,       'specs',    2,  2],
      [:newline,     nil,        8,  2],

      [:text,        'a',        4,  3],
      [:l_paren,     nil,        6,  3],
      [:text,        '2',        7,  3],
      [:r_paren,     nil,        8,  3],
      [:newline,     nil,        9,  3],

      [:text,        'b',        6,  4],
      [:l_paren,     nil,        8,  4],
      [:requirement, '~>',       9,  4],
      [:text,        '3.0',     12,  4],
      [:comma,       nil,       15,  4],
      [:requirement, '>=',      17,  4],
      [:text,        '3.0.1',   20,  4],
      [:r_paren,     nil,       25,  4],
      [:newline,     nil,       26,  4],
    ]

    assert_equal expected, tokenize_lockfile
  end

  def test_unget
    tokenizer = Gem::RequestSet::Lockfile::Tokenizer.new "\n"
    tokenizer.unshift :token
    parser = tokenizer.make_parser nil, nil

    assert_equal :token, parser.get
  end

  def write_lockfile lockfile
    open @lock_file, 'w' do |io|
      io.write lockfile
    end
  end

  def tokenize_lockfile
    Gem::RequestSet::Lockfile::Tokenizer.from_file(@lock_file).to_a
  end
end
