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
89a3c353
Unverified
Commit
89a3c353
authored
Jun 08, 2021
by
Lau Ching Jun
Committed by
GitHub
Jun 08, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add more analytics for hot reload in flutter_tools. (#83972)
parent
1e328e2b
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
814 additions
and
322 deletions
+814
-322
logger.dart
packages/flutter_tools/lib/src/base/logger.dart
+4
-1
devfs.dart
packages/flutter_tools/lib/src/devfs.dart
+35
-4
custom_dimensions.dart
...es/flutter_tools/lib/src/reporting/custom_dimensions.dart
+30
-0
events.dart
packages/flutter_tools/lib/src/reporting/events.dart
+15
-0
resident_runner.dart
packages/flutter_tools/lib/src/resident_runner.dart
+3
-1
run_hot.dart
packages/flutter_tools/lib/src/run_hot.dart
+406
-265
logger_test.dart
...es/flutter_tools/test/general.shard/base/logger_test.dart
+4
-51
devfs_test.dart
packages/flutter_tools/test/general.shard/devfs_test.dart
+45
-0
hot_test.dart
packages/flutter_tools/test/general.shard/hot_test.dart
+219
-0
fakes.dart
packages/flutter_tools/test/src/fakes.dart
+53
-0
No files found.
packages/flutter_tools/lib/src/base/logger.dart
View file @
89a3c353
...
...
@@ -19,7 +19,10 @@ class StopwatchFactory {
const
StopwatchFactory
();
/// Create a new [Stopwatch] instance.
Stopwatch
createStopwatch
()
=>
Stopwatch
();
///
/// The optional [name] parameter is useful in tests when there are multiple
/// instances being created.
Stopwatch
createStopwatch
([
String
name
=
''
])
=>
Stopwatch
();
}
typedef
VoidCallback
=
void
Function
();
...
...
packages/flutter_tools/lib/src/devfs.dart
View file @
89a3c353
...
...
@@ -399,18 +399,34 @@ class UpdateFSReport {
int
invalidatedSourcesCount
=
0
,
int
syncedBytes
=
0
,
this
.
fastReassembleClassName
,
int
scannedSourcesCount
=
0
,
Duration
compileDuration
=
Duration
.
zero
,
Duration
transferDuration
=
Duration
.
zero
,
Duration
findInvalidatedDuration
=
Duration
.
zero
,
})
:
_success
=
success
,
_invalidatedSourcesCount
=
invalidatedSourcesCount
,
_syncedBytes
=
syncedBytes
;
_syncedBytes
=
syncedBytes
,
_scannedSourcesCount
=
scannedSourcesCount
,
_compileDuration
=
compileDuration
,
_transferDuration
=
transferDuration
,
_findInvalidatedDuration
=
findInvalidatedDuration
;
bool
get
success
=>
_success
;
int
get
invalidatedSourcesCount
=>
_invalidatedSourcesCount
;
int
get
syncedBytes
=>
_syncedBytes
;
int
get
scannedSourcesCount
=>
_scannedSourcesCount
;
Duration
get
compileDuration
=>
_compileDuration
;
Duration
get
transferDuration
=>
_transferDuration
;
Duration
get
findInvalidatedDuration
=>
_findInvalidatedDuration
;
bool
_success
;
String
fastReassembleClassName
;
int
_invalidatedSourcesCount
;
int
_syncedBytes
;
int
_scannedSourcesCount
;
Duration
_compileDuration
;
Duration
_transferDuration
;
Duration
_findInvalidatedDuration
;
void
incorporateResults
(
UpdateFSReport
report
)
{
if
(!
report
.
_success
)
{
...
...
@@ -419,6 +435,10 @@ class UpdateFSReport {
fastReassembleClassName
??=
report
.
fastReassembleClassName
;
_invalidatedSourcesCount
+=
report
.
_invalidatedSourcesCount
;
_syncedBytes
+=
report
.
_syncedBytes
;
_scannedSourcesCount
+=
report
.
_scannedSourcesCount
;
_compileDuration
+=
report
.
_compileDuration
;
_transferDuration
+=
report
.
_transferDuration
;
_findInvalidatedDuration
+=
report
.
_findInvalidatedDuration
;
}
}
...
...
@@ -435,6 +455,7 @@ class DevFS {
@required
FileSystem
fileSystem
,
HttpClient
httpClient
,
Duration
uploadRetryThrottle
,
StopwatchFactory
stopwatchFactory
=
const
StopwatchFactory
(),
})
:
_vmService
=
serviceProtocol
,
_logger
=
logger
,
_fileSystem
=
fileSystem
,
...
...
@@ -446,13 +467,14 @@ class DevFS {
uploadRetryThrottle:
uploadRetryThrottle
,
httpClient:
httpClient
??
((
context
.
get
<
HttpClientFactory
>()
==
null
)
?
HttpClient
()
:
context
.
get
<
HttpClientFactory
>()())
)
;
:
context
.
get
<
HttpClientFactory
>()())
),
_stopwatchFactory
=
stopwatchFactory
;
final
FlutterVmService
_vmService
;
final
_DevFSHttpWriter
_httpWriter
;
final
Logger
_logger
;
final
FileSystem
_fileSystem
;
final
StopwatchFactory
_stopwatchFactory
;
final
String
fsName
;
final
Directory
rootDirectory
;
...
...
@@ -575,6 +597,7 @@ class DevFS {
// Await the compiler response after checking if the bundle is updated. This allows the file
// stating to be done while waiting for the frontend_server response.
final
Stopwatch
compileTimer
=
_stopwatchFactory
.
createStopwatch
(
'compile'
)..
start
();
final
Future
<
CompilerOutput
>
pendingCompilerOutput
=
generator
.
recompile
(
mainUri
,
invalidatedFiles
,
...
...
@@ -582,7 +605,11 @@ class DevFS {
fs:
_fileSystem
,
projectRootPath:
projectRootPath
,
packageConfig:
packageConfig
,
);
).
then
((
CompilerOutput
result
)
{
compileTimer
.
stop
();
return
result
;
});
if
(
bundle
!=
null
)
{
// The tool writes the assets into the AssetBundle working dir so that they
// are in the same location in DevFS and the iOS simulator.
...
...
@@ -627,15 +654,19 @@ class DevFS {
}
}
_logger
.
printTrace
(
'Updating files.'
);
final
Stopwatch
transferTimer
=
_stopwatchFactory
.
createStopwatch
(
'transfer'
)..
start
();
if
(
dirtyEntries
.
isNotEmpty
)
{
await
(
devFSWriter
??
_httpWriter
).
write
(
dirtyEntries
,
_baseUri
,
_httpWriter
);
}
transferTimer
.
stop
();
_logger
.
printTrace
(
'DevFS: Sync finished'
);
return
UpdateFSReport
(
success:
true
,
syncedBytes:
syncedBytes
,
invalidatedSourcesCount:
invalidatedFiles
.
length
,
fastReassembleClassName:
_checkIfSingleWidgetReloadApplied
(),
compileDuration:
compileTimer
.
elapsed
,
transferDuration:
transferTimer
.
elapsed
,
);
}
...
...
packages/flutter_tools/lib/src/reporting/custom_dimensions.dart
View file @
89a3c353
...
...
@@ -61,6 +61,11 @@ class CustomDimensions {
this
.
fastReassemble
,
this
.
nullSafeMigratedLibraries
,
this
.
nullSafeTotalLibraries
,
this
.
hotEventCompileTimeInMs
,
this
.
hotEventFindInvalidatedTimeInMs
,
this
.
hotEventScannedSourcesCount
,
this
.
hotEventReassembleTimeInMs
,
this
.
hotEventReloadVMTimeInMs
,
});
final
String
?
sessionHostOsDetails
;
// cd1
...
...
@@ -113,6 +118,11 @@ class CustomDimensions {
final
bool
?
fastReassemble
;
// cd48
final
int
?
nullSafeMigratedLibraries
;
// cd49
final
int
?
nullSafeTotalLibraries
;
// cd50
final
int
?
hotEventCompileTimeInMs
;
// cd 51
final
int
?
hotEventFindInvalidatedTimeInMs
;
// cd 52
final
int
?
hotEventScannedSourcesCount
;
// cd 53
final
int
?
hotEventReassembleTimeInMs
;
// cd 54
final
int
?
hotEventReloadVMTimeInMs
;
// cd 55
/// Convert to a map that will be used to upload to the analytics backend.
Map
<
String
,
String
>
toMap
()
=>
<
String
,
String
>{
...
...
@@ -166,6 +176,11 @@ class CustomDimensions {
if
(
fastReassemble
!=
null
)
cdKey
(
CustomDimensionsEnum
.
fastReassemble
):
fastReassemble
.
toString
(),
if
(
nullSafeMigratedLibraries
!=
null
)
cdKey
(
CustomDimensionsEnum
.
nullSafeMigratedLibraries
):
nullSafeMigratedLibraries
.
toString
(),
if
(
nullSafeTotalLibraries
!=
null
)
cdKey
(
CustomDimensionsEnum
.
nullSafeTotalLibraries
):
nullSafeTotalLibraries
.
toString
(),
if
(
hotEventCompileTimeInMs
!=
null
)
cdKey
(
CustomDimensionsEnum
.
hotEventCompileTimeInMs
):
hotEventCompileTimeInMs
.
toString
(),
if
(
hotEventFindInvalidatedTimeInMs
!=
null
)
cdKey
(
CustomDimensionsEnum
.
hotEventFindInvalidatedTimeInMs
):
hotEventFindInvalidatedTimeInMs
.
toString
(),
if
(
hotEventScannedSourcesCount
!=
null
)
cdKey
(
CustomDimensionsEnum
.
hotEventScannedSourcesCount
):
hotEventScannedSourcesCount
.
toString
(),
if
(
hotEventReassembleTimeInMs
!=
null
)
cdKey
(
CustomDimensionsEnum
.
hotEventReassembleTimeInMs
):
hotEventReassembleTimeInMs
.
toString
(),
if
(
hotEventReloadVMTimeInMs
!=
null
)
cdKey
(
CustomDimensionsEnum
.
hotEventReloadVMTimeInMs
):
hotEventReloadVMTimeInMs
.
toString
(),
};
/// Merge the values of two [CustomDimensions] into one. If a value is defined
...
...
@@ -226,6 +241,11 @@ class CustomDimensions {
fastReassemble:
other
.
fastReassemble
??
fastReassemble
,
nullSafeMigratedLibraries:
other
.
nullSafeMigratedLibraries
??
nullSafeMigratedLibraries
,
nullSafeTotalLibraries:
other
.
nullSafeTotalLibraries
??
nullSafeTotalLibraries
,
hotEventCompileTimeInMs:
other
.
hotEventCompileTimeInMs
??
hotEventCompileTimeInMs
,
hotEventFindInvalidatedTimeInMs:
other
.
hotEventFindInvalidatedTimeInMs
??
hotEventFindInvalidatedTimeInMs
,
hotEventScannedSourcesCount:
other
.
hotEventScannedSourcesCount
??
hotEventScannedSourcesCount
,
hotEventReassembleTimeInMs:
other
.
hotEventReassembleTimeInMs
??
hotEventReassembleTimeInMs
,
hotEventReloadVMTimeInMs:
other
.
hotEventReloadVMTimeInMs
??
hotEventReloadVMTimeInMs
,
);
}
...
...
@@ -280,6 +300,11 @@ class CustomDimensions {
fastReassemble:
_extractBool
(
map
,
CustomDimensionsEnum
.
fastReassemble
),
nullSafeMigratedLibraries:
_extractInt
(
map
,
CustomDimensionsEnum
.
nullSafeMigratedLibraries
),
nullSafeTotalLibraries:
_extractInt
(
map
,
CustomDimensionsEnum
.
nullSafeTotalLibraries
),
hotEventCompileTimeInMs:
_extractInt
(
map
,
CustomDimensionsEnum
.
hotEventCompileTimeInMs
),
hotEventFindInvalidatedTimeInMs:
_extractInt
(
map
,
CustomDimensionsEnum
.
hotEventFindInvalidatedTimeInMs
),
hotEventScannedSourcesCount:
_extractInt
(
map
,
CustomDimensionsEnum
.
hotEventScannedSourcesCount
),
hotEventReassembleTimeInMs:
_extractInt
(
map
,
CustomDimensionsEnum
.
hotEventReassembleTimeInMs
),
hotEventReloadVMTimeInMs:
_extractInt
(
map
,
CustomDimensionsEnum
.
hotEventReloadVMTimeInMs
),
);
static
bool
?
_extractBool
(
Map
<
String
,
String
>
map
,
CustomDimensionsEnum
field
)
=>
...
...
@@ -365,6 +390,11 @@ enum CustomDimensionsEnum {
fastReassemble
,
// cd48
nullSafeMigratedLibraries
,
// cd49
nullSafeTotalLibraries
,
// cd50
hotEventCompileTimeInMs
,
// cd51
hotEventFindInvalidatedTimeInMs
,
// cd52
hotEventScannedSourcesCount
,
// cd53
hotEventReassembleTimeInMs
,
// cd54
hotEventReloadVMTimeInMs
,
// cd55
}
String
cdKey
(
CustomDimensionsEnum
cd
)
=>
'cd
${cd.index + 1}
'
;
packages/flutter_tools/lib/src/reporting/events.dart
View file @
89a3c353
...
...
@@ -49,6 +49,11 @@ class HotEvent extends UsageEvent {
this
.
invalidatedSourcesCount
,
this
.
transferTimeInMs
,
this
.
overallTimeInMs
,
this
.
compileTimeInMs
,
this
.
findInvalidatedTimeInMs
,
this
.
scannedSourcesCount
,
this
.
reassembleTimeInMs
,
this
.
reloadVMTimeInMs
,
})
:
super
(
'hot'
,
parameter
,
flutterUsage:
globals
.
flutterUsage
);
final
String
?
reason
;
...
...
@@ -65,6 +70,11 @@ class HotEvent extends UsageEvent {
final
int
?
invalidatedSourcesCount
;
final
int
?
transferTimeInMs
;
final
int
?
overallTimeInMs
;
final
int
?
compileTimeInMs
;
final
int
?
findInvalidatedTimeInMs
;
final
int
?
scannedSourcesCount
;
final
int
?
reassembleTimeInMs
;
final
int
?
reloadVMTimeInMs
;
@override
void
send
()
{
...
...
@@ -83,6 +93,11 @@ class HotEvent extends UsageEvent {
hotEventTransferTimeInMs:
transferTimeInMs
,
hotEventOverallTimeInMs:
overallTimeInMs
,
fastReassemble:
fastReassemble
,
hotEventCompileTimeInMs:
compileTimeInMs
,
hotEventFindInvalidatedTimeInMs:
findInvalidatedTimeInMs
,
hotEventScannedSourcesCount:
scannedSourcesCount
,
hotEventReassembleTimeInMs:
reassembleTimeInMs
,
hotEventReloadVMTimeInMs:
reloadVMTimeInMs
,
);
flutterUsage
.
sendEvent
(
category
,
parameter
,
parameters:
parameters
);
}
...
...
packages/flutter_tools/lib/src/resident_runner.dart
View file @
89a3c353
...
...
@@ -1460,7 +1460,7 @@ abstract class ResidentRunner extends ResidentHandlers {
}
class
OperationResult
{
OperationResult
(
this
.
code
,
this
.
message
,
{
this
.
fatal
=
false
});
OperationResult
(
this
.
code
,
this
.
message
,
{
this
.
fatal
=
false
,
this
.
updateFSReport
});
/// The result of the operation; a non-zero code indicates a failure.
final
int
code
;
...
...
@@ -1471,6 +1471,8 @@ class OperationResult {
/// Whether this error should cause the runner to exit.
final
bool
fatal
;
final
UpdateFSReport
updateFSReport
;
bool
get
isOk
=>
code
==
0
;
static
final
OperationResult
ok
=
OperationResult
(
0
,
''
);
...
...
packages/flutter_tools/lib/src/run_hot.dart
View file @
89a3c353
...
...
@@ -82,7 +82,13 @@ class HotRunner extends ResidentRunner {
bool
ipv6
=
false
,
bool
machine
=
false
,
ResidentDevtoolsHandlerFactory
devtoolsHandler
=
createDefaultHandler
,
})
:
super
(
StopwatchFactory
stopwatchFactory
=
const
StopwatchFactory
(),
ReloadSourcesHelper
reloadSourcesHelper
=
_defaultReloadSourcesHelper
,
ReassembleHelper
reassembleHelper
=
_defaultReassembleHelper
,
})
:
_stopwatchFactory
=
stopwatchFactory
,
_reloadSourcesHelper
=
reloadSourcesHelper
,
_reassembleHelper
=
reassembleHelper
,
super
(
devices
,
target:
target
,
debuggingOptions:
debuggingOptions
,
...
...
@@ -95,6 +101,10 @@ class HotRunner extends ResidentRunner {
devtoolsHandler:
devtoolsHandler
,
);
final
StopwatchFactory
_stopwatchFactory
;
final
ReloadSourcesHelper
_reloadSourcesHelper
;
final
ReassembleHelper
_reassembleHelper
;
final
bool
benchmarkMode
;
final
File
applicationBinary
;
final
bool
hostIsIde
;
...
...
@@ -119,6 +129,31 @@ class HotRunner extends ResidentRunner {
DateTime
firstBuildTime
;
String
_targetPlatform
;
String
_sdkName
;
bool
_emulator
;
Future
<
void
>
_calculateTargetPlatform
()
async
{
if
(
_targetPlatform
!=
null
)
{
return
;
}
if
(
flutterDevices
.
length
==
1
)
{
final
Device
device
=
flutterDevices
.
first
.
device
;
_targetPlatform
=
getNameForTargetPlatform
(
await
device
.
targetPlatform
);
_sdkName
=
await
device
.
sdkNameAndVersion
;
_emulator
=
await
device
.
isLocalEmulator
;
}
else
if
(
flutterDevices
.
length
>
1
)
{
_targetPlatform
=
'multiple'
;
_sdkName
=
'multiple'
;
_emulator
=
false
;
}
else
{
_targetPlatform
=
'unknown'
;
_sdkName
=
'unknown'
;
_emulator
=
false
;
}
}
void
_addBenchmarkData
(
String
name
,
int
value
)
{
benchmarkData
[
name
]
??=
<
int
>[];
benchmarkData
[
name
].
add
(
value
);
...
...
@@ -315,9 +350,15 @@ class HotRunner extends ResidentRunner {
bool
enableDevTools
=
false
,
String
route
,
})
async
{
await
_calculateTargetPlatform
();
final
Stopwatch
appStartedTimer
=
Stopwatch
()..
start
();
final
File
mainFile
=
globals
.
fs
.
file
(
mainPath
);
firstBuildTime
=
DateTime
.
now
();
Duration
totalCompileTime
=
Duration
.
zero
;
Duration
totalLaunchAppTime
=
Duration
.
zero
;
final
List
<
Future
<
bool
>>
startupTasks
=
<
Future
<
bool
>>[];
for
(
final
FlutterDevice
device
in
flutterDevices
)
{
// Here we initialize the frontend_server concurrently with the platform
...
...
@@ -326,6 +367,7 @@ class HotRunner extends ResidentRunner {
// subsequent invocation in devfs will not overwrite.
await
runSourceGenerators
();
if
(
device
.
generator
!=
null
)
{
final
Stopwatch
compileTimer
=
Stopwatch
()..
start
();
startupTasks
.
add
(
device
.
generator
.
recompile
(
mainFile
.
uri
,
...
...
@@ -342,14 +384,35 @@ class HotRunner extends ResidentRunner {
packageConfig:
debuggingOptions
.
buildInfo
.
packageConfig
,
projectRootPath:
FlutterProject
.
current
().
directory
.
absolute
.
path
,
fs:
globals
.
fs
,
).
then
((
CompilerOutput
output
)
=>
output
?.
errorCount
==
0
)
).
then
((
CompilerOutput
output
)
{
compileTimer
.
stop
();
totalCompileTime
+=
compileTimer
.
elapsed
;
return
output
?.
errorCount
==
0
;
})
);
}
final
Stopwatch
launchAppTimer
=
Stopwatch
()..
start
();
startupTasks
.
add
(
device
.
runHot
(
hotRunner:
this
,
route:
route
,
).
then
((
int
result
)
=>
result
==
0
));
).
then
((
int
result
)
{
totalLaunchAppTime
+=
launchAppTimer
.
elapsed
;
return
result
==
0
;
}));
}
unawaited
(
appStartedCompleter
?.
future
?.
then
((
_
)
=>
HotEvent
(
'reload-ready'
,
targetPlatform:
_targetPlatform
,
sdkName:
_sdkName
,
emulator:
_emulator
,
fullRestart:
null
,
fastReassemble:
null
,
overallTimeInMs:
appStartedTimer
.
elapsed
.
inMilliseconds
,
compileTimeInMs:
totalCompileTime
.
inMilliseconds
,
transferTimeInMs:
totalLaunchAppTime
.
inMilliseconds
,
)?.
send
()));
try
{
final
List
<
bool
>
results
=
await
Future
.
wait
(
startupTasks
);
if
(!
results
.
every
((
bool
passed
)
=>
passed
))
{
...
...
@@ -392,6 +455,7 @@ class HotRunner extends ResidentRunner {
}
}
final
Stopwatch
findInvalidationTimer
=
_stopwatchFactory
.
createStopwatch
(
'updateDevFS'
)..
start
();
final
InvalidationResult
invalidationResult
=
await
projectFileInvalidator
.
findInvalidated
(
lastCompiled:
flutterDevices
[
0
].
devFS
.
lastCompiled
,
urisToMonitor:
flutterDevices
[
0
].
devFS
.
sources
,
...
...
@@ -400,6 +464,7 @@ class HotRunner extends ResidentRunner {
packageConfig:
flutterDevices
[
0
].
devFS
.
lastPackageConfig
??
debuggingOptions
.
buildInfo
.
packageConfig
,
);
findInvalidationTimer
.
stop
();
final
File
entrypointFile
=
globals
.
fs
.
file
(
mainPath
);
if
(!
entrypointFile
.
existsSync
())
{
globals
.
printError
(
...
...
@@ -409,7 +474,11 @@ class HotRunner extends ResidentRunner {
'flutter is restarted or the file is restored.'
);
}
final
UpdateFSReport
results
=
UpdateFSReport
(
success:
true
);
final
UpdateFSReport
results
=
UpdateFSReport
(
success:
true
,
scannedSourcesCount:
flutterDevices
[
0
].
devFS
.
sources
.
length
,
findInvalidatedDuration:
findInvalidationTimer
.
elapsed
,
);
for
(
final
FlutterDevice
device
in
flutterDevices
)
{
results
.
incorporateResults
(
await
device
.
updateDevFS
(
mainUri:
entrypointFile
.
absolute
.
uri
,
...
...
@@ -429,12 +498,6 @@ class HotRunner extends ResidentRunner {
return
results
;
}
void
_resetDevFSCompileTime
()
{
for
(
final
FlutterDevice
device
in
flutterDevices
)
{
device
.
devFS
.
resetLastCompiled
();
}
}
void
_resetDirtyAssets
()
{
for
(
final
FlutterDevice
device
in
flutterDevices
)
{
device
.
devFS
.
assetPathsToEvict
.
clear
();
...
...
@@ -574,7 +637,11 @@ class HotRunner extends ResidentRunner {
// Toggle the main dill name after successfully uploading.
_swap
=!
_swap
;
return
OperationResult
.
ok
;
return
OperationResult
(
OperationResult
.
ok
.
code
,
OperationResult
.
ok
.
message
,
updateFSReport:
updatedDevFS
,
);
}
/// Returns [true] if the reload was successful.
...
...
@@ -612,23 +679,7 @@ class HotRunner extends ResidentRunner {
if
(
flutterDevices
.
any
((
FlutterDevice
device
)
=>
device
.
devFS
==
null
))
{
return
OperationResult
(
1
,
'Device initialization has not completed.'
);
}
String
targetPlatform
;
String
sdkName
;
bool
emulator
;
if
(
flutterDevices
.
length
==
1
)
{
final
Device
device
=
flutterDevices
.
first
.
device
;
targetPlatform
=
getNameForTargetPlatform
(
await
device
.
targetPlatform
);
sdkName
=
await
device
.
sdkNameAndVersion
;
emulator
=
await
device
.
isLocalEmulator
;
}
else
if
(
flutterDevices
.
length
>
1
)
{
targetPlatform
=
'multiple'
;
sdkName
=
'multiple'
;
emulator
=
false
;
}
else
{
targetPlatform
=
'unknown'
;
sdkName
=
'unknown'
;
emulator
=
false
;
}
await
_calculateTargetPlatform
();
final
Stopwatch
timer
=
Stopwatch
()..
start
();
// Run source generation if needed.
...
...
@@ -636,9 +687,9 @@ class HotRunner extends ResidentRunner {
if
(
fullRestart
)
{
final
OperationResult
result
=
await
_fullRestartHelper
(
targetPlatform:
targetPlatform
,
sdkName:
sdkName
,
emulator:
emulator
,
targetPlatform:
_
targetPlatform
,
sdkName:
_
sdkName
,
emulator:
_
emulator
,
reason:
reason
,
silent:
silent
,
);
...
...
@@ -649,9 +700,9 @@ class HotRunner extends ResidentRunner {
return
result
;
}
final
OperationResult
result
=
await
_hotReloadHelper
(
targetPlatform:
targetPlatform
,
sdkName:
sdkName
,
emulator:
emulator
,
targetPlatform:
_
targetPlatform
,
sdkName:
_
sdkName
,
emulator:
_
emulator
,
reason:
reason
,
pause:
pause
,
);
...
...
@@ -682,14 +733,32 @@ class HotRunner extends ResidentRunner {
);
}
OperationResult
result
;
String
restartEvent
=
'restart'
;
String
restartEvent
;
try
{
final
Stopwatch
restartTimer
=
_stopwatchFactory
.
createStopwatch
(
'fullRestartHelper'
)..
start
();
if
(!(
await
hotRunnerConfig
.
setupHotRestart
()))
{
return
OperationResult
(
1
,
'setupHotRestart failed'
);
}
result
=
await
_restartFromSources
(
reason:
reason
,);
result
=
await
_restartFromSources
(
reason:
reason
);
restartTimer
.
stop
();
if
(!
result
.
isOk
)
{
restartEvent
=
'restart-failed'
;
}
else
{
HotEvent
(
'restart'
,
targetPlatform:
targetPlatform
,
sdkName:
sdkName
,
emulator:
emulator
,
fullRestart:
true
,
reason:
reason
,
fastReassemble:
null
,
overallTimeInMs:
restartTimer
.
elapsed
.
inMilliseconds
,
syncedBytes:
result
.
updateFSReport
?.
syncedBytes
,
invalidatedSourcesCount:
result
.
updateFSReport
?.
invalidatedSourcesCount
,
transferTimeInMs:
result
.
updateFSReport
?.
transferDuration
?.
inMilliseconds
,
compileTimeInMs:
result
.
updateFSReport
?.
compileDuration
?.
inMilliseconds
,
findInvalidatedTimeInMs:
result
.
updateFSReport
?.
findInvalidatedDuration
?.
inMilliseconds
,
scannedSourcesCount:
result
.
updateFSReport
?.
scannedSourcesCount
,
).
send
();
}
}
on
vm_service
.
SentinelException
catch
(
err
,
st
)
{
restartEvent
=
'exception'
;
...
...
@@ -698,6 +767,9 @@ class HotRunner extends ResidentRunner {
restartEvent
=
'exception'
;
return
OperationResult
(
1
,
'hot restart failed to complete:
$err
\n
$st
'
,
fatal:
true
);
}
finally
{
// The `restartEvent` variable will be null if restart succeeded. We will
// only handle the case when it failed here.
if
(
restartEvent
!=
null
)
{
HotEvent
(
restartEvent
,
targetPlatform:
targetPlatform
,
sdkName:
sdkName
,
...
...
@@ -706,6 +778,7 @@ class HotRunner extends ResidentRunner {
reason:
reason
,
fastReassemble:
null
,
).
send
();
}
status
?.
cancel
();
}
return
result
;
...
...
@@ -772,24 +845,6 @@ class HotRunner extends ResidentRunner {
return
result
;
}
Future
<
List
<
Future
<
vm_service
.
ReloadReport
>>>
_reloadDeviceSources
(
FlutterDevice
device
,
String
entryPath
,
{
bool
pause
=
false
,
})
async
{
final
String
deviceEntryUri
=
device
.
devFS
.
baseUri
.
resolve
(
entryPath
).
toString
();
final
vm_service
.
VM
vm
=
await
device
.
vmService
.
service
.
getVM
();
return
<
Future
<
vm_service
.
ReloadReport
>>[
for
(
final
vm_service
.
IsolateRef
isolateRef
in
vm
.
isolates
)
device
.
vmService
.
service
.
reloadSources
(
isolateRef
.
id
,
pause:
pause
,
rootLibUri:
deviceEntryUri
,
)
];
}
Future
<
OperationResult
>
_reloadSources
({
String
targetPlatform
,
String
sdkName
,
...
...
@@ -809,7 +864,7 @@ class HotRunner extends ResidentRunner {
}
}
final
Stopwatch
reloadTimer
=
Stopwatch
(
)..
start
();
final
Stopwatch
reloadTimer
=
_stopwatchFactory
.
createStopwatch
(
'reloadSources:reload'
)..
start
();
final
Stopwatch
devFSTimer
=
Stopwatch
()..
start
();
final
UpdateFSReport
updatedDevFS
=
await
_updateDevFS
();
// Record time it took to synchronize to DevFS.
...
...
@@ -819,9 +874,12 @@ class HotRunner extends ResidentRunner {
return
OperationResult
(
1
,
'DevFS synchronization failed'
);
}
String
reloadMessage
=
'Reloaded 0 libraries'
;
final
Stopwatch
reloadVMTimer
=
_stopwatchFactory
.
createStopwatch
(
'reloadSources:vm'
)..
start
();
final
Map
<
String
,
Object
>
firstReloadDetails
=
<
String
,
Object
>{};
if
(
updatedDevFS
.
invalidatedSourcesCount
>
0
)
{
final
OperationResult
result
=
await
_reloadSourcesHelper
(
this
,
flutterDevices
,
pause
,
firstReloadDetails
,
targetPlatform
,
...
...
@@ -836,107 +894,25 @@ class HotRunner extends ResidentRunner {
}
else
{
_addBenchmarkData
(
'hotReloadVMReloadMilliseconds'
,
0
);
}
reloadVMTimer
.
stop
();
final
Stopwatch
reassembleTimer
=
Stopwatch
()..
start
();
await
_evictDirtyAssets
();
// Check if any isolates are paused and reassemble those that aren't.
final
Map
<
FlutterView
,
FlutterVmService
>
reassembleViews
=
<
FlutterView
,
FlutterVmService
>{};
final
List
<
Future
<
void
>>
reassembleFutures
=
<
Future
<
void
>>[];
String
serviceEventKind
;
int
pausedIsolatesFound
=
0
;
bool
failedReassemble
=
false
;
for
(
final
FlutterDevice
device
in
flutterDevices
)
{
final
List
<
FlutterView
>
views
=
viewCache
[
device
];
for
(
final
FlutterView
view
in
views
)
{
// Check if the isolate is paused, and if so, don't reassemble. Ignore the
// PostPauseEvent event - the client requesting the pause will resume the app.
final
vm_service
.
Isolate
isolate
=
await
device
.
vmService
.
getIsolateOrNull
(
view
.
uiIsolate
.
id
);
final
vm_service
.
Event
pauseEvent
=
isolate
?.
pauseEvent
;
if
(
pauseEvent
!=
null
&&
isPauseEvent
(
pauseEvent
.
kind
)
&&
pauseEvent
.
kind
!=
vm_service
.
EventKind
.
kPausePostRequest
)
{
pausedIsolatesFound
+=
1
;
if
(
serviceEventKind
==
null
)
{
serviceEventKind
=
pauseEvent
.
kind
;
}
else
if
(
serviceEventKind
!=
pauseEvent
.
kind
)
{
serviceEventKind
=
''
;
// many kinds
}
}
else
{
reassembleViews
[
view
]
=
device
.
vmService
;
// If the tool identified a change in a single widget, do a fast instead
// of a full reassemble.
Future
<
void
>
reassembleWork
;
if
(
updatedDevFS
.
fastReassembleClassName
!=
null
)
{
reassembleWork
=
device
.
vmService
.
flutterFastReassemble
(
isolateId:
view
.
uiIsolate
.
id
,
className:
updatedDevFS
.
fastReassembleClassName
,
);
}
else
{
reassembleWork
=
device
.
vmService
.
flutterReassemble
(
isolateId:
view
.
uiIsolate
.
id
,
final
Stopwatch
reassembleTimer
=
_stopwatchFactory
.
createStopwatch
(
'reloadSources:reassemble'
)..
start
();
final
ReassembleResult
reassembleResult
=
await
_reassembleHelper
(
flutterDevices
,
viewCache
,
onSlow
,
reloadMessage
,
updatedDevFS
.
fastReassembleClassName
,
);
}
reassembleFutures
.
add
(
reassembleWork
.
catchError
((
dynamic
error
)
{
failedReassemble
=
true
;
globals
.
printError
(
'Reassembling
${view.uiIsolate.name}
failed:
$error
'
);
},
test:
(
dynamic
error
)
=>
error
is
Exception
));
}
}
}
if
(
pausedIsolatesFound
>
0
)
{
if
(
onSlow
!=
null
)
{
onSlow
(
'
${_describePausedIsolates(pausedIsolatesFound, serviceEventKind)}
; interface might not update.'
);
}
if
(
reassembleViews
.
isEmpty
)
{
globals
.
printTrace
(
'Skipping reassemble because all isolates are paused.'
);
shouldReportReloadTime
=
reassembleResult
.
shouldReportReloadTime
;
if
(
reassembleResult
.
reassembleViews
.
isEmpty
)
{
return
OperationResult
(
OperationResult
.
ok
.
code
,
reloadMessage
);
}
}
assert
(
reassembleViews
.
isNotEmpty
);
globals
.
printTrace
(
'Reassembling application'
);
final
Future
<
void
>
reassembleFuture
=
Future
.
wait
<
void
>(
reassembleFutures
);
await
reassembleFuture
.
timeout
(
const
Duration
(
seconds:
2
),
onTimeout:
()
async
{
if
(
pausedIsolatesFound
>
0
)
{
shouldReportReloadTime
=
false
;
return
;
// probably no point waiting, they're probably deadlocked and we've already warned.
}
// Check if any isolate is newly paused.
globals
.
printTrace
(
'This is taking a long time; will now check for paused isolates.'
);
int
postReloadPausedIsolatesFound
=
0
;
String
serviceEventKind
;
for
(
final
FlutterView
view
in
reassembleViews
.
keys
)
{
final
vm_service
.
Isolate
isolate
=
await
reassembleViews
[
view
]
.
getIsolateOrNull
(
view
.
uiIsolate
.
id
);
if
(
isolate
==
null
)
{
continue
;
}
if
(
isolate
.
pauseEvent
!=
null
&&
isPauseEvent
(
isolate
.
pauseEvent
.
kind
))
{
postReloadPausedIsolatesFound
+=
1
;
if
(
serviceEventKind
==
null
)
{
serviceEventKind
=
isolate
.
pauseEvent
.
kind
;
}
else
if
(
serviceEventKind
!=
isolate
.
pauseEvent
.
kind
)
{
serviceEventKind
=
''
;
// many kinds
}
}
}
globals
.
printTrace
(
'Found
$postReloadPausedIsolatesFound
newly paused isolate(s).'
);
if
(
postReloadPausedIsolatesFound
==
0
)
{
await
reassembleFuture
;
// must just be taking a long time... keep waiting!
return
;
}
shouldReportReloadTime
=
false
;
if
(
onSlow
!=
null
)
{
onSlow
(
'
${_describePausedIsolates(postReloadPausedIsolatesFound, serviceEventKind)}
.'
);
}
},
);
// Record time it took for Flutter to reassemble the application.
reassembleTimer
.
stop
();
_addBenchmarkData
(
'hotReloadFlutterReassembleMilliseconds'
,
reassembleTimer
.
elapsed
.
inMilliseconds
);
reloadTimer
.
stop
();
...
...
@@ -961,10 +937,15 @@ class HotRunner extends ResidentRunner {
syncedProceduresCount:
firstReloadDetails
[
'receivedProceduresCount'
]
as
int
??
0
,
syncedBytes:
updatedDevFS
.
syncedBytes
,
invalidatedSourcesCount:
updatedDevFS
.
invalidatedSourcesCount
,
transferTimeInMs:
devFSTimer
.
elapsed
.
inMilliseconds
,
transferTimeInMs:
updatedDevFS
.
transferDuration
.
inMilliseconds
,
fastReassemble:
featureFlags
.
isSingleWidgetReloadEnabled
?
updatedDevFS
.
fastReassembleClassName
!=
null
:
null
,
compileTimeInMs:
updatedDevFS
.
compileDuration
.
inMilliseconds
,
findInvalidatedTimeInMs:
updatedDevFS
.
findInvalidatedDuration
.
inMilliseconds
,
scannedSourcesCount:
updatedDevFS
.
scannedSourcesCount
,
reassembleTimeInMs:
reassembleTimer
.
elapsed
.
inMilliseconds
,
reloadVMTimeInMs:
reloadVMTimer
.
elapsed
.
inMilliseconds
,
).
send
();
if
(
shouldReportReloadTime
)
{
...
...
@@ -973,23 +954,124 @@ class HotRunner extends ResidentRunner {
_addBenchmarkData
(
'hotReloadMillisecondsToFrame'
,
reloadInMs
);
}
// Only report timings if we reloaded a single view without any errors.
if
((
reassemble
Views
.
length
==
1
)
&&
!
failedReassemble
&&
shouldReportReloadTime
)
{
if
((
reassemble
Result
.
reassembleViews
.
length
==
1
)
&&
!
reassembleResult
.
failedReassemble
&&
shouldReportReloadTime
)
{
globals
.
flutterUsage
.
sendTiming
(
'hot'
,
'reload'
,
reloadDuration
);
}
return
OperationResult
(
failedReassemble
?
1
:
OperationResult
.
ok
.
code
,
reassembleResult
.
failedReassemble
?
1
:
OperationResult
.
ok
.
code
,
reloadMessage
,
);
}
Future
<
OperationResult
>
_reloadSourcesHelper
(
@override
void
printHelp
({
@required
bool
details
})
{
globals
.
printStatus
(
'Flutter run key commands.'
);
commandHelp
.
r
.
print
();
if
(
supportsRestart
)
{
commandHelp
.
R
.
print
();
}
if
(
details
)
{
printHelpDetails
();
commandHelp
.
hWithDetails
.
print
();
}
else
{
commandHelp
.
hWithoutDetails
.
print
();
}
if
(
_didAttach
)
{
commandHelp
.
d
.
print
();
}
commandHelp
.
c
.
print
();
commandHelp
.
q
.
print
();
globals
.
printStatus
(
''
);
if
(
debuggingOptions
.
buildInfo
.
nullSafetyMode
==
NullSafetyMode
.
sound
)
{
globals
.
printStatus
(
'💪 Running with sound null safety 💪'
,
emphasis:
true
);
}
else
{
globals
.
printStatus
(
'Running with unsound null safety'
,
emphasis:
true
,
);
globals
.
printStatus
(
'For more information see https://dart.dev/null-safety/unsound-null-safety'
,
);
}
globals
.
printStatus
(
''
);
printDebuggerList
();
}
Future
<
void
>
_evictDirtyAssets
()
async
{
final
List
<
Future
<
Map
<
String
,
dynamic
>>>
futures
=
<
Future
<
Map
<
String
,
dynamic
>>>[];
for
(
final
FlutterDevice
device
in
flutterDevices
)
{
if
(
device
.
devFS
.
assetPathsToEvict
.
isEmpty
)
{
continue
;
}
final
List
<
FlutterView
>
views
=
await
device
.
vmService
.
getFlutterViews
();
if
(
views
.
first
.
uiIsolate
==
null
)
{
globals
.
printError
(
'Application isolate not found for
$device
'
);
continue
;
}
for
(
final
String
assetPath
in
device
.
devFS
.
assetPathsToEvict
)
{
futures
.
add
(
device
.
vmService
.
flutterEvictAsset
(
assetPath
,
isolateId:
views
.
first
.
uiIsolate
.
id
,
)
);
}
device
.
devFS
.
assetPathsToEvict
.
clear
();
}
return
Future
.
wait
<
Map
<
String
,
dynamic
>>(
futures
);
}
@override
Future
<
void
>
cleanupAfterSignal
()
async
{
await
stopEchoingDeviceLog
();
await
hotRunnerConfig
.
runPreShutdownOperations
();
if
(
_didAttach
)
{
appFinished
();
}
else
{
await
exitApp
();
}
}
@override
Future
<
void
>
preExit
()
async
{
await
_cleanupDevFS
();
await
hotRunnerConfig
.
runPreShutdownOperations
();
await
super
.
preExit
();
}
@override
Future
<
void
>
cleanupAtFinish
()
async
{
for
(
final
FlutterDevice
flutterDevice
in
flutterDevices
)
{
await
flutterDevice
.
device
.
dispose
();
}
await
_cleanupDevFS
();
await
residentDevtoolsHandler
.
shutdown
();
await
stopEchoingDeviceLog
();
}
}
typedef
ReloadSourcesHelper
=
Future
<
OperationResult
>
Function
(
HotRunner
hotRunner
,
List
<
FlutterDevice
>
flutterDevices
,
bool
pause
,
Map
<
String
,
dynamic
>
firstReloadDetails
,
String
targetPlatform
,
String
sdkName
,
bool
emulator
,
String
reason
,
)
async
{
);
Future
<
OperationResult
>
_defaultReloadSourcesHelper
(
HotRunner
hotRunner
,
List
<
FlutterDevice
>
flutterDevices
,
bool
pause
,
Map
<
String
,
dynamic
>
firstReloadDetails
,
String
targetPlatform
,
String
sdkName
,
bool
emulator
,
String
reason
,
)
async
{
final
Stopwatch
vmReloadTimer
=
Stopwatch
()..
start
();
const
String
entryPath
=
'main.dart.incremental.dill'
;
final
List
<
Future
<
DeviceReloadReport
>>
allReportsFutures
=
<
Future
<
DeviceReloadReport
>>[];
...
...
@@ -1008,7 +1090,7 @@ class HotRunner extends ResidentRunner {
// Don't print errors because they will be printed further down when
// `validateReloadReport` is called again.
await
device
.
updateReloadStatus
(
validateReloadReport
(
firstReport
,
printErrors:
false
),
HotRunner
.
validateReloadReport
(
firstReport
,
printErrors:
false
),
);
return
DeviceReloadReport
(
device
,
reports
);
},
...
...
@@ -1016,7 +1098,7 @@ class HotRunner extends ResidentRunner {
}
final
List
<
DeviceReloadReport
>
reports
=
await
Future
.
wait
(
allReportsFutures
);
final
vm_service
.
ReloadReport
reloadReport
=
reports
.
first
.
reports
[
0
];
if
(!
validateReloadReport
(
reloadReport
))
{
if
(!
HotRunner
.
validateReloadReport
(
reloadReport
))
{
// Reload failed.
HotEvent
(
'reload-reject'
,
targetPlatform:
targetPlatform
,
...
...
@@ -1028,7 +1110,7 @@ class HotRunner extends ResidentRunner {
).
send
();
// Reset devFS lastCompileTime to ensure the file will still be marked
// as dirty on subsequent reloads.
_resetDevFSCompileTime
(
);
_resetDevFSCompileTime
(
flutterDevices
);
final
ReloadReportContents
contents
=
ReloadReportContents
.
fromReloadReport
(
reloadReport
);
return
OperationResult
(
1
,
'Reload rejected:
${contents.notices.join("\n")}
'
);
}
...
...
@@ -1041,11 +1123,158 @@ class HotRunner extends ResidentRunner {
globals
.
printTrace
(
'reloaded
$loadedLibraryCount
of
$finalLibraryCount
libraries'
);
// reloadMessage = 'Reloaded $loadedLibraryCount of $finalLibraryCount libraries';
// Record time it took for the VM to reload the sources.
_addBenchmarkData
(
'hotReloadVMReloadMilliseconds'
,
vmReloadTimer
.
elapsed
.
inMilliseconds
);
hotRunner
.
_addBenchmarkData
(
'hotReloadVMReloadMilliseconds'
,
vmReloadTimer
.
elapsed
.
inMilliseconds
);
return
OperationResult
(
0
,
'Reloaded
$loadedLibraryCount
of
$finalLibraryCount
libraries'
);
}
Future
<
List
<
Future
<
vm_service
.
ReloadReport
>>>
_reloadDeviceSources
(
FlutterDevice
device
,
String
entryPath
,
{
bool
pause
=
false
,
})
async
{
final
String
deviceEntryUri
=
device
.
devFS
.
baseUri
.
resolve
(
entryPath
).
toString
();
final
vm_service
.
VM
vm
=
await
device
.
vmService
.
service
.
getVM
();
return
<
Future
<
vm_service
.
ReloadReport
>>[
for
(
final
vm_service
.
IsolateRef
isolateRef
in
vm
.
isolates
)
device
.
vmService
.
service
.
reloadSources
(
isolateRef
.
id
,
pause:
pause
,
rootLibUri:
deviceEntryUri
,
)
];
}
void
_resetDevFSCompileTime
(
List
<
FlutterDevice
>
flutterDevices
)
{
for
(
final
FlutterDevice
device
in
flutterDevices
)
{
device
.
devFS
.
resetLastCompiled
();
}
}
@visibleForTesting
class
ReassembleResult
{
ReassembleResult
(
this
.
reassembleViews
,
this
.
failedReassemble
,
this
.
shouldReportReloadTime
);
final
Map
<
FlutterView
,
FlutterVmService
>
reassembleViews
;
final
bool
failedReassemble
;
final
bool
shouldReportReloadTime
;
}
String
_describePausedIsolates
(
int
pausedIsolatesFound
,
String
serviceEventKind
)
{
typedef
ReassembleHelper
=
Future
<
ReassembleResult
>
Function
(
List
<
FlutterDevice
>
flutterDevices
,
Map
<
FlutterDevice
,
List
<
FlutterView
>>
viewCache
,
void
Function
(
String
message
)
onSlow
,
String
reloadMessage
,
String
fastReassembleClassName
,
);
Future
<
ReassembleResult
>
_defaultReassembleHelper
(
List
<
FlutterDevice
>
flutterDevices
,
Map
<
FlutterDevice
,
List
<
FlutterView
>>
viewCache
,
void
Function
(
String
message
)
onSlow
,
String
reloadMessage
,
String
fastReassembleClassName
,
)
async
{
// Check if any isolates are paused and reassemble those that aren't.
final
Map
<
FlutterView
,
FlutterVmService
>
reassembleViews
=
<
FlutterView
,
FlutterVmService
>{};
final
List
<
Future
<
void
>>
reassembleFutures
=
<
Future
<
void
>>[];
String
serviceEventKind
;
int
pausedIsolatesFound
=
0
;
bool
failedReassemble
=
false
;
bool
shouldReportReloadTime
=
true
;
for
(
final
FlutterDevice
device
in
flutterDevices
)
{
final
List
<
FlutterView
>
views
=
viewCache
[
device
];
for
(
final
FlutterView
view
in
views
)
{
// Check if the isolate is paused, and if so, don't reassemble. Ignore the
// PostPauseEvent event - the client requesting the pause will resume the app.
final
vm_service
.
Isolate
isolate
=
await
device
.
vmService
.
getIsolateOrNull
(
view
.
uiIsolate
.
id
);
final
vm_service
.
Event
pauseEvent
=
isolate
?.
pauseEvent
;
if
(
pauseEvent
!=
null
&&
isPauseEvent
(
pauseEvent
.
kind
)
&&
pauseEvent
.
kind
!=
vm_service
.
EventKind
.
kPausePostRequest
)
{
pausedIsolatesFound
+=
1
;
if
(
serviceEventKind
==
null
)
{
serviceEventKind
=
pauseEvent
.
kind
;
}
else
if
(
serviceEventKind
!=
pauseEvent
.
kind
)
{
serviceEventKind
=
''
;
// many kinds
}
}
else
{
reassembleViews
[
view
]
=
device
.
vmService
;
// If the tool identified a change in a single widget, do a fast instead
// of a full reassemble.
Future
<
void
>
reassembleWork
;
if
(
fastReassembleClassName
!=
null
)
{
reassembleWork
=
device
.
vmService
.
flutterFastReassemble
(
isolateId:
view
.
uiIsolate
.
id
,
className:
fastReassembleClassName
,
);
}
else
{
reassembleWork
=
device
.
vmService
.
flutterReassemble
(
isolateId:
view
.
uiIsolate
.
id
,
);
}
reassembleFutures
.
add
(
reassembleWork
.
catchError
((
dynamic
error
)
{
failedReassemble
=
true
;
globals
.
printError
(
'Reassembling
${view.uiIsolate.name}
failed:
$error
'
);
},
test:
(
dynamic
error
)
=>
error
is
Exception
));
}
}
}
if
(
pausedIsolatesFound
>
0
)
{
if
(
onSlow
!=
null
)
{
onSlow
(
'
${_describePausedIsolates(pausedIsolatesFound, serviceEventKind)}
; interface might not update.'
);
}
if
(
reassembleViews
.
isEmpty
)
{
globals
.
printTrace
(
'Skipping reassemble because all isolates are paused.'
);
return
ReassembleResult
(
reassembleViews
,
failedReassemble
,
shouldReportReloadTime
);
}
}
assert
(
reassembleViews
.
isNotEmpty
);
globals
.
printTrace
(
'Reassembling application'
);
final
Future
<
void
>
reassembleFuture
=
Future
.
wait
<
void
>(
reassembleFutures
);
await
reassembleFuture
.
timeout
(
const
Duration
(
seconds:
2
),
onTimeout:
()
async
{
if
(
pausedIsolatesFound
>
0
)
{
shouldReportReloadTime
=
false
;
return
;
// probably no point waiting, they're probably deadlocked and we've already warned.
}
// Check if any isolate is newly paused.
globals
.
printTrace
(
'This is taking a long time; will now check for paused isolates.'
);
int
postReloadPausedIsolatesFound
=
0
;
String
serviceEventKind
;
for
(
final
FlutterView
view
in
reassembleViews
.
keys
)
{
final
vm_service
.
Isolate
isolate
=
await
reassembleViews
[
view
]
.
getIsolateOrNull
(
view
.
uiIsolate
.
id
);
if
(
isolate
==
null
)
{
continue
;
}
if
(
isolate
.
pauseEvent
!=
null
&&
isPauseEvent
(
isolate
.
pauseEvent
.
kind
))
{
postReloadPausedIsolatesFound
+=
1
;
if
(
serviceEventKind
==
null
)
{
serviceEventKind
=
isolate
.
pauseEvent
.
kind
;
}
else
if
(
serviceEventKind
!=
isolate
.
pauseEvent
.
kind
)
{
serviceEventKind
=
''
;
// many kinds
}
}
}
globals
.
printTrace
(
'Found
$postReloadPausedIsolatesFound
newly paused isolate(s).'
);
if
(
postReloadPausedIsolatesFound
==
0
)
{
await
reassembleFuture
;
// must just be taking a long time... keep waiting!
return
;
}
shouldReportReloadTime
=
false
;
if
(
onSlow
!=
null
)
{
onSlow
(
'
${_describePausedIsolates(postReloadPausedIsolatesFound, serviceEventKind)}
.'
);
}
},
);
return
ReassembleResult
(
reassembleViews
,
failedReassemble
,
shouldReportReloadTime
);
}
String
_describePausedIsolates
(
int
pausedIsolatesFound
,
String
serviceEventKind
)
{
assert
(
pausedIsolatesFound
>
0
);
final
StringBuffer
message
=
StringBuffer
();
bool
plural
;
...
...
@@ -1083,94 +1312,6 @@ class HotRunner extends ResidentRunner {
message
.
write
(
'paused'
);
}
return
message
.
toString
();
}
@override
void
printHelp
({
@required
bool
details
})
{
globals
.
printStatus
(
'Flutter run key commands.'
);
commandHelp
.
r
.
print
();
if
(
supportsRestart
)
{
commandHelp
.
R
.
print
();
}
if
(
details
)
{
printHelpDetails
();
commandHelp
.
hWithDetails
.
print
();
}
else
{
commandHelp
.
hWithoutDetails
.
print
();
}
if
(
_didAttach
)
{
commandHelp
.
d
.
print
();
}
commandHelp
.
c
.
print
();
commandHelp
.
q
.
print
();
globals
.
printStatus
(
''
);
if
(
debuggingOptions
.
buildInfo
.
nullSafetyMode
==
NullSafetyMode
.
sound
)
{
globals
.
printStatus
(
'💪 Running with sound null safety 💪'
,
emphasis:
true
);
}
else
{
globals
.
printStatus
(
'Running with unsound null safety'
,
emphasis:
true
,
);
globals
.
printStatus
(
'For more information see https://dart.dev/null-safety/unsound-null-safety'
,
);
}
globals
.
printStatus
(
''
);
printDebuggerList
();
}
Future
<
void
>
_evictDirtyAssets
()
async
{
final
List
<
Future
<
Map
<
String
,
dynamic
>>>
futures
=
<
Future
<
Map
<
String
,
dynamic
>>>[];
for
(
final
FlutterDevice
device
in
flutterDevices
)
{
if
(
device
.
devFS
.
assetPathsToEvict
.
isEmpty
)
{
continue
;
}
final
List
<
FlutterView
>
views
=
await
device
.
vmService
.
getFlutterViews
();
if
(
views
.
first
.
uiIsolate
==
null
)
{
globals
.
printError
(
'Application isolate not found for
$device
'
);
continue
;
}
for
(
final
String
assetPath
in
device
.
devFS
.
assetPathsToEvict
)
{
futures
.
add
(
device
.
vmService
.
flutterEvictAsset
(
assetPath
,
isolateId:
views
.
first
.
uiIsolate
.
id
,
)
);
}
device
.
devFS
.
assetPathsToEvict
.
clear
();
}
return
Future
.
wait
<
Map
<
String
,
dynamic
>>(
futures
);
}
@override
Future
<
void
>
cleanupAfterSignal
()
async
{
await
stopEchoingDeviceLog
();
await
hotRunnerConfig
.
runPreShutdownOperations
();
if
(
_didAttach
)
{
appFinished
();
}
else
{
await
exitApp
();
}
}
@override
Future
<
void
>
preExit
()
async
{
await
_cleanupDevFS
();
await
hotRunnerConfig
.
runPreShutdownOperations
();
await
super
.
preExit
();
}
@override
Future
<
void
>
cleanupAtFinish
()
async
{
for
(
final
FlutterDevice
flutterDevice
in
flutterDevices
)
{
await
flutterDevice
.
device
.
dispose
();
}
await
_cleanupDevFS
();
await
residentDevtoolsHandler
.
shutdown
();
await
stopEchoingDeviceLog
();
}
}
/// The result of an invalidation check from [ProjectFileInvalidator].
...
...
@@ -1225,7 +1366,7 @@ class ProjectFileInvalidator {
assert
(
urisToMonitor
.
isEmpty
);
return
InvalidationResult
(
packageConfig:
packageConfig
,
uris:
<
Uri
>[]
uris:
<
Uri
>[]
,
);
}
...
...
packages/flutter_tools/test/general.shard/base/logger_test.dart
View file @
89a3c353
...
...
@@ -244,7 +244,7 @@ void main() {
);
final
VerboseLogger
verboseLogger
=
VerboseLogger
(
mockLogger
,
stopwatchFactory:
FakeStopwatchFactory
(
fakeStopWatch
),
stopwatchFactory:
FakeStopwatchFactory
(
stopwatch:
fakeStopWatch
),
);
verboseLogger
.
printStatus
(
'Hey Hey Hey Hey'
);
...
...
@@ -266,7 +266,7 @@ void main() {
outputPreferences:
OutputPreferences
.
test
(
showColor:
true
),
);
final
VerboseLogger
verboseLogger
=
VerboseLogger
(
mockLogger
,
stopwatchFactory:
FakeStopwatchFactory
(
fakeStopWatch
),
mockLogger
,
stopwatchFactory:
FakeStopwatchFactory
(
stopwatch:
fakeStopWatch
),
);
verboseLogger
.
printStatus
(
'Hey Hey Hey Hey'
);
...
...
@@ -377,7 +377,7 @@ void main() {
mockStopwatch
=
FakeStopwatch
();
mockStdio
=
FakeStdio
();
called
=
0
;
stopwatchFactory
=
FakeStopwatchFactory
(
mockStopwatch
);
stopwatchFactory
=
FakeStopwatchFactory
(
stopwatch:
mockStopwatch
);
});
List
<
String
>
outputStdout
()
=>
mockStdio
.
writtenToStdout
.
join
(
''
).
split
(
'
\n
'
);
...
...
@@ -938,7 +938,7 @@ void main() {
),
stdio:
fakeStdio
,
outputPreferences:
OutputPreferences
.
test
(
showColor:
false
),
stopwatchFactory:
FakeStopwatchFactory
(
fakeStopwatch
),
stopwatchFactory:
FakeStopwatchFactory
(
stopwatch:
fakeStopwatch
),
);
final
Status
status
=
logger
.
startProgress
(
'Hello'
,
...
...
@@ -1062,53 +1062,6 @@ void main() {
});
}
class
FakeStopwatch
implements
Stopwatch
{
@override
bool
get
isRunning
=>
_isRunning
;
bool
_isRunning
=
false
;
@override
void
start
()
=>
_isRunning
=
true
;
@override
void
stop
()
=>
_isRunning
=
false
;
@override
Duration
elapsed
=
Duration
.
zero
;
@override
int
get
elapsedMicroseconds
=>
elapsed
.
inMicroseconds
;
@override
int
get
elapsedMilliseconds
=>
elapsed
.
inMilliseconds
;
@override
int
get
elapsedTicks
=>
elapsed
.
inMilliseconds
;
@override
int
get
frequency
=>
1000
;
@override
void
reset
()
{
_isRunning
=
false
;
elapsed
=
Duration
.
zero
;
}
@override
String
toString
()
=>
'
$runtimeType
$elapsed
$isRunning
'
;
}
class
FakeStopwatchFactory
implements
StopwatchFactory
{
FakeStopwatchFactory
([
this
.
stopwatch
]);
Stopwatch
stopwatch
;
@override
Stopwatch
createStopwatch
()
{
return
stopwatch
??
FakeStopwatch
();
}
}
/// A fake [Logger] that throws the [Invocation] for any method call.
class
FakeLogger
implements
Logger
{
@override
...
...
packages/flutter_tools/test/general.shard/devfs_test.dart
View file @
89a3c353
...
...
@@ -425,6 +425,51 @@ void main() {
Uri
.
parse
(
'goodbye'
):
DevFSFileContent
(
file
),
},
Uri
.
parse
(
'/foo/bar/devfs/'
)),
throwsA
(
isA
<
DevFSException
>()));
});
testWithoutContext
(
'DevFS correctly records the elapsed time'
,
()
async
{
final
FileSystem
fileSystem
=
MemoryFileSystem
.
test
();
// final FakeDevFSWriter writer = FakeDevFSWriter();
final
FakeVmServiceHost
fakeVmServiceHost
=
FakeVmServiceHost
(
requests:
<
VmServiceExpectation
>[
createDevFSRequest
],
httpAddress:
Uri
.
parse
(
'http://localhost'
),
);
final
DevFS
devFS
=
DevFS
(
fakeVmServiceHost
.
vmService
,
'test'
,
fileSystem
.
currentDirectory
,
fileSystem:
fileSystem
,
logger:
BufferLogger
.
test
(),
osUtils:
FakeOperatingSystemUtils
(),
httpClient:
FakeHttpClient
.
any
(),
stopwatchFactory:
FakeStopwatchFactory
(
stopwatches:
<
String
,
Stopwatch
>{
'compile'
:
FakeStopwatch
()..
elapsed
=
const
Duration
(
seconds:
3
),
'transfer'
:
FakeStopwatch
()..
elapsed
=
const
Duration
(
seconds:
5
),
}),
);
await
devFS
.
create
();
final
FakeResidentCompiler
residentCompiler
=
FakeResidentCompiler
();
residentCompiler
.
onRecompile
=
(
Uri
mainUri
,
List
<
Uri
>
invalidatedFiles
)
async
{
fileSystem
.
file
(
'lib/foo.txt.dill'
).
createSync
(
recursive:
true
);
return
const
CompilerOutput
(
'lib/foo.txt.dill'
,
0
,
<
Uri
>[]);
};
final
UpdateFSReport
report
=
await
devFS
.
update
(
mainUri:
Uri
.
parse
(
'lib/main.dart'
),
generator:
residentCompiler
,
dillOutputPath:
'lib/foo.dill'
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
invalidatedFiles:
<
Uri
>[],
packageConfig:
PackageConfig
.
empty
,
);
expect
(
report
.
success
,
true
);
expect
(
report
.
compileDuration
,
const
Duration
(
seconds:
3
));
expect
(
report
.
transferDuration
,
const
Duration
(
seconds:
5
));
});
}
class
FakeResidentCompiler
extends
Fake
implements
ResidentCompiler
{
...
...
packages/flutter_tools/test/general.shard/hot_test.dart
View file @
89a3c353
...
...
@@ -7,6 +7,7 @@
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/application_package.dart'
;
import
'package:flutter_tools/src/artifacts.dart'
;
import
'package:flutter_tools/src/asset.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
...
...
@@ -14,17 +15,20 @@ import 'package:flutter_tools/src/build_info.dart';
import
'package:flutter_tools/src/compile.dart'
;
import
'package:flutter_tools/src/devfs.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/reporting/reporting.dart'
;
import
'package:flutter_tools/src/resident_devtools_handler.dart'
;
import
'package:flutter_tools/src/resident_runner.dart'
;
import
'package:flutter_tools/src/run_hot.dart'
;
import
'package:flutter_tools/src/vmservice.dart'
;
import
'package:meta/meta.dart'
;
import
'package:package_config/package_config.dart'
;
import
'package:test/fake.dart'
;
import
'package:vm_service/vm_service.dart'
as
vm_service
;
import
'../src/common.dart'
;
import
'../src/context.dart'
;
import
'../src/fake_vm_services.dart'
;
import
'../src/fakes.dart'
;
final
vm_service
.
Isolate
fakeUnpausedIsolate
=
vm_service
.
Isolate
(
id:
'1'
,
...
...
@@ -146,9 +150,11 @@ void main() {
group
(
'hotRestart'
,
()
{
final
FakeResidentCompiler
residentCompiler
=
FakeResidentCompiler
();
FileSystem
fileSystem
;
TestUsage
testUsage
;
setUp
(()
{
fileSystem
=
MemoryFileSystem
.
test
();
testUsage
=
TestUsage
();
});
testUsingContext
(
'setup function fails'
,
()
async
{
...
...
@@ -228,6 +234,158 @@ void main() {
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
});
testUsingContext
(
'correctly tracks time spent for analytics for hot restart'
,
()
async
{
final
FakeDevice
device
=
FakeDevice
();
final
FakeFlutterDevice
fakeFlutterDevice
=
FakeFlutterDevice
(
device
);
final
List
<
FlutterDevice
>
devices
=
<
FlutterDevice
>[
fakeFlutterDevice
,
];
fakeFlutterDevice
.
updateDevFSReport
=
UpdateFSReport
(
success:
true
,
invalidatedSourcesCount:
2
,
syncedBytes:
4
,
scannedSourcesCount:
8
,
compileDuration:
const
Duration
(
seconds:
16
),
transferDuration:
const
Duration
(
seconds:
32
),
);
final
FakeStopwatchFactory
fakeStopwatchFactory
=
FakeStopwatchFactory
(
stopwatches:
<
String
,
Stopwatch
>{
'fullRestartHelper'
:
FakeStopwatch
()..
elapsed
=
const
Duration
(
seconds:
64
),
'updateDevFS'
:
FakeStopwatch
()..
elapsed
=
const
Duration
(
seconds:
128
),
},
);
(
fakeFlutterDevice
.
devFS
as
FakeDevFs
).
baseUri
=
Uri
.
parse
(
'file:///base_uri'
);
final
OperationResult
result
=
await
HotRunner
(
devices
,
debuggingOptions:
DebuggingOptions
.
disabled
(
BuildInfo
.
debug
),
target:
'main.dart'
,
devtoolsHandler:
createNoOpHandler
,
stopwatchFactory:
fakeStopwatchFactory
,
).
restart
(
fullRestart:
true
);
expect
(
result
.
isOk
,
true
);
expect
(
testUsage
.
events
,
<
TestUsageEvent
>[
const
TestUsageEvent
(
'hot'
,
'restart'
,
parameters:
CustomDimensions
(
hotEventTargetPlatform:
'flutter-tester'
,
hotEventSdkName:
'Tester'
,
hotEventEmulator:
false
,
hotEventFullRestart:
true
,
hotEventOverallTimeInMs:
64000
,
hotEventSyncedBytes:
4
,
hotEventInvalidatedSourcesCount:
2
,
hotEventTransferTimeInMs:
32000
,
hotEventCompileTimeInMs:
16000
,
hotEventFindInvalidatedTimeInMs:
128000
,
hotEventScannedSourcesCount:
8
,
)),
]);
},
overrides:
<
Type
,
Generator
>{
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
),
Artifacts:
()
=>
Artifacts
.
test
(),
FileSystem:
()
=>
fileSystem
,
Platform:
()
=>
FakePlatform
(
operatingSystem:
'linux'
),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
Usage:
()
=>
testUsage
,
});
testUsingContext
(
'correctly tracks time spent for analytics for hot reload'
,
()
async
{
final
FakeDevice
device
=
FakeDevice
();
final
FakeFlutterDevice
fakeFlutterDevice
=
FakeFlutterDevice
(
device
);
final
List
<
FlutterDevice
>
devices
=
<
FlutterDevice
>[
fakeFlutterDevice
,
];
fakeFlutterDevice
.
updateDevFSReport
=
UpdateFSReport
(
success:
true
,
invalidatedSourcesCount:
6
,
syncedBytes:
8
,
scannedSourcesCount:
16
,
compileDuration:
const
Duration
(
seconds:
16
),
transferDuration:
const
Duration
(
seconds:
32
),
);
final
FakeStopwatchFactory
fakeStopwatchFactory
=
FakeStopwatchFactory
(
stopwatches:
<
String
,
Stopwatch
>{
'updateDevFS'
:
FakeStopwatch
()..
elapsed
=
const
Duration
(
seconds:
64
),
'reloadSources:reload'
:
FakeStopwatch
()..
elapsed
=
const
Duration
(
seconds:
128
),
'reloadSources:reassemble'
:
FakeStopwatch
()..
elapsed
=
const
Duration
(
seconds:
256
),
'reloadSources:vm'
:
FakeStopwatch
()..
elapsed
=
const
Duration
(
seconds:
512
),
},
);
(
fakeFlutterDevice
.
devFS
as
FakeDevFs
).
baseUri
=
Uri
.
parse
(
'file:///base_uri'
);
final
OperationResult
result
=
await
HotRunner
(
devices
,
debuggingOptions:
DebuggingOptions
.
disabled
(
BuildInfo
.
debug
),
target:
'main.dart'
,
devtoolsHandler:
createNoOpHandler
,
stopwatchFactory:
fakeStopwatchFactory
,
reloadSourcesHelper:
(
HotRunner
hotRunner
,
List
<
FlutterDevice
>
flutterDevices
,
bool
pause
,
Map
<
String
,
dynamic
>
firstReloadDetails
,
String
targetPlatform
,
String
sdkName
,
bool
emulator
,
String
reason
,
)
async
{
firstReloadDetails
[
'finalLibraryCount'
]
=
2
;
firstReloadDetails
[
'receivedLibraryCount'
]
=
3
;
firstReloadDetails
[
'receivedClassesCount'
]
=
4
;
firstReloadDetails
[
'receivedProceduresCount'
]
=
5
;
return
OperationResult
.
ok
;
},
reassembleHelper:
(
List
<
FlutterDevice
>
flutterDevices
,
Map
<
FlutterDevice
,
List
<
FlutterView
>>
viewCache
,
void
Function
(
String
message
)
onSlow
,
String
reloadMessage
,
String
fastReassembleClassName
,
)
async
=>
ReassembleResult
(
<
FlutterView
,
FlutterVmService
>{
null
:
null
},
false
,
true
,
),
).
restart
(
fullRestart:
false
);
expect
(
result
.
isOk
,
true
);
expect
(
testUsage
.
events
,
<
TestUsageEvent
>[
const
TestUsageEvent
(
'hot'
,
'reload'
,
parameters:
CustomDimensions
(
hotEventFinalLibraryCount:
2
,
hotEventSyncedLibraryCount:
3
,
hotEventSyncedClassesCount:
4
,
hotEventSyncedProceduresCount:
5
,
hotEventSyncedBytes:
8
,
hotEventInvalidatedSourcesCount:
6
,
hotEventTransferTimeInMs:
32000
,
hotEventOverallTimeInMs:
128000
,
hotEventTargetPlatform:
'flutter-tester'
,
hotEventSdkName:
'Tester'
,
hotEventEmulator:
false
,
hotEventFullRestart:
false
,
fastReassemble:
false
,
hotEventCompileTimeInMs:
16000
,
hotEventFindInvalidatedTimeInMs:
64000
,
hotEventScannedSourcesCount:
16
,
hotEventReassembleTimeInMs:
256000
,
hotEventReloadVMTimeInMs:
512000
,
)),
]);
},
overrides:
<
Type
,
Generator
>{
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
),
Artifacts:
()
=>
Artifacts
.
test
(),
FileSystem:
()
=>
fileSystem
,
Platform:
()
=>
FakePlatform
(
operatingSystem:
'linux'
),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
Usage:
()
=>
testUsage
,
});
});
group
(
'hot attach'
,
()
{
...
...
@@ -299,6 +457,21 @@ void main() {
class
FakeDevFs
extends
Fake
implements
DevFS
{
@override
Future
<
void
>
destroy
()
async
{
}
@override
List
<
Uri
>
sources
=
<
Uri
>[];
@override
DateTime
lastCompiled
;
@override
PackageConfig
lastPackageConfig
;
@override
Set
<
String
>
assetPathsToEvict
=
<
String
>{};
@override
Uri
baseUri
;
}
class
FakeDevice
extends
Fake
implements
Device
{
...
...
@@ -325,6 +498,9 @@ class FakeDevice extends Fake implements Device {
@override
Future
<
bool
>
get
isLocalEmulator
async
=>
false
;
@override
String
get
name
=>
'Fake Device'
;
@override
Future
<
bool
>
stopApp
(
covariant
ApplicationPackage
app
,
{
...
...
@@ -343,6 +519,7 @@ class FakeFlutterDevice extends Fake implements FlutterDevice {
FakeFlutterDevice
(
this
.
device
);
bool
stoppedEchoingDeviceLog
=
false
;
UpdateFSReport
updateDevFSReport
;
@override
final
FakeDevice
device
;
...
...
@@ -354,6 +531,28 @@ class FakeFlutterDevice extends Fake implements FlutterDevice {
@override
DevFS
devFS
=
FakeDevFs
();
@override
FlutterVmService
get
vmService
=>
FakeFlutterVmService
();
@override
ResidentCompiler
generator
;
@override
Future
<
UpdateFSReport
>
updateDevFS
({
Uri
mainUri
,
String
target
,
AssetBundle
bundle
,
DateTime
firstBuildTime
,
bool
bundleFirstUpload
=
false
,
bool
bundleDirty
=
false
,
bool
fullRestart
=
false
,
String
projectRootPath
,
String
pathToReload
,
@required
String
dillOutputPath
,
@required
List
<
Uri
>
invalidatedFiles
,
@required
PackageConfig
packageConfig
,
})
async
=>
updateDevFSReport
;
}
class
TestFlutterDevice
extends
FlutterDevice
{
...
...
@@ -405,3 +604,23 @@ class FakeResidentCompiler extends Fake implements ResidentCompiler {
@override
void
accept
()
{}
}
class
FakeFlutterVmService
extends
Fake
implements
FlutterVmService
{
@override
vm_service
.
VmService
get
service
=>
FakeVmService
();
@override
Future
<
List
<
FlutterView
>>
getFlutterViews
({
bool
returnEarly
=
false
,
Duration
delay
=
const
Duration
(
milliseconds:
50
)})
async
{
return
<
FlutterView
>[];
}
}
class
FakeVmService
extends
Fake
implements
vm_service
.
VmService
{
@override
Future
<
vm_service
.
VM
>
getVM
()
async
=>
FakeVm
();
}
class
FakeVm
extends
Fake
implements
vm_service
.
VM
{
@override
List
<
vm_service
.
IsolateRef
>
get
isolates
=>
<
vm_service
.
IsolateRef
>[];
}
packages/flutter_tools/test/src/fakes.dart
View file @
89a3c353
...
...
@@ -535,3 +535,56 @@ class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
@override
Future
<
int
>
findFreePort
({
bool
ipv6
=
false
})
async
=>
12345
;
}
class
FakeStopwatch
implements
Stopwatch
{
@override
bool
get
isRunning
=>
_isRunning
;
bool
_isRunning
=
false
;
@override
void
start
()
=>
_isRunning
=
true
;
@override
void
stop
()
=>
_isRunning
=
false
;
@override
Duration
elapsed
=
Duration
.
zero
;
@override
int
get
elapsedMicroseconds
=>
elapsed
.
inMicroseconds
;
@override
int
get
elapsedMilliseconds
=>
elapsed
.
inMilliseconds
;
@override
int
get
elapsedTicks
=>
elapsed
.
inMilliseconds
;
@override
int
get
frequency
=>
1000
;
@override
void
reset
()
{
_isRunning
=
false
;
elapsed
=
Duration
.
zero
;
}
@override
String
toString
()
=>
'
$runtimeType
$elapsed
$isRunning
'
;
}
class
FakeStopwatchFactory
implements
StopwatchFactory
{
FakeStopwatchFactory
({
Stopwatch
?
stopwatch
,
Map
<
String
,
Stopwatch
>?
stopwatches
})
:
stopwatches
=
<
String
,
Stopwatch
>{
if
(
stopwatches
!=
null
)
...
stopwatches
,
if
(
stopwatch
!=
null
)
''
:
stopwatch
,
};
Map
<
String
,
Stopwatch
>
stopwatches
;
@override
Stopwatch
createStopwatch
([
String
name
=
''
])
{
return
stopwatches
[
name
]
??
FakeStopwatch
();
}
}
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