diff --git a/Microsoft.ML.sln b/Microsoft.ML.sln
index c9be0e726b..9d969b6be2 100644
--- a/Microsoft.ML.sln
+++ b/Microsoft.ML.sln
@@ -256,6 +256,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.DataView", "
pkg\Microsoft.Data.DataView\Microsoft.Data.DataView.symbols.nupkgproj = pkg\Microsoft.Data.DataView\Microsoft.Data.DataView.symbols.nupkgproj
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RemoteExecutorConsoleApp", "test\RemoteExecutorConsoleApp\RemoteExecutorConsoleApp.csproj", "{5E920CAC-5A28-42FB-936E-49C472130953}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -914,6 +916,18 @@ Global
{85D0CAFD-2FE8-496A-88C7-585D35B94243}.Release-Intrinsics|Any CPU.Build.0 = Release-Intrinsics|Any CPU
{85D0CAFD-2FE8-496A-88C7-585D35B94243}.Release-netfx|Any CPU.ActiveCfg = Release-netfx|Any CPU
{85D0CAFD-2FE8-496A-88C7-585D35B94243}.Release-netfx|Any CPU.Build.0 = Release-netfx|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Debug-Intrinsics|Any CPU.ActiveCfg = Debug-Intrinsics|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Debug-Intrinsics|Any CPU.Build.0 = Debug-Intrinsics|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Debug-netfx|Any CPU.ActiveCfg = Debug-netfx|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Debug-netfx|Any CPU.Build.0 = Debug-netfx|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Release-Intrinsics|Any CPU.ActiveCfg = Release-Intrinsics|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Release-Intrinsics|Any CPU.Build.0 = Release-Intrinsics|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Release-netfx|Any CPU.ActiveCfg = Release-netfx|Any CPU
+ {5E920CAC-5A28-42FB-936E-49C472130953}.Release-netfx|Any CPU.Build.0 = Release-netfx|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -996,6 +1010,7 @@ Global
{A84717CB-F11A-41C5-A74D-C0F1D47B7431} = {D3D38B03-B557-484D-8348-8BADEE4DF592}
{85D0CAFD-2FE8-496A-88C7-585D35B94243} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
{31D38B21-102B-41C0-9E0A-2FE0BF68D123} = {D3D38B03-B557-484D-8348-8BADEE4DF592}
+ {5E920CAC-5A28-42FB-936E-49C472130953} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {41165AF1-35BB-4832-A189-73060F82B01D}
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index b708cba96e..1aa4c973c7 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -6,6 +6,8 @@
net461
netcoreapp3.0
win-x64
+
+ $(AllowedReferenceRelatedFileExtensions);.runtimeconfig.json;.runtimeconfig.dev.json;.deps.json
false
true
diff --git a/test/Microsoft.ML.Benchmarks/Harness/Configs.cs b/test/Microsoft.ML.Benchmarks/Harness/Configs.cs
index c40c1e76e2..fcc4b5d419 100644
--- a/test/Microsoft.ML.Benchmarks/Harness/Configs.cs
+++ b/test/Microsoft.ML.Benchmarks/Harness/Configs.cs
@@ -38,7 +38,7 @@ protected virtual Job GetJobDefinition()
///
private IToolchain CreateToolchain()
{
-#if NET461
+#if NETFRAMEWORK
var tfm = "net461";
var csProj = CsProjClassicNetToolchain.Net461;
#else
diff --git a/test/Microsoft.ML.Benchmarks/Harness/ProjectGenerator.cs b/test/Microsoft.ML.Benchmarks/Harness/ProjectGenerator.cs
index b4987a09c9..32d5e5e5e0 100644
--- a/test/Microsoft.ML.Benchmarks/Harness/ProjectGenerator.cs
+++ b/test/Microsoft.ML.Benchmarks/Harness/ProjectGenerator.cs
@@ -29,7 +29,7 @@ public class ProjectGenerator : CsProjGenerator
public ProjectGenerator(string targetFrameworkMoniker) : base(targetFrameworkMoniker, null, null, null)
{
-#if NET461
+#if NETFRAMEWORK
runtimeIdentifier = "win-x64";
#endif
}
diff --git a/test/Microsoft.ML.Benchmarks/Microsoft.ML.Benchmarks.csproj b/test/Microsoft.ML.Benchmarks/Microsoft.ML.Benchmarks.csproj
index 0cc163e1fb..81516c3eb8 100644
--- a/test/Microsoft.ML.Benchmarks/Microsoft.ML.Benchmarks.csproj
+++ b/test/Microsoft.ML.Benchmarks/Microsoft.ML.Benchmarks.csproj
@@ -2,7 +2,7 @@
Exe
7.2
- Microsoft.ML.Benchmarks.Program
+ false
diff --git a/test/Microsoft.ML.CpuMath.PerformanceTests/Microsoft.ML.CpuMath.PerformanceTests.csproj b/test/Microsoft.ML.CpuMath.PerformanceTests/Microsoft.ML.CpuMath.PerformanceTests.csproj
index ccd6712fd3..5c7f587879 100644
--- a/test/Microsoft.ML.CpuMath.PerformanceTests/Microsoft.ML.CpuMath.PerformanceTests.csproj
+++ b/test/Microsoft.ML.CpuMath.PerformanceTests/Microsoft.ML.CpuMath.PerformanceTests.csproj
@@ -2,7 +2,7 @@
Exe
7.2
- Microsoft.ML.CpuMath.PerformanceTests.Program
+ false
netcoreapp3.0
true
diff --git a/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/Microsoft.ML.CpuMath.UnitTests.netcoreapp.csproj b/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/Microsoft.ML.CpuMath.UnitTests.netcoreapp.csproj
index a94c11f5ef..b0c85309fb 100644
--- a/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/Microsoft.ML.CpuMath.UnitTests.netcoreapp.csproj
+++ b/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/Microsoft.ML.CpuMath.UnitTests.netcoreapp.csproj
@@ -6,6 +6,7 @@
+
diff --git a/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/UnitTests.cs b/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/UnitTests.cs
index ee9ba74ea8..e68694a234 100644
--- a/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/UnitTests.cs
+++ b/test/Microsoft.ML.CpuMath.UnitTests.netcoreapp/UnitTests.cs
@@ -5,22 +5,31 @@
using System;
using System.Collections.Generic;
using Microsoft.ML.Internal.CpuMath;
+using Microsoft.ML.TestFramework;
using Xunit;
namespace Microsoft.ML.CpuMath.UnitTests
{
public class CpuMathUtilsUnitTests
{
- private readonly float[][] _testArrays;
- private readonly int[] _testIndexArray;
- private readonly AlignedArray[] _testMatrices;
- private readonly AlignedArray[] _testSrcVectors;
- private readonly AlignedArray[] _testDstVectors;
- private readonly int _vectorAlignment = CpuMathUtils.GetVectorAlignment();
- private readonly FloatEqualityComparer _comparer;
- private readonly FloatEqualityComparerForMatMul _matMulComparer;
-
- public CpuMathUtilsUnitTests()
+ private static readonly float[][] _testArrays;
+ private static readonly int[] _testIndexArray;
+ private static readonly AlignedArray[] _testMatrices;
+ private static readonly AlignedArray[] _testSrcVectors;
+ private static readonly AlignedArray[] _testDstVectors;
+ private static readonly int _vectorAlignment = CpuMathUtils.GetVectorAlignment();
+ private static readonly FloatEqualityComparer _comparer;
+ private static readonly FloatEqualityComparerForMatMul _matMulComparer;
+ private static readonly string defaultMode = "defaultMode";
+#if NETCOREAPP3_0
+ private static Dictionary DisableAvxEnvironmentVariables;
+ private static Dictionary DisableAvxAndSseEnvironmentVariables;
+ private static readonly string disableAvx = "COMPlus_EnableAvx";
+ private static readonly string disableSse = "COMPlus_EnableSse";
+ private static readonly string disableAvxAndSse = "COMPlus_EnableHWIntrinsic";
+#endif
+
+ static CpuMathUtilsUnitTests()
{
// Padded array whose length is a multiple of 4
float[] testArray1 = new float[16] { 1.96f, -2.38f, -9.76f, 13.84f, -106.37f, -26.93f, 32.45f, 3.29f, 1.96f, -2.38f, -9.76f, 13.84f, -106.37f, -26.93f, 32.45f, 3.29f };
@@ -75,433 +84,681 @@ public CpuMathUtilsUnitTests()
testDstVectorAligned2.CopyFrom(testDstVector2);
_testDstVectors = new AlignedArray[] { testDstVectorAligned1, testDstVectorAligned2 };
+
+#if NETCOREAPP3_0
+ DisableAvxEnvironmentVariables = new Dictionary()
+ {
+ { disableAvx , "0" }
+ };
+
+ DisableAvxAndSseEnvironmentVariables = new Dictionary()
+ {
+ { disableAvx , "0" },
+ { disableSse , "0" }
+ };
+#endif
}
+ private static void CheckProperFlag(string mode)
+ {
+#if NETCOREAPP3_0
+ if (mode == defaultMode)
+ {
+ Assert.True(System.Runtime.Intrinsics.X86.Avx.IsSupported);
+ Assert.True(System.Runtime.Intrinsics.X86.Sse.IsSupported);
+ }
+ else if (mode == disableAvx)
+ {
+ Assert.False(System.Runtime.Intrinsics.X86.Avx.IsSupported);
+ Assert.True(System.Runtime.Intrinsics.X86.Sse.IsSupported);
+ }
+ else if (mode == disableAvxAndSse)
+ {
+ Assert.False(System.Runtime.Intrinsics.X86.Avx.IsSupported);
+ Assert.False(System.Runtime.Intrinsics.X86.Sse.IsSupported);
+ }
+#endif
+ }
+
+ public static TheoryData> AddData() => new TheoryData>()
+ {
+ { defaultMode, "0", null },
+ { defaultMode, "1", null },
+
+#if NETCOREAPP3_0
+ { disableAvx, "0", DisableAvxEnvironmentVariables },
+ { disableAvx, "1", DisableAvxEnvironmentVariables },
+
+ { disableAvxAndSse, "0", DisableAvxAndSseEnvironmentVariables },
+ { disableAvxAndSse, "1", DisableAvxAndSseEnvironmentVariables },
+#endif
+ };
+
+ public static TheoryData> AddScaleData() => new TheoryData>()
+ {
+ { defaultMode, "0", "1.7", null },
+ { defaultMode, "1", "1.7", null },
+ { defaultMode, "0", "-1.7", null },
+ { defaultMode, "1", "-1.7", null },
+
+#if NETCOREAPP3_0
+ { disableAvx, "0", "1.7", DisableAvxEnvironmentVariables },
+ { disableAvx, "1", "1.7", DisableAvxEnvironmentVariables },
+ { disableAvx, "0", "-1.7", DisableAvxEnvironmentVariables },
+ { disableAvx, "1", "-1.7", DisableAvxEnvironmentVariables },
+
+ { disableAvxAndSse, "0", "1.7", DisableAvxAndSseEnvironmentVariables },
+ { disableAvxAndSse, "1", "1.7", DisableAvxAndSseEnvironmentVariables },
+ { disableAvxAndSse, "0", "-1.7", DisableAvxAndSseEnvironmentVariables },
+ { disableAvxAndSse, "1", "-1.7", DisableAvxAndSseEnvironmentVariables },
+#endif
+ };
+
+ public static TheoryData> MatMulData => new TheoryData>()
+ {
+ { defaultMode, "0", "0", "0", null },
+ { defaultMode, "1", "1", "0", null },
+ { defaultMode, "1", "0", "1", null },
+#if NETCOREAPP3_0
+ { disableAvx, "0", "0", "0", DisableAvxEnvironmentVariables },
+ { disableAvx, "1", "1", "0", DisableAvxEnvironmentVariables },
+ { disableAvx, "1", "0", "1", DisableAvxEnvironmentVariables },
+
+ { disableAvxAndSse , "0", "0", "0", DisableAvxAndSseEnvironmentVariables },
+ { disableAvxAndSse , "1", "1", "0", DisableAvxAndSseEnvironmentVariables },
+ { disableAvxAndSse , "1", "0", "1", DisableAvxAndSseEnvironmentVariables },
+#endif
+ };
+
[Theory]
- [InlineData(0, 0, 0, new float[] { -416.6801f, -416.6801f, -416.6801f, -416.6801f, -416.6801f, -416.6801f, -416.6801f, -416.6801f })]
- [InlineData(1, 1, 0, new float[] { 1496f, 3672f, 5848f, 8024f, 10200f, 12376f, 14552f, 16728f })]
- [InlineData(1, 0, 1, new float[] { 204f, 492f, 780f, 1068f, 1356f, 1644f, 1932f, 2220f, 2508f, 2796f, 3084f, 3372f, 3660f, 3948f, 4236f, 4524f })]
- public void MatMulTest(int matTest, int srcTest, int dstTest, float[] expected)
+ [MemberData(nameof(MatMulData))]
+ public void MatMulTest(string mode, string matTest, string srcTest, string dstTest, Dictionary environmentVariables)
{
- AlignedArray mat = _testMatrices[matTest];
- AlignedArray src = _testSrcVectors[srcTest];
- AlignedArray dst = _testDstVectors[dstTest];
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2, arg3) =>
+ {
+ CheckProperFlag(arg0);
+ AlignedArray mat = _testMatrices[int.Parse(arg1)];
+ AlignedArray src = _testSrcVectors[int.Parse(arg2)];
+ AlignedArray dst = _testDstVectors[int.Parse(arg3)];
- CpuMathUtils.MatrixTimesSource(false, mat, src, dst, dst.Size);
- float[] actual = new float[dst.Size];
- dst.CopyTo(actual, 0, dst.Size);
- Assert.Equal(expected, actual, _matMulComparer);
+ float[] expected = new float[dst.Size];
+ for (int i = 0; i < dst.Size; i++)
+ {
+ float dotProduct = 0;
+ for (int j = 0; j < src.Size; j++)
+ {
+ dotProduct += mat[i * src.Size + j] * src[j];
+ }
+ expected[i] = dotProduct;
+ }
+
+ CpuMathUtils.MatrixTimesSource(false, mat, src, dst, dst.Size);
+ float[] actual = new float[dst.Size];
+ dst.CopyTo(actual, 0, dst.Size);
+ Assert.Equal(expected, actual, _matMulComparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, matTest, srcTest, dstTest, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 0, 0, new float[] { 70.56001f, -85.68f, -351.36f, 498.24f, -3829.32f, -969.48f, 1168.2f, 118.44f })]
- [InlineData(1, 0, 1, new float[] { 2724f, 2760f, 2796f, 2832f, 2868f, 2904f, 2940f, 2976f, 3012f, 3048f, 3084f, 3120f, 3156f, 3192f, 3228f, 3264f })]
- [InlineData(1, 1, 0, new float[] { 11016f, 11152f, 11288f, 11424f, 11560f, 11696f, 11832f, 11968f })]
- public void MatMulTranTest(int matTest, int srcTest, int dstTest, float[] expected)
+ [MemberData(nameof(MatMulData))]
+ public void MatMulTranTest(string mode, string matTest, string srcTest, string dstTest, Dictionary environmentVariables)
{
- AlignedArray mat = _testMatrices[matTest];
- AlignedArray src = _testSrcVectors[srcTest];
- AlignedArray dst = _testDstVectors[dstTest];
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2, arg3) =>
+ {
+ CheckProperFlag(arg0);
+ AlignedArray mat = _testMatrices[int.Parse(arg1)];
+ AlignedArray src = _testSrcVectors[int.Parse(arg2)];
+ AlignedArray dst = _testDstVectors[int.Parse(arg3)];
- CpuMathUtils.MatrixTimesSource(true, mat, src, dst, src.Size);
- float[] actual = new float[dst.Size];
- dst.CopyTo(actual, 0, dst.Size);
- Assert.Equal(expected, actual, _matMulComparer);
+ float[] expected = new float[dst.Size];
+ for (int i = 0; i < dst.Size; i++)
+ {
+ float dotProduct = 0;
+ for (int j = 0; j < src.Size; j++)
+ {
+ dotProduct += mat[j * dst.Size + i] * src[j];
+ }
+ expected[i] = dotProduct;
+ }
+
+ CpuMathUtils.MatrixTimesSource(true, mat, src, dst, src.Size);
+ float[] actual = new float[dst.Size];
+ dst.CopyTo(actual, 0, dst.Size);
+ Assert.Equal(expected, actual, _matMulComparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, matTest, srcTest, dstTest, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 0, 0, new float[] { 38.25002f, 38.25002f, 38.25002f, 38.25002f, 38.25002f, 38.25002f, 38.25002f, 38.25002f })]
- [InlineData(1, 1, 0, new float[] { 910f, 2190f, 3470f, 4750f, 6030f, 7310f, 8590f, 9870f })]
- [InlineData(1, 0, 1, new float[] { 95f, 231f, 367f, 503f, 639f, 775f, 911f, 1047f, 1183f, 1319f, 1455f, 1591f, 1727f, 1863f, 1999f, 2135f })]
- public void MatTimesSrcSparseTest(int matTest, int srcTest, int dstTest, float[] expected)
+ [MemberData(nameof(MatMulData))]
+ public void MatTimesSrcSparseTest(string mode, string matTest, string srcTest, string dstTest, Dictionary environmentVariables)
{
- AlignedArray mat = _testMatrices[matTest];
- AlignedArray src = _testSrcVectors[srcTest];
- AlignedArray dst = _testDstVectors[dstTest];
- int[] idx = _testIndexArray;
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2, arg3) =>
+ {
+ CheckProperFlag(arg0);
+ AlignedArray mat = _testMatrices[int.Parse(arg1)];
+ AlignedArray src = _testSrcVectors[int.Parse(arg2)];
+ AlignedArray dst = _testDstVectors[int.Parse(arg3)];
+ int[] idx = _testIndexArray;
+
+ float[] expected = new float[dst.Size];
+ int limit = (int.Parse(arg2) == 0) ? 4 : 9;
+ for (int i = 0; i < dst.Size; i++)
+ {
+ float dotProduct = 0;
+ for (int j = 0; j < limit; j++)
+ {
+ int col = idx[j];
+ dotProduct += mat[i * src.Size + col] * src[col];
+ }
+ expected[i] = dotProduct;
+ }
+
+ CpuMathUtils.MatrixTimesSource(mat, idx, src, 0, 0, limit, dst, dst.Size);
+ float[] actual = new float[dst.Size];
+ dst.CopyTo(actual, 0, dst.Size);
+ Assert.Equal(expected, actual, _matMulComparer);
+ return RemoteExecutor.SuccessExitCode;
- CpuMathUtils.MatrixTimesSource(mat, idx, src, 0, 0, (srcTest == 0) ? 4 : 9, dst, dst.Size);
- float[] actual = new float[dst.Size];
- dst.CopyTo(actual, 0, dst.Size);
- Assert.Equal(expected, actual, _matMulComparer);
+ }, mode, matTest, srcTest, dstTest, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void AddScalarUTest(int test, float defaultScale)
+ [MemberData(nameof(AddScaleData))]
+ public void AddScalarUTest(string mode, string test, string scale, Dictionary environmentVariables)
{
- float[] dst = (float[])_testArrays[test].Clone();
- float[] expected = (float[])dst.Clone();
-
- for (int i = 0; i < expected.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- expected[i] += defaultScale;
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] dst = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] expected = (float[])dst.Clone();
- CpuMathUtils.Add(defaultScale, dst);
- var actual = dst;
- Assert.Equal(expected, actual, _comparer);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ expected[i] += defaultScale;
+ }
+
+ CpuMathUtils.Add(defaultScale, dst);
+ var actual = dst;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void ScaleTest(int test, float defaultScale)
+ [MemberData(nameof(AddScaleData))]
+ public void ScaleTest(string mode, string test, string scale, Dictionary environmentVariables)
{
- float[] dst = (float[])_testArrays[test].Clone();
- float[] expected = (float[])dst.Clone();
-
- for (int i = 0; i < expected.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- expected[i] *= defaultScale;
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] dst = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] expected = (float[])dst.Clone();
- CpuMathUtils.Scale(defaultScale, dst);
- var actual = dst;
- Assert.Equal(expected, actual, _comparer);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ expected[i] *= defaultScale;
+ }
+
+ CpuMathUtils.Scale(defaultScale, dst);
+ var actual = dst;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void ScaleSrcUTest(int test, float defaultScale)
+ [MemberData(nameof(AddScaleData))]
+ public void ScaleSrcUTest(string mode, string test, string scale, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- float[] dst = (float[])src.Clone();
- float[] expected = (float[])dst.Clone();
-
- for (int i = 0; i < expected.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- expected[i] *= defaultScale;
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] dst = (float[])src.Clone();
+ float[] expected = (float[])dst.Clone();
- CpuMathUtils.Scale(defaultScale, src, dst, dst.Length);
- var actual = dst;
- Assert.Equal(expected, actual, _comparer);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ expected[i] *= defaultScale;
+ }
+
+ CpuMathUtils.Scale(defaultScale, src, dst, dst.Length);
+ var actual = dst;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void ScaleAddUTest(int test, float defaultScale)
+ [MemberData(nameof(AddScaleData))]
+ public void ScaleAddUTest(string mode, string test, string scale, Dictionary environmentVariables)
{
- float[] dst = (float[])_testArrays[test].Clone();
- float[] expected = (float[])dst.Clone();
-
- for (int i = 0; i < expected.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- expected[i] = defaultScale * (dst[i] + defaultScale);
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] dst = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] expected = (float[])dst.Clone();
+
+ for (int i = 0; i < expected.Length; i++)
+ {
+ expected[i] = defaultScale * (dst[i] + defaultScale);
+ }
+
+ CpuMathUtils.ScaleAdd(defaultScale, defaultScale, dst);
+ var actual = dst;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
- CpuMathUtils.ScaleAdd(defaultScale, defaultScale, dst);
- var actual = dst;
- Assert.Equal(expected, actual, _comparer);
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void AddScaleUTest(int test, float defaultScale)
+ [MemberData(nameof(AddScaleData))]
+ public void AddScaleUTest(string mode, string test, string scale, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- float[] dst = (float[])src.Clone();
- float[] expected = (float[])dst.Clone();
-
- for (int i = 0; i < expected.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- expected[i] *= (1 + defaultScale);
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] dst = (float[])src.Clone();
+ float[] expected = (float[])dst.Clone();
- CpuMathUtils.AddScale(defaultScale, src, dst, dst.Length);
- var actual = dst;
- Assert.Equal(expected, actual, _comparer);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ expected[i] *= (1 + defaultScale);
+ }
+
+ CpuMathUtils.AddScale(defaultScale, src, dst, dst.Length);
+ var actual = dst;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void AddScaleSUTest(int test, float defaultScale)
- {
- float[] src = (float[])_testArrays[test].Clone();
- float[] dst = (float[])src.Clone();
- int[] idx = _testIndexArray;
- float[] expected = (float[])dst.Clone();
-
- CpuMathUtils.AddScale(defaultScale, src, idx, dst, idx.Length);
- for (int i = 0; i < idx.Length; i++)
+ [MemberData(nameof(AddScaleData))]
+ public void AddScaleSUTest(string mode, string test, string scale, Dictionary environmentVariables)
+ {
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- int index = idx[i];
- expected[index] += defaultScale * src[i];
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] dst = (float[])src.Clone();
+ int[] idx = _testIndexArray;
+ float[] expected = (float[])dst.Clone();
+
+ CpuMathUtils.AddScale(defaultScale, src, idx, dst, idx.Length);
+ for (int i = 0; i < idx.Length; i++)
+ {
+ int index = idx[i];
+ expected[index] += defaultScale * src[i];
+ }
- Assert.Equal(expected, dst, _comparer);
+ Assert.Equal(expected, dst, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void AddScaleCopyUTest(int test, float defaultScale)
- {
- float[] src = (float[])_testArrays[test].Clone();
- float[] dst = (float[])src.Clone();
- float[] result = (float[])dst.Clone();
- float[] expected = (float[])dst.Clone();
-
- for (int i = 0; i < expected.Length; i++)
+ [MemberData(nameof(AddScaleData))]
+ public void AddScaleCopyUTest(string mode, string test, string scale, Dictionary environmentVariables)
+ {
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- expected[i] *= (1 + defaultScale);
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] dst = (float[])src.Clone();
+ float[] result = (float[])dst.Clone();
+ float[] expected = (float[])dst.Clone();
+
+ for (int i = 0; i < expected.Length; i++)
+ {
+ expected[i] *= (1 + defaultScale);
+ }
- CpuMathUtils.AddScaleCopy(defaultScale, src, dst, result, dst.Length);
- var actual = result;
- Assert.Equal(expected, actual, _comparer);
+ CpuMathUtils.AddScaleCopy(defaultScale, src, dst, result, dst.Length);
+ var actual = result;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0)]
- [InlineData(1)]
- public void AddUTest(int test)
+ [MemberData(nameof(AddData))]
+ public void AddUTest(string mode, string test, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- float[] dst = (float[])src.Clone();
- float[] expected = (float[])src.Clone();
-
- // Ensures src and dst are different arrays
- for (int i = 0; i < dst.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
{
- dst[i] += 1;
- }
+ CheckProperFlag(arg0);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] dst = (float[])src.Clone();
+ float[] expected = (float[])src.Clone();
- for (int i = 0; i < expected.Length; i++)
- {
- expected[i] = 2 * expected[i] + 1;
- }
+ // Ensures src and dst are different arrays
+ for (int i = 0; i < dst.Length; i++)
+ {
+ dst[i] += 1;
+ }
- CpuMathUtils.Add(src, dst, dst.Length);
- var actual = dst;
- Assert.Equal(expected, actual, _comparer);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ expected[i] = 2 * expected[i] + 1;
+ }
+
+ CpuMathUtils.Add(src, dst, dst.Length);
+ var actual = dst;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions (environmentVariables));
}
[Theory]
- [InlineData(0)]
- [InlineData(1)]
- public void AddSUTest(int test)
- {
- float[] src = (float[])_testArrays[test].Clone();
- float[] dst = (float[])src.Clone();
- int[] idx = _testIndexArray;
- float[] expected = (float[])dst.Clone();
-
- expected[0] = 3.92f;
- expected[2] = -12.14f;
- expected[5] = -36.69f;
- expected[6] = 46.29f;
- expected[8] = -104.41f;
- expected[11] = -13.09f;
- expected[12] = -73.92f;
- expected[13] = -23.64f;
- expected[14] = 34.41f;
-
- CpuMathUtils.Add(src, idx, dst, idx.Length);
- var actual = dst;
- Assert.Equal(expected, actual, _comparer);
+ [MemberData(nameof(AddData))]
+ public void AddSUTest(string mode, string test, Dictionary environmentVariables)
+ {
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
+ {
+ CheckProperFlag(arg0);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] dst = (float[])src.Clone();
+ int[] idx = _testIndexArray;
+ float[] expected = (float[])dst.Clone();
+
+ expected[0] = 3.92f;
+ expected[2] = -12.14f;
+ expected[5] = -36.69f;
+ expected[6] = 46.29f;
+ expected[8] = -104.41f;
+ expected[11] = -13.09f;
+ expected[12] = -73.92f;
+ expected[13] = -23.64f;
+ expected[14] = 34.41f;
+
+ CpuMathUtils.Add(src, idx, dst, idx.Length);
+ var actual = dst;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0)]
- [InlineData(1)]
- public void MulElementWiseUTest(int test)
+ [MemberData(nameof(AddData))]
+ public void MulElementWiseUTest(string mode, string test, Dictionary environmentVariables)
{
- float[] src1 = (float[])_testArrays[test].Clone();
- float[] src2 = (float[])src1.Clone();
- float[] dst = (float[])src1.Clone();
-
- // Ensures src1 and src2 are different arrays
- for (int i = 0; i < src2.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
{
- src2[i] += 1;
- }
+ CheckProperFlag(arg1);
+ float[] src1 = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] src2 = (float[])src1.Clone();
+ float[] dst = (float[])src1.Clone();
- float[] expected = (float[])src1.Clone();
+ // Ensures src1 and src2 are different arrays
+ for (int i = 0; i < src2.Length; i++)
+ {
+ src2[i] += 1;
+ }
- for (int i = 0; i < expected.Length; i++)
- {
- expected[i] *= (1 + expected[i]);
- }
+ float[] expected = (float[])src1.Clone();
- CpuMathUtils.MulElementWise(src1, src2, dst, dst.Length);
- var actual = dst;
- Assert.Equal(expected, actual, _comparer);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ expected[i] *= (1 + expected[i]);
+ }
+
+ CpuMathUtils.MulElementWise(src1, src2, dst, dst.Length);
+ var actual = dst;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, -187.8f)]
- [InlineData(1, -191.09f)]
- public void SumTest(int test, float expected)
+ [MemberData(nameof(AddData))]
+ public void SumTest(string mode, string test, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- var actual = CpuMathUtils.Sum(src);
- Assert.Equal(expected, actual, 2);
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
+ {
+ CheckProperFlag(arg0);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float expected = 0;
+ for (int i = 0; i < src.Length; i++)
+ {
+ expected += src[i];
+ }
+
+ var actual = CpuMathUtils.Sum(src);
+ Assert.Equal(expected, actual, 2);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 26799.8752f)]
- [InlineData(1, 26789.0511f)]
- public void SumSqUTest(int test, float expected)
+ [MemberData(nameof(AddData))]
+ public void SumSqUTest(string mode, string test, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- var actual = CpuMathUtils.SumSq(src);
- Assert.Equal(expected, actual, 2);
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
+ {
+ CheckProperFlag(arg0);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float expected = 0;
+ for (int i = 0; i < src.Length; i++)
+ {
+ expected += src[i] * src[i];
+ }
+
+ var actual = CpuMathUtils.SumSq(src);
+ Assert.Equal(expected, actual, 2);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void SumSqDiffUTest(int test, float defaultScale)
+ [MemberData(nameof(AddScaleData))]
+ public void SumSqDiffUTest(string mode, string test, string scale, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- var actual = CpuMathUtils.SumSq(defaultScale, src);
-
- float expected = 0;
- for (int i = 0; i < src.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- expected += (src[i] - defaultScale) * (src[i] - defaultScale);
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ var actual = CpuMathUtils.SumSq(defaultScale, src);
- Assert.Equal(expected, actual, 2);
+ float expected = 0;
+ for (int i = 0; i < src.Length; i++)
+ {
+ expected += (src[i] - defaultScale) * (src[i] - defaultScale);
+ }
+
+ Assert.Equal(expected, actual, 2);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 393.96f)]
- [InlineData(1, 390.67f)]
- public void SumAbsUTest(int test, float expected)
+ [MemberData(nameof(AddData))]
+ public void SumAbsUTest(string mode, string test, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- var actual = CpuMathUtils.SumAbs(src);
- Assert.Equal(expected, actual, 2);
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
+ {
+ CheckProperFlag(arg0);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float expected = 0;
+ for (int i = 0; i < src.Length; i++)
+ {
+ expected += Math.Abs(src[i]);
+ }
+
+ var actual = CpuMathUtils.SumAbs(src);
+ Assert.Equal(expected, actual, 2);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void SumAbsDiffUTest(int test, float defaultScale)
+ [MemberData(nameof(AddScaleData))]
+ public void SumAbsDiffUTest(string mode, string test, string scale, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- var actual = CpuMathUtils.SumAbs(defaultScale, src);
-
- float expected = 0;
- for (int i = 0; i < src.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- expected += Math.Abs(src[i] - defaultScale);
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ var actual = CpuMathUtils.SumAbs(defaultScale, src);
+
+ float expected = 0;
+ for (int i = 0; i < src.Length; i++)
+ {
+ expected += Math.Abs(src[i] - defaultScale);
+ }
- Assert.Equal(expected, actual, 2);
+ Assert.Equal(expected, actual, 2);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 106.37f)]
- [InlineData(1, 106.37f)]
- public void MaxAbsUTest(int test, float expected)
+ [MemberData(nameof(AddData))]
+ public void MaxAbsUTest(string mode, string test, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- var actual = CpuMathUtils.MaxAbs(src);
- Assert.Equal(expected, actual, 2);
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
+ {
+ CheckProperFlag(arg0);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ var actual = CpuMathUtils.MaxAbs(src);
+
+ float expected = 0;
+ for (int i = 0; i < src.Length; i++)
+ {
+ float abs = Math.Abs(src[i]);
+ if (abs > expected)
+ {
+ expected = abs;
+ }
+ }
+
+ Assert.Equal(expected, actual, 2);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void MaxAbsDiffUTest(int test, float defaultScale)
+ [MemberData(nameof(AddScaleData))]
+ public void MaxAbsDiffUTest(string mode, string test, string scale, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- var actual = CpuMathUtils.MaxAbsDiff(defaultScale, src);
-
- float expected = 0;
- for (int i = 0; i < src.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- float abs = Math.Abs(src[i] - defaultScale);
- if (abs > expected)
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ var actual = CpuMathUtils.MaxAbsDiff(defaultScale, src);
+
+ float expected = 0;
+ for (int i = 0; i < src.Length; i++)
{
- expected = abs;
+ float abs = Math.Abs(src[i] - defaultScale);
+ if (abs > expected)
+ {
+ expected = abs;
+ }
}
- }
-
- Assert.Equal(expected, actual, 2);
+ Assert.Equal(expected, actual, 2);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 26612.0752f)]
- [InlineData(1, 26597.9611f)]
- public void DotUTest(int test, float expected)
+ [MemberData(nameof(AddData))]
+ public void DotUTest(string mode, string test, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- float[] dst = (float[])src.Clone();
-
- for (int i = 0; i < dst.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
{
- dst[i] += 1;
- }
+ CheckProperFlag(arg0);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] dst = (float[])src.Clone();
+
+ for (int i = 0; i < dst.Length; i++)
+ {
+ dst[i] += 1;
+ }
- var actual = CpuMathUtils.DotProductDense(src, dst, dst.Length);
- Assert.Equal(expected, actual, 1);
+ float expected = 0;
+ for (int i = 0; i < dst.Length; i++)
+ {
+ expected += src[i] * dst[i];
+ }
+
+ var actual = CpuMathUtils.DotProductDense(src, dst, dst.Length);
+ Assert.Equal(expected, actual, 1);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, -3406.2154f)]
- [InlineData(1, -3406.2154f)]
- public void DotSUTest(int test, float expected)
+ [MemberData(nameof(AddData))]
+ public void DotSUTest(string mode, string test, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- float[] dst = (float[])src.Clone();
- int[] idx = _testIndexArray;
-
- // Ensures src and dst are different arrays
- for (int i = 0; i < dst.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
{
- dst[i] += 1;
- }
+ CheckProperFlag(arg0);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] dst = (float[])src.Clone();
+ int[] idx = _testIndexArray;
- var actual = CpuMathUtils.DotProductSparse(src, dst, idx, idx.Length);
- Assert.Equal(expected, actual, 2);
+ // Ensures src and dst are different arrays
+ for (int i = 0; i < dst.Length; i++)
+ {
+ dst[i] += 1;
+ }
+
+ float expected = 0;
+ for (int i = 0; i < idx.Length; i++)
+ {
+ int index = idx[i];
+ expected += src[index] * dst[i];
+ }
+
+ var actual = CpuMathUtils.DotProductSparse(src, dst, idx, idx.Length);
+ Assert.Equal(expected, actual, 2);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
- [InlineData(0, 16.0f)]
- [InlineData(1, 15.0f)]
- public void Dist2Test(int test, float expected)
+ [MemberData(nameof(AddData))]
+ public void Dist2Test(string mode, string test, Dictionary environmentVariables)
{
- float[] src = (float[])_testArrays[test].Clone();
- float[] dst = (float[])src.Clone();
-
- // Ensures src and dst are different arrays
- for (int i = 0; i < dst.Length; i++)
+ RemoteExecutor.RemoteInvoke((arg0, arg1) =>
{
- dst[i] += 1;
- }
+ CheckProperFlag(arg0);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] dst = (float[])src.Clone();
+
+ // Ensures src and dst are different arrays
+ for (int i = 0; i < dst.Length; i++)
+ {
+ dst[i] += 1;
+ }
- var actual = CpuMathUtils.L2DistSquared(src, dst, dst.Length);
- Assert.Equal(expected, actual, 0);
+ float expected = 0;
+ for (int i = 0; i < dst.Length; i++)
+ {
+ float distance = src[i] - dst[i];
+ expected += distance * distance;
+ }
+
+ var actual = CpuMathUtils.L2DistSquared(src, dst, dst.Length);
+ Assert.Equal(expected, actual, 0);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, new RemoteInvokeOptions(environmentVariables));
}
[Theory]
@@ -533,51 +790,58 @@ public void ZeroMatrixItemsCoreTest(int test, int[] idx, float[] expected)
}
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void SdcaL1UpdateUTest(int test, float defaultScale)
- {
- float[] src = (float[])_testArrays[test].Clone();
- float[] v = (float[])src.Clone();
- float[] w = (float[])src.Clone();
- float[] expected = (float[])w.Clone();
-
- for (int i = 0; i < expected.Length; i++)
+ [MemberData(nameof(AddScaleData))]
+ public void SdcaL1UpdateUTest(string mode, string test, string scale, Dictionary environmentVariables)
+ {
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- float value = src[i] * (1 + defaultScale);
- expected[i] = Math.Abs(value) > defaultScale ? (value > 0 ? value - defaultScale : value + defaultScale) : 0;
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] v = (float[])src.Clone();
+ float[] w = (float[])src.Clone();
+ float[] expected = (float[])w.Clone();
+
+ for (int i = 0; i < expected.Length; i++)
+ {
+ float value = src[i] * (1 + defaultScale);
+ expected[i] = Math.Abs(value) > defaultScale ? (value > 0 ? value - defaultScale : value + defaultScale) : 0;
+ }
- CpuMathUtils.SdcaL1UpdateDense(defaultScale, src.Length, src, defaultScale, v, w);
- var actual = w;
- Assert.Equal(expected, actual, _comparer);
+ CpuMathUtils.SdcaL1UpdateDense(defaultScale, src.Length, src, defaultScale, v, w);
+ var actual = w;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
+
[Theory]
- [InlineData(0, 1.7f)]
- [InlineData(1, 1.7f)]
- [InlineData(0, -1.7f)]
- [InlineData(1, -1.7f)]
- public void SdcaL1UpdateSUTest(int test, float defaultScale)
- {
- float[] src = (float[])_testArrays[test].Clone();
- float[] v = (float[])src.Clone();
- float[] w = (float[])src.Clone();
- int[] idx = _testIndexArray;
- float[] expected = (float[])w.Clone();
-
- for (int i = 0; i < idx.Length; i++)
+ [MemberData(nameof(AddScaleData))]
+ public void SdcaL1UpdateSUTest(string mode, string test, string scale, Dictionary environmentVariables)
+ {
+ RemoteExecutor.RemoteInvoke((arg0, arg1, arg2) =>
{
- int index = idx[i];
- float value = v[index] + src[i] * defaultScale;
- expected[index] = Math.Abs(value) > defaultScale ? (value > 0 ? value - defaultScale : value + defaultScale) : 0;
- }
+ CheckProperFlag(arg0);
+ float defaultScale = float.Parse(arg2);
+ float[] src = (float[])_testArrays[int.Parse(arg1)].Clone();
+ float[] v = (float[])src.Clone();
+ float[] w = (float[])src.Clone();
+ int[] idx = _testIndexArray;
+ float[] expected = (float[])w.Clone();
+
+ for (int i = 0; i < idx.Length; i++)
+ {
+ int index = idx[i];
+ float value = v[index] + src[i] * defaultScale;
+ expected[index] = Math.Abs(value) > defaultScale ? (value > 0 ? value - defaultScale : value + defaultScale) : 0;
+ }
- CpuMathUtils.SdcaL1UpdateSparse(defaultScale, idx.Length, src, idx, defaultScale, v, w);
- var actual = w;
- Assert.Equal(expected, actual, _comparer);
+ CpuMathUtils.SdcaL1UpdateSparse(defaultScale, idx.Length, src, idx, defaultScale, v, w);
+ var actual = w;
+ Assert.Equal(expected, actual, _comparer);
+ return RemoteExecutor.SuccessExitCode;
+ }, mode, test, scale, new RemoteInvokeOptions(environmentVariables));
}
}
diff --git a/test/Microsoft.ML.CpuMath.UnitTests.netstandard/Microsoft.ML.CpuMath.UnitTests.netstandard.csproj b/test/Microsoft.ML.CpuMath.UnitTests.netstandard/Microsoft.ML.CpuMath.UnitTests.netstandard.csproj
index 00ba8546b1..55e79969a5 100644
--- a/test/Microsoft.ML.CpuMath.UnitTests.netstandard/Microsoft.ML.CpuMath.UnitTests.netstandard.csproj
+++ b/test/Microsoft.ML.CpuMath.UnitTests.netstandard/Microsoft.ML.CpuMath.UnitTests.netstandard.csproj
@@ -2,6 +2,7 @@
+
diff --git a/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs b/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs
index 67bc430575..b3c2faf3be 100644
--- a/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs
+++ b/test/Microsoft.ML.TestFramework/BaseTestBaseline.cs
@@ -34,7 +34,7 @@ public abstract partial class BaseTestBaseline : BaseTestClass
protected BaseTestBaseline(ITestOutputHelper output) : base(output)
{
-#if NET461
+#if NETFRAMEWORK
NotFullFramework = false;
LessThanNetCore30AndNotFullFramework = false;
#endif
diff --git a/test/Microsoft.ML.TestFramework/BaseTestClass.cs b/test/Microsoft.ML.TestFramework/BaseTestClass.cs
index 3d8f1795a1..fbb85bcf2f 100644
--- a/test/Microsoft.ML.TestFramework/BaseTestClass.cs
+++ b/test/Microsoft.ML.TestFramework/BaseTestClass.cs
@@ -26,7 +26,7 @@ static BaseTestClass()
private static string GetRepoRoot()
{
-#if NET461
+#if NETFRAMEWORK
string directory = AppDomain.CurrentDomain.BaseDirectory;
#else
string directory = AppContext.BaseDirectory;
@@ -51,7 +51,7 @@ public BaseTestClass(ITestOutputHelper output)
//correct results that are on en-US locale.
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
-#if NET461
+#if NETFRAMEWORK
// Substring is removing initial "file:///" from the codebase string.
var currentAssemblyLocation = new FileInfo(Directory.GetParent(typeof(BaseTestClass).Assembly.CodeBase.Substring(8)).FullName);
#else
diff --git a/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj b/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj
index f355684c18..a8a0ad7000 100644
--- a/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj
+++ b/test/Microsoft.ML.TestFramework/Microsoft.ML.TestFramework.csproj
@@ -16,6 +16,7 @@
+
diff --git a/test/Microsoft.ML.TestFramework/PasteArguments.cs b/test/Microsoft.ML.TestFramework/PasteArguments.cs
new file mode 100644
index 0000000000..5898815d11
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/PasteArguments.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 System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.ML.TestFramework
+{
+ internal static partial class PasteArguments
+ {
+ internal static void AppendArgument(StringBuilder stringBuilder, string argument)
+ {
+ if (stringBuilder.Length != 0)
+ {
+ stringBuilder.Append(' ');
+ }
+
+ // Parsing rules for non-argv[0] arguments:
+ // - Backslash is a normal character except followed by a quote.
+ // - 2N backslashes followed by a quote ==> N literal backslashes followed by unescaped quote
+ // - 2N+1 backslashes followed by a quote ==> N literal backslashes followed by a literal quote
+ // - Parsing stops at first whitespace outside of quoted region.
+ // - (post 2008 rule): A closing quote followed by another quote ==> literal quote, and parsing remains in quoting mode.
+ if (argument.Length != 0 && ContainsNoWhitespaceOrQuotes(argument))
+ {
+ // Simple case - no quoting or changes needed.
+ stringBuilder.Append(argument);
+ }
+ else
+ {
+ stringBuilder.Append(Quote);
+ int idx = 0;
+ while (idx < argument.Length)
+ {
+ char c = argument[idx++];
+ if (c == Backslash)
+ {
+ int numBackSlash = 1;
+ while (idx < argument.Length && argument[idx] == Backslash)
+ {
+ idx++;
+ numBackSlash++;
+ }
+
+ if (idx == argument.Length)
+ {
+ // We'll emit an end quote after this so must double the number of backslashes.
+ stringBuilder.Append(Backslash, numBackSlash * 2);
+ }
+ else if (argument[idx] == Quote)
+ {
+ // Backslashes will be followed by a quote. Must double the number of backslashes.
+ stringBuilder.Append(Backslash, numBackSlash * 2 + 1);
+ stringBuilder.Append(Quote);
+ idx++;
+ }
+ else
+ {
+ // Backslash will not be followed by a quote, so emit as normal characters.
+ stringBuilder.Append(Backslash, numBackSlash);
+ }
+
+ continue;
+ }
+
+ if (c == Quote)
+ {
+ // Escape the quote so it appears as a literal. This also guarantees that we won't end up generating a closing quote followed
+ // by another quote (which parses differently pre-2008 vs. post-2008.)
+ stringBuilder.Append(Backslash);
+ stringBuilder.Append(Quote);
+ continue;
+ }
+
+ stringBuilder.Append(c);
+ }
+
+ stringBuilder.Append(Quote);
+ }
+ }
+
+ private static bool ContainsNoWhitespaceOrQuotes(string s)
+ {
+ for (int i = 0; i < s.Length; i++)
+ {
+ char c = s[i];
+ if (char.IsWhiteSpace(c) || c == Quote)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private const char Quote = '\"';
+ private const char Backslash = '\\';
+ ///
+ /// Repastes a set of arguments into a linear string that parses back into the originals under pre- or post-2008 VC parsing rules.
+ /// The rules for parsing the executable name (argv[0]) are special, so you must indicate whether the first argument actually is argv[0].
+ ///
+ internal static string Paste(IEnumerable arguments, bool pasteFirstArgumentUsingArgV0Rules)
+ {
+ var stringBuilder = new StringBuilder();
+
+ foreach (string argument in arguments)
+ {
+ if (pasteFirstArgumentUsingArgV0Rules)
+ {
+ pasteFirstArgumentUsingArgV0Rules = false;
+
+ // Special rules for argv[0]
+ // - Backslash is a normal character.
+ // - Quotes used to include whitespace characters.
+ // - Parsing ends at first whitespace outside quoted region.
+ // - No way to get a literal quote past the parser.
+
+ bool hasWhitespace = false;
+ foreach (char c in argument)
+ {
+ if (c == Quote)
+ {
+ throw new ApplicationException("The argv[0] argument cannot include a double quote.");
+ }
+ if (char.IsWhiteSpace(c))
+ {
+ hasWhitespace = true;
+ }
+ }
+ if (argument.Length == 0 || hasWhitespace)
+ {
+ stringBuilder.Append(Quote);
+ stringBuilder.Append(argument);
+ stringBuilder.Append(Quote);
+ }
+ else
+ {
+ stringBuilder.Append(argument);
+ }
+ }
+ else
+ {
+ AppendArgument(stringBuilder, argument);
+ }
+ }
+
+ return stringBuilder.ToString();
+ }
+ }
+}
diff --git a/test/Microsoft.ML.TestFramework/RemoteExecutor.cs b/test/Microsoft.ML.TestFramework/RemoteExecutor.cs
new file mode 100644
index 0000000000..17c4d6e15a
--- /dev/null
+++ b/test/Microsoft.ML.TestFramework/RemoteExecutor.cs
@@ -0,0 +1,204 @@
+// 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.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Microsoft.ML.TestFramework
+{
+ ///
+ /// Base class used for all tests that need to spawn a remote process.
+ /// Most of the code has been taken from RemoteExecutorTestBase class in the corefx repo.
+ ///
+ public static class RemoteExecutor
+ {
+ /// The name of the test console app.
+ public static readonly string TestConsoleApp = Path.GetFullPath(@"RemoteExecutorConsoleApp.dll");
+#if NETFRAMEWORK
+ public static readonly string HostRunner = Path.GetFullPath(@"RemoteExecutorConsoleApp.exe");
+ private static readonly string ExtraParameter = "";
+#else
+ public static readonly string HostRunner = Process.GetCurrentProcess().MainModule.FileName;
+ private static readonly string ExtraParameter = TestConsoleApp;
+#endif
+ /// A timeout (milliseconds) after which a wait on a remote operation should be considered a failure.
+ public const int FailWaitTimeoutMilliseconds = 60 * 1000;
+
+ /// The exit code returned when the test process exits successfully.
+ public const int SuccessExitCode = 42;
+
+ /// Invokes the method from this assembly in another process using the specified arguments.
+ /// The method to invoke.
+ /// The first argument to pass to the method.
+ /// The second argument to pass to the method.
+ /// Options to use for the invocation.
+ public static void RemoteInvoke(
+ Func method,
+ string arg1, string arg2,
+ RemoteInvokeOptions options = null)
+ {
+ RemoteInvoke(GetMethodInfo(method), new[] { arg1, arg2 }, options);
+ }
+
+ /// Invokes the method from this assembly in another process using the specified arguments.
+ /// The method to invoke.
+ /// The first argument to pass to the method.
+ /// The second argument to pass to the method.
+ /// The third argument to pass to the method.
+ /// Options to use for the invocation.
+ public static void RemoteInvoke(
+ Func method,
+ string arg1, string arg2, string arg3,
+ RemoteInvokeOptions options = null)
+ {
+ RemoteInvoke(GetMethodInfo(method), new[] { arg1, arg2, arg3 }, options);
+ }
+
+ /// Invokes the method from this assembly in another process using the specified arguments.
+ /// The method to invoke.
+ /// The first argument to pass to the method.
+ /// The second argument to pass to the method.
+ /// The third argument to pass to the method.
+ /// The fourth argument to pass to the method.
+ /// Options to use for the invocation.
+ public static void RemoteInvoke(
+ Func method,
+ string arg1, string arg2, string arg3, string arg4,
+ RemoteInvokeOptions options = null)
+ {
+ RemoteInvoke(GetMethodInfo(method), new[] { arg1, arg2, arg3, arg4 }, options);
+ }
+
+ /// Invokes the method from this assembly in another process using the specified arguments.
+ /// The method to invoke.
+ /// The arguments to pass to the method.
+ /// Options to use for the invocation.
+ /// true if this function should paste the arguments (e.g. surrounding with quotes); false if that responsibility is left up to the caller.
+ private static void RemoteInvoke(MethodInfo method, string[] args, RemoteInvokeOptions options, bool pasteArguments = true)
+ {
+ options = options ?? new RemoteInvokeOptions();
+
+ // Verify the specified method returns an int (the exit code) or nothing,
+ // and that if it accepts any arguments, they're all strings.
+ Assert.True(method.ReturnType == typeof(void) || method.ReturnType == typeof(int) || method.ReturnType == typeof(Task));
+ Assert.All(method.GetParameters(), pi => Assert.Equal(typeof(string), pi.ParameterType));
+
+ // And make sure it's in this assembly. This isn't critical, but it helps with deployment to know
+ // that the method to invoke is available because we're already running in this assembly.
+ Type t = method.DeclaringType;
+ Assembly a = t.GetTypeInfo().Assembly;
+
+ // Start the other process and return a wrapper for it to handle its lifetime and exit checking.
+ ProcessStartInfo psi = options.StartInfo;
+ psi.UseShellExecute = false;
+
+ // If we need the host (if it exists), use it, otherwise target the console app directly.
+ string metadataArgs = PasteArguments.Paste(new string[] { a.FullName, t.FullName, method.Name, options.ExceptionFile }, pasteFirstArgumentUsingArgV0Rules: false);
+ string passedArgs = pasteArguments ? PasteArguments.Paste(args, pasteFirstArgumentUsingArgV0Rules: false) : string.Join(" ", args);
+ string testConsoleAppArgs = ExtraParameter + " " + metadataArgs + " " + passedArgs;
+
+ psi.FileName = HostRunner;
+ psi.Arguments = testConsoleAppArgs;
+
+ // Return the handle to the process, which may or not be started
+ CheckProcess(Process.Start(psi), options);
+ }
+
+ private static void CheckProcess(Process process, RemoteInvokeOptions Options)
+ {
+ if (process != null)
+ {
+ // A bit unorthodox to do throwing operations in a Dispose, but by doing it here we avoid
+ // needing to do this in every derived test and keep each test much simpler.
+ try
+ {
+ Assert.True(process.WaitForExit(Options.TimeOut),
+ $"Timed out after {Options.TimeOut}ms waiting for remote process {process.Id}");
+
+ if (File.Exists(Options.ExceptionFile))
+ {
+ throw new RemoteExecutionException(File.ReadAllText(Options.ExceptionFile));
+ }
+
+ if (Options.CheckExitCode)
+ {
+ int expected = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Options.ExpectedExitCode : unchecked((sbyte)Options.ExpectedExitCode);
+ int actual = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? process.ExitCode : unchecked((sbyte)process.ExitCode);
+
+ Assert.True(expected == actual, $"Exit code was {process.ExitCode} but it should have been {Options.ExpectedExitCode}");
+ }
+ }
+ finally
+ {
+ if (File.Exists(Options.ExceptionFile))
+ {
+ File.Delete(Options.ExceptionFile);
+ }
+
+ // Cleanup
+ try { process.Kill(); }
+ catch { } // ignore all cleanup errors
+
+ process.Dispose();
+ process = null;
+ }
+ }
+ }
+
+ private sealed class RemoteExecutionException : XunitException
+ {
+ internal RemoteExecutionException(string stackTrace) : base("Remote process failed with an unhandled exception.", stackTrace) { }
+ }
+
+ private static MethodInfo GetMethodInfo(Delegate d)
+ {
+ // RemoteInvoke doesn't support marshaling state on classes associated with
+ // the delegate supplied (often a display class of a lambda). If such fields
+ // are used, odd errors result, e.g. NullReferenceExceptions during the remote
+ // execution. Try to ward off the common cases by proactively failing early
+ // if it looks like such fields are needed.
+ if (d.Target != null)
+ {
+ // The only fields on the type should be compiler-defined (any fields of the compiler's own
+ // making generally include '<' and '>', as those are invalid in C# source). Note that this logic
+ // may need to be revised in the future as the compiler changes, as this relies on the specifics of
+ // actually how the compiler handles lifted fields for lambdas.
+ Type targetType = d.Target.GetType();
+ Assert.All(
+ targetType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
+ fi => Assert.True(fi.Name.IndexOf('<') != -1, $"Field marshaling is not supported by {nameof(RemoteInvoke)}: {fi.Name}"));
+ }
+
+ return d.GetMethodInfo();
+ }
+ }
+
+ /// Options used with RemoteInvoke.
+ public sealed class RemoteInvokeOptions
+ {
+ public RemoteInvokeOptions(Dictionary environmentVariables = null)
+ {
+ if (environmentVariables != null)
+ {
+ foreach (var item in environmentVariables)
+ {
+ StartInfo.EnvironmentVariables.Add(item.Key, item.Value);
+ }
+ }
+ }
+
+ public ProcessStartInfo StartInfo { get; set; } = new ProcessStartInfo();
+ public bool CheckExitCode { get; set; } = true;
+ public int TimeOut { get; set; } = RemoteExecutor.FailWaitTimeoutMilliseconds;
+ public int ExpectedExitCode { get; set; } = RemoteExecutor.SuccessExitCode;
+ public string ExceptionFile { get; } = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ }
+}
diff --git a/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs b/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs
new file mode 100644
index 0000000000..13adabd9f6
--- /dev/null
+++ b/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs
@@ -0,0 +1,124 @@
+// 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;
+using System.Runtime.ExceptionServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace RemoteExecutorConsoleApp
+{
+ class Program
+ {
+ static int Main(string[] args)
+ {
+ if (args.Length < 4)
+ {
+ Console.Error.WriteLine("Usage: {0} assemblyName typeName methodName exceptionFile [additionalArgs]", typeof(Program).GetTypeInfo().Assembly.GetName().Name);
+ Environment.Exit(-1);
+ return -1;
+ }
+
+ string assemblyName = args[0];
+ string typeName = args[1];
+ string methodName = args[2];
+ string exceptionFile = args[3];
+ string[] additionalArgs = args.Length > 4 ?
+ Subarray(args, 4, args.Length - 4) :
+ Array.Empty();
+
+ // Load the specified assembly, type, and method, then invoke the method.
+ // The program's exit code is the return value of the invoked method.
+ Assembly a = null;
+ Type t = null;
+ MethodInfo mi = null;
+ object instance = null;
+ int exitCode = 0;
+
+ try
+ {
+ // Create the test class if necessary
+ try
+ {
+ a = Assembly.Load(assemblyName);
+ }
+ catch(FileNotFoundException)
+ {
+ a = Assembly.LoadFrom(assemblyName.Split(',')[0] + ".dll");
+ }
+
+ t = a.GetType(typeName);
+ mi = t.GetTypeInfo().GetDeclaredMethod(methodName);
+ if (!mi.IsStatic)
+ {
+ instance = Activator.CreateInstance(t);
+ }
+
+ // Invoke the test
+ object result = mi.Invoke(instance, additionalArgs);
+
+ if (result is Task task)
+ {
+ exitCode = task.GetAwaiter().GetResult();
+ }
+ else if (result is int exit)
+ {
+ exitCode = exit;
+ }
+ }
+ catch (Exception exc)
+ {
+ if (exc is TargetInvocationException && exc.InnerException != null)
+ exc = exc.InnerException;
+
+ var output = new StringBuilder();
+ output.AppendLine();
+ output.AppendLine("Child exception:");
+ output.AppendLine(" " + exc);
+ output.AppendLine();
+ output.AppendLine("Child process:");
+ output.AppendLine(string.Format(" {0} {1} {2}", a, t, mi));
+ output.AppendLine();
+
+ if (additionalArgs.Length > 0)
+ {
+ output.AppendLine("Child arguments:");
+ output.AppendLine(" " + string.Join(", ", additionalArgs));
+ }
+
+ File.WriteAllText(exceptionFile, output.ToString());
+
+ ExceptionDispatchInfo.Capture(exc).Throw();
+ }
+ finally
+ {
+ (instance as IDisposable)?.Dispose();
+ }
+
+ // Use Exit rather than simply returning the exit code so that we forcibly shut down
+ // the process even if there are foreground threads created by the operation that would
+ // end up keeping the process alive potentially indefinitely.
+ try
+ {
+ Environment.Exit(exitCode);
+ }
+ catch (PlatformNotSupportedException)
+ {
+ }
+
+ return exitCode;
+ }
+
+ private static T[] Subarray(T[] arr, int offset, int count)
+ {
+ var newArr = new T[count];
+ Array.Copy(arr, offset, newArr, 0, count);
+ return newArr;
+ }
+ }
+}
diff --git a/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj b/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj
new file mode 100644
index 0000000000..4aee4bc458
--- /dev/null
+++ b/test/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj
@@ -0,0 +1,7 @@
+
+
+
+ Exe
+ false
+
+