Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

// META: global=window,worker
// META: script=resources/readable-stream-from-array.js
// META: script=resources/readable-stream-to-array.js
'use strict';
const inputString = 'I \u{1F499} streams';
const expectedOutputBytes = [0x49, 0x20, 0xf0, 0x9f, 0x92, 0x99, 0x20, 0x73,
0x74, 0x72, 0x65, 0x61, 0x6d, 0x73];
// This is a character that must be represented in two code units in a string,
// ie. it is not in the Basic Multilingual Plane.
const astralCharacter = '\u{1F499}'; // BLUE HEART
const astralCharacterEncoded = [0xf0, 0x9f, 0x92, 0x99];
const leading = astralCharacter[0];
const trailing = astralCharacter[1];
const replacementEncoded = [0xef, 0xbf, 0xbd];
// These tests assume that the implementation correctly classifies leading and
// trailing surrogates and treats all the code units in each set equivalently.
const testCases = [
{
input: [inputString],
output: [expectedOutputBytes],
description: 'encoding one string of UTF-8 should give one complete chunk'
},
{
input: [leading, trailing],
output: [astralCharacterEncoded],
description: 'a character split between chunks should be correctly encoded'
},
{
input: [leading, trailing + astralCharacter],
output: [astralCharacterEncoded.concat(astralCharacterEncoded)],
description: 'a character following one split between chunks should be ' +
'correctly encoded'
},
{
input: [leading, trailing + leading, trailing],
output: [astralCharacterEncoded, astralCharacterEncoded],
description: 'two consecutive astral characters each split down the ' +
'middle should be correctly reassembled'
},
{
input: [leading, trailing + leading + leading, trailing],
output: [astralCharacterEncoded.concat(replacementEncoded), astralCharacterEncoded],
description: 'two consecutive astral characters each split down the ' +
'middle with an invalid surrogate in the middle should be correctly ' +
'encoded'
},
{
input: [leading],
output: [replacementEncoded],
description: 'a stream ending in a leading surrogate should emit a ' +
'replacement character as a final chunk'
},
{
input: [leading, astralCharacter],
output: [replacementEncoded.concat(astralCharacterEncoded)],
description: 'an unmatched surrogate at the end of a chunk followed by ' +
'an astral character in the next chunk should be replaced with ' +
'the replacement character at the start of the next output chunk'
},
{
input: [leading, 'A'],
output: [replacementEncoded.concat([65])],
description: 'an unmatched surrogate at the end of a chunk followed by ' +
'an ascii character in the next chunk should be replaced with ' +
'the replacement character at the start of the next output chunk'
},
{
input: [leading, leading, trailing],
output: [replacementEncoded, astralCharacterEncoded],
description: 'an unmatched surrogate at the end of a chunk followed by ' +
'a plane 1 character split into two chunks should result in ' +
'the encoded plane 1 character appearing in the last output chunk'
},
{
input: [leading, leading],
output: [replacementEncoded, replacementEncoded],
description: 'two leading chunks should result in two replacement ' +
'characters'
},
{
input: [leading + leading, trailing],
output: [replacementEncoded, astralCharacterEncoded],
description: 'a non-terminal unpaired leading surrogate should ' +
'immediately be replaced'
},
{
input: [trailing, astralCharacter],
output: [replacementEncoded, astralCharacterEncoded],
description: 'a terminal unpaired trailing surrogate should ' +
'immediately be replaced'
},
{
input: [leading, '', trailing],
output: [astralCharacterEncoded],
description: 'a leading surrogate chunk should be carried past empty chunks'
},
{
input: [leading, ''],
output: [replacementEncoded],
description: 'a leading surrogate chunk should error when it is clear ' +
'it didn\'t form a pair'
},
{
input: [''],
output: [],
description: 'an empty string should result in no output chunk'
},
{
input: ['', inputString],
output: [expectedOutputBytes],
description: 'a leading empty chunk should be ignored'
},
{
input: [inputString, ''],
output: [expectedOutputBytes],
description: 'a trailing empty chunk should be ignored'
},
{
input: ['A'],
output: [[65]],
description: 'a plain ASCII chunk should be converted'
},
{
input: ['\xff'],
output: [[195, 191]],
description: 'characters in the ISO-8859-1 range should be encoded correctly'
},
];
for (const {input, output, description} of testCases) {
promise_test(async () => {
const inputStream = readableStreamFromArray(input);
const outputStream = inputStream.pipeThrough(new TextEncoderStream());
const chunkArray = await readableStreamToArray(outputStream);
assert_equals(chunkArray.length, output.length,
'number of chunks should match');
for (let i = 0; i < output.length; ++i) {
assert_array_equals(chunkArray[i], output[i], `chunk ${i} should match`);
}
}, description);
}