blob: 77aa2f5ca2bf8a7254cc7f300467abaa3bce2d71 [file] [log] [blame] [edit]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. 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:async';
import 'dart:io';
import 'package:code_assets/code_assets.dart';
import 'package:data_assets/data_assets.dart';
import 'package:file/local.dart';
import 'package:hooks_runner/hooks_runner.dart';
import 'package:logging/logging.dart';
import 'package:test/test.dart';
import '../helpers.dart';
Future<void> runPubGet({
required Uri workingDirectory,
required Logger logger,
}) async {
final result = await runProcess(
executable: Uri.file(Platform.resolvedExecutable),
arguments: [
'pub',
'--suppress-analytics', // Prevent extra log entries.
'get',
],
workingDirectory: workingDirectory,
logger: logger,
);
expect(result.exitCode, 0);
}
Future<Result<BuildResult, HooksRunnerFailure>> buildDataAssets(
Uri packageUri, {
String? runPackageName,
List<String>? capturedLogs,
bool linkingEnabled = false,
}) => build(
packageUri,
logger,
dartExecutable,
capturedLogs: capturedLogs,
buildAssetTypes: [BuildAssetType.data],
runPackageName: runPackageName,
linkingEnabled: linkingEnabled,
);
Future<Result<BuildResult, HooksRunnerFailure>> buildCodeAssets(
Uri packageUri, {
String? runPackageName,
List<String>? capturedLogs,
}) => build(
packageUri,
logger,
dartExecutable,
capturedLogs: capturedLogs,
buildAssetTypes: [BuildAssetType.code],
runPackageName: runPackageName,
);
enum BuildAssetType { code, data }
Future<Result<BuildResult, HooksRunnerFailure>> build(
Uri packageUri,
Logger logger,
Uri dartExecutable, {
LinkModePreference linkModePreference = LinkModePreference.dynamic,
CCompilerConfig? cCompiler,
List<String>? capturedLogs,
String? runPackageName,
IOSSdk? targetIOSSdk,
int? targetIOSVersion,
int? targetMacOSVersion,
int? targetAndroidNdkApi,
Target? target,
bool linkingEnabled = false,
required List<BuildAssetType> buildAssetTypes,
Map<String, String>? hookEnvironment,
UserDefines? userDefines,
}) async {
final targetOS = target?.os ?? OS.current;
final runPackageName_ =
runPackageName ?? packageUri.pathSegments.lastWhere((e) => e.isNotEmpty);
final packageLayout = await PackageLayout.fromWorkingDirectory(
const LocalFileSystem(),
packageUri,
runPackageName_,
includeDevDependencies: false,
);
return await runWithLog(capturedLogs, () async {
final result =
await NativeAssetsBuildRunner(
logger: logger,
dartExecutable: dartExecutable,
fileSystem: const LocalFileSystem(),
hookEnvironment: hookEnvironment,
packageLayout: packageLayout,
userDefines: userDefines,
).build(
extensions: [
if (buildAssetTypes.contains(BuildAssetType.code))
CodeAssetExtension(
targetArchitecture:
target?.architecture ?? Architecture.current,
targetOS: targetOS,
linkModePreference: linkModePreference,
cCompiler: cCompiler ?? dartCICompilerConfig,
iOS: targetOS == OS.iOS
? IOSCodeConfig(
targetSdk: targetIOSSdk!,
targetVersion: targetIOSVersion!,
)
: null,
macOS: targetOS == OS.macOS
? MacOSCodeConfig(
targetVersion:
targetMacOSVersion ?? defaultMacOSVersion,
)
: null,
android: targetOS == OS.android
? AndroidCodeConfig(targetNdkApi: targetAndroidNdkApi!)
: null,
),
if (buildAssetTypes.contains(BuildAssetType.data))
DataAssetsExtension(),
],
linkingEnabled: linkingEnabled,
);
if (result.isFailure) return result;
final buildResult = result.success;
expect(await buildResult.encodedAssets.allExist(), true);
for (final encodedAssetsForLinking
in buildResult.encodedAssetsForLinking.values) {
expect(await encodedAssetsForLinking.allExist(), true);
}
return result;
});
}
Future<Result<LinkResult, HooksRunnerFailure>> link(
Uri packageUri,
Logger logger,
Uri dartExecutable, {
LinkModePreference linkModePreference = LinkModePreference.dynamic,
CCompilerConfig? cCompiler,
List<String>? capturedLogs,
String? runPackageName,
required BuildResult buildResult,
Uri? resourceIdentifiers,
IOSSdk? targetIOSSdk,
int? targetIOSVersion,
int? targetMacOSVersion,
int? targetAndroidNdkApi,
Target? target,
required List<BuildAssetType> buildAssetTypes,
}) async {
final targetOS = target?.os ?? OS.current;
final runPackageName_ =
runPackageName ?? packageUri.pathSegments.lastWhere((e) => e.isNotEmpty);
final packageLayout = await PackageLayout.fromWorkingDirectory(
const LocalFileSystem(),
packageUri,
runPackageName_,
includeDevDependencies: false,
);
return await runWithLog(capturedLogs, () async {
final result =
await NativeAssetsBuildRunner(
logger: logger,
dartExecutable: dartExecutable,
fileSystem: const LocalFileSystem(),
packageLayout: packageLayout,
).link(
extensions: [
if (buildAssetTypes.contains(BuildAssetType.code))
CodeAssetExtension(
targetArchitecture:
target?.architecture ?? Architecture.current,
targetOS: target?.os ?? OS.current,
linkModePreference: linkModePreference,
cCompiler: cCompiler ?? dartCICompilerConfig,
iOS: targetOS == OS.iOS
? IOSCodeConfig(
targetSdk: targetIOSSdk!,
targetVersion: targetIOSVersion!,
)
: null,
macOS: targetOS == OS.macOS
? MacOSCodeConfig(
targetVersion:
targetMacOSVersion ?? defaultMacOSVersion,
)
: null,
android: targetOS == OS.android
? AndroidCodeConfig(targetNdkApi: targetAndroidNdkApi!)
: null,
),
if (buildAssetTypes.contains(BuildAssetType.data))
DataAssetsExtension(),
],
buildResult: buildResult,
resourceIdentifiers: resourceIdentifiers,
);
if (result.isFailure) return result;
expect(await result.success.encodedAssets.allExist(), true);
return result;
});
}
Future<(BuildResult?, LinkResult?)> buildAndLink(
Uri packageUri,
Logger logger,
Uri dartExecutable, {
LinkModePreference linkModePreference = LinkModePreference.dynamic,
CCompilerConfig? cCompiler,
List<String>? capturedLogs,
PackageLayout? packageLayout,
String? runPackageName,
IOSSdk? targetIOSSdk,
int? targetIOSVersion,
int? targetMacOSVersion,
int? targetAndroidNdkApi,
Target? target,
Uri? resourceIdentifiers,
required List<BuildAssetType> buildAssetTypes,
}) async => await runWithLog(capturedLogs, () async {
final runPackageName_ =
runPackageName ?? packageUri.pathSegments.lastWhere((e) => e.isNotEmpty);
final packageLayout = await PackageLayout.fromWorkingDirectory(
const LocalFileSystem(),
packageUri,
runPackageName_,
includeDevDependencies: false,
);
final buildRunner = NativeAssetsBuildRunner(
logger: logger,
dartExecutable: dartExecutable,
fileSystem: const LocalFileSystem(),
packageLayout: packageLayout,
);
final targetOS = target?.os ?? OS.current;
final buildResult = await buildRunner.build(
extensions: [
if (buildAssetTypes.contains(BuildAssetType.code))
CodeAssetExtension(
targetArchitecture: target?.architecture ?? Architecture.current,
targetOS: target?.os ?? OS.current,
linkModePreference: linkModePreference,
cCompiler: cCompiler ?? dartCICompilerConfig,
iOS: targetOS == OS.iOS
? IOSCodeConfig(
targetSdk: targetIOSSdk!,
targetVersion: targetIOSVersion!,
)
: null,
macOS: targetOS == OS.macOS
? MacOSCodeConfig(
targetVersion: targetMacOSVersion ?? defaultMacOSVersion,
)
: null,
android: targetOS == OS.android
? AndroidCodeConfig(targetNdkApi: targetAndroidNdkApi!)
: null,
),
if (buildAssetTypes.contains(BuildAssetType.data)) DataAssetsExtension(),
],
linkingEnabled: true,
);
if (buildResult.isFailure) return (null, null);
expect(await buildResult.success.encodedAssets.allExist(), true);
for (final encodedAssetsForLinking
in buildResult.success.encodedAssetsForLinking.values) {
expect(await encodedAssetsForLinking.allExist(), true);
}
final linkResult = await buildRunner.link(
extensions: [
if (buildAssetTypes.contains(BuildAssetType.code))
CodeAssetExtension(
targetArchitecture: target?.architecture ?? Architecture.current,
targetOS: target?.os ?? OS.current,
linkModePreference: linkModePreference,
cCompiler: cCompiler ?? dartCICompilerConfig,
iOS: targetOS == OS.iOS
? IOSCodeConfig(
targetSdk: targetIOSSdk!,
targetVersion: targetIOSVersion!,
)
: null,
macOS: targetOS == OS.macOS
? MacOSCodeConfig(
targetVersion: targetMacOSVersion ?? defaultMacOSVersion,
)
: null,
android: targetOS == OS.android
? AndroidCodeConfig(targetNdkApi: targetAndroidNdkApi!)
: null,
),
if (buildAssetTypes.contains(BuildAssetType.data)) DataAssetsExtension(),
],
buildResult: buildResult.success,
resourceIdentifiers: resourceIdentifiers,
);
if (linkResult.isFailure) return (buildResult.success, null);
expect(await linkResult.success.encodedAssets.allExist(), true);
return (buildResult.success, linkResult.success);
});
Future<T> runWithLog<T>(
List<String>? capturedLogs,
Future<T> Function() f,
) async {
StreamSubscription<LogRecord>? subscription;
if (capturedLogs != null) {
subscription = logger.onRecord.listen(
(event) => capturedLogs.add(event.message),
);
}
final result = await f();
if (subscription != null) {
await subscription.cancel();
}
return result;
}
Future<void> expectSymbols({
required CodeAsset asset,
required List<String> symbols,
}) async {
if (Platform.isLinux) {
final assetUri = asset.file!;
final nmResult = await runProcess(
executable: Uri(path: 'nm'),
arguments: ['-D', assetUri.toFilePath()],
logger: logger,
);
expect(nmResult.stdout, stringContainsInOrder(symbols));
}
}
final CCompilerConfig? dartCICompilerConfig = (() {
// Specifically for running our tests on Dart CI with the test runner, we
// recognize specific variables to setup the C Compiler configuration.
final env = Platform.environment;
final cc = env['DART_HOOK_TESTING_C_COMPILER__CC'];
final ar = env['DART_HOOK_TESTING_C_COMPILER__AR'];
final ld = env['DART_HOOK_TESTING_C_COMPILER__LD'];
final envScript = env['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT'];
final envScriptArgs =
env['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT_ARGUMENTS']
?.split(' ')
.map((arg) => arg.trim())
.where((arg) => arg.isNotEmpty)
.toList();
if (cc != null && ar != null && ld != null) {
return CCompilerConfig(
archiver: Uri.file(ar),
compiler: Uri.file(cc),
linker: Uri.file(ld),
windows: WindowsCCompilerConfig(
developerCommandPrompt: envScript == null
? null
: DeveloperCommandPrompt(
script: Uri.file(envScript),
arguments: envScriptArgs ?? [],
),
),
);
}
return null;
})();
int defaultMacOSVersion = 13;