11cb0ef41Sopenharmony_ci// META: global=window,worker
21cb0ef41Sopenharmony_ci// META: script=resources/readable-stream-from-array.js
31cb0ef41Sopenharmony_ci// META: script=resources/readable-stream-to-array.js
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci'use strict';
61cb0ef41Sopenharmony_ciconst inputString = 'I \u{1F499} streams';
71cb0ef41Sopenharmony_ciconst expectedOutputBytes = [0x49, 0x20, 0xf0, 0x9f, 0x92, 0x99, 0x20, 0x73,
81cb0ef41Sopenharmony_ci                             0x74, 0x72, 0x65, 0x61, 0x6d, 0x73];
91cb0ef41Sopenharmony_ci// This is a character that must be represented in two code units in a string,
101cb0ef41Sopenharmony_ci// ie. it is not in the Basic Multilingual Plane.
111cb0ef41Sopenharmony_ciconst astralCharacter = '\u{1F499}';  // BLUE HEART
121cb0ef41Sopenharmony_ciconst astralCharacterEncoded = [0xf0, 0x9f, 0x92, 0x99];
131cb0ef41Sopenharmony_ciconst leading = astralCharacter[0];
141cb0ef41Sopenharmony_ciconst trailing = astralCharacter[1];
151cb0ef41Sopenharmony_ciconst replacementEncoded = [0xef, 0xbf, 0xbd];
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ci// These tests assume that the implementation correctly classifies leading and
181cb0ef41Sopenharmony_ci// trailing surrogates and treats all the code units in each set equivalently.
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ciconst testCases = [
211cb0ef41Sopenharmony_ci  {
221cb0ef41Sopenharmony_ci    input: [inputString],
231cb0ef41Sopenharmony_ci    output: [expectedOutputBytes],
241cb0ef41Sopenharmony_ci    description: 'encoding one string of UTF-8 should give one complete chunk'
251cb0ef41Sopenharmony_ci  },
261cb0ef41Sopenharmony_ci  {
271cb0ef41Sopenharmony_ci    input: [leading, trailing],
281cb0ef41Sopenharmony_ci    output: [astralCharacterEncoded],
291cb0ef41Sopenharmony_ci    description: 'a character split between chunks should be correctly encoded'
301cb0ef41Sopenharmony_ci  },
311cb0ef41Sopenharmony_ci  {
321cb0ef41Sopenharmony_ci    input: [leading, trailing + astralCharacter],
331cb0ef41Sopenharmony_ci    output: [astralCharacterEncoded.concat(astralCharacterEncoded)],
341cb0ef41Sopenharmony_ci    description: 'a character following one split between chunks should be ' +
351cb0ef41Sopenharmony_ci        'correctly encoded'
361cb0ef41Sopenharmony_ci  },
371cb0ef41Sopenharmony_ci  {
381cb0ef41Sopenharmony_ci    input: [leading, trailing + leading, trailing],
391cb0ef41Sopenharmony_ci    output: [astralCharacterEncoded, astralCharacterEncoded],
401cb0ef41Sopenharmony_ci    description: 'two consecutive astral characters each split down the ' +
411cb0ef41Sopenharmony_ci        'middle should be correctly reassembled'
421cb0ef41Sopenharmony_ci  },
431cb0ef41Sopenharmony_ci  {
441cb0ef41Sopenharmony_ci    input: [leading, trailing + leading + leading, trailing],
451cb0ef41Sopenharmony_ci    output: [astralCharacterEncoded.concat(replacementEncoded), astralCharacterEncoded],
461cb0ef41Sopenharmony_ci    description: 'two consecutive astral characters each split down the ' +
471cb0ef41Sopenharmony_ci        'middle with an invalid surrogate in the middle should be correctly ' +
481cb0ef41Sopenharmony_ci        'encoded'
491cb0ef41Sopenharmony_ci  },
501cb0ef41Sopenharmony_ci  {
511cb0ef41Sopenharmony_ci    input: [leading],
521cb0ef41Sopenharmony_ci    output: [replacementEncoded],
531cb0ef41Sopenharmony_ci    description: 'a stream ending in a leading surrogate should emit a ' +
541cb0ef41Sopenharmony_ci        'replacement character as a final chunk'
551cb0ef41Sopenharmony_ci  },
561cb0ef41Sopenharmony_ci  {
571cb0ef41Sopenharmony_ci    input: [leading, astralCharacter],
581cb0ef41Sopenharmony_ci    output: [replacementEncoded.concat(astralCharacterEncoded)],
591cb0ef41Sopenharmony_ci    description: 'an unmatched surrogate at the end of a chunk followed by ' +
601cb0ef41Sopenharmony_ci        'an astral character in the next chunk should be replaced with ' +
611cb0ef41Sopenharmony_ci        'the replacement character at the start of the next output chunk'
621cb0ef41Sopenharmony_ci  },
631cb0ef41Sopenharmony_ci  {
641cb0ef41Sopenharmony_ci    input: [leading, 'A'],
651cb0ef41Sopenharmony_ci    output: [replacementEncoded.concat([65])],
661cb0ef41Sopenharmony_ci    description: 'an unmatched surrogate at the end of a chunk followed by ' +
671cb0ef41Sopenharmony_ci        'an ascii character in the next chunk should be replaced with ' +
681cb0ef41Sopenharmony_ci        'the replacement character at the start of the next output chunk'
691cb0ef41Sopenharmony_ci  },
701cb0ef41Sopenharmony_ci  {
711cb0ef41Sopenharmony_ci    input: [leading, leading, trailing],
721cb0ef41Sopenharmony_ci    output: [replacementEncoded, astralCharacterEncoded],
731cb0ef41Sopenharmony_ci    description: 'an unmatched surrogate at the end of a chunk followed by ' +
741cb0ef41Sopenharmony_ci        'a plane 1 character split into two chunks should result in ' +
751cb0ef41Sopenharmony_ci        'the encoded plane 1 character appearing in the last output chunk'
761cb0ef41Sopenharmony_ci  },
771cb0ef41Sopenharmony_ci  {
781cb0ef41Sopenharmony_ci    input: [leading, leading],
791cb0ef41Sopenharmony_ci    output: [replacementEncoded, replacementEncoded],
801cb0ef41Sopenharmony_ci    description: 'two leading chunks should result in two replacement ' +
811cb0ef41Sopenharmony_ci        'characters'
821cb0ef41Sopenharmony_ci  },
831cb0ef41Sopenharmony_ci  {
841cb0ef41Sopenharmony_ci    input: [leading + leading, trailing],
851cb0ef41Sopenharmony_ci    output: [replacementEncoded, astralCharacterEncoded],
861cb0ef41Sopenharmony_ci    description: 'a non-terminal unpaired leading surrogate should ' +
871cb0ef41Sopenharmony_ci        'immediately be replaced'
881cb0ef41Sopenharmony_ci  },
891cb0ef41Sopenharmony_ci  {
901cb0ef41Sopenharmony_ci    input: [trailing, astralCharacter],
911cb0ef41Sopenharmony_ci    output: [replacementEncoded, astralCharacterEncoded],
921cb0ef41Sopenharmony_ci    description: 'a terminal unpaired trailing surrogate should ' +
931cb0ef41Sopenharmony_ci        'immediately be replaced'
941cb0ef41Sopenharmony_ci  },
951cb0ef41Sopenharmony_ci  {
961cb0ef41Sopenharmony_ci    input: [leading, '', trailing],
971cb0ef41Sopenharmony_ci    output: [astralCharacterEncoded],
981cb0ef41Sopenharmony_ci    description: 'a leading surrogate chunk should be carried past empty chunks'
991cb0ef41Sopenharmony_ci  },
1001cb0ef41Sopenharmony_ci  {
1011cb0ef41Sopenharmony_ci    input: [leading, ''],
1021cb0ef41Sopenharmony_ci    output: [replacementEncoded],
1031cb0ef41Sopenharmony_ci    description: 'a leading surrogate chunk should error when it is clear ' +
1041cb0ef41Sopenharmony_ci        'it didn\'t form a pair'
1051cb0ef41Sopenharmony_ci  },
1061cb0ef41Sopenharmony_ci  {
1071cb0ef41Sopenharmony_ci    input: [''],
1081cb0ef41Sopenharmony_ci    output: [],
1091cb0ef41Sopenharmony_ci    description: 'an empty string should result in no output chunk'
1101cb0ef41Sopenharmony_ci  },
1111cb0ef41Sopenharmony_ci  {
1121cb0ef41Sopenharmony_ci    input: ['', inputString],
1131cb0ef41Sopenharmony_ci    output: [expectedOutputBytes],
1141cb0ef41Sopenharmony_ci    description: 'a leading empty chunk should be ignored'
1151cb0ef41Sopenharmony_ci  },
1161cb0ef41Sopenharmony_ci  {
1171cb0ef41Sopenharmony_ci    input: [inputString, ''],
1181cb0ef41Sopenharmony_ci    output: [expectedOutputBytes],
1191cb0ef41Sopenharmony_ci    description: 'a trailing empty chunk should be ignored'
1201cb0ef41Sopenharmony_ci  },
1211cb0ef41Sopenharmony_ci  {
1221cb0ef41Sopenharmony_ci    input: ['A'],
1231cb0ef41Sopenharmony_ci    output: [[65]],
1241cb0ef41Sopenharmony_ci    description: 'a plain ASCII chunk should be converted'
1251cb0ef41Sopenharmony_ci  },
1261cb0ef41Sopenharmony_ci  {
1271cb0ef41Sopenharmony_ci    input: ['\xff'],
1281cb0ef41Sopenharmony_ci    output: [[195, 191]],
1291cb0ef41Sopenharmony_ci    description: 'characters in the ISO-8859-1 range should be encoded correctly'
1301cb0ef41Sopenharmony_ci  },
1311cb0ef41Sopenharmony_ci];
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_cifor (const {input, output, description} of testCases) {
1341cb0ef41Sopenharmony_ci  promise_test(async () => {
1351cb0ef41Sopenharmony_ci    const inputStream = readableStreamFromArray(input);
1361cb0ef41Sopenharmony_ci    const outputStream = inputStream.pipeThrough(new TextEncoderStream());
1371cb0ef41Sopenharmony_ci    const chunkArray = await readableStreamToArray(outputStream);
1381cb0ef41Sopenharmony_ci    assert_equals(chunkArray.length, output.length,
1391cb0ef41Sopenharmony_ci                  'number of chunks should match');
1401cb0ef41Sopenharmony_ci    for (let i = 0; i < output.length; ++i) {
1411cb0ef41Sopenharmony_ci      assert_array_equals(chunkArray[i], output[i], `chunk ${i} should match`);
1421cb0ef41Sopenharmony_ci    }
1431cb0ef41Sopenharmony_ci  }, description);
1441cb0ef41Sopenharmony_ci}
145