1 // ***********************************************************************
2 // Copyright (c) 2008-2015 Charlie Poole
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
25 #define NUNIT_FRAMEWORK
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.Reflection;
33 using NUnit.Compatibility;
34 using NUnit.Framework.Interfaces;
35 using NUnit.Framework.Internal;
36 using NUnit.Framework.Internal.Builders;
38 namespace NUnit.Framework
41 /// TestCaseSourceAttribute indicates the source to be used to
42 /// provide test fixture instances for a test class.
44 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
45 public class TestFixtureSourceAttribute : NUnitAttribute, IFixtureBuilder
47 private readonly NUnitTestFixtureBuilder _builder = new NUnitTestFixtureBuilder();
50 /// Error message string is public so the tests can use it
52 public const string MUST_BE_STATIC = "The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.";
57 /// Construct with the name of the method, property or field that will provide data
59 /// <param name="sourceName">The name of a static method, property or field that will provide data.</param>
60 public TestFixtureSourceAttribute(string sourceName)
62 this.SourceName = sourceName;
66 /// Construct with a Type and name
68 /// <param name="sourceType">The Type that will provide data</param>
69 /// <param name="sourceName">The name of a static method, property or field that will provide data.</param>
70 public TestFixtureSourceAttribute(Type sourceType, string sourceName)
72 this.SourceType = sourceType;
73 this.SourceName = sourceName;
77 /// Construct with a Type
79 /// <param name="sourceType">The type that will provide data</param>
80 public TestFixtureSourceAttribute(Type sourceType)
82 this.SourceType = sourceType;
90 /// The name of a the method, property or fiend to be used as a source
92 public string SourceName { get; private set; }
95 /// A Type to be used as a source
97 public Type SourceType { get; private set; }
100 /// Gets or sets the category associated with every fixture created from
101 /// this attribute. May be a single category or a comma-separated list.
103 public string Category { get; set; }
107 #region IFixtureBuilder Members
110 /// Construct one or more TestFixtures from a given Type,
111 /// using available parameter data.
113 /// <param name="typeInfo">The TypeInfo for which fixures are to be constructed.</param>
114 /// <returns>One or more TestFixtures as TestSuite</returns>
115 public IEnumerable<TestSuite> BuildFrom(ITypeInfo typeInfo)
117 Type sourceType = SourceType ?? typeInfo.Type;
119 foreach (TestFixtureParameters parms in GetParametersFor(sourceType))
120 yield return _builder.BuildFrom(typeInfo, parms);
125 #region Helper Methods
128 /// Returns a set of ITestFixtureData items for use as arguments
129 /// to a parameterized test fixture.
131 /// <param name="sourceType">The type for which data is needed.</param>
132 /// <returns></returns>
133 public IEnumerable<ITestFixtureData> GetParametersFor(Type sourceType)
135 List<ITestFixtureData> data = new List<ITestFixtureData>();
139 IEnumerable source = GetTestFixtureSource(sourceType);
143 foreach (object item in source)
145 var parms = item as ITestFixtureData;
149 object[] args = item as object[];
152 args = new object[] { item };
155 parms = new TestFixtureParameters(args);
158 if (this.Category != null)
159 foreach (string cat in this.Category.Split(new char[] { ',' }))
160 parms.Properties.Add(PropertyNames.Category, cat);
169 data.Add(new TestFixtureParameters(ex));
175 private IEnumerable GetTestFixtureSource(Type sourceType)
177 // Handle Type implementing IEnumerable separately
178 if (SourceName == null)
179 return Reflect.Construct(sourceType) as IEnumerable;
181 MemberInfo[] members = sourceType.GetMember(SourceName,
182 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
184 if (members.Length == 1)
186 MemberInfo member = members[0];
188 var field = member as FieldInfo;
190 return field.IsStatic
191 ? (IEnumerable)field.GetValue(null)
192 : SourceMustBeStaticError();
194 var property = member as PropertyInfo;
195 if (property != null)
196 return property.GetGetMethod(true).IsStatic
197 ? (IEnumerable)property.GetValue(null, null)
198 : SourceMustBeStaticError();
200 var m = member as MethodInfo;
203 ? (IEnumerable)m.Invoke(null, null)
204 : SourceMustBeStaticError();
210 private static IEnumerable SourceMustBeStaticError()
212 var parms = new TestFixtureParameters();
213 parms.RunState = RunState.NotRunnable;
214 parms.Properties.Set(PropertyNames.SkipReason, MUST_BE_STATIC);
215 return new TestFixtureParameters[] { parms };