Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -553,9 +553,7 @@ public void TearDown()
roslynIdMap = null;
if (tempDllPath != null && File.Exists(tempDllPath))
{
try
{ File.Delete(tempDllPath); }
catch { /* best effort cleanup */ }
Tester.RepeatOnIOError(() => File.Delete(tempDllPath));
}
}

Expand Down
25 changes: 18 additions & 7 deletions ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1161,26 +1161,37 @@ public static async Task RunAndCompareOutput(string testFileName, string outputF
}
}

internal static void RepeatOnIOError(Action action, int numTries = 5)
/// <summary>
/// Retries an IO operation that a virus scanner, the indexer or a compiler that has only
/// just exited can still hold a handle on, backing off between attempts. Every caller is
/// deleting a temp file, so a failure is reported and swallowed: the file is left behind,
/// which costs nothing, where throwing out of a fixture teardown reports an error for a
/// run that otherwise passed.
/// </summary>
internal static void RepeatOnIOError(Action action, int numTries = 8)
{
for (int i = 0; i < numTries - 1; i++)
Exception lastError = null;
int delay = 10;
for (int i = 0; i < numTries; i++)
{
try
{
action();
return;
}
catch (IOException)
catch (IOException ex)
{
lastError = ex;
}
catch (UnauthorizedAccessException)
catch (UnauthorizedAccessException ex)
{
// potential virus scanner problem
lastError = ex;
}
Thread.Sleep(10);
Thread.Sleep(delay);
delay = Math.Min(delay * 2, 500);
}
// If the last try still fails, don't catch the exception
action();
TestContext.Out.WriteLine($"Cleanup could not delete a temp file after {numTries} tries, leaving it behind: {lastError?.Message}");
}

public static async Task SignAssembly(string assemblyPath, string keyFilePath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@
<None Include="TestCases\Pretty\NullableRefTypes.cs" />
<Compile Remove="TestCases\Ugly\AggressiveScalarReplacementOfAggregates.Expected.cs" />
<None Include="TestCases\Ugly\AggressiveScalarReplacementOfAggregates.Expected.cs" />
<Compile Remove="TestCases\Ugly\QualifiedMethodGroup.Expected.cs" />
<None Include="TestCases\Ugly\QualifiedMethodGroup.Expected.cs" />
<Compile Remove="TestCases\Ugly\BaseQualifier.Expected.cs" />
<None Include="TestCases\Ugly\BaseQualifier.Expected.cs" />
<Compile Remove="TestCases\Ugly\NoArrayInitializers.Expected.cs" />
<None Include="TestCases\Ugly\NoArrayInitializers.Expected.cs" />
<Compile Remove="TestCases\Ugly\NoAutomaticProperties.Expected.cs" />
Expand Down
6 changes: 6 additions & 0 deletions ICSharpCode.Decompiler.Tests/Output/LocationsInAstTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

Expand Down Expand Up @@ -55,6 +56,7 @@ public class LocationsInAstTests
const string SequencePointSampleName = "ICSharpCode.Decompiler.Tests.TestCases.LocationsInAst.SequencePointSample";

static CompilerResults compiledSamples;
static readonly List<MetadataFile> openedModules = new List<MetadataFile>();

[OneTimeSetUp]
public void CompileSamples()
Expand All @@ -66,13 +68,17 @@ public void CompileSamples()
[OneTimeTearDown]
public void DeleteCompiledSamples()
{
// The module keeps the assembly mapped, so it has to go before the file can.
foreach (var module in openedModules)
module.Dispose();
compiledSamples?.DeleteTempFiles();
}

static CSharpDecompiler CreateDecompiler(out DecompilerSettings settings)
{
string assemblyPath = compiledSamples.PathToAssembly;
var module = new PEFile(assemblyPath);
openedModules.Add(module);
var resolver = new UniversalAssemblyResolver(assemblyPath, false, module.Metadata.DetectTargetFrameworkId());
settings = new DecompilerSettings();
return new CSharpDecompiler(module, resolver, settings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Tests.Helpers;
using ICSharpCode.Decompiler.TypeSystem;

using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -107,7 +108,7 @@ public void TearDown()
foreach (var module in openedModules)
module.Dispose();
if (Directory.Exists(tempDirectory))
Directory.Delete(tempDirectory, recursive: true);
Tester.RepeatOnIOError(() => Directory.Delete(tempDirectory, recursive: true));
}

/// <summary>
Expand Down
40 changes: 40 additions & 0 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,46 @@ public void BaseQualifiers()
}
}

internal class OverridingParent : Parent
{
#if LEGACY_CSC
public virtual int Prop {
get {
return 1;
}
}
#else
public virtual int Prop => 1;
#endif

public override void Virtual()
{
}
}

internal class OverridingChild : OverridingParent
{
#if LEGACY_CSC
public override int Prop {
get {
return 2;
}
}
#else
public override int Prop => 2;
#endif

// Neither member is overridden here, so the unqualified spelling would bind to the
// same one and the qualifier looks redundant - but Virtual is an override and Prop is
// overridden further down, so dropping it would dispatch to this type's member
// instead of the one the base call names.
public void BaseQualifiersOnOverriddenMembers()
{
base.Virtual();
base.Prop.ToString();
}
}

#pragma warning disable CS8981
private class i
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;

namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly;

public class BaseQualifierBase : BaseQualifierRoot
{
public int NonVirtualProperty { get; set; }
public virtual int VirtualProperty { get; set; }
public override int AbstractProperty { get; set; }
public override int OverriddenProperty { get; set; }
public sealed override int SealedProperty { get; set; }

public void NonVirtualMethod()
{
}

public virtual void VirtualMethod()
{
}
}

public class BaseQualifierDerived : BaseQualifierBase
{
public int ReadNonVirtualProperty()
{
return NonVirtualProperty;
}

public int ReadSealedProperty()
{
return SealedProperty;
}

public Action NonVirtualMethodGroup()
{
return NonVirtualMethod;
}

public int ReadVirtualProperty()
{
return base.VirtualProperty;
}

public int ReadAbstractProperty()
{
return base.AbstractProperty;
}

public int ReadOverriddenProperty()
{
return base.OverriddenProperty;
}

public Action VirtualMethodGroup()
{
return base.VirtualMethod;
}
}

public abstract class BaseQualifierRoot
{
public abstract int AbstractProperty { get; set; }
public virtual int OverriddenProperty { get; set; }
public virtual int SealedProperty { get; set; }
}
68 changes: 68 additions & 0 deletions ICSharpCode.Decompiler.Tests/TestCases/Ugly/BaseQualifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;

namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
{
public abstract class BaseQualifierRoot
{
public abstract int AbstractProperty { get; set; }
public virtual int OverriddenProperty { get; set; }
public virtual int SealedProperty { get; set; }
}

public class BaseQualifierBase : BaseQualifierRoot
{
public int NonVirtualProperty { get; set; }
public virtual int VirtualProperty { get; set; }
public override int AbstractProperty { get; set; }
public override int OverriddenProperty { get; set; }
public sealed override int SealedProperty { get; set; }

public void NonVirtualMethod()
{
}

public virtual void VirtualMethod()
{
}
}

public class BaseQualifierDerived : BaseQualifierBase
{
// Dropping "base." leaves the reference dispatching virtually. That reaches the same
// member unless the member can be overridden, so only the overridable ones keep it.
public int ReadNonVirtualProperty()
{
return base.NonVirtualProperty;
}

public int ReadSealedProperty()
{
return base.SealedProperty;
}

public Action NonVirtualMethodGroup()
{
return base.NonVirtualMethod;
}

public int ReadVirtualProperty()
{
return base.VirtualProperty;
}

public int ReadAbstractProperty()
{
return base.AbstractProperty;
}

public int ReadOverriddenProperty()
{
return base.OverriddenProperty;
}

public Action VirtualMethodGroup()
{
return base.VirtualMethod;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly;

public class QualifiedMethodGroup
{
public int Value;

public event EventHandler Changed;

public void Subscribe()
{
this.Changed += new EventHandler(this.OnChanged);
this.Value = 1;
}

private void OnChanged(object sender, EventArgs e)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
{
public class QualifiedMethodGroup
{
public event EventHandler Changed;

public int Value;

public void Subscribe()
{
Changed += OnChanged;
Value = 1;
}

private void OnChanged(object sender, EventArgs e)
{
}
}
}
Loading
Loading