|
5 | 5 | return if Prism::BACKEND == :FFI |
6 | 6 |
|
7 | 7 | 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 |
111 | 47 | 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 } |
118 | 52 | 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) |
131 | 67 | end |
132 | 68 | end |
133 | 69 |
|
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 |
144 | 115 | end |
145 | 116 |
|
146 | 117 | private |
147 | 118 |
|
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) |
151 | 122 |
|
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 |
157 | 126 |
|
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 |
162 | 131 | end |
163 | 132 | end |
164 | 133 | end |
|
0 commit comments