File: Collections\CopyOnWritePropertyDictionary_Tests.cs
Web Access
Project: ..\..\..\src\Build.UnitTests\Microsoft.Build.Engine.UnitTests.csproj (Microsoft.Build.Engine.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using Microsoft.Build.Collections;
 
using Shouldly;
 
using Xunit;
 
namespace Microsoft.Build.UnitTests.OM.Collections
{
    /// <summary>
    /// Tests for <see cref="CopyOnWritePropertyDictionary{T}"/>.
    /// </summary>
    public class CopyOnWritePropertyDictionary_Tests
    {
        [Fact]
        public void Count()
        {
            var dic = CreateInstance();
 
            dic.Count.ShouldBe(0);
 
            dic.Set(new("a"));
 
            dic.Count.ShouldBe(1);
 
            dic.Set(new("b"));
 
            dic.Count.ShouldBe(2);
 
            dic.Set(new("c"));
 
            dic.Count.ShouldBe(3);
 
            dic.Clear();
 
            dic.Count.ShouldBe(0);
        }
 
        [Fact]
        public void Indexer()
        {
            var dic = CreateInstance();
 
            MockValue a = new("a");
            MockValue b = new("b");
 
            dic["a"].ShouldBeNull();
            dic["b"].ShouldBeNull();
 
            dic["a"] = a;
 
            dic["a"].ShouldBeSameAs(a);
            dic["b"].ShouldBeNull();
 
            dic["b"] = b;
 
            dic["a"].ShouldBeSameAs(a);
            dic["b"].ShouldBeSameAs(b);
 
            // Cannot set a null value
            Assert.ThrowsAny<Exception>(() => dic["a"] = null!);
 
            // Value's key must match the specified key
            Assert.ThrowsAny<Exception>(() => dic["a"] = b);
        }
 
        [Fact]
        public void Contains()
        {
            var dic = CreateInstance();
 
            MockValue a = new("a");
            MockValue b = new("b");
 
            dic.Contains("a").ShouldBeFalse();
            dic.Contains("b").ShouldBeFalse();
 
            dic["a"] = a;
 
            dic.Contains("a").ShouldBeTrue();
            dic.Contains("b").ShouldBeFalse();
 
            dic["b"] = b;
 
            dic.Contains("a").ShouldBeTrue();
            dic.Contains("b").ShouldBeTrue();
        }
 
        [Fact]
        public void Clear()
        {
            var dic = CreateInstance("a", "b", "c");
 
            dic.Count.ShouldBe(3);
 
            dic.Clear();
 
            dic.Count.ShouldBe(0);
        }
 
        [Fact]
        public void Enumeration()
        {
            var dic = CreateInstance();
 
            MockValue a = new("a");
            MockValue b = new("b");
 
            dic.Set(a);
            dic.Set(b);
 
            dic.ShouldBeSetEquivalentTo(new[] { a, b });
        }
 
        [Fact]
        public void Equal()
        {
            var dic1 = CreateInstance("a", "b", "c");
            var dic2 = CreateInstance("a", "b", "c");
            var dic3 = CreateInstance("c", "b", "a");      // reversed order
            var dic4 = CreateInstance("a", "b");           // missing item
            var dic5 = CreateInstance("a", "b", "c", "d"); // extra item
 
            Test(dic1, dic1, true);
            Test(dic1, dic2, true);
            Test(dic1, dic3, true);
 
            Test(dic1, dic4, false);
            Test(dic1, dic5, false);
 
            static void Test(CopyOnWritePropertyDictionary<MockValue> a, CopyOnWritePropertyDictionary<MockValue> b, bool expected)
            {
                if (expected)
                {
                    // Test equality in both directions
                    a.ShouldBe(b);
                    b.ShouldBe(a);
                }
                else
                {
                    // Test equality in both directions
                    a.ShouldNotBe(b);
                    b.ShouldNotBe(a);
                }
            }
        }
 
        [Fact]
        public void Remove()
        {
            var dic = CreateInstance("a", "b", "c");
 
            dic.Remove("ZZZ").ShouldBeFalse();
 
            dic.Remove("a").ShouldBeTrue();
            dic.Remove("a").ShouldBeFalse();
            dic.Remove("b").ShouldBeTrue();
            dic.Remove("c").ShouldBeTrue();
 
            dic.Count.ShouldBe(0);
        }
 
        [Fact]
        public void ImportProperties()
        {
            var dic = CreateInstance();
 
            MockValue a = new("a");
            MockValue b = new("b");
 
            dic.ImportProperties(new[] { a, b });
 
            dic.ShouldBeSetEquivalentTo(new[] { a, b });
        }
 
        [Fact]
        public void DeepClone()
        {
            CopyOnWritePropertyDictionary<MockValue> source = CreateInstance("a", "b", "c");
            CopyOnWritePropertyDictionary<MockValue> clone = (CopyOnWritePropertyDictionary<MockValue>)source.DeepClone();
 
            source.ShouldBe(clone);
            source.ShouldNotBeSameAs(clone);
        }
 
        private static CopyOnWritePropertyDictionary<MockValue> CreateInstance(params string[] values)
        {
            CopyOnWritePropertyDictionary<MockValue> dic = new CopyOnWritePropertyDictionary<MockValue>();
 
            foreach (string value in values)
            {
                dic.Set(new(value));
            }
 
            return dic;
        }
 
        private sealed class MockValue : IKeyed, IValued, IEquatable<MockValue>, IEquatable<object>, IImmutable
        {
            public MockValue(string s) => Key = s;
 
            public string Key { get; }
 
            public string EscapedValue => Key;
 
            public bool Equals(MockValue? other)
            {
                return other != null && Key == other.Key;
            }
 
            public new bool Equals(object? other)
            {
                if (other is MockValue mv)
                {
                    return Equals(mv);
                }
                return false;
            }
        }
    }
}