// 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:args/command_runner.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/ide_config.dart'; import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import 'package:flutter_tools/src/template.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/test_flutter_command_runner.dart'; void main() { group('ide_config', () { Directory tempDir; Directory templateDir; Directory intellijDir; Directory toolsDir; Map<String, String> _getFilesystemContents([ Directory root ]) { final String tempPath = tempDir.absolute.path; final List<String> paths = (root ?? tempDir).listSync(recursive: true).map((FileSystemEntity entity) { final String relativePath = globals.fs.path.relative(entity.path, from: tempPath); return relativePath; }).toList(); final Map<String, String> contents = <String, String>{}; for (final String path in paths) { final String absPath = globals.fs.path.join(tempPath, path); if (globals.fs.isDirectorySync(absPath)) { contents[path] = 'dir'; } else if (globals.fs.isFileSync(absPath)) { contents[path] = globals.fs.file(absPath).readAsStringSync(); } } return contents; } Map<String, String> _getManifest(Directory base, String marker, { bool isTemplate = false }) { final String basePath = globals.fs.path.relative(base.path, from: tempDir.absolute.path); final String suffix = isTemplate ? Template.copyTemplateExtension : ''; return <String, String>{ globals.fs.path.join(basePath, '.idea'): 'dir', globals.fs.path.join(basePath, '.idea', 'modules.xml$suffix'): 'modules $marker', globals.fs.path.join(basePath, '.idea', 'vcs.xml$suffix'): 'vcs $marker', globals.fs.path.join(basePath, '.idea', '.name$suffix'): 'codeStyleSettings $marker', globals.fs.path.join(basePath, '.idea', 'runConfigurations'): 'dir', globals.fs.path.join(basePath, '.idea', 'runConfigurations', 'hello_world.xml$suffix'): 'hello_world $marker', globals.fs.path.join(basePath, 'flutter.iml$suffix'): 'flutter $marker', globals.fs.path.join(basePath, 'packages', 'new', 'deep.iml$suffix'): 'deep $marker', globals.fs.path.join(basePath, 'example', 'gallery', 'android.iml$suffix'): 'android $marker', }; } void _populateDir(Map<String, String> manifest) { for (final String key in manifest.keys) { if (manifest[key] == 'dir') { tempDir.childDirectory(key).createSync(recursive: true); } } for (final String key in manifest.keys) { if (manifest[key] != 'dir') { tempDir.childFile(key) ..createSync(recursive: true) ..writeAsStringSync(manifest[key]); } } } bool _fileOrDirectoryExists(String path) { final String absPath = globals.fs.path.join(tempDir.absolute.path, path); return globals.fs.file(absPath).existsSync() || globals.fs.directory(absPath).existsSync(); } Future<void> _updateIdeConfig({ Directory dir, List<String> args = const <String>[], Map<String, String> expectedContents = const <String, String>{}, List<String> unexpectedPaths = const <String>[], }) async { dir ??= tempDir; Cache.flutterRoot = tempDir.absolute.path; final IdeConfigCommand command = IdeConfigCommand(); final CommandRunner<void> runner = createTestCommandRunner(command); await runner.run(<String>[ 'ide-config', ...args, ]); for (final String path in expectedContents.keys) { final String absPath = globals.fs.path.join(tempDir.absolute.path, path); expect(_fileOrDirectoryExists(globals.fs.path.join(dir.path, path)), true, reason: "$path doesn't exist"); if (globals.fs.file(absPath).existsSync()) { expect(globals.fs.file(absPath).readAsStringSync(), equals(expectedContents[path]), reason: "$path contents don't match"); } } for (final String path in unexpectedPaths) { expect(_fileOrDirectoryExists(globals.fs.path.join(dir.path, path)), false, reason: '$path exists'); } } setUpAll(() { Cache.disableLocking(); }); setUp(() { tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_ide_config_test.'); final Directory packagesDir = tempDir.childDirectory('packages')..createSync(recursive: true); toolsDir = packagesDir.childDirectory('flutter_tools')..createSync(); templateDir = toolsDir.childDirectory('ide_templates')..createSync(); intellijDir = templateDir.childDirectory('intellij')..createSync(); }); tearDown(() { tryToDelete(tempDir); }); testUsingContext("doesn't touch existing files without --overwrite", () async { final Map<String, String> templateManifest = _getManifest( intellijDir, 'template', isTemplate: true, ); final Map<String, String> flutterManifest = _getManifest( tempDir, 'existing', ); _populateDir(templateManifest); _populateDir(flutterManifest); final Map<String, String> expectedContents = _getFilesystemContents(); return _updateIdeConfig( expectedContents: expectedContents, ); }); testUsingContext('creates non-existent files', () async { final Map<String, String> templateManifest = _getManifest( intellijDir, 'template', isTemplate: true, ); final Map<String, String> flutterManifest = _getManifest( tempDir, 'template', ); _populateDir(templateManifest); final Map<String, String> expectedContents = <String, String>{ ...templateManifest, ...flutterManifest, }; return _updateIdeConfig( expectedContents: expectedContents, ); }); testUsingContext('overwrites existing files with --overwrite', () async { final Map<String, String> templateManifest = _getManifest( intellijDir, 'template', isTemplate: true, ); final Map<String, String> flutterManifest = _getManifest( tempDir, 'existing', ); _populateDir(templateManifest); _populateDir(flutterManifest); final Map<String, String> overwrittenManifest = _getManifest( tempDir, 'template', ); final Map<String, String> expectedContents = <String, String>{ ...templateManifest, ...overwrittenManifest, }; return _updateIdeConfig( args: <String>['--overwrite'], expectedContents: expectedContents, ); }); testUsingContext('only adds new templates without --overwrite', () async { final Map<String, String> templateManifest = _getManifest( intellijDir, 'template', isTemplate: true, ); final String flutterIml = globals.fs.path.join( 'packages', 'flutter_tools', 'ide_templates', 'intellij', 'flutter.iml${Template.copyTemplateExtension}', ); templateManifest.remove(flutterIml); _populateDir(templateManifest); templateManifest[flutterIml] = 'flutter existing'; final Map<String, String> flutterManifest = _getManifest( tempDir, 'existing', ); _populateDir(flutterManifest); final Map<String, String> expectedContents = <String, String>{ ...flutterManifest, ...templateManifest, }; return _updateIdeConfig( args: <String>['--update-templates'], expectedContents: expectedContents, ); }); testUsingContext('update all templates with --overwrite', () async { final Map<String, String> templateManifest = _getManifest( intellijDir, 'template', isTemplate: true, ); _populateDir(templateManifest); final Map<String, String> flutterManifest = _getManifest( tempDir, 'existing', ); _populateDir(flutterManifest); final Map<String, String> updatedTemplates = _getManifest( intellijDir, 'existing', isTemplate: true, ); final Map<String, String> expectedContents = <String, String>{ ...flutterManifest, ...updatedTemplates, }; return _updateIdeConfig( args: <String>['--update-templates', '--overwrite'], expectedContents: expectedContents, ); }); testUsingContext('removes deleted imls with --overwrite', () async { final Map<String, String> templateManifest = _getManifest( intellijDir, 'template', isTemplate: true, ); _populateDir(templateManifest); final Map<String, String> flutterManifest = _getManifest( tempDir, 'existing', ); flutterManifest.remove('flutter.iml'); _populateDir(flutterManifest); final Map<String, String> updatedTemplates = _getManifest( intellijDir, 'existing', isTemplate: true, ); final String flutterIml = globals.fs.path.join( 'packages', 'flutter_tools', 'ide_templates', 'intellij', 'flutter.iml${Template.copyTemplateExtension}', ); updatedTemplates.remove(flutterIml); final Map<String, String> expectedContents = <String, String>{ ...flutterManifest, ...updatedTemplates, }; return _updateIdeConfig( args: <String>['--update-templates', '--overwrite'], expectedContents: expectedContents, ); }); testUsingContext('removes deleted imls with --overwrite, including empty parent dirs', () async { final Map<String, String> templateManifest = _getManifest( intellijDir, 'template', isTemplate: true, ); _populateDir(templateManifest); final Map<String, String> flutterManifest = _getManifest( tempDir, 'existing', ); flutterManifest.remove(globals.fs.path.join('packages', 'new', 'deep.iml')); _populateDir(flutterManifest); final Map<String, String> updatedTemplates = _getManifest( intellijDir, 'existing', isTemplate: true, ); String deepIml = globals.fs.path.join( 'packages', 'flutter_tools', 'ide_templates', 'intellij'); // Remove the all the dir entries too. updatedTemplates.remove(deepIml); deepIml = globals.fs.path.join(deepIml, 'packages'); updatedTemplates.remove(deepIml); deepIml = globals.fs.path.join(deepIml, 'new'); updatedTemplates.remove(deepIml); deepIml = globals.fs.path.join(deepIml, 'deep.iml'); updatedTemplates.remove(deepIml); final Map<String, String> expectedContents = <String, String>{ ...flutterManifest, ...updatedTemplates, }; return _updateIdeConfig( args: <String>['--update-templates', '--overwrite'], expectedContents: expectedContents, ); }); }); }