From c8f02c95eac7bc162fe9bd94c2f9e8eb0a0fc65e Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 20 Aug 2019 20:48:40 -0700 Subject: [PATCH] Add tests for platform views (#11319) --- .../dev/flutter/scenarios/MainActivity.java | 108 ++++++++------- .../flutter/scenarios/TextPlatformView.java | 32 +++++ .../scenarios/TextPlatformViewFactory.java | 43 ++++++ .../Scenarios.xcodeproj/project.pbxproj | 28 ++-- .../ios/Scenarios/Scenarios/AppDelegate.m | 38 +++++- .../ios/Scenarios/Scenarios/Info.plist | 2 + .../Scenarios/Scenarios/TextPlatformView.h | 23 ++++ .../Scenarios/Scenarios/TextPlatformView.m | 63 +++++++++ .../ScenariosUITests/PlatformViewUITests.m | 110 ++++++++++++++++ .../ios/Scenarios/ScenariosUITests/README.md | 13 ++ .../ScenariosUITests/ScenariosUITests.m | 42 ------ .../golden_platform_view_D21AAP.png | Bin 0 -> 82806 bytes ...lden_platform_view_iPhone SE_simulator.png | Bin 0 -> 27388 bytes testing/scenario_app/lib/main.dart | 10 +- .../scenario_app/lib/src/platform_view.dart | 123 ++++++++++++++++++ testing/scenario_app/lib/src/scenario.dart | 4 +- 16 files changed, 536 insertions(+), 103 deletions(-) create mode 100644 testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformView.java create mode 100644 testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformViewFactory.java create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.h create mode 100644 testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/README.md delete mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/ScenariosUITests.m create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_D21AAP.png create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_iPhone SE_simulator.png create mode 100644 testing/scenario_app/lib/src/platform_view.dart diff --git a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/MainActivity.java b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/MainActivity.java index d1632efa79f..16c964f2276 100644 --- a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/MainActivity.java +++ b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/MainActivity.java @@ -17,66 +17,78 @@ import io.flutter.Log; import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.android.FlutterFragment; import io.flutter.embedding.android.FlutterView; +import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterShellArgs; import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; import io.flutter.plugin.common.BasicMessageChannel; import io.flutter.plugin.common.BinaryCodec; public class MainActivity extends FlutterActivity implements OnFirstFrameRenderedListener { - final static String TAG = "Scenarios"; + final static String TAG = "Scenarios"; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - final Intent launchIntent = getIntent(); - if ("com.google.intent.action.TEST_LOOP".equals(launchIntent.getAction())) { - if(Build.VERSION.SDK_INT > 22){ - requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); - } - // Run for one minute, get the timeline data, write it, and finish. - final Uri logFileUri = launchIntent.getData(); - new Handler().postDelayed(() -> writeTimelineData(logFileUri), 20000); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final Intent launchIntent = getIntent(); + if ("com.google.intent.action.TEST_LOOP".equals(launchIntent.getAction())) { + if (Build.VERSION.SDK_INT > 22) { + requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); + } + // Run for one minute, get the timeline data, write it, and finish. + final Uri logFileUri = launchIntent.getData(); + new Handler().postDelayed(() -> writeTimelineData(logFileUri), 20000); } + } - public void onFirstFrameRendered() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - reportFullyDrawn(); - } + public void onFirstFrameRendered() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + reportFullyDrawn(); } + } - @Override - @NonNull - public FlutterShellArgs getFlutterShellArgs() { - FlutterShellArgs args = FlutterShellArgs.fromIntent(getIntent()); - args.add(FlutterShellArgs.ARG_TRACE_STARTUP); - args.add(FlutterShellArgs.ARG_ENABLE_DART_PROFILING); - args.add(FlutterShellArgs.ARG_VERBOSE_LOGGING); + @Override + @NonNull + public FlutterShellArgs getFlutterShellArgs() { + FlutterShellArgs args = FlutterShellArgs.fromIntent(getIntent()); + args.add(FlutterShellArgs.ARG_TRACE_STARTUP); + args.add(FlutterShellArgs.ARG_ENABLE_DART_PROFILING); + args.add(FlutterShellArgs.ARG_VERBOSE_LOGGING); - return args; + return args; + } + + @Override + public void configureFlutterEngine(FlutterEngine flutterEngine) { + flutterEngine.getPlatformViewsController() + .getRegistry() + .registerViewFactory( + "scenarios/textPlatformView", + new TextPlatformViewFactory()); + } + + + private void writeTimelineData(Uri logFile) { + if (logFile == null) { + throw new IllegalArgumentException(); } - - private void writeTimelineData(Uri logFile) { - if (logFile == null) { - throw new IllegalArgumentException(); - } - if (getFlutterEngine() == null) { - Log.e(TAG, "Could not write timeline data - no engine."); - return; - } - final BasicMessageChannel channel = new BasicMessageChannel<>( - getFlutterEngine().getDartExecutor(), "write_timeline", BinaryCodec.INSTANCE); - channel.send(null, (ByteBuffer reply) -> { - try { - final FileDescriptor fd = getContentResolver() - .openAssetFileDescriptor(logFile, "w").getFileDescriptor(); - final FileOutputStream outputStream = new FileOutputStream(fd); - outputStream.write(reply.array()); - outputStream.close(); - } catch (IOException ex) { - Log.e(TAG, "Could not write timeline file: " + ex.toString()); - } - finish(); - }); + if (getFlutterEngine() == null) { + Log.e(TAG, "Could not write timeline data - no engine."); + return; } + final BasicMessageChannel channel = new BasicMessageChannel<>( + getFlutterEngine().getDartExecutor(), "write_timeline", BinaryCodec.INSTANCE); + channel.send(null, (ByteBuffer reply) -> { + try { + final FileDescriptor fd = getContentResolver() + .openAssetFileDescriptor(logFile, "w") + .getFileDescriptor(); + final FileOutputStream outputStream = new FileOutputStream(fd); + outputStream.write(reply.array()); + outputStream.close(); + } catch (IOException ex) { + Log.e(TAG, "Could not write timeline file: " + ex.toString()); + } + finish(); + }); + } } diff --git a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformView.java b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformView.java new file mode 100644 index 00000000000..b54d0c22c5f --- /dev/null +++ b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformView.java @@ -0,0 +1,32 @@ +// Copyright 2018 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. + +package dev.flutter.scenarios; + +import android.content.Context; +import android.graphics.Color; +import android.view.View; +import android.widget.TextView; + +import io.flutter.plugin.platform.PlatformView; + +public class TextPlatformView implements PlatformView { + private final TextView textView; + + @SuppressWarnings("unchecked") + TextPlatformView(final Context context, int id, String params) { + textView = new TextView(context); + textView.setTextSize(72); + textView.setBackgroundColor(Color.rgb(255, 255, 255)); + textView.setText(params); + } + + @Override + public View getView() { + return textView; + } + + @Override + public void dispose() {} +} diff --git a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformViewFactory.java b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformViewFactory.java new file mode 100644 index 00000000000..79653ba8994 --- /dev/null +++ b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformViewFactory.java @@ -0,0 +1,43 @@ +// Copyright 2018 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. + +package dev.flutter.scenarios; + +import android.content.Context; +import android.support.annotation.Nullable; + +import java.nio.ByteBuffer; + +import io.flutter.plugin.common.MessageCodec; +import io.flutter.plugin.common.StringCodec; +import io.flutter.plugin.platform.PlatformView; +import io.flutter.plugin.platform.PlatformViewFactory; + +public final class TextPlatformViewFactory extends PlatformViewFactory { + TextPlatformViewFactory() { + super(new MessageCodec() { + @Nullable + @Override + public ByteBuffer encodeMessage(@Nullable Object o) { + if (o instanceof String) { + return StringCodec.INSTANCE.encodeMessage((String)o); + } + return null; + } + + @Nullable + @Override + public Object decodeMessage(@Nullable ByteBuffer byteBuffer) { + return StringCodec.INSTANCE.decodeMessage(byteBuffer); + } + }); + } + + @SuppressWarnings("unchecked") + @Override + public PlatformView create(Context context, int id, Object args) { + String params = (String) args; + return new TextPlatformView(context, id, params); + } +} diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index 5e76e5c4334..17ef6c9a421 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -21,9 +21,11 @@ 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 248D76CB22E388370012F0C1 /* AppDelegate.m */; }; 248D76D422E388380012F0C1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 248D76D322E388380012F0C1 /* Assets.xcassets */; }; 248D76DA22E388380012F0C1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 248D76D922E388380012F0C1 /* main.m */; }; - 248D76E422E388380012F0C1 /* ScenariosTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 248D76E322E388380012F0C1 /* ScenariosTests.m */; }; - 248D76EF22E388380012F0C1 /* ScenariosUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 248D76EE22E388380012F0C1 /* ScenariosUITests.m */; }; + 248D76EF22E388380012F0C1 /* PlatformViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 248D76EE22E388380012F0C1 /* PlatformViewUITests.m */; }; 248FDFC422FE7CD0009CC7CD /* FlutterEngineTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 248FDFC322FE7CD0009CC7CD /* FlutterEngineTest.m */; }; + 24D47D1B230C79840069DD5E /* golden_platform_view_D21AAP.png in Resources */ = {isa = PBXBuildFile; fileRef = 24D47D1A230C79840069DD5E /* golden_platform_view_D21AAP.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 */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -91,12 +93,16 @@ 248D76D822E388380012F0C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 248D76D922E388380012F0C1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 248D76DF22E388380012F0C1 /* ScenariosTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScenariosTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 248D76E322E388380012F0C1 /* ScenariosTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScenariosTests.m; sourceTree = ""; }; 248D76E522E388380012F0C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 248D76EA22E388380012F0C1 /* ScenariosUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScenariosUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 248D76EE22E388380012F0C1 /* ScenariosUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScenariosUITests.m; sourceTree = ""; }; + 248D76EE22E388380012F0C1 /* PlatformViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewUITests.m; sourceTree = ""; }; 248D76F022E388380012F0C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 248FDFC322FE7CD0009CC7CD /* FlutterEngineTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlutterEngineTest.m; sourceTree = ""; }; + 24D47D1A230C79840069DD5E /* golden_platform_view_D21AAP.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = golden_platform_view_D21AAP.png; sourceTree = ""; }; + 24D47D1C230CA2700069DD5E /* golden_platform_view_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_iPhone SE_simulator.png"; sourceTree = ""; }; + 24D47D1E230CA4480069DD5E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 24F1FB87230B4579005ACE7C /* TextPlatformView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextPlatformView.m; sourceTree = ""; }; + 24F1FB88230B4579005ACE7C /* TextPlatformView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextPlatformView.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -152,6 +158,8 @@ 248D76C922E388370012F0C1 /* Scenarios */ = { isa = PBXGroup; children = ( + 24F1FB88230B4579005ACE7C /* TextPlatformView.h */, + 24F1FB87230B4579005ACE7C /* TextPlatformView.m */, 248D76CA22E388370012F0C1 /* AppDelegate.h */, 248D76CB22E388370012F0C1 /* AppDelegate.m */, 248D76D322E388380012F0C1 /* Assets.xcassets */, @@ -166,7 +174,6 @@ children = ( 248FDFC322FE7CD0009CC7CD /* FlutterEngineTest.m */, 0DB781FC22EA2C0300E9B371 /* FlutterViewControllerTest.m */, - 248D76E322E388380012F0C1 /* ScenariosTests.m */, 248D76E522E388380012F0C1 /* Info.plist */, ); path = ScenariosTests; @@ -175,8 +182,11 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( - 248D76EE22E388380012F0C1 /* ScenariosUITests.m */, + 24D47D1C230CA2700069DD5E /* golden_platform_view_iPhone SE_simulator.png */, + 24D47D1A230C79840069DD5E /* golden_platform_view_D21AAP.png */, + 248D76EE22E388380012F0C1 /* PlatformViewUITests.m */, 248D76F022E388380012F0C1 /* Info.plist */, + 24D47D1E230CA4480069DD5E /* README.md */, ); path = ScenariosUITests; sourceTree = ""; @@ -311,6 +321,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 24D47D1B230C79840069DD5E /* golden_platform_view_D21AAP.png in Resources */, + 24D47D1D230CA2700069DD5E /* golden_platform_view_iPhone SE_simulator.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -322,6 +334,7 @@ buildActionMask = 2147483647; files = ( 248D76DA22E388380012F0C1 /* main.m in Sources */, + 24F1FB89230B4579005ACE7C /* TextPlatformView.m in Sources */, 248D76CC22E388370012F0C1 /* AppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -330,7 +343,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 248D76E422E388380012F0C1 /* ScenariosTests.m in Sources */, 0DB7820222EA493B00E9B371 /* FlutterViewControllerTest.m in Sources */, 248FDFC422FE7CD0009CC7CD /* FlutterEngineTest.m in Sources */, ); @@ -340,7 +352,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 248D76EF22E388380012F0C1 /* ScenariosUITests.m in Sources */, + 248D76EF22E388380012F0C1 /* PlatformViewUITests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 97af94c5320..38e14bda4f3 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -1,12 +1,48 @@ #include "AppDelegate.h" +#import "TextPlatformView.h" + +@interface NoStatusBarFlutterViewController : FlutterViewController + +@end + +@implementation NoStatusBarFlutterViewController +- (BOOL)prefersStatusBarHidden { + return YES; +} +@end @implementation AppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - self.window.rootViewController = [[UIViewController alloc] init]; + + // 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] 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:[@"text_platform_view" dataUsingEncoding:NSUTF8StringEncoding]]; + }]; + TextPlatformViewFactory* textPlatformViewFactory = + [[TextPlatformViewFactory alloc] initWithMessenger:flutterViewController.binaryMessenger]; + NSObject* registrar = + [flutterViewController.engine registrarForPlugin:@"scenarios/TextPlatformViewPlugin"]; + [registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView"]; + self.window.rootViewController = flutterViewController; + } else { + self.window.rootViewController = [[UIViewController alloc] init]; + } [self.window makeKeyAndVisible]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/Info.plist b/testing/scenario_app/ios/Scenarios/Scenarios/Info.plist index ce0bb08859a..3c41170863c 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/Info.plist +++ b/testing/scenario_app/ios/Scenarios/Scenarios/Info.plist @@ -30,6 +30,8 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + io.flutter.embedded_views_preview + UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.h b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.h new file mode 100644 index 00000000000..e88fbe9ee2f --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.h @@ -0,0 +1,23 @@ +// Copyright 2013 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface TextPlatformView : NSObject + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject*)messenger; + +- (UIView*)view; +@end + +@interface TextPlatformViewFactory : NSObject +- (instancetype)initWithMessenger:(NSObject*)messenger; +@end + +NS_ASSUME_NONNULL_END diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m new file mode 100644 index 00000000000..c75b5c815d0 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m @@ -0,0 +1,63 @@ +// Copyright 2018 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 "TextPlatformView.h" + +@implementation TextPlatformViewFactory { + NSObject* _messenger; +} + +- (instancetype)initWithMessenger:(NSObject*)messenger { + self = [super init]; + if (self) { + _messenger = messenger; + } + return self; +} + +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + TextPlatformView* textPlatformView = [[TextPlatformView alloc] initWithFrame:frame + viewIdentifier:viewId + arguments:args + binaryMessenger:_messenger]; + return textPlatformView; +} + +- (NSObject*)createArgsCodec { + return [FlutterStringCodec sharedInstance]; +} + +@end + +@implementation TextPlatformView { + int64_t _viewId; + UITextView* _textView; + FlutterMethodChannel* _channel; +} + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject*)messenger { + if ([super init]) { + _viewId = viewId; + _textView = [[UITextView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; + _textView.textColor = UIColor.blueColor; + _textView.backgroundColor = UIColor.lightGrayColor; + [_textView setFont:[UIFont systemFontOfSize:52]]; + _textView.text = args; + } + return self; +} + +- (UIView*)view { + return _textView; +} + +// - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { +// } + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m new file mode 100644 index 00000000000..0252f185ba7 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m @@ -0,0 +1,110 @@ +// 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 +#import +#include + +#import "../Scenarios/TextPlatformView.h" + +@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]; +} + +- (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]; + if (golden) { + XCTAttachment* goldenAttachment = [XCTAttachment attachmentWithImage:golden]; + goldenAttachment.lifetime = XCTAttachmentLifetimeKeepAlways; + [self addAttachment:goldenAttachment]; + } + + XCTAttachment* attachment = [XCTAttachment attachmentWithScreenshot:screenshot]; + attachment.lifetime = XCTAttachmentLifetimeKeepAlways; + [self addAttachment:attachment]; + 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; +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/README.md b/testing/scenario_app/ios/Scenarios/ScenariosUITests/README.md new file mode 100644 index 00000000000..d5d3bc62860 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/README.md @@ -0,0 +1,13 @@ +# PlatformView UI Tests + +This folder contains a test for platform views. It renders a platform view +and does a screen shot comparison against a known good configuration. + +The screen shots are named `golden_platform_view_MODEL`, with `_simulator` +appended for simulators. The model numbers for physical devices correspond +to the `hw.model` sys call, and will represent the model numbers. Simulator +names are taken from the environment. + +New devices require running the test on the device, gathering the attachment +and verifying it manually, and then adding an appropriately named file to +this folder. diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/ScenariosUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/ScenariosUITests.m deleted file mode 100644 index fc47337b583..00000000000 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/ScenariosUITests.m +++ /dev/null @@ -1,42 +0,0 @@ -// -// ScenariosUITests.m -// ScenariosUITests -// -// Created by Dan Field on 7/20/19. -// Copyright © 2019 flutter. All rights reserved. -// - -#import - -@interface ScenariosUITests : XCTestCase - -@end - -@implementation ScenariosUITests - -- (void)setUp { - // Put setup code here. This method is called before the invocation of each test method in the - // class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - self.continueAfterFailure = NO; - - // UI tests must launch the application that they test. Doing this in setup will make sure it - // happens for each test method. - [[[XCUIApplication alloc] init] launch]; - - // In UI tests it’s important to set the initial state - such as interface orientation - required - // for your tests before they run. The setUp method is a good place to do this. -} - -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the - // class. -} - -- (void)testExample { - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. -} - -@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_D21AAP.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_D21AAP.png new file mode 100644 index 0000000000000000000000000000000000000000..bd59044cc20776dff84951bca2822f7ff5883033 GIT binary patch literal 82806 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 HjNkna{veBV literal 0 HcmV?d00001 diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index db707955465..18a64dab083 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -10,10 +10,12 @@ import 'dart:typed_data'; import 'dart:ui'; import 'src/animated_color_square.dart'; +import 'src/platform_view.dart'; import 'src/scenario.dart'; Map _scenarios = { 'animated_color_square': AnimatedColorSquareScenario(window), + 'text_platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)'), }; Scenario _currentScenario = _scenarios['animated_color_square']; @@ -30,7 +32,10 @@ void main() { window.sendPlatformMessage('scenario_status', data, null); } -Future _handlePlatformMessage(String name, ByteData data, PlatformMessageResponseCallback callback) async { +Future _handlePlatformMessage( + String name, ByteData data, PlatformMessageResponseCallback callback) async { + print(name); + print(utf8.decode(data.buffer.asUint8List())); if (name == 'set_scenario' && data != null) { final String scenarioName = utf8.decode(data.buffer.asUint8List()); final Scenario candidateScenario = _scenarios[scenarioName]; @@ -59,7 +64,8 @@ Future _getTimelineData() async { final Map cpuTimelineJson = await _getJson(cpuProfileTimelineUri); final Map vmServiceTimelineJson = await _getJson(vmServiceTimelineUri); final Map cpuResult = cpuTimelineJson['result'].cast(); - final Map vmServiceResult = vmServiceTimelineJson['result'].cast(); + final Map vmServiceResult = + vmServiceTimelineJson['result'].cast(); return json.encode({ 'stackFrames': cpuResult['stackFrames'], diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart new file mode 100644 index 00000000000..4286469ce29 --- /dev/null +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -0,0 +1,123 @@ +// 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 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'scenario.dart'; + +List _to32(int value) { + final Uint8List temp = Uint8List(4); + temp.buffer.asByteData().setInt32(0, value, Endian.little); + return temp; +} + +List _to64(num value) { + final Uint8List temp = Uint8List(15); + if (value is double) { + temp.buffer.asByteData().setFloat64(7, value, Endian.little); + } else if (value is int) { + temp.buffer.asByteData().setInt64(7, value, Endian.little); + } + return temp; +} + +/// A simple platform view. +class PlatformViewScenario extends Scenario { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + const int _valueInt32 = 3; + const int _valueFloat64 = 6; + const int _valueString = 7; + const int _valueUint8List = 8; + const int _valueMap = 13; + final Uint8List message = Uint8List.fromList([ + _valueString, + 'create'.length, // this is safe as long as these are all single byte characters. + ...utf8.encode('create'), + _valueMap, + if (Platform.isIOS) + 3, // 3 entries in map for iOS. + if (Platform.isAndroid) + 6, // 6 entries in map for Android. + _valueString, + 'id'.length, + ...utf8.encode('id'), + _valueInt32, + ..._to32(id), + _valueString, + 'viewType'.length, + ...utf8.encode('viewType'), + _valueString, + 'scenarios/textPlatformView'.length, + ...utf8.encode('scenarios/textPlatformView'), + if (Platform.isAndroid) ...[ + _valueString, + 'width'.length, + ...utf8.encode('width'), + _valueFloat64, + ..._to64(500.0), + _valueString, + 'height'.length, + ...utf8.encode('height'), + _valueFloat64, + ..._to64(500.0), + _valueString, + 'direction'.length, + ...utf8.encode('direction'), + _valueInt32, + ..._to32(0), // LTR + ], + _valueString, + 'params'.length, + ...utf8.encode('params'), + _valueUint8List, + text.length, + ...utf8.encode(text), + ]); + + window.sendPlatformMessage( + 'flutter/platform_views', + message.buffer.asByteData(), + (ByteData response) { + if (Platform.isAndroid) { + _textureId = response.getInt64(2); + } + }, + ); + } + + int _textureId; + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + if (Platform.isIOS) { + builder.addPlatformView(0, width: 500, height: 500); + } else if (Platform.isAndroid && _textureId != null) { + builder.addTexture(_textureId, offset: const Offset(150, 300), width: 500, height: 500); + } else { + 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)); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} diff --git a/testing/scenario_app/lib/src/scenario.dart b/testing/scenario_app/lib/src/scenario.dart index 348e204d6e1..be474016a31 100644 --- a/testing/scenario_app/lib/src/scenario.dart +++ b/testing/scenario_app/lib/src/scenario.dart @@ -21,10 +21,10 @@ abstract class Scenario { /// flushed. /// /// See [Window.onDrawFrame] for more details. - void onDrawFrame(); + void onDrawFrame() {} /// Called by the program when the window metrics have changed. /// /// See [Window.onMetricsChanged]. - void onMetricsChanged(); + void onMetricsChanged() {} }