blob: cc74a69e8993cddabbae21a3ceb196bf6d4f0c7a [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:convert/convert.dart';
import 'package:test/test.dart';
void main() {
group('encoder', () {
test("doesn't percent-encode unreserved characters", () {
var safeChars = 'abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'0123456789-._~';
expect(percent.encode([...safeChars.codeUnits]), equals(safeChars));
});
test('percent-encodes reserved ASCII characters', () {
expect(percent.encode([...' `{@[,/^}\x7f\x00%'.codeUnits]),
equals('%20%60%7B%40%5B%2C%2F%5E%7D%7F%00%25'));
});
test('percent-encodes non-ASCII characters', () {
expect(percent.encode([0x80, 0xFF]), equals('%80%FF'));
});
test('mixes encoded and unencoded characters', () {
expect(percent.encode([...'a+b=\x80'.codeUnits]), equals('a%2Bb%3D%80'));
});
group('with chunked conversion', () {
test('percent-encodes byte arrays', () {
var results = <String>[];
var controller = StreamController<String>(sync: true);
controller.stream.listen(results.add);
var sink = percent.encoder.startChunkedConversion(controller.sink);
sink.add([...'a+b=\x80'.codeUnits]);
expect(results, equals(['a%2Bb%3D%80']));
sink.add([0x00, 0x01, 0xfe, 0xff]);
expect(results, equals(['a%2Bb%3D%80', '%00%01%FE%FF']));
});
test('handles empty and single-byte lists', () {
var results = <String>[];
var controller = StreamController<String>(sync: true);
controller.stream.listen(results.add);
var sink = percent.encoder.startChunkedConversion(controller.sink);
sink.add([]);
expect(results, equals(['']));
sink.add([0x00]);
expect(results, equals(['', '%00']));
sink.add([]);
expect(results, equals(['', '%00', '']));
});
});
test('rejects non-bytes', () {
expect(() => percent.encode([0x100]), throwsFormatException);
var sink =
percent.encoder.startChunkedConversion(StreamController(sync: true));
expect(() => sink.add([0x100]), throwsFormatException);
});
});
group('decoder', () {
test('converts percent-encoded strings to byte arrays', () {
expect(
percent.decode('a%2Bb%3D%801'), equals([...'a+b=\x801'.codeUnits]));
});
test('supports lowercase letters', () {
expect(percent.decode('a%2bb%3d%80'), equals([...'a+b=\x80'.codeUnits]));
});
test('supports more aggressive encoding', () {
expect(percent.decode('%61%2E%5A'), equals([...'a.Z'.codeUnits]));
});
test('supports less aggressive encoding', () {
var chars = ' `{@[,/^}\x7F\x00';
expect(percent.decode(chars), equals([...chars.codeUnits]));
});
group('with chunked conversion', () {
late List<List<int>> results;
late StringConversionSink sink;
setUp(() {
results = [];
var controller = StreamController<List<int>>(sync: true);
controller.stream.listen(results.add);
sink = percent.decoder.startChunkedConversion(controller.sink);
});
test('converts percent to byte arrays', () {
sink.add('a%2Bb%3D%801');
expect(
results,
equals([
[...'a+b=\x801'.codeUnits]
]));
sink.add('%00%01%FE%FF');
expect(
results,
equals([
[...'a+b=\x801'.codeUnits],
[0x00, 0x01, 0xfe, 0xff]
]));
});
test('supports trailing percents and digits split across chunks', () {
sink.add('ab%');
expect(
results,
equals([
[...'ab'.codeUnits]
]));
sink.add('2');
expect(
results,
equals([
[...'ab'.codeUnits]
]));
sink.add('0cd%2');
expect(
results,
equals([
[...'ab'.codeUnits],
[...' cd'.codeUnits]
]));
sink.add('0');
expect(
results,
equals([
[...'ab'.codeUnits],
[...' cd'.codeUnits],
[...' '.codeUnits]
]));
});
test('supports empty strings', () {
sink.add('');
expect(results, isEmpty);
sink.add('%');
expect(results, equals([[]]));
sink.add('');
expect(results, equals([[]]));
sink.add('2');
expect(results, equals([[]]));
sink.add('');
expect(results, equals([[]]));
sink.add('0');
expect(
results,
equals([
[],
[0x20]
]));
});
test('rejects dangling % detected in close()', () {
sink.add('ab%');
expect(
results,
equals([
[...'ab'.codeUnits]
]));
expect(() => sink.close(), throwsFormatException);
});
test('rejects dangling digit detected in close()', () {
sink.add('ab%2');
expect(
results,
equals([
[...'ab'.codeUnits]
]));
expect(() => sink.close(), throwsFormatException);
});
test('rejects danging % detected in addSlice()', () {
sink.addSlice('ab%', 0, 3, false);
expect(
results,
equals([
[...'ab'.codeUnits]
]));
expect(() => sink.addSlice('ab%', 0, 3, true), throwsFormatException);
});
test('rejects danging digit detected in addSlice()', () {
sink.addSlice('ab%2', 0, 3, false);
expect(
results,
equals([
[...'ab'.codeUnits]
]));
expect(() => sink.addSlice('ab%2', 0, 3, true), throwsFormatException);
});
});
group('rejects non-ASCII character', () {
for (var char in ['\u0141', '\u{10041}']) {
test('"$char"', () {
expect(() => percent.decode('a$char'), throwsFormatException);
expect(() => percent.decode('${char}a'), throwsFormatException);
var sink = percent.decoder
.startChunkedConversion(StreamController(sync: true));
expect(() => sink.add(char), throwsFormatException);
});
}
});
test('rejects % followed by non-hex', () {
expect(() => percent.decode('%z2'), throwsFormatException);
expect(() => percent.decode('%2z'), throwsFormatException);
});
test('rejects dangling % detected in convert()', () {
expect(() => percent.decode('ab%'), throwsFormatException);
});
test('rejects dangling digit detected in convert()', () {
expect(() => percent.decode('ab%2'), throwsFormatException);
});
});
}