Skip to content
This repository was archived by the owner on Mar 19, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions ILMerge.Tests/BaselineTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Reflection;
using NUnit.Framework;

namespace ILMerging.Tests
{
[TestFixture]
public sealed class BaselineTests
{
[Test]
public void Single_input()
{
var ilMerge = new ILMerge();
ilMerge.SetUpInputAssemblyForTest(Assembly.GetExecutingAssembly());
ilMerge.MergeToTempFileForTest(".dll");
}
}
}
24 changes: 24 additions & 0 deletions ILMerge.Tests/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Linq;
using System.Reflection;
using ILMerging.Tests.Helpers;

namespace ILMerging.Tests
{
internal static class Extensions
{
public static void SetUpInputAssemblyForTest(this ILMerge ilMerge, Assembly inputAssembly)
{
ilMerge.SetSearchDirectories(ShadowCopyUtils.GetTransitiveClosureDirectories(inputAssembly).ToArray());
ilMerge.SetInputAssemblies(new[] { inputAssembly.Location });
}

public static void MergeToTempFileForTest(this ILMerge ilMerge, string outputExtension)
{
using (var outputFile = TempFile.WithExtension(outputExtension))
{
ilMerge.OutputFile = outputFile;
ilMerge.Merge();
}
}
}
}
115 changes: 115 additions & 0 deletions ILMerge.Tests/Helpers/ProcessUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

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

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.Clear();
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.Clear();
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;
}
}
}
43 changes: 43 additions & 0 deletions ILMerge.Tests/Helpers/ShadowCopyUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace ILMerging.Tests.Helpers
{
public static class ShadowCopyUtils
{
public static IEnumerable<Assembly> GetTransitiveClosure(params Assembly[] assemblies)
{
var finishedAssemblies = new HashSet<Assembly>();

using (var en = StackEnumerator.Create(assemblies))
foreach (var assembly in en)
{
if (!finishedAssemblies.Add(assembly)) continue;
yield return assembly;
en.Recurse(assembly.GetReferencedAssemblies().Select(Assembly.Load));
}
}

/// <summary>
/// Necessary because of test runners like ReSharper and NCrunch which shadow copy each assembly to an isolated directory
/// </summary>
public static IEnumerable<string> GetTransitiveClosureDirectories(params Assembly[] assemblies)
{
return GetTransitiveClosure(assemblies)
.Where(_ => !_.GlobalAssemblyCache)
.Select(_ => Path.GetDirectoryName(_.Location))
.Distinct(StringComparer.OrdinalIgnoreCase);
}

/// <summary>
/// Necessary because of test runners like ReSharper and NCrunch which shadow copy each assembly to an isolated directory
/// </summary>
public static string GenerateILMergeLibCliSwitches(params Assembly[] assemblies)
{
return string.Join(" ", GetTransitiveClosureDirectories(Assembly.GetExecutingAssembly()).Select(_ => $"/lib:\"{_}\""));
}
}
}
153 changes: 153 additions & 0 deletions ILMerge.Tests/Helpers/StackEnumerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace ILMerging.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 static StackEnumerator<TContext, T> Create<TContext, T>(TContext initialContext, params T[] initial) => new StackEnumerator<TContext, T>(initialContext, initial);
public static StackEnumerator<TContext, T> Create<TContext, T>(TContext initialContext, IEnumerable<T> initial) => new StackEnumerator<TContext, T>(initialContext, initial);
public static StackEnumerator<TContext, T> Create<TContext, T>(TContext initialContext, IEnumerator<T> initial) => new StackEnumerator<TContext, T>(initialContext, 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 ?? System.Linq.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();
}
}

public sealed class StackEnumerator<TContext, T> : IDisposable
{
public struct ContextCurrent
{
public TContext Context { get; }

public T Current { get; }

public ContextCurrent(TContext context, T current)
{
Context = context;
Current = current;
}
}

private readonly Stack<Tuple<TContext, IEnumerator<T>>> stack = new Stack<Tuple<TContext, IEnumerator<T>>>();
private Tuple<TContext, IEnumerator<T>> current;

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

return true;
}

public ContextCurrent Current => new ContextCurrent(current.Item1, current.Item2.Current);

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

public StackEnumerator(TContext initialContext, IEnumerator<T> initial)
{
current = Tuple.Create(initialContext, initial ?? System.Linq.Enumerable.Empty<T>().GetEnumerator());
}
public StackEnumerator(TContext initialContext, IEnumerable<T> initial) : this(initialContext, initial?.GetEnumerator())
{
}
public StackEnumerator(TContext initialContext, params T[] initial) : this(initialContext, (IEnumerable<T>)initial)
{
}

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

public void Dispose()
{
current.Item2.Dispose();
foreach (var item in stack)
item.Item2.Dispose();
stack.Clear();
}
}
}
41 changes: 41 additions & 0 deletions ILMerge.Tests/Helpers/TempFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace ILMerging.Tests.Helpers
{
[DebuggerDisplay("{ToString(),nq}")]
public sealed class TempFile : IDisposable
{
public TempFile() : this(System.IO.Path.GetTempFileName())
{
}

public TempFile(string path)
{
this.path = path;
}

public static TempFile WithExtension(string extension)
{
return new TempFile(
System.IO.Path.Combine(
System.IO.Path.GetTempPath(),
System.IO.Path.ChangeExtension(System.IO.Path.GetRandomFileName(), extension)));
}

private string path;
public string Path => path;

public static implicit operator string(TempFile tempFile) => tempFile.path;

public override string ToString() => path;

public void Dispose()
{
var path = Interlocked.Exchange(ref this.path, null);
if (path != null) File.Delete(path);
}
}
}
Loading