1 // ***********************************************************************
2 // Copyright (c) 2007 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
31 using NUnit.Framework.Interfaces;
32 using NUnit.Framework.Internal.Filters;
34 namespace NUnit.Framework.Internal
37 /// Interface to be implemented by filters applied to tests.
38 /// The filter applies when running the test, after it has been
39 /// loaded, since this is the only time an ITest exists.
42 public abstract class TestFilter : ITestFilter
45 /// Unique Empty filter.
47 public readonly static TestFilter Empty = new EmptyFilter();
50 /// Indicates whether this is the EmptyFilter
54 get { return this is TestFilter.EmptyFilter; }
58 /// Indicates whether this is a top-level filter,
59 /// not contained in any other filter.
61 public bool TopLevel { get; set; }
64 /// Determine if a particular test passes the filter criteria. The default
65 /// implementation checks the test itself, its parents and any descendants.
67 /// Derived classes may override this method or any of the Match methods
68 /// to change the behavior of the filter.
70 /// <param name="test">The test to which the filter is applied</param>
71 /// <returns>True if the test passes the filter, otherwise false</returns>
72 public virtual bool Pass(ITest test)
74 return Match(test) || MatchParent(test) || MatchDescendant(test);
78 /// Determine if a test matches the filter expicitly. That is, it must
79 /// be a direct match of the test itself or one of it's children.
81 /// <param name="test">The test to which the filter is applied</param>
82 /// <returns>True if the test matches the filter explicityly, otherwise false</returns>
83 public virtual bool IsExplicitMatch(ITest test)
85 return Match(test) || MatchDescendant(test);
89 /// Determine whether the test itself matches the filter criteria, without
90 /// examining either parents or descendants. This is overridden by each
91 /// different type of filter to perform the necessary tests.
93 /// <param name="test">The test to which the filter is applied</param>
94 /// <returns>True if the filter matches the any parent of the test</returns>
95 public abstract bool Match(ITest test);
98 /// Determine whether any ancestor of the test matches the filter criteria
100 /// <param name="test">The test to which the filter is applied</param>
101 /// <returns>True if the filter matches the an ancestor of the test</returns>
102 public bool MatchParent(ITest test)
104 return test.Parent != null && (Match(test.Parent) || MatchParent(test.Parent));
108 /// Determine whether any descendant of the test matches the filter criteria.
110 /// <param name="test">The test to be matched</param>
111 /// <returns>True if at least one descendant matches the filter criteria</returns>
112 protected virtual bool MatchDescendant(ITest test)
114 if (test.Tests == null)
117 foreach (ITest child in test.Tests)
119 if (Match(child) || MatchDescendant(child))
126 private static readonly char[] COMMA = new char[] { ',' };
129 /// Create a TestFilter instance from an xml representation.
131 public static TestFilter FromXml(string xmlText)
133 TNode topNode = TNode.FromXml(xmlText);
135 if (topNode.Name != "filter")
136 throw new Exception("Expected filter element at top level");
138 int count = topNode.ChildNodes.Count;
140 TestFilter filter = count == 0
143 ? FromXml(topNode.FirstChild)
146 filter.TopLevel = true;
152 /// Create a TestFilter from it's TNode representation
154 public static TestFilter FromXml(TNode node)
156 bool isRegex = node.Attributes["re"] == "1";
162 var andFilter = new AndFilter();
163 foreach (var childNode in node.ChildNodes)
164 andFilter.Add(FromXml(childNode));
168 var orFilter = new OrFilter();
169 foreach (var childNode in node.ChildNodes)
170 orFilter.Add(FromXml(childNode));
174 return new NotFilter(FromXml(node.FirstChild));
177 return new IdFilter(node.Value);
180 return new FullNameFilter(node.Value) { IsRegex = isRegex };
183 return new TestNameFilter(node.Value) { IsRegex = isRegex };
186 return new MethodNameFilter(node.Value) { IsRegex = isRegex };
189 return new ClassNameFilter(node.Value) { IsRegex = isRegex };
192 return new CategoryFilter(node.Value) { IsRegex = isRegex };
195 string name = node.Attributes["name"];
197 return new PropertyFilter(name, node.Value) { IsRegex = isRegex };
201 throw new ArgumentException("Invalid filter element: " + node.Name, "xmlNode");
205 /// Nested class provides an empty filter - one that always
206 /// returns true when called. It never matches explicitly.
209 private class EmptyFilter : TestFilter
211 public override bool Match( ITest test )
216 public override bool Pass( ITest test )
221 public override bool IsExplicitMatch( ITest test )
226 public override TNode AddToXml(TNode parentNode, bool recursive)
228 return parentNode.AddElement("filter");
232 #region IXmlNodeBuilder Implementation
237 /// <param name="recursive">True if recursive</param>
238 /// <returns>The added XML node</returns>
239 public TNode ToXml(bool recursive)
241 return AddToXml(new TNode("dummy"), recursive);
247 /// <param name="parentNode">Parent node</param>
248 /// <param name="recursive">True if recursive</param>
249 /// <returns>The added XML node</returns>
250 public abstract TNode AddToXml(TNode parentNode, bool recursive);