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
8109dcc2
Unverified
Commit
8109dcc2
authored
Apr 20, 2020
by
Jenn Magder
Committed by
GitHub
Apr 20, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
CrashReportSender dependency injection (#54924)
parent
9202e547
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
248 additions
and
347 deletions
+248
-347
runner.dart
packages/flutter_tools/lib/runner.dart
+18
-19
crash_reporting.dart
...ages/flutter_tools/lib/src/reporting/crash_reporting.dart
+25
-24
reporting.dart
packages/flutter_tools/lib/src/reporting/reporting.dart
+2
-0
crash_reporting_test.dart
...lutter_tools/test/general.shard/crash_reporting_test.dart
+203
-302
runner_test.dart
.../flutter_tools/test/general.shard/runner/runner_test.dart
+0
-2
No files found.
packages/flutter_tools/lib/runner.dart
View file @
8109dcc2
...
...
@@ -7,7 +7,7 @@ import 'dart:async';
import
'package:args/command_runner.dart'
;
import
'package:intl/intl.dart'
as
intl
;
import
'package:intl/intl_standalone.dart'
as
intl_standalone
;
import
'package:
meta/meta.dart'
;
import
'package:
http/http.dart'
as
http
;
import
'src/base/common.dart'
;
import
'src/base/context.dart'
;
...
...
@@ -27,13 +27,13 @@ import 'src/runner/flutter_command_runner.dart';
Future
<
int
>
run
(
List
<
String
>
args
,
List
<
FlutterCommand
>
commands
,
{
bool
muteCommandLogging
=
false
,
bool
verbose
=
false
,
bool
verboseHelp
=
false
,
bool
reportCrashes
,
String
flutterVersion
,
Map
<
Type
,
Generator
>
overrides
,
})
async
{
bool
muteCommandLogging
=
false
,
bool
verbose
=
false
,
bool
verboseHelp
=
false
,
bool
reportCrashes
,
String
flutterVersion
,
Map
<
Type
,
Generator
>
overrides
,
})
async
{
if
(
muteCommandLogging
)
{
// Remove the verbose option; for help and doctor, users don't need to see
// verbose logs.
...
...
@@ -121,7 +121,14 @@ Future<int> _handleToolError(
// Report to both [Usage] and [CrashReportSender].
globals
.
flutterUsage
.
sendException
(
error
);
await
CrashReportSender
.
instance
.
sendReport
(
final
CrashReportSender
crashReportSender
=
CrashReportSender
(
client:
http
.
Client
(),
usage:
globals
.
flutterUsage
,
platform:
globals
.
platform
,
logger:
globals
.
logger
,
operatingSystemUtils:
globals
.
os
,
);
await
crashReportSender
.
sendReport
(
error:
error
,
stackTrace:
stackTrace
,
getFlutterVersion:
getFlutterVersion
,
...
...
@@ -184,18 +191,10 @@ String _crashCommand(List<String> args) => 'flutter ${args.join(' ')}';
String
_crashException
(
dynamic
error
)
=>
'
${error.runtimeType}
:
$error
'
;
/// File system used by the crash reporting logic.
///
/// We do not want to use the file system stored in the context because it may
/// be recording. Additionally, in the case of a crash we do not trust the
/// integrity of the [AppContext].
@visibleForTesting
FileSystem
crashFileSystem
=
const
LocalFileSystem
();
/// Saves the crash report to a local file.
Future
<
File
>
_createLocalCrashReport
(
List
<
String
>
args
,
dynamic
error
,
StackTrace
stackTrace
,
String
doctorText
)
async
{
File
crashFile
=
globals
.
fsUtils
.
getUniqueFile
(
crashFileSystem
.
currentDirectory
,
globals
.
fs
.
currentDirectory
,
'flutter'
,
'log'
,
);
...
...
@@ -219,7 +218,7 @@ Future<File> _createLocalCrashReport(List<String> args, dynamic error, StackTrac
}
on
FileSystemException
catch
(
_
)
{
// Fallback to the system temporary directory.
crashFile
=
globals
.
fsUtils
.
getUniqueFile
(
crashFileSystem
.
systemTempDirectory
,
globals
.
fs
.
systemTempDirectory
,
'flutter'
,
'log'
,
);
...
...
packages/flutter_tools/lib/src/reporting/crash_reporting.dart
View file @
8109dcc2
...
...
@@ -35,28 +35,29 @@ const String _kStackTraceFilename = 'stacktrace_file';
/// environment is behind a firewall and unable to send crash reports to
/// Google, or when you wish to use your own server for collecting crash
/// reports from Flutter Tools.
/// * In tests call [initializeWith] and provide a mock implementation of
/// [http.Client].
class
CrashReportSender
{
CrashReportSender
.
_
(
this
.
_client
);
CrashReportSender
({
@required
http
.
Client
client
,
@required
Usage
usage
,
@required
Platform
platform
,
@required
Logger
logger
,
@required
OperatingSystemUtils
operatingSystemUtils
,
})
:
_client
=
client
,
_usage
=
usage
,
_platform
=
platform
,
_logger
=
logger
,
_operatingSystemUtils
=
operatingSystemUtils
;
static
CrashReportSender
_instance
;
static
CrashReportSender
get
instance
=>
_instance
??
CrashReportSender
.
_
(
http
.
Client
());
final
http
.
Client
_client
;
final
Usage
_usage
;
final
Platform
_platform
;
final
Logger
_logger
;
final
OperatingSystemUtils
_operatingSystemUtils
;
bool
_crashReportSent
=
false
;
/// Overrides the default [http.Client] with [client] for testing purposes.
@visibleForTesting
static
void
initializeWith
(
http
.
Client
client
)
{
_instance
=
CrashReportSender
.
_
(
client
);
}
final
http
.
Client
_client
;
final
Usage
_usage
=
globals
.
flutterUsage
;
Uri
get
_baseUrl
{
final
String
overrideUrl
=
globals
.
platform
.
environment
[
'FLUTTER_CRASH_SERVER_BASE_URL'
];
final
String
overrideUrl
=
_
platform
.
environment
[
'FLUTTER_CRASH_SERVER_BASE_URL'
];
if
(
overrideUrl
!=
null
)
{
return
Uri
.
parse
(
overrideUrl
);
...
...
@@ -90,7 +91,7 @@ class CrashReportSender {
return
;
}
globals
.
printTrace
(
'Sending crash report to Google.'
);
_logger
.
printTrace
(
'Sending crash report to Google.'
);
final
Uri
uri
=
_baseUrl
.
replace
(
queryParameters:
<
String
,
String
>{
...
...
@@ -103,8 +104,8 @@ class CrashReportSender {
req
.
fields
[
'uuid'
]
=
_usage
.
clientId
;
req
.
fields
[
'product'
]
=
_kProductId
;
req
.
fields
[
'version'
]
=
flutterVersion
;
req
.
fields
[
'osName'
]
=
globals
.
platform
.
operatingSystem
;
req
.
fields
[
'osVersion'
]
=
globals
.
o
s
.
name
;
// this actually includes version
req
.
fields
[
'osName'
]
=
_
platform
.
operatingSystem
;
req
.
fields
[
'osVersion'
]
=
_operatingSystemUtil
s
.
name
;
// this actually includes version
req
.
fields
[
'type'
]
=
_kDartTypeId
;
req
.
fields
[
'error_runtime_type'
]
=
'
${error.runtimeType}
'
;
req
.
fields
[
'error_message'
]
=
'
$error
'
;
...
...
@@ -120,20 +121,20 @@ class CrashReportSender {
if
(
resp
.
statusCode
==
200
)
{
final
String
reportId
=
await
http
.
ByteStream
(
resp
.
stream
)
.
bytesToString
();
globals
.
printTrace
(
'Crash report sent (report ID:
$reportId
)'
);
.
bytesToString
();
_logger
.
printTrace
(
'Crash report sent (report ID:
$reportId
)'
);
_crashReportSent
=
true
;
}
else
{
globals
.
printError
(
'Failed to send crash report. Server responded with HTTP status code
${resp.statusCode}
'
);
_logger
.
printError
(
'Failed to send crash report. Server responded with HTTP status code
${resp.statusCode}
'
);
}
// Catch all exceptions to print the message that makes clear that the
// crash logger crashed.
}
catch
(
sendError
,
sendStackTrace
)
{
// ignore: avoid_catches_without_on_clauses
if
(
sendError
is
SocketException
||
sendError
is
HttpException
)
{
globals
.
printError
(
'Failed to send crash report due to a network error:
$sendError
'
);
_logger
.
printError
(
'Failed to send crash report due to a network error:
$sendError
'
);
}
else
{
// If the sender itself crashes, just print. We did our best.
globals
.
printError
(
'Crash report sender itself crashed:
$sendError
\n
$sendStackTrace
'
);
_logger
.
printError
(
'Crash report sender itself crashed:
$sendError
\n
$sendStackTrace
'
);
}
}
}
...
...
packages/flutter_tools/lib/src/reporting/reporting.dart
View file @
8109dcc2
...
...
@@ -10,11 +10,13 @@ import 'package:file/file.dart';
import
'package:http/http.dart'
as
http
;
import
'package:intl/intl.dart'
;
import
'package:meta/meta.dart'
;
import
'package:platform/platform.dart'
;
import
'package:usage/usage_io.dart'
;
import
'../base/file_system.dart'
;
import
'../base/io.dart'
;
import
'../base/logger.dart'
;
import
'../base/os.dart'
;
import
'../base/process.dart'
;
import
'../base/time.dart'
;
import
'../build_system/exceptions.dart'
;
...
...
packages/flutter_tools/test/general.shard/crash_reporting_test.dart
View file @
8109dcc2
...
...
@@ -5,160 +5,205 @@
import
'dart:async'
;
import
'dart:convert'
;
import
'package:file/file.dart'
;
import
'package:file/local.dart'
;
import
'package:file/memory.dart'
;
import
'package:flutter_tools/runner.dart'
as
tools
;
import
'package:flutter_tools/src/base/common.dart'
;
import
'package:flutter_tools/src/base/context.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/cache.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/os.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:flutter_tools/src/globals.dart'
as
globals
;
import
'package:http/http.dart'
;
import
'package:http/testing.dart'
;
import
'package:
quiver/testing/async
.dart'
;
import
'package:
mockito/mockito
.dart'
;
import
'package:platform/platform.dart'
;
import
'../src/common.dart'
;
import
'../src/context.dart'
;
void
main
(
)
{
group
(
'crash reporting'
,
()
{
setUpAll
(()
{
Cache
.
disableLocking
();
});
BufferLogger
logger
;
MockUsage
mockUsage
;
Platform
platform
;
OperatingSystemUtils
operatingSystemUtils
;
setUp
(()
async
{
logger
=
BufferLogger
.
test
();
mockUsage
=
MockUsage
();
when
(
mockUsage
.
clientId
).
thenReturn
(
'00000000-0000-4000-0000-000000000000'
);
platform
=
FakePlatform
(
environment:
<
String
,
String
>{},
operatingSystem:
'linux'
);
operatingSystemUtils
=
OperatingSystemUtils
(
fileSystem:
MemoryFileSystem
.
test
(),
logger:
logger
,
platform:
platform
,
processManager:
FakeProcessManager
.
any
(),
);
setUp
(()
async
{
tools
.
crashFileSystem
=
MemoryFileSystem
();
setExitFunctionForTests
((
_
)
{
});
MockCrashReportSender
.
sendCalls
=
0
;
});
MockCrashReportSender
.
sendCalls
=
0
;
});
Future
<
void
>
verifyCrashReportSent
(
RequestInfo
crashInfo
,
{
int
crashes
=
1
,
})
async
{
// Verify that we sent the crash report.
expect
(
crashInfo
.
method
,
'POST'
);
expect
(
crashInfo
.
uri
,
Uri
(
scheme:
'https'
,
host:
'clients2.google.com'
,
port:
443
,
path:
'/cr/report'
,
queryParameters:
<
String
,
String
>{
'product'
:
'Flutter_Tools'
,
'version'
:
'test-version'
,
},
));
expect
(
crashInfo
.
fields
[
'uuid'
],
'00000000-0000-4000-0000-000000000000'
);
expect
(
crashInfo
.
fields
[
'product'
],
'Flutter_Tools'
);
expect
(
crashInfo
.
fields
[
'version'
],
'test-version'
);
expect
(
crashInfo
.
fields
[
'osName'
],
'linux'
);
expect
(
crashInfo
.
fields
[
'osVersion'
],
'Linux'
);
expect
(
crashInfo
.
fields
[
'type'
],
'DartError'
);
expect
(
crashInfo
.
fields
[
'error_runtime_type'
],
'StateError'
);
expect
(
crashInfo
.
fields
[
'error_message'
],
'Bad state: Test bad state error'
);
expect
(
crashInfo
.
fields
[
'comments'
],
'crash'
);
expect
(
logger
.
traceText
,
contains
(
'Sending crash report to Google.'
));
expect
(
logger
.
traceText
,
contains
(
'Crash report sent (report ID: test-report-id)'
));
}
testWithoutContext
(
'suppress analytics'
,
()
async
{
when
(
mockUsage
.
suppressAnalytics
).
thenReturn
(
true
);
final
CrashReportSender
crashReportSender
=
CrashReportSender
(
client:
CrashingCrashReportSender
(
const
SocketException
(
'no internets'
)),
usage:
mockUsage
,
platform:
platform
,
logger:
logger
,
operatingSystemUtils:
operatingSystemUtils
,
);
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'test-version'
,
command:
'crash'
,
);
expect
(
logger
.
traceText
,
isEmpty
);
});
tearDown
(
()
{
tools
.
crashFileSystem
=
const
LocalFileSystem
();
restoreExitFunction
(
);
group
(
'allow analytics'
,
()
{
setUp
(()
async
{
when
(
mockUsage
.
suppressAnalytics
).
thenReturn
(
false
);
});
test
Using
Context
(
'should send crash reports'
,
()
async
{
test
Without
Context
(
'should send crash reports'
,
()
async
{
final
RequestInfo
requestInfo
=
RequestInfo
();
CrashReportSender
.
initializeWith
(
MockCrashReportSender
(
requestInfo
));
final
int
exitCode
=
await
tools
.
run
(
<
String
>[
'crash'
],
<
FlutterCommand
>[
_CrashCommand
()],
reportCrashes:
true
,
flutterVersion:
'test-version'
,
final
CrashReportSender
crashReportSender
=
CrashReportSender
(
client:
MockCrashReportSender
(
requestInfo
),
usage:
mockUsage
,
platform:
platform
,
logger:
logger
,
operatingSystemUtils:
operatingSystemUtils
,
);
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'test-version'
,
command:
'crash'
,
);
expect
(
exitCode
,
1
);
await
verifyCrashReportSent
(
requestInfo
);
},
overrides:
<
Type
,
Generator
>{
Stdio:
()
=>
_NoStderr
(),
});
testUsingContext
(
'should print an explanatory message when there is a SocketException'
,
()
async
{
final
Completer
<
int
>
exitCodeCompleter
=
Completer
<
int
>();
setExitFunctionForTests
((
int
exitCode
)
{
exitCodeCompleter
.
complete
(
exitCode
);
});
testWithoutContext
(
'should print an explanatory message when there is a SocketException'
,
()
async
{
final
CrashReportSender
crashReportSender
=
CrashReportSender
(
client:
CrashingCrashReportSender
(
const
SocketException
(
'no internets'
)),
usage:
mockUsage
,
platform:
platform
,
logger:
logger
,
operatingSystemUtils:
operatingSystemUtils
,
);
CrashReportSender
.
initializeWith
(
CrashingCrashReportSender
(
const
SocketException
(
'no internets'
)));
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'test-version'
,
command:
'crash'
,
);
unawaited
(
tools
.
run
(
<
String
>[
'crash'
],
<
FlutterCommand
>[
_CrashAsyncCommand
()],
reportCrashes:
true
,
flutterVersion:
'test-version'
,
));
expect
(
await
exitCodeCompleter
.
future
,
1
);
expect
(
testLogger
.
errorText
,
contains
(
'Failed to send crash report due to a network error'
));
},
overrides:
<
Type
,
Generator
>{
Stdio:
()
=>
_NoStderr
(),
expect
(
logger
.
errorText
,
contains
(
'Failed to send crash report due to a network error'
));
});
testUsingContext
(
'should print an explanatory message when there is an HttpException'
,
()
async
{
final
Completer
<
int
>
exitCodeCompleter
=
Completer
<
int
>();
setExitFunctionForTests
((
int
exitCode
)
{
exitCodeCompleter
.
complete
(
exitCode
);
});
testWithoutContext
(
'should print an explanatory message when there is an HttpException'
,
()
async
{
final
CrashReportSender
crashReportSender
=
CrashReportSender
(
client:
CrashingCrashReportSender
(
const
HttpException
(
'no internets'
)),
usage:
mockUsage
,
platform:
platform
,
logger:
logger
,
operatingSystemUtils:
operatingSystemUtils
,
);
CrashReportSender
.
initializeWith
(
CrashingCrashReportSender
(
const
HttpException
(
'no internets'
)));
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'test-version'
,
command:
'crash'
,
);
unawaited
(
tools
.
run
(
<
String
>[
'crash'
],
<
FlutterCommand
>[
_CrashAsyncCommand
()],
reportCrashes:
true
,
flutterVersion:
'test-version'
,
));
expect
(
await
exitCodeCompleter
.
future
,
1
);
expect
(
testLogger
.
errorText
,
contains
(
'Failed to send crash report due to a network error'
));
},
overrides:
<
Type
,
Generator
>{
Stdio:
()
=>
_NoStderr
(),
expect
(
logger
.
errorText
,
contains
(
'Failed to send crash report due to a network error'
));
});
testUsingContext
(
'should send crash reports when async throws'
,
()
async
{
final
Completer
<
int
>
exitCodeCompleter
=
Completer
<
int
>();
setExitFunctionForTests
((
int
exitCode
)
{
exitCodeCompleter
.
complete
(
exitCode
);
});
testWithoutContext
(
'should send only one crash report when sent many times'
,
()
async
{
final
RequestInfo
requestInfo
=
RequestInfo
();
CrashReportSender
.
initializeWith
(
MockCrashReportSender
(
requestInfo
));
final
CrashReportSender
crashReportSender
=
CrashReportSender
(
client:
MockCrashReportSender
(
requestInfo
),
usage:
mockUsage
,
platform:
platform
,
logger:
logger
,
operatingSystemUtils:
operatingSystemUtils
,
);
unawaited
(
tools
.
run
(
<
String
>[
'crash'
],
<
FlutterCommand
>[
_CrashAsyncCommand
()],
reportCrashes:
true
,
flutterVersion:
'test-version'
,
));
expect
(
await
exitCodeCompleter
.
future
,
1
);
await
verifyCrashReportSent
(
requestInfo
);
},
overrides:
<
Type
,
Generator
>{
Stdio:
()
=>
_NoStderr
(),
});
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'test-version'
,
command:
'crash'
,
);
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
);
}
});
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'test-version'
,
command:
'crash'
,
);
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'test-version'
,
command:
'crash'
,
);
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'test-version'
,
command:
'crash'
,
);
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:
()
=>
_NoStderr
(),
});
test
Using
Context
(
'should not send a crash report if on a user-branch'
,
()
async
{
test
Without
Context
(
'should not send a crash report if on a user-branch'
,
()
async
{
String
method
;
Uri
uri
;
CrashReportSender
.
initializeWith
(
MockClient
((
Request
request
)
async
{
final
MockClient
mockClient
=
MockClient
((
Request
request
)
async
{
method
=
request
.
method
;
uri
=
request
.
url
;
...
...
@@ -166,41 +211,60 @@ void main() {
'test-report-id'
,
200
,
);
})
)
;
});
final
int
exitCode
=
await
tools
.
run
(
<
String
>[
'crash'
],
<
FlutterCommand
>[
_CrashCommand
()],
reportCrashes:
true
,
flutterVersion:
'[user-branch]/v1.2.3'
,
final
CrashReportSender
crashReportSender
=
CrashReportSender
(
client:
mockClient
,
usage:
mockUsage
,
platform:
platform
,
logger:
logger
,
operatingSystemUtils:
operatingSystemUtils
,
);
expect
(
exitCode
,
1
);
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'[user-branch]/v1.2.3'
,
command:
'crash'
,
);
// Verify that the report wasn't sent
expect
(
method
,
null
);
expect
(
uri
,
null
);
expect
(
testLogger
.
traceText
,
isNot
(
contains
(
'Crash report sent'
)));
},
overrides:
<
Type
,
Generator
>{
Stdio:
()
=>
_NoStderr
(),
expect
(
logger
.
traceText
,
isNot
(
contains
(
'Crash report sent'
)));
});
test
Using
Context
(
'can override base URL'
,
()
async
{
test
Without
Context
(
'can override base URL'
,
()
async
{
Uri
uri
;
CrashReportSender
.
initializeWith
(
MockClient
((
Request
request
)
async
{
final
MockClient
mockClient
=
MockClient
((
Request
request
)
async
{
uri
=
request
.
url
;
return
Response
(
'test-report-id'
,
200
);
}));
});
final
Platform
environmentPlatform
=
FakePlatform
(
operatingSystem:
'linux'
,
environment:
<
String
,
String
>{
'HOME'
:
'/'
,
'FLUTTER_CRASH_SERVER_BASE_URL'
:
'https://localhost:12345/fake_server'
,
},
script:
Uri
(
scheme:
'data'
),
);
final
int
exitCode
=
await
tools
.
run
(
<
String
>[
'crash'
],
<
FlutterCommand
>[
_CrashCommand
()],
reportCrashes:
true
,
flutterVersion:
'test-version'
,
final
CrashReportSender
crashReportSender
=
CrashReportSender
(
client:
mockClient
,
usage:
mockUsage
,
platform:
environmentPlatform
,
logger:
logger
,
operatingSystemUtils:
operatingSystemUtils
,
);
expect
(
exitCode
,
1
);
await
crashReportSender
.
sendReport
(
error:
StateError
(
'Test bad state error'
),
stackTrace:
null
,
getFlutterVersion:
()
=>
'test-version'
,
command:
'crash'
,
);
// Verify that we sent the crash report.
expect
(
uri
,
isNotNull
);
...
...
@@ -214,16 +278,6 @@ void main() {
'version'
:
'test-version'
,
},
));
},
overrides:
<
Type
,
Generator
>{
Platform:
()
=>
FakePlatform
(
operatingSystem:
'linux'
,
environment:
<
String
,
String
>{
'HOME'
:
'/'
,
'FLUTTER_CRASH_SERVER_BASE_URL'
:
'https://localhost:12345/fake_server'
,
},
script:
Uri
(
scheme:
'data'
),
),
Stdio:
()
=>
_NoStderr
(),
});
});
}
...
...
@@ -234,42 +288,6 @@ class RequestInfo {
Map
<
String
,
String
>
fields
;
}
Future
<
void
>
verifyCrashReportSent
(
RequestInfo
crashInfo
,
{
int
crashes
=
1
,
})
async
{
// Verify that we sent the crash report.
expect
(
crashInfo
.
method
,
'POST'
);
expect
(
crashInfo
.
uri
,
Uri
(
scheme:
'https'
,
host:
'clients2.google.com'
,
port:
443
,
path:
'/cr/report'
,
queryParameters:
<
String
,
String
>{
'product'
:
'Flutter_Tools'
,
'version'
:
'test-version'
,
},
));
expect
(
crashInfo
.
fields
[
'uuid'
],
'00000000-0000-4000-0000-000000000000'
);
expect
(
crashInfo
.
fields
[
'product'
],
'Flutter_Tools'
);
expect
(
crashInfo
.
fields
[
'version'
],
'test-version'
);
expect
(
crashInfo
.
fields
[
'osName'
],
globals
.
platform
.
operatingSystem
);
expect
(
crashInfo
.
fields
[
'osVersion'
],
'fake OS name and version'
);
expect
(
crashInfo
.
fields
[
'type'
],
'DartError'
);
expect
(
crashInfo
.
fields
[
'error_runtime_type'
],
'StateError'
);
expect
(
crashInfo
.
fields
[
'error_message'
],
'Bad state: Test bad state error'
);
expect
(
crashInfo
.
fields
[
'comments'
],
'crash'
);
expect
(
testLogger
.
traceText
,
contains
(
'Sending crash report to Google.'
));
expect
(
testLogger
.
traceText
,
contains
(
'Crash report sent (report ID: test-report-id)'
));
// Verify that we've written the crash report to disk.
final
List
<
String
>
writtenFiles
=
(
await
tools
.
crashFileSystem
.
directory
(
'/'
).
list
(
recursive:
true
).
toList
())
.
map
((
FileSystemEntity
e
)
=>
e
.
path
).
toList
();
expect
(
writtenFiles
,
hasLength
(
crashes
));
expect
(
writtenFiles
,
contains
(
'flutter_01.log'
));
}
class
MockCrashReportSender
extends
MockClient
{
MockCrashReportSender
(
RequestInfo
crashInfo
)
:
super
((
Request
request
)
async
{
MockCrashReportSender
.
sendCalls
++;
...
...
@@ -283,14 +301,14 @@ class MockCrashReportSender extends MockClient {
utf8
.
decode
(
request
.
bodyBytes
)
.
split
(
'--
$boundary
'
)
.
map
<
List
<
String
>>((
String
part
)
{
final
Match
nameMatch
=
RegExp
(
r'name="(.*)"'
).
firstMatch
(
part
);
if
(
nameMatch
==
null
)
{
return
null
;
}
final
String
name
=
nameMatch
[
1
];
final
String
value
=
part
.
split
(
'
\n
'
).
skip
(
2
).
join
(
'
\n
'
).
trim
();
return
<
String
>[
name
,
value
];
})
final
Match
nameMatch
=
RegExp
(
r'name="(.*)"'
).
firstMatch
(
part
);
if
(
nameMatch
==
null
)
{
return
null
;
}
final
String
name
=
nameMatch
[
1
];
final
String
value
=
part
.
split
(
'
\n
'
).
skip
(
2
).
join
(
'
\n
'
).
trim
();
return
<
String
>[
name
,
value
];
})
.
where
((
List
<
String
>
pair
)
=>
pair
!=
null
),
key:
(
dynamic
key
)
{
final
List
<
String
>
pair
=
key
as
List
<
String
>;
...
...
@@ -317,78 +335,6 @@ class CrashingCrashReportSender extends MockClient {
});
}
/// Throws a random error to simulate a CLI crash.
class
_CrashCommand
extends
FlutterCommand
{
@override
String
get
description
=>
'Simulates a crash'
;
@override
String
get
name
=>
'crash'
;
@override
Future
<
FlutterCommandResult
>
runCommand
()
async
{
void
fn1
()
{
throw
StateError
(
'Test bad state error'
);
}
void
fn2
()
{
fn1
();
}
void
fn3
()
{
fn2
();
}
fn3
();
return
FlutterCommandResult
.
success
();
}
}
/// Throws StateError from async callback.
class
_CrashAsyncCommand
extends
FlutterCommand
{
@override
String
get
description
=>
'Simulates a crash'
;
@override
String
get
name
=>
'crash'
;
@override
Future
<
FlutterCommandResult
>
runCommand
()
async
{
Timer
.
run
(()
{
throw
StateError
(
'Test bad state error'
);
});
return
Completer
<
FlutterCommandResult
>().
future
;
// expect StateError
}
}
/// 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
{
...
...
@@ -399,49 +345,4 @@ class FakeDoctorValidatorsProvider implements DoctorValidatorsProvider {
List
<
Workflow
>
get
workflows
=>
<
Workflow
>[];
}
class
_NoStderr
extends
Stdio
{
_NoStderr
();
@override
IOSink
get
stderr
=>
const
_NoopIOSink
();
}
class
_NoopIOSink
implements
IOSink
{
const
_NoopIOSink
();
@override
Encoding
get
encoding
=>
utf8
;
@override
set
encoding
(
_
)
=>
throw
UnsupportedError
(
''
);
@override
void
add
(
_
)
{
}
@override
void
write
(
_
)
{
}
@override
void
writeAll
(
_
,
[
__
=
''
])
{
}
@override
void
writeln
([
_
=
''
])
{
}
@override
void
writeCharCode
(
_
)
{
}
@override
void
addError
(
_
,
[
__
])
{
}
@override
Future
<
dynamic
>
addStream
(
_
)
async
{
}
@override
Future
<
dynamic
>
flush
()
async
{
}
@override
Future
<
dynamic
>
close
()
async
{
}
@override
Future
<
dynamic
>
get
done
async
{
}
}
class
MockUsage
extends
Mock
implements
Usage
{}
packages/flutter_tools/test/general.shard/runner/runner_test.dart
View file @
8109dcc2
...
...
@@ -24,7 +24,6 @@ void main() {
MockGitHubTemplateCreator
mockGitHubTemplateCreator
;
setUp
(()
{
mockGitHubTemplateCreator
=
MockGitHubTemplateCreator
();
runner
.
crashFileSystem
=
MemoryFileSystem
();
// Instead of exiting with dart:io exit(), this causes an exception to
// be thrown, which we catch with the onError callback in the zone below.
io
.
setExitFunctionForTests
((
int
_
)
{
throw
'test exit'
;});
...
...
@@ -32,7 +31,6 @@ void main() {
});
tearDown
(()
{
runner
.
crashFileSystem
=
const
LocalFileSystem
();
io
.
restoreExitFunction
();
Cache
.
enableLocking
();
});
...
...
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