blob: 95b87567e664a2333b35d1d5a97957a1bc34a60d [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome')
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
final Uint8List chunkOne = Uint8List.fromList(<int>[0, 1, 2, 3, 4, 5]);
final Uint8List chunkTwo = Uint8List.fromList(<int>[6, 7, 8, 9, 10]);
void main() {
group(consolidateHttpClientResponseBytes, () {
late MockHttpClientResponse response;
setUp(() {
response = MockHttpClientResponse(
chunkOne: chunkOne,
chunkTwo: chunkTwo,
);
});
test('Converts an HttpClientResponse with contentLength to bytes', () async {
response.contentLength = chunkOne.length + chunkTwo.length;
final List<int> bytes =
await consolidateHttpClientResponseBytes(response);
expect(bytes, <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
});
test('Converts a compressed HttpClientResponse with contentLength to bytes', () async {
response.contentLength = chunkOne.length;
final List<int> bytes =
await consolidateHttpClientResponseBytes(response);
expect(bytes, <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
});
test('Converts an HttpClientResponse without contentLength to bytes', () async {
response.contentLength = -1;
final List<int> bytes =
await consolidateHttpClientResponseBytes(response);
expect(bytes, <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
});
test('Notifies onBytesReceived for every chunk of bytes', () async {
final int syntheticTotal = (chunkOne.length + chunkTwo.length) * 2;
response.contentLength = syntheticTotal;
final List<int?> records = <int?>[];
await consolidateHttpClientResponseBytes(
response,
onBytesReceived: (int cumulative, int? total) {
records.addAll(<int?>[cumulative, total]);
},
);
expect(records, <int>[
chunkOne.length,
syntheticTotal,
chunkOne.length + chunkTwo.length,
syntheticTotal,
]);
});
test('forwards errors from HttpClientResponse', () async {
response = MockHttpClientResponse(error: Exception('Test Error'));
response.contentLength = -1;
expect(consolidateHttpClientResponseBytes(response), throwsException);
});
test('Propagates error to Future return value if onBytesReceived throws', () async {
response.contentLength = -1;
final Future<List<int>> result = consolidateHttpClientResponseBytes(
response,
onBytesReceived: (int cumulative, int? total) {
throw 'misbehaving callback';
},
);
expect(result, throwsA(equals('misbehaving callback')));
});
group('when gzipped', () {
final List<int> gzipped = gzip.encode(chunkOne.followedBy(chunkTwo).toList());
final List<int> gzippedChunkOne = gzipped.sublist(0, gzipped.length ~/ 2);
final List<int> gzippedChunkTwo = gzipped.sublist(gzipped.length ~/ 2);
setUp(() {
response = MockHttpClientResponse(chunkOne: gzippedChunkOne, chunkTwo: gzippedChunkTwo);
response.compressionState = HttpClientResponseCompressionState.compressed;
});
test('Uncompresses GZIP bytes if autoUncompress is true and response.compressionState is compressed', () async {
response.contentLength = gzipped.length;
final List<int> bytes = await consolidateHttpClientResponseBytes(response);
expect(bytes, <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
});
test('returns gzipped bytes if autoUncompress is false and response.compressionState is compressed', () async {
response.contentLength = gzipped.length;
final List<int> bytes = await consolidateHttpClientResponseBytes(response, autoUncompress: false);
expect(bytes, gzipped);
});
test('Notifies onBytesReceived with gzipped numbers', () async {
response.contentLength = gzipped.length;
final List<int?> records = <int?>[];
await consolidateHttpClientResponseBytes(
response,
onBytesReceived: (int cumulative, int? total) {
records.addAll(<int?>[cumulative, total]);
},
);
expect(records, <int>[
gzippedChunkOne.length,
gzipped.length,
gzipped.length,
gzipped.length,
]);
});
test('Notifies onBytesReceived with expectedContentLength of -1 if response.compressionState is decompressed', () async {
final int syntheticTotal = (chunkOne.length + chunkTwo.length) * 2;
response.compressionState = HttpClientResponseCompressionState.decompressed;
response.contentLength = syntheticTotal;
final List<int?> records = <int?>[];
await consolidateHttpClientResponseBytes(
response,
onBytesReceived: (int cumulative, int? total) {
records.addAll(<int?>[cumulative, total]);
},
);
expect(records, <int?>[
gzippedChunkOne.length,
null,
gzipped.length,
null,
]);
});
});
});
}
class MockHttpClientResponse extends Fake implements HttpClientResponse {
MockHttpClientResponse({this.error, this.chunkOne = const <int>[], this.chunkTwo = const <int>[]});
final dynamic error;
final List<int> chunkOne;
final List<int> chunkTwo;
@override
int contentLength = 0;
@override
HttpClientResponseCompressionState compressionState = HttpClientResponseCompressionState.notCompressed;
@override
StreamSubscription<List<int>> listen(void Function(List<int> event)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError}) {
if (error != null) {
return Stream<List<int>>.fromFuture(Future<List<int>>.error(error as Object)).listen(
onData,
onDone: onDone,
onError: onError,
cancelOnError: cancelOnError,
);
}
return Stream<List<int>>.fromIterable(<List<int>>[chunkOne, chunkTwo]).listen(
onData,
onDone: onDone,
onError: onError,
cancelOnError: cancelOnError,
);
}
}