image_resolution_test.dart 10.2 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
            image: new TestAssetImage(image)
137
          ) :
138
          new Image(
139
            key: key,
140
            image: new TestAssetImage(image),
141 142
            height: imageSize,
            width: imageSize,
143
            fit: BoxFit.fill
144 145 146 147 148 149
          )
      )
    )
  );
}

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

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

void main() {
164
  const String image = 'assets/image.png';
165

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

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

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

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

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

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

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

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
  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);
  });

303
}