using System; using System.Collections.Generic; using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core.Composing; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Tests.Components; namespace Umbraco.Tests.Composing { [TestFixture] public class CollectionBuildersTests { private Composition _composition; [SetUp] public void Setup() { Current.Reset(); var register = RegisterFactory.Create(); _composition = new Composition(register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); } [TearDown] public void TearDown() { Current.Reset(); } [Test] public void ContainsTypes() { var builder = _composition.WithCollectionBuilder() .Append() .Append(); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); Assert.IsFalse(builder.Has()); //Assert.IsFalse(col.ContainsType()); // does not compile var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved2)); } [Test] public void CanClearBuilderBeforeCollectionIsCreated() { var builder = _composition.WithCollectionBuilder() .Append() .Append(); builder.Clear(); Assert.IsFalse(builder.Has()); Assert.IsFalse(builder.Has()); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col); } [Test] public void CannotClearBuilderOnceCollectionIsCreated() { var builder = _composition.WithCollectionBuilder() .Append() .Append(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); Assert.Throws(() => builder.Clear()); } [Test] public void CanAppendToBuilder() { var builder = _composition.WithCollectionBuilder(); builder.Append(); builder.Append(); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); Assert.IsFalse(builder.Has()); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved2)); } [Test] public void CannotAppendToBuilderOnceCollectionIsCreated() { var builder = _composition.WithCollectionBuilder(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); Assert.Throws(() => builder.Append() ); } [Test] public void CanAppendDuplicateToBuilderAndDeDuplicate() { var builder = _composition.WithCollectionBuilder(); builder.Append(); builder.Append(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1)); } [Test] public void CannotAppendInvalidTypeToBUilder() { var builder = _composition.WithCollectionBuilder(); //builder.Append(); // does not compile Assert.Throws(() => builder.Append(new[] { typeof (Resolved4) }) // throws ); } [Test] public void CanRemoveFromBuilder() { var builder = _composition.WithCollectionBuilder() .Append() .Append() .Remove(); Assert.IsTrue(builder.Has()); Assert.IsFalse(builder.Has()); Assert.IsFalse(builder.Has()); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1)); } [Test] public void CanRemoveMissingFromBuilder() { var builder = _composition.WithCollectionBuilder() .Append() .Append() .Remove(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved2)); } [Test] public void CannotRemoveFromBuilderOnceCollectionIsCreated() { var builder = _composition.WithCollectionBuilder() .Append() .Append(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); Assert.Throws(() => builder.Remove() // throws ); } [Test] public void CanInsertIntoBuilder() { var builder = _composition.WithCollectionBuilder() .Append() .Append() .Insert(); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved3), typeof(Resolved1), typeof(Resolved2)); } [Test] public void CannotInsertIntoBuilderOnceCollectionIsCreated() { var builder = _composition.WithCollectionBuilder() .Append() .Append(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); Assert.Throws(() => builder.Insert() // throws ); } [Test] public void CanInsertDuplicateIntoBuilderAndDeDuplicate() { var builder = _composition.WithCollectionBuilder() .Append() .Append() .Insert(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved2), typeof(Resolved1)); } [Test] public void CanInsertIntoEmptyBuilder() { var builder = _composition.WithCollectionBuilder(); builder.Insert(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved2)); } [Test] public void CannotInsertIntoBuilderAtWrongIndex() { var builder = _composition.WithCollectionBuilder() .Append() .Append(); Assert.Throws(() => builder.Insert(99) // throws ); Assert.Throws(() => builder.Insert(-1) // throws ); } [Test] public void CanInsertIntoBuilderBefore() { var builder = _composition.WithCollectionBuilder() .Append() .Append() .InsertBefore(); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved3), typeof(Resolved2)); } [Test] public void CanInsertIntoBuilderAfter() { var builder = _composition.WithCollectionBuilder() .Append() .Append() .InsertAfter(); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved3), typeof(Resolved2)); } [Test] public void CanInsertIntoBuilderAfterLast() { var builder = _composition.WithCollectionBuilder() .Append() .Append() .InsertAfter(); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved2), typeof(Resolved3)); } [Test] public void CannotInsertIntoBuilderBeforeOnceCollectionIsCreated() { var builder = _composition.WithCollectionBuilder() .Append() .Append(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); Assert.Throws(() => builder.InsertBefore() ); } [Test] public void CanInsertDuplicateIntoBuilderBeforeAndDeDuplicate() { var builder = _composition.WithCollectionBuilder() .Append() .Append() .InsertBefore(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved2), typeof(Resolved1)); } [Test] public void CannotInsertIntoBuilderBeforeMissing() { var builder = _composition.WithCollectionBuilder() .Append(); Assert.Throws(() => builder.InsertBefore() ); } [Test] public void ScopeBuilderCreatesScopedCollection() { _composition.WithCollectionBuilder() .Append() .Append(); // CreateCollection creates a new collection each time // but the container manages the scope, so to test the scope // the collection must come from the container var factory = _composition.CreateFactory(); using (factory.BeginScope()) { var col1 = factory.GetInstance(); AssertCollection(col1, typeof(Resolved1), typeof(Resolved2)); var col2 = factory.GetInstance(); AssertCollection(col2, typeof(Resolved1), typeof(Resolved2)); AssertSameCollection(factory, col1, col2); } } [Test] public void TransientBuilderCreatesTransientCollection() { _composition.WithCollectionBuilder() .Append() .Append(); // CreateCollection creates a new collection each time // but the container manages the scope, so to test the scope // the collection must come from the container var factory = _composition.CreateFactory(); var col1 = factory.GetInstance(); AssertCollection(col1, typeof(Resolved1), typeof(Resolved2)); var col2 = factory.GetInstance(); AssertCollection(col1, typeof(Resolved1), typeof(Resolved2)); AssertNotSameCollection(col1, col2); } [Test] public void BuilderRespectsTypesOrder() { var builder = _composition.WithCollectionBuilder() .Append() .Insert() .InsertBefore(); var factory = _composition.CreateFactory(); var col1 = builder.CreateCollection(factory); AssertCollection(col1, typeof(Resolved1), typeof(Resolved2), typeof(Resolved3)); } [Test] public void ScopeBuilderRespectsContainerScope() { _composition.WithCollectionBuilder() .Append() .Append(); // CreateCollection creates a new collection each time // but the container manages the scope, so to test the scope // the collection must come from the container TestCollection col1A, col1B; var factory = _composition.CreateFactory(); using (factory.BeginScope()) { col1A = factory.GetInstance(); col1B = factory.GetInstance(); AssertCollection(col1A, typeof(Resolved1), typeof(Resolved2)); AssertCollection(col1B, typeof(Resolved1), typeof(Resolved2)); AssertSameCollection(factory, col1A, col1B); } TestCollection col2; using (factory.BeginScope()) { col2 = factory.GetInstance(); } AssertCollection(col2, typeof(Resolved1), typeof(Resolved2)); AssertNotSameCollection(col1A, col2); } [Test] public void WeightedBuilderCreatesWeightedCollection() { var builder = _composition.WithCollectionBuilder() .Add() .Add(); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved2), typeof(Resolved1)); } [Test] public void WeightedBuilderSetWeight() { var builder = _composition.WithCollectionBuilder() .Add() .Add(); builder.SetWeight(10); var factory = _composition.CreateFactory(); var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved2)); } #region Assertions private static void AssertCollection(IEnumerable col, params Type[] expected) { var colA = col.ToArray(); Assert.AreEqual(expected.Length, colA.Length); for (var i = 0; i < expected.Length; i++) Assert.IsInstanceOf(expected[i], colA[i]); } private static void AssertSameCollection(IFactory factory, IEnumerable col1, IEnumerable col2) { Assert.AreSame(col1, col2); var col1A = col1.ToArray(); var col2A = col2.ToArray(); Assert.AreEqual(col1A.Length, col2A.Length); // Ensure each item in each collection is the same but also // resolve each item from the factory to ensure it's also the same since // it should have the same lifespan. for (var i = 0; i < col1A.Length; i++) { Assert.AreSame(col1A[i], col2A[i]); var itemA = factory.GetInstance(col1A[i].GetType()); var itemB = factory.GetInstance(col2A[i].GetType()); Assert.AreSame(itemA, itemB); } } private static void AssertNotSameCollection(IEnumerable col1, IEnumerable col2) { Assert.AreNotSame(col1, col2); var col1A = col1.ToArray(); var col2A = col2.ToArray(); Assert.AreEqual(col1A.Length, col2A.Length); for (var i = 0; i < col1A.Length; i++) { Assert.AreNotSame(col1A[i], col2A[i]); } } #endregion #region Test Objects public abstract class Resolved { } public class Resolved1 : Resolved { } [Weight(50)] // default is 100 public class Resolved2 : Resolved { } public class Resolved3 : Resolved { } public class Resolved4 // not! : Resolved { } // ReSharper disable once ClassNeverInstantiated.Local private class TestCollectionBuilder : OrderedCollectionBuilderBase { protected override TestCollectionBuilder This => this; } // ReSharper disable once ClassNeverInstantiated.Local private class TestCollectionBuilderTransient : OrderedCollectionBuilderBase { protected override TestCollectionBuilderTransient This => this; protected override Lifetime CollectionLifetime => Lifetime.Transient; // transient } // ReSharper disable once ClassNeverInstantiated.Local private class TestCollectionBuilderScope : OrderedCollectionBuilderBase { protected override TestCollectionBuilderScope This => this; protected override Lifetime CollectionLifetime => Lifetime.Scope; } // ReSharper disable once ClassNeverInstantiated.Local private class TestCollectionBuilderWeighted : WeightedCollectionBuilderBase { protected override TestCollectionBuilderWeighted This => this; } // ReSharper disable once ClassNeverInstantiated.Local private class TestCollection : BuilderCollectionBase { public TestCollection(IEnumerable items) : base(items) { } } #endregion } }