main
cs 217 lines 9.51 KB
Raw
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 WixInternal.TestSupport
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using System.Text;
9 using System.Xml.Linq;
10 using WixInternal.TestSupport.XunitExtensions;
11 using Xunit;
12 using Xunit.Sdk;
13
14 public class WixAssert : Assert
15 {
16 public static void CompareLineByLine(string[] expectedLines, string[] actualLines)
17 {
18 var lineNumber = 0;
19
20 for (; lineNumber < expectedLines.Length && lineNumber < actualLines.Length; ++lineNumber)
21 {
22 WixAssert.StringEqual($"{lineNumber}: {expectedLines[lineNumber]}", $"{lineNumber}: {actualLines[lineNumber]}");
23 }
24
25 var additionalExpectedLines = expectedLines.Length > lineNumber ? String.Join(Environment.NewLine, expectedLines.Skip(lineNumber).Select((s, i) => $"{lineNumber + i}: {s}")) : $"Missing {actualLines.Length - lineNumber} lines";
26 var additionalActualLines = actualLines.Length > lineNumber ? String.Join(Environment.NewLine, actualLines.Skip(lineNumber).Select((s, i) => $"{lineNumber + i}: {s}")) : $"Missing {expectedLines.Length - lineNumber} lines";
27
28 Assert.Equal<object>(additionalExpectedLines, additionalActualLines, StringObjectEqualityComparer.InvariantCulture);
29 }
30
31 public static void CompareXml(XContainer xExpected, XContainer xActual)
32 {
33 var expecteds = ComparableElements(xExpected);
34 var actuals = ComparableElements(xActual);
35
36 CompareLineByLine(expecteds.OrderBy(s => s).ToArray(), actuals.OrderBy(s => s).ToArray());
37 }
38
39 public static void CompareXml(string expectedPath, string actualPath)
40 {
41 var expectedDoc = XDocument.Load(expectedPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
42 var actualDoc = XDocument.Load(actualPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
43
44 CompareXml(expectedDoc, actualDoc);
45 }
46
47 private static IEnumerable<string> ComparableElements(XContainer container)
48 {
49 return container.Descendants().Select(x => $"{x.Name.LocalName}:{String.Join(",", x.Attributes().OrderBy(a => a.Name.LocalName).Select(a => $"{a.Name.LocalName}={ComparableAttribute(a)}"))}");
50 }
51
52 private static string ComparableAttribute(XAttribute attribute)
53 {
54 switch (attribute.Name.LocalName)
55 {
56 case "SourceFile":
57 return "<SourceFile>";
58 default:
59 return attribute.Value;
60 }
61 }
62
63 /// <summary>
64 /// Dynamically skips the test.
65 /// Requires that the test was marked with a fact attribute derived from <see cref="WixInternal.TestSupport.XunitExtensions.SkippableFactAttribute" />
66 /// or <see cref="WixInternal.TestSupport.XunitExtensions.SkippableTheoryAttribute" />
67 /// </summary>
68 public static void Skip(string message)
69 {
70 throw new SkipTestException(message);
71 }
72
73 public static void SpecificReturnCode(int hrExpected, int hr, string format, params object[] formatArgs)
74 {
75 if (hrExpected != hr)
76 {
77 throw new SpecificReturnCodeException(hrExpected, hr, String.Format(format, formatArgs));
78 }
79 }
80
81 public static void Succeeded(int hr, string format, params object[] formatArgs)
82 {
83 if (0 > hr)
84 {
85 throw new SucceededException(hr, String.Format(format, formatArgs));
86 }
87 }
88
89 public static void StringCollectionEmpty(IList<string> collection)
90 {
91 if (collection.Count > 0)
92 {
93 Assert.Fail($"The collection was expected to be empty, but instead was [{Environment.NewLine}\"{String.Join($"\", {Environment.NewLine}\"", collection)}\"{Environment.NewLine}]");
94 }
95 }
96
97 public static void StringEqual(string expected, string actual, bool ignoreCase = false)
98 {
99 WixStringEqualException.ThrowIfNotEqual(expected, actual, ignoreCase);
100 }
101
102 public static void NotStringEqual(string expected, string actual, bool ignoreCase = false)
103 {
104 var comparer = ignoreCase ? StringObjectEqualityComparer.InvariantCultureIgnoreCase : StringObjectEqualityComparer.InvariantCulture;
105 Assert.NotEqual<object>(expected, actual, comparer);
106 }
107
108 // There appears to have been a bug in VC++, which might or might not have been partially
109 // or completely corrected. It was unable to disambiguate a call to:
110 // Xunit::Assert::Throws(System::Type^, System::Action^)
111 // from a call to:
112 // Xunit::Assert::Throws(System::Type^, System::Func<System::Object^>^)
113 // that implicitly ignores its return value.
114 //
115 // The ambiguity may have been reported by some versions of the compiler and not by others.
116 // Some versions of the compiler may not have emitted any code in this situation, making it
117 // appear that the test has passed when, in fact, it hasn't been run.
118 //
119 // This situation is not an issue for C#.
120 //
121 // The following method is used to isolate DUtilTests in order to overcome the above problem.
122
123 /// <summary>
124 /// This shim allows C++/CLR code to call the Xunit method with the same signature
125 /// without getting an ambiguous overload error. If the specified test code
126 /// fails to generate an exception of the exact specified type, an assertion
127 /// exception is thrown. Otherwise, execution flow proceeds as normal.
128 /// </summary>
129 /// <typeparam name="T">The type name of the expected exception.</typeparam>
130 /// <param name="testCode">An Action delegate to run the test code.</param>
131 public static new void Throws<T>(System.Action testCode)
132 where T : System.Exception
133 {
134 Xunit.Assert.Throws<T>(testCode);
135 }
136
137 // This shim has been tested, but is not currently used anywhere. It was provided
138 // at the same time as the preceding shim because it involved the same overload
139 // resolution conflict.
140
141 /// <summary>
142 /// This shim allows C++/CLR code to call the Xunit method with the same signature
143 /// without getting an ambiguous overload error. If the specified test code
144 /// fails to generate an exception of the exact specified type, an assertion
145 /// exception is thrown. Otherwise, execution flow proceeds as normal.
146 /// </summary>
147 /// <param name="exceptionType">The type object associated with exceptions of the expected type.</param>
148 /// <param name="testCode">An Action delegate to run the test code.</param>
149 /// <returns>An exception of a type other than the type specified, is such an exception is thrown.</returns>
150 public static new System.Exception Throws(System.Type exceptionType, System.Action testCode)
151 {
152 return Xunit.Assert.Throws(exceptionType, testCode);
153 }
154 }
155
156 internal class StringObjectEqualityComparer : IEqualityComparer<object>
157 {
158 public static readonly StringObjectEqualityComparer InvariantCultureIgnoreCase = new StringObjectEqualityComparer(true);
159 public static readonly StringObjectEqualityComparer InvariantCulture = new StringObjectEqualityComparer(false);
160
161 private readonly StringComparer stringComparer;
162
163 public StringObjectEqualityComparer(bool ignoreCase)
164 {
165 this.stringComparer = ignoreCase ? StringComparer.InvariantCultureIgnoreCase : StringComparer.InvariantCulture;
166 }
167
168 public new bool Equals(object x, object y)
169 {
170 return this.stringComparer.Equals((string)x, (string)y);
171 }
172
173 public int GetHashCode(object obj)
174 {
175 return this.stringComparer.GetHashCode((string)obj);
176 }
177 }
178
179 public class WixStringEqualException : XunitException
180 {
181 public WixStringEqualException(string userMessage) : base(userMessage) { }
182
183 public static void ThrowIfNotEqual(string expected, string actual, bool ignoreCase)
184 {
185 var comparer = ignoreCase ? StringObjectEqualityComparer.InvariantCultureIgnoreCase : StringObjectEqualityComparer.InvariantCulture;
186 if (comparer.Equals(expected, actual))
187 {
188 return;
189 }
190
191 var sbMessage = new StringBuilder();
192
193 try
194 {
195 Assert.Equal(expected, actual, ignoreCase);
196 }
197 catch (XunitException xe)
198 {
199 // If either string is not completely in the message, then make sure it gets in there.
200 if (!xe.Message.Contains(expected) || !xe.Message.Contains(actual))
201 {
202 sbMessage.AppendLine(xe.Message);
203 sbMessage.AppendLine();
204 sbMessage.AppendFormat("Expected: {0}", expected);
205 sbMessage.AppendLine();
206 sbMessage.AppendFormat("Actual: {0}", actual);
207 }
208 else
209 {
210 throw;
211 }
212 }
213
214 throw new WixStringEqualException(sbMessage.ToString());
215 }
216 }
217 }