Skip to content

Commit

Permalink
Add native unit tests to iOS and macOS templates (#117147)
Browse files Browse the repository at this point in the history
* Improve Swift plugin implementation

* Add iOS tests

* Review feedback on structure

* Remove duplicate scheme file

* Add macOS

* Add iOS

* swift test tweaks

* unit tests

* Whitespace

* Add e2e tests
  • Loading branch information
stuartmorgan committed Dec 21, 2022
1 parent 9a347fb commit 2a50236
Show file tree
Hide file tree
Showing 18 changed files with 633 additions and 93 deletions.
3 changes: 2 additions & 1 deletion dev/devicelab/lib/framework/ios.dart
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ Future<bool> runXcodeTests({
required String platformDirectory,
required String destination,
required String testName,
String configuration = 'Release',
bool skipCodesign = false,
}) async {
final Map<String, String> environment = Platform.environment;
Expand All @@ -161,7 +162,7 @@ Future<bool> runXcodeTests({
'-scheme',
'Runner',
'-configuration',
'Release',
configuration,
'-destination',
destination,
'-resultBundlePath',
Expand Down
44 changes: 41 additions & 3 deletions dev/devicelab/lib/tasks/plugin_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;

import '../framework/framework.dart';
import '../framework/ios.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';

Expand Down Expand Up @@ -59,7 +60,10 @@ class PluginTest {
}
section('Test plugin');
if (runFlutterTest) {
await plugin.test();
await plugin.runFlutterTest();
if (!dartOnlyPlugin) {
await plugin.example.runNativeTests(buildTarget);
}
}
section('Create Flutter app');
final _FlutterProject app = await _FlutterProject.create(tempDir, options, buildTarget,
Expand All @@ -73,7 +77,7 @@ class PluginTest {
await app.build(buildTarget, validateNativeBuildProject: !dartOnlyPlugin);
if (runFlutterTest) {
section('Test app');
await app.test();
await app.runFlutterTest();
}
} finally {
await plugin.delete();
Expand All @@ -98,6 +102,10 @@ class _FlutterProject {

File get pubspecFile => File(path.join(rootPath, 'pubspec.yaml'));

_FlutterProject get example {
return _FlutterProject(Directory(path.join(rootPath)), 'example');
}

Future<void> addPlugin(String plugin, {String? pluginPath}) async {
final File pubspec = pubspecFile;
String content = await pubspec.readAsString();
Expand Down Expand Up @@ -151,12 +159,42 @@ class $dartPluginClass {
}
}

Future<void> test() async {
Future<void> runFlutterTest() async {
await inDirectory(Directory(rootPath), () async {
await flutter('test');
});
}

Future<void> runNativeTests(String buildTarget) async {
// Native unit tests rely on building the app first to generate necessary
// build files.
await build(buildTarget, validateNativeBuildProject: false);

if (buildTarget == 'ios') {
await testWithNewIOSSimulator('TestNativeUnitTests', (String deviceId) async {
if (!await runXcodeTests(
platformDirectory: path.join(rootPath, 'ios'),
destination: 'id=$deviceId',
configuration: 'Debug',
testName: 'native_plugin_unit_tests_ios',
skipCodesign: true,
)) {
throw TaskResult.failure('Platform unit tests failed');
}
});
} else if (buildTarget == 'macos') {
if (!await runXcodeTests(
platformDirectory: path.join(rootPath, 'macos'),
destination: 'platform=macOS',
configuration: 'Debug',
testName: 'native_plugin_unit_tests_macos',
skipCodesign: true,
)) {
throw TaskResult.failure('Platform unit tests failed');
}
}
}

static Future<_FlutterProject> create(
Directory directory,
List<String> options,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 331C80F3294D02FB00263BE5 /* RunnerTests.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
Expand All @@ -16,6 +17,16 @@
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
Expand All @@ -32,6 +43,8 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C80F1294D02FB00263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80F3294D02FB00263BE5 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
Expand All @@ -47,6 +60,13 @@
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
331C80EE294D02FB00263BE5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
Expand All @@ -57,6 +77,14 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
331C80F2294D02FB00263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C80F3294D02FB00263BE5 /* RunnerTests.m */,
);
path = RunnerTests;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
Expand All @@ -73,6 +101,7 @@
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
331C80F2294D02FB00263BE5 /* RunnerTests */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
Expand All @@ -81,6 +110,7 @@
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C80F1294D02FB00263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
Expand Down Expand Up @@ -112,6 +142,24 @@
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
331C80F0294D02FB00263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C80ED294D02FB00263BE5 /* Sources */,
331C80EE294D02FB00263BE5 /* Frameworks */,
331C80EF294D02FB00263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C80F6294D02FB00263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
Expand Down Expand Up @@ -141,6 +189,10 @@
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C80F0294D02FB00263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
};
Expand All @@ -160,11 +212,19 @@
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C80F0294D02FB00263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
331C80EF294D02FB00263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
Expand Down Expand Up @@ -212,6 +272,14 @@
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
331C80ED294D02FB00263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
Expand All @@ -224,6 +292,14 @@
};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
331C80F6294D02FB00263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
Expand Down Expand Up @@ -315,6 +391,45 @@
};
name = Profile;
};
331C80F8294D02FB00263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}}.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C80F9294D02FB00263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}}.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C80FA294D02FB00263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}}.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
Expand Down Expand Up @@ -465,6 +580,16 @@
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C80F8294D02FB00263BE5 /* Debug */,
331C80F9294D02FB00263BE5 /* Release */,
331C80FA294D02FB00263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C80F0294D02FB00263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
{{#withPlatformChannelPluginHook}}

@import {{pluginProjectName}};

// This demonstrates a simple unit test of the Objective-C portion of this plugin's implementation.
//
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
{{/withPlatformChannelPluginHook}}

@interface RunnerTests : XCTestCase

@end

@implementation RunnerTests

{{#withPlatformChannelPluginHook}}
- (void)testExample {
{{pluginClass}} *plugin = [[{{pluginClass}} alloc] init];

FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getPlatformVersion"
arguments:nil];
XCTestExpectation *expectation = [self expectationWithDescription:@"result block must be called"];
[plugin handleMethodCall:call
result:^(id result) {
NSString *expected = [NSString
stringWithFormat:@"iOS %@", UIDevice.currentDevice.systemVersion];
XCTAssertEqualObjects(result, expected);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:1 handler:nil];
}
{{/withPlatformChannelPluginHook}}
{{^withPlatformChannelPluginHook}}
- (void)testExample {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
{{/withPlatformChannelPluginHook}}

@end
Loading

0 comments on commit 2a50236

Please sign in to comment.