image_resolution_test.dart 10.3 KB
Newer Older
1 2 3 4 5
// Copyright 2016 The Chromium Authors. 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';
6
import 'dart:typed_data';
7
import 'dart:ui' as ui show Image, ImageByteFormat;
8

9
import 'package:flutter/foundation.dart';
10 11 12
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
13
import 'package:flutter_test/flutter_test.dart';
14

15
class TestImage implements ui.Image {
16 17
  TestImage(this.scale);
  final double scale;
18 19

  @override
20
  int get width => (48*scale).floor();
21 22

  @override
23
  int get height => (48*scale).floor();
24 25

  @override
26
  void dispose() { }
27 28

  @override
29
  Future<ByteData> toByteData({ui.ImageByteFormat format}) async {
30 31
    throw new UnsupportedError('Cannot encode test image');
  }
32 33
}

34 35
class TestByteData implements ByteData {
  TestByteData(this.scale);
36
  final double scale;
37 38 39

  @override
  dynamic noSuchMethod(Invocation invocation) => null;
40 41
}

42
const String testManifest = '''
43 44
{
  "assets/image.png" : [
45
    "assets/image.png",
46 47 48 49 50 51 52 53
    "assets/1.5x/image.png",
    "assets/2.0x/image.png",
    "assets/3.0x/image.png",
    "assets/4.0x/image.png"
  ]
}
''';

54
class TestAssetBundle extends CachingAssetBundle {
55
  TestAssetBundle({ this.manifest = testManifest });
56 57 58

  final String manifest;

59
  @override
60 61
  Future<ByteData> load(String key) {
    ByteData data;
62 63
    switch (key) {
      case 'assets/image.png':
64
        data = new TestByteData(1.0);
65
        break;
66 67 68
      case 'assets/1.0x/image.png':
        data = new TestByteData(10.0); // see "...with a main asset and a 1.0x asset"
        break;
69
      case 'assets/1.5x/image.png':
70
        data = new TestByteData(1.5);
71 72
        break;
      case 'assets/2.0x/image.png':
73
        data = new TestByteData(2.0);
74 75
        break;
      case 'assets/3.0x/image.png':
76
        data = new TestByteData(3.0);
77 78
        break;
      case 'assets/4.0x/image.png':
79
        data = new TestByteData(4.0);
80 81
        break;
    }
82
    return new SynchronousFuture<ByteData>(data);
83 84 85
  }

  @override
86
  Future<String> loadString(String key, { bool cache = true }) {
87
    if (key == 'AssetManifest.json')
88
      return new SynchronousFuture<String>(manifest);
89
    return null;
90
  }
91 92

  @override
93
  String toString() => '${describeIdentity(this)}()';
94 95
}

96 97
class FakeImageStreamCompleter extends ImageStreamCompleter {
  FakeImageStreamCompleter(Future<ImageInfo> image) {
98
    image.then<void>(setImage);
99 100 101
  }
}

102 103 104
class TestAssetImage extends AssetImage {
  TestAssetImage(String name) : super(name);

105
  @override
106 107
  ImageStreamCompleter load(AssetBundleImageKey key) {
    ImageInfo imageInfo;
108
    key.bundle.load(key.name).then<void>((ByteData data) {
109 110 111
      final TestByteData testData = data;
      final ui.Image image = new TestImage(testData.scale);
      imageInfo = new ImageInfo(image: image, scale: key.scale);
112
    });
113 114 115 116
    assert(imageInfo != null);
    return new FakeImageStreamCompleter(
      new SynchronousFuture<ImageInfo>(imageInfo)
    );
117
  }
118 119
}

120
Widget buildImageAtRatio(String image, Key key, double ratio, bool inferSize, [AssetBundle bundle]) {
121 122 123 124 125 126 127
  const double windowSize = 500.0; // 500 logical pixels
  const double imageSize = 200.0; // 200 logical pixels

  return new MediaQuery(
    data: new MediaQueryData(
      size: const Size(windowSize, windowSize),
      devicePixelRatio: ratio,
128
      padding: const EdgeInsets.all(0.0)
129
    ),
130
    child: new DefaultAssetBundle(
131
      bundle: bundle ?? new TestAssetBundle(),
132 133
      child: new Center(
        child: inferSize ?
134
          new Image(
135
            key: key,
136
            excludeFromSemantics: true,
137
            image: new TestAssetImage(image)
138
          ) :
139
          new Image(
140
            key: key,
141
            excludeFromSemantics: true,
142
            image: new TestAssetImage(image),
143 144
            height: imageSize,
            width: imageSize,
145
            fit: BoxFit.fill
146 147 148 149 150 151
          )
      )
    )
  );
}

152
RenderImage getRenderImage(WidgetTester tester, Key key) {
153
  return tester.renderObject<RenderImage>(find.byKey(key));
154
}
155
TestImage getTestImage(WidgetTester tester, Key key) {
156
  return tester.renderObject<RenderImage>(find.byKey(key)).image;
157 158
}

159
Future<Null> pumpTreeToLayout(WidgetTester tester, Widget widget) {
160
  const Duration pumpDuration = const Duration(milliseconds: 0);
161
  const EnginePhase pumpPhase = EnginePhase.layout;
162
  return tester.pumpWidget(widget, pumpDuration, pumpPhase);
163 164 165
}

void main() {
166
  const String image = 'assets/image.png';
167

168
  testWidgets('Image for device pixel ratio 1.0', (WidgetTester tester) async {
169
    const double ratio = 1.0;
170
    Key key = new GlobalKey();
171
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false));
172 173 174
    expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
    expect(getTestImage(tester, key).scale, 1.0);
    key = new GlobalKey();
175
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true));
176 177
    expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
    expect(getTestImage(tester, key).scale, 1.0);
178 179
  });

180
  testWidgets('Image for device pixel ratio 0.5', (WidgetTester tester) async {
181
    const double ratio = 0.5;
182
    Key key = new GlobalKey();
183
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false));
184 185 186
    expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
    expect(getTestImage(tester, key).scale, 1.0);
    key = new GlobalKey();
187
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true));
188 189
    expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
    expect(getTestImage(tester, key).scale, 1.0);
190 191
  });

192
  testWidgets('Image for device pixel ratio 1.5', (WidgetTester tester) async {
193
    const double ratio = 1.5;
194
    Key key = new GlobalKey();
195
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false));
196 197 198
    expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
    expect(getTestImage(tester, key).scale, 1.5);
    key = new GlobalKey();
199
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true));
200 201
    expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
    expect(getTestImage(tester, key).scale, 1.5);
202 203
  });

204
  testWidgets('Image for device pixel ratio 1.75', (WidgetTester tester) async {
205
    const double ratio = 1.75;
206
    Key key = new GlobalKey();
207
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false));
208 209 210
    expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
    expect(getTestImage(tester, key).scale, 1.5);
    key = new GlobalKey();
211
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true));
212 213
    expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
    expect(getTestImage(tester, key).scale, 1.5);
214 215
  });

216
  testWidgets('Image for device pixel ratio 2.3', (WidgetTester tester) async {
217
    const double ratio = 2.3;
218
    Key key = new GlobalKey();
219
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false));
220 221 222
    expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
    expect(getTestImage(tester, key).scale, 2.0);
    key = new GlobalKey();
223
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true));
224 225
    expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
    expect(getTestImage(tester, key).scale, 2.0);
226 227
  });

228
  testWidgets('Image for device pixel ratio 3.7', (WidgetTester tester) async {
229
    const double ratio = 3.7;
230
    Key key = new GlobalKey();
231
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false));
232 233 234
    expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
    expect(getTestImage(tester, key).scale, 4.0);
    key = new GlobalKey();
235
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true));
236 237
    expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
    expect(getTestImage(tester, key).scale, 4.0);
238 239
  });

240
  testWidgets('Image for device pixel ratio 5.1', (WidgetTester tester) async {
241
    const double ratio = 5.1;
242
    Key key = new GlobalKey();
243
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false));
244 245 246
    expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
    expect(getTestImage(tester, key).scale, 4.0);
    key = new GlobalKey();
247
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true));
248 249
    expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
    expect(getTestImage(tester, key).scale, 4.0);
250 251
  });

252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
  testWidgets('Image for device pixel ratio 1.0, with no main asset', (WidgetTester tester) async {
    const String manifest = '''
    {
      "assets/image.png" : [
        "assets/1.5x/image.png",
        "assets/2.0x/image.png",
        "assets/3.0x/image.png",
        "assets/4.0x/image.png"
      ]
    }
    ''';
    final AssetBundle bundle = new TestAssetBundle(manifest: manifest);

    const double ratio = 1.0;
    Key key = new GlobalKey();
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, bundle));
    expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
    expect(getTestImage(tester, key).scale, 1.5);
    key = new GlobalKey();
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true, bundle));
    expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
    expect(getTestImage(tester, key).scale, 1.5);
  });

  testWidgets('Image for device pixel ratio 1.0, with a main asset and a 1.0x asset', (WidgetTester tester) async {
    // If both a main asset and a 1.0x asset are specified, then prefer
    // the 1.0x asset.

    const String manifest = '''
    {
      "assets/image.png" : [
        "assets/image.png",
        "assets/1.0x/image.png",
        "assets/1.5x/image.png",
        "assets/2.0x/image.png",
        "assets/3.0x/image.png",
        "assets/4.0x/image.png"
      ]
    }
    ''';
    final AssetBundle bundle = new TestAssetBundle(manifest: manifest);

    const double ratio = 1.0;
    Key key = new GlobalKey();
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, bundle));
    expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
    expect(getTestImage(tester, key).scale, 10.0);
    key = new GlobalKey();
    await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true, bundle));
    expect(getRenderImage(tester, key).size, const Size(480.0, 480.0));
    expect(getTestImage(tester, key).scale, 10.0);
  });

305
}