Skip to content

Commit e86196d

Browse files
committed
More thoroughly test unescapes
1 parent f7ff3a3 commit e86196d

File tree

1 file changed

+112
-143
lines changed

1 file changed

+112
-143
lines changed

test/prism/unescape_test.rb

Lines changed: 112 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -5,160 +5,129 @@
55
return if Prism::BACKEND == :FFI
66

77
module Prism
8-
class UnescapeNoneTest < TestCase
9-
def test_backslash
10-
assert_unescape_none("\\")
11-
end
12-
13-
def test_single_quote
14-
assert_unescape_none("'")
15-
end
16-
17-
private
18-
19-
def assert_unescape_none(source)
20-
assert_equal(source, Debug.unescape_none(source))
21-
end
22-
end
23-
24-
class UnescapeMinimalTest < TestCase
25-
def test_backslash
26-
assert_unescape_minimal("\\", "\\\\")
27-
end
28-
29-
def test_single_quote
30-
assert_unescape_minimal("'", "\\'")
31-
end
32-
33-
def test_single_char
34-
assert_unescape_minimal("\\a", "\\a")
35-
end
36-
37-
private
38-
39-
def assert_unescape_minimal(expected, source)
40-
assert_equal(expected, Debug.unescape_minimal(source))
41-
end
42-
end
43-
44-
class UnescapeAllTest < TestCase
45-
def test_backslash
46-
assert_unescape_all("\\", "\\\\")
47-
end
48-
49-
def test_single_quote
50-
assert_unescape_all("'", "\\'")
51-
end
52-
53-
def test_single_char
54-
assert_unescape_all("\a", "\\a")
55-
assert_unescape_all("\b", "\\b")
56-
assert_unescape_all("\e", "\\e")
57-
assert_unescape_all("\f", "\\f")
58-
assert_unescape_all("\n", "\\n")
59-
assert_unescape_all("\r", "\\r")
60-
assert_unescape_all("\s", "\\s")
61-
assert_unescape_all("\t", "\\t")
62-
assert_unescape_all("\v", "\\v")
63-
end
64-
65-
def test_octal
66-
assert_unescape_all("\a", "\\7")
67-
assert_unescape_all("#", "\\43")
68-
assert_unescape_all("a", "\\141")
69-
end
70-
71-
def test_hexadecimal
72-
assert_unescape_all("\a", "\\x7")
73-
assert_unescape_all("#", "\\x23")
74-
assert_unescape_all("a", "\\x61")
75-
end
76-
77-
def test_deletes
78-
assert_unescape_all("\x7f", "\\c?")
79-
assert_unescape_all("\x7f", "\\C-?")
80-
end
81-
82-
def test_unicode_codepoint
83-
assert_unescape_all("a", "\\u0061")
84-
assert_unescape_all("Ā", "\\u0100", "UTF-8")
85-
assert_unescape_all("က", "\\u1000", "UTF-8")
86-
assert_unescape_all("တ", "\\u1010", "UTF-8")
87-
88-
assert_nil(unescape_all("\\uxxxx"))
89-
end
90-
91-
def test_unicode_codepoints
92-
assert_unescape_all("a", "\\u{61}")
93-
assert_unescape_all("Ā", "\\u{0100}", "UTF-8")
94-
assert_unescape_all("က", "\\u{1000}", "UTF-8")
95-
assert_unescape_all("တ", "\\u{1010}", "UTF-8")
96-
assert_unescape_all("𐀀", "\\u{10000}", "UTF-8")
97-
assert_unescape_all("𐀐", "\\u{10010}", "UTF-8")
98-
assert_unescape_all("aĀကတ𐀀𐀐", "\\u{ 61\s100\n1000\t1010\r10000\v10010 }", "UTF-8")
99-
100-
assert_nil(unescape_all("\\u{110000}"))
101-
assert_nil(unescape_all("\\u{110000 110001 110002}"))
102-
end
103-
104-
def test_control_characters
105-
each_printable do |chr|
106-
byte = eval("\"\\c#{chr}\"").bytes.first
107-
assert_unescape_all(byte.chr, "\\c#{chr}")
108-
109-
byte = eval("\"\\C-#{chr}\"").bytes.first
110-
assert_unescape_all(byte.chr, "\\C-#{chr}")
8+
class UnescapeTest < TestCase
9+
module Context
10+
class Base
11+
attr_reader :left, :right
12+
13+
def initialize(left, right)
14+
@left = left
15+
@right = right
16+
end
17+
18+
def name
19+
"#{left}#{right}".delete("\n")
20+
end
21+
22+
private
23+
24+
def code(escape)
25+
"#{left}\\#{escape}#{right}".b
26+
end
27+
28+
def ruby(escape)
29+
yield eval(code(escape))
30+
rescue SyntaxError
31+
:error
32+
end
33+
34+
def prism(escape)
35+
result = Prism.parse(code(escape))
36+
37+
if result.success?
38+
yield result.value.statements.body.first
39+
else
40+
:error
41+
end
42+
end
43+
44+
def `(command)
45+
command
46+
end
11147
end
112-
end
113-
114-
def test_meta_characters
115-
each_printable do |chr|
116-
byte = eval("\"\\M-#{chr}\"").bytes.first
117-
assert_unescape_all(byte.chr, "\\M-#{chr}")
48+
49+
class List < Base
50+
def ruby_result(escape) = ruby(escape) { |value| value.first.to_s }
51+
def prism_result(escape) = prism(escape) { |node| node.elements.first.unescaped }
11852
end
119-
end
120-
121-
def test_meta_control_characters
122-
each_printable do |chr|
123-
byte = eval("\"\\M-\\c#{chr}\"").bytes.first
124-
assert_unescape_all(byte.chr, "\\M-\\c#{chr}")
125-
126-
byte = eval("\"\\M-\\C-#{chr}\"").bytes.first
127-
assert_unescape_all(byte.chr, "\\M-\\C-#{chr}")
128-
129-
byte = eval("\"\\c\\M-#{chr}\"").bytes.first
130-
assert_unescape_all(byte.chr, "\\c\\M-#{chr}")
53+
54+
class Symbol < Base
55+
def ruby_result(escape) = ruby(escape, &:to_s)
56+
def prism_result(escape) = prism(escape, &:unescaped)
57+
end
58+
59+
class String < Base
60+
def ruby_result(escape) = ruby(escape, &:itself)
61+
def prism_result(escape) = prism(escape, &:unescaped)
62+
end
63+
64+
class RegExp < Base
65+
def ruby_result(escape) = ruby(escape, &:source)
66+
def prism_result(escape) = prism(escape, &:unescaped)
13167
end
13268
end
13369

134-
def test_escaping_normal_characters
135-
assert_unescape_all("d", "\\d")
136-
assert_unescape_all("g", "\\g")
137-
end
138-
139-
def test_whitespace_escaping_string_list
140-
assert_equal("a b", Debug.unescape_whitespace("a\\ b"))
141-
assert_equal("a\tb", Debug.unescape_whitespace("a\\\tb"))
142-
assert_equal("a\nb", Debug.unescape_whitespace("a\\\nb"))
143-
assert_equal("a\nb", Debug.unescape_whitespace("a\\\r\nb"))
70+
ascii = (0...128).map(&:chr)
71+
ascii8 = (128...256).map(&:chr)
72+
73+
octal = [*("0".."7")]
74+
octal = octal.product(octal).map(&:join).concat(octal.product(octal).product(octal).map(&:join))
75+
76+
hex = [*("a".."f"), *("A".."F"), *("0".."9")]
77+
hex = hex.map { |h| "x#{h}" }.concat(hex.product(hex).map { |h| "x#{h.join}" }).concat(["5", "6"].product(hex.sample(4)).product(hex.sample(4)).product(hex.sample(4)).map { |h| "u#{h.join}" })
78+
79+
hexes = [*("a".."f"), *("A".."F"), *("0".."9")]
80+
hexes = ["5", "6"].product(hexes.sample(2)).product(hexes.sample(2)).product(hexes.sample(2)).map { |h| "u{00#{h.join}}" }
81+
82+
ctrls = ascii.grep(/[[:print:]]/).flat_map { |c| ["C-#{c}", "c#{c}", "M-#{c}", "M-\\C-#{c}", "M-\\c#{c}", "c\\M-#{c}"] }
83+
84+
contexts = [
85+
Context::String.new("?", ""),
86+
Context::String.new("'", "'"),
87+
Context::String.new("\"", "\""),
88+
Context::String.new("%q[", "]"),
89+
Context::String.new("%Q[", "]"),
90+
Context::String.new("%[", "]"),
91+
Context::String.new("`", "`"),
92+
Context::String.new("<<~H\n", "\nH"),
93+
Context::String.new("<<~'H'\n", "\nH"),
94+
Context::String.new("<<~\"H\"\n", "\nH"),
95+
Context::String.new("<<~`H`\n", "\nH"),
96+
Context::List.new("%w[", "]"),
97+
Context::List.new("%W[", "]"),
98+
Context::List.new("%i[", "]"),
99+
Context::List.new("%I[", "]"),
100+
Context::Symbol.new("%s[", "]"),
101+
Context::Symbol.new(":'", "'"),
102+
Context::Symbol.new(":\"", "\""),
103+
Context::RegExp.new("/", "/"),
104+
Context::RegExp.new("%r[", "]")
105+
]
106+
107+
escapes = [*ascii, *ascii8, *octal, *hex, *hexes, *ctrls]
108+
109+
contexts.each do |context|
110+
escapes.each do |escape|
111+
define_method(:"test_#{context.name}_#{escape.inspect}") do
112+
assert_unescape(context, escape)
113+
end
114+
end
144115
end
145116

146117
private
147118

148-
def unescape_all(source)
149-
Debug.unescape_all(source)
150-
end
119+
def assert_unescape(context, escape)
120+
expected = context.ruby_result(escape)
121+
actual = context.prism_result(escape)
151122

152-
def assert_unescape_all(expected, source, forced_encoding = nil)
153-
result = unescape_all(source)
154-
result.force_encoding(forced_encoding) if forced_encoding
155-
assert_equal(expected, result)
156-
end
123+
message = -> do
124+
"Expected #{context.name} to unescape #{escape.inspect} to #{expected.inspect}, but got #{actual.inspect}"
125+
end
157126

158-
def each_printable
159-
(1..127).each do |ord|
160-
chr = ord.chr
161-
yield chr if chr.match?(/[[:print:]]/) && chr != " " && chr != "\\"
127+
if expected == :error
128+
assert_equal expected, actual, message
129+
else
130+
assert_equal expected.bytes, actual.bytes, message
162131
end
163132
end
164133
end

0 commit comments

Comments
 (0)