mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This reverts commit b1d5f03351590a0954d19c7584186f0722323169. Turns out this isn't needed for https://github.com/flutter/flutter/issues/166489 after all. It appeared that SwiftPM wasn't copying the dSYM, but I now think I had my build modes mixed up (since dSYMs are only relevant to release mode). ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
206 lines
6.8 KiB
Dart
206 lines
6.8 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.
|
|
|
|
import 'package:file/file.dart';
|
|
import 'package:file/local.dart';
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
import 'package:process/process.dart';
|
|
import 'package:vm_service/vm_service.dart';
|
|
|
|
import '../src/common.dart';
|
|
import 'test_driver.dart';
|
|
|
|
/// The [FileSystem] for the integration test environment.
|
|
const FileSystem fileSystem = LocalFileSystem();
|
|
|
|
/// The (real) `flutter` binary (i.e. `{ROOT}/bin/flutter`) to execute in tests.
|
|
final String flutterBin = fileSystem.path.join(
|
|
getFlutterRoot(),
|
|
'bin',
|
|
platform.isWindows ? 'flutter.bat' : 'flutter',
|
|
);
|
|
|
|
/// The [Platform] for the integration test environment.
|
|
const Platform platform = LocalPlatform();
|
|
|
|
/// The [ProcessManager] for the integration test environment.
|
|
const ProcessManager processManager = LocalProcessManager();
|
|
|
|
/// Creates a temporary directory but resolves any symlinks to return the real
|
|
/// underlying path to avoid issues with breakpoints/hot reload.
|
|
/// https://github.com/flutter/flutter/pull/21741
|
|
Directory createResolvedTempDirectorySync(String prefix) {
|
|
assert(prefix.endsWith('.'));
|
|
final Directory tempDirectory = fileSystem.systemTempDirectory.createTempSync('flutter_$prefix');
|
|
return fileSystem.directory(tempDirectory.resolveSymbolicLinksSync());
|
|
}
|
|
|
|
void writeFile(String path, String content, {bool writeFutureModifiedDate = false}) {
|
|
final File file = fileSystem.file(path)
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync(content, flush: true);
|
|
// Some integration tests on Windows to not see this file as being modified
|
|
// recently enough for the hot reload to pick this change up unless the
|
|
// modified time is written in the future.
|
|
if (writeFutureModifiedDate) {
|
|
file.setLastModifiedSync(DateTime.now().add(const Duration(seconds: 5)));
|
|
}
|
|
}
|
|
|
|
void writeBytesFile(String path, List<int> content) {
|
|
fileSystem.file(path)
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(content, flush: true);
|
|
}
|
|
|
|
Future<void> getPackages(String folder) async {
|
|
final command = <String>[fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'), 'pub', 'get'];
|
|
final ProcessResult result = await processManager.run(command, workingDirectory: folder);
|
|
if (result.exitCode != 0) {
|
|
throw Exception('flutter pub get failed: ${result.stderr}\n${result.stdout}');
|
|
}
|
|
}
|
|
|
|
const kLocalEngineEnvironment = 'FLUTTER_LOCAL_ENGINE';
|
|
const kLocalEngineHostEnvironment = 'FLUTTER_LOCAL_ENGINE_HOST';
|
|
const kLocalEngineLocation = 'FLUTTER_LOCAL_ENGINE_SRC_PATH';
|
|
|
|
List<String> getLocalEngineArguments() {
|
|
return <String>[
|
|
if (platform.environment.containsKey(kLocalEngineEnvironment))
|
|
'--local-engine=${platform.environment[kLocalEngineEnvironment]}',
|
|
if (platform.environment.containsKey(kLocalEngineLocation))
|
|
'--local-engine-src-path=${platform.environment[kLocalEngineLocation]}',
|
|
if (platform.environment.containsKey(kLocalEngineHostEnvironment))
|
|
'--local-engine-host=${platform.environment[kLocalEngineHostEnvironment]}',
|
|
];
|
|
}
|
|
|
|
Future<void> pollForServiceExtensionValue<T>({
|
|
required FlutterTestDriver testDriver,
|
|
required String extension,
|
|
required T continuePollingValue,
|
|
required Matcher matches,
|
|
String valueKey = 'value',
|
|
}) async {
|
|
for (var i = 0; i < 10; i++) {
|
|
final Response response = await testDriver.callServiceExtension(extension);
|
|
if (response.json?[valueKey] as T == continuePollingValue) {
|
|
await Future<void>.delayed(const Duration(seconds: 1));
|
|
} else {
|
|
expect(response.json?[valueKey] as T, matches);
|
|
return;
|
|
}
|
|
}
|
|
fail(
|
|
"Did not find expected value for service extension '$extension'. All call"
|
|
" attempts responded with '$continuePollingValue'.",
|
|
);
|
|
}
|
|
|
|
abstract final class AppleTestUtils {
|
|
static const requiredSymbols = <String>[
|
|
'_kDartIsolateSnapshotData',
|
|
'_kDartIsolateSnapshotInstructions',
|
|
'_kDartVmSnapshotData',
|
|
'_kDartVmSnapshotInstructions',
|
|
];
|
|
|
|
static List<String> getExportedSymbols(String dwarfPath) {
|
|
final ProcessResult nm = processManager.runSync(<String>[
|
|
'nm',
|
|
'--debug-syms', // nm docs: 'Show all symbols, even debugger only'
|
|
'--defined-only',
|
|
'--just-symbol-name',
|
|
dwarfPath,
|
|
'-arch',
|
|
'arm64',
|
|
]);
|
|
final String nmOutput = (nm.stdout as String).trim();
|
|
return nmOutput.isEmpty ? const <String>[] : nmOutput.split('\n');
|
|
}
|
|
}
|
|
|
|
/// Matcher to be used for [ProcessResult] returned
|
|
/// from a process run
|
|
///
|
|
/// The default for [exitCode] will be 0 while
|
|
/// [stdoutPattern] and [stderrPattern] are both optional
|
|
class ProcessResultMatcher extends Matcher {
|
|
const ProcessResultMatcher({this.exitCode = 0, this.stdoutPattern, this.stderrPattern});
|
|
|
|
/// The expected exit code to get returned from a process run
|
|
final int exitCode;
|
|
|
|
/// Substring to find in the process's stdout
|
|
final Pattern? stdoutPattern;
|
|
|
|
/// Substring to find in the process's stderr
|
|
final Pattern? stderrPattern;
|
|
|
|
@override
|
|
Description describe(Description description) {
|
|
description.add('a process with exit code $exitCode');
|
|
if (stdoutPattern != null) {
|
|
description.add(' and stdout: "$stdoutPattern"');
|
|
}
|
|
if (stderrPattern != null) {
|
|
description.add(' and stderr: "$stderrPattern"');
|
|
}
|
|
|
|
return description;
|
|
}
|
|
|
|
@override
|
|
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
|
|
final result = item as ProcessResult;
|
|
var foundStdout = true;
|
|
var foundStderr = true;
|
|
|
|
final stdout = result.stdout as String;
|
|
final stderr = result.stderr as String;
|
|
if (stdoutPattern != null) {
|
|
foundStdout = stdout.contains(stdoutPattern!);
|
|
matchState['stdout'] = stdout;
|
|
} else if (stdout.isNotEmpty) {
|
|
// even if we were not asserting on stdout, show stdout for debug purposes
|
|
matchState['stdout'] = stdout;
|
|
}
|
|
|
|
if (stderrPattern != null) {
|
|
foundStderr = stderr.contains(stderrPattern!);
|
|
matchState['stderr'] = stderr;
|
|
} else if (stderr.isNotEmpty) {
|
|
matchState['stderr'] = stderr;
|
|
}
|
|
|
|
return result.exitCode == exitCode && foundStdout && foundStderr;
|
|
}
|
|
|
|
@override
|
|
Description describeMismatch(
|
|
Object? item,
|
|
Description mismatchDescription,
|
|
Map<dynamic, dynamic> matchState,
|
|
bool verbose,
|
|
) {
|
|
final result = item! as ProcessResult;
|
|
|
|
if (result.exitCode != exitCode) {
|
|
mismatchDescription.add('Actual exitCode was ${result.exitCode}\n');
|
|
}
|
|
|
|
if (matchState.containsKey('stdout')) {
|
|
mismatchDescription.add('Actual stdout:\n${matchState["stdout"]}\n');
|
|
}
|
|
|
|
if (matchState.containsKey('stderr')) {
|
|
mismatchDescription.add('Actual stderr:\n${matchState["stderr"]}\n');
|
|
}
|
|
|
|
return mismatchDescription;
|
|
}
|
|
}
|