consolidate_response_test.dart 6.18 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
@TestOn('!chrome')
6

7 8
import 'dart:async';
import 'dart:io';
9
import 'dart:typed_data';
10 11

import 'package:flutter/foundation.dart';
12
import 'package:flutter_test/flutter_test.dart';
13

14 15
final Uint8List chunkOne = Uint8List.fromList(<int>[0, 1, 2, 3, 4, 5]);
final Uint8List chunkTwo = Uint8List.fromList(<int>[6, 7, 8, 9, 10]);
16

17
void main() {
18
  group(consolidateHttpClientResponseBytes, () {
19
    late MockHttpClientResponse response;
20 21

    setUp(() {
22 23 24 25
      response = MockHttpClientResponse(
        chunkOne: chunkOne,
        chunkTwo: chunkTwo,
      );
26 27
    });

28
    test('Converts an HttpClientResponse with contentLength to bytes', () async {
29
      response.contentLength = chunkOne.length + chunkTwo.length;
30 31
      final List<int> bytes =
          await consolidateHttpClientResponseBytes(response);
32 33 34 35

      expect(bytes, <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
    });

36
    test('Converts a compressed HttpClientResponse with contentLength to bytes', () async {
37
      response.contentLength = chunkOne.length;
38 39
      final List<int> bytes =
          await consolidateHttpClientResponseBytes(response);
40 41 42 43

      expect(bytes, <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
    });

44
    test('Converts an HttpClientResponse without contentLength to bytes', () async {
45
      response.contentLength = -1;
46 47
      final List<int> bytes =
          await consolidateHttpClientResponseBytes(response);
48 49 50 51

      expect(bytes, <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
    });

52 53
    test('Notifies onBytesReceived for every chunk of bytes', () async {
      final int syntheticTotal = (chunkOne.length + chunkTwo.length) * 2;
54
      response.contentLength = syntheticTotal;
55
      final List<int?> records = <int?>[];
56
      await consolidateHttpClientResponseBytes(
57
        response,
58 59
        onBytesReceived: (int cumulative, int? total) {
          records.addAll(<int?>[cumulative, total]);
60 61 62 63 64 65 66 67 68 69 70
        },
      );

      expect(records, <int>[
        chunkOne.length,
        syntheticTotal,
        chunkOne.length + chunkTwo.length,
        syntheticTotal,
      ]);
    });

71
    test('forwards errors from HttpClientResponse', () async {
72 73
      response = MockHttpClientResponse(error: Exception('Test Error'));
      response.contentLength = -1;
74

Dan Field's avatar
Dan Field committed
75
      expect(consolidateHttpClientResponseBytes(response), throwsException);
76
    });
77 78

    test('Propagates error to Future return value if onBytesReceived throws', () async {
79
      response.contentLength = -1;
80
      final Future<List<int>> result = consolidateHttpClientResponseBytes(
81
        response,
82
        onBytesReceived: (int cumulative, int? total) {
83 84 85 86 87 88 89 90
          throw 'misbehaving callback';
        },
      );

      expect(result, throwsA(equals('misbehaving callback')));
    });

    group('when gzipped', () {
91 92 93
      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);
94 95

      setUp(() {
96 97
        response = MockHttpClientResponse(chunkOne: gzippedChunkOne, chunkTwo: gzippedChunkTwo);
        response.compressionState = HttpClientResponseCompressionState.compressed;
98 99
      });

100
      test('Uncompresses GZIP bytes if autoUncompress is true and response.compressionState is compressed', () async {
101
        response.contentLength = gzipped.length;
102
        final List<int> bytes = await consolidateHttpClientResponseBytes(response);
103 104 105
        expect(bytes, <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
      });

106
      test('returns gzipped bytes if autoUncompress is false and response.compressionState is compressed', () async {
107
        response.contentLength = gzipped.length;
108
        final List<int> bytes = await consolidateHttpClientResponseBytes(response, autoUncompress: false);
109 110 111 112
        expect(bytes, gzipped);
      });

      test('Notifies onBytesReceived with gzipped numbers', () async {
113
        response.contentLength = gzipped.length;
114
        final List<int?> records = <int?>[];
115
        await consolidateHttpClientResponseBytes(
116
          response,
117 118
          onBytesReceived: (int cumulative, int? total) {
            records.addAll(<int?>[cumulative, total]);
119 120 121 122 123 124 125 126 127 128 129
          },
        );

        expect(records, <int>[
          gzippedChunkOne.length,
          gzipped.length,
          gzipped.length,
          gzipped.length,
        ]);
      });

130
      test('Notifies onBytesReceived with expectedContentLength of -1 if response.compressionState is decompressed', () async {
131
        final int syntheticTotal = (chunkOne.length + chunkTwo.length) * 2;
132 133
        response.compressionState = HttpClientResponseCompressionState.decompressed;
        response.contentLength = syntheticTotal;
134
        final List<int?> records = <int?>[];
135
        await consolidateHttpClientResponseBytes(
136
          response,
137 138
          onBytesReceived: (int cumulative, int? total) {
            records.addAll(<int?>[cumulative, total]);
139 140 141
          },
        );

142
        expect(records, <int?>[
143
          gzippedChunkOne.length,
144
          null,
145
          gzipped.length,
146
          null,
147 148 149
        ]);
      });
    });
150
  }, skip: kIsWeb);
151 152
}

153
class MockHttpClientResponse extends Fake implements HttpClientResponse {
154
  MockHttpClientResponse({this.error, this.chunkOne = const <int>[], this.chunkTwo = const <int>[]});
155 156 157 158 159 160 161 162 163 164 165 166

  final dynamic error;
  final List<int> chunkOne;
  final List<int> chunkTwo;

  @override
  int contentLength = 0;

  @override
  HttpClientResponseCompressionState compressionState = HttpClientResponseCompressionState.notCompressed;

  @override
167
  StreamSubscription<List<int>> listen(void Function(List<int> event)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError}) {
168
    if (error != null) {
169
      return Stream<List<int>>.fromFuture(Future<List<int>>.error(error as Object)).listen(
170 171 172 173 174 175
          onData,
          onDone: onDone,
          onError: onError,
          cancelOnError: cancelOnError,
        );
    }
176
    return Stream<List<int>>.fromIterable(<List<int>>[chunkOne, chunkTwo]).listen(
177 178 179 180 181 182 183
      onData,
      onDone: onDone,
      onError: onError,
      cancelOnError: cancelOnError,
    );
  }
}