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
359daf4f
Unverified
Commit
359daf4f
authored
4 years ago
by
Jonah Williams
Committed by
GitHub
4 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flutter_tools] reland: remove globals from android device/testing (#57614)
parent
478d4c9c
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
606 additions
and
557 deletions
+606
-557
android_console.dart
packages/flutter_tools/lib/src/android/android_console.dart
+4
-4
android_device.dart
packages/flutter_tools/lib/src/android/android_device.dart
+114
-87
android_device_discovery.dart
...utter_tools/lib/src/android/android_device_discovery.dart
+24
-2
android_device_discovery_test.dart
.../general.shard/android/android_device_discovery_test.dart
+33
-4
android_device_start_test.dart
...test/general.shard/android/android_device_start_test.dart
+26
-21
android_device_stop_test.dart
.../test/general.shard/android/android_device_stop_test.dart
+15
-1
android_device_test.dart
...tools/test/general.shard/android/android_device_test.dart
+323
-383
android_emulator_test.dart
...ols/test/general.shard/android/android_emulator_test.dart
+1
-2
android_install_test.dart
...ools/test/general.shard/android/android_install_test.dart
+66
-53
No files found.
packages/flutter_tools/lib/src/android/android_console.dart
View file @
359daf4f
...
...
@@ -5,19 +5,19 @@
import
'dart:async'
;
import
'package:async/async.dart'
;
import
'../base/context.dart'
;
import
'../base/io.dart'
;
import
'../convert.dart'
;
/// Default factory that creates a real Android console connection.
final
AndroidConsoleSocketFactory
_kAndroidConsoleSocketFactory
=
(
String
host
,
int
port
)
=>
Socket
.
connect
(
host
,
port
);
///
/// The default implementation will create real connections to a device.
/// Override this in tests with an implementation that returns mock responses.
Future
<
Socket
>
kAndroidConsoleSocketFactory
(
String
host
,
int
port
)
=>
Socket
.
connect
(
host
,
port
);
/// Currently active implementation of the AndroidConsoleFactory.
///
/// The default implementation will create real connections to a device.
/// Override this in tests with an implementation that returns mock responses.
AndroidConsoleSocketFactory
get
androidConsoleSocketFactory
=>
context
.
get
<
AndroidConsoleSocketFactory
>()
??
_kAndroidConsoleSocketFactory
;
typedef
AndroidConsoleSocketFactory
=
Future
<
Socket
>
Function
(
String
host
,
int
port
);
/// Creates a console connection to an Android emulator that can be used to run
...
...
This diff is collapsed.
Click to expand it.
packages/flutter_tools/lib/src/android/android_device.dart
View file @
359daf4f
...
...
@@ -14,11 +14,11 @@ import '../base/common.dart' show throwToolExit, unawaited;
import
'../base/file_system.dart'
;
import
'../base/io.dart'
;
import
'../base/logger.dart'
;
import
'../base/platform.dart'
;
import
'../base/process.dart'
;
import
'../build_info.dart'
;
import
'../convert.dart'
;
import
'../device.dart'
;
import
'../globals.dart'
as
globals
;
import
'../project.dart'
;
import
'../protocol_discovery.dart'
;
...
...
@@ -46,11 +46,11 @@ const Map<String, _HardwareType> _kKnownHardware = <String, _HardwareType>{
'samsungexynos7570'
:
_HardwareType
.
physical
,
};
bool
allowHeapCorruptionOnWindows
(
int
exitCode
)
{
bool
allowHeapCorruptionOnWindows
(
int
exitCode
,
Platform
platform
)
{
// In platform tools 29.0.0 adb.exe seems to be ending with this heap
// corruption error code on seemingly successful termination.
// So we ignore this error on Windows.
return
exitCode
==
-
1073740940
&&
globals
.
platform
.
isWindows
;
return
exitCode
==
-
1073740940
&&
platform
.
isWindows
;
}
class
AndroidDevice
extends
Device
{
...
...
@@ -59,12 +59,36 @@ class AndroidDevice extends Device {
this
.
productID
,
this
.
modelID
,
this
.
deviceCodeName
,
})
:
super
(
id
,
category:
Category
.
mobile
,
platformType:
PlatformType
.
android
,
ephemeral:
true
,
);
@required
Logger
logger
,
@required
ProcessManager
processManager
,
@required
Platform
platform
,
@required
AndroidSdk
androidSdk
,
@required
FileSystem
fileSystem
,
TimeoutConfiguration
timeoutConfiguration
=
const
TimeoutConfiguration
(),
AndroidConsoleSocketFactory
androidConsoleSocketFactory
=
kAndroidConsoleSocketFactory
,
})
:
_logger
=
logger
,
_processManager
=
processManager
,
_androidSdk
=
androidSdk
,
_platform
=
platform
,
_fileSystem
=
fileSystem
,
_androidConsoleSocketFactory
=
androidConsoleSocketFactory
,
_timeoutConfiguration
=
timeoutConfiguration
,
_processUtils
=
ProcessUtils
(
logger:
logger
,
processManager:
processManager
),
super
(
id
,
category:
Category
.
mobile
,
platformType:
PlatformType
.
android
,
ephemeral:
true
,
);
final
Logger
_logger
;
final
ProcessManager
_processManager
;
final
AndroidSdk
_androidSdk
;
final
Platform
_platform
;
final
FileSystem
_fileSystem
;
final
ProcessUtils
_processUtils
;
final
AndroidConsoleSocketFactory
_androidConsoleSocketFactory
;
final
TimeoutConfiguration
_timeoutConfiguration
;
final
String
productID
;
final
String
modelID
;
...
...
@@ -72,31 +96,31 @@ class AndroidDevice extends Device {
Map
<
String
,
String
>
_properties
;
bool
_isLocalEmulator
;
TargetPlatform
_
p
latform
;
TargetPlatform
_
applicationP
latform
;
Future
<
String
>
_getProperty
(
String
name
)
async
{
if
(
_properties
==
null
)
{
_properties
=
<
String
,
String
>{};
final
List
<
String
>
propCommand
=
adbCommandForDevice
(<
String
>[
'shell'
,
'getprop'
]);
globals
.
printTrace
(
propCommand
.
join
(
' '
));
_logger
.
printTrace
(
propCommand
.
join
(
' '
));
try
{
// We pass an encoding of latin1 so that we don't try and interpret the
// `adb shell getprop` result as UTF8.
final
ProcessResult
result
=
await
globals
.
processManager
.
run
(
final
ProcessResult
result
=
await
_
processManager
.
run
(
propCommand
,
stdoutEncoding:
latin1
,
stderrEncoding:
latin1
,
);
if
(
result
.
exitCode
==
0
||
allowHeapCorruptionOnWindows
(
result
.
exitCode
))
{
if
(
result
.
exitCode
==
0
||
allowHeapCorruptionOnWindows
(
result
.
exitCode
,
_platform
))
{
_properties
=
parseAdbDeviceProperties
(
result
.
stdout
as
String
);
}
else
{
globals
.
printError
(
'Error
${result.exitCode}
retrieving device properties for
$name
:'
);
globals
.
printError
(
result
.
stderr
as
String
);
_logger
.
printError
(
'Error
${result.exitCode}
retrieving device properties for
$name
:'
);
_logger
.
printError
(
result
.
stderr
as
String
);
}
}
on
ProcessException
catch
(
error
)
{
globals
.
printError
(
'Error retrieving device properties for
$name
:
$error
'
);
_logger
.
printError
(
'Error retrieving device properties for
$name
:
$error
'
);
}
}
...
...
@@ -107,14 +131,14 @@ class AndroidDevice extends Device {
Future
<
bool
>
get
isLocalEmulator
async
{
if
(
_isLocalEmulator
==
null
)
{
final
String
hardware
=
await
_getProperty
(
'ro.hardware'
);
globals
.
printTrace
(
'ro.hardware =
$hardware
'
);
_logger
.
printTrace
(
'ro.hardware =
$hardware
'
);
if
(
_kKnownHardware
.
containsKey
(
hardware
))
{
// Look for known hardware models.
_isLocalEmulator
=
_kKnownHardware
[
hardware
]
==
_HardwareType
.
emulator
;
}
else
{
// Fall back to a best-effort heuristic-based approach.
final
String
characteristics
=
await
_getProperty
(
'ro.build.characteristics'
);
globals
.
printTrace
(
'ro.build.characteristics =
$characteristics
'
);
_logger
.
printTrace
(
'ro.build.characteristics =
$characteristics
'
);
_isLocalEmulator
=
characteristics
!=
null
&&
characteristics
.
contains
(
'emulator'
);
}
}
...
...
@@ -144,27 +168,27 @@ class AndroidDevice extends Device {
const
String
host
=
'localhost'
;
final
int
port
=
int
.
parse
(
portMatch
.
group
(
1
));
globals
.
printTrace
(
'Fetching avd name for
$name
via Android console on
$host
:
$port
'
);
_logger
.
printTrace
(
'Fetching avd name for
$name
via Android console on
$host
:
$port
'
);
try
{
final
Socket
socket
=
await
androidConsoleSocketFactory
(
host
,
port
);
final
Socket
socket
=
await
_
androidConsoleSocketFactory
(
host
,
port
);
final
AndroidConsole
console
=
AndroidConsole
(
socket
);
try
{
await
console
.
connect
()
.
timeout
(
timeoutConfiguration
.
fastOperation
,
.
timeout
(
_
timeoutConfiguration
.
fastOperation
,
onTimeout:
()
=>
throw
TimeoutException
(
'Connection timed out'
));
return
await
console
.
getAvdName
()
.
timeout
(
timeoutConfiguration
.
fastOperation
,
.
timeout
(
_
timeoutConfiguration
.
fastOperation
,
onTimeout:
()
=>
throw
TimeoutException
(
'"avd name" timed out'
));
}
finally
{
console
.
destroy
();
}
}
on
Exception
catch
(
e
)
{
globals
.
printTrace
(
'Failed to fetch avd name for emulator at
$host
:
$port
:
$e
'
);
_logger
.
printTrace
(
'Failed to fetch avd name for emulator at
$host
:
$port
:
$e
'
);
// If we fail to connect to the device, we should not fail so just return
// an empty name. This data is best-effort.
return
null
;
...
...
@@ -173,7 +197,7 @@ class AndroidDevice extends Device {
@override
Future
<
TargetPlatform
>
get
targetPlatform
async
{
if
(
_
p
latform
==
null
)
{
if
(
_
applicationP
latform
==
null
)
{
// http://developer.android.com/ndk/guides/abis.html (x86, armeabi-v7a, ...)
switch
(
await
_getProperty
(
'ro.product.cpu.abi'
))
{
case
'arm64-v8a'
:
...
...
@@ -183,24 +207,24 @@ class AndroidDevice extends Device {
// to assuming 64 bit.
final
String
abilist
=
await
_getProperty
(
'ro.product.cpu.abilist'
);
if
(
abilist
==
null
||
abilist
.
contains
(
'arm64-v8a'
))
{
_
p
latform
=
TargetPlatform
.
android_arm64
;
_
applicationP
latform
=
TargetPlatform
.
android_arm64
;
}
else
{
_
p
latform
=
TargetPlatform
.
android_arm
;
_
applicationP
latform
=
TargetPlatform
.
android_arm
;
}
break
;
case
'x86_64'
:
_
p
latform
=
TargetPlatform
.
android_x64
;
_
applicationP
latform
=
TargetPlatform
.
android_x64
;
break
;
case
'x86'
:
_
p
latform
=
TargetPlatform
.
android_x86
;
_
applicationP
latform
=
TargetPlatform
.
android_x86
;
break
;
default
:
_
p
latform
=
TargetPlatform
.
android_arm
;
_
applicationP
latform
=
TargetPlatform
.
android_arm
;
break
;
}
}
return
_
p
latform
;
return
_
applicationP
latform
;
}
@override
...
...
@@ -217,7 +241,7 @@ class AndroidDevice extends Device {
AndroidDevicePortForwarder
_portForwarder
;
List
<
String
>
adbCommandForDevice
(
List
<
String
>
args
)
{
return
<
String
>[
getAdbPath
(
globals
.
androidSdk
),
'-s'
,
id
,
...
args
];
return
<
String
>[
getAdbPath
(
_
androidSdk
),
'-s'
,
id
,
...
args
];
}
String
runAdbCheckedSync
(
...
...
@@ -226,13 +250,13 @@ class AndroidDevice extends Device {
bool
allowReentrantFlutter
=
false
,
Map
<
String
,
String
>
environment
,
})
{
return
processUtils
.
runSync
(
return
_
processUtils
.
runSync
(
adbCommandForDevice
(
params
),
throwOnError:
true
,
workingDirectory:
workingDirectory
,
allowReentrantFlutter:
allowReentrantFlutter
,
environment:
environment
,
whiteListFailures:
allowHeapCorruptionOnWindows
,
whiteListFailures:
(
int
value
)
=>
allowHeapCorruptionOnWindows
(
value
,
_platform
)
,
).
stdout
.
trim
();
}
...
...
@@ -241,12 +265,12 @@ class AndroidDevice extends Device {
String
workingDirectory
,
bool
allowReentrantFlutter
=
false
,
})
async
{
return
processUtils
.
run
(
return
_
processUtils
.
run
(
adbCommandForDevice
(
params
),
throwOnError:
true
,
workingDirectory:
workingDirectory
,
allowReentrantFlutter:
allowReentrantFlutter
,
whiteListFailures:
allowHeapCorruptionOnWindows
,
whiteListFailures:
(
int
value
)
=>
allowHeapCorruptionOnWindows
(
value
,
_platform
)
,
);
}
...
...
@@ -268,27 +292,27 @@ class AndroidDevice extends Device {
}
return
false
;
}
globals
.
printError
(
_logger
.
printError
(
'Unrecognized adb version string
$adbVersion
. Skipping version check.'
);
return
true
;
}
Future
<
bool
>
_checkForSupportedAdbVersion
()
async
{
if
(
globals
.
androidSdk
==
null
)
{
if
(
_
androidSdk
==
null
)
{
return
false
;
}
try
{
final
RunResult
adbVersion
=
await
processUtils
.
run
(
<
String
>[
getAdbPath
(
globals
.
androidSdk
),
'version'
],
final
RunResult
adbVersion
=
await
_
processUtils
.
run
(
<
String
>[
getAdbPath
(
_
androidSdk
),
'version'
],
throwOnError:
true
,
);
if
(
_isValidAdbVersion
(
adbVersion
.
stdout
))
{
return
true
;
}
globals
.
printError
(
'The ADB at "
${getAdbPath(globals.
androidSdk)}
" is too old; please install version 1.0.39 or later.'
);
_logger
.
printError
(
'The ADB at "
${getAdbPath(_
androidSdk)}
" is too old; please install version 1.0.39 or later.'
);
}
on
Exception
catch
(
error
,
trace
)
{
globals
.
printError
(
'Error running ADB:
$error
'
,
stackTrace:
trace
);
_logger
.
printError
(
'Error running ADB:
$error
'
,
stackTrace:
trace
);
}
return
false
;
...
...
@@ -300,8 +324,8 @@ class AndroidDevice extends Device {
// output lines like this, which we want to ignore:
// adb server is out of date. killing..
// * daemon started successfully *
await
processUtils
.
run
(
<
String
>[
getAdbPath
(
globals
.
androidSdk
),
'start-server'
],
await
_
processUtils
.
run
(
<
String
>[
getAdbPath
(
_
androidSdk
),
'start-server'
],
throwOnError:
true
,
);
...
...
@@ -313,12 +337,12 @@ class AndroidDevice extends Device {
final
int
sdkVersionParsed
=
int
.
tryParse
(
sdkVersion
);
if
(
sdkVersionParsed
==
null
)
{
globals
.
printError
(
'Unexpected response from getprop: "
$sdkVersion
"'
);
_logger
.
printError
(
'Unexpected response from getprop: "
$sdkVersion
"'
);
return
false
;
}
if
(
sdkVersionParsed
<
minApiLevel
)
{
globals
.
printError
(
_logger
.
printError
(
'The Android version (
$sdkVersion
) on the target device is too old. Please '
'use a
$minVersionName
(version
$minApiLevel
/
$minVersionText
) device or later.'
);
return
false
;
...
...
@@ -326,8 +350,8 @@ class AndroidDevice extends Device {
return
true
;
}
on
Exception
catch
(
e
,
stacktrace
)
{
globals
.
printError
(
'Unexpected failure from adb:
$e
'
);
globals
.
printError
(
'Stacktrace:
$stacktrace
'
);
_logger
.
printError
(
'Unexpected failure from adb:
$e
'
);
_logger
.
printError
(
'Stacktrace:
$stacktrace
'
);
return
false
;
}
}
...
...
@@ -337,13 +361,13 @@ class AndroidDevice extends Device {
}
Future
<
String
>
_getDeviceApkSha1
(
AndroidApk
apk
)
async
{
final
RunResult
result
=
await
processUtils
.
run
(
final
RunResult
result
=
await
_
processUtils
.
run
(
adbCommandForDevice
(<
String
>[
'shell'
,
'cat'
,
_getDeviceSha1Path
(
apk
)]));
return
result
.
stdout
;
}
String
_getSourceSha1
(
AndroidApk
apk
)
{
final
File
shaFile
=
globals
.
fs
.
file
(
'
${apk.file.path}
.sha1'
);
final
File
shaFile
=
_fileSystem
.
file
(
'
${apk.file.path}
.sha1'
);
return
shaFile
.
existsSync
()
?
shaFile
.
readAsStringSync
()
:
''
;
}
...
...
@@ -357,7 +381,7 @@ class AndroidDevice extends Device {
final
RunResult
listOut
=
await
runAdbCheckedAsync
(<
String
>[
'shell'
,
'pm'
,
'list'
,
'packages'
,
app
.
id
]);
return
LineSplitter
.
split
(
listOut
.
stdout
).
contains
(
'package:
${app.id}
'
);
}
on
Exception
catch
(
error
)
{
globals
.
printTrace
(
'
$error
'
);
_logger
.
printTrace
(
'
$error
'
);
return
false
;
}
}
...
...
@@ -371,7 +395,7 @@ class AndroidDevice extends Device {
@override
Future
<
bool
>
installApp
(
AndroidApk
app
)
async
{
if
(!
app
.
file
.
existsSync
())
{
globals
.
printError
(
'"
${globals.fs
.path.relative(app.file.path)}
" does not exist.'
);
_logger
.
printError
(
'"
${_fileSystem
.path.relative(app.file.path)}
" does not exist.'
);
return
false
;
}
...
...
@@ -380,8 +404,11 @@ class AndroidDevice extends Device {
return
false
;
}
final
Status
status
=
globals
.
logger
.
startProgress
(
'Installing
${globals.fs.path.relative(app.file.path)}
...'
,
timeout:
timeoutConfiguration
.
slowOperation
);
final
RunResult
installResult
=
await
processUtils
.
run
(
final
Status
status
=
_logger
.
startProgress
(
'Installing
${_fileSystem.path.relative(app.file.path)}
...'
,
timeout:
_timeoutConfiguration
.
slowOperation
,
);
final
RunResult
installResult
=
await
_processUtils
.
run
(
adbCommandForDevice
(<
String
>[
'install'
,
'-t'
,
'-r'
,
app
.
file
.
path
]));
status
.
stop
();
// Some versions of adb exit with exit code 0 even on failure :(
...
...
@@ -389,12 +416,12 @@ class AndroidDevice extends Device {
final
RegExp
failureExp
=
RegExp
(
r'^Failure.*$'
,
multiLine:
true
);
final
String
failure
=
failureExp
.
stringMatch
(
installResult
.
stdout
);
if
(
failure
!=
null
)
{
globals
.
printError
(
'Package install error:
$failure
'
);
_logger
.
printError
(
'Package install error:
$failure
'
);
return
false
;
}
if
(
installResult
.
exitCode
!=
0
)
{
globals
.
printError
(
'Error: ADB exited with exit code
${installResult.exitCode}
'
);
globals
.
printError
(
'
$installResult
'
);
_logger
.
printError
(
'Error: ADB exited with exit code
${installResult.exitCode}
'
);
_logger
.
printError
(
'
$installResult
'
);
return
false
;
}
try
{
...
...
@@ -402,7 +429,7 @@ class AndroidDevice extends Device {
'shell'
,
'echo'
,
'-n'
,
_getSourceSha1
(
app
),
'>'
,
_getDeviceSha1Path
(
app
),
]);
}
on
ProcessException
catch
(
error
)
{
globals
.
printError
(
'adb shell failed to write the SHA hash:
$error
.'
);
_logger
.
printError
(
'adb shell failed to write the SHA hash:
$error
.'
);
return
false
;
}
return
true
;
...
...
@@ -417,19 +444,19 @@ class AndroidDevice extends Device {
String
uninstallOut
;
try
{
final
RunResult
uninstallResult
=
await
processUtils
.
run
(
final
RunResult
uninstallResult
=
await
_
processUtils
.
run
(
adbCommandForDevice
(<
String
>[
'uninstall'
,
app
.
id
]),
throwOnError:
true
,
);
uninstallOut
=
uninstallResult
.
stdout
;
}
on
Exception
catch
(
error
)
{
globals
.
printError
(
'adb uninstall failed:
$error
'
);
_logger
.
printError
(
'adb uninstall failed:
$error
'
);
return
false
;
}
final
RegExp
failureExp
=
RegExp
(
r'^Failure.*$'
,
multiLine:
true
);
final
String
failure
=
failureExp
.
stringMatch
(
uninstallOut
);
if
(
failure
!=
null
)
{
globals
.
printError
(
'Package uninstall error:
$failure
'
);
_logger
.
printError
(
'Package uninstall error:
$failure
'
);
return
false
;
}
...
...
@@ -440,21 +467,21 @@ class AndroidDevice extends Device {
final
bool
wasInstalled
=
await
isAppInstalled
(
package
);
if
(
wasInstalled
)
{
if
(
await
isLatestBuildInstalled
(
package
))
{
globals
.
printTrace
(
'Latest build already installed.'
);
_logger
.
printTrace
(
'Latest build already installed.'
);
return
true
;
}
}
globals
.
printTrace
(
'Installing APK.'
);
_logger
.
printTrace
(
'Installing APK.'
);
if
(!
await
installApp
(
package
))
{
globals
.
printTrace
(
'Warning: Failed to install APK.'
);
_logger
.
printTrace
(
'Warning: Failed to install APK.'
);
if
(
wasInstalled
)
{
globals
.
printStatus
(
'Uninstalling old version...'
);
_logger
.
printStatus
(
'Uninstalling old version...'
);
if
(!
await
uninstallApp
(
package
))
{
globals
.
printError
(
'Error: Uninstalling old version failed.'
);
_logger
.
printError
(
'Error: Uninstalling old version failed.'
);
return
false
;
}
if
(!
await
installApp
(
package
))
{
globals
.
printError
(
'Error: Failed to install APK again.'
);
_logger
.
printError
(
'Error: Failed to install APK again.'
);
return
false
;
}
return
true
;
...
...
@@ -484,7 +511,7 @@ class AndroidDevice extends Device {
final
TargetPlatform
devicePlatform
=
await
targetPlatform
;
if
(
devicePlatform
==
TargetPlatform
.
android_x86
&&
!
debuggingOptions
.
buildInfo
.
isDebug
)
{
globals
.
printError
(
'Profile and release builds are only supported on ARM/x64 targets.'
);
_logger
.
printError
(
'Profile and release builds are only supported on ARM/x64 targets.'
);
return
LaunchResult
.
failed
();
}
...
...
@@ -503,12 +530,12 @@ class AndroidDevice extends Device {
androidArch
=
AndroidArch
.
x86
;
break
;
default
:
globals
.
printError
(
'Android platforms are only supported.'
);
_logger
.
printError
(
'Android platforms are only supported.'
);
return
LaunchResult
.
failed
();
}
if
(!
prebuiltApplication
||
globals
.
androidSdk
.
licensesAvailable
&&
globals
.
androidSdk
.
latestVersion
==
null
)
{
globals
.
printTrace
(
'Building APK'
);
if
(!
prebuiltApplication
||
_androidSdk
.
licensesAvailable
&&
_
androidSdk
.
latestVersion
==
null
)
{
_logger
.
printTrace
(
'Building APK'
);
final
FlutterProject
project
=
FlutterProject
.
current
();
await
androidBuilder
.
buildApk
(
project:
project
,
...
...
@@ -528,7 +555,7 @@ class AndroidDevice extends Device {
throwToolExit
(
'Problem building Android application: see above error(s).'
);
}
globals
.
printTrace
(
"Stopping app '
${package.name}
' on
$name
."
);
_logger
.
printTrace
(
"Stopping app '
${package.name}
' on
$name
."
);
await
stopApp
(
package
);
if
(!
await
_installLatestApp
(
package
))
{
...
...
@@ -536,7 +563,7 @@ class AndroidDevice extends Device {
}
final
bool
traceStartup
=
platformArgs
[
'trace-startup'
]
as
bool
??
false
;
globals
.
printTrace
(
'
$this
startApp'
);
_logger
.
printTrace
(
'
$this
startApp'
);
ProtocolDiscovery
observatoryDiscovery
;
...
...
@@ -601,7 +628,7 @@ class AndroidDevice extends Device {
final
String
result
=
(
await
runAdbCheckedAsync
(
cmd
)).
stdout
;
// This invocation returns 0 even when it fails.
if
(
result
.
contains
(
'Error: '
))
{
globals
.
printError
(
result
.
trim
(),
wrap:
false
);
_logger
.
printError
(
result
.
trim
(),
wrap:
false
);
return
LaunchResult
.
failed
();
}
...
...
@@ -612,7 +639,7 @@ class AndroidDevice extends Device {
// Wait for the service protocol port here. This will complete once the
// device has printed "Observatory is listening on...".
globals
.
printTrace
(
'Waiting for observatory port to be available...'
);
_logger
.
printTrace
(
'Waiting for observatory port to be available...'
);
// TODO(danrubel): Waiting for observatory services can be made common across all devices.
try
{
...
...
@@ -620,7 +647,7 @@ class AndroidDevice extends Device {
if
(
debuggingOptions
.
buildInfo
.
isDebug
||
debuggingOptions
.
buildInfo
.
isProfile
)
{
observatoryUri
=
await
observatoryDiscovery
.
uri
;
if
(
observatoryUri
==
null
)
{
globals
.
printError
(
_logger
.
printError
(
'Error waiting for a debug connection: '
'The log reader stopped unexpectedly'
,
);
...
...
@@ -629,7 +656,7 @@ class AndroidDevice extends Device {
}
return
LaunchResult
.
succeeded
(
observatoryUri:
observatoryUri
);
}
on
Exception
catch
(
error
)
{
globals
.
printError
(
'Error waiting for a debug connection:
$error
'
);
_logger
.
printError
(
'Error waiting for a debug connection:
$error
'
);
return
LaunchResult
.
failed
();
}
finally
{
await
observatoryDiscovery
.
cancel
();
...
...
@@ -651,13 +678,13 @@ class AndroidDevice extends Device {
return
Future
<
bool
>.
value
(
false
);
}
final
List
<
String
>
command
=
adbCommandForDevice
(<
String
>[
'shell'
,
'am'
,
'force-stop'
,
app
.
id
]);
return
processUtils
.
stream
(
command
).
then
<
bool
>(
(
int
exitCode
)
=>
exitCode
==
0
||
allowHeapCorruptionOnWindows
(
exitCode
));
return
_
processUtils
.
stream
(
command
).
then
<
bool
>(
(
int
exitCode
)
=>
exitCode
==
0
||
allowHeapCorruptionOnWindows
(
exitCode
,
_platform
));
}
@override
Future
<
MemoryInfo
>
queryMemoryInfo
()
async
{
final
RunResult
runResult
=
await
processUtils
.
run
(
adbCommandForDevice
(<
String
>[
final
RunResult
runResult
=
await
_
processUtils
.
run
(
adbCommandForDevice
(<
String
>[
'shell'
,
'dumpsys'
,
'meminfo'
,
...
...
@@ -673,7 +700,7 @@ class AndroidDevice extends Device {
@override
void
clearLogs
()
{
processUtils
.
runSync
(
adbCommandForDevice
(<
String
>[
'logcat'
,
'-c'
]));
_
processUtils
.
runSync
(
adbCommandForDevice
(<
String
>[
'logcat'
,
'-c'
]));
}
@override
...
...
@@ -685,23 +712,23 @@ class AndroidDevice extends Device {
if
(
includePastLogs
)
{
return
_pastLogReader
??=
await
AdbLogReader
.
createLogReader
(
this
,
globals
.
processManager
,
_
processManager
,
includePastLogs:
true
,
);
}
else
{
return
_logReader
??=
await
AdbLogReader
.
createLogReader
(
this
,
globals
.
processManager
,
_
processManager
,
);
}
}
@override
DevicePortForwarder
get
portForwarder
=>
_portForwarder
??=
AndroidDevicePortForwarder
(
processManager:
globals
.
processManager
,
logger:
globals
.
logger
,
processManager:
_
processManager
,
logger:
_
logger
,
deviceId:
id
,
adbPath:
globals
.
androidSdk
.
adbPath
,
adbPath:
_
androidSdk
.
adbPath
,
);
static
final
RegExp
_timeRegExp
=
RegExp
(
r'^\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}'
,
multiLine:
true
);
...
...
@@ -715,7 +742,7 @@ class AndroidDevice extends Device {
'shell'
,
'-x'
,
'logcat'
,
'-v'
,
'time'
,
'-t'
,
'1'
]);
}
on
Exception
catch
(
error
)
{
globals
.
printError
(
'Failed to extract the most recent timestamp from the Android log:
$error
.'
);
_logger
.
printError
(
'Failed to extract the most recent timestamp from the Android log:
$error
.'
);
return
null
;
}
final
Match
timeMatch
=
_timeRegExp
.
firstMatch
(
output
);
...
...
@@ -732,7 +759,7 @@ class AndroidDevice extends Device {
Future
<
void
>
takeScreenshot
(
File
outputFile
)
async
{
const
String
remotePath
=
'/data/local/tmp/flutter_screenshot.png'
;
await
runAdbCheckedAsync
(<
String
>[
'shell'
,
'screencap'
,
'-p'
,
remotePath
]);
await
processUtils
.
run
(
await
_
processUtils
.
run
(
adbCommandForDevice
(<
String
>[
'pull'
,
remotePath
,
outputFile
.
path
]),
throwOnError:
true
,
);
...
...
This diff is collapsed.
Click to expand it.
packages/flutter_tools/lib/src/android/android_device_discovery.dart
View file @
359daf4f
...
...
@@ -6,8 +6,10 @@ import 'package:meta/meta.dart';
import
'package:process/process.dart'
;
import
'../base/common.dart'
;
import
'../base/file_system.dart'
;
import
'../base/io.dart'
;
import
'../base/logger.dart'
;
import
'../base/platform.dart'
;
import
'../base/process.dart'
;
import
'../device.dart'
;
import
'../globals.dart'
as
globals
;
...
...
@@ -63,7 +65,11 @@ class AndroidDevices extends PollingDeviceDiscovery {
'ANDROID_HOME environment variable:
${exception.executable}
'
);
}
final
List
<
AndroidDevice
>
devices
=
<
AndroidDevice
>[];
parseADBDeviceOutput
(
text
,
devices:
devices
);
parseADBDeviceOutput
(
text
,
devices:
devices
,
timeoutConfiguration:
timeoutConfiguration
,
);
return
devices
;
}
...
...
@@ -80,7 +86,11 @@ class AndroidDevices extends PollingDeviceDiscovery {
}
else
{
final
String
text
=
result
.
stdout
;
final
List
<
String
>
diagnostics
=
<
String
>[];
parseADBDeviceOutput
(
text
,
diagnostics:
diagnostics
);
parseADBDeviceOutput
(
text
,
diagnostics:
diagnostics
,
timeoutConfiguration:
timeoutConfiguration
,
);
return
diagnostics
;
}
}
...
...
@@ -96,6 +106,12 @@ class AndroidDevices extends PollingDeviceDiscovery {
String
text
,
{
List
<
AndroidDevice
>
devices
,
List
<
String
>
diagnostics
,
AndroidSdk
androidSdk
,
FileSystem
fileSystem
,
Logger
logger
,
Platform
platform
,
ProcessManager
processManager
,
@required
TimeoutConfiguration
timeoutConfiguration
,
})
{
// Check for error messages from adb
if
(!
text
.
contains
(
'List of devices'
))
{
...
...
@@ -154,6 +170,12 @@ class AndroidDevices extends PollingDeviceDiscovery {
productID:
info
[
'product'
],
modelID:
info
[
'model'
]
??
deviceID
,
deviceCodeName:
info
[
'device'
],
androidSdk:
androidSdk
??
globals
.
androidSdk
,
fileSystem:
fileSystem
??
globals
.
fs
,
logger:
logger
??
globals
.
logger
,
platform:
platform
??
globals
.
platform
,
processManager:
processManager
??
globals
.
processManager
,
timeoutConfiguration:
timeoutConfiguration
,
));
}
}
else
{
...
...
This diff is collapsed.
Click to expand it.
packages/flutter_tools/test/general.shard/android/android_device_discovery_test.dart
View file @
359daf4f
...
...
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/android/android_device.dart'
;
import
'package:flutter_tools/src/android/android_device_discovery.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/logger.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:mockito/mockito.dart'
;
...
...
@@ -69,7 +71,15 @@ void main() {
List of devices attached
05a02bac device usb:336592896X product:razor model:Nexus_7 device:flo
'''
,
devices:
devices
);
'''
,
devices:
devices
,
androidSdk:
MockAndroidSdk
(),
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
any
(),
timeoutConfiguration:
const
TimeoutConfiguration
(),
platform:
FakePlatform
(),
fileSystem:
MemoryFileSystem
.
test
(),
);
expect
(
devices
,
hasLength
(
1
));
expect
(
devices
.
first
.
name
,
'Nexus 7'
);
...
...
@@ -84,7 +94,15 @@ localhost:36790 device
0149947A0D01500C device usb:340787200X
emulator-5612 host features:shell_2
'''
,
devices:
devices
);
'''
,
devices:
devices
,
androidSdk:
MockAndroidSdk
(),
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
any
(),
timeoutConfiguration:
const
TimeoutConfiguration
(),
platform:
FakePlatform
(),
fileSystem:
MemoryFileSystem
.
test
(),
);
expect
(
devices
,
hasLength
(
3
));
expect
(
devices
.
first
.
name
,
'localhost:36790'
);
...
...
@@ -95,7 +113,15 @@ emulator-5612 host features:shell_2
AndroidDevices
.
parseADBDeviceOutput
(
'''
List of devices attached
ZX1G22JJWR device usb:3-3 product:shamu model:Nexus_6 device:shamu features:cmd,shell_v2
'''
,
devices:
devices
);
'''
,
devices:
devices
,
androidSdk:
MockAndroidSdk
(),
logger:
BufferLogger
.
test
(),
processManager:
FakeProcessManager
.
any
(),
timeoutConfiguration:
const
TimeoutConfiguration
(),
platform:
FakePlatform
(),
fileSystem:
MemoryFileSystem
.
test
(),
);
expect
(
devices
,
hasLength
(
1
));
expect
(
devices
.
first
.
name
,
'Nexus 6'
);
...
...
@@ -108,7 +134,10 @@ ZX1G22JJWR device usb:3-3 product:shamu model:Nexus_6 device:shamu f
It appears you do not have '
Android
SDK
Platform
-
tools
' installed.
Use the '
android
' tool to install them:
android update sdk --no-ui --filter '
platform
-
tools
'
'''
,
devices:
devices
,
diagnostics:
diagnostics
);
'''
,
devices:
devices
,
diagnostics:
diagnostics
,
timeoutConfiguration:
const
TimeoutConfiguration
(),
);
expect
(
devices
,
isEmpty
);
expect
(
diagnostics
,
hasLength
(
1
));
...
...
This diff is collapsed.
Click to expand it.
packages/flutter_tools/test/general.shard/android/android_device_start_test.dart
View file @
359daf4f
...
...
@@ -7,6 +7,8 @@ import 'package:flutter_tools/src/android/android_device.dart';
import
'package:flutter_tools/src/android/android_sdk.dart'
;
import
'package:flutter_tools/src/application_package.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/build_info.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:mockito/mockito.dart'
;
...
...
@@ -55,11 +57,16 @@ void main() {
TargetPlatform
.
android_arm64
,
TargetPlatform
.
android_x64
,
])
{
testUsingContext
(
'AndroidDevice.startApp allows release builds on
$targetPlatform
'
,
()
async
{
const
String
deviceId
=
'1234'
;
testWithoutContext
(
'AndroidDevice.startApp allows release builds on
$targetPlatform
'
,
()
async
{
final
String
arch
=
getNameForAndroidArch
(
getAndroidArchForName
(
getNameForTargetPlatform
(
targetPlatform
)));
final
AndroidDevice
device
=
AndroidDevice
(
deviceId
,
modelID:
'TestModel'
);
final
AndroidDevice
device
=
AndroidDevice
(
'1234'
,
modelID:
'TestModel'
,
fileSystem:
fileSystem
,
processManager:
processManager
,
logger:
BufferLogger
.
test
(),
platform:
FakePlatform
(
operatingSystem:
'linux'
),
androidSdk:
androidSdk
,
);
final
File
apkFile
=
fileSystem
.
file
(
'app.apk'
)..
createSync
();
final
AndroidApk
apk
=
AndroidApk
(
id:
'FlutterApp'
,
...
...
@@ -117,16 +124,17 @@ void main() {
expect
(
launchResult
.
started
,
true
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
},
overrides:
<
Type
,
Generator
>{
AndroidSdk:
()
=>
androidSdk
,
FileSystem:
()
=>
fileSystem
,
ProcessManager:
()
=>
processManager
,
});
}
testUsingContext
(
'AndroidDevice.startApp does not allow release builds on x86'
,
()
async
{
const
String
deviceId
=
'1234'
;
final
AndroidDevice
device
=
AndroidDevice
(
deviceId
,
modelID:
'TestModel'
);
testWithoutContext
(
'AndroidDevice.startApp does not allow release builds on x86'
,
()
async
{
final
AndroidDevice
device
=
AndroidDevice
(
'1234'
,
modelID:
'TestModel'
,
fileSystem:
fileSystem
,
processManager:
processManager
,
logger:
BufferLogger
.
test
(),
platform:
FakePlatform
(
operatingSystem:
'linux'
),
androidSdk:
androidSdk
,
);
final
File
apkFile
=
fileSystem
.
file
(
'app.apk'
)..
createSync
();
final
AndroidApk
apk
=
AndroidApk
(
id:
'FlutterApp'
,
...
...
@@ -155,15 +163,16 @@ void main() {
expect
(
launchResult
.
started
,
false
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
},
overrides:
<
Type
,
Generator
>{
AndroidSdk:
()
=>
androidSdk
,
FileSystem:
()
=>
fileSystem
,
ProcessManager:
()
=>
processManager
,
});
testUsingContext
(
'AndroidDevice.startApp forwards all supported debugging options'
,
()
async
{
const
String
deviceId
=
'1234'
;
final
AndroidDevice
device
=
AndroidDevice
(
deviceId
,
modelID:
'TestModel'
);
testWithoutContext
(
'AndroidDevice.startApp forwards all supported debugging options'
,
()
async
{
final
AndroidDevice
device
=
AndroidDevice
(
'1234'
,
modelID:
'TestModel'
,
fileSystem:
fileSystem
,
processManager:
processManager
,
logger:
BufferLogger
.
test
(),
platform:
FakePlatform
(
operatingSystem:
'linux'
),
androidSdk:
androidSdk
,
);
final
File
apkFile
=
fileSystem
.
file
(
'app.apk'
)..
createSync
();
final
AndroidApk
apk
=
AndroidApk
(
id:
'FlutterApp'
,
...
...
@@ -264,10 +273,6 @@ void main() {
// This fails to start due to observatory discovery issues.
expect
(
launchResult
.
started
,
false
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
},
overrides:
<
Type
,
Generator
>{
AndroidSdk:
()
=>
androidSdk
,
FileSystem:
()
=>
fileSystem
,
ProcessManager:
()
=>
processManager
,
});
}
...
...
This diff is collapsed.
Click to expand it.
packages/flutter_tools/test/general.shard/android/android_device_stop_test.dart
View file @
359daf4f
...
...
@@ -2,14 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/android/android_device.dart'
;
import
'package:flutter_tools/src/android/android_sdk.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:mockito/mockito.dart'
;
import
'../../src/common.dart'
;
import
'../../src/context.dart'
;
void
main
(
)
{
testWithoutContext
(
'AndroidDevice.stopApp handles a null ApplicationPackage'
,
()
async
{
final
AndroidDevice
androidDevice
=
AndroidDevice
(
'2'
);
final
AndroidDevice
androidDevice
=
AndroidDevice
(
'1234'
,
androidSdk:
MockAndroidSdk
(),
fileSystem:
MemoryFileSystem
.
test
(),
logger:
BufferLogger
.
test
(),
platform:
FakePlatform
(
operatingSystem:
'linux'
),
processManager:
FakeProcessManager
.
any
(),
);
expect
(
await
androidDevice
.
stopApp
(
null
),
false
);
});
}
class
MockAndroidSdk
extends
Mock
implements
AndroidSdk
{}
This diff is collapsed.
Click to expand it.
packages/flutter_tools/test/general.shard/android/android_device_test.dart
View file @
359daf4f
...
...
@@ -9,13 +9,13 @@ import 'dart:typed_data';
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/android/android_console.dart'
;
import
'package:flutter_tools/src/android/android_device.dart'
;
import
'package:flutter_tools/src/a
pplication_package
.dart'
;
import
'package:flutter_tools/src/a
ndroid/android_sdk
.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/build_info.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/globals.dart'
as
globals
;
import
'package:flutter_tools/src/project.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:process/process.dart'
;
...
...
@@ -24,239 +24,150 @@ import '../../src/common.dart';
import
'../../src/context.dart'
;
void
main
(
)
{
test
Using
Context
(
'AndroidDevice stores the requested id'
,
()
{
const
String
deviceId
=
'1234'
;
final
AndroidDevice
device
=
AndroidDevice
(
deviceId
);
expect
(
device
.
id
,
deviceId
);
test
Without
Context
(
'AndroidDevice stores the requested id'
,
()
{
final
AndroidDevice
device
=
setUpAndroidDevice
()
;
expect
(
device
.
id
,
'1234'
);
});
group
(
'parseAdbDeviceProperties'
,
()
{
test
(
'parse adb shell output'
,
()
{
final
Map
<
String
,
String
>
properties
=
parseAdbDeviceProperties
(
kAdbShellGetprop
);
expect
(
properties
,
isNotNull
);
expect
(
properties
[
'ro.build.characteristics'
],
'emulator'
);
expect
(
properties
[
'ro.product.cpu.abi'
],
'x86_64'
);
expect
(
properties
[
'ro.build.version.sdk'
],
'23'
);
});
testWithoutContext
(
'parseAdbDeviceProperties parses adb shell output'
,
()
{
final
Map
<
String
,
String
>
properties
=
parseAdbDeviceProperties
(
kAdbShellGetprop
);
expect
(
properties
,
isNotNull
);
expect
(
properties
[
'ro.build.characteristics'
],
'emulator'
);
expect
(
properties
[
'ro.product.cpu.abi'
],
'x86_64'
);
expect
(
properties
[
'ro.build.version.sdk'
],
'23'
);
});
group
(
'adb.exe exiting with heap corruption on windows'
,
()
{
final
ProcessManager
mockProcessManager
=
MockProcessManager
();
String
hardware
;
String
buildCharacteristics
;
setUp
(()
{
hardware
=
'goldfish'
;
buildCharacteristics
=
'unused'
;
exitCode
=
-
1
;
when
(
mockProcessManager
.
run
(
argThat
(
contains
(
'getprop'
)),
stderrEncoding:
anyNamed
(
'stderrEncoding'
),
stdoutEncoding:
anyNamed
(
'stdoutEncoding'
),
)).
thenAnswer
((
_
)
{
final
StringBuffer
buf
=
StringBuffer
()
..
writeln
(
'[ro.hardware]: [
$hardware
]'
)..
writeln
(
'[ro.build.characteristics]: [
$buildCharacteristics
]'
);
final
ProcessResult
result
=
ProcessResult
(
1
,
exitCode
,
buf
.
toString
(),
''
);
return
Future
<
ProcessResult
>.
value
(
result
);
});
});
testUsingContext
(
'nonHeapCorruptionErrorOnWindows'
,
()
async
{
exitCode
=
-
1073740941
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
isLocalEmulator
,
false
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
Platform:
()
=>
FakePlatform
(
operatingSystem:
'windows'
,
environment:
<
String
,
String
>{
'ANDROID_HOME'
:
'/'
,
},
),
});
testUsingContext
(
'heapCorruptionOnWindows'
,
()
async
{
exitCode
=
-
1073740940
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
isLocalEmulator
,
true
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
Platform:
()
=>
FakePlatform
(
operatingSystem:
'windows'
,
environment:
<
String
,
String
>{
'ANDROID_HOME'
:
'/'
,
},
),
});
testUsingContext
(
'heapCorruptionExitCodeOnLinux'
,
()
async
{
exitCode
=
-
1073740940
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
isLocalEmulator
,
false
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
Platform:
()
=>
FakePlatform
(
operatingSystem:
'linux'
,
environment:
<
String
,
String
>{
'ANDROID_HOME'
:
'/'
,
},
),
});
testUsingContext
(
'noErrorOnLinux'
,
()
async
{
exitCode
=
0
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
isLocalEmulator
,
true
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
Platform:
()
=>
FakePlatform
(
operatingSystem:
'linux'
,
environment:
<
String
,
String
>{
'ANDROID_HOME'
:
'/'
,
},
),
});
testWithoutContext
(
'adb exiting with heap corruption is only allowed on windows'
,
()
async
{
final
List
<
FakeCommand
>
commands
=
<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
],
stdout:
'[ro.hardware]: [goldfish]
\n
[ro.build.characteristics]: [unused]'
,
// Heap corruption exit code.
exitCode:
-
1073740940
,
)
];
final
AndroidDevice
windowsDevice
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(
commands
.
toList
()),
platform:
FakePlatform
(
operatingSystem:
'windows'
),
);
final
AndroidDevice
linuxDevice
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(
commands
.
toList
()),
platform:
FakePlatform
(
operatingSystem:
'linux'
),
);
final
AndroidDevice
macOsDevice
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(
commands
.
toList
()),
platform:
FakePlatform
(
operatingSystem:
'macos'
)
);
// Parsing succeedes despite the error.
expect
(
await
windowsDevice
.
isLocalEmulator
,
true
);
// Parsing fails and these default to false.
expect
(
await
linuxDevice
.
isLocalEmulator
,
false
);
expect
(
await
macOsDevice
.
isLocalEmulator
,
false
);
});
group
(
'ABI detection'
,
()
{
ProcessManager
mockProcessManager
;
String
cpu
;
String
abilist
;
setUp
(()
{
mockProcessManager
=
MockProcessManager
();
cpu
=
'unknown'
;
abilist
=
'unknown'
;
when
(
mockProcessManager
.
run
(
argThat
(
contains
(
'getprop'
)),
stderrEncoding:
anyNamed
(
'stderrEncoding'
),
stdoutEncoding:
anyNamed
(
'stdoutEncoding'
),
)).
thenAnswer
((
_
)
{
final
StringBuffer
buf
=
StringBuffer
()
..
writeln
(
'[ro.product.cpu.abi]: [
$cpu
]'
)
..
writeln
(
'[ro.product.cpu.abilist]: [
$abilist
]'
);
final
ProcessResult
result
=
ProcessResult
(
1
,
0
,
buf
.
toString
(),
''
);
return
Future
<
ProcessResult
>.
value
(
result
);
});
});
testUsingContext
(
'detects x64'
,
()
async
{
cpu
=
'x86_64'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
targetPlatform
,
TargetPlatform
.
android_x64
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
});
testUsingContext
(
'detects x86'
,
()
async
{
cpu
=
'x86'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
targetPlatform
,
TargetPlatform
.
android_x86
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
});
testUsingContext
(
'unknown device defaults to 32bit arm'
,
()
async
{
cpu
=
'???'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
targetPlatform
,
TargetPlatform
.
android_arm
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
});
testUsingContext
(
'detects 64 bit arm'
,
()
async
{
cpu
=
'arm64-v8a'
;
abilist
=
'arm64-v8a,'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
// If both abi properties agree, we are 64 bit.
expect
(
await
device
.
targetPlatform
,
TargetPlatform
.
android_arm64
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
});
testUsingContext
(
'detects kindle fire ABI'
,
()
async
{
cpu
=
'arm64-v8a'
;
abilist
=
'arm'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
// If one does not contain arm64, assume 32 bit.
expect
(
await
device
.
targetPlatform
,
TargetPlatform
.
android_arm
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
});
testWithoutContext
(
'AndroidDevice can detect TargetPlatform from property '
'abi and abiList'
,
()
async
{
// The format is [ABI, ABI list]: expected target platform.
final
Map
<
List
<
String
>,
TargetPlatform
>
values
=
<
List
<
String
>,
TargetPlatform
>{
<
String
>[
'x86_64'
,
'unknown'
]:
TargetPlatform
.
android_x64
,
<
String
>[
'x86'
,
'unknown'
]:
TargetPlatform
.
android_x86
,
// The default ABI is arm32
<
String
>[
'???'
,
'unknown'
]:
TargetPlatform
.
android_arm
,
<
String
>[
'arm64-v8a'
,
'arm64-v8a,'
]:
TargetPlatform
.
android_arm64
,
// The Kindle Fire runs 32 bit apps on 64 bit hardware.
<
String
>[
'arm64-v8a'
,
'arm'
]:
TargetPlatform
.
android_arm
,
};
for
(
final
MapEntry
<
List
<
String
>,
TargetPlatform
>
entry
in
values
.
entries
)
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
FakeCommand
(
command:
const
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
],
stdout:
'[ro.product.cpu.abi]: [
${entry.key.first}
]
\n
'
'[ro.product.cpu.abilist]: [
${entry.key.last}
]'
)
]),
);
expect
(
await
device
.
targetPlatform
,
entry
.
value
);
}
});
group
(
'isLocalEmulator'
,
()
{
final
ProcessManager
mockProcessManager
=
MockProcessManager
();
String
hardware
;
String
buildCharacteristics
;
setUp
(()
{
hardware
=
'unknown'
;
buildCharacteristics
=
'unused'
;
when
(
mockProcessManager
.
run
(
argThat
(
contains
(
'getprop'
)),
stderrEncoding:
anyNamed
(
'stderrEncoding'
),
stdoutEncoding:
anyNamed
(
'stdoutEncoding'
),
)).
thenAnswer
((
_
)
{
final
StringBuffer
buf
=
StringBuffer
()
..
writeln
(
'[ro.hardware]: [
$hardware
]'
)
..
writeln
(
'[ro.build.characteristics]: [
$buildCharacteristics
]'
);
final
ProcessResult
result
=
ProcessResult
(
1
,
0
,
buf
.
toString
(),
''
);
return
Future
<
ProcessResult
>.
value
(
result
);
});
});
testUsingContext
(
'knownPhysical'
,
()
async
{
hardware
=
'samsungexynos7420'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
isLocalEmulator
,
false
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'knownPhysical Samsung SM G570M'
,
()
async
{
hardware
=
'samsungexynos7570'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
isLocalEmulator
,
false
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'knownEmulator'
,
()
async
{
hardware
=
'goldfish'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
isLocalEmulator
,
true
);
expect
(
await
device
.
supportsHardwareRendering
,
true
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'unknownPhysical'
,
()
async
{
buildCharacteristics
=
'att'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
isLocalEmulator
,
false
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'unknownEmulator'
,
()
async
{
buildCharacteristics
=
'att,emulator'
;
final
AndroidDevice
device
=
AndroidDevice
(
'test'
);
expect
(
await
device
.
isLocalEmulator
,
true
);
expect
(
await
device
.
supportsHardwareRendering
,
true
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
});
testWithoutContext
(
'AndroidDevice can detect local emulator for known types'
,
()
async
{
final
Set
<
String
>
knownPhyiscal
=
<
String
>{
'qcom'
,
'samsungexynos7420'
,
'samsungexynos7580'
,
'samsungexynos7870'
,
'samsungexynos7880'
,
'samsungexynos8890'
,
'samsungexynos8895'
,
'samsungexynos9810'
,
'samsungexynos7570'
,
};
final
Set
<
String
>
knownEmulator
=
<
String
>{
'goldfish'
,
'ranchu'
,
};
for
(
final
String
hardware
in
knownPhyiscal
.
followedBy
(
knownEmulator
))
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
FakeCommand
(
command:
const
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
,
],
stdout:
'[ro.hardware]: [
$hardware
]
\n
'
'[ro.build.characteristics]: [unused]'
),
])
);
expect
(
await
device
.
isLocalEmulator
,
knownEmulator
.
contains
(
hardware
));
}
});
testUsingContext
(
'isSupportedForProject is true on module project'
,
()
async
{
globals
.
fs
.
file
(
'pubspec.yaml'
)
testWithoutContext
(
'AndroidDevice can detect unknown hardware'
,
()
async
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
,
],
stdout:
'[ro.hardware]: [unknown]
\n
'
'[ro.build.characteristics]: [att]'
),
])
);
expect
(
await
device
.
isLocalEmulator
,
false
);
});
testWithoutContext
(
'AndroidDevice can detect unknown emulator'
,
()
async
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
,
],
stdout:
'[ro.hardware]: [unknown]
\n
'
'[ro.build.characteristics]: [att,emulator]'
),
])
);
expect
(
await
device
.
isLocalEmulator
,
true
);
expect
(
await
device
.
supportsHardwareRendering
,
true
);
});
testWithoutContext
(
'isSupportedForProject is true on module project'
,
()
async
{
final
FileSystem
fileSystem
=
MemoryFileSystem
.
test
();
fileSystem
.
file
(
'pubspec.yaml'
)
..
createSync
()
..
writeAsStringSync
(
r''
'
name: example
...
...
@@ -264,174 +175,188 @@ name: example
flutter:
module: {}
'''
);
globals
.
fs
.
file
(
'.packages'
).
createSync
();
final
FlutterProject
flutterProject
=
FlutterProject
.
current
();
expect
(
AndroidDevice
(
'test'
).
isSupportedForProject
(
flutterProject
),
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
MemoryFileSystem
(),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
fileSystem
.
file
(
'.packages'
).
createSync
();
final
FlutterProject
flutterProject
=
FlutterProjectFactory
(
fileSystem:
fileSystem
,
logger:
BufferLogger
.
test
(),
).
fromDirectory
(
fileSystem
.
currentDirectory
);
final
AndroidDevice
device
=
setUpAndroidDevice
(
fileSystem:
fileSystem
);
expect
(
device
.
isSupportedForProject
(
flutterProject
),
true
);
});
testUsingContext
(
'isSupportedForProject is true with editable host app'
,
()
async
{
globals
.
fs
.
file
(
'pubspec.yaml'
).
createSync
();
globals
.
fs
.
file
(
'.packages'
).
createSync
();
globals
.
fs
.
directory
(
'android'
).
createSync
();
final
FlutterProject
flutterProject
=
FlutterProject
.
current
();
testWithoutContext
(
'isSupportedForProject is true with editable host app'
,
()
async
{
final
FileSystem
fileSystem
=
MemoryFileSystem
.
test
();
fileSystem
.
file
(
'pubspec.yaml'
).
createSync
();
fileSystem
.
file
(
'.packages'
).
createSync
();
fileSystem
.
directory
(
'android'
).
createSync
();
final
FlutterProject
flutterProject
=
FlutterProjectFactory
(
fileSystem:
fileSystem
,
logger:
BufferLogger
.
test
(),
).
fromDirectory
(
fileSystem
.
currentDirectory
);
expect
(
AndroidDevice
(
'test'
).
isSupportedForProject
(
flutterProject
),
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
MemoryFileSystem
(),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
final
AndroidDevice
device
=
setUpAndroidDevice
(
fileSystem:
fileSystem
);
expect
(
device
.
isSupportedForProject
(
flutterProject
),
true
);
});
testUsingContext
(
'isSupportedForProject is false with no host app and no module'
,
()
async
{
globals
.
fs
.
file
(
'pubspec.yaml'
).
createSync
();
globals
.
fs
.
file
(
'.packages'
).
createSync
();
final
FlutterProject
flutterProject
=
FlutterProject
.
current
();
testWithoutContext
(
'isSupportedForProject is false with no host app and no module'
,
()
async
{
final
FileSystem
fileSystem
=
MemoryFileSystem
.
test
();
fileSystem
.
file
(
'pubspec.yaml'
).
createSync
();
fileSystem
.
file
(
'.packages'
).
createSync
();
final
FlutterProject
flutterProject
=
FlutterProjectFactory
(
fileSystem:
fileSystem
,
logger:
BufferLogger
.
test
(),
).
fromDirectory
(
fileSystem
.
currentDirectory
);
final
AndroidDevice
device
=
setUpAndroidDevice
(
fileSystem:
fileSystem
);
expect
(
device
.
isSupportedForProject
(
flutterProject
),
false
);
});
expect
(
AndroidDevice
(
'test'
).
isSupportedForProject
(
flutterProject
),
false
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
MemoryFileSystem
(),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
testWithoutContext
(
'AndroidDevice returns correct ID for responsive emulator'
,
()
async
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'emulator-5555'
,
'shell'
,
'getprop'
],
stdout:
'[ro.hardware]: [goldfish]'
)
]),
id:
'emulator-5555'
,
androidConsoleSocketFactory:
(
String
host
,
int
port
)
async
=>
MockWorkingAndroidConsoleSocket
(
'dummyEmulatorId'
),
);
expect
(
await
device
.
emulatorId
,
equals
(
'dummyEmulatorId'
));
});
group
(
'emulatorId'
,
()
{
final
ProcessManager
mockProcessManager
=
MockProcessManager
();
const
String
dummyEmulatorId
=
'dummyEmulatorId'
;
final
Future
<
Socket
>
Function
(
String
host
,
int
port
)
unresponsiveSocket
=
(
String
host
,
int
port
)
async
=>
MockUnresponsiveAndroidConsoleSocket
();
final
Future
<
Socket
>
Function
(
String
host
,
int
port
)
disconnectingSocket
=
(
String
host
,
int
port
)
async
=>
MockDisconnectingAndroidConsoleSocket
();
final
Future
<
Socket
>
Function
(
String
host
,
int
port
)
workingSocket
=
(
String
host
,
int
port
)
async
=>
MockWorkingAndroidConsoleSocket
(
dummyEmulatorId
);
String
hardware
;
bool
socketWasCreated
;
setUp
(()
{
hardware
=
'goldfish'
;
// Known emulator
socketWasCreated
=
false
;
when
(
mockProcessManager
.
run
(
argThat
(
contains
(
'getprop'
)),
stderrEncoding:
anyNamed
(
'stderrEncoding'
),
stdoutEncoding:
anyNamed
(
'stdoutEncoding'
),
)).
thenAnswer
((
_
)
{
final
StringBuffer
buf
=
StringBuffer
()
..
writeln
(
'[ro.hardware]: [
$hardware
]'
);
final
ProcessResult
result
=
ProcessResult
(
1
,
0
,
buf
.
toString
(),
''
);
return
Future
<
ProcessResult
>.
value
(
result
);
});
});
testUsingContext
(
'returns correct ID for responsive emulator'
,
()
async
{
final
AndroidDevice
device
=
AndroidDevice
(
'emulator-5555'
);
expect
(
await
device
.
emulatorId
,
equals
(
dummyEmulatorId
));
},
overrides:
<
Type
,
Generator
>{
AndroidConsoleSocketFactory:
()
=>
workingSocket
,
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'does not create socket for non-emulator devices'
,
()
async
{
hardware
=
'samsungexynos7420'
;
// Still use an emulator-looking ID so we can be sure the failure is due
// to the isLocalEmulator field and not because the ID doesn't contain a
// port.
final
AndroidDevice
device
=
AndroidDevice
(
'emulator-5555'
);
expect
(
await
device
.
emulatorId
,
isNull
);
expect
(
socketWasCreated
,
isFalse
);
},
overrides:
<
Type
,
Generator
>{
AndroidConsoleSocketFactory:
()
=>
(
String
host
,
int
port
)
async
{
testWithoutContext
(
'AndroidDevice does not create socket for non-emulator devices'
,
()
async
{
bool
socketWasCreated
=
false
;
// Still use an emulator-looking ID so we can be sure the failure is due
// to the isLocalEmulator field and not because the ID doesn't contain a
// port.
final
AndroidDevice
device
=
setUpAndroidDevice
(
id:
'emulator-5555'
,
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'emulator-5555'
,
'shell'
,
'getprop'
],
stdout:
'[ro.hardware]: [samsungexynos7420]'
)
]),
androidConsoleSocketFactory:
(
String
host
,
int
port
)
async
{
socketWasCreated
=
true
;
throw
'Socket was created for non-emulator'
;
},
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'does not create socket for emulators with no port'
,
()
async
{
final
AndroidDevice
device
=
AndroidDevice
(
'emulator-noport'
);
expect
(
await
device
.
emulatorId
,
isNull
);
expect
(
socketWasCreated
,
isFalse
);
},
overrides:
<
Type
,
Generator
>{
AndroidConsoleSocketFactory:
()
=>
(
String
host
,
int
port
)
async
{
}
);
expect
(
await
device
.
emulatorId
,
isNull
);
expect
(
socketWasCreated
,
isFalse
);
});
testWithoutContext
(
'AndroidDevice does not create socket for emulators with no port'
,
()
async
{
bool
socketWasCreated
=
false
;
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
],
stdout:
'[ro.hardware]: [goldfish]'
)
]),
androidConsoleSocketFactory:
(
String
host
,
int
port
)
async
{
socketWasCreated
=
true
;
throw
'Socket was created for emulator without port in ID'
;
},
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'returns null for connection error'
,
()
async
{
final
AndroidDevice
device
=
AndroidDevice
(
'emulator-5555'
);
expect
(
await
device
.
emulatorId
,
isNull
);
},
overrides:
<
Type
,
Generator
>{
AndroidConsoleSocketFactory:
()
{
return
(
String
host
,
int
port
)
=>
throw
Exception
(
'Fake socket error'
);
},
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'returns null for unresponsive device'
,
()
async
{
final
AndroidDevice
device
=
AndroidDevice
(
'emulator-5555'
);
expect
(
await
device
.
emulatorId
,
isNull
);
},
overrides:
<
Type
,
Generator
>{
AndroidConsoleSocketFactory:
()
=>
unresponsiveSocket
,
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'returns null on early disconnect'
,
()
async
{
final
AndroidDevice
device
=
AndroidDevice
(
'emulator-5555'
);
expect
(
await
device
.
emulatorId
,
isNull
);
},
overrides:
<
Type
,
Generator
>{
AndroidConsoleSocketFactory:
()
=>
disconnectingSocket
,
ProcessManager:
()
=>
mockProcessManager
,
});
);
expect
(
await
device
.
emulatorId
,
isNull
);
expect
(
socketWasCreated
,
isFalse
);
});
testWithoutContext
(
'AndroidDevice.emulatorId is null for connection error'
,
()
async
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
],
stdout:
'[ro.hardware]: [goldfish]'
)
]),
androidConsoleSocketFactory:
(
String
host
,
int
port
)
=>
throw
Exception
(
'Fake socket error'
),
);
expect
(
await
device
.
emulatorId
,
isNull
);
});
testWithoutContext
(
'AndroidDevice.emulatorId is null for unresponsive device'
,
()
async
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
],
stdout:
'[ro.hardware]: [goldfish]'
)
]),
androidConsoleSocketFactory:
(
String
host
,
int
port
)
async
=>
MockUnresponsiveAndroidConsoleSocket
(),
);
expect
(
await
device
.
emulatorId
,
isNull
);
});
testWithoutContext
(
'AndroidDevice.emulatorId is null on early disconnect'
,
()
async
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
],
stdout:
'[ro.hardware]: [goldfish]'
)
]),
androidConsoleSocketFactory:
(
String
host
,
int
port
)
async
=>
MockDisconnectingAndroidConsoleSocket
()
);
expect
(
await
device
.
emulatorId
,
isNull
);
});
group
(
'logcat'
,
()
{
final
ProcessManager
mockProcessManager
=
MockProcessManager
();
final
AndroidDevice
device
=
AndroidDevice
(
'1234'
);
testUsingContext
(
'lastLogcatTimestamp returns null if shell command failed'
,
()
async
{
when
(
mockProcessManager
.
runSync
(
argThat
(
contains
(
'logcat'
))))
.
thenReturn
(
ProcessResult
(
0
,
1
,
''
,
''
));
expect
(
device
.
lastLogcatTimestamp
,
isNull
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
});
testUsingContext
(
'AdbLogReaders for past+future and future logs are not the same'
,
()
async
{
when
(
mockProcessManager
.
run
(
argThat
(
contains
(
'getprop'
)),
stderrEncoding:
anyNamed
(
'stderrEncoding'
),
stdoutEncoding:
anyNamed
(
'stdoutEncoding'
),
)).
thenAnswer
((
_
)
{
final
StringBuffer
buf
=
StringBuffer
()
..
writeln
(
'[ro.build.version.sdk]: [23]'
);
final
ProcessResult
result
=
ProcessResult
(
1
,
exitCode
,
buf
.
toString
(),
''
);
return
Future
<
ProcessResult
>.
value
(
result
);
});
when
(
mockProcessManager
.
run
(
argThat
(
contains
(
'shell'
)),
stderrEncoding:
anyNamed
(
'stderrEncoding'
),
stdoutEncoding:
anyNamed
(
'stdoutEncoding'
),
)).
thenAnswer
((
_
)
{
final
StringBuffer
buf
=
StringBuffer
()
..
writeln
(
'11-27 15:39:04.506'
);
final
ProcessResult
result
=
ProcessResult
(
1
,
exitCode
,
buf
.
toString
(),
''
);
return
Future
<
ProcessResult
>.
value
(
result
);
});
final
DeviceLogReader
pastLogReader
=
await
device
.
getLogReader
(
includePastLogs:
true
);
final
DeviceLogReader
defaultLogReader
=
await
device
.
getLogReader
();
expect
(
pastLogReader
,
isNot
(
equals
(
defaultLogReader
)));
// Getting again is cached.
expect
(
pastLogReader
,
equals
(
await
device
.
getLogReader
(
includePastLogs:
true
)));
expect
(
defaultLogReader
,
equals
(
await
device
.
getLogReader
()));
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
});
testWithoutContext
(
'AndroidDevice lastLogcatTimestamp returns null if shell command failed'
,
()
async
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'-x'
,
'logcat'
,
'-v'
,
'time'
,
'-t'
,
'1'
],
exitCode:
1
,
)
])
);
expect
(
device
.
lastLogcatTimestamp
,
isNull
);
});
test
(
'Can parse adb shell dumpsys info'
,
()
{
testWithoutContext
(
'AndroidDevice AdbLogReaders for past+future and future logs are not the same'
,
()
async
{
final
AndroidDevice
device
=
setUpAndroidDevice
(
processManager:
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
],
stdout:
'[ro.build.version.sdk]: [23]'
,
exitCode:
1
,
),
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'logcat'
,
'-v'
,
'time'
,
'-s'
,
'flutter'
],
),
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'logcat'
,
'-v'
,
'time'
],
)
])
);
final
DeviceLogReader
pastLogReader
=
await
device
.
getLogReader
(
includePastLogs:
true
);
final
DeviceLogReader
defaultLogReader
=
await
device
.
getLogReader
();
expect
(
pastLogReader
,
isNot
(
equals
(
defaultLogReader
)));
// Getting again is cached.
expect
(
pastLogReader
,
equals
(
await
device
.
getLogReader
(
includePastLogs:
true
)));
expect
(
defaultLogReader
,
equals
(
await
device
.
getLogReader
()));
});
testWithoutContext
(
'Can parse adb shell dumpsys info'
,
()
{
const
String
exampleOutput
=
r''
'
Applications Memory Usage (in Kilobytes):
Uptime: 441088659 Realtime: 521464097
...
...
@@ -517,6 +442,28 @@ Uptime: 441088659 Realtime: 521464097
});
}
AndroidDevice
setUpAndroidDevice
(
{
String
id
,
AndroidSdk
androidSdk
,
FileSystem
fileSystem
,
ProcessManager
processManager
,
Platform
platform
,
AndroidConsoleSocketFactory
androidConsoleSocketFactory
=
kAndroidConsoleSocketFactory
,
})
{
androidSdk
??=
MockAndroidSdk
();
when
(
androidSdk
.
adbPath
).
thenReturn
(
'adb'
);
return
AndroidDevice
(
id
??
'1234'
,
logger:
BufferLogger
.
test
(),
platform:
platform
??
FakePlatform
(
operatingSystem:
'linux'
),
androidSdk:
androidSdk
,
fileSystem:
fileSystem
??
MemoryFileSystem
.
test
(),
processManager:
processManager
??
FakeProcessManager
.
any
(),
androidConsoleSocketFactory:
androidConsoleSocketFactory
,
timeoutConfiguration:
const
TimeoutConfiguration
(),
);
}
class
MockAndroidSdk
extends
Mock
implements
AndroidSdk
{}
class
MockProcessManager
extends
Mock
implements
ProcessManager
{}
const
String
kAdbShellGetprop
=
'''
...
...
@@ -737,10 +684,3 @@ class MockDisconnectingAndroidConsoleSocket extends Mock implements Socket {
_controller
.
close
();
}
}
class
AndroidPackageTest
extends
ApplicationPackage
{
AndroidPackageTest
()
:
super
(
id:
'app-id'
);
@override
String
get
name
=>
'app-package'
;
}
This diff is collapsed.
Click to expand it.
packages/flutter_tools/test/general.shard/android/android_emulator_test.dart
View file @
359daf4f
...
...
@@ -11,7 +11,6 @@ import 'package:flutter_tools/src/android/android_sdk.dart'
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:flutter_tools/src/globals.dart'
as
globals
;
import
'package:mockito/mockito.dart'
;
import
'package:quiver/testing/async.dart'
;
...
...
@@ -131,7 +130,7 @@ void main() {
testUsingContext
(
'succeeds'
,
()
async
{
final
AndroidEmulator
emulator
=
AndroidEmulator
(
emulatorID
);
expect
(
getEmulatorPath
(
globals
.
android
Sdk
),
mockSdk
.
emulatorPath
);
expect
(
getEmulatorPath
(
mock
Sdk
),
mockSdk
.
emulatorPath
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
FakeAsync
().
run
((
FakeAsync
time
)
{
unawaited
(
emulator
.
launch
().
whenComplete
(
completer
.
complete
));
...
...
This diff is collapsed.
Click to expand it.
packages/flutter_tools/test/general.shard/android/android_install_test.dart
View file @
359daf4f
...
...
@@ -7,7 +7,8 @@ import 'package:flutter_tools/src/android/android_device.dart';
import
'package:flutter_tools/src/android/android_sdk.dart'
;
import
'package:flutter_tools/src/application_package.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/globals.dart'
as
globals
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:mockito/mockito.dart'
;
import
'../../src/common.dart'
;
...
...
@@ -28,66 +29,50 @@ const FakeCommand kStoreShaCommand = FakeCommand(
);
void
main
(
)
{
testUsingContext
(
'Cannot install app on API level below 16'
,
()
async
{
final
File
apk
=
globals
.
fs
.
file
(
'app.apk'
)..
createSync
();
final
AndroidApk
androidApk
=
AndroidApk
(
file:
apk
,
id:
'app'
,
versionCode:
22
,
launchActivity:
'Main'
,
);
final
AndroidDevice
androidDevice
=
AndroidDevice
(
'1234'
);
when
(
globals
.
androidSdk
.
adbPath
).
thenReturn
(
'adb'
);
final
FakeProcessManager
processManager
=
globals
.
processManager
as
FakeProcessManager
;
expect
(
await
androidDevice
.
installApp
(
androidApk
),
false
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
FakeProcessManager
.
list
(<
FakeCommand
>[
testWithoutContext
(
'Cannot install app on API level below 16'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
kAdbVersionCommand
,
kAdbStartServerCommand
,
const
FakeCommand
(
command:
<
String
>[
'adb'
,
'-s'
,
'1234'
,
'shell'
,
'getprop'
],
stdout:
'[ro.build.version.sdk]: [11]'
,
),
]),
FileSystem:
()
=>
MemoryFileSystem
.
test
(),
AndroidSdk:
()
=>
MockAndroidSdk
(),
});
testUsingContext
(
'Cannot install app if APK file is missing'
,
()
async
{
final
File
apk
=
globals
.
fs
.
file
(
'app.apk'
);
]);
final
FileSystem
fileSystem
=
MemoryFileSystem
.
test
();
final
File
apk
=
fileSystem
.
file
(
'app.apk'
)..
createSync
();
final
AndroidApk
androidApk
=
AndroidApk
(
file:
apk
,
id:
'app'
,
versionCode:
22
,
launchActivity:
'Main'
,
);
final
AndroidDevice
androidDevice
=
AndroidDevice
(
'1234'
);
final
AndroidDevice
androidDevice
=
setUpAndroidDevice
(
fileSystem:
fileSystem
,
processManager:
processManager
,
);
expect
(
await
androidDevice
.
installApp
(
androidApk
),
false
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
FakeProcessManager
.
list
(<
FakeCommand
>[]),
FileSystem:
()
=>
MemoryFileSystem
.
test
(),
AndroidSdk:
()
=>
MockAndroidSdk
(),
expect
(
processManager
.
hasRemainingExpectations
,
false
);
});
testUsingContext
(
'Can install app on API level 16 or greater'
,
()
async
{
final
File
apk
=
globals
.
fs
.
file
(
'app.apk'
)..
createSync
();
testWithoutContext
(
'Cannot install app if APK file is missing'
,
()
async
{
final
FileSystem
fileSystem
=
MemoryFileSystem
.
test
();
final
File
apk
=
fileSystem
.
file
(
'app.apk'
);
final
AndroidApk
androidApk
=
AndroidApk
(
file:
apk
,
id:
'app'
,
versionCode:
22
,
launchActivity:
'Main'
,
);
final
AndroidDevice
androidDevice
=
AndroidDevice
(
'1234'
);
when
(
globals
.
androidSdk
.
adbPath
).
thenReturn
(
'adb'
);
final
FakeProcessManager
processManager
=
globals
.
processManager
as
FakeProcessManager
;
final
AndroidDevice
androidDevice
=
setUpAndroidDevice
(
fileSystem:
fileSystem
,
)
;
expect
(
await
androidDevice
.
installApp
(
androidApk
),
true
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
FakeProcessManager
.
list
(<
FakeCommand
>[
expect
(
await
androidDevice
.
installApp
(
androidApk
),
false
);
});
testWithoutContext
(
'Can install app on API level 16 or greater'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
kAdbVersionCommand
,
kAdbStartServerCommand
,
const
FakeCommand
(
...
...
@@ -96,27 +81,26 @@ void main() {
),
kInstallCommand
,
kStoreShaCommand
,
]),
FileSystem:
()
=>
MemoryFileSystem
.
test
(),
AndroidSdk:
()
=>
MockAndroidSdk
(),
});
testUsingContext
(
'Defaults to API level 16 if adb returns a null response'
,
()
async
{
final
File
apk
=
globals
.
fs
.
file
(
'app.apk'
)..
createSync
();
]);
final
FileSystem
fileSystem
=
MemoryFileSystem
.
test
();
final
File
apk
=
fileSystem
.
file
(
'app.apk'
)..
createSync
();
final
AndroidApk
androidApk
=
AndroidApk
(
file:
apk
,
id:
'app'
,
versionCode:
22
,
launchActivity:
'Main'
,
);
final
AndroidDevice
androidDevice
=
AndroidDevice
(
'1234'
);
when
(
globals
.
androidSdk
.
adbPath
).
thenReturn
(
'adb'
);
final
FakeProcessManager
processManager
=
globals
.
processManager
as
FakeProcessManager
;
final
AndroidDevice
androidDevice
=
setUpAndroidDevice
(
fileSystem:
fileSystem
,
processManager:
processManager
,
);
expect
(
await
androidDevice
.
installApp
(
androidApk
),
true
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
FakeProcessManager
.
list
(<
FakeCommand
>[
});
testWithoutContext
(
'Defaults to API level 16 if adb returns a null response'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
kAdbVersionCommand
,
kAdbStartServerCommand
,
const
FakeCommand
(
...
...
@@ -124,10 +108,39 @@ void main() {
),
kInstallCommand
,
kStoreShaCommand
,
]),
FileSystem:
()
=>
MemoryFileSystem
.
test
(),
AndroidSdk:
()
=>
MockAndroidSdk
(),
]);
final
FileSystem
fileSystem
=
MemoryFileSystem
.
test
();
final
File
apk
=
fileSystem
.
file
(
'app.apk'
)..
createSync
();
final
AndroidApk
androidApk
=
AndroidApk
(
file:
apk
,
id:
'app'
,
versionCode:
22
,
launchActivity:
'Main'
,
);
final
AndroidDevice
androidDevice
=
setUpAndroidDevice
(
fileSystem:
fileSystem
,
processManager:
processManager
,
);
expect
(
await
androidDevice
.
installApp
(
androidApk
),
true
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
});
}
AndroidDevice
setUpAndroidDevice
(
{
AndroidSdk
androidSdk
,
FileSystem
fileSystem
,
ProcessManager
processManager
,
})
{
androidSdk
??=
MockAndroidSdk
();
when
(
androidSdk
.
adbPath
).
thenReturn
(
'adb'
);
return
AndroidDevice
(
'1234'
,
logger:
BufferLogger
.
test
(),
platform:
FakePlatform
(
operatingSystem:
'linux'
),
androidSdk:
androidSdk
,
fileSystem:
fileSystem
??
MemoryFileSystem
.
test
(),
processManager:
processManager
??
FakeProcessManager
.
any
(),
);
}
class
MockAndroidSdk
extends
Mock
implements
AndroidSdk
{}
This diff is collapsed.
Click to expand it.
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