| // 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); |
| defineReflectiveTests(RegionRendererTestDriveD); |
| }); |
| } |
| |
| @reflectiveTest |
| class RegionRendererTest extends RegionRendererTestBase { |
| /// Returns the basename of [testFile], used in traces. |
| String get _testFileBasename => |
| resourceProvider.pathContext.basename(testFile); |
| |
| 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 ($_testFileBasename: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(_testFileBasename).path)); |
| } |
| |
| 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 ($_testFileBasename:1:1)')); |
| expect(trace.entries[1].description, equals('data flow')); |
| expect(trace.entries[2].description, |
| equals('null literal ($_testFileBasename: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)); |
| } |
| } |
| |
| class RegionRendererTestBase extends NnbdMigrationTestBase { |
| PathMapper pathMapper; |
| |
| /// 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(); |
| } |
| } |
| |
| @reflectiveTest |
| class RegionRendererTestDriveD extends RegionRendererTestBase { |
| @override |
| String get homePath => _switchToDriveD(super.homePath); |
| |
| @override |
| void setUp() { |
| super.setUp(); |
| } |
| |
| 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. |
| 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; |
| var coreLibText = resourceProvider.getFile(sdkCoreLib).readAsStringSync(); |
| var expectedOffset = |
| 'List.from'.allMatches(coreLibText).single.start + 'List.'.length; |
| var expectedLine = |
| '\n'.allMatches(coreLibText.substring(0, expectedOffset)).length + 1; |
| expect( |
| entry.link.href, |
| equals('$sdkCoreLibUriPath?' |
| 'offset=$expectedOffset&' |
| 'line=$expectedLine&' |
| '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)); |
| } |
| |
| /// 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 resourceProvider |
| .convertPath(path) |
| .replaceFirst(RegExp('^C:\\\\'), 'D:\\'); |
| } |
| } |