diff options
author | Rob Mensching <rob@firegiant.com> | 2020-07-18 14:57:08 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2020-07-18 15:06:43 -0700 |
commit | e04caab11fb8f2cac4d575ef1e352221bd421586 (patch) | |
tree | 7c9752f17fab2793cabb07e73a42b52d573e1249 /src | |
parent | 92bb1d2d74e46714459c2d0fc23f185329745718 (diff) | |
download | wix-e04caab11fb8f2cac4d575ef1e352221bd421586.tar.gz wix-e04caab11fb8f2cac4d575ef1e352221bd421586.tar.bz2 wix-e04caab11fb8f2cac4d575ef1e352221bd421586.zip |
Separate "format" from "convert"
Closes wixtoolset/issues#6215
Diffstat (limited to 'src')
-rw-r--r-- | src/WixToolset.Converters/ConvertCommand.cs | 269 | ||||
-rw-r--r-- | src/WixToolset.Converters/ConverterExtensionCommandLine.cs | 11 | ||||
-rw-r--r-- | src/WixToolset.Converters/FixupCommandBase.cs | 267 | ||||
-rw-r--r-- | src/WixToolset.Converters/FormatCommand.cs | 60 | ||||
-rw-r--r-- | src/WixToolset.Converters/WixConverter.cs | 179 | ||||
-rw-r--r-- | src/test/WixToolsetTest.Converters/ConverterFixture.cs | 104 | ||||
-rw-r--r-- | src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs | 10 | ||||
-rw-r--r-- | src/test/WixToolsetTest.Converters/FormatFixture.cs | 117 |
8 files changed, 589 insertions, 428 deletions
diff --git a/src/WixToolset.Converters/ConvertCommand.cs b/src/WixToolset.Converters/ConvertCommand.cs index 51e7b997..50ca7249 100644 --- a/src/WixToolset.Converters/ConvertCommand.cs +++ b/src/WixToolset.Converters/ConvertCommand.cs | |||
@@ -3,283 +3,58 @@ | |||
3 | namespace WixToolset.Converters | 3 | namespace WixToolset.Converters |
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Threading; | 6 | using System.Threading; |
9 | using System.Threading.Tasks; | 7 | using System.Threading.Tasks; |
10 | using System.Xml; | ||
11 | using WixToolset.Extensibility.Data; | ||
12 | using WixToolset.Extensibility.Services; | 8 | using WixToolset.Extensibility.Services; |
13 | 9 | ||
14 | internal class ConvertCommand : ICommandLineCommand | 10 | internal class ConvertCommand : FixupCommandBase |
15 | { | 11 | { |
16 | private const string SettingsFileDefault = "wixcop.settings.xml"; | 12 | private const string SettingsFileDefault = "wix.convert.settings.xml"; |
17 | 13 | ||
18 | public ConvertCommand(IWixToolsetServiceProvider serviceProvider) | 14 | public ConvertCommand(IWixToolsetServiceProvider serviceProvider) |
19 | { | 15 | { |
20 | this.Messaging = serviceProvider.GetService<IMessaging>(); | 16 | this.Messaging = serviceProvider.GetService<IMessaging>(); |
21 | |||
22 | this.IndentationAmount = 4; // default indentation amount | ||
23 | this.ErrorsAsWarnings = new HashSet<string>(); | ||
24 | this.ExemptFiles = new HashSet<string>(); | ||
25 | this.IgnoreErrors = new HashSet<string>(); | ||
26 | this.SearchPatternResults = new HashSet<string>(); | ||
27 | this.SearchPatterns = new List<string>(); | ||
28 | } | 17 | } |
29 | 18 | ||
30 | private IMessaging Messaging { get; } | 19 | private IMessaging Messaging { get; } |
31 | 20 | ||
32 | public bool ShowLogo { get; private set; } | 21 | public override Task<int> ExecuteAsync(CancellationToken cancellationToken) |
33 | |||
34 | public bool StopParsing { get; private set; } | ||
35 | |||
36 | private bool ShowHelp { get; set; } | ||
37 | |||
38 | private HashSet<string> ErrorsAsWarnings { get; } | ||
39 | |||
40 | private HashSet<string> ExemptFiles { get; } | ||
41 | |||
42 | private bool FixErrors { get; set; } | ||
43 | |||
44 | private int IndentationAmount { get; set; } | ||
45 | |||
46 | private HashSet<string> IgnoreErrors { get; } | ||
47 | |||
48 | private HashSet<string> SearchPatternResults { get; } | ||
49 | |||
50 | private List<string> SearchPatterns { get; } | ||
51 | |||
52 | private string SettingsFile1 { get; set; } | ||
53 | |||
54 | private string SettingsFile2 { get; set; } | ||
55 | |||
56 | private bool SubDirectories { get; set; } | ||
57 | |||
58 | public bool TryParseArgument(ICommandLineParser parser, string argument) | ||
59 | { | ||
60 | if (!parser.IsSwitch(argument)) | ||
61 | { | ||
62 | this.SearchPatterns.Add(argument); | ||
63 | return true; | ||
64 | } | ||
65 | |||
66 | var parameter = argument.Substring(1); | ||
67 | switch (parameter.ToLowerInvariant()) | ||
68 | { | ||
69 | case "?": | ||
70 | this.ShowHelp = true; | ||
71 | this.ShowLogo = true; | ||
72 | this.StopParsing = true; | ||
73 | return true; | ||
74 | |||
75 | case "f": | ||
76 | this.FixErrors = true; | ||
77 | return true; | ||
78 | |||
79 | case "nologo": | ||
80 | this.ShowLogo = false; | ||
81 | return true; | ||
82 | |||
83 | case "s": | ||
84 | this.SubDirectories = true; | ||
85 | return true; | ||
86 | |||
87 | default: // other parameters | ||
88 | if (parameter.StartsWith("set1", StringComparison.Ordinal)) | ||
89 | { | ||
90 | this.SettingsFile1 = parameter.Substring(4); | ||
91 | return true; | ||
92 | } | ||
93 | else if (parameter.StartsWith("set2", StringComparison.Ordinal)) | ||
94 | { | ||
95 | this.SettingsFile2 = parameter.Substring(4); | ||
96 | return true; | ||
97 | } | ||
98 | else if (parameter.StartsWith("indent:", StringComparison.Ordinal)) | ||
99 | { | ||
100 | try | ||
101 | { | ||
102 | this.IndentationAmount = Convert.ToInt32(parameter.Substring(7)); | ||
103 | } | ||
104 | catch | ||
105 | { | ||
106 | parser.ErrorArgument = parameter; // $"Invalid numeric argument: {parameter}"; | ||
107 | } | ||
108 | return true; | ||
109 | } | ||
110 | |||
111 | return false; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | public Task<int> ExecuteAsync(CancellationToken cancellationToken) | ||
116 | { | 22 | { |
117 | if (this.ShowHelp) | 23 | if (this.ShowHelp) |
118 | { | 24 | { |
119 | DisplayHelp(); | 25 | DisplayHelp(); |
120 | return Task.FromResult(1); | 26 | return Task.FromResult(-1); |
121 | } | ||
122 | |||
123 | // parse the settings if any were specified | ||
124 | if (null != this.SettingsFile1 || null != this.SettingsFile2) | ||
125 | { | ||
126 | this.ParseSettingsFiles(this.SettingsFile1, this.SettingsFile2); | ||
127 | } | ||
128 | else | ||
129 | { | ||
130 | if (File.Exists(ConvertCommand.SettingsFileDefault)) | ||
131 | { | ||
132 | this.ParseSettingsFiles(ConvertCommand.SettingsFileDefault, null); | ||
133 | } | ||
134 | } | 27 | } |
135 | 28 | ||
136 | var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); | 29 | var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); |
137 | 30 | ||
138 | var errors = this.InspectSubDirectories(converter, Path.GetFullPath("."), cancellationToken); | 31 | this.ParseSettings(SettingsFileDefault); |
32 | |||
33 | var errors = base.Inspect(Inspector, cancellationToken); | ||
139 | 34 | ||
140 | foreach (var searchPattern in this.SearchPatterns) | 35 | return Task.FromResult(errors); |
36 | |||
37 | int Inspector(string file, bool fix) | ||
141 | { | 38 | { |
142 | if (!this.SearchPatternResults.Contains(searchPattern)) | 39 | return converter.ConvertFile(file, fix); |
143 | { | ||
144 | Console.Error.WriteLine("Could not find file \"{0}\"", searchPattern); | ||
145 | errors++; | ||
146 | } | ||
147 | } | 40 | } |
148 | |||
149 | return Task.FromResult(errors != 0 ? 2 : 0); | ||
150 | } | 41 | } |
151 | 42 | ||
152 | private static void DisplayHelp() | 43 | private static void DisplayHelp() |
153 | { | 44 | { |
154 | Console.WriteLine(" usage: wix.exe convert sourceFile [sourceFile ...]"); | ||
155 | Console.WriteLine(); | 45 | Console.WriteLine(); |
156 | Console.WriteLine(" -f fix errors automatically for writable files"); | 46 | Console.WriteLine("Usage: wix convert [options] sourceFile [sourceFile ...]"); |
157 | Console.WriteLine(" -nologo suppress displaying the logo information"); | ||
158 | Console.WriteLine(" -s search for matching files in current dir and subdirs"); | ||
159 | Console.WriteLine(" -set1<file> primary settings file"); | ||
160 | Console.WriteLine(" -set2<file> secondary settings file (overrides primary)"); | ||
161 | Console.WriteLine(" -indent:<n> indentation multiple (overrides default of 4)"); | ||
162 | Console.WriteLine(" -? this help information"); | ||
163 | Console.WriteLine(); | 47 | Console.WriteLine(); |
164 | Console.WriteLine(" sourceFile may use wildcards like *.wxs"); | 48 | Console.WriteLine("Options:"); |
165 | } | 49 | Console.WriteLine(" -h|--help Show command line help."); |
166 | 50 | Console.WriteLine(" --nologo Suppress displaying the logo information."); | |
167 | /// <summary> | 51 | Console.WriteLine(" -n|--dry-run Only display errors, do not update files."); |
168 | /// Get the files that match a search path pattern. | 52 | Console.WriteLine(" -r|--recurse Search for matching files in current dir and subdirs."); |
169 | /// </summary> | 53 | Console.WriteLine(" -set1<file> Primary settings file."); |
170 | /// <param name="baseDir">The base directory at which to begin the search.</param> | 54 | Console.WriteLine(" -set2<file> Secondary settings file (overrides primary)."); |
171 | /// <param name="searchPath">The search path pattern.</param> | 55 | Console.WriteLine(" -indent:<n> Indentation multiple (overrides default of 4)."); |
172 | /// <returns>The files matching the pattern.</returns> | 56 | Console.WriteLine(); |
173 | private static string[] GetFiles(string baseDir, string searchPath) | 57 | Console.WriteLine(" sourceFile may use wildcards like *.wxs"); |
174 | { | ||
175 | // convert alternate directory separators to the standard one | ||
176 | var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||
177 | var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); | ||
178 | string[] files = null; | ||
179 | |||
180 | try | ||
181 | { | ||
182 | if (0 > lastSeparator) | ||
183 | { | ||
184 | files = Directory.GetFiles(baseDir, filePath); | ||
185 | } | ||
186 | else // found directory separator | ||
187 | { | ||
188 | var searchPattern = filePath.Substring(lastSeparator + 1); | ||
189 | |||
190 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), searchPattern); | ||
191 | } | ||
192 | } | ||
193 | catch (DirectoryNotFoundException) | ||
194 | { | ||
195 | // don't let this function throw the DirectoryNotFoundException. (this exception | ||
196 | // occurs for non-existant directories and invalid characters in the searchPattern) | ||
197 | } | ||
198 | |||
199 | return files; | ||
200 | } | ||
201 | |||
202 | /// <summary> | ||
203 | /// Inspect sub-directories. | ||
204 | /// </summary> | ||
205 | /// <param name="directory">The directory whose sub-directories will be inspected.</param> | ||
206 | /// <returns>The number of errors that were found.</returns> | ||
207 | private int InspectSubDirectories(WixConverter converter, string directory, CancellationToken cancellationToken) | ||
208 | { | ||
209 | var errors = 0; | ||
210 | |||
211 | foreach (var searchPattern in this.SearchPatterns) | ||
212 | { | ||
213 | foreach (var sourceFilePath in GetFiles(directory, searchPattern)) | ||
214 | { | ||
215 | cancellationToken.ThrowIfCancellationRequested(); | ||
216 | |||
217 | var file = new FileInfo(sourceFilePath); | ||
218 | |||
219 | if (!this.ExemptFiles.Contains(file.Name.ToUpperInvariant())) | ||
220 | { | ||
221 | this.SearchPatternResults.Add(searchPattern); | ||
222 | errors += converter.ConvertFile(file.FullName, this.FixErrors); | ||
223 | } | ||
224 | } | ||
225 | } | ||
226 | |||
227 | if (this.SubDirectories) | ||
228 | { | ||
229 | foreach (var childDirectoryPath in Directory.GetDirectories(directory)) | ||
230 | { | ||
231 | errors += this.InspectSubDirectories(converter, childDirectoryPath, cancellationToken); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | return errors; | ||
236 | } | ||
237 | |||
238 | /// <summary> | ||
239 | /// Parse the primary and secondary settings files. | ||
240 | /// </summary> | ||
241 | /// <param name="localSettingsFile1">The primary settings file.</param> | ||
242 | /// <param name="localSettingsFile2">The secondary settings file.</param> | ||
243 | private void ParseSettingsFiles(string localSettingsFile1, string localSettingsFile2) | ||
244 | { | ||
245 | if (null == localSettingsFile1 && null != localSettingsFile2) | ||
246 | { | ||
247 | throw new ArgumentException("Cannot specify a secondary settings file (set2) without a primary settings file (set1).", nameof(localSettingsFile2)); | ||
248 | } | ||
249 | |||
250 | var settingsFile = localSettingsFile1; | ||
251 | while (null != settingsFile) | ||
252 | { | ||
253 | var doc = new XmlDocument(); | ||
254 | doc.Load(settingsFile); | ||
255 | |||
256 | // get the types of tests that will have their errors displayed as warnings | ||
257 | var testsIgnoredElements = doc.SelectNodes("/Settings/IgnoreErrors/Test"); | ||
258 | foreach (XmlElement test in testsIgnoredElements) | ||
259 | { | ||
260 | var key = test.GetAttribute("Id"); | ||
261 | this.IgnoreErrors.Add(key); | ||
262 | } | ||
263 | |||
264 | // get the types of tests that will have their errors displayed as warnings | ||
265 | var testsAsWarningsElements = doc.SelectNodes("/Settings/ErrorsAsWarnings/Test"); | ||
266 | foreach (XmlElement test in testsAsWarningsElements) | ||
267 | { | ||
268 | var key = test.GetAttribute("Id"); | ||
269 | this.ErrorsAsWarnings.Add(key); | ||
270 | } | ||
271 | |||
272 | // get the exempt files | ||
273 | var localExemptFiles = doc.SelectNodes("/Settings/ExemptFiles/File"); | ||
274 | foreach (XmlElement file in localExemptFiles) | ||
275 | { | ||
276 | var key = file.GetAttribute("Name").ToUpperInvariant(); | ||
277 | this.ExemptFiles.Add(key); | ||
278 | } | ||
279 | |||
280 | settingsFile = localSettingsFile2; | ||
281 | localSettingsFile2 = null; | ||
282 | } | ||
283 | } | 58 | } |
284 | } | 59 | } |
285 | } | 60 | } |
diff --git a/src/WixToolset.Converters/ConverterExtensionCommandLine.cs b/src/WixToolset.Converters/ConverterExtensionCommandLine.cs index ed4b613e..d78b89cc 100644 --- a/src/WixToolset.Converters/ConverterExtensionCommandLine.cs +++ b/src/WixToolset.Converters/ConverterExtensionCommandLine.cs | |||
@@ -21,8 +21,11 @@ namespace WixToolset.Converters | |||
21 | 21 | ||
22 | private IWixToolsetServiceProvider ServiceProvider { get; } | 22 | private IWixToolsetServiceProvider ServiceProvider { get; } |
23 | 23 | ||
24 | // TODO: Do something with CommandLineSwitches | 24 | public override IEnumerable<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[] |
25 | public override IEnumerable<ExtensionCommandLineSwitch> CommandLineSwitches => base.CommandLineSwitches; | 25 | { |
26 | new ExtensionCommandLineSwitch { Switch = "convert", Description = "Convert v3 source code to v4 source code." }, | ||
27 | new ExtensionCommandLineSwitch { Switch = "format", Description = "Ensures consistent formatting of source code." }, | ||
28 | }; | ||
26 | 29 | ||
27 | public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) | 30 | public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) |
28 | { | 31 | { |
@@ -32,6 +35,10 @@ namespace WixToolset.Converters | |||
32 | { | 35 | { |
33 | command = new ConvertCommand(this.ServiceProvider); | 36 | command = new ConvertCommand(this.ServiceProvider); |
34 | } | 37 | } |
38 | else if ("format".Equals(argument, StringComparison.OrdinalIgnoreCase)) | ||
39 | { | ||
40 | command = new FormatCommand(this.ServiceProvider); | ||
41 | } | ||
35 | 42 | ||
36 | return command != null; | 43 | return command != null; |
37 | } | 44 | } |
diff --git a/src/WixToolset.Converters/FixupCommandBase.cs b/src/WixToolset.Converters/FixupCommandBase.cs new file mode 100644 index 00000000..0f58fbdb --- /dev/null +++ b/src/WixToolset.Converters/FixupCommandBase.cs | |||
@@ -0,0 +1,267 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Converters | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Threading; | ||
9 | using System.Threading.Tasks; | ||
10 | using System.Xml; | ||
11 | using WixToolset.Extensibility.Data; | ||
12 | using WixToolset.Extensibility.Services; | ||
13 | |||
14 | internal abstract class FixupCommandBase : ICommandLineCommand | ||
15 | { | ||
16 | protected FixupCommandBase() | ||
17 | { | ||
18 | this.IndentationAmount = 4; // default indentation amount | ||
19 | this.ErrorsAsWarnings = new HashSet<string>(); | ||
20 | this.ExemptFiles = new HashSet<string>(); | ||
21 | this.IgnoreErrors = new HashSet<string>(); | ||
22 | this.SearchPatternResults = new HashSet<string>(); | ||
23 | this.SearchPatterns = new List<string>(); | ||
24 | } | ||
25 | |||
26 | public bool ShowLogo { get; private set; } | ||
27 | |||
28 | public bool StopParsing { get; private set; } | ||
29 | |||
30 | protected bool ShowHelp { get; set; } | ||
31 | |||
32 | protected bool DryRun { get; set; } | ||
33 | |||
34 | protected HashSet<string> ErrorsAsWarnings { get; } | ||
35 | |||
36 | protected HashSet<string> IgnoreErrors { get; } | ||
37 | |||
38 | protected HashSet<string> ExemptFiles { get; } | ||
39 | |||
40 | protected int IndentationAmount { get; set; } | ||
41 | |||
42 | protected bool Recurse { get; set; } | ||
43 | |||
44 | private HashSet<string> SearchPatternResults { get; } | ||
45 | |||
46 | private List<string> SearchPatterns { get; } | ||
47 | |||
48 | private string SettingsFile1 { get; set; } | ||
49 | |||
50 | private string SettingsFile2 { get; set; } | ||
51 | |||
52 | public bool TryParseArgument(ICommandLineParser parser, string argument) | ||
53 | { | ||
54 | if (!parser.IsSwitch(argument)) | ||
55 | { | ||
56 | this.SearchPatterns.Add(argument); | ||
57 | return true; | ||
58 | } | ||
59 | |||
60 | var parameter = argument.Substring(1); | ||
61 | switch (parameter.ToLowerInvariant()) | ||
62 | { | ||
63 | case "?": | ||
64 | case "h": | ||
65 | case "-help": | ||
66 | this.ShowHelp = true; | ||
67 | this.ShowLogo = true; | ||
68 | this.StopParsing = true; | ||
69 | return true; | ||
70 | |||
71 | case "n": | ||
72 | case "--dry-run": | ||
73 | this.DryRun = true; | ||
74 | return true; | ||
75 | |||
76 | case "nologo": | ||
77 | case "-nologo": | ||
78 | this.ShowLogo = false; | ||
79 | return true; | ||
80 | |||
81 | case "s": | ||
82 | case "r": | ||
83 | case "-recurse": | ||
84 | this.Recurse = true; | ||
85 | return true; | ||
86 | |||
87 | default: // other parameters | ||
88 | if (parameter.StartsWith("set1", StringComparison.Ordinal)) | ||
89 | { | ||
90 | this.SettingsFile1 = parameter.Substring(4); | ||
91 | return true; | ||
92 | } | ||
93 | else if (parameter.StartsWith("set2", StringComparison.Ordinal)) | ||
94 | { | ||
95 | this.SettingsFile2 = parameter.Substring(4); | ||
96 | return true; | ||
97 | } | ||
98 | else if (parameter.StartsWith("indent:", StringComparison.Ordinal)) | ||
99 | { | ||
100 | try | ||
101 | { | ||
102 | this.IndentationAmount = Convert.ToInt32(parameter.Substring(7)); | ||
103 | } | ||
104 | catch | ||
105 | { | ||
106 | parser.ErrorArgument = parameter; // $"Invalid numeric argument: {parameter}"; | ||
107 | } | ||
108 | return true; | ||
109 | } | ||
110 | |||
111 | return false; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken); | ||
116 | |||
117 | protected void ParseSettings(string defaultSettingsFile) | ||
118 | { | ||
119 | // parse the settings if any were specified | ||
120 | if (null != this.SettingsFile1 || null != this.SettingsFile2) | ||
121 | { | ||
122 | this.ParseSettingsFiles(this.SettingsFile1, this.SettingsFile2); | ||
123 | } | ||
124 | else | ||
125 | { | ||
126 | if (File.Exists(defaultSettingsFile)) | ||
127 | { | ||
128 | this.ParseSettingsFiles(defaultSettingsFile, null); | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | protected int Inspect(Func<string, bool, int> inspector, CancellationToken cancellationToken) | ||
134 | { | ||
135 | var errors = this.InspectSubDirectories(inspector, Path.GetFullPath("."), cancellationToken); | ||
136 | |||
137 | foreach (var searchPattern in this.SearchPatterns) | ||
138 | { | ||
139 | if (!this.SearchPatternResults.Contains(searchPattern)) | ||
140 | { | ||
141 | Console.Error.WriteLine("Could not find file \"{0}\"", searchPattern); | ||
142 | errors++; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | return errors; | ||
147 | } | ||
148 | |||
149 | /// <summary> | ||
150 | /// Inspect sub-directories. | ||
151 | /// </summary> | ||
152 | /// <param name="directory">The directory whose sub-directories will be inspected.</param> | ||
153 | /// <returns>The number of errors that were found.</returns> | ||
154 | private int InspectSubDirectories(Func<string, bool, int> inspector, string directory, CancellationToken cancellationToken) | ||
155 | { | ||
156 | var errors = 0; | ||
157 | |||
158 | foreach (var searchPattern in this.SearchPatterns) | ||
159 | { | ||
160 | foreach (var sourceFilePath in GetFiles(directory, searchPattern)) | ||
161 | { | ||
162 | cancellationToken.ThrowIfCancellationRequested(); | ||
163 | |||
164 | var file = new FileInfo(sourceFilePath); | ||
165 | |||
166 | if (!this.ExemptFiles.Contains(file.Name.ToUpperInvariant())) | ||
167 | { | ||
168 | this.SearchPatternResults.Add(searchPattern); | ||
169 | errors += inspector(file.FullName, !this.DryRun); | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | if (this.Recurse) | ||
175 | { | ||
176 | foreach (var childDirectoryPath in Directory.GetDirectories(directory)) | ||
177 | { | ||
178 | errors += this.InspectSubDirectories(inspector, childDirectoryPath, cancellationToken); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | return errors; | ||
183 | } | ||
184 | |||
185 | /// <summary> | ||
186 | /// Parse the primary and secondary settings files. | ||
187 | /// </summary> | ||
188 | /// <param name="localSettingsFile1">The primary settings file.</param> | ||
189 | /// <param name="localSettingsFile2">The secondary settings file.</param> | ||
190 | private void ParseSettingsFiles(string localSettingsFile1, string localSettingsFile2) | ||
191 | { | ||
192 | if (null == localSettingsFile1 && null != localSettingsFile2) | ||
193 | { | ||
194 | throw new ArgumentException("Cannot specify a secondary settings file (set2) without a primary settings file (set1).", nameof(localSettingsFile2)); | ||
195 | } | ||
196 | |||
197 | var settingsFile = localSettingsFile1; | ||
198 | while (null != settingsFile) | ||
199 | { | ||
200 | var doc = new XmlDocument(); | ||
201 | doc.Load(settingsFile); | ||
202 | |||
203 | // get the types of tests that will have their errors displayed as warnings | ||
204 | var testsIgnoredElements = doc.SelectNodes("/Settings/IgnoreErrors/Test"); | ||
205 | foreach (XmlElement test in testsIgnoredElements) | ||
206 | { | ||
207 | var key = test.GetAttribute("Id"); | ||
208 | this.IgnoreErrors.Add(key); | ||
209 | } | ||
210 | |||
211 | // get the types of tests that will have their errors displayed as warnings | ||
212 | var testsAsWarningsElements = doc.SelectNodes("/Settings/ErrorsAsWarnings/Test"); | ||
213 | foreach (XmlElement test in testsAsWarningsElements) | ||
214 | { | ||
215 | var key = test.GetAttribute("Id"); | ||
216 | this.ErrorsAsWarnings.Add(key); | ||
217 | } | ||
218 | |||
219 | // get the exempt files | ||
220 | var localExemptFiles = doc.SelectNodes("/Settings/ExemptFiles/File"); | ||
221 | foreach (XmlElement file in localExemptFiles) | ||
222 | { | ||
223 | var key = file.GetAttribute("Name").ToUpperInvariant(); | ||
224 | this.ExemptFiles.Add(key); | ||
225 | } | ||
226 | |||
227 | settingsFile = localSettingsFile2; | ||
228 | localSettingsFile2 = null; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | /// <summary> | ||
233 | /// Get the files that match a search path pattern. | ||
234 | /// </summary> | ||
235 | /// <param name="baseDir">The base directory at which to begin the search.</param> | ||
236 | /// <param name="searchPath">The search path pattern.</param> | ||
237 | /// <returns>The files matching the pattern.</returns> | ||
238 | private static string[] GetFiles(string baseDir, string searchPath) | ||
239 | { | ||
240 | // convert alternate directory separators to the standard one | ||
241 | var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||
242 | var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); | ||
243 | string[] files = null; | ||
244 | |||
245 | try | ||
246 | { | ||
247 | if (0 > lastSeparator) | ||
248 | { | ||
249 | files = Directory.GetFiles(baseDir, filePath); | ||
250 | } | ||
251 | else // found directory separator | ||
252 | { | ||
253 | var searchPattern = filePath.Substring(lastSeparator + 1); | ||
254 | |||
255 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), searchPattern); | ||
256 | } | ||
257 | } | ||
258 | catch (DirectoryNotFoundException) | ||
259 | { | ||
260 | // don't let this function throw the DirectoryNotFoundException. (this exception | ||
261 | // occurs for non-existant directories and invalid characters in the searchPattern) | ||
262 | } | ||
263 | |||
264 | return files; | ||
265 | } | ||
266 | } | ||
267 | } | ||
diff --git a/src/WixToolset.Converters/FormatCommand.cs b/src/WixToolset.Converters/FormatCommand.cs new file mode 100644 index 00000000..e9965df3 --- /dev/null +++ b/src/WixToolset.Converters/FormatCommand.cs | |||
@@ -0,0 +1,60 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Converters | ||
4 | { | ||
5 | using System; | ||
6 | using System.Threading; | ||
7 | using System.Threading.Tasks; | ||
8 | using WixToolset.Extensibility.Services; | ||
9 | |||
10 | internal class FormatCommand : FixupCommandBase | ||
11 | { | ||
12 | private const string SettingsFileDefault = "wix.format.settings.xml"; | ||
13 | |||
14 | public FormatCommand(IWixToolsetServiceProvider serviceProvider) | ||
15 | { | ||
16 | this.Messaging = serviceProvider.GetService<IMessaging>(); | ||
17 | } | ||
18 | |||
19 | private IMessaging Messaging { get; } | ||
20 | |||
21 | public override Task<int> ExecuteAsync(CancellationToken cancellationToken) | ||
22 | { | ||
23 | if (this.ShowHelp) | ||
24 | { | ||
25 | DisplayHelp(); | ||
26 | return Task.FromResult(-1); | ||
27 | } | ||
28 | |||
29 | var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); | ||
30 | |||
31 | this.ParseSettings(SettingsFileDefault); | ||
32 | |||
33 | var errors = base.Inspect(Inspector, cancellationToken); | ||
34 | |||
35 | return Task.FromResult(errors); | ||
36 | |||
37 | int Inspector(string file, bool fix) | ||
38 | { | ||
39 | return converter.FormatFile(file, fix); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | private static void DisplayHelp() | ||
44 | { | ||
45 | Console.WriteLine(); | ||
46 | Console.WriteLine("Usage: wix format [options] sourceFile [sourceFile ...]"); | ||
47 | Console.WriteLine(); | ||
48 | Console.WriteLine("Options:"); | ||
49 | Console.WriteLine(" -h|--help Show command line help."); | ||
50 | Console.WriteLine(" --nologo Suppress displaying the logo information."); | ||
51 | Console.WriteLine(" -n|--dry-run Only display errors, do not update files."); | ||
52 | Console.WriteLine(" -r|--recurse Search for matching files in current dir and subdirs."); | ||
53 | Console.WriteLine(" -set1<file> Primary settings file."); | ||
54 | Console.WriteLine(" -set2<file> Secondary settings file (overrides primary)."); | ||
55 | Console.WriteLine(" -indent:<n> Indentation multiple (overrides default of 4)."); | ||
56 | Console.WriteLine(); | ||
57 | Console.WriteLine(" sourceFile may use wildcards like *.wxs"); | ||
58 | } | ||
59 | } | ||
60 | } | ||
diff --git a/src/WixToolset.Converters/WixConverter.cs b/src/WixToolset.Converters/WixConverter.cs index a05c7f58..bfeed03e 100644 --- a/src/WixToolset.Converters/WixConverter.cs +++ b/src/WixToolset.Converters/WixConverter.cs | |||
@@ -19,6 +19,12 @@ namespace WixToolset.Converters | |||
19 | /// </summary> | 19 | /// </summary> |
20 | public class WixConverter | 20 | public class WixConverter |
21 | { | 21 | { |
22 | private enum ConvertOperation | ||
23 | { | ||
24 | Convert, | ||
25 | Format, | ||
26 | } | ||
27 | |||
22 | private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); | 28 | private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); |
23 | private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters | 29 | private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters |
24 | 30 | ||
@@ -107,18 +113,6 @@ namespace WixToolset.Converters | |||
107 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, | 113 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, |
108 | }; | 114 | }; |
109 | 115 | ||
110 | private readonly static SortedSet<string> Wix3Namespaces = new SortedSet<string> | ||
111 | { | ||
112 | "http://schemas.microsoft.com/wix/2006/wi", | ||
113 | "http://schemas.microsoft.com/wix/2006/localization", | ||
114 | }; | ||
115 | |||
116 | private readonly static SortedSet<string> Wix4Namespaces = new SortedSet<string> | ||
117 | { | ||
118 | "http://wixtoolset.org/schemas/v4/wxs", | ||
119 | "http://wixtoolset.org/schemas/v4/wxl", | ||
120 | }; | ||
121 | |||
122 | private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping; | 116 | private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping; |
123 | 117 | ||
124 | /// <summary> | 118 | /// <summary> |
@@ -193,6 +187,8 @@ namespace WixToolset.Converters | |||
193 | 187 | ||
194 | private int IndentationAmount { get; set; } | 188 | private int IndentationAmount { get; set; } |
195 | 189 | ||
190 | private ConvertOperation Operation { get; set; } | ||
191 | |||
196 | private string SourceFile { get; set; } | 192 | private string SourceFile { get; set; } |
197 | 193 | ||
198 | private int SourceVersion { get; set; } | 194 | private int SourceVersion { get; set; } |
@@ -205,22 +201,11 @@ namespace WixToolset.Converters | |||
205 | /// <returns>The number of errors found.</returns> | 201 | /// <returns>The number of errors found.</returns> |
206 | public int ConvertFile(string sourceFile, bool saveConvertedFile) | 202 | public int ConvertFile(string sourceFile, bool saveConvertedFile) |
207 | { | 203 | { |
208 | XDocument document; | 204 | var document = this.OpenSourceFile(sourceFile); |
209 | |||
210 | // Set the instance info. | ||
211 | this.Errors = 0; | ||
212 | this.SourceFile = sourceFile; | ||
213 | this.SourceVersion = 0; | ||
214 | 205 | ||
215 | try | 206 | if (document is null) |
216 | { | 207 | { |
217 | document = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | 208 | return 1; |
218 | } | ||
219 | catch (XmlException e) | ||
220 | { | ||
221 | this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); | ||
222 | |||
223 | return this.Errors; | ||
224 | } | 209 | } |
225 | 210 | ||
226 | this.ConvertDocument(document); | 211 | this.ConvertDocument(document); |
@@ -228,17 +213,7 @@ namespace WixToolset.Converters | |||
228 | // Fix errors if requested and necessary. | 213 | // Fix errors if requested and necessary. |
229 | if (saveConvertedFile && 0 < this.Errors) | 214 | if (saveConvertedFile && 0 < this.Errors) |
230 | { | 215 | { |
231 | try | 216 | this.SaveDocument(document); |
232 | { | ||
233 | using (var writer = XmlWriter.Create(this.SourceFile, new XmlWriterSettings { OmitXmlDeclaration = true })) | ||
234 | { | ||
235 | document.Save(writer); | ||
236 | } | ||
237 | } | ||
238 | catch (UnauthorizedAccessException) | ||
239 | { | ||
240 | this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); | ||
241 | } | ||
242 | } | 217 | } |
243 | 218 | ||
244 | return this.Errors; | 219 | return this.Errors; |
@@ -251,13 +226,68 @@ namespace WixToolset.Converters | |||
251 | /// <returns>The number of errors found.</returns> | 226 | /// <returns>The number of errors found.</returns> |
252 | public int ConvertDocument(XDocument document) | 227 | public int ConvertDocument(XDocument document) |
253 | { | 228 | { |
229 | // Reset the instance info. | ||
254 | this.Errors = 0; | 230 | this.Errors = 0; |
255 | this.SourceVersion = 0; | 231 | this.SourceVersion = 0; |
232 | this.Operation = ConvertOperation.Convert; | ||
233 | |||
234 | // Remove the declaration. | ||
235 | if (null != document.Declaration) | ||
236 | { | ||
237 | if (this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) | ||
238 | { | ||
239 | document.Declaration = null; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | TrimLeadingText(document); | ||
244 | |||
245 | // Start converting the nodes at the top. | ||
246 | this.ConvertNodes(document.Nodes(), 0); | ||
247 | |||
248 | return this.Errors; | ||
249 | } | ||
256 | 250 | ||
257 | var declaration = document.Declaration; | 251 | /// <summary> |
252 | /// Format a file. | ||
253 | /// </summary> | ||
254 | /// <param name="sourceFile">The file to format.</param> | ||
255 | /// <param name="saveConvertedFile">Option to save the format errors that are found.</param> | ||
256 | /// <returns>The number of errors found.</returns> | ||
257 | public int FormatFile(string sourceFile, bool saveConvertedFile) | ||
258 | { | ||
259 | var document = this.OpenSourceFile(sourceFile); | ||
260 | |||
261 | if (document is null) | ||
262 | { | ||
263 | return 1; | ||
264 | } | ||
265 | |||
266 | this.FormatDocument(document); | ||
267 | |||
268 | // Fix errors if requested and necessary. | ||
269 | if (saveConvertedFile && 0 < this.Errors) | ||
270 | { | ||
271 | this.SaveDocument(document); | ||
272 | } | ||
273 | |||
274 | return this.Errors; | ||
275 | } | ||
276 | |||
277 | /// <summary> | ||
278 | /// Format a document. | ||
279 | /// </summary> | ||
280 | /// <param name="document">The document to format.</param> | ||
281 | /// <returns>The number of errors found.</returns> | ||
282 | public int FormatDocument(XDocument document) | ||
283 | { | ||
284 | // Reset the instance info. | ||
285 | this.Errors = 0; | ||
286 | this.SourceVersion = 0; | ||
287 | this.Operation = ConvertOperation.Format; | ||
258 | 288 | ||
259 | // Remove the declaration. | 289 | // Remove the declaration. |
260 | if (null != declaration) | 290 | if (null != document.Declaration) |
261 | { | 291 | { |
262 | if (this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) | 292 | if (this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) |
263 | { | 293 | { |
@@ -273,6 +303,37 @@ namespace WixToolset.Converters | |||
273 | return this.Errors; | 303 | return this.Errors; |
274 | } | 304 | } |
275 | 305 | ||
306 | private XDocument OpenSourceFile(string sourceFile) | ||
307 | { | ||
308 | this.SourceFile = sourceFile; | ||
309 | |||
310 | try | ||
311 | { | ||
312 | return XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
313 | } | ||
314 | catch (XmlException e) | ||
315 | { | ||
316 | this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); | ||
317 | } | ||
318 | |||
319 | return null; | ||
320 | } | ||
321 | |||
322 | private void SaveDocument(XDocument document) | ||
323 | { | ||
324 | try | ||
325 | { | ||
326 | using (var writer = XmlWriter.Create(this.SourceFile, new XmlWriterSettings { OmitXmlDeclaration = true })) | ||
327 | { | ||
328 | document.Save(writer); | ||
329 | } | ||
330 | } | ||
331 | catch (UnauthorizedAccessException) | ||
332 | { | ||
333 | this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); | ||
334 | } | ||
335 | } | ||
336 | |||
276 | private void ConvertNodes(IEnumerable<XNode> nodes, int level) | 337 | private void ConvertNodes(IEnumerable<XNode> nodes, int level) |
277 | { | 338 | { |
278 | // Note we operate on a copy of the node list since we may | 339 | // Note we operate on a copy of the node list since we may |
@@ -901,7 +962,10 @@ namespace WixToolset.Converters | |||
901 | /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> | 962 | /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> |
902 | private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) | 963 | private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) |
903 | { | 964 | { |
904 | if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error | 965 | // Ignore the error if explicitly ignored or outside the range of the current operation. |
966 | if (this.IgnoreErrors.Contains(converterTestType) || | ||
967 | (this.Operation == ConvertOperation.Convert && converterTestType < ConverterTestType.DeclarationPresent) || | ||
968 | (this.Operation == ConvertOperation.Format && converterTestType > ConverterTestType.DeclarationPresent)) | ||
905 | { | 969 | { |
906 | return false; | 970 | return false; |
907 | } | 971 | } |
@@ -909,7 +973,7 @@ namespace WixToolset.Converters | |||
909 | // Increase the error count. | 973 | // Increase the error count. |
910 | this.Errors++; | 974 | this.Errors++; |
911 | 975 | ||
912 | var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); | 976 | var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wix.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); |
913 | var warning = this.ErrorsAsWarnings.Contains(converterTestType); | 977 | var warning = this.ErrorsAsWarnings.Contains(converterTestType); |
914 | var display = String.Format(CultureInfo.CurrentCulture, message, args); | 978 | var display = String.Format(CultureInfo.CurrentCulture, message, args); |
915 | 979 | ||
@@ -1050,39 +1114,19 @@ namespace WixToolset.Converters | |||
1050 | UnauthorizedAccessException, | 1114 | UnauthorizedAccessException, |
1051 | 1115 | ||
1052 | /// <summary> | 1116 | /// <summary> |
1053 | /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'. | ||
1054 | /// </summary> | ||
1055 | DeclarationEncodingWrong, | ||
1056 | |||
1057 | /// <summary> | ||
1058 | /// Displayed when the XML declaration is missing from the source file. | ||
1059 | /// </summary> | ||
1060 | DeclarationMissing, | ||
1061 | |||
1062 | /// <summary> | ||
1063 | /// Displayed when the whitespace preceding a CDATA node is wrong. | ||
1064 | /// </summary> | ||
1065 | WhitespacePrecedingCDATAWrong, | ||
1066 | |||
1067 | /// <summary> | ||
1068 | /// Displayed when the whitespace preceding a node is wrong. | 1117 | /// Displayed when the whitespace preceding a node is wrong. |
1069 | /// </summary> | 1118 | /// </summary> |
1070 | WhitespacePrecedingNodeWrong, | 1119 | WhitespacePrecedingNodeWrong, |
1071 | 1120 | ||
1072 | /// <summary> | 1121 | /// <summary> |
1073 | /// Displayed when an element is not empty as it should be. | 1122 | /// Displayed when the whitespace preceding an end element is wrong. |
1074 | /// </summary> | ||
1075 | NotEmptyElement, | ||
1076 | |||
1077 | /// <summary> | ||
1078 | /// Displayed when the whitespace following a CDATA node is wrong. | ||
1079 | /// </summary> | 1123 | /// </summary> |
1080 | WhitespaceFollowingCDATAWrong, | 1124 | WhitespacePrecedingEndElementWrong, |
1081 | 1125 | ||
1082 | /// <summary> | 1126 | /// <summary> |
1083 | /// Displayed when the whitespace preceding an end element is wrong. | 1127 | /// Displayed when the XML declaration is present in the source file. |
1084 | /// </summary> | 1128 | /// </summary> |
1085 | WhitespacePrecedingEndElementWrong, | 1129 | DeclarationPresent, |
1086 | 1130 | ||
1087 | /// <summary> | 1131 | /// <summary> |
1088 | /// Displayed when the xmlns attribute is missing from the document element. | 1132 | /// Displayed when the xmlns attribute is missing from the document element. |
@@ -1155,11 +1199,6 @@ namespace WixToolset.Converters | |||
1155 | AutoGuidUnnecessary, | 1199 | AutoGuidUnnecessary, |
1156 | 1200 | ||
1157 | /// <summary> | 1201 | /// <summary> |
1158 | /// Displayed when the XML declaration is present in the source file. | ||
1159 | /// </summary> | ||
1160 | DeclarationPresent, | ||
1161 | |||
1162 | /// <summary> | ||
1163 | /// The Feature Absent attribute renamed to AllowAbsent. | 1202 | /// The Feature Absent attribute renamed to AllowAbsent. |
1164 | /// </summary> | 1203 | /// </summary> |
1165 | FeatureAbsentAttributeReplaced, | 1204 | FeatureAbsentAttributeReplaced, |
diff --git a/src/test/WixToolsetTest.Converters/ConverterFixture.cs b/src/test/WixToolsetTest.Converters/ConverterFixture.cs index 6e2ad2c5..cf89ba7e 100644 --- a/src/test/WixToolsetTest.Converters/ConverterFixture.cs +++ b/src/test/WixToolsetTest.Converters/ConverterFixture.cs | |||
@@ -40,110 +40,6 @@ namespace WixToolsetTest.Converters | |||
40 | } | 40 | } |
41 | 41 | ||
42 | [Fact] | 42 | [Fact] |
43 | public void CanFixWhitespace() | ||
44 | { | ||
45 | var parse = String.Join(Environment.NewLine, | ||
46 | "<?xml version='1.0' encoding='utf-8'?>", | ||
47 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
48 | " <Fragment>", | ||
49 | " <Property Id='Prop'", | ||
50 | " Value='Val'>", | ||
51 | " </Property>", | ||
52 | " </Fragment>", | ||
53 | "</Wix>"); | ||
54 | |||
55 | var expected = String.Join(Environment.NewLine, | ||
56 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
57 | " <Fragment>", | ||
58 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
59 | " </Fragment>", | ||
60 | "</Wix>"); | ||
61 | |||
62 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
63 | |||
64 | var messaging = new MockMessaging(); | ||
65 | var converter = new WixConverter(messaging, 4, null, null); | ||
66 | |||
67 | var errors = converter.ConvertDocument(document); | ||
68 | |||
69 | var actual = UnformattedDocumentString(document); | ||
70 | |||
71 | Assert.Equal(expected, actual); | ||
72 | Assert.Equal(5, errors); | ||
73 | } | ||
74 | |||
75 | [Fact] | ||
76 | public void CanPreserveNewLines() | ||
77 | { | ||
78 | var parse = String.Join(Environment.NewLine, | ||
79 | "<?xml version='1.0' encoding='utf-8'?>", | ||
80 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
81 | " <Fragment>", | ||
82 | "", | ||
83 | " <Property Id='Prop' Value='Val' />", | ||
84 | "", | ||
85 | " </Fragment>", | ||
86 | "</Wix>"); | ||
87 | |||
88 | var expected = String.Join(Environment.NewLine, | ||
89 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
90 | " <Fragment>", | ||
91 | "", | ||
92 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
93 | "", | ||
94 | " </Fragment>", | ||
95 | "</Wix>"); | ||
96 | |||
97 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
98 | |||
99 | var messaging = new MockMessaging(); | ||
100 | var converter = new WixConverter(messaging, 4, null, null); | ||
101 | |||
102 | var conversions = converter.ConvertDocument(document); | ||
103 | |||
104 | var actual = UnformattedDocumentString(document); | ||
105 | |||
106 | Assert.Equal(expected, actual); | ||
107 | Assert.Equal(4, conversions); | ||
108 | } | ||
109 | |||
110 | [Fact] | ||
111 | public void CanConvertWithNewLineAtEndOfFile() | ||
112 | { | ||
113 | var parse = String.Join(Environment.NewLine, | ||
114 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
115 | " <Fragment>", | ||
116 | "", | ||
117 | " <Property Id='Prop' Value='Val' />", | ||
118 | "", | ||
119 | " </Fragment>", | ||
120 | "</Wix>", | ||
121 | ""); | ||
122 | |||
123 | var expected = String.Join(Environment.NewLine, | ||
124 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
125 | " <Fragment>", | ||
126 | "", | ||
127 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
128 | "", | ||
129 | " </Fragment>", | ||
130 | "</Wix>", | ||
131 | ""); | ||
132 | |||
133 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
134 | |||
135 | var messaging = new MockMessaging(); | ||
136 | var converter = new WixConverter(messaging, 4, null, null); | ||
137 | |||
138 | var conversions = converter.ConvertDocument(document); | ||
139 | |||
140 | var actual = UnformattedDocumentString(document); | ||
141 | |||
142 | Assert.Equal(expected, actual); | ||
143 | Assert.Equal(3, conversions); | ||
144 | } | ||
145 | |||
146 | [Fact] | ||
147 | public void CanConvertMainNamespace() | 43 | public void CanConvertMainNamespace() |
148 | { | 44 | { |
149 | var parse = String.Join(Environment.NewLine, | 45 | var parse = String.Join(Environment.NewLine, |
diff --git a/src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs b/src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs index 5eaeb985..79cc3f69 100644 --- a/src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs +++ b/src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs | |||
@@ -84,7 +84,7 @@ namespace WixToolsetTest.Converters | |||
84 | var settingsFile = Path.Combine(folder, "wixcop.settings.xml"); | 84 | var settingsFile = Path.Combine(folder, "wixcop.settings.xml"); |
85 | 85 | ||
86 | var result = RunConversion(targetFile, settingsFile: settingsFile); | 86 | var result = RunConversion(targetFile, settingsFile: settingsFile); |
87 | Assert.Equal(2, result.ExitCode); | 87 | Assert.Equal(7, result.ExitCode); |
88 | 88 | ||
89 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | 89 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); |
90 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); | 90 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); |
@@ -108,7 +108,7 @@ namespace WixToolsetTest.Converters | |||
108 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); | 108 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); |
109 | 109 | ||
110 | var result = RunConversion(targetFile); | 110 | var result = RunConversion(targetFile); |
111 | Assert.Equal(2, result.ExitCode); | 111 | Assert.Equal(10, result.ExitCode); |
112 | 112 | ||
113 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | 113 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); |
114 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); | 114 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); |
@@ -133,7 +133,7 @@ namespace WixToolsetTest.Converters | |||
133 | 133 | ||
134 | var result = RunConversion(targetFile); | 134 | var result = RunConversion(targetFile); |
135 | 135 | ||
136 | Assert.Equal(2, result.ExitCode); | 136 | Assert.Equal(10, result.ExitCode); |
137 | Assert.Single(result.Messages.Where(message => message.ToString().EndsWith("(QtExecCmdTimeoutAmbiguous)"))); | 137 | Assert.Single(result.Messages.Where(message => message.ToString().EndsWith("(QtExecCmdTimeoutAmbiguous)"))); |
138 | 138 | ||
139 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | 139 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); |
@@ -142,7 +142,7 @@ namespace WixToolsetTest.Converters | |||
142 | 142 | ||
143 | // still fails because QtExecCmdTimeoutAmbiguous is unfixable | 143 | // still fails because QtExecCmdTimeoutAmbiguous is unfixable |
144 | var result2 = RunConversion(targetFile); | 144 | var result2 = RunConversion(targetFile); |
145 | Assert.Equal(2, result2.ExitCode); | 145 | Assert.Equal(1, result2.ExitCode); |
146 | } | 146 | } |
147 | } | 147 | } |
148 | 148 | ||
@@ -153,7 +153,7 @@ namespace WixToolsetTest.Converters | |||
153 | var exitCode = WixRunner.Execute(new[] | 153 | var exitCode = WixRunner.Execute(new[] |
154 | { | 154 | { |
155 | "convert", | 155 | "convert", |
156 | fixErrors ? "-f" : null, | 156 | fixErrors ? null : "--dry-run", |
157 | String.IsNullOrEmpty(settingsFile) ? null : "-set1" + settingsFile, | 157 | String.IsNullOrEmpty(settingsFile) ? null : "-set1" + settingsFile, |
158 | targetFile | 158 | targetFile |
159 | }, serviceProvider, out var messages); | 159 | }, serviceProvider, out var messages); |
diff --git a/src/test/WixToolsetTest.Converters/FormatFixture.cs b/src/test/WixToolsetTest.Converters/FormatFixture.cs new file mode 100644 index 00000000..739fba66 --- /dev/null +++ b/src/test/WixToolsetTest.Converters/FormatFixture.cs | |||
@@ -0,0 +1,117 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolsetTest.Converters | ||
4 | { | ||
5 | using System; | ||
6 | using System.Xml.Linq; | ||
7 | using WixToolset.Converters; | ||
8 | using WixToolsetTest.Converters.Mocks; | ||
9 | using Xunit; | ||
10 | |||
11 | public class FormatFixture : BaseConverterFixture | ||
12 | { | ||
13 | [Fact] | ||
14 | public void CanFixWhitespace() | ||
15 | { | ||
16 | var parse = String.Join(Environment.NewLine, | ||
17 | "<?xml version='1.0' encoding='utf-8'?>", | ||
18 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
19 | " <Fragment>", | ||
20 | " <Property Id='Prop'", | ||
21 | " Value='Val'>", | ||
22 | " </Property>", | ||
23 | " </Fragment>", | ||
24 | "</Wix>"); | ||
25 | |||
26 | var expected = String.Join(Environment.NewLine, | ||
27 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
28 | " <Fragment>", | ||
29 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
30 | " </Fragment>", | ||
31 | "</Wix>"); | ||
32 | |||
33 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
34 | |||
35 | var messaging = new MockMessaging(); | ||
36 | var converter = new WixConverter(messaging, 4, null, null); | ||
37 | |||
38 | var errors = converter.FormatDocument(document); | ||
39 | |||
40 | var actual = UnformattedDocumentString(document); | ||
41 | |||
42 | Assert.Equal(expected, actual); | ||
43 | Assert.Equal(5, errors); | ||
44 | } | ||
45 | |||
46 | [Fact] | ||
47 | public void CanPreserveNewLines() | ||
48 | { | ||
49 | var parse = String.Join(Environment.NewLine, | ||
50 | "<?xml version='1.0' encoding='utf-8'?>", | ||
51 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
52 | " <Fragment>", | ||
53 | "", | ||
54 | " <Property Id='Prop' Value='Val' />", | ||
55 | "", | ||
56 | " </Fragment>", | ||
57 | "</Wix>"); | ||
58 | |||
59 | var expected = String.Join(Environment.NewLine, | ||
60 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
61 | " <Fragment>", | ||
62 | "", | ||
63 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
64 | "", | ||
65 | " </Fragment>", | ||
66 | "</Wix>"); | ||
67 | |||
68 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
69 | |||
70 | var messaging = new MockMessaging(); | ||
71 | var converter = new WixConverter(messaging, 4, null, null); | ||
72 | |||
73 | var conversions = converter.FormatDocument(document); | ||
74 | |||
75 | var actual = UnformattedDocumentString(document); | ||
76 | |||
77 | Assert.Equal(expected, actual); | ||
78 | Assert.Equal(4, conversions); | ||
79 | } | ||
80 | |||
81 | [Fact] | ||
82 | public void CanFormatWithNewLineAtEndOfFile() | ||
83 | { | ||
84 | var parse = String.Join(Environment.NewLine, | ||
85 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
86 | " <Fragment>", | ||
87 | "", | ||
88 | " <Property Id='Prop' Value='Val' />", | ||
89 | "", | ||
90 | " </Fragment>", | ||
91 | "</Wix>", | ||
92 | ""); | ||
93 | |||
94 | var expected = String.Join(Environment.NewLine, | ||
95 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
96 | " <Fragment>", | ||
97 | "", | ||
98 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
99 | "", | ||
100 | " </Fragment>", | ||
101 | "</Wix>", | ||
102 | ""); | ||
103 | |||
104 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
105 | |||
106 | var messaging = new MockMessaging(); | ||
107 | var converter = new WixConverter(messaging, 4, null, null); | ||
108 | |||
109 | var conversions = converter.FormatDocument(document); | ||
110 | |||
111 | var actual = UnformattedDocumentString(document); | ||
112 | |||
113 | Assert.Equal(expected, actual); | ||
114 | Assert.Equal(3, conversions); | ||
115 | } | ||
116 | } | ||
117 | } | ||