1 // ***********************************************************************
2 // Copyright (c) 2014 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.Generic;
31 using NUnit.Framework.Interfaces;
33 namespace NUnit.Framework.Internal.Builders
36 /// Built-in SuiteBuilder for all types of test classes.
38 public class DefaultSuiteBuilder : ISuiteBuilder
40 // Builder we use for fixtures without any fixture attribute specified
41 private NUnitTestFixtureBuilder _defaultBuilder = new NUnitTestFixtureBuilder();
43 #region ISuiteBuilder Methods
45 /// Checks to see if the provided Type is a fixture.
46 /// To be considered a fixture, it must be a non-abstract
47 /// class with one or more attributes implementing the
48 /// IFixtureBuilder interface or one or more methods
51 /// <param name="typeInfo">The fixture type to check</param>
52 /// <returns>True if the fixture can be built, false if not</returns>
53 public bool CanBuildFrom(ITypeInfo typeInfo)
55 if (typeInfo.IsAbstract && !typeInfo.IsSealed)
58 if (typeInfo.IsDefined<IFixtureBuilder>(true))
61 // Generics must have an attribute in order to provide
62 // them with arguments to determine the specific type.
63 // TODO: What about automatic fixtures? Should there
64 // be some kind of error shown?
65 if (typeInfo.IsGenericTypeDefinition)
68 return typeInfo.HasMethodWithAttribute(typeof(IImplyFixture));
72 /// Build a TestSuite from TypeInfo provided.
74 /// <param name="typeInfo">The fixture type to build</param>
75 /// <returns>A TestSuite built from that type</returns>
76 public TestSuite BuildFrom(ITypeInfo typeInfo)
78 var fixtures = new List<TestSuite>();
82 IFixtureBuilder[] builders = GetFixtureBuilderAttributes(typeInfo);
84 foreach (var builder in builders)
85 foreach (var fixture in builder.BuildFrom(typeInfo))
86 fixtures.Add(fixture);
88 if (typeInfo.IsGenericType)
89 return BuildMultipleFixtures(typeInfo, fixtures);
91 switch (fixtures.Count)
94 return _defaultBuilder.BuildFrom(typeInfo);
98 return BuildMultipleFixtures(typeInfo, fixtures);
103 var fixture = new TestFixture(typeInfo);
104 fixture.RunState = RunState.NotRunnable;
106 if (ex is System.Reflection.TargetInvocationException)
107 ex = ex.InnerException;
108 var msg = "An exception was thrown while loading the test." + Env.NewLine + ex.ToString();
109 fixture.Properties.Add(PropertyNames.SkipReason, msg);
116 #region Helper Methods
118 private TestSuite BuildMultipleFixtures(ITypeInfo typeInfo, IEnumerable<TestSuite> fixtures)
120 TestSuite suite = new ParameterizedFixtureSuite(typeInfo);
122 foreach (var fixture in fixtures)
129 /// We look for attributes implementing IFixtureBuilder at one level
130 /// of inheritance at a time. Attributes on base classes are not used
131 /// unless there are no fixture builder attributes at all on the derived
132 /// class. This is by design.
134 /// <param name="typeInfo">The type being examined for attributes</param>
135 /// <returns>A list of the attributes found.</returns>
136 private IFixtureBuilder[] GetFixtureBuilderAttributes(ITypeInfo typeInfo)
138 IFixtureBuilder[] attrs = new IFixtureBuilder[0];
140 while (typeInfo != null && !typeInfo.IsType(typeof(object)))
142 attrs = (IFixtureBuilder[])typeInfo.GetCustomAttributes<IFixtureBuilder>(false);
144 if (attrs.Length > 0)
146 // We want to eliminate duplicates that have no args.
147 // If there is just one, no duplication is possible.
148 if (attrs.Length == 1)
151 // Count how many have arguments
153 foreach (var attr in attrs)
154 if (HasArguments(attr))
157 // If all have args, just return them
158 if (withArgs == attrs.Length)
161 // If none of them have args, return the first one
163 return new IFixtureBuilder[] { attrs[0] };
165 // Some of each - extract those with args
166 var result = new IFixtureBuilder[withArgs];
168 foreach (var attr in attrs)
169 if (HasArguments(attr))
170 result[count++] = attr;
175 typeInfo = typeInfo.BaseType;
181 private bool HasArguments(IFixtureBuilder attr)
183 // Only TestFixtureAttribute can be used without arguments
184 var temp = attr as TestFixtureAttribute;
186 return temp == null || temp.Arguments.Length > 0 || temp.TypeArgs.Length > 0;