ruby/test/yarp/newline_test.rb
Kevin Newton 439f069b4b [ruby/yarp] Move tests from test/* to test/yarp/* to match CRuby
This should make it easier on the sync to determine what changed
and hopefully result in fewer merge conflicts that have to be
manually resolved.

17d82afbfc
2023-08-25 21:15:49 +00:00

100 lines
2.8 KiB
Ruby

# frozen_string_literal: true
require_relative "test_helper"
return unless defined?(RubyVM::InstructionSequence)
# It is useful to have a diff even if the strings to compare are big
# However, ruby/ruby does not have a version of Test::Unit with access to
# max_diff_target_string_size
if defined?(Test::Unit::Assertions::AssertionMessage)
Test::Unit::Assertions::AssertionMessage.max_diff_target_string_size = 5000
end
class NewlineTest < Test::Unit::TestCase
class NewlineVisitor < YARP::Visitor
attr_reader :source, :newlines
def initialize(source)
@source = source
@newlines = []
end
def visit(node)
newlines << source.line(node.location.start_offset) if node&.newline?
super(node)
end
end
base = File.dirname(__dir__)
Dir["{lib,test}/**/*.rb", base: base].each do |relative|
define_method("test_newline_flags_#{relative}") do
assert_newlines(base, relative)
end
end
private
def assert_newlines(base, relative)
filepath = File.join(base, relative)
source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
expected = rubyvm_lines(source)
result = YARP.parse_file(filepath)
assert_empty result.errors
result.mark_newlines
visitor = NewlineVisitor.new(result.source)
result.value.accept(visitor)
actual = visitor.newlines
source.each_line.with_index(1) do |line, line_number|
# Lines like `while (foo = bar)` result in two line flags in the bytecode
# but only one newline flag in the AST. We need to remove the extra line
# flag from the bytecode to make the test pass.
if line.match?(/while \(/)
index = expected.index(line_number)
expected.delete_at(index) if index
end
# Lines like `foo =` where the value is on the next line result in another
# line flag in the bytecode but only one newline flag in the AST.
if line.match?(/^\s+\w+ =$/)
if source.lines[line_number].match?(/^\s+case/)
actual[actual.index(line_number)] += 1
else
actual.delete_at(actual.index(line_number))
end
end
if line.match?(/^\s+\w+ = \[$/)
if !expected.include?(line_number) && !expected.include?(line_number + 2)
actual[actual.index(line_number)] += 1
end
end
end
assert_equal expected, actual
end
def ignore_warnings
previous_verbosity = $VERBOSE
$VERBOSE = nil
yield
ensure
$VERBOSE = previous_verbosity
end
def rubyvm_lines(source)
queue = [ignore_warnings { RubyVM::InstructionSequence.compile(source) }]
lines = []
while iseq = queue.shift
lines.concat(iseq.trace_points.filter_map { |line, event| line if event == :line })
iseq.each_child { |insn| queue << insn unless insn.label.start_with?("ensure in ") }
end
lines.sort
end
end