d00ef8611a
Change-Id: I8310e4a32cacbe98310c1dbaa3b3563b1ff1a1ee Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/191862 Reviewed-by: Danny Tuppeny <danny@tuppeny.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
627 lines
23 KiB
Dart
627 lines
23 KiB
Dart
// 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:analyzer/file_system/memory_file_system.dart';
|
|
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol_common.dart'
|
|
hide NavigationTarget;
|
|
import 'package:nnbd_migration/nnbd_migration.dart';
|
|
import 'package:nnbd_migration/src/front_end/dartfix_listener.dart';
|
|
import 'package:nnbd_migration/src/front_end/migration_info.dart';
|
|
import 'package:nnbd_migration/src/front_end/migration_state.dart';
|
|
import 'package:nnbd_migration/src/front_end/navigation_tree_renderer.dart';
|
|
import 'package:nnbd_migration/src/front_end/offset_mapper.dart';
|
|
import 'package:nnbd_migration/src/front_end/path_mapper.dart';
|
|
import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart';
|
|
import 'package:nnbd_migration/src/preview/preview_site.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
import '../front_end/nnbd_migration_test_base.dart';
|
|
import '../utilities/test_logger.dart';
|
|
|
|
void main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(PreviewSiteTest);
|
|
defineReflectiveTests(PreviewSiteWithEngineTest);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class PreviewSiteTest with ResourceProviderMixin, PreviewSiteTestMixin {
|
|
@override
|
|
Future<void> performEdit(String path, int offset, String replacement) {
|
|
final pathUri = Uri.file(path).path;
|
|
return site
|
|
.performEdit(Uri.parse('localhost://$pathUri?offset=$offset&end=$offset'
|
|
'&replacement=${Uri.encodeComponent(replacement)}'));
|
|
}
|
|
|
|
void setUp() {
|
|
dartfixListener = DartFixListener(null, ListenerClient());
|
|
resourceProvider = MemoryResourceProvider();
|
|
final migrationInfo =
|
|
MigrationInfo({}, {}, resourceProvider.pathContext, null);
|
|
state = MigrationState(
|
|
null, null, dartfixListener, null, {}, (String path) => true);
|
|
state.pathMapper = PathMapper(resourceProvider);
|
|
state.migrationInfo = migrationInfo;
|
|
logger = TestLogger(false /*isVerbose*/);
|
|
site = PreviewSite(state, () async {
|
|
return state;
|
|
}, () {}, logger);
|
|
}
|
|
|
|
void test_apply_regress41391() async {
|
|
final path = convertPath('/test.dart');
|
|
final file = getFile(path);
|
|
final analysisOptionsPath = convertPath('/analysis_options.yaml');
|
|
final analysisOptions = getFile(analysisOptionsPath);
|
|
analysisOptions.writeAsStringSync('analyzer:');
|
|
final content = 'void main() {}';
|
|
file.writeAsStringSync(content);
|
|
site.unitInfoMap[path] = UnitInfo(path)..diskContent = content;
|
|
// Add a source change for analysis_options, which has no UnitInfo.
|
|
dartfixListener.addSourceFileEdit(
|
|
'enable experiment',
|
|
Location(analysisOptionsPath, 9, 0, 1, 9, 1, 9),
|
|
SourceFileEdit(analysisOptionsPath, 0, edits: [
|
|
SourceEdit(9, 0, '\n enable-experiment:\n - non-nullable')
|
|
]));
|
|
// This should not crash.
|
|
site.performApply([]);
|
|
expect(analysisOptions.readAsStringSync(), '''
|
|
analyzer:
|
|
enable-experiment:
|
|
- non-nullable''');
|
|
expect(state.hasBeenApplied, true);
|
|
}
|
|
|
|
void test_applyChangesEmpty() {
|
|
final file = getFile('/test.dart');
|
|
file.writeAsStringSync('void main() {}');
|
|
site.performApply([]);
|
|
expect(file.readAsStringSync(), 'void main() {}');
|
|
expect(state.hasBeenApplied, true);
|
|
}
|
|
|
|
void test_applyChangesTwiceThrows() {
|
|
site.performApply([]);
|
|
expect(() => site.performApply([]), throwsA(isA<StateError>()));
|
|
}
|
|
|
|
void test_applyMigration_sanityCheck_dontApply() async {
|
|
final path = convertPath('/test.dart');
|
|
final file = getFile(path);
|
|
site.unitInfoMap[path] = UnitInfo(path)
|
|
..diskContent = '// different content';
|
|
final currentContent = 'void main() {}';
|
|
file.writeAsStringSync(currentContent);
|
|
dartfixListener.addSourceFileEdit(
|
|
'test change',
|
|
Location(path, 10, 0, 1, 10, 1, 10),
|
|
SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')]));
|
|
expect(() => site.performApply([]), throwsA(isA<StateError>()));
|
|
expect(file.readAsStringSync(), currentContent);
|
|
expect(state.hasBeenApplied, false);
|
|
}
|
|
|
|
void test_applyMultipleChanges() {
|
|
final path = convertPath('/test.dart');
|
|
final file = getFile(path);
|
|
final content = 'void main() {}';
|
|
file.writeAsStringSync(content);
|
|
site.unitInfoMap[path] = UnitInfo(path)..diskContent = content;
|
|
dartfixListener.addSourceFileEdit(
|
|
'test change',
|
|
Location(path, 10, 0, 1, 10, 1, 10),
|
|
SourceFileEdit(path, 0, edits: [
|
|
SourceEdit(10, 0, 'List args'),
|
|
SourceEdit(13, 0, '\n print(args);\n')
|
|
]));
|
|
site.performApply([]);
|
|
expect(file.readAsStringSync(), '''
|
|
void main(List args) {
|
|
print(args);
|
|
}''');
|
|
expect(state.hasBeenApplied, true);
|
|
}
|
|
|
|
void test_applySingleChange() {
|
|
final path = convertPath('/test.dart');
|
|
final file = getFile(path);
|
|
final content = 'void main() {}';
|
|
file.writeAsStringSync(content);
|
|
site.unitInfoMap[path] = UnitInfo(path)..diskContent = content;
|
|
dartfixListener.addSourceFileEdit(
|
|
'test change',
|
|
Location(path, 10, 0, 1, 10, 1, 10),
|
|
SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')]));
|
|
site.performApply([]);
|
|
expect(file.readAsStringSync(), 'void main(List args) {}');
|
|
expect(state.hasBeenApplied, true);
|
|
}
|
|
|
|
void test_optOutOfNullSafety_blankLines() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety(' \n \n'),
|
|
equals('// @dart=2.9\n\n \n \n'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_blankLines_windows() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety(' \r\n \r\n'),
|
|
equals('// @dart=2.9\r\n\r\n \r\n \r\n'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_commentThenCode() {
|
|
expect(
|
|
IncrementalPlan.optCodeOutOfNullSafety('// comment\n\nvoid main() {}'),
|
|
equals('// comment\n\n// @dart=2.9\n\nvoid main() {}'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_commentThenCode_windows() {
|
|
expect(
|
|
IncrementalPlan.optCodeOutOfNullSafety(
|
|
'// comment\r\n\r\nvoid main() {}'),
|
|
equals('// comment\r\n\r\n// @dart=2.9\r\n\r\nvoid main() {}'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_commentThenDirective() {
|
|
expect(
|
|
IncrementalPlan.optCodeOutOfNullSafety(
|
|
'// comment\nimport "dart:core";'),
|
|
equals('// comment\n\n// @dart=2.9\n\nimport "dart:core";'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_commentThenDirective_multiLine() {
|
|
expect(
|
|
IncrementalPlan.optCodeOutOfNullSafety(
|
|
'// comment\n// comment\nimport "dart:core";'),
|
|
equals(
|
|
'// comment\n// comment\n\n// @dart=2.9\n\nimport "dart:core";'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_commentThenSemicolon() {
|
|
expect(
|
|
IncrementalPlan.optCodeOutOfNullSafety('// comment\n;\nvoid main() {}'),
|
|
equals('// comment\n\n// @dart=2.9\n\n;\nvoid main() {}'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_empty() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety(''), equals('// @dart=2.9'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_singleComment_multiLine() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety('// line 1\n// line 2'),
|
|
equals('// line 1\n// line 2\n\n// @dart=2.9\n'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_singleComment_multiLine_indented() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety(' // line 1\n // line 2'),
|
|
equals(' // line 1\n // line 2\n\n// @dart=2.9\n'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_singleComment_singleLine() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety('// comment'),
|
|
equals('// comment\n\n// @dart=2.9\n'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_singleComment_singleLine_trailingNewline() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety('// comment\n'),
|
|
equals('// comment\n\n\n// @dart=2.9\n'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_singleLine() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety('void main() {}'),
|
|
equals('// @dart=2.9\n\nvoid main() {}'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_singleLine_afterBlankLines() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety('\n\nvoid main() {}'),
|
|
equals('// @dart=2.9\n\n\n\nvoid main() {}'));
|
|
}
|
|
|
|
void test_optOutOfNullSafety_singleLine_windows() {
|
|
expect(IncrementalPlan.optCodeOutOfNullSafety('void main() {}\r\n'),
|
|
equals('// @dart=2.9\r\n\r\nvoid main() {}\r\n'));
|
|
}
|
|
|
|
void test_performEdit() {
|
|
final path = convertPath('/test.dart');
|
|
final file = getFile(path);
|
|
final content = 'int foo() {}';
|
|
final unitInfo = UnitInfo(path)
|
|
..diskContent = content
|
|
..content = content
|
|
..diskChangesOffsetMapper = OffsetMapper.identity;
|
|
site.unitInfoMap[path] = unitInfo;
|
|
file.writeAsStringSync(content);
|
|
performEdit(path, 3, '/*?*/');
|
|
expect(file.readAsStringSync(), 'int/*?*/ foo() {}');
|
|
expect(state.hasBeenApplied, false);
|
|
expect(state.needsRerun, true);
|
|
expect(unitInfo.content, 'int/*?*/ foo() {}');
|
|
}
|
|
|
|
void test_performEdit_sanityCheck_dontApply() {
|
|
final path = convertPath('/test.dart');
|
|
final file = getFile(path);
|
|
site.unitInfoMap[path] = UnitInfo(path)
|
|
..diskContent = '// different content';
|
|
final currentContent = 'void main() {}';
|
|
file.writeAsStringSync(currentContent);
|
|
expect(() => performEdit(path, 0, 'foo'), throwsA(isA<StateError>()));
|
|
expect(file.readAsStringSync(), currentContent);
|
|
expect(state.hasBeenApplied, false);
|
|
}
|
|
}
|
|
|
|
mixin PreviewSiteTestMixin {
|
|
PreviewSite site;
|
|
DartFixListener dartfixListener;
|
|
MigrationState state;
|
|
TestLogger logger;
|
|
|
|
Future<void> performEdit(String path, int offset, String replacement) {
|
|
final pathUri = Uri.file(path).path;
|
|
return site
|
|
.performEdit(Uri.parse('localhost://$pathUri?offset=$offset&end=$offset'
|
|
'&replacement=${Uri.encodeComponent(replacement)}'));
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class PreviewSiteWithEngineTest extends NnbdMigrationTestBase
|
|
with ResourceProviderMixin, PreviewSiteTestMixin {
|
|
MigrationInfo migrationInfo;
|
|
|
|
Future<void> setUpMigrationInfo(Map<String, String> files,
|
|
{bool Function(String) shouldBeMigratedFunction,
|
|
Iterable<String> pathsToProcess}) async {
|
|
shouldBeMigratedFunction ??= (String path) => true;
|
|
pathsToProcess ??= files.keys;
|
|
await buildInfoForTestFiles(files,
|
|
includedRoot: projectPath,
|
|
shouldBeMigratedFunction: shouldBeMigratedFunction,
|
|
pathsToProcess: pathsToProcess);
|
|
dartfixListener = DartFixListener(null, ListenerClient());
|
|
migrationInfo =
|
|
MigrationInfo(infos, {}, resourceProvider.pathContext, projectPath);
|
|
state = MigrationState(
|
|
null, null, dartfixListener, null, {}, shouldBeMigratedFunction);
|
|
nodeMapper = state.nodeMapper;
|
|
state.pathMapper = PathMapper(resourceProvider);
|
|
state.migrationInfo = migrationInfo;
|
|
logger = TestLogger(false /*isVerbose*/);
|
|
site = PreviewSite(state, () async {
|
|
return state;
|
|
}, () {}, logger);
|
|
}
|
|
|
|
void test_applyHintAction() async {
|
|
await setUpMigrationInfo({});
|
|
final path = convertPath('$projectPath/bin/test.dart');
|
|
final file = getFile(path);
|
|
final content = r'''
|
|
int x;
|
|
int y = x;
|
|
''';
|
|
file.writeAsStringSync(content);
|
|
final migratedContent = '''
|
|
int? x;
|
|
int? y = x;
|
|
''';
|
|
final unitInfo = await buildInfoForSingleTestFile(content,
|
|
migratedContent: migratedContent);
|
|
site.unitInfoMap[path] = unitInfo;
|
|
await site.performHintAction(
|
|
unitInfo.regions[1].traces[0].entries[0].hintActions[0]);
|
|
await site.performHintAction(
|
|
unitInfo.regions[1].traces[0].entries[2].hintActions[0]);
|
|
expect(file.readAsStringSync(), '''
|
|
int/*?*/ x;
|
|
int/*?*/ y = x;
|
|
''');
|
|
expect(unitInfo.content, '''
|
|
int/*?*/? x;
|
|
int/*?*/? y = x;
|
|
''');
|
|
assertRegion(
|
|
region: unitInfo.regions[0], offset: unitInfo.content.indexOf('? x'));
|
|
assertRegion(
|
|
region: unitInfo.regions[1], offset: unitInfo.content.indexOf('? y'));
|
|
final targets = List<NavigationTarget>.from(unitInfo.targets);
|
|
assertInTargets(
|
|
targets: targets,
|
|
offset: unitInfo.content.indexOf('x'),
|
|
offsetMapper: unitInfo.offsetMapper);
|
|
assertInTargets(
|
|
targets: targets,
|
|
offset: unitInfo.content.indexOf('y'),
|
|
offsetMapper: unitInfo.offsetMapper);
|
|
var trace = unitInfo.regions[1].traces[0];
|
|
assertTraceEntry(unitInfo, trace.entries[0], 'y',
|
|
unitInfo.content.indexOf('int/*?*/? y'), contains('y (test.dart:2:1)'));
|
|
assertTraceEntry(unitInfo, trace.entries[1], 'y',
|
|
unitInfo.content.indexOf('= x;') + '= '.length, contains('data flow'));
|
|
expect(state.hasBeenApplied, false);
|
|
expect(state.needsRerun, true);
|
|
}
|
|
|
|
void test_applyHintAction_removeHint() async {
|
|
await setUpMigrationInfo({});
|
|
final path = convertPath('$projectPath/bin/test.dart');
|
|
final file = getFile(path);
|
|
final content = r'''
|
|
int/*!*/ x;
|
|
int y = x;
|
|
''';
|
|
file.writeAsStringSync(content);
|
|
final migratedContent = '''
|
|
int/*!*/ x;
|
|
int y = x;
|
|
''';
|
|
final unitInfo = await buildInfoForSingleTestFile(content,
|
|
migratedContent: migratedContent);
|
|
site.unitInfoMap[path] = unitInfo;
|
|
await site.performHintAction(
|
|
unitInfo.regions[0].traces[0].entries[0].hintActions[0]);
|
|
expect(file.readAsStringSync(), '''
|
|
int x;
|
|
int y = x;
|
|
''');
|
|
expect(unitInfo.content, '''
|
|
int x;
|
|
int y = x;
|
|
''');
|
|
expect(unitInfo.regions, hasLength(1));
|
|
assertRegion(
|
|
kind: NullabilityFixKind.typeNotMadeNullable,
|
|
region: unitInfo.regions[0],
|
|
offset: unitInfo.content.indexOf(' y'));
|
|
final targets = List<NavigationTarget>.from(unitInfo.targets);
|
|
assertInTargets(
|
|
targets: targets,
|
|
offset: unitInfo.content.indexOf('x'),
|
|
offsetMapper: unitInfo.offsetMapper);
|
|
expect(state.hasBeenApplied, false);
|
|
expect(state.needsRerun, true);
|
|
}
|
|
|
|
void test_applyMigration_doNotOptOutFileNotInPathsToProcess() async {
|
|
final pathA = convertPath('$projectPath/lib/a.dart');
|
|
final pathB = convertPath('$projectPath/lib/b.dart');
|
|
final content = 'void main() {}';
|
|
await setUpMigrationInfo({pathA: content, pathB: content},
|
|
// Neither [pathA] nor [[pathB] should be migrated.
|
|
shouldBeMigratedFunction: (String path) => false,
|
|
pathsToProcess: [pathA]);
|
|
site.unitInfoMap[pathA] = UnitInfo(pathA)
|
|
..diskContent = content
|
|
..wasExplicitlyOptedOut = false
|
|
..migrationStatus = UnitMigrationStatus.optingOut;
|
|
site.unitInfoMap[pathB] = UnitInfo(pathB)
|
|
..diskContent = content
|
|
..wasExplicitlyOptedOut = false
|
|
..migrationStatus = UnitMigrationStatus.optingOut;
|
|
var navigationTree =
|
|
NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
|
|
site.performApply(navigationTree);
|
|
expect(getFile(pathA).readAsStringSync(), '''
|
|
// @dart=2.9
|
|
|
|
void main() {}''');
|
|
expect(getFile(pathB).readAsStringSync(), 'void main() {}');
|
|
}
|
|
|
|
void test_applyMigration_migratePreviouslyOptedOutFile() async {
|
|
final path = convertPath('$projectPath/lib/a.dart');
|
|
final content = '''
|
|
// @dart=2.9
|
|
|
|
void main() {}''';
|
|
await setUpMigrationInfo({path: content});
|
|
site.unitInfoMap[path] = UnitInfo(path)
|
|
..diskContent = content
|
|
..wasExplicitlyOptedOut = true;
|
|
dartfixListener.addSourceFileEdit(
|
|
'remove DLV comment',
|
|
Location(path, 0, 14, 1, 1, 3, 1),
|
|
SourceFileEdit(path, 0, edits: [SourceEdit(0, 14, '')]));
|
|
var navigationTree =
|
|
NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
|
|
site.performApply(navigationTree);
|
|
expect(getFile(path).readAsStringSync(), 'void main() {}');
|
|
expect(logger.stdoutBuffer.toString(), contains('''
|
|
Migrated 1 file:
|
|
${convertPath('lib/a.dart')}
|
|
'''));
|
|
}
|
|
|
|
void test_applyMigration_optOutEmptyFile() async {
|
|
final path = convertPath('$projectPath/lib/a.dart');
|
|
final content = '';
|
|
await setUpMigrationInfo({path: content});
|
|
site.unitInfoMap[path] = UnitInfo(path)
|
|
..diskContent = content
|
|
..wasExplicitlyOptedOut = false;
|
|
var navigationTree =
|
|
NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
|
|
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
|
|
(libDir.subtree.single as NavigationTreeFileNode).migrationStatus =
|
|
UnitMigrationStatus.optingOut;
|
|
site.performApply(navigationTree);
|
|
expect(getFile(path).readAsStringSync(), '// @dart=2.9');
|
|
expect(logger.stdoutBuffer.toString(), contains('''
|
|
Opted 1 file out of null safety with a new Dart language version comment:
|
|
${convertPath('lib/a.dart')}
|
|
'''));
|
|
}
|
|
|
|
void test_applyMigration_optOutFileWithEdits() async {
|
|
final path = convertPath('$projectPath/lib/a.dart');
|
|
final content = 'void main() {}';
|
|
await setUpMigrationInfo({path: content});
|
|
site.unitInfoMap[path] = UnitInfo(path)
|
|
..diskContent = content
|
|
..wasExplicitlyOptedOut = false;
|
|
dartfixListener.addSourceFileEdit(
|
|
'test change',
|
|
Location(path, 10, 0, 1, 10, 1, 10),
|
|
SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')]));
|
|
var navigationTree =
|
|
NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
|
|
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
|
|
(libDir.subtree.single as NavigationTreeFileNode).migrationStatus =
|
|
UnitMigrationStatus.optingOut;
|
|
site.performApply(navigationTree);
|
|
expect(getFile(path).readAsStringSync(), '''
|
|
// @dart=2.9
|
|
|
|
void main() {}''');
|
|
expect(logger.stdoutBuffer.toString(), contains('''
|
|
Opted 1 file out of null safety with a new Dart language version comment:
|
|
${convertPath('lib/a.dart')}
|
|
'''));
|
|
}
|
|
|
|
void test_applyMigration_optOutFileWithoutEdits() async {
|
|
final path = convertPath('$projectPath/lib/a.dart');
|
|
final content = 'void main() {}';
|
|
await setUpMigrationInfo({path: content});
|
|
site.unitInfoMap[path] = UnitInfo(path)
|
|
..diskContent = content
|
|
..wasExplicitlyOptedOut = false;
|
|
var navigationTree =
|
|
NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
|
|
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
|
|
(libDir.subtree.single as NavigationTreeFileNode).migrationStatus =
|
|
UnitMigrationStatus.optingOut;
|
|
site.performApply(navigationTree);
|
|
expect(getFile(path).readAsStringSync(), '''
|
|
// @dart=2.9
|
|
|
|
void main() {}''');
|
|
expect(logger.stdoutBuffer.toString(), contains('''
|
|
Opted 1 file out of null safety with a new Dart language version comment:
|
|
${convertPath('lib/a.dart')}
|
|
'''));
|
|
}
|
|
|
|
void test_applyMigration_optOutOne_migrateAnother() async {
|
|
final pathA = convertPath('$projectPath/lib/a.dart');
|
|
final pathB = convertPath('$projectPath/lib/b.dart');
|
|
final content = 'void main() {}';
|
|
await setUpMigrationInfo({pathA: content, pathB: content});
|
|
site.unitInfoMap[pathA] = UnitInfo(pathA)
|
|
..diskContent = content
|
|
..wasExplicitlyOptedOut = false;
|
|
site.unitInfoMap[pathB] = UnitInfo(pathB)
|
|
..diskContent = content
|
|
..wasExplicitlyOptedOut = false;
|
|
dartfixListener.addSourceFileEdit(
|
|
'test change',
|
|
Location(pathA, 10, 0, 1, 10, 1, 10),
|
|
SourceFileEdit(pathA, 0, edits: [SourceEdit(10, 0, 'List args')]));
|
|
dartfixListener.addSourceFileEdit(
|
|
'test change',
|
|
Location(pathB, 10, 0, 1, 10, 1, 10),
|
|
SourceFileEdit(pathB, 0, edits: [SourceEdit(10, 0, 'List args')]));
|
|
var navigationTree =
|
|
NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
|
|
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
|
|
(libDir.subtree[0] as NavigationTreeFileNode).migrationStatus =
|
|
UnitMigrationStatus.optingOut;
|
|
site.performApply(navigationTree);
|
|
expect(getFile(pathA).readAsStringSync(), '''
|
|
// @dart=2.9
|
|
|
|
void main() {}''');
|
|
expect(getFile(pathB).readAsStringSync(), '''
|
|
void main(List args) {}''');
|
|
expect(logger.stdoutBuffer.toString(), contains('''
|
|
Migrated 1 file:
|
|
${convertPath('lib/b.dart')}
|
|
Opted 1 file out of null safety with a new Dart language version comment:
|
|
${convertPath('lib/a.dart')}
|
|
'''));
|
|
}
|
|
|
|
void test_applyMigration_optOutPreviouslyOptedOutFile() async {
|
|
final path = convertPath('$projectPath/lib/a.dart');
|
|
final content = '''
|
|
// @dart=2.9
|
|
|
|
int a;''';
|
|
await setUpMigrationInfo({path: content});
|
|
site.unitInfoMap[path] = UnitInfo(path)
|
|
..diskContent = content
|
|
..wasExplicitlyOptedOut = true;
|
|
dartfixListener.addSourceFileEdit(
|
|
'remove DLV comment',
|
|
Location(path, 0, 14, 1, 1, 3, 1),
|
|
SourceFileEdit(path, 0, edits: [SourceEdit(0, 14, '')]));
|
|
var navigationTree =
|
|
NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
|
|
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
|
|
(libDir.subtree.single as NavigationTreeFileNode).migrationStatus =
|
|
UnitMigrationStatus.optingOut;
|
|
site.performApply(navigationTree);
|
|
expect(getFile(path).readAsStringSync(), content);
|
|
expect(logger.stdoutBuffer.toString(), contains('''
|
|
Kept 1 file opted out of null safety:
|
|
${convertPath('lib/a.dart')}
|
|
'''));
|
|
}
|
|
|
|
void test_performEdit_multiple() async {
|
|
await setUpMigrationInfo({});
|
|
final path = convertPath('/test.dart');
|
|
final file = getFile(path);
|
|
final content = r'''
|
|
int x;
|
|
int y = x;
|
|
''';
|
|
file.writeAsStringSync(content);
|
|
final migratedContent = '''
|
|
int? x;
|
|
int? y = x;
|
|
''';
|
|
final unitInfo = await buildInfoForSingleTestFile(content,
|
|
migratedContent: migratedContent);
|
|
site.unitInfoMap[path] = unitInfo;
|
|
final firstEditOffset = unitInfo.regions[0].edits[0].offset;
|
|
performEdit(path, firstEditOffset, '/*?*/');
|
|
final secondEditOffset = unitInfo.regions[1].edits[0].offset;
|
|
performEdit(path, secondEditOffset, '/*?*/');
|
|
expect(file.readAsStringSync(), '''
|
|
int/*?*/ x;
|
|
int/*?*/ y = x;
|
|
''');
|
|
expect(unitInfo.content, '''
|
|
int/*?*/? x;
|
|
int/*?*/? y = x;
|
|
''');
|
|
assertRegion(
|
|
region: unitInfo.regions[0], offset: unitInfo.content.indexOf('? x'));
|
|
assertRegion(
|
|
region: unitInfo.regions[1], offset: unitInfo.content.indexOf('? y'));
|
|
final targets = List<NavigationTarget>.from(unitInfo.targets);
|
|
assertInTargets(
|
|
targets: targets,
|
|
offset: unitInfo.content.indexOf('x'),
|
|
offsetMapper: unitInfo.offsetMapper);
|
|
assertInTargets(
|
|
targets: targets,
|
|
offset: unitInfo.content.indexOf('y'),
|
|
offsetMapper: unitInfo.offsetMapper);
|
|
var trace = unitInfo.regions[1].traces[0];
|
|
assertTraceEntry(unitInfo, trace.entries[0], 'y',
|
|
unitInfo.content.indexOf('int/*?*/? y'), contains('y (test.dart:2:1)'));
|
|
assertTraceEntry(unitInfo, trace.entries[1], 'y',
|
|
unitInfo.content.indexOf('= x;') + '= '.length, contains('data flow'));
|
|
expect(state.hasBeenApplied, false);
|
|
expect(state.needsRerun, true);
|
|
}
|
|
}
|