Skip to content
58 changes: 58 additions & 0 deletions PolyShim.Tests/NetCore10/ConditionalWeakTableTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Runtime.CompilerServices;
using FluentAssertions;
using Xunit;

namespace PolyShim.Tests.NetCore10;

public class ConditionalWeakTableTests
{
[Fact]
public void GetValue_Test()
{
// Arrange
var table = new ConditionalWeakTable<object, string>();
var missingKey = new object();
var existingKey = new object();
table.Add(existingKey, "hello");

// Act
var createdValue = table.GetValue(missingKey, _ => "world");
var existingValue = table.GetValue(existingKey, _ => "ignored");

// Assert
createdValue.Should().Be("world");
existingValue.Should().Be("hello");
}

[Fact]
public void TryGetValue_Test()
{
// Arrange
var table = new ConditionalWeakTable<object, string>();
var existingKey = new object();
var missingKey = new object();
table.Add(existingKey, "hello");

// Act & Assert
table.TryGetValue(existingKey, out var existingValue).Should().BeTrue();
existingValue.Should().Be("hello");

table.TryGetValue(missingKey, out _).Should().BeFalse();
}

[Fact]
public void Remove_Test()
{
// Arrange
var table = new ConditionalWeakTable<object, string>();
var existingKey = new object();
var missingKey = new object();
table.Add(existingKey, "hello");

// Act & Assert
table.Remove(existingKey).Should().BeTrue();
table.TryGetValue(existingKey, out _).Should().BeFalse();

table.Remove(missingKey).Should().BeFalse();
}
}
33 changes: 33 additions & 0 deletions PolyShim.Tests/NetCore10/WeakReferenceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using FluentAssertions;
using Xunit;

namespace PolyShim.Tests.NetCore10;

public class WeakReferenceTests
{
[Fact]
public void TryGetTarget_Test()
{
// Arrange
var obj = new object();
var reference = new WeakReference<object>(obj);

// Act & assert
reference.TryGetTarget(out var target).Should().BeTrue();
target.Should().BeSameAs(obj);
}

[Fact]
public void TryGetTarget_AfterSet_Test()
{
// Arrange
var reference = new WeakReference<object>(new object());
var obj = new object();
reference.SetTarget(obj);

// Act & assert
reference.TryGetTarget(out var target).Should().BeTrue();
target.Should().BeSameAs(obj);
}
}
38 changes: 38 additions & 0 deletions PolyShim.Tests/NetCore30/ProcessStartInfoTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Diagnostics;
using FluentAssertions;
using Xunit;

namespace PolyShim.Tests.NetCore30;

public class ProcessStartInfoTests
{
[Fact]
public void ArgumentList_Test()
{
// Arrange
var startInfo = new ProcessStartInfo { UseShellExecute = false, CreateNoWindow = true };

if (OperatingSystem.IsWindows())
{
startInfo.FileName = "powershell";
startInfo.ArgumentList.Add("-Command");
startInfo.ArgumentList.Add("exit 42");
}
else
{
startInfo.FileName = "sh";
startInfo.ArgumentList.Add("-c");
startInfo.ArgumentList.Add("exit 42");
}

using var process = new Process { StartInfo = startInfo };
process.Start();

// Act
process.WaitForExit();

// Assert
process.ExitCode.Should().Be(42);
}
}
104 changes: 104 additions & 0 deletions PolyShim/NetCore10/ConditionalWeakTable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#if (NETFRAMEWORK && !NET40_OR_GREATER) || (NETSTANDARD && !NETSTANDARD1_3_OR_GREATER)
#nullable enable
#pragma warning disable CS0436

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;

namespace System.Runtime.CompilerServices;

// https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2
#if !POLYSHIM_INCLUDE_COVERAGE
[ExcludeFromCodeCoverage]
#endif
internal sealed class ConditionalWeakTable<TKey, TValue>
where TKey : class
where TValue : class
{
public delegate TValue CreateValueCallback(TKey key);

private sealed class Entry(WeakReference key, TValue value)
{
public readonly WeakReference Key = key;
public readonly TValue Value = value;
}

private readonly List<Entry> _entries = [];
private readonly Lock _lock = new();

private void Purge()
{
for (var i = _entries.Count - 1; i >= 0; i--)
{
if (!_entries[i].Key.IsAlive)
_entries.RemoveAt(i);
}
}

public TValue GetValue(TKey key, CreateValueCallback createValueCallback)
{
using (_lock.EnterScope())
{
Purge();

foreach (var entry in _entries)
{
if (ReferenceEquals(entry.Key.Target, key))
return entry.Value;
}

var value = createValueCallback(key);
_entries.Add(new Entry(new WeakReference(key), value));

return value;
}
}

public bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value)
{
using (_lock.EnterScope())
{
Purge();

foreach (var entry in _entries)
{
if (ReferenceEquals(entry.Key.Target, key))
{
value = entry.Value;
return true;
}
}

value = default;
return false;
}
}

public void Add(TKey key, TValue value)
{
using (_lock.EnterScope())
{
Purge();
_entries.Add(new Entry(new WeakReference(key), value));
}
}

public bool Remove(TKey key)
{
using (_lock.EnterScope())
{
for (var i = 0; i < _entries.Count; i++)
{
if (ReferenceEquals(_entries[i].Key.Target, key))
{
_entries.RemoveAt(i);
return true;
}
}

return false;
}
}
}
#endif
29 changes: 29 additions & 0 deletions PolyShim/NetCore10/WeakReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#if NETFRAMEWORK && !NET45_OR_GREATER
#nullable enable
#pragma warning disable CS0436

using System.Diagnostics.CodeAnalysis;

namespace System;

// https://learn.microsoft.com/dotnet/api/system.weakreference-1
#if !POLYSHIM_INCLUDE_COVERAGE
[ExcludeFromCodeCoverage]
#endif
internal sealed class WeakReference<T>(T target, bool trackResurrection)
where T : class
{
private readonly WeakReference _reference = new(target, trackResurrection);

public WeakReference(T target)
: this(target, false) { }

public void SetTarget(T target) => _reference.Target = target;

public bool TryGetTarget([NotNullWhen(true)] out T? target)
{
target = _reference.Target as T;
return target is not null;
}
}
#endif
Loading
Loading