using System; using System.Utility; using Chernobyl.Attribution; using Chernobyl.Collections.Generic.Event; using Chernobyl.Reflection; using Chernobyl.Values; using NUnit.Framework; namespace Chernobyl.Dependency { [TestFixture, Description("Tests for the SetAttribute types.")] public class SetAttributeTests { // Note: Originally there was a test here to ensure that SetAttribute // threw an InvalidTypeException when the ParameterInfo.Member is not a // MethodBase. The only way to hit this exception was to pass in a parameter // of a static constructor. However, this was removed since static constructor's // cannot have parameters and therefore the parameter could not be passed // to the SetAttribute.Process(ParameterInfo) method. If it was not // static then the SetAttribute.Process(ParameterInfo) method would // throw an exception stating that the constructor was not static (which // is good). Therefore the test was removed as the exception was unreachable, // at least for now in C# code. /////////////////////////////////////////////////////////////////////// #region Exception Tests /////////////////////////////////////////////////////////////////////// [Test, Description("SetAttribute throws a MethodException when " + "ParameterInfo.Member is not static.")] public virtual void MethodExceptionWhenMethodIsNotStatic() { Assert.Throws(() => ProcessReturnValue(NotStaticMethod)); } [Test, Description("SetAttribute throws MethodException when return is " + "not attributed with an IKeyAttribute.")] public virtual void MethodExceptionWhenReturnIsNotAttributedWithKey() { Assert.Throws(() => ProcessReturnValue(ReturnNotKeyAttributedMethod)); } #endregion /////////////////////////////////////////////////////////////////////// #region Return Tests /////////////////////////////////////////////////////////////////////// [Test, Description("SetAttribute can return the values from one " + "static method that has no dependencies.")] public void ReturnOfStaticMethodIsStored() { VerifyContainerValue(null, new Guid(Ids.FirstName), Returns.FirstName, AttributedType.FirstNameMethod + " result"); } [Test, Description("SetAttribute can return the values from one " + "static method that has a dependency.")] public void ReturnOfDependentStaticMethodIsStored() { VerifyContainerValue(null, new Guid(Ids.Name), Returns.FirstName + Returns.LastName, AttributedType.NameMethod + " result"); } [Test, Description("SetAttribute can return the values from multiple " + "static methods that have multiple dependencies.")] public void ReturnOfDependentStaticMethodsAreStored() { VerifyContainerValue(null, typeof(string), Returns.Combined, AttributedType.CombineMethod + " result"); } #endregion /////////////////////////////////////////////////////////////////////// #region Utilities /////////////////////////////////////////////////////////////////////// /// /// Clears the container to prevent shared state between tests. Then /// grabs the attributed type and processes it to store its values in /// the container. /// /// The container the values are expected to be in. IEventDictionary> PrepareContainer() { // Clear the container to prevent shared state screwing up the tests. // Processing the type will leave the instance stored in the default // instance of the StaticStorageAttribute. var container = StaticStorageAttribute.Default.Container; container.Clear(); // Create the attributed type and process it. var type = CreateAttributedType(); ProcessMethodsAttribute processMethods = new ProcessMethodsAttribute(); processMethods.Process(type); return container; } /// /// Verifies that the number of items in the /// is the expected amount. /// /// The instance whose count is to be verified. void VerifyContainerCount(IEventDictionary> container) { // Verify the number of stored items. container.Count.IsEqualTo(AttributedType.ReturnValueCount, "Number of stored return values"); } /// /// Verifies that the expected value is in the container. /// /// The instance to look in for the value. If /// this value is null then the container will be automatically retrieved /// and is count will be verified. /// The key to use when searching for the value. /// The expected value of the value found. /// The name of the value. void VerifyContainerValue(IEventDictionary> container, object key, object expectedValue, string valueName) { if (container == null) { container = PrepareContainer(); VerifyContainerCount(container); } // Verify that the value expected is in the container. IValue combineResult = container[key]; combineResult.Request((sender, args) => { args.NewValue.IsEqualTo(expectedValue, valueName); Assert.Pass(); }); Assert.Fail("The return value of the static method was not returned."); } /// /// A utility method to help with testing. Creates the tested instance /// using and uses it to /// /// The name of the method whose return value is to /// be processed. void ProcessReturnValue(string method) { var set = CreateSetAttribute(); var methodInfo = GetType().GetMethod(method); set.Process(methodInfo.ReturnParameter); } /// /// The name of . /// public const string ConstructorName = "SetAttributeTests"; /// /// The return values expected from the static methods in this class. /// public struct Returns { public const string LastName = "Smith"; public const string FirstName = "Bob"; public const string Hello = "Hello "; public const string Combined = Hello + FirstName + LastName; } /// /// Identifiers for the instances returned from static in this class. /// public struct Ids { public const string Hello = "C2B5220D-FFC3-4F91-836A-497E44FB4B18"; public const string Name = "14322572-1ED6-4369-B2C9-1E99DB4530C6"; public const string FirstName = "16F148CA-D270-41EC-A905-8965661A5893"; public const string LastName = "1C04A7B9-8716-4A74-B379-EAB7DE6876A3"; } /// /// The name of the method. /// public const string NotStaticMethod = "NotStatic"; [return: Set, Guid(Ids.Hello)] public string NotStatic() { return string.Empty; } /// /// The name of the method. /// public const string ReturnNotKeyAttributedMethod = "ReturnNotKeyAttributed"; [return: Set] public static string ReturnNotKeyAttributed() { return string.Empty; } class AttributedType { /// /// The name of the method. /// public const string CombineMethod = "Combine"; /// /// The name of the method. /// public const string FirstNameMethod = "FirstName"; /// /// The name of the method. /// public const string NameMethod = "Name"; /// /// Number of return values in this type. /// public const int ReturnValueCount = 5; [ProcessParameters] [return: Set, Type(typeof(string))] public static string Combine([Get, Guid(Ids.Hello)]string hello, [Get, Guid(Ids.Name)]string name) { return hello + name; } [ProcessParameters] [return: Set, Guid(Ids.Hello)] public static string Hello() { return Returns.Hello; } [ProcessParameters] [return: Set, Guid(Ids.Name)] public static string Name([Get, Guid(Ids.FirstName)]string firstName, [Get, Guid(Ids.LastName)]string lastName) { return firstName + lastName; } [ProcessParameters] [return: Set, Guid(Ids.FirstName)] public static string FirstName() { return Returns.FirstName; } [ProcessParameters] [return: Set, Guid(Ids.LastName)] public static string LastName() { return Returns.LastName; } } #endregion /////////////////////////////////////////////////////////////////////// #region Overridables /////////////////////////////////////////////////////////////////////// /// /// Returns the instance to be tested. /// /// The instance to be tested. public virtual SetAttribute CreateSetAttribute() { return new SetAttribute(); } /// /// Returns a that contains methods that have been /// attributed with the s that are to be tested. /// /// The instance containing the setted methods. public virtual Type CreateAttributedType() { return typeof (AttributedType); } #endregion } }