Skip to content

Commit 0b1d9ff

Browse files
author
boris
committed
add exhaustive tests for ANSI-C quoted strings
1 parent cb7bc4e commit 0b1d9ff

1 file changed

Lines changed: 107 additions & 2 deletions

File tree

‎test/string-utils.cjs‎

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
/* global describe, it */
1+
/* global describe, it, xit */
22

33
const { strictEqual } = require('assert')
4-
const { camelCase, decamelize, looksLikeNumber } = require('../build/index.cjs')
4+
const util = require('util')
5+
const exec = util.promisify(require('child_process').exec)
6+
const {
7+
camelCase,
8+
decamelize,
9+
looksLikeNumber,
10+
parseAnsiCQuotedString
11+
} = require('../build/index.cjs')
512

613
describe('string-utils', function () {
714
describe('camelCase', () => {
@@ -36,4 +43,102 @@ describe('string-utils', function () {
3643
strictEqual(looksLikeNumber('apple'), false)
3744
})
3845
})
46+
47+
describe('parseAnsiCQuotedString', () => {
48+
async function check (ansiString, encoding = 'utf8') {
49+
const result = parseAnsiCQuotedString(ansiString)
50+
51+
const resultBuffer = Buffer.from(result, encoding)
52+
const firstNull = resultBuffer.indexOf(0)
53+
const resultUpToFirstNull = firstNull === -1 ? resultBuffer : resultBuffer.slice(0, firstNull)
54+
55+
const { stdout } = await exec(
56+
'printf "%s" ' + ansiString,
57+
{ shell: '/bin/bash', encoding: 'buffer' }
58+
)
59+
60+
if (!resultUpToFirstNull.equals(stdout)) {
61+
console.log(JSON.stringify(ansiString), ansiString[4], ansiString.codePointAt(4))
62+
console.log(resultUpToFirstNull, 'should be', stdout)
63+
if (!resultUpToFirstNull.equals(resultBuffer)) {
64+
console.log('(', resultUpToFirstNull, 'is actually', resultBuffer, ')')
65+
}
66+
console.log()
67+
}
68+
69+
resultUpToFirstNull.should.deep.equal(stdout)
70+
}
71+
72+
async function checkAll (prefix, value, maxLength, encoding = 'utf8') {
73+
await check("$'\\" + prefix + value + "'", encoding)
74+
while (value.length < maxLength) {
75+
value = '0' + value
76+
await check("$'\\" + prefix + value + "'", encoding)
77+
if (value.toLowerCase() !== value) {
78+
await check("$'\\" + prefix + value.toLowerCase() + "'", encoding)
79+
}
80+
}
81+
}
82+
83+
// Skip exhaustive testing because it takes a long time
84+
xit('parses all octal codes in ANSI-C quoted strings like bash', async () => {
85+
for (let i = 0; i <= 0o777; i++) {
86+
if (i % 256 === 0) { // null character
87+
continue
88+
}
89+
await checkAll('', i.toString(8), 3, 'ascii')
90+
}
91+
}).timeout(5000)
92+
93+
xit('parses all hex codes in ANSI-C quoted strings like bash', async () => {
94+
for (let i = 1; i <= 0xFF; i++) { // start at 1 to skip null
95+
await checkAll('x', i.toString(16), 2, 'ascii')
96+
}
97+
98+
for (let i = 1; i <= 0xFFFF; i++) {
99+
// The high and low surrogates "\ud800" to "\udfff" fail this test
100+
// because when the resulting string is encoded to a Buffer with UTF-8,
101+
// it becomes a "?" replacement character, but the underlying data is correct.
102+
// https://en.wikipedia.org/wiki/UTF-16#U+D800_to_U+DFFF
103+
if (i >= 0xD800 && i <= 0xDFFF) {
104+
continue
105+
}
106+
await checkAll('u', i.toString(16), 4)
107+
}
108+
109+
for (let i = 1; i <= 0x10FFFF; i++) {
110+
if (i >= 0xD800 && i <= 0xDFFF) {
111+
continue
112+
}
113+
await checkAll('U', i.toString(16), 8)
114+
}
115+
116+
// TODO: echo -n $'\U110000' produces f4 90 80 80 but String.fromCodePoint(0x110000)
117+
// raises an error in node: Uncaught RangeError: Invalid code point 1114112
118+
}).timeout(5 * 60 * 60 * 1000)
119+
120+
xit('parses control codes in ANSI-C quoted strings like bash', async () => {
121+
// We skip \x01 and \x7F because those don't make sense as control codes
122+
// and because bash parses them differently.
123+
for (let i = 2; i < 0x7F; i++) {
124+
const chr = String.fromCodePoint(i)
125+
if (chr === "'" || chr === '\\') {
126+
continue
127+
}
128+
await check("$'\\c" + chr + "'")
129+
}
130+
}).timeout(60 * 1000)
131+
132+
// We skip this because we do not handle non-ASCII characters after a control code,
133+
// unlike bash.
134+
xit('parses all non-ASCII codes in ANSI-C quoted strings like bash', async () => {
135+
for (let i = 0x80; i <= 0x10FFFF; i++) {
136+
const chr = String.fromCodePoint(i)
137+
if (chr === "'" || chr === '\\' || (i >= 0xD800 && i <= 0xDFFF)) {
138+
continue
139+
}
140+
await check("$'\\c" + chr + "'")
141+
}
142+
}).timeout(5 * 60 * 60 * 1000)
143+
})
39144
})

0 commit comments

Comments
 (0)