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
e3ee5c6b
Unverified
Commit
e3ee5c6b
authored
Jul 13, 2019
by
Jonah Williams
Committed by
GitHub
Jul 13, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add better handling of JSON-RPC exception (#36082)
parent
d919e694
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
197 additions
and
22 deletions
+197
-22
resident_runner.dart
packages/flutter_tools/lib/src/resident_runner.dart
+22
-7
run_hot.dart
packages/flutter_tools/lib/src/run_hot.dart
+35
-0
usage.dart
packages/flutter_tools/lib/src/usage.dart
+6
-1
resident_runner_test.dart
...lutter_tools/test/general.shard/resident_runner_test.dart
+113
-14
terminal_handler_test.dart
...utter_tools/test/general.shard/terminal_handler_test.dart
+21
-0
No files found.
packages/flutter_tools/lib/src/resident_runner.dart
View file @
e3ee5c6b
...
...
@@ -844,7 +844,7 @@ abstract class ResidentRunner {
}
class
OperationResult
{
OperationResult
(
this
.
code
,
this
.
message
);
OperationResult
(
this
.
code
,
this
.
message
,
{
this
.
fatal
=
false
}
);
/// The result of the operation; a non-zero code indicates a failure.
final
int
code
;
...
...
@@ -852,6 +852,9 @@ class OperationResult {
/// A user facing message about the results of the operation.
final
String
message
;
/// Whether this error should cause the runner to exit.
final
bool
fatal
;
bool
get
isOk
=>
code
==
0
;
static
final
OperationResult
ok
=
OperationResult
(
0
,
''
);
...
...
@@ -906,8 +909,14 @@ class TerminalHandler {
void
registerSignalHandlers
()
{
assert
(
residentRunner
.
stayResident
);
io
.
ProcessSignal
.
SIGINT
.
watch
().
listen
(
_cleanUpAndExit
);
io
.
ProcessSignal
.
SIGTERM
.
watch
().
listen
(
_cleanUpAndExit
);
io
.
ProcessSignal
.
SIGINT
.
watch
().
listen
((
io
.
ProcessSignal
signal
)
{
_cleanUp
(
signal
);
io
.
exit
(
0
);
});
io
.
ProcessSignal
.
SIGTERM
.
watch
().
listen
((
io
.
ProcessSignal
signal
)
{
_cleanUp
(
signal
);
io
.
exit
(
0
);
});
if
(!
residentRunner
.
supportsServiceProtocol
||
!
residentRunner
.
supportsRestart
)
return
;
io
.
ProcessSignal
.
SIGUSR1
.
watch
().
listen
(
_handleSignal
);
...
...
@@ -990,6 +999,9 @@ class TerminalHandler {
return
false
;
}
final
OperationResult
result
=
await
residentRunner
.
restart
(
fullRestart:
false
);
if
(
result
.
fatal
)
{
throwToolExit
(
result
.
message
);
}
if
(!
result
.
isOk
)
{
printStatus
(
'Try again after fixing the above error(s).'
,
emphasis:
true
);
}
...
...
@@ -1000,6 +1012,9 @@ class TerminalHandler {
return
false
;
}
final
OperationResult
result
=
await
residentRunner
.
restart
(
fullRestart:
true
);
if
(
result
.
fatal
)
{
throwToolExit
(
result
.
message
);
}
if
(!
result
.
isOk
)
{
printStatus
(
'Try again after fixing the above error(s).'
,
emphasis:
true
);
}
...
...
@@ -1051,7 +1066,8 @@ class TerminalHandler {
await
_commonTerminalInputHandler
(
command
);
}
catch
(
error
,
st
)
{
printError
(
'
$error
\n
$st
'
);
await
_cleanUpAndExit
(
null
);
await
_cleanUp
(
null
);
rethrow
;
}
finally
{
_processingUserRequest
=
false
;
}
...
...
@@ -1073,11 +1089,10 @@ class TerminalHandler {
}
}
Future
<
void
>
_cleanUp
AndExit
(
io
.
ProcessSignal
signal
)
async
{
Future
<
void
>
_cleanUp
(
io
.
ProcessSignal
signal
)
async
{
terminal
.
singleCharMode
=
false
;
await
subscription
.
cancel
();
await
subscription
?
.
cancel
();
await
residentRunner
.
cleanupAfterSignal
();
io
.
exit
(
0
);
}
}
...
...
packages/flutter_tools/lib/src/run_hot.dart
View file @
e3ee5c6b
...
...
@@ -516,6 +516,9 @@ class HotRunner extends ResidentRunner {
final
OperationResult
result
=
await
_restartFromSources
(
reason:
reason
,
benchmarkMode:
benchmarkMode
,);
if
(!
result
.
isOk
)
return
result
;
}
on
rpc
.
RpcException
{
await
_measureJsonRpcException
(
flutterDevices
,
fullRestart
);
return
OperationResult
(
1
,
'hot restart failed to complete'
,
fatal:
true
);
}
finally
{
status
.
cancel
();
}
...
...
@@ -545,6 +548,9 @@ class HotRunner extends ResidentRunner {
showTime
=
false
;
},
);
}
on
rpc
.
RpcException
{
await
_measureJsonRpcException
(
flutterDevices
,
fullRestart
);
return
OperationResult
(
1
,
'hot reload failed to complete'
,
fatal:
true
);
}
finally
{
status
.
cancel
();
}
...
...
@@ -946,3 +952,32 @@ class ProjectFileInvalidator {
return
invalidatedFiles
;
}
}
// This is an error case we would like to know more about.
Future
<
void
>
_measureJsonRpcException
(
List
<
FlutterDevice
>
flutterDevices
,
bool
fullRestart
)
async
{
String
targetPlatform
;
String
deviceSdk
;
bool
emulator
;
if
(
flutterDevices
.
length
==
1
)
{
final
Device
device
=
flutterDevices
.
first
.
device
;
targetPlatform
=
getNameForTargetPlatform
(
await
device
.
targetPlatform
);
deviceSdk
=
await
device
.
sdkNameAndVersion
;
emulator
=
await
device
.
isLocalEmulator
;
}
else
if
(
flutterDevices
.
length
>
1
)
{
targetPlatform
=
'multiple'
;
deviceSdk
=
'multiple'
;
emulator
=
false
;
}
else
{
targetPlatform
=
'unknown'
;
deviceSdk
=
'unknown'
;
emulator
=
false
;
}
flutterUsage
.
sendEvent
(
'hot'
,
'exception'
,
parameters:
<
String
,
String
>{
reloadExceptionTargetPlatform:
targetPlatform
,
reloadExceptionSdkName:
deviceSdk
,
reloadExceptionEmulator:
emulator
.
toString
(),
reloadExceptionFullRestart:
fullRestart
.
toString
(),
},
);
}
packages/flutter_tools/lib/src/usage.dart
View file @
e3ee5c6b
...
...
@@ -49,7 +49,12 @@ const String kCommandBuildBundleTargetPlatform = 'cd24';
const
String
kCommandBuildBundleIsModule
=
'cd25'
;
const
String
kCommandResult
=
'cd26'
;
// Next ID: cd27
const
String
reloadExceptionTargetPlatform
=
'cd27'
;
const
String
reloadExceptionSdkName
=
'cd28'
;
const
String
reloadExceptionEmulator
=
'cd29'
;
const
String
reloadExceptionFullRestart
=
'cd30'
;
// Next ID: cd31
Usage
get
flutterUsage
=>
Usage
.
instance
;
...
...
packages/flutter_tools/test/general.shard/resident_runner_test.dart
View file @
e3ee5c6b
...
...
@@ -4,12 +4,15 @@
import
'dart:async'
;
import
'package:flutter_tools/src/base/common.dart'
;
import
'package:flutter_tools/src/build_info.dart'
;
import
'package:flutter_tools/src/devfs.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/resident_runner.dart'
;
import
'package:flutter_tools/src/run_hot.dart'
;
import
'package:flutter_tools/src/usage.dart'
;
import
'package:flutter_tools/src/vmservice.dart'
;
import
'package:json_rpc_2/json_rpc_2.dart'
;
import
'package:mockito/mockito.dart'
;
import
'../src/common.dart'
;
...
...
@@ -19,30 +22,34 @@ void main() {
group
(
'ResidentRunner'
,
()
{
final
Uri
testUri
=
Uri
.
parse
(
'foo://bar'
);
Testbed
testbed
;
Mock
Device
mock
Device
;
Mock
FlutterDevice
mockFlutter
Device
;
MockVMService
mockVMService
;
MockDevFS
mockDevFS
;
MockFlutterView
mockFlutterView
;
ResidentRunner
residentRunner
;
MockDevice
mockDevice
;
setUp
(()
{
testbed
=
Testbed
(
setup:
()
{
residentRunner
=
HotRunner
(
<
FlutterDevice
>[
mockDevice
,
mock
Flutter
Device
,
],
stayResident:
false
,
debuggingOptions:
DebuggingOptions
.
enabled
(
BuildInfo
.
debug
),
);
});
mockFlutterDevice
=
MockFlutterDevice
();
mockDevice
=
MockDevice
();
mockVMService
=
MockVMService
();
mockDevFS
=
MockDevFS
();
mockFlutterView
=
MockFlutterView
();
// DevFS Mocks
when
(
mockDevFS
.
lastCompiled
).
thenReturn
(
DateTime
(
2000
));
when
(
mockDevFS
.
sources
).
thenReturn
(<
Uri
>[]);
when
(
mockDevFS
.
destroy
()).
thenAnswer
((
Invocation
invocation
)
async
{
});
// FlutterDevice Mocks.
when
(
mockDevice
.
updateDevFS
(
when
(
mock
Flutter
Device
.
updateDevFS
(
// Intentionally provide empty list to match above mock.
invalidatedFiles:
<
Uri
>[],
mainPath:
anyNamed
(
'mainPath'
),
...
...
@@ -61,27 +68,29 @@ void main() {
invalidatedSourcesCount:
0
,
);
});
when
(
mockDevice
.
devFS
).
thenReturn
(
mockDevFS
);
when
(
mockDevice
.
views
).
thenReturn
(<
FlutterView
>[
MockFlutterView
(),
when
(
mock
Flutter
Device
.
devFS
).
thenReturn
(
mockDevFS
);
when
(
mock
Flutter
Device
.
views
).
thenReturn
(<
FlutterView
>[
mockFlutterView
]);
when
(
mockDevice
.
stopEchoingDeviceLog
()).
thenAnswer
((
Invocation
invocation
)
async
{
});
when
(
mockDevice
.
observatoryUris
).
thenReturn
(<
Uri
>[
when
(
mockFlutterDevice
.
device
).
thenReturn
(
mockDevice
);
when
(
mockFlutterView
.
uiIsolate
).
thenReturn
(
MockIsolate
());
when
(
mockFlutterDevice
.
stopEchoingDeviceLog
()).
thenAnswer
((
Invocation
invocation
)
async
{
});
when
(
mockFlutterDevice
.
observatoryUris
).
thenReturn
(<
Uri
>[
testUri
,
]);
when
(
mockDevice
.
connect
(
when
(
mock
Flutter
Device
.
connect
(
reloadSources:
anyNamed
(
'reloadSources'
),
restart:
anyNamed
(
'restart'
),
compileExpression:
anyNamed
(
'compileExpression'
)
)).
thenAnswer
((
Invocation
invocation
)
async
{
});
when
(
mockDevice
.
setupDevFS
(
any
,
any
,
packagesFilePath:
anyNamed
(
'packagesFilePath'
)))
when
(
mock
Flutter
Device
.
setupDevFS
(
any
,
any
,
packagesFilePath:
anyNamed
(
'packagesFilePath'
)))
.
thenAnswer
((
Invocation
invocation
)
async
{
return
testUri
;
});
when
(
mockDevice
.
vmServices
).
thenReturn
(<
VMService
>[
when
(
mock
Flutter
Device
.
vmServices
).
thenReturn
(<
VMService
>[
mockVMService
,
]);
when
(
mockDevice
.
refreshViews
()).
thenAnswer
((
Invocation
invocation
)
async
{
});
when
(
mock
Flutter
Device
.
refreshViews
()).
thenAnswer
((
Invocation
invocation
)
async
{
});
// VMService mocks.
when
(
mockVMService
.
wsAddress
).
thenReturn
(
testUri
);
when
(
mockVMService
.
done
).
thenAnswer
((
Invocation
invocation
)
{
...
...
@@ -101,16 +110,106 @@ void main() {
expect
(
await
result
,
0
);
verify
(
mockDevice
.
initLogReader
()).
called
(
1
);
verify
(
mock
Flutter
Device
.
initLogReader
()).
called
(
1
);
expect
(
onConnectionInfo
.
isCompleted
,
true
);
expect
((
await
connectionInfo
).
baseUri
,
'foo://bar'
);
expect
(
onAppStart
.
isCompleted
,
true
);
}));
test
(
'Can handle an RPC exception from hot reload'
,
()
=>
testbed
.
run
(()
async
{
when
(
mockDevice
.
sdkNameAndVersion
).
thenAnswer
((
Invocation
invocation
)
async
{
return
'Example'
;
});
when
(
mockDevice
.
targetPlatform
).
thenAnswer
((
Invocation
invocation
)
async
{
return
TargetPlatform
.
android_arm
;
});
when
(
mockDevice
.
isLocalEmulator
).
thenAnswer
((
Invocation
invocation
)
async
{
return
false
;
});
final
Completer
<
DebugConnectionInfo
>
onConnectionInfo
=
Completer
<
DebugConnectionInfo
>.
sync
();
final
Completer
<
void
>
onAppStart
=
Completer
<
void
>.
sync
();
unawaited
(
residentRunner
.
attach
(
appStartedCompleter:
onAppStart
,
connectionInfoCompleter:
onConnectionInfo
,
));
await
onAppStart
.
future
;
when
(
mockFlutterDevice
.
updateDevFS
(
mainPath:
anyNamed
(
'mainPath'
),
target:
anyNamed
(
'target'
),
bundle:
anyNamed
(
'bundle'
),
firstBuildTime:
anyNamed
(
'firstBuildTime'
),
bundleFirstUpload:
anyNamed
(
'bundleFirstUpload'
),
bundleDirty:
anyNamed
(
'bundleDirty'
),
fullRestart:
anyNamed
(
'fullRestart'
),
projectRootPath:
anyNamed
(
'projectRootPath'
),
pathToReload:
anyNamed
(
'pathToReload'
),
invalidatedFiles:
anyNamed
(
'invalidatedFiles'
),
)).
thenThrow
(
RpcException
(
666
,
'something bad happened'
));
final
OperationResult
result
=
await
residentRunner
.
restart
(
fullRestart:
false
);
expect
(
result
.
fatal
,
true
);
expect
(
result
.
code
,
1
);
verify
(
flutterUsage
.
sendEvent
(
'hot'
,
'exception'
,
parameters:
<
String
,
String
>{
reloadExceptionTargetPlatform:
getNameForTargetPlatform
(
TargetPlatform
.
android_arm
),
reloadExceptionSdkName:
'Example'
,
reloadExceptionEmulator:
'false'
,
reloadExceptionFullRestart:
'false'
,
})).
called
(
1
);
},
overrides:
<
Type
,
Generator
>{
Usage:
()
=>
MockUsage
(),
}));
test
(
'Can handle an RPC exception from hot restart'
,
()
=>
testbed
.
run
(()
async
{
when
(
mockDevice
.
sdkNameAndVersion
).
thenAnswer
((
Invocation
invocation
)
async
{
return
'Example'
;
});
when
(
mockDevice
.
targetPlatform
).
thenAnswer
((
Invocation
invocation
)
async
{
return
TargetPlatform
.
android_arm
;
});
when
(
mockDevice
.
isLocalEmulator
).
thenAnswer
((
Invocation
invocation
)
async
{
return
false
;
});
when
(
mockDevice
.
supportsHotRestart
).
thenReturn
(
true
);
final
Completer
<
DebugConnectionInfo
>
onConnectionInfo
=
Completer
<
DebugConnectionInfo
>.
sync
();
final
Completer
<
void
>
onAppStart
=
Completer
<
void
>.
sync
();
unawaited
(
residentRunner
.
attach
(
appStartedCompleter:
onAppStart
,
connectionInfoCompleter:
onConnectionInfo
,
));
await
onAppStart
.
future
;
when
(
mockFlutterDevice
.
updateDevFS
(
mainPath:
anyNamed
(
'mainPath'
),
target:
anyNamed
(
'target'
),
bundle:
anyNamed
(
'bundle'
),
firstBuildTime:
anyNamed
(
'firstBuildTime'
),
bundleFirstUpload:
anyNamed
(
'bundleFirstUpload'
),
bundleDirty:
anyNamed
(
'bundleDirty'
),
fullRestart:
anyNamed
(
'fullRestart'
),
projectRootPath:
anyNamed
(
'projectRootPath'
),
pathToReload:
anyNamed
(
'pathToReload'
),
invalidatedFiles:
anyNamed
(
'invalidatedFiles'
),
)).
thenThrow
(
RpcException
(
666
,
'something bad happened'
));
final
OperationResult
result
=
await
residentRunner
.
restart
(
fullRestart:
true
);
expect
(
result
.
fatal
,
true
);
expect
(
result
.
code
,
1
);
verify
(
flutterUsage
.
sendEvent
(
'hot'
,
'exception'
,
parameters:
<
String
,
String
>{
reloadExceptionTargetPlatform:
getNameForTargetPlatform
(
TargetPlatform
.
android_arm
),
reloadExceptionSdkName:
'Example'
,
reloadExceptionEmulator:
'false'
,
reloadExceptionFullRestart:
'true'
,
})).
called
(
1
);
},
overrides:
<
Type
,
Generator
>{
Usage:
()
=>
MockUsage
(),
}));
});
}
class
MockDevice
extends
Mock
implements
FlutterDevice
{}
class
Mock
Flutter
Device
extends
Mock
implements
FlutterDevice
{}
class
MockFlutterView
extends
Mock
implements
FlutterView
{}
class
MockVMService
extends
Mock
implements
VMService
{}
class
MockDevFS
extends
Mock
implements
DevFS
{}
class
MockIsolate
extends
Mock
implements
Isolate
{}
class
MockDevice
extends
Mock
implements
Device
{}
class
MockUsage
extends
Mock
implements
Usage
{}
packages/flutter_tools/test/general.shard/terminal_handler_test.dart
View file @
e3ee5c6b
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'package:flutter_tools/src/base/common.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/build_info.dart'
;
import
'package:flutter_tools/src/device.dart'
;
...
...
@@ -246,6 +247,16 @@ void main() {
expect
(
bufferLogger
.
statusText
,
contains
(
'Try again after fixing the above error(s).'
));
});
testUsingContext
(
'r - hotReload supported and fails fatally'
,
()
async
{
when
(
mockResidentRunner
.
canHotReload
).
thenReturn
(
true
);
when
(
mockResidentRunner
.
hotMode
).
thenReturn
(
true
);
when
(
mockResidentRunner
.
restart
(
fullRestart:
false
))
.
thenAnswer
((
Invocation
invocation
)
async
{
return
OperationResult
(
1
,
'fail'
,
fatal:
true
);
});
expect
(
terminalHandler
.
processTerminalInput
(
'r'
),
throwsA
(
isInstanceOf
<
ToolExit
>()));
});
testUsingContext
(
'r - hotReload unsupported'
,
()
async
{
when
(
mockResidentRunner
.
canHotReload
).
thenReturn
(
false
);
await
terminalHandler
.
processTerminalInput
(
'r'
);
...
...
@@ -281,6 +292,16 @@ void main() {
expect
(
bufferLogger
.
statusText
,
contains
(
'Try again after fixing the above error(s).'
));
});
testUsingContext
(
'R - hotRestart supported and fails fatally'
,
()
async
{
when
(
mockResidentRunner
.
canHotRestart
).
thenReturn
(
true
);
when
(
mockResidentRunner
.
hotMode
).
thenReturn
(
true
);
when
(
mockResidentRunner
.
restart
(
fullRestart:
true
))
.
thenAnswer
((
Invocation
invocation
)
async
{
return
OperationResult
(
1
,
'fail'
,
fatal:
true
);
});
expect
(()
=>
terminalHandler
.
processTerminalInput
(
'R'
),
throwsA
(
isInstanceOf
<
ToolExit
>()));
});
testUsingContext
(
'R - hot restart unsupported'
,
()
async
{
when
(
mockResidentRunner
.
canHotRestart
).
thenReturn
(
false
);
await
terminalHandler
.
processTerminalInput
(
'R'
);
...
...
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