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
6d77996d
Unverified
Commit
6d77996d
authored
Nov 19, 2019
by
Emmanuel Garcia
Committed by
GitHub
Nov 19, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Attach looks at future observatory URIs (#44637)
parent
37eec862
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
699 additions
and
245 deletions
+699
-245
android_device.dart
packages/flutter_tools/lib/src/android/android_device.dart
+3
-30
attach.dart
packages/flutter_tools/lib/src/commands/attach.dart
+103
-74
devfs.dart
packages/flutter_tools/lib/src/devfs.dart
+6
-2
protocol_discovery.dart
packages/flutter_tools/lib/src/protocol_discovery.dart
+87
-23
resident_runner.dart
packages/flutter_tools/lib/src/resident_runner.dart
+103
-30
run_cold.dart
packages/flutter_tools/lib/src/run_cold.dart
+3
-4
run_hot.dart
packages/flutter_tools/lib/src/run_hot.dart
+3
-3
attach_test.dart
...utter_tools/test/commands.shard/hermetic/attach_test.dart
+231
-28
devfs_test.dart
packages/flutter_tools/test/general.shard/devfs_test.dart
+4
-0
protocol_discovery_test.dart
...ter_tools/test/general.shard/protocol_discovery_test.dart
+124
-40
resident_runner_test.dart
...lutter_tools/test/general.shard/resident_runner_test.dart
+5
-7
mocks.dart
packages/flutter_tools/test/src/mocks.dart
+27
-4
No files found.
packages/flutter_tools/lib/src/android/android_device.dart
View file @
6d77996d
...
@@ -846,25 +846,9 @@ class _AdbLogReader extends DeviceLogReader {
...
@@ -846,25 +846,9 @@ class _AdbLogReader extends DeviceLogReader {
@override
@override
String
get
name
=>
device
.
name
;
String
get
name
=>
device
.
name
;
DateTime
_timeOrigin
;
DateTime
_adbTimestampToDateTime
(
String
adbTimestamp
)
{
// The adb timestamp format is: mm-dd hours:minutes:seconds.milliseconds
// Dart's DateTime parse function accepts this format so long as we provide
// the year, resulting in:
// yyyy-mm-dd hours:minutes:seconds.milliseconds.
return
DateTime
.
parse
(
'
${DateTime.now().year}
-
$adbTimestamp
'
);
}
void
_start
()
{
void
_start
()
{
// Start the adb logcat process.
// Start the adb logcat process and filter logs by the "flutter" tag.
final
List
<
String
>
args
=
<
String
>[
'shell'
,
'-x'
,
'logcat'
,
'-v'
,
'time'
];
final
List
<
String
>
args
=
<
String
>[
'shell'
,
'-x'
,
'logcat'
,
'-v'
,
'time'
,
'-s'
,
'flutter'
];
final
String
lastTimestamp
=
device
.
lastLogcatTimestamp
;
if
(
lastTimestamp
!=
null
)
{
_timeOrigin
=
_adbTimestampToDateTime
(
lastTimestamp
);
}
else
{
_timeOrigin
=
null
;
}
processUtils
.
start
(
device
.
adbCommandForDevice
(
args
)).
then
<
void
>((
Process
process
)
{
processUtils
.
start
(
device
.
adbCommandForDevice
(
args
)).
then
<
void
>((
Process
process
)
{
_process
=
process
;
_process
=
process
;
// We expect logcat streams to occasionally contain invalid utf-8,
// We expect logcat streams to occasionally contain invalid utf-8,
...
@@ -914,18 +898,7 @@ class _AdbLogReader extends DeviceLogReader {
...
@@ -914,18 +898,7 @@ class _AdbLogReader extends DeviceLogReader {
// mm-dd hh:mm:ss.milliseconds Priority/Tag( PID): ....
// mm-dd hh:mm:ss.milliseconds Priority/Tag( PID): ....
void
_onLine
(
String
line
)
{
void
_onLine
(
String
line
)
{
final
Match
timeMatch
=
AndroidDevice
.
_timeRegExp
.
firstMatch
(
line
);
final
Match
timeMatch
=
AndroidDevice
.
_timeRegExp
.
firstMatch
(
line
);
if
(
timeMatch
==
null
)
{
if
(
timeMatch
==
null
||
line
.
length
==
timeMatch
.
end
)
{
return
;
}
if
(
_timeOrigin
!=
null
)
{
final
String
timestamp
=
timeMatch
.
group
(
0
);
final
DateTime
time
=
_adbTimestampToDateTime
(
timestamp
);
if
(!
time
.
isAfter
(
_timeOrigin
))
{
// Ignore log messages before the origin.
return
;
}
}
if
(
line
.
length
==
timeMatch
.
end
)
{
return
;
return
;
}
}
// Chop off the time.
// Chop off the time.
...
...
packages/flutter_tools/lib/src/commands/attach.dart
View file @
6d77996d
...
@@ -4,6 +4,8 @@
...
@@ -4,6 +4,8 @@
import
'dart:async'
;
import
'dart:async'
;
import
'package:meta/meta.dart'
;
import
'../artifacts.dart'
;
import
'../artifacts.dart'
;
import
'../base/common.dart'
;
import
'../base/common.dart'
;
import
'../base/context.dart'
;
import
'../base/context.dart'
;
...
@@ -200,16 +202,14 @@ class AttachCommand extends FlutterCommand {
...
@@ -200,16 +202,14 @@ class AttachCommand extends FlutterCommand {
notifyingLogger:
NotifyingLogger
(),
logToStdout:
true
)
notifyingLogger:
NotifyingLogger
(),
logToStdout:
true
)
:
null
;
:
null
;
Uri
observatoryUri
;
Stream
<
Uri
>
observatoryUri
;
bool
usesIpv6
=
ipv6
;
bool
usesIpv6
=
ipv6
;
final
String
ipv6Loopback
=
InternetAddress
.
loopbackIPv6
.
address
;
final
String
ipv6Loopback
=
InternetAddress
.
loopbackIPv6
.
address
;
final
String
ipv4Loopback
=
InternetAddress
.
loopbackIPv4
.
address
;
final
String
ipv4Loopback
=
InternetAddress
.
loopbackIPv4
.
address
;
final
String
hostname
=
usesIpv6
?
ipv6Loopback
:
ipv4Loopback
;
final
String
hostname
=
usesIpv6
?
ipv6Loopback
:
ipv4Loopback
;
bool
attachLogger
=
false
;
if
(
devicePort
==
null
&&
debugUri
==
null
)
{
if
(
devicePort
==
null
&&
debugUri
==
null
)
{
if
(
device
is
FuchsiaDevice
)
{
if
(
device
is
FuchsiaDevice
)
{
attachLogger
=
true
;
final
String
module
=
stringArg
(
'module'
);
final
String
module
=
stringArg
(
'module'
);
if
(
module
==
null
)
{
if
(
module
==
null
)
{
throwToolExit
(
'
\'
--module
\'
is required for attaching to a Fuchsia device'
);
throwToolExit
(
'
\'
--module
\'
is required for attaching to a Fuchsia device'
);
...
@@ -218,8 +218,7 @@ class AttachCommand extends FlutterCommand {
...
@@ -218,8 +218,7 @@ class AttachCommand extends FlutterCommand {
FuchsiaIsolateDiscoveryProtocol
isolateDiscoveryProtocol
;
FuchsiaIsolateDiscoveryProtocol
isolateDiscoveryProtocol
;
try
{
try
{
isolateDiscoveryProtocol
=
device
.
getIsolateDiscoveryProtocol
(
module
);
isolateDiscoveryProtocol
=
device
.
getIsolateDiscoveryProtocol
(
module
);
observatoryUri
=
await
isolateDiscoveryProtocol
.
uri
;
observatoryUri
=
Stream
<
Uri
>.
fromFuture
(
isolateDiscoveryProtocol
.
uri
).
asBroadcastStream
();
printStatus
(
'Done.'
);
// FYI, this message is used as a sentinel in tests.
}
catch
(
_
)
{
}
catch
(
_
)
{
isolateDiscoveryProtocol
?.
dispose
();
isolateDiscoveryProtocol
?.
dispose
();
final
List
<
ForwardedPort
>
ports
=
device
.
portForwarder
.
forwardedPorts
.
toList
();
final
List
<
ForwardedPort
>
ports
=
device
.
portForwarder
.
forwardedPorts
.
toList
();
...
@@ -229,85 +228,55 @@ class AttachCommand extends FlutterCommand {
...
@@ -229,85 +228,55 @@ class AttachCommand extends FlutterCommand {
rethrow
;
rethrow
;
}
}
}
else
if
((
device
is
IOSDevice
)
||
(
device
is
IOSSimulator
))
{
}
else
if
((
device
is
IOSDevice
)
||
(
device
is
IOSSimulator
))
{
observatoryUri
=
await
MDnsObservatoryDiscovery
.
instance
.
getObservatoryUri
(
observatoryUri
=
Stream
<
Uri
>
appId
,
.
fromFuture
(
device
,
MDnsObservatoryDiscovery
.
instance
.
getObservatoryUri
(
usesIpv6:
usesIpv6
,
appId
,
deviceVmservicePort:
deviceVmservicePort
,
device
,
);
usesIpv6:
usesIpv6
,
deviceVmservicePort:
deviceVmservicePort
,
)
).
asBroadcastStream
();
}
}
// If MDNS discovery fails or we're not on iOS, fallback to ProtocolDiscovery.
// If MDNS discovery fails or we're not on iOS, fallback to ProtocolDiscovery.
if
(
observatoryUri
==
null
)
{
if
(
observatoryUri
==
null
)
{
ProtocolDiscovery
observatoryDiscovery
;
final
ProtocolDiscovery
observatoryDiscovery
=
try
{
ProtocolDiscovery
.
observatory
(
observatoryDiscovery
=
ProtocolDiscovery
.
observatory
(
device
.
getLogReader
(),
device
.
getLogReader
(),
portForwarder:
device
.
portForwarder
,
portForwarder:
device
.
portForwarder
,
ipv6:
ipv6
,
ipv6:
ipv6
,
devicePort:
deviceVmservicePort
,
devicePort:
deviceVmservicePort
,
hostPort:
hostVmservicePort
,
hostPort:
hostVmservicePort
,
);
);
printStatus
(
'Waiting for a connection from Flutter on
${device.name}
...'
);
printStatus
(
'Waiting for a connection from Flutter on
${device.name}
...'
);
observatoryUri
=
await
observatoryDiscovery
.
uri
;
observatoryUri
=
observatoryDiscovery
.
uris
;
// Determine ipv6 status from the scanned logs.
// Determine ipv6 status from the scanned logs.
usesIpv6
=
observatoryDiscovery
.
ipv6
;
usesIpv6
=
observatoryDiscovery
.
ipv6
;
printStatus
(
'Done.'
);
// FYI, this message is used as a sentinel in tests.
}
catch
(
error
)
{
throwToolExit
(
'Failed to establish a debug connection with
${device.name}
:
$error
'
);
}
finally
{
await
observatoryDiscovery
?.
cancel
();
}
}
}
}
else
{
}
else
{
observatoryUri
=
await
buildObservatoryUri
(
observatoryUri
=
Stream
<
Uri
>
device
,
.
fromFuture
(
debugUri
?.
host
??
hostname
,
buildObservatoryUri
(
devicePort
??
debugUri
.
port
,
device
,
hostVmservicePort
,
debugUri
?.
host
??
hostname
,
debugUri
?.
path
,
devicePort
??
debugUri
.
port
,
);
hostVmservicePort
,
}
debugUri
?.
path
,
try
{
final
bool
useHot
=
getBuildInfo
().
isDebug
;
final
FlutterDevice
flutterDevice
=
await
FlutterDevice
.
create
(
device
,
flutterProject:
flutterProject
,
trackWidgetCreation:
boolArg
(
'track-widget-creation'
),
fileSystemRoots:
stringsArg
(
'filesystem-root'
),
fileSystemScheme:
stringArg
(
'filesystem-scheme'
),
viewFilter:
stringArg
(
'isolate-filter'
),
target:
stringArg
(
'target'
),
targetModel:
TargetModel
(
stringArg
(
'target-model'
)),
buildMode:
getBuildMode
(),
dartDefines:
dartDefines
,
);
flutterDevice
.
observatoryUris
=
<
Uri
>[
observatoryUri
];
final
List
<
FlutterDevice
>
flutterDevices
=
<
FlutterDevice
>[
flutterDevice
];
final
DebuggingOptions
debuggingOptions
=
DebuggingOptions
.
enabled
(
getBuildInfo
());
terminal
.
usesTerminalUi
=
daemon
==
null
;
final
ResidentRunner
runner
=
useHot
?
hotRunnerFactory
.
build
(
flutterDevices
,
target:
targetFile
,
debuggingOptions:
debuggingOptions
,
packagesFilePath:
globalResults
[
'packages'
]
as
String
,
projectRootPath:
stringArg
(
'project-root'
),
dillOutputPath:
stringArg
(
'output-dill'
),
ipv6:
usesIpv6
,
flutterProject:
flutterProject
,
)
)
:
ColdRunner
(
).
asBroadcastStream
();
flutterDevices
,
}
target:
targetFile
,
debuggingOptions:
debuggingOptions
,
terminal
.
usesTerminalUi
=
daemon
==
null
;
ipv6:
usesIpv6
,
);
if
(
attachLogger
)
{
flutterDevice
.
startEchoingDeviceLog
();
}
try
{
int
result
;
int
result
;
if
(
daemon
!=
null
)
{
if
(
daemon
!=
null
)
{
final
ResidentRunner
runner
=
await
createResidentRunner
(
observatoryUris:
observatoryUri
,
device:
device
,
flutterProject:
flutterProject
,
usesIpv6:
usesIpv6
,
);
AppInstance
app
;
AppInstance
app
;
try
{
try
{
app
=
await
daemon
.
appDomain
.
launch
(
app
=
await
daemon
.
appDomain
.
launch
(
...
@@ -324,20 +293,34 @@ class AttachCommand extends FlutterCommand {
...
@@ -324,20 +293,34 @@ class AttachCommand extends FlutterCommand {
}
}
result
=
await
app
.
runner
.
waitForAppToFinish
();
result
=
await
app
.
runner
.
waitForAppToFinish
();
assert
(
result
!=
null
);
assert
(
result
!=
null
);
}
else
{
return
;
}
while
(
true
)
{
final
ResidentRunner
runner
=
await
createResidentRunner
(
observatoryUris:
observatoryUri
,
device:
device
,
flutterProject:
flutterProject
,
usesIpv6:
usesIpv6
,
);
final
Completer
<
void
>
onAppStart
=
Completer
<
void
>.
sync
();
final
Completer
<
void
>
onAppStart
=
Completer
<
void
>.
sync
();
TerminalHandler
terminalHandler
;
unawaited
(
onAppStart
.
future
.
whenComplete
(()
{
unawaited
(
onAppStart
.
future
.
whenComplete
(()
{
TerminalHandler
(
runner
)
terminalHandler
=
TerminalHandler
(
runner
)
..
setupTerminal
()
..
setupTerminal
()
..
registerSignalHandlers
();
..
registerSignalHandlers
();
}));
}));
result
=
await
runner
.
attach
(
result
=
await
runner
.
attach
(
appStartedCompleter:
onAppStart
,
appStartedCompleter:
onAppStart
,
);
);
if
(
result
!=
0
)
{
throwToolExit
(
null
,
exitCode:
result
);
}
terminalHandler
?.
stop
();
assert
(
result
!=
null
);
assert
(
result
!=
null
);
}
if
(
runner
.
exited
||
!
runner
.
isWaitingForObservatory
)
{
if
(
result
!=
0
)
{
break
;
throwToolExit
(
null
,
exitCode:
result
);
}
printStatus
(
'Waiting for a new connection from Flutter on
${device.name}
...'
);
}
}
}
finally
{
}
finally
{
final
List
<
ForwardedPort
>
ports
=
device
.
portForwarder
.
forwardedPorts
.
toList
();
final
List
<
ForwardedPort
>
ports
=
device
.
portForwarder
.
forwardedPorts
.
toList
();
...
@@ -347,6 +330,52 @@ class AttachCommand extends FlutterCommand {
...
@@ -347,6 +330,52 @@ class AttachCommand extends FlutterCommand {
}
}
}
}
Future
<
ResidentRunner
>
createResidentRunner
({
@required
Stream
<
Uri
>
observatoryUris
,
@required
Device
device
,
@required
FlutterProject
flutterProject
,
@required
bool
usesIpv6
,
})
async
{
assert
(
observatoryUris
!=
null
);
assert
(
device
!=
null
);
assert
(
flutterProject
!=
null
);
assert
(
usesIpv6
!=
null
);
final
FlutterDevice
flutterDevice
=
await
FlutterDevice
.
create
(
device
,
flutterProject:
flutterProject
,
trackWidgetCreation:
boolArg
(
'track-widget-creation'
),
fileSystemRoots:
stringsArg
(
'filesystem-root'
),
fileSystemScheme:
stringArg
(
'filesystem-scheme'
),
viewFilter:
stringArg
(
'isolate-filter'
),
target:
stringArg
(
'target'
),
targetModel:
TargetModel
(
stringArg
(
'target-model'
)),
buildMode:
getBuildMode
(),
dartDefines:
dartDefines
,
);
flutterDevice
.
observatoryUris
=
observatoryUris
;
final
List
<
FlutterDevice
>
flutterDevices
=
<
FlutterDevice
>[
flutterDevice
];
final
DebuggingOptions
debuggingOptions
=
DebuggingOptions
.
enabled
(
getBuildInfo
());
return
getBuildInfo
().
isDebug
?
hotRunnerFactory
.
build
(
flutterDevices
,
target:
targetFile
,
debuggingOptions:
debuggingOptions
,
packagesFilePath:
globalResults
[
'packages'
]
as
String
,
projectRootPath:
stringArg
(
'project-root'
),
dillOutputPath:
stringArg
(
'output-dill'
),
ipv6:
usesIpv6
,
flutterProject:
flutterProject
,
)
:
ColdRunner
(
flutterDevices
,
target:
targetFile
,
debuggingOptions:
debuggingOptions
,
ipv6:
usesIpv6
,
);
}
Future
<
void
>
_validateArguments
()
async
{
}
Future
<
void
>
_validateArguments
()
async
{
}
}
}
...
...
packages/flutter_tools/lib/src/devfs.dart
View file @
6d77996d
...
@@ -11,6 +11,7 @@ import 'asset.dart';
...
@@ -11,6 +11,7 @@ import 'asset.dart';
import
'base/context.dart'
;
import
'base/context.dart'
;
import
'base/file_system.dart'
;
import
'base/file_system.dart'
;
import
'base/io.dart'
;
import
'base/io.dart'
;
import
'base/net.dart'
;
import
'build_info.dart'
;
import
'build_info.dart'
;
import
'bundle.dart'
;
import
'bundle.dart'
;
import
'compile.dart'
;
import
'compile.dart'
;
...
@@ -265,17 +266,20 @@ class DevFSException implements Exception {
...
@@ -265,17 +266,20 @@ class DevFSException implements Exception {
class
_DevFSHttpWriter
{
class
_DevFSHttpWriter
{
_DevFSHttpWriter
(
this
.
fsName
,
VMService
serviceProtocol
)
_DevFSHttpWriter
(
this
.
fsName
,
VMService
serviceProtocol
)
:
httpAddress
=
serviceProtocol
.
httpAddress
;
:
httpAddress
=
serviceProtocol
.
httpAddress
,
_client
=
(
context
.
get
<
HttpClientFactory
>()
==
null
)
?
HttpClient
()
:
context
.
get
<
HttpClientFactory
>()();
final
String
fsName
;
final
String
fsName
;
final
Uri
httpAddress
;
final
Uri
httpAddress
;
final
HttpClient
_client
;
static
const
int
kMaxInFlight
=
6
;
static
const
int
kMaxInFlight
=
6
;
int
_inFlight
=
0
;
int
_inFlight
=
0
;
Map
<
Uri
,
DevFSContent
>
_outstanding
;
Map
<
Uri
,
DevFSContent
>
_outstanding
;
Completer
<
void
>
_completer
;
Completer
<
void
>
_completer
;
final
HttpClient
_client
=
HttpClient
();
Future
<
void
>
write
(
Map
<
Uri
,
DevFSContent
>
entries
)
async
{
Future
<
void
>
write
(
Map
<
Uri
,
DevFSContent
>
entries
)
async
{
_client
.
maxConnectionsPerHost
=
kMaxInFlight
;
_client
.
maxConnectionsPerHost
=
kMaxInFlight
;
...
...
packages/flutter_tools/lib/src/protocol_discovery.dart
View file @
6d77996d
...
@@ -17,16 +17,23 @@ class ProtocolDiscovery {
...
@@ -17,16 +17,23 @@ class ProtocolDiscovery {
this
.
logReader
,
this
.
logReader
,
this
.
serviceName
,
{
this
.
serviceName
,
{
this
.
portForwarder
,
this
.
portForwarder
,
this
.
throttleDuration
,
this
.
hostPort
,
this
.
hostPort
,
this
.
devicePort
,
this
.
devicePort
,
this
.
ipv6
,
this
.
ipv6
,
})
:
assert
(
logReader
!=
null
)
{
})
:
assert
(
logReader
!=
null
)
_deviceLogSubscription
=
logReader
.
logLines
.
listen
(
_handleLine
);
{
_deviceLogSubscription
=
logReader
.
logLines
.
listen
(
_handleLine
,
onDone:
_stopScrapingLogs
,
);
_uriStreamController
=
StreamController
<
Uri
>.
broadcast
();
}
}
factory
ProtocolDiscovery
.
observatory
(
factory
ProtocolDiscovery
.
observatory
(
DeviceLogReader
logReader
,
{
DeviceLogReader
logReader
,
{
DevicePortForwarder
portForwarder
,
DevicePortForwarder
portForwarder
,
Duration
throttleDuration
=
const
Duration
(
milliseconds:
200
),
@required
int
hostPort
,
@required
int
hostPort
,
@required
int
devicePort
,
@required
int
devicePort
,
@required
bool
ipv6
,
@required
bool
ipv6
,
...
@@ -36,6 +43,7 @@ class ProtocolDiscovery {
...
@@ -36,6 +43,7 @@ class ProtocolDiscovery {
logReader
,
logReader
,
kObservatoryService
,
kObservatoryService
,
portForwarder:
portForwarder
,
portForwarder:
portForwarder
,
throttleDuration:
throttleDuration
,
hostPort:
hostPort
,
hostPort:
hostPort
,
devicePort:
devicePort
,
devicePort:
devicePort
,
ipv6:
ipv6
,
ipv6:
ipv6
,
...
@@ -49,50 +57,70 @@ class ProtocolDiscovery {
...
@@ -49,50 +57,70 @@ class ProtocolDiscovery {
final
int
devicePort
;
final
int
devicePort
;
final
bool
ipv6
;
final
bool
ipv6
;
final
Completer
<
Uri
>
_completer
=
Completer
<
Uri
>();
/// The time to wait before forwarding a new observatory URIs from [logReader].
final
Duration
throttleDuration
;
StreamSubscription
<
String
>
_deviceLogSubscription
;
StreamSubscription
<
String
>
_deviceLogSubscription
;
StreamController
<
Uri
>
_uriStreamController
;
/// The discovered service URI.
/// The discovered service URI.
/// Use [uris] instead.
// TODO(egarciad): replace `uri` for `uris`.
Future
<
Uri
>
get
uri
{
return
uris
.
first
;
}
/// The discovered service URIs.
///
///
/// Port forwarding is only attempted when this is invoked, in case we never
/// When a new observatory URI is available in [logReader],
/// need to port forward.
/// the URIs are forwarded at most once every [throttleDuration].
Future
<
Uri
>
get
uri
async
{
///
final
Uri
rawUri
=
await
_completer
.
future
;
/// Port forwarding is only attempted when this is invoked,
return
await
_forwardPort
(
rawUri
);
/// for each observatory URI in the stream.
Stream
<
Uri
>
get
uris
{
return
_uriStreamController
.
stream
.
transform
(
_throttle
<
Uri
>(
waitDuration:
throttleDuration
,
))
.
asyncMap
<
Uri
>(
_forwardPort
);
}
}
Future
<
void
>
cancel
()
=>
_stopScrapingLogs
();
Future
<
void
>
cancel
()
=>
_stopScrapingLogs
();
Future
<
void
>
_stopScrapingLogs
()
async
{
Future
<
void
>
_stopScrapingLogs
()
async
{
await
_uriStreamController
?.
close
();
await
_deviceLogSubscription
?.
cancel
();
await
_deviceLogSubscription
?.
cancel
();
_deviceLogSubscription
=
null
;
_deviceLogSubscription
=
null
;
}
}
void
_handleLine
(
String
line
)
{
Match
_getPatternMatch
(
String
line
)
{
Uri
uri
;
final
RegExp
r
=
RegExp
(
'
${RegExp.escape(serviceName)}
listening on ((http|
\
/
\
/)[a-zA-Z0-9:/=_
\\
-
\
.
\\
[
\\
]]+)'
);
final
RegExp
r
=
RegExp
(
'
${RegExp.escape(serviceName)}
listening on ((http|
\
/
\
/)[a-zA-Z0-9:/=_
\\
-
\
.
\\
[
\\
]]+)'
);
final
Match
match
=
r
.
firstMatch
(
line
);
return
r
.
firstMatch
(
line
);
}
Uri
_getObservatoryUri
(
String
line
)
{
final
Match
match
=
_getPatternMatch
(
line
);
if
(
match
!=
null
)
{
if
(
match
!=
null
)
{
try
{
return
Uri
.
parse
(
match
[
1
]);
uri
=
Uri
.
parse
(
match
[
1
]);
}
}
on
FormatException
catch
(
error
,
stackTrace
)
{
return
null
;
_stopScrapingLogs
();
}
_completer
.
completeError
(
error
,
stackTrace
);
}
void
_handleLine
(
String
line
)
{
Uri
uri
;
try
{
uri
=
_getObservatoryUri
(
line
);
}
on
FormatException
catch
(
error
,
stackTrace
)
{
_uriStreamController
.
addError
(
error
,
stackTrace
);
}
}
if
(
uri
==
null
)
{
if
(
uri
==
null
)
{
return
;
return
;
}
}
if
(
devicePort
!=
null
&&
uri
.
port
!=
devicePort
)
{
if
(
devicePort
!=
null
&&
uri
.
port
!=
devicePort
)
{
printTrace
(
'skipping potential observatory
$uri
due to device port mismatch'
);
printTrace
(
'skipping potential observatory
$uri
due to device port mismatch'
);
return
;
return
;
}
}
_uriStreamController
.
add
(
uri
);
assert
(!
_completer
.
isCompleted
);
_stopScrapingLogs
();
_completer
.
complete
(
uri
);
}
}
Future
<
Uri
>
_forwardPort
(
Uri
deviceUri
)
async
{
Future
<
Uri
>
_forwardPort
(
Uri
deviceUri
)
async
{
...
@@ -110,7 +138,43 @@ class ProtocolDiscovery {
...
@@ -110,7 +138,43 @@ class ProtocolDiscovery {
if
(
ipv6
)
{
if
(
ipv6
)
{
hostUri
=
hostUri
.
replace
(
host:
InternetAddress
.
loopbackIPv6
.
host
);
hostUri
=
hostUri
.
replace
(
host:
InternetAddress
.
loopbackIPv6
.
host
);
}
}
return
hostUri
;
return
hostUri
;
}
}
}
}
/// This transformer will produce an event at most once every [waitDuration].
///
/// For example, consider a `waitDuration` of `10ms`, and list of event names
/// and arrival times: `a (0ms), b (5ms), c (11ms), d (21ms)`.
/// The events `c` and `d` will be produced as a result.
StreamTransformer
<
S
,
S
>
_throttle
<
S
>({
@required
Duration
waitDuration
,
})
{
assert
(
waitDuration
!=
null
);
S
latestLine
;
int
lastExecution
;
Future
<
void
>
throttleFuture
;
return
StreamTransformer
<
S
,
S
>
.
fromHandlers
(
handleData:
(
S
value
,
EventSink
<
S
>
sink
)
{
latestLine
=
value
;
final
int
currentTime
=
DateTime
.
now
().
millisecondsSinceEpoch
;
lastExecution
??=
currentTime
;
final
int
remainingTime
=
currentTime
-
lastExecution
;
final
int
nextExecutionTime
=
remainingTime
>
waitDuration
.
inMilliseconds
?
0
:
waitDuration
.
inMilliseconds
-
remainingTime
;
throttleFuture
??=
Future
<
void
>
.
delayed
(
Duration
(
milliseconds:
nextExecutionTime
))
.
whenComplete
(()
{
sink
.
add
(
latestLine
);
throttleFuture
=
null
;
lastExecution
=
DateTime
.
now
().
millisecondsSinceEpoch
;
});
}
);
}
packages/flutter_tools/lib/src/resident_runner.dart
View file @
6d77996d
...
@@ -131,16 +131,20 @@ class FlutterDevice {
...
@@ -131,16 +131,20 @@ class FlutterDevice {
final
Device
device
;
final
Device
device
;
final
ResidentCompiler
generator
;
final
ResidentCompiler
generator
;
List
<
Uri
>
observatoryUris
;
Stream
<
Uri
>
observatoryUris
;
List
<
VMService
>
vmServices
;
List
<
VMService
>
vmServices
;
DevFS
devFS
;
DevFS
devFS
;
ApplicationPackage
package
;
ApplicationPackage
package
;
List
<
String
>
fileSystemRoots
;
List
<
String
>
fileSystemRoots
;
String
fileSystemScheme
;
String
fileSystemScheme
;
StreamSubscription
<
String
>
_loggingSubscription
;
StreamSubscription
<
String
>
_loggingSubscription
;
bool
_isListeningForObservatoryUri
;
final
String
viewFilter
;
final
String
viewFilter
;
final
bool
trackWidgetCreation
;
final
bool
trackWidgetCreation
;
/// Whether the stream [observatoryUris] is still open.
bool
get
isWaitingForObservatory
=>
_isListeningForObservatoryUri
??
false
;
/// If the [reloadSources] parameter is not null the 'reloadSources' service
/// If the [reloadSources] parameter is not null the 'reloadSources' service
/// will be registered.
/// will be registered.
/// The 'reloadSources' service can be used by other Service Protocol clients
/// The 'reloadSources' service can be used by other Service Protocol clients
...
@@ -154,23 +158,50 @@ class FlutterDevice {
...
@@ -154,23 +158,50 @@ class FlutterDevice {
ReloadSources
reloadSources
,
ReloadSources
reloadSources
,
Restart
restart
,
Restart
restart
,
CompileExpression
compileExpression
,
CompileExpression
compileExpression
,
})
async
{
})
{
if
(
vmServices
!=
null
)
{
final
Completer
<
void
>
completer
=
Completer
<
void
>();
return
;
StreamSubscription
<
void
>
subscription
;
}
bool
isWaitingForVm
=
false
;
final
List
<
VMService
>
localVmServices
=
List
<
VMService
>(
observatoryUris
.
length
);
for
(
int
i
=
0
;
i
<
observatoryUris
.
length
;
i
+=
1
)
{
subscription
=
observatoryUris
.
listen
((
Uri
observatoryUri
)
async
{
printTrace
(
'Connecting to service protocol:
${observatoryUris[i]}
'
);
// FYI, this message is used as a sentinel in tests.
localVmServices
[
i
]
=
await
VMService
.
connect
(
printTrace
(
'Connecting to service protocol:
$observatoryUri
'
);
observatoryUris
[
i
],
isWaitingForVm
=
true
;
reloadSources:
reloadSources
,
VMService
service
;
restart:
restart
,
compileExpression:
compileExpression
,
try
{
);
service
=
await
VMService
.
connect
(
printTrace
(
'Successfully connected to service protocol:
${observatoryUris[i]}
'
);
observatoryUri
,
}
reloadSources:
reloadSources
,
vmServices
=
localVmServices
;
restart:
restart
,
device
.
getLogReader
(
app:
package
).
connectedVMServices
=
vmServices
;
compileExpression:
compileExpression
,
);
}
on
Exception
catch
(
exception
)
{
printTrace
(
'Fail to connect to service protocol:
$observatoryUri
:
$exception
'
);
if
(!
completer
.
isCompleted
&&
!
_isListeningForObservatoryUri
)
{
completer
.
completeError
(
'failed to connect to
$observatoryUri
'
);
}
return
;
}
if
(
completer
.
isCompleted
)
{
return
;
}
printTrace
(
'Successfully connected to service protocol:
$observatoryUri
'
);
vmServices
=
<
VMService
>[
service
];
device
.
getLogReader
(
app:
package
).
connectedVMServices
=
vmServices
;
completer
.
complete
();
await
subscription
.
cancel
();
},
onError:
(
dynamic
error
)
{
printTrace
(
'Fail to handle observatory URI:
$error
'
);
},
onDone:
()
{
_isListeningForObservatoryUri
=
false
;
if
(!
completer
.
isCompleted
&&
!
isWaitingForVm
)
{
completer
.
completeError
(
'connection to device ended too early'
);
}
});
_isListeningForObservatoryUri
=
true
;
return
completer
.
future
;
}
}
Future
<
void
>
refreshViews
()
async
{
Future
<
void
>
refreshViews
()
async
{
...
@@ -221,6 +252,7 @@ class FlutterDevice {
...
@@ -221,6 +252,7 @@ class FlutterDevice {
if
(
flutterViews
.
any
((
FlutterView
view
)
{
if
(
flutterViews
.
any
((
FlutterView
view
)
{
return
view
!=
null
&&
return
view
!=
null
&&
view
.
uiIsolate
!=
null
&&
view
.
uiIsolate
!=
null
&&
view
.
uiIsolate
.
pauseEvent
!=
null
&&
view
.
uiIsolate
.
pauseEvent
.
isPauseEvent
;
view
.
uiIsolate
.
pauseEvent
.
isPauseEvent
;
}
}
))
{
))
{
...
@@ -431,9 +463,13 @@ class FlutterDevice {
...
@@ -431,9 +463,13 @@ class FlutterDevice {
return
2
;
return
2
;
}
}
if
(
result
.
hasObservatory
)
{
if
(
result
.
hasObservatory
)
{
observatoryUris
=
<
Uri
>[
result
.
observatoryUri
];
observatoryUris
=
Stream
<
Uri
>
.
value
(
result
.
observatoryUri
)
.
asBroadcastStream
();
}
else
{
}
else
{
observatoryUris
=
<
Uri
>[];
observatoryUris
=
const
Stream
<
Uri
>
.
empty
()
.
asBroadcastStream
();
}
}
return
0
;
return
0
;
}
}
...
@@ -491,9 +527,13 @@ class FlutterDevice {
...
@@ -491,9 +527,13 @@ class FlutterDevice {
return
2
;
return
2
;
}
}
if
(
result
.
hasObservatory
)
{
if
(
result
.
hasObservatory
)
{
observatoryUris
=
<
Uri
>[
result
.
observatoryUri
];
observatoryUris
=
Stream
<
Uri
>
.
value
(
result
.
observatoryUri
)
.
asBroadcastStream
();
}
else
{
}
else
{
observatoryUris
=
<
Uri
>[];
observatoryUris
=
const
Stream
<
Uri
>
.
empty
()
.
asBroadcastStream
();
}
}
return
0
;
return
0
;
}
}
...
@@ -613,14 +653,21 @@ abstract class ResidentRunner {
...
@@ -613,14 +653,21 @@ abstract class ResidentRunner {
/// The parent location of the incremental artifacts.
/// The parent location of the incremental artifacts.
@visibleForTesting
@visibleForTesting
final
Directory
artifactDirectory
;
final
Directory
artifactDirectory
;
final
Completer
<
int
>
_finished
=
Completer
<
int
>();
final
String
packagesFilePath
;
final
String
packagesFilePath
;
final
String
projectRootPath
;
final
String
projectRootPath
;
final
String
mainPath
;
final
String
mainPath
;
final
AssetBundle
assetBundle
;
final
AssetBundle
assetBundle
;
bool
_exited
=
false
;
bool
_exited
=
false
;
bool
hotMode
;
Completer
<
int
>
_finished
=
Completer
<
int
>();
bool
hotMode
;
/// Returns true if every device is streaming observatory URIs.
bool
get
isWaitingForObservatory
{
return
flutterDevices
.
every
((
FlutterDevice
device
)
{
return
device
.
isWaitingForObservatory
;
});
}
String
get
dillOutputPath
=>
_dillOutputPath
??
fs
.
path
.
join
(
artifactDirectory
.
path
,
'app.dill'
);
String
get
dillOutputPath
=>
_dillOutputPath
??
fs
.
path
.
join
(
artifactDirectory
.
path
,
'app.dill'
);
String
getReloadPath
({
bool
fullRestart
})
=>
mainPath
+
(
fullRestart
?
''
:
'.incremental'
)
+
'.dill'
;
String
getReloadPath
({
bool
fullRestart
})
=>
mainPath
+
(
fullRestart
?
''
:
'.incremental'
)
+
'.dill'
;
...
@@ -631,6 +678,9 @@ abstract class ResidentRunner {
...
@@ -631,6 +678,9 @@ abstract class ResidentRunner {
bool
get
isRunningRelease
=>
debuggingOptions
.
buildInfo
.
isRelease
;
bool
get
isRunningRelease
=>
debuggingOptions
.
buildInfo
.
isRelease
;
bool
get
supportsServiceProtocol
=>
isRunningDebug
||
isRunningProfile
;
bool
get
supportsServiceProtocol
=>
isRunningDebug
||
isRunningProfile
;
/// Returns [true] if the resident runner exited after invoking [exit()].
bool
get
exited
=>
_exited
;
/// Whether this runner can hot restart.
/// Whether this runner can hot restart.
///
///
/// To prevent scenarios where only a subset of devices are hot restarted,
/// To prevent scenarios where only a subset of devices are hot restarted,
...
@@ -862,6 +912,8 @@ abstract class ResidentRunner {
...
@@ -862,6 +912,8 @@ abstract class ResidentRunner {
throw
'The service protocol is not enabled.'
;
throw
'The service protocol is not enabled.'
;
}
}
_finished
??=
Completer
<
int
>();
bool
viewFound
=
false
;
bool
viewFound
=
false
;
for
(
FlutterDevice
device
in
flutterDevices
)
{
for
(
FlutterDevice
device
in
flutterDevices
)
{
await
device
.
connect
(
await
device
.
connect
(
...
@@ -912,22 +964,25 @@ abstract class ResidentRunner {
...
@@ -912,22 +964,25 @@ abstract class ResidentRunner {
// User requested the application exit.
// User requested the application exit.
return
;
return
;
}
}
if
(
_finished
.
isCompleted
)
{
if
(
_finished
==
null
||
_finished
.
isCompleted
)
{
return
;
return
;
}
}
printStatus
(
'Lost connection to device.'
);
printStatus
(
'Lost connection to device.'
);
_finished
.
complete
(
0
);
_finished
.
complete
(
0
);
_finished
=
null
;
}
}
void
appFinished
()
{
void
appFinished
()
{
if
(
_finished
.
isCompleted
)
{
if
(
_finished
==
null
||
_finished
.
isCompleted
)
{
return
;
return
;
}
}
printStatus
(
'Application finished.'
);
printStatus
(
'Application finished.'
);
_finished
.
complete
(
0
);
_finished
.
complete
(
0
);
_finished
=
null
;
}
}
Future
<
int
>
waitForAppToFinish
()
async
{
Future
<
int
>
waitForAppToFinish
()
async
{
_finished
??=
Completer
<
int
>();
final
int
exitCode
=
await
_finished
.
future
;
final
int
exitCode
=
await
_finished
.
future
;
assert
(
exitCode
!=
null
);
assert
(
exitCode
!=
null
);
await
cleanupAtFinish
();
await
cleanupAtFinish
();
...
@@ -1045,15 +1100,33 @@ class TerminalHandler {
...
@@ -1045,15 +1100,33 @@ class TerminalHandler {
subscription
=
terminal
.
keystrokes
.
listen
(
processTerminalInput
);
subscription
=
terminal
.
keystrokes
.
listen
(
processTerminalInput
);
}
}
final
Map
<
io
.
ProcessSignal
,
Object
>
_signalTokens
=
<
io
.
ProcessSignal
,
Object
>{};
void
_addSignalHandler
(
io
.
ProcessSignal
signal
,
SignalHandler
handler
)
{
_signalTokens
[
signal
]
=
signals
.
addHandler
(
signal
,
handler
);
}
void
registerSignalHandlers
()
{
void
registerSignalHandlers
()
{
assert
(
residentRunner
.
stayResident
);
assert
(
residentRunner
.
stayResident
);
signals
.
addHandler
(
io
.
ProcessSignal
.
SIGINT
,
_cleanUp
);
signals
.
addHandler
(
io
.
ProcessSignal
.
SIGTERM
,
_cleanUp
);
_addSignalHandler
(
io
.
ProcessSignal
.
SIGINT
,
_cleanUp
);
_addSignalHandler
(
io
.
ProcessSignal
.
SIGTERM
,
_cleanUp
);
if
(!
residentRunner
.
supportsServiceProtocol
||
!
residentRunner
.
supportsRestart
)
{
if
(!
residentRunner
.
supportsServiceProtocol
||
!
residentRunner
.
supportsRestart
)
{
return
;
return
;
}
}
signals
.
addHandler
(
io
.
ProcessSignal
.
SIGUSR1
,
_handleSignal
);
_addSignalHandler
(
io
.
ProcessSignal
.
SIGUSR1
,
_handleSignal
);
signals
.
addHandler
(
io
.
ProcessSignal
.
SIGUSR2
,
_handleSignal
);
_addSignalHandler
(
io
.
ProcessSignal
.
SIGUSR2
,
_handleSignal
);
}
/// Unregisters terminal signal and keystroke handlers.
void
stop
()
{
assert
(
residentRunner
.
stayResident
);
for
(
MapEntry
<
io
.
ProcessSignal
,
Object
>
entry
in
_signalTokens
.
entries
)
{
signals
.
removeHandler
(
entry
.
key
,
entry
.
value
);
}
_signalTokens
.
clear
();
subscription
.
cancel
();
}
}
/// Returns [true] if the input has been handled by this function.
/// Returns [true] if the input has been handled by this function.
...
...
packages/flutter_tools/lib/src/run_cold.dart
View file @
6d77996d
...
@@ -83,7 +83,7 @@ class ColdRunner extends ResidentRunner {
...
@@ -83,7 +83,7 @@ class ColdRunner extends ResidentRunner {
if
(
flutterDevices
.
first
.
observatoryUris
!=
null
)
{
if
(
flutterDevices
.
first
.
observatoryUris
!=
null
)
{
// For now, only support one debugger connection.
// For now, only support one debugger connection.
connectionInfoCompleter
?.
complete
(
DebugConnectionInfo
(
connectionInfoCompleter
?.
complete
(
DebugConnectionInfo
(
httpUri:
flutterDevices
.
first
.
observatoryUris
.
first
,
httpUri:
flutterDevices
.
first
.
vmServices
.
first
.
httpAddress
,
wsUri:
flutterDevices
.
first
.
vmServices
.
first
.
wsAddress
,
wsUri:
flutterDevices
.
first
.
vmServices
.
first
.
wsAddress
,
));
));
}
}
...
@@ -184,9 +184,8 @@ class ColdRunner extends ResidentRunner {
...
@@ -184,9 +184,8 @@ class ColdRunner extends ResidentRunner {
for
(
FlutterDevice
device
in
flutterDevices
)
{
for
(
FlutterDevice
device
in
flutterDevices
)
{
final
String
dname
=
device
.
device
.
name
;
final
String
dname
=
device
.
device
.
name
;
if
(
device
.
observatoryUris
!=
null
)
{
if
(
device
.
observatoryUris
!=
null
)
{
for
(
Uri
uri
in
device
.
observatoryUris
)
{
for
(
VMService
vm
in
device
.
vmServices
)
{
printStatus
(
'An Observatory debugger and profiler on
$dname
is available at
$uri
'
);
printStatus
(
'An Observatory debugger and profiler on
$dname
is available at:
${vm.wsAddress}
'
);
haveAnything
=
true
;
}
}
}
}
}
}
...
...
packages/flutter_tools/lib/src/run_hot.dart
View file @
6d77996d
...
@@ -180,7 +180,7 @@ class HotRunner extends ResidentRunner {
...
@@ -180,7 +180,7 @@ class HotRunner extends ResidentRunner {
// Only handle one debugger connection.
// Only handle one debugger connection.
connectionInfoCompleter
.
complete
(
connectionInfoCompleter
.
complete
(
DebugConnectionInfo
(
DebugConnectionInfo
(
httpUri:
flutterDevices
.
first
.
observatoryUris
.
first
,
httpUri:
flutterDevices
.
first
.
vmServices
.
first
.
httpAddress
,
wsUri:
flutterDevices
.
first
.
vmServices
.
first
.
wsAddress
,
wsUri:
flutterDevices
.
first
.
vmServices
.
first
.
wsAddress
,
baseUri:
baseUris
.
first
.
toString
(),
baseUri:
baseUris
.
first
.
toString
(),
),
),
...
@@ -987,8 +987,8 @@ class HotRunner extends ResidentRunner {
...
@@ -987,8 +987,8 @@ class HotRunner extends ResidentRunner {
printStatus
(
message
);
printStatus
(
message
);
for
(
FlutterDevice
device
in
flutterDevices
)
{
for
(
FlutterDevice
device
in
flutterDevices
)
{
final
String
dname
=
device
.
device
.
name
;
final
String
dname
=
device
.
device
.
name
;
for
(
Uri
uri
in
device
.
observatoryUri
s
)
{
for
(
VMService
vm
in
device
.
vmService
s
)
{
printStatus
(
'An Observatory debugger and profiler on
$dname
is available at:
$
uri
'
);
printStatus
(
'An Observatory debugger and profiler on
$dname
is available at:
$
{vm.wsAddress}
'
);
}
}
}
}
final
String
quitMessage
=
_didAttach
final
String
quitMessage
=
_didAttach
...
...
packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
View file @
6d77996d
...
@@ -7,24 +7,31 @@ import 'dart:async';
...
@@ -7,24 +7,31 @@ import 'dart:async';
import
'package:file/memory.dart'
;
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/base/common.dart'
;
import
'package:flutter_tools/src/base/common.dart'
;
import
'package:flutter_tools/src/base/file_system.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/logger.dart'
;
import
'package:flutter_tools/src/base/net.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/base/terminal.dart'
;
import
'package:flutter_tools/src/base/terminal.dart'
;
import
'package:flutter_tools/src/cache.dart'
;
import
'package:flutter_tools/src/cache.dart'
;
import
'package:flutter_tools/src/commands/attach.dart'
;
import
'package:flutter_tools/src/commands/attach.dart'
;
import
'package:flutter_tools/src/convert.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/ios/devices.dart'
;
import
'package:flutter_tools/src/ios/devices.dart'
;
import
'package:flutter_tools/src/mdns_discovery.dart'
;
import
'package:flutter_tools/src/mdns_discovery.dart'
;
import
'package:flutter_tools/src/project.dart'
;
import
'package:flutter_tools/src/resident_runner.dart'
;
import
'package:flutter_tools/src/resident_runner.dart'
;
import
'package:flutter_tools/src/run_hot.dart'
;
import
'package:flutter_tools/src/run_hot.dart'
;
import
'package:flutter_tools/src/vmservice.dart'
;
import
'package:meta/meta.dart'
;
import
'package:meta/meta.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:process/process.dart'
;
import
'package:process/process.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'../../src/common.dart'
;
import
'../../src/common.dart'
;
import
'../../src/context.dart'
;
import
'../../src/context.dart'
;
import
'../../src/mocks.dart'
;
import
'../../src/mocks.dart'
;
void
main
(
)
{
void
main
(
)
{
group
(
'attach'
,
()
{
group
(
'attach'
,
()
{
StreamLogger
logger
;
StreamLogger
logger
;
...
@@ -49,11 +56,16 @@ void main() {
...
@@ -49,11 +56,16 @@ void main() {
MockDeviceLogReader
mockLogReader
;
MockDeviceLogReader
mockLogReader
;
MockPortForwarder
portForwarder
;
MockPortForwarder
portForwarder
;
MockAndroidDevice
device
;
MockAndroidDevice
device
;
MockProcessManager
mockProcessManager
;
MockHttpClient
httpClient
;
Completer
<
void
>
vmServiceDoneCompleter
;
setUp
(()
{
setUp
(()
{
mockProcessManager
=
MockProcessManager
();
mockLogReader
=
MockDeviceLogReader
();
mockLogReader
=
MockDeviceLogReader
();
portForwarder
=
MockPortForwarder
();
portForwarder
=
MockPortForwarder
();
device
=
MockAndroidDevice
();
device
=
MockAndroidDevice
();
vmServiceDoneCompleter
=
Completer
<
void
>();
when
(
device
.
portForwarder
)
when
(
device
.
portForwarder
)
.
thenReturn
(
portForwarder
);
.
thenReturn
(
portForwarder
);
when
(
portForwarder
.
forward
(
devicePort
,
hostPort:
anyNamed
(
'hostPort'
)))
when
(
portForwarder
.
forward
(
devicePort
,
hostPort:
anyNamed
(
'hostPort'
)))
...
@@ -63,6 +75,14 @@ void main() {
...
@@ -63,6 +75,14 @@ void main() {
when
(
portForwarder
.
unforward
(
any
))
when
(
portForwarder
.
unforward
(
any
))
.
thenAnswer
((
_
)
async
=>
null
);
.
thenAnswer
((
_
)
async
=>
null
);
final
HttpClientRequest
httpClientRequest
=
MockHttpClientRequest
();
httpClient
=
MockHttpClient
();
when
(
httpClient
.
putUrl
(
any
))
.
thenAnswer
((
_
)
=>
Future
<
HttpClientRequest
>.
value
(
httpClientRequest
));
when
(
httpClientRequest
.
headers
).
thenReturn
(
MockHttpHeaders
());
when
(
httpClientRequest
.
close
())
.
thenAnswer
((
_
)
=>
Future
<
HttpClientResponse
>.
value
(
MockHttpClientResponse
()));
// We cannot add the device to a device manager because that is
// We cannot add the device to a device manager because that is
// only enabled by the context of each testUsingContext call.
// only enabled by the context of each testUsingContext call.
//
//
...
@@ -77,17 +97,15 @@ void main() {
...
@@ -77,17 +97,15 @@ void main() {
testUsingContext
(
'finds observatory port and forwards'
,
()
async
{
testUsingContext
(
'finds observatory port and forwards'
,
()
async
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
// Now that the reader is used, start writing messages to it.
// Now that the reader is used, start writing messages to it.
Timer
.
run
(()
{
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
});
return
mockLogReader
;
return
mockLogReader
;
});
});
testDeviceManager
.
addDevice
(
device
);
testDeviceManager
.
addDevice
(
device
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
final
Completer
<
void
>
completer
=
Completer
<
void
>();
final
StreamSubscription
<
String
>
loggerSubscription
=
logger
.
stream
.
listen
((
String
message
)
{
final
StreamSubscription
<
String
>
loggerSubscription
=
logger
.
stream
.
listen
((
String
message
)
{
if
(
message
==
'[
stdout] Done.
'
)
{
if
(
message
==
'[
verbose] Observatory URL on device: http://127.0.0.1:
$devicePort
'
)
{
// The "
Done." message is output by the AttachCommand when it's done
.
// The "
Observatory URL on device" message is output by the ProtocolDiscovery when it found the observatory
.
completer
.
complete
();
completer
.
complete
();
}
}
});
});
...
@@ -96,6 +114,7 @@ void main() {
...
@@ -96,6 +114,7 @@ void main() {
verify
(
verify
(
portForwarder
.
forward
(
devicePort
,
hostPort:
anyNamed
(
'hostPort'
)),
portForwarder
.
forward
(
devicePort
,
hostPort:
anyNamed
(
'hostPort'
)),
).
called
(
1
);
).
called
(
1
);
await
mockLogReader
.
dispose
();
await
expectLoggerInterruptEndsTask
(
task
,
logger
);
await
expectLoggerInterruptEndsTask
(
task
,
logger
);
await
loggerSubscription
.
cancel
();
await
loggerSubscription
.
cancel
();
},
overrides:
<
Type
,
Generator
>{
},
overrides:
<
Type
,
Generator
>{
...
@@ -104,13 +123,116 @@ void main() {
...
@@ -104,13 +123,116 @@ void main() {
Logger:
()
=>
logger
,
Logger:
()
=>
logger
,
});
});
testUsingContext
(
'finds all observatory ports and forwards them'
,
()
async
{
testFileSystem
.
file
(
testFileSystem
.
path
.
join
(
'.packages'
)).
createSync
();
testFileSystem
.
file
(
testFileSystem
.
path
.
join
(
'lib'
,
'main.dart'
)).
createSync
();
testFileSystem
.
file
(
testFileSystem
.
path
.
join
(
'build'
,
'flutter_assets'
,
'AssetManifest.json'
))
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
'{}'
);
when
(
device
.
name
).
thenReturn
(
'MockAndroidDevice'
);
when
(
device
.
getLogReader
()).
thenReturn
(
mockLogReader
);
final
Process
dartProcess
=
MockProcess
();
final
StreamController
<
List
<
int
>>
compilerStdoutController
=
StreamController
<
List
<
int
>>();
when
(
dartProcess
.
stdout
).
thenAnswer
((
_
)
=>
compilerStdoutController
.
stream
);
when
(
dartProcess
.
stderr
)
.
thenAnswer
((
_
)
=>
Stream
<
List
<
int
>>.
fromFuture
(
Future
<
List
<
int
>>.
value
(
const
<
int
>[])));
when
(
dartProcess
.
stdin
).
thenAnswer
((
_
)
=>
MockStdIn
());
final
Completer
<
int
>
dartProcessExitCode
=
Completer
<
int
>();
when
(
dartProcess
.
exitCode
).
thenAnswer
((
_
)
=>
dartProcessExitCode
.
future
);
when
(
mockProcessManager
.
start
(
any
)).
thenAnswer
((
_
)
=>
Future
<
Process
>.
value
(
dartProcess
));
testDeviceManager
.
addDevice
(
device
);
final
List
<
String
>
observatoryLogs
=
<
String
>[];
await
FakeAsync
().
run
((
FakeAsync
time
)
{
unawaited
(
runZoned
(()
async
{
final
StreamSubscription
<
String
>
loggerSubscription
=
logger
.
stream
.
listen
((
String
message
)
{
// The "Observatory URL on device" message is output by the ProtocolDiscovery when it found the observatory.
if
(
message
.
startsWith
(
'[verbose] Observatory URL on device'
))
{
observatoryLogs
.
add
(
message
);
}
if
(
message
==
'[stdout] Waiting for a connection from Flutter on MockAndroidDevice...'
)
{
observatoryLogs
.
add
(
message
);
}
if
(
message
==
'[stdout] Lost connection to device.'
)
{
observatoryLogs
.
add
(
message
);
}
if
(
message
.
contains
(
'To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".'
))
{
observatoryLogs
.
add
(
message
);
}
});
final
TestHotRunnerFactory
testHotRunnerFactory
=
TestHotRunnerFactory
();
final
Future
<
void
>
task
=
createTestCommandRunner
(
AttachCommand
(
hotRunnerFactory:
testHotRunnerFactory
)
).
run
(<
String
>[
'attach'
]);
// First iteration of the attach loop.
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:0001'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:1234'
);
time
.
elapse
(
const
Duration
(
milliseconds:
200
));
compilerStdoutController
.
add
(
utf8
.
encode
(
'result abc
\n
line1
\n
line2
\n
abc
\n
abc /path/to/main.dart.dill 0
\n
'
));
time
.
flushMicrotasks
();
testHotRunnerFactory
.
fakeAppFinishedUnexpectedly
();
time
.
flushMicrotasks
();
// Second iteration of the attach loop.
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:0002'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:1235'
);
time
.
elapse
(
const
Duration
(
milliseconds:
200
));
compilerStdoutController
.
add
(
utf8
.
encode
(
'result abc
\n
line1
\n
line2
\n
abc
\n
abc /path/to/main.dart.dill 0
\n
'
));
time
.
flushMicrotasks
();
dartProcessExitCode
.
complete
(
0
);
await
loggerSubscription
.
cancel
();
await
testHotRunnerFactory
.
exitApp
();
await
task
;
}));
});
expect
(
observatoryLogs
.
length
,
7
);
expect
(
observatoryLogs
[
0
],
'[stdout] Waiting for a connection from Flutter on MockAndroidDevice...'
);
expect
(
observatoryLogs
[
1
],
'[verbose] Observatory URL on device: http://127.0.0.1:1234'
);
expect
(
observatoryLogs
[
2
],
'[stdout] Lost connection to device.'
);
expect
(
observatoryLogs
[
3
].
contains
(
'To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R"'
),
isTrue
);
expect
(
observatoryLogs
[
4
],
'[verbose] Observatory URL on device: http://127.0.0.1:1235'
);
expect
(
observatoryLogs
[
5
],
'[stdout] Lost connection to device.'
);
expect
(
observatoryLogs
[
6
].
contains
(
'To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R"'
),
isTrue
);
verify
(
portForwarder
.
forward
(
1234
,
hostPort:
anyNamed
(
'hostPort'
))).
called
(
1
);
verify
(
portForwarder
.
forward
(
1235
,
hostPort:
anyNamed
(
'hostPort'
))).
called
(
1
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
testFileSystem
,
HttpClientFactory:
()
=>
()
=>
httpClient
,
ProcessManager:
()
=>
mockProcessManager
,
Logger:
()
=>
logger
,
VMServiceConnector:
()
=>
getFakeVmServiceFactory
(
vmServiceDoneCompleter:
vmServiceDoneCompleter
,
),
});
testUsingContext
(
'Fails with tool exit on bad Observatory uri'
,
()
async
{
testUsingContext
(
'Fails with tool exit on bad Observatory uri'
,
()
async
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
// Now that the reader is used, start writing messages to it.
// Now that the reader is used, start writing messages to it.
Timer
.
run
(()
{
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Observatory listening on http:/:/127.0.0.1:
$devicePort
'
);
mockLogReader
.
addLine
(
'Observatory listening on http:/:/127.0.0.1:
$devicePort
'
);
mockLogReader
.
dispose
();
});
return
mockLogReader
;
return
mockLogReader
;
});
});
testDeviceManager
.
addDevice
(
device
);
testDeviceManager
.
addDevice
(
device
);
...
@@ -125,10 +247,8 @@ void main() {
...
@@ -125,10 +247,8 @@ void main() {
testUsingContext
(
'accepts filesystem parameters'
,
()
async
{
testUsingContext
(
'accepts filesystem parameters'
,
()
async
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
// Now that the reader is used, start writing messages to it.
// Now that the reader is used, start writing messages to it.
Timer
.
run
(()
{
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
});
return
mockLogReader
;
return
mockLogReader
;
});
});
testDeviceManager
.
addDevice
(
device
);
testDeviceManager
.
addDevice
(
device
);
...
@@ -141,6 +261,8 @@ void main() {
...
@@ -141,6 +261,8 @@ void main() {
final
MockHotRunner
mockHotRunner
=
MockHotRunner
();
final
MockHotRunner
mockHotRunner
=
MockHotRunner
();
when
(
mockHotRunner
.
attach
(
appStartedCompleter:
anyNamed
(
'appStartedCompleter'
)))
when
(
mockHotRunner
.
attach
(
appStartedCompleter:
anyNamed
(
'appStartedCompleter'
)))
.
thenAnswer
((
_
)
async
=>
0
);
.
thenAnswer
((
_
)
async
=>
0
);
when
(
mockHotRunner
.
exited
).
thenReturn
(
false
);
when
(
mockHotRunner
.
isWaitingForObservatory
).
thenReturn
(
false
);
final
MockHotRunnerFactory
mockHotRunnerFactory
=
MockHotRunnerFactory
();
final
MockHotRunnerFactory
mockHotRunnerFactory
=
MockHotRunnerFactory
();
when
(
when
(
...
@@ -204,10 +326,8 @@ void main() {
...
@@ -204,10 +326,8 @@ void main() {
testUsingContext
(
'exits when ipv6 is specified and debug-port is not'
,
()
async
{
testUsingContext
(
'exits when ipv6 is specified and debug-port is not'
,
()
async
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
// Now that the reader is used, start writing messages to it.
// Now that the reader is used, start writing messages to it.
Timer
.
run
(()
{
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
});
return
mockLogReader
;
return
mockLogReader
;
});
});
testDeviceManager
.
addDevice
(
device
);
testDeviceManager
.
addDevice
(
device
);
...
@@ -228,10 +348,8 @@ void main() {
...
@@ -228,10 +348,8 @@ void main() {
testUsingContext
(
'exits when observatory-port is specified and debug-port is not'
,
()
async
{
testUsingContext
(
'exits when observatory-port is specified and debug-port is not'
,
()
async
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
when
(
device
.
getLogReader
()).
thenAnswer
((
_
)
{
// Now that the reader is used, start writing messages to it.
// Now that the reader is used, start writing messages to it.
Timer
.
run
(()
{
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
});
return
mockLogReader
;
return
mockLogReader
;
});
});
testDeviceManager
.
addDevice
(
device
);
testDeviceManager
.
addDevice
(
device
);
...
@@ -250,7 +368,6 @@ void main() {
...
@@ -250,7 +368,6 @@ void main() {
},);
},);
});
});
testUsingContext
(
'selects specified target'
,
()
async
{
testUsingContext
(
'selects specified target'
,
()
async
{
const
int
devicePort
=
499
;
const
int
devicePort
=
499
;
const
int
hostPort
=
42
;
const
int
hostPort
=
42
;
...
@@ -277,16 +394,16 @@ void main() {
...
@@ -277,16 +394,16 @@ void main() {
flutterProject:
anyNamed
(
'flutterProject'
),
flutterProject:
anyNamed
(
'flutterProject'
),
ipv6:
false
,
ipv6:
false
,
)).
thenReturn
(
mockHotRunner
);
)).
thenReturn
(
mockHotRunner
);
when
(
mockHotRunner
.
exited
).
thenReturn
(
false
);
when
(
mockHotRunner
.
isWaitingForObservatory
).
thenReturn
(
false
);
testDeviceManager
.
addDevice
(
device
);
testDeviceManager
.
addDevice
(
device
);
when
(
device
.
getLogReader
())
when
(
device
.
getLogReader
())
.
thenAnswer
((
_
)
{
.
thenAnswer
((
_
)
{
// Now that the reader is used, start writing messages to it.
// Now that the reader is used, start writing messages to it.
Timer
.
run
(()
{
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
'Foo'
);
mockLogReader
.
addLine
(
mockLogReader
.
addLine
(
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
'Observatory listening on http://127.0.0.1:
$devicePort
'
);
});
return
mockLogReader
;
return
mockLogReader
;
});
});
final
File
foo
=
fs
.
file
(
'lib/foo.dart'
)
final
File
foo
=
fs
.
file
(
'lib/foo.dart'
)
...
@@ -570,3 +687,89 @@ Future<void> expectLoggerInterruptEndsTask(Future<void> task, StreamLogger logge
...
@@ -570,3 +687,89 @@ Future<void> expectLoggerInterruptEndsTask(Future<void> task, StreamLogger logge
expect
(
error
.
exitCode
,
2
);
// ...with exit code 2.
expect
(
error
.
exitCode
,
2
);
// ...with exit code 2.
}
}
}
}
VMServiceConnector
getFakeVmServiceFactory
(
{
@required
Completer
<
void
>
vmServiceDoneCompleter
,
})
{
assert
(
vmServiceDoneCompleter
!=
null
);
return
(
Uri
httpUri
,
{
ReloadSources
reloadSources
,
Restart
restart
,
CompileExpression
compileExpression
,
CompressionOptions
compression
,
})
async
{
final
VMService
vmService
=
VMServiceMock
();
final
VM
vm
=
VMMock
();
when
(
vmService
.
vm
).
thenReturn
(
vm
);
when
(
vmService
.
isClosed
).
thenReturn
(
false
);
when
(
vmService
.
done
).
thenAnswer
((
_
)
{
return
Future
<
void
>.
value
(
null
);
});
when
(
vm
.
refreshViews
(
waitForViews:
anyNamed
(
'waitForViews'
)))
.
thenAnswer
((
_
)
=>
Future
<
void
>.
value
(
null
));
when
(
vm
.
views
)
.
thenReturn
(<
FlutterView
>[
FlutterViewMock
()]);
when
(
vm
.
createDevFS
(
any
))
.
thenAnswer
((
_
)
=>
Future
<
Map
<
String
,
dynamic
>>.
value
(<
String
,
dynamic
>{
'uri'
:
'/'
,}));
return
vmService
;
};
}
class
TestHotRunnerFactory
extends
HotRunnerFactory
{
HotRunner
_runner
;
@override
HotRunner
build
(
List
<
FlutterDevice
>
devices
,
{
String
target
,
DebuggingOptions
debuggingOptions
,
bool
benchmarkMode
=
false
,
File
applicationBinary
,
bool
hostIsIde
=
false
,
String
projectRootPath
,
String
packagesFilePath
,
String
dillOutputPath
,
bool
stayResident
=
true
,
bool
ipv6
=
false
,
FlutterProject
flutterProject
,
})
{
_runner
??=
HotRunner
(
devices
,
target:
target
,
debuggingOptions:
debuggingOptions
,
benchmarkMode:
benchmarkMode
,
applicationBinary:
applicationBinary
,
hostIsIde:
hostIsIde
,
projectRootPath:
projectRootPath
,
packagesFilePath:
packagesFilePath
,
dillOutputPath:
dillOutputPath
,
stayResident:
stayResident
,
ipv6:
ipv6
,
);
return
_runner
;
}
void
fakeAppFinishedUnexpectedly
()
{
assert
(
_runner
!=
null
);
_runner
.
appFinished
();
}
Future
<
void
>
exitApp
()
async
{
assert
(
_runner
!=
null
);
await
_runner
.
exit
();
}
}
class
VMMock
extends
Mock
implements
VM
{}
class
VMServiceMock
extends
Mock
implements
VMService
{}
class
FlutterViewMock
extends
Mock
implements
FlutterView
{}
class
MockProcessManager
extends
Mock
implements
ProcessManager
{}
class
MockProcess
extends
Mock
implements
Process
{}
class
MockHttpClientRequest
extends
Mock
implements
HttpClientRequest
{}
class
MockHttpClientResponse
extends
Mock
implements
HttpClientResponse
{}
class
MockHttpHeaders
extends
Mock
implements
HttpHeaders
{}
\ No newline at end of file
packages/flutter_tools/test/general.shard/devfs_test.dart
View file @
6d77996d
...
@@ -10,6 +10,7 @@ import 'package:file/file.dart';
...
@@ -10,6 +10,7 @@ import 'package:file/file.dart';
import
'package:file/memory.dart'
;
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/net.dart'
;
import
'package:flutter_tools/src/compile.dart'
;
import
'package:flutter_tools/src/compile.dart'
;
import
'package:flutter_tools/src/devfs.dart'
;
import
'package:flutter_tools/src/devfs.dart'
;
import
'package:flutter_tools/src/vmservice.dart'
;
import
'package:flutter_tools/src/vmservice.dart'
;
...
@@ -159,6 +160,7 @@ void main() {
...
@@ -159,6 +160,7 @@ void main() {
verify
(
httpRequest
.
close
()).
called
(
kFailedAttempts
+
1
);
verify
(
httpRequest
.
close
()).
called
(
kFailedAttempts
+
1
);
},
overrides:
<
Type
,
Generator
>{
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
FileSystem:
()
=>
fs
,
HttpClientFactory:
()
=>
()
=>
httpClient
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
});
});
});
...
@@ -208,6 +210,7 @@ void main() {
...
@@ -208,6 +210,7 @@ void main() {
expect
(
report
.
success
,
true
);
expect
(
report
.
success
,
true
);
},
overrides:
<
Type
,
Generator
>{
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
FileSystem:
()
=>
fs
,
HttpClient:
()
=>
()
=>
HttpClient
(),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
});
...
@@ -310,6 +313,7 @@ void main() {
...
@@ -310,6 +313,7 @@ void main() {
expect
(
devFS
.
lastCompiled
,
isNot
(
previousCompile
));
expect
(
devFS
.
lastCompiled
,
isNot
(
previousCompile
));
},
overrides:
<
Type
,
Generator
>{
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
FileSystem:
()
=>
fs
,
HttpClient:
()
=>
()
=>
HttpClient
(),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
});
});
});
...
...
packages/flutter_tools/test/general.shard/protocol_discovery_test.dart
View file @
6d77996d
...
@@ -6,6 +6,7 @@ import 'dart:async';
...
@@ -6,6 +6,7 @@ import 'dart:async';
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/protocol_discovery.dart'
;
import
'package:flutter_tools/src/protocol_discovery.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'../src/common.dart'
;
import
'../src/common.dart'
;
import
'../src/context.dart'
;
import
'../src/context.dart'
;
...
@@ -16,40 +17,47 @@ void main() {
...
@@ -16,40 +17,47 @@ void main() {
MockDeviceLogReader
logReader
;
MockDeviceLogReader
logReader
;
ProtocolDiscovery
discoverer
;
ProtocolDiscovery
discoverer
;
group
(
'no port forwarding'
,
()
{
/// Performs test set-up functionality that must be performed as part of
int
devicePort
;
/// the `test()` pass and not part of the `setUp()` pass.
///
/// Performs test set-up functionality that must be performed as part of
/// This exists to make sure we're not creating an error that tries to
/// the `test()` pass and not part of the `setUp()` pass.
/// cross an error-zone boundary. Our use of `testUsingContext()` runs the
///
/// test code inside an error zone, but the `setUp()` code is not run in
/// This exists to make sure we're not creating an error that tries to
/// any zone. This creates the potential for errors that try to cross
/// cross an error-zone boundary. Our use of `testUsingContext()` runs the
/// error-zone boundaries, which are considered uncaught.
/// test code inside an error zone, but the `setUp()` code is not run in
///
/// any zone. This creates the potential for errors that try to cross
/// This also exists for cases where our initialization requires access to
/// error-zone boundaries, which are considered uncaught.
/// a `Context` object, which is only set up inside the zone.
///
///
/// This also exists for cases where our initialization requires access to
/// These issues do not pertain to real code and are a test-only concern,
/// a `Context` object, which is only set up inside the zone.
/// because in real code, the zone is set up in `main()`.
///
///
/// These issues do not pertain to real code and are a test-only concern,
/// See also: [runZoned]
/// because in real code, the zone is set up in `main()`.
void
initialize
({
///
int
devicePort
,
/// See also: [runZoned]
Duration
throttleDuration
=
const
Duration
(
milliseconds:
200
),
void
initialize
()
{
})
{
logReader
=
MockDeviceLogReader
();
logReader
=
MockDeviceLogReader
();
discoverer
=
ProtocolDiscovery
.
observatory
(
logReader
,
ipv6:
false
,
hostPort:
null
,
devicePort:
devicePort
);
discoverer
=
ProtocolDiscovery
.
observatory
(
}
logReader
,
ipv6:
false
,
hostPort:
null
,
devicePort:
devicePort
,
throttleDuration:
throttleDuration
,
);
}
testUsingContext
(
'returns non-null uri future'
,
()
async
{
initialize
();
expect
(
discoverer
.
uri
,
isNotNull
);
});
group
(
'no port forwarding'
,
()
{
tearDown
(()
{
tearDown
(()
{
discoverer
.
cancel
();
discoverer
.
cancel
();
logReader
.
dispose
();
logReader
.
dispose
();
});
});
testUsingContext
(
'returns non-null uri future'
,
()
async
{
initialize
();
expect
(
discoverer
.
uri
,
isNotNull
);
});
testUsingContext
(
'discovers uri if logs already produced output'
,
()
async
{
testUsingContext
(
'discovers uri if logs already produced output'
,
()
async
{
initialize
();
initialize
();
logReader
.
addLine
(
'HELLO WORLD'
);
logReader
.
addLine
(
'HELLO WORLD'
);
...
@@ -78,9 +86,7 @@ void main() {
...
@@ -78,9 +86,7 @@ void main() {
testUsingContext
(
'uri throws if logs produce bad line'
,
()
async
{
testUsingContext
(
'uri throws if logs produce bad line'
,
()
async
{
initialize
();
initialize
();
Timer
.
run
(()
{
logReader
.
addLine
(
'Observatory listening on http://127.0.0.1:apple'
);
logReader
.
addLine
(
'Observatory listening on http://127.0.0.1:apple'
);
});
expect
(
discoverer
.
uri
,
throwsA
(
isFormatException
));
expect
(
discoverer
.
uri
,
throwsA
(
isFormatException
));
});
});
...
@@ -123,8 +129,7 @@ void main() {
...
@@ -123,8 +129,7 @@ void main() {
});
});
testUsingContext
(
'skips uri if port does not match the requested vmservice - requested last'
,
()
async
{
testUsingContext
(
'skips uri if port does not match the requested vmservice - requested last'
,
()
async
{
devicePort
=
12346
;
initialize
(
devicePort:
12346
);
initialize
();
final
Future
<
Uri
>
uriFuture
=
discoverer
.
uri
;
final
Future
<
Uri
>
uriFuture
=
discoverer
.
uri
;
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
...
@@ -134,8 +139,7 @@ void main() {
...
@@ -134,8 +139,7 @@ void main() {
});
});
testUsingContext
(
'skips uri if port does not match the requested vmservice - requested first'
,
()
async
{
testUsingContext
(
'skips uri if port does not match the requested vmservice - requested first'
,
()
async
{
devicePort
=
12346
;
initialize
(
devicePort:
12346
);
initialize
();
final
Future
<
Uri
>
uriFuture
=
discoverer
.
uri
;
final
Future
<
Uri
>
uriFuture
=
discoverer
.
uri
;
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
...
@@ -143,6 +147,86 @@ void main() {
...
@@ -143,6 +147,86 @@ void main() {
expect
(
uri
.
port
,
12346
);
expect
(
uri
.
port
,
12346
);
expect
(
'
$uri
'
,
'http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
expect
(
'
$uri
'
,
'http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
});
});
testUsingContext
(
'first uri in the stream is the last one from the log'
,
()
async
{
initialize
();
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
final
Uri
uri
=
await
discoverer
.
uris
.
first
;
expect
(
uri
.
port
,
12345
);
expect
(
'
$uri
'
,
'http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
});
testUsingContext
(
'first uri in the stream is the last one from the log that matches the port'
,
()
async
{
initialize
(
devicePort:
12345
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12344/PTwjm8Ii8qg=/'
);
final
Uri
uri
=
await
discoverer
.
uris
.
first
;
expect
(
uri
.
port
,
12345
);
expect
(
'
$uri
'
,
'http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
});
testUsingContext
(
'uris in the stream are throttled'
,
()
async
{
const
Duration
kThrottleDuration
=
Duration
(
milliseconds:
10
);
FakeAsync
().
run
((
FakeAsync
time
)
{
initialize
(
throttleDuration:
kThrottleDuration
);
final
List
<
Uri
>
discoveredUris
=
<
Uri
>[];
discoverer
.
uris
.
listen
((
Uri
uri
)
{
discoveredUris
.
add
(
uri
);
});
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
time
.
elapse
(
kThrottleDuration
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12344/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12343/PTwjm8Ii8qg=/'
);
time
.
elapse
(
kThrottleDuration
);
expect
(
discoveredUris
.
length
,
2
);
expect
(
discoveredUris
[
0
].
port
,
12345
);
expect
(
'
${discoveredUris[0]}
'
,
'http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
expect
(
discoveredUris
[
1
].
port
,
12343
);
expect
(
'
${discoveredUris[1]}
'
,
'http://127.0.0.1:12343/PTwjm8Ii8qg=/'
);
});
});
testUsingContext
(
'uris in the stream are throttled when they match the port'
,
()
async
{
const
Duration
kThrottleTimeInMilliseconds
=
Duration
(
milliseconds:
10
);
FakeAsync
().
run
((
FakeAsync
time
)
{
initialize
(
devicePort:
12345
,
throttleDuration:
kThrottleTimeInMilliseconds
,
);
final
List
<
Uri
>
discoveredUris
=
<
Uri
>[];
discoverer
.
uris
.
listen
((
Uri
uri
)
{
discoveredUris
.
add
(
uri
);
});
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
time
.
elapse
(
kThrottleTimeInMilliseconds
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qc=/'
);
logReader
.
addLine
(
'I/flutter : Observatory listening on http://127.0.0.1:12344/PTwjm8Ii8qf=/'
);
time
.
elapse
(
kThrottleTimeInMilliseconds
);
expect
(
discoveredUris
.
length
,
2
);
expect
(
discoveredUris
[
0
].
port
,
12345
);
expect
(
'
${discoveredUris[0]}
'
,
'http://127.0.0.1:12345/PTwjm8Ii8qg=/'
);
expect
(
discoveredUris
[
1
].
port
,
12345
);
expect
(
'
${discoveredUris[1]}
'
,
'http://127.0.0.1:12345/PTwjm8Ii8qc=/'
);
});
});
});
});
group
(
'port forwarding'
,
()
{
group
(
'port forwarding'
,
()
{
...
@@ -164,7 +248,7 @@ void main() {
...
@@ -164,7 +248,7 @@ void main() {
expect
(
'
$uri
'
,
'http://127.0.0.1:99/PTwjm8Ii8qg=/'
);
expect
(
'
$uri
'
,
'http://127.0.0.1:99/PTwjm8Ii8qg=/'
);
await
discoverer
.
cancel
();
await
discoverer
.
cancel
();
logReader
.
dispose
();
await
logReader
.
dispose
();
});
});
testUsingContext
(
'specified port'
,
()
async
{
testUsingContext
(
'specified port'
,
()
async
{
...
@@ -185,7 +269,7 @@ void main() {
...
@@ -185,7 +269,7 @@ void main() {
expect
(
'
$uri
'
,
'http://127.0.0.1:1243/PTwjm8Ii8qg=/'
);
expect
(
'
$uri
'
,
'http://127.0.0.1:1243/PTwjm8Ii8qg=/'
);
await
discoverer
.
cancel
();
await
discoverer
.
cancel
();
logReader
.
dispose
();
await
logReader
.
dispose
();
});
});
testUsingContext
(
'specified port zero'
,
()
async
{
testUsingContext
(
'specified port zero'
,
()
async
{
...
@@ -206,7 +290,7 @@ void main() {
...
@@ -206,7 +290,7 @@ void main() {
expect
(
'
$uri
'
,
'http://127.0.0.1:99/PTwjm8Ii8qg=/'
);
expect
(
'
$uri
'
,
'http://127.0.0.1:99/PTwjm8Ii8qg=/'
);
await
discoverer
.
cancel
();
await
discoverer
.
cancel
();
logReader
.
dispose
();
await
logReader
.
dispose
();
});
});
testUsingContext
(
'ipv6'
,
()
async
{
testUsingContext
(
'ipv6'
,
()
async
{
...
@@ -227,7 +311,7 @@ void main() {
...
@@ -227,7 +311,7 @@ void main() {
expect
(
'
$uri
'
,
'http://[::1]:54777/PTwjm8Ii8qg=/'
);
expect
(
'
$uri
'
,
'http://[::1]:54777/PTwjm8Ii8qg=/'
);
await
discoverer
.
cancel
();
await
discoverer
.
cancel
();
logReader
.
dispose
();
await
logReader
.
dispose
();
});
});
testUsingContext
(
'ipv6 with Ascii Escape code'
,
()
async
{
testUsingContext
(
'ipv6 with Ascii Escape code'
,
()
async
{
...
@@ -248,7 +332,7 @@ void main() {
...
@@ -248,7 +332,7 @@ void main() {
expect
(
'
$uri
'
,
'http://[::1]:54777/PTwjm8Ii8qg=/'
);
expect
(
'
$uri
'
,
'http://[::1]:54777/PTwjm8Ii8qg=/'
);
await
discoverer
.
cancel
();
await
discoverer
.
cancel
();
logReader
.
dispose
();
await
logReader
.
dispose
();
});
});
});
});
});
});
...
...
packages/flutter_tools/test/general.shard/resident_runner_test.dart
View file @
6d77996d
...
@@ -95,9 +95,7 @@ void main() {
...
@@ -95,9 +95,7 @@ void main() {
when
(
mockFlutterView
.
uiIsolate
).
thenReturn
(
mockIsolate
);
when
(
mockFlutterView
.
uiIsolate
).
thenReturn
(
mockIsolate
);
when
(
mockFlutterView
.
runFromSource
(
any
,
any
,
any
)).
thenAnswer
((
Invocation
invocation
)
async
{});
when
(
mockFlutterView
.
runFromSource
(
any
,
any
,
any
)).
thenAnswer
((
Invocation
invocation
)
async
{});
when
(
mockFlutterDevice
.
stopEchoingDeviceLog
()).
thenAnswer
((
Invocation
invocation
)
async
{
});
when
(
mockFlutterDevice
.
stopEchoingDeviceLog
()).
thenAnswer
((
Invocation
invocation
)
async
{
});
when
(
mockFlutterDevice
.
observatoryUris
).
thenReturn
(<
Uri
>[
when
(
mockFlutterDevice
.
observatoryUris
).
thenAnswer
((
_
)
=>
Stream
<
Uri
>.
value
(
testUri
));
testUri
,
]);
when
(
mockFlutterDevice
.
connect
(
when
(
mockFlutterDevice
.
connect
(
reloadSources:
anyNamed
(
'reloadSources'
),
reloadSources:
anyNamed
(
'reloadSources'
),
restart:
anyNamed
(
'restart'
),
restart:
anyNamed
(
'restart'
),
...
@@ -636,7 +634,7 @@ void main() {
...
@@ -636,7 +634,7 @@ void main() {
final
TestFlutterDevice
flutterDevice
=
TestFlutterDevice
(
final
TestFlutterDevice
flutterDevice
=
TestFlutterDevice
(
mockDevice
,
mockDevice
,
<
FlutterView
>[],
<
FlutterView
>[],
observatoryUris:
<
Uri
>[
testUri
]
observatoryUris:
Stream
<
Uri
>.
value
(
testUri
),
);
);
await
flutterDevice
.
connect
();
await
flutterDevice
.
connect
();
...
@@ -657,7 +655,7 @@ class MockUsage extends Mock implements Usage {}
...
@@ -657,7 +655,7 @@ class MockUsage extends Mock implements Usage {}
class
MockProcessManager
extends
Mock
implements
ProcessManager
{}
class
MockProcessManager
extends
Mock
implements
ProcessManager
{}
class
MockServiceEvent
extends
Mock
implements
ServiceEvent
{}
class
MockServiceEvent
extends
Mock
implements
ServiceEvent
{}
class
TestFlutterDevice
extends
FlutterDevice
{
class
TestFlutterDevice
extends
FlutterDevice
{
TestFlutterDevice
(
Device
device
,
this
.
views
,
{
List
<
Uri
>
observatoryUris
})
TestFlutterDevice
(
Device
device
,
this
.
views
,
{
Stream
<
Uri
>
observatoryUris
})
:
super
(
device
,
buildMode:
BuildMode
.
debug
,
trackWidgetCreation:
false
)
{
:
super
(
device
,
buildMode:
BuildMode
.
debug
,
trackWidgetCreation:
false
)
{
_observatoryUris
=
observatoryUris
;
_observatoryUris
=
observatoryUris
;
}
}
...
@@ -666,8 +664,8 @@ class TestFlutterDevice extends FlutterDevice {
...
@@ -666,8 +664,8 @@ class TestFlutterDevice extends FlutterDevice {
final
List
<
FlutterView
>
views
;
final
List
<
FlutterView
>
views
;
@override
@override
List
<
Uri
>
get
observatoryUris
=>
_observatoryUris
;
Stream
<
Uri
>
get
observatoryUris
=>
_observatoryUris
;
List
<
Uri
>
_observatoryUris
;
Stream
<
Uri
>
_observatoryUris
;
}
}
class
ThrowingForwardingFileSystem
extends
ForwardingFileSystem
{
class
ThrowingForwardingFileSystem
extends
ForwardingFileSystem
{
...
...
packages/flutter_tools/test/src/mocks.dart
View file @
6d77996d
...
@@ -533,6 +533,12 @@ class MockAndroidDevice extends Mock implements AndroidDevice {
...
@@ -533,6 +533,12 @@ class MockAndroidDevice extends Mock implements AndroidDevice {
@override
@override
bool
isSupported
()
=>
true
;
bool
isSupported
()
=>
true
;
@override
bool
get
supportsHotRestart
=>
true
;
@override
bool
get
supportsFlutterExit
=>
false
;
@override
@override
bool
isSupportedForProject
(
FlutterProject
flutterProject
)
=>
true
;
bool
isSupportedForProject
(
FlutterProject
flutterProject
)
=>
true
;
}
}
...
@@ -563,16 +569,33 @@ class MockDeviceLogReader extends DeviceLogReader {
...
@@ -563,16 +569,33 @@ class MockDeviceLogReader extends DeviceLogReader {
@override
@override
String
get
name
=>
'MockLogReader'
;
String
get
name
=>
'MockLogReader'
;
final
StreamController
<
String
>
_linesController
=
StreamController
<
String
>.
broadcast
();
StreamController
<
String
>
_cachedLinesController
;
final
List
<
String
>
_lineQueue
=
<
String
>[];
StreamController
<
String
>
get
_linesController
{
_cachedLinesController
??=
StreamController
<
String
>
.
broadcast
(
onListen:
()
{
_lineQueue
.
forEach
(
_linesController
.
add
);
_lineQueue
.
clear
();
});
return
_cachedLinesController
;
}
@override
@override
Stream
<
String
>
get
logLines
=>
_linesController
.
stream
;
Stream
<
String
>
get
logLines
=>
_linesController
.
stream
;
void
addLine
(
String
line
)
=>
_linesController
.
add
(
line
);
void
addLine
(
String
line
)
{
if
(
_linesController
.
hasListener
)
{
_linesController
.
add
(
line
);
}
else
{
_lineQueue
.
add
(
line
);
}
}
@override
@override
void
dispose
()
{
Future
<
void
>
dispose
()
async
{
_linesController
.
close
();
_lineQueue
.
clear
();
await
_linesController
.
close
();
}
}
}
}
...
...
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