Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,24 @@ - (void)requestApplicationTermination:(id)sender

#pragma mark -

@implementation FlutterPasteboard

- (NSInteger)clearContents {
return [[NSPasteboard generalPasteboard] clearContents];
}

- (NSString*)stringForType:(NSPasteboardType)dataType {
return [[NSPasteboard generalPasteboard] stringForType:dataType];
}

- (BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
}

@end

#pragma mark -

/**
* `FlutterPluginRegistrar` implementation handling a single plugin.
*/
Expand Down Expand Up @@ -452,6 +470,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix
allowHeadlessExecution:(BOOL)allowHeadlessExecution {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_pasteboard = [[FlutterPasteboard alloc] init];
_active = NO;
_visible = NO;
_project = project ?: [[FlutterDartProject alloc] init];
Expand Down Expand Up @@ -1186,31 +1205,25 @@ - (void)playSystemSound:(NSString*)soundType {
}

- (NSDictionary*)getClipboardData:(NSString*)format {
NSPasteboard* pasteboard = self.pasteboard;
if ([format isEqualToString:@(kTextPlainFormat)]) {
NSString* stringInPasteboard = [pasteboard stringForType:NSPasteboardTypeString];
NSString* stringInPasteboard = [self.pasteboard stringForType:NSPasteboardTypeString];
return stringInPasteboard == nil ? nil : @{@"text" : stringInPasteboard};
}
return nil;
}

- (void)setClipboardData:(NSDictionary*)data {
NSPasteboard* pasteboard = self.pasteboard;
NSString* text = data[@"text"];
[pasteboard clearContents];
[self.pasteboard clearContents];
if (text && ![text isEqual:[NSNull null]]) {
[pasteboard setString:text forType:NSPasteboardTypeString];
[self.pasteboard setString:text forType:NSPasteboardTypeString];
}
}

- (BOOL)clipboardHasStrings {
return [self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
}

- (NSPasteboard*)pasteboard {
return [NSPasteboard generalPasteboard];
}

- (std::vector<std::string>)switches {
return flutter::GetSwitchesFromEnvironment();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,40 @@
// found in the LICENSE file.

#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h"

#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"

#include "flutter/testing/testing.h"

/**
* Fake pasteboard implementation to allow tests to work in environments without a real
* pasteboard.
*/
@interface FakePasteboard : FlutterPasteboard
@end

@implementation FakePasteboard {
NSString* _result;
}

- (NSInteger)clearContents {
size_t changeCount = (_result != nil) ? 1 : 0;
_result = nil;
return changeCount;
}

- (NSString*)stringForType:(NSPasteboardType)dataType {
return _result;
}

- (BOOL)setString:(NSString*)string forType:(NSPasteboardType)dataType {
_result = string;
return YES;
}

@end

namespace flutter::testing {

FlutterEngineTest::FlutterEngineTest() = default;
Expand Down Expand Up @@ -45,26 +74,19 @@
}

id CreateMockFlutterEngine(NSString* pasteboardString) {
{
NSString* fixtures = @(testing::GetFixturesPath());
FlutterDartProject* project = [[FlutterDartProject alloc]
initWithAssetsPath:fixtures
ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test"
project:project
allowHeadlessExecution:true];

// Mock pasteboard so that this test will work in environments without a
// real pasteboard.
id pasteboardMock = OCMClassMock([NSPasteboard class]);
OCMExpect([pasteboardMock stringForType:[OCMArg any]]).andDo(^(NSInvocation* invocation) {
NSString* returnValue = pasteboardString.length > 0 ? pasteboardString : nil;
[invocation setReturnValue:&returnValue];
});
id engineMock = OCMPartialMock(engine);
OCMStub([engineMock pasteboard]).andReturn(pasteboardMock);
return engineMock;
}
NSString* fixtures = @(testing::GetFixturesPath());
FlutterDartProject* project = [[FlutterDartProject alloc]
initWithAssetsPath:fixtures
ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test"
project:project
allowHeadlessExecution:true];

FakePasteboard* pasteboardMock = [[FakePasteboard alloc] init];
[pasteboardMock setString:pasteboardString forType:NSPasteboardTypeString];
engine.pasteboard = pasteboardMock;
id engineMock = OCMPartialMock(engine);
return engineMock;
}

MockFlutterEngineTest::MockFlutterEngineTest() = default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) {
result:(nullable FlutterResult)result;
@end

/**
* An NSPasteboard wrapper object to allow for substitution of a fake in unit tests.
*/
@interface FlutterPasteboard : NSObject
- (NSInteger)clearContents;
- (NSString*)stringForType:(NSPasteboardType)dataType;
- (BOOL)setString:(NSString*)string forType:(NSPasteboardType)dataType;
@end

@interface FlutterEngine ()

/**
Expand Down Expand Up @@ -98,7 +107,7 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) {
/**
* This just returns the NSPasteboard so that it can be mocked in the tests.
*/
@property(nonatomic, readonly, nonnull) NSPasteboard* pasteboard;
@property(nonatomic, nonnull) FlutterPasteboard* pasteboard;

/**
* The command line arguments array for the engine.
Expand Down