mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
384 lines
13 KiB
Dart
384 lines
13 KiB
Dart
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// @dart = 2.6
|
|
import 'dart:typed_data' show Float64List;
|
|
import 'dart:ui';
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('pushTransform validates the matrix', () {
|
|
final SceneBuilder builder = SceneBuilder();
|
|
final Float64List matrix4 = Float64List.fromList(<double>[
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, 0, 0, 1,
|
|
]);
|
|
expect(builder.pushTransform(matrix4), isNotNull);
|
|
|
|
final Float64List matrix4WrongLength = Float64List.fromList(<double>[
|
|
1, 0, 0, 0,
|
|
0, 1, 0,
|
|
0, 0, 1, 0,
|
|
0, 0, 0,
|
|
]);
|
|
assert(() {
|
|
expect(
|
|
() => builder.pushTransform(matrix4WrongLength),
|
|
throwsA(const TypeMatcher<AssertionError>()),
|
|
);
|
|
return true;
|
|
}());
|
|
|
|
final Float64List matrix4NaN = Float64List.fromList(<double>[
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, 0, 0, double.nan,
|
|
]);
|
|
assert(() {
|
|
expect(
|
|
() => builder.pushTransform(matrix4NaN),
|
|
throwsA(const TypeMatcher<AssertionError>()),
|
|
);
|
|
return true;
|
|
}());
|
|
|
|
final Float64List matrix4Infinity = Float64List.fromList(<double>[
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, 0, 0, double.infinity,
|
|
]);
|
|
assert(() {
|
|
expect(
|
|
() => builder.pushTransform(matrix4Infinity),
|
|
throwsA(const TypeMatcher<AssertionError>()),
|
|
);
|
|
return true;
|
|
}());
|
|
});
|
|
|
|
test('SceneBuilder accepts typed layers', () {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final OpacityEngineLayer opacity1 = builder1.pushOpacity(100);
|
|
expect(opacity1, isNotNull);
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
final OpacityEngineLayer opacity2 = builder2.pushOpacity(200, oldLayer: opacity1);
|
|
expect(opacity2, isNotNull);
|
|
builder2.pop();
|
|
builder2.build();
|
|
});
|
|
|
|
// Attempts to use the same layer first as `oldLayer` then in `addRetained`.
|
|
void testPushThenIllegalRetain(_TestNoSharingFunction pushFunction) {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final EngineLayer layer = pushFunction(builder1, null);
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
pushFunction(builder2, layer);
|
|
builder2.pop();
|
|
assert(() {
|
|
try {
|
|
builder2.addRetained(layer);
|
|
fail('Expected addRetained to throw AssertionError but it returned successully');
|
|
} on AssertionError catch (error) {
|
|
expect(error.toString(), contains('The layer is already being used'));
|
|
}
|
|
return true;
|
|
}());
|
|
builder2.build();
|
|
}
|
|
|
|
// Attempts to use the same layer first in `addRetained` then as `oldLayer`.
|
|
void testAddRetainedThenIllegalPush(_TestNoSharingFunction pushFunction) {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final EngineLayer layer = pushFunction(builder1, null);
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
builder2.addRetained(layer);
|
|
assert(() {
|
|
try {
|
|
pushFunction(builder2, layer);
|
|
fail('Expected push to throw AssertionError but it returned successully');
|
|
} on AssertionError catch (error) {
|
|
expect(error.toString(), contains('The layer is already being used'));
|
|
}
|
|
return true;
|
|
}());
|
|
builder2.build();
|
|
}
|
|
|
|
// Attempts to retain the same layer twice in the same scene.
|
|
void testDoubleAddRetained(_TestNoSharingFunction pushFunction) {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final EngineLayer layer = pushFunction(builder1, null);
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
builder2.addRetained(layer);
|
|
assert(() {
|
|
try {
|
|
builder2.addRetained(layer);
|
|
fail('Expected second addRetained to throw AssertionError but it returned successully');
|
|
} on AssertionError catch (error) {
|
|
expect(error.toString(), contains('The layer is already being used'));
|
|
}
|
|
return true;
|
|
}());
|
|
builder2.build();
|
|
}
|
|
|
|
// Attempts to use the same layer as `oldLayer` twice in the same scene.
|
|
void testPushOldLayerTwice(_TestNoSharingFunction pushFunction) {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final EngineLayer layer = pushFunction(builder1, null);
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
pushFunction(builder2, layer);
|
|
assert(() {
|
|
try {
|
|
pushFunction(builder2, layer);
|
|
fail('Expected push to throw AssertionError but it returned successully');
|
|
} on AssertionError catch (error) {
|
|
expect(error.toString(), contains('was previously used as oldLayer'));
|
|
}
|
|
return true;
|
|
}());
|
|
builder2.build();
|
|
}
|
|
|
|
// Attempts to use a child of a retained layer as an `oldLayer`.
|
|
void testPushChildLayerOfRetainedLayer(_TestNoSharingFunction pushFunction) {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final EngineLayer layer = pushFunction(builder1, null);
|
|
final OpacityEngineLayer childLayer = builder1.pushOpacity(123);
|
|
builder1.pop();
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
builder2.addRetained(layer);
|
|
assert(() {
|
|
try {
|
|
builder2.pushOpacity(321, oldLayer: childLayer);
|
|
fail('Expected pushOpacity to throw AssertionError but it returned successully');
|
|
} on AssertionError catch (error) {
|
|
expect(error.toString(), contains('The layer is already being used'));
|
|
}
|
|
return true;
|
|
}());
|
|
builder2.build();
|
|
}
|
|
|
|
// Attempts to retain a layer whose child is already used as `oldLayer` elsewhere in the scene.
|
|
void testRetainParentLayerOfPushedChild(_TestNoSharingFunction pushFunction) {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final EngineLayer layer = pushFunction(builder1, null);
|
|
final OpacityEngineLayer childLayer = builder1.pushOpacity(123);
|
|
builder1.pop();
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
builder2.pushOpacity(234, oldLayer: childLayer);
|
|
builder2.pop();
|
|
assert(() {
|
|
try {
|
|
builder2.addRetained(layer);
|
|
fail('Expected addRetained to throw AssertionError but it returned successully');
|
|
} on AssertionError catch (error) {
|
|
expect(error.toString(), contains('The layer is already being used'));
|
|
}
|
|
return true;
|
|
}());
|
|
builder2.build();
|
|
}
|
|
|
|
// Attempts to retain a layer that has been used as `oldLayer` in a previous frame.
|
|
void testRetainOldLayer(_TestNoSharingFunction pushFunction) {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final EngineLayer layer = pushFunction(builder1, null);
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
pushFunction(builder2, layer);
|
|
builder2.pop();
|
|
assert(() {
|
|
try {
|
|
final SceneBuilder builder3 = SceneBuilder();
|
|
builder3.addRetained(layer);
|
|
fail('Expected addRetained to throw AssertionError but it returned successully');
|
|
} on AssertionError catch (error) {
|
|
expect(error.toString(), contains('was previously used as oldLayer'));
|
|
}
|
|
return true;
|
|
}());
|
|
builder2.build();
|
|
}
|
|
|
|
// Attempts to pass layer as `oldLayer` that has been used as `oldLayer` in a previous frame.
|
|
void testPushOldLayer(_TestNoSharingFunction pushFunction) {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final EngineLayer layer = pushFunction(builder1, null);
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
pushFunction(builder2, layer);
|
|
builder2.pop();
|
|
assert(() {
|
|
try {
|
|
final SceneBuilder builder3 = SceneBuilder();
|
|
pushFunction(builder3, layer);
|
|
fail('Expected addRetained to throw AssertionError but it returned successully');
|
|
} on AssertionError catch (error) {
|
|
expect(error.toString(), contains('was previously used as oldLayer'));
|
|
}
|
|
return true;
|
|
}());
|
|
builder2.build();
|
|
}
|
|
|
|
// Attempts to retain a parent of a layer used as `oldLayer` in a previous frame.
|
|
void testRetainsParentOfOldLayer(_TestNoSharingFunction pushFunction) {
|
|
final SceneBuilder builder1 = SceneBuilder();
|
|
final EngineLayer parentLayer = pushFunction(builder1, null);
|
|
final OpacityEngineLayer childLayer = builder1.pushOpacity(123);
|
|
builder1.pop();
|
|
builder1.pop();
|
|
builder1.build();
|
|
|
|
final SceneBuilder builder2 = SceneBuilder();
|
|
builder2.pushOpacity(321, oldLayer: childLayer);
|
|
builder2.pop();
|
|
assert(() {
|
|
try {
|
|
final SceneBuilder builder3 = SceneBuilder();
|
|
builder3.addRetained(parentLayer);
|
|
fail('Expected addRetained to throw AssertionError but it returned successully');
|
|
} on AssertionError catch (error) {
|
|
expect(error.toString(), contains('was previously used as oldLayer'));
|
|
}
|
|
return true;
|
|
}());
|
|
builder2.build();
|
|
}
|
|
|
|
void testNoSharing(_TestNoSharingFunction pushFunction) {
|
|
testPushThenIllegalRetain(pushFunction);
|
|
testAddRetainedThenIllegalPush(pushFunction);
|
|
testDoubleAddRetained(pushFunction);
|
|
testPushOldLayerTwice(pushFunction);
|
|
testPushChildLayerOfRetainedLayer(pushFunction);
|
|
testRetainParentLayerOfPushedChild(pushFunction);
|
|
testRetainOldLayer(pushFunction);
|
|
testPushOldLayer(pushFunction);
|
|
testRetainsParentOfOldLayer(pushFunction);
|
|
}
|
|
|
|
test('SceneBuilder does not share a layer between addRetained and push*', () {
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushOffset(0, 0, oldLayer: oldLayer as OffsetEngineLayer);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushTransform(Float64List(16), oldLayer: oldLayer as TransformEngineLayer);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushClipRect(Rect.zero, oldLayer: oldLayer as ClipRectEngineLayer);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushClipRRect(RRect.zero, oldLayer: oldLayer as ClipRRectEngineLayer);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushClipPath(Path(), oldLayer: oldLayer as ClipPathEngineLayer);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushOpacity(100, oldLayer: oldLayer as OpacityEngineLayer);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushBackdropFilter(ImageFilter.blur(), oldLayer: oldLayer as BackdropFilterEngineLayer);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushShaderMask(
|
|
Gradient.radial(
|
|
const Offset(0, 0),
|
|
10,
|
|
const <Color>[Color.fromARGB(0, 0, 0, 0), Color.fromARGB(0, 255, 255, 255)],
|
|
),
|
|
Rect.zero,
|
|
BlendMode.color,
|
|
oldLayer: oldLayer as ShaderMaskEngineLayer,
|
|
);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushPhysicalShape(path: Path(), color: const Color.fromARGB(0, 0, 0, 0), oldLayer: oldLayer as PhysicalShapeEngineLayer, elevation: 0.0);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushColorFilter(
|
|
const ColorFilter.mode(
|
|
Color.fromARGB(0, 0, 0, 0),
|
|
BlendMode.color,
|
|
),
|
|
oldLayer: oldLayer as ColorFilterEngineLayer,
|
|
);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushColorFilter(
|
|
const ColorFilter.matrix(<double>[
|
|
1, 0, 0, 0, 0,
|
|
0, 1, 0, 0, 0,
|
|
0, 0, 1, 0, 0,
|
|
0, 0, 0, 1, 0,
|
|
]),
|
|
oldLayer: oldLayer as ColorFilterEngineLayer,
|
|
);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushColorFilter(
|
|
const ColorFilter.linearToSrgbGamma(),
|
|
oldLayer: oldLayer as ColorFilterEngineLayer,
|
|
);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushColorFilter(
|
|
const ColorFilter.srgbToLinearGamma(),
|
|
oldLayer: oldLayer as ColorFilterEngineLayer,
|
|
);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushImageFilter(
|
|
ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
|
|
oldLayer: oldLayer as ImageFilterEngineLayer,
|
|
);
|
|
});
|
|
testNoSharing((SceneBuilder builder, EngineLayer oldLayer) {
|
|
return builder.pushImageFilter(
|
|
ImageFilter.matrix(Float64List.fromList(<double>[
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
0, 0, 0, 1,
|
|
])),
|
|
oldLayer: oldLayer as ImageFilterEngineLayer,
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
typedef _TestNoSharingFunction = EngineLayer Function(SceneBuilder builder, EngineLayer oldLayer);
|