Commit 1ada132e authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Add first replay test (#8628)

parent 5571cb11
...@@ -6,6 +6,7 @@ import 'package:path/path.dart' as p; ...@@ -6,6 +6,7 @@ import 'package:path/path.dart' as p;
String flutterRoot = p.dirname(p.dirname(p.dirname(p.fromUri(Platform.script)))); String flutterRoot = p.dirname(p.dirname(p.dirname(p.fromUri(Platform.script))));
String flutter = p.join(flutterRoot, 'bin', Platform.isWindows ? 'flutter.bat' : 'flutter'); String flutter = p.join(flutterRoot, 'bin', Platform.isWindows ? 'flutter.bat' : 'flutter');
String dart = p.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'dart.exe' : 'dart'); String dart = p.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'dart.exe' : 'dart');
String pub = p.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'pub.bat' : 'pub');
String flutterTestArgs = Platform.environment['FLUTTER_TEST_ARGS']; String flutterTestArgs = Platform.environment['FLUTTER_TEST_ARGS'];
/// When you call this, you can set FLUTTER_TEST_ARGS to pass custom /// When you call this, you can set FLUTTER_TEST_ARGS to pass custom
...@@ -81,6 +82,9 @@ Future<Null> main() async { ...@@ -81,6 +82,9 @@ Future<Null> main() async {
await _runAllDartTests(p.join(flutterRoot, 'packages', 'flutter_tools'), await _runAllDartTests(p.join(flutterRoot, 'packages', 'flutter_tools'),
environment: <String, String>{ 'FLUTTER_ROOT': flutterRoot }, environment: <String, String>{ 'FLUTTER_ROOT': flutterRoot },
); );
await _pubRunTest(p.join(flutterRoot, 'packages', 'flutter_tools'),
testPath: 'test/replay',
);
await _runAllDartTests(p.join(flutterRoot, 'dev', 'devicelab')); await _runAllDartTests(p.join(flutterRoot, 'dev', 'devicelab'));
await _runFlutterTest(p.join(flutterRoot, 'dev', 'manual_tests')); await _runFlutterTest(p.join(flutterRoot, 'dev', 'manual_tests'));
...@@ -93,6 +97,16 @@ Future<Null> main() async { ...@@ -93,6 +97,16 @@ Future<Null> main() async {
} }
} }
Future<Null> _pubRunTest(
String workingDirectory, {
String testPath,
}) {
final List<String> args = <String>['run', 'test'];
if (testPath != null)
args.add(testPath);
return _runCmd(pub, args, workingDirectory: workingDirectory);
}
Future<Null> _runCmd(String executable, List<String> arguments, { Future<Null> _runCmd(String executable, List<String> arguments, {
String workingDirectory, String workingDirectory,
Map<String, String> environment, Map<String, String> environment,
......
...@@ -18,7 +18,6 @@ import 'src/base/context.dart'; ...@@ -18,7 +18,6 @@ import 'src/base/context.dart';
import 'src/base/file_system.dart'; import 'src/base/file_system.dart';
import 'src/base/io.dart'; import 'src/base/io.dart';
import 'src/base/logger.dart'; import 'src/base/logger.dart';
import 'src/base/os.dart';
import 'src/base/platform.dart'; import 'src/base/platform.dart';
import 'src/base/process.dart'; import 'src/base/process.dart';
import 'src/base/utils.dart'; import 'src/base/utils.dart';
...@@ -49,7 +48,6 @@ import 'src/devfs.dart'; ...@@ -49,7 +48,6 @@ import 'src/devfs.dart';
import 'src/device.dart'; import 'src/device.dart';
import 'src/doctor.dart'; import 'src/doctor.dart';
import 'src/globals.dart'; import 'src/globals.dart';
import 'src/ios/mac.dart';
import 'src/ios/simulators.dart'; import 'src/ios/simulators.dart';
import 'src/run_hot.dart'; import 'src/run_hot.dart';
import 'src/runner/flutter_command.dart'; import 'src/runner/flutter_command.dart';
...@@ -131,11 +129,8 @@ Future<int> run(List<String> args, List<FlutterCommand> subCommands, { ...@@ -131,11 +129,8 @@ Future<int> run(List<String> args, List<FlutterCommand> subCommands, {
context.putIfAbsent(HotRunnerConfig, () => new HotRunnerConfig()); context.putIfAbsent(HotRunnerConfig, () => new HotRunnerConfig());
context.putIfAbsent(Cache, () => new Cache()); context.putIfAbsent(Cache, () => new Cache());
context.putIfAbsent(Artifacts, () => new CachedArtifacts()); context.putIfAbsent(Artifacts, () => new CachedArtifacts());
context.putIfAbsent(OperatingSystemUtils, () => new OperatingSystemUtils());
context.putIfAbsent(Xcode, () => new Xcode());
context.putIfAbsent(IOSSimulatorUtils, () => new IOSSimulatorUtils()); context.putIfAbsent(IOSSimulatorUtils, () => new IOSSimulatorUtils());
context.putIfAbsent(SimControl, () => new SimControl()); context.putIfAbsent(SimControl, () => new SimControl());
context.putIfAbsent(Usage, () => new Usage());
// Initialize the system locale. // Initialize the system locale.
await intl.findSystemLocale(); await intl.findSystemLocale();
...@@ -192,14 +187,14 @@ Future<int> _handleToolError( ...@@ -192,14 +187,14 @@ Future<int> _handleToolError(
// We've crashed; emit a log report. // We've crashed; emit a log report.
stderr.writeln(); stderr.writeln();
flutterUsage.sendException(error, chain);
if (!reportCrashes) { if (!reportCrashes) {
// Print the stack trace on the bots - don't write a crash report. // Print the stack trace on the bots - don't write a crash report.
stderr.writeln('$error'); stderr.writeln('$error');
stderr.writeln(chain.terse.toString()); stderr.writeln(chain.terse.toString());
return _exit(1); return _exit(1);
} else { } else {
flutterUsage.sendException(error, chain);
if (error is String) if (error is String)
stderr.writeln('Oops; flutter has exited unexpectedly: "$error".'); stderr.writeln('Oops; flutter has exited unexpectedly: "$error".');
else else
...@@ -208,7 +203,7 @@ Future<int> _handleToolError( ...@@ -208,7 +203,7 @@ Future<int> _handleToolError(
await CrashReportSender.instance.sendReport( await CrashReportSender.instance.sendReport(
error: error, error: error,
stackTrace: chain, stackTrace: chain,
flutterVersion: getFlutterVersion(), getFlutterVersion: getFlutterVersion,
); );
try { try {
final File file = await _createLocalCrashReport(args, error, chain); final File file = await _createLocalCrashReport(args, error, chain);
......
...@@ -14,7 +14,7 @@ import 'process.dart'; ...@@ -14,7 +14,7 @@ import 'process.dart';
import 'process_manager.dart'; import 'process_manager.dart';
/// Returns [OperatingSystemUtils] active in the current app context (i.e. zone). /// Returns [OperatingSystemUtils] active in the current app context (i.e. zone).
OperatingSystemUtils get os => context[OperatingSystemUtils]; OperatingSystemUtils get os => context.putIfAbsent(OperatingSystemUtils, () => new OperatingSystemUtils());
abstract class OperatingSystemUtils { abstract class OperatingSystemUtils {
factory OperatingSystemUtils() { factory OperatingSystemUtils() {
......
...@@ -72,7 +72,7 @@ class CrashReportSender { ...@@ -72,7 +72,7 @@ class CrashReportSender {
Future<Null> sendReport({ Future<Null> sendReport({
@required dynamic error, @required dynamic error,
@required dynamic stackTrace, @required dynamic stackTrace,
@required String flutterVersion, @required String getFlutterVersion(),
}) async { }) async {
try { try {
// TODO(yjbanov): we only actually send crash reports in tests. When we // TODO(yjbanov): we only actually send crash reports in tests. When we
...@@ -86,6 +86,7 @@ class CrashReportSender { ...@@ -86,6 +86,7 @@ class CrashReportSender {
printStatus('Sending crash report to Google.'); printStatus('Sending crash report to Google.');
final String flutterVersion = getFlutterVersion();
final Uri uri = _baseUri.replace( final Uri uri = _baseUri.replace(
queryParameters: <String, String>{ queryParameters: <String, String>{
'product': _kProductId, 'product': _kProductId,
......
...@@ -59,7 +59,7 @@ class Xcode { ...@@ -59,7 +59,7 @@ class Xcode {
} }
/// Returns [Xcode] active in the current app context. /// Returns [Xcode] active in the current app context.
static Xcode get instance => context[Xcode]; static Xcode get instance => context.putIfAbsent(Xcode, () => new Xcode());
bool get isInstalledAndMeetsVersionCheck => isInstalled && xcodeVersionSatisfactory; bool get isInstalledAndMeetsVersionCheck => isInstalled && xcodeVersionSatisfactory;
......
...@@ -38,7 +38,7 @@ class Usage { ...@@ -38,7 +38,7 @@ class Usage {
} }
/// Returns [Usage] active in the current app context. /// Returns [Usage] active in the current app context.
static Usage get instance => context[Usage]; static Usage get instance => context.putIfAbsent(Usage, () => new Usage());
Analytics _analytics; Analytics _analytics;
......
...@@ -4,8 +4,11 @@ ...@@ -4,8 +4,11 @@
import 'dart:async'; import 'dart:async';
import 'package:file/local.dart';
import 'package:flutter_tools/executable.dart' as tools; import 'package:flutter_tools/executable.dart' as tools;
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/base/io.dart' as io;
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../src/common.dart'; import '../src/common.dart';
...@@ -24,11 +27,11 @@ void testReplay( ...@@ -24,11 +27,11 @@ void testReplay(
bool skip, bool skip,
}) { }) {
setUp(() { setUp(() {
setExitFunctionForTests(); io.setExitFunctionForTests();
}); });
tearDown(() { tearDown(() {
restoreExitFunction(); io.restoreExitFunction();
}); });
testUsingContext( testUsingContext(
...@@ -55,7 +58,27 @@ void testReplay( ...@@ -55,7 +58,27 @@ void testReplay(
/// '--no-resident', /// '--no-resident',
/// ] /// ]
/// ``` /// ```
void expectProcessExits(List<String> command, {dynamic exitCode: 0}) { void expectProcessExits(
final Future<Null> mainFuture = tools.main(command); FlutterCommand command, {
expect(mainFuture, throwsProcessExit(exitCode)); List<String> args: const <String>[],
dynamic exitCode: 0,
}) {
final Future<Null> runFuture = tools.run(
<String>[command.name]..addAll(args),
<FlutterCommand>[command],
reportCrashes: false,
flutterVersion: 'test',
);
expect(runFuture, throwsProcessExit(exitCode));
}
/// The base path of the replay tests.
String get replayBase {
return const LocalFileSystem().path.joinAll(<String>[
Cache.flutterRoot,
'packages',
'flutter_tools',
'test',
'replay',
]);
} }
https://storage.googleapis.com/flutter_infra/flutter/fonts/13ac995daa9dda0a6ba0a45f1fccc541e616a74c/fonts.zip
https://storage.googleapis.com/flutter_infra/flutter/fonts/13ac995daa9dda0a6ba0a45f1fccc541e616a74c/fonts.zip
\ No newline at end of file
f9c1f5fa539fdfcbcae65a1dd09d1d05949da459
\ No newline at end of file
f9c1f5fa539fdfcbcae65a1dd09d1d05949da459
\ No newline at end of file
{
"numberOfProcessors": 24,
"pathSeparator": "/",
"operatingSystem": "macos",
"localHostname": "flutter-macpro.flutter.io",
"environment": {
"TERM_PROGRAM": "Apple_Terminal",
"SHELL": "/bin/bash",
"TERM": "xterm-256color",
"CLICOLOR": "1",
"TMPDIR": "/var/folders/n5/klvyv2fj5lj2lhn_61080t0m003g3j/T/",
"Apple_PubSub_Socket_Render": "/private/tmp/com.apple.launchd.U3lCTUziZt/Render",
"TERM_PROGRAM_VERSION": "388",
"TERM_SESSION_ID": "33E2EBBF-8ED6-4E02-BBE3-2A71E5E8675D",
"USER": "flutter",
"FLUTTER_ROOT": "/Users/flutter/project/flutter/flutter",
"SSH_AUTH_SOCK": "/private/tmp/com.apple.launchd.pOrPDczlgR/Listeners",
"__CF_USER_TEXT_ENCODING": "0x1BC71:0x0:0x0",
"LSCOLORS": "ExGxFxdaCxDADAadhbheFx",
"PATH": "/Users/flutter/homebrew/bin:/Users/flutter/project/flutter/flutter/bin:/Users/flutter/project/flutter/engine/src/third_party/android_tools/sdk/platform-tools:/Users/flutter/project/flutter/flutter/bin/cache/dart-sdk/bin:/Users/flutter/project/depot_tools:/Users/flutter/bin:/usr/local/git/current/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/X11/bin",
"PWD": "/Users/flutter/project/flutter/flutter/examples/hello_world",
"LANG": "en_US.UTF-8",
"XPC_FLAGS": "0x0",
"XPC_SERVICE_NAME": "0",
"SHLVL": "2",
"HOME": "/Users/flutter",
"LOGNAME": "flutter",
"_": "/Users/flutter/project/flutter/flutter/bin/cache/dart-sdk/bin/dart"
},
"executable": "/Users/flutter/project/flutter/flutter/bin/cache/dart-sdk/bin/dart",
"resolvedExecutable": "/Users/flutter/project/flutter/flutter/bin/cache/dart-sdk/bin/dart",
"script": "file:///Users/flutter/project/flutter/flutter/bin/cache/flutter_tools.snapshot",
"executableArguments": [],
"packageRoot": null,
"packageConfig": null,
"version": "1.23.0-dev.2.0 (Tue Feb 28 07:18:56 2017) on \"macos_x64\""
}
Usage: idevice_id [OPTIONS] [UDID]
Prints device name or a list of attached devices.
The UDID is a 40-digit hexadecimal number of the device
for which the name should be retrieved.
-l, --list list UDID of all attached devices
-d, --debug enable communication debugging
-h, --help prints usage information
Homepage: <http://libimobiledevice.org>
{
"devices" : {
"iOS 10.1" : [
{
"state" : "Booted",
"availability" : "(available)",
"name" : "iPhone 6s",
"udid" : "AA8FFDB0-593A-44E4-A927-8E49A138BDA5"
}
]
}
}
Mar 8 11:59:38 flutter-macpro hello_flutter[60434]: Diagnostic server listening on http://127.0.0.1:64939
Mar 8 11:59:38 flutter-macpro hello_flutter[60434]: Observatory listening on http://127.0.0.1:8100/
[
{
"type": "run",
"body": {
"pid": 60403,
"basename": "000.git.60403",
"command": [
"git",
"rev-parse",
"HEAD"
],
"workingDirectory": "/Users/flutter/project/flutter/flutter",
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60404,
"basename": "001.git.60404",
"command": [
"git",
"rev-parse",
"--abbrev-ref",
"HEAD"
],
"workingDirectory": "/Users/flutter/project/flutter/flutter",
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60405,
"basename": "002.defaults.60405",
"command": [
"/usr/bin/defaults",
"read",
"/Applications/Android Studio.app/Contents/Info",
"CFBundleShortVersionString"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "can_run",
"body": {
"executable": "/Applications/Android Studio.app/Contents/gradle/gradle-2.14.1/bin/gradle",
"result": true
}
},
{
"type": "can_run",
"body": {
"executable": "/Users/flutter/Library/Android/sdk/platform-tools/adb",
"result": true
}
},
{
"type": "run",
"body": {
"pid": 60406,
"basename": "005.xcode-select.60406",
"command": [
"xcode-select",
"--print-path"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60407,
"basename": "006.xcodebuild.60407",
"command": [
"xcodebuild",
"-version"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60408,
"basename": "007.clang.60408",
"command": [
"/usr/bin/xcrun",
"clang"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 1
}
},
{
"type": "run",
"body": {
"pid": 60409,
"basename": "008.adb.60409",
"command": [
"/Users/flutter/Library/Android/sdk/platform-tools/adb",
"devices",
"-l"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60410,
"basename": "009.idevice_id.60410",
"command": [
"idevice_id",
"-h"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60411,
"basename": "010.which.60411",
"command": [
"which",
"idevice_id"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60412,
"basename": "011.idevice_id.60412",
"command": [
"/Users/flutter/homebrew/bin/idevice_id",
"-l"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60413,
"basename": "012.simctl.60413",
"command": [
"/usr/bin/xcrun",
"simctl",
"list",
"--json",
"devices"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60414,
"basename": "013.unzip.60414",
"command": [
"unzip",
"-o",
"-q",
"hello_flutter.ipa",
"-d",
"/var/folders/n5/klvyv2fj5lj2lhn_61080t0m003g3j/T/flutter_app_N0zaZx"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60423,
"basename": "014.defaults.60423",
"command": [
"/usr/bin/defaults",
"read",
"/var/folders/n5/klvyv2fj5lj2lhn_61080t0m003g3j/T/flutter_app_N0zaZx/Payload/hello_flutter.app/Info",
"CFBundleIdentifier"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60424,
"basename": "015.simctl.60424",
"command": [
"/usr/bin/xcrun",
"simctl",
"install",
"AA8FFDB0-593A-44E4-A927-8E49A138BDA5",
"/var/folders/n5/klvyv2fj5lj2lhn_61080t0m003g3j/T/flutter_app_N0zaZx/Payload/hello_flutter.app"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60432,
"basename": "016.tail.60432",
"command": [
"tail",
"-n",
"0",
"-F",
"/Users/flutter/Library/Logs/CoreSimulator/AA8FFDB0-593A-44E4-A927-8E49A138BDA5/system.log"
],
"includeParentEnvironment": true,
"runInShell": false,
"mode": "ProcessStartMode.NORMAL",
"daemon": false,
"notResponding": false,
"exitCode": -15
}
},
{
"type": "run",
"body": {
"pid": 60433,
"basename": "017.simctl.60433",
"command": [
"/usr/bin/xcrun",
"simctl",
"launch",
"AA8FFDB0-593A-44E4-A927-8E49A138BDA5",
"io.flutter.HelloFlutter",
"--enable-dart-profiling",
"--enable-checked-mode",
"--observatory-port=8100",
"--diagnostic-port=8101"
],
"includeParentEnvironment": true,
"runInShell": false,
"stdoutEncoding": "system",
"stderrEncoding": "system",
"daemon": false,
"notResponding": false,
"exitCode": 0
}
},
{
"type": "run",
"body": {
"pid": 60435,
"basename": "018.tail.60435",
"command": [
"tail",
"-n",
"0",
"-F",
"/private/var/log/system.log"
],
"includeParentEnvironment": true,
"runInShell": false,
"mode": "ProcessStartMode.NORMAL",
"daemon": false,
"notResponding": false,
"exitCode": -15
}
}
]
// Copyright 2015 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 'package:flutter_tools/src/commands/run.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import '../common.dart';
void main() {
testReplay('runsWithoutError', () async {
final FileSystem fs = const LocalFileSystem();
final String replay = fs.path.join(replayBase, 'osx', 'simulator_application_binary');
expectProcessExits(
new RunCommand(),
args: <String>[
'--no-hot',
'--no-resident',
'--device-id=iPhone',
'--use-application-binary=hello_flutter.ipa',
'--replay-from=$replay',
],
);
});
}
...@@ -108,26 +108,27 @@ void _printBufferedErrors(AppContext testContext) { ...@@ -108,26 +108,27 @@ void _printBufferedErrors(AppContext testContext) {
String getFlutterRoot() { String getFlutterRoot() {
Error invalidScript() => new StateError('Invalid script: ${platform.script}'); Error invalidScript() => new StateError('Invalid script: ${platform.script}');
String toolsPath; Uri scriptUri;
switch (platform.script.scheme) { switch (platform.script.scheme) {
case 'file': case 'file':
final List<String> parts = fs.path.split(fs.path.fromUri(platform.script)); scriptUri = platform.script;
final int toolsIndex = parts.indexOf('flutter_tools');
if (toolsIndex == -1)
throw invalidScript();
toolsPath = fs.path.joinAll(parts.sublist(0, toolsIndex + 1));
break; break;
case 'data': case 'data':
final RegExp flutterTools = new RegExp(r'(file://[^%]*[/\\]flutter_tools)'); final RegExp flutterTools = new RegExp(r'(file://[^%]*[/\\]flutter_tools[^%]+\.dart)%');
final Match match = flutterTools.firstMatch(platform.script.path); final Match match = flutterTools.firstMatch(platform.script.path);
if (match == null) if (match == null)
throw invalidScript(); throw invalidScript();
toolsPath = Uri.parse(match.group(1)).path; scriptUri = Uri.parse(match.group(1));
break; break;
default: default:
throw invalidScript(); throw invalidScript();
} }
final List<String> parts = fs.path.split(fs.path.fromUri(scriptUri));
final int toolsIndex = parts.indexOf('flutter_tools');
if (toolsIndex == -1)
throw invalidScript();
final String toolsPath = fs.path.joinAll(parts.sublist(0, toolsIndex + 1));
return fs.path.normalize(fs.path.join(toolsPath, '..', '..')); return fs.path.normalize(fs.path.join(toolsPath, '..', '..'));
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment