blob: 9b2cd101b19bb085db01ea2eda599f6dd3d201b0 [file] [log] [blame]
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:nnbd_migration/src/front_end/migration_info.dart';
import 'package:nnbd_migration/src/front_end/path_mapper.dart';
import 'package:nnbd_migration/src/front_end/region_renderer.dart';
import 'package:nnbd_migration/src/front_end/web/edit_details.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'nnbd_migration_test_base.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(RegionRendererTest);
});
}
@reflectiveTest
class RegionRendererTest extends NnbdMigrationTestBase {
PathMapper pathMapper;
/// Returns the path of [testFile] used in traces.
///
/// On Windows, we display the absolute path of the test file.
/// On Posix, we display the path of the target file relative to the current
/// file.
// TODO(srawlins): I doubt this is intentional. While I don't see a bug,
// the discrepancy could lead to confusion and may be an indicator of bugs.
String get _testFilePathForTrace =>
resourceProvider.pathContext.style == p.Style.windows
? testFile
: resourceProvider.pathContext.basename(testFile);
/// Render the region at [offset], using a [MigrationInfo] which knows only
/// about the library at `infos.single`.
EditDetails renderRegion(int offset) {
var migrationInfo =
MigrationInfo(infos, {}, resourceProvider.pathContext, projectPath);
var unitInfo = infos.single;
var region = unitInfo.regionAt(offset);
pathMapper = PathMapper(resourceProvider);
return RegionRenderer(
region, unitInfo, migrationInfo, pathMapper, 'AUTH_TOKEN')
.render();
}
Future<void> test_informationalRegion_containsTrace() async {
await buildInfoForSingleTestFile('f(int a) => a.isEven;',
migratedContent: 'f(int a) => a.isEven;');
var response = renderRegion(5);
expect(response.traces, hasLength(1));
var trace = response.traces[0];
expect(trace.description, equals('Non-nullability reason'));
}
Future<void> test_informationalRegion_containsTraceEntryDescriptions() async {
await buildInfoForSingleTestFile('f(int a) => a.isEven;',
migratedContent: 'f(int a) => a.isEven;');
var response = renderRegion(5);
expect(response.traces, hasLength(1));
var trace = response.traces[0];
expect(trace.entries, hasLength(2));
expect(trace.entries[0].description,
equals('parameter 0 of f ($_testFilePathForTrace:1:3)'));
expect(trace.entries[1].description, equals('data flow'));
}
Future<void> test_informationalRegion_containsTraceLinks() async {
await buildInfoForSingleTestFile('f(int a) => a.isEven;',
migratedContent: 'f(int a) => a.isEven;');
var response = renderRegion(5);
expect(response.traces, hasLength(1));
var trace = response.traces[0];
var entry = trace.entries[0];
expect(entry.link, isNotNull);
var testFileUriPath = resourceProvider.pathContext.toUri(testFile).path;
expect(entry.link.href,
equals('$testFileUriPath?offset=2&line=1&authToken=AUTH_TOKEN'));
expect(entry.link.path,
equals(resourceProvider.pathContext.toUri(_testFilePathForTrace).path));
}
Future<void>
test_informationalRegion_containsTraceLinks_separateDrive() async {
// See https://github.com/dart-lang/sdk/issues/43178. Linking from a file on
// one drive to a file on another drive can cause problems.
projectPath = _switchToDriveD(projectPath);
testFolder = _switchToDriveD(testFolder);
testFile = _switchToDriveD(testFile);
await buildInfoForSingleTestFile(r'''
f(List<int> a) {
if (1 == 2) List.from(a);
}
g() {
f(null);
}
''', migratedContent: r'''
f(List<int >? a) {
if (1 == 2) List.from(a!);
}
g() {
f(null);
}
''');
var response = renderRegion(44); // The inserted null-check.
expect(response.displayPath,
equals(_switchToDriveD(convertPath('/home/tests/bin/test.dart'))));
expect(response.traces, hasLength(2));
var trace = response.traces[1];
expect(trace.description, equals('Non-nullability reason'));
expect(trace.entries, hasLength(1));
var entry = trace.entries[0];
expect(entry.link, isNotNull);
var sdkCoreLib = convertPath('/sdk/lib/core/core.dart');
var sdkCoreLibUriPath = resourceProvider.pathContext.toUri(sdkCoreLib).path;
expect(entry.link.href,
equals('$sdkCoreLibUriPath?offset=3730&line=166&authToken=AUTH_TOKEN'));
// On Windows, the path will simply be the absolute path to the core
// library, because there is no relative route from C:\ to D:\. On Posix,
// the path is relative.
var expectedLinkPath = resourceProvider.pathContext.style == p.Style.windows
? sdkCoreLibUriPath
: '../../..$sdkCoreLibUriPath';
expect(entry.link.path, equals(expectedLinkPath));
}
Future<void> test_modifiedOutput_containsExplanation() async {
await buildInfoForSingleTestFile('int a = null;',
migratedContent: 'int? a = null;');
var response = renderRegion(3);
expect(response.explanation, equals("Changed type 'int' to be nullable"));
}
Future<void> test_modifiedOutput_containsPath() async {
await buildInfoForSingleTestFile('int a = null;',
migratedContent: 'int? a = null;');
var response = renderRegion(3);
expect(response.displayPath, equals(testFile));
expect(response.uriPath, equals(pathMapper.map(testFile)));
expect(response.line, equals(1));
}
Future<void> test_modifiedOutput_containsTraceForNullabilityReason() async {
await buildInfoForSingleTestFile('int a = null;',
migratedContent: 'int? a = null;');
var response = renderRegion(3);
expect(response.traces, hasLength(1));
var trace = response.traces[0];
expect(trace.description, equals('Nullability reason'));
expect(trace.entries, hasLength(4));
expect(
trace.entries[0].description, equals('a ($_testFilePathForTrace:1:1)'));
expect(trace.entries[1].description, equals('data flow'));
expect(trace.entries[2].description,
equals('null literal ($_testFilePathForTrace:1:9)'));
expect(trace.entries[3].description, equals('literal expression'));
}
Future<void> test_unmodifiedOutput_containsExplanation() async {
await buildInfoForSingleTestFile('f(int a) => a.isEven;',
migratedContent: 'f(int a) => a.isEven;');
var response = renderRegion(5);
expect(response.explanation, equals("Type 'int' was not made nullable"));
}
Future<void> test_unmodifiedOutput_containsPath() async {
await buildInfoForSingleTestFile('f(int a) => a.isEven;',
migratedContent: 'f(int a) => a.isEven;');
var response = renderRegion(5);
expect(response.displayPath, equals(testFile));
expect(response.uriPath, equals(pathMapper.map(testFile)));
expect(response.line, equals(1));
}
/// On Windows, replace the C:\ relative root in [path] with the D:\ relative
/// root.
///
/// On Posix, nothing is be replaced.
String _switchToDriveD(String path) {
assert(resourceProvider.pathContext.isAbsolute(path));
return path.replaceFirst(RegExp('^C:\\\\'), 'D:\\');
}
}