|
// 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.
// NOTE: This code is derived from an implementation originally in dotnet/runtime:
// https://github.com/dotnet/runtime/blob/v5.0.2/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTest.cs
//
// See the commentary in https://github.com/dotnet/roslyn/pull/50156 for notes on incorporating changes made to the
// reference implementation.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis.Collections;
using Xunit;
namespace Microsoft.CodeAnalysis.UnitTests.Collections
{
public class ImmutableSegmentedDictionaryBuilderTest : ImmutableDictionaryBuilderTestBase
{
[Fact]
public void CreateBuilder()
{
var builder = ImmutableSegmentedDictionary.CreateBuilder<string, string>();
Assert.Same(EqualityComparer<string>.Default, builder.KeyComparer);
builder = ImmutableSegmentedDictionary.CreateBuilder<string, string>(StringComparer.Ordinal);
Assert.Same(StringComparer.Ordinal, builder.KeyComparer);
}
[Fact]
public void ToBuilder()
{
var builder = ImmutableSegmentedDictionary<int, string>.Empty.ToBuilder();
builder.Add(3, "3");
builder.Add(5, "5");
Assert.Equal(2, builder.Count);
Assert.True(builder.ContainsKey(3));
Assert.True(builder.ContainsKey(5));
Assert.False(builder.ContainsKey(7));
var set = builder.ToImmutable();
Assert.Equal(builder.Count, set.Count);
builder.Add(8, "8");
Assert.Equal(3, builder.Count);
Assert.Equal(2, set.Count);
Assert.True(builder.ContainsKey(8));
Assert.False(set.ContainsKey(8));
}
[Fact]
public void BuilderFromMap()
{
var set = ImmutableSegmentedDictionary<int, string>.Empty.Add(1, "1");
var builder = set.ToBuilder();
Assert.True(builder.ContainsKey(1));
builder.Add(3, "3");
builder.Add(5, "5");
Assert.Equal(3, builder.Count);
Assert.True(builder.ContainsKey(3));
Assert.True(builder.ContainsKey(5));
Assert.False(builder.ContainsKey(7));
var set2 = builder.ToImmutable();
Assert.Equal(builder.Count, set2.Count);
Assert.True(set2.ContainsKey(1));
builder.Add(8, "8");
Assert.Equal(4, builder.Count);
Assert.Equal(3, set2.Count);
Assert.True(builder.ContainsKey(8));
Assert.False(set.ContainsKey(8));
Assert.False(set2.ContainsKey(8));
}
[Fact]
public void SeveralChanges()
{
var mutable = ImmutableSegmentedDictionary<int, string>.Empty.ToBuilder();
var immutable1 = mutable.ToImmutable();
Assert.True(IsSame(immutable1, mutable.ToImmutable())); // "The Immutable property getter is creating new objects without any differences."
mutable.Add(1, "a");
var immutable2 = mutable.ToImmutable();
Assert.False(IsSame(immutable1, immutable2)); // "Mutating the collection did not reset the Immutable property."
Assert.True(IsSame(immutable2, mutable.ToImmutable())); // "The Immutable property getter is creating new objects without any differences."
Assert.Equal(1, immutable2.Count);
}
[Fact]
public void EnumerateBuilderWhileMutating()
{
var builder = ImmutableSegmentedDictionary<int, string?>.Empty
.AddRange(Enumerable.Range(1, 10).Select(n => new KeyValuePair<int, string?>(n, null)))
.ToBuilder();
Assert.Equal(
Enumerable.Range(1, 10).Select(n => new KeyValuePair<int, string?>(n, null)),
builder);
var enumerator = builder.GetEnumerator();
Assert.True(enumerator.MoveNext());
builder.Add(11, null);
// Verify that a new enumerator will succeed.
Assert.Equal(
Enumerable.Range(1, 11).Select(n => new KeyValuePair<int, string?>(n, null)),
builder);
// Try enumerating further with the previous enumerable now that we've changed the collection.
Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
enumerator.Reset();
enumerator.MoveNext(); // resetting should fix the problem.
// Verify that by obtaining a new enumerator, we can enumerate all the contents.
Assert.Equal(
Enumerable.Range(1, 11).Select(n => new KeyValuePair<int, string?>(n, null)),
builder);
}
[Fact]
public void BuilderReusesUnchangedImmutableInstances()
{
var collection = ImmutableSegmentedDictionary<int, string?>.Empty.Add(1, null);
var builder = collection.ToBuilder();
Assert.True(IsSame(collection, builder.ToImmutable())); // no changes at all.
builder.Add(2, null);
var newImmutable = builder.ToImmutable();
Assert.False(IsSame(collection, newImmutable)); // first ToImmutable with changes should be a new instance.
Assert.True(IsSame(newImmutable, builder.ToImmutable())); // second ToImmutable without changes should be the same instance.
}
[Fact]
public void AddRange()
{
var builder = ImmutableSegmentedDictionary.Create<string, int>().ToBuilder();
builder.AddRange(new Dictionary<string, int> { { "a", 1 }, { "b", 2 } });
Assert.Equal(2, builder.Count);
Assert.Equal(1, builder["a"]);
Assert.Equal(2, builder["b"]);
}
[Fact]
public void RemoveRange()
{
var builder =
ImmutableSegmentedDictionary.Create<string, int>()
.AddRange(new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } })
.ToBuilder();
Assert.Equal(3, builder.Count);
builder.RemoveRange(new[] { "a", "b" });
Assert.Equal(1, builder.Count);
Assert.Equal(3, builder["c"]);
}
[Fact]
public void Clear()
{
var builder = ImmutableSegmentedDictionary.Create<string, int>().ToBuilder();
builder.Add("five", 5);
Assert.Equal(1, builder.Count);
builder.Clear();
Assert.Equal(0, builder.Count);
}
[Fact]
public void ContainsValue()
{
var map = ImmutableSegmentedDictionary.Create<string, int>().Add("five", 5);
var builder = map.ToBuilder();
Assert.True(builder.ContainsValue(5));
Assert.False(builder.ContainsValue(4));
}
[Fact]
public void KeyComparer()
{
var builder = ImmutableSegmentedDictionary.Create<string, string>()
.Add("a", "1").Add("B", "1").ToBuilder();
Assert.Same(EqualityComparer<string>.Default, builder.KeyComparer);
Assert.True(builder.ContainsKey("a"));
Assert.False(builder.ContainsKey("A"));
builder.KeyComparer = StringComparer.OrdinalIgnoreCase;
Assert.Same(StringComparer.OrdinalIgnoreCase, builder.KeyComparer);
Assert.Equal(2, builder.Count);
Assert.True(builder.ContainsKey("a"));
Assert.True(builder.ContainsKey("A"));
Assert.True(builder.ContainsKey("b"));
var set = builder.ToImmutable();
Assert.Same(StringComparer.OrdinalIgnoreCase, set.KeyComparer);
Assert.True(set.ContainsKey("a"));
Assert.True(set.ContainsKey("A"));
Assert.True(set.ContainsKey("b"));
}
[Fact]
public void KeyComparerCollisions()
{
// First check where collisions have matching values.
var builder = ImmutableSegmentedDictionary.Create<string, string>()
.Add("a", "1").Add("A", "1").ToBuilder();
builder.KeyComparer = StringComparer.OrdinalIgnoreCase;
Assert.Equal(1, builder.Count);
Assert.True(builder.ContainsKey("a"));
var set = builder.ToImmutable();
Assert.Same(StringComparer.OrdinalIgnoreCase, set.KeyComparer);
Assert.Equal(1, set.Count);
Assert.True(set.ContainsKey("a"));
// Now check where collisions have conflicting values.
builder = ImmutableSegmentedDictionary.Create<string, string>()
.Add("a", "1").Add("A", "2").Add("b", "3").ToBuilder();
Assert.Throws<ArgumentException>(null, () => builder.KeyComparer = StringComparer.OrdinalIgnoreCase);
}
[Fact]
public void KeyComparerEmptyCollection()
{
var builder = ImmutableSegmentedDictionary.Create<string, string>()
.Add("a", "1").Add("B", "1").ToBuilder();
Assert.Same(EqualityComparer<string>.Default, builder.KeyComparer);
builder.KeyComparer = StringComparer.OrdinalIgnoreCase;
Assert.Same(StringComparer.OrdinalIgnoreCase, builder.KeyComparer);
var set = builder.ToImmutable();
Assert.Same(StringComparer.OrdinalIgnoreCase, set.KeyComparer);
}
[Fact]
public void GetValueOrDefaultOfConcreteType()
{
var empty = ImmutableSegmentedDictionary.Create<string, int>().ToBuilder();
var populated = ImmutableSegmentedDictionary.Create<string, int>().Add("a", 5).ToBuilder();
Assert.Equal(0, empty.GetValueOrDefault("a"));
Assert.Equal(1, empty.GetValueOrDefault("a", 1));
Assert.Equal(5, populated.GetValueOrDefault("a"));
Assert.Equal(5, populated.GetValueOrDefault("a", 1));
}
[Fact(Skip = "Not implemented: https://github.com/dotnet/roslyn/issues/50657")]
public void DebuggerAttributesValid()
{
DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableSegmentedDictionary.CreateBuilder<string, int>());
ImmutableSegmentedDictionary<int, string>.Builder builder = ImmutableSegmentedDictionary.CreateBuilder<int, string>();
builder.Add(1, "One");
builder.Add(2, "Two");
DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(builder);
PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute<DebuggerBrowsableAttribute>()!.State == DebuggerBrowsableState.RootHidden);
KeyValuePair<int, string>[]? items = itemProperty.GetValue(info.Instance) as KeyValuePair<int, string>[];
Assert.Equal(builder, items);
}
[Fact(Skip = "Not implemented: https://github.com/dotnet/roslyn/issues/50657")]
public static void TestDebuggerAttributes_Null()
{
Type proxyType = DebuggerAttributes.GetProxyType(ImmutableSegmentedDictionary.Create<string, int>());
TargetInvocationException tie = Assert.Throws<TargetInvocationException>(() => Activator.CreateInstance(proxyType, (object?)null));
Assert.IsType<ArgumentNullException>(tie.InnerException);
}
[Fact]
public void ToImmutableDictionary()
{
ImmutableSegmentedDictionary<int, int>.Builder builder = ImmutableSegmentedDictionary.CreateBuilder<int, int>();
builder.Add(0, 0);
builder.Add(1, 1);
builder.Add(2, 2);
var dictionary = builder.ToImmutableSegmentedDictionary();
Assert.Equal(0, dictionary[0]);
Assert.Equal(1, dictionary[1]);
Assert.Equal(2, dictionary[2]);
builder[1] = 5;
Assert.Equal(5, builder[1]);
Assert.Equal(1, dictionary[1]);
builder.Clear();
Assert.True(builder.ToImmutableSegmentedDictionary().IsEmpty);
Assert.False(dictionary.IsEmpty);
ImmutableSegmentedDictionary<int, int>.Builder? nullBuilder = null;
Assert.Throws<ArgumentNullException>("builder", () => nullBuilder!.ToImmutableSegmentedDictionary());
}
protected override IImmutableDictionary<TKey, TValue> GetEmptyImmutableDictionary<TKey, TValue>()
{
return ImmutableSegmentedDictionary.Create<TKey, TValue>();
}
protected override IImmutableDictionary<string, TValue> Empty<TValue>(StringComparer comparer)
{
return ImmutableSegmentedDictionary.Create<string, TValue>(comparer);
}
protected override bool TryGetKeyHelper<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey equalKey, out TKey actualKey)
{
return ((ImmutableSegmentedDictionary<TKey, TValue>.Builder)dictionary).TryGetKey(equalKey, out actualKey);
}
protected override IDictionary<TKey, TValue> GetBuilder<TKey, TValue>(IImmutableDictionary<TKey, TValue>? basis)
{
return ((ImmutableSegmentedDictionary<TKey, TValue>)(basis ?? GetEmptyImmutableDictionary<TKey, TValue>())).ToBuilder();
}
}
}
|