mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
312 lines
11 KiB
Dart
312 lines
11 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// @dart = 2.8
|
|
|
|
import 'package:file/file.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/devfs.dart';
|
|
import 'package:flutter_tools/src/project.dart';
|
|
import 'package:flutter_tools/src/reporting/reporting.dart';
|
|
|
|
import '../src/common.dart';
|
|
import '../src/context.dart';
|
|
import '../src/fake_http_client.dart';
|
|
|
|
const String _kShortURL = 'https://www.example.com/short';
|
|
|
|
void main() {
|
|
BufferLogger logger;
|
|
FileSystem fs;
|
|
setUp(() {
|
|
logger = BufferLogger.test();
|
|
fs = MemoryFileSystem.test();
|
|
});
|
|
|
|
group('GitHub template creator', () {
|
|
testWithoutContext('similar issues URL', () {
|
|
expect(
|
|
GitHubTemplateCreator.toolCrashSimilarIssuesURL('this is a 100% error'),
|
|
'https://github.com/flutter/flutter/issues?q=is%3Aissue+this+is+a+100%25+error',
|
|
);
|
|
});
|
|
|
|
group('sanitized error message', () {
|
|
testWithoutContext('ProcessException', () {
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
const ProcessException('cd', <String>['path/to/something'])
|
|
),
|
|
'ProcessException: Command: cd, OS error code: 0',
|
|
);
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
const ProcessException('cd', <String>['path/to/something'], 'message')
|
|
),
|
|
'ProcessException: message Command: cd, OS error code: 0',
|
|
);
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
const ProcessException('cd', <String>['path/to/something'], 'message', -19)
|
|
),
|
|
'ProcessException: message Command: cd, OS error code: -19',
|
|
);
|
|
});
|
|
|
|
testWithoutContext('FileSystemException', () {
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
const FileSystemException('delete failed', 'path/to/something')
|
|
),
|
|
'FileSystemException: delete failed, null',
|
|
);
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
const FileSystemException('delete failed', 'path/to/something', OSError('message', -19))
|
|
),
|
|
'FileSystemException: delete failed, OS Error: message, errno = -19',
|
|
);
|
|
});
|
|
|
|
testWithoutContext('SocketException', () {
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
SocketException(
|
|
'message',
|
|
osError: const OSError('message', -19),
|
|
address: InternetAddress.anyIPv6,
|
|
port: 2000
|
|
)
|
|
),
|
|
'SocketException: message, OS Error: message, errno = -19',
|
|
);
|
|
});
|
|
|
|
testWithoutContext('DevFSException', () {
|
|
final StackTrace stackTrace = StackTrace.fromString('''
|
|
#0 _File.open.<anonymous closure> (dart:io/file_impl.dart:366:9)
|
|
#1 _rootRunUnary (dart:async/zone.dart:1141:38)''');
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
DevFSException('message', ArgumentError('argument error message'), stackTrace)
|
|
),
|
|
'DevFSException: message',
|
|
);
|
|
});
|
|
|
|
testWithoutContext('ArgumentError', () {
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
ArgumentError('argument error message')
|
|
),
|
|
'ArgumentError: Invalid argument(s): argument error message',
|
|
);
|
|
});
|
|
|
|
testWithoutContext('Error', () {
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
FakeError()
|
|
),
|
|
'FakeError: (#0 _File.open.<anonymous closure> (dart:io/file_impl.dart:366:9))',
|
|
);
|
|
});
|
|
|
|
testWithoutContext('String', () {
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
'May have non-tool-internal info, very long string, 0b8abb4724aa590dd0f429683339b' // ignore: missing_whitespace_between_adjacent_strings
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
'24aa590dd0f429683339b1e045a1594d0b8abb4724aa590dd0f429683339b1e045a1594d0b8abb'
|
|
),
|
|
'String: <1,016 characters>',
|
|
);
|
|
});
|
|
|
|
testWithoutContext('Exception', () {
|
|
expect(
|
|
GitHubTemplateCreator.sanitizedCrashException(
|
|
Exception('May have non-tool-internal info')
|
|
),
|
|
'_Exception',
|
|
);
|
|
});
|
|
});
|
|
|
|
group('new issue template URL', () {
|
|
StackTrace stackTrace;
|
|
Error error;
|
|
const String command = 'flutter test';
|
|
const String doctorText = ' [✓] Flutter (Channel report';
|
|
|
|
setUp(() async {
|
|
stackTrace = StackTrace.fromString('trace');
|
|
error = ArgumentError('argument error message');
|
|
});
|
|
|
|
testUsingContext('shortened', () async {
|
|
final GitHubTemplateCreator creator = GitHubTemplateCreator(
|
|
fileSystem: fs,
|
|
logger: logger,
|
|
client: FakeHttpClient.list(<FakeRequest>[
|
|
FakeRequest(Uri.parse('https://git.io'), method: HttpMethod.post, response: const FakeResponse(
|
|
statusCode: 201,
|
|
headers: <String, List<String>>{
|
|
HttpHeaders.locationHeader: <String>[_kShortURL],
|
|
}
|
|
))
|
|
]),
|
|
flutterProjectFactory: FlutterProjectFactory(
|
|
fileSystem: fs,
|
|
logger: logger,
|
|
),
|
|
);
|
|
expect(
|
|
await creator.toolCrashIssueTemplateGitHubURL(command, error, stackTrace, doctorText),
|
|
_kShortURL
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => MemoryFileSystem.test(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
});
|
|
|
|
testUsingContext('with network failure', () async {
|
|
final GitHubTemplateCreator creator = GitHubTemplateCreator(
|
|
fileSystem: fs,
|
|
logger: logger,
|
|
client: FakeHttpClient.list(<FakeRequest>[
|
|
FakeRequest(Uri.parse('https://git.io'), method: HttpMethod.post, response: const FakeResponse(
|
|
statusCode: 500,
|
|
))
|
|
]),
|
|
flutterProjectFactory: FlutterProjectFactory(
|
|
fileSystem: fs,
|
|
logger: logger,
|
|
),
|
|
);
|
|
expect(
|
|
await creator.toolCrashIssueTemplateGitHubURL(command, error, stackTrace, doctorText),
|
|
'https://github.com/flutter/flutter/issues/new?title=%5Btool_crash%5D+ArgumentError%3A+'
|
|
'Invalid+argument%28s%29%3A+argument+error+message&body=%23%23+Command%0A%60%60%60%0A'
|
|
'flutter+test%0A%60%60%60%0A%0A%23%23+Steps+to+Reproduce%0A1.+...%0A2.+...%0A3.+...%0'
|
|
'A%0A%23%23+Logs%0AArgumentError%3A+Invalid+argument%28s%29%3A+argument+error+message'
|
|
'%0A%60%60%60%0Atrace%0A%60%60%60%0A%60%60%60%0A+%5B%E2%9C%93%5D+Flutter+%28Channel+r'
|
|
'eport%0A%60%60%60%0A%0A%23%23+Flutter+Application+Metadata%0ANo+pubspec+in+working+d'
|
|
'irectory.%0A&labels=tool%2Csevere%3A+crash'
|
|
);
|
|
expect(logger.traceText, contains('Failed to shorten GitHub template URL'));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => MemoryFileSystem.test(),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
});
|
|
|
|
testUsingContext('app metadata', () async {
|
|
final GitHubTemplateCreator creator = GitHubTemplateCreator(
|
|
fileSystem: fs,
|
|
logger: logger,
|
|
client: FakeHttpClient.any(),
|
|
flutterProjectFactory: FlutterProjectFactory(
|
|
fileSystem: fs,
|
|
logger: logger,
|
|
),
|
|
);
|
|
final Directory projectDirectory = fs.currentDirectory;
|
|
|
|
projectDirectory
|
|
.childFile('pubspec.yaml')
|
|
.writeAsStringSync('''
|
|
name: failing_app
|
|
version: 2.0.1+100
|
|
flutter:
|
|
uses-material-design: true
|
|
module:
|
|
androidX: true
|
|
androidPackage: com.example.failing.android
|
|
iosBundleIdentifier: com.example.failing.ios
|
|
''');
|
|
|
|
final File pluginsFile = projectDirectory.childFile('.flutter-plugins');
|
|
pluginsFile
|
|
.writeAsStringSync('''
|
|
camera=/fake/pub.dartlang.org/camera-0.5.7+2/
|
|
device_info=/fake/pub.dartlang.org/pub.dartlang.org/device_info-0.4.1+4/
|
|
''');
|
|
|
|
final File metadataFile = projectDirectory.childFile('.metadata');
|
|
metadataFile
|
|
.writeAsStringSync('''
|
|
version:
|
|
revision: 0b8abb4724aa590dd0f429683339b1e045a1594d
|
|
channel: stable
|
|
|
|
project_type: app
|
|
''');
|
|
|
|
final String actualURL = await creator.toolCrashIssueTemplateGitHubURL(command, error, stackTrace, doctorText);
|
|
final String actualBody = Uri.parse(actualURL).queryParameters['body'];
|
|
const String expectedBody = '''
|
|
## Command
|
|
```
|
|
flutter test
|
|
```
|
|
|
|
## Steps to Reproduce
|
|
1. ...
|
|
2. ...
|
|
3. ...
|
|
|
|
## Logs
|
|
ArgumentError: Invalid argument(s): argument error message
|
|
```
|
|
trace
|
|
```
|
|
```
|
|
[✓] Flutter (Channel report
|
|
```
|
|
|
|
## Flutter Application Metadata
|
|
**Type**: app
|
|
**Version**: 2.0.1+100
|
|
**Material**: true
|
|
**Android X**: true
|
|
**Module**: true
|
|
**Plugin**: false
|
|
**Android package**: com.example.failing.android
|
|
**iOS bundle identifier**: com.example.failing.ios
|
|
**Creation channel**: stable
|
|
**Creation framework version**: 0b8abb4724aa590dd0f429683339b1e045a1594d
|
|
### Plugins
|
|
camera-0.5.7+2
|
|
device_info-0.4.1+4
|
|
|
|
''';
|
|
|
|
expect(actualBody, expectedBody);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => fs,
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
class FakeError extends Error {
|
|
@override
|
|
StackTrace get stackTrace => StackTrace.fromString('''
|
|
#0 _File.open.<anonymous closure> (dart:io/file_impl.dart:366:9)
|
|
#1 _rootRunUnary (dart:async/zone.dart:1141:38)''');
|
|
}
|