Skip to content

Commit b7f745a

Browse files
committed
Refactor unparser specs to accommodate assertions against non-default parser/builder options
This commit refactors the specs in two ways: 1. Accommodate non-default parser/builder options such as `emit_file_line_as_literals` For example, specifying parser builder options: with_builder_options({ emit_file_line_as_literals: true }) do assert_generates '__FILE__', '"(string)"' assert_generates '__LINE__', '1' end with_builder_options({ emit_file_line_as_literals: false }) do assert_source '__FILE__' assert_source '__LINE__' end 2. Simpler accommodation for future Ruby changes so assertions can easily be targeted at versions of Ruby after a certain version or prior to a certain version. For example, when 2.4 is added, we can block certain behavior to end at 2.3, and other behavior to start at 2.4. For example, specifying Ruby versions for particular assertions. with_ruby_versions(beginning_at: '2.3') do assert_terminated 'a&.b' assert_terminated 'a&.b(c)' end with_ruby_versions(only: '2.3') do # ... asertions end with_ruby_versions(ending_at: '2.3') do # ... assertions end
1 parent bf07696 commit b7f745a

2 files changed

Lines changed: 93 additions & 20 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module ParserClassGenerator
2+
def self.generate_with_options(base_parser_class, builder_options)
3+
# This builds a dynamic subclass of the base_parser_class (e.g. Parser::Ruby23)
4+
# and overrides the default_parser method to return a parser whose builder
5+
# has various options set.
6+
#
7+
# Currently the only builder option is :emit_file_line_as_literals
8+
9+
Class.new(base_parser_class) do
10+
define_singleton_method(:default_parser) do |*args|
11+
super(*args).tap do |parser|
12+
parser.builder.emit_file_line_as_literals = builder_options[:emit_file_line_as_literals]
13+
end
14+
end
15+
16+
define_singleton_method(:inspect) do
17+
"#{base_parser_class.inspect} with builder options: #{builder_options.inspect}"
18+
end
19+
end
20+
end
21+
end

spec/unit/unparser_spec.rb

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,76 @@
11
require 'spec_helper'
22
require 'parser/all'
3+
require 'support/parser_class_generator'
34

45
describe Unparser, mutant_expression: 'Unparser::Emitter*' do
56
describe '.unparse' do
67

7-
PARSERS = IceNine.deep_freeze(
8+
RUBY_VERSION_PARSERS = IceNine.deep_freeze(
89
'2.1' => Parser::Ruby21,
910
'2.2' => Parser::Ruby22,
1011
'2.3' => Parser::Ruby23
1112
)
1213

13-
RUBIES = PARSERS.keys.freeze
14+
RUBY_VERSIONS = RUBY_VERSION_PARSERS.keys.freeze
1415

15-
def self.parser_for_ruby_version(version)
16-
PARSERS.fetch(version) do
17-
raise "Unrecognized Ruby version #{version}"
16+
def self.builder_options
17+
@builder_options ||= {}
18+
end
19+
20+
def self.builder_options=(options)
21+
@builder_options = options
22+
end
23+
24+
def self.ruby_versions
25+
@ruby_versions ||= RUBY_VERSIONS
26+
end
27+
28+
def self.ruby_versions=(versions)
29+
@ruby_versions = versions
30+
end
31+
32+
def self.with_ruby_versions(beginning_at: nil, ending_at: nil, only: nil)
33+
original_ruby_versions = ruby_versions
34+
if only
35+
self.ruby_versions = only & ruby_versions # intersection
36+
else
37+
if ending_at
38+
idx = ruby_versions.index(ending_at) || fail('Invalid Ruby specified')
39+
self.ruby_versions = ruby_versions[0..idx]
40+
end
41+
if beginning_at
42+
idx = ruby_versions.index(beginning_at) || fail('Invalid Ruby specified')
43+
self.ruby_versions = ruby_versions[idx..-1]
44+
end
1845
end
46+
47+
yield
48+
49+
self.ruby_versions = original_ruby_versions
1950
end
2051

21-
def self.with_versions(versions)
22-
versions.each do |version|
23-
yield parser_for_ruby_version(version)
52+
def self.current_parsers
53+
ruby_versions.map do |ruby_version|
54+
if builder_options != {}
55+
ParserClassGenerator.generate_with_options(parser_for_ruby_version(ruby_version), builder_options)
56+
else
57+
parser_for_ruby_version(ruby_version)
58+
end
59+
end
60+
end
61+
62+
def self.with_builder_options(options)
63+
original_options = builder_options
64+
self.builder_options = builder_options.merge(options)
65+
66+
yield
67+
68+
self.builder_options = original_options
69+
end
70+
71+
def self.parser_for_ruby_version(version)
72+
RUBY_VERSION_PARSERS.fetch(version) do
73+
raise "Unrecognized Ruby version #{version}"
2474
end
2575
end
2676

@@ -50,14 +100,14 @@ def self.assert_unterminated(expression)
50100
assert_source("(#{expression}).foo")
51101
end
52102

53-
def self.assert_terminated(expression, rubies = RUBIES)
54-
assert_source(expression, rubies)
55-
assert_source("foo(#{expression})", rubies)
56-
assert_source("#{expression}.foo", rubies)
103+
def self.assert_terminated(expression)
104+
assert_source(expression)
105+
assert_source("foo(#{expression})")
106+
assert_source("#{expression}.foo")
57107
end
58108

59-
def self.assert_generates(ast_or_string, expected, versions = RUBIES)
60-
with_versions(versions) do |parser|
109+
def self.assert_generates(ast_or_string, expected)
110+
current_parsers.each do |parser|
61111
it "should generate #{ast_or_string} as #{expected} under #{parser.inspect}" do
62112
if ast_or_string.is_a?(String)
63113
expected = strip(expected)
@@ -69,16 +119,16 @@ def self.assert_generates(ast_or_string, expected, versions = RUBIES)
69119
end
70120
end
71121

72-
def self.assert_round_trip(input, versions = RUBIES)
73-
with_versions(versions) do |parser|
122+
def self.assert_round_trip(input)
123+
current_parsers.each do |parser|
74124
it "should round trip #{input} under #{parser.inspect}" do
75125
assert_round_trip(input, parser)
76126
end
77127
end
78128
end
79129

80-
def self.assert_source(input, versions = RUBIES)
81-
assert_round_trip(strip(input), versions)
130+
def self.assert_source(input)
131+
assert_round_trip(strip(input))
82132
end
83133

84134
context 'kwargs' do
@@ -408,8 +458,10 @@ def foo
408458
end
409459

410460
context 'conditional send (csend)' do
411-
assert_terminated 'a&.b', %w(2.3)
412-
assert_terminated 'a&.b(c)', %w(2.3)
461+
with_ruby_versions(beginning_at: '2.3') do
462+
assert_terminated 'a&.b'
463+
assert_terminated 'a&.b(c)'
464+
end
413465
end
414466

415467
context 'send' do

0 commit comments

Comments
 (0)