From 8c8e03138988abebefaa83000500b77a98c7d084 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 18 Aug 2020 11:29:51 -0700 Subject: [PATCH 01/23] Add crash tests --- eng/helix/content/RunTests/TestRunner.cs | 18 +- src/Identity/src/Common/AssemblyResolution.cs | 58 ++ src/Identity/src/Common/AssemblyResolver.cs | 109 ++++ src/Identity/src/Common/BuildTask.Desktop.cs | 16 + src/Identity/src/Common/BuildTask.cs | 152 ++++++ src/Identity/src/Common/ExponentialRetry.cs | 74 +++ .../src/Common/TestUtilities/AssertEx.cs | 502 ++++++++++++++++++ .../src/Common/TestUtilities/DiffUtil.cs | 272 ++++++++++ .../src/Common/TestUtilities/MockEngine.cs | 122 +++++ .../src/ProcessUtilities/ProcessUtil.cs | 198 +++++++ .../ProcessUtilities/ProcessUtilities.csproj | 7 + .../src/ProcessWithAv/ProcessWithAv.csproj | 18 + src/Identity/src/ProcessWithAv/Program.cs | 18 + .../ProcessWithCrash/ProcessWithCrash.csproj | 18 + src/Identity/src/ProcessWithCrash/Program.cs | 29 + .../ProcessWithHang/ProcessWithHang.csproj | 18 + src/Identity/src/ProcessWithHang/Program.cs | 13 + .../Helix1.HangTestRunner.Test.csproj | 14 + .../Helix1.HangTestRunner.Test/UnitTest1.cs | 29 + .../Helix2.LogOutput.Test.csproj | 10 + .../test/Helix2.LogOutput.Test/UnitTest1.cs | 34 ++ .../Helix3.AsyncTimeout.Test.csproj | 14 + .../TaskExtensions.cs | 133 +++++ .../Helix3.AsyncTimeout.Test/UnitTest1.cs | 31 ++ .../Helix4.CrashTestRunner.Test.csproj | 14 + .../Helix4.CrashTestRunner.Test/UnitTest1.cs | 50 ++ .../Helix5.Segfault.Test.csproj | 15 + .../test/Helix5.Segfault.Test/UnitTest1.cs | 17 + .../Helix6.OutOfProcessHang.Test.csproj | 16 + .../Helix6.OutOfProcessHang.Test/UnitTest1.cs | 31 ++ .../Helix7.ProcessWithAv.Test.csproj | 14 + .../Helix7.ProcessWithAv.Test/UnitTest1.cs | 29 + .../Helix8.OutOfProcessCrash.Test.csproj | 15 + .../UnitTest1.cs | 29 + .../Helix9.FailFast.Test.csproj | 14 + .../test/Helix9.FailFast.Test/UnitTest1.cs | 20 + 36 files changed, 2167 insertions(+), 4 deletions(-) create mode 100644 src/Identity/src/Common/AssemblyResolution.cs create mode 100644 src/Identity/src/Common/AssemblyResolver.cs create mode 100644 src/Identity/src/Common/BuildTask.Desktop.cs create mode 100644 src/Identity/src/Common/BuildTask.cs create mode 100644 src/Identity/src/Common/ExponentialRetry.cs create mode 100644 src/Identity/src/Common/TestUtilities/AssertEx.cs create mode 100644 src/Identity/src/Common/TestUtilities/DiffUtil.cs create mode 100644 src/Identity/src/Common/TestUtilities/MockEngine.cs create mode 100644 src/Identity/src/ProcessUtilities/ProcessUtil.cs create mode 100644 src/Identity/src/ProcessUtilities/ProcessUtilities.csproj create mode 100644 src/Identity/src/ProcessWithAv/ProcessWithAv.csproj create mode 100644 src/Identity/src/ProcessWithAv/Program.cs create mode 100644 src/Identity/src/ProcessWithCrash/ProcessWithCrash.csproj create mode 100644 src/Identity/src/ProcessWithCrash/Program.cs create mode 100644 src/Identity/src/ProcessWithHang/ProcessWithHang.csproj create mode 100644 src/Identity/src/ProcessWithHang/Program.cs create mode 100644 src/Identity/test/Helix1.HangTestRunner.Test/Helix1.HangTestRunner.Test.csproj create mode 100644 src/Identity/test/Helix1.HangTestRunner.Test/UnitTest1.cs create mode 100644 src/Identity/test/Helix2.LogOutput.Test/Helix2.LogOutput.Test.csproj create mode 100644 src/Identity/test/Helix2.LogOutput.Test/UnitTest1.cs create mode 100644 src/Identity/test/Helix3.AsyncTimeout.Test/Helix3.AsyncTimeout.Test.csproj create mode 100644 src/Identity/test/Helix3.AsyncTimeout.Test/TaskExtensions.cs create mode 100644 src/Identity/test/Helix3.AsyncTimeout.Test/UnitTest1.cs create mode 100644 src/Identity/test/Helix4.CrashTestRunner.Test/Helix4.CrashTestRunner.Test.csproj create mode 100644 src/Identity/test/Helix4.CrashTestRunner.Test/UnitTest1.cs create mode 100644 src/Identity/test/Helix5.Segfault.Test/Helix5.Segfault.Test.csproj create mode 100644 src/Identity/test/Helix5.Segfault.Test/UnitTest1.cs create mode 100644 src/Identity/test/Helix6.OutOfProcessHang.Test/Helix6.OutOfProcessHang.Test.csproj create mode 100644 src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs create mode 100644 src/Identity/test/Helix7.ProcessWithAv.Test/Helix7.ProcessWithAv.Test.csproj create mode 100644 src/Identity/test/Helix7.ProcessWithAv.Test/UnitTest1.cs create mode 100644 src/Identity/test/Helix8.OutOfProcessCrash.Test/Helix8.OutOfProcessCrash.Test.csproj create mode 100644 src/Identity/test/Helix8.OutOfProcessCrash.Test/UnitTest1.cs create mode 100644 src/Identity/test/Helix9.FailFast.Test/Helix9.FailFast.Test.csproj create mode 100644 src/Identity/test/Helix9.FailFast.Test/UnitTest1.cs diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index f99c2769f0e2..baea724f3705 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -312,9 +312,7 @@ public void UploadResults() // Combine the directory name + log name for the copied log file name to avoid overwriting duplicate test names in different test projects var logName = $"{Path.GetFileName(Path.GetDirectoryName(file))}_{Path.GetFileName(file)}"; Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, logName)}"); - // Need to copy to HELIX_WORKITEM_UPLOAD_ROOT and HELIX_WORKITEM_UPLOAD_ROOT/../ in order for Azure Devops attachments to link properly and for Helix to store the logs File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, logName)); - File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, "..", logName)); } } else @@ -328,15 +326,27 @@ public void UploadResults() { var fileName = Path.GetFileName(file); Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)}"); - // Need to copy to HELIX_WORKITEM_UPLOAD_ROOT and HELIX_WORKITEM_UPLOAD_ROOT/../ in order for Azure Devops attachments to link properly and for Helix to store the logs File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)); - File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, "..", fileName)); } } else { Console.WriteLine("No dmps found in TestResults"); } + Console.WriteLine($"Copying TestResults/**/*.trx to {HELIX_WORKITEM_UPLOAD_ROOT}/"); + if (Directory.Exists("TestResults")) + { + foreach (var file in Directory.EnumerateFiles("TestResults", "*.trx", SearchOption.AllDirectories)) + { + var fileName = Path.GetFileName(file); + Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)}"); + File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)); + } + } + else + { + Console.WriteLine("No trx found in TestResults"); + } } } } diff --git a/src/Identity/src/Common/AssemblyResolution.cs b/src/Identity/src/Common/AssemblyResolution.cs new file mode 100644 index 000000000000..09673ce631e4 --- /dev/null +++ b/src/Identity/src/Common/AssemblyResolution.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET472 + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet +{ + internal static class AssemblyResolution + { + internal static TaskLoggingHelper Log; + + public static void Initialize() + { + AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve; + } + + private static Assembly AssemblyResolve(object sender, ResolveEventArgs args) + { + var name = new AssemblyName(args.Name); + + if (!name.Name.Equals("System.Collections.Immutable", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + var fullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "System.Collections.Immutable.dll"); + + Assembly sci; + try + { + sci = Assembly.LoadFile(fullPath); + } + catch (Exception e) + { + Log?.LogWarning($"AssemblyResolve: exception while loading '{fullPath}': {e.Message}"); + return null; + } + + if (name.Version <= sci.GetName().Version) + { + Log?.LogMessage(MessageImportance.Low, $"AssemblyResolve: loaded '{fullPath}' to {AppDomain.CurrentDomain.FriendlyName}"); + return sci; + } + + return null; + } + } +} + +#endif diff --git a/src/Identity/src/Common/AssemblyResolver.cs b/src/Identity/src/Common/AssemblyResolver.cs new file mode 100644 index 000000000000..8f266c2e61df --- /dev/null +++ b/src/Identity/src/Common/AssemblyResolver.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace Microsoft.DotNet.Build.Common.Desktop +{ + /// + /// Used to enable app-local assembly unification. + /// + internal static class AssemblyResolver + { + static AssemblyResolver() + { + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + } + + /// + /// Call to enable the assembly resolver for the current AppDomain. + /// + public static void Enable() + { + // intentionally empty. This is just meant to ensure the static constructor + // has run. + } + + private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + // apply any existing policy + AssemblyName referenceName = new AssemblyName(AppDomain.CurrentDomain.ApplyPolicy(args.Name)); + + string fileName = referenceName.Name + ".dll"; + string assemblyPath = null; + string probingPath = null; + Assembly assm = null; + + // look next to requesting assembly + assemblyPath = args.RequestingAssembly?.Location; + if (!String.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + Debug.WriteLine($"Considering {probingPath} based on RequestingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look next to the executing assembly + assemblyPath = Assembly.GetExecutingAssembly().Location; + if (!String.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + + Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look in AppDomain base directory + probingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + Debug.WriteLine($"Considering {probingPath} based on BaseDirectory"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + + // look in current directory + Debug.WriteLine($"Considering {fileName}"); + if (Probe(fileName, referenceName.Version, out assm)) + { + return assm; + } + + return null; + } + + /// + /// Considers a path to load for satisfying an assembly ref and loads it + /// if the file exists and version is sufficient. + /// + /// Path to consider for load + /// Minimum version to consider + /// loaded assembly + /// true if assembly was loaded + private static bool Probe(string filePath, Version minimumVersion, out Assembly assembly) + { + if (File.Exists(filePath)) + { + AssemblyName name = AssemblyName.GetAssemblyName(filePath); + + if (name.Version >= minimumVersion) + { + assembly = Assembly.Load(name); + return true; + } + } + + assembly = null; + return false; + } + } +} diff --git a/src/Identity/src/Common/BuildTask.Desktop.cs b/src/Identity/src/Common/BuildTask.Desktop.cs new file mode 100644 index 000000000000..5dac62182594 --- /dev/null +++ b/src/Identity/src/Common/BuildTask.Desktop.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.DotNet.Build.Common.Desktop; + +namespace Microsoft.DotNet.Build.Tasks +{ + public partial class BuildTask + { + static BuildTask() + { + AssemblyResolver.Enable(); + } + } +} diff --git a/src/Identity/src/Common/BuildTask.cs b/src/Identity/src/Common/BuildTask.cs new file mode 100644 index 000000000000..e497ade909e5 --- /dev/null +++ b/src/Identity/src/Common/BuildTask.cs @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; + +namespace Microsoft.DotNet.Build.Tasks +{ + public abstract partial class BuildTask : ITask + { + private Log _log = null; + + internal Log Log + { + get { return _log ?? (_log = new Log(new TaskLoggingHelper(this))); } + } + + public BuildTask() + { + } + + public IBuildEngine BuildEngine + { + get; + set; + } + + public ITaskHost HostObject + { + get; + set; + } + + public abstract bool Execute(); + } + + internal class Log : ILog + { + private readonly TaskLoggingHelper _logger; + public Log(TaskLoggingHelper logger) + { + _logger = logger; + } + + public void LogError(string message, params object[] messageArgs) + { + _logger.LogError(message, messageArgs); + } + + public void LogErrorFromException(Exception exception, bool showStackTrace) + { + _logger.LogErrorFromException(exception, showStackTrace); + } + + public void LogMessage(string message, params object[] messageArgs) + { + _logger.LogMessage(message, messageArgs); + } + + public void LogMessage(LogImportance importance, string message, params object[] messageArgs) + { + _logger.LogMessage((MessageImportance)importance, message, messageArgs); + } + + public void LogWarning(string message, params object[] messageArgs) + { + _logger.LogWarning(message, messageArgs); + } + + public bool HasLoggedErrors { get { return _logger.HasLoggedErrors; } } + } + + public enum LogImportance + { + Low = MessageImportance.Low, + Normal = MessageImportance.Normal, + High = MessageImportance.High + } + + + public interface ILog + { + // + // Summary: + // Logs an error with the specified message. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // Optional arguments for formatting the message string. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogError(string message, params object[] messageArgs); + + // + // Summary: + // Logs a message with the specified string. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // The arguments for formatting the message. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogMessage(string message, params object[] messageArgs); + + // + // Summary: + // Logs a message with the specified string and importance. + // + // Parameters: + // importance: + // One of the enumeration values that specifies the importance of the message. + // + // message: + // The message. + // + // messageArgs: + // The arguments for formatting the message. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogMessage(LogImportance importance, string message, params object[] messageArgs); + + // + // Summary: + // Logs a warning with the specified message. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // Optional arguments for formatting the message string. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogWarning(string message, params object[] messageArgs); + } +} diff --git a/src/Identity/src/Common/ExponentialRetry.cs b/src/Identity/src/Common/ExponentialRetry.cs new file mode 100644 index 000000000000..984d2b7dc760 --- /dev/null +++ b/src/Identity/src/Common/ExponentialRetry.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.DotNet.VersionTools.Util +{ + public class ExponentialRetry + { + private Random _random = new Random(); + + public int MaxAttempts { get; set; } = 10; + + /// + /// Base, in seconds, raised to the power of the number of retries so far. + /// + public double DelayBase { get; set; } = 6; + + /// + /// A constant, in seconds, added to (base^retries) to find the delay before retrying. + /// + /// The default is -1 to make the first retry instant, because ((base^0)-1) == 0. + /// + public double DelayConstant { get; set; } = -1; + + public double MinRandomFactor { get; set; } = 0.5; + public double MaxRandomFactor { get; set; } = 1.0; + + public CancellationToken DefaultCancellationToken { get; set; } = CancellationToken.None; + + public Task RunAsync(Func> actionSuccessfulAsync) + { + return RunAsync(actionSuccessfulAsync, DefaultCancellationToken); + } + + public async Task RunAsync( + Func> actionSuccessfulAsync, + CancellationToken cancellationToken) + { + for (int i = 0; i < MaxAttempts; i++) + { + string attempt = $"Attempt {i + 1}/{MaxAttempts}"; + Trace.TraceInformation(attempt); + + if (await actionSuccessfulAsync(i)) + { + return true; + } + + double randomFactor = + _random.NextDouble() * (MaxRandomFactor - MinRandomFactor) + MinRandomFactor; + + TimeSpan delay = TimeSpan.FromSeconds( + (Math.Pow(DelayBase, i) + DelayConstant) * randomFactor); + + Trace.TraceInformation($"{attempt} failed. Waiting {delay} before next try."); + + try + { + await Task.Delay(delay, cancellationToken); + } + catch (TaskCanceledException) + { + break; + } + } + return false; + } + } +} diff --git a/src/Identity/src/Common/TestUtilities/AssertEx.cs b/src/Identity/src/Common/TestUtilities/AssertEx.cs new file mode 100644 index 000000000000..26e92367065e --- /dev/null +++ b/src/Identity/src/Common/TestUtilities/AssertEx.cs @@ -0,0 +1,502 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using Xunit; + +namespace TestUtilities +{ + /// + /// Assert style type to deal with the lack of features in xUnit's Assert type + /// + public static class AssertEx + { + #region AssertEqualityComparer + + private class AssertEqualityComparer : IEqualityComparer + { + private static readonly IEqualityComparer s_instance = new AssertEqualityComparer(); + + private static bool CanBeNull() + { + var type = typeof(T); + return !type.GetTypeInfo().IsValueType || + (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)); + } + + public static bool IsNull(T @object) + { + if (!CanBeNull()) + { + return false; + } + + return object.Equals(@object, default(T)); + } + + public static bool Equals(T left, T right) + { + return s_instance.Equals(left, right); + } + + bool IEqualityComparer.Equals(T x, T y) + { + if (CanBeNull()) + { + if (object.Equals(x, default(T))) + { + return object.Equals(y, default(T)); + } + + if (object.Equals(y, default(T))) + { + return false; + } + } + + if (x.GetType() != y.GetType()) + { + return false; + } + + var equatable = x as IEquatable; + if (equatable != null) + { + return equatable.Equals(y); + } + + var comparableT = x as IComparable; + if (comparableT != null) + { + return comparableT.CompareTo(y) == 0; + } + + var comparable = x as IComparable; + if (comparable != null) + { + return comparable.CompareTo(y) == 0; + } + + var enumerableX = x as IEnumerable; + var enumerableY = y as IEnumerable; + + if (enumerableX != null && enumerableY != null) + { + var enumeratorX = enumerableX.GetEnumerator(); + var enumeratorY = enumerableY.GetEnumerator(); + + while (true) + { + bool hasNextX = enumeratorX.MoveNext(); + bool hasNextY = enumeratorY.MoveNext(); + + if (!hasNextX || !hasNextY) + { + return hasNextX == hasNextY; + } + + if (!Equals(enumeratorX.Current, enumeratorY.Current)) + { + return false; + } + } + } + + return object.Equals(x, y); + } + + int IEqualityComparer.GetHashCode(T obj) + { + throw new NotImplementedException(); + } + } + + #endregion + + public static void AreEqual(T expected, T actual, string message = null, IEqualityComparer comparer = null) + { + if (ReferenceEquals(expected, actual)) + { + return; + } + + if (expected == null) + { + Fail("expected was null, but actual wasn't\r\n" + message); + } + else if (actual == null) + { + Fail("actual was null, but expected wasn't\r\n" + message); + } + else + { + if (!(comparer != null ? + comparer.Equals(expected, actual) : + AssertEqualityComparer.Equals(expected, actual))) + { + Fail("Expected and actual were different.\r\n" + + "Expected: " + expected + "\r\n" + + "Actual: " + actual + "\r\n" + + message); + } + } + } + + public static void Equal(ImmutableArray expected, IEnumerable actual, Func comparer = null, string message = null) + { + if (actual == null || expected.IsDefault) + { + Assert.True((actual == null) == expected.IsDefault, message); + } + else + { + Equal((IEnumerable)expected, actual, comparer, message); + } + } + + public static void Equal(IEnumerable expected, ImmutableArray actual, Func comparer = null, string message = null, string itemSeparator = null) + { + if (expected == null || actual.IsDefault) + { + Assert.True((expected == null) == actual.IsDefault, message); + } + else + { + Equal(expected, (IEnumerable)actual, comparer, message, itemSeparator); + } + } + + public static void Equal(ImmutableArray expected, ImmutableArray actual, Func comparer = null, string message = null, string itemSeparator = null) + { + Equal(expected, (IEnumerable)actual, comparer, message, itemSeparator); + } + + public static void Equal(IEnumerable expected, IEnumerable actual, Func comparer = null, string message = null, + string itemSeparator = null, Func itemInspector = null) + { + if (ReferenceEquals(expected, actual)) + { + return; + } + + if (expected == null) + { + Fail("expected was null, but actual wasn't\r\n" + message); + } + else if (actual == null) + { + Fail("actual was null, but expected wasn't\r\n" + message); + } + else if (!SequenceEqual(expected, actual, comparer)) + { + string assertMessage = GetAssertMessage(expected, actual, comparer, itemInspector, itemSeparator); + + if (message != null) + { + assertMessage = message + "\r\n" + assertMessage; + } + + Assert.True(false, assertMessage); + } + } + + private static bool SequenceEqual(IEnumerable expected, IEnumerable actual, Func comparer = null) + { + var enumerator1 = expected.GetEnumerator(); + var enumerator2 = actual.GetEnumerator(); + + while (true) + { + var hasNext1 = enumerator1.MoveNext(); + var hasNext2 = enumerator2.MoveNext(); + + if (hasNext1 != hasNext2) + { + return false; + } + + if (!hasNext1) + { + break; + } + + var value1 = enumerator1.Current; + var value2 = enumerator2.Current; + + if (!(comparer != null ? comparer(value1, value2) : AssertEqualityComparer.Equals(value1, value2))) + { + return false; + } + } + + return true; + } + + public static void SetEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer comparer = null, string message = null, string itemSeparator = "\r\n") + { + var expectedSet = new HashSet(expected, comparer); + var result = expected.Count() == actual.Count() && expectedSet.SetEquals(actual); + if (!result) + { + if (string.IsNullOrEmpty(message)) + { + message = GetAssertMessage(expected, actual); + } + + Assert.True(result, message); + } + } + + public static void SetEqual(IEnumerable actual, params T[] expected) + { + var expectedSet = new HashSet(expected); + Assert.True(expectedSet.SetEquals(actual), string.Format("Expected: {0}\nActual: {1}", ToString(expected), ToString(actual))); + } + + public static void None(IEnumerable actual, Func predicate) + { + var none = !actual.Any(predicate); + if (!none) + { + Assert.True(none, string.Format( + "Unexpected item found among existing items: {0}\nExisting items: {1}", + ToString(actual.First(predicate)), + ToString(actual))); + } + } + + public static void Any(IEnumerable actual, Func predicate) + { + var any = actual.Any(predicate); + Assert.True(any, string.Format("No expected item was found.\nExisting items: {0}", ToString(actual))); + } + + public static void All(IEnumerable actual, Func predicate) + { + var all = actual.All(predicate); + if (!all) + { + Assert.True(all, string.Format( + "Not all items satisfy condition:\n{0}", + ToString(actual.Where(i => !predicate(i))))); + } + } + + public static string ToString(object o) + { + return Convert.ToString(o); + } + + public static string ToString(IEnumerable list, string separator = ", ", Func itemInspector = null) + { + if (itemInspector == null) + { + itemInspector = i => Convert.ToString(i); + } + + return string.Join(separator, list.Select(itemInspector)); + } + + public static void Fail(string message) + { + Assert.False(true, message); + } + + public static void Fail(string format, params object[] args) + { + Assert.False(true, string.Format(format, args)); + } + + public static void Null(T @object, string message = null) + { + Assert.True(AssertEqualityComparer.IsNull(@object), message); + } + + public static void NotNull(T @object, string message = null) + { + Assert.False(AssertEqualityComparer.IsNull(@object), message); + } + + // compares against a baseline + public static void AssertEqualToleratingWhitespaceDifferences( + string expected, + string actual, + bool escapeQuotes = true, + [CallerFilePath]string expectedValueSourcePath = null, + [CallerLineNumber]int expectedValueSourceLine = 0) + { + if (!EqualIgnoringWhitespace(expected, actual)) + { + Assert.True(false, GetAssertMessage(expected, actual, escapeQuotes, expectedValueSourcePath, expectedValueSourceLine)); + } + } + + public static bool EqualIgnoringWhitespace(string left, string right) + => NormalizeWhitespace(left) == NormalizeWhitespace(right); + + public static void ThrowsArgumentNull(string parameterName, Action del) + { + try + { + del(); + } + catch (ArgumentNullException e) + { + Assert.Equal(parameterName, e.ParamName); + } + } + + public static void ThrowsArgumentException(string parameterName, Action del) + { + try + { + del(); + } + catch (ArgumentException e) + { + Assert.Equal(parameterName, e.ParamName); + } + } + + public static T Throws(Action del, bool allowDerived = false) where T : Exception + { + try + { + del(); + } + catch (Exception ex) + { + var type = ex.GetType(); + if (type.Equals(typeof(T))) + { + // We got exactly the type we wanted + return (T)ex; + } + + if (allowDerived && typeof(T).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + { + // We got a derived type + return (T)ex; + } + + // We got some other type. We know that type != typeof(T), and so we'll use Assert.Equal since Xunit + // will give a nice Expected/Actual output for this + Assert.Equal(typeof(T), type); + } + + throw new Exception("No exception was thrown."); + } + + internal static string NormalizeWhitespace(string input) + { + var output = new StringBuilder(); + var inputLines = input.Split('\n', '\r'); + foreach (var line in inputLines) + { + var trimmedLine = line.Trim(); + if (trimmedLine.Length > 0) + { + if (!(trimmedLine[0] == '{' || trimmedLine[0] == '}')) + { + output.Append(" "); + } + + output.AppendLine(trimmedLine); + } + } + + return output.ToString(); + } + + public static string GetAssertMessage(string expected, string actual, bool escapeQuotes = false, string expectedValueSourcePath = null, int expectedValueSourceLine = 0) + { + return GetAssertMessage(DiffUtil.Lines(expected), DiffUtil.Lines(actual), escapeQuotes, expectedValueSourcePath, expectedValueSourceLine); + } + + public static string GetAssertMessage(IEnumerable expected, IEnumerable actual, bool escapeQuotes, string expectedValueSourcePath = null, int expectedValueSourceLine = 0) + { + Func itemInspector = escapeQuotes ? new Func(t => t.ToString().Replace("\"", "\"\"")) : null; + return GetAssertMessage(expected, actual, itemInspector: itemInspector, itemSeparator: "\r\n", expectedValueSourcePath: expectedValueSourcePath, expectedValueSourceLine: expectedValueSourceLine); + } + + public static string GetAssertMessage( + IEnumerable expected, + IEnumerable actual, + Func comparer = null, + Func itemInspector = null, + string itemSeparator = null, + string expectedValueSourcePath = null, + int expectedValueSourceLine = 0) + { + if (itemInspector == null) + { + if (expected is IEnumerable) + { + itemInspector = b => $"0x{b:X2}"; + } + else + { + itemInspector = new Func(obj => (obj != null) ? obj.ToString() : ""); + } + } + + if (itemSeparator == null) + { + if (expected is IEnumerable) + { + itemSeparator = ", "; + } + else + { + itemSeparator = ",\r\n"; + } + } + + var message = new StringBuilder(); + message.AppendLine(); + message.AppendLine("Actual:"); + message.AppendLine(string.Join(itemSeparator, actual.Select(itemInspector))); + + message.AppendLine(); + message.AppendLine("Expected:"); + message.AppendLine(string.Join(itemSeparator, expected.Select(itemInspector))); + + message.AppendLine(); + message.AppendLine("Diff:"); + message.Append(DiffUtil.DiffReport(expected, actual, comparer, itemInspector, itemSeparator)); + + return message.ToString(); + } + + public static void AssertLinesEqual(string expected, string actual, string message = null, Func comparer = null) + { + if (expected == actual) + { + return; + } + + Assert.NotNull(expected); + Assert.NotNull(actual); + + IEnumerable GetLines(string str) => + str.Trim().Replace("\r\n", "\n").Split(new[] { '\r', '\n' }, StringSplitOptions.None); + + Equal( + GetLines(expected), + GetLines(actual), + message: message, + comparer: comparer ?? new Func((left, right) => left.Trim() == right.Trim()), + itemInspector: line => line.Replace("\"", "\"\""), + itemSeparator: Environment.NewLine); + } + + } +} diff --git a/src/Identity/src/Common/TestUtilities/DiffUtil.cs b/src/Identity/src/Common/TestUtilities/DiffUtil.cs new file mode 100644 index 000000000000..a9a42007ff6d --- /dev/null +++ b/src/Identity/src/Common/TestUtilities/DiffUtil.cs @@ -0,0 +1,272 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace TestUtilities +{ + public class DiffUtil + { + private enum EditKind + { + /// + /// No change. + /// + None = 0, + + /// + /// Node value was updated. + /// + Update = 1, + + /// + /// Node was inserted. + /// + Insert = 2, + + /// + /// Node was deleted. + /// + Delete = 3, + } + + private class LCS : LongestCommonSubsequence> + { + public static readonly LCS Default = new LCS((left, right) => EqualityComparer.Default.Equals(left, right)); + + private readonly Func _comparer; + + public LCS(Func comparer) + { + _comparer = comparer; + } + + protected override bool ItemsEqual(IList sequenceA, int indexA, IList sequenceB, int indexB) + { + return _comparer(sequenceA[indexA], sequenceB[indexB]); + } + + public IEnumerable CalculateDiff(IList sequenceA, IList sequenceB, Func toString) + { + foreach (var edit in GetEdits(sequenceA, sequenceA.Count, sequenceB, sequenceB.Count).Reverse()) + { + switch (edit.Kind) + { + case EditKind.Delete: + yield return "--> " + toString(sequenceA[edit.IndexA]); + break; + + case EditKind.Insert: + yield return "++> " + toString(sequenceB[edit.IndexB]); + break; + + case EditKind.Update: + yield return " " + toString(sequenceB[edit.IndexB]); + break; + } + } + } + } + + public static string DiffReport(IEnumerable expected, IEnumerable actual, Func comparer = null, Func toString = null, string separator = ",\r\n") + { + var lcs = (comparer != null) ? new LCS(comparer) : LCS.Default; + toString = toString ?? new Func(obj => obj.ToString()); + + IList expectedList = expected as IList ?? new List(expected); + IList actualList = actual as IList ?? new List(actual); + + return string.Join(separator, lcs.CalculateDiff(expectedList, actualList, toString)); + } + + private static readonly char[] s_lineSplitChars = new[] { '\r', '\n' }; + + public static string[] Lines(string s) + { + return s.Split(s_lineSplitChars, StringSplitOptions.RemoveEmptyEntries); + } + + public static string DiffReport(string expected, string actual) + { + var exlines = Lines(expected); + var aclines = Lines(actual); + return DiffReport(exlines, aclines, separator: "\r\n"); + } + + /// + /// Calculates Longest Common Subsequence. + /// + private abstract class LongestCommonSubsequence + { + protected struct Edit + { + public readonly EditKind Kind; + public readonly int IndexA; + public readonly int IndexB; + + internal Edit(EditKind kind, int indexA, int indexB) + { + this.Kind = kind; + this.IndexA = indexA; + this.IndexB = indexB; + } + } + + private const int DeleteCost = 1; + private const int InsertCost = 1; + private const int UpdateCost = 2; + + protected abstract bool ItemsEqual(TSequence sequenceA, int indexA, TSequence sequenceB, int indexB); + + protected IEnumerable> GetMatchingPairs(TSequence sequenceA, int lengthA, TSequence sequenceB, int lengthB) + { + int[,] d = ComputeCostMatrix(sequenceA, lengthA, sequenceB, lengthB); + int i = lengthA; + int j = lengthB; + + while (i != 0 && j != 0) + { + if (d[i, j] == d[i - 1, j] + DeleteCost) + { + i--; + } + else if (d[i, j] == d[i, j - 1] + InsertCost) + { + j--; + } + else + { + i--; + j--; + yield return new KeyValuePair(i, j); + } + } + } + + protected IEnumerable GetEdits(TSequence sequenceA, int lengthA, TSequence sequenceB, int lengthB) + { + int[,] d = ComputeCostMatrix(sequenceA, lengthA, sequenceB, lengthB); + int i = lengthA; + int j = lengthB; + + while (i != 0 && j != 0) + { + if (d[i, j] == d[i - 1, j] + DeleteCost) + { + i--; + yield return new Edit(EditKind.Delete, i, -1); + } + else if (d[i, j] == d[i, j - 1] + InsertCost) + { + j--; + yield return new Edit(EditKind.Insert, -1, j); + } + else + { + i--; + j--; + yield return new Edit(EditKind.Update, i, j); + } + } + + while (i > 0) + { + i--; + yield return new Edit(EditKind.Delete, i, -1); + } + + while (j > 0) + { + j--; + yield return new Edit(EditKind.Insert, -1, j); + } + } + + /// + /// Returns a distance [0..1] of the specified sequences. + /// The smaller distance the more of their elements match. + /// + /// + /// Returns a distance [0..1] of the specified sequences. + /// The smaller distance the more of their elements match. + /// + protected double ComputeDistance(TSequence sequenceA, int lengthA, TSequence sequenceB, int lengthB) + { + Debug.Assert(lengthA >= 0 && lengthB >= 0); + + if (lengthA == 0 || lengthB == 0) + { + return (lengthA == lengthB) ? 0.0 : 1.0; + } + + int lcsLength = 0; + foreach (var pair in GetMatchingPairs(sequenceA, lengthA, sequenceB, lengthB)) + { + lcsLength++; + } + + int max = Math.Max(lengthA, lengthB); + Debug.Assert(lcsLength <= max); + return 1.0 - (double)lcsLength / (double)max; + } + + /// + /// Calculates costs of all paths in an edit graph starting from vertex (0,0) and ending in vertex (lengthA, lengthB). + /// + /// + /// The edit graph for A and B has a vertex at each point in the grid (i,j), i in [0, lengthA] and j in [0, lengthB]. + /// + /// The vertices of the edit graph are connected by horizontal, vertical, and diagonal directed edges to form a directed acyclic graph. + /// Horizontal edges connect each vertex to its right neighbor. + /// Vertical edges connect each vertex to the neighbor below it. + /// Diagonal edges connect vertex (i,j) to vertex (i-1,j-1) if (sequenceA[i-1],sequenceB[j-1]) is true. + /// + /// Editing starts with S = []. + /// Move along horizontal edge (i-1,j)-(i,j) represents the fact that sequenceA[i-1] is not added to S. + /// Move along vertical edge (i,j-1)-(i,j) represents an insert of sequenceB[j-1] to S. + /// Move along diagonal edge (i-1,j-1)-(i,j) represents an addition of sequenceB[j-1] to S via an acceptable + /// change of sequenceA[i-1] to sequenceB[j-1]. + /// + /// In every vertex the cheapest outgoing edge is selected. + /// The number of diagonal edges on the path from (0,0) to (lengthA, lengthB) is the length of the longest common subsequence. + /// + private int[,] ComputeCostMatrix(TSequence sequenceA, int lengthA, TSequence sequenceB, int lengthB) + { + var la = lengthA + 1; + var lb = lengthB + 1; + + // TODO: Optimization possible: O(ND) time, O(N) space + // EUGENE W. MYERS: An O(ND) Difference Algorithm and Its Variations + var d = new int[la, lb]; + + d[0, 0] = 0; + for (int i = 1; i <= lengthA; i++) + { + d[i, 0] = d[i - 1, 0] + DeleteCost; + } + + for (int j = 1; j <= lengthB; j++) + { + d[0, j] = d[0, j - 1] + InsertCost; + } + + for (int i = 1; i <= lengthA; i++) + { + for (int j = 1; j <= lengthB; j++) + { + int m1 = d[i - 1, j - 1] + (ItemsEqual(sequenceA, i - 1, sequenceB, j - 1) ? 0 : UpdateCost); + int m2 = d[i - 1, j] + DeleteCost; + int m3 = d[i, j - 1] + InsertCost; + d[i, j] = Math.Min(Math.Min(m1, m2), m3); + } + } + + return d; + } + } + } +} diff --git a/src/Identity/src/Common/TestUtilities/MockEngine.cs b/src/Identity/src/Common/TestUtilities/MockEngine.cs new file mode 100644 index 000000000000..b1efe5e7ec47 --- /dev/null +++ b/src/Identity/src/Common/TestUtilities/MockEngine.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.DotNet.Arcade.Sdk.Tests.Utilities +{ + internal class MockEngine : IBuildEngine5 + { + private readonly ITestOutputHelper _output; + + public MockEngine() + { + } + + public MockEngine(ITestOutputHelper output) + { + _output = output; + } + + public ICollection Messages { get; } = new List(); + public ICollection Warnings { get; } = new List(); + public ICollection Errors { get; } = new List(); + + public bool IsRunningMultipleNodes => false; + + public bool ContinueOnError { get; set; } + + public int LineNumberOfTaskNode => 0; + + public int ColumnNumberOfTaskNode => 0; + + public string ProjectFileOfTaskNode => ""; + + public void LogMessageEvent(BuildMessageEventArgs e) + { + _output?.WriteLine($"{e.Importance} : {e.Message}"); + Messages.Add(e); + } + + public void LogWarningEvent(BuildWarningEventArgs e) + { + _output?.WriteLine($"warning {e.Code}: {e.Message}"); + Warnings.Add(e); + } + + public void LogErrorEvent(BuildErrorEventArgs e) + { + _output?.WriteLine($"error {e.Code}: {e.Message}"); + Errors.Add(e); + if (!ContinueOnError) + { + throw new XunitException("Task error: " + e.Message); + } + } + + public void LogCustomEvent(CustomBuildEventArgs e) + { + _output?.WriteLine(e.Message ?? string.Empty); + } + + public void LogTelemetry(string eventName, IDictionary properties) + { + if (_output != null) + { + _output?.WriteLine($"telemetry {eventName}: {properties.Aggregate(string.Empty, (sum, piece) => $"{sum}, {piece.Key} = {piece.Value}")}"); + } + } + + #region NotImplemented + + public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion) + { + throw new NotImplementedException(); + } + + public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs) + { + throw new NotImplementedException(); + } + + public BuildEngineResult BuildProjectFilesInParallel(string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IList[] removeGlobalProperties, string[] toolsVersion, bool returnTargetOutputs) + { + throw new NotImplementedException(); + } + + public bool BuildProjectFilesInParallel(string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion) + { + throw new NotImplementedException(); + } + + public object GetRegisteredTaskObject(object key, RegisteredTaskObjectLifetime lifetime) + { + throw new NotImplementedException(); + } + + public void Reacquire() + { + throw new NotImplementedException(); + } + + public void RegisterTaskObject(object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection) + { + throw new NotImplementedException(); + } + + public object UnregisterTaskObject(object key, RegisteredTaskObjectLifetime lifetime) + { + throw new NotImplementedException(); + } + + public void Yield() + { + throw new NotImplementedException(); + } + #endregion + } +} + diff --git a/src/Identity/src/ProcessUtilities/ProcessUtil.cs b/src/Identity/src/ProcessUtilities/ProcessUtil.cs new file mode 100644 index 000000000000..0efce44f13f1 --- /dev/null +++ b/src/Identity/src/ProcessUtilities/ProcessUtil.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ProcessUtilities +{ + public static class ProcessUtil + { + [DllImport("libc", SetLastError = true, EntryPoint = "kill")] + private static extern int sys_kill(int pid, int sig); + + public static Task CaptureDumpAsync() + { + var dumpDirectoryPath = Environment.GetEnvironmentVariable("HELIX_DUMP_FOLDER"); + + if (dumpDirectoryPath == null) + { + return Task.CompletedTask; + } + + var process = Process.GetCurrentProcess(); + var dumpFilePath = Path.Combine(dumpDirectoryPath, $"{process.ProcessName}-{process.Id}.dmp"); + + return CaptureDumpAsync(process.Id, dumpFilePath); + } + + public static Task CaptureDumpAsync(int pid) + { + var dumpDirectoryPath = Environment.GetEnvironmentVariable("HELIX_DUMP_FOLDER"); + + if (dumpDirectoryPath == null) + { + return Task.CompletedTask; + } + + var process = Process.GetProcessById(pid); + var dumpFilePath = Path.Combine(dumpDirectoryPath, $"{process.ProcessName}.{process.Id}.dmp"); + + return CaptureDumpAsync(process.Id, dumpFilePath); + } + + public static Task CaptureDumpAsync(int pid, string dumpFilePath) + { + // Skip this on OSX, we know it's unsupported right now + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // Can we capture stacks or do a gcdump instead? + return Task.CompletedTask; + } + + return RunAsync("dotnet-dump", $"collect -p {pid} -o \"{dumpFilePath}\""); + } + + public static async Task<(string, string, int)> RunAsync( + string filename, + string arguments, + string workingDirectory = null, + string dumpDirectoryPath = null, + bool throwOnError = true, + IDictionary environmentVariables = null, + Action outputDataReceived = null, + Action errorDataReceived = null, + Action onStart = null, + CancellationToken cancellationToken = default) + { + using var process = new Process() + { + StartInfo = + { + FileName = filename, + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }, + EnableRaisingEvents = true + }; + + + if (workingDirectory != null) + { + process.StartInfo.WorkingDirectory = workingDirectory; + } + + dumpDirectoryPath ??= Environment.GetEnvironmentVariable("HELIX_DUMP_FOLDER"); + + if (dumpDirectoryPath != null) + { + process.StartInfo.EnvironmentVariables["COMPlus_DbgEnableMiniDump"] = "1"; + process.StartInfo.EnvironmentVariables["COMPlus_DbgMiniDumpName"] = Path.Combine(dumpDirectoryPath, $"{Path.GetFileName(filename)}.%d.dmp"); + } + + if (environmentVariables != null) + { + foreach (var kvp in environmentVariables) + { + process.StartInfo.Environment.Add(kvp); + } + } + + var outputBuilder = new StringBuilder(); + process.OutputDataReceived += (_, e) => + { + if (e.Data != null) + { + if (outputDataReceived != null) + { + outputDataReceived.Invoke(e.Data); + } + else + { + outputBuilder.AppendLine(e.Data); + } + } + }; + + var errorBuilder = new StringBuilder(); + process.ErrorDataReceived += (_, e) => + { + if (e.Data != null) + { + if (errorDataReceived != null) + { + errorDataReceived.Invoke(e.Data); + } + else + { + errorBuilder.AppendLine(e.Data); + } + } + }; + + var processLifetimeTask = new TaskCompletionSource<(string, string, int)>(); + + process.Exited += (_, e) => + { + if (throwOnError && process.ExitCode != 0) + { + processLifetimeTask.TrySetException(new InvalidOperationException($"Command {filename} {arguments} returned exit code {process.ExitCode}. \r\nStdOut:{outputBuilder}, \r\nStdErr: {errorBuilder}")); + } + else + { + processLifetimeTask.TrySetResult((outputBuilder.ToString(), errorBuilder.ToString(), process.ExitCode)); + } + }; + + process.Start(); + onStart?.Invoke(process.Id); + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + var cancelledTcs = new TaskCompletionSource(); + await using var _ = cancellationToken.Register(() => cancelledTcs.TrySetResult(null)); + + var result = await Task.WhenAny(processLifetimeTask.Task, cancelledTcs.Task); + + if (result == cancelledTcs.Task) + { + if (dumpDirectoryPath != null) + { + var dumpFilePath = Path.Combine(dumpDirectoryPath, $"{Path.GetFileName(filename)}.{process.Id}.dmp"); + // Capture a process dump if the dumpDirectory is set + await CaptureDumpAsync(process.Id, dumpFilePath); + } + + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + sys_kill(process.Id, sig: 2); // SIGINT + + var cancel = new CancellationTokenSource(); + + await Task.WhenAny(processLifetimeTask.Task, Task.Delay(TimeSpan.FromSeconds(5), cancel.Token)); + + cancel.Cancel(); + } + + if (!process.HasExited) + { + process.CloseMainWindow(); + + if (!process.HasExited) + { + process.Kill(); + } + } + } + + return await processLifetimeTask.Task; + } + } +} diff --git a/src/Identity/src/ProcessUtilities/ProcessUtilities.csproj b/src/Identity/src/ProcessUtilities/ProcessUtilities.csproj new file mode 100644 index 000000000000..cb631906963a --- /dev/null +++ b/src/Identity/src/ProcessUtilities/ProcessUtilities.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/src/Identity/src/ProcessWithAv/ProcessWithAv.csproj b/src/Identity/src/ProcessWithAv/ProcessWithAv.csproj new file mode 100644 index 000000000000..4f4f0e64432a --- /dev/null +++ b/src/Identity/src/ProcessWithAv/ProcessWithAv.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp5.0 + true + + + + + + + + + + + diff --git a/src/Identity/src/ProcessWithAv/Program.cs b/src/Identity/src/ProcessWithAv/Program.cs new file mode 100644 index 000000000000..2ec7fe587485 --- /dev/null +++ b/src/Identity/src/ProcessWithAv/Program.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading; + +namespace ProcessWithAv +{ + public class Program + { + static void Main(string[] args) + { + Thread.Sleep(5000); + + unsafe + { + *(int*)0x12345678 = 0x1; + } + } + } +} diff --git a/src/Identity/src/ProcessWithCrash/ProcessWithCrash.csproj b/src/Identity/src/ProcessWithCrash/ProcessWithCrash.csproj new file mode 100644 index 000000000000..ec6ebb7780d9 --- /dev/null +++ b/src/Identity/src/ProcessWithCrash/ProcessWithCrash.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp5.0 + + + + + + + + + + + + diff --git a/src/Identity/src/ProcessWithCrash/Program.cs b/src/Identity/src/ProcessWithCrash/Program.cs new file mode 100644 index 000000000000..2abe9c2515d3 --- /dev/null +++ b/src/Identity/src/ProcessWithCrash/Program.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProcessWithCrash +{ + public class Program + { + static Task Main(string[] args) + { + var tcs = new TaskCompletionSource(); + + ThreadPool.QueueUserWorkItem(state => + { + NullReference(null); + + tcs.TrySetResult(null); + }, + null); + + return tcs.Task; + } + + private static void NullReference(object o) + { + o.ToString(); + } + } +} diff --git a/src/Identity/src/ProcessWithHang/ProcessWithHang.csproj b/src/Identity/src/ProcessWithHang/ProcessWithHang.csproj new file mode 100644 index 000000000000..c2b1d72220ba --- /dev/null +++ b/src/Identity/src/ProcessWithHang/ProcessWithHang.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp5.0 + + + + + + + + + + + + diff --git a/src/Identity/src/ProcessWithHang/Program.cs b/src/Identity/src/ProcessWithHang/Program.cs new file mode 100644 index 000000000000..effd07256f89 --- /dev/null +++ b/src/Identity/src/ProcessWithHang/Program.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading.Tasks; + +namespace ProcessWithHang +{ + public class Program + { + static async Task Main(string[] args) + { + await new TaskCompletionSource().Task; + } + } +} diff --git a/src/Identity/test/Helix1.HangTestRunner.Test/Helix1.HangTestRunner.Test.csproj b/src/Identity/test/Helix1.HangTestRunner.Test/Helix1.HangTestRunner.Test.csproj new file mode 100644 index 000000000000..c3e3821f5aa2 --- /dev/null +++ b/src/Identity/test/Helix1.HangTestRunner.Test/Helix1.HangTestRunner.Test.csproj @@ -0,0 +1,14 @@ + + + + $(DefaultNetCoreTargetFramework) + true + true + false + + + + + + + diff --git a/src/Identity/test/Helix1.HangTestRunner.Test/UnitTest1.cs b/src/Identity/test/Helix1.HangTestRunner.Test/UnitTest1.cs new file mode 100644 index 000000000000..8b4b49c51fb0 --- /dev/null +++ b/src/Identity/test/Helix1.HangTestRunner.Test/UnitTest1.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace XUnitTestProject1 +{ + public class UnitTest1 + { + [Fact] + public async Task HangTestRunner() + { + var tcs = new TaskCompletionSource(); + + await tcs.Task; + } + + [Fact] + public async Task SlowTest() + { + await Task.Delay(1000); + } + + [Fact] + public async Task SlowerTest() + { + await Task.Delay(5000); + } + } +} diff --git a/src/Identity/test/Helix2.LogOutput.Test/Helix2.LogOutput.Test.csproj b/src/Identity/test/Helix2.LogOutput.Test/Helix2.LogOutput.Test.csproj new file mode 100644 index 000000000000..4282ef35b627 --- /dev/null +++ b/src/Identity/test/Helix2.LogOutput.Test/Helix2.LogOutput.Test.csproj @@ -0,0 +1,10 @@ + + + + $(DefaultNetCoreTargetFramework) + true + true + false + + + diff --git a/src/Identity/test/Helix2.LogOutput.Test/UnitTest1.cs b/src/Identity/test/Helix2.LogOutput.Test/UnitTest1.cs new file mode 100644 index 000000000000..f4ec46c6bfa4 --- /dev/null +++ b/src/Identity/test/Helix2.LogOutput.Test/UnitTest1.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections; +using Xunit; +using Xunit.Abstractions; + +namespace XUnitTestProject2 +{ + public class UnitTest1 + { + private readonly ITestOutputHelper _testOutputHelper; + + public UnitTest1(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + + [Fact] + public void LogTestOutput() + { + Console.WriteLine("This is direct console output from the test"); + + _testOutputHelper.WriteLine("This is line from a test!"); + + _testOutputHelper.WriteLine("This is line from another line from a test!"); + + foreach (DictionaryEntry pair in Environment.GetEnvironmentVariables()) + { + _testOutputHelper.WriteLine(pair.Key + "=" + pair.Value); + } + + Assert.True(false); + } + } +} diff --git a/src/Identity/test/Helix3.AsyncTimeout.Test/Helix3.AsyncTimeout.Test.csproj b/src/Identity/test/Helix3.AsyncTimeout.Test/Helix3.AsyncTimeout.Test.csproj new file mode 100644 index 000000000000..c3e3821f5aa2 --- /dev/null +++ b/src/Identity/test/Helix3.AsyncTimeout.Test/Helix3.AsyncTimeout.Test.csproj @@ -0,0 +1,14 @@ + + + + $(DefaultNetCoreTargetFramework) + true + true + false + + + + + + + diff --git a/src/Identity/test/Helix3.AsyncTimeout.Test/TaskExtensions.cs b/src/Identity/test/Helix3.AsyncTimeout.Test/TaskExtensions.cs new file mode 100644 index 000000000000..50747ae0aa65 --- /dev/null +++ b/src/Identity/test/Helix3.AsyncTimeout.Test/TaskExtensions.cs @@ -0,0 +1,133 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace XUnitTestProject3 +{ + static class TaskExtensions + { + private const int DefaultTimeout = 5 * 1000; + + public static Task OrTimeout(this ValueTask task, int milliseconds = DefaultTimeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) + { + return OrTimeout(task, new TimeSpan(0, 0, 0, 0, milliseconds), memberName, filePath, lineNumber); + } + + public static Task OrTimeout(this ValueTask task, TimeSpan timeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) + { + return task.AsTask().TimeoutAfter(timeout, filePath, lineNumber ?? 0); + } + + public static Task OrTimeout(this Task task, int milliseconds = DefaultTimeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) + { + return OrTimeout(task, new TimeSpan(0, 0, 0, 0, milliseconds), memberName, filePath, lineNumber); + } + + public static Task OrTimeout(this Task task, TimeSpan timeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) + { + return task.TimeoutAfter(timeout, filePath, lineNumber ?? 0); + } + + public static Task OrTimeout(this ValueTask task, int milliseconds = DefaultTimeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) => + OrTimeout(task, new TimeSpan(0, 0, 0, 0, milliseconds), memberName, filePath, lineNumber); + + public static Task OrTimeout(this ValueTask task, TimeSpan timeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) => + task.AsTask().OrTimeout(timeout, memberName, filePath, lineNumber); + + public static Task OrTimeout(this Task task, int milliseconds = DefaultTimeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) + { + return OrTimeout(task, new TimeSpan(0, 0, 0, 0, milliseconds), memberName, filePath, lineNumber); + } + + public static Task OrTimeout(this Task task, TimeSpan timeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) + { + return task.TimeoutAfter(timeout, filePath, lineNumber ?? 0); + } + + public static async Task OrThrowIfOtherFails(this Task task, Task otherTask) + { + var completed = await Task.WhenAny(task, otherTask); + if (completed == otherTask && otherTask.IsFaulted) + { + // Manifest the exception + otherTask.GetAwaiter().GetResult(); + throw new Exception("Unreachable code"); + } + else + { + // Await the task we were asked to await. Either it's finished, or the otherTask finished successfully, and it's not our job to check that + await task; + } + } + + public static async Task OrThrowIfOtherFails(this Task task, Task otherTask) + { + await OrThrowIfOtherFails((Task)task, otherTask); + + // If we get here, 'task' is finished and succeeded. + return task.GetAwaiter().GetResult(); + } + + public static async Task TimeoutAfter(this Task task, TimeSpan timeout, + [CallerFilePath] string filePath = null, + [CallerLineNumber] int lineNumber = default) + { + // Don't create a timer if the task is already completed + // or the debugger is attached + if (task.IsCompleted || Debugger.IsAttached) + { + return await task; + } + + var cts = new CancellationTokenSource(); + if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token))) + { + cts.Cancel(); + return await task; + } + else + { + throw new TestTimeoutException(CreateMessage(timeout, filePath, lineNumber)); + } + } + + public static async Task TimeoutAfter(this Task task, TimeSpan timeout, + [CallerFilePath] string filePath = null, + [CallerLineNumber] int lineNumber = default) + { + // Don't create a timer if the task is already completed + // or the debugger is attached + if (task.IsCompleted || Debugger.IsAttached) + { + await task; + return; + } + + var cts = new CancellationTokenSource(); + if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token))) + { + cts.Cancel(); + await task; + } + else + { + throw new TimeoutException(CreateMessage(timeout, filePath, lineNumber)); + } + } + + private static string CreateMessage(TimeSpan timeout, string filePath, int lineNumber) + => string.IsNullOrEmpty(filePath) + ? $"The operation timed out after reaching the limit of {timeout.TotalMilliseconds}ms." + : $"The operation at {filePath}:{lineNumber} timed out after reaching the limit of {timeout.TotalMilliseconds}ms."; + } + + public class TestTimeoutException : TimeoutException + { + public TestTimeoutException(string message) : base(message) + { + + } + } +} diff --git a/src/Identity/test/Helix3.AsyncTimeout.Test/UnitTest1.cs b/src/Identity/test/Helix3.AsyncTimeout.Test/UnitTest1.cs new file mode 100644 index 000000000000..96e1e1d684ff --- /dev/null +++ b/src/Identity/test/Helix3.AsyncTimeout.Test/UnitTest1.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using ProcessUtilities; +using Xunit; + +namespace XUnitTestProject3 +{ + public class UnitTest1 + { + static UnitTest1() + { + AppDomain.CurrentDomain.FirstChanceException += (sender, e) => + { + if (e.Exception is TestTimeoutException) + { + // Block forever, this allows the test host to collect a dump with the test still on the stack + new ManualResetEventSlim().Wait(); + } + }; + } + + [Fact] + public async Task AsyncTimeoutTest() + { + var tcs = new TaskCompletionSource(); + + await tcs.Task.OrTimeout(); + } + } +} diff --git a/src/Identity/test/Helix4.CrashTestRunner.Test/Helix4.CrashTestRunner.Test.csproj b/src/Identity/test/Helix4.CrashTestRunner.Test/Helix4.CrashTestRunner.Test.csproj new file mode 100644 index 000000000000..c3e3821f5aa2 --- /dev/null +++ b/src/Identity/test/Helix4.CrashTestRunner.Test/Helix4.CrashTestRunner.Test.csproj @@ -0,0 +1,14 @@ + + + + $(DefaultNetCoreTargetFramework) + true + true + false + + + + + + + diff --git a/src/Identity/test/Helix4.CrashTestRunner.Test/UnitTest1.cs b/src/Identity/test/Helix4.CrashTestRunner.Test/UnitTest1.cs new file mode 100644 index 000000000000..90f012208ea4 --- /dev/null +++ b/src/Identity/test/Helix4.CrashTestRunner.Test/UnitTest1.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace XUnitTestProject4 +{ + public class OtherComponent + { + public static void Execute(TaskCompletionSource tcs) + { + ThreadPool.QueueUserWorkItem(_ => + { + BuggyCode(); + + tcs.TrySetResult(null); + }, + null); + + static void BuggyCode() => throw new InvalidOperationException(); + } + } + + public class UnitTest1 + { + [Fact] + public async Task Test1() + { + await Task.Delay(1000); + } + + [Fact] + public async Task CrashTestRunner() + { + var tcs = new TaskCompletionSource(); + + OtherComponent.Execute(tcs); + + await tcs.Task; + } + + + [Fact] + public async Task Test2() + { + await Task.Delay(5000); + Assert.True(false); + } + } +} diff --git a/src/Identity/test/Helix5.Segfault.Test/Helix5.Segfault.Test.csproj b/src/Identity/test/Helix5.Segfault.Test/Helix5.Segfault.Test.csproj new file mode 100644 index 000000000000..465d7f13c5dc --- /dev/null +++ b/src/Identity/test/Helix5.Segfault.Test/Helix5.Segfault.Test.csproj @@ -0,0 +1,15 @@ + + + + $(DefaultNetCoreTargetFramework) + true + true + false + true + + + + + + + diff --git a/src/Identity/test/Helix5.Segfault.Test/UnitTest1.cs b/src/Identity/test/Helix5.Segfault.Test/UnitTest1.cs new file mode 100644 index 000000000000..f880a81e6901 --- /dev/null +++ b/src/Identity/test/Helix5.Segfault.Test/UnitTest1.cs @@ -0,0 +1,17 @@ +using System; +using Xunit; + +namespace XUnitTestProject5 +{ + public class UnitTest1 + { + [Fact] + public void Segfault() + { + unsafe + { + *(int*)0x12345678 = 0x1; + } + } + } +} diff --git a/src/Identity/test/Helix6.OutOfProcessHang.Test/Helix6.OutOfProcessHang.Test.csproj b/src/Identity/test/Helix6.OutOfProcessHang.Test/Helix6.OutOfProcessHang.Test.csproj new file mode 100644 index 000000000000..bed5d97d2fa7 --- /dev/null +++ b/src/Identity/test/Helix6.OutOfProcessHang.Test/Helix6.OutOfProcessHang.Test.csproj @@ -0,0 +1,16 @@ + + + + $(DefaultNetCoreTargetFramework) + true + true + false + true + + + + + + + + diff --git a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs new file mode 100644 index 000000000000..559aaac0278e --- /dev/null +++ b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs @@ -0,0 +1,31 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using ProcessUtilities; +using Xunit; +using Xunit.Abstractions; + +namespace XUnitTestProject6 +{ + public class UnitTest1 + { + private ITestOutputHelper _testOutputHelper; + + public UnitTest1(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + + [Fact] + public async Task OutOfProcessHang() + { + var path = typeof(ProcessWithHang.Program).Assembly.Location; + + _testOutputHelper.WriteLine($"About to execute: {path}"); + + await ProcessUtil.RunAsync("dotnet", path, cancellationToken: new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token); + } + } +} diff --git a/src/Identity/test/Helix7.ProcessWithAv.Test/Helix7.ProcessWithAv.Test.csproj b/src/Identity/test/Helix7.ProcessWithAv.Test/Helix7.ProcessWithAv.Test.csproj new file mode 100644 index 000000000000..d24ff0c12f13 --- /dev/null +++ b/src/Identity/test/Helix7.ProcessWithAv.Test/Helix7.ProcessWithAv.Test.csproj @@ -0,0 +1,14 @@ + + + $(DefaultNetCoreTargetFramework) + true + true + false + + + + + + + + diff --git a/src/Identity/test/Helix7.ProcessWithAv.Test/UnitTest1.cs b/src/Identity/test/Helix7.ProcessWithAv.Test/UnitTest1.cs new file mode 100644 index 000000000000..bf7e085e0972 --- /dev/null +++ b/src/Identity/test/Helix7.ProcessWithAv.Test/UnitTest1.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using ProcessUtilities; +using Xunit; +using Xunit.Abstractions; + +namespace XUnitTestProject7 +{ + public class UnitTest1 + { + private ITestOutputHelper _testOutputHelper; + + public UnitTest1(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + + [Fact] + public async Task OutOfProcessAv() + { + var path = typeof(ProcessWithAv.Program).Assembly.Location; + + _testOutputHelper.WriteLine($"About to execute: {path}"); + + await ProcessUtil.RunAsync("dotnet", path); + } + } +} diff --git a/src/Identity/test/Helix8.OutOfProcessCrash.Test/Helix8.OutOfProcessCrash.Test.csproj b/src/Identity/test/Helix8.OutOfProcessCrash.Test/Helix8.OutOfProcessCrash.Test.csproj new file mode 100644 index 000000000000..94e5a16e865f --- /dev/null +++ b/src/Identity/test/Helix8.OutOfProcessCrash.Test/Helix8.OutOfProcessCrash.Test.csproj @@ -0,0 +1,15 @@ + + + $(DefaultNetCoreTargetFramework) + true + true + false + XUnit + + + + + + + + diff --git a/src/Identity/test/Helix8.OutOfProcessCrash.Test/UnitTest1.cs b/src/Identity/test/Helix8.OutOfProcessCrash.Test/UnitTest1.cs new file mode 100644 index 000000000000..612cc05980fe --- /dev/null +++ b/src/Identity/test/Helix8.OutOfProcessCrash.Test/UnitTest1.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using ProcessUtilities; +using Xunit; +using Xunit.Abstractions; + +namespace XUnitTestProject8 +{ + public class UnitTest1 + { + private ITestOutputHelper _testOutputHelper; + + public UnitTest1(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + + [Fact] + public async Task OutOfProcessCrash() + { + var path = typeof(ProcessWithCrash.Program).Assembly.Location; + + _testOutputHelper.WriteLine($"About to execute: {path}"); + + await ProcessUtil.RunAsync("dotnet", path); + } + } +} diff --git a/src/Identity/test/Helix9.FailFast.Test/Helix9.FailFast.Test.csproj b/src/Identity/test/Helix9.FailFast.Test/Helix9.FailFast.Test.csproj new file mode 100644 index 000000000000..2084ccd4f290 --- /dev/null +++ b/src/Identity/test/Helix9.FailFast.Test/Helix9.FailFast.Test.csproj @@ -0,0 +1,14 @@ + + + $(DefaultNetCoreTargetFramework) + true + true + false + XUnit + + + + + + + diff --git a/src/Identity/test/Helix9.FailFast.Test/UnitTest1.cs b/src/Identity/test/Helix9.FailFast.Test/UnitTest1.cs new file mode 100644 index 000000000000..b0065a33e6a3 --- /dev/null +++ b/src/Identity/test/Helix9.FailFast.Test/UnitTest1.cs @@ -0,0 +1,20 @@ +using System; +using Xunit; + +namespace XUnitTestProject9 +{ + public class UnitTest1 + { + [Fact] + public void PassingTest() + { + + } + + [Fact] + public void FailFast() + { + Environment.FailFast("Oh noes"); + } + } +} From c37192e10fc7452ceb52b469dd1e29c19549541c Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 19 Aug 2020 09:56:40 -0700 Subject: [PATCH 02/23] Try hardcoding sdk version for custom bits --- eng/targets/Helix.targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/targets/Helix.targets b/eng/targets/Helix.targets index 706c407778f2..08be4516bf7b 100644 --- a/eng/targets/Helix.targets +++ b/eng/targets/Helix.targets @@ -139,8 +139,8 @@ Usage: dotnet msbuild /t:Helix src/MyTestProject.csproj $(TargetFileName) @(HelixPreCommand) @(HelixPostCommand) - call runtests.cmd $(TargetFileName) $(NETCoreSdkVersion) $(MicrosoftNETCoreAppRuntimeVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfPackageVersion) Microsoft.AspNetCore.App.Runtime.win-x64.$(SharedFxVersion).nupkg Microsoft.AspNetCore.App.Ref.$(SharedFxVersion).nupkg $(HelixTimeout) - ./runtests.sh $(TargetFileName) $(NETCoreSdkVersion) $(MicrosoftNETCoreAppRuntimeVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfPackageVersion) Microsoft.AspNetCore.App.Runtime.win-x64.$(SharedFxVersion).nupkg Microsoft.AspNetCore.App.Ref.$(SharedFxVersion).nupkg $(HelixTimeout) + call runtests.cmd $(TargetFileName) 5.0.100-rc.1.20418.3 $(MicrosoftNETCoreAppRuntimeVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfPackageVersion) Microsoft.AspNetCore.App.Runtime.win-x64.$(SharedFxVersion).nupkg Microsoft.AspNetCore.App.Ref.$(SharedFxVersion).nupkg $(HelixTimeout) + ./runtests.sh $(TargetFileName) 5.0.100-rc.1.20418.3 $(MicrosoftNETCoreAppRuntimeVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfPackageVersion) Microsoft.AspNetCore.App.Runtime.win-x64.$(SharedFxVersion).nupkg Microsoft.AspNetCore.App.Ref.$(SharedFxVersion).nupkg $(HelixTimeout) $(HelixCommand) $(HelixTimeout) From e48af3fc4e5d9bf712484db02962c2d0815f7340 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 19 Aug 2020 11:40:07 -0700 Subject: [PATCH 03/23] Enable matrix to capture dumps --- .azure/pipelines/helix-matrix.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.azure/pipelines/helix-matrix.yml b/.azure/pipelines/helix-matrix.yml index 6e491364008b..c49d667ee851 100644 --- a/.azure/pipelines/helix-matrix.yml +++ b/.azure/pipelines/helix-matrix.yml @@ -1,5 +1,9 @@ # We only want to run full helix matrix on master -pr: none +pr: + autoCancel: true + branches: + include: + - '*' trigger: none schedules: - cron: "0 */12 * * *" From 6738c4d2a7d0b82c051ba0675eb9cb2d998a27b7 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 24 Aug 2020 10:28:45 -0700 Subject: [PATCH 04/23] Update UnitTest1.cs --- src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs index 559aaac0278e..933e401e2d22 100644 --- a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs +++ b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs @@ -25,7 +25,7 @@ public async Task OutOfProcessHang() _testOutputHelper.WriteLine($"About to execute: {path}"); - await ProcessUtil.RunAsync("dotnet", path, cancellationToken: new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token); + await ProcessUtil.RunAsync("dotnet", path); } } } From 2ae2eddbe69d3c5735e083a1689504a5f92c9c38 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 24 Aug 2020 13:13:03 -0700 Subject: [PATCH 05/23] Try using vstest dump path --- eng/helix/content/runtests.cmd | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/helix/content/runtests.cmd b/eng/helix/content/runtests.cmd index 1016d25aad8f..14a69c375de8 100644 --- a/eng/helix/content/runtests.cmd +++ b/eng/helix/content/runtests.cmd @@ -24,6 +24,7 @@ set DOTNET_ROOT=%DOTNET_HOME%\%$arch% set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 set DOTNET_MULTILEVEL_LOOKUP=0 set DOTNET_CLI_HOME=%HELIX_CORRELATION_PAYLOAD%\home +set VSTEST_DUMP_PATH=%HELIX_WORKITEM_ROOT% set "PATH=%DOTNET_ROOT%;%PATH%;%HELIX_CORRELATION_PAYLOAD%\node\bin" echo Set path to: "%PATH%" From f698b86bf55bdc3605a82f426a4876f521d2ff07 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 24 Aug 2020 13:13:28 -0700 Subject: [PATCH 06/23] Try using VSTEST_DUMP_PATH --- eng/helix/content/runtests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/helix/content/runtests.sh b/eng/helix/content/runtests.sh index 713f51cab90f..4a088f1ec173 100644 --- a/eng/helix/content/runtests.sh +++ b/eng/helix/content/runtests.sh @@ -24,6 +24,7 @@ export DOTNET_MULTILEVEL_LOOKUP=0 export DOTNET_CLI_HOME="$DIR/.home$RANDOM" export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +export VSTEST_DUMP_PATH=$HELIX_WORKITEM_ROOT RESET="\033[0m" RED="\033[0;31m" From ef63bd479e590bf0153e1f5df78d453fd2baae57 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 24 Aug 2020 13:15:26 -0700 Subject: [PATCH 07/23] Copy sequence file, don't copy dumps for now --- eng/helix/content/RunTests/TestRunner.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index baea724f3705..ca1884677807 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -325,8 +325,8 @@ public void UploadResults() foreach (var file in Directory.EnumerateFiles("TestResults", "*.dmp", SearchOption.AllDirectories)) { var fileName = Path.GetFileName(file); - Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)}"); - File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)); + Console.WriteLine($"Would have Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)}"); + //File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)); } } else @@ -347,6 +347,20 @@ public void UploadResults() { Console.WriteLine("No trx found in TestResults"); } + Console.WriteLine($"Copying TestResults/**/sequence.xml to {HELIX_WORKITEM_UPLOAD_ROOT}/"); + if (Directory.Exists("TestResults")) + { + foreach (var file in Directory.EnumerateFiles("TestResults", "sequence.xml", SearchOption.AllDirectories)) + { + Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, "sequence.xml")}"); + File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, "sequence.xml")); + } + } + else + { + Console.WriteLine("No trx found in TestResults"); + } + } } } From 1cd8fc8fae31184988ac41f664602870033e6a08 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 24 Aug 2020 14:14:21 -0700 Subject: [PATCH 08/23] Duh upload root --- eng/helix/content/runtests.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/helix/content/runtests.cmd b/eng/helix/content/runtests.cmd index 14a69c375de8..24e27db30add 100644 --- a/eng/helix/content/runtests.cmd +++ b/eng/helix/content/runtests.cmd @@ -24,7 +24,7 @@ set DOTNET_ROOT=%DOTNET_HOME%\%$arch% set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 set DOTNET_MULTILEVEL_LOOKUP=0 set DOTNET_CLI_HOME=%HELIX_CORRELATION_PAYLOAD%\home -set VSTEST_DUMP_PATH=%HELIX_WORKITEM_ROOT% +set VSTEST_DUMP_PATH=%HELIX_WORKITEM_UPLOAD_ROOT% set "PATH=%DOTNET_ROOT%;%PATH%;%HELIX_CORRELATION_PAYLOAD%\node\bin" echo Set path to: "%PATH%" From 0cd388414ea9adf84550aa49b1aaf30b594514cf Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 24 Aug 2020 14:14:37 -0700 Subject: [PATCH 09/23] Upload root --- eng/helix/content/runtests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/helix/content/runtests.sh b/eng/helix/content/runtests.sh index 4a088f1ec173..39031cc2aa3f 100644 --- a/eng/helix/content/runtests.sh +++ b/eng/helix/content/runtests.sh @@ -24,7 +24,7 @@ export DOTNET_MULTILEVEL_LOOKUP=0 export DOTNET_CLI_HOME="$DIR/.home$RANDOM" export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -export VSTEST_DUMP_PATH=$HELIX_WORKITEM_ROOT +export VSTEST_DUMP_PATH=$HELIX_WORKITEM_UPLOAD_ROOT RESET="\033[0m" RED="\033[0;31m" From e30899c9830ac26b69992a5dc142fc6eb39aff55 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 24 Aug 2020 14:15:15 -0700 Subject: [PATCH 10/23] Update TestRunner.cs --- eng/helix/content/RunTests/TestRunner.cs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index ca1884677807..31c9b795e5cb 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -319,20 +319,6 @@ public void UploadResults() { Console.WriteLine("No logs found in artifacts/log"); } - Console.WriteLine($"Copying TestResults/**/*.dmp to {HELIX_WORKITEM_UPLOAD_ROOT}/"); - if (Directory.Exists("TestResults")) - { - foreach (var file in Directory.EnumerateFiles("TestResults", "*.dmp", SearchOption.AllDirectories)) - { - var fileName = Path.GetFileName(file); - Console.WriteLine($"Would have Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)}"); - //File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)); - } - } - else - { - Console.WriteLine("No dmps found in TestResults"); - } Console.WriteLine($"Copying TestResults/**/*.trx to {HELIX_WORKITEM_UPLOAD_ROOT}/"); if (Directory.Exists("TestResults")) { @@ -358,7 +344,7 @@ public void UploadResults() } else { - Console.WriteLine("No trx found in TestResults"); + Console.WriteLine("No sequence.xml found in TestResults"); } } From 025f462c20186a80817b984345c5a68432899832 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 25 Aug 2020 16:52:04 -0700 Subject: [PATCH 11/23] Try a different hang simulation --- src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs index 933e401e2d22..7c60596a94b9 100644 --- a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs +++ b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using ProcessUtilities; @@ -25,7 +26,9 @@ public async Task OutOfProcessHang() _testOutputHelper.WriteLine($"About to execute: {path}"); - await ProcessUtil.RunAsync("dotnet", path); + var sleepCmd = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "timeout" : "sleep"; + + await ProcessUtil.RunAsync("timeout", "10", cancellationToken: new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token); } } } From ec5958aaee970a393b0f8eef71ba208a5a73c587 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 25 Aug 2020 16:54:12 -0700 Subject: [PATCH 12/23] Fixup --- src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs index 7c60596a94b9..117a6d50a29b 100644 --- a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs +++ b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs @@ -28,7 +28,7 @@ public async Task OutOfProcessHang() var sleepCmd = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "timeout" : "sleep"; - await ProcessUtil.RunAsync("timeout", "10", cancellationToken: new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token); + await ProcessUtil.RunAsync(sleepCmd, "10", cancellationToken: new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token); } } } From 96b3dd6b2f62e1331b0bdd26b96858f72221ae3e Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 25 Aug 2020 20:21:00 -0700 Subject: [PATCH 13/23] Update UnitTest1.cs --- src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs index 117a6d50a29b..2817290f6dcc 100644 --- a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs +++ b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs @@ -28,7 +28,7 @@ public async Task OutOfProcessHang() var sleepCmd = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "timeout" : "sleep"; - await ProcessUtil.RunAsync(sleepCmd, "10", cancellationToken: new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token); + await ProcessUtil.RunAsync(sleepCmd, "600"); } } } From 765d0cd0b58e2df73735803cc88f3ad30c2e2e81 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 26 Aug 2020 03:53:42 -0700 Subject: [PATCH 14/23] Try using dump folder --- eng/helix/content/runtests.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/helix/content/runtests.cmd b/eng/helix/content/runtests.cmd index 24e27db30add..74e53710f7d5 100644 --- a/eng/helix/content/runtests.cmd +++ b/eng/helix/content/runtests.cmd @@ -24,7 +24,7 @@ set DOTNET_ROOT=%DOTNET_HOME%\%$arch% set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 set DOTNET_MULTILEVEL_LOOKUP=0 set DOTNET_CLI_HOME=%HELIX_CORRELATION_PAYLOAD%\home -set VSTEST_DUMP_PATH=%HELIX_WORKITEM_UPLOAD_ROOT% +set VSTEST_DUMP_PATH=%HELIX_DUMP_FOLDER% set "PATH=%DOTNET_ROOT%;%PATH%;%HELIX_CORRELATION_PAYLOAD%\node\bin" echo Set path to: "%PATH%" From 7d094c1f153b24ec27d1dd1449faa01f0cfc3d8d Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 26 Aug 2020 03:54:00 -0700 Subject: [PATCH 15/23] Try using dump folder --- eng/helix/content/runtests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/helix/content/runtests.sh b/eng/helix/content/runtests.sh index 39031cc2aa3f..ac0d099e6984 100644 --- a/eng/helix/content/runtests.sh +++ b/eng/helix/content/runtests.sh @@ -24,7 +24,7 @@ export DOTNET_MULTILEVEL_LOOKUP=0 export DOTNET_CLI_HOME="$DIR/.home$RANDOM" export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -export VSTEST_DUMP_PATH=$HELIX_WORKITEM_UPLOAD_ROOT +export VSTEST_DUMP_PATH=$HELIX_DUMP_FOLDER RESET="\033[0m" RED="\033[0;31m" From 52ef578f40d71d37d4c67b7aa27f5375e70a2448 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 26 Aug 2020 04:02:29 -0700 Subject: [PATCH 16/23] Fix sequence file search pattern --- eng/helix/content/RunTests/TestRunner.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index 31c9b795e5cb..d6bf1939ba37 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -333,18 +333,19 @@ public void UploadResults() { Console.WriteLine("No trx found in TestResults"); } - Console.WriteLine($"Copying TestResults/**/sequence.xml to {HELIX_WORKITEM_UPLOAD_ROOT}/"); + Console.WriteLine($"Copying TestResults/**/Sequence*.xml to {HELIX_WORKITEM_UPLOAD_ROOT}/"); if (Directory.Exists("TestResults")) { - foreach (var file in Directory.EnumerateFiles("TestResults", "sequence.xml", SearchOption.AllDirectories)) + foreach (var file in Directory.EnumerateFiles("TestResults", "Sequence*.xml", SearchOption.AllDirectories)) { - Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, "sequence.xml")}"); - File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, "sequence.xml")); + var fileName = Path.GetFileName(file); + Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)}"); + File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)); } } else { - Console.WriteLine("No sequence.xml found in TestResults"); + Console.WriteLine("No sequence xml file found in TestResults"); } } From 41a49f7d4450b3117fb53b4402ae6b05a8145317 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 26 Aug 2020 11:47:42 -0700 Subject: [PATCH 17/23] Update Helix6.OutOfProcessHang.Test.csproj --- .../Helix6.OutOfProcessHang.Test.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Identity/test/Helix6.OutOfProcessHang.Test/Helix6.OutOfProcessHang.Test.csproj b/src/Identity/test/Helix6.OutOfProcessHang.Test/Helix6.OutOfProcessHang.Test.csproj index bed5d97d2fa7..96ef0fb7194d 100644 --- a/src/Identity/test/Helix6.OutOfProcessHang.Test/Helix6.OutOfProcessHang.Test.csproj +++ b/src/Identity/test/Helix6.OutOfProcessHang.Test/Helix6.OutOfProcessHang.Test.csproj @@ -10,7 +10,7 @@ - - + + From 278aa555272ae0cb80c382aee0eb560fc4852db7 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 26 Aug 2020 11:48:15 -0700 Subject: [PATCH 18/23] Update UnitTest1.cs --- src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs index 2817290f6dcc..933e401e2d22 100644 --- a/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs +++ b/src/Identity/test/Helix6.OutOfProcessHang.Test/UnitTest1.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using System.IO; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using ProcessUtilities; @@ -26,9 +25,7 @@ public async Task OutOfProcessHang() _testOutputHelper.WriteLine($"About to execute: {path}"); - var sleepCmd = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "timeout" : "sleep"; - - await ProcessUtil.RunAsync(sleepCmd, "600"); + await ProcessUtil.RunAsync("dotnet", path); } } } From 0503b8b827e13491dfe99dc442a672387e5623b1 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 28 Aug 2020 11:11:30 -0700 Subject: [PATCH 19/23] Add diag logs to test runs for now --- eng/helix/content/RunTests/TestRunner.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index d6bf1939ba37..18e17b7cf21a 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -237,7 +237,8 @@ public async Task RunTestsAsync() { // Timeout test run 5 minutes before the Helix job would timeout var cts = new CancellationTokenSource(Options.Timeout.Subtract(TimeSpan.FromMinutes(5))); - var commonTestArgs = $"test {Options.Target} --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=5m\""; + var upload = Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT"); + var commonTestArgs = $"test {Options.Target} --diag:{upload}\log.txt --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=5m\""; if (Options.Quarantined) { Console.WriteLine("Running quarantined tests."); From 40d9301512d92a8a174978840d1389ce4e1b5199 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 28 Aug 2020 11:12:56 -0700 Subject: [PATCH 20/23] Update TestRunner.cs --- eng/helix/content/RunTests/TestRunner.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index 18e17b7cf21a..f1086251162e 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -237,8 +237,8 @@ public async Task RunTestsAsync() { // Timeout test run 5 minutes before the Helix job would timeout var cts = new CancellationTokenSource(Options.Timeout.Subtract(TimeSpan.FromMinutes(5))); - var upload = Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT"); - var commonTestArgs = $"test {Options.Target} --diag:{upload}\log.txt --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=5m\""; + var diagLog = Path.Combine(Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT"), "log.txt"); + var commonTestArgs = $"test {Options.Target} --diag:{diagLog} --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=5m\""; if (Options.Quarantined) { Console.WriteLine("Running quarantined tests."); From 143687377a830c9a60761f7b025707b2f8c3ac22 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 10 Sep 2020 09:07:14 -0700 Subject: [PATCH 21/23] Display ulimit -c --- eng/helix/content/runtests.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eng/helix/content/runtests.sh b/eng/helix/content/runtests.sh index ac0d099e6984..a2bc69168ede 100644 --- a/eng/helix/content/runtests.sh +++ b/eng/helix/content/runtests.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +echo Verifying ulimit -c +ulimit -c + dotnet_sdk_version="$2" dotnet_runtime_version="$3" From 6c4dfcd78d7eade3a054593c0dff52436fc25461 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 11 Sep 2020 11:03:12 -0700 Subject: [PATCH 22/23] Update TestRunner.cs --- eng/helix/content/RunTests/TestRunner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index f1086251162e..e6518c908aa0 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -237,7 +237,7 @@ public async Task RunTestsAsync() { // Timeout test run 5 minutes before the Helix job would timeout var cts = new CancellationTokenSource(Options.Timeout.Subtract(TimeSpan.FromMinutes(5))); - var diagLog = Path.Combine(Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT"), "log.txt"); + var diagLog = Path.Combine(Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT"), "vstest.log"); var commonTestArgs = $"test {Options.Target} --diag:{diagLog} --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=5m\""; if (Options.Quarantined) { From 7d3c1ec8f8511d971f187c8a9a69ca32e1ed3868 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 21 Sep 2020 09:35:43 -0700 Subject: [PATCH 23/23] Update Helix.targets --- eng/targets/Helix.targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/targets/Helix.targets b/eng/targets/Helix.targets index 8ada41db4952..ba28388707f2 100644 --- a/eng/targets/Helix.targets +++ b/eng/targets/Helix.targets @@ -130,8 +130,8 @@ When the targeting pack builds, it has exactly the same version as the shared framework. Passing SharedFxVersion because that's needed even when the targeting pack isn't building. --> - call runtests.cmd $(TargetFileName) 5.0.100-rc.1.20418.3 $(MicrosoftNETCoreAppInternalPackageVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfPackageVersion) $(HelixTimeout) $(DotNetRuntimeSourceFeedKey) - ./runtests.sh $(TargetFileName) 5.0.100-rc.1.20418.3 $(MicrosoftNETCoreAppInternalPackageVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfPackageVersion) $(HelixTimeout) $(DotNetRuntimeSourceFeedKey) + call runtests.cmd $(TargetFileName) $(NETCoreSdkVersion) $(MicrosoftNETCoreAppInternalPackageVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfPackageVersion) $(HelixTimeout) $(DotNetRuntimeSourceFeedKey) + ./runtests.sh $(TargetFileName) $(NETCoreSdkVersion) $(MicrosoftNETCoreAppInternalPackageVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfPackageVersion) $(HelixTimeout) $(DotNetRuntimeSourceFeedKey) $(HelixCommand) $(HelixTimeout)