Skip to content

Commit

Permalink
Added tests for SocketException using --explore
Browse files Browse the repository at this point in the history
  • Loading branch information
jnm2 committed May 10, 2017
1 parent 1015e49 commit 4f3672e
Show file tree
Hide file tree
Showing 12 changed files with 393 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using NUnit.Framework;

namespace NUnit.Tests
{
public class AccessesCurrentTestContextDuringDiscovery
{
public static int[] TestCases()
{
var _ = TestContext.CurrentContext;
return new[] { 0 };
}

[TestCaseSource(nameof(TestCases))]
public void Access_by_TestCaseSource(int arg) { }

[Test]
public void Access_by_ValueSource([ValueSource(nameof(TestCases))] int arg) { }
}
}
1 change: 1 addition & 0 deletions src/NUnitEngine/mock-assembly/mock-assembly.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="AccessesCurrentTestContextDuringDiscovery.cs" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
Expand Down
21 changes: 21 additions & 0 deletions src/NUnitEngine/nunit.engine.tests/Helpers/On.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Threading;

namespace NUnit.Engine.Tests.Helpers
{
public static class On
{
public static IDisposable Dispose(Action action) => new OnDisposeAction(action);
private sealed class OnDisposeAction : IDisposable
{
private Action action;

public OnDisposeAction(Action action)
{
this.action = action;
}

public void Dispose() => Interlocked.Exchange(ref action, null)?.Invoke();
}
}
}
116 changes: 116 additions & 0 deletions src/NUnitEngine/nunit.engine.tests/Helpers/ProcessUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace NUnit.Engine.Tests.Helpers
{
public static class ProcessUtils
{
public static ProcessResult Run(ProcessStartInfo startInfo)
{
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.CreateNoWindow = true;

using (var process = new Process { StartInfo = startInfo })
{
var standardStreamData = new List<StandardStreamData>();
var currentData = new StringBuilder();
var currentDataIsError = false;

process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null) return;
if (currentDataIsError)
{
if (currentData.Length != 0)
standardStreamData.Add(new StandardStreamData(currentDataIsError, currentData.ToString()));
currentData = new StringBuilder();
currentDataIsError = false;
}
currentData.AppendLine(e.Data);
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data == null) return;
if (!currentDataIsError)
{
if (currentData.Length != 0)
standardStreamData.Add(new StandardStreamData(currentDataIsError, currentData.ToString()));
currentData = new StringBuilder();
currentDataIsError = true;
}
currentData.AppendLine(e.Data);
};

process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();

if (currentData.Length != 0)
standardStreamData.Add(new StandardStreamData(currentDataIsError, currentData.ToString()));

return new ProcessResult(process.ExitCode, standardStreamData.ToArray());
}
}

[DebuggerDisplay("{ToString(),nq}")]
public struct ProcessResult
{
public ProcessResult(int exitCode, StandardStreamData[] standardStreamData)
{
ExitCode = exitCode;
StandardStreamData = standardStreamData;
}

public int ExitCode { get; }
public StandardStreamData[] StandardStreamData { get; }

public override string ToString() => ToString(true);

/// <param name="showStreamSource">If true, appends "[stdout] " or "[stderr] " to the beginning of each line.</param>
public string ToString(bool showStreamSource)
{
var r = new StringBuilder("Exit code ").Append(ExitCode);

if (StandardStreamData.Length != 0) r.AppendLine();

foreach (var data in StandardStreamData)
{
if (showStreamSource)
{
var lines = data.Data.Split(new[] { Environment.NewLine }, StringSplitOptions.None);

// StandardStreamData.Data always ends with a blank line, so skip that
for (var i = 0; i < lines.Length - 1; i++)
r.Append(data.IsError ? "[stderr] " : "[stdout] ").AppendLine(lines[i]);
}
else
{
r.Append(data.Data);
}
}

return r.ToString();
}
}

[DebuggerDisplay("{ToString(),nq}")]
public struct StandardStreamData
{
public StandardStreamData(bool isError, string data)
{
IsError = isError;
Data = data;
}

public bool IsError { get; }
public string Data { get; }

public override string ToString() => (IsError ? "[stderr] " : "[stdout] ") + Data;
}
}
}
36 changes: 36 additions & 0 deletions src/NUnitEngine/nunit.engine.tests/Helpers/ShadowCopyUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace NUnit.Engine.Tests.Helpers
{
public static class ShadowCopyUtils
{
/// <summary>
/// Returns the transitive closure of assemblies needed to copy.
/// Deals with assembly names rather than paths to work with runners that shadow copy.
/// </summary>
public static ICollection<string> GetAllNeededAssemblyPaths(params string[] assemblyNames)
{
var r = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

var dependencies = StackEnumerator.Create(
from assemblyName in assemblyNames
select new AssemblyName(assemblyName));

foreach (var dependencyName in dependencies)
{
var dependency = Assembly.ReflectionOnlyLoad(dependencyName.FullName);

if (!dependency.GlobalAssemblyCache && r.Add(Path.GetFullPath(dependency.Location)))
{
dependencies.Recurse(dependency.GetReferencedAssemblies());
}
}

return r;
}
}
}
76 changes: 76 additions & 0 deletions src/NUnitEngine/nunit.engine.tests/Helpers/StackEnumerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace NUnit.Engine.Tests.Helpers
{
public static class StackEnumerator
{
public static StackEnumerator<T> Create<T>(params T[] initial) => new StackEnumerator<T>(initial);
public static StackEnumerator<T> Create<T>(IEnumerable<T> initial) => new StackEnumerator<T>(initial);
public static StackEnumerator<T> Create<T>(IEnumerator<T> initial) => new StackEnumerator<T>(initial);
}

public sealed class StackEnumerator<T> : IDisposable
{
private readonly Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>();
private IEnumerator<T> current;

public bool MoveNext()
{
while (!current.MoveNext())
{
current.Dispose();
if (stack.Count == 0) return false;
current = stack.Pop();
}

return true;
}

public T Current => current.Current;

public void Recurse(IEnumerator<T> newCurrent)
{
if (newCurrent == null) return;
stack.Push(current);
current = newCurrent;
}
public void Recurse(IEnumerable<T> newCurrent)
{
if (newCurrent == null) return;
Recurse(newCurrent.GetEnumerator());
}
public void Recurse(params T[] newCurrent)
{
Recurse((IEnumerable<T>)newCurrent);
}

public StackEnumerator(IEnumerator<T> initial)
{
current = initial ?? Enumerable.Empty<T>().GetEnumerator();
}
public StackEnumerator(IEnumerable<T> initial) : this(initial?.GetEnumerator())
{
}
public StackEnumerator(params T[] initial) : this((IEnumerable<T>)initial)
{
}

// Foreach support
[EditorBrowsable(EditorBrowsableState.Never)]
public StackEnumerator<T> GetEnumerator()
{
return this;
}

public void Dispose()
{
current.Dispose();
foreach (var item in stack)
item.Dispose();
stack.Clear();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.IO;
using NUnit.Engine.Tests.Helpers;

namespace NUnit.Engine.Tests.Integration
{
internal sealed class DirectoryWithNeededAssemblies : IDisposable
{
public string Directory { get; }

/// <summary>
/// Returns the transitive closure of assemblies needed to copy.
/// Deals with assembly names rather than paths to work with runners that shadow copy.
/// </summary>
public DirectoryWithNeededAssemblies(params string[] assemblyNames)
{
Directory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
System.IO.Directory.CreateDirectory(Directory);

foreach (var neededAssembly in ShadowCopyUtils.GetAllNeededAssemblyPaths(assemblyNames))
{
File.Copy(neededAssembly, Path.Combine(Directory, Path.GetFileName(neededAssembly)));
}
}

public void Dispose()
{
System.IO.Directory.Delete(Directory, true);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using NUnit.Framework;

namespace NUnit.Engine.Tests.Integration
{
[TestFixture, Category("Integration")]
public abstract class IntegrationTests
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.IO;
using NUnit.Framework;

namespace NUnit.Engine.Tests.Integration
{
internal sealed class MockAssemblyInDirectoryWithFramework : IDisposable
{
private readonly DirectoryWithNeededAssemblies directory;

public string MockAssemblyDll => Path.Combine(directory.Directory, "mock-assembly.dll");

public MockAssemblyInDirectoryWithFramework()
{
directory = new DirectoryWithNeededAssemblies("mock-assembly");

Assert.That(Path.Combine(directory.Directory, "nunit.framework.dll"), Does.Exist, "This test must be run with nunit.framework.dll in the same directory as the mock assembly.");
}

public void Dispose()
{
directory.Dispose();
}
}
}
25 changes: 25 additions & 0 deletions src/NUnitEngine/nunit.engine.tests/Integration/RemoteAgentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Diagnostics;
using NUnit.Engine.Tests.Helpers;
using NUnit.Framework;

namespace NUnit.Engine.Tests.Integration
{
public sealed class RemoteAgentTests : IntegrationTests
{
[Test]
public void Explore_does_not_throw_SocketException()
{
using (var runner = new RunnerInDirectoryWithoutFramework())
using (var test = new MockAssemblyInDirectoryWithFramework())
{
for (var times = 0; times < 3; times++)
{
var result = ProcessUtils.Run(new ProcessStartInfo(runner.ConsoleExe, $"--explore \"{test.MockAssemblyDll}\""));
Assert.That(result.StandardStreamData, Has.None.With.Property("Data").Contains("System.Net.Sockets.SocketException"));
Assert.That(result.StandardStreamData, Has.None.With.Property("IsError").True);
Assert.That(result, Has.Property("ExitCode").Zero);
}
}
}
}
}
Loading

0 comments on commit 4f3672e

Please sign in to comment.