mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

When the `"${native_assets_path}"*.framework` glob doesn't resolve anything, the bash will run the loop once with the original unglobbed value: `/path/to/native/assets/*.framework`. Skip this case to prevent the build from failing when there are no frameworks to sign. To reproduce this build failure: 1. Enable native assets in the Flutter tool: `flutter config --enable-native-assets` 2. Create a Flutter project with the default template: `flutter create test_native_assets` 3. Add a build hook that does nothing (`hook/build.dart`). 4. Try to build/run the app: `flutter run --debug -d macos`
705 lines
26 KiB
Dart
705 lines
26 KiB
Dart
// Copyright 2014 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.
|
|
|
|
// This test exercises the embedding of the native assets mapping in dill files.
|
|
// An initial dill file is created by `flutter assemble` and used for running
|
|
// the application. This dill must contain the mapping.
|
|
// When doing hot reload, this mapping must stay in place.
|
|
// When doing a hot restart, a new dill file is pushed. This dill file must also
|
|
// contain the native assets mapping.
|
|
// When doing a hot reload, this mapping must stay in place.
|
|
|
|
@Timeout(Duration(minutes: 10))
|
|
library;
|
|
|
|
import 'dart:io';
|
|
|
|
import 'package:file/file.dart';
|
|
import 'package:file_testing/file_testing.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/os.dart';
|
|
import 'package:native_assets_cli/native_assets_cli_internal.dart';
|
|
|
|
import '../../src/common.dart';
|
|
import '../test_utils.dart' show ProcessResultMatcher, fileSystem, platform;
|
|
import '../transition_test_utils.dart';
|
|
|
|
final String hostOs = platform.operatingSystem;
|
|
|
|
final List<String> devices = <String>[
|
|
'flutter-tester',
|
|
hostOs,
|
|
];
|
|
|
|
final List<String> buildSubcommands = <String>[
|
|
hostOs,
|
|
if (hostOs == 'macos') 'ios',
|
|
'apk',
|
|
];
|
|
|
|
final List<String> add2appBuildSubcommands = <String>[
|
|
if (hostOs == 'macos') ...<String>[
|
|
'macos-framework',
|
|
'ios-framework',
|
|
],
|
|
];
|
|
|
|
/// The build modes to target for each flutter command that supports passing
|
|
/// a build mode.
|
|
///
|
|
/// The flow of compiling kernel as well as bundling dylibs can differ based on
|
|
/// build mode, so we should cover this.
|
|
const List<String> buildModes = <String>[
|
|
'debug',
|
|
'profile',
|
|
'release',
|
|
];
|
|
|
|
const String packageName = 'package_with_native_assets';
|
|
|
|
const String exampleAppName = '${packageName}_example';
|
|
|
|
void main() {
|
|
if (!platform.isMacOS && !platform.isLinux && !platform.isWindows) {
|
|
// TODO(dacoharkes): Implement Fuchsia. https://github.com/flutter/flutter/issues/129757
|
|
return;
|
|
}
|
|
|
|
setUpAll(() {
|
|
processManager.runSync(<String>[
|
|
flutterBin,
|
|
'config',
|
|
'--enable-native-assets',
|
|
]);
|
|
});
|
|
|
|
for (final String device in devices) {
|
|
for (final String buildMode in buildModes) {
|
|
if (device == 'flutter-tester' && buildMode != 'debug') {
|
|
continue;
|
|
}
|
|
final String hotReload = buildMode == 'debug' ? ' hot reload and hot restart' : '';
|
|
testWithoutContext('flutter run$hotReload with native assets $device $buildMode', () async {
|
|
await inTempDir((Directory tempDirectory) async {
|
|
final Directory packageDirectory = await createTestProject(packageName, tempDirectory);
|
|
final Directory exampleDirectory = packageDirectory.childDirectory('example');
|
|
|
|
final ProcessTestResult result = await runFlutter(
|
|
<String>[
|
|
'run',
|
|
'-d$device',
|
|
'--$buildMode',
|
|
],
|
|
exampleDirectory.path,
|
|
<Transition>[
|
|
Multiple(<Pattern>[
|
|
'Flutter run key commands.',
|
|
], handler: (String line) {
|
|
if (buildMode == 'debug') {
|
|
// Do a hot reload diff on the initial dill file.
|
|
return 'r';
|
|
} else {
|
|
// No hot reload and hot restart in release mode.
|
|
return 'q';
|
|
}
|
|
}),
|
|
if (buildMode == 'debug') ...<Transition>[
|
|
Barrier(
|
|
'Performing hot reload...'.padRight(progressMessageWidth),
|
|
logging: true,
|
|
),
|
|
Multiple(<Pattern>[
|
|
RegExp('Reloaded .*'),
|
|
], handler: (String line) {
|
|
// Do a hot restart, pushing a new complete dill file.
|
|
return 'R';
|
|
}),
|
|
Barrier('Performing hot restart...'.padRight(progressMessageWidth)),
|
|
Multiple(<Pattern>[
|
|
RegExp('Restarted application .*'),
|
|
], handler: (String line) {
|
|
// Do another hot reload, pushing a diff to the second dill file.
|
|
return 'r';
|
|
}),
|
|
Barrier(
|
|
'Performing hot reload...'.padRight(progressMessageWidth),
|
|
logging: true,
|
|
),
|
|
Multiple(<Pattern>[
|
|
RegExp('Reloaded .*'),
|
|
], handler: (String line) {
|
|
return 'q';
|
|
}),
|
|
],
|
|
const Barrier('Application finished.'),
|
|
],
|
|
logging: false,
|
|
);
|
|
if (result.exitCode != 0) {
|
|
throw Exception('flutter run failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}');
|
|
}
|
|
final String stdout = result.stdout.join('\n');
|
|
// Check that we did not fail to resolve the native function in the
|
|
// dynamic library.
|
|
expect(stdout, isNot(contains("Invalid argument(s): Couldn't resolve native function 'sum'")));
|
|
// And also check that we did not have any other exceptions that might
|
|
// shadow the exception we would have gotten.
|
|
expect(stdout, isNot(contains('EXCEPTION CAUGHT BY WIDGETS LIBRARY')));
|
|
|
|
switch (device) {
|
|
case 'macos':
|
|
expectDylibIsBundledMacOS(exampleDirectory, buildMode);
|
|
case 'linux':
|
|
expectDylibIsBundledLinux(exampleDirectory, buildMode);
|
|
case 'windows':
|
|
expectDylibIsBundledWindows(exampleDirectory, buildMode);
|
|
}
|
|
if (device == hostOs) {
|
|
expectCCompilerIsConfigured(exampleDirectory);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
testWithoutContext('flutter test with native assets', () async {
|
|
await inTempDir((Directory tempDirectory) async {
|
|
final Directory packageDirectory = await createTestProject(packageName, tempDirectory);
|
|
|
|
final ProcessTestResult result = await runFlutter(
|
|
<String>[
|
|
'test',
|
|
],
|
|
packageDirectory.path,
|
|
<Transition>[
|
|
Barrier(RegExp('.* All tests passed!')),
|
|
],
|
|
logging: false,
|
|
);
|
|
if (result.exitCode != 0) {
|
|
throw Exception('flutter test failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}');
|
|
}
|
|
});
|
|
});
|
|
|
|
for (final String buildSubcommand in buildSubcommands) {
|
|
for (final String buildMode in buildModes) {
|
|
testWithoutContext('flutter build $buildSubcommand with native assets $buildMode', () async {
|
|
await inTempDir((Directory tempDirectory) async {
|
|
final Directory packageDirectory = await createTestProject(packageName, tempDirectory);
|
|
final Directory exampleDirectory = packageDirectory.childDirectory('example');
|
|
|
|
final ProcessResult result = processManager.runSync(
|
|
<String>[
|
|
flutterBin,
|
|
'build',
|
|
buildSubcommand,
|
|
'--$buildMode',
|
|
if (buildSubcommand == 'ios') '--no-codesign',
|
|
],
|
|
workingDirectory: exampleDirectory.path,
|
|
);
|
|
if (result.exitCode != 0) {
|
|
throw Exception('flutter build failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}');
|
|
}
|
|
|
|
switch (buildSubcommand) {
|
|
case 'macos':
|
|
expectDylibIsBundledMacOS(exampleDirectory, buildMode);
|
|
expectDylibIsCodeSignedMacOS(exampleDirectory, buildMode);
|
|
case 'ios':
|
|
expectDylibIsBundledIos(exampleDirectory, buildMode);
|
|
case 'linux':
|
|
expectDylibIsBundledLinux(exampleDirectory, buildMode);
|
|
case 'windows':
|
|
expectDylibIsBundledWindows(exampleDirectory, buildMode);
|
|
case 'apk':
|
|
expectDylibIsBundledAndroid(exampleDirectory, buildMode);
|
|
}
|
|
expectCCompilerIsConfigured(exampleDirectory);
|
|
});
|
|
});
|
|
}
|
|
|
|
testWithoutContext('flutter build $buildSubcommand succeeds without libraries', () async {
|
|
await inTempDir((Directory tempDirectory) async {
|
|
final Directory projectDirectory = await createTestProjectWithNoCBuild(packageName, tempDirectory);
|
|
|
|
final ProcessResult result = processManager.runSync(
|
|
<String>[
|
|
flutterBin,
|
|
'build',
|
|
buildSubcommand,
|
|
'--debug',
|
|
if (buildSubcommand == 'ios') '--no-codesign',
|
|
],
|
|
workingDirectory: projectDirectory.path,
|
|
);
|
|
if (result.exitCode != 0) {
|
|
throw Exception('flutter build failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}');
|
|
}
|
|
});
|
|
});
|
|
|
|
// This could be an hermetic unit test if the native_assets_builder
|
|
// could mock process runs and file system.
|
|
// https://github.com/dart-lang/native/issues/90.
|
|
testWithoutContext('flutter build $buildSubcommand error on static libraries', () async {
|
|
await inTempDir((Directory tempDirectory) async {
|
|
final Directory packageDirectory = await createTestProject(packageName, tempDirectory);
|
|
final File buildDotDart =
|
|
packageDirectory.childDirectory('hook').childFile('build.dart');
|
|
final String buildDotDartContents = await buildDotDart.readAsString();
|
|
// Overrides the build to output static libraries.
|
|
final String buildDotDartContentsNew = buildDotDartContents.replaceFirst(
|
|
'await build(args, (config, output) async {',
|
|
'''
|
|
await build([
|
|
'-D${LinkModePreferenceImpl.configKey}=${LinkModePreferenceImpl.static}',
|
|
...args,
|
|
], (config, output) async {
|
|
''',
|
|
);
|
|
expect(buildDotDartContentsNew, isNot(buildDotDartContents));
|
|
await buildDotDart.writeAsString(buildDotDartContentsNew);
|
|
final Directory exampleDirectory = packageDirectory.childDirectory('example');
|
|
|
|
final ProcessResult result = processManager.runSync(
|
|
<String>[
|
|
flutterBin,
|
|
'build',
|
|
buildSubcommand,
|
|
if (buildSubcommand == 'ios') '--no-codesign',
|
|
if (buildSubcommand == 'windows') '-v' // Requires verbose mode for error.
|
|
],
|
|
workingDirectory: exampleDirectory.path,
|
|
);
|
|
expect(
|
|
(result.stdout as String) + (result.stderr as String),
|
|
contains('link mode set to static, but this is not yet supported'),
|
|
);
|
|
expect(result.exitCode, isNot(0));
|
|
});
|
|
});
|
|
}
|
|
|
|
for (final String add2appBuildSubcommand in add2appBuildSubcommands) {
|
|
testWithoutContext('flutter build $add2appBuildSubcommand with native assets', () async {
|
|
await inTempDir((Directory tempDirectory) async {
|
|
final Directory packageDirectory = await createTestProject(packageName, tempDirectory);
|
|
final Directory exampleDirectory = packageDirectory.childDirectory('example');
|
|
|
|
final ProcessResult result = processManager.runSync(
|
|
<String>[
|
|
flutterBin,
|
|
'build',
|
|
add2appBuildSubcommand,
|
|
],
|
|
workingDirectory: exampleDirectory.path,
|
|
);
|
|
if (result.exitCode != 0) {
|
|
throw Exception('flutter build failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}');
|
|
}
|
|
|
|
for (final String buildMode in buildModes) {
|
|
expectDylibIsBundledWithFrameworks(exampleDirectory, buildMode, add2appBuildSubcommand.replaceAll('-framework', ''));
|
|
}
|
|
expectCCompilerIsConfigured(exampleDirectory);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
void expectDylibIsCodeSignedMacOS(Directory appDirectory, String buildMode) {
|
|
final Directory appBundle = appDirectory.childDirectory('build/$hostOs/Build/Products/${buildMode.upperCaseFirst()}/$exampleAppName.app');
|
|
final Directory frameworksFolder = appBundle.childDirectory('Contents/Frameworks');
|
|
expect(frameworksFolder, exists);
|
|
const String frameworkName = packageName;
|
|
final Directory frameworkDir = frameworksFolder.childDirectory('$frameworkName.framework');
|
|
final ProcessResult codesign =
|
|
processManager.runSync(<String>['codesign', '-dv', frameworkDir.absolute.path]);
|
|
expect(codesign.exitCode, 0);
|
|
|
|
// Expect adhoc signature, but not linker-signed (which would mean no code-signing happened after linking).
|
|
final List<String> lines = codesign.stderr.toString().split('\n');
|
|
final bool isLinkerSigned = lines.any((String line) => line.contains('linker-signed'));
|
|
final bool isAdhoc = lines.any((String line) => line.contains('Signature=adhoc'));
|
|
expect(isAdhoc, isTrue);
|
|
expect(isLinkerSigned, isFalse);
|
|
}
|
|
|
|
/// For `flutter build` we can't easily test whether running the app works.
|
|
/// Check that we have the dylibs in the app.
|
|
void expectDylibIsBundledMacOS(Directory appDirectory, String buildMode) {
|
|
final Directory appBundle = appDirectory.childDirectory('build/$hostOs/Build/Products/${buildMode.upperCaseFirst()}/$exampleAppName.app');
|
|
expect(appBundle, exists);
|
|
final Directory frameworksFolder =
|
|
appBundle.childDirectory('Contents/Frameworks');
|
|
expect(frameworksFolder, exists);
|
|
|
|
// MyFramework.framework/
|
|
// MyFramework -> Versions/Current/MyFramework
|
|
// Resources -> Versions/Current/Resources
|
|
// Versions/
|
|
// A/
|
|
// MyFramework
|
|
// Resources/
|
|
// Info.plist
|
|
// Current -> A
|
|
const String frameworkName = packageName;
|
|
final Directory frameworkDir =
|
|
frameworksFolder.childDirectory('$frameworkName.framework');
|
|
final Directory versionsDir = frameworkDir.childDirectory('Versions');
|
|
final Directory versionADir = versionsDir.childDirectory('A');
|
|
final Directory resourcesDir = versionADir.childDirectory('Resources');
|
|
expect(resourcesDir, exists);
|
|
final File dylibFile = versionADir.childFile(frameworkName);
|
|
expect(dylibFile, exists);
|
|
final Link currentLink = versionsDir.childLink('Current');
|
|
expect(currentLink, exists);
|
|
expect(currentLink.resolveSymbolicLinksSync(), versionADir.path);
|
|
final Link resourcesLink = frameworkDir.childLink('Resources');
|
|
expect(resourcesLink, exists);
|
|
expect(resourcesLink.resolveSymbolicLinksSync(), resourcesDir.path);
|
|
final Link dylibLink = frameworkDir.childLink(frameworkName);
|
|
expect(dylibLink, exists);
|
|
expect(dylibLink.resolveSymbolicLinksSync(), dylibFile.path);
|
|
final String infoPlist = resourcesDir.childFile('Info.plist').readAsStringSync();
|
|
expect(infoPlist, '''
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CFBundleDevelopmentRegion</key>
|
|
<string>en</string>
|
|
<key>CFBundleExecutable</key>
|
|
<string>package_with_native_assets</string>
|
|
<key>CFBundleIdentifier</key>
|
|
<string>io.flutter.flutter.native-assets.package-with-native-assets</string>
|
|
<key>CFBundleInfoDictionaryVersion</key>
|
|
<string>6.0</string>
|
|
<key>CFBundleName</key>
|
|
<string>package_with_native_assets</string>
|
|
<key>CFBundlePackageType</key>
|
|
<string>FMWK</string>
|
|
<key>CFBundleShortVersionString</key>
|
|
<string>1.0</string>
|
|
<key>CFBundleSignature</key>
|
|
<string>????</string>
|
|
<key>CFBundleVersion</key>
|
|
<string>1.0</string>
|
|
</dict>
|
|
</plist>''');
|
|
}
|
|
|
|
void expectDylibIsBundledIos(Directory appDirectory, String buildMode) {
|
|
final Directory appBundle = appDirectory.childDirectory('build/ios/${buildMode.upperCaseFirst()}-iphoneos/Runner.app');
|
|
expect(appBundle, exists);
|
|
final Directory frameworksFolder = appBundle.childDirectory('Frameworks');
|
|
expect(frameworksFolder, exists);
|
|
const String frameworkName = packageName;
|
|
final File dylib = frameworksFolder
|
|
.childDirectory('$frameworkName.framework')
|
|
.childFile(frameworkName);
|
|
expect(dylib, exists);
|
|
final String infoPlist = frameworksFolder
|
|
.childDirectory('$frameworkName.framework')
|
|
.childFile('Info.plist').readAsStringSync();
|
|
expect(infoPlist, '''
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CFBundleDevelopmentRegion</key>
|
|
<string>en</string>
|
|
<key>CFBundleExecutable</key>
|
|
<string>package_with_native_assets</string>
|
|
<key>CFBundleIdentifier</key>
|
|
<string>io.flutter.flutter.native-assets.package-with-native-assets</string>
|
|
<key>CFBundleInfoDictionaryVersion</key>
|
|
<string>6.0</string>
|
|
<key>CFBundleName</key>
|
|
<string>package_with_native_assets</string>
|
|
<key>CFBundlePackageType</key>
|
|
<string>FMWK</string>
|
|
<key>CFBundleShortVersionString</key>
|
|
<string>1.0</string>
|
|
<key>CFBundleSignature</key>
|
|
<string>????</string>
|
|
<key>CFBundleVersion</key>
|
|
<string>1.0</string>
|
|
<key>MinimumOSVersion</key>
|
|
<string>12.0</string>
|
|
</dict>
|
|
</plist>''');
|
|
}
|
|
|
|
/// Checks that dylibs are bundled.
|
|
///
|
|
/// Sample path: build/linux/x64/release/bundle/lib/libmy_package.so
|
|
void expectDylibIsBundledLinux(Directory appDirectory, String buildMode) {
|
|
// Linux does not support cross compilation, so always only check current architecture.
|
|
final String architecture = ArchitectureImpl.current.dartPlatform;
|
|
final Directory appBundle = appDirectory
|
|
.childDirectory('build')
|
|
.childDirectory(hostOs)
|
|
.childDirectory(architecture)
|
|
.childDirectory(buildMode)
|
|
.childDirectory('bundle');
|
|
expect(appBundle, exists);
|
|
final Directory dylibsFolder = appBundle.childDirectory('lib');
|
|
expect(dylibsFolder, exists);
|
|
final File dylib =
|
|
dylibsFolder.childFile(OSImpl.linux.dylibFileName(packageName));
|
|
expect(dylib, exists);
|
|
}
|
|
|
|
/// Checks that dylibs are bundled.
|
|
///
|
|
/// Sample path: build\windows\x64\runner\Debug\my_package_example.exe
|
|
void expectDylibIsBundledWindows(Directory appDirectory, String buildMode) {
|
|
// Linux does not support cross compilation, so always only check current architecture.
|
|
final String architecture = ArchitectureImpl.current.dartPlatform;
|
|
final Directory appBundle = appDirectory
|
|
.childDirectory('build')
|
|
.childDirectory(hostOs)
|
|
.childDirectory(architecture)
|
|
.childDirectory('runner')
|
|
.childDirectory(buildMode.upperCaseFirst());
|
|
expect(appBundle, exists);
|
|
final File dylib =
|
|
appBundle.childFile(OSImpl.windows.dylibFileName(packageName));
|
|
expect(dylib, exists);
|
|
}
|
|
|
|
void expectDylibIsBundledAndroid(Directory appDirectory, String buildMode) {
|
|
final File apk = appDirectory
|
|
.childDirectory('build')
|
|
.childDirectory('app')
|
|
.childDirectory('outputs')
|
|
.childDirectory('flutter-apk')
|
|
.childFile('app-$buildMode.apk');
|
|
expect(apk, exists);
|
|
final OperatingSystemUtils osUtils = OperatingSystemUtils(
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
platform: platform,
|
|
processManager: processManager,
|
|
);
|
|
final Directory apkUnzipped = appDirectory.childDirectory('apk-unzipped');
|
|
apkUnzipped.createSync();
|
|
osUtils.unzip(apk, apkUnzipped);
|
|
final Directory lib = apkUnzipped.childDirectory('lib');
|
|
for (final String arch in <String>['arm64-v8a', 'armeabi-v7a', 'x86_64']) {
|
|
final Directory archDir = lib.childDirectory(arch);
|
|
expect(archDir, exists);
|
|
// The dylibs should be next to the flutter and app so.
|
|
expect(archDir.childFile('libflutter.so'), exists);
|
|
if (buildMode != 'debug') {
|
|
expect(archDir.childFile('libapp.so'), exists);
|
|
}
|
|
final File dylib =
|
|
archDir.childFile(OSImpl.android.dylibFileName(packageName));
|
|
expect(dylib, exists);
|
|
}
|
|
}
|
|
|
|
/// For `flutter build` we can't easily test whether running the app works.
|
|
/// Check that we have the dylibs in the app.
|
|
void expectDylibIsBundledWithFrameworks(Directory appDirectory, String buildMode, String os) {
|
|
final Directory frameworksFolder = appDirectory.childDirectory('build/$os/framework/${buildMode.upperCaseFirst()}');
|
|
expect(frameworksFolder, exists);
|
|
const String frameworkName = packageName;
|
|
final File dylib = frameworksFolder
|
|
.childDirectory('$frameworkName.framework')
|
|
.childFile(frameworkName);
|
|
expect(dylib, exists);
|
|
}
|
|
|
|
/// Check that the native assets are built with the C Compiler that Flutter uses.
|
|
///
|
|
/// This inspects the build configuration to see if the C compiler was configured.
|
|
void expectCCompilerIsConfigured(Directory appDirectory) {
|
|
final Directory nativeAssetsBuilderDir = appDirectory.childDirectory('.dart_tool/native_assets_builder/');
|
|
for (final Directory subDir in nativeAssetsBuilderDir.listSync().whereType<Directory>()) {
|
|
final File config = subDir.childFile('config.json');
|
|
expect(config, exists);
|
|
final String contents = config.readAsStringSync();
|
|
// Dry run does not pass compiler info.
|
|
if (contents.contains('"dry_run": true')) {
|
|
continue;
|
|
}
|
|
expect(contents, contains('"cc": '));
|
|
}
|
|
}
|
|
|
|
extension on String {
|
|
String upperCaseFirst() {
|
|
return replaceFirst(this[0], this[0].toUpperCase());
|
|
}
|
|
}
|
|
|
|
Future<Directory> createTestProject(String packageName, Directory tempDirectory) async {
|
|
final ProcessResult result = processManager.runSync(
|
|
<String>[
|
|
flutterBin,
|
|
'create',
|
|
'--no-pub',
|
|
'--template=package_ffi',
|
|
packageName,
|
|
],
|
|
workingDirectory: tempDirectory.path,
|
|
);
|
|
if (result.exitCode != 0) {
|
|
throw Exception(
|
|
'flutter create failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}',
|
|
);
|
|
}
|
|
|
|
final Directory packageDirectory = tempDirectory.childDirectory(packageName);
|
|
|
|
// No platform-specific boilerplate files.
|
|
expect(packageDirectory.childDirectory('android/'), isNot(exists));
|
|
expect(packageDirectory.childDirectory('ios/'), isNot(exists));
|
|
expect(packageDirectory.childDirectory('linux/'), isNot(exists));
|
|
expect(packageDirectory.childDirectory('macos/'), isNot(exists));
|
|
expect(packageDirectory.childDirectory('windows/'), isNot(exists));
|
|
|
|
await pinDependencies(packageDirectory.childFile('pubspec.yaml'));
|
|
await pinDependencies(
|
|
packageDirectory.childDirectory('example').childFile('pubspec.yaml'));
|
|
|
|
await addLinkHookDepedendency(packageDirectory);
|
|
|
|
final ProcessResult result2 = await processManager.run(
|
|
<String>[
|
|
flutterBin,
|
|
'pub',
|
|
'get',
|
|
],
|
|
workingDirectory: packageDirectory.path,
|
|
);
|
|
expect(result2, const ProcessResultMatcher());
|
|
|
|
return packageDirectory;
|
|
}
|
|
|
|
Future<Directory> createTestProjectWithNoCBuild(String packageName, Directory tempDirectory) async {
|
|
final ProcessResult result = processManager.runSync(
|
|
<String>[
|
|
flutterBin,
|
|
'create',
|
|
'--no-pub',
|
|
packageName,
|
|
],
|
|
workingDirectory: tempDirectory.path,
|
|
);
|
|
if (result.exitCode != 0) {
|
|
throw Exception(
|
|
'flutter create failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}',
|
|
);
|
|
}
|
|
|
|
final Directory packageDirectory = tempDirectory.childDirectory(packageName);
|
|
|
|
final ProcessResult result2 = await processManager.run(
|
|
<String>[
|
|
flutterBin,
|
|
'pub',
|
|
'add',
|
|
'native_assets_cli',
|
|
],
|
|
workingDirectory: packageDirectory.path,
|
|
);
|
|
expect(result2, const ProcessResultMatcher());
|
|
|
|
await pinDependencies(packageDirectory.childFile('pubspec.yaml'));
|
|
|
|
final ProcessResult result3 = await processManager.run(
|
|
<String>[
|
|
flutterBin,
|
|
'pub',
|
|
'get',
|
|
],
|
|
workingDirectory: packageDirectory.path,
|
|
);
|
|
expect(result3, const ProcessResultMatcher());
|
|
|
|
// Add build hook that does nothing to the package.
|
|
final File buildHook = packageDirectory.childDirectory('hook').childFile('build.dart');
|
|
buildHook.createSync(recursive: true);
|
|
buildHook.writeAsStringSync('''
|
|
import 'package:native_assets_cli/native_assets_cli.dart';
|
|
|
|
void main(List<String> args) async {
|
|
await build(args, (config, output) async {});
|
|
}
|
|
''');
|
|
|
|
return packageDirectory;
|
|
}
|
|
|
|
Future<void> addLinkHookDepedendency(Directory packageDirectory) async {
|
|
final Directory flutterDirectory = fileSystem.currentDirectory.parent.parent;
|
|
final Directory linkHookDirectory = flutterDirectory
|
|
.childDirectory('dev')
|
|
.childDirectory('integration_tests')
|
|
.childDirectory('link_hook');
|
|
expect(linkHookDirectory, exists);
|
|
|
|
final File pubspecFile = packageDirectory.childFile('pubspec.yaml');
|
|
final String pubspecOld =
|
|
(await pubspecFile.readAsString()).replaceAll('\r\n', '\n');
|
|
final String pubspecNew = pubspecOld.replaceFirst('''
|
|
dependencies:
|
|
''', '''
|
|
dependencies:
|
|
link_hook:
|
|
path: ${linkHookDirectory.path}
|
|
''');
|
|
expect(pubspecNew, isNot(pubspecOld));
|
|
await pubspecFile.writeAsString(pubspecNew);
|
|
|
|
final File dartFile =
|
|
packageDirectory.childDirectory('lib').childFile('$packageName.dart');
|
|
final String dartFileOld =
|
|
(await dartFile.readAsString()).replaceAll('\r\n', '\n');
|
|
// Replace with something that results in the same resulting int, so that the
|
|
// tests don't have to be updated.
|
|
final String dartFileNew = dartFileOld.replaceFirst(
|
|
'''
|
|
import '${packageName}_bindings_generated.dart' as bindings;
|
|
''',
|
|
'''
|
|
import 'package:link_hook/link_hook.dart' as l;
|
|
|
|
import '${packageName}_bindings_generated.dart' as bindings;
|
|
''',
|
|
);
|
|
expect(dartFileNew, isNot(dartFileOld));
|
|
final String dartFileNew2 = dartFileNew.replaceFirst(
|
|
'int sum(int a, int b) => bindings.sum(a, b);',
|
|
'int sum(int a, int b) => bindings.sum(a, b) + l.difference(2, 1) - 1;',
|
|
);
|
|
expect(dartFileNew2, isNot(dartFileNew));
|
|
await dartFile.writeAsString(dartFileNew2);
|
|
}
|
|
|
|
Future<void> pinDependencies(File pubspecFile) async {
|
|
expect(pubspecFile, exists);
|
|
final String oldPubspec = await pubspecFile.readAsString();
|
|
final String newPubspec = oldPubspec.replaceAll(RegExp(r':\s*\^'), ': ');
|
|
expect(newPubspec, isNot(oldPubspec));
|
|
await pubspecFile.writeAsString(newPubspec);
|
|
}
|
|
|
|
Future<void> inTempDir(Future<void> Function(Directory tempDirectory) fun) async {
|
|
final Directory tempDirectory = fileSystem.directory(fileSystem.systemTempDirectory.createTempSync().resolveSymbolicLinksSync());
|
|
try {
|
|
await fun(tempDirectory);
|
|
} finally {
|
|
tryToDelete(tempDirectory);
|
|
}
|
|
}
|