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
f640ad69
Unverified
Commit
f640ad69
authored
May 27, 2020
by
Jonah Williams
Committed by
GitHub
May 27, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flutter_tools] ensure emulator command does not crash with missing avdmanager (#57703)
parent
ffc56ff7
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
533 additions
and
306 deletions
+533
-306
android_emulator.dart
packages/flutter_tools/lib/src/android/android_emulator.dart
+110
-61
android_workflow.dart
packages/flutter_tools/lib/src/android/android_workflow.dart
+9
-3
daemon.dart
packages/flutter_tools/lib/src/commands/daemon.dart
+8
-1
emulators.dart
packages/flutter_tools/lib/src/commands/emulators.dart
+1
-1
context_runner.dart
packages/flutter_tools/lib/src/context_runner.dart
+10
-2
emulator.dart
packages/flutter_tools/lib/src/emulator.dart
+61
-33
ios_emulators.dart
packages/flutter_tools/lib/src/ios/ios_emulators.dart
+3
-0
android_device_discovery_test.dart
.../general.shard/android/android_device_discovery_test.dart
+9
-3
android_emulator_test.dart
...ols/test/general.shard/android/android_emulator_test.dart
+100
-70
emulator_test.dart
packages/flutter_tools/test/general.shard/emulator_test.dart
+222
-132
No files found.
packages/flutter_tools/lib/src/android/android_emulator.dart
View file @
f640ad69
...
@@ -5,36 +5,131 @@
...
@@ -5,36 +5,131 @@
import
'dart:async'
;
import
'dart:async'
;
import
'package:meta/meta.dart'
;
import
'package:meta/meta.dart'
;
import
'package:process/process.dart'
;
import
'../android/android_sdk.dart'
;
import
'../android/android_sdk.dart'
;
import
'../android/android_workflow.dart'
;
import
'../android/android_workflow.dart'
;
import
'../base/common.dart'
;
import
'../base/common.dart'
;
import
'../base/file_system.dart'
;
import
'../base/file_system.dart'
;
import
'../base/io.dart'
;
import
'../base/io.dart'
;
import
'../base/logger.dart'
;
import
'../base/process.dart'
;
import
'../base/process.dart'
;
import
'../base/utils.dart'
;
import
'../base/utils.dart'
;
import
'../convert.dart'
;
import
'../convert.dart'
;
import
'../device.dart'
;
import
'../device.dart'
;
import
'../emulator.dart'
;
import
'../emulator.dart'
;
import
'../globals.dart'
as
globals
;
import
'android_sdk.dart'
;
import
'android_sdk.dart'
;
class
AndroidEmulators
extends
EmulatorDiscovery
{
class
AndroidEmulators
extends
EmulatorDiscovery
{
AndroidEmulators
({
@required
AndroidSdk
androidSdk
,
@required
AndroidWorkflow
androidWorkflow
,
@required
FileSystem
fileSystem
,
@required
Logger
logger
,
@required
ProcessManager
processManager
,
})
:
_androidSdk
=
androidSdk
,
_androidWorkflow
=
androidWorkflow
,
_fileSystem
=
fileSystem
,
_logger
=
logger
,
_processManager
=
processManager
,
_processUtils
=
ProcessUtils
(
logger:
logger
,
processManager:
processManager
);
final
AndroidWorkflow
_androidWorkflow
;
final
AndroidSdk
_androidSdk
;
final
FileSystem
_fileSystem
;
final
Logger
_logger
;
final
ProcessManager
_processManager
;
final
ProcessUtils
_processUtils
;
@override
@override
bool
get
supportsPlatform
=>
true
;
bool
get
supportsPlatform
=>
true
;
@override
@override
bool
get
canListAnything
=>
androidWorkflow
.
canListEmulators
;
bool
get
canListAnything
=>
_androidWorkflow
.
canListEmulators
;
@override
bool
get
canLaunchAnything
=>
_androidWorkflow
.
canListEmulators
&&
_androidSdk
.
getAvdManagerPath
()
!=
null
;
@override
@override
Future
<
List
<
Emulator
>>
get
emulators
async
=>
getEmulatorAvds
();
Future
<
List
<
Emulator
>>
get
emulators
=>
_getEmulatorAvds
();
/// Return the list of available emulator AVDs.
Future
<
List
<
AndroidEmulator
>>
_getEmulatorAvds
()
async
{
final
String
emulatorPath
=
getEmulatorPath
(
_androidSdk
);
if
(
emulatorPath
==
null
)
{
return
<
AndroidEmulator
>[];
}
final
String
listAvdsOutput
=
(
await
_processUtils
.
run
(
<
String
>[
emulatorPath
,
'-list-avds'
])).
stdout
.
trim
();
final
List
<
AndroidEmulator
>
emulators
=
<
AndroidEmulator
>[];
if
(
listAvdsOutput
!=
null
)
{
_extractEmulatorAvdInfo
(
listAvdsOutput
,
emulators
);
}
return
emulators
;
}
/// Parse the given `emulator -list-avds` output in [text], and fill out the given list
/// of emulators by reading information from the relevant ini files.
void
_extractEmulatorAvdInfo
(
String
text
,
List
<
AndroidEmulator
>
emulators
)
{
for
(
final
String
id
in
text
.
trim
().
split
(
'
\n
'
).
where
((
String
l
)
=>
l
!=
''
))
{
emulators
.
add
(
_loadEmulatorInfo
(
id
));
}
}
AndroidEmulator
_loadEmulatorInfo
(
String
id
)
{
id
=
id
.
trim
();
final
String
avdPath
=
getAvdPath
();
final
AndroidEmulator
androidEmulatorWithoutProperties
=
AndroidEmulator
(
id
,
processManager:
_processManager
,
logger:
_logger
,
androidSdk:
_androidSdk
,
);
if
(
avdPath
==
null
)
{
return
androidEmulatorWithoutProperties
;
}
final
File
iniFile
=
_fileSystem
.
file
(
_fileSystem
.
path
.
join
(
avdPath
,
'
$id
.ini'
));
if
(!
iniFile
.
existsSync
())
{
return
androidEmulatorWithoutProperties
;
}
final
Map
<
String
,
String
>
ini
=
parseIniLines
(
iniFile
.
readAsLinesSync
());
if
(
ini
[
'path'
]
==
null
)
{
return
androidEmulatorWithoutProperties
;
}
final
File
configFile
=
_fileSystem
.
file
(
_fileSystem
.
path
.
join
(
ini
[
'path'
],
'config.ini'
));
if
(!
configFile
.
existsSync
())
{
return
androidEmulatorWithoutProperties
;
}
final
Map
<
String
,
String
>
properties
=
parseIniLines
(
configFile
.
readAsLinesSync
());
return
AndroidEmulator
(
id
,
properties:
properties
,
processManager:
_processManager
,
logger:
_logger
,
androidSdk:
_androidSdk
,
);
}
}
}
class
AndroidEmulator
extends
Emulator
{
class
AndroidEmulator
extends
Emulator
{
AndroidEmulator
(
String
id
,
[
this
.
_properties
])
AndroidEmulator
(
String
id
,
{
:
super
(
id
,
_properties
!=
null
&&
_properties
.
isNotEmpty
);
Map
<
String
,
String
>
properties
,
@required
Logger
logger
,
@required
AndroidSdk
androidSdk
,
@required
ProcessManager
processManager
,
})
:
_properties
=
properties
,
_logger
=
logger
,
_androidSdk
=
androidSdk
,
_processUtils
=
ProcessUtils
(
logger:
logger
,
processManager:
processManager
),
super
(
id
,
properties
!=
null
&&
properties
.
isNotEmpty
);
final
Map
<
String
,
String
>
_properties
;
final
Map
<
String
,
String
>
_properties
;
final
Logger
_logger
;
final
ProcessUtils
_processUtils
;
final
AndroidSdk
_androidSdk
;
// Android Studio uses the ID with underscores replaced with spaces
// Android Studio uses the ID with underscores replaced with spaces
// for the name if displayname is not set so we do the same.
// for the name if displayname is not set so we do the same.
...
@@ -54,8 +149,8 @@ class AndroidEmulator extends Emulator {
...
@@ -54,8 +149,8 @@ class AndroidEmulator extends Emulator {
@override
@override
Future
<
void
>
launch
()
async
{
Future
<
void
>
launch
()
async
{
final
Process
process
=
await
processUtils
.
start
(
final
Process
process
=
await
_
processUtils
.
start
(
<
String
>[
getEmulatorPath
(
globals
.
androidSdk
),
'-avd'
,
id
],
<
String
>[
getEmulatorPath
(
_
androidSdk
),
'-avd'
,
id
],
);
);
// Record output from the emulator process.
// Record output from the emulator process.
...
@@ -81,7 +176,7 @@ class AndroidEmulator extends Emulator {
...
@@ -81,7 +176,7 @@ class AndroidEmulator extends Emulator {
bool
earlyFailure
=
true
;
bool
earlyFailure
=
true
;
unawaited
(
process
.
exitCode
.
then
((
int
status
)
async
{
unawaited
(
process
.
exitCode
.
then
((
int
status
)
async
{
if
(
status
==
0
)
{
if
(
status
==
0
)
{
globals
.
printTrace
(
'The Android emulator exited successfully'
);
_logger
.
printTrace
(
'The Android emulator exited successfully'
);
return
;
return
;
}
}
// Make sure the process' stdout and stderr are drained.
// Make sure the process' stdout and stderr are drained.
...
@@ -89,18 +184,18 @@ class AndroidEmulator extends Emulator {
...
@@ -89,18 +184,18 @@ class AndroidEmulator extends Emulator {
unawaited
(
stdoutSubscription
.
cancel
());
unawaited
(
stdoutSubscription
.
cancel
());
unawaited
(
stderrSubscription
.
cancel
());
unawaited
(
stderrSubscription
.
cancel
());
if
(
stdoutList
.
isNotEmpty
)
{
if
(
stdoutList
.
isNotEmpty
)
{
globals
.
printTrace
(
'Android emulator stdout:'
);
_logger
.
printTrace
(
'Android emulator stdout:'
);
stdoutList
.
forEach
(
globals
.
printTrace
);
stdoutList
.
forEach
(
_logger
.
printTrace
);
}
}
if
(!
earlyFailure
&&
stderrList
.
isEmpty
)
{
if
(!
earlyFailure
&&
stderrList
.
isEmpty
)
{
globals
.
printStatus
(
'The Android emulator exited with code
$status
'
);
_logger
.
printStatus
(
'The Android emulator exited with code
$status
'
);
return
;
return
;
}
}
final
String
when
=
earlyFailure
?
'during startup'
:
'after startup'
;
final
String
when
=
earlyFailure
?
'during startup'
:
'after startup'
;
globals
.
printError
(
'The Android emulator exited with code
$status
$when
'
);
_logger
.
printError
(
'The Android emulator exited with code
$status
$when
'
);
globals
.
printError
(
'Android emulator stderr:'
);
_logger
.
printError
(
'Android emulator stderr:'
);
stderrList
.
forEach
(
globals
.
printError
);
stderrList
.
forEach
(
_logger
.
printError
);
globals
.
printError
(
'Address these issues and try again.'
);
_logger
.
printError
(
'Address these issues and try again.'
);
}));
}));
// Wait a few seconds for the emulator to start.
// Wait a few seconds for the emulator to start.
...
@@ -110,52 +205,6 @@ class AndroidEmulator extends Emulator {
...
@@ -110,52 +205,6 @@ class AndroidEmulator extends Emulator {
}
}
}
}
/// Return the list of available emulator AVDs.
List
<
AndroidEmulator
>
getEmulatorAvds
()
{
final
String
emulatorPath
=
getEmulatorPath
(
globals
.
androidSdk
);
if
(
emulatorPath
==
null
)
{
return
<
AndroidEmulator
>[];
}
final
String
listAvdsOutput
=
processUtils
.
runSync
(
<
String
>[
emulatorPath
,
'-list-avds'
]).
stdout
.
trim
();
final
List
<
AndroidEmulator
>
emulators
=
<
AndroidEmulator
>[];
if
(
listAvdsOutput
!=
null
)
{
extractEmulatorAvdInfo
(
listAvdsOutput
,
emulators
);
}
return
emulators
;
}
/// Parse the given `emulator -list-avds` output in [text], and fill out the given list
/// of emulators by reading information from the relevant ini files.
void
extractEmulatorAvdInfo
(
String
text
,
List
<
AndroidEmulator
>
emulators
)
{
for
(
final
String
id
in
text
.
trim
().
split
(
'
\n
'
).
where
((
String
l
)
=>
l
!=
''
))
{
emulators
.
add
(
_loadEmulatorInfo
(
id
));
}
}
AndroidEmulator
_loadEmulatorInfo
(
String
id
)
{
id
=
id
.
trim
();
final
String
avdPath
=
getAvdPath
();
if
(
avdPath
!=
null
)
{
final
File
iniFile
=
globals
.
fs
.
file
(
globals
.
fs
.
path
.
join
(
avdPath
,
'
$id
.ini'
));
if
(
iniFile
.
existsSync
())
{
final
Map
<
String
,
String
>
ini
=
parseIniLines
(
iniFile
.
readAsLinesSync
());
if
(
ini
[
'path'
]
!=
null
)
{
final
File
configFile
=
globals
.
fs
.
file
(
globals
.
fs
.
path
.
join
(
ini
[
'path'
],
'config.ini'
));
if
(
configFile
.
existsSync
())
{
final
Map
<
String
,
String
>
properties
=
parseIniLines
(
configFile
.
readAsLinesSync
());
return
AndroidEmulator
(
id
,
properties
);
}
}
}
}
return
AndroidEmulator
(
id
);
}
@visibleForTesting
@visibleForTesting
Map
<
String
,
String
>
parseIniLines
(
List
<
String
>
contents
)
{
Map
<
String
,
String
>
parseIniLines
(
List
<
String
>
contents
)
{
...
...
packages/flutter_tools/lib/src/android/android_workflow.dart
View file @
f640ad69
...
@@ -44,17 +44,23 @@ final RegExp licenseNotAccepted = RegExp(r'licenses? not accepted', caseSensitiv
...
@@ -44,17 +44,23 @@ final RegExp licenseNotAccepted = RegExp(r'licenses? not accepted', caseSensitiv
final
RegExp
licenseAccepted
=
RegExp
(
r'All SDK package licenses accepted.'
);
final
RegExp
licenseAccepted
=
RegExp
(
r'All SDK package licenses accepted.'
);
class
AndroidWorkflow
implements
Workflow
{
class
AndroidWorkflow
implements
Workflow
{
AndroidWorkflow
({
@required
AndroidSdk
androidSdk
,
})
:
_androidSdk
=
androidSdk
;
final
AndroidSdk
_androidSdk
;
@override
@override
bool
get
appliesToHostPlatform
=>
true
;
bool
get
appliesToHostPlatform
=>
true
;
@override
@override
bool
get
canListDevices
=>
getAdbPath
(
globals
.
androidSdk
)
!=
null
;
bool
get
canListDevices
=>
getAdbPath
(
_
androidSdk
)
!=
null
;
@override
@override
bool
get
canLaunchDevices
=>
globals
.
androidSdk
!=
null
&&
globals
.
androidSdk
.
validateSdkWellFormed
().
isEmpty
;
bool
get
canLaunchDevices
=>
_androidSdk
!=
null
&&
_
androidSdk
.
validateSdkWellFormed
().
isEmpty
;
@override
@override
bool
get
canListEmulators
=>
getEmulatorPath
(
globals
.
androidSdk
)
!=
null
;
bool
get
canListEmulators
=>
getEmulatorPath
(
_
androidSdk
)
!=
null
;
}
}
class
AndroidValidator
extends
DoctorValidator
{
class
AndroidValidator
extends
DoctorValidator
{
...
...
packages/flutter_tools/lib/src/commands/daemon.dart
View file @
f640ad69
...
@@ -7,6 +7,7 @@ import 'dart:async';
...
@@ -7,6 +7,7 @@ import 'dart:async';
import
'package:meta/meta.dart'
;
import
'package:meta/meta.dart'
;
import
'package:uuid/uuid.dart'
;
import
'package:uuid/uuid.dart'
;
import
'../android/android_workflow.dart'
;
import
'../base/common.dart'
;
import
'../base/common.dart'
;
import
'../base/context.dart'
;
import
'../base/context.dart'
;
import
'../base/file_system.dart'
;
import
'../base/file_system.dart'
;
...
@@ -1039,7 +1040,13 @@ class EmulatorDomain extends Domain {
...
@@ -1039,7 +1040,13 @@ class EmulatorDomain extends Domain {
registerHandler
(
'create'
,
create
);
registerHandler
(
'create'
,
create
);
}
}
EmulatorManager
emulators
=
EmulatorManager
();
EmulatorManager
emulators
=
EmulatorManager
(
fileSystem:
globals
.
fs
,
logger:
globals
.
logger
,
androidSdk:
globals
.
androidSdk
,
processManager:
globals
.
processManager
,
androidWorkflow:
androidWorkflow
,
);
Future
<
List
<
Map
<
String
,
dynamic
>>>
getEmulators
([
Map
<
String
,
dynamic
>
args
])
async
{
Future
<
List
<
Map
<
String
,
dynamic
>>>
getEmulators
([
Map
<
String
,
dynamic
>
args
])
async
{
final
List
<
Emulator
>
list
=
await
emulators
.
getAllAvailableEmulators
();
final
List
<
Emulator
>
list
=
await
emulators
.
getAllAvailableEmulators
();
...
...
packages/flutter_tools/lib/src/commands/emulators.dart
View file @
f640ad69
...
@@ -104,7 +104,7 @@ class EmulatorsCommand extends FlutterCommand {
...
@@ -104,7 +104,7 @@ class EmulatorsCommand extends FlutterCommand {
void
_printEmulatorList
(
List
<
Emulator
>
emulators
,
String
message
)
{
void
_printEmulatorList
(
List
<
Emulator
>
emulators
,
String
message
)
{
globals
.
printStatus
(
'
$message
\n
'
);
globals
.
printStatus
(
'
$message
\n
'
);
Emulator
.
printEmulators
(
emulators
);
Emulator
.
printEmulators
(
emulators
,
globals
.
logger
);
_printAdditionalInfo
(
showCreateInstruction:
true
,
showRunInstruction:
true
);
_printAdditionalInfo
(
showCreateInstruction:
true
,
showRunInstruction:
true
);
}
}
...
...
packages/flutter_tools/lib/src/context_runner.dart
View file @
f640ad69
...
@@ -79,7 +79,9 @@ Future<T> runInContext<T>(
...
@@ -79,7 +79,9 @@ Future<T> runInContext<T>(
processManager:
globals
.
processManager
,
processManager:
globals
.
processManager
,
userMessages:
globals
.
userMessages
,
userMessages:
globals
.
userMessages
,
),
),
AndroidWorkflow:
()
=>
AndroidWorkflow
(),
AndroidWorkflow:
()
=>
AndroidWorkflow
(
androidSdk:
globals
.
androidSdk
,
),
ApplicationPackageFactory:
()
=>
ApplicationPackageFactory
(),
ApplicationPackageFactory:
()
=>
ApplicationPackageFactory
(),
Artifacts:
()
=>
CachedArtifacts
(
Artifacts:
()
=>
CachedArtifacts
(
fileSystem:
globals
.
fs
,
fileSystem:
globals
.
fs
,
...
@@ -125,7 +127,13 @@ Future<T> runInContext<T>(
...
@@ -125,7 +127,13 @@ Future<T> runInContext<T>(
DeviceManager:
()
=>
DeviceManager
(),
DeviceManager:
()
=>
DeviceManager
(),
Doctor:
()
=>
Doctor
(
logger:
globals
.
logger
),
Doctor:
()
=>
Doctor
(
logger:
globals
.
logger
),
DoctorValidatorsProvider:
()
=>
DoctorValidatorsProvider
.
defaultInstance
,
DoctorValidatorsProvider:
()
=>
DoctorValidatorsProvider
.
defaultInstance
,
EmulatorManager:
()
=>
EmulatorManager
(),
EmulatorManager:
()
=>
EmulatorManager
(
androidSdk:
globals
.
androidSdk
,
processManager:
globals
.
processManager
,
logger:
globals
.
logger
,
fileSystem:
globals
.
fs
,
androidWorkflow:
androidWorkflow
,
),
FeatureFlags:
()
=>
const
FeatureFlags
(),
FeatureFlags:
()
=>
const
FeatureFlags
(),
FlutterVersion:
()
=>
FlutterVersion
(
const
SystemClock
()),
FlutterVersion:
()
=>
FlutterVersion
(
const
SystemClock
()),
FuchsiaArtifacts:
()
=>
FuchsiaArtifacts
.
find
(),
FuchsiaArtifacts:
()
=>
FuchsiaArtifacts
.
find
(),
...
...
packages/flutter_tools/lib/src/emulator.dart
View file @
f640ad69
...
@@ -6,28 +6,49 @@ import 'dart:async';
...
@@ -6,28 +6,49 @@ import 'dart:async';
import
'dart:math'
as
math
;
import
'dart:math'
as
math
;
import
'package:meta/meta.dart'
;
import
'package:meta/meta.dart'
;
import
'package:process/process.dart'
;
import
'android/android_emulator.dart'
;
import
'android/android_emulator.dart'
;
import
'android/android_sdk.dart'
;
import
'android/android_sdk.dart'
;
import
'android/android_workflow.dart'
;
import
'base/context.dart'
;
import
'base/context.dart'
;
import
'base/file_system.dart'
;
import
'base/logger.dart'
;
import
'base/process.dart'
;
import
'base/process.dart'
;
import
'device.dart'
;
import
'device.dart'
;
import
'globals.dart'
as
globals
;
import
'ios/ios_emulators.dart'
;
import
'ios/ios_emulators.dart'
;
EmulatorManager
get
emulatorManager
=>
context
.
get
<
EmulatorManager
>();
EmulatorManager
get
emulatorManager
=>
context
.
get
<
EmulatorManager
>();
/// A class to get all available emulators.
/// A class to get all available emulators.
class
EmulatorManager
{
class
EmulatorManager
{
/// Constructing EmulatorManager is cheap; they only do expensive work if some
EmulatorManager
({
/// of their methods are called.
@required
AndroidSdk
androidSdk
,
EmulatorManager
()
{
@required
Logger
logger
,
// Register the known discoverers.
@required
ProcessManager
processManager
,
_emulatorDiscoverers
.
add
(
AndroidEmulators
());
@required
AndroidWorkflow
androidWorkflow
,
_emulatorDiscoverers
.
add
(
IOSEmulators
());
@required
FileSystem
fileSystem
,
})
:
_androidSdk
=
androidSdk
,
_processUtils
=
ProcessUtils
(
logger:
logger
,
processManager:
processManager
),
_androidEmulators
=
AndroidEmulators
(
androidSdk:
androidSdk
,
logger:
logger
,
processManager:
processManager
,
fileSystem:
fileSystem
,
androidWorkflow:
androidWorkflow
)
{
_emulatorDiscoverers
.
add
(
_androidEmulators
);
}
}
final
List
<
EmulatorDiscovery
>
_emulatorDiscoverers
=
<
EmulatorDiscovery
>[];
final
AndroidSdk
_androidSdk
;
final
AndroidEmulators
_androidEmulators
;
final
ProcessUtils
_processUtils
;
// Constructing EmulatorManager is cheap; they only do expensive work if some
// of their methods are called.
final
List
<
EmulatorDiscovery
>
_emulatorDiscoverers
=
<
EmulatorDiscovery
>[
IOSEmulators
(),
];
Future
<
List
<
Emulator
>>
getEmulatorsMatching
(
String
searchText
)
async
{
Future
<
List
<
Emulator
>>
getEmulatorsMatching
(
String
searchText
)
async
{
final
List
<
Emulator
>
emulators
=
await
getAllAvailableEmulators
();
final
List
<
Emulator
>
emulators
=
await
getAllAvailableEmulators
();
...
@@ -64,7 +85,7 @@ class EmulatorManager {
...
@@ -64,7 +85,7 @@ class EmulatorManager {
/// Return the list of all available emulators.
/// Return the list of all available emulators.
Future
<
CreateEmulatorResult
>
createEmulator
({
String
name
})
async
{
Future
<
CreateEmulatorResult
>
createEmulator
({
String
name
})
async
{
if
(
name
==
null
||
name
==
''
)
{
if
(
name
==
null
||
name
.
isEmpty
)
{
const
String
autoName
=
'flutter_emulator'
;
const
String
autoName
=
'flutter_emulator'
;
// Don't use getEmulatorsMatching here, as it will only return one
// Don't use getEmulatorsMatching here, as it will only return one
// if there's an exact match and we need all those with this prefix
// if there's an exact match and we need all those with this prefix
...
@@ -80,6 +101,11 @@ class EmulatorManager {
...
@@ -80,6 +101,11 @@ class EmulatorManager {
name
=
'
${autoName}
_
${++suffix}
'
;
name
=
'
${autoName}
_
${++suffix}
'
;
}
}
}
}
if
(!
_androidEmulators
.
canLaunchAnything
)
{
return
CreateEmulatorResult
(
name
,
success:
false
,
error:
'avdmanager is missing from the Android SDK'
);
}
final
String
device
=
await
_getPreferredAvailableDevice
();
final
String
device
=
await
_getPreferredAvailableDevice
();
if
(
device
==
null
)
{
if
(
device
==
null
)
{
...
@@ -113,17 +139,15 @@ class EmulatorManager {
...
@@ -113,17 +139,15 @@ class EmulatorManager {
.
join
(
'
\n
'
)
.
join
(
'
\n
'
)
.
trim
();
.
trim
();
}
}
final
RunResult
runResult
=
await
_processUtils
.
run
(<
String
>[
final
List
<
String
>
args
=
<
String
>[
getAvdManagerPath
(
_androidSdk
),
getAvdManagerPath
(
globals
.
androidSdk
),
'create'
,
'create'
,
'avd'
,
'avd'
,
'-n'
,
name
,
'-n'
,
name
,
'-k'
,
sdkId
,
'-k'
,
sdkId
,
'-d'
,
device
,
'-d'
,
device
,
];
],
environment:
_androidSdk
?.
sdkManagerEnv
,
final
RunResult
runResult
=
processUtils
.
runSync
(
args
,
);
environment:
globals
.
androidSdk
?.
sdkManagerEnv
);
return
CreateEmulatorResult
(
return
CreateEmulatorResult
(
name
,
name
,
success:
runResult
.
exitCode
==
0
,
success:
runResult
.
exitCode
==
0
,
...
@@ -136,15 +160,16 @@ class EmulatorManager {
...
@@ -136,15 +160,16 @@ class EmulatorManager {
'pixel'
,
'pixel'
,
'pixel_xl'
,
'pixel_xl'
,
];
];
Future
<
String
>
_getPreferredAvailableDevice
()
async
{
Future
<
String
>
_getPreferredAvailableDevice
()
async
{
final
List
<
String
>
args
=
<
String
>[
final
List
<
String
>
args
=
<
String
>[
getAvdManagerPath
(
globals
.
androidSdk
),
getAvdManagerPath
(
_
androidSdk
),
'list'
,
'list'
,
'device'
,
'device'
,
'-c'
,
'-c'
,
];
];
final
RunResult
runResult
=
processUtils
.
runSync
(
args
,
final
RunResult
runResult
=
await
_processUtils
.
run
(
args
,
environment:
globals
.
androidSdk
?.
sdkManagerEnv
);
environment:
_
androidSdk
?.
sdkManagerEnv
);
if
(
runResult
.
exitCode
!=
0
)
{
if
(
runResult
.
exitCode
!=
0
)
{
return
null
;
return
null
;
}
}
...
@@ -160,29 +185,30 @@ class EmulatorManager {
...
@@ -160,29 +185,30 @@ class EmulatorManager {
);
);
}
}
RegExp
androidApiVersion
=
RegExp
(
r';android-(\d+);'
);
static
final
RegExp
_androidApiVersion
=
RegExp
(
r';android-(\d+);'
);
Future
<
String
>
_getPreferredSdkId
()
async
{
Future
<
String
>
_getPreferredSdkId
()
async
{
// It seems that to get the available list of images, we need to send a
// It seems that to get the available list of images, we need to send a
// request to create without the image and it'll provide us a list :-(
// request to create without the image and it'll provide us a list :-(
final
List
<
String
>
args
=
<
String
>[
final
List
<
String
>
args
=
<
String
>[
getAvdManagerPath
(
globals
.
androidSdk
),
getAvdManagerPath
(
_
androidSdk
),
'create'
,
'create'
,
'avd'
,
'avd'
,
'-n'
,
'temp'
,
'-n'
,
'temp'
,
];
];
final
RunResult
runResult
=
processUtils
.
runSync
(
args
,
final
RunResult
runResult
=
await
_processUtils
.
run
(
args
,
environment:
globals
.
androidSdk
?.
sdkManagerEnv
);
environment:
_
androidSdk
?.
sdkManagerEnv
);
// Get the list of IDs that match our criteria
// Get the list of IDs that match our criteria
final
List
<
String
>
availableIDs
=
runResult
.
stderr
final
List
<
String
>
availableIDs
=
runResult
.
stderr
.
split
(
'
\n
'
)
.
split
(
'
\n
'
)
.
where
((
String
l
)
=>
androidApiVersion
.
hasMatch
(
l
))
.
where
((
String
l
)
=>
_
androidApiVersion
.
hasMatch
(
l
))
.
where
((
String
l
)
=>
l
.
contains
(
'system-images'
))
.
where
((
String
l
)
=>
l
.
contains
(
'system-images'
))
.
where
((
String
l
)
=>
l
.
contains
(
'google_apis_playstore'
))
.
where
((
String
l
)
=>
l
.
contains
(
'google_apis_playstore'
))
.
toList
();
.
toList
();
final
List
<
int
>
availableApiVersions
=
availableIDs
final
List
<
int
>
availableApiVersions
=
availableIDs
.
map
<
String
>((
String
id
)
=>
androidApiVersion
.
firstMatch
(
id
).
group
(
1
))
.
map
<
String
>((
String
id
)
=>
_
androidApiVersion
.
firstMatch
(
id
).
group
(
1
))
.
map
<
int
>((
String
apiVersion
)
=>
int
.
parse
(
apiVersion
))
.
map
<
int
>((
String
apiVersion
)
=>
int
.
parse
(
apiVersion
))
.
toList
();
.
toList
();
...
@@ -209,10 +235,12 @@ class EmulatorManager {
...
@@ -209,10 +235,12 @@ class EmulatorManager {
abstract
class
EmulatorDiscovery
{
abstract
class
EmulatorDiscovery
{
bool
get
supportsPlatform
;
bool
get
supportsPlatform
;
/// Whether this emulator discovery is capable of listing any emulators given the
/// Whether this emulator discovery is capable of listing any emulators.
/// current environment configuration.
bool
get
canListAnything
;
bool
get
canListAnything
;
/// Whether this emulator discovery is capabale of launching new emulators.
bool
get
canLaunchAnything
;
Future
<
List
<
Emulator
>>
get
emulators
;
Future
<
List
<
Emulator
>>
get
emulators
;
}
}
...
@@ -280,8 +308,8 @@ abstract class Emulator {
...
@@ -280,8 +308,8 @@ abstract class Emulator {
.
toList
();
.
toList
();
}
}
static
void
printEmulators
(
List
<
Emulator
>
emulators
)
{
static
void
printEmulators
(
List
<
Emulator
>
emulators
,
Logger
logger
)
{
descriptions
(
emulators
).
forEach
(
globals
.
printStatus
);
descriptions
(
emulators
).
forEach
(
logger
.
printStatus
);
}
}
}
}
...
...
packages/flutter_tools/lib/src/ios/ios_emulators.dart
View file @
f640ad69
...
@@ -19,6 +19,9 @@ class IOSEmulators extends EmulatorDiscovery {
...
@@ -19,6 +19,9 @@ class IOSEmulators extends EmulatorDiscovery {
@override
@override
Future
<
List
<
Emulator
>>
get
emulators
async
=>
getEmulators
();
Future
<
List
<
Emulator
>>
get
emulators
async
=>
getEmulators
();
@override
bool
get
canLaunchAnything
=>
canListAnything
;
}
}
class
IOSEmulator
extends
Emulator
{
class
IOSEmulator
extends
Emulator
{
...
...
packages/flutter_tools/test/general.shard/android/android_device_discovery_test.dart
View file @
f640ad69
...
@@ -20,7 +20,9 @@ void main() {
...
@@ -20,7 +20,9 @@ void main() {
final
AndroidDevices
androidDevices
=
AndroidDevices
(
final
AndroidDevices
androidDevices
=
AndroidDevices
(
androidSdk:
MockAndroidSdk
(
null
),
androidSdk:
MockAndroidSdk
(
null
),
logger:
BufferLogger
.
test
(),
logger:
BufferLogger
.
test
(),
androidWorkflow:
AndroidWorkflow
(),
androidWorkflow:
AndroidWorkflow
(
androidSdk:
MockAndroidSdk
(
null
),
),
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[]),
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[]),
);
);
...
@@ -39,7 +41,9 @@ void main() {
...
@@ -39,7 +41,9 @@ void main() {
final
AndroidDevices
androidDevices
=
AndroidDevices
(
final
AndroidDevices
androidDevices
=
AndroidDevices
(
androidSdk:
MockAndroidSdk
(),
androidSdk:
MockAndroidSdk
(),
logger:
BufferLogger
.
test
(),
logger:
BufferLogger
.
test
(),
androidWorkflow:
AndroidWorkflow
(),
androidWorkflow:
AndroidWorkflow
(
androidSdk:
MockAndroidSdk
(),
),
processManager:
processManager
,
processManager:
processManager
,
);
);
...
@@ -57,7 +61,9 @@ void main() {
...
@@ -57,7 +61,9 @@ void main() {
final
AndroidDevices
androidDevices
=
AndroidDevices
(
final
AndroidDevices
androidDevices
=
AndroidDevices
(
androidSdk:
MockAndroidSdk
(),
androidSdk:
MockAndroidSdk
(),
logger:
BufferLogger
.
test
(),
logger:
BufferLogger
.
test
(),
androidWorkflow:
AndroidWorkflow
(),
androidWorkflow:
AndroidWorkflow
(
androidSdk:
MockAndroidSdk
(),
),
processManager:
processManager
,
processManager:
processManager
,
);
);
...
...
packages/flutter_tools/test/general.shard/android/android_emulator_test.dart
View file @
f640ad69
...
@@ -4,12 +4,11 @@
...
@@ -4,12 +4,11 @@
import
'dart:async'
;
import
'dart:async'
;
import
'package:file/file.dart'
;
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/android/android_sdk.dart'
import
'package:flutter_tools/src/android/android_sdk.dart'
show
getEmulatorPath
,
AndroidSdk
;
show
getEmulatorPath
;
import
'package:flutter_tools/src/android/android_emulator.dart'
;
import
'package:flutter_tools/src/android/android_emulator.dart'
;
import
'package:flutter_tools/src/base/common.dart'
;
import
'package:flutter_tools/src/base/common.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'package:quiver/testing/async.dart'
;
...
@@ -19,24 +18,42 @@ import '../../src/context.dart';
...
@@ -19,24 +18,42 @@ import '../../src/context.dart';
import
'../../src/fake_process_manager.dart'
;
import
'../../src/fake_process_manager.dart'
;
import
'../../src/mocks.dart'
show
MockAndroidSdk
;
import
'../../src/mocks.dart'
show
MockAndroidSdk
;
const
String
emulatorID
=
'i1234'
;
const
String
errorText
=
'[Android emulator test error]'
;
const
List
<
String
>
kEmulatorLaunchCommand
=
<
String
>[
'emulator'
,
'-avd'
,
emulatorID
,
];
void
main
(
)
{
void
main
(
)
{
group
(
'android_emulator'
,
()
{
group
(
'android_emulator'
,
()
{
test
Using
Context
(
'flags emulators without config'
,
()
{
test
Without
Context
(
'flags emulators without config'
,
()
{
const
String
emulatorID
=
'1234'
;
const
String
emulatorID
=
'1234'
;
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
);
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
any
(),
androidSdk:
MockAndroidSdk
(),
);
expect
(
emulator
.
id
,
emulatorID
);
expect
(
emulator
.
id
,
emulatorID
);
expect
(
emulator
.
hasConfig
,
false
);
expect
(
emulator
.
hasConfig
,
false
);
});
});
testUsingContext
(
'flags emulators with config'
,
()
{
testWithoutContext
(
'flags emulators with config'
,
()
{
const
String
emulatorID
=
'1234'
;
const
String
emulatorID
=
'1234'
;
final
AndroidEmulator
emulator
=
AndroidEmulator
(
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
emulatorID
,
const
<
String
,
String
>{
'name'
:
'test'
},
properties:
const
<
String
,
String
>{
'name'
:
'test'
},
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
any
(),
androidSdk:
MockAndroidSdk
(),
);
);
expect
(
emulator
.
id
,
emulatorID
);
expect
(
emulator
.
id
,
emulatorID
);
expect
(
emulator
.
hasConfig
,
true
);
expect
(
emulator
.
hasConfig
,
true
);
});
});
testUsingContext
(
'reads expected metadata'
,
()
{
testWithoutContext
(
'reads expected metadata'
,
()
{
const
String
emulatorID
=
'1234'
;
const
String
emulatorID
=
'1234'
;
const
String
manufacturer
=
'Me'
;
const
String
manufacturer
=
'Me'
;
const
String
displayName
=
'The best one'
;
const
String
displayName
=
'The best one'
;
...
@@ -44,33 +61,57 @@ void main() {
...
@@ -44,33 +61,57 @@ void main() {
'hw.device.manufacturer'
:
manufacturer
,
'hw.device.manufacturer'
:
manufacturer
,
'avd.ini.displayname'
:
displayName
,
'avd.ini.displayname'
:
displayName
,
};
};
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties
);
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties:
properties
,
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
any
(),
androidSdk:
MockAndroidSdk
(),
);
expect
(
emulator
.
id
,
emulatorID
);
expect
(
emulator
.
id
,
emulatorID
);
expect
(
emulator
.
name
,
displayName
);
expect
(
emulator
.
name
,
displayName
);
expect
(
emulator
.
manufacturer
,
manufacturer
);
expect
(
emulator
.
manufacturer
,
manufacturer
);
expect
(
emulator
.
category
,
Category
.
mobile
);
expect
(
emulator
.
category
,
Category
.
mobile
);
expect
(
emulator
.
platformType
,
PlatformType
.
android
);
expect
(
emulator
.
platformType
,
PlatformType
.
android
);
});
});
testUsingContext
(
'prefers displayname for name'
,
()
{
testWithoutContext
(
'prefers displayname for name'
,
()
{
const
String
emulatorID
=
'1234'
;
const
String
emulatorID
=
'1234'
;
const
String
displayName
=
'The best one'
;
const
String
displayName
=
'The best one'
;
final
Map
<
String
,
String
>
properties
=
<
String
,
String
>{
final
Map
<
String
,
String
>
properties
=
<
String
,
String
>{
'avd.ini.displayname'
:
displayName
,
'avd.ini.displayname'
:
displayName
,
};
};
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties
);
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties:
properties
,
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
any
(),
androidSdk:
MockAndroidSdk
(),
);
expect
(
emulator
.
name
,
displayName
);
expect
(
emulator
.
name
,
displayName
);
});
});
testUsingContext
(
'uses cleaned up ID if no displayname is set'
,
()
{
testWithoutContext
(
'uses cleaned up ID if no displayname is set'
,
()
{
// Android Studio uses the ID with underscores replaced with spaces
// Android Studio uses the ID with underscores replaced with spaces
// for the name if displayname is not set so we do the same.
// for the name if displayname is not set so we do the same.
const
String
emulatorID
=
'This_is_my_ID'
;
const
String
emulatorID
=
'This_is_my_ID'
;
final
Map
<
String
,
String
>
properties
=
<
String
,
String
>{
final
Map
<
String
,
String
>
properties
=
<
String
,
String
>{
'avd.ini.notadisplayname'
:
'this is not a display name'
,
'avd.ini.notadisplayname'
:
'this is not a display name'
,
};
};
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties
);
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
properties:
properties
,
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
any
(),
androidSdk:
MockAndroidSdk
(),
);
expect
(
emulator
.
name
,
'This is my ID'
);
expect
(
emulator
.
name
,
'This is my ID'
);
});
});
testUsingContext
(
'parses ini files'
,
()
{
testWithoutContext
(
'parses ini files'
,
()
{
const
String
iniFile
=
'''
const
String
iniFile
=
'''
hw.device.name=My Test Name
hw.device.name=My Test Name
#hw.device.name=Bad Name
#hw.device.name=Bad Name
...
@@ -79,6 +120,7 @@ void main() {
...
@@ -79,6 +120,7 @@ void main() {
avd.ini.displayname = dispName
avd.ini.displayname = dispName
'''
;
'''
;
final
Map
<
String
,
String
>
results
=
parseIniLines
(
iniFile
.
split
(
'
\n
'
));
final
Map
<
String
,
String
>
results
=
parseIniLines
(
iniFile
.
split
(
'
\n
'
));
expect
(
results
[
'hw.device.name'
],
'My Test Name'
);
expect
(
results
[
'hw.device.name'
],
'My Test Name'
);
expect
(
results
[
'hw.device.manufacturer'
],
'Me'
);
expect
(
results
[
'hw.device.manufacturer'
],
'Me'
);
expect
(
results
[
'avd.ini.displayname'
],
'dispName'
);
expect
(
results
[
'avd.ini.displayname'
],
'dispName'
);
...
@@ -86,51 +128,24 @@ void main() {
...
@@ -86,51 +128,24 @@ void main() {
});
});
group
(
'Android emulator launch '
,
()
{
group
(
'Android emulator launch '
,
()
{
const
String
emulatorID
=
'i1234'
;
const
String
errorText
=
'[Android emulator test error]'
;
MockAndroidSdk
mockSdk
;
MockAndroidSdk
mockSdk
;
FakeProcessManager
successProcessManager
;
FakeProcessManager
errorProcessManager
;
FakeProcessManager
lateFailureProcessManager
;
MemoryFileSystem
fs
;
setUp
(()
{
setUp
(()
{
fs
=
MemoryFileSystem
();
mockSdk
=
MockAndroidSdk
();
mockSdk
=
MockAndroidSdk
();
when
(
mockSdk
.
emulatorPath
).
thenReturn
(
'emulator'
);
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
{
testWithoutContext
(
'succeeds'
,
()
async
{
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
);
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
kEmulatorLaunchCommand
),
]),
androidSdk:
mockSdk
,
logger:
BufferLogger
.
test
(),
);
expect
(
getEmulatorPath
(
mockSdk
),
mockSdk
.
emulatorPath
);
expect
(
getEmulatorPath
(
mockSdk
),
mockSdk
.
emulatorPath
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
final
Completer
<
void
>
completer
=
Completer
<
void
>();
FakeAsync
().
run
((
FakeAsync
time
)
{
FakeAsync
().
run
((
FakeAsync
time
)
{
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
...
@@ -138,15 +153,24 @@ void main() {
...
@@ -138,15 +153,24 @@ void main() {
time
.
flushMicrotasks
();
time
.
flushMicrotasks
();
});
});
await
completer
.
future
;
await
completer
.
future
;
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
successProcessManager
,
AndroidSdk:
()
=>
mockSdk
,
FileSystem:
()
=>
fs
,
});
});
testUsingContext
(
'prints error on failure'
,
()
async
{
testWithoutContext
(
'prints error on failure'
,
()
async
{
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
);
final
BufferLogger
logger
=
BufferLogger
.
test
();
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
kEmulatorLaunchCommand
,
exitCode:
1
,
stderr:
errorText
,
stdout:
'dummy text'
,
duration:
Duration
(
seconds:
1
),
),
]),
androidSdk:
mockSdk
,
logger:
logger
,
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
final
Completer
<
void
>
completer
=
Completer
<
void
>();
FakeAsync
().
run
((
FakeAsync
time
)
{
FakeAsync
().
run
((
FakeAsync
time
)
{
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
...
@@ -155,15 +179,24 @@ void main() {
...
@@ -155,15 +179,24 @@ void main() {
});
});
await
completer
.
future
;
await
completer
.
future
;
expect
(
testLogger
.
errorText
,
contains
(
errorText
));
expect
(
logger
.
errorText
,
contains
(
errorText
));
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
errorProcessManager
,
AndroidSdk:
()
=>
mockSdk
,
FileSystem:
()
=>
fs
,
});
});
testUsingContext
(
'prints nothing on late failure with empty stderr'
,
()
async
{
testWithoutContext
(
'prints nothing on late failure with empty stderr'
,
()
async
{
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
);
final
BufferLogger
logger
=
BufferLogger
.
test
();
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
,
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
kEmulatorLaunchCommand
,
exitCode:
1
,
stderr:
''
,
stdout:
'dummy text'
,
duration:
Duration
(
seconds:
4
),
),
]),
androidSdk:
mockSdk
,
logger:
logger
,
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
final
Completer
<
void
>
completer
=
Completer
<
void
>();
FakeAsync
().
run
((
FakeAsync
time
)
async
{
FakeAsync
().
run
((
FakeAsync
time
)
async
{
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
...
@@ -171,11 +204,8 @@ void main() {
...
@@ -171,11 +204,8 @@ void main() {
time
.
flushMicrotasks
();
time
.
flushMicrotasks
();
});
});
await
completer
.
future
;
await
completer
.
future
;
expect
(
testLogger
.
errorText
,
isEmpty
);
},
overrides:
<
Type
,
Generator
>{
expect
(
logger
.
errorText
,
isEmpty
);
ProcessManager:
()
=>
lateFailureProcessManager
,
AndroidSdk:
()
=>
mockSdk
,
FileSystem:
()
=>
fs
,
});
});
});
});
}
}
packages/flutter_tools/test/general.shard/emulator_test.dart
View file @
f640ad69
...
@@ -5,10 +5,10 @@
...
@@ -5,10 +5,10 @@
import
'dart:async'
;
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:convert'
;
import
'package:collection/collection.dart'
show
ListEquality
;
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/android/android_sdk.dart'
;
import
'package:flutter_tools/src/android/android_workflow.dart'
;
import
'package:flutter_tools/src/base/config.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/emulator.dart'
;
import
'package:flutter_tools/src/emulator.dart'
;
import
'package:flutter_tools/src/ios/ios_emulators.dart'
;
import
'package:flutter_tools/src/ios/ios_emulators.dart'
;
...
@@ -20,106 +20,252 @@ import '../src/common.dart';
...
@@ -20,106 +20,252 @@ import '../src/common.dart';
import
'../src/context.dart'
;
import
'../src/context.dart'
;
import
'../src/mocks.dart'
;
import
'../src/mocks.dart'
;
const
FakeEmulator
emulator1
=
FakeEmulator
(
'Nexus_5'
,
'Nexus 5'
,
'Google'
);
const
FakeEmulator
emulator2
=
FakeEmulator
(
'Nexus_5X_API_27_x86'
,
'Nexus 5X'
,
'Google'
);
const
FakeEmulator
emulator3
=
FakeEmulator
(
'iOS Simulator'
,
'iOS Simulator'
,
'Apple'
);
const
List
<
Emulator
>
emulators
=
<
Emulator
>[
emulator1
,
emulator2
,
emulator3
,
];
// We have to send a command that fails in order to get the list of valid
// system images paths. This is an example of the output to use in the mock.
const
String
fakeCreateFailureOutput
=
'Error: Package path (-k) not specified. Valid system image paths are:
\n
'
'system-images;android-27;google_apis;x86
\n
'
'system-images;android-P;google_apis;x86
\n
'
'system-images;android-27;google_apis_playstore;x86
\n
'
'null
\n
'
;
// Yep, these really end with null (on dantup's machine at least)
const
FakeCommand
kListEmulatorsCommand
=
FakeCommand
(
command:
<
String
>[
'avdmanager'
,
'create'
,
'avd'
,
'-n'
,
'temp'
],
stderr:
fakeCreateFailureOutput
,
exitCode:
1
,
);
void
main
(
)
{
void
main
(
)
{
MockProcessManager
mockProcessManager
;
MockProcessManager
mockProcessManager
;
MockConfig
mockConfig
;
MockAndroidSdk
mockSdk
;
MockAndroidSdk
mockSdk
;
MockXcode
mockXcode
;
MockXcode
mockXcode
;
setUp
(()
{
setUp
(()
{
mockProcessManager
=
MockProcessManager
();
mockProcessManager
=
MockProcessManager
();
mockConfig
=
MockConfig
();
mockSdk
=
MockAndroidSdk
();
mockSdk
=
MockAndroidSdk
();
mockXcode
=
MockXcode
();
mockXcode
=
MockXcode
();
when
(
mockSdk
.
avdManagerPath
).
thenReturn
(
'avdmanager'
);
when
(
mockSdk
.
avdManagerPath
).
thenReturn
(
'avdmanager'
);
when
(
mockSdk
.
getAvdManagerPath
()).
thenReturn
(
'avdmanager'
);
when
(
mockSdk
.
emulatorPath
).
thenReturn
(
'emulator'
);
when
(
mockSdk
.
emulatorPath
).
thenReturn
(
'emulator'
);
});
});
group
(
'EmulatorManager'
,
()
{
group
(
'EmulatorManager'
,
()
{
// iOS discovery uses context.
testUsingContext
(
'getEmulators'
,
()
async
{
testUsingContext
(
'getEmulators'
,
()
async
{
// Test that EmulatorManager.getEmulators() doesn't throw.
// Test that EmulatorManager.getEmulators() doesn't throw.
final
List
<
Emulator
>
emulators
=
final
EmulatorManager
emulatorManager
=
EmulatorManager
(
await
emulatorManager
.
getAllAvailableEmulators
();
fileSystem:
MemoryFileSystem
.
test
(),
expect
(
emulators
,
isList
);
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'emulator'
,
'-list-avds'
],
stdout:
'existing-avd-1'
,
),
]),
androidSdk:
mockSdk
,
androidWorkflow:
AndroidWorkflow
(
androidSdk:
mockSdk
,
),
);
await
expectLater
(()
async
=>
await
emulatorManager
.
getAllAvailableEmulators
(),
returnsNormally
);
});
});
testUsingContext
(
'getEmulatorsById'
,
()
async
{
testWithoutContext
(
'getEmulatorsById'
,
()
async
{
const
_MockEmulator
emulator1
=
final
TestEmulatorManager
testEmulatorManager
=
TestEmulatorManager
(
emulators
);
_MockEmulator
(
'Nexus_5'
,
'Nexus 5'
,
'Google'
);
const
_MockEmulator
emulator2
=
_MockEmulator
(
'Nexus_5X_API_27_x86'
,
'Nexus 5X'
,
'Google'
);
const
_MockEmulator
emulator3
=
_MockEmulator
(
'iOS Simulator'
,
'iOS Simulator'
,
'Apple'
);
final
List
<
Emulator
>
emulators
=
<
Emulator
>[
emulator1
,
emulator2
,
emulator3
,
];
final
TestEmulatorManager
testEmulatorManager
=
TestEmulatorManager
(
emulators
);
Future
<
void
>
expectEmulator
(
String
id
,
List
<
Emulator
>
expected
)
async
{
expect
(
await
testEmulatorManager
.
getEmulatorsMatching
(
'Nexus_5'
),
<
Emulator
>[
emulator1
]);
expect
(
await
testEmulatorManager
.
getEmulatorsMatching
(
id
),
expected
);
expect
(
await
testEmulatorManager
.
getEmulatorsMatching
(
'Nexus_5X'
),
<
Emulator
>[
emulator2
]);
}
expect
(
await
testEmulatorManager
.
getEmulatorsMatching
(
'Nexus_5X_API_27_x86'
),
<
Emulator
>[
emulator2
]);
expect
(
await
testEmulatorManager
.
getEmulatorsMatching
(
'Nexus'
),
<
Emulator
>[
emulator1
,
emulator2
]);
expect
(
await
testEmulatorManager
.
getEmulatorsMatching
(
'iOS Simulator'
),
<
Emulator
>[
emulator3
]);
expect
(
await
testEmulatorManager
.
getEmulatorsMatching
(
'ios'
),
<
Emulator
>[
emulator3
]);
});
await
expectEmulator
(
'Nexus_5'
,
<
Emulator
>[
emulator1
]);
testUsingContext
(
'create emulator with a missing avdmanager does not crash.'
,
()
async
{
await
expectEmulator
(
'Nexus_5X'
,
<
Emulator
>[
emulator2
]);
when
(
mockSdk
.
avdManagerPath
).
thenReturn
(
null
);
await
expectEmulator
(
'Nexus_5X_API_27_x86'
,
<
Emulator
>[
emulator2
]);
when
(
mockSdk
.
getAvdManagerPath
()).
thenReturn
(
null
);
await
expectEmulator
(
'Nexus'
,
<
Emulator
>[
emulator1
,
emulator2
]);
final
EmulatorManager
emulatorManager
=
EmulatorManager
(
await
expectEmulator
(
'iOS Simulator'
,
<
Emulator
>[
emulator3
]);
fileSystem:
MemoryFileSystem
.
test
(),
await
expectEmulator
(
'ios'
,
<
Emulator
>[
emulator3
]);
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'emulator'
,
'-list-avds'
],
stdout:
'existing-avd-1'
,
),
]),
androidSdk:
mockSdk
,
androidWorkflow:
AndroidWorkflow
(
androidSdk:
mockSdk
,
),
);
final
CreateEmulatorResult
result
=
await
emulatorManager
.
createEmulator
();
expect
(
result
.
success
,
false
);
expect
(
result
.
error
,
contains
(
'avdmanager is missing from the Android SDK'
));
});
});
// iOS discovery uses context.
testUsingContext
(
'create emulator with an empty name does not fail'
,
()
async
{
testUsingContext
(
'create emulator with an empty name does not fail'
,
()
async
{
final
CreateEmulatorResult
res
=
await
emulatorManager
.
createEmulator
();
final
EmulatorManager
emulatorManager
=
EmulatorManager
(
expect
(
res
.
success
,
equals
(
true
));
fileSystem:
MemoryFileSystem
.
test
(),
},
overrides:
<
Type
,
Generator
>{
logger:
BufferLogger
.
test
(),
ProcessManager:
()
=>
mockProcessManager
,
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
Config:
()
=>
mockConfig
,
const
FakeCommand
(
AndroidSdk:
()
=>
mockSdk
,
command:
<
String
>[
'emulator'
,
'-list-avds'
],
stdout:
'existing-avd-1'
,
),
const
FakeCommand
(
command:
<
String
>[
'avdmanager'
,
'list'
,
'device'
,
'-c'
],
stdout:
'test
\n
test2
\n
pixel
\n
pixel-xl
\n
'
,
),
kListEmulatorsCommand
,
const
FakeCommand
(
command:
<
String
>[
'avdmanager'
,
'create'
,
'avd'
,
'-n'
,
'flutter_emulator'
,
'-k'
,
'system-images;android-27;google_apis_playstore;x86'
,
'-d'
,
'pixel'
,
],
)
]),
androidSdk:
mockSdk
,
androidWorkflow:
AndroidWorkflow
(
androidSdk:
mockSdk
,
),
);
final
CreateEmulatorResult
result
=
await
emulatorManager
.
createEmulator
();
expect
(
result
.
success
,
true
);
});
});
testUsingContext
(
'create emulator with a unique name does not throw'
,
()
async
{
testWithoutContext
(
'create emulator with a unique name does not throw'
,
()
async
{
final
CreateEmulatorResult
res
=
final
EmulatorManager
emulatorManager
=
EmulatorManager
(
await
emulatorManager
.
createEmulator
(
name:
'test'
);
fileSystem:
MemoryFileSystem
.
test
(),
expect
(
res
.
success
,
equals
(
true
));
logger:
BufferLogger
.
test
(),
},
overrides:
<
Type
,
Generator
>{
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
ProcessManager:
()
=>
mockProcessManager
,
const
FakeCommand
(
Config:
()
=>
mockConfig
,
command:
<
String
>[
'avdmanager'
,
'list'
,
'device'
,
'-c'
],
AndroidSdk:
()
=>
mockSdk
,
stdout:
'test
\n
test2
\n
pixel
\n
pixel-xl
\n
'
,
),
kListEmulatorsCommand
,
const
FakeCommand
(
command:
<
String
>[
'avdmanager'
,
'create'
,
'avd'
,
// The specified name is given with the -n flag.
'-n'
,
'test'
,
'-k'
,
'system-images;android-27;google_apis_playstore;x86'
,
'-d'
,
'pixel'
,
],
)
]),
androidSdk:
mockSdk
,
androidWorkflow:
AndroidWorkflow
(
androidSdk:
mockSdk
,
),
);
final
CreateEmulatorResult
result
=
await
emulatorManager
.
createEmulator
(
name:
'test'
);
expect
(
result
.
success
,
true
);
});
});
testUsingContext
(
'create emulator with an existing name errors'
,
()
async
{
testWithoutContext
(
'create emulator with an existing name errors'
,
()
async
{
final
CreateEmulatorResult
res
=
final
EmulatorManager
emulatorManager
=
EmulatorManager
(
await
emulatorManager
.
createEmulator
(
name:
'existing-avd-1'
);
fileSystem:
MemoryFileSystem
.
test
(),
expect
(
res
.
success
,
equals
(
false
));
logger:
BufferLogger
.
test
(),
},
overrides:
<
Type
,
Generator
>{
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
ProcessManager:
()
=>
mockProcessManager
,
const
FakeCommand
(
Config:
()
=>
mockConfig
,
command:
<
String
>[
'avdmanager'
,
'list'
,
'device'
,
'-c'
],
AndroidSdk:
()
=>
mockSdk
,
stdout:
'test
\n
test2
\n
pixel
\n
pixel-xl
\n
'
,
),
kListEmulatorsCommand
,
const
FakeCommand
(
command:
<
String
>[
'avdmanager'
,
'create'
,
'avd'
,
'-n'
,
'existing-avd-1'
,
'-k'
,
'system-images;android-27;google_apis_playstore;x86'
,
'-d'
,
'pixel'
,
],
exitCode:
1
,
stderr:
"Error: Android Virtual Device 'existing-avd-1' already exists.
\n
"
'Use --force if you want to replace it.'
)
]),
androidSdk:
mockSdk
,
androidWorkflow:
AndroidWorkflow
(
androidSdk:
mockSdk
,
),
);
final
CreateEmulatorResult
result
=
await
emulatorManager
.
createEmulator
(
name:
'existing-avd-1'
);
expect
(
result
.
success
,
false
);
});
});
// iOS discovery uses context.
testUsingContext
(
'create emulator without a name but when default exists adds a suffix'
,
()
async
{
testUsingContext
(
'create emulator without a name but when default exists adds a suffix'
,
()
async
{
// First will get default name.
final
EmulatorManager
emulatorManager
=
EmulatorManager
(
CreateEmulatorResult
res
=
await
emulatorManager
.
createEmulator
();
fileSystem:
MemoryFileSystem
.
test
(),
expect
(
res
.
success
,
equals
(
true
));
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
final
String
defaultName
=
res
.
emulatorName
;
const
FakeCommand
(
command:
<
String
>[
'emulator'
,
'-list-avds'
],
// Second...
stdout:
'existing-avd-1
\n
flutter_emulator'
,
res
=
await
emulatorManager
.
createEmulator
();
),
expect
(
res
.
success
,
equals
(
true
));
const
FakeCommand
(
expect
(
res
.
emulatorName
,
equals
(
'
${defaultName}
_2'
));
command:
<
String
>[
'avdmanager'
,
'list'
,
'device'
,
'-c'
],
stdout:
'test
\n
test2
\n
pixel
\n
pixel-xl
\n
'
,
// Third...
),
res
=
await
emulatorManager
.
createEmulator
();
kListEmulatorsCommand
,
expect
(
res
.
success
,
equals
(
true
));
const
FakeCommand
(
expect
(
res
.
emulatorName
,
equals
(
'
${defaultName}
_3'
));
command:
<
String
>[
},
overrides:
<
Type
,
Generator
>{
'avdmanager'
,
ProcessManager:
()
=>
mockProcessManager
,
'create'
,
Config:
()
=>
mockConfig
,
'avd'
,
AndroidSdk:
()
=>
mockSdk
,
// a "_2" suffix is added to disambiguate from the existing emulator.
'-n'
,
'flutter_emulator_2'
,
'-k'
,
'system-images;android-27;google_apis_playstore;x86'
,
'-d'
,
'pixel'
,
],
)
]),
androidSdk:
mockSdk
,
androidWorkflow:
AndroidWorkflow
(
androidSdk:
mockSdk
,
),
);
final
CreateEmulatorResult
result
=
await
emulatorManager
.
createEmulator
();
expect
(
result
.
success
,
true
);
expect
(
result
.
emulatorName
,
'flutter_emulator_2'
);
});
});
});
});
...
@@ -142,7 +288,6 @@ void main() {
...
@@ -142,7 +288,6 @@ void main() {
expect
(
didAttemptToRunSimulator
,
equals
(
true
));
expect
(
didAttemptToRunSimulator
,
equals
(
true
));
},
overrides:
<
Type
,
Generator
>{
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
ProcessManager:
()
=>
mockProcessManager
,
Config:
()
=>
mockConfig
,
Xcode:
()
=>
mockXcode
,
Xcode:
()
=>
mockXcode
,
});
});
});
});
...
@@ -159,8 +304,8 @@ class TestEmulatorManager extends EmulatorManager {
...
@@ -159,8 +304,8 @@ class TestEmulatorManager extends EmulatorManager {
}
}
}
}
class
_Mock
Emulator
extends
Emulator
{
class
Fake
Emulator
extends
Emulator
{
const
_Mock
Emulator
(
String
id
,
this
.
name
,
this
.
manufacturer
)
const
Fake
Emulator
(
String
id
,
this
.
name
,
this
.
manufacturer
)
:
super
(
id
,
true
);
:
super
(
id
,
true
);
@override
@override
...
@@ -181,20 +326,7 @@ class _MockEmulator extends Emulator {
...
@@ -181,20 +326,7 @@ class _MockEmulator extends Emulator {
}
}
}
}
class
MockConfig
extends
Mock
implements
Config
{}
class
MockProcessManager
extends
Mock
implements
ProcessManager
{
class
MockProcessManager
extends
Mock
implements
ProcessManager
{
/// We have to send a command that fails in order to get the list of valid
/// system images paths. This is an example of the output to use in the mock.
static
const
String
mockCreateFailureOutput
=
'Error: Package path (-k) not specified. Valid system image paths are:
\n
'
'system-images;android-27;google_apis;x86
\n
'
'system-images;android-P;google_apis;x86
\n
'
'system-images;android-27;google_apis_playstore;x86
\n
'
'null
\n
'
;
// Yep, these really end with null (on dantup's machine at least)
static
const
ListEquality
<
String
>
_equality
=
ListEquality
<
String
>();
final
List
<
String
>
_existingAvds
=
<
String
>[
'existing-avd-1'
];
@override
@override
ProcessResult
runSync
(
ProcessResult
runSync
(
...
@@ -212,51 +344,9 @@ class MockProcessManager extends Mock implements ProcessManager {
...
@@ -212,51 +344,9 @@ class MockProcessManager extends Mock implements ProcessManager {
case
'/usr/bin/xcode-select'
:
case
'/usr/bin/xcode-select'
:
throw
ProcessException
(
program
,
args
);
throw
ProcessException
(
program
,
args
);
break
;
break
;
case
'emulator'
:
return
_handleEmulator
(
args
);
case
'avdmanager'
:
return
_handleAvdManager
(
args
);
}
}
throw
StateError
(
'Unexpected process call:
$command
'
);
throw
StateError
(
'Unexpected process call:
$command
'
);
}
}
ProcessResult
_handleEmulator
(
List
<
String
>
args
)
{
if
(
_equality
.
equals
(
args
,
<
String
>[
'-list-avds'
]))
{
return
ProcessResult
(
101
,
0
,
'
${_existingAvds.join('\n')}
\n
'
,
''
);
}
throw
ProcessException
(
'emulator'
,
args
);
}
ProcessResult
_handleAvdManager
(
List
<
String
>
args
)
{
if
(
_equality
.
equals
(
args
,
<
String
>[
'list'
,
'device'
,
'-c'
]))
{
return
ProcessResult
(
101
,
0
,
'test
\n
test2
\n
pixel
\n
pixel-xl
\n
'
,
''
);
}
if
(
_equality
.
equals
(
args
,
<
String
>[
'create'
,
'avd'
,
'-n'
,
'temp'
]))
{
return
ProcessResult
(
101
,
1
,
''
,
mockCreateFailureOutput
);
}
if
(
args
.
length
==
8
&&
_equality
.
equals
(
args
,
<
String
>[
'create'
,
'avd'
,
'-n'
,
args
[
3
],
'-k'
,
args
[
5
],
'-d'
,
args
[
7
]]))
{
// In order to support testing auto generation of names we need to support
// tracking any created emulators and reject when they already exist so this
// mock will compare the name of the AVD being created with the fake existing
// list and either reject if it exists, or add it to the list and return success.
final
String
name
=
args
[
3
];
// Error if this AVD already existed
if
(
_existingAvds
.
contains
(
name
))
{
return
ProcessResult
(
101
,
1
,
''
,
"Error: Android Virtual Device '
$name
' already exists.
\n
"
'Use --force if you want to replace it.'
);
}
else
{
_existingAvds
.
add
(
name
);
return
ProcessResult
(
101
,
0
,
''
,
''
);
}
}
throw
ProcessException
(
'emulator'
,
args
);
}
}
}
class
MockXcode
extends
Mock
implements
Xcode
{}
class
MockXcode
extends
Mock
implements
Xcode
{}
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