blob: 642a6251c9a90f18c88dfa4ff7c8a60269234012 [file] [log] [blame]
// Copyright 2017 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 'dart:async';
import 'dart:convert';
import 'dart:io' as dart_io;
import 'package:file/file.dart';
import 'package:file/local.dart' as io;
import 'package:path/path.dart' as path;
import 'utils.dart';
const String _kProvisioningConfigFileEnvironmentVariable = 'FLUTTER_DEVICELAB_XCODE_PROVISIONING_CONFIG';
const String _kTestXcconfigFileName = 'TestConfig.xcconfig';
const FileSystem _fs = io.LocalFileSystem();
/// Patches the given Xcode project adding provisioning certificates and team
/// information required to build and run the project, if
/// FLUTTER_DEVICELAB_XCODE_PROVISIONING_CONFIG is set. If it is not set,
/// we rely on automatic signing by Xcode.
Future<void> prepareProvisioningCertificates(String flutterProjectPath) async {
final String certificateConfig = await _readProvisioningConfigFile();
if (certificateConfig == null) {
// No cert config available, rely on automatic signing by Xcode.
return;
}
await _patchXcconfigFilesIfNotPatched(flutterProjectPath);
final File testXcconfig = _fs.file(path.join(flutterProjectPath, 'ios/Flutter/$_kTestXcconfigFileName'));
await testXcconfig.writeAsString(certificateConfig);
}
Future<void> runPodInstallForCustomPodfile(String flutterProjectPath) async {
final String iosPath = path.join(flutterProjectPath, 'ios');
exec('pod', <String>['install', '--project-directory=$iosPath']);
}
Future<void> _patchXcconfigFilesIfNotPatched(String flutterProjectPath) async {
final List<File> xcconfigFiles = <File>[
_fs.file(path.join(flutterProjectPath, 'ios/Flutter/Flutter.xcconfig')),
_fs.file(path.join(flutterProjectPath, 'ios/Flutter/Debug.xcconfig')),
_fs.file(path.join(flutterProjectPath, 'ios/Flutter/Release.xcconfig')),
];
bool xcconfigFileExists = false;
for (final File file in xcconfigFiles) {
if (await file.exists()) {
xcconfigFileExists = true;
const String include = '#include "$_kTestXcconfigFileName"';
final String contents = await file.readAsString();
final bool alreadyPatched = contents.contains(include);
if (!alreadyPatched) {
final IOSink patchOut = file.openWrite(mode: FileMode.append);
patchOut.writeln(); // in case EOF is not preceded by line break
patchOut.writeln(include);
await patchOut.close();
}
}
}
if (!xcconfigFileExists) {
final String candidatesFormatted = xcconfigFiles.map<String>((File f) => f.path).join(', ');
throw 'Failed to locate a xcconfig file to patch with provisioning profile '
'info. Tried: $candidatesFormatted';
}
}
Future<String> _readProvisioningConfigFile() async {
void throwUsageError(String specificMessage) {
throw '''
================================================================================
You are attempting to build an XCode project, which requires a provisioning
certificate and team information. The test framework attempted to locate an
.xcconfig file whose path is defined by the environment variable
$_kProvisioningConfigFileEnvironmentVariable.
$specificMessage
================================================================================
'''.trim();
}
if (!dart_io.Platform.environment.containsKey(_kProvisioningConfigFileEnvironmentVariable)) {
print('''
$_kProvisioningConfigFileEnvironmentVariable variable is not defined in your
environment. Relying on automatic signing by Xcode...
'''.trim());
return null;
}
final String filePath = dart_io.Platform.environment[_kProvisioningConfigFileEnvironmentVariable];
final File file = _fs.file(filePath);
if (!(await file.exists())) {
throwUsageError('''
File not found: $filePath
It is defined by environment variable $_kProvisioningConfigFileEnvironmentVariable
'''.trim());
}
return file.readAsString();
}
void _checkExitCode(int code) {
if (code != 0) {
throw Exception(
'Unexpected exit code = $code!',
);
}
}
Future<void> _execAndCheck(String executable, List<String> args) async {
_checkExitCode(await exec(executable, args));
}
// Measure the CPU/GPU percentage for [duration] while a Flutter app is running
// on an iOS device (e.g., right after a Flutter driver test has finished, which
// doesn't close the Flutter app, and the Flutter app has an indefinite
// animation). The return should have a format like the following json
// ```
// {"gpu_percentage":12.6,"cpu_percentage":18.15}
// ```
Future<Map<String, dynamic>> measureIosCpuGpu({
Duration duration = const Duration(seconds: 10),
String deviceId,
}) async {
await _execAndCheck('pub', <String>[
'global',
'activate',
'gauge',
'0.1.4',
]);
await _execAndCheck('pub', <String>[
'global',
'run',
'gauge',
'ioscpugpu',
'new',
if (deviceId != null) ...<String>['-w', deviceId],
'-l',
'${duration.inMilliseconds}',
]);
return json.decode(file('$cwd/result.json').readAsStringSync());
}
Future<String> dylibSymbols(String pathToDylib) {
return eval('nm', <String>['-g', pathToDylib]);
}