| // 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. |
| |
| import 'package:file/memory.dart'; |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/convert.dart'; |
| import 'package:flutter_tools/src/plugins.dart'; |
| import 'package:flutter_tools/src/platform_plugins.dart'; |
| import 'package:flutter_tools/src/project.dart'; |
| import 'package:flutter_tools/src/windows/visual_studio_solution_utils.dart'; |
| import 'package:mockito/mockito.dart'; |
| |
| import '../../src/common.dart'; |
| |
| void main() { |
| group('Visual Studio Solution Utils', () { |
| // Magic values; see visual_studio_solution_utils.dart. |
| const String solutionTypeGuidFolder = '2150E333-8FDC-42A3-9474-1A3956D46DE8'; |
| const String solutionTypeGuidVcxproj = '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942'; |
| const String flutterPluginSolutionFolderGuid = '5C2E738A-1DD3-445A-AAC8-EEB9648DD07C'; |
| |
| // Arbitrary random GUIDs to use for fake plugins. |
| const String pluginAGuid = '9F200BC4-A747-43D1-8B72-B778F2C4D048'; |
| const String pluginBGuid = '39AC79B8-28A6-4526-B5FF-9C83F59B3AF0'; |
| const String pluginCGuid = '8E010714-28FF-416A-BC6F-9CDE336A02A7'; |
| const String pluginDGuid = '304F1860-7E8B-4C99-8E1D-F5E55259F5C3'; |
| |
| FileSystem fs; |
| MockWindowsProject project; |
| |
| setUp(() async { |
| fs = MemoryFileSystem(style: FileSystemStyle.windows); |
| |
| project = MockWindowsProject(); |
| when(project.pluginConfigKey).thenReturn('windows'); |
| final Directory windowsManagedDirectory = fs.directory('C:').childDirectory('windows').childDirectory('flutter'); |
| when(project.solutionFile).thenReturn(windowsManagedDirectory.parent.childFile('Runner.sln')); |
| when(project.vcprojFile).thenReturn(windowsManagedDirectory.parent.childFile('Runner.vcxproj')); |
| when(project.pluginSymlinkDirectory).thenReturn(windowsManagedDirectory.childDirectory('ephemeral').childDirectory('.plugin_symlinks')); |
| }); |
| |
| // Returns a solution file contents for a solution without any plugins. |
| void writeSolutionWithoutPlugins() { |
| project.solutionFile.createSync(recursive: true); |
| project.solutionFile.writeAsStringSync(''' |
| Microsoft Visual Studio Solution File, Format Version 12.00 |
| # Visual Studio Version 16 |
| VisualStudioVersion = 16.0.29709.97 |
| MinimumVisualStudioVersion = 10.0.40219.1 |
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Runner", "Runner.vcxproj", "{3842E94C-E348-463A-ADBE-625A2B69B628}" |
| ProjectSection(ProjectDependencies) = postProject |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} = {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} |
| EndProjectSection |
| EndProject |
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Flutter Build", "FlutterBuild.vcxproj", "{6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}" |
| EndProject |
| Global |
| GlobalSection(SolutionConfigurationPlatforms) = preSolution |
| Debug|x64 = Debug|x64 |
| Profile|x64 = Profile|x64 |
| Release|x64 = Release|x64 |
| EndGlobalSection |
| GlobalSection(ProjectConfigurationPlatforms) = postSolution |
| {3842E94C-E348-463A-ADBE-625A2B69B628}.Debug|x64.ActiveCfg = Debug|x64 |
| {3842E94C-E348-463A-ADBE-625A2B69B628}.Debug|x64.Build.0 = Debug|x64 |
| {3842E94C-E348-463A-ADBE-625A2B69B628}.Profile|x64.ActiveCfg = Profile|x64 |
| {3842E94C-E348-463A-ADBE-625A2B69B628}.Profile|x64.Build.0 = Profile|x64 |
| {3842E94C-E348-463A-ADBE-625A2B69B628}.Release|x64.ActiveCfg = Release|x64 |
| {3842E94C-E348-463A-ADBE-625A2B69B628}.Release|x64.Build.0 = Release|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Debug|x64.ActiveCfg = Debug|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Debug|x64.Build.0 = Debug|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Profile|x64.ActiveCfg = Profile|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Profile|x64.Build.0 = Profile|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Release|x64.ActiveCfg = Release|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Release|x64.Build.0 = Release|x64 |
| EndGlobalSection |
| GlobalSection(SolutionProperties) = preSolution |
| HideSolutionNode = FALSE |
| EndGlobalSection |
| GlobalSection(ExtensibilityGlobals) = postSolution |
| SolutionGuid = {B8A69CB0-A974-4774-9EBD-1E5EECACD186} |
| EndGlobalSection |
| EndGlobal'''); |
| } |
| |
| // Returns a solution file contents for a solution with plugins A, B, and C |
| // already added. |
| void writeSolutionWithPlugins() { |
| const String pluginSymlinkSubdirPath = r'Flutter\ephemeral\.plugin_symlinks'; |
| const String pluginProjectSubpath = r'windows\plugin.vcxproj'; |
| project.solutionFile.createSync(recursive: true); |
| project.solutionFile.writeAsStringSync(''' |
| Microsoft Visual Studio Solution File, Format Version 12.00 |
| # Visual Studio Version 16 |
| VisualStudioVersion = 16.0.29709.97 |
| MinimumVisualStudioVersion = 10.0.40219.1 |
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Runner", "Runner.vcxproj", "{5A827760-CF8B-408A-99A3-B6C0AD2271E7}" |
| ProjectSection(ProjectDependencies) = postProject |
| {$pluginAGuid} = {$pluginAGuid} |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} = {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} |
| {$pluginBGuid} = {$pluginBGuid} |
| {$pluginCGuid} = {$pluginCGuid} |
| EndProjectSection |
| EndProject |
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plugin_a", "$pluginSymlinkSubdirPath\\plugin_a\\$pluginProjectSubpath", "{$pluginAGuid}" |
| ProjectSection(ProjectDependencies) = postProject |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} = {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} |
| EndProjectSection |
| EndProject |
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Flutter Build", "FlutterBuild.vcxproj", "{6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}" |
| EndProject |
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plugin_b", "$pluginSymlinkSubdirPath\\plugin_b\\$pluginProjectSubpath", "{$pluginBGuid}" |
| ProjectSection(ProjectDependencies) = postProject |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} = {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} |
| EndProjectSection |
| EndProject |
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plugin_c", "$pluginSymlinkSubdirPath\\plugin_c\\$pluginProjectSubpath", "{$pluginCGuid}" |
| ProjectSection(ProjectDependencies) = postProject |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} = {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} |
| EndProjectSection |
| EndProject |
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Flutter Plugins", "Flutter Plugins", "{5C2E738A-1DD3-445A-AAC8-EEB9648DD07C}" |
| EndProject |
| Global |
| GlobalSection(SolutionConfigurationPlatforms) = preSolution |
| Debug|x64 = Debug|x64 |
| Profile|x64 = Profile|x64 |
| Release|x64 = Release|x64 |
| EndGlobalSection |
| GlobalSection(ProjectConfigurationPlatforms) = postSolution |
| {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Debug|x64.ActiveCfg = Debug|x64 |
| {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Debug|x64.Build.0 = Debug|x64 |
| {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Profile|x64.ActiveCfg = Profile|x64 |
| {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Profile|x64.Build.0 = Profile|x64 |
| {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Release|x64.ActiveCfg = Release|x64 |
| {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Release|x64.Build.0 = Release|x64 |
| {$pluginAGuid}.Debug|x64.ActiveCfg = Debug|x64 |
| {$pluginAGuid}.Debug|x64.Build.0 = Debug|x64 |
| {$pluginAGuid}.Profile|x64.ActiveCfg = Profile|x64 |
| {$pluginAGuid}.Profile|x64.Build.0 = Profile|x64 |
| {$pluginAGuid}.Release|x64.ActiveCfg = Release|x64 |
| {$pluginAGuid}.Release|x64.Build.0 = Release|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Debug|x64.ActiveCfg = Debug|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Debug|x64.Build.0 = Debug|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Profile|x64.ActiveCfg = Profile|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Profile|x64.Build.0 = Profile|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Release|x64.ActiveCfg = Release|x64 |
| {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Release|x64.Build.0 = Release|x64 |
| {$pluginBGuid}.Debug|x64.ActiveCfg = Debug|x64 |
| {$pluginBGuid}.Debug|x64.Build.0 = Debug|x64 |
| {$pluginBGuid}.Profile|x64.ActiveCfg = Profile|x64 |
| {$pluginBGuid}.Profile|x64.Build.0 = Profile|x64 |
| {$pluginBGuid}.Release|x64.ActiveCfg = Release|x64 |
| {$pluginBGuid}.Release|x64.Build.0 = Release|x64 |
| {$pluginCGuid}.Debug|x64.ActiveCfg = Debug|x64 |
| {$pluginCGuid}.Debug|x64.Build.0 = Debug|x64 |
| {$pluginCGuid}.Profile|x64.ActiveCfg = Profile|x64 |
| {$pluginCGuid}.Profile|x64.Build.0 = Profile|x64 |
| {$pluginCGuid}.Release|x64.ActiveCfg = Release|x64 |
| {$pluginCGuid}.Release|x64.Build.0 = Release|x64 |
| EndGlobalSection |
| GlobalSection(SolutionProperties) = preSolution |
| HideSolutionNode = FALSE |
| EndGlobalSection |
| GlobalSection(NestedProjects) = preSolution |
| {$pluginCGuid} = {$flutterPluginSolutionFolderGuid} |
| {$pluginBGuid} = {$flutterPluginSolutionFolderGuid} |
| {$pluginAGuid} = {$flutterPluginSolutionFolderGuid} |
| EndGlobalSection |
| GlobalSection(ExtensibilityGlobals) = postSolution |
| SolutionGuid = {6C8A8041-10D8-4BEB-B73D-C02BCE62120B} |
| EndGlobalSection |
| EndGlobal'''); |
| } |
| |
| void writeDummyPluginProject(String pluginName, String guid) { |
| final File pluginProjectFile = project.pluginSymlinkDirectory |
| .childDirectory(pluginName) |
| .childDirectory('windows') |
| .childFile('plugin.vcxproj'); |
| pluginProjectFile.createSync(recursive: true); |
| pluginProjectFile.writeAsStringSync(''' |
| <?xml version="1.0" encoding="utf-8"?> |
| <Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
| <ItemGroup Label="ProjectConfigurations"> |
| <ProjectConfiguration Include="Debug|x64"> |
| <Configuration>Debug</Configuration> |
| <Platform>x64</Platform> |
| </ProjectConfiguration> |
| <ProjectConfiguration Include="Release|x64"> |
| <Configuration>Release</Configuration> |
| <Platform>x64</Platform> |
| </ProjectConfiguration> |
| </ItemGroup> |
| <PropertyGroup Label="Globals"> |
| <VCProjectVersion>15.0</VCProjectVersion> |
| <ProjectGuid>{$guid}</ProjectGuid> |
| <ProjectName>$pluginName</ProjectName> |
| <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> |
| </PropertyGroup> |
| <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.Default.props" /> |
| <PropertyGroup Label="Configuration"> |
| <PlatformToolset>v142</PlatformToolset> |
| </PropertyGroup> |
| <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.props" /> |
| <ImportGroup Label="ExtensionSettings"> |
| </ImportGroup> |
| <ImportGroup Label="Shared"> |
| </ImportGroup> |
| <ImportGroup Label="PropertySheets" /> |
| <PropertyGroup Label="UserMacros" /> |
| <PropertyGroup /> |
| <ItemDefinitionGroup /> |
| <ItemGroup> |
| </ItemGroup> |
| <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.targets" /> |
| <ImportGroup Label="ExtensionTargets"> |
| </ImportGroup> |
| </Project>'''); |
| } |
| |
| // Configures and returns a mock plugin with the given name and GUID in the |
| // project's plugin symlink directory. |
| Plugin getMockPlugin(String name, String guid, {bool createProject = true}) { |
| final MockPlugin plugin = MockPlugin(); |
| when(plugin.platforms).thenReturn(<String, PluginPlatform>{project.pluginConfigKey: null}); |
| when(plugin.name).thenReturn(name); |
| when(plugin.path).thenReturn(project.pluginSymlinkDirectory.childDirectory(name).path); |
| if (createProject) { |
| writeDummyPluginProject(name, guid); |
| } |
| return plugin; |
| } |
| |
| test('Adding the first plugin to a solution adds the expected references', () async { |
| writeSolutionWithoutPlugins(); |
| |
| final List<Plugin> plugins = <Plugin>[ |
| getMockPlugin('plugin_a', pluginAGuid), |
| ]; |
| await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); |
| |
| final String newSolutionContents = project.solutionFile.readAsStringSync(); |
| |
| // Check for: |
| // - Plugin project. |
| final String pluginSubpath = fs.path.join('Flutter', 'ephemeral', '.plugin_symlinks', 'plugin_a', 'windows', 'plugin.vcxproj'); |
| expect(newSolutionContents, contains('Project("{$solutionTypeGuidVcxproj}") = "plugin_a", "$pluginSubpath", "{$pluginAGuid}"')); |
| // - A dependency on the plugin project (from the Runner). |
| expect(newSolutionContents, contains('{$pluginAGuid} = {$pluginAGuid}')); |
| // - Plugin project configurations. |
| expect(newSolutionContents, contains('{$pluginAGuid}.Debug|x64.ActiveCfg = Debug|x64')); |
| expect(newSolutionContents, contains('{$pluginAGuid}.Debug|x64.Build.0 = Debug|x64')); |
| expect(newSolutionContents, contains('{$pluginAGuid}.Profile|x64.ActiveCfg = Profile|x64')); |
| expect(newSolutionContents, contains('{$pluginAGuid}.Profile|x64.Build.0 = Profile|x64')); |
| expect(newSolutionContents, contains('{$pluginAGuid}.Release|x64.ActiveCfg = Release|x64')); |
| expect(newSolutionContents, contains('{$pluginAGuid}.Release|x64.Build.0 = Release|x64')); |
| // - A plugin folder, and a child reference for the plugin. |
| expect(newSolutionContents, contains('Project("{$solutionTypeGuidFolder}") = "Flutter Plugins", "Flutter Plugins", "{$flutterPluginSolutionFolderGuid}"')); |
| expect(newSolutionContents, contains('{$pluginAGuid} = {$flutterPluginSolutionFolderGuid}')); |
| }); |
| |
| test('Removing a plugin removes entries as expected', () async { |
| writeSolutionWithPlugins(); |
| |
| final List<Plugin> plugins = <Plugin>[ |
| getMockPlugin('plugin_a', pluginAGuid), |
| getMockPlugin('plugin_c', pluginCGuid), |
| ]; |
| await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); |
| |
| final String newSolutionContents = project.solutionFile.readAsStringSync(); |
| |
| // There should be no references to plugin B anywhere. |
| expect(newSolutionContents.contains(pluginBGuid), false); |
| |
| // All the plugin A & C references should still be present in modified |
| // sections. |
| for (final String guid in <String>[pluginAGuid, pluginCGuid]) { |
| expect(newSolutionContents, contains('{$guid} = {$guid}')); |
| expect(newSolutionContents, contains('{$guid}.Debug|x64.ActiveCfg = Debug|x64')); |
| expect(newSolutionContents, contains('{$guid} = {$flutterPluginSolutionFolderGuid}')); |
| } |
| }); |
| |
| test('Removing all plugins works', () async { |
| writeSolutionWithPlugins(); |
| |
| final List<Plugin> plugins = <Plugin>[ |
| ]; |
| await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins( plugins); |
| |
| final String newSolutionContents = project.solutionFile.readAsStringSync(); |
| |
| // There should be no references to any of the plugins. |
| expect(newSolutionContents.contains(pluginAGuid), false); |
| expect(newSolutionContents.contains(pluginBGuid), false); |
| expect(newSolutionContents.contains(pluginCGuid), false); |
| // Nor any plugins in the Flutter Plugins folder. |
| expect(newSolutionContents.contains('= {$flutterPluginSolutionFolderGuid}'), false); |
| }); |
| |
| test('Adjusting the plugin list by adding and removing adjusts entries as expected', () async { |
| writeSolutionWithPlugins(); |
| |
| final List<Plugin> plugins = <Plugin>[ |
| getMockPlugin('plugin_b', pluginBGuid), |
| getMockPlugin('plugin_c', pluginCGuid), |
| getMockPlugin('plugin_d', pluginDGuid), |
| ]; |
| await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); |
| |
| final String newSolutionContents = project.solutionFile.readAsStringSync(); |
| |
| // There should be no references to plugin A anywhere. |
| expect(newSolutionContents.contains(pluginAGuid), false); |
| |
| // All the plugin B & C references should still be present in modified |
| // sections. |
| for (final String guid in <String>[pluginBGuid, pluginCGuid]) { |
| expect(newSolutionContents, contains('{$guid} = {$guid}')); |
| expect(newSolutionContents, contains('{$guid}.Debug|x64.ActiveCfg = Debug|x64')); |
| expect(newSolutionContents, contains('{$guid} = {$flutterPluginSolutionFolderGuid}')); |
| } |
| |
| // All the plugin D values should be added: |
| // - Plugin project. |
| final String pluginSubpath = fs.path.join('Flutter', 'ephemeral', '.plugin_symlinks', 'plugin_d', 'windows', 'plugin.vcxproj'); |
| expect(newSolutionContents, contains('Project("{$solutionTypeGuidVcxproj}") = "plugin_d", "$pluginSubpath", "{$pluginDGuid}"')); |
| // - A dependency on the plugin project (from the Runner). |
| expect(newSolutionContents, contains('{$pluginDGuid} = {$pluginDGuid}')); |
| // - Plugin project configurations. |
| expect(newSolutionContents, contains('{$pluginDGuid}.Debug|x64.ActiveCfg = Debug|x64')); |
| expect(newSolutionContents, contains('{$pluginDGuid}.Debug|x64.Build.0 = Debug|x64')); |
| expect(newSolutionContents, contains('{$pluginDGuid}.Profile|x64.ActiveCfg = Profile|x64')); |
| expect(newSolutionContents, contains('{$pluginDGuid}.Profile|x64.Build.0 = Profile|x64')); |
| expect(newSolutionContents, contains('{$pluginDGuid}.Release|x64.ActiveCfg = Release|x64')); |
| expect(newSolutionContents, contains('{$pluginDGuid}.Release|x64.Build.0 = Release|x64')); |
| // - A child reference for the plugin in the Flutter Plugins folder. |
| expect(newSolutionContents, contains('{$pluginDGuid} = {$flutterPluginSolutionFolderGuid}')); |
| }); |
| |
| test('Adding plugins doesn\'t create duplicate entries', () async { |
| writeSolutionWithPlugins(); |
| |
| final List<Plugin> plugins = <Plugin>[ |
| getMockPlugin('plugin_a', pluginAGuid), |
| getMockPlugin('plugin_b', pluginBGuid), |
| getMockPlugin('plugin_c', pluginCGuid), |
| getMockPlugin('plugin_d', pluginDGuid), |
| ]; |
| await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); |
| |
| final String newSolutionContents = project.solutionFile.readAsStringSync(); |
| // There should only be: |
| // - one Flutter Plugins folder. |
| expect('Project("{$solutionTypeGuidFolder}")'.allMatches(newSolutionContents).length, 1); |
| // - one copy of plugin A's project. |
| expect('Project("{$solutionTypeGuidVcxproj}") = "plugin_a"'.allMatches(newSolutionContents).length, 1); |
| // - one copy of plugin A's configuration entries. |
| expect('{$pluginAGuid}.Debug|x64.ActiveCfg = Debug|x64'.allMatches(newSolutionContents).length, 1); |
| // - one dependency from the Runner to plugin A. |
| expect('{$pluginAGuid} = {$pluginAGuid}'.allMatches(newSolutionContents).length, 1); |
| // - one copy of plugin A in Flutter Plugins. |
| expect('{$pluginAGuid} = {$flutterPluginSolutionFolderGuid}'.allMatches(newSolutionContents).length, 1); |
| }); |
| |
| test('Adding plugins doesn\'t change ordering', () async { |
| writeSolutionWithPlugins(); |
| |
| final List<Plugin> plugins = <Plugin>[ |
| getMockPlugin('plugin_a', pluginAGuid), |
| getMockPlugin('plugin_b', pluginBGuid), |
| getMockPlugin('plugin_c', pluginCGuid), |
| getMockPlugin('plugin_d', pluginDGuid), |
| ]; |
| await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); |
| |
| final String newSolutionContents = project.solutionFile.readAsStringSync(); |
| // Plugin A should still be before Flutter Build in the Runner dependencies. |
| expect(newSolutionContents.indexOf('{$pluginAGuid} = {$pluginAGuid}'), |
| lessThan(newSolutionContents.indexOf('{6419BF13-6ECD-4CD2-9E85-E566A1F03F8F} = {6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}'))); |
| // ... and in the build configuration list. |
| expect(newSolutionContents.indexOf('{$pluginAGuid}.Debug|x64.ActiveCfg = Debug|x64'), |
| lessThan(newSolutionContents.indexOf('{6419BF13-6ECD-4CD2-9E85-E566A1F03F8F}.Debug|x64.ActiveCfg = Debug|x64'))); |
| // And plugin C should still be before plugin A in the Flutter Plugins nesting list. |
| expect(newSolutionContents.indexOf('{$pluginCGuid} = {$flutterPluginSolutionFolderGuid}'), |
| lessThan(newSolutionContents.indexOf('{$pluginAGuid} = {$flutterPluginSolutionFolderGuid}'))); |
| }); |
| |
| test('Updating solution preserves BOM', () async { |
| writeSolutionWithPlugins(); |
| |
| final List<Plugin> plugins = <Plugin>[]; |
| await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); |
| |
| // Visual Studio expects sln files to start with a BOM. |
| final List<int> solutionStartingBytes = project.solutionFile.readAsBytesSync().take(3).toList(); |
| final List<int> bomBytes = utf8.encode(String.fromCharCode(unicodeBomCharacterRune)); |
| expect(solutionStartingBytes, bomBytes); |
| }); |
| |
| test('Updating solution dosen\'t introduce unexpected whitespace', () async { |
| writeSolutionWithPlugins(); |
| |
| final List<Plugin> plugins = <Plugin>[ |
| getMockPlugin('plugin_a', pluginAGuid), |
| getMockPlugin('plugin_b', pluginBGuid), |
| ]; |
| await VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins); |
| |
| final String newSolutionContents = project.solutionFile.readAsStringSync(); |
| // Project, EndProject, Global, and EndGlobal should be at the start of |
| // lines. |
| expect(RegExp(r'^[ \t]+Project\(', multiLine: true).hasMatch(newSolutionContents), false); |
| expect(RegExp(r'^[ \t]+EndProject\s*$', multiLine: true).hasMatch(newSolutionContents), false); |
| expect(RegExp(r'^[ \t]+Global\s*$', multiLine: true).hasMatch(newSolutionContents), false); |
| expect(RegExp(r'^[ \t]+EndGlobal\s*$', multiLine: true).hasMatch(newSolutionContents), false); |
| // ProjectSection, GlobalSection, and their ends should be indented |
| // exactly one tab. |
| expect(RegExp(r'^([ \t]+\t|\t[ \t]+)ProjectSection\(', multiLine: true).hasMatch(newSolutionContents), false); |
| expect(RegExp(r'^([ \t]+\t|\t[ \t]+)EndProjectSection\s*$', multiLine: true).hasMatch(newSolutionContents), false); |
| expect(RegExp(r'^([ \t]+\t|\t[ \t]+)GlobalSection\(\s*$', multiLine: true).hasMatch(newSolutionContents), false); |
| expect(RegExp(r'^([ \t]+\t|\t[ \t]+)EndGlobalSection\s*$', multiLine: true).hasMatch(newSolutionContents), false); |
| }); |
| |
| test('A plugin without a project exits without crashing', () async { |
| writeSolutionWithoutPlugins(); |
| |
| final List<Plugin> plugins = <Plugin>[ |
| getMockPlugin('plugin_a', pluginAGuid, createProject: false), |
| ]; |
| expect(() => VisualStudioSolutionUtils(project: project, fileSystem: fs).updatePlugins(plugins), |
| throwsToolExit()); |
| }); |
| }); |
| } |
| |
| class MockWindowsProject extends Mock implements WindowsProject {} |
| class MockPlugin extends Mock implements Plugin {} |