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
36e8b93d
Unverified
Commit
36e8b93d
authored
Aug 21, 2019
by
Zachary Anderson
Committed by
GitHub
Aug 21, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flutter_tool] Only send one crash report per run (#38925)
parent
ea64b84d
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
116 additions
and
34 deletions
+116
-34
runner.dart
packages/flutter_tools/lib/runner.dart
+32
-31
crash_reporting.dart
...ages/flutter_tools/lib/src/reporting/crash_reporting.dart
+7
-0
crash_reporting_test.dart
...lutter_tools/test/general.shard/crash_reporting_test.dart
+77
-3
No files found.
packages/flutter_tools/lib/runner.dart
View file @
36e8b93d
...
...
@@ -115,39 +115,40 @@ Future<int> _handleToolError(
stderr
.
writeln
(
'
$error
'
);
stderr
.
writeln
(
stackTrace
.
toString
());
return
_exit
(
1
);
}
else
{
// Report to both [Usage] and [CrashReportSender].
flutterUsage
.
sendException
(
error
);
await
CrashReportSender
.
instance
.
sendReport
(
error:
error
,
stackTrace:
stackTrace
,
getFlutterVersion:
getFlutterVersion
,
command:
args
.
join
(
' '
),
);
}
if
(
error
is
String
)
stderr
.
writeln
(
'Oops; flutter has exited unexpectedly: "
$error
".'
);
else
stderr
.
writeln
(
'Oops; flutter has exited unexpectedly.'
);
// Report to both [Usage] and [CrashReportSender].
flutterUsage
.
sendException
(
error
);
await
CrashReportSender
.
instance
.
sendReport
(
error:
error
,
stackTrace:
stackTrace
,
getFlutterVersion:
getFlutterVersion
,
command:
args
.
join
(
' '
),
);
try
{
final
File
file
=
await
_createLocalCrashReport
(
args
,
error
,
stackTrace
);
stderr
.
writeln
(
'Crash report written to
${file.path}
;
\n
'
'please let us know at https://github.com/flutter/flutter/issues.'
,
);
return
_exit
(
1
);
}
catch
(
error
)
{
stderr
.
writeln
(
'Unable to generate crash report due to secondary error:
$error
\n
'
'please let us know at https://github.com/flutter/flutter/issues.'
,
);
// Any exception throw here (including one thrown by `_exit()`) will
// get caught by our zone's `onError` handler. In order to avoid an
// infinite error loop, we throw an error that is recognized above
// and will trigger an immediate exit.
throw
ProcessExit
(
1
,
immediate:
true
);
}
if
(
error
is
String
)
{
stderr
.
writeln
(
'Oops; flutter has exited unexpectedly: "
$error
".'
);
}
else
{
stderr
.
writeln
(
'Oops; flutter has exited unexpectedly.'
);
}
try
{
final
File
file
=
await
_createLocalCrashReport
(
args
,
error
,
stackTrace
);
stderr
.
writeln
(
'Crash report written to
${file.path}
;
\n
'
'please let us know at https://github.com/flutter/flutter/issues.'
,
);
return
_exit
(
1
);
}
catch
(
error
)
{
stderr
.
writeln
(
'Unable to generate crash report due to secondary error:
$error
\n
'
'please let us know at https://github.com/flutter/flutter/issues.'
,
);
// Any exception throw here (including one thrown by `_exit()`) will
// get caught by our zone's `onError` handler. In order to avoid an
// infinite error loop, we throw an error that is recognized above
// and will trigger an immediate exit.
throw
ProcessExit
(
1
,
immediate:
true
);
}
}
}
...
...
packages/flutter_tools/lib/src/reporting/crash_reporting.dart
View file @
36e8b93d
...
...
@@ -44,6 +44,8 @@ class CrashReportSender {
static
CrashReportSender
get
instance
=>
_instance
??
CrashReportSender
.
_
(
http
.
Client
());
bool
_crashReportSent
=
false
;
/// Overrides the default [http.Client] with [client] for testing purposes.
@visibleForTesting
static
void
initializeWith
(
http
.
Client
client
)
{
...
...
@@ -76,6 +78,10 @@ class CrashReportSender {
@required
String
getFlutterVersion
(),
@required
String
command
,
})
async
{
// Only send one crash report per run.
if
(
_crashReportSent
)
{
return
;
}
try
{
final
String
flutterVersion
=
getFlutterVersion
();
...
...
@@ -116,6 +122,7 @@ class CrashReportSender {
final
String
reportId
=
await
http
.
ByteStream
(
resp
.
stream
)
.
bytesToString
();
printStatus
(
'Crash report sent (report ID:
$reportId
)'
);
_crashReportSent
=
true
;
}
else
{
printError
(
'Failed to send crash report. Server responded with HTTP status code
${resp.statusCode}
'
);
}
...
...
packages/flutter_tools/test/general.shard/crash_reporting_test.dart
View file @
36e8b93d
...
...
@@ -14,11 +14,13 @@ import 'package:flutter_tools/src/base/io.dart';
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/cache.dart'
;
import
'package:flutter_tools/src/doctor.dart'
;
import
'package:flutter_tools/src/reporting/reporting.dart'
;
import
'package:flutter_tools/src/runner/flutter_command.dart'
;
import
'package:http/http.dart'
;
import
'package:http/testing.dart'
;
import
'package:pedantic/pedantic.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'../src/common.dart'
;
import
'../src/context.dart'
;
...
...
@@ -32,6 +34,7 @@ void main() {
setUp
(()
async
{
tools
.
crashFileSystem
=
MemoryFileSystem
();
setExitFunctionForTests
((
_
)
{
});
MockCrashReportSender
.
sendCalls
=
0
;
});
tearDown
(()
{
...
...
@@ -72,12 +75,43 @@ void main() {
reportCrashes:
true
,
flutterVersion:
'test-version'
,
));
expect
(
await
exitCodeCompleter
.
future
,
equals
(
1
)
);
expect
(
await
exitCodeCompleter
.
future
,
1
);
await
verifyCrashReportSent
(
requestInfo
);
},
overrides:
<
Type
,
Generator
>{
Stdio:
()
=>
const
_NoStderr
(),
});
testUsingContext
(
'should send only one crash report when async throws many'
,
()
async
{
final
Completer
<
int
>
exitCodeCompleter
=
Completer
<
int
>();
setExitFunctionForTests
((
int
exitCode
)
{
if
(!
exitCodeCompleter
.
isCompleted
)
{
exitCodeCompleter
.
complete
(
exitCode
);
}
});
final
RequestInfo
requestInfo
=
RequestInfo
();
final
MockCrashReportSender
sender
=
MockCrashReportSender
(
requestInfo
);
CrashReportSender
.
initializeWith
(
sender
);
FakeAsync
().
run
((
FakeAsync
time
)
{
time
.
elapse
(
const
Duration
(
seconds:
1
));
unawaited
(
tools
.
run
(
<
String
>[
'crash'
],
<
FlutterCommand
>[
_MultiCrashAsyncCommand
(
crashes:
4
)],
reportCrashes:
true
,
flutterVersion:
'test-version'
,
));
time
.
elapse
(
const
Duration
(
seconds:
1
));
time
.
flushMicrotasks
();
});
expect
(
await
exitCodeCompleter
.
future
,
1
);
expect
(
MockCrashReportSender
.
sendCalls
,
1
);
await
verifyCrashReportSent
(
requestInfo
,
crashes:
4
);
},
overrides:
<
Type
,
Generator
>{
DoctorValidatorsProvider:
()
=>
FakeDoctorValidatorsProvider
(),
Stdio:
()
=>
const
_NoStderr
(),
});
testUsingContext
(
'should not send a crash report if on a user-branch'
,
()
async
{
String
method
;
Uri
uri
;
...
...
@@ -159,7 +193,9 @@ class RequestInfo {
Map
<
String
,
String
>
fields
;
}
Future
<
void
>
verifyCrashReportSent
(
RequestInfo
crashInfo
)
async
{
Future
<
void
>
verifyCrashReportSent
(
RequestInfo
crashInfo
,
{
int
crashes
=
1
,
})
async
{
// Verify that we sent the crash report.
expect
(
crashInfo
.
method
,
'POST'
);
expect
(
crashInfo
.
uri
,
Uri
(
...
...
@@ -190,12 +226,13 @@ Future<void> verifyCrashReportSent(RequestInfo crashInfo) async {
final
List
<
String
>
writtenFiles
=
(
await
tools
.
crashFileSystem
.
directory
(
'/'
).
list
(
recursive:
true
).
toList
())
.
map
((
FileSystemEntity
e
)
=>
e
.
path
).
toList
();
expect
(
writtenFiles
,
hasLength
(
1
));
expect
(
writtenFiles
,
hasLength
(
crashes
));
expect
(
writtenFiles
,
contains
(
'flutter_01.log'
));
}
class
MockCrashReportSender
extends
MockClient
{
MockCrashReportSender
(
RequestInfo
crashInfo
)
:
super
((
Request
request
)
async
{
MockCrashReportSender
.
sendCalls
++;
crashInfo
.
method
=
request
.
method
;
crashInfo
.
uri
=
request
.
url
;
...
...
@@ -229,6 +266,8 @@ class MockCrashReportSender extends MockClient {
200
,
);
});
static
int
sendCalls
=
0
;
}
/// Throws a random error to simulate a CLI crash.
...
...
@@ -278,6 +317,41 @@ class _CrashAsyncCommand extends FlutterCommand {
}
}
/// Generates multiple asynchronous unhandled exceptions.
class
_MultiCrashAsyncCommand
extends
FlutterCommand
{
_MultiCrashAsyncCommand
({
int
crashes
=
1
,
})
:
_crashes
=
crashes
;
final
int
_crashes
;
@override
String
get
description
=>
'Simulates a crash'
;
@override
String
get
name
=>
'crash'
;
@override
Future
<
FlutterCommandResult
>
runCommand
()
async
{
for
(
int
i
=
0
;
i
<
_crashes
;
i
++)
{
Timer
.
run
(()
{
throw
StateError
(
'Test bad state error'
);
});
}
return
Completer
<
FlutterCommandResult
>().
future
;
// expect StateError
}
}
/// A DoctorValidatorsProvider that overrides the default validators without
/// overriding the doctor.
class
FakeDoctorValidatorsProvider
implements
DoctorValidatorsProvider
{
@override
List
<
DoctorValidator
>
get
validators
=>
<
DoctorValidator
>[];
@override
List
<
Workflow
>
get
workflows
=>
<
Workflow
>[];
}
class
_NoStderr
extends
Stdio
{
const
_NoStderr
();
...
...
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