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
2135e9fb
Unverified
Commit
2135e9fb
authored
Nov 20, 2019
by
Emmanuel Garcia
Committed by
GitHub
Nov 20, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reland: Attach looks at future observatory URIs (#45228)
parent
51b027c2
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
687 additions
and
244 deletions
+687
-244
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
+98
-28
run_cold.dart
packages/flutter_tools/lib/src/run_cold.dart
+4
-5
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
+223
-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 @
2135e9fb
...
@@ -855,25 +855,9 @@ class _AdbLogReader extends DeviceLogReader {
...
@@ -855,25 +855,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,
...
@@ -923,18 +907,7 @@ class _AdbLogReader extends DeviceLogReader {
...
@@ -923,18 +907,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 @
2135e9fb
...
@@ -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 @
2135e9fb
...
@@ -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 @
2135e9fb
...
@@ -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 @
2135e9fb
...
@@ -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
(
...
@@ -1045,15 +1097,33 @@ class TerminalHandler {
...
@@ -1045,15 +1097,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 @
2135e9fb
...
@@ -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
,
));
));
}
}
...
@@ -183,10 +183,9 @@ class ColdRunner extends ResidentRunner {
...
@@ -183,10 +183,9 @@ class ColdRunner extends ResidentRunner {
bool
haveAnything
=
false
;
bool
haveAnything
=
false
;
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
.
vmServices
!=
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 @
2135e9fb
...
@@ -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 @
2135e9fb
...
@@ -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,113 @@ void main() {
...
@@ -104,13 +123,113 @@ 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
();
// 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 +244,8 @@ void main() {
...
@@ -125,10 +244,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 +258,8 @@ void main() {
...
@@ -141,6 +258,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 +323,8 @@ void main() {
...
@@ -204,10 +323,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 +345,8 @@ void main() {
...
@@ -228,10 +345,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 +365,6 @@ void main() {
...
@@ -250,7 +365,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 +391,16 @@ void main() {
...
@@ -277,16 +391,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 +684,84 @@ Future<void> expectLoggerInterruptEndsTask(Future<void> task, StreamLogger logge
...
@@ -570,3 +684,84 @@ 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
;
}
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 @
2135e9fb
...
@@ -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 @
2135e9fb
...
@@ -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 @
2135e9fb
...
@@ -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 @
2135e9fb
...
@@ -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