blob: 939dc28e20a3fbda254fb0ba5b5149b32d5ddf91 [file] [log] [blame]
// 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';
import 'dart:typed_data';
import 'dart:ui' as ui show Image, ImageByteFormat;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import '../painting/image_data.dart';
void main() {
testWidgets('Verify Image resets its RenderImage when changing providers', (WidgetTester tester) async {
final GlobalKey key = new GlobalKey();
final TestImageProvider imageProvider1 = new TestImageProvider();
await tester.pumpWidget(
new Container(
key: key,
child: new Image(
image: imageProvider1
)
),
null,
EnginePhase.layout,
);
RenderImage renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNull);
imageProvider1.complete();
await tester.idle(); // resolve the future from the image provider
await tester.pump(null, EnginePhase.layout);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNotNull);
final TestImageProvider imageProvider2 = new TestImageProvider();
await tester.pumpWidget(
new Container(
key: key,
child: new Image(
image: imageProvider2
)
),
null,
EnginePhase.layout
);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNull);
});
testWidgets('Verify Image doesn\'t reset its RenderImage when changing providers if it has gaplessPlayback set', (WidgetTester tester) async {
final GlobalKey key = new GlobalKey();
final TestImageProvider imageProvider1 = new TestImageProvider();
await tester.pumpWidget(
new Container(
key: key,
child: new Image(
gaplessPlayback: true,
image: imageProvider1
)
),
null,
EnginePhase.layout
);
RenderImage renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNull);
imageProvider1.complete();
await tester.idle(); // resolve the future from the image provider
await tester.pump(null, EnginePhase.layout);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNotNull);
final TestImageProvider imageProvider2 = new TestImageProvider();
await tester.pumpWidget(
new Container(
key: key,
child: new Image(
gaplessPlayback: true,
image: imageProvider2
)
),
null,
EnginePhase.layout
);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNotNull);
});
testWidgets('Verify Image resets its RenderImage when changing providers if it has a key', (WidgetTester tester) async {
final GlobalKey key = new GlobalKey();
final TestImageProvider imageProvider1 = new TestImageProvider();
await tester.pumpWidget(
new Image(
key: key,
image: imageProvider1
),
null,
EnginePhase.layout
);
RenderImage renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNull);
imageProvider1.complete();
await tester.idle(); // resolve the future from the image provider
await tester.pump(null, EnginePhase.layout);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNotNull);
final TestImageProvider imageProvider2 = new TestImageProvider();
await tester.pumpWidget(
new Image(
key: key,
image: imageProvider2
),
null,
EnginePhase.layout
);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNull);
});
testWidgets('Verify Image doesn\'t reset its RenderImage when changing providers if it has gaplessPlayback set', (WidgetTester tester) async {
final GlobalKey key = new GlobalKey();
final TestImageProvider imageProvider1 = new TestImageProvider();
await tester.pumpWidget(
new Image(
key: key,
gaplessPlayback: true,
image: imageProvider1
),
null,
EnginePhase.layout
);
RenderImage renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNull);
imageProvider1.complete();
await tester.idle(); // resolve the future from the image provider
await tester.pump(null, EnginePhase.layout);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNotNull);
final TestImageProvider imageProvider2 = new TestImageProvider();
await tester.pumpWidget(
new Image(
key: key,
gaplessPlayback: true,
image: imageProvider2
),
null,
EnginePhase.layout
);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNotNull);
});
testWidgets('Verify ImageProvider configuration inheritance', (WidgetTester tester) async {
final GlobalKey mediaQueryKey1 = new GlobalKey(debugLabel: 'mediaQueryKey1');
final GlobalKey mediaQueryKey2 = new GlobalKey(debugLabel: 'mediaQueryKey2');
final GlobalKey imageKey = new GlobalKey(debugLabel: 'image');
final TestImageProvider imageProvider = new TestImageProvider();
// Of the two nested MediaQuery objects, the innermost one,
// mediaQuery2, should define the configuration of the imageProvider.
await tester.pumpWidget(
new MediaQuery(
key: mediaQueryKey1,
data: const MediaQueryData(
devicePixelRatio: 10.0,
padding: EdgeInsets.zero,
),
child: new MediaQuery(
key: mediaQueryKey2,
data: const MediaQueryData(
devicePixelRatio: 5.0,
padding: EdgeInsets.zero,
),
child: new Image(
key: imageKey,
image: imageProvider
),
)
)
);
expect(imageProvider._lastResolvedConfiguration.devicePixelRatio, 5.0);
// This is the same widget hierarchy as before except that the
// two MediaQuery objects have exchanged places. The imageProvider
// should be resolved again, with the new innermost MediaQuery.
await tester.pumpWidget(
new MediaQuery(
key: mediaQueryKey2,
data: const MediaQueryData(
devicePixelRatio: 5.0,
padding: EdgeInsets.zero,
),
child: new MediaQuery(
key: mediaQueryKey1,
data: const MediaQueryData(
devicePixelRatio: 10.0,
padding: EdgeInsets.zero,
),
child: new Image(
key: imageKey,
image: imageProvider
),
)
)
);
expect(imageProvider._lastResolvedConfiguration.devicePixelRatio, 10.0);
});
testWidgets('Verify ImageProvider configuration inheritance again', (WidgetTester tester) async {
final GlobalKey mediaQueryKey1 = new GlobalKey(debugLabel: 'mediaQueryKey1');
final GlobalKey mediaQueryKey2 = new GlobalKey(debugLabel: 'mediaQueryKey2');
final GlobalKey imageKey = new GlobalKey(debugLabel: 'image');
final TestImageProvider imageProvider = new TestImageProvider();
// This is just a variation on the previous test. In this version the location
// of the Image changes and the MediaQuery widgets do not.
await tester.pumpWidget(
new Row(
textDirection: TextDirection.ltr,
children: <Widget> [
new MediaQuery(
key: mediaQueryKey2,
data: const MediaQueryData(
devicePixelRatio: 5.0,
padding: EdgeInsets.zero,
),
child: new Image(
key: imageKey,
image: imageProvider
)
),
new MediaQuery(
key: mediaQueryKey1,
data: const MediaQueryData(
devicePixelRatio: 10.0,
padding: EdgeInsets.zero,
),
child: new Container(width: 100.0)
)
]
)
);
expect(imageProvider._lastResolvedConfiguration.devicePixelRatio, 5.0);
await tester.pumpWidget(
new Row(
textDirection: TextDirection.ltr,
children: <Widget> [
new MediaQuery(
key: mediaQueryKey2,
data: const MediaQueryData(
devicePixelRatio: 5.0,
padding: EdgeInsets.zero,
),
child: new Container(width: 100.0)
),
new MediaQuery(
key: mediaQueryKey1,
data: const MediaQueryData(
devicePixelRatio: 10.0,
padding: EdgeInsets.zero,
),
child: new Image(
key: imageKey,
image: imageProvider
)
)
]
)
);
expect(imageProvider._lastResolvedConfiguration.devicePixelRatio, 10.0);
});
testWidgets('Verify Image stops listening to ImageStream', (WidgetTester tester) async {
final TestImageProvider imageProvider = new TestImageProvider();
await tester.pumpWidget(new Image(image: imageProvider));
final State<Image> image = tester.state/*State<Image>*/(find.byType(Image));
expect(image.toString(), equalsIgnoringHashCodes('_ImageState#00000(stream: ImageStream#00000(OneFrameImageStreamCompleter#00000, unresolved, 1 listener), pixels: null)'));
imageProvider.complete();
await tester.pump();
expect(image.toString(), equalsIgnoringHashCodes('_ImageState#00000(stream: ImageStream#00000(OneFrameImageStreamCompleter#00000, [100×100] @ 1.0x, 1 listener), pixels: [100×100] @ 1.0x)'));
await tester.pumpWidget(new Container());
expect(image.toString(), equalsIgnoringHashCodes('_ImageState#00000(lifecycle state: defunct, not mounted, stream: ImageStream#00000(OneFrameImageStreamCompleter#00000, [100×100] @ 1.0x, 0 listeners), pixels: [100×100] @ 1.0x)'));
});
testWidgets('Image.memory control test', (WidgetTester tester) async {
await tester.pumpWidget(new Image.memory(new Uint8List.fromList(kTransparentImage)));
});
testWidgets('Image color and colorBlend parameters', (WidgetTester tester) async {
await tester.pumpWidget(
new Image(
image: new TestImageProvider(),
color: const Color(0xFF00FF00),
colorBlendMode: BlendMode.clear
)
);
final RenderImage renderer = tester.renderObject<RenderImage>(find.byType(Image));
expect(renderer.color, const Color(0xFF00FF00));
expect(renderer.colorBlendMode, BlendMode.clear);
});
testWidgets('Precache', (WidgetTester tester) async {
final TestImageProvider provider = new TestImageProvider();
Future<Null> precache;
await tester.pumpWidget(
new Builder(
builder: (BuildContext context) {
precache = precacheImage(provider, context);
return new Container();
}
)
);
provider.complete();
await precache;
expect(provider._lastResolvedConfiguration, isNotNull);
// Check that a second resolve of the same image is synchronous.
final ImageStream stream = provider.resolve(provider._lastResolvedConfiguration);
bool isSync;
stream.addListener((ImageInfo image, bool sync) { isSync = sync; });
expect(isSync, isTrue);
});
testWidgets('TickerMode controls stream registration', (WidgetTester tester) async {
final TestImageStreamCompleter imageStreamCompleter = new TestImageStreamCompleter();
final Image image = new Image(
image: new TestImageProvider(streamCompleter: imageStreamCompleter),
);
await tester.pumpWidget(
new TickerMode(
enabled: true,
child: image,
),
);
expect(imageStreamCompleter.listeners.length, 1);
await tester.pumpWidget(
new TickerMode(
enabled: false,
child: image,
),
);
expect(imageStreamCompleter.listeners.length, 0);
});
testWidgets('Verify Image shows correct RenderImage when changing to an already completed provider', (WidgetTester tester) async {
final GlobalKey key = new GlobalKey();
final TestImageProvider imageProvider1 = new TestImageProvider();
final TestImageProvider imageProvider2 = new TestImageProvider();
await tester.pumpWidget(
new Container(
key: key,
child: new Image(
image: imageProvider1
)
),
null,
EnginePhase.layout
);
RenderImage renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNull);
imageProvider1.complete();
imageProvider2.complete();
await tester.idle(); // resolve the future from the image provider
await tester.pump(null, EnginePhase.layout);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNotNull);
final ui.Image oldImage = renderImage.image;
await tester.pumpWidget(
new Container(
key: key,
child: new Image(
image: imageProvider2
)
),
null,
EnginePhase.layout
);
renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNotNull);
expect(renderImage.image, isNot(equals(oldImage)));
});
testWidgets('Image State can be reconfigured to use another image', (WidgetTester tester) async {
final Image image1 = new Image(image: new TestImageProvider()..complete(), width: 10.0);
final Image image2 = new Image(image: new TestImageProvider()..complete(), width: 20.0);
final Column column = new Column(children: <Widget>[image1, image2]);
await tester.pumpWidget(column, null, EnginePhase.layout);
final Column columnSwapped = new Column(children: <Widget>[image2, image1]);
await tester.pumpWidget(columnSwapped, null, EnginePhase.layout);
final List<RenderImage> renderObjects = tester.renderObjectList<RenderImage>(find.byType(Image)).toList();
expect(renderObjects, hasLength(2));
expect(renderObjects[0].image, isNotNull);
expect(renderObjects[0].width, 20.0);
expect(renderObjects[1].image, isNotNull);
expect(renderObjects[1].width, 10.0);
});
}
class TestImageProvider extends ImageProvider<TestImageProvider> {
final Completer<ImageInfo> _completer = new Completer<ImageInfo>();
ImageStreamCompleter _streamCompleter;
ImageConfiguration _lastResolvedConfiguration;
TestImageProvider({ImageStreamCompleter streamCompleter}) {
_streamCompleter = streamCompleter
?? new OneFrameImageStreamCompleter(_completer.future);
}
@override
Future<TestImageProvider> obtainKey(ImageConfiguration configuration) {
return new SynchronousFuture<TestImageProvider>(this);
}
@override
ImageStream resolve(ImageConfiguration configuration) {
_lastResolvedConfiguration = configuration;
return super.resolve(configuration);
}
@override
ImageStreamCompleter load(TestImageProvider key) => _streamCompleter;
void complete() {
_completer.complete(new ImageInfo(image: new TestImage()));
}
@override
String toString() => '${describeIdentity(this)}()';
}
class TestImageStreamCompleter extends ImageStreamCompleter {
final List<ImageListener> listeners = <ImageListener> [];
@override
void addListener(ImageListener listener) {
listeners.add(listener);
}
@override
void removeListener(ImageListener listener) {
listeners.remove(listener);
}
}
class TestImage implements ui.Image {
@override
int get width => 100;
@override
int get height => 100;
@override
void dispose() { }
@override
Future<ByteData> toByteData({ui.ImageByteFormat format}) async {
throw new UnsupportedError('Cannot encode test image');
}
@override
String toString() => '[$width\u00D7$height]';
}