# frozen_string_literal: true

# This test is going to use the RubyVM::InstructionSequence class to compile
# local tables and compare against them to ensure we have the same locals in the
# same order. This is important to guarantee that we compile indices correctly
# on CRuby (in terms of compatibility).
#
# There have also been changes made in other versions of Ruby, so we only want
# to test on the most recent versions.
return if !defined?(RubyVM::InstructionSequence) || RUBY_VERSION < "3.2"

# Omit tests if running on a 32-bit machine because there is a bug with how
# Ruby is handling large ISeqs on 32-bit machines
return if RUBY_PLATFORM =~ /i686/

require_relative "test_helper"

class LocalsTest < Test::Unit::TestCase
  invalid = []
  todos = []

  # Invalid break
  invalid << "break.txt"
  invalid << "if.txt"
  invalid << "rescue.txt"
  invalid << "seattlerb/block_break.txt"
  invalid << "unless.txt"
  invalid << "whitequark/break.txt"
  invalid << "whitequark/break_block.txt"

  # Invalid next
  invalid << "next.txt"
  invalid << "seattlerb/block_next.txt"
  invalid << "unparser/corpus/literal/control.txt"
  invalid << "whitequark/next.txt"
  invalid << "whitequark/next_block.txt"

  # Invalid redo
  invalid << "keywords.txt"
  invalid << "whitequark/redo.txt"

  # Invalid retry
  invalid << "whitequark/retry.txt"

  # Invalid yield
  invalid << "seattlerb/dasgn_icky2.txt"
  invalid << "seattlerb/yield_arg.txt"
  invalid << "seattlerb/yield_call_assocs.txt"
  invalid << "seattlerb/yield_empty_parens.txt"
  invalid << "unparser/corpus/literal/yield.txt"
  invalid << "whitequark/args_assocs.txt"
  invalid << "whitequark/args_assocs_legacy.txt"
  invalid << "whitequark/yield.txt"
  invalid << "yield.txt"

  # Dead code eliminated
  invalid << "whitequark/ruby_bug_10653.txt"

  # case :a
  # in Symbol(*lhs, x, *rhs)
  # end
  todos << "seattlerb/case_in.txt"

  # <<~HERE
  #   #{<<~THERE}
  #   THERE
  # HERE
  todos << "seattlerb/heredoc_nested.txt"

  base = File.join(__dir__, "fixtures")
  skips = invalid | todos

  Dir["**/*.txt", base: base].each do |relative|
    next if skips.include?(relative)

    filepath = File.join(base, relative)
    define_method("test_#{relative}") { assert_locals(filepath) }
  end

  def setup
    @previous_default_external = Encoding.default_external
    ignore_warnings { Encoding.default_external = Encoding::UTF_8 }
  end

  def teardown
    ignore_warnings { Encoding.default_external = @previous_default_external }
  end

  private

  def assert_locals(filepath)
    source = File.read(filepath)

    expected = YARP.const_get(:Debug).cruby_locals(source)
    actual = YARP.const_get(:Debug).yarp_locals(source)

    assert_equal(expected, actual)
  end

  def ignore_warnings
    previous_verbosity = $VERBOSE
    $VERBOSE = nil
    yield
  ensure
    $VERBOSE = previous_verbosity
  end
end
