Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
e8222aaf
Commit
e8222aaf
authored
Jan 17, 2020
by
Zachary Anderson
Committed by
Flutter GitHub Bot
Jan 17, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flutter_tool] Don't crash on Android emulator startup failure (#48995)
parent
033c23f5
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
168 additions
and
28 deletions
+168
-28
android_emulator.dart
packages/flutter_tools/lib/src/android/android_emulator.dart
+55
-10
emulators.dart
packages/flutter_tools/lib/src/commands/emulators.dart
+1
-10
android_emulator_test.dart
...ols/test/general.shard/android/android_emulator_test.dart
+112
-8
No files found.
packages/flutter_tools/lib/src/android/android_emulator.dart
View file @
e8222aaf
...
...
@@ -8,8 +8,12 @@ import 'package:meta/meta.dart';
import
'../android/android_sdk.dart'
;
import
'../android/android_workflow.dart'
;
import
'../base/common.dart'
;
import
'../base/file_system.dart'
;
import
'../base/io.dart'
;
import
'../base/process.dart'
;
import
'../base/utils.dart'
;
import
'../convert.dart'
;
import
'../device.dart'
;
import
'../emulator.dart'
;
import
'../globals.dart'
as
globals
;
...
...
@@ -50,18 +54,59 @@ class AndroidEmulator extends Emulator {
@override
Future
<
void
>
launch
()
async
{
final
Future
<
void
>
launchResult
=
processUtils
.
run
(
<
String
>[
getEmulatorPath
(),
'-avd'
,
id
],
throwOnError:
true
,
final
Process
process
=
await
processUtils
.
start
(
<
String
>[
getEmulatorPath
(
androidSdk
),
'-avd'
,
id
],
);
// The emulator continues running on a successful launch, so if it hasn't
// quit within 3 seconds we assume that's a success and just return. This
// means that on a slow machine, a failure that takes more than three
// seconds won't be recognized as such... :-/
return
Future
.
any
<
void
>(<
Future
<
void
>>[
launchResult
,
Future
<
void
>.
delayed
(
const
Duration
(
seconds:
3
)),
// Record output from the emulator process.
final
List
<
String
>
stdoutList
=
<
String
>[];
final
List
<
String
>
stderrList
=
<
String
>[];
final
StreamSubscription
<
String
>
stdoutSubscription
=
process
.
stdout
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
(
stdoutList
.
add
);
final
StreamSubscription
<
String
>
stderrSubscription
=
process
.
stderr
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
(
stderrList
.
add
);
final
Future
<
void
>
stdioFuture
=
waitGroup
<
void
>(<
Future
<
void
>>[
stdoutSubscription
.
asFuture
<
void
>(),
stderrSubscription
.
asFuture
<
void
>(),
]);
// The emulator continues running on success, so we don't wait for the
// process to complete before continuing. However, if the process fails
// after the startup phase (3 seconds), then we only echo its output if
// its error code is non-zero and its stderr is non-empty.
bool
earlyFailure
=
true
;
unawaited
(
process
.
exitCode
.
then
((
int
status
)
async
{
if
(
status
==
0
)
{
globals
.
printTrace
(
'The Android emulator exited successfully'
);
return
;
}
// Make sure the process' stdout and stderr are drained.
await
stdioFuture
;
unawaited
(
stdoutSubscription
.
cancel
());
unawaited
(
stderrSubscription
.
cancel
());
if
(
stdoutList
.
isNotEmpty
)
{
globals
.
printTrace
(
'Android emulator stdout:'
);
stdoutList
.
forEach
(
globals
.
printTrace
);
}
if
(!
earlyFailure
&&
stderrList
.
isEmpty
)
{
globals
.
printStatus
(
'The Android emulator exited with code
$status
'
);
return
;
}
final
String
when
=
earlyFailure
?
'during startup'
:
'after startup'
;
globals
.
printError
(
'The Android emulator exited with code
$status
$when
'
);
globals
.
printError
(
'Android emulator stderr:'
);
stderrList
.
forEach
(
globals
.
printError
);
globals
.
printError
(
'Address these issues and try again.'
);
}));
// Wait a few seconds for the emulator to start.
await
Future
<
void
>.
delayed
(
const
Duration
(
seconds:
3
));
earlyFailure
=
false
;
return
;
}
}
...
...
packages/flutter_tools/lib/src/commands/emulators.dart
View file @
e8222aaf
...
...
@@ -69,16 +69,7 @@ class EmulatorsCommand extends FlutterCommand {
"More than one emulator matches '
$id
':"
,
);
}
else
{
try
{
await
emulators
.
first
.
launch
();
}
catch
(
e
)
{
if
(
e
is
String
)
{
globals
.
printError
(
e
);
}
else
{
rethrow
;
}
}
await
emulators
.
first
.
launch
();
}
}
...
...
packages/flutter_tools/test/general.shard/android/android_emulator_test.dart
View file @
e8222aaf
...
...
@@ -2,11 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'package:file/file.dart'
;
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/android/android_sdk.dart'
show
getEmulatorPath
,
AndroidSdk
,
androidSdk
;
import
'package:flutter_tools/src/android/android_emulator.dart'
;
import
'package:flutter_tools/src/base/common.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'../../src/common.dart'
;
import
'../../src/context.dart'
;
import
'../../src/fake_process_manager.dart'
;
import
'../../src/mocks.dart'
show
MockAndroidSdk
;
void
main
(
)
{
group
(
'android_emulator'
,
()
{
...
...
@@ -18,8 +29,10 @@ void main() {
});
testUsingContext
(
'flags emulators with config'
,
()
{
const
String
emulatorID
=
'1234'
;
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
<
String
,
String
>{
'name'
:
'test'
});
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
<
String
,
String
>{
'name'
:
'test'
},
);
expect
(
emulator
.
id
,
emulatorID
);
expect
(
emulator
.
hasConfig
,
true
);
});
...
...
@@ -31,8 +44,7 @@ void main() {
'hw.device.manufacturer'
:
manufacturer
,
'avd.ini.displayname'
:
displayName
,
};
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties
);
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties
);
expect
(
emulator
.
id
,
emulatorID
);
expect
(
emulator
.
name
,
displayName
);
expect
(
emulator
.
manufacturer
,
manufacturer
);
...
...
@@ -45,8 +57,7 @@ void main() {
final
Map
<
String
,
String
>
properties
=
<
String
,
String
>{
'avd.ini.displayname'
:
displayName
,
};
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties
);
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties
);
expect
(
emulator
.
name
,
displayName
);
});
testUsingContext
(
'uses cleaned up ID if no displayname is set'
,
()
{
...
...
@@ -56,8 +67,7 @@ void main() {
final
Map
<
String
,
String
>
properties
=
<
String
,
String
>{
'avd.ini.notadisplayname'
:
'this is not a display name'
,
};
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties
);
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties
);
expect
(
emulator
.
name
,
'This is my ID'
);
});
testUsingContext
(
'parses ini files'
,
()
{
...
...
@@ -74,4 +84,98 @@ void main() {
expect
(
results
[
'avd.ini.displayname'
],
'dispName'
);
});
});
group
(
'Android emulator launch '
,
()
{
const
String
emulatorID
=
'i1234'
;
const
String
errorText
=
'[Android emulator test error]'
;
MockAndroidSdk
mockSdk
;
FakeProcessManager
successProcessManager
;
FakeProcessManager
errorProcessManager
;
FakeProcessManager
lateFailureProcessManager
;
MemoryFileSystem
fs
;
setUp
(()
{
fs
=
MemoryFileSystem
();
mockSdk
=
MockAndroidSdk
();
when
(
mockSdk
.
emulatorPath
).
thenReturn
(
'emulator'
);
const
List
<
String
>
command
=
<
String
>[
'emulator'
,
'-avd'
,
emulatorID
,
];
successProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
command
),
]);
errorProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
command
,
exitCode:
1
,
stderr:
errorText
,
stdout:
'dummy text'
,
duration:
Duration
(
seconds:
1
),
),
]);
lateFailureProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
command
,
exitCode:
1
,
stderr:
''
,
stdout:
'dummy text'
,
duration:
Duration
(
seconds:
4
),
),
]);
});
testUsingContext
(
'succeeds'
,
()
async
{
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
);
expect
(
getEmulatorPath
(
androidSdk
),
mockSdk
.
emulatorPath
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
FakeAsync
().
run
((
FakeAsync
time
)
{
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
time
.
elapse
(
const
Duration
(
seconds:
5
));
time
.
flushMicrotasks
();
});
await
completer
.
future
;
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
successProcessManager
,
AndroidSdk:
()
=>
mockSdk
,
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'prints error on failure'
,
()
async
{
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
FakeAsync
().
run
((
FakeAsync
time
)
{
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
time
.
elapse
(
const
Duration
(
seconds:
5
));
time
.
flushMicrotasks
();
});
await
completer
.
future
;
expect
(
testLogger
.
errorText
,
contains
(
errorText
));
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
errorProcessManager
,
AndroidSdk:
()
=>
mockSdk
,
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'prints nothing on late failure with empty stderr'
,
()
async
{
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
FakeAsync
().
run
((
FakeAsync
time
)
async
{
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
time
.
elapse
(
const
Duration
(
seconds:
5
));
time
.
flushMicrotasks
();
});
await
completer
.
future
;
expect
(
testLogger
.
errorText
,
isEmpty
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
lateFailureProcessManager
,
AndroidSdk:
()
=>
mockSdk
,
FileSystem:
()
=>
fs
,
});
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment