mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add iOS platform view mutation XCUITests to the scenario app (flutter/engine#11652)
This commit is contained in:
parent
256c557bee
commit
f2eb1d4bd7
@ -252,7 +252,8 @@ UIView* FlutterPlatformViewsController::ReconstructClipViewsChain(int number_of_
|
||||
}
|
||||
// If there were not enough existing clip views, add more.
|
||||
while (clipIndex < number_of_clips) {
|
||||
ChildClippingView* clippingView = [ChildClippingView new];
|
||||
ChildClippingView* clippingView =
|
||||
[[ChildClippingView alloc] initWithFrame:flutter_view_.get().bounds];
|
||||
[clippingView addSubview:head];
|
||||
head = clippingView;
|
||||
clipIndex++;
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
package io.flutter.plugins;
|
||||
|
||||
import io.flutter.plugin.common.PluginRegistry;
|
||||
|
||||
/**
|
||||
* Generated file. Do not edit.
|
||||
*/
|
||||
public final class GeneratedPluginRegistrant {
|
||||
public static void registerWith(PluginRegistry registry) {
|
||||
if (alreadyRegisteredWith(registry)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
|
||||
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
|
||||
if (registry.hasPlugin(key)) {
|
||||
return true;
|
||||
}
|
||||
registry.registrarFor(key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
#ifndef GeneratedPluginRegistrant_h
|
||||
#define GeneratedPluginRegistrant_h
|
||||
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface GeneratedPluginRegistrant : NSObject
|
||||
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
|
||||
@end
|
||||
|
||||
#endif /* GeneratedPluginRegistrant_h */
|
||||
@ -0,0 +1,12 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
#import "GeneratedPluginRegistrant.h"
|
||||
|
||||
@implementation GeneratedPluginRegistrant
|
||||
|
||||
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
|
||||
}
|
||||
|
||||
@end
|
||||
@ -30,6 +30,14 @@
|
||||
24D47D1B230C79840069DD5E /* golden_platform_view_D211AP.png in Resources */ = {isa = PBXBuildFile; fileRef = 24D47D1A230C79840069DD5E /* golden_platform_view_D211AP.png */; };
|
||||
24D47D1D230CA2700069DD5E /* golden_platform_view_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 24D47D1C230CA2700069DD5E /* golden_platform_view_iPhone SE_simulator.png */; };
|
||||
24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 24F1FB87230B4579005ACE7C /* TextPlatformView.m */; };
|
||||
6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */; };
|
||||
6816DBA12317573300A51400 /* GoldenImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA02317573300A51400 /* GoldenImage.m */; };
|
||||
6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */; };
|
||||
6816DBAA2318696600A51400 /* golden_platform_view_clippath_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA52318696600A51400 /* golden_platform_view_clippath_iPhone SE_simulator.png */; };
|
||||
6816DBAB2318696600A51400 /* golden_platform_view_transform_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA62318696600A51400 /* golden_platform_view_transform_iPhone SE_simulator.png */; };
|
||||
6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; };
|
||||
6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; };
|
||||
6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -113,6 +121,17 @@
|
||||
24D47D1E230CA4480069DD5E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
24F1FB87230B4579005ACE7C /* TextPlatformView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextPlatformView.m; sourceTree = "<group>"; };
|
||||
24F1FB88230B4579005ACE7C /* TextPlatformView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextPlatformView.h; sourceTree = "<group>"; };
|
||||
6816DB9C231750ED00A51400 /* GoldenPlatformViewTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenPlatformViewTests.h; sourceTree = "<group>"; };
|
||||
6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenPlatformViewTests.m; sourceTree = "<group>"; };
|
||||
6816DB9F2317573300A51400 /* GoldenImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenImage.h; sourceTree = "<group>"; };
|
||||
6816DBA02317573300A51400 /* GoldenImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenImage.m; sourceTree = "<group>"; };
|
||||
6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlatformViewGoldenTestManager.h; sourceTree = "<group>"; };
|
||||
6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGoldenTestManager.m; sourceTree = "<group>"; };
|
||||
6816DBA52318696600A51400 /* golden_platform_view_clippath_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_clippath_iPhone SE_simulator.png"; sourceTree = "<group>"; };
|
||||
6816DBA62318696600A51400 /* golden_platform_view_transform_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_transform_iPhone SE_simulator.png"; sourceTree = "<group>"; };
|
||||
6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = "<group>"; };
|
||||
6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = "<group>"; };
|
||||
6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -200,9 +219,20 @@
|
||||
244EA6CF230DBE8900B2D26E /* golden_platform_view_D21AP.png */,
|
||||
24D47D1C230CA2700069DD5E /* golden_platform_view_iPhone SE_simulator.png */,
|
||||
24D47D1A230C79840069DD5E /* golden_platform_view_D211AP.png */,
|
||||
6816DBA52318696600A51400 /* golden_platform_view_clippath_iPhone SE_simulator.png */,
|
||||
6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */,
|
||||
6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */,
|
||||
6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */,
|
||||
6816DBA62318696600A51400 /* golden_platform_view_transform_iPhone SE_simulator.png */,
|
||||
248D76EE22E388380012F0C1 /* PlatformViewUITests.m */,
|
||||
248D76F022E388380012F0C1 /* Info.plist */,
|
||||
24D47D1E230CA4480069DD5E /* README.md */,
|
||||
6816DB9C231750ED00A51400 /* GoldenPlatformViewTests.h */,
|
||||
6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */,
|
||||
6816DB9F2317573300A51400 /* GoldenImage.h */,
|
||||
6816DBA02317573300A51400 /* GoldenImage.m */,
|
||||
6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */,
|
||||
6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */,
|
||||
);
|
||||
path = ScenariosUITests;
|
||||
sourceTree = "<group>";
|
||||
@ -340,9 +370,14 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */,
|
||||
6816DBAB2318696600A51400 /* golden_platform_view_transform_iPhone SE_simulator.png in Resources */,
|
||||
6816DBAA2318696600A51400 /* golden_platform_view_clippath_iPhone SE_simulator.png in Resources */,
|
||||
6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */,
|
||||
24D47D1B230C79840069DD5E /* golden_platform_view_D211AP.png in Resources */,
|
||||
24D47D1D230CA2700069DD5E /* golden_platform_view_iPhone SE_simulator.png in Resources */,
|
||||
244EA6D0230DBE8900B2D26E /* golden_platform_view_D21AP.png in Resources */,
|
||||
6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -375,6 +410,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6816DBA12317573300A51400 /* GoldenImage.m in Sources */,
|
||||
6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */,
|
||||
6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */,
|
||||
248D76EF22E388380012F0C1 /* PlatformViewUITests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@ -25,27 +25,55 @@
|
||||
|
||||
// This argument is used by the XCUITest for Platform Views so that the app
|
||||
// under test will create platform views.
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--platform-view"]) {
|
||||
FlutterEngine* engine = [[FlutterEngine alloc] initWithScenario:@"text_platform_view"
|
||||
withCompletion:nil];
|
||||
[engine runWithEntrypoint:nil];
|
||||
// The launchArgsMap should match the one in the `PlatformVieGoldenTestManager`.
|
||||
NSDictionary<NSString*, NSString*>* launchArgsMap = @{
|
||||
@"--platform-view" : @"platform_view",
|
||||
@"--platform-view-cliprect" : @"platform_view_cliprect",
|
||||
@"--platform-view-cliprrect" : @"platform_view_cliprrect",
|
||||
@"--platform-view-clippath" : @"platform_view_clippath",
|
||||
@"--platform-view-transform" : @"platform_view_transform",
|
||||
@"--platform-view-opacity" : @"platform_view_opacity",
|
||||
};
|
||||
__block NSString* goldenTestName = nil;
|
||||
[launchArgsMap
|
||||
enumerateKeysAndObjectsUsingBlock:^(NSString* argument, NSString* testName, BOOL* stop) {
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:argument]) {
|
||||
goldenTestName = testName;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
FlutterViewController* flutterViewController =
|
||||
[[NoStatusBarFlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil];
|
||||
TextPlatformViewFactory* textPlatformViewFactory =
|
||||
[[TextPlatformViewFactory alloc] initWithMessenger:flutterViewController.binaryMessenger];
|
||||
NSObject<FlutterPluginRegistrar>* registrar =
|
||||
[flutterViewController.engine registrarForPlugin:@"scenarios/TextPlatformViewPlugin"];
|
||||
[registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView"];
|
||||
self.window.rootViewController = flutterViewController;
|
||||
if (goldenTestName) {
|
||||
[self readyContextForPlatformViewTests:goldenTestName];
|
||||
} else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) {
|
||||
self.window.rootViewController = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:nil];
|
||||
} else {
|
||||
self.window.rootViewController = [[UIViewController alloc] init];
|
||||
}
|
||||
[self.window makeKeyAndVisible];
|
||||
|
||||
[self.window makeKeyAndVisible];
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier {
|
||||
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"PlatformViewTest" project:nil];
|
||||
[engine runWithEntrypoint:nil];
|
||||
|
||||
FlutterViewController* flutterViewController =
|
||||
[[NoStatusBarFlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil];
|
||||
[engine.binaryMessenger
|
||||
setMessageHandlerOnChannel:@"scenario_status"
|
||||
binaryMessageHandler:^(NSData* _Nullable message, FlutterBinaryReply _Nonnull reply) {
|
||||
[engine.binaryMessenger
|
||||
sendOnChannel:@"set_scenario"
|
||||
message:[scenarioIdentifier dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
}];
|
||||
TextPlatformViewFactory* textPlatformViewFactory =
|
||||
[[TextPlatformViewFactory alloc] initWithMessenger:flutterViewController.binaryMessenger];
|
||||
NSObject<FlutterPluginRegistrar>* registrar =
|
||||
[flutterViewController.engine registrarForPlugin:@"scenarios/TextPlatformViewPlugin"];
|
||||
[registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView"];
|
||||
self.window.rootViewController = flutterViewController;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GoldenImage : NSObject
|
||||
|
||||
@property(readonly, copy, nonatomic) NSString* goldenName;
|
||||
@property(readonly, strong, nonatomic) UIImage* image;
|
||||
|
||||
// Initilize with the golden file's prefix.
|
||||
//
|
||||
// Create an image from a golden file named prefix+devicemodel.
|
||||
- (instancetype)initWithGoldenNamePrefix:(NSString*)prefix;
|
||||
|
||||
// Compare this GoldenImage to `image`.
|
||||
//
|
||||
// Return YES if the `image` of this GoldenImage have the same pixels of provided `image`.
|
||||
- (BOOL)compareGoldenToImage:(UIImage*)image;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -0,0 +1,94 @@
|
||||
// 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.
|
||||
|
||||
#import "GoldenImage.h"
|
||||
#import <XCTest/XCTest.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
@interface GoldenImage ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation GoldenImage
|
||||
|
||||
- (instancetype)initWithGoldenNamePrefix:(NSString*)prefix {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_goldenName = [prefix stringByAppendingString:_platformName()];
|
||||
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
|
||||
NSURL* goldenURL = [bundle URLForResource:_goldenName withExtension:@"png"];
|
||||
NSData* data = [NSData dataWithContentsOfURL:goldenURL];
|
||||
_image = [[UIImage alloc] initWithData:data];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)compareGoldenToImage:(UIImage*)image {
|
||||
if (!self.image || !image) {
|
||||
return NO;
|
||||
}
|
||||
CGImageRef imageRefA = [self.image CGImage];
|
||||
CGImageRef imageRefB = [image CGImage];
|
||||
|
||||
NSUInteger widthA = CGImageGetWidth(imageRefA);
|
||||
NSUInteger heightA = CGImageGetHeight(imageRefA);
|
||||
NSUInteger widthB = CGImageGetWidth(imageRefB);
|
||||
NSUInteger heightB = CGImageGetHeight(imageRefB);
|
||||
|
||||
if (widthA != widthB || heightA != heightB) {
|
||||
return NO;
|
||||
}
|
||||
NSUInteger bytesPerPixel = 4;
|
||||
NSUInteger size = widthA * heightA * bytesPerPixel;
|
||||
NSMutableData* rawA = [NSMutableData dataWithLength:size];
|
||||
NSMutableData* rawB = [NSMutableData dataWithLength:size];
|
||||
|
||||
if (!rawA || !rawB) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
|
||||
NSUInteger bytesPerRow = bytesPerPixel * widthA;
|
||||
NSUInteger bitsPerComponent = 8;
|
||||
CGContextRef contextA =
|
||||
CGBitmapContextCreate(rawA.mutableBytes, widthA, heightA, bitsPerComponent, bytesPerRow,
|
||||
colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
|
||||
CGContextDrawImage(contextA, CGRectMake(0, 0, widthA, heightA), imageRefA);
|
||||
CGContextRelease(contextA);
|
||||
|
||||
CGContextRef contextB =
|
||||
CGBitmapContextCreate(rawB.mutableBytes, widthA, heightA, bitsPerComponent, bytesPerRow,
|
||||
colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(contextB, CGRectMake(0, 0, widthA, heightA), imageRefB);
|
||||
CGContextRelease(contextB);
|
||||
|
||||
if (memcmp(rawA.mutableBytes, rawB.mutableBytes, size)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
NS_INLINE NSString* _platformName() {
|
||||
NSString* simulatorName =
|
||||
[[NSProcessInfo processInfo].environment objectForKey:@"SIMULATOR_DEVICE_NAME"];
|
||||
if (simulatorName) {
|
||||
return [NSString stringWithFormat:@"%@_simulator", simulatorName];
|
||||
}
|
||||
|
||||
size_t size;
|
||||
sysctlbyname("hw.model", NULL, &size, NULL, 0);
|
||||
char* answer = malloc(size);
|
||||
sysctlbyname("hw.model", answer, &size, NULL, 0);
|
||||
|
||||
NSString* results = [NSString stringWithCString:answer encoding:NSUTF8StringEncoding];
|
||||
free(answer);
|
||||
return results;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "PlatformViewGoldenTestManager.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// The base class of all the PlatformView golden tests.
|
||||
//
|
||||
// A new PlatformView golden tests can subclass this and override the `-initiWithInvocation:`
|
||||
// method, which then retun the `-initWithManager:invocation:`
|
||||
//
|
||||
// Then in any test method, call `checkGolden` to perform the golden test.
|
||||
//
|
||||
// This base class doesn't run any test case on its own.
|
||||
@interface GoldenPlatformViewTests : XCTestCase
|
||||
|
||||
// Initialize with a `PlatformViewGoldenTestManager`.
|
||||
- (instancetype)initWithManager:(PlatformViewGoldenTestManager*)manager
|
||||
invocation:(NSInvocation*)invocation;
|
||||
|
||||
// Take a sceenshot of the test app and check it has the same pixels with goldenImage inside the
|
||||
// `PlatformViewGoldenTestManager`.
|
||||
- (void)checkGolden;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -0,0 +1,68 @@
|
||||
// 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.
|
||||
|
||||
#import "GoldenPlatformViewTests.h"
|
||||
#include <sys/sysctl.h>
|
||||
#import "PlatformViewGoldenTestManager.h"
|
||||
|
||||
static const NSInteger kSecondsToWaitForPlatformView = 30;
|
||||
|
||||
@interface GoldenPlatformViewTests ()
|
||||
|
||||
@property(nonatomic, copy) NSString* goldenName;
|
||||
@property(nonatomic, strong) XCUIApplication* application;
|
||||
|
||||
@property(nonatomic, strong) PlatformViewGoldenTestManager* manager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GoldenPlatformViewTests
|
||||
|
||||
- (instancetype)initWithManager:(PlatformViewGoldenTestManager*)manager
|
||||
invocation:(NSInvocation*)invocation {
|
||||
self = [super initWithInvocation:invocation];
|
||||
_manager = manager;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
self.continueAfterFailure = NO;
|
||||
|
||||
self.application = [[XCUIApplication alloc] init];
|
||||
self.application.launchArguments = @[ self.manager.launchArg ];
|
||||
[self.application launch];
|
||||
}
|
||||
|
||||
// Note: don't prefix with "test" or GoldenPlatformViewTests will run instead of the subclasses.
|
||||
- (void)checkGolden {
|
||||
XCUIElement* element = self.application.textViews.firstMatch;
|
||||
BOOL exists = [element waitForExistenceWithTimeout:kSecondsToWaitForPlatformView];
|
||||
if (!exists) {
|
||||
XCTFail(@"It took longer than %@ second to find the platform view."
|
||||
@"There might be issues with the platform view's construction,"
|
||||
@"or with how the scenario is built.",
|
||||
@(kSecondsToWaitForPlatformView));
|
||||
}
|
||||
|
||||
GoldenImage* golden = self.manager.goldenImage;
|
||||
|
||||
XCUIScreenshot* screenshot = [[XCUIScreen mainScreen] screenshot];
|
||||
XCTAttachment* attachment = [XCTAttachment attachmentWithScreenshot:screenshot];
|
||||
attachment.lifetime = XCTAttachmentLifetimeKeepAlways;
|
||||
[self addAttachment:attachment];
|
||||
|
||||
if (golden.image) {
|
||||
XCTAttachment* goldenAttachment = [XCTAttachment attachmentWithImage:golden.image];
|
||||
goldenAttachment.lifetime = XCTAttachmentLifetimeKeepAlways;
|
||||
[self addAttachment:goldenAttachment];
|
||||
} else {
|
||||
XCTFail(@"This test will fail - no golden named %@ found. Follow the steps in the "
|
||||
@"README to add a new golden.",
|
||||
golden.goldenName);
|
||||
}
|
||||
|
||||
XCTAssertTrue([golden compareGoldenToImage:screenshot.image]);
|
||||
}
|
||||
@end
|
||||
@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "GoldenImage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSDictionary* launchArgsMap;
|
||||
|
||||
// Manages a `GoldenPlatformViewTests`.
|
||||
//
|
||||
// It creates the correct `identifer` based on the `launchArg`.
|
||||
// It also generates the correct GoldenImage based on the `identifier`.
|
||||
@interface PlatformViewGoldenTestManager : NSObject
|
||||
|
||||
@property(readonly, strong, nonatomic) GoldenImage* goldenImage;
|
||||
@property(readonly, copy, nonatomic) NSString* identifier;
|
||||
@property(readonly, copy, nonatomic) NSString* launchArg;
|
||||
|
||||
// Initilize with launchArg.
|
||||
//
|
||||
// Crahes if the launchArg is not mapped in `Appdelegate.launchArgsMap`.
|
||||
- (instancetype)initWithLaunchArg:(NSString*)launchArg;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -0,0 +1,41 @@
|
||||
// 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.
|
||||
|
||||
#import "PlatformViewGoldenTestManager.h"
|
||||
|
||||
@interface PlatformViewGoldenTestManager ()
|
||||
|
||||
@property(readwrite, strong, nonatomic) GoldenImage* goldenImage;
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlatformViewGoldenTestManager
|
||||
|
||||
NSDictionary* launchArgsMap;
|
||||
|
||||
- (instancetype)initWithLaunchArg:(NSString*)launchArg {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// The launchArgsMap should match the one in the `PlatformVieGoldenTestManager`.
|
||||
static NSDictionary<NSString*, NSString*>* launchArgsMap;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
launchArgsMap = @{
|
||||
@"--platform-view" : @"platform_view",
|
||||
@"--platform-view-cliprect" : @"platform_view_cliprect",
|
||||
@"--platform-view-cliprrect" : @"platform_view_cliprrect",
|
||||
@"--platform-view-clippath" : @"platform_view_clippath",
|
||||
@"--platform-view-transform" : @"platform_view_transform",
|
||||
@"--platform-view-opacity" : @"platform_view_opacity",
|
||||
};
|
||||
});
|
||||
_identifier = launchArgsMap[launchArg];
|
||||
NSString* prefix = [NSString stringWithFormat:@"golden_%@_", _identifier];
|
||||
_goldenImage = [[GoldenImage alloc] initWithGoldenNamePrefix:prefix];
|
||||
_launchArg = launchArg;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -2,114 +2,113 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import <Flutter/flutter.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#include <sys/sysctl.h>
|
||||
#import "GoldenPlatformViewTests.h"
|
||||
|
||||
#import "../Scenarios/TextPlatformView.h"
|
||||
@interface PlatformViewUITests : GoldenPlatformViewTests
|
||||
|
||||
@interface PlatformViewUITests : XCTestCase
|
||||
@property(nonatomic, strong) XCUIApplication* application;
|
||||
@end
|
||||
|
||||
@implementation PlatformViewUITests
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
self.continueAfterFailure = NO;
|
||||
|
||||
self.application = [[XCUIApplication alloc] init];
|
||||
self.application.launchArguments = @[ @"--platform-view" ];
|
||||
[self.application launch];
|
||||
- (instancetype)initWithInvocation:(NSInvocation*)invocation {
|
||||
PlatformViewGoldenTestManager* manager =
|
||||
[[PlatformViewGoldenTestManager alloc] initWithLaunchArg:@"--platform-view"];
|
||||
return [super initWithManager:manager invocation:invocation];
|
||||
}
|
||||
|
||||
- (void)testPlatformView {
|
||||
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
|
||||
NSString* goldenName =
|
||||
[NSString stringWithFormat:@"golden_platform_view_%@", [self platformName]];
|
||||
NSString* path = [bundle pathForResource:goldenName ofType:@"png"];
|
||||
UIImage* golden = [[UIImage alloc] initWithContentsOfFile:path];
|
||||
|
||||
XCUIScreenshot* screenshot = [[XCUIScreen mainScreen] screenshot];
|
||||
XCTAttachment* attachment = [XCTAttachment attachmentWithScreenshot:screenshot];
|
||||
attachment.lifetime = XCTAttachmentLifetimeKeepAlways;
|
||||
[self addAttachment:attachment];
|
||||
|
||||
if (golden) {
|
||||
XCTAttachment* goldenAttachment = [XCTAttachment attachmentWithImage:golden];
|
||||
goldenAttachment.lifetime = XCTAttachmentLifetimeKeepAlways;
|
||||
[self addAttachment:goldenAttachment];
|
||||
} else {
|
||||
XCTFail(@"This test will fail - no golden named %@ found. Follow the steps in the "
|
||||
@"README to add a new golden.",
|
||||
goldenName);
|
||||
}
|
||||
|
||||
XCTAssertTrue([self compareImage:golden toOther:screenshot.image]);
|
||||
}
|
||||
|
||||
- (NSString*)platformName {
|
||||
NSString* simulatorName =
|
||||
[[NSProcessInfo processInfo].environment objectForKey:@"SIMULATOR_DEVICE_NAME"];
|
||||
if (simulatorName) {
|
||||
return [NSString stringWithFormat:@"%@_simulator", simulatorName];
|
||||
}
|
||||
|
||||
size_t size;
|
||||
sysctlbyname("hw.model", NULL, &size, NULL, 0);
|
||||
char* answer = malloc(size);
|
||||
sysctlbyname("hw.model", answer, &size, NULL, 0);
|
||||
|
||||
NSString* results = [NSString stringWithCString:answer encoding:NSUTF8StringEncoding];
|
||||
free(answer);
|
||||
return results;
|
||||
}
|
||||
|
||||
- (BOOL)compareImage:(UIImage*)a toOther:(UIImage*)b {
|
||||
CGImageRef imageRefA = [a CGImage];
|
||||
CGImageRef imageRefB = [b CGImage];
|
||||
|
||||
NSUInteger widthA = CGImageGetWidth(imageRefA);
|
||||
NSUInteger heightA = CGImageGetHeight(imageRefA);
|
||||
NSUInteger widthB = CGImageGetWidth(imageRefB);
|
||||
NSUInteger heightB = CGImageGetHeight(imageRefB);
|
||||
|
||||
if (widthA != widthB || heightA != heightB) {
|
||||
return NO;
|
||||
}
|
||||
NSUInteger bytesPerPixel = 4;
|
||||
NSUInteger size = widthA * heightA * bytesPerPixel;
|
||||
NSMutableData* rawA = [NSMutableData dataWithLength:size];
|
||||
NSMutableData* rawB = [NSMutableData dataWithLength:size];
|
||||
|
||||
if (!rawA || !rawB) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
|
||||
NSUInteger bytesPerRow = bytesPerPixel * widthA;
|
||||
NSUInteger bitsPerComponent = 8;
|
||||
CGContextRef contextA =
|
||||
CGBitmapContextCreate(rawA.mutableBytes, widthA, heightA, bitsPerComponent, bytesPerRow,
|
||||
colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
|
||||
CGContextDrawImage(contextA, CGRectMake(0, 0, widthA, heightA), imageRefA);
|
||||
CGContextRelease(contextA);
|
||||
|
||||
CGContextRef contextB =
|
||||
CGBitmapContextCreate(rawB.mutableBytes, widthA, heightA, bitsPerComponent, bytesPerRow,
|
||||
colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(contextB, CGRectMake(0, 0, widthA, heightA), imageRefB);
|
||||
CGContextRelease(contextB);
|
||||
|
||||
if (memcmp(rawA.mutableBytes, rawB.mutableBytes, size)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
[self checkGolden];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// Clip Rect Tests
|
||||
@interface PlatformViewMutationClipRectTests : GoldenPlatformViewTests
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlatformViewMutationClipRectTests
|
||||
|
||||
- (instancetype)initWithInvocation:(NSInvocation*)invocation {
|
||||
PlatformViewGoldenTestManager* manager =
|
||||
[[PlatformViewGoldenTestManager alloc] initWithLaunchArg:@"--platform-view-cliprect"];
|
||||
return [super initWithManager:manager invocation:invocation];
|
||||
}
|
||||
|
||||
- (void)testPlatformView {
|
||||
[self checkGolden];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface PlatformViewMutationClipRRectTests : GoldenPlatformViewTests
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlatformViewMutationClipRRectTests
|
||||
|
||||
- (instancetype)initWithInvocation:(NSInvocation*)invocation {
|
||||
PlatformViewGoldenTestManager* manager =
|
||||
[[PlatformViewGoldenTestManager alloc] initWithLaunchArg:@"--platform-view-cliprrect"];
|
||||
return [super initWithManager:manager invocation:invocation];
|
||||
}
|
||||
|
||||
- (void)testPlatformView {
|
||||
[self checkGolden];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface PlatformViewMutationClipPathTests : GoldenPlatformViewTests
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlatformViewMutationClipPathTests
|
||||
|
||||
- (instancetype)initWithInvocation:(NSInvocation*)invocation {
|
||||
PlatformViewGoldenTestManager* manager =
|
||||
[[PlatformViewGoldenTestManager alloc] initWithLaunchArg:@"--platform-view-clippath"];
|
||||
return [super initWithManager:manager invocation:invocation];
|
||||
}
|
||||
|
||||
- (void)testPlatformView {
|
||||
[self checkGolden];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface PlatformViewMutationTransformTests : GoldenPlatformViewTests
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlatformViewMutationTransformTests
|
||||
|
||||
- (instancetype)initWithInvocation:(NSInvocation*)invocation {
|
||||
PlatformViewGoldenTestManager* manager =
|
||||
[[PlatformViewGoldenTestManager alloc] initWithLaunchArg:@"--platform-view-transform"];
|
||||
return [super initWithManager:manager invocation:invocation];
|
||||
}
|
||||
|
||||
- (void)testPlatformView {
|
||||
[self checkGolden];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface PlatformViewMutationOpacityTests : GoldenPlatformViewTests
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlatformViewMutationOpacityTests
|
||||
|
||||
- (instancetype)initWithInvocation:(NSInvocation*)invocation {
|
||||
PlatformViewGoldenTestManager* manager =
|
||||
[[PlatformViewGoldenTestManager alloc] initWithLaunchArg:@"--platform-view-opacity"];
|
||||
return [super initWithManager:manager invocation:invocation];
|
||||
}
|
||||
|
||||
- (void)testPlatformView {
|
||||
[self checkGolden];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@ -16,7 +16,12 @@ import 'src/scenario.dart';
|
||||
|
||||
Map<String, Scenario> _scenarios = <String, Scenario>{
|
||||
'animated_color_square': AnimatedColorSquareScenario(window),
|
||||
'text_platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)'),
|
||||
'platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)', id: 0),
|
||||
'platform_view_cliprect': PlatformViewClipRectScenario(window, 'PlatformViewClipRect', id: 1),
|
||||
'platform_view_cliprrect': PlatformViewClipRRectScenario(window, 'PlatformViewClipRRect', id: 2),
|
||||
'platform_view_clippath': PlatformViewClipPathScenario(window, 'PlatformViewClipPath', id: 3),
|
||||
'platform_view_transform': PlatformViewTransformScenario(window, 'PlatformViewTransform', id: 4),
|
||||
'platform_view_opacity': PlatformViewOpacityScenario(window, 'PlatformViewOpacity', id: 5),
|
||||
'poppable_screen': PoppableScreenScenario(window),
|
||||
};
|
||||
|
||||
|
||||
@ -4,8 +4,10 @@
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
import 'scenario.dart';
|
||||
|
||||
@ -26,13 +28,147 @@ List<int> _to64(num value) {
|
||||
}
|
||||
|
||||
/// A simple platform view.
|
||||
class PlatformViewScenario extends Scenario {
|
||||
class PlatformViewScenario extends Scenario
|
||||
with _BasePlatformViewScenarioMixin {
|
||||
/// Creates the PlatformView scenario.
|
||||
///
|
||||
/// The [window] parameter must not be null.
|
||||
PlatformViewScenario(Window window, String text, {int id = 0})
|
||||
: assert(window != null),
|
||||
super(window) {
|
||||
constructScenario(window, text, id);
|
||||
}
|
||||
|
||||
@override
|
||||
void onBeginFrame(Duration duration) {
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
|
||||
builder.pushOffset(0, 0);
|
||||
|
||||
finishBuilderByAddingPlatformViewAndPicture(builder, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Platform view with clip rect.
|
||||
class PlatformViewClipRectScenario extends Scenario
|
||||
with _BasePlatformViewScenarioMixin {
|
||||
/// Constructs a platform view with clip rect scenario.
|
||||
PlatformViewClipRectScenario(Window window, String text, {int id = 0})
|
||||
: assert(window != null),
|
||||
super(window) {
|
||||
constructScenario(window, text, id);
|
||||
}
|
||||
|
||||
@override
|
||||
void onBeginFrame(Duration duration) {
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
builder.pushOffset(0, 0);
|
||||
builder.pushClipRect(const Rect.fromLTRB(100, 100, 400, 400));
|
||||
finishBuilderByAddingPlatformViewAndPicture(builder, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Platform view with clip rrect.
|
||||
class PlatformViewClipRRectScenario extends PlatformViewScenario {
|
||||
/// Constructs a platform view with clip rrect scenario.
|
||||
PlatformViewClipRRectScenario(Window window, String text, {int id = 0})
|
||||
: super(window, text, id: id);
|
||||
|
||||
@override
|
||||
void onBeginFrame(Duration duration) {
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
|
||||
builder.pushOffset(0, 0);
|
||||
builder.pushClipRRect(
|
||||
RRect.fromLTRBAndCorners(
|
||||
100,
|
||||
100,
|
||||
400,
|
||||
400,
|
||||
topLeft: const Radius.circular(15),
|
||||
topRight: const Radius.circular(50),
|
||||
bottomLeft: const Radius.circular(50),
|
||||
),
|
||||
);
|
||||
finishBuilderByAddingPlatformViewAndPicture(builder, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Platform view with clip path.
|
||||
class PlatformViewClipPathScenario extends PlatformViewScenario {
|
||||
/// Constructs a platform view with clip rrect scenario.
|
||||
PlatformViewClipPathScenario(Window window, String text, {int id = 0})
|
||||
: super(window, text, id: id);
|
||||
|
||||
@override
|
||||
void onBeginFrame(Duration duration) {
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
|
||||
builder.pushOffset(0, 0);
|
||||
|
||||
// Create a path of rectangle with width of 200 and height of 300, starting from (100, 100).
|
||||
//
|
||||
// Refer to "../../ios/Scenarios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone SE_simulator.png" for the exact path after clipping.
|
||||
Path path = Path();
|
||||
path.moveTo(100, 100);
|
||||
path.quadraticBezierTo(50, 250, 100, 400);
|
||||
path.lineTo(350, 400);
|
||||
path.cubicTo(400, 300, 300, 200, 350, 100);
|
||||
path.close();
|
||||
builder.pushClipPath(path);
|
||||
|
||||
finishBuilderByAddingPlatformViewAndPicture(builder, 3);
|
||||
}
|
||||
}
|
||||
|
||||
/// Platform view with transform.
|
||||
class PlatformViewTransformScenario extends PlatformViewScenario {
|
||||
/// Constructs a platform view with transform scenario.
|
||||
PlatformViewTransformScenario(Window window, String text, {int id = 0})
|
||||
: super(window, text, id: id);
|
||||
|
||||
@override
|
||||
void onBeginFrame(Duration duration) {
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
|
||||
builder.pushOffset(0, 0);
|
||||
final Matrix4 matrix4 = Matrix4.identity()
|
||||
..rotateZ(1)
|
||||
..scale(0.5, 0.5, 1.0)
|
||||
..translate(1000.0, 100.0, 0.0);
|
||||
|
||||
builder.pushTransform(matrix4.storage);
|
||||
|
||||
finishBuilderByAddingPlatformViewAndPicture(builder, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/// Platform view with opacity.
|
||||
class PlatformViewOpacityScenario extends PlatformViewScenario {
|
||||
/// Constructs a platform view with transform scenario.
|
||||
PlatformViewOpacityScenario(Window window, String text, {int id = 0})
|
||||
: super(window, text, id: id);
|
||||
|
||||
@override
|
||||
void onBeginFrame(Duration duration) {
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
|
||||
builder.pushOffset(0, 0);
|
||||
builder.pushOpacity(150);
|
||||
|
||||
finishBuilderByAddingPlatformViewAndPicture(builder, 5);
|
||||
}
|
||||
}
|
||||
|
||||
mixin _BasePlatformViewScenarioMixin on Scenario {
|
||||
int _textureId;
|
||||
|
||||
/// Construct the platform view related scenario
|
||||
///
|
||||
/// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`.
|
||||
/// Call this method in the constructor of the platform view related scenarios
|
||||
/// to perform necessary set up.
|
||||
void constructScenario(Window window, String text, int id) {
|
||||
const int _valueInt32 = 3;
|
||||
const int _valueFloat64 = 6;
|
||||
const int _valueString = 7;
|
||||
@ -40,7 +176,7 @@ class PlatformViewScenario extends Scenario {
|
||||
const int _valueMap = 13;
|
||||
final Uint8List message = Uint8List.fromList(<int>[
|
||||
_valueString,
|
||||
'create'.length, // this is safe as long as these are all single byte characters.
|
||||
'create'.length, // this won't work if we use multi-byte characters.
|
||||
...utf8.encode('create'),
|
||||
_valueMap,
|
||||
if (Platform.isIOS)
|
||||
@ -94,29 +230,27 @@ class PlatformViewScenario extends Scenario {
|
||||
);
|
||||
}
|
||||
|
||||
int _textureId;
|
||||
|
||||
@override
|
||||
void onBeginFrame(Duration duration) {
|
||||
final SceneBuilder builder = SceneBuilder();
|
||||
|
||||
builder.pushOffset(0, 0);
|
||||
|
||||
// Add a platform view and a picture to the scene, then finish the `sceneBuilder`.
|
||||
void finishBuilderByAddingPlatformViewAndPicture(SceneBuilder sceneBuilder, int viewId) {
|
||||
if (Platform.isIOS) {
|
||||
builder.addPlatformView(0, width: 500, height: 500);
|
||||
sceneBuilder.addPlatformView(viewId, width: 500, height: 500);
|
||||
} else if (Platform.isAndroid && _textureId != null) {
|
||||
builder.addTexture(_textureId, offset: const Offset(150, 300), width: 500, height: 500);
|
||||
sceneBuilder.addTexture(_textureId,
|
||||
offset: const Offset(150, 300), width: 500, height: 500);
|
||||
} else {
|
||||
throw UnsupportedError('Platform ${Platform.operatingSystem} is not supported');
|
||||
throw UnsupportedError(
|
||||
'Platform ${Platform.operatingSystem} is not supported');
|
||||
}
|
||||
|
||||
final PictureRecorder recorder = PictureRecorder();
|
||||
final Canvas canvas = Canvas(recorder);
|
||||
canvas.drawCircle(const Offset(50, 50), 50, Paint()..color = const Color(0xFFABCDEF));
|
||||
canvas.drawCircle(
|
||||
const Offset(50, 50),
|
||||
50,
|
||||
Paint()..color = const Color(0xFFABCDEF),
|
||||
);
|
||||
final Picture picture = recorder.endRecording();
|
||||
builder.addPicture(const Offset(300, 300), picture);
|
||||
|
||||
final Scene scene = builder.build();
|
||||
sceneBuilder.addPicture(const Offset(300, 300), picture);
|
||||
final Scene scene = sceneBuilder.build();
|
||||
window.render(scene);
|
||||
scene.dispose();
|
||||
}
|
||||
|
||||
@ -8,3 +8,4 @@ dependencies:
|
||||
path: ../../../out/host_debug_unopt/gen/dart-pkg/sky_engine
|
||||
sky_services:
|
||||
path: ../../../out/host_debug_unopt/gen/dart-pkg/sky_services
|
||||
vector_math: ^2.0.8
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user