image_provider_test.dart 5.72 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
import 'dart:async';
6
import 'dart:io';
7 8
import 'dart:typed_data';
import 'dart:ui';
9

10
import 'package:file/memory.dart';
11
import 'package:flutter/foundation.dart';
12
import 'package:flutter/painting.dart';
13
import 'package:flutter/services.dart';
14 15
import 'package:flutter_test/flutter_test.dart';

16
import '../image_data.dart';
17
import '../rendering/rendering_tester.dart';
18
import 'mocks_for_image_cache.dart';
19

20
void main() {
21
  TestRenderingFlutterBinding.ensureInitialized();
22

23
  FlutterExceptionHandler? oldError;
24 25 26 27 28 29
  setUp(() {
    oldError = FlutterError.onError;
  });

  tearDown(() {
    FlutterError.onError = oldError;
30 31
    PaintingBinding.instance.imageCache.clear();
    PaintingBinding.instance.imageCache.clearLiveImages();
32 33
  });

34 35 36 37 38 39 40 41 42
  test('obtainKey errors will be caught', () async {
    final ImageProvider imageProvider = ObtainKeyErrorImageProvider();
    final Completer<bool> caughtError = Completer<bool>();
    FlutterError.onError = (FlutterErrorDetails details) {
      caughtError.complete(false);
    };
    final ImageStream stream = imageProvider.resolve(ImageConfiguration.empty);
    stream.addListener(ImageStreamListener((ImageInfo info, bool syncCall) {
      caughtError.complete(false);
43
    }, onError: (dynamic error, StackTrace? stackTrace) {
44 45 46 47
      caughtError.complete(true);
    }));
    expect(await caughtError.future, true);
  });
48

49 50 51 52 53 54 55
  test('obtainKey errors will be caught - check location', () async {
    final ImageProvider imageProvider = ObtainKeyErrorImageProvider();
    final Completer<bool> caughtError = Completer<bool>();
    FlutterError.onError = (FlutterErrorDetails details) {
      caughtError.complete(true);
    };
    await imageProvider.obtainCacheStatus(configuration: ImageConfiguration.empty);
56

57 58
    expect(await caughtError.future, true);
  });
59

60 61 62 63
  test('File image with empty file throws expected error and evicts from cache', () async {
    final Completer<StateError> error = Completer<StateError>();
    FlutterError.onError = (FlutterErrorDetails details) {
      error.complete(details.exception as StateError);
64
    };
65 66 67
    final MemoryFileSystem fs = MemoryFileSystem();
    final File file = fs.file('/empty.png')..createSync(recursive: true);
    final FileImage provider = FileImage(file);
68

69 70
    expect(imageCache.statusForKey(provider).untracked, true);
    expect(imageCache.pendingImageCount, 0);
71

72
    provider.resolve(ImageConfiguration.empty);
73

74 75
    expect(imageCache.statusForKey(provider).pending, true);
    expect(imageCache.pendingImageCount, 1);
76

77
    expect(await error.future, isStateError);
78 79
    expect(imageCache.statusForKey(provider).untracked, true);
    expect(imageCache.pendingImageCount, 0);
80 81
  });

82 83 84 85 86 87 88 89 90
  test('File image with empty file throws expected error (load)', () async {
    final Completer<StateError> error = Completer<StateError>();
    FlutterError.onError = (FlutterErrorDetails details) {
      error.complete(details.exception as StateError);
    };
    final MemoryFileSystem fs = MemoryFileSystem();
    final File file = fs.file('/empty.png')..createSync(recursive: true);
    final FileImage provider = FileImage(file);

91
    expect(provider.load(provider, (Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) async {
92 93
      return Future<Codec>.value(FakeCodec());
    }), isA<MultiFrameImageStreamCompleter>());
94 95 96

    expect(await error.future, isStateError);
  });
97

98
  Future<Codec> decoder(Uint8List bytes, {int? cacheWidth, int? cacheHeight, bool? allowUpscaling}) async {
99 100 101 102 103
    return FakeCodec();
  }

  test('File image sets tag', () async {
    final MemoryFileSystem fs = MemoryFileSystem();
104
    final File file = fs.file('/blue.png')..createSync(recursive: true)..writeAsBytesSync(kBlueSquarePng);
105 106
    final FileImage provider = FileImage(file);

107
    final MultiFrameImageStreamCompleter completer = provider.load(provider, decoder) as MultiFrameImageStreamCompleter;
108 109 110 111 112

    expect(completer.debugLabel, file.path);
  });

  test('Memory image sets tag', () async {
113
    final Uint8List bytes = Uint8List.fromList(kBlueSquarePng);
114 115
    final MemoryImage provider = MemoryImage(bytes);

116
    final MultiFrameImageStreamCompleter completer = provider.load(provider, decoder) as MultiFrameImageStreamCompleter;
117 118 119 120 121 122 123 124

    expect(completer.debugLabel, 'MemoryImage(${describeIdentity(bytes)})');
  });

  test('Asset image sets tag', () async {
    const String asset = 'images/blue.png';
    final ExactAssetImage provider = ExactAssetImage(asset, bundle: _TestAssetBundle());
    final AssetBundleImageKey key = await provider.obtainKey(ImageConfiguration.empty);
125
    final MultiFrameImageStreamCompleter completer = provider.load(key, decoder) as MultiFrameImageStreamCompleter;
126 127 128 129 130

    expect(completer.debugLabel, asset);
  });

  test('Resize image sets tag', () async {
131
    final Uint8List bytes = Uint8List.fromList(kBlueSquarePng);
132 133 134
    final ResizeImage provider = ResizeImage(MemoryImage(bytes), width: 40, height: 40);
    final MultiFrameImageStreamCompleter completer = provider.load(
      await provider.obtainKey(ImageConfiguration.empty),
135
      decoder,
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    ) as MultiFrameImageStreamCompleter;

    expect(completer.debugLabel, 'MemoryImage(${describeIdentity(bytes)}) - Resized(40×40)');
  });
}

class FakeCodec implements Codec {
  @override
  void dispose() {}

  @override
  int get frameCount => throw UnimplementedError();

  @override
  Future<FrameInfo> getNextFrame() {
    throw UnimplementedError();
  }

  @override
  int get repetitionCount => throw UnimplementedError();
}

class _TestAssetBundle extends CachingAssetBundle {
  @override
  Future<ByteData> load(String key) async {
161
    return Uint8List.fromList(kBlueSquarePng).buffer.asByteData();
162
  }
163
}