Merge changes from topic 'upgrade-junit4.12'

* changes:
  Extra generic type information to aid certain javacs.
  Revert matchers back to 4.10 to compile against Hamcrest 1.1
  Remove support for stuck threads
  Remove DisableOnDebug (new in 4.12) as it is not supported on Android
  Upgrade to JUnit 4.12
diff --git a/LICENSE-junit.txt b/LICENSE-junit.txt
new file mode 100644
index 0000000..fb68629
--- /dev/null
+++ b/LICENSE-junit.txt
@@ -0,0 +1,214 @@
+JUnit
+
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
+CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+      a) in the case of the initial Contributor, the initial code and
+         documentation distributed under this Agreement, and
+      b) in the case of each subsequent Contributor:
+
+      i) changes to the Program, and
+
+      ii) additions to the Program;
+
+      where such changes and/or additions to the Program originate from and are
+distributed by that particular Contributor. A Contribution 'originates' from a
+Contributor if it was added to the Program by such Contributor itself or anyone
+acting on such Contributor's behalf. Contributions do not include additions to
+the Program which: (i) are separate modules of software distributed in
+conjunction with the Program under their own license agreement, and (ii) are
+not derivative works of the Program. 
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which are
+necessarily infringed by the use or sale of its Contribution alone or when
+combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+2. GRANT OF RIGHTS
+
+      a) Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide, royalty-free copyright license to
+reproduce, prepare derivative works of, publicly display, publicly perform,
+distribute and sublicense the Contribution of such Contributor, if any, and
+such derivative works, in source code and object code form.
+
+      b) Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide, royalty-free patent license under
+Licensed Patents to make, use, sell, offer to sell, import and otherwise
+transfer the Contribution of such Contributor, if any, in source code and
+object code form. This patent license shall apply to the combination of the
+Contribution and the Program if, at the time the Contribution is added by the
+Contributor, such addition of the Contribution causes such combination to be
+covered by the Licensed Patents. The patent license shall not apply to any
+other combinations which include the Contribution. No hardware per se is
+licensed hereunder. 
+
+      c) Recipient understands that although each Contributor grants the
+licenses to its Contributions set forth herein, no assurances are provided by
+any Contributor that the Program does not infringe the patent or other
+intellectual property rights of any other entity. Each Contributor disclaims
+any liability to Recipient for claims brought by any other entity based on
+infringement of intellectual property rights or otherwise. As a condition to
+exercising the rights and licenses granted hereunder, each Recipient hereby
+assumes sole responsibility to secure any other intellectual property rights
+needed, if any. For example, if a third party patent license is required to
+allow Recipient to distribute the Program, it is Recipient's responsibility to
+acquire that license before distributing the Program.
+
+      d) Each Contributor represents that to its knowledge it has sufficient
+copyright rights in its Contribution, if any, to grant the copyright license
+set forth in this Agreement. 
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under
+its own license agreement, provided that:
+
+      a) it complies with the terms and conditions of this Agreement; and
+
+      b) its license agreement:
+
+      i) effectively disclaims on behalf of all Contributors all warranties and
+conditions, express and implied, including warranties or conditions of title
+and non-infringement, and implied warranties or conditions of merchantability
+and fitness for a particular purpose; 
+
+      ii) effectively excludes on behalf of all Contributors all liability for
+damages, including direct, indirect, special, incidental and consequential
+damages, such as lost profits; 
+
+      iii) states that any provisions which differ from this Agreement are
+offered by that Contributor alone and not by any other party; and
+
+      iv) states that source code for the Program is available from such
+Contributor, and informs licensees how to obtain it in a reasonable manner on
+or through a medium customarily used for software exchange. 
+
+When the Program is made available in source code form:
+
+      a) it must be made available under this Agreement; and 
+
+      b) a copy of this Agreement must be included with each copy of the
+Program. 
+
+Contributors may not remove or alter any copyright notices contained within the
+Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if
+any, in a manner that reasonably allows subsequent Recipients to identify the
+originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with
+respect to end users, business partners and the like. While this license is
+intended to facilitate the commercial use of the Program, the Contributor who
+includes the Program in a commercial product offering should do so in a manner
+which does not create potential liability for other Contributors. Therefore, if
+a Contributor includes the Program in a commercial product offering, such
+Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
+every other Contributor ("Indemnified Contributor") against any losses, damages
+and costs (collectively "Losses") arising from claims, lawsuits and other legal
+actions brought by a third party against the Indemnified Contributor to the
+extent caused by the acts or omissions of such Commercial Contributor in
+connection with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In order
+to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial Contributor
+to control, and cooperate with the Commercial Contributor in, the defense and
+any related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product
+offering, Product X. That Contributor is then a Commercial Contributor. If that
+Commercial Contributor then makes performance claims, or offers warranties
+related to Product X, those performance claims and warranties are such
+Commercial Contributor's responsibility alone. Under this section, the
+Commercial Contributor would have to defend claims against the other
+Contributors related to those performance claims and warranties, and if a court
+requires any other Contributor to pay any damages as a result, the Commercial
+Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
+Recipient is solely responsible for determining the appropriateness of using
+and distributing the Program and assumes all risks associated with its exercise
+of rights under this Agreement, including but not limited to the risks and
+costs of program errors, compliance with applicable laws, damage to or loss of
+data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
+CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS
+GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable
+law, it shall not affect the validity or enforceability of the remainder of the
+terms of this Agreement, and without further action by the parties hereto, such
+provision shall be reformed to the minimum extent necessary to make such
+provision valid and enforceable.
+
+If Recipient institutes patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's rights
+granted under Section 2(b) shall terminate as of the date such litigation is
+filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to
+comply with any of the material terms or conditions of this Agreement and does
+not cure such failure in a reasonable period of time after becoming aware of
+such noncompliance. If all Recipient's rights under this Agreement terminate,
+Recipient agrees to cease use and distribution of the Program as soon as
+reasonably practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall continue
+and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in
+order to avoid inconsistency the Agreement is copyrighted and may only be
+modified in the following manner. The Agreement Steward reserves the right to
+publish new versions (including revisions) of this Agreement from time to time.
+No one other than the Agreement Steward has the right to modify this Agreement.
+The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each new version
+of the Agreement will be given a distinguishing version number. The Program
+(including Contributions) may always be distributed subject to the version of
+the Agreement under which it was received. In addition, after a new version of
+the Agreement is published, Contributor may elect to distribute the Program
+(including its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to
+the intellectual property of any Contributor under this Agreement, whether
+expressly, by implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to this
+Agreement will bring a legal action under this Agreement more than one year
+after the cause of action arose. Each party waives its rights to a jury trial
+in any resulting litigation. 
+
diff --git a/MODULE_LICENSE_CPL b/MODULE_LICENSE_EPL
similarity index 100%
rename from MODULE_LICENSE_CPL
rename to MODULE_LICENSE_EPL
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index 1168d94..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,88 +0,0 @@
-Common Public License - v 1.0
-
-
-THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
-
-
-1. DEFINITIONS
-
-"Contribution" means:
-
-a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
-b) in the case of each subsequent Contributor:
-i)	 changes to the Program, and
-ii)	 additions to the Program;
-where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
-
-"Contributor" means any person or entity that distributes the Program.
-
-
-"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
-
-
-"Program" means the Contributions distributed in accordance with this Agreement.
-
-
-"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
-
-
-2. GRANT OF RIGHTS
-
-a)	Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
-b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
-c)	Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
-d)	Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
-3. REQUIREMENTS
-
-A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
-
-a)	it complies with the terms and conditions of this Agreement; and
-b)	its license agreement:
-i)	effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
-ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
-iii)	states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
-iv)	states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
-When the Program is made available in source code form:
-
-a)	it must be made available under this Agreement; and
-b)	a copy of this Agreement must be included with each copy of the Program.
-
-Contributors may not remove or alter any copyright notices contained within the Program.
-
-
-Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
-
-
-4. COMMERCIAL DISTRIBUTION
-
-Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
-
-
-For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
-
-
-5. NO WARRANTY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
-
-
-6. DISCLAIMER OF LIABILITY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-
-7. GENERAL
-
-If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-
-If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
-
-
-All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
-
-
-Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
-
-
-This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
\ No newline at end of file
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 0000000..69b4faf
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,5 @@
+   ===================================================================================
+   ==  Notices and attributions required by libraries that the project depends on   ==
+   ===================================================================================
+
+ The JUnit depends on Java Hamcrest (http://hamcrest.org/JavaHamcrest/).
diff --git a/README.android b/README.android
deleted file mode 100644
index c4194e4..0000000
--- a/README.android
+++ /dev/null
@@ -1,4 +0,0 @@
-This is junit4.10 source, currently intended for host side use. 
-
-Obtained from https://github.com/downloads/KentBeck/junit/junit-4.10-src.jar
-
diff --git a/README.html b/README.html
deleted file mode 100644
index 42f29a6..0000000
--- a/README.html
+++ /dev/null
@@ -1,672 +0,0 @@
-<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
-<html>
-<head>
-   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-   <meta name="GENERATOR" content="Microsoft FrontPage 4.0">
-   <meta name="Author" content="Erich Gamma, Kent Beck, and David Saff">
-   <title>JUnit 4.6</title>
-</head>
-<body>
-
-<h1>
-<b><font color="#00CC00">J</font><font color="#FF0000">U</font><font color="#000000">nit
-4.6</b></h1> 
-<br>Brought to you by <a href="http://www.threeriversinstitute.org">Kent Beck</a>, Erich 
-Gamma, and <a href="http://david.saff.net">David Saff</a>. 
-<br>FAQ edited by <a href="http://www.clarkware.com">Mike Clark</a>. Web mastering by Erik 
-Meade.
-<br>(see also <a href="http://www.junit.org">JUnit.org</a>)
-
-<hr WIDTH="100%">
-<br>6 April 2009
-<p>JUnit is a simple framework to write repeatable tests. It is an instance
-of the xUnit architecture for unit testing frameworks.
-<ul>
-<li>
-<a href="#Summary of">Summary of Changes</a></li>
-
-<li>
-<a href="#Contents">Contents</a></li>
-
-<li>
-<a href="#Installation">Installation</a></li>
-
-<li>
-<a href="#Getting">Getting Started</a></li>
-
-<li>
-<a href="#Documentation">Documentation</a></li>
-<li>
-<a href="#Known Defects">Known Defects</a></li>
-</ul>
-
-<a NAME="Summary of">
-<h2>Summary of Changes in version 4.6</h2>
-
-<h3>Max</h3>
-
-<p>JUnit now includes a new experimental Core, <code>MaxCore</code>.  <code>MaxCore</code>
-remembers the results of previous test runs in order to run new
-tests out of order.  <code>MaxCore</code> prefers new tests to old tests, fast
-tests to slow tests, and recently failing tests to tests that last
-failed long ago.  There's currently not a standard UI for running
-<code>MaxCore</code> included in JUnit, but there is a UI included in the JUnit
-Max Eclipse plug-in at:</p>
-
-<p>http://www.junitmax.com/junitmax/subscribe.html</p>
-
-<p>Example:</p>
-
-<pre><code>public static class TwoUnEqualTests {
-    @Test
-    public void slow() throws InterruptedException {
-        Thread.sleep(100);
-        fail();
-    }
-
-    @Test
-    public void fast() {
-        fail();
-    }
-}
-
-@Test
-public void rememberOldRuns() {
-    File maxFile = new File("history.max");
-    MaxCore firstMax = MaxCore.storedLocally(maxFile);
-    firstMax.run(TwoUnEqualTests.class);
-
-    MaxCore useHistory= MaxCore.storedLocally(maxFile);
-    List&lt;Failure&gt; failures= useHistory.run(TwoUnEqualTests.class)
-            .getFailures();
-    assertEquals("fast", failures.get(0).getDescription().getMethodName());
-    assertEquals("slow", failures.get(1).getDescription().getMethodName());
-}
-</code></pre>
-
-<h3>Test scheduling strategies</h3>
-
-<p><code>JUnitCore</code> now includes an experimental method that allows you to
-specify a model of the <code>Computer</code> that runs your tests.  Currently,
-the only built-in Computers are the default, serial runner, and two
-runners provided in the <code>ParallelRunner</code> class:
-<code>ParallelRunner.classes()</code>, which runs classes in parallel, and
-<code>ParallelRunner.methods()</code>, which runs classes and methods in parallel.</p>
-
-<p>This feature is currently less stable than MaxCore, and may be
-merged with MaxCore in some way in the future.</p>
-
-<p>Example:</p>
-
-<pre><code>public static class Example {
-    @Test public void one() throws InterruptedException {
-        Thread.sleep(1000);
-    }
-    @Test public void two() throws InterruptedException {
-        Thread.sleep(1000);
-    }
-}
-
-@Test public void testsRunInParallel() {
-    long start= System.currentTimeMillis();
-    Result result= JUnitCore.runClasses(ParallelComputer.methods(),
-            Example.class);
-    assertTrue(result.wasSuccessful());
-    long end= System.currentTimeMillis();
-    assertThat(end - start, betweenInclusive(1000, 1500));
-}
-</code></pre>
-
-<h3>Comparing double arrays</h3>
-
-<p>Arrays of doubles can be compared, using a delta allowance for equality:</p>
-
-<pre><code>@Test
-public void doubleArraysAreEqual() {
-    assertArrayEquals(new double[] {1.0, 2.0}, new double[] {1.0, 2.0}, 0.01);
-}
-</code></pre>
-
-<h3><code>Filter.matchDescription</code> API</h3>
-
-<p>Since 4.0, it has been possible to run a single method using the <code>Request.method</code> 
-API.  In 4.6, the filter that implements this is exposed as <code>Filter.matchDescription</code>.</p>
-
-<h3>Documentation</h3>
-
-<ul>
-<li><p>A couple classes and packages that once had empty javadoc have been
-doc'ed.</p></li>
-<li><p>Added how to run JUnit from the command line to the cookbook.</p></li>
-<li><p>junit-4.x.zip now contains build.xml</p></li>
-</ul>
-
-<h3>Bug fixes</h3>
-
-<ul>
-<li>Fixed overly permissive @DataPoint processing (2191102)</li>
-<li>Fixed bug in test counting after an ignored method (2106324)</li>
-</ul>
-
-<h2>Summary of Changes in version 4.5</h2>
-
-<h3>Installation</h3>
-
-<ul>
-<li>We are releasing <code>junit-4.6.jar</code>, which contains all the classes
-necessary to run JUnit, and <code>junit-dep-4.6.jar</code>, which leaves out
-hamcrest classes, for developers who already use hamcrest outside of
-JUnit.</li>
-</ul>
-
-<h3>Basic JUnit operation</h3>
-
-<ul>
-<li><p>JUnitCore now more often exits with the correct exit code (0 for
-success, 1 for failure)</p></li>
-<li><p>Badly formed test classes (exceptions in constructors, classes
-without tests, multiple constructors, Suite without @SuiteClasses)
-produce more helpful error messages</p></li>
-<li><p>Test classes whose only test methods are inherited from superclasses
-now run.</p></li>
-<li><p>Optimization to annotation processing can cut JUnit overhead by more than half
-on large test classes, especially when using Theories.  [Bug 1796847]</p></li>
-<li><p>A failing assumption in a constructor ignores the class</p></li>
-<li><p>Correct results when comparing the string "null" with potentially
-null values.  [Bug 1857283]</p></li>
-<li><p>Annotating a class with <code>@RunWith(JUnit4.class)</code> will always invoke the
-default JUnit 4 runner in the current version of JUnit.  This default changed
-from <code>JUnit4ClassRunner</code> in 4.4 to <code>BlockJUnit4ClassRunner</code> in 4.5 (see below),
-and may change again.</p></li>
-</ul>
-
-<h3>Extension</h3>
-
-<ul>
-<li><p><code>BlockJUnit4Runner</code> is a new implementation of the standard JUnit 4
-test class functionality.  In contrast to <code>JUnit4ClassRunner</code> (the old
-implementation):</p>
-
-<ul>
-<li><p><code>BlockJUnit4Runner</code> has a much simpler implementation based on
-Statements, allowing new operations to be inserted into the
-appropriate point in the execution flow.</p></li>
-<li><p><code>BlockJUnit4Runner</code> is published, and extension and reuse are
-encouraged, whereas <code>JUnit4ClassRunner</code> was in an internal package,
-and is now deprecated.</p></li>
-</ul></li>
-<li><p><code>ParentRunner</code> is a base class for runners that iterate over
-a list of "children", each an object representing a test or suite to run.
-<code>ParentRunner</code> provides filtering, sorting, <code>@BeforeClass</code>, <code>@AfterClass</code>,
-and method validation to subclasses.</p></li>
-<li><p><code>TestClass</code> wraps a class to be run, providing efficient, repeated access
-to all methods with a given annotation.</p></li>
-<li><p>The new <code>RunnerBuilder</code> API allows extending the behavior of
-Suite-like custom runners.</p></li>
-<li><p><code>AssumptionViolatedException.toString()</code> is more informative</p></li>
-</ul>
-
-<h3>Extra Runners</h3>
-
-<ul>
-<li><p><code>Parameterized.eachOne()</code> has been removed</p></li>
-<li><p>New runner <code>Enclosed</code> runs all static inner classes of an outer class.</p></li>
-</ul>
-
-<h3>Theories</h3>
-
-<ul>
-<li><p><code>@Before</code> and <code>@After</code> methods are run before and after each set of attempted parameters
-on a Theory, and each set of parameters is run on a new instance of the test class.</p></li>
-<li><p>Exposed API's <code>ParameterSignature.getType()</code> and <code>ParameterSignature.getAnnotations()</code></p></li>
-<li><p>An array of data points can be introduced by a field or method
-marked with the new annotation <code>@DataPoints</code></p></li>
-<li><p>The Theories custom runner has been refactored to make it faster and
-easier to extend</p></li>
-</ul>
-
-<h3>Development</h3>
-
-<ul>
-<li><p>Source has been split into directories <code>src/main/java</code> and
-<code>src/test/java</code>, making it easier to exclude tests from builds, and
-making JUnit more maven-friendly</p></li>
-<li><p>Test classes in <code>org.junit.tests</code> have been organized into
-subpackages, hopefully making finding tests easier.</p></li>
-<li><p><code>ResultMatchers</code> has more informative descriptions.</p></li>
-<li><p><code>TestSystem</code> allows testing return codes and other system-level interactions.</p></li>
-</ul>
-
-<h2>Summary of Changes in version 4.4</h2>
-
-<p>JUnit is designed to efficiently capture developers' intentions about
-their code, and quickly check their code matches those intentions.
-Over the last year, we've been talking about what things developers
-would like to say about their code that have been difficult in the
-past, and how we can make them easier.</p>
-
-<h3>assertThat</h3>
-
-<p>Two years ago, Joe Walnes built a <a href="http://joe.truemesh.com/blog/000511.html">new assertion mechanism</a> on top of what was 
-then <a href="http://www.jmock.org/download.html">JMock 1</a>.  The method name was <code>assertThat</code>, and the syntax looked like this:</p>
-
-<pre><code>assertThat(x, is(3));
-assertThat(x, is(not(4)));
-assertThat(responseString, either(containsString("color")).or(containsString("colour")));
-assertThat(myList, hasItem("3"));
-</code></pre>
-
-<p>More generally:</p>
-
-<pre><code>assertThat([value], [matcher statement]);
-</code></pre>
-
-<p>Advantages of this assertion syntax include:</p>
-
-<ul>
-<li><p>More readable and typeable: this syntax allows you to think in terms of subject, verb, object
-(assert "x is 3") rathern than <code>assertEquals</code>, which uses verb, object, subject (assert "equals 3 x")</p></li>
-<li><p>Combinations: any matcher statement <code>s</code> can be negated (<code>not(s)</code>), combined (<code>either(s).or(t)</code>),
-mapped to a collection (<code>each(s)</code>), or used in custom combinations (<code>afterFiveSeconds(s)</code>)</p></li>
-<li><p>Readable failure messages.  Compare</p>
-
-<pre><code>assertTrue(responseString.contains("color") || responseString.contains("colour"));
-// ==&gt; failure message: 
-// java.lang.AssertionError:
-
-
-assertThat(responseString, anyOf(containsString("color"), containsString("colour")));
-// ==&gt; failure message:
-// java.lang.AssertionError: 
-// Expected: (a string containing "color" or a string containing "colour")
-//      got: "Please choose a font"
-</code></pre></li>
-<li><p>Custom Matchers.  By implementing the <code>Matcher</code> interface yourself, you can get all of the
-above benefits for your own custom assertions.</p></li>
-<li><p>For a more thorough description of these points, see <a href="http://joe.truemesh.com/blog/000511.html">Joe Walnes's
-original post</a>.:</p></li>
-</ul>
-
-<p>We have decided to include this API directly in JUnit.
-It's an extensible and readable syntax, and because it enables
-new features, like <a href="#assumptions">assumptions</a> and <a href="#theories">theories</a>.</p>
-
-<p>Some notes:</p>
-
-<ul>
-<li>The old assert methods are never, ever, going away. <br />
-Developers may continue using the old <code>assertEquals</code>, <code>assertTrue</code>, and
-so on.</li>
-<li><p>The second parameter of an <code>assertThat</code> statement is a <code>Matcher</code>.
-We include the Matchers we want as static imports, like this:</p>
-
-<pre><code>import static org.hamcrest.CoreMatchers.is;
-</code></pre>
-
-<p>or:</p>
-
-<pre><code>import static org.hamcrest.CoreMatchers.*;
-</code></pre></li>
-<li><p>Manually importing <code>Matcher</code> methods can be frustrating.  [Eclipse
-3.3][] includes the ability to 
-define
-"Favorite" classes to import static methods from, which makes it easier 
-(Search for "Favorites" in the Preferences dialog).
-We expect that support for static imports will improve in all Java IDEs in the future.</p></li>
-<li><p>To allow compatibility with a wide variety of possible matchers, 
-we have decided to include the classes from hamcrest-core,
-from the <a href="http://code.google.com/p/hamcrest/">Hamcrest</a> project.  This is the first time that
-third-party classes have been included in JUnit.  </p></li>
-<li><p>To allow developers to maintain full control of the classpath contents, the JUnit distribution also provides an unbundled junit-dep jar,
-ie without hamcrest-core classes included.  This is intended for situations when using other libraries that also depend on hamcrest-core, to
-avoid classloading conflicts or issues.  Developers using junit-dep should ensure a compatible version of hamcrest-core jar (ie 1.1+) is present in the classpath.</p></li>
-<li><p>JUnit currently ships with a few matchers, defined in 
-<code>org.hamcrest.CoreMatchers</code> and <code>org.junit.matchers.JUnitMatchers</code>. <br />
-To use many, many more, consider downloading the <a href="http://hamcrest.googlecode.com/files/hamcrest-all-1.1.jar">full hamcrest package</a>.</p></li>
-<li><p>JUnit contains special support for comparing string and array
-values, giving specific information on how they differ.  This is not
-yet available using the <code>assertThat</code> syntax, but we hope to bring
-the two assert methods into closer alignment in future releases.</p></li>
-</ul>
-
-<h3>assumeThat</h3>
-
-<p><a name="assumptions" />
-Ideally, the developer writing a test has control of all of the forces that might cause a test to fail.
-If this isn't immediately possible, making dependencies explicit can often improve a design. <br />
-For example, if a test fails when run in a different locale than the developer intended,
-it can be fixed by explicitly passing a locale to the domain code.</p>
-
-<p>However, sometimes this is not desirable or possible. <br />
-It's good to be able to run a test against the code as it is currently written, 
-implicit assumptions and all, or to write a test that exposes a known bug.
-For these situations, JUnit now includes the ability to express "assumptions":</p>
-
-<pre><code>import static org.junit.Assume.*
-
-@Test public void filenameIncludesUsername() {
-   assumeThat(File.separatorChar, is('/'));
-   assertThat(new User("optimus").configFileName(), is("configfiles/optimus.cfg"));
-}
-
-@Test public void correctBehaviorWhenFilenameIsNull() {
-   assumeTrue(bugFixed("13356"));  // bugFixed is not included in JUnit
-   assertThat(parse(null), is(new NullDocument()));
-}
-</code></pre>
-
-<p>With this beta release, a failed assumption will lead to the test being marked as passing,
-regardless of what the code below the assumption may assert.
-In the future, this may change, and a failed assumption may lead to the test being ignored:
-however, third-party runners do not currently allow this option.</p>
-
-<p>We have included <code>assumeTrue</code> for convenience, but thanks to the
-inclusion of Hamcrest, we do not need to create <code>assumeEquals</code>,
-<code>assumeSame</code>, and other analogues to the <code>assert*</code> methods.  All of
-those functionalities are subsumed in assumeThat, with the appropriate
-matcher.</p>
-
-<p>A failing assumption in a <code>@Before</code> or <code>@BeforeClass</code> method will have the same effect
-as a failing assumption in each <code>@Test</code> method of the class.</p>
-
-<h3>Theories</h3>
-
-<p><a name="theories" />
-More flexible and expressive assertions, combined with the ability to
-state assumptions clearly, lead to a new kind of statement of intent, 
-which we call a "Theory".  A test captures the intended behavior in
-one particular scenario.  A theory allows a developer to be
-as precise as desired about the behavior of the code in possibly
-infinite numbers of possible scenarios.  For example:</p>
-
-<pre><code>@RunWith(Theories.class)
-public class UserTest {
-  @DataPoint public static String GOOD_USERNAME = "optimus";
-  @DataPoint public static String USERNAME_WITH_SLASH = "optimus/prime";
-
-  @Theory public void filenameIncludesUsername(String username) {
-    assumeThat(username, not(containsString("/")));
-    assertThat(new User(username).configFileName(), containsString(username));
-  }
-}
-</code></pre>
-
-<p>This makes it clear that the user's filename should be included in the
-config file name, only if it doesn't contain a slash.  Another test
-or theory might define what happens when a username does contain a slash.</p>
-
-<p><code>UserTest</code> will attempt to run <code>filenameIncludesUsername</code> on 
-every compatible <code>DataPoint</code> defined in the class.  If any of the
-assumptions fail, the data point is silently ignored.  If all of the
-assumptions pass, but an assertion fails, the test fails.</p>
-
-<p>The support for Theories has been absorbed from the <a href="http://popper.tigris.org">Popper</a>
-project, and <a href="http://popper.tigris.org/tutorial.html">more complete documentation</a> can be found
-there.</p>
-
-<p>Defining general statements in this way can jog the developer's memory
-about other potential data points and tests, also allows <a href="http://www.junitfactory.org">automated
-tools</a> to <a href="http://shareandenjoy.saff.net/2007/04/popper-and-junitfactory.html">search</a> for new, unexpected data
-points that expose bugs.</p>
-
-<h3>Other changes</h3>
-
-<p>This release contains other bug fixes and new features.  Among them:</p>
-
-<ul>
-<li><p>Annotated descriptions</p>
-
-<p>Runner UIs, Filters, and Sorters operate on Descriptions of test
-methods and test classes.  These Descriptions now include the
-annotations on the original Java source element, allowing for richer
-display of test results, and easier development of annotation-based
-filters.</p></li>
-<li><p>Bug fix (1715326): assertEquals now compares all Numbers using their
-native implementation of <code>equals</code>.  This assertion, which passed in
-4.3, will now fail:</p>
-
-<p>assertEquals(new Integer(1), new Long(1));</p>
-
-<p>Non-integer Numbers (Floats, Doubles, BigDecimals, etc),
-which were compared incorrectly in 4.3, are now fixed.</p></li>
-<li><p><code>assertEquals(long, long)</code> and <code>assertEquals(double, double)</code> have
-been re-introduced to the <code>Assert</code> class, to take advantage of
-Java's native widening conversions.  Therefore, this still passes:</p>
-
-<p>assertEquals(1, 1L);</p></li>
-<li><p>The default runner for JUnit 4 test classes has been refactored.
-The old version was named <code>TestClassRunner</code>, and the new is named
-<code>JUnit4ClassRunner</code>.  Likewise, <code>OldTestClassRunner</code> is now
-<code>JUnit3ClassRunner</code>.  The new design allows variations in running
-individual test classes to be expressed with fewer custom classes.
-For a good example, see the source to
-<code>org.junit.experimental.theories.Theories</code>.</p></li>
-<li><p>The rules for determining which runner is applied by default to a
-test class have been simplified:</p>
-
-<ol>
-<li><p>If the class has a <code>@RunWith</code> annotation, the annotated runner
-class is used.</p></li>
-<li><p>If the class can be run with the JUnit 3 test runner (it
-subclasses <code>TestCase</code>, or contains a <code>public static Test suite()</code>
-method), JUnit38ClassRunner is used.</p></li>
-<li><p>Otherwise, JUnit4ClassRunner is used.</p></li>
-</ol>
-
-<p>This default guess can always be overridden by an explicit
-<code>@RunWith(JUnit4ClassRunner.class)</code> or
-<code>@RunWith(JUnit38ClassRunner.class)</code> annotation.</p>
-
-<p>The old class names <code>TestClassRunner</code> and <code>OldTestClassRunner</code>
-remain as deprecated.</p></li>
-<li><p>Bug fix (1739095): Filters and Sorters work correctly on test
-classes that contain a <code>suite</code> method like:</p>
-
-<p>public static junit.framework.Test suite() {
-  return new JUnit4TestAdapter(MyTest.class);
-}</p></li>
-<li><p>Bug fix (1745048): @After methods are now correctly called 
-after a test method times out.</p></li>
-</ul>
-
-<h2>
-<a NAME="Summary of"></a>Summary of Changes in version 4.3.1</h2>
-<p>
-<ul>
-<li>Bug fix: 4.3 introduced a 
-<a href="https://sourceforge.net/tracker/?func=detail&atid=115278&aid=1684562&group_id=15278">bug</a>
-that caused a NullPointerException
-when comparing a null reference to a non-null reference in <tt>assertEquals</tt>.
-This has been fixed.
-<li>Bug fix: The binary jar for 4.3 <a href="https://sourceforge.net/tracker/?func=detail&atid=115278&aid=1686931&group_id=15278">accidentally</a> included the tests and sample code,
-which are now removed for a smaller download, but, as always, available from the
-full zip.
-</ul>
-</p>
-
-<h2>
-<a NAME="Summary of"></a>Summary of Changes with version 4.3</h2>
-<p>
-<ul>
-<li>Changes in array equality.  Using <tt>assertEquals</tt> to compare array contents is now deprecated.
-In the future, <tt>assertEquals</tt> will revert to its pre-4.0 meaning of comparing objects based on
-Java's <tt>Object.equals</tt> semantics.  To compare array contents, use the new, more reliable 
-<tt>Assert.assertArrayEquals</tt> methods.
-<li>The <tt>@Ignore</tt> annotation can now be applied to classes, to ignore the entire class, instead of
-individual methods.
-<li>Originally, developers who wanted to use a static <tt>suite()</tt> method from JUnit 3.x with a JUnit 4.x
-runner had to annotate the class with <tt>@RunWith(AllTests.class)</tt>.  In the common case, this requirement
-has been removed.  However, when such a class is wrapped with a JUnit4TestAdapter (which we believe is rare), the
-results may not be as expected.
-<li>Improved error messages for array comparison("arrays first differed at element [1][0]")
-<li>Bug fix: Inaccessible base class is caught at test construction time.
-<li>Bug fix: Circular suites are caught at test construction time.
-<li>Bug fix: Test constructors that throw exceptions are reported correctly.
-<li><b>For committers and extenders</b>
-<ul>
-<li>Sources now are in a separate "src" directory (this means a big break in the CVS history)
-<li>Improved documentation in <tt>Request</tt>, <tt>RunWith</tt>
-</ul>
-</ul>
-</p>
-
-<h2>
-<a NAME="Summary of"></a>Summary of Changes with version 4.2</h2>
-<p>
-<ul>
-<li>Bug fix: Inaccessible base class is caught at test construction time.
-<li>Bug fix: Circular suites are caught at test construction time.
-<li>Improved error messages for array comparison("arrays first differed at element [1][0]")
-<li>Test constructors that throw exceptions are reported correctly.
-</ul>
-</p>
-
-
-<h2>
-<a NAME="Summary of"></a>Summary of Changes with version 4.1</h2>
-<p>
-<ul>
-<li>Bug fix: listeners now get a correct test running time, rather than always being told 0 secs.
-<li>The @RunWith annotation is now inherited by subclasses: 
-all subclasses of an abstract test class will be run by the same runner.
-<li>The build script fails if the JUnit unit tests fail
-<li>The faq has been updated
-<li>Javadoc has been improved, with more internal links, and package descriptions added (Thanks, Matthias Schmidt!)
-<li>An acknowledgements.txt file has been created to credit outside contributions
-<li>The <tt>Enclosed</tt> runner, which runs all of the static inner classes of a given class, has been added
-to <tt>org.junit.runners</tt>.
-</ul>
-</p>
-
-<h2>Summary of Changes with version 4.0</h2>
-<p>
-The architecture of JUnit 4.0 is a substantial departure from that of earlier releases. 
-Instead of 
-tagging test classes by subclassing junit.framework.TestCase and tagging test methods by 
-starting their name with "test", you now tag test methods with the @Test annotation.
-</p>
-
-
-<h2>
-<a NAME="Contents"></a>Contents of the Release</h2>
-
-<table CELLSPACING=0 CELLPADDING=0 >
-<tr>
-<td><tt>README.html&nbsp;</tt></td>
-
-<td>this file</td>
-</tr>
-
-<tr>
-<td><tt>junit-4.6.jar</tt></td>
-
-<td>a jar file with the JUnit framework, bundled with the hamcrest-core-1.1 dependency.</td>
-</tr>
-
-<tr>
-<td><tt>junit-dep-4.6.jar</tt></td>
-
-<td>a jar file with the JUnit framework, unbundled from any external dependencies.  
-Choosing to use this jar developers will need to also provide in the classpath a compatible version of external dependencies (ie hamcrest-core-1.1+)</td>
-</tr>
-
-<tr>
-<td><tt>junit-4.6-src.jar</tt></td>
-
-<td>a jar file with the source code of the JUnit framework</td>
-</tr>
-
-<tr>
-<td><tt>org/junit</tt></td>
-
-<td>the source code of the basic JUnit annotations and classes</td>
-</tr>
-
-<tr>
-<td><tt>&nbsp;&nbsp;&nbsp; samples</tt></td>
-
-<td>sample test cases</td>
-</tr>
-
-<tr>
-<td><tt>&nbsp;&nbsp;&nbsp; tests</tt></td>
-
-<td>test cases for JUnit itself</td>
-</tr>
-
-<tr>
-<td><tt>javadoc</tt></td>
-
-<td>javadoc generated documentation</td>
-</tr>
-
-<tr>
-<td><tt>doc</tt></td>
-
-<td>documentation and articles</td>
-</tr>
-</table>
-
-<h2>
-<a NAME="Installation"></a>Installation</h2>
-Below are the installation steps for installing JUnit:
-<ol>
-<li>
-unzip the junit4.6.zip file</li>
-
-<li>
-add<i> </i><b>junit-4.6.jar</b> to the CLASSPATH. For example: 
-<tt> set classpath=%classpath%;INSTALL_DIR\junit-4.6.jar;INSTALL_DIR</tt></li>
-
-<li>
-test the installation by running <tt>java org.junit.runner.JUnitCore org.junit.tests.AllTests</tt></li>
-
-<br><b><font color="#FF0000">Notice</font></b>: that the tests are not
-contained in the junit-4.6.jar but in the installation directory directly.
-Therefore make sure that the installation directory is on the class path
-</ol>
-<b><font color="#FF0000">Important</font></b>: don't install junit-4.6.jar
-into the extension directory of your JDK installation. If you do so the
-test class on the files system will not be found.
-<h2>
-<a NAME="Getting"></a>Getting Started</h2>
-To get started with unit testing and JUnit read the article:
-<a href="doc/cookbook/cookbook.htm">JUnit Cookbook</a>.
-<br>This article describes basic test writing using JUnit 4.
-<p>You find additional samples in the org.junit.samples package:
-<ul>
-<li>
-SimpleTest.java - some simple test cases</li>
-
-<li>
-VectorTest.java - test cases for java.util.Vector</li>
-</ul>
-
-<h2>
-<a NAME="Documentation"></a>Documentation</h2>
-
-<blockquote><a href="doc/cookbook/cookbook.htm">JUnit Cookbook</a>
-<br>&nbsp;&nbsp;&nbsp; A cookbook for implementing tests with JUnit.
-<br><a href="javadoc/index.html">Javadoc</a>
-<br>&nbsp;&nbsp;&nbsp; API documentation generated with javadoc.
-<br><a href="doc/faq/faq.htm">Frequently asked questions</a>
-<br>&nbsp;&nbsp;&nbsp; Some frequently asked questions about using JUnit.
-<br><a href="cpl-v10.html">License</a>
-<br>&nbsp;&nbsp;&nbsp; The terms of the common public license used for JUnit.<br>
-</blockquote>
-The following documents still describe JUnit 3.8.
-<blockquote>
-<br><a href="doc/testinfected/testing.htm">Test Infected - Programmers
-Love Writing Tests</a>
-<br>&nbsp;&nbsp;&nbsp; An article demonstrating the development process
-with JUnit.
-<br><a href="doc/cookstour/cookstour.htm">JUnit - A cooks tour</a>
-</blockquote>
-
-<hr WIDTH="100%">
-<!--webbot bot="HTMLMarkup" startspan --><a href="http://sourceforge.net"><IMG
-                  src="http://sourceforge.net/sflogo.php?group_id=15278"
-                  width="88" height="31" border="0" alt="SourceForge Logo"></a><!--webbot
-bot="HTMLMarkup" endspan -->
-</body>
-</html>
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..660111c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+# JUnit 4
+JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.
+
+For more information, please visit:
+* [Wiki](https://github.com/junit-team/junit/wiki)
+* [Download and Install guide](https://github.com/junit-team/junit/wiki/Download-and-Install)
+* [Getting Started](https://github.com/junit-team/junit/wiki/Getting-started)
+
+[![Latest Build Status](https://junit.ci.cloudbees.com/job/JUnit/badge/icon)](https://junit.ci.cloudbees.com/)
+
+[![Built on DEV@cloud](http://www.cloudbees.com/sites/default/files/Button-Built-on-CB-1.png)](http://www.cloudbees.com/foss/foss-dev.cb)
+
diff --git a/README.version b/README.version
index 1be475f..41e2c6f 100644
--- a/README.version
+++ b/README.version
@@ -1,3 +1,9 @@
-URL: https://github.com/junit-team/junit/archive/r4.10.tar.gz
-Version: 4.10
+URL: https://github.com/junit-team/junit/archive/r4.12.tar.gz
+Version: 4.12
 BugComponent: 40416
+
+Local Changes:
+    Remove DisableOnDebug (new in 4.12) as it is not supported on Android
+    Remove support for stuck threads
+    Revert matchers back to 4.10 to compile against Hamcrest 1.1
+    Extra generic type information to aid certain javacs.
diff --git a/cpl-v10.html b/cpl-v10.html
deleted file mode 100644
index 36aa208..0000000
--- a/cpl-v10.html
+++ /dev/null
@@ -1,125 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
-<HTML>
-<HEAD>
-<TITLE>Common Public License - v 1.0</TITLE>
-<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-</HEAD>
-
-<BODY BGCOLOR="#FFFFFF" VLINK="#800000">
-
-
-<P ALIGN="CENTER"><B>Common Public License - v 1.0</B>
-<P><B></B><FONT SIZE="3"></FONT>
-<P><FONT SIZE="3"></FONT><FONT SIZE="2">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT").  ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.</FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"><B>1.  DEFINITIONS</B></FONT>
-<P><FONT SIZE="2">"Contribution" means:</FONT>
-
-<UL><FONT SIZE="2">a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and<BR CLEAR="LEFT">
-b) in the case of each subsequent Contributor:</FONT></UL>
-
-
-<UL><FONT SIZE="2">i)	 	changes to the Program, and</FONT></UL>
-
-
-<UL><FONT SIZE="2">ii)		additions to the Program;</FONT></UL>
-
-
-<UL><FONT SIZE="2">where such changes and/or additions to the Program originate from and are distributed by that particular Contributor.  </FONT><FONT SIZE="2">A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf.  </FONT><FONT SIZE="2">Contributions do not include additions to the Program which:  (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.  </FONT></UL>
-
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2">"Contributor" means any person or entity that distributes the Program.</FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2">"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.  </FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2">"Program" means the Contributions distributed in accordance with this Agreement.</FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2">"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.</FONT>
-<P><FONT SIZE="2"><B></B></FONT>
-<P><FONT SIZE="2"><B>2.  GRANT OF RIGHTS</B></FONT>
-
-<UL><FONT SIZE="2"></FONT><FONT SIZE="2">a)	</FONT><FONT SIZE="2">Subject to the terms of this Agreement, each Contributor hereby grants</FONT><FONT SIZE="2"> Recipient a non-exclusive, worldwide, royalty-free copyright license to</FONT><FONT SIZE="2" COLOR="#FF0000"> </FONT><FONT SIZE="2">reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.</FONT></UL>
-
-
-<UL><FONT SIZE="2"></FONT></UL>
-
-
-<UL><FONT SIZE="2"></FONT><FONT SIZE="2">b) 	Subject to the terms of this Agreement, each Contributor hereby grants </FONT><FONT SIZE="2">Recipient a non-exclusive, worldwide,</FONT><FONT SIZE="2" COLOR="#008000"> </FONT><FONT SIZE="2">royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form.  This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents.  The patent license shall not apply to any other combinations which include the Contribution.  No hardware per se is licensed hereunder.   </FONT></UL>
-
-
-<UL><FONT SIZE="2"></FONT></UL>
-
-
-<UL><FONT SIZE="2">c)	Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity.  Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise.  As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any.  For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.</FONT></UL>
-
-
-<UL><FONT SIZE="2"></FONT></UL>
-
-
-<UL><FONT SIZE="2">d)	Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. </FONT></UL>
-
-
-<UL><FONT SIZE="2"></FONT></UL>
-
-<P><FONT SIZE="2"><B>3.  REQUIREMENTS</B></FONT>
-<P><FONT SIZE="2"><B></B>A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:</FONT>
-
-<UL><FONT SIZE="2">a)	it complies with the terms and conditions of this Agreement; and</FONT></UL>
-
-
-<UL><FONT SIZE="2">b)	its license agreement:</FONT></UL>
-
-
-<UL><FONT SIZE="2">i)	effectively disclaims</FONT><FONT SIZE="2"> on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; </FONT></UL>
-
-
-<UL><FONT SIZE="2">ii) 	effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; </FONT></UL>
-
-
-<UL><FONT SIZE="2">iii)</FONT><FONT SIZE="2">	states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and</FONT></UL>
-
-
-<UL><FONT SIZE="2">iv)	states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.</FONT><FONT SIZE="2" COLOR="#0000FF"> </FONT><FONT SIZE="2" COLOR="#FF0000"></FONT></UL>
-
-
-<UL><FONT SIZE="2" COLOR="#FF0000"></FONT><FONT SIZE="2"></FONT></UL>
-
-<P><FONT SIZE="2">When the Program is made available in source code form:</FONT>
-
-<UL><FONT SIZE="2">a)	it must be made available under this Agreement; and </FONT></UL>
-
-
-<UL><FONT SIZE="2">b)	a copy of this Agreement must be included with each copy of the Program.  </FONT></UL>
-
-<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT>
-<P><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT><FONT SIZE="2">Contributors may not remove or alter any copyright notices contained within the Program.  </FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2">Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.  </FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"><B>4.  COMMERCIAL DISTRIBUTION</B></FONT>
-<P><FONT SIZE="2">Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like.  While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors.   Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering.  The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement.  In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations.  The Indemnified Contributor may participate in any such claim at its own expense.</FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2">For example, a Contributor might include the Program in a commercial product offering, Product X.  That Contributor is then a Commercial Contributor.  If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone.  Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.</FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"></FONT>
-<P><FONT SIZE="2" COLOR="#0000FF"></FONT><FONT SIZE="2"><B>5.  NO WARRANTY</B></FONT>
-<P><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is</FONT><FONT SIZE="2"> solely responsible for determining the appropriateness of using and distributing </FONT><FONT SIZE="2">the Program</FONT><FONT SIZE="2"> and assumes all risks associated with its exercise of rights under this Agreement</FONT><FONT SIZE="2">, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, </FONT><FONT SIZE="2">programs or equipment, and unavailability or interruption of operations</FONT><FONT SIZE="2">.  </FONT><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2"><B>6.  DISCLAIMER OF LIABILITY</B></FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES </FONT><FONT SIZE="2">(INCLUDING WITHOUT LIMITATION LOST PROFITS),</FONT><FONT SIZE="2"> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"><B>7.  GENERAL</B></FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2">If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.</FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2">If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed.  In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. </FONT><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2">All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance.  If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable.  However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.  </FONT><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2">Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted  and may only be modified in the following manner. The Agreement Steward reserves the right to </FONT><FONT SIZE="2">publish new versions (including revisions) of this Agreement from time to </FONT><FONT SIZE="2">time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward.   IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity.  </FONT><FONT SIZE="2">Each new version of the Agreement will be given a distinguishing version number.  The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new </FONT><FONT SIZE="2">version.  </FONT><FONT SIZE="2">Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, </FONT><FONT SIZE="2">by implication, estoppel or otherwise</FONT><FONT SIZE="2">.</FONT><FONT SIZE="2">  All rights in the Program not expressly granted under this Agreement are reserved.</FONT>
-<P><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2">This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose.  Each party waives its rights to a jury trial in any resulting litigation.</FONT>
-<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
-<P><FONT SIZE="2"></FONT>
-
-</BODY>
-
-</HTML>
\ No newline at end of file
diff --git a/epl-v10.html b/epl-v10.html
new file mode 100644
index 0000000..3998fce
--- /dev/null
+++ b/epl-v10.html
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Public License - Version 1.0</title>
+<style type="text/css">
+  body {
+    size: 8.5in 11.0in;
+    margin: 0.25in 0.5in 0.25in 0.5in;
+    tab-interval: 0.5in;
+    }
+  p {  	
+    margin-left: auto;
+    margin-top:  0.5em;
+    margin-bottom: 0.5em;
+    }
+  p.list {
+  	margin-left: 0.5in;
+    margin-top:  0.05em;
+    margin-bottom: 0.05em;
+    }
+  </style>
+
+</head>
+
+<body lang="EN-US">
+
+<h2>Eclipse Public License - v 1.0</h2>
+
+<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR
+DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
+AGREEMENT.</p>
+
+<p><b>1. DEFINITIONS</b></p>
+
+<p>&quot;Contribution&quot; means:</p>
+
+<p class="list">a) in the case of the initial Contributor, the initial
+code and documentation distributed under this Agreement, and</p>
+<p class="list">b) in the case of each subsequent Contributor:</p>
+<p class="list">i) changes to the Program, and</p>
+<p class="list">ii) additions to the Program;</p>
+<p class="list">where such changes and/or additions to the Program
+originate from and are distributed by that particular Contributor. A
+Contribution 'originates' from a Contributor if it was added to the
+Program by such Contributor itself or anyone acting on such
+Contributor's behalf. Contributions do not include additions to the
+Program which: (i) are separate modules of software distributed in
+conjunction with the Program under their own license agreement, and (ii)
+are not derivative works of the Program.</p>
+
+<p>&quot;Contributor&quot; means any person or entity that distributes
+the Program.</p>
+
+<p>&quot;Licensed Patents&quot; mean patent claims licensable by a
+Contributor which are necessarily infringed by the use or sale of its
+Contribution alone or when combined with the Program.</p>
+
+<p>&quot;Program&quot; means the Contributions distributed in accordance
+with this Agreement.</p>
+
+<p>&quot;Recipient&quot; means anyone who receives the Program under
+this Agreement, including all Contributors.</p>
+
+<p><b>2. GRANT OF RIGHTS</b></p>
+
+<p class="list">a) Subject to the terms of this Agreement, each
+Contributor hereby grants Recipient a non-exclusive, worldwide,
+royalty-free copyright license to reproduce, prepare derivative works
+of, publicly display, publicly perform, distribute and sublicense the
+Contribution of such Contributor, if any, and such derivative works, in
+source code and object code form.</p>
+
+<p class="list">b) Subject to the terms of this Agreement, each
+Contributor hereby grants Recipient a non-exclusive, worldwide,
+royalty-free patent license under Licensed Patents to make, use, sell,
+offer to sell, import and otherwise transfer the Contribution of such
+Contributor, if any, in source code and object code form. This patent
+license shall apply to the combination of the Contribution and the
+Program if, at the time the Contribution is added by the Contributor,
+such addition of the Contribution causes such combination to be covered
+by the Licensed Patents. The patent license shall not apply to any other
+combinations which include the Contribution. No hardware per se is
+licensed hereunder.</p>
+
+<p class="list">c) Recipient understands that although each Contributor
+grants the licenses to its Contributions set forth herein, no assurances
+are provided by any Contributor that the Program does not infringe the
+patent or other intellectual property rights of any other entity. Each
+Contributor disclaims any liability to Recipient for claims brought by
+any other entity based on infringement of intellectual property rights
+or otherwise. As a condition to exercising the rights and licenses
+granted hereunder, each Recipient hereby assumes sole responsibility to
+secure any other intellectual property rights needed, if any. For
+example, if a third party patent license is required to allow Recipient
+to distribute the Program, it is Recipient's responsibility to acquire
+that license before distributing the Program.</p>
+
+<p class="list">d) Each Contributor represents that to its knowledge it
+has sufficient copyright rights in its Contribution, if any, to grant
+the copyright license set forth in this Agreement.</p>
+
+<p><b>3. REQUIREMENTS</b></p>
+
+<p>A Contributor may choose to distribute the Program in object code
+form under its own license agreement, provided that:</p>
+
+<p class="list">a) it complies with the terms and conditions of this
+Agreement; and</p>
+
+<p class="list">b) its license agreement:</p>
+
+<p class="list">i) effectively disclaims on behalf of all Contributors
+all warranties and conditions, express and implied, including warranties
+or conditions of title and non-infringement, and implied warranties or
+conditions of merchantability and fitness for a particular purpose;</p>
+
+<p class="list">ii) effectively excludes on behalf of all Contributors
+all liability for damages, including direct, indirect, special,
+incidental and consequential damages, such as lost profits;</p>
+
+<p class="list">iii) states that any provisions which differ from this
+Agreement are offered by that Contributor alone and not by any other
+party; and</p>
+
+<p class="list">iv) states that source code for the Program is available
+from such Contributor, and informs licensees how to obtain it in a
+reasonable manner on or through a medium customarily used for software
+exchange.</p>
+
+<p>When the Program is made available in source code form:</p>
+
+<p class="list">a) it must be made available under this Agreement; and</p>
+
+<p class="list">b) a copy of this Agreement must be included with each
+copy of the Program.</p>
+
+<p>Contributors may not remove or alter any copyright notices contained
+within the Program.</p>
+
+<p>Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.</p>
+
+<p><b>4. COMMERCIAL DISTRIBUTION</b></p>
+
+<p>Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use of
+the Program, the Contributor who includes the Program in a commercial
+product offering should do so in a manner which does not create
+potential liability for other Contributors. Therefore, if a Contributor
+includes the Program in a commercial product offering, such Contributor
+(&quot;Commercial Contributor&quot;) hereby agrees to defend and
+indemnify every other Contributor (&quot;Indemnified Contributor&quot;)
+against any losses, damages and costs (collectively &quot;Losses&quot;)
+arising from claims, lawsuits and other legal actions brought by a third
+party against the Indemnified Contributor to the extent caused by the
+acts or omissions of such Commercial Contributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In
+order to qualify, an Indemnified Contributor must: a) promptly notify
+the Commercial Contributor in writing of such claim, and b) allow the
+Commercial Contributor to control, and cooperate with the Commercial
+Contributor in, the defense and any related settlement negotiations. The
+Indemnified Contributor may participate in any such claim at its own
+expense.</p>
+
+<p>For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.</p>
+
+<p><b>5. NO WARRANTY</b></p>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
+ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement , including but not limited to
+the risks and costs of program errors, compliance with applicable laws,
+damage to or loss of data, programs or equipment, and unavailability or
+interruption of operations.</p>
+
+<p><b>6. DISCLAIMER OF LIABILITY</b></p>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>
+
+<p><b>7. GENERAL</b></p>
+
+<p>If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further action
+by the parties hereto, such provision shall be reformed to the minimum
+extent necessary to make such provision valid and enforceable.</p>
+
+<p>If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other
+software or hardware) infringes such Recipient's patent(s), then such
+Recipient's rights granted under Section 2(b) shall terminate as of the
+date such litigation is filed.</p>
+
+<p>All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of time
+after becoming aware of such noncompliance. If all Recipient's rights
+under this Agreement terminate, Recipient agrees to cease use and
+distribution of the Program as soon as reasonably practicable. However,
+Recipient's obligations under this Agreement and any licenses granted by
+Recipient relating to the Program shall continue and survive.</p>
+
+<p>Everyone is permitted to copy and distribute copies of this
+Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The
+Agreement Steward reserves the right to publish new versions (including
+revisions) of this Agreement from time to time. No one other than the
+Agreement Steward has the right to modify this Agreement. The Eclipse
+Foundation is the initial Agreement Steward. The Eclipse Foundation may
+assign the responsibility to serve as the Agreement Steward to a
+suitable separate entity. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new version
+of the Agreement is published, Contributor may elect to distribute the
+Program (including its Contributions) under the new version. Except as
+expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
+rights or licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under this
+Agreement are reserved.</p>
+
+<p>This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No party
+to this Agreement will bring a legal action under this Agreement more
+than one year after the cause of action arose. Each party waives its
+rights to a jury trial in any resulting litigation.</p>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/main/java/junit/extensions/ActiveTestSuite.java b/src/main/java/junit/extensions/ActiveTestSuite.java
index 0623565..95c5e2e 100644
--- a/src/main/java/junit/extensions/ActiveTestSuite.java
+++ b/src/main/java/junit/extensions/ActiveTestSuite.java
@@ -10,61 +10,61 @@
  * test in a separate thread and waits until all
  * threads have terminated.
  * -- Aarhus Radisson Scandinavian Center 11th floor
- */ 
+ */
 public class ActiveTestSuite extends TestSuite {
-	private volatile int fActiveTestDeathCount;
+    private volatile int fActiveTestDeathCount;
 
-	public ActiveTestSuite() {
-	}
-		
-	public ActiveTestSuite(Class<? extends TestCase> theClass) {
-		super(theClass);
-	}
-	
-	public ActiveTestSuite(String name) {
-		super (name);
-	}
-	
-	public ActiveTestSuite(Class<? extends TestCase> theClass, String name) {
-		super(theClass, name);
-	}
-	
-	@Override
-	public void run(TestResult result) {
-		fActiveTestDeathCount= 0;
-		super.run(result);
-		waitUntilFinished();
-	}
-	
-	@Override
-	public void runTest(final Test test, final TestResult result) {
-		Thread t= new Thread() {
-			@Override
-			public void run() {
-				try {
-					// inlined due to limitation in VA/Java 
-					//ActiveTestSuite.super.runTest(test, result);
-					test.run(result);
-				} finally {
-					ActiveTestSuite.this.runFinished();
-				}
-			}
-		};
-		t.start();
-	}
+    public ActiveTestSuite() {
+    }
 
-	synchronized void waitUntilFinished() {
-		while (fActiveTestDeathCount < testCount()) {
-			try {
-				wait();
-			} catch (InterruptedException e) {
-				return; // ignore
-			}
-		}
-	}
-	
-	synchronized public void runFinished() {
-		fActiveTestDeathCount++;
-		notifyAll();
-	}
+    public ActiveTestSuite(Class<? extends TestCase> theClass) {
+        super(theClass);
+    }
+
+    public ActiveTestSuite(String name) {
+        super(name);
+    }
+
+    public ActiveTestSuite(Class<? extends TestCase> theClass, String name) {
+        super(theClass, name);
+    }
+
+    @Override
+    public void run(TestResult result) {
+        fActiveTestDeathCount = 0;
+        super.run(result);
+        waitUntilFinished();
+    }
+
+    @Override
+    public void runTest(final Test test, final TestResult result) {
+        Thread t = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    // inlined due to limitation in VA/Java
+                    //ActiveTestSuite.super.runTest(test, result);
+                    test.run(result);
+                } finally {
+                    ActiveTestSuite.this.runFinished();
+                }
+            }
+        };
+        t.start();
+    }
+
+    synchronized void waitUntilFinished() {
+        while (fActiveTestDeathCount < testCount()) {
+            try {
+                wait();
+            } catch (InterruptedException e) {
+                return; // ignore
+            }
+        }
+    }
+
+    synchronized public void runFinished() {
+        fActiveTestDeathCount++;
+        notifyAll();
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/junit/extensions/RepeatedTest.java b/src/main/java/junit/extensions/RepeatedTest.java
index 3b687a5..22c690a 100644
--- a/src/main/java/junit/extensions/RepeatedTest.java
+++ b/src/main/java/junit/extensions/RepeatedTest.java
@@ -5,34 +5,35 @@
 
 /**
  * A Decorator that runs a test repeatedly.
- * 
  */
 public class RepeatedTest extends TestDecorator {
-	private int fTimesRepeat;
+    private int fTimesRepeat;
 
-	public RepeatedTest(Test test, int repeat) {
-		super(test);
-		if (repeat < 0)
-			throw new IllegalArgumentException("Repetition count must be >= 0");
-		fTimesRepeat= repeat;
-	}
+    public RepeatedTest(Test test, int repeat) {
+        super(test);
+        if (repeat < 0) {
+            throw new IllegalArgumentException("Repetition count must be >= 0");
+        }
+        fTimesRepeat = repeat;
+    }
 
-	@Override
-	public int countTestCases() {
-		return super.countTestCases() * fTimesRepeat;
-	}
+    @Override
+    public int countTestCases() {
+        return super.countTestCases() * fTimesRepeat;
+    }
 
-	@Override
-	public void run(TestResult result) {
-		for (int i= 0; i < fTimesRepeat; i++) {
-			if (result.shouldStop())
-				break;
-			super.run(result);
-		}
-	}
+    @Override
+    public void run(TestResult result) {
+        for (int i = 0; i < fTimesRepeat; i++) {
+            if (result.shouldStop()) {
+                break;
+            }
+            super.run(result);
+        }
+    }
 
-	@Override
-	public String toString() {
-		return super.toString() + "(repeated)";
-	}
+    @Override
+    public String toString() {
+        return super.toString() + "(repeated)";
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/junit/extensions/TestDecorator.java b/src/main/java/junit/extensions/TestDecorator.java
index d9ae474..2b74f30 100644
--- a/src/main/java/junit/extensions/TestDecorator.java
+++ b/src/main/java/junit/extensions/TestDecorator.java
@@ -8,36 +8,35 @@
  * A Decorator for Tests. Use TestDecorator as the base class for defining new
  * test decorators. Test decorator subclasses can be introduced to add behaviour
  * before or after a test is run.
- * 
  */
 public class TestDecorator extends Assert implements Test {
-	protected Test fTest;
+    protected Test fTest;
 
-	public TestDecorator(Test test) {
-		fTest= test;
-	}
+    public TestDecorator(Test test) {
+        fTest = test;
+    }
 
-	/**
-	 * The basic run behaviour.
-	 */
-	public void basicRun(TestResult result) {
-		fTest.run(result);
-	}
+    /**
+     * The basic run behaviour.
+     */
+    public void basicRun(TestResult result) {
+        fTest.run(result);
+    }
 
-	public int countTestCases() {
-		return fTest.countTestCases();
-	}
+    public int countTestCases() {
+        return fTest.countTestCases();
+    }
 
-	public void run(TestResult result) {
-		basicRun(result);
-	}
+    public void run(TestResult result) {
+        basicRun(result);
+    }
 
-	@Override
-	public String toString() {
-		return fTest.toString();
-	}
+    @Override
+    public String toString() {
+        return fTest.toString();
+    }
 
-	public Test getTest() {
-		return fTest;
-	}
+    public Test getTest() {
+        return fTest;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/junit/extensions/TestSetup.java b/src/main/java/junit/extensions/TestSetup.java
index 00dcd21..fcdca8c 100644
--- a/src/main/java/junit/extensions/TestSetup.java
+++ b/src/main/java/junit/extensions/TestSetup.java
@@ -11,32 +11,32 @@
  */
 public class TestSetup extends TestDecorator {
 
-	public TestSetup(Test test) {
-		super(test);
-	}
+    public TestSetup(Test test) {
+        super(test);
+    }
 
-	@Override
-	public void run(final TestResult result) {
-		Protectable p= new Protectable() {
-			public void protect() throws Exception {
-				setUp();
-				basicRun(result);
-				tearDown();
-			}
-		};
-		result.runProtected(this, p);
-	}
+    @Override
+    public void run(final TestResult result) {
+        Protectable p = new Protectable() {
+            public void protect() throws Exception {
+                setUp();
+                basicRun(result);
+                tearDown();
+            }
+        };
+        result.runProtected(this, p);
+    }
 
-	/**
-	 * Sets up the fixture. Override to set up additional fixture state.
-	 */
-	protected void setUp() throws Exception {
-	}
+    /**
+     * Sets up the fixture. Override to set up additional fixture state.
+     */
+    protected void setUp() throws Exception {
+    }
 
-	/**
-	 * Tears down the fixture. Override to tear down the additional fixture
-	 * state.
-	 */
-	protected void tearDown() throws Exception {
-	}
+    /**
+     * Tears down the fixture. Override to tear down the additional fixture
+     * state.
+     */
+    protected void tearDown() throws Exception {
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/Assert.java b/src/main/java/junit/framework/Assert.java
index 3dcc23d..663461c 100644
--- a/src/main/java/junit/framework/Assert.java
+++ b/src/main/java/junit/framework/Assert.java
@@ -2,295 +2,338 @@
 
 /**
  * A set of assert methods.  Messages are only displayed when an assert fails.
+ *
+ * @deprecated Please use {@link org.junit.Assert} instead.
  */
-
+@Deprecated
 public class Assert {
-	/**
-	 * Protect constructor since it is a static only class
-	 */
-	protected Assert() {
-	}
+    /**
+     * Protect constructor since it is a static only class
+     */
+    protected Assert() {
+    }
 
-	/**
-	 * Asserts that a condition is true. If it isn't it throws
-	 * an AssertionFailedError with the given message.
-	 */
-	static public void assertTrue(String message, boolean condition) {
-		if (!condition)
-			fail(message);
-	}
-	/**
-	 * Asserts that a condition is true. If it isn't it throws
-	 * an AssertionFailedError.
-	 */
-	static public void assertTrue(boolean condition) {
-		assertTrue(null, condition);
-	}
-	/**
-	 * Asserts that a condition is false. If it isn't it throws
-	 * an AssertionFailedError with the given message.
-	 */
-	static public void assertFalse(String message, boolean condition) {
-		assertTrue(message, !condition);
-	}
-	/**
-	 * Asserts that a condition is false. If it isn't it throws
-	 * an AssertionFailedError.
-	 */
-	static public void assertFalse(boolean condition) {
-		assertFalse(null, condition);
-	}
-	/**
-	 * Fails a test with the given message.
-	 */
-	static public void fail(String message) {
-		if (message == null) {
-			throw new AssertionFailedError();
-		}
-		throw new AssertionFailedError(message);
-	}
-	/**
-	 * Fails a test with no message.
-	 */
-	static public void fail() {
-		fail(null);
-	}
-	/**
-	 * Asserts that two objects are equal. If they are not
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-	static public void assertEquals(String message, Object expected, Object actual) {
-		if (expected == null && actual == null)
-			return;
-		if (expected != null && expected.equals(actual))
-			return;
-		failNotEquals(message, expected, actual);
-	}
-	/**
-	 * Asserts that two objects are equal. If they are not
-	 * an AssertionFailedError is thrown.
-	 */
-	static public void assertEquals(Object expected, Object actual) {
-	    assertEquals(null, expected, actual);
-	}
-	/**
-	 * Asserts that two Strings are equal. 
-	 */
-	static public void assertEquals(String message, String expected, String actual) {
-		if (expected == null && actual == null)
-			return;
-		if (expected != null && expected.equals(actual))
-			return;
-		String cleanMessage= message == null ? "" : message;
-		throw new ComparisonFailure(cleanMessage, expected, actual);
-	}
-	/**
-	 * Asserts that two Strings are equal. 
-	 */
-	static public void assertEquals(String expected, String actual) {
-	    assertEquals(null, expected, actual);
-	}
-	/**
-	 * Asserts that two doubles are equal concerning a delta.  If they are not
-	 * an AssertionFailedError is thrown with the given message.  If the expected
-	 * value is infinity then the delta value is ignored.
-	 */
-	static public void assertEquals(String message, double expected, double actual, double delta) {
-		if (Double.compare(expected, actual) == 0)
-			return;
-		if (!(Math.abs(expected-actual) <= delta))
-			failNotEquals(message, new Double(expected), new Double(actual));
-	}
-	/**
-	 * Asserts that two doubles are equal concerning a delta. If the expected
-	 * value is infinity then the delta value is ignored.
-	 */
-	static public void assertEquals(double expected, double actual, double delta) {
-	    assertEquals(null, expected, actual, delta);
-	}
-	/**
-	 * Asserts that two floats are equal concerning a positive delta. If they
-	 * are not an AssertionFailedError is thrown with the given message. If the
-	 * expected value is infinity then the delta value is ignored.
-	 */
-	static public void assertEquals(String message, float expected, float actual, float delta) {
-		if (Float.compare(expected, actual) == 0)
-			return;
-		if (!(Math.abs(expected - actual) <= delta))
-				failNotEquals(message, new Float(expected), new Float(actual));
-	}
-	/**
-	 * Asserts that two floats are equal concerning a delta. If the expected
-	 * value is infinity then the delta value is ignored.
-	 */
-	static public void assertEquals(float expected, float actual, float delta) {
-		assertEquals(null, expected, actual, delta);
-	}
-	/**
-	 * Asserts that two longs are equal. If they are not
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-	static public void assertEquals(String message, long expected, long actual) {
-	    assertEquals(message, new Long(expected), new Long(actual));
-	}
-	/**
-	 * Asserts that two longs are equal.
-	 */
-	static public void assertEquals(long expected, long actual) {
-	    assertEquals(null, expected, actual);
-	}
-	/**
-	 * Asserts that two booleans are equal. If they are not
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-	static public void assertEquals(String message, boolean expected, boolean actual) {
-    		assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
-  	}
-	/**
-	 * Asserts that two booleans are equal.
- 	 */
-	static public void assertEquals(boolean expected, boolean actual) {
-		assertEquals(null, expected, actual);
-	}
-	/**
-	 * Asserts that two bytes are equal. If they are not
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-  	static public void assertEquals(String message, byte expected, byte actual) {
-		assertEquals(message, new Byte(expected), new Byte(actual));
-	}
-	/**
-   	 * Asserts that two bytes are equal.
-	 */
-	static public void assertEquals(byte expected, byte actual) {
-		assertEquals(null, expected, actual);
-	}
-	/**
-	 * Asserts that two chars are equal. If they are not
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-  	static public void assertEquals(String message, char expected, char actual) {
-    		assertEquals(message, new Character(expected), new Character(actual));
-  	}
-	/**
-	 * Asserts that two chars are equal.
-	 */
-  	static public void assertEquals(char expected, char actual) {
-		assertEquals(null, expected, actual);
-	}
-	/**
-	 * Asserts that two shorts are equal. If they are not
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-	static public void assertEquals(String message, short expected, short actual) {
-    		assertEquals(message, new Short(expected), new Short(actual));
-	}
-  	/**
-	 * Asserts that two shorts are equal.
-	 */
-	static public void assertEquals(short expected, short actual) {
-		assertEquals(null, expected, actual);
-	}
-	/**
-	 * Asserts that two ints are equal. If they are not
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-  	static public void assertEquals(String message, int expected, int actual) {
-		assertEquals(message, new Integer(expected), new Integer(actual));
-  	}
-  	/**
-   	 * Asserts that two ints are equal.
-	 */
-  	static public void assertEquals(int expected, int actual) {
-  		assertEquals(null, expected, actual);
-	}
-	/**
-	 * Asserts that an object isn't null.
-	 */
-	static public void assertNotNull(Object object) {
-		assertNotNull(null, object);
-	}
-	/**
-	 * Asserts that an object isn't null. If it is
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-	static public void assertNotNull(String message, Object object) {
-		assertTrue(message, object != null);
-	}
-	/**
-	 * Asserts that an object is null. If it isn't an {@link AssertionError} is
-	 * thrown.
-	 * Message contains: Expected: <null> but was: object
-	 * 
-	 * @param object
-	 *            Object to check or <code>null</code>
-	 */
-	static public void assertNull(Object object) {
-		String message = "Expected: <null> but was: " + String.valueOf(object);
-		assertNull(message, object);
-	}
-	/**
-	 * Asserts that an object is null.  If it is not
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-	static public void assertNull(String message, Object object) {
-		assertTrue(message, object == null);
-	}
-	/**
-	 * Asserts that two objects refer to the same object. If they are not
-	 * an AssertionFailedError is thrown with the given message.
-	 */
-	static public void assertSame(String message, Object expected, Object actual) {
-		if (expected == actual)
-			return;
-		failNotSame(message, expected, actual);
-	}
-	/**
-	 * Asserts that two objects refer to the same object. If they are not
-	 * the same an AssertionFailedError is thrown.
-	 */
-	static public void assertSame(Object expected, Object actual) {
-	    assertSame(null, expected, actual);
-	}
-	/**
-	 * Asserts that two objects do not refer to the same object. If they do
-	 * refer to the same object an AssertionFailedError is thrown with the
-	 * given message.
-	 */
-	static public void assertNotSame(String message, Object expected, Object actual) {
-		if (expected == actual)
-			failSame(message);
-	}
-	/**
-	 * Asserts that two objects do not refer to the same object. If they do
-	 * refer to the same object an AssertionFailedError is thrown.
-	 */
-	static public void assertNotSame(Object expected, Object actual) {
-		assertNotSame(null, expected, actual);
-	}
+    /**
+     * Asserts that a condition is true. If it isn't it throws
+     * an AssertionFailedError with the given message.
+     */
+    static public void assertTrue(String message, boolean condition) {
+        if (!condition) {
+            fail(message);
+        }
+    }
 
-	static public void failSame(String message) {
-		String formatted= "";
- 		if (message != null)
- 			formatted= message+" ";
- 		fail(formatted+"expected not same");
-	}
+    /**
+     * Asserts that a condition is true. If it isn't it throws
+     * an AssertionFailedError.
+     */
+    static public void assertTrue(boolean condition) {
+        assertTrue(null, condition);
+    }
 
-	static public void failNotSame(String message, Object expected, Object actual) {
-		String formatted= "";
-		if (message != null)
-			formatted= message+" ";
-		fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
-	}
+    /**
+     * Asserts that a condition is false. If it isn't it throws
+     * an AssertionFailedError with the given message.
+     */
+    static public void assertFalse(String message, boolean condition) {
+        assertTrue(message, !condition);
+    }
 
-	static public void failNotEquals(String message, Object expected, Object actual) {
-		fail(format(message, expected, actual));
-	}
+    /**
+     * Asserts that a condition is false. If it isn't it throws
+     * an AssertionFailedError.
+     */
+    static public void assertFalse(boolean condition) {
+        assertFalse(null, condition);
+    }
 
-	public static String format(String message, Object expected, Object actual) {
-		String formatted= "";
-		if (message != null && message.length() > 0)
-			formatted= message+" ";
-		return formatted+"expected:<"+expected+"> but was:<"+actual+">";
-	}
+    /**
+     * Fails a test with the given message.
+     */
+    static public void fail(String message) {
+        if (message == null) {
+            throw new AssertionFailedError();
+        }
+        throw new AssertionFailedError(message);
+    }
+
+    /**
+     * Fails a test with no message.
+     */
+    static public void fail() {
+        fail(null);
+    }
+
+    /**
+     * Asserts that two objects are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, Object expected, Object actual) {
+        if (expected == null && actual == null) {
+            return;
+        }
+        if (expected != null && expected.equals(actual)) {
+            return;
+        }
+        failNotEquals(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two objects are equal. If they are not
+     * an AssertionFailedError is thrown.
+     */
+    static public void assertEquals(Object expected, Object actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that two Strings are equal.
+     */
+    static public void assertEquals(String message, String expected, String actual) {
+        if (expected == null && actual == null) {
+            return;
+        }
+        if (expected != null && expected.equals(actual)) {
+            return;
+        }
+        String cleanMessage = message == null ? "" : message;
+        throw new ComparisonFailure(cleanMessage, expected, actual);
+    }
+
+    /**
+     * Asserts that two Strings are equal.
+     */
+    static public void assertEquals(String expected, String actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that two doubles are equal concerning a delta.  If they are not
+     * an AssertionFailedError is thrown with the given message.  If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(String message, double expected, double actual, double delta) {
+        if (Double.compare(expected, actual) == 0) {
+            return;
+        }
+        if (!(Math.abs(expected - actual) <= delta)) {
+            failNotEquals(message, new Double(expected), new Double(actual));
+        }
+    }
+
+    /**
+     * Asserts that two doubles are equal concerning a delta. If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(double expected, double actual, double delta) {
+        assertEquals(null, expected, actual, delta);
+    }
+
+    /**
+     * Asserts that two floats are equal concerning a positive delta. If they
+     * are not an AssertionFailedError is thrown with the given message. If the
+     * expected value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(String message, float expected, float actual, float delta) {
+        if (Float.compare(expected, actual) == 0) {
+            return;
+        }
+        if (!(Math.abs(expected - actual) <= delta)) {
+            failNotEquals(message, new Float(expected), new Float(actual));
+        }
+    }
+
+    /**
+     * Asserts that two floats are equal concerning a delta. If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(float expected, float actual, float delta) {
+        assertEquals(null, expected, actual, delta);
+    }
+
+    /**
+     * Asserts that two longs are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, long expected, long actual) {
+        assertEquals(message, Long.valueOf(expected), Long.valueOf(actual));
+    }
+
+    /**
+     * Asserts that two longs are equal.
+     */
+    static public void assertEquals(long expected, long actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that two booleans are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, boolean expected, boolean actual) {
+        assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
+    }
+
+    /**
+     * Asserts that two booleans are equal.
+     */
+    static public void assertEquals(boolean expected, boolean actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that two bytes are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, byte expected, byte actual) {
+        assertEquals(message, Byte.valueOf(expected), Byte.valueOf(actual));
+    }
+
+    /**
+     * Asserts that two bytes are equal.
+     */
+    static public void assertEquals(byte expected, byte actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that two chars are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, char expected, char actual) {
+        assertEquals(message, Character.valueOf(expected), Character.valueOf(actual));
+    }
+
+    /**
+     * Asserts that two chars are equal.
+     */
+    static public void assertEquals(char expected, char actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that two shorts are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, short expected, short actual) {
+        assertEquals(message, Short.valueOf(expected), Short.valueOf(actual));
+    }
+
+    /**
+     * Asserts that two shorts are equal.
+     */
+    static public void assertEquals(short expected, short actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that two ints are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, int expected, int actual) {
+        assertEquals(message, Integer.valueOf(expected), Integer.valueOf(actual));
+    }
+
+    /**
+     * Asserts that two ints are equal.
+     */
+    static public void assertEquals(int expected, int actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that an object isn't null.
+     */
+    static public void assertNotNull(Object object) {
+        assertNotNull(null, object);
+    }
+
+    /**
+     * Asserts that an object isn't null. If it is
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertNotNull(String message, Object object) {
+        assertTrue(message, object != null);
+    }
+
+    /**
+     * Asserts that an object is null. If it isn't an {@link AssertionError} is
+     * thrown.
+     * Message contains: Expected: <null> but was: object
+     *
+     * @param object Object to check or <code>null</code>
+     */
+    static public void assertNull(Object object) {
+        if (object != null) {
+            assertNull("Expected: <null> but was: " + object.toString(), object);
+        }
+    }
+
+    /**
+     * Asserts that an object is null.  If it is not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertNull(String message, Object object) {
+        assertTrue(message, object == null);
+    }
+
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertSame(String message, Object expected, Object actual) {
+        if (expected == actual) {
+            return;
+        }
+        failNotSame(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * the same an AssertionFailedError is thrown.
+     */
+    static public void assertSame(Object expected, Object actual) {
+        assertSame(null, expected, actual);
+    }
+
+    /**
+     * Asserts that two objects do not refer to the same object. If they do
+     * refer to the same object an AssertionFailedError is thrown with the
+     * given message.
+     */
+    static public void assertNotSame(String message, Object expected, Object actual) {
+        if (expected == actual) {
+            failSame(message);
+        }
+    }
+
+    /**
+     * Asserts that two objects do not refer to the same object. If they do
+     * refer to the same object an AssertionFailedError is thrown.
+     */
+    static public void assertNotSame(Object expected, Object actual) {
+        assertNotSame(null, expected, actual);
+    }
+
+    static public void failSame(String message) {
+        String formatted = (message != null) ? message + " " : "";
+        fail(formatted + "expected not same");
+    }
+
+    static public void failNotSame(String message, Object expected, Object actual) {
+        String formatted = (message != null) ? message + " " : "";
+        fail(formatted + "expected same:<" + expected + "> was not:<" + actual + ">");
+    }
+
+    static public void failNotEquals(String message, Object expected, Object actual) {
+        fail(format(message, expected, actual));
+    }
+
+    public static String format(String message, Object expected, Object actual) {
+        String formatted = "";
+        if (message != null && message.length() > 0) {
+            formatted = message + " ";
+        }
+        return formatted + "expected:<" + expected + "> but was:<" + actual + ">";
+    }
 }
diff --git a/src/main/java/junit/framework/AssertionFailedError.java b/src/main/java/junit/framework/AssertionFailedError.java
index 0d7802c..e11fbec 100644
--- a/src/main/java/junit/framework/AssertionFailedError.java
+++ b/src/main/java/junit/framework/AssertionFailedError.java
@@ -5,16 +5,25 @@
  */
 public class AssertionFailedError extends AssertionError {
 
-	private static final long serialVersionUID= 1L;
+    private static final long serialVersionUID = 1L;
+    
+    /**
+     * Constructs a new AssertionFailedError without a detail message.
+     */
+    public AssertionFailedError() {
+    }
 
-	public AssertionFailedError() {
-	}
+    /**
+     * Constructs a new AssertionFailedError with the specified detail message.
+     * A null message is replaced by an empty String.
+     * @param message the detail message. The detail message is saved for later 
+     * retrieval by the {@code Throwable.getMessage()} method.
+     */
+    public AssertionFailedError(String message) {
+        super(defaultString(message));
+    }
 
-	public AssertionFailedError(String message) {
-		super(defaultString(message));
-	}
-
-	private static String defaultString(String message) {
-		return message == null ? "" : message;
-	}
+    private static String defaultString(String message) {
+        return message == null ? "" : message;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/ComparisonCompactor.java b/src/main/java/junit/framework/ComparisonCompactor.java
index bbc3ba1..fa20a8e 100644
--- a/src/main/java/junit/framework/ComparisonCompactor.java
+++ b/src/main/java/junit/framework/ComparisonCompactor.java
@@ -2,71 +2,76 @@
 
 public class ComparisonCompactor {
 
-	private static final String ELLIPSIS= "...";
-	private static final String DELTA_END= "]";
-	private static final String DELTA_START= "[";
-	
-	private int fContextLength;
-	private String fExpected;
-	private String fActual;
-	private int fPrefix;
-	private int fSuffix;
+    private static final String ELLIPSIS = "...";
+    private static final String DELTA_END = "]";
+    private static final String DELTA_START = "[";
 
-	public ComparisonCompactor(int contextLength, String expected, String actual) {
-		fContextLength= contextLength;
-		fExpected= expected;
-		fActual= actual;
-	}
+    private int fContextLength;
+    private String fExpected;
+    private String fActual;
+    private int fPrefix;
+    private int fSuffix;
 
-	public String compact(String message) {
-		if (fExpected == null || fActual == null || areStringsEqual())
-			return Assert.format(message, fExpected, fActual);
+    public ComparisonCompactor(int contextLength, String expected, String actual) {
+        fContextLength = contextLength;
+        fExpected = expected;
+        fActual = actual;
+    }
 
-		findCommonPrefix();
-		findCommonSuffix();
-		String expected= compactString(fExpected);
-		String actual= compactString(fActual);
-		return Assert.format(message, expected, actual);
-	}
+    public String compact(String message) {
+        if (fExpected == null || fActual == null || areStringsEqual()) {
+            return Assert.format(message, fExpected, fActual);
+        }
 
-	private String compactString(String source) {
-		String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
-		if (fPrefix > 0)
-			result= computeCommonPrefix() + result;
-		if (fSuffix > 0)
-			result= result + computeCommonSuffix();
-		return result;
-	}
+        findCommonPrefix();
+        findCommonSuffix();
+        String expected = compactString(fExpected);
+        String actual = compactString(fActual);
+        return Assert.format(message, expected, actual);
+    }
 
-	private void findCommonPrefix() {
-		fPrefix= 0;
-		int end= Math.min(fExpected.length(), fActual.length());
-		for (; fPrefix < end; fPrefix++) {
-			if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))
-				break;
-		}
-	}
+    private String compactString(String source) {
+        String result = DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
+        if (fPrefix > 0) {
+            result = computeCommonPrefix() + result;
+        }
+        if (fSuffix > 0) {
+            result = result + computeCommonSuffix();
+        }
+        return result;
+    }
 
-	private void findCommonSuffix() {
-		int expectedSuffix= fExpected.length() - 1;
-		int actualSuffix= fActual.length() - 1;
-		for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
-			if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix))
-				break;
-		}
-		fSuffix=  fExpected.length() - expectedSuffix;
-	}
+    private void findCommonPrefix() {
+        fPrefix = 0;
+        int end = Math.min(fExpected.length(), fActual.length());
+        for (; fPrefix < end; fPrefix++) {
+            if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix)) {
+                break;
+            }
+        }
+    }
 
-	private String computeCommonPrefix() {
-		return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
-	}
+    private void findCommonSuffix() {
+        int expectedSuffix = fExpected.length() - 1;
+        int actualSuffix = fActual.length() - 1;
+        for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
+            if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix)) {
+                break;
+            }
+        }
+        fSuffix = fExpected.length() - expectedSuffix;
+    }
 
-	private String computeCommonSuffix() {
-		int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
-		return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");
-	}
+    private String computeCommonPrefix() {
+        return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
+    }
 
-	private boolean areStringsEqual() {
-		return fExpected.equals(fActual);
-	}
+    private String computeCommonSuffix() {
+        int end = Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
+        return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");
+    }
+
+    private boolean areStringsEqual() {
+        return fExpected.equals(fActual);
+    }
 }
diff --git a/src/main/java/junit/framework/ComparisonFailure.java b/src/main/java/junit/framework/ComparisonFailure.java
index 5077993..66433ef 100644
--- a/src/main/java/junit/framework/ComparisonFailure.java
+++ b/src/main/java/junit/framework/ComparisonFailure.java
@@ -2,51 +2,55 @@
 
 /**
  * Thrown when an assert equals for Strings failed.
- * 
+ *
  * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
  */
 public class ComparisonFailure extends AssertionFailedError {
-	private static final int MAX_CONTEXT_LENGTH= 20;
-	private static final long serialVersionUID= 1L;
-	
-	private String fExpected;
-	private String fActual;
+    private static final int MAX_CONTEXT_LENGTH = 20;
+    private static final long serialVersionUID = 1L;
 
-	/**
-	 * Constructs a comparison failure.
-	 * @param message the identifying message or null
-	 * @param expected the expected string value
-	 * @param actual the actual string value
-	 */
-	public ComparisonFailure (String message, String expected, String actual) {
-		super (message);
-		fExpected= expected;
-		fActual= actual;
-	}
-	
-	/**
-	 * Returns "..." in place of common prefix and "..." in
-	 * place of common suffix between expected and actual.
-	 * 
-	 * @see Throwable#getMessage()
-	 */
-	@Override
-	public String getMessage() {
-		return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
-	}
-	
-	/**
-	 * Gets the actual string value
-	 * @return the actual string value
-	 */
-	public String getActual() {
-		return fActual;
-	}
-	/**
-	 * Gets the expected string value
-	 * @return the expected string value
-	 */
-	public String getExpected() {
-		return fExpected;
-	}
+    private String fExpected;
+    private String fActual;
+
+    /**
+     * Constructs a comparison failure.
+     *
+     * @param message the identifying message or null
+     * @param expected the expected string value
+     * @param actual the actual string value
+     */
+    public ComparisonFailure(String message, String expected, String actual) {
+        super(message);
+        fExpected = expected;
+        fActual = actual;
+    }
+
+    /**
+     * Returns "..." in place of common prefix and "..." in
+     * place of common suffix between expected and actual.
+     *
+     * @see Throwable#getMessage()
+     */
+    @Override
+    public String getMessage() {
+        return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
+    }
+
+    /**
+     * Gets the actual string value
+     *
+     * @return the actual string value
+     */
+    public String getActual() {
+        return fActual;
+    }
+
+    /**
+     * Gets the expected string value
+     *
+     * @return the expected string value
+     */
+    public String getExpected() {
+        return fExpected;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/JUnit4TestAdapter.java b/src/main/java/junit/framework/JUnit4TestAdapter.java
index a05a313..cbb66db 100644
--- a/src/main/java/junit/framework/JUnit4TestAdapter.java
+++ b/src/main/java/junit/framework/JUnit4TestAdapter.java
@@ -14,72 +14,73 @@
 import org.junit.runner.manipulation.Sorter;
 
 public class JUnit4TestAdapter implements Test, Filterable, Sortable, Describable {
-	private final Class<?> fNewTestClass;
+    private final Class<?> fNewTestClass;
 
-	private final Runner fRunner;
+    private final Runner fRunner;
 
-	private final JUnit4TestAdapterCache fCache;
+    private final JUnit4TestAdapterCache fCache;
 
-	public JUnit4TestAdapter(Class<?> newTestClass) {
-		this(newTestClass, JUnit4TestAdapterCache.getDefault());
-	}
+    public JUnit4TestAdapter(Class<?> newTestClass) {
+        this(newTestClass, JUnit4TestAdapterCache.getDefault());
+    }
 
-	public JUnit4TestAdapter(final Class<?> newTestClass,
-			JUnit4TestAdapterCache cache) {
-		fCache = cache;
-		fNewTestClass = newTestClass;
-		fRunner = Request.classWithoutSuiteMethod(newTestClass).getRunner();
-	}
+    public JUnit4TestAdapter(final Class<?> newTestClass, JUnit4TestAdapterCache cache) {
+        fCache = cache;
+        fNewTestClass = newTestClass;
+        fRunner = Request.classWithoutSuiteMethod(newTestClass).getRunner();
+    }
 
-	public int countTestCases() {
-		return fRunner.testCount();
-	}
+    public int countTestCases() {
+        return fRunner.testCount();
+    }
 
-	public void run(TestResult result) {
-		fRunner.run(fCache.getNotifier(result, this));
-	}
+    public void run(TestResult result) {
+        fRunner.run(fCache.getNotifier(result, this));
+    }
 
-	// reflective interface for Eclipse
-	public List<Test> getTests() {
-		return fCache.asTestList(getDescription());
-	}
+    // reflective interface for Eclipse
+    public List<Test> getTests() {
+        return fCache.asTestList(getDescription());
+    }
 
-	// reflective interface for Eclipse
-	public Class<?> getTestClass() {
-		return fNewTestClass;
-	}
-	
-	public Description getDescription() {
-		Description description= fRunner.getDescription();		
-		return removeIgnored(description);
-	}
+    // reflective interface for Eclipse
+    public Class<?> getTestClass() {
+        return fNewTestClass;
+    }
 
-	private Description removeIgnored(Description description) {
-		if (isIgnored(description))
-			return Description.EMPTY;
-		Description result = description.childlessCopy();
-		for (Description each : description.getChildren()) {
-			Description child= removeIgnored(each);
-			if (! child.isEmpty())
-				result.addChild(child);
-		}
-		return result;
-	}
+    public Description getDescription() {
+        Description description = fRunner.getDescription();
+        return removeIgnored(description);
+    }
 
-	private boolean isIgnored(Description description) {
-		return description.getAnnotation(Ignore.class) != null;
-	}
+    private Description removeIgnored(Description description) {
+        if (isIgnored(description)) {
+            return Description.EMPTY;
+        }
+        Description result = description.childlessCopy();
+        for (Description each : description.getChildren()) {
+            Description child = removeIgnored(each);
+            if (!child.isEmpty()) {
+                result.addChild(child);
+            }
+        }
+        return result;
+    }
 
-	@Override
-	public String toString() {
-		return fNewTestClass.getName();
-	}
+    private boolean isIgnored(Description description) {
+        return description.getAnnotation(Ignore.class) != null;
+    }
 
-	public void filter(Filter filter) throws NoTestsRemainException {
-		filter.apply(fRunner);
-	}
+    @Override
+    public String toString() {
+        return fNewTestClass.getName();
+    }
 
-	public void sort(Sorter sorter) {
-		sorter.apply(fRunner);
-	}
+    public void filter(Filter filter) throws NoTestsRemainException {
+        filter.apply(fRunner);
+    }
+
+    public void sort(Sorter sorter) {
+        sorter.apply(fRunner);
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/JUnit4TestAdapterCache.java b/src/main/java/junit/framework/JUnit4TestAdapterCache.java
index 26175c5..603f261 100644
--- a/src/main/java/junit/framework/JUnit4TestAdapterCache.java
+++ b/src/main/java/junit/framework/JUnit4TestAdapterCache.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package junit.framework;
 
 import java.util.ArrayList;
@@ -14,68 +11,67 @@
 import org.junit.runner.notification.RunNotifier;
 
 public class JUnit4TestAdapterCache extends HashMap<Description, Test> {
-	private static final long serialVersionUID = 1L;
-	private static final JUnit4TestAdapterCache fInstance = new JUnit4TestAdapterCache();
+    private static final long serialVersionUID = 1L;
+    private static final JUnit4TestAdapterCache fInstance = new JUnit4TestAdapterCache();
 
-	public static JUnit4TestAdapterCache getDefault() {
-		return fInstance;
-	}
-	
-	public Test asTest(Description description) {
-		if (description.isSuite())
-			return createTest(description);
-		else {
-			if (!containsKey(description))
-				put(description, createTest(description));
-			return get(description);
-		}
-	}
+    public static JUnit4TestAdapterCache getDefault() {
+        return fInstance;
+    }
 
-	Test createTest(Description description) {
-		if (description.isTest())
-			return new JUnit4TestCaseFacade(description);
-		else {
-			TestSuite suite = new TestSuite(description.getDisplayName());
-			for (Description child : description.getChildren())
-				suite.addTest(asTest(child));
-			return suite;
-		}
-	}
+    public Test asTest(Description description) {
+        if (description.isSuite()) {
+            return createTest(description);
+        } else {
+            if (!containsKey(description)) {
+                put(description, createTest(description));
+            }
+            return get(description);
+        }
+    }
 
-	public RunNotifier getNotifier(final TestResult result,
-			final JUnit4TestAdapter adapter) {
-		RunNotifier notifier = new RunNotifier();
-		notifier.addListener(new RunListener() {
-			@Override
-			public void testFailure(Failure failure) throws Exception {
-				result.addError(asTest(failure.getDescription()), failure.getException());
-			}
+    Test createTest(Description description) {
+        if (description.isTest()) {
+            return new JUnit4TestCaseFacade(description);
+        } else {
+            TestSuite suite = new TestSuite(description.getDisplayName());
+            for (Description child : description.getChildren()) {
+                suite.addTest(asTest(child));
+            }
+            return suite;
+        }
+    }
 
-			@Override
-			public void testFinished(Description description)
-					throws Exception {
-				result.endTest(asTest(description));
-			}
+    public RunNotifier getNotifier(final TestResult result, final JUnit4TestAdapter adapter) {
+        RunNotifier notifier = new RunNotifier();
+        notifier.addListener(new RunListener() {
+            @Override
+            public void testFailure(Failure failure) throws Exception {
+                result.addError(asTest(failure.getDescription()), failure.getException());
+            }
 
-			@Override
-			public void testStarted(Description description)
-					throws Exception {
-				result.startTest(asTest(description));
-			}
-		});
-		return notifier;
-	}
+            @Override
+            public void testFinished(Description description) throws Exception {
+                result.endTest(asTest(description));
+            }
 
-	public List<Test> asTestList(Description description) {
-		if (description.isTest())
-			return Arrays.asList(asTest(description));
-		else {
-			List<Test> returnThis = new ArrayList<Test>();
-			for (Description child : description.getChildren()) {
-				returnThis.add(asTest(child));
-			}
-			return returnThis;
-		}
-	}
+            @Override
+            public void testStarted(Description description) throws Exception {
+                result.startTest(asTest(description));
+            }
+        });
+        return notifier;
+    }
+
+    public List<Test> asTestList(Description description) {
+        if (description.isTest()) {
+            return Arrays.asList(asTest(description));
+        } else {
+            List<Test> returnThis = new ArrayList<Test>();
+            for (Description child : description.getChildren()) {
+                returnThis.add(asTest(child));
+            }
+            return returnThis;
+        }
+    }
 
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/JUnit4TestCaseFacade.java b/src/main/java/junit/framework/JUnit4TestCaseFacade.java
index fd43822..5fd8ac7 100644
--- a/src/main/java/junit/framework/JUnit4TestCaseFacade.java
+++ b/src/main/java/junit/framework/JUnit4TestCaseFacade.java
@@ -1,33 +1,30 @@
-/**
- * 
- */
 package junit.framework;
 
 import org.junit.runner.Describable;
 import org.junit.runner.Description;
 
 public class JUnit4TestCaseFacade implements Test, Describable {
-	private final Description fDescription;
+    private final Description fDescription;
 
-	JUnit4TestCaseFacade(Description description) {
-		fDescription = description;
-	}
+    JUnit4TestCaseFacade(Description description) {
+        fDescription = description;
+    }
 
-	@Override
-	public String toString() {
-		return getDescription().toString();
-	}
+    @Override
+    public String toString() {
+        return getDescription().toString();
+    }
 
-	public int countTestCases() {
-		return 1;
-	}
+    public int countTestCases() {
+        return 1;
+    }
 
-	public void run(TestResult result) {
-		throw new RuntimeException(
-				"This test stub created only for informational purposes.");
-	}
+    public void run(TestResult result) {
+        throw new RuntimeException(
+                "This test stub created only for informational purposes.");
+    }
 
-	public Description getDescription() {
-		return fDescription;
-	}
+    public Description getDescription() {
+        return fDescription;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/Protectable.java b/src/main/java/junit/framework/Protectable.java
index e143237..9f30b10 100644
--- a/src/main/java/junit/framework/Protectable.java
+++ b/src/main/java/junit/framework/Protectable.java
@@ -7,8 +7,8 @@
  */
 public interface Protectable {
 
-	/**
-	 * Run the the following method protected.
-	 */
-	public abstract void protect() throws Throwable;
+    /**
+     * Run the the following method protected.
+     */
+    public abstract void protect() throws Throwable;
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/Test.java b/src/main/java/junit/framework/Test.java
index a016ee8..db95c6c 100644
--- a/src/main/java/junit/framework/Test.java
+++ b/src/main/java/junit/framework/Test.java
@@ -6,12 +6,13 @@
  * @see TestResult
  */
 public interface Test {
-	/**
-	 * Counts the number of test cases that will be run by this test.
-	 */
-	public abstract int countTestCases();
-	/**
-	 * Runs a test and collects its result in a TestResult instance.
-	 */
-	public abstract void run(TestResult result);
+    /**
+     * Counts the number of test cases that will be run by this test.
+     */
+    public abstract int countTestCases();
+
+    /**
+     * Runs a test and collects its result in a TestResult instance.
+     */
+    public abstract void run(TestResult result);
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/TestCase.java b/src/main/java/junit/framework/TestCase.java
index b047ec9..b89ce71 100644
--- a/src/main/java/junit/framework/TestCase.java
+++ b/src/main/java/junit/framework/TestCase.java
@@ -36,7 +36,7 @@
  *       assertTrue(result == 5.0);
  *    }
  * </pre>
- * 
+ *
  * Once the methods are defined you can run them. The framework supports
  * both a static type safe and more dynamic way to run a test.
  * In the static way you override the runTest method and define the method to
@@ -49,7 +49,6 @@
  * };
  * test.run();
  * </pre>
- * 
  * The dynamic way uses reflection to implement {@link #runTest()}. It dynamically finds
  * and invokes a method.
  * In this case the name of the test case has to correspond to the test method
@@ -58,7 +57,7 @@
  * TestCase test= new MathTest("testAdd");
  * test.run();
  * </pre>
- * 
+ *
  * The tests to be run can be collected into a TestSuite. JUnit provides
  * different <i>test runners</i> which can run a test suite and collect the results.
  * A test runner either expects a static method <code>suite</code> as the entry
@@ -70,143 +69,478 @@
  *    return suite;
  * }
  * </pre>
+ *
  * @see TestResult
  * @see TestSuite
  */
 public abstract class TestCase extends Assert implements Test {
-	/**
-	 * the name of the test case
-	 */
-	private String fName;
+    /**
+     * the name of the test case
+     */
+    private String fName;
 
-	/**
-	 * No-arg constructor to enable serialization. This method
-	 * is not intended to be used by mere mortals without calling setName().
-	 */
-	public TestCase() {
-		fName= null;
-	}
-	/**
-	 * Constructs a test case with the given name.
-	 */
-	public TestCase(String name) {
-		fName= name;
-	}
-	/**
-	 * Counts the number of test cases executed by run(TestResult result).
-	 */
-	public int countTestCases() {
-		return 1;
-	}
-	/**
-	 * Creates a default TestResult object
-	 *
-	 * @see TestResult
-	 */
-	protected TestResult createResult() {
-	    return new TestResult();
-	}
-	/**
-	 * A convenience method to run this test, collecting the results with a
-	 * default TestResult object.
-	 *
-	 * @see TestResult
-	 */
-	public TestResult run() {
-		TestResult result= createResult();
-		run(result);
-		return result;
-	}
-	/**
-	 * Runs the test case and collects the results in TestResult.
-	 */
-	public void run(TestResult result) {
-		result.run(this);
-	}
-	/**
-	 * Runs the bare test sequence.
-	 * @throws Throwable if any exception is thrown
-	 */
-	public void runBare() throws Throwable {
-		Throwable exception= null;
-		setUp();
-		try {
-			runTest();
-		} catch (Throwable running) {
-			exception= running;
-		}
-		finally {
-			try {
-				tearDown();
-			} catch (Throwable tearingDown) {
-				if (exception == null) exception= tearingDown;
-			}
-		}
-		if (exception != null) throw exception;
-	}
-	/**
-	 * Override to run the test and assert its state.
-	 * @throws Throwable if any exception is thrown
-	 */
-	protected void runTest() throws Throwable {
-		assertNotNull("TestCase.fName cannot be null", fName); // Some VMs crash when calling getMethod(null,null);
-		Method runMethod= null;
-		try {
-			// use getMethod to get all public inherited
-			// methods. getDeclaredMethods returns all
-			// methods of this class but excludes the
-			// inherited ones.
-			runMethod= getClass().getMethod(fName, (Class[])null);
-		} catch (NoSuchMethodException e) {
-			fail("Method \""+fName+"\" not found");
-		}
-		if (!Modifier.isPublic(runMethod.getModifiers())) {
-			fail("Method \""+fName+"\" should be public");
-		}
+    /**
+     * No-arg constructor to enable serialization. This method
+     * is not intended to be used by mere mortals without calling setName().
+     */
+    public TestCase() {
+        fName = null;
+    }
 
-		try {
-			runMethod.invoke(this);
-		}
-		catch (InvocationTargetException e) {
-			e.fillInStackTrace();
-			throw e.getTargetException();
-		}
-		catch (IllegalAccessException e) {
-			e.fillInStackTrace();
-			throw e;
-		}
-	}
-	/**
-	 * Sets up the fixture, for example, open a network connection.
-	 * This method is called before a test is executed.
-	 */
-	protected void setUp() throws Exception {
-	}
-	/**
-	 * Tears down the fixture, for example, close a network connection.
-	 * This method is called after a test is executed.
-	 */
-	protected void tearDown() throws Exception {
-	}
-	/**
-	 * Returns a string representation of the test case
-	 */
-	@Override
-	public String toString() {
-	    return getName() + "(" + getClass().getName() + ")";
-	}
-	/**
-	 * Gets the name of a TestCase
-	 * @return the name of the TestCase
-	 */
-	public String getName() {
-		return fName;
-	}
-	/**
-	 * Sets the name of a TestCase
-	 * @param name the name to set
-	 */
-	public void setName(String name) {
-		fName= name;
-	}
+    /**
+     * Constructs a test case with the given name.
+     */
+    public TestCase(String name) {
+        fName = name;
+    }
+
+    /**
+     * Counts the number of test cases executed by run(TestResult result).
+     */
+    public int countTestCases() {
+        return 1;
+    }
+
+    /**
+     * Creates a default TestResult object
+     *
+     * @see TestResult
+     */
+    protected TestResult createResult() {
+        return new TestResult();
+    }
+
+    /**
+     * A convenience method to run this test, collecting the results with a
+     * default TestResult object.
+     *
+     * @see TestResult
+     */
+    public TestResult run() {
+        TestResult result = createResult();
+        run(result);
+        return result;
+    }
+
+    /**
+     * Runs the test case and collects the results in TestResult.
+     */
+    public void run(TestResult result) {
+        result.run(this);
+    }
+
+    /**
+     * Runs the bare test sequence.
+     *
+     * @throws Throwable if any exception is thrown
+     */
+    public void runBare() throws Throwable {
+        Throwable exception = null;
+        setUp();
+        try {
+            runTest();
+        } catch (Throwable running) {
+            exception = running;
+        } finally {
+            try {
+                tearDown();
+            } catch (Throwable tearingDown) {
+                if (exception == null) exception = tearingDown;
+            }
+        }
+        if (exception != null) throw exception;
+    }
+
+    /**
+     * Override to run the test and assert its state.
+     *
+     * @throws Throwable if any exception is thrown
+     */
+    protected void runTest() throws Throwable {
+        assertNotNull("TestCase.fName cannot be null", fName); // Some VMs crash when calling getMethod(null,null);
+        Method runMethod = null;
+        try {
+            // use getMethod to get all public inherited
+            // methods. getDeclaredMethods returns all
+            // methods of this class but excludes the
+            // inherited ones.
+            runMethod = getClass().getMethod(fName, (Class[]) null);
+        } catch (NoSuchMethodException e) {
+            fail("Method \"" + fName + "\" not found");
+        }
+        if (!Modifier.isPublic(runMethod.getModifiers())) {
+            fail("Method \"" + fName + "\" should be public");
+        }
+
+        try {
+            runMethod.invoke(this);
+        } catch (InvocationTargetException e) {
+            e.fillInStackTrace();
+            throw e.getTargetException();
+        } catch (IllegalAccessException e) {
+            e.fillInStackTrace();
+            throw e;
+        }
+    }
+
+    /**
+     * Asserts that a condition is true. If it isn't it throws
+     * an AssertionFailedError with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertTrue(String message, boolean condition) {
+        Assert.assertTrue(message, condition);
+    }
+
+    /**
+     * Asserts that a condition is true. If it isn't it throws
+     * an AssertionFailedError.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertTrue(boolean condition) {
+        Assert.assertTrue(condition);
+    }
+
+    /**
+     * Asserts that a condition is false. If it isn't it throws
+     * an AssertionFailedError with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertFalse(String message, boolean condition) {
+        Assert.assertFalse(message, condition);
+    }
+
+    /**
+     * Asserts that a condition is false. If it isn't it throws
+     * an AssertionFailedError.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertFalse(boolean condition) {
+        Assert.assertFalse(condition);
+    }
+
+    /**
+     * Fails a test with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void fail(String message) {
+        Assert.fail(message);
+    }
+
+    /**
+     * Fails a test with no message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void fail() {
+        Assert.fail();
+    }
+
+    /**
+     * Asserts that two objects are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, Object expected, Object actual) {
+        Assert.assertEquals(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two objects are equal. If they are not
+     * an AssertionFailedError is thrown.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(Object expected, Object actual) {
+        Assert.assertEquals(expected, actual);
+    }
+
+    /**
+     * Asserts that two Strings are equal.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, String expected, String actual) {
+        Assert.assertEquals(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two Strings are equal.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String expected, String actual) {
+        Assert.assertEquals(expected, actual);
+    }
+
+    /**
+     * Asserts that two doubles are equal concerning a delta.  If they are not
+     * an AssertionFailedError is thrown with the given message.  If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, double expected, double actual, double delta) {
+        Assert.assertEquals(message, expected, actual, delta);
+    }
+
+    /**
+     * Asserts that two doubles are equal concerning a delta. If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(double expected, double actual, double delta) {
+        Assert.assertEquals(expected, actual, delta);
+    }
+
+    /**
+     * Asserts that two floats are equal concerning a positive delta. If they
+     * are not an AssertionFailedError is thrown with the given message. If the
+     * expected value is infinity then the delta value is ignored.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, float expected, float actual, float delta) {
+        Assert.assertEquals(message, expected, actual, delta);
+    }
+
+    /**
+     * Asserts that two floats are equal concerning a delta. If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(float expected, float actual, float delta) {
+        Assert.assertEquals(expected, actual, delta);
+    }
+
+    /**
+     * Asserts that two longs are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, long expected, long actual) {
+        Assert.assertEquals(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two longs are equal.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(long expected, long actual) {
+        Assert.assertEquals(expected, actual);
+    }
+
+    /**
+     * Asserts that two booleans are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, boolean expected, boolean actual) {
+        Assert.assertEquals(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two booleans are equal.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(boolean expected, boolean actual) {
+        Assert.assertEquals(expected, actual);
+    }
+
+    /**
+     * Asserts that two bytes are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, byte expected, byte actual) {
+        Assert.assertEquals(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two bytes are equal.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(byte expected, byte actual) {
+        Assert.assertEquals(expected, actual);
+    }
+
+    /**
+     * Asserts that two chars are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, char expected, char actual) {
+        Assert.assertEquals(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two chars are equal.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(char expected, char actual) {
+        Assert.assertEquals(expected, actual);
+    }
+
+    /**
+     * Asserts that two shorts are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, short expected, short actual) {
+        Assert.assertEquals(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two shorts are equal.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(short expected, short actual) {
+        Assert.assertEquals(expected, actual);
+    }
+
+    /**
+     * Asserts that two ints are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(String message, int expected, int actual) {
+        Assert.assertEquals(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two ints are equal.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEquals(int expected, int actual) {
+        Assert.assertEquals(expected, actual);
+    }
+
+    /**
+     * Asserts that an object isn't null.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertNotNull(Object object) {
+        Assert.assertNotNull(object);
+    }
+
+    /**
+     * Asserts that an object isn't null. If it is
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertNotNull(String message, Object object) {
+        Assert.assertNotNull(message, object);
+    }
+
+    /**
+     * Asserts that an object is null. If it isn't an {@link AssertionError} is
+     * thrown.
+     * Message contains: Expected: <null> but was: object
+     *
+     * @param object Object to check or <code>null</code>
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertNull(Object object) {
+        Assert.assertNull(object);
+    }
+
+    /**
+     * Asserts that an object is null.  If it is not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertNull(String message, Object object) {
+        Assert.assertNull(message, object);
+    }
+
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertSame(String message, Object expected, Object actual) {
+        Assert.assertSame(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * the same an AssertionFailedError is thrown.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertSame(Object expected, Object actual) {
+        Assert.assertSame(expected, actual);
+    }
+
+    /**
+     * Asserts that two objects do not refer to the same object. If they do
+     * refer to the same object an AssertionFailedError is thrown with the
+     * given message.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertNotSame(String message, Object expected, Object actual) {
+        Assert.assertNotSame(message, expected, actual);
+    }
+
+    /**
+     * Asserts that two objects do not refer to the same object. If they do
+     * refer to the same object an AssertionFailedError is thrown.
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertNotSame(Object expected, Object actual) {
+        Assert.assertNotSame(expected, actual);
+    }
+
+    @SuppressWarnings("deprecation")
+    public static void failSame(String message) {
+        Assert.failSame(message);
+    }
+
+    @SuppressWarnings("deprecation")
+    public static void failNotSame(String message, Object expected, Object actual) {
+        Assert.failNotSame(message, expected, actual);
+    }
+
+    @SuppressWarnings("deprecation")
+    public static void failNotEquals(String message, Object expected, Object actual) {
+        Assert.failNotEquals(message, expected, actual);
+    }
+
+    @SuppressWarnings("deprecation")
+    public static String format(String message, Object expected, Object actual) {
+        return Assert.format(message, expected, actual);
+    }
+
+    /**
+     * Sets up the fixture, for example, open a network connection.
+     * This method is called before a test is executed.
+     */
+    protected void setUp() throws Exception {
+    }
+
+    /**
+     * Tears down the fixture, for example, close a network connection.
+     * This method is called after a test is executed.
+     */
+    protected void tearDown() throws Exception {
+    }
+
+    /**
+     * Returns a string representation of the test case
+     */
+    @Override
+    public String toString() {
+        return getName() + "(" + getClass().getName() + ")";
+    }
+
+    /**
+     * Gets the name of a TestCase
+     *
+     * @return the name of the TestCase
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Sets the name of a TestCase
+     *
+     * @param name the name to set
+     */
+    public void setName(String name) {
+        fName = name;
+    }
 }
diff --git a/src/main/java/junit/framework/TestFailure.java b/src/main/java/junit/framework/TestFailure.java
index 6662b1f..6168b58 100644
--- a/src/main/java/junit/framework/TestFailure.java
+++ b/src/main/java/junit/framework/TestFailure.java
@@ -5,54 +5,69 @@
 
 
 /**
- * A <code>TestFailure</code> collects a failed test together with
+ * A {@code TestFailure} collects a failed test together with
  * the caught exception.
+ *
  * @see TestResult
  */
-public class TestFailure extends Object {
-	protected Test fFailedTest;
-	protected Throwable fThrownException;
-	
+public class TestFailure {
+    protected Test fFailedTest;
+    protected Throwable fThrownException;
 
-	/**
-	 * Constructs a TestFailure with the given test and exception.
-	 */
-	public TestFailure(Test failedTest, Throwable thrownException) {
-		fFailedTest= failedTest;
-		fThrownException= thrownException;
-	}
-	/**
-	 * Gets the failed test.
-	 */
-	public Test failedTest() {
-	    return fFailedTest;
-	}
-	/**
-	 * Gets the thrown exception.
-	 */
-	public Throwable thrownException() {
-	    return fThrownException;
-	}
-	/**
-	 * Returns a short description of the failure.
-	 */
-	@Override
-	public String toString() {
-	    StringBuffer buffer= new StringBuffer();
-	    buffer.append(fFailedTest+": "+fThrownException.getMessage());
-	    return buffer.toString();
-	}
-	public String trace() {
-		StringWriter stringWriter= new StringWriter();
-		PrintWriter writer= new PrintWriter(stringWriter);
-		thrownException().printStackTrace(writer);
-		StringBuffer buffer= stringWriter.getBuffer();
-		return buffer.toString();
-	}
-	public String exceptionMessage() {
-		return thrownException().getMessage();
-	}
-	public boolean isFailure() {
-		return thrownException() instanceof AssertionFailedError;
-	}
+    /**
+     * Constructs a TestFailure with the given test and exception.
+     */
+    public TestFailure(Test failedTest, Throwable thrownException) {
+        fFailedTest = failedTest;
+        fThrownException = thrownException;
+    }
+
+    /**
+     * Gets the failed test.
+     */
+    public Test failedTest() {
+        return fFailedTest;
+    }
+
+    /**
+     * Gets the thrown exception.
+     */
+    public Throwable thrownException() {
+        return fThrownException;
+    }
+
+    /**
+     * Returns a short description of the failure.
+     */
+    @Override
+    public String toString() {
+        return fFailedTest + ": " + fThrownException.getMessage();
+    }
+    
+    /**
+     * Returns a String containing the stack trace of the error
+     * thrown by TestFailure.
+     */
+    public String trace() {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter writer = new PrintWriter(stringWriter);
+        thrownException().printStackTrace(writer);
+        return stringWriter.toString();
+    }
+
+    /**
+     * Returns a String containing the message from the thrown exception.
+     */
+    public String exceptionMessage() {
+        return thrownException().getMessage();
+    }
+
+    /**
+     * Returns {@code true} if the error is considered a failure
+     * (i.e. if it is an instance of {@code AssertionFailedError}),
+     * {@code false} otherwise.
+     */
+    public boolean isFailure() {
+        return thrownException() instanceof AssertionFailedError;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/TestListener.java b/src/main/java/junit/framework/TestListener.java
index 9b69443..32d1a7f 100644
--- a/src/main/java/junit/framework/TestListener.java
+++ b/src/main/java/junit/framework/TestListener.java
@@ -4,20 +4,23 @@
  * A Listener for test progress
  */
 public interface TestListener {
-	/**
- 	 * An error occurred.
- 	 */
-	public void addError(Test test, Throwable t);
-	/**
- 	 * A failure occurred.
- 	 */
- 	public void addFailure(Test test, AssertionFailedError t);  
-	/**
-	 * A test ended.
-	 */
- 	public void endTest(Test test); 
-	/**
-	 * A test started.
-	 */
-	public void startTest(Test test);
+    /**
+     * An error occurred.
+     */
+    public void addError(Test test, Throwable e);
+
+    /**
+     * A failure occurred.
+     */
+    public void addFailure(Test test, AssertionFailedError e);
+
+    /**
+     * A test ended.
+     */
+    public void endTest(Test test);
+
+    /**
+     * A test started.
+     */
+    public void startTest(Test test);
 }
\ No newline at end of file
diff --git a/src/main/java/junit/framework/TestResult.java b/src/main/java/junit/framework/TestResult.java
index 5768e9a..8332542 100644
--- a/src/main/java/junit/framework/TestResult.java
+++ b/src/main/java/junit/framework/TestResult.java
@@ -14,156 +14,172 @@
  *
  * @see Test
  */
-public class TestResult extends Object {
-	protected List<TestFailure> fFailures;
-	protected List<TestFailure> fErrors;
-	protected List<TestListener> fListeners;
-	protected int fRunTests;
-	private boolean fStop;
-	
-	public TestResult() {
-		fFailures= new ArrayList<TestFailure>();
-		fErrors= new ArrayList<TestFailure>();
-		fListeners= new ArrayList<TestListener>();
-		fRunTests= 0;
-		fStop= false;
-	}
-	/**
-	 * Adds an error to the list of errors. The passed in exception
-	 * caused the error.
-	 */
-	public synchronized void addError(Test test, Throwable t) {
-		fErrors.add(new TestFailure(test, t));
-		for (TestListener each : cloneListeners())
-			each.addError(test, t);
-	}
-	/**
-	 * Adds a failure to the list of failures. The passed in exception
-	 * caused the failure.
-	 */
-	public synchronized void addFailure(Test test, AssertionFailedError t) {
-		fFailures.add(new TestFailure(test, t));
-		for (TestListener each : cloneListeners())
-			each.addFailure(test, t);
-	}
-	/**
-	 * Registers a TestListener
-	 */
-	public synchronized void addListener(TestListener listener) {
-		fListeners.add(listener);
-	}
-	/**
-	 * Unregisters a TestListener
-	 */
-	public synchronized void removeListener(TestListener listener) {
-		fListeners.remove(listener);
-	}
-	/**
-	 * Returns a copy of the listeners.
-	 */
-	private synchronized List<TestListener> cloneListeners() {
-		List<TestListener> result= new ArrayList<TestListener>();
-		result.addAll(fListeners);
-		return result;
-	}
-	/**
-	 * Informs the result that a test was completed.
-	 */
-	public void endTest(Test test) {
-		for (TestListener each : cloneListeners())
-			each.endTest(test);
-	}
-	/**
-	 * Gets the number of detected errors.
-	 */
-	public synchronized int errorCount() {
-		return fErrors.size();
-	}
-	/**
-	 * Returns an Enumeration for the errors
-	 */
-	public synchronized Enumeration<TestFailure> errors() {
-		return Collections.enumeration(fErrors);
-	}
-	
+public class TestResult {
+    protected List<TestFailure> fFailures;
+    protected List<TestFailure> fErrors;
+    protected List<TestListener> fListeners;
+    protected int fRunTests;
+    private boolean fStop;
 
-	/**
-	 * Gets the number of detected failures.
-	 */
-	public synchronized int failureCount() {
-		return fFailures.size();
-	}
-	/**
-	 * Returns an Enumeration for the failures
-	 */
-	public synchronized Enumeration<TestFailure> failures() {
-		return Collections.enumeration(fFailures);
-	}
-	
-	/**
-	 * Runs a TestCase.
-	 */
-	protected void run(final TestCase test) {
-		startTest(test);
-		Protectable p= new Protectable() {
-			public void protect() throws Throwable {
-				test.runBare();
-			}
-		};
-		runProtected(test, p);
+    public TestResult() {
+        fFailures = new ArrayList<TestFailure>();
+        fErrors = new ArrayList<TestFailure>();
+        fListeners = new ArrayList<TestListener>();
+        fRunTests = 0;
+        fStop = false;
+    }
 
-		endTest(test);
-	}
-	/**
-	 * Gets the number of run tests.
-	 */
-	public synchronized int runCount() {
-		return fRunTests;
-	}
-	/**
-	 * Runs a TestCase.
-	 */
-	public void runProtected(final Test test, Protectable p) {
-		try {
-			p.protect();
-		} 
-		catch (AssertionFailedError e) {
-			addFailure(test, e);
-		}
-		catch (ThreadDeath e) { // don't catch ThreadDeath by accident
-			throw e;
-		}
-		catch (Throwable e) {
-			addError(test, e);
-		}
-	}
-	/**
-	 * Checks whether the test run should stop
-	 */
-	public synchronized boolean shouldStop() {
-		return fStop;
-	}
-	/**
-	 * Informs the result that a test will be started.
-	 */
-	public void startTest(Test test) {
-		final int count= test.countTestCases();
-		synchronized(this) {
-			fRunTests+= count;
-		}
-		for (TestListener each : cloneListeners())
-			each.startTest(test);
-	}
-	/**
-	 * Marks that the test run should stop.
-	 */
-	public synchronized void stop() {
-		fStop= true;
-	}
-	/**
-	 * Returns whether the entire test was successful or not.
-	 */
-	public synchronized boolean wasSuccessful() {
-		return failureCount() == 0 && errorCount() == 0;
-	}
-}
\ No newline at end of file
+    /**
+     * Adds an error to the list of errors. The passed in exception
+     * caused the error.
+     */
+    public synchronized void addError(Test test, Throwable e) {
+        fErrors.add(new TestFailure(test, e));
+        for (TestListener each : cloneListeners()) {
+            each.addError(test, e);
+        }
+    }
+
+    /**
+     * Adds a failure to the list of failures. The passed in exception
+     * caused the failure.
+     */
+    public synchronized void addFailure(Test test, AssertionFailedError e) {
+        fFailures.add(new TestFailure(test, e));
+        for (TestListener each : cloneListeners()) {
+            each.addFailure(test, e);
+        }
+    }
+
+    /**
+     * Registers a TestListener
+     */
+    public synchronized void addListener(TestListener listener) {
+        fListeners.add(listener);
+    }
+
+    /**
+     * Unregisters a TestListener
+     */
+    public synchronized void removeListener(TestListener listener) {
+        fListeners.remove(listener);
+    }
+
+    /**
+     * Returns a copy of the listeners.
+     */
+    private synchronized List<TestListener> cloneListeners() {
+        List<TestListener> result = new ArrayList<TestListener>();
+        result.addAll(fListeners);
+        return result;
+    }
+
+    /**
+     * Informs the result that a test was completed.
+     */
+    public void endTest(Test test) {
+        for (TestListener each : cloneListeners()) {
+            each.endTest(test);
+        }
+    }
+
+    /**
+     * Gets the number of detected errors.
+     */
+    public synchronized int errorCount() {
+        return fErrors.size();
+    }
+
+    /**
+     * Returns an Enumeration for the errors
+     */
+    public synchronized Enumeration<TestFailure> errors() {
+        return Collections.enumeration(fErrors);
+    }
+
+
+    /**
+     * Gets the number of detected failures.
+     */
+    public synchronized int failureCount() {
+        return fFailures.size();
+    }
+
+    /**
+     * Returns an Enumeration for the failures
+     */
+    public synchronized Enumeration<TestFailure> failures() {
+        return Collections.enumeration(fFailures);
+    }
+
+    /**
+     * Runs a TestCase.
+     */
+    protected void run(final TestCase test) {
+        startTest(test);
+        Protectable p = new Protectable() {
+            public void protect() throws Throwable {
+                test.runBare();
+            }
+        };
+        runProtected(test, p);
+
+        endTest(test);
+    }
+
+    /**
+     * Gets the number of run tests.
+     */
+    public synchronized int runCount() {
+        return fRunTests;
+    }
+
+    /**
+     * Runs a TestCase.
+     */
+    public void runProtected(final Test test, Protectable p) {
+        try {
+            p.protect();
+        } catch (AssertionFailedError e) {
+            addFailure(test, e);
+        } catch (ThreadDeath e) { // don't catch ThreadDeath by accident
+            throw e;
+        } catch (Throwable e) {
+            addError(test, e);
+        }
+    }
+
+    /**
+     * Checks whether the test run should stop
+     */
+    public synchronized boolean shouldStop() {
+        return fStop;
+    }
+
+    /**
+     * Informs the result that a test will be started.
+     */
+    public void startTest(Test test) {
+        final int count = test.countTestCases();
+        synchronized (this) {
+            fRunTests += count;
+        }
+        for (TestListener each : cloneListeners()) {
+            each.startTest(test);
+        }
+    }
+
+    /**
+     * Marks that the test run should stop.
+     */
+    public synchronized void stop() {
+        fStop = true;
+    }
+
+    /**
+     * Returns whether the entire test was successful or not.
+     */
+    public synchronized boolean wasSuccessful() {
+        return failureCount() == 0 && errorCount() == 0;
+    }
+}
diff --git a/src/main/java/junit/framework/TestSuite.java b/src/main/java/junit/framework/TestSuite.java
index 336efd1..366f1cf 100644
--- a/src/main/java/junit/framework/TestSuite.java
+++ b/src/main/java/junit/framework/TestSuite.java
@@ -11,8 +11,10 @@
 import java.util.List;
 import java.util.Vector;
 
+import org.junit.internal.MethodSorter;
+
 /**
- * <p>A <code>TestSuite</code> is a <code>Composite</code> of Tests.
+ * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
  * It runs a collection of test cases. Here is an example using
  * the dynamic test definition.
  * <pre>
@@ -20,288 +22,298 @@
  * suite.addTest(new MathTest("testAdd"));
  * suite.addTest(new MathTest("testDivideByZero"));
  * </pre>
- * </p>
- * 
- * <p>Alternatively, a TestSuite can extract the tests to be run automatically.
+ * <p>
+ * Alternatively, a TestSuite can extract the tests to be run automatically.
  * To do so you pass the class of your TestCase class to the
  * TestSuite constructor.
  * <pre>
  * TestSuite suite= new TestSuite(MathTest.class);
  * </pre>
- * </p>
- * 
- * <p>This constructor creates a suite with all the methods
- * starting with "test" that take no arguments.</p>
- * 
- * <p>A final option is to do the same for a large array of test classes.
+ * <p>
+ * This constructor creates a suite with all the methods
+ * starting with "test" that take no arguments.
+ * <p>
+ * A final option is to do the same for a large array of test classes.
  * <pre>
  * Class[] testClasses = { MathTest.class, AnotherTest.class }
  * TestSuite suite= new TestSuite(testClasses);
  * </pre>
- * </p>
  *
  * @see Test
  */
 public class TestSuite implements Test {
 
-	/**
-	 * ...as the moon sets over the early morning Merlin, Oregon
-	 * mountains, our intrepid adventurers type...
-	 */
-	static public Test createTest(Class<?> theClass, String name) {
-		Constructor<?> constructor;
-		try {
-			constructor= getTestConstructor(theClass);
-		} catch (NoSuchMethodException e) {
-			return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
-		}
-		Object test;
-		try {
-			if (constructor.getParameterTypes().length == 0) {
-				test= constructor.newInstance(new Object[0]);
-				if (test instanceof TestCase)
-					((TestCase) test).setName(name);
-			} else {
-				test= constructor.newInstance(new Object[]{name});
-			}
-		} catch (InstantiationException e) {
-			return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
-		} catch (InvocationTargetException e) {
-			return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
-		} catch (IllegalAccessException e) {
-			return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
-		}
-		return (Test) test;
-	}
-	
-	/**
-	 * Gets a constructor which takes a single String as
-	 * its argument or a no arg constructor.
-	 */
-	public static Constructor<?> getTestConstructor(Class<?> theClass) throws NoSuchMethodException {
-		try {
-			return theClass.getConstructor(String.class);	
-		} catch (NoSuchMethodException e) {
-			// fall through
-		}
-		return theClass.getConstructor(new Class[0]);
-	}
-
-	/**
-	 * Returns a test which will fail and log a warning message.
-	 */
-	public static Test warning(final String message) {
-		return new TestCase("warning") {
-			@Override
-			protected void runTest() {
-				fail(message);
-			}
-		};
-	}
-
-	/**
-	 * Converts the stack trace into a string
-	 */
-	private static String exceptionToString(Throwable t) {
-		StringWriter stringWriter= new StringWriter();
-		PrintWriter writer= new PrintWriter(stringWriter);
-		t.printStackTrace(writer);
-		return stringWriter.toString();
-	}
-	
-	private String fName;
-
-	private Vector<Test> fTests= new Vector<Test>(10); // Cannot convert this to List because it is used directly by some test runners
+    /**
+     * ...as the moon sets over the early morning Merlin, Oregon
+     * mountains, our intrepid adventurers type...
+     */
+    static public Test createTest(Class<?> theClass, String name) {
+        Constructor<?> constructor;
+        try {
+            constructor = getTestConstructor(theClass);
+        } catch (NoSuchMethodException e) {
+            return warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()");
+        }
+        Object test;
+        try {
+            if (constructor.getParameterTypes().length == 0) {
+                test = constructor.newInstance(new Object[0]);
+                if (test instanceof TestCase) {
+                    ((TestCase) test).setName(name);
+                }
+            } else {
+                test = constructor.newInstance(new Object[]{name});
+            }
+        } catch (InstantiationException e) {
+            return (warning("Cannot instantiate test case: " + name + " (" + exceptionToString(e) + ")"));
+        } catch (InvocationTargetException e) {
+            return (warning("Exception in constructor: " + name + " (" + exceptionToString(e.getTargetException()) + ")"));
+        } catch (IllegalAccessException e) {
+            return (warning("Cannot access test case: " + name + " (" + exceptionToString(e) + ")"));
+        }
+        return (Test) test;
+    }
 
     /**
-	 * Constructs an empty TestSuite.
-	 */
-	public TestSuite() {
-	}
-	
-	/**
-	 * Constructs a TestSuite from the given class. Adds all the methods
-	 * starting with "test" as test cases to the suite.
-	 * Parts of this method were written at 2337 meters in the Hueffihuette,
-	 * Kanton Uri
-	 */
-	public TestSuite(final Class<?> theClass) {
-		addTestsFromTestCase(theClass);
-	}
+     * Gets a constructor which takes a single String as
+     * its argument or a no arg constructor.
+     */
+    public static Constructor<?> getTestConstructor(Class<?> theClass) throws NoSuchMethodException {
+        try {
+            return theClass.getConstructor(String.class);
+        } catch (NoSuchMethodException e) {
+            // fall through
+        }
+        return theClass.getConstructor();
+    }
 
-	private void addTestsFromTestCase(final Class<?> theClass) {
-		fName= theClass.getName();
-		try {
-			getTestConstructor(theClass); // Avoid generating multiple error messages
-		} catch (NoSuchMethodException e) {
-			addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
-			return;
-		}
+    /**
+     * Returns a test which will fail and log a warning message.
+     */
+    public static Test warning(final String message) {
+        return new TestCase("warning") {
+            @Override
+            protected void runTest() {
+                fail(message);
+            }
+        };
+    }
 
-		if (!Modifier.isPublic(theClass.getModifiers())) {
-			addTest(warning("Class "+theClass.getName()+" is not public"));
-			return;
-		}
+    /**
+     * Converts the stack trace into a string
+     */
+    private static String exceptionToString(Throwable e) {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter writer = new PrintWriter(stringWriter);
+        e.printStackTrace(writer);
+        return stringWriter.toString();
+    }
 
-		Class<?> superClass= theClass;
-		List<String> names= new ArrayList<String>();
-		while (Test.class.isAssignableFrom(superClass)) {
-			for (Method each : superClass.getDeclaredMethods())
-				addTestMethod(each, names, theClass);
-			superClass= superClass.getSuperclass();
-		}
-		if (fTests.size() == 0)
-			addTest(warning("No tests found in "+theClass.getName()));
-	}
-	
-	/**
-	 * Constructs a TestSuite from the given class with the given name.
-	 * @see TestSuite#TestSuite(Class)
-	 */
-	public TestSuite(Class<? extends TestCase>  theClass, String name) {
-		this(theClass);
-		setName(name);
-	}
-	
-   	/**
-	 * Constructs an empty TestSuite.
-	 */
-	public TestSuite(String name) {
-		setName(name);
-	}
-	
-	/**
-	 * Constructs a TestSuite from the given array of classes.  
-	 * @param classes {@link TestCase}s
-	 */
-	public TestSuite (Class<?>... classes) {
-		for (Class<?> each : classes)
-			addTest(testCaseForClass(each));
-	}
+    private String fName;
 
-	private Test testCaseForClass(Class<?> each) {
-		if (TestCase.class.isAssignableFrom(each))
-			return new TestSuite(each.asSubclass(TestCase.class));
-		else
-			return warning(each.getCanonicalName() + " does not extend TestCase");
-	}
-	
-	/**
-	 * Constructs a TestSuite from the given array of classes with the given name.
-	 * @see TestSuite#TestSuite(Class[])
-	 */
-	public TestSuite(Class<? extends TestCase>[] classes, String name) {
-		this(classes);
-		setName(name);
-	}
-	
-	/**
-	 * Adds a test to the suite.
-	 */
-	public void addTest(Test test) {
-		fTests.add(test);
-	}
+    private Vector<Test> fTests = new Vector<Test>(10); // Cannot convert this to List because it is used directly by some test runners
 
-	/**
-	 * Adds the tests from the given class to the suite
-	 */
-	public void addTestSuite(Class<? extends TestCase> testClass) {
-		addTest(new TestSuite(testClass));
-	}
-	
-	/**
-	 * Counts the number of test cases that will be run by this test.
-	 */
-	public int countTestCases() {
-		int count= 0;
-		for (Test each : fTests)
-			count+=  each.countTestCases();
-		return count;
-	}
+    /**
+     * Constructs an empty TestSuite.
+     */
+    public TestSuite() {
+    }
 
-	/**
-	 * Returns the name of the suite. Not all
-	 * test suites have a name and this method
-	 * can return null.
-	 */
-	public String getName() {
-		return fName;
-	}
-	 
-	/**
-	 * Runs the tests and collects their result in a TestResult.
-	 */
-	public void run(TestResult result) {
-		for (Test each : fTests) {
-	  		if (result.shouldStop() )
-	  			break;
-			runTest(each, result);
-		}
-	}
+    /**
+     * Constructs a TestSuite from the given class. Adds all the methods
+     * starting with "test" as test cases to the suite.
+     * Parts of this method were written at 2337 meters in the Hueffihuette,
+     * Kanton Uri
+     */
+    public TestSuite(final Class<?> theClass) {
+        addTestsFromTestCase(theClass);
+    }
 
-	public void runTest(Test test, TestResult result) {
-		test.run(result);
-	}
-	 
-	/**
-	 * Sets the name of the suite.
-	 * @param name the name to set
-	 */
-	public void setName(String name) {
-		fName= name;
-	}
+    private void addTestsFromTestCase(final Class<?> theClass) {
+        fName = theClass.getName();
+        try {
+            getTestConstructor(theClass); // Avoid generating multiple error messages
+        } catch (NoSuchMethodException e) {
+            addTest(warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()"));
+            return;
+        }
 
-	/**
-	 * Returns the test at the given index
-	 */
-	public Test testAt(int index) {
-		return fTests.get(index);
-	}
-	
-	/**
-	 * Returns the number of tests in this suite
-	 */
-	public int testCount() {
-		return fTests.size();
-	}
-	
-	/**
-	 * Returns the tests as an enumeration
-	 */
-	public Enumeration<Test> tests() {
-		return fTests.elements();
-	}
-	
-	/**
-	 */
-	@Override
-	public String toString() {
-		if (getName() != null)
-			return getName();
-		return super.toString();
-	 }
+        if (!Modifier.isPublic(theClass.getModifiers())) {
+            addTest(warning("Class " + theClass.getName() + " is not public"));
+            return;
+        }
 
-	private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
-		String name= m.getName();
-		if (names.contains(name))
-			return;
-		if (! isPublicTestMethod(m)) {
-			if (isTestMethod(m))
-				addTest(warning("Test method isn't public: "+ m.getName() + "(" + theClass.getCanonicalName() + ")"));
-			return;
-		}
-		names.add(name);
-		addTest(createTest(theClass, name));
-	}
+        Class<?> superClass = theClass;
+        List<String> names = new ArrayList<String>();
+        while (Test.class.isAssignableFrom(superClass)) {
+            for (Method each : MethodSorter.getDeclaredMethods(superClass)) {
+                addTestMethod(each, names, theClass);
+            }
+            superClass = superClass.getSuperclass();
+        }
+        if (fTests.size() == 0) {
+            addTest(warning("No tests found in " + theClass.getName()));
+        }
+    }
 
-	private boolean isPublicTestMethod(Method m) {
-		return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
-	 }
-	 
-	private boolean isTestMethod(Method m) {
-		return 
-			m.getParameterTypes().length == 0 && 
-			m.getName().startsWith("test") && 
-			m.getReturnType().equals(Void.TYPE);
-	 }
-}
\ No newline at end of file
+    /**
+     * Constructs a TestSuite from the given class with the given name.
+     *
+     * @see TestSuite#TestSuite(Class)
+     */
+    public TestSuite(Class<? extends TestCase> theClass, String name) {
+        this(theClass);
+        setName(name);
+    }
+
+    /**
+     * Constructs an empty TestSuite.
+     */
+    public TestSuite(String name) {
+        setName(name);
+    }
+
+    /**
+     * Constructs a TestSuite from the given array of classes.
+     *
+     * @param classes {@link TestCase}s
+     */
+    public TestSuite(Class<?>... classes) {
+        for (Class<?> each : classes) {
+            addTest(testCaseForClass(each));
+        }
+    }
+
+    private Test testCaseForClass(Class<?> each) {
+        if (TestCase.class.isAssignableFrom(each)) {
+            return new TestSuite(each.asSubclass(TestCase.class));
+        } else {
+            return warning(each.getCanonicalName() + " does not extend TestCase");
+        }
+    }
+
+    /**
+     * Constructs a TestSuite from the given array of classes with the given name.
+     *
+     * @see TestSuite#TestSuite(Class[])
+     */
+    public TestSuite(Class<? extends TestCase>[] classes, String name) {
+        this(classes);
+        setName(name);
+    }
+
+    /**
+     * Adds a test to the suite.
+     */
+    public void addTest(Test test) {
+        fTests.add(test);
+    }
+
+    /**
+     * Adds the tests from the given class to the suite
+     */
+    public void addTestSuite(Class<? extends TestCase> testClass) {
+        addTest(new TestSuite(testClass));
+    }
+
+    /**
+     * Counts the number of test cases that will be run by this test.
+     */
+    public int countTestCases() {
+        int count = 0;
+        for (Test each : fTests) {
+            count += each.countTestCases();
+        }
+        return count;
+    }
+
+    /**
+     * Returns the name of the suite. Not all
+     * test suites have a name and this method
+     * can return null.
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Runs the tests and collects their result in a TestResult.
+     */
+    public void run(TestResult result) {
+        for (Test each : fTests) {
+            if (result.shouldStop()) {
+                break;
+            }
+            runTest(each, result);
+        }
+    }
+
+    public void runTest(Test test, TestResult result) {
+        test.run(result);
+    }
+
+    /**
+     * Sets the name of the suite.
+     *
+     * @param name the name to set
+     */
+    public void setName(String name) {
+        fName = name;
+    }
+
+    /**
+     * Returns the test at the given index
+     */
+    public Test testAt(int index) {
+        return fTests.get(index);
+    }
+
+    /**
+     * Returns the number of tests in this suite
+     */
+    public int testCount() {
+        return fTests.size();
+    }
+
+    /**
+     * Returns the tests as an enumeration
+     */
+    public Enumeration<Test> tests() {
+        return fTests.elements();
+    }
+
+    /**
+     */
+    @Override
+    public String toString() {
+        if (getName() != null) {
+            return getName();
+        }
+        return super.toString();
+    }
+
+    private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
+        String name = m.getName();
+        if (names.contains(name)) {
+            return;
+        }
+        if (!isPublicTestMethod(m)) {
+            if (isTestMethod(m)) {
+                addTest(warning("Test method isn't public: " + m.getName() + "(" + theClass.getCanonicalName() + ")"));
+            }
+            return;
+        }
+        names.add(name);
+        addTest(createTest(theClass, name));
+    }
+
+    private boolean isPublicTestMethod(Method m) {
+        return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
+    }
+
+    private boolean isTestMethod(Method m) {
+        return m.getParameterTypes().length == 0 &&
+                m.getName().startsWith("test") &&
+                m.getReturnType().equals(Void.TYPE);
+    }
+}
diff --git a/src/main/java/junit/runner/BaseTestRunner.java b/src/main/java/junit/runner/BaseTestRunner.java
index 6a4b090..8268323 100644
--- a/src/main/java/junit/runner/BaseTestRunner.java
+++ b/src/main/java/junit/runner/BaseTestRunner.java
@@ -25,294 +25,303 @@
  * This class was born live on stage in Sardinia during XP2000.
  */
 public abstract class BaseTestRunner implements TestListener {
-	public static final String SUITE_METHODNAME= "suite";
+    public static final String SUITE_METHODNAME = "suite";
 
-	private static Properties fPreferences;
-	static int fgMaxMessageLength= 500;
-	static boolean fgFilterStack= true;
-	boolean fLoading= true;
+    private static Properties fPreferences;
+    static int fgMaxMessageLength = 500;
+    static boolean fgFilterStack = true;
+    boolean fLoading = true;
 
     /*
     * Implementation of TestListener
     */
-	public synchronized void startTest(Test test) {
-		testStarted(test.toString());
-	}
+    public synchronized void startTest(Test test) {
+        testStarted(test.toString());
+    }
 
-	protected static void setPreferences(Properties preferences) {
-		fPreferences= preferences;
-	}
+    protected static void setPreferences(Properties preferences) {
+        fPreferences = preferences;
+    }
 
-	protected static Properties getPreferences() {
-		if (fPreferences == null) {
-			fPreferences= new Properties();
-	 		fPreferences.put("loading", "true");
- 			fPreferences.put("filterstack", "true");
-  			readPreferences();
-		}
-		return fPreferences;
-	}
+    protected static Properties getPreferences() {
+        if (fPreferences == null) {
+            fPreferences = new Properties();
+            fPreferences.put("loading", "true");
+            fPreferences.put("filterstack", "true");
+            readPreferences();
+        }
+        return fPreferences;
+    }
 
-	public static void savePreferences() throws IOException {
-		FileOutputStream fos= new FileOutputStream(getPreferencesFile());
-		try {
-			getPreferences().store(fos, "");
-		} finally {
-			fos.close();
-		}
-	}
+    public static void savePreferences() throws IOException {
+        FileOutputStream fos = new FileOutputStream(getPreferencesFile());
+        try {
+            getPreferences().store(fos, "");
+        } finally {
+            fos.close();
+        }
+    }
 
-	public static void setPreference(String key, String value) {
-		getPreferences().put(key, value);
-	}
+    public static void setPreference(String key, String value) {
+        getPreferences().put(key, value);
+    }
 
-	public synchronized void endTest(Test test) {
-		testEnded(test.toString());
-	}
+    public synchronized void endTest(Test test) {
+        testEnded(test.toString());
+    }
 
-	public synchronized void addError(final Test test, final Throwable t) {
-		testFailed(TestRunListener.STATUS_ERROR, test, t);
-	}
+    public synchronized void addError(final Test test, final Throwable e) {
+        testFailed(TestRunListener.STATUS_ERROR, test, e);
+    }
 
-	public synchronized void addFailure(final Test test, final AssertionFailedError t) {
-		testFailed(TestRunListener.STATUS_FAILURE, test, t);
-	}
+    public synchronized void addFailure(final Test test, final AssertionFailedError e) {
+        testFailed(TestRunListener.STATUS_FAILURE, test, e);
+    }
 
-	// TestRunListener implementation
+    // TestRunListener implementation
 
-	public abstract void testStarted(String testName);
+    public abstract void testStarted(String testName);
 
-	public abstract void testEnded(String testName);
+    public abstract void testEnded(String testName);
 
-	public abstract void testFailed(int status, Test test, Throwable t);
+    public abstract void testFailed(int status, Test test, Throwable e);
 
-	/**
-	 * Returns the Test corresponding to the given suite. This is
-	 * a template method, subclasses override runFailed(), clearStatus().
-	 */
-	public Test getTest(String suiteClassName) {
-		if (suiteClassName.length() <= 0) {
-			clearStatus();
-			return null;
-		}
-		Class<?> testClass= null;
-		try {
-			testClass= loadSuiteClass(suiteClassName);
-		} catch (ClassNotFoundException e) {
-			String clazz= e.getMessage();
-			if (clazz == null)
-				clazz= suiteClassName;
-			runFailed("Class not found \""+clazz+"\"");
-			return null;
-		} catch(Exception e) {
-			runFailed("Error: "+e.toString());
-			return null;
-		}
-		Method suiteMethod= null;
-		try {
-			suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]);
-	 	} catch(Exception e) {
-	 		// try to extract a test suite automatically
-			clearStatus();
-			return new TestSuite(testClass);
-		}
-		if (! Modifier.isStatic(suiteMethod.getModifiers())) {
-			runFailed("Suite() method must be static");
-			return null;
-		}
-		Test test= null;
-		try {
-			test= (Test)suiteMethod.invoke(null, (Object[])new Class[0]); // static method
-			if (test == null)
-				return test;
-		}
-		catch (InvocationTargetException e) {
-			runFailed("Failed to invoke suite():" + e.getTargetException().toString());
-			return null;
-		}
-		catch (IllegalAccessException e) {
-			runFailed("Failed to invoke suite():" + e.toString());
-			return null;
-		}
+    /**
+     * Returns the Test corresponding to the given suite. This is
+     * a template method, subclasses override runFailed(), clearStatus().
+     */
+    public Test getTest(String suiteClassName) {
+        if (suiteClassName.length() <= 0) {
+            clearStatus();
+            return null;
+        }
+        Class<?> testClass = null;
+        try {
+            testClass = loadSuiteClass(suiteClassName);
+        } catch (ClassNotFoundException e) {
+            String clazz = e.getMessage();
+            if (clazz == null) {
+                clazz = suiteClassName;
+            }
+            runFailed("Class not found \"" + clazz + "\"");
+            return null;
+        } catch (Exception e) {
+            runFailed("Error: " + e.toString());
+            return null;
+        }
+        Method suiteMethod = null;
+        try {
+            suiteMethod = testClass.getMethod(SUITE_METHODNAME);
+        } catch (Exception e) {
+            // try to extract a test suite automatically
+            clearStatus();
+            return new TestSuite(testClass);
+        }
+        if (!Modifier.isStatic(suiteMethod.getModifiers())) {
+            runFailed("Suite() method must be static");
+            return null;
+        }
+        Test test = null;
+        try {
+            test = (Test) suiteMethod.invoke(null); // static method
+            if (test == null) {
+                return test;
+            }
+        } catch (InvocationTargetException e) {
+            runFailed("Failed to invoke suite():" + e.getTargetException().toString());
+            return null;
+        } catch (IllegalAccessException e) {
+            runFailed("Failed to invoke suite():" + e.toString());
+            return null;
+        }
 
-		clearStatus();
-		return test;
-	}
+        clearStatus();
+        return test;
+    }
 
-	/**
-	 * Returns the formatted string of the elapsed time.
-	 */
-	public String elapsedTimeAsString(long runTime) {
-		return NumberFormat.getInstance().format((double)runTime/1000);
-	}
+    /**
+     * Returns the formatted string of the elapsed time.
+     */
+    public String elapsedTimeAsString(long runTime) {
+        return NumberFormat.getInstance().format((double) runTime / 1000);
+    }
 
-	/**
-	 * Processes the command line arguments and
-	 * returns the name of the suite class to run or null
-	 */
-	protected String processArguments(String[] args) {
-		String suiteName= null;
-		for (int i= 0; i < args.length; i++) {
-			if (args[i].equals("-noloading")) {
-				setLoading(false);
-			} else if (args[i].equals("-nofilterstack")) {
-				fgFilterStack= false;
-			} else if (args[i].equals("-c")) {
-				if (args.length > i+1)
-					suiteName= extractClassName(args[i+1]);
-				else
-					System.out.println("Missing Test class name");
-				i++;
-			} else {
-				suiteName= args[i];
-			}
-		}
-		return suiteName;
-	}
+    /**
+     * Processes the command line arguments and
+     * returns the name of the suite class to run or null
+     */
+    protected String processArguments(String[] args) {
+        String suiteName = null;
+        for (int i = 0; i < args.length; i++) {
+            if (args[i].equals("-noloading")) {
+                setLoading(false);
+            } else if (args[i].equals("-nofilterstack")) {
+                fgFilterStack = false;
+            } else if (args[i].equals("-c")) {
+                if (args.length > i + 1) {
+                    suiteName = extractClassName(args[i + 1]);
+                } else {
+                    System.out.println("Missing Test class name");
+                }
+                i++;
+            } else {
+                suiteName = args[i];
+            }
+        }
+        return suiteName;
+    }
 
-	/**
-	 * Sets the loading behaviour of the test runner
-	 */
-	public void setLoading(boolean enable) {
-		fLoading= enable;
-	}
-	/**
-	 * Extract the class name from a String in VA/Java style
-	 */
-	public String extractClassName(String className) {
-		if(className.startsWith("Default package for"))
-			return className.substring(className.lastIndexOf(".")+1);
-		return className;
-	}
+    /**
+     * Sets the loading behaviour of the test runner
+     */
+    public void setLoading(boolean enable) {
+        fLoading = enable;
+    }
 
-	/**
-	 * Truncates a String to the maximum length.
-	 */
-	public static String truncate(String s) {
-		if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength)
-			s= s.substring(0, fgMaxMessageLength)+"...";
-		return s;
-	}
+    /**
+     * Extract the class name from a String in VA/Java style
+     */
+    public String extractClassName(String className) {
+        if (className.startsWith("Default package for")) {
+            return className.substring(className.lastIndexOf(".") + 1);
+        }
+        return className;
+    }
 
-	/**
-	 * Override to define how to handle a failed loading of
-	 * a test suite.
-	 */
-	protected abstract void runFailed(String message);
+    /**
+     * Truncates a String to the maximum length.
+     */
+    public static String truncate(String s) {
+        if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength) {
+            s = s.substring(0, fgMaxMessageLength) + "...";
+        }
+        return s;
+    }
 
-	/**
-	 * Returns the loaded Class for a suite name.
-	 */
-	protected Class<?> loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
-		return Class.forName(suiteClassName);
-	}
+    /**
+     * Override to define how to handle a failed loading of
+     * a test suite.
+     */
+    protected abstract void runFailed(String message);
 
-	/**
-	 * Clears the status message.
-	 */
-	protected void clearStatus() { // Belongs in the GUI TestRunner class
-	}
+    /**
+     * Returns the loaded Class for a suite name.
+     */
+    protected Class<?> loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
+        return Class.forName(suiteClassName);
+    }
 
-	protected boolean useReloadingTestSuiteLoader() {
-		return getPreference("loading").equals("true") && fLoading;
-	}
+    /**
+     * Clears the status message.
+     */
+    protected void clearStatus() { // Belongs in the GUI TestRunner class
+    }
 
-	private static File getPreferencesFile() {
-	 	String home= System.getProperty("user.home");
- 		return new File(home, "junit.properties");
- 	}
+    protected boolean useReloadingTestSuiteLoader() {
+        return getPreference("loading").equals("true") && fLoading;
+    }
 
- 	private static void readPreferences() {
- 		InputStream is= null;
- 		try {
- 			is= new FileInputStream(getPreferencesFile());
- 			setPreferences(new Properties(getPreferences()));
-			getPreferences().load(is);
-		} catch (IOException e) {
-			try {
-				if (is != null)
-					is.close();
-			} catch (IOException e1) {
-			}
-		}
- 	}
+    private static File getPreferencesFile() {
+        String home = System.getProperty("user.home");
+        return new File(home, "junit.properties");
+    }
 
- 	public static String getPreference(String key) {
- 		return getPreferences().getProperty(key);
- 	}
+    private static void readPreferences() {
+        InputStream is = null;
+        try {
+            is = new FileInputStream(getPreferencesFile());
+            setPreferences(new Properties(getPreferences()));
+            getPreferences().load(is);
+        } catch (IOException ignored) {
+        } finally {
+            try {
+                if (is != null) {
+                    is.close();
+                }
+            } catch (IOException e1) {
+            }
+        }
+    }
 
- 	public static int getPreference(String key, int dflt) {
- 		String value= getPreference(key);
- 		int intValue= dflt;
- 		if (value == null)
- 			return intValue;
- 		try {
- 			intValue= Integer.parseInt(value);
- 	 	} catch (NumberFormatException ne) {
- 		}
- 		return intValue;
- 	}
+    public static String getPreference(String key) {
+        return getPreferences().getProperty(key);
+    }
 
-	/**
-	 * Returns a filtered stack trace
-	 */
-	public static String getFilteredTrace(Throwable t) {
-		StringWriter stringWriter= new StringWriter();
-		PrintWriter writer= new PrintWriter(stringWriter);
-		t.printStackTrace(writer);
-		StringBuffer buffer= stringWriter.getBuffer();
-		String trace= buffer.toString();
-		return BaseTestRunner.getFilteredTrace(trace);
-	}
+    public static int getPreference(String key, int dflt) {
+        String value = getPreference(key);
+        int intValue = dflt;
+        if (value == null) {
+            return intValue;
+        }
+        try {
+            intValue = Integer.parseInt(value);
+        } catch (NumberFormatException ne) {
+        }
+        return intValue;
+    }
 
-	/**
-	 * Filters stack frames from internal JUnit classes
-	 */
-	public static String getFilteredTrace(String stack) {
-		if (showStackRaw())
-			return stack;
+    /**
+     * Returns a filtered stack trace
+     */
+    public static String getFilteredTrace(Throwable e) {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter writer = new PrintWriter(stringWriter);
+        e.printStackTrace(writer);
+        String trace = stringWriter.toString();
+        return BaseTestRunner.getFilteredTrace(trace);
+    }
 
-		StringWriter sw= new StringWriter();
-		PrintWriter pw= new PrintWriter(sw);
-		StringReader sr= new StringReader(stack);
-		BufferedReader br= new BufferedReader(sr);
+    /**
+     * Filters stack frames from internal JUnit classes
+     */
+    public static String getFilteredTrace(String stack) {
+        if (showStackRaw()) {
+            return stack;
+        }
 
-		String line;
-		try {
-			while ((line= br.readLine()) != null) {
-				if (!filterLine(line))
-					pw.println(line);
-			}
-		} catch (Exception IOException) {
-			return stack; // return the stack unfiltered
-		}
-		return sw.toString();
-	}
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        StringReader sr = new StringReader(stack);
+        BufferedReader br = new BufferedReader(sr);
 
-	protected static boolean showStackRaw() {
-		return !getPreference("filterstack").equals("true") || fgFilterStack == false;
-	}
+        String line;
+        try {
+            while ((line = br.readLine()) != null) {
+                if (!filterLine(line)) {
+                    pw.println(line);
+                }
+            }
+        } catch (Exception IOException) {
+            return stack; // return the stack unfiltered
+        }
+        return sw.toString();
+    }
 
-	static boolean filterLine(String line) {
-		String[] patterns= new String[] {
-			"junit.framework.TestCase",
-			"junit.framework.TestResult",
-			"junit.framework.TestSuite",
-			"junit.framework.Assert.", // don't filter AssertionFailure
-			"junit.swingui.TestRunner",
-			"junit.awtui.TestRunner",
-			"junit.textui.TestRunner",
-			"java.lang.reflect.Method.invoke("
-		};
-		for (int i= 0; i < patterns.length; i++) {
-			if (line.indexOf(patterns[i]) > 0)
-				return true;
-		}
-		return false;
-	}
+    protected static boolean showStackRaw() {
+        return !getPreference("filterstack").equals("true") || fgFilterStack == false;
+    }
 
- 	static {
- 		fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength);
- 	}
+    static boolean filterLine(String line) {
+        String[] patterns = new String[]{
+                "junit.framework.TestCase",
+                "junit.framework.TestResult",
+                "junit.framework.TestSuite",
+                "junit.framework.Assert.", // don't filter AssertionFailure
+                "junit.swingui.TestRunner",
+                "junit.awtui.TestRunner",
+                "junit.textui.TestRunner",
+                "java.lang.reflect.Method.invoke("
+        };
+        for (int i = 0; i < patterns.length; i++) {
+            if (line.indexOf(patterns[i]) > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static {
+        fgMaxMessageLength = getPreference("maxmessage", fgMaxMessageLength);
+    }
 
 }
diff --git a/src/main/java/junit/runner/TestRunListener.java b/src/main/java/junit/runner/TestRunListener.java
index b11ef07..b5e22f5 100644
--- a/src/main/java/junit/runner/TestRunListener.java
+++ b/src/main/java/junit/runner/TestRunListener.java
@@ -1,19 +1,25 @@
 package junit.runner;
+
 /**
  * A listener interface for observing the
  * execution of a test run. Unlike TestListener,
  * this interface using only primitive objects,
  * making it suitable for remote test execution.
  */
- public interface TestRunListener {
-     /* test status constants*/
-     public static final int STATUS_ERROR= 1;
-     public static final int STATUS_FAILURE= 2;
+public interface TestRunListener {
+    /* test status constants*/
+    public static final int STATUS_ERROR = 1;
+    public static final int STATUS_FAILURE = 2;
 
-     public void testRunStarted(String testSuiteName, int testCount);
-     public void testRunEnded(long elapsedTime);
-     public void testRunStopped(long elapsedTime);
-     public void testStarted(String testName);
-     public void testEnded(String testName);
-     public void testFailed(int status, String testName, String trace);
+    public void testRunStarted(String testSuiteName, int testCount);
+
+    public void testRunEnded(long elapsedTime);
+
+    public void testRunStopped(long elapsedTime);
+
+    public void testStarted(String testName);
+
+    public void testEnded(String testName);
+
+    public void testFailed(int status, String testName, String trace);
 }
diff --git a/src/main/java/junit/runner/Version.java b/src/main/java/junit/runner/Version.java
index 21aabfa..eaf3db7 100644
--- a/src/main/java/junit/runner/Version.java
+++ b/src/main/java/junit/runner/Version.java
@@ -9,7 +9,7 @@
 	}
 
 	public static String id() {
-		return "4.10-SNAPSHOT";
+		return "4.12-SNAPSHOT";
 	}
 	
 	public static void main(String[] args) {
diff --git a/src/main/java/junit/runner/logo.gif b/src/main/java/junit/runner/logo.gif
deleted file mode 100644
index d0e1547..0000000
--- a/src/main/java/junit/runner/logo.gif
+++ /dev/null
Binary files differ
diff --git a/src/main/java/junit/runner/smalllogo.gif b/src/main/java/junit/runner/smalllogo.gif
deleted file mode 100644
index 7b25eaf..0000000
--- a/src/main/java/junit/runner/smalllogo.gif
+++ /dev/null
Binary files differ
diff --git a/src/main/java/junit/textui/ResultPrinter.java b/src/main/java/junit/textui/ResultPrinter.java
index f2f01f5..95f10f4 100644
--- a/src/main/java/junit/textui/ResultPrinter.java
+++ b/src/main/java/junit/textui/ResultPrinter.java
@@ -1,4 +1,3 @@
-
 package junit.textui;
 
 import java.io.PrintStream;
@@ -13,127 +12,126 @@
 import junit.runner.BaseTestRunner;
 
 public class ResultPrinter implements TestListener {
-	PrintStream fWriter;
-	int fColumn= 0;
-	
-	public ResultPrinter(PrintStream writer) {
-		fWriter= writer;
-	}
-	
-	/* API for use by textui.TestRunner
-	 */
+    PrintStream fWriter;
+    int fColumn = 0;
 
-	synchronized void print(TestResult result, long runTime) {
-		printHeader(runTime);
-	    printErrors(result);
-	    printFailures(result);
-	    printFooter(result);
-	}
+    public ResultPrinter(PrintStream writer) {
+        fWriter = writer;
+    }
 
-	void printWaitPrompt() {
-		getWriter().println();
-		getWriter().println("<RETURN> to continue");
-	}
-	
-	/* Internal methods 
-	 */
+    /* API for use by textui.TestRunner */
 
-	protected void printHeader(long runTime) {
-		getWriter().println();
-		getWriter().println("Time: "+elapsedTimeAsString(runTime));
-	}
-	
-	protected void printErrors(TestResult result) {
-		printDefects(result.errors(), result.errorCount(), "error");
-	}
-	
-	protected void printFailures(TestResult result) {
-		printDefects(result.failures(), result.failureCount(), "failure");
-	}
-	
-	protected void printDefects(Enumeration<TestFailure> booBoos, int count, String type) {
-		if (count == 0) return;
-		if (count == 1)
-			getWriter().println("There was " + count + " " + type + ":");
-		else
-			getWriter().println("There were " + count + " " + type + "s:");
-		for (int i= 1; booBoos.hasMoreElements(); i++) {
-			printDefect(booBoos.nextElement(), i);
-		}
-	}
-	
-	public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes
-		printDefectHeader(booBoo, count);
-		printDefectTrace(booBoo);
-	}
+    synchronized void print(TestResult result, long runTime) {
+        printHeader(runTime);
+        printErrors(result);
+        printFailures(result);
+        printFooter(result);
+    }
 
-	protected void printDefectHeader(TestFailure booBoo, int count) {
-		// I feel like making this a println, then adding a line giving the throwable a chance to print something
-		// before we get to the stack trace.
-		getWriter().print(count + ") " + booBoo.failedTest());
-	}
+    void printWaitPrompt() {
+        getWriter().println();
+        getWriter().println("<RETURN> to continue");
+    }
 
-	protected void printDefectTrace(TestFailure booBoo) {
-		getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace()));
-	}
+    /* Internal methods */
 
-	protected void printFooter(TestResult result) {
-		if (result.wasSuccessful()) {
-			getWriter().println();
-			getWriter().print("OK");
-			getWriter().println (" (" + result.runCount() + " test" + (result.runCount() == 1 ? "": "s") + ")");
+    protected void printHeader(long runTime) {
+        getWriter().println();
+        getWriter().println("Time: " + elapsedTimeAsString(runTime));
+    }
 
-		} else {
-			getWriter().println();
-			getWriter().println("FAILURES!!!");
-			getWriter().println("Tests run: "+result.runCount()+ 
-				         ",  Failures: "+result.failureCount()+
-				         ",  Errors: "+result.errorCount());
-		}
-	    getWriter().println();
-	}
+    protected void printErrors(TestResult result) {
+        printDefects(result.errors(), result.errorCount(), "error");
+    }
 
+    protected void printFailures(TestResult result) {
+        printDefects(result.failures(), result.failureCount(), "failure");
+    }
 
-	/**
-	 * Returns the formatted string of the elapsed time.
-	 * Duplicated from BaseTestRunner. Fix it.
-	 */
-	protected String elapsedTimeAsString(long runTime) {
-		return NumberFormat.getInstance().format((double)runTime/1000);
-	}
+    protected void printDefects(Enumeration<TestFailure> booBoos, int count, String type) {
+        if (count == 0) return;
+        if (count == 1) {
+            getWriter().println("There was " + count + " " + type + ":");
+        } else {
+            getWriter().println("There were " + count + " " + type + "s:");
+        }
+        for (int i = 1; booBoos.hasMoreElements(); i++) {
+            printDefect(booBoos.nextElement(), i);
+        }
+    }
 
-	public PrintStream getWriter() {
-		return fWriter;
-	}
-	/**
-	 * @see junit.framework.TestListener#addError(Test, Throwable)
-	 */
-	public void addError(Test test, Throwable t) {
-		getWriter().print("E");
-	}
+    public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes
+        printDefectHeader(booBoo, count);
+        printDefectTrace(booBoo);
+    }
 
-	/**
-	 * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
-	 */
-	public void addFailure(Test test, AssertionFailedError t) {
-		getWriter().print("F");
-	}
+    protected void printDefectHeader(TestFailure booBoo, int count) {
+        // I feel like making this a println, then adding a line giving the throwable a chance to print something
+        // before we get to the stack trace.
+        getWriter().print(count + ") " + booBoo.failedTest());
+    }
 
-	/**
-	 * @see junit.framework.TestListener#endTest(Test)
-	 */
-	public void endTest(Test test) {
-	}
+    protected void printDefectTrace(TestFailure booBoo) {
+        getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace()));
+    }
 
-	/**
-	 * @see junit.framework.TestListener#startTest(Test)
-	 */
-	public void startTest(Test test) {
-		getWriter().print(".");
-		if (fColumn++ >= 40) {
-			getWriter().println();
-			fColumn= 0;
-		}
-	}
+    protected void printFooter(TestResult result) {
+        if (result.wasSuccessful()) {
+            getWriter().println();
+            getWriter().print("OK");
+            getWriter().println(" (" + result.runCount() + " test" + (result.runCount() == 1 ? "" : "s") + ")");
+
+        } else {
+            getWriter().println();
+            getWriter().println("FAILURES!!!");
+            getWriter().println("Tests run: " + result.runCount() +
+                    ",  Failures: " + result.failureCount() +
+                    ",  Errors: " + result.errorCount());
+        }
+        getWriter().println();
+    }
+
+    /**
+     * Returns the formatted string of the elapsed time.
+     * Duplicated from BaseTestRunner. Fix it.
+     */
+    protected String elapsedTimeAsString(long runTime) {
+        return NumberFormat.getInstance().format((double) runTime / 1000);
+    }
+
+    public PrintStream getWriter() {
+        return fWriter;
+    }
+
+    /**
+     * @see junit.framework.TestListener#addError(Test, Throwable)
+     */
+    public void addError(Test test, Throwable e) {
+        getWriter().print("E");
+    }
+
+    /**
+     * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+     */
+    public void addFailure(Test test, AssertionFailedError t) {
+        getWriter().print("F");
+    }
+
+    /**
+     * @see junit.framework.TestListener#endTest(Test)
+     */
+    public void endTest(Test test) {
+    }
+
+    /**
+     * @see junit.framework.TestListener#startTest(Test)
+     */
+    public void startTest(Test test) {
+        getWriter().print(".");
+        if (fColumn++ >= 40) {
+            getWriter().println();
+            fColumn = 0;
+        }
+    }
 
 }
diff --git a/src/main/java/junit/textui/TestRunner.java b/src/main/java/junit/textui/TestRunner.java
index 046448e..4d78f77 100644
--- a/src/main/java/junit/textui/TestRunner.java
+++ b/src/main/java/junit/textui/TestRunner.java
@@ -15,189 +15,192 @@
  * <pre>
  * java junit.textui.TestRunner [-wait] TestCaseClass
  * </pre>
- * 
- * <p>TestRunner expects the name of a TestCase class as argument.
- * If this class defines a static <code>suite</code> method it 
- * will be invoked and the returned test is run. Otherwise all 
- * the methods starting with "test" having no arguments are run.</p>
- * 
- * <p> When the wait command line argument is given TestRunner
- * waits until the users types RETURN.</p>
- * 
- * <p>TestRunner prints a trace as the tests are executed followed by a
- * summary at the end.</p>
+ * <p>
+ * TestRunner expects the name of a TestCase class as argument.
+ * If this class defines a static <code>suite</code> method it
+ * will be invoked and the returned test is run. Otherwise all
+ * the methods starting with "test" having no arguments are run.
+ * <p>
+ * When the wait command line argument is given TestRunner
+ * waits until the users types RETURN.
+ * <p>
+ * TestRunner prints a trace as the tests are executed followed by a
+ * summary at the end.
  */
 public class TestRunner extends BaseTestRunner {
-	private ResultPrinter fPrinter;
-	
-	public static final int SUCCESS_EXIT= 0;
-	public static final int FAILURE_EXIT= 1;
-	public static final int EXCEPTION_EXIT= 2;
+    private ResultPrinter fPrinter;
 
-	/**
-	 * Constructs a TestRunner.
-	 */
-	public TestRunner() {
-		this(System.out);
-	}
+    public static final int SUCCESS_EXIT = 0;
+    public static final int FAILURE_EXIT = 1;
+    public static final int EXCEPTION_EXIT = 2;
 
-	/**
-	 * Constructs a TestRunner using the given stream for all the output
-	 */
-	public TestRunner(PrintStream writer) {
-		this(new ResultPrinter(writer));
-	}
-	
-	/**
-	 * Constructs a TestRunner using the given ResultPrinter all the output
-	 */
-	public TestRunner(ResultPrinter printer) {
-		fPrinter= printer;
-	}
-	
-	/**
-	 * Runs a suite extracted from a TestCase subclass.
-	 */
-	static public void run(Class<? extends TestCase> testClass) {
-		run(new TestSuite(testClass));
-	}
+    /**
+     * Constructs a TestRunner.
+     */
+    public TestRunner() {
+        this(System.out);
+    }
 
-	/**
-	 * Runs a single test and collects its results.
-	 * This method can be used to start a test run
-	 * from your program.
-	 * <pre>
-	 * public static void main (String[] args) {
-	 *    test.textui.TestRunner.run(suite());
-	 * }
-	 * </pre>
-	 */
-	static public TestResult run(Test test) {
-		TestRunner runner= new TestRunner();
-		return runner.doRun(test);
-	}
+    /**
+     * Constructs a TestRunner using the given stream for all the output
+     */
+    public TestRunner(PrintStream writer) {
+        this(new ResultPrinter(writer));
+    }
 
-	/**
-	 * Runs a single test and waits until the user
-	 * types RETURN.
-	 */
-	static public void runAndWait(Test suite) {
-		TestRunner aTestRunner= new TestRunner();
-		aTestRunner.doRun(suite, true);
-	}
+    /**
+     * Constructs a TestRunner using the given ResultPrinter all the output
+     */
+    public TestRunner(ResultPrinter printer) {
+        fPrinter = printer;
+    }
 
-	@Override
-	public void testFailed(int status, Test test, Throwable t) {
-	}
-	
-	@Override
-	public void testStarted(String testName) {
-	}
-	
-	@Override
-	public void testEnded(String testName) {
-	}
+    /**
+     * Runs a suite extracted from a TestCase subclass.
+     */
+    static public void run(Class<? extends TestCase> testClass) {
+        run(new TestSuite(testClass));
+    }
 
-	/**
-	 * Creates the TestResult to be used for the test run.
-	 */
-	protected TestResult createTestResult() {
-		return new TestResult();
-	}
-	
-	public TestResult doRun(Test test) {
-		return doRun(test, false);
-	}
-	
-	public TestResult doRun(Test suite, boolean wait) {
-		TestResult result= createTestResult();
-		result.addListener(fPrinter);
-		long startTime= System.currentTimeMillis();
-		suite.run(result);
-		long endTime= System.currentTimeMillis();
-		long runTime= endTime-startTime;
-		fPrinter.print(result, runTime);
+    /**
+     * Runs a single test and collects its results.
+     * This method can be used to start a test run
+     * from your program.
+     * <pre>
+     * public static void main (String[] args) {
+     *    test.textui.TestRunner.run(suite());
+     * }
+     * </pre>
+     */
+    static public TestResult run(Test test) {
+        TestRunner runner = new TestRunner();
+        return runner.doRun(test);
+    }
 
-		pause(wait);
-		return result;
-	}
+    /**
+     * Runs a single test and waits until the user
+     * types RETURN.
+     */
+    static public void runAndWait(Test suite) {
+        TestRunner aTestRunner = new TestRunner();
+        aTestRunner.doRun(suite, true);
+    }
 
-	protected void pause(boolean wait) {
-		if (!wait) return;
-		fPrinter.printWaitPrompt();
-		try {
-			System.in.read();
-		}
-		catch(Exception e) {
-		}
-	}
-	
-	public static void main(String args[]) {
-		TestRunner aTestRunner= new TestRunner();
-		try {
-			TestResult r= aTestRunner.start(args);
-			if (!r.wasSuccessful()) 
-				System.exit(FAILURE_EXIT);
-			System.exit(SUCCESS_EXIT);
-		} catch(Exception e) {
-			System.err.println(e.getMessage());
-			System.exit(EXCEPTION_EXIT);
-		}
-	}
+    @Override
+    public void testFailed(int status, Test test, Throwable e) {
+    }
 
-	/**
-	 * Starts a test run. Analyzes the command line arguments and runs the given
-	 * test suite.
-	 */
-	public TestResult start(String args[]) throws Exception {
-		String testCase= "";
-		String method= "";
-		boolean wait= false;
+    @Override
+    public void testStarted(String testName) {
+    }
 
-		for (int i= 0; i < args.length; i++) {
-			if (args[i].equals("-wait"))
-				wait= true;
-			else if (args[i].equals("-c"))
-				testCase= extractClassName(args[++i]);
-			else if (args[i].equals("-m")) {
-				String arg= args[++i];
-				int lastIndex= arg.lastIndexOf('.');
-				testCase= arg.substring(0, lastIndex);
-				method= arg.substring(lastIndex + 1);
-			} else if (args[i].equals("-v"))
-				System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
-			else
-				testCase= args[i];
-		}
+    @Override
+    public void testEnded(String testName) {
+    }
 
-		if (testCase.equals(""))
-			throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
+    /**
+     * Creates the TestResult to be used for the test run.
+     */
+    protected TestResult createTestResult() {
+        return new TestResult();
+    }
 
-		try {
-			if (!method.equals("")) 
-				return runSingleMethod(testCase, method, wait);
-			Test suite= getTest(testCase);
-			return doRun(suite, wait);
-		} catch (Exception e) {
-			throw new Exception("Could not create and run test suite: " + e);
-		}
-	}
+    public TestResult doRun(Test test) {
+        return doRun(test, false);
+    }
 
-	protected TestResult runSingleMethod(String testCase, String method, boolean wait) throws Exception {
-		Class<? extends TestCase> testClass= loadSuiteClass(testCase).asSubclass(TestCase.class);
-		Test test= TestSuite.createTest(testClass, method);
-		return doRun(test, wait);
-	}
+    public TestResult doRun(Test suite, boolean wait) {
+        TestResult result = createTestResult();
+        result.addListener(fPrinter);
+        long startTime = System.currentTimeMillis();
+        suite.run(result);
+        long endTime = System.currentTimeMillis();
+        long runTime = endTime - startTime;
+        fPrinter.print(result, runTime);
 
-	@Override
-	protected void runFailed(String message) {
-		System.err.println(message);
-		System.exit(FAILURE_EXIT);
-	}
-	
-	public void setPrinter(ResultPrinter printer) {
-		fPrinter= printer;
-	}
-		
-	
+        pause(wait);
+        return result;
+    }
+
+    protected void pause(boolean wait) {
+        if (!wait) return;
+        fPrinter.printWaitPrompt();
+        try {
+            System.in.read();
+        } catch (Exception e) {
+        }
+    }
+
+    public static void main(String args[]) {
+        TestRunner aTestRunner = new TestRunner();
+        try {
+            TestResult r = aTestRunner.start(args);
+            if (!r.wasSuccessful()) {
+                System.exit(FAILURE_EXIT);
+            }
+            System.exit(SUCCESS_EXIT);
+        } catch (Exception e) {
+            System.err.println(e.getMessage());
+            System.exit(EXCEPTION_EXIT);
+        }
+    }
+
+    /**
+     * Starts a test run. Analyzes the command line arguments and runs the given
+     * test suite.
+     */
+    public TestResult start(String args[]) throws Exception {
+        String testCase = "";
+        String method = "";
+        boolean wait = false;
+
+        for (int i = 0; i < args.length; i++) {
+            if (args[i].equals("-wait")) {
+                wait = true;
+            } else if (args[i].equals("-c")) {
+                testCase = extractClassName(args[++i]);
+            } else if (args[i].equals("-m")) {
+                String arg = args[++i];
+                int lastIndex = arg.lastIndexOf('.');
+                testCase = arg.substring(0, lastIndex);
+                method = arg.substring(lastIndex + 1);
+            } else if (args[i].equals("-v")) {
+                System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
+            } else {
+                testCase = args[i];
+            }
+        }
+
+        if (testCase.equals("")) {
+            throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
+        }
+
+        try {
+            if (!method.equals("")) {
+                return runSingleMethod(testCase, method, wait);
+            }
+            Test suite = getTest(testCase);
+            return doRun(suite, wait);
+        } catch (Exception e) {
+            throw new Exception("Could not create and run test suite: " + e);
+        }
+    }
+
+    protected TestResult runSingleMethod(String testCase, String method, boolean wait) throws Exception {
+        Class<? extends TestCase> testClass = loadSuiteClass(testCase).asSubclass(TestCase.class);
+        Test test = TestSuite.createTest(testClass, method);
+        return doRun(test, wait);
+    }
+
+    @Override
+    protected void runFailed(String message) {
+        System.err.println(message);
+        System.exit(FAILURE_EXIT);
+    }
+
+    public void setPrinter(ResultPrinter printer) {
+        fPrinter = printer;
+    }
+
+
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/After.java b/src/main/java/org/junit/After.java
index 39aa6e5..eae7e3a 100644
--- a/src/main/java/org/junit/After.java
+++ b/src/main/java/org/junit/After.java
@@ -6,15 +6,15 @@
 import java.lang.annotation.Target;
 
 /**
- * <p>If you allocate external resources in a {@link org.junit.Before} method you need to release them
+ * If you allocate external resources in a {@link org.junit.Before} method you need to release them
  * after the test runs. Annotating a <code>public void</code> method
  * with <code>&#064;After</code> causes that method to be run after the {@link org.junit.Test} method. All <code>&#064;After</code>
- * methods are guaranteed to run even if a {@link org.junit.Before} or {@link org.junit.Test} method throws an 
+ * methods are guaranteed to run even if a {@link org.junit.Before} or {@link org.junit.Test} method throws an
  * exception. The <code>&#064;After</code> methods declared in superclasses will be run after those of the current
- * class.</p>
- * 
+ * class, unless they are overridden in the current class.
+ * <p>
  * Here is a simple example:
-* <pre>
+ * <pre>
  * public class Example {
  *    File output;
  *    &#064;Before public void createOutputFile() {
@@ -28,9 +28,10 @@
  *    }
  * }
  * </pre>
- * 
+ *
  * @see org.junit.Before
  * @see org.junit.Test
+ * @since 4.0
  */
 
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/src/main/java/org/junit/AfterClass.java b/src/main/java/org/junit/AfterClass.java
index 2d6bc80..dba4109 100644
--- a/src/main/java/org/junit/AfterClass.java
+++ b/src/main/java/org/junit/AfterClass.java
@@ -6,15 +6,15 @@
 import java.lang.annotation.Target;
 
 /**
- * <p>If you allocate expensive external resources in a {@link org.junit.BeforeClass} method you need to release them
+ * If you allocate expensive external resources in a {@link org.junit.BeforeClass} method you need to release them
  * after all the tests in the class have run. Annotating a <code>public static void</code> method
  * with <code>&#064;AfterClass</code> causes that method to be run after all the tests in the class have been run. All <code>&#064;AfterClass</code>
- * methods are guaranteed to run even if a {@link org.junit.BeforeClass} method throws an 
+ * methods are guaranteed to run even if a {@link org.junit.BeforeClass} method throws an
  * exception. The <code>&#064;AfterClass</code> methods declared in superclasses will be run after those of the current
- * class.</p>
- * 
+ * class, unless they are shadowed in the current class.
+ * <p>
  * Here is a simple example:
-* <pre>
+ * <pre>
  * public class Example {
  *    private static DatabaseConnection database;
  *    &#064;BeforeClass public static void login() {
@@ -31,9 +31,10 @@
  *    }
  * }
  * </pre>
- * 
+ *
  * @see org.junit.BeforeClass
  * @see org.junit.Test
+ * @since 4.0
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
diff --git a/src/main/java/org/junit/Assume.java b/src/main/java/org/junit/Assume.java
index 7b6c21a..c96ff99 100644
--- a/src/main/java/org/junit/Assume.java
+++ b/src/main/java/org/junit/Assume.java
@@ -4,24 +4,22 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
+
 import org.hamcrest.Matcher;
-import org.junit.internal.AssumptionViolatedException;
 import org.junit.internal.matchers.Each;
 
 /**
  * A set of methods useful for stating assumptions about the conditions in which a test is meaningful.
- * A failed assumption does not mean the code is broken, but that the test provides no useful information.
- * The default JUnit runner treats tests with failing assumptions as ignored.  Custom runners may behave differently.
- * 
- * For example:
- * <pre>
- * // only provides information if database is reachable.
- * \@Test public void calculateTotalSalary() {
- *    DBConnection dbc = Database.connect();
- *    assumeNotNull(dbc);
- *    // ...
- * }
- * </pre>
+ * A failed assumption does not mean the code is broken, but that the test provides no useful information. Assume
+ * basically means "don't run this test if these conditions don't apply". The default JUnit runner skips tests with
+ * failing assumptions. Custom runners may behave differently.
+ * <p>
+ *     A good example of using assumptions is in <a href="https://github.com/junit-team/junit/wiki/Theories">Theories</a> where they are needed to exclude certain datapoints that aren't suitable or allowed for a certain test case.
+ * </p>
+ * Failed assumptions are usually not logged, because there may be many tests that don't apply to certain
+ * configurations.
+ *
+ * <p>
  * These methods can be used directly: <code>Assume.assumeTrue(...)</code>, however, they
  * read better if they are referenced through static import:<br/>
  * <pre>
@@ -29,66 +27,133 @@
  *    ...
  *    assumeTrue(...);
  * </pre>
+ * </p>
+ *
+ * @see <a href="https://github.com/junit-team/junit/wiki/Theories">Theories</a>
+ *
+ * @since 4.4
  */
 public class Assume {
-	/**
-	 * If called with an expression evaluating to {@code false}, the test will halt and be ignored.
-	 * @param b
-	 */
-	public static void assumeTrue(boolean b) {
-		assumeThat(b, is(true));
-	}
+    /**
+     * If called with an expression evaluating to {@code false}, the test will halt and be ignored.
+     */
+    public static void assumeTrue(boolean b) {
+        assumeThat(b, is(true));
+    }
+
+    /**
+     * The inverse of {@link #assumeTrue(boolean)}.
+     */
+    public static void assumeFalse(boolean b) {
+        assumeTrue(!b);
+    }
+
+    /**
+     * If called with an expression evaluating to {@code false}, the test will halt and be ignored.
+     *
+     * @param b If <code>false</code>, the method will attempt to stop the test and ignore it by
+     * throwing {@link AssumptionViolatedException}.
+     * @param message A message to pass to {@link AssumptionViolatedException}.
+     */
+    public static void assumeTrue(String message, boolean b) {
+        if (!b) throw new AssumptionViolatedException(message);
+    }
+
+    /**
+     * The inverse of {@link #assumeTrue(String, boolean)}.
+     */
+    public static void assumeFalse(String message, boolean b) {
+        assumeTrue(message, !b);
+    }
 
 	/**
 	 * If called with one or more null elements in <code>objects</code>, the test will halt and be ignored.
 	 * @param objects
 	 */
-		public static void assumeNotNull(Object... objects) {
+	public static void assumeNotNull(Object... objects) {
 		assumeThat(asList(objects), Each.each(notNullValue()));
 	}
 
-	    /**
-	     * Call to assume that <code>actual</code> satisfies the condition specified by <code>matcher</code>.
-	     * If not, the test halts and is ignored.
-	     * Example:
-	     * <pre>:
-	     *   assumeThat(1, is(1)); // passes
-	     *   foo(); // will execute
-	     *   assumeThat(0, is(1)); // assumption failure! test halts
-	     *   int x = 1 / 0; // will never execute
-	     * </pre>
-	     *   
-	     * @param <T> the static type accepted by the matcher (this can flag obvious compile-time problems such as {@code assumeThat(1, is("a"))}
-	     * @param actual the computed value being compared
-	     * @param matcher an expression, built of {@link Matcher}s, specifying allowed values
-	     * 
-	     * @see org.hamcrest.CoreMatchers
-	     * @see org.junit.matchers.JUnitMatchers
-	     */
-	public static <T> void assumeThat(T actual, Matcher<T> matcher) {
-		if (!matcher.matches(actual))
-			throw new AssumptionViolatedException(actual, matcher); 
-	}
+    /**
+     * Call to assume that <code>actual</code> satisfies the condition specified by <code>matcher</code>.
+     * If not, the test halts and is ignored.
+     * Example:
+     * <pre>:
+     *   assumeThat(1, is(1)); // passes
+     *   foo(); // will execute
+     *   assumeThat(0, is(1)); // assumption failure! test halts
+     *   int x = 1 / 0; // will never execute
+     * </pre>
+     *
+     * @param <T> the static type accepted by the matcher (this can flag obvious compile-time problems such as {@code assumeThat(1, is("a"))}
+     * @param actual the computed value being compared
+     * @param matcher an expression, built of {@link Matcher}s, specifying allowed values
+     * @see org.hamcrest.CoreMatchers
+     * @see org.junit.matchers.JUnitMatchers
+     */
+    public static <T> void assumeThat(T actual, Matcher<T> matcher) {
+        if (!matcher.matches(actual)) {
+            throw new AssumptionViolatedException(actual, matcher);
+        }
+    }
 
     /**
-	 * Use to assume that an operation completes normally.  If {@code t} is non-null, the test will halt and be ignored.
-	 * 
-	 * For example:
-	 * <pre>
-	 * \@Test public void parseDataFile() {
-	 *   DataFile file;
-	 *   try {
-	 *     file = DataFile.open("sampledata.txt");
-	 *   } catch (IOException e) {
-	 *     // stop test and ignore if data can't be opened
-	 *     assumeNoException(e);
-	 *   }
-	 *   // ...
-	 * }
-	 * </pre>
-	 * @param t if non-null, the offending exception
-	 */
-	public static void assumeNoException(Throwable t) {
-		assumeThat(t, nullValue());
-	}
+     * Call to assume that <code>actual</code> satisfies the condition specified by <code>matcher</code>.
+     * If not, the test halts and is ignored.
+     * Example:
+     * <pre>:
+     *   assumeThat("alwaysPasses", 1, is(1)); // passes
+     *   foo(); // will execute
+     *   assumeThat("alwaysFails", 0, is(1)); // assumption failure! test halts
+     *   int x = 1 / 0; // will never execute
+     * </pre>
+     *
+     * @param <T> the static type accepted by the matcher (this can flag obvious compile-time problems such as {@code assumeThat(1, is("a"))}
+     * @param actual the computed value being compared
+     * @param matcher an expression, built of {@link Matcher}s, specifying allowed values
+     * @see org.hamcrest.CoreMatchers
+     * @see org.junit.matchers.JUnitMatchers
+     */
+    public static <T> void assumeThat(String message, T actual, Matcher<T> matcher) {
+        if (!matcher.matches(actual)) {
+            throw new AssumptionViolatedException(message, actual, matcher);
+        }
+    }
+
+    /**
+     * Use to assume that an operation completes normally.  If {@code e} is non-null, the test will halt and be ignored.
+     *
+     * For example:
+     * <pre>
+     * \@Test public void parseDataFile() {
+     *   DataFile file;
+     *   try {
+     *     file = DataFile.open("sampledata.txt");
+     *   } catch (IOException e) {
+     *     // stop test and ignore if data can't be opened
+     *     assumeNoException(e);
+     *   }
+     *   // ...
+     * }
+     * </pre>
+     *
+     * @param e if non-null, the offending exception
+     */
+    public static void assumeNoException(Throwable e) {
+        assumeThat(e, nullValue());
+    }
+
+    /**
+     * Attempts to halt the test and ignore it if Throwable <code>e</code> is
+     * not <code>null</code>. Similar to {@link #assumeNoException(Throwable)},
+     * but provides an additional message that can explain the details
+     * concerning the assumption.
+     *
+     * @param e if non-null, the offending exception
+     * @param message Additional message to pass to {@link AssumptionViolatedException}.
+     * @see #assumeNoException(Throwable)
+     */
+    public static void assumeNoException(String message, Throwable e) {
+        assumeThat(message, e, nullValue());
+    }
 }
diff --git a/src/main/java/org/junit/AssumptionViolatedException.java b/src/main/java/org/junit/AssumptionViolatedException.java
new file mode 100644
index 0000000..e48ddf0
--- /dev/null
+++ b/src/main/java/org/junit/AssumptionViolatedException.java
@@ -0,0 +1,46 @@
+package org.junit;
+
+import org.hamcrest.Matcher;
+
+/**
+ * An exception class used to implement <i>assumptions</i> (state in which a given test
+ * is meaningful and should or should not be executed). A test for which an assumption
+ * fails should not generate a test case failure.
+ *
+ * @see org.junit.Assume
+ * @since 4.12
+ */
+@SuppressWarnings("deprecation")
+public class AssumptionViolatedException extends org.junit.internal.AssumptionViolatedException {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * An assumption exception with the given <i>actual</i> value and a <i>matcher</i> describing 
+     * the expectation that failed.
+     */
+    public <T> AssumptionViolatedException(T actual, Matcher<T> matcher) {
+        super(actual, matcher);
+    }
+
+    /**
+     * An assumption exception with a message with the given <i>actual</i> value and a
+     * <i>matcher</i> describing the expectation that failed.
+     */
+    public <T> AssumptionViolatedException(String message, T expected, Matcher<T> matcher) {
+        super(message, expected, matcher);
+    }
+
+    /**
+     * An assumption exception with the given message only.
+     */
+    public AssumptionViolatedException(String message) {
+        super(message);
+    }
+
+    /**
+     * An assumption exception with the given message and a cause.
+     */
+    public AssumptionViolatedException(String assumption, Throwable t) {
+        super(assumption, t);
+    }
+}
diff --git a/src/main/java/org/junit/Before.java b/src/main/java/org/junit/Before.java
index 66b34ee..def8adb 100644
--- a/src/main/java/org/junit/Before.java
+++ b/src/main/java/org/junit/Before.java
@@ -6,13 +6,12 @@
 import java.lang.annotation.Target;
 
 /**
- * <p>When writing tests, it is common to find that several tests need similar 
+ * When writing tests, it is common to find that several tests need similar
  * objects created before they can run. Annotating a <code>public void</code> method
  * with <code>&#064;Before</code> causes that method to be run before the {@link org.junit.Test} method.
- * The <code>&#064;Before</code> methods of superclasses will be run before those of the current class.
- * No other ordering is defined.
- * </p>
- * 
+ * The <code>&#064;Before</code> methods of superclasses will be run before those of the current class,
+ * unless they are overridden in the current class. No other ordering is defined.
+ * <p>
  * Here is a simple example:
  * <pre>
  * public class Example {
@@ -28,9 +27,10 @@
  *    }
  * }
  * </pre>
- * 
+ *
  * @see org.junit.BeforeClass
  * @see org.junit.After
+ * @since 4.0
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
diff --git a/src/main/java/org/junit/BeforeClass.java b/src/main/java/org/junit/BeforeClass.java
index 35b7854..8183fa0 100644
--- a/src/main/java/org/junit/BeforeClass.java
+++ b/src/main/java/org/junit/BeforeClass.java
@@ -6,13 +6,13 @@
 import java.lang.annotation.Target;
 
 /**
- * <p>Sometimes several tests need to share computationally expensive setup
- * (like logging into a database). While this can compromise the independence of 
+ * Sometimes several tests need to share computationally expensive setup
+ * (like logging into a database). While this can compromise the independence of
  * tests, sometimes it is a necessary optimization. Annotating a <code>public static void</code> no-arg method
- * with <code>@BeforeClass</code> causes it to be run once before any of 
+ * with <code>@BeforeClass</code> causes it to be run once before any of
  * the test methods in the class. The <code>@BeforeClass</code> methods of superclasses
- * will be run before those the current class.</p>
- * 
+ * will be run before those of the current class, unless they are shadowed in the current class.
+ * <p>
  * For example:
  * <pre>
  * public class Example {
@@ -27,7 +27,9 @@
  *    }
  * }
  * </pre>
+ *
  * @see org.junit.AfterClass
+ * @since 4.0
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
diff --git a/src/main/java/org/junit/ClassRule.java b/src/main/java/org/junit/ClassRule.java
index 97a111f..02c40a7 100644
--- a/src/main/java/org/junit/ClassRule.java
+++ b/src/main/java/org/junit/ClassRule.java
@@ -6,55 +6,82 @@
 import java.lang.annotation.Target;
 
 /**
- * Annotates static fields that contain rules. Such a field must be public,
- * static, and a subtype of {@link org.junit.rules.TestRule}.  
- * The {@link org.junit.runners.model.Statement} passed 
- * to the {@link org.junit.rules.TestRule} will run any {@link BeforeClass} methods, 
+ * Annotates static fields that reference rules or methods that return them. A field must be public,
+ * static, and a subtype of {@link org.junit.rules.TestRule}.  A method must be public static, and return
+ * a subtype of {@link org.junit.rules.TestRule}.
+ * <p>
+ * The {@link org.junit.runners.model.Statement} passed
+ * to the {@link org.junit.rules.TestRule} will run any {@link BeforeClass} methods,
  * then the entire body of the test class (all contained methods, if it is
- * a standard JUnit test class, or all contained classes, if it is a 
+ * a standard JUnit test class, or all contained classes, if it is a
  * {@link org.junit.runners.Suite}), and finally any {@link AfterClass} methods.
- * 
+ * <p>
  * The statement passed to the {@link org.junit.rules.TestRule} will never throw an exception,
  * and throwing an exception from the {@link org.junit.rules.TestRule} will result in undefined
- * behavior.  This means that some {@link org.junit.rules.TestRule}s, such as 
- * {@link org.junit.rules.ErrorCollector}, 
- * {@link org.junit.rules.ExpectedException}, 
+ * behavior.  This means that some {@link org.junit.rules.TestRule}s, such as
+ * {@link org.junit.rules.ErrorCollector},
+ * {@link org.junit.rules.ExpectedException},
  * and {@link org.junit.rules.Timeout},
  * have undefined behavior when used as {@link ClassRule}s.
- * 
+ * <p>
  * If there are multiple
  * annotated {@link ClassRule}s on a class, they will be applied in an order
  * that depends on your JVM's implementation of the reflection API, which is
- * undefined, in general.
- *
+ * undefined, in general. However, Rules defined by fields will always be applied
+ * before Rules defined by methods.
+ * <p>
  * For example, here is a test suite that connects to a server once before
  * all the test classes run, and disconnects after they are finished:
- * 
  * <pre>
- * 
  * &#064;RunWith(Suite.class)
  * &#064;SuiteClasses({A.class, B.class, C.class})
  * public class UsesExternalResource {
- * 	public static Server myServer= new Server();
- * 
- * 	&#064;ClassRule
- * 	public static ExternalResource resource= new ExternalResource() {
- * 		&#064;Override
- * 		protected void before() throws Throwable {
- * 			myServer.connect();
- * 		};
- * 
- * 		&#064;Override
- * 		protected void after() {
- * 			myServer.disconnect();
- * 		};
- * 	};
+ *     public static Server myServer= new Server();
+ *
+ *     &#064;ClassRule
+ *     public static ExternalResource resource= new ExternalResource() {
+ *       &#064;Override
+ *       protected void before() throws Throwable {
+ *          myServer.connect();
+ *      }
+ *
+ *      &#064;Override
+ *      protected void after() {
+ * 	        myServer.disconnect();
+ *      }
+ *   };
  * }
  * </pre>
- * 
- * For more information and more examples, see {@link org.junit.rules.TestRule}. 
+ * <p>
+ * and the same using a method
+ * <pre>
+ * &#064;RunWith(Suite.class)
+ * &#064;SuiteClasses({A.class, B.class, C.class})
+ * public class UsesExternalResource {
+ *     public static Server myServer= new Server();
+ *
+ *     &#064;ClassRule
+ *     public static ExternalResource getResource() {
+ *         return new ExternalResource() {
+ *             &#064;Override
+ *             protected void before() throws Throwable {
+ *                 myServer.connect();
+ *             }
+ *
+ *             &#064;Override
+ *             protected void after() {
+ *                 myServer.disconnect();
+ *             }
+ *         };
+ *     }
+ * }
+ * </pre>
+ * <p>
+ * For more information and more examples, see {@link org.junit.rules.TestRule}.
+ *
+ * @since 4.9
  */
 @Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD})
+@Target({ElementType.FIELD, ElementType.METHOD})
 public @interface ClassRule {
 }
diff --git a/src/main/java/org/junit/ComparisonFailure.java b/src/main/java/org/junit/ComparisonFailure.java
index d37db4f..9563e61 100644
--- a/src/main/java/org/junit/ComparisonFailure.java
+++ b/src/main/java/org/junit/ComparisonFailure.java
@@ -1,138 +1,171 @@
 package org.junit;
 
 /**
- * Thrown when an {@link org.junit.Assert#assertEquals(Object, Object) assertEquals(String, String)} fails. Create and throw
- * a <code>ComparisonFailure</code> manually if you want to show users the difference between two complex 
- * strings.
- * 
+ * Thrown when an {@link org.junit.Assert#assertEquals(Object, Object) assertEquals(String, String)} fails.
+ * Create and throw a <code>ComparisonFailure</code> manually if you want to show users the
+ * difference between two complex strings.
+ * <p/>
  * Inspired by a patch from Alex Chaffee (alex@purpletech.com)
+ *
+ * @since 4.0
  */
-public class ComparisonFailure extends AssertionError {	
-	/** 
-	 * The maximum length for fExpected and fActual. If it is exceeded, the strings should be shortened. 
-	 * @see ComparisonCompactor
-	 */
-	private static final int MAX_CONTEXT_LENGTH= 20;
-	private static final long serialVersionUID= 1L;
-	
-	private String fExpected;
-	private String fActual;
+public class ComparisonFailure extends AssertionError {
+    /**
+     * The maximum length for expected and actual strings. If it is exceeded, the strings should be shortened.
+     *
+     * @see ComparisonCompactor
+     */
+    private static final int MAX_CONTEXT_LENGTH = 20;
+    private static final long serialVersionUID = 1L;
 
-	/**
-	 * Constructs a comparison failure.
-	 * @param message the identifying message or null
-	 * @param expected the expected string value
-	 * @param actual the actual string value
-	 */
-	public ComparisonFailure (String message, String expected, String actual) {
-		super (message);
-		fExpected= expected;
-		fActual= actual;
-	}
-	
-	/**
-	 * Returns "..." in place of common prefix and "..." in
-	 * place of common suffix between expected and actual.
-	 * 
-	 * @see Throwable#getMessage()
-	 */
-	@Override
-	public String getMessage() {
-		return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
-	}
-	
-	/**
-	 * Returns the actual string value
-	 * @return the actual string value
-	 */
-	public String getActual() {
-		return fActual;
-	}
-	/**
-	 * Returns the expected string value
-	 * @return the expected string value
-	 */
-	public String getExpected() {
-		return fExpected;
-	}
-	
-	private static class ComparisonCompactor {
-		private static final String ELLIPSIS= "...";
-		private static final String DELTA_END= "]";
-		private static final String DELTA_START= "[";
-		
-		/**
-		 * The maximum length for <code>expected</code> and <code>actual</code>. When <code>contextLength</code> 
-		 * is exceeded, the Strings are shortened
-		 */
-		private int fContextLength;
-		private String fExpected;
-		private String fActual;
-		private int fPrefix;
-		private int fSuffix;
+    /*
+     * We have to use the f prefix until the next major release to ensure
+     * serialization compatibility. 
+     * See https://github.com/junit-team/junit/issues/976
+     */
+    private String fExpected;
+    private String fActual;
 
-		/**
-		 * @param contextLength the maximum length for <code>expected</code> and <code>actual</code>. When contextLength 
-		 * is exceeded, the Strings are shortened
-		 * @param expected the expected string value
-		 * @param actual the actual string value
-		 */
-		public ComparisonCompactor(int contextLength, String expected, String actual) {
-			fContextLength= contextLength;
-			fExpected= expected;
-			fActual= actual;
-		}
+    /**
+     * Constructs a comparison failure.
+     *
+     * @param message the identifying message or null
+     * @param expected the expected string value
+     * @param actual the actual string value
+     */
+    public ComparisonFailure(String message, String expected, String actual) {
+        super(message);
+        this.fExpected = expected;
+        this.fActual = actual;
+    }
 
-		private String compact(String message) {
-			if (fExpected == null || fActual == null || areStringsEqual())
-				return Assert.format(message, fExpected, fActual);
+    /**
+     * Returns "..." in place of common prefix and "..." in place of common suffix between expected and actual.
+     *
+     * @see Throwable#getMessage()
+     */
+    @Override
+    public String getMessage() {
+        return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
+    }
 
-			findCommonPrefix();
-			findCommonSuffix();
-			String expected= compactString(fExpected);
-			String actual= compactString(fActual);
-			return Assert.format(message, expected, actual);
-		}
+    /**
+     * Returns the actual string value
+     *
+     * @return the actual string value
+     */
+    public String getActual() {
+        return fActual;
+    }
 
-		private String compactString(String source) {
-			String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
-			if (fPrefix > 0)
-				result= computeCommonPrefix() + result;
-			if (fSuffix > 0)
-				result= result + computeCommonSuffix();
-			return result;
-		}
+    /**
+     * Returns the expected string value
+     *
+     * @return the expected string value
+     */
+    public String getExpected() {
+        return fExpected;
+    }
 
-		private void findCommonPrefix() {
-			fPrefix= 0;
-			int end= Math.min(fExpected.length(), fActual.length());
-			for (; fPrefix < end; fPrefix++) {
-				if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))
-					break;
-			}
-		}
+    private static class ComparisonCompactor {
+        private static final String ELLIPSIS = "...";
+        private static final String DIFF_END = "]";
+        private static final String DIFF_START = "[";
 
-		private void findCommonSuffix() {
-			int expectedSuffix= fExpected.length() - 1;
-			int actualSuffix= fActual.length() - 1;
-			for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
-				if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix))
-					break;
-			}
-			fSuffix=  fExpected.length() - expectedSuffix;
-		}
+        /**
+         * The maximum length for <code>expected</code> and <code>actual</code> strings to show. When
+         * <code>contextLength</code> is exceeded, the Strings are shortened.
+         */
+        private final int contextLength;
+        private final String expected;
+        private final String actual;
 
-		private String computeCommonPrefix() {
-			return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
-		}
+        /**
+         * @param contextLength the maximum length of context surrounding the difference between the compared strings.
+         * When context length is exceeded, the prefixes and suffixes are compacted.
+         * @param expected the expected string value
+         * @param actual the actual string value
+         */
+        public ComparisonCompactor(int contextLength, String expected, String actual) {
+            this.contextLength = contextLength;
+            this.expected = expected;
+            this.actual = actual;
+        }
 
-		private String computeCommonSuffix() {
-			int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
-			return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");
-		}
+        public String compact(String message) {
+            if (expected == null || actual == null || expected.equals(actual)) {
+                return Assert.format(message, expected, actual);
+            } else {
+                DiffExtractor extractor = new DiffExtractor();
+                String compactedPrefix = extractor.compactPrefix();
+                String compactedSuffix = extractor.compactSuffix();
+                return Assert.format(message,
+                        compactedPrefix + extractor.expectedDiff() + compactedSuffix,
+                        compactedPrefix + extractor.actualDiff() + compactedSuffix);
+            }
+        }
 
-		private boolean areStringsEqual() {
-			return fExpected.equals(fActual);
-		}
-	}
-}
\ No newline at end of file
+        private String sharedPrefix() {
+            int end = Math.min(expected.length(), actual.length());
+            for (int i = 0; i < end; i++) {
+                if (expected.charAt(i) != actual.charAt(i)) {
+                    return expected.substring(0, i);
+                }
+            }
+            return expected.substring(0, end);
+        }
+
+        private String sharedSuffix(String prefix) {
+            int suffixLength = 0;
+            int maxSuffixLength = Math.min(expected.length() - prefix.length(),
+                    actual.length() - prefix.length()) - 1;
+            for (; suffixLength <= maxSuffixLength; suffixLength++) {
+                if (expected.charAt(expected.length() - 1 - suffixLength)
+                        != actual.charAt(actual.length() - 1 - suffixLength)) {
+                    break;
+                }
+            }
+            return expected.substring(expected.length() - suffixLength);
+        }
+
+        private class DiffExtractor {
+            private final String sharedPrefix;
+            private final String sharedSuffix;
+
+            /**
+             * Can not be instantiated outside {@link org.junit.ComparisonFailure.ComparisonCompactor}.
+             */
+            private DiffExtractor() {
+                sharedPrefix = sharedPrefix();
+                sharedSuffix = sharedSuffix(sharedPrefix);
+            }
+
+            public String expectedDiff() {
+                return extractDiff(expected);
+            }
+
+            public String actualDiff() {
+                return extractDiff(actual);
+            }
+
+            public String compactPrefix() {
+                if (sharedPrefix.length() <= contextLength) {
+                    return sharedPrefix;
+                }
+                return ELLIPSIS + sharedPrefix.substring(sharedPrefix.length() - contextLength);
+            }
+
+            public String compactSuffix() {
+                if (sharedSuffix.length() <= contextLength) {
+                    return sharedSuffix;
+                }
+                return sharedSuffix.substring(0, contextLength) + ELLIPSIS;
+            }
+
+            private String extractDiff(String source) {
+                return DIFF_START + source.substring(sharedPrefix.length(), source.length() - sharedSuffix.length())
+                        + DIFF_END;
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/junit/FixMethodOrder.java b/src/main/java/org/junit/FixMethodOrder.java
new file mode 100644
index 0000000..aaa0313
--- /dev/null
+++ b/src/main/java/org/junit/FixMethodOrder.java
@@ -0,0 +1,41 @@
+package org.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.runners.MethodSorters;
+
+/**
+ * This class allows the user to choose the order of execution of the methods within a test class.
+ *
+ * <p>The default order of execution of JUnit tests within a class is deterministic but not predictable.
+ * The order of execution is not guaranteed for Java 7 (and some previous versions), and can even change
+ * from run to run, so the order of execution was changed to be deterministic (in JUnit 4.11)
+ *
+ * <p>It is recommended that test methods be written so that they are independent of the order that they are executed.
+ * However, there may be a number of dependent tests either through error or by design.
+ * This class allows the user to specify the order of execution of test methods.
+ *
+ * <p>For possibilities, see {@link MethodSorters}
+ *
+ * Here is an example:
+ *
+ * <pre>
+ * &#064;FixMethodOrder(MethodSorters.NAME_ASCENDING)
+ * public class MyTest {
+ * }
+ * </pre>
+ *
+ * @see org.junit.runners.MethodSorters
+ * @since 4.11
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface FixMethodOrder {
+    /**
+     * Optionally specify <code>value</code> to have the methods executed in a particular order
+     */
+    MethodSorters value() default MethodSorters.DEFAULT;
+}
diff --git a/src/main/java/org/junit/Ignore.java b/src/main/java/org/junit/Ignore.java
index de530a9..db23581 100644
--- a/src/main/java/org/junit/Ignore.java
+++ b/src/main/java/org/junit/Ignore.java
@@ -6,34 +6,35 @@
 import java.lang.annotation.Target;
 
 /**
- * <p>Sometimes you want to temporarily disable a test or a group of tests. Methods annotated with 
+ * Sometimes you want to temporarily disable a test or a group of tests. Methods annotated with
  * {@link org.junit.Test} that are also annotated with <code>&#064;Ignore</code> will not be executed as tests.
- * Also, you can annotate a class containing test methods with <code>&#064;Ignore</code> and none of the containing 
- * tests will be executed. Native JUnit 4 test runners should report the number of ignored tests along with the 
- * number of tests that ran and the number of tests that failed.</p>
- * 
- * For example:
+ * Also, you can annotate a class containing test methods with <code>&#064;Ignore</code> and none of the containing
+ * tests will be executed. Native JUnit 4 test runners should report the number of ignored tests along with the
+ * number of tests that ran and the number of tests that failed.
+ *
+ * <p>For example:
  * <pre>
  *    &#064;Ignore &#064;Test public void something() { ...
  * </pre>
- * &#064;Ignore takes an optional default parameter if you want to record why a test is being ignored:<br/>
+ * &#064;Ignore takes an optional default parameter if you want to record why a test is being ignored:
  * <pre>
  *    &#064;Ignore("not ready yet") &#064;Test public void something() { ...
  * </pre>
- * &#064;Ignore can also be applied to the test class:<br/>
+ * &#064;Ignore can also be applied to the test class:
  * <pre>
- *		&#064;Ignore public class IgnoreMe {
- *			&#064;Test public void test1() { ... }
- *			&#064;Test public void test2() { ... }
- *		}
+ *      &#064;Ignore public class IgnoreMe {
+ *          &#064;Test public void test1() { ... }
+ *          &#064;Test public void test2() { ... }
+ *         }
  * </pre>
  *
+ * @since 4.0
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD, ElementType.TYPE})
 public @interface Ignore {
-	/**
-	 * The optional reason why the test is ignored.
-	 */
-	String value() default ""; 
+    /**
+     * The optional reason why the test is ignored.
+     */
+    String value() default "";
 }
diff --git a/src/main/java/org/junit/Rule.java b/src/main/java/org/junit/Rule.java
index 9e67c07..711235c 100644
--- a/src/main/java/org/junit/Rule.java
+++ b/src/main/java/org/junit/Rule.java
@@ -6,42 +6,65 @@
 import java.lang.annotation.Target;
 
 /**
- * Annotates fields that contain rules. Such a field must be public, not
- * static, and a subtype of {@link org.junit.rules.TestRule}.  
- * The {@link org.junit.runners.model.Statement} passed 
- * to the {@link org.junit.rules.TestRule} will run any {@link Before} methods, 
+ * Annotates fields that reference rules or methods that return a rule. A field must be public, not
+ * static, and a subtype of {@link org.junit.rules.TestRule} (preferred) or
+ * {@link org.junit.rules.MethodRule}. A method must be public, not static,
+ * and must return a subtype of {@link org.junit.rules.TestRule} (preferred) or
+ * {@link org.junit.rules.MethodRule}.
+ * <p>
+ * The {@link org.junit.runners.model.Statement} passed
+ * to the {@link org.junit.rules.TestRule} will run any {@link Before} methods,
  * then the {@link Test} method, and finally any {@link After} methods,
  * throwing an exception if any of these fail.  If there are multiple
- * annotated {@link Rule}s on a class, they will be applied in an order
+ * annotated {@link Rule}s on a class, they will be applied in order of fields first, then methods.
+ * However, if there are multiple fields (or methods) they will be applied in an order
  * that depends on your JVM's implementation of the reflection API, which is
- * undefined, in general.
- *
+ * undefined, in general. Rules defined by fields will always be applied
+ * before Rules defined by methods. You can use a {@link org.junit.rules.RuleChain} if you want
+ * to have control over the order in which the Rules are applied.
+ * <p>
  * For example, here is a test class that creates a temporary folder before
  * each test method, and deletes it after each:
- *
  * <pre>
  * public static class HasTempFolder {
- * 	&#064;Rule
- * 	public TemporaryFolder folder= new TemporaryFolder();
- * 
- * 	&#064;Test
- * 	public void testUsingTempFolder() throws IOException {
- * 		File createdFile= folder.newFile(&quot;myfile.txt&quot;);
- * 		File createdFolder= folder.newFolder(&quot;subfolder&quot;);
- * 		// ...
- * 	}
+ *     &#064;Rule
+ *     public TemporaryFolder folder= new TemporaryFolder();
+ *
+ *     &#064;Test
+ *     public void testUsingTempFolder() throws IOException {
+ *         File createdFile= folder.newFile(&quot;myfile.txt&quot;);
+ *         File createdFolder= folder.newFolder(&quot;subfolder&quot;);
+ *         // ...
+ *     }
  * }
  * </pre>
- * 
- * For more information and more examples, see 
- * {@link org.junit.rules.TestRule}. 
+ * <p>
+ * And the same using a method.
+ * <pre>
+ * public static class HasTempFolder {
+ *     private TemporaryFolder folder= new TemporaryFolder();
  *
- * Note: for backwards compatibility, this annotation may also mark
- * fields of type {@link org.junit.rules.MethodRule}, which will be honored.  However,
- * this is a deprecated interface and feature.
+ *     &#064;Rule
+ *     public TemporaryFolder getFolder() {
+ *         return folder;
+ *     }
+ *
+ *     &#064;Test
+ *     public void testUsingTempFolder() throws IOException {
+ *         File createdFile= folder.newFile(&quot;myfile.txt&quot;);
+ *         File createdFolder= folder.newFolder(&quot;subfolder&quot;);
+ *         // ...
+ *     }
+ * }
+ * </pre>
+ * <p>
+ * For more information and more examples, see
+ * {@link org.junit.rules.TestRule}.
+ *
+ * @since 4.7
  */
 @Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD})
+@Target({ElementType.FIELD, ElementType.METHOD})
 public @interface Rule {
 
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/Test.java b/src/main/java/org/junit/Test.java
index 23dc78a..71ac428 100644
--- a/src/main/java/org/junit/Test.java
+++ b/src/main/java/org/junit/Test.java
@@ -6,25 +6,24 @@
 import java.lang.annotation.Target;
 
 /**
- * <p>The <code>Test</code> annotation tells JUnit that the <code>public void</code> method
+ * The <code>Test</code> annotation tells JUnit that the <code>public void</code> method
  * to which it is attached can be run as a test case. To run the method,
  * JUnit first constructs a fresh instance of the class then invokes the
  * annotated method. Any exceptions thrown by the test will be reported
  * by JUnit as a failure. If no exceptions are thrown, the test is assumed
- * to have succeeded.</p>
- * 
- * <p>A simple test looks like this:
+ * to have succeeded.
+ * <p>
+ * A simple test looks like this:
  * <pre>
  * public class Example {
- *    <b>&#064;Test</b> 
+ *    <b>&#064;Test</b>
  *    public void method() {
  *       org.junit.Assert.assertTrue( new ArrayList().isEmpty() );
  *    }
  * }
  * </pre>
- * </p>
- * 
- * <p>The <code>Test</code> annotation supports two optional parameters.
+ * <p>
+ * The <code>Test</code> annotation supports two optional parameters.
  * The first, <code>expected</code>, declares that a test method should throw
  * an exception. If it doesn't throw an exception or if it throws a different exception
  * than the one declared, the test fails. For example, the following test succeeds:
@@ -32,37 +31,68 @@
  *    &#064;Test(<b>expected=IndexOutOfBoundsException.class</b>) public void outOfBounds() {
  *       new ArrayList&lt;Object&gt;().get(1);
  *    }
- * </pre></p>
- * 
- * <p>The second optional parameter, <code>timeout</code>, causes a test to fail if it takes 
+ * </pre>
+ * If the exception's message or one of its properties should be verified, the
+ * {@link org.junit.rules.ExpectedException ExpectedException} rule can be used. Further
+ * information about exception testing can be found at the
+ * <a href="https://github.com/junit-team/junit/wiki/Exception-testing">JUnit Wiki</a>.
+ * <p>
+ * The second optional parameter, <code>timeout</code>, causes a test to fail if it takes
  * longer than a specified amount of clock time (measured in milliseconds). The following test fails:
  * <pre>
  *    &#064;Test(<b>timeout=100</b>) public void infinity() {
  *       while(true);
  *    }
- * </pre></p>
+ * </pre>
+ * <b>Warning</b>: while <code>timeout</code> is useful to catch and terminate
+ * infinite loops, it should <em>not</em> be considered deterministic. The
+ * following test may or may not fail depending on how the operating system
+ * schedules threads:
+ * <pre>
+ *    &#064;Test(<b>timeout=100</b>) public void sleep100() {
+ *       Thread.sleep(100);
+ *    }
+ * </pre>
+ * <b>THREAD SAFETY WARNING:</b> Test methods with a timeout parameter are run in a thread other than the
+ * thread which runs the fixture's @Before and @After methods. This may yield different behavior for
+ * code that is not thread safe when compared to the same test method without a timeout parameter.
+ * <b>Consider using the {@link org.junit.rules.Timeout} rule instead</b>, which ensures a test method is run on the
+ * same thread as the fixture's @Before and @After methods.
+ *
+ * @since 4.0
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD})
 public @interface Test {
-	
-	/**
-	 * Default empty exception
-	 */
-	static class None extends Throwable {
-		private static final long serialVersionUID= 1L;		
-		private None() {
-		}
-	}
-	
-	/**
-	 * Optionally specify <code>expected</code>, a Throwable, to cause a test method to succeed iff 
-	 * an exception of the specified class is thrown by the method.
-	 */
-	Class<? extends Throwable> expected() default None.class;
-	
-	/** 
-	 * Optionally specify <code>timeout</code> in milliseconds to cause a test method to fail if it
-	 * takes longer than that number of milliseconds.*/
-	long timeout() default 0L; 
+
+    /**
+     * Default empty exception
+     */
+    static class None extends Throwable {
+        private static final long serialVersionUID = 1L;
+
+        private None() {
+        }
+    }
+
+    /**
+     * Optionally specify <code>expected</code>, a Throwable, to cause a test method to succeed if
+     * and only if an exception of the specified class is thrown by the method. If the Throwable's
+     * message or one of its properties should be verified, the
+     * {@link org.junit.rules.ExpectedException ExpectedException} rule can be used instead.
+     */
+    Class<? extends Throwable> expected() default None.class;
+
+    /**
+     * Optionally specify <code>timeout</code> in milliseconds to cause a test method to fail if it
+     * takes longer than that number of milliseconds.
+     * <p>
+     * <b>THREAD SAFETY WARNING:</b> Test methods with a timeout parameter are run in a thread other than the
+     * thread which runs the fixture's @Before and @After methods. This may yield different behavior for
+     * code that is not thread safe when compared to the same test method without a timeout parameter.
+     * <b>Consider using the {@link org.junit.rules.Timeout} rule instead</b>, which ensures a test method is run on the
+     * same thread as the fixture's @Before and @After methods.
+     * </p>
+     */
+    long timeout() default 0L;
 }
diff --git a/src/main/java/org/junit/experimental/ParallelComputer.java b/src/main/java/org/junit/experimental/ParallelComputer.java
index fccb97c..97da0f7 100644
--- a/src/main/java/org/junit/experimental/ParallelComputer.java
+++ b/src/main/java/org/junit/experimental/ParallelComputer.java
@@ -1,11 +1,8 @@
 package org.junit.experimental;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 
 import org.junit.runner.Computer;
 import org.junit.runner.Runner;
@@ -15,64 +12,56 @@
 import org.junit.runners.model.RunnerScheduler;
 
 public class ParallelComputer extends Computer {
-	private final boolean fClasses;
+    private final boolean classes;
 
-	private final boolean fMethods;
+    private final boolean methods;
 
-	public ParallelComputer(boolean classes, boolean methods) {
-		fClasses= classes;
-		fMethods= methods;
-	}
+    public ParallelComputer(boolean classes, boolean methods) {
+        this.classes = classes;
+        this.methods = methods;
+    }
 
-	public static Computer classes() {
-		return new ParallelComputer(true, false);
-	}
+    public static Computer classes() {
+        return new ParallelComputer(true, false);
+    }
 
-	public static Computer methods() {
-		return new ParallelComputer(false, true);
-	}
+    public static Computer methods() {
+        return new ParallelComputer(false, true);
+    }
 
-	private static <T> Runner parallelize(Runner runner) {
-		if (runner instanceof ParentRunner<?>) {
-			((ParentRunner<?>) runner).setScheduler(new RunnerScheduler() {
-				private final List<Future<Object>> fResults= new ArrayList<Future<Object>>();
+    private static Runner parallelize(Runner runner) {
+        if (runner instanceof ParentRunner) {
+            ((ParentRunner<?>) runner).setScheduler(new RunnerScheduler() {
+                private final ExecutorService fService = Executors.newCachedThreadPool();
 
-				private final ExecutorService fService= Executors
-						.newCachedThreadPool();
+                public void schedule(Runnable childStatement) {
+                    fService.submit(childStatement);
+                }
 
-				public void schedule(final Runnable childStatement) {
-					fResults.add(fService.submit(new Callable<Object>() {
-						public Object call() throws Exception {
-							childStatement.run();
-							return null;
-						}
-					}));
-				}
+                public void finished() {
+                    try {
+                        fService.shutdown();
+                        fService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace(System.err);
+                    }
+                }
+            });
+        }
+        return runner;
+    }
 
-				public void finished() {
-					for (Future<Object> each : fResults)
-						try {
-							each.get();
-						} catch (Exception e) {
-							e.printStackTrace();
-						}
-				}
-			});
-		}
-		return runner;
-	}
+    @Override
+    public Runner getSuite(RunnerBuilder builder, java.lang.Class<?>[] classes)
+            throws InitializationError {
+        Runner suite = super.getSuite(builder, classes);
+        return this.classes ? parallelize(suite) : suite;
+    }
 
-	@Override
-	public Runner getSuite(RunnerBuilder builder, java.lang.Class<?>[] classes)
-			throws InitializationError {
-		Runner suite= super.getSuite(builder, classes);
-		return fClasses ? parallelize(suite) : suite;
-	}
-
-	@Override
-	protected Runner getRunner(RunnerBuilder builder, Class<?> testClass)
-			throws Throwable {
-		Runner runner= super.getRunner(builder, testClass);
-		return fMethods ? parallelize(runner) : runner;
-	}
+    @Override
+    protected Runner getRunner(RunnerBuilder builder, Class<?> testClass)
+            throws Throwable {
+        Runner runner = super.getRunner(builder, testClass);
+        return methods ? parallelize(runner) : runner;
+    }
 }
diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java
index d57b4d3..290c180 100644
--- a/src/main/java/org/junit/experimental/categories/Categories.java
+++ b/src/main/java/org/junit/experimental/categories/Categories.java
@@ -1,13 +1,10 @@
-/**
- * 
- */
 package org.junit.experimental.categories;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.junit.runner.Description;
 import org.junit.runner.manipulation.Filter;
@@ -20,173 +17,369 @@
  * From a given set of test classes, runs only the classes and methods that are
  * annotated with either the category given with the @IncludeCategory
  * annotation, or a subtype of that category.
- * 
+ * <p>
  * Note that, for now, annotating suites with {@code @Category} has no effect.
  * Categories must be annotated on the direct method or class.
- * 
+ * <p>
  * Example:
- * 
  * <pre>
  * public interface FastTests {
  * }
- * 	
+ *
  * public interface SlowTests {
  * }
- * 
+ *
+ * public interface SmokeTests
+ * }
+ *
  * public static class A {
- * 	&#064;Test
- * 	public void a() {
- * 		fail();
- * 	}
- * 
- * 	&#064;Category(SlowTests.class)
- * 	&#064;Test
- * 	public void b() {
- * 	}
+ *     &#064;Test
+ *     public void a() {
+ *         fail();
+ *     }
+ *
+ *     &#064;Category(SlowTests.class)
+ *     &#064;Test
+ *     public void b() {
+ *     }
+ *
+ *     &#064;Category({FastTests.class, SmokeTests.class})
+ *     &#064;Test
+ *     public void c() {
+ *     }
  * }
- * 
- * &#064;Category( { SlowTests.class, FastTests.class })
+ *
+ * &#064;Category({SlowTests.class, FastTests.class})
  * public static class B {
- * 	&#064;Test
- * 	public void c() {
- * 
- * 	}
+ *     &#064;Test
+ *     public void d() {
+ *     }
  * }
- * 
+ *
  * &#064;RunWith(Categories.class)
  * &#064;IncludeCategory(SlowTests.class)
- * &#064;SuiteClasses( { A.class, B.class })
+ * &#064;SuiteClasses({A.class, B.class})
  * // Note that Categories is a kind of Suite
  * public static class SlowTestSuite {
+ *     // Will run A.b and B.d, but not A.a and A.c
  * }
  * </pre>
+ * <p>
+ * Example to run multiple categories:
+ * <pre>
+ * &#064;RunWith(Categories.class)
+ * &#064;IncludeCategory({FastTests.class, SmokeTests.class})
+ * &#064;SuiteClasses({A.class, B.class})
+ * public static class FastOrSmokeTestSuite {
+ *     // Will run A.c and B.d, but not A.b because it is not any of FastTests or SmokeTests
+ * }
+ * </pre>
+ *
+ * @version 4.12
+ * @see <a href="https://github.com/junit-team/junit/wiki/Categories">Categories at JUnit wiki</a>
  */
 public class Categories extends Suite {
-	// the way filters are implemented makes this unnecessarily complicated,
-	// buggy, and difficult to specify.  A new way of handling filters could
-	// someday enable a better new implementation.
-        // https://github.com/KentBeck/junit/issues/issue/172
-	
-	@Retention(RetentionPolicy.RUNTIME)
-	public @interface IncludeCategory {
-		public Class<?> value();
-	}
 
-	@Retention(RetentionPolicy.RUNTIME)
-	public @interface ExcludeCategory {
-		public Class<?> value();
-	}
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface IncludeCategory {
+        /**
+         * Determines the tests to run that are annotated with categories specified in
+         * the value of this annotation or their subtypes unless excluded with {@link ExcludeCategory}.
+         */
+        public Class<?>[] value() default {};
 
-	public static class CategoryFilter extends Filter {
-		public static CategoryFilter include(Class<?> categoryType) {
-			return new CategoryFilter(categoryType, null);
-		}
+        /**
+         * If <tt>true</tt>, runs tests annotated with <em>any</em> of the categories in
+         * {@link IncludeCategory#value()}. Otherwise, runs tests only if annotated with <em>all</em> of the categories.
+         */
+        public boolean matchAny() default true;
+    }
 
-		private final Class<?> fIncluded;
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface ExcludeCategory {
+        /**
+         * Determines the tests which do not run if they are annotated with categories specified in the
+         * value of this annotation or their subtypes regardless of being included in {@link IncludeCategory#value()}.
+         */
+        public Class<?>[] value() default {};
 
-		private final Class<?> fExcluded;
+        /**
+         * If <tt>true</tt>, the tests annotated with <em>any</em> of the categories in {@link ExcludeCategory#value()}
+         * do not run. Otherwise, the tests do not run if and only if annotated with <em>all</em> categories.
+         */
+        public boolean matchAny() default true;
+    }
 
-		public CategoryFilter(Class<?> includedCategory,
-				Class<?> excludedCategory) {
-			fIncluded= includedCategory;
-			fExcluded= excludedCategory;
-		}
+    public static class CategoryFilter extends Filter {
+        private final Set<Class<?>> included;
+        private final Set<Class<?>> excluded;
+        private final boolean includedAny;
+        private final boolean excludedAny;
 
-		@Override
-		public String describe() {
-			return "category " + fIncluded;
-		}
+        public static CategoryFilter include(boolean matchAny, Class<?>... categories) {
+            if (hasNull(categories)) {
+                throw new NullPointerException("has null category");
+            }
+            return categoryFilter(matchAny, createSet(categories), true, null);
+        }
 
-		@Override
-		public boolean shouldRun(Description description) {
-			if (hasCorrectCategoryAnnotation(description))
-				return true;
-			for (Description each : description.getChildren())
-				if (shouldRun(each))
-					return true;
-			return false;
-		}
+        public static CategoryFilter include(Class<?> category) {
+            return include(true, category);
+        }
 
-		private boolean hasCorrectCategoryAnnotation(Description description) {
-			List<Class<?>> categories= categories(description);
-			if (categories.isEmpty())
-				return fIncluded == null;
-			for (Class<?> each : categories)
-				if (fExcluded != null && fExcluded.isAssignableFrom(each))
-					return false;
-			for (Class<?> each : categories)
-				if (fIncluded == null || fIncluded.isAssignableFrom(each))
-					return true;
-			return false;
-		}
+        public static CategoryFilter include(Class<?>... categories) {
+            return include(true, categories);
+        }
 
-		private List<Class<?>> categories(Description description) {
-			ArrayList<Class<?>> categories= new ArrayList<Class<?>>();
-			categories.addAll(Arrays.asList(directCategories(description)));
-			categories.addAll(Arrays.asList(directCategories(parentDescription(description))));
-			return categories;
-		}
+        public static CategoryFilter exclude(boolean matchAny, Class<?>... categories) {
+            if (hasNull(categories)) {
+                throw new NullPointerException("has null category");
+            }
+            return categoryFilter(true, null, matchAny, createSet(categories));
+        }
 
-		private Description parentDescription(Description description) {
-			Class<?> testClass= description.getTestClass();
-			if (testClass == null)
-				return null;
-			return Description.createSuiteDescription(testClass);
-		}
+        public static CategoryFilter exclude(Class<?> category) {
+            return exclude(true, category);
+        }
 
-		private Class<?>[] directCategories(Description description) {
-			if (description == null)
-				return new Class<?>[0];
-			Category annotation= description.getAnnotation(Category.class);
-			if (annotation == null)
-				return new Class<?>[0];
-			return annotation.value();
-		}
-	}
+        public static CategoryFilter exclude(Class<?>... categories) {
+            return exclude(true, categories);
+        }
 
-	public Categories(Class<?> klass, RunnerBuilder builder)
-			throws InitializationError {
-		super(klass, builder);
-		try {
-			filter(new CategoryFilter(getIncludedCategory(klass),
-					getExcludedCategory(klass)));
-		} catch (NoTestsRemainException e) {
-			throw new InitializationError(e);
-		}
-		assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription());
-	}
+        public static CategoryFilter categoryFilter(boolean matchAnyInclusions, Set<Class<?>> inclusions,
+                                                    boolean matchAnyExclusions, Set<Class<?>> exclusions) {
+            return new CategoryFilter(matchAnyInclusions, inclusions, matchAnyExclusions, exclusions);
+        }
 
-	private Class<?> getIncludedCategory(Class<?> klass) {
-		IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class);
-		return annotation == null ? null : annotation.value();
-	}
+        protected CategoryFilter(boolean matchAnyIncludes, Set<Class<?>> includes,
+                               boolean matchAnyExcludes, Set<Class<?>> excludes) {
+            includedAny = matchAnyIncludes;
+            excludedAny = matchAnyExcludes;
+            included = copyAndRefine(includes);
+            excluded = copyAndRefine(excludes);
+        }
 
-	private Class<?> getExcludedCategory(Class<?> klass) {
-		ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class);
-		return annotation == null ? null : annotation.value();
-	}
+        /**
+         * @see #toString()
+         */
+        @Override
+        public String describe() {
+            return toString();
+        }
 
-	private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError {
-		if (!canHaveCategorizedChildren(description))
-			assertNoDescendantsHaveCategoryAnnotations(description);
-		for (Description each : description.getChildren())
-			assertNoCategorizedDescendentsOfUncategorizeableParents(each);
-	}
-	
-	private void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError {			
-		for (Description each : description.getChildren()) {
-			if (each.getAnnotation(Category.class) != null)
-				throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods.");
-			assertNoDescendantsHaveCategoryAnnotations(each);
-		}
-	}
+        /**
+         * Returns string in the form <tt>&quot;[included categories] - [excluded categories]&quot;</tt>, where both
+         * sets have comma separated names of categories.
+         *
+         * @return string representation for the relative complement of excluded categories set
+         * in the set of included categories. Examples:
+         * <ul>
+         *  <li> <tt>&quot;categories [all]&quot;</tt> for all included categories and no excluded ones;
+         *  <li> <tt>&quot;categories [all] - [A, B]&quot;</tt> for all included categories and given excluded ones;
+         *  <li> <tt>&quot;categories [A, B] - [C, D]&quot;</tt> for given included categories and given excluded ones.
+         * </ul>
+         * @see Class#toString() name of category
+         */
+        @Override public String toString() {
+            StringBuilder description= new StringBuilder("categories ")
+                .append(included.isEmpty() ? "[all]" : included);
+            if (!excluded.isEmpty()) {
+                description.append(" - ").append(excluded);
+            }
+            return description.toString();
+        }
 
-	// If children have names like [0], our current magical category code can't determine their
-	// parentage.
-	private static boolean canHaveCategorizedChildren(Description description) {
-		for (Description each : description.getChildren())
-			if (each.getTestClass() == null)
-				return false;
-		return true;
-	}
-}
\ No newline at end of file
+        @Override
+        public boolean shouldRun(Description description) {
+            if (hasCorrectCategoryAnnotation(description)) {
+                return true;
+            }
+
+            for (Description each : description.getChildren()) {
+                if (shouldRun(each)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private boolean hasCorrectCategoryAnnotation(Description description) {
+            final Set<Class<?>> childCategories= categories(description);
+
+            // If a child has no categories, immediately return.
+            if (childCategories.isEmpty()) {
+                return included.isEmpty();
+            }
+
+            if (!excluded.isEmpty()) {
+                if (excludedAny) {
+                    if (matchesAnyParentCategories(childCategories, excluded)) {
+                        return false;
+                    }
+                } else {
+                    if (matchesAllParentCategories(childCategories, excluded)) {
+                        return false;
+                    }
+                }
+            }
+
+            if (included.isEmpty()) {
+                // Couldn't be excluded, and with no suite's included categories treated as should run.
+                return true;
+            } else {
+                if (includedAny) {
+                    return matchesAnyParentCategories(childCategories, included);
+                } else {
+                    return matchesAllParentCategories(childCategories, included);
+                }
+            }
+        }
+
+        /**
+         * @return <tt>true</tt> if at least one (any) parent category match a child, otherwise <tt>false</tt>.
+         * If empty <tt>parentCategories</tt>, returns <tt>false</tt>.
+         */
+        private boolean matchesAnyParentCategories(Set<Class<?>> childCategories, Set<Class<?>> parentCategories) {
+            for (Class<?> parentCategory : parentCategories) {
+                if (hasAssignableTo(childCategories, parentCategory)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * @return <tt>false</tt> if at least one parent category does not match children, otherwise <tt>true</tt>.
+         * If empty <tt>parentCategories</tt>, returns <tt>true</tt>.
+         */
+        private boolean matchesAllParentCategories(Set<Class<?>> childCategories, Set<Class<?>> parentCategories) {
+            for (Class<?> parentCategory : parentCategories) {
+                if (!hasAssignableTo(childCategories, parentCategory)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private static Set<Class<?>> categories(Description description) {
+            Set<Class<?>> categories= new HashSet<Class<?>>();
+            Collections.addAll(categories, directCategories(description));
+            Collections.addAll(categories, directCategories(parentDescription(description)));
+            return categories;
+        }
+
+        private static Description parentDescription(Description description) {
+            Class<?> testClass= description.getTestClass();
+            return testClass == null ? null : Description.createSuiteDescription(testClass);
+        }
+
+        private static Class<?>[] directCategories(Description description) {
+            if (description == null) {
+                return new Class<?>[0];
+            }
+
+            Category annotation= description.getAnnotation(Category.class);
+            return annotation == null ? new Class<?>[0] : annotation.value();
+        }
+
+        private static Set<Class<?>> copyAndRefine(Set<Class<?>> classes) {
+            HashSet<Class<?>> c= new HashSet<Class<?>>();
+            if (classes != null) {
+                c.addAll(classes);
+            }
+            c.remove(null);
+            return c;
+        }
+
+        private static boolean hasNull(Class<?>... classes) {
+            if (classes == null) return false;
+            for (Class<?> clazz : classes) {
+                if (clazz == null) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    public Categories(Class<?> klass, RunnerBuilder builder) throws InitializationError {
+        super(klass, builder);
+        try {
+            Set<Class<?>> included= getIncludedCategory(klass);
+            Set<Class<?>> excluded= getExcludedCategory(klass);
+            boolean isAnyIncluded= isAnyIncluded(klass);
+            boolean isAnyExcluded= isAnyExcluded(klass);
+
+            filter(CategoryFilter.categoryFilter(isAnyIncluded, included, isAnyExcluded, excluded));
+        } catch (NoTestsRemainException e) {
+            throw new InitializationError(e);
+        }
+        assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription());
+    }
+
+    private static Set<Class<?>> getIncludedCategory(Class<?> klass) {
+        IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class);
+        return createSet(annotation == null ? null : annotation.value());
+    }
+
+    private static boolean isAnyIncluded(Class<?> klass) {
+        IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class);
+        return annotation == null || annotation.matchAny();
+    }
+
+    private static Set<Class<?>> getExcludedCategory(Class<?> klass) {
+        ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class);
+        return createSet(annotation == null ? null : annotation.value());
+    }
+
+    private static boolean isAnyExcluded(Class<?> klass) {
+        ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class);
+        return annotation == null || annotation.matchAny();
+    }
+
+    private static void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError {
+        if (!canHaveCategorizedChildren(description)) {
+            assertNoDescendantsHaveCategoryAnnotations(description);
+        }
+        for (Description each : description.getChildren()) {
+            assertNoCategorizedDescendentsOfUncategorizeableParents(each);
+        }
+    }
+
+    private static void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError {
+        for (Description each : description.getChildren()) {
+            if (each.getAnnotation(Category.class) != null) {
+                throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods.");
+            }
+            assertNoDescendantsHaveCategoryAnnotations(each);
+        }
+    }
+
+    // If children have names like [0], our current magical category code can't determine their parentage.
+    private static boolean canHaveCategorizedChildren(Description description) {
+        for (Description each : description.getChildren()) {
+            if (each.getTestClass() == null) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean hasAssignableTo(Set<Class<?>> assigns, Class<?> to) {
+        for (final Class<?> from : assigns) {
+            if (to.isAssignableFrom(from)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static Set<Class<?>> createSet(Class<?>... t) {
+        final Set<Class<?>> set= new HashSet<Class<?>>();
+        if (t != null) {
+            Collections.addAll(set, t);
+        }
+        return set;
+    }
+}
diff --git a/src/main/java/org/junit/experimental/categories/Category.java b/src/main/java/org/junit/experimental/categories/Category.java
index 3a4c0b9..8eae836 100644
--- a/src/main/java/org/junit/experimental/categories/Category.java
+++ b/src/main/java/org/junit/experimental/categories/Category.java
@@ -1,43 +1,48 @@
 package org.junit.experimental.categories;
 
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
+import org.junit.validator.ValidateWith;
+
 /**
  * Marks a test class or test method as belonging to one or more categories of tests.
  * The value is an array of arbitrary classes.
- * 
+ *
  * This annotation is only interpreted by the Categories runner (at present).
- * 
+ *
  * For example:
-<pre>
-	public interface FastTests {}
-	public interface SlowTests {}
-
-	public static class A {
-		&#064;Test
-		public void a() {
-			fail();
-		}
-
-		&#064;Category(SlowTests.class)
-		&#064;Test
-		public void b() {
-		}
-	}
-
-	&#064;Category({SlowTests.class, FastTests.class})
-	public static class B {
-		&#064;Test
-		public void c() {
-
-		}
-	}
-</pre>
- * 
+ * <pre>
+ * public interface FastTests {}
+ * public interface SlowTests {}
+ *
+ * public static class A {
+ * &#064;Test
+ * public void a() {
+ * fail();
+ * }
+ *
+ * &#064;Category(SlowTests.class)
+ * &#064;Test
+ * public void b() {
+ * }
+ * }
+ *
+ * &#064;Category({SlowTests.class, FastTests.class})
+ * public static class B {
+ * &#064;Test
+ * public void c() {
+ *
+ * }
+ * }
+ * </pre>
+ *
  * For more usage, see code example on {@link Categories}.
  */
 @Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@ValidateWith(CategoryValidator.class)
 public @interface Category {
-	Class<?>[] value();
+    Class<?>[] value();
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/categories/CategoryFilterFactory.java b/src/main/java/org/junit/experimental/categories/CategoryFilterFactory.java
new file mode 100644
index 0000000..cee1ae7
--- /dev/null
+++ b/src/main/java/org/junit/experimental/categories/CategoryFilterFactory.java
@@ -0,0 +1,47 @@
+package org.junit.experimental.categories;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.junit.internal.Classes;

+import org.junit.runner.FilterFactory;

+import org.junit.runner.FilterFactoryParams;

+import org.junit.runner.manipulation.Filter;

+

+/**

+ * Implementation of FilterFactory for Category filtering.

+ */

+abstract class CategoryFilterFactory implements FilterFactory {

+    /**

+     * Creates a {@link org.junit.experimental.categories.Categories.CategoryFilter} given a

+     * {@link FilterFactoryParams} argument.

+     *

+     * @param params Parameters needed to create the {@link Filter}

+     */

+    public Filter createFilter(FilterFactoryParams params) throws FilterNotCreatedException {

+        try {

+            return createFilter(parseCategories(params.getArgs()));

+        } catch (ClassNotFoundException e) {

+            throw new FilterNotCreatedException(e);

+        }

+    }

+

+    /**

+     * Creates a {@link org.junit.experimental.categories.Categories.CategoryFilter} given an array of classes.

+     *

+     * @param categories Category classes.

+     */

+    protected abstract Filter createFilter(List<Class<?>> categories);

+

+    private List<Class<?>> parseCategories(String categories) throws ClassNotFoundException {

+        List<Class<?>> categoryClasses = new ArrayList<Class<?>>();

+

+        for (String category : categories.split(",")) {

+            Class<?> categoryClass = Classes.getClass(category);

+

+            categoryClasses.add(categoryClass);

+        }

+

+        return categoryClasses;

+    }

+}

diff --git a/src/main/java/org/junit/experimental/categories/CategoryValidator.java b/src/main/java/org/junit/experimental/categories/CategoryValidator.java
new file mode 100644
index 0000000..491d8ac
--- /dev/null
+++ b/src/main/java/org/junit/experimental/categories/CategoryValidator.java
@@ -0,0 +1,62 @@
+package org.junit.experimental.categories;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.unmodifiableList;
+import static java.util.Collections.unmodifiableSet;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.validator.AnnotationValidator;
+
+/**
+ * Validates that there are no errors in the use of the {@code Category}
+ * annotation. If there is, a {@code Throwable} object will be added to the list
+ * of errors.
+ *
+ * @since 4.12
+ */
+public final class CategoryValidator extends AnnotationValidator {
+
+    @SuppressWarnings("unchecked")
+    private static final Set<Class<? extends Annotation>> INCOMPATIBLE_ANNOTATIONS = unmodifiableSet(new HashSet<Class<? extends Annotation>>(
+            asList(BeforeClass.class, AfterClass.class, Before.class, After.class)));
+
+    /**
+     * Adds to {@code errors} a throwable for each problem detected. Looks for
+     * {@code BeforeClass}, {@code AfterClass}, {@code Before} and {@code After}
+     * annotations.
+     *
+     * @param method the method that is being validated
+     * @return A list of exceptions detected
+     *
+     * @since 4.12
+     */
+    @Override
+    public List<Exception> validateAnnotatedMethod(FrameworkMethod method) {
+        List<Exception> errors = new ArrayList<Exception>();
+        Annotation[] annotations = method.getAnnotations();
+        for (Annotation annotation : annotations) {
+            for (Class<?> clazz : INCOMPATIBLE_ANNOTATIONS) {
+                if (annotation.annotationType().isAssignableFrom(clazz)) {
+                    addErrorMessage(errors, clazz);
+                }
+            }
+        }
+        return unmodifiableList(errors);
+    }
+
+    private void addErrorMessage(List<Exception> errors, Class<?> clazz) {
+        String message = String.format("@%s can not be combined with @Category",
+                clazz.getSimpleName());
+        errors.add(new Exception(message));
+    }
+}
diff --git a/src/main/java/org/junit/experimental/categories/ExcludeCategories.java b/src/main/java/org/junit/experimental/categories/ExcludeCategories.java
new file mode 100644
index 0000000..8ccb6b5
--- /dev/null
+++ b/src/main/java/org/junit/experimental/categories/ExcludeCategories.java
@@ -0,0 +1,52 @@
+package org.junit.experimental.categories;

+

+import java.util.HashSet;

+import java.util.List;

+import java.util.Set;

+

+import org.junit.experimental.categories.Categories.CategoryFilter;

+import org.junit.runner.manipulation.Filter;

+

+/**

+ * {@link org.junit.runner.FilterFactory} to exclude categories.

+ *

+ * The {@link Filter} that is created will filter out tests that are categorized with any of the

+ * given categories.

+ *

+ * Usage from command line:

+ * <code>

+ *     --filter=org.junit.experimental.categories.ExcludeCategories=pkg.of.Cat1,pkg.of.Cat2

+ * </code>

+ *

+ * Usage from API:

+ * <code>

+ *     new ExcludeCategories().createFilter(Cat1.class, Cat2.class);

+ * </code>

+ */

+public final class ExcludeCategories extends CategoryFilterFactory {

+    /**

+     * Creates a {@link Filter} which is only passed by tests that are

+     * not categorized with any of the specified categories.

+     *

+     * @param categories Category classes.

+     */

+    @Override

+    protected Filter createFilter(List<Class<?>> categories) {

+        return new ExcludesAny(categories);

+    }

+

+    private static class ExcludesAny extends CategoryFilter {

+        public ExcludesAny(List<Class<?>> categories) {

+            this(new HashSet<Class<?>>(categories));

+        }

+

+        public ExcludesAny(Set<Class<?>> categories) {

+            super(true, null, true, categories);

+        }

+

+        @Override

+        public String describe() {

+            return "excludes " + super.describe();

+        }

+    }

+}

diff --git a/src/main/java/org/junit/experimental/categories/IncludeCategories.java b/src/main/java/org/junit/experimental/categories/IncludeCategories.java
new file mode 100644
index 0000000..38eb693
--- /dev/null
+++ b/src/main/java/org/junit/experimental/categories/IncludeCategories.java
@@ -0,0 +1,52 @@
+package org.junit.experimental.categories;

+

+import java.util.HashSet;

+import java.util.List;

+import java.util.Set;

+

+import org.junit.experimental.categories.Categories.CategoryFilter;

+import org.junit.runner.manipulation.Filter;

+

+/**

+ * {@link org.junit.runner.FilterFactory} to include categories.

+ *

+ * The {@link Filter} that is created will filter out tests that are categorized with any of the

+ * given categories.

+ *

+ * Usage from command line:

+ * <code>

+ *     --filter=org.junit.experimental.categories.IncludeCategories=pkg.of.Cat1,pkg.of.Cat2

+ * </code>

+ *

+ * Usage from API:

+ * <code>

+ *     new IncludeCategories().createFilter(Cat1.class, Cat2.class);

+ * </code>

+ */

+public final class IncludeCategories extends CategoryFilterFactory {

+    /**

+     * Creates a {@link Filter} which is only passed by tests that are

+     * categorized with any of the specified categories.

+     *

+     * @param categories Category classes.

+     */

+    @Override

+    protected Filter createFilter(List<Class<?>> categories) {

+        return new IncludesAny(categories);

+    }

+

+    private static class IncludesAny extends CategoryFilter {

+        public IncludesAny(List<Class<?>> categories) {

+            this(new HashSet<Class<?>>(categories));

+        }

+

+        public IncludesAny(Set<Class<?>> categories) {

+            super(true, categories, true, null);

+        }

+

+        @Override

+        public String describe() {

+            return "includes " + super.describe();

+        }

+    }

+}

diff --git a/src/main/java/org/junit/experimental/max/CouldNotReadCoreException.java b/src/main/java/org/junit/experimental/max/CouldNotReadCoreException.java
index 03c3c8c..116d755 100644
--- a/src/main/java/org/junit/experimental/max/CouldNotReadCoreException.java
+++ b/src/main/java/org/junit/experimental/max/CouldNotReadCoreException.java
@@ -4,12 +4,12 @@
  * Thrown when Max cannot read the MaxCore serialization
  */
 public class CouldNotReadCoreException extends Exception {
-	private static final long serialVersionUID= 1L;
+    private static final long serialVersionUID = 1L;
 
-	/**
-	 * Constructs
-	 */
-	public CouldNotReadCoreException(Throwable e) {
-		super(e);
-	}
+    /**
+     * Constructs
+     */
+    public CouldNotReadCoreException(Throwable e) {
+        super(e);
+    }
 }
diff --git a/src/main/java/org/junit/experimental/max/MaxCore.java b/src/main/java/org/junit/experimental/max/MaxCore.java
index a2a34a9..625cade 100644
--- a/src/main/java/org/junit/experimental/max/MaxCore.java
+++ b/src/main/java/org/junit/experimental/max/MaxCore.java
@@ -6,7 +6,6 @@
 import java.util.List;
 
 import junit.framework.TestSuite;
-
 import org.junit.internal.requests.SortingRequest;
 import org.junit.internal.runners.ErrorReportingRunner;
 import org.junit.internal.runners.JUnit38ClassRunner;
@@ -21,150 +20,162 @@
 /**
  * A replacement for JUnitCore, which keeps track of runtime and failure history, and reorders tests
  * to maximize the chances that a failing test occurs early in the test run.
- * 
+ *
  * The rules for sorting are:
  * <ol>
  * <li> Never-run tests first, in arbitrary order
  * <li> Group remaining tests by the date at which they most recently failed.
  * <li> Sort groups such that the most recent failure date is first, and never-failing tests are at the end.
- * <li> Within a group, run the fastest tests first. 
+ * <li> Within a group, run the fastest tests first.
  * </ol>
  */
 public class MaxCore {
-	private static final String MALFORMED_JUNIT_3_TEST_CLASS_PREFIX= "malformed JUnit 3 test class: ";
-	
-	/**
-	 * Create a new MaxCore from a serialized file stored at storedResults
-	 * @deprecated use storedLocally()
-	 */
-	@Deprecated
-	public static MaxCore forFolder(String folderName) {
-		return storedLocally(new File(folderName));
-	}
-	
-	/**
-	 * Create a new MaxCore from a serialized file stored at storedResults
-	 */
-	public static MaxCore storedLocally(File storedResults) {
-		return new MaxCore(storedResults);
-	}
+    private static final String MALFORMED_JUNIT_3_TEST_CLASS_PREFIX = "malformed JUnit 3 test class: ";
 
-	private final MaxHistory fHistory;
+    /**
+     * Create a new MaxCore from a serialized file stored at storedResults
+     *
+     * @deprecated use storedLocally()
+     */
+    @Deprecated
+    public static MaxCore forFolder(String folderName) {
+        return storedLocally(new File(folderName));
+    }
 
-	private MaxCore(File storedResults) {
-		fHistory = MaxHistory.forFolder(storedResults);
-	}
+    /**
+     * Create a new MaxCore from a serialized file stored at storedResults
+     */
+    public static MaxCore storedLocally(File storedResults) {
+        return new MaxCore(storedResults);
+    }
 
-	/**
-	 * Run all the tests in <code>class</code>.
-	 * @return a {@link Result} describing the details of the test run and the failed tests.
-	 */
-	public Result run(Class<?> testClass) {
-		return run(Request.aClass(testClass));
-	}
+    private final MaxHistory history;
 
-	/**
-	 * Run all the tests contained in <code>request</code>.
-	 * @param request the request describing tests
-	 * @return a {@link Result} describing the details of the test run and the failed tests.
-	 */
-	public Result run(Request request) {
-		return run(request, new JUnitCore());
-	}
+    private MaxCore(File storedResults) {
+        history = MaxHistory.forFolder(storedResults);
+    }
 
-	/**
-	 * Run all the tests contained in <code>request</code>.
-	 * 
-	 * This variant should be used if {@code core} has attached listeners that this
-	 * run should notify.
-	 * 
-	 * @param request the request describing tests
-	 * @param core a JUnitCore to delegate to.
-	 * @return a {@link Result} describing the details of the test run and the failed tests.
-	 */
-	public Result run(Request request, JUnitCore core) {
-		core.addListener(fHistory.listener());
-		return core.run(sortRequest(request).getRunner());
-	}
-	
-	/**
-	 * @param request
-	 * @return a new Request, which contains all of the same tests, but in a new order.
-	 */
-	public Request sortRequest(Request request) {
-		if (request instanceof SortingRequest) // We'll pay big karma points for this
-			return request;
-		List<Description> leaves= findLeaves(request);
-		Collections.sort(leaves, fHistory.testComparator());
-		return constructLeafRequest(leaves);
-	}
+    /**
+     * Run all the tests in <code>class</code>.
+     *
+     * @return a {@link Result} describing the details of the test run and the failed tests.
+     */
+    public Result run(Class<?> testClass) {
+        return run(Request.aClass(testClass));
+    }
 
-	private Request constructLeafRequest(List<Description> leaves) {
-		final List<Runner> runners = new ArrayList<Runner>();
-		for (Description each : leaves)
-			runners.add(buildRunner(each));
-		return new Request() {
-			@Override
-			public Runner getRunner() {
-				try {
-					return new Suite((Class<?>)null, runners) {};
-				} catch (InitializationError e) {
-					return new ErrorReportingRunner(null, e);
-				}
-			}
-		};
-	}
+    /**
+     * Run all the tests contained in <code>request</code>.
+     *
+     * @param request the request describing tests
+     * @return a {@link Result} describing the details of the test run and the failed tests.
+     */
+    public Result run(Request request) {
+        return run(request, new JUnitCore());
+    }
 
-	private Runner buildRunner(Description each) {
-		if (each.toString().equals("TestSuite with 0 tests"))
-			return Suite.emptySuite();
-		if (each.toString().startsWith(MALFORMED_JUNIT_3_TEST_CLASS_PREFIX))
-			// This is cheating, because it runs the whole class 
-			// to get the warning for this method, but we can't do better, 
-			// because JUnit 3.8's
-			// thrown away which method the warning is for.
-			return new JUnit38ClassRunner(new TestSuite(getMalformedTestClass(each)));
-		Class<?> type= each.getTestClass();
-		if (type == null)
-			throw new RuntimeException("Can't build a runner from description [" + each + "]");
-		String methodName= each.getMethodName();
-		if (methodName == null)
-			return Request.aClass(type).getRunner();
-		return Request.method(type, methodName).getRunner();
-	}
+    /**
+     * Run all the tests contained in <code>request</code>.
+     *
+     * This variant should be used if {@code core} has attached listeners that this
+     * run should notify.
+     *
+     * @param request the request describing tests
+     * @param core a JUnitCore to delegate to.
+     * @return a {@link Result} describing the details of the test run and the failed tests.
+     */
+    public Result run(Request request, JUnitCore core) {
+        core.addListener(history.listener());
+        return core.run(sortRequest(request).getRunner());
+    }
 
-	private Class<?> getMalformedTestClass(Description each) {
-		try {
-			return Class.forName(each.toString().replace(MALFORMED_JUNIT_3_TEST_CLASS_PREFIX, ""));
-		} catch (ClassNotFoundException e) {
-			return null;
-		}
-	}
+    /**
+     * @return a new Request, which contains all of the same tests, but in a new order.
+     */
+    public Request sortRequest(Request request) {
+        if (request instanceof SortingRequest) {
+            // We'll pay big karma points for this
+            return request;
+        }
+        List<Description> leaves = findLeaves(request);
+        Collections.sort(leaves, history.testComparator());
+        return constructLeafRequest(leaves);
+    }
 
-	/**
-	 * @param request a request to run
-	 * @return a list of method-level tests to run, sorted in the order
-	 * specified in the class comment.
-	 */
-	public List<Description> sortedLeavesForTest(Request request) {
-		return findLeaves(sortRequest(request));
-	}
-	
-	private List<Description> findLeaves(Request request) {
-		List<Description> results= new ArrayList<Description>();
-		findLeaves(null, request.getRunner().getDescription(), results);
-		return results;
-	}
-	
-	private void findLeaves(Description parent, Description description, List<Description> results) {
-		if (description.getChildren().isEmpty())
-			if (description.toString().equals("warning(junit.framework.TestSuite$1)"))
-				results.add(Description.createSuiteDescription(MALFORMED_JUNIT_3_TEST_CLASS_PREFIX + parent));
-			else
-				results.add(description);
-		else
-			for (Description each : description.getChildren())
-				findLeaves(description, each, results);
-	}
-}
+    private Request constructLeafRequest(List<Description> leaves) {
+        final List<Runner> runners = new ArrayList<Runner>();
+        for (Description each : leaves) {
+            runners.add(buildRunner(each));
+        }
+        return new Request() {
+            @Override
+            public Runner getRunner() {
+                try {
+                    return new Suite((Class<?>) null, runners) {
+                    };
+                } catch (InitializationError e) {
+                    return new ErrorReportingRunner(null, e);
+                }
+            }
+        };
+    }
 
+    private Runner buildRunner(Description each) {
+        if (each.toString().equals("TestSuite with 0 tests")) {
+            return Suite.emptySuite();
+        }
+        if (each.toString().startsWith(MALFORMED_JUNIT_3_TEST_CLASS_PREFIX)) {
+            // This is cheating, because it runs the whole class
+            // to get the warning for this method, but we can't do better,
+            // because JUnit 3.8's
+            // thrown away which method the warning is for.
+            return new JUnit38ClassRunner(new TestSuite(getMalformedTestClass(each)));
+        }
+        Class<?> type = each.getTestClass();
+        if (type == null) {
+            throw new RuntimeException("Can't build a runner from description [" + each + "]");
+        }
+        String methodName = each.getMethodName();
+        if (methodName == null) {
+            return Request.aClass(type).getRunner();
+        }
+        return Request.method(type, methodName).getRunner();
+    }
+
+    private Class<?> getMalformedTestClass(Description each) {
+        try {
+            return Class.forName(each.toString().replace(MALFORMED_JUNIT_3_TEST_CLASS_PREFIX, ""));
+        } catch (ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
+     * @param request a request to run
+     * @return a list of method-level tests to run, sorted in the order
+     *         specified in the class comment.
+     */
+    public List<Description> sortedLeavesForTest(Request request) {
+        return findLeaves(sortRequest(request));
+    }
+
+    private List<Description> findLeaves(Request request) {
+        List<Description> results = new ArrayList<Description>();
+        findLeaves(null, request.getRunner().getDescription(), results);
+        return results;
+    }
+
+    private void findLeaves(Description parent, Description description, List<Description> results) {
+        if (description.getChildren().isEmpty()) {
+            if (description.toString().equals("warning(junit.framework.TestSuite$1)")) {
+                results.add(Description.createSuiteDescription(MALFORMED_JUNIT_3_TEST_CLASS_PREFIX + parent));
+            } else {
+                results.add(description);
+            }
+        } else {
+            for (Description each : description.getChildren()) {
+                findLeaves(description, each, results);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/max/MaxHistory.java b/src/main/java/org/junit/experimental/max/MaxHistory.java
index e091793..45a4033 100644
--- a/src/main/java/org/junit/experimental/max/MaxHistory.java
+++ b/src/main/java/org/junit/experimental/max/MaxHistory.java
@@ -24,143 +24,150 @@
  * </ul>
  */
 public class MaxHistory implements Serializable {
-	private static final long serialVersionUID= 1L;
+    private static final long serialVersionUID = 1L;
 
-	/**
-	 * Loads a {@link MaxHistory} from {@code file}, or generates a new one that
-	 * will be saved to {@code file}.
-	 */
-	public static MaxHistory forFolder(File file) {
-		if (file.exists())
-			try {
-				return readHistory(file);
-			} catch (CouldNotReadCoreException e) {
-				e.printStackTrace();
-				file.delete();
-			}
-		return new MaxHistory(file);
-	}
+    /**
+     * Loads a {@link MaxHistory} from {@code file}, or generates a new one that
+     * will be saved to {@code file}.
+     */
+    public static MaxHistory forFolder(File file) {
+        if (file.exists()) {
+            try {
+                return readHistory(file);
+            } catch (CouldNotReadCoreException e) {
+                e.printStackTrace();
+                file.delete();
+            }
+        }
+        return new MaxHistory(file);
+    }
 
-	private static MaxHistory readHistory(File storedResults)
-			throws CouldNotReadCoreException {
-		try {
-			FileInputStream file= new FileInputStream(storedResults);
-			try {
-				ObjectInputStream stream= new ObjectInputStream(file);
-				try {
-					return (MaxHistory) stream.readObject();
-				} finally {
-					stream.close();
-				}
-			} finally {
-				file.close();
-			}
-		} catch (Exception e) {
-			throw new CouldNotReadCoreException(e);
-		}
-	}
+    private static MaxHistory readHistory(File storedResults)
+            throws CouldNotReadCoreException {
+        try {
+            FileInputStream file = new FileInputStream(storedResults);
+            try {
+                ObjectInputStream stream = new ObjectInputStream(file);
+                try {
+                    return (MaxHistory) stream.readObject();
+                } finally {
+                    stream.close();
+                }
+            } finally {
+                file.close();
+            }
+        } catch (Exception e) {
+            throw new CouldNotReadCoreException(e);
+        }
+    }
 
-	private final Map<String, Long> fDurations= new HashMap<String, Long>();
+    /*
+     * We have to use the f prefix until the next major release to ensure
+     * serialization compatibility. 
+     * See https://github.com/junit-team/junit/issues/976
+     */
+    private final Map<String, Long> fDurations = new HashMap<String, Long>();
+    private final Map<String, Long> fFailureTimestamps = new HashMap<String, Long>();
+    private final File fHistoryStore;
 
-	private final Map<String, Long> fFailureTimestamps= new HashMap<String, Long>();
+    private MaxHistory(File storedResults) {
+        fHistoryStore = storedResults;
+    }
 
-	private final File fHistoryStore;
+    private void save() throws IOException {
+        ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(
+                fHistoryStore));
+        stream.writeObject(this);
+        stream.close();
+    }
 
-	private MaxHistory(File storedResults) {
-		fHistoryStore= storedResults;
-	}
+    Long getFailureTimestamp(Description key) {
+        return fFailureTimestamps.get(key.toString());
+    }
 
-	private void save() throws IOException {
-		ObjectOutputStream stream= new ObjectOutputStream(new FileOutputStream(
-				fHistoryStore));
-		stream.writeObject(this);
-		stream.close();
-	}
+    void putTestFailureTimestamp(Description key, long end) {
+        fFailureTimestamps.put(key.toString(), end);
+    }
 
-	Long getFailureTimestamp(Description key) {
-		return fFailureTimestamps.get(key.toString());
-	}
+    boolean isNewTest(Description key) {
+        return !fDurations.containsKey(key.toString());
+    }
 
-	void putTestFailureTimestamp(Description key, long end) {
-		fFailureTimestamps.put(key.toString(), end);
-	}
+    Long getTestDuration(Description key) {
+        return fDurations.get(key.toString());
+    }
 
-	boolean isNewTest(Description key) {
-		return !fDurations.containsKey(key.toString());
-	}
+    void putTestDuration(Description description, long duration) {
+        fDurations.put(description.toString(), duration);
+    }
 
-	Long getTestDuration(Description key) {
-		return fDurations.get(key.toString());
-	}
+    private final class RememberingListener extends RunListener {
+        private long overallStart = System.currentTimeMillis();
 
-	void putTestDuration(Description description, long duration) {
-		fDurations.put(description.toString(), duration);
-	}
+        private Map<Description, Long> starts = new HashMap<Description, Long>();
 
-	private final class RememberingListener extends RunListener {
-		private long overallStart= System.currentTimeMillis();
+        @Override
+        public void testStarted(Description description) throws Exception {
+            starts.put(description, System.nanoTime()); // Get most accurate
+            // possible time
+        }
 
-		private Map<Description, Long> starts= new HashMap<Description, Long>();
+        @Override
+        public void testFinished(Description description) throws Exception {
+            long end = System.nanoTime();
+            long start = starts.get(description);
+            putTestDuration(description, end - start);
+        }
 
-		@Override
-		public void testStarted(Description description) throws Exception {
-			starts.put(description, System.nanoTime()); // Get most accurate
-			// possible time
-		}
+        @Override
+        public void testFailure(Failure failure) throws Exception {
+            putTestFailureTimestamp(failure.getDescription(), overallStart);
+        }
 
-		@Override
-		public void testFinished(Description description) throws Exception {
-			long end= System.nanoTime();
-			long start= starts.get(description);
-			putTestDuration(description, end - start);
-		}
+        @Override
+        public void testRunFinished(Result result) throws Exception {
+            save();
+        }
+    }
 
-		@Override
-		public void testFailure(Failure failure) throws Exception {
-			putTestFailureTimestamp(failure.getDescription(), overallStart);
-		}
+    private class TestComparator implements Comparator<Description> {
+        public int compare(Description o1, Description o2) {
+            // Always prefer new tests
+            if (isNewTest(o1)) {
+                return -1;
+            }
+            if (isNewTest(o2)) {
+                return 1;
+            }
+            // Then most recently failed first
+            int result = getFailure(o2).compareTo(getFailure(o1));
+            return result != 0 ? result
+                    // Then shorter tests first
+                    : getTestDuration(o1).compareTo(getTestDuration(o2));
+        }
 
-		@Override
-		public void testRunFinished(Result result) throws Exception {
-			save();
-		}
-	}
+        private Long getFailure(Description key) {
+            Long result = getFailureTimestamp(key);
+            if (result == null) {
+                return 0L; // 0 = "never failed (that I know about)"
+            }
+            return result;
+        }
+    }
 
-	private class TestComparator implements Comparator<Description> {
-		public int compare(Description o1, Description o2) {
-			// Always prefer new tests
-			if (isNewTest(o1))
-				return -1;
-			if (isNewTest(o2))
-				return 1;
-			// Then most recently failed first
-			int result= getFailure(o2).compareTo(getFailure(o1));
-			return result != 0 ? result
-			// Then shorter tests first
-					: getTestDuration(o1).compareTo(getTestDuration(o2));
-		}
+    /**
+     * @return a listener that will update this history based on the test
+     *         results reported.
+     */
+    public RunListener listener() {
+        return new RememberingListener();
+    }
 
-		private Long getFailure(Description key) {
-			Long result= getFailureTimestamp(key);
-			if (result == null)
-				return 0L; // 0 = "never failed (that I know about)"
-			return result;
-		}
-	}
-
-	/**
-	 * @return a listener that will update this history based on the test
-	 *         results reported.
-	 */
-	public RunListener listener() {
-		return new RememberingListener();
-	}
-
-	/**
-	 * @return a comparator that ranks tests based on the JUnit Max sorting
-	 *         rules, as described in the {@link MaxCore} class comment.
-	 */
-	public Comparator<Description> testComparator() {
-		return new TestComparator();
-	}
+    /**
+     * @return a comparator that ranks tests based on the JUnit Max sorting
+     *         rules, as described in the {@link MaxCore} class comment.
+     */
+    public Comparator<Description> testComparator() {
+        return new TestComparator();
+    }
 }
diff --git a/src/main/java/org/junit/experimental/results/FailureList.java b/src/main/java/org/junit/experimental/results/FailureList.java
index f4bc9b7..e02eeae 100644
--- a/src/main/java/org/junit/experimental/results/FailureList.java
+++ b/src/main/java/org/junit/experimental/results/FailureList.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.experimental.results;
 
 import java.util.List;
@@ -10,22 +7,22 @@
 import org.junit.runner.notification.RunListener;
 
 class FailureList {
-	private final List<Failure> failures;
+    private final List<Failure> failures;
 
-	public FailureList(List<Failure> failures) {
-		this.failures= failures;
-	}
+    public FailureList(List<Failure> failures) {
+        this.failures = failures;
+    }
 
-	public Result result() {
-		Result result= new Result();
-		RunListener listener= result.createListener();
-		for (Failure failure : failures) {
-			try {
-				listener.testFailure(failure);
-			} catch (Exception e) {
-				throw new RuntimeException("I can't believe this happened");
-			}
-		}
-		return result;
-	}
+    public Result result() {
+        Result result = new Result();
+        RunListener listener = result.createListener();
+        for (Failure failure : failures) {
+            try {
+                listener.testFailure(failure);
+            } catch (Exception e) {
+                throw new RuntimeException("I can't believe this happened");
+            }
+        }
+        return result;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/results/PrintableResult.java b/src/main/java/org/junit/experimental/results/PrintableResult.java
index 8bc6f54..ffe22f0 100644
--- a/src/main/java/org/junit/experimental/results/PrintableResult.java
+++ b/src/main/java/org/junit/experimental/results/PrintableResult.java
@@ -14,50 +14,50 @@
  * A test result that prints nicely in error messages.
  * This is only intended to be used in JUnit self-tests.
  * For example:
- * 
+ *
  * <pre>
  *    assertThat(testResult(HasExpectedException.class), isSuccessful());
  * </pre>
  */
 public class PrintableResult {
-	/**
-	 * The result of running JUnit on {@code type}
-	 */
-	public static PrintableResult testResult(Class<?> type) {
-		return testResult(Request.aClass(type));
-	}
-	
-	/**
-	 * The result of running JUnit on Request {@code request}
-	 */
-	public static PrintableResult testResult(Request request) {
-		return new PrintableResult(new JUnitCore().run(request));
-	}
-	
-	private Result result;
+    private Result result;
 
-	/**
-	 * A result that includes the given {@code failures}
-	 */
-	public PrintableResult(List<Failure> failures) {
-		this(new FailureList(failures).result());
-	}
+    /**
+     * The result of running JUnit on {@code type}
+     */
+    public static PrintableResult testResult(Class<?> type) {
+        return testResult(Request.aClass(type));
+    }
 
-	private PrintableResult(Result result) {
-		this.result = result;
-	}
-	
-	@Override
-	public String toString() {
-		ByteArrayOutputStream stream = new ByteArrayOutputStream();
-		new TextListener(new PrintStream(stream)).testRunFinished(result);
-		return stream.toString();
-	}
+    /**
+     * The result of running JUnit on Request {@code request}
+     */
+    public static PrintableResult testResult(Request request) {
+        return new PrintableResult(new JUnitCore().run(request));
+    }
 
-	/**
-	 * Returns the number of failures in this result.
-	 */
-	public int failureCount() {
-		return result.getFailures().size();
-	}
+    /**
+     * A result that includes the given {@code failures}
+     */
+    public PrintableResult(List<Failure> failures) {
+        this(new FailureList(failures).result());
+    }
+
+    private PrintableResult(Result result) {
+        this.result = result;
+    }
+
+    /**
+     * Returns the number of failures in this result.
+     */
+    public int failureCount() {
+        return result.getFailures().size();
+    }
+
+    @Override
+    public String toString() {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        new TextListener(new PrintStream(stream)).testRunFinished(result);
+        return stream.toString();
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/results/ResultMatchers.java b/src/main/java/org/junit/experimental/results/ResultMatchers.java
index 220d0dc..cf58f1b 100644
--- a/src/main/java/org/junit/experimental/results/ResultMatchers.java
+++ b/src/main/java/org/junit/experimental/results/ResultMatchers.java
@@ -3,68 +3,68 @@
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
-import org.junit.internal.matchers.TypeSafeMatcher;
+import org.hamcrest.TypeSafeMatcher;
 
 /**
  * Matchers on a PrintableResult, to enable JUnit self-tests.
  * For example:
- * 
+ *
  * <pre>
- * 		assertThat(testResult(HasExpectedException.class), isSuccessful());
+ * assertThat(testResult(HasExpectedException.class), isSuccessful());
  * </pre>
  */
 public class ResultMatchers {
-	/**
-	 * Matches if the tests are all successful
-	 */
-	public static Matcher<PrintableResult> isSuccessful() {
-		return failureCountIs(0);
-	}
+    /**
+     * Matches if the tests are all successful
+     */
+    public static Matcher<PrintableResult> isSuccessful() {
+        return failureCountIs(0);
+    }
 
-	/**
-	 * Matches if there are {@code count} failures
-	 */
-	public static Matcher<PrintableResult> failureCountIs(final int count) {
-		return new TypeSafeMatcher<PrintableResult>() {
-			public void describeTo(Description description) {
-				description.appendText("has " + count + " failures");
-			}
+    /**
+     * Matches if there are {@code count} failures
+     */
+    public static Matcher<PrintableResult> failureCountIs(final int count) {
+        return new TypeSafeMatcher<PrintableResult>() {
+            public void describeTo(Description description) {
+                description.appendText("has " + count + " failures");
+            }
 
-			@Override
-			public boolean matchesSafely(PrintableResult item) {
-				return item.failureCount() == count;
-			}
-		};
-	}
-	
-	/**
-	 * Matches if the result has exactly one failure, and it contains {@code string}
-	 */
-	public static Matcher<Object> hasSingleFailureContaining(final String string) {
-		return new BaseMatcher<Object>() {
-			public boolean matches(Object item) {
-				return item.toString().contains(string) && failureCountIs(1).matches(item);
-			}
+            @Override
+            public boolean matchesSafely(PrintableResult item) {
+                return item.failureCount() == count;
+            }
+        };
+    }
 
-			public void describeTo(Description description) {
-				description.appendText("has single failure containing " + string);
-			}
-		};
-	}
+    /**
+     * Matches if the result has exactly one failure, and it contains {@code string}
+     */
+    public static Matcher<Object> hasSingleFailureContaining(final String string) {
+        return new BaseMatcher<Object>() {
+            public boolean matches(Object item) {
+                return item.toString().contains(string) && failureCountIs(1).matches(item);
+            }
 
-	/**
-	 * Matches if the result has one or more failures, and at least one of them
-	 * contains {@code string}
-	 */
-	public static Matcher<PrintableResult> hasFailureContaining(final String string) {
-		return new BaseMatcher<PrintableResult>() {
-			public boolean matches(Object item) {
-				return item.toString().contains(string);
-			}
+            public void describeTo(Description description) {
+                description.appendText("has single failure containing " + string);
+            }
+        };
+    }
 
-			public void describeTo(Description description) {
-				description.appendText("has failure containing " + string);
-			}
-		};
-	}
+    /**
+     * Matches if the result has one or more failures, and at least one of them
+     * contains {@code string}
+     */
+    public static Matcher<PrintableResult> hasFailureContaining(final String string) {
+        return new BaseMatcher<PrintableResult>() {
+            public boolean matches(Object item) {
+                return item.toString().contains(string);
+            }
+
+            public void describeTo(Description description) {
+                description.appendText("has failure containing " + string);
+            }
+        };
+    }
 }
diff --git a/src/main/java/org/junit/experimental/runners/Enclosed.java b/src/main/java/org/junit/experimental/runners/Enclosed.java
index b0560ed..610b970 100644
--- a/src/main/java/org/junit/experimental/runners/Enclosed.java
+++ b/src/main/java/org/junit/experimental/runners/Enclosed.java
@@ -1,31 +1,45 @@
 package org.junit.experimental.runners;
 
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
 import org.junit.runners.Suite;
 import org.junit.runners.model.RunnerBuilder;
 
-
 /**
  * If you put tests in inner classes, Ant, for example, won't find them. By running the outer class
  * with Enclosed, the tests in the inner classes will be run. You might put tests in inner classes
- * to group them for convenience or to share constants.
- * 
- *  So, for example:
- *  <pre>
- *  \@RunWith(Enclosed.class)
- *  public class ListTests {
- *  	...useful shared stuff...
- *  	public static class OneKindOfListTest {...}
- *  	public static class AnotherKind {...}
- *  }
- *  </pre>
- *  
- *  For a real example, @see org.junit.tests.manipulation.SortableTest.
+ * to group them for convenience or to share constants. Abstract inner classes are ignored.
+ * <p>
+ * So, for example:
+ * <pre>
+ * &#064;RunWith(Enclosed.class)
+ * public class ListTests {
+ *     ...useful shared stuff...
+ *     public static class OneKindOfListTest {...}
+ *     public static class AnotherKind {...}
+ *     abstract public static class Ignored {...}
+ * }
+ * </pre>
  */
 public class Enclosed extends Suite {
-	/**
-	 * Only called reflectively. Do not use programmatically.
-	 */
-	public Enclosed(Class<?> klass, RunnerBuilder builder) throws Throwable {
-		super(builder, klass, klass.getClasses());
-	}
+    /**
+     * Only called reflectively. Do not use programmatically.
+     */
+    public Enclosed(Class<?> klass, RunnerBuilder builder) throws Throwable {
+        super(builder, klass, filterAbstractClasses(klass.getClasses()));
+    }
+    
+    private static Class<?>[] filterAbstractClasses(final Class<?>[] classes) {     
+        final List<Class<?>> filteredList= new ArrayList<Class<?>>(classes.length);
+
+        for (final Class<?> clazz : classes) {
+            if (!Modifier.isAbstract(clazz.getModifiers())) {
+                filteredList.add(clazz);
+            }
+        }
+        
+        return filteredList.toArray(new Class<?>[filteredList.size()]);
+    }   
 }
diff --git a/src/main/java/org/junit/experimental/theories/DataPoint.java b/src/main/java/org/junit/experimental/theories/DataPoint.java
index 2aaba6a..0a017bb 100644
--- a/src/main/java/org/junit/experimental/theories/DataPoint.java
+++ b/src/main/java/org/junit/experimental/theories/DataPoint.java
@@ -1,9 +1,56 @@
 package org.junit.experimental.theories;
 
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
+/**
+ * Annotating an field or method with &#064;DataPoint will cause the field value
+ * or the value returned by the method to be used as a potential parameter for
+ * theories in that class, when run with the
+ * {@link org.junit.experimental.theories.Theories Theories} runner.
+ * <p>
+ * A DataPoint is only considered as a potential value for parameters for
+ * which its type is assignable. When multiple {@code DataPoint}s exist 
+ * with overlapping types more control can be obtained by naming each DataPoint 
+ * using the value of this annotation, e.g. with
+ * <code>&#064;DataPoint({"dataset1", "dataset2"})</code>, and then specifying
+ * which named set to consider as potential values for each parameter using the
+ * {@link org.junit.experimental.theories.FromDataPoints &#064;FromDataPoints}
+ * annotation.
+ * <p>
+ * Parameters with no specified source (i.e. without &#064;FromDataPoints or
+ * other {@link org.junit.experimental.theories.ParametersSuppliedBy
+ * &#064;ParameterSuppliedBy} annotations) will use all {@code DataPoint}s that are
+ * assignable to the parameter type as potential values, including named sets of
+ * {@code DataPoint}s.
+ * 
+ * <pre>
+ * &#064;DataPoint
+ * public static String dataPoint = "value";
+ * 
+ * &#064;DataPoint("generated")
+ * public static String generatedDataPoint() {
+ *     return "generated value";
+ * }
+ * 
+ * &#064;Theory
+ * public void theoryMethod(String param) {
+ *     ...
+ * }
+ * </pre>
+ * 
+ * @see org.junit.experimental.theories.Theories
+ * @see org.junit.experimental.theories.Theory
+ * @see org.junit.experimental.theories.DataPoint
+ * @see org.junit.experimental.theories.FromDataPoints
+ */
 @Retention(RetentionPolicy.RUNTIME)
+@Target({FIELD, METHOD})
 public @interface DataPoint {
-
-}
+    String[] value() default {};
+    Class<? extends Throwable>[] ignoredExceptions() default {};
+}
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/theories/DataPoints.java b/src/main/java/org/junit/experimental/theories/DataPoints.java
index 42145e3..b47461b 100644
--- a/src/main/java/org/junit/experimental/theories/DataPoints.java
+++ b/src/main/java/org/junit/experimental/theories/DataPoints.java
@@ -1,9 +1,64 @@
 package org.junit.experimental.theories;
 
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
+/**
+ * Annotating an array or iterable-typed field or method with &#064;DataPoints
+ * will cause the values in the array or iterable given to be used as potential
+ * parameters for theories in that class when run with the
+ * {@link org.junit.experimental.theories.Theories Theories} runner.
+ * <p>
+ * DataPoints will only be considered as potential values for parameters for
+ * which their types are assignable. When multiple sets of DataPoints exist with
+ * overlapping types more control can be obtained by naming the DataPoints using
+ * the value of this annotation, e.g. with
+ * <code>&#064;DataPoints({"dataset1", "dataset2"})</code>, and then specifying
+ * which named set to consider as potential values for each parameter using the
+ * {@link org.junit.experimental.theories.FromDataPoints &#064;FromDataPoints}
+ * annotation.
+ * <p>
+ * Parameters with no specified source (i.e. without &#064;FromDataPoints or
+ * other {@link org.junit.experimental.theories.ParametersSuppliedBy
+ * &#064;ParameterSuppliedBy} annotations) will use all DataPoints that are
+ * assignable to the parameter type as potential values, including named sets of
+ * DataPoints.
+ * <p>
+ * DataPoints methods whose array types aren't assignable from the target
+ * parameter type (and so can't possibly return relevant values) will not be
+ * called when generating values for that parameter. Iterable-typed datapoints
+ * methods must always be called though, as this information is not available
+ * here after generic type erasure, so expensive methods returning iterable
+ * datapoints are a bad idea.
+ * 
+ * <pre>
+ * &#064;DataPoints
+ * public static String[] dataPoints = new String[] { ... };
+ * 
+ * &#064;DataPoints
+ * public static String[] generatedDataPoints() {
+ *     return new String[] { ... };
+ * }
+ * 
+ * &#064;Theory
+ * public void theoryMethod(String param) {
+ *     ...
+ * }
+ * </pre>
+ * 
+ * @see org.junit.experimental.theories.Theories
+ * @see org.junit.experimental.theories.Theory
+ * @see org.junit.experimental.theories.DataPoint
+ * @see org.junit.experimental.theories.FromDataPoints
+ */
 @Retention(RetentionPolicy.RUNTIME)
+@Target({ FIELD, METHOD })
 public @interface DataPoints {
+    String[] value() default {};
 
+    Class<? extends Throwable>[] ignoredExceptions() default {};
 }
diff --git a/src/main/java/org/junit/experimental/theories/FromDataPoints.java b/src/main/java/org/junit/experimental/theories/FromDataPoints.java
new file mode 100644
index 0000000..2b149ca
--- /dev/null
+++ b/src/main/java/org/junit/experimental/theories/FromDataPoints.java
@@ -0,0 +1,54 @@
+package org.junit.experimental.theories;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.experimental.theories.internal.SpecificDataPointsSupplier;
+
+/**
+ * Annotating a parameter of a {@link org.junit.experimental.theories.Theory
+ * &#064;Theory} method with <code>&#064;FromDataPoints</code> will limit the
+ * datapoints considered as potential values for that parameter to just the
+ * {@link org.junit.experimental.theories.DataPoints DataPoints} with the given
+ * name. DataPoint names can be given as the value parameter of the
+ * &#064;DataPoints annotation.
+ * <p>
+ * DataPoints without names will not be considered as values for any parameters
+ * annotated with &#064;FromDataPoints.
+ * <pre>
+ * &#064;DataPoints
+ * public static String[] unnamed = new String[] { ... };
+ * 
+ * &#064;DataPoints("regexes")
+ * public static String[] regexStrings = new String[] { ... };
+ * 
+ * &#064;DataPoints({"forMatching", "alphanumeric"})
+ * public static String[] testStrings = new String[] { ... }; 
+ * 
+ * &#064;Theory
+ * public void stringTheory(String param) {
+ *     // This will be called with every value in 'regexStrings',
+ *     // 'testStrings' and 'unnamed'.
+ * }
+ * 
+ * &#064;Theory
+ * public void regexTheory(&#064;FromDataPoints("regexes") String regex,
+ *                         &#064;FromDataPoints("forMatching") String value) {
+ *     // This will be called with only the values in 'regexStrings' as 
+ *     // regex, only the values in 'testStrings' as value, and none 
+ *     // of the values in 'unnamed'.
+ * }
+ * </pre>
+ * 
+ * @see org.junit.experimental.theories.Theory
+ * @see org.junit.experimental.theories.DataPoint
+ * @see org.junit.experimental.theories.DataPoints
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+@ParametersSuppliedBy(SpecificDataPointsSupplier.class)
+public @interface FromDataPoints {
+    String value();
+}
diff --git a/src/main/java/org/junit/experimental/theories/ParameterSignature.java b/src/main/java/org/junit/experimental/theories/ParameterSignature.java
index e7150fc..cf22583 100644
--- a/src/main/java/org/junit/experimental/theories/ParameterSignature.java
+++ b/src/main/java/org/junit/experimental/theories/ParameterSignature.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.experimental.theories;
 
 import java.lang.annotation.Annotation;
@@ -8,83 +5,130 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class ParameterSignature {
-	public static ArrayList<ParameterSignature> signatures(Method method) {
-		return signatures(method.getParameterTypes(), method
-				.getParameterAnnotations());
-	}
+    
+    private static final Map<Class<?>, Class<?>> CONVERTABLE_TYPES_MAP = buildConvertableTypesMap();
+    
+    private static Map<Class<?>, Class<?>> buildConvertableTypesMap() {
+        Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
 
-	public static List<ParameterSignature> signatures(Constructor<?> constructor) {
-		return signatures(constructor.getParameterTypes(), constructor
-				.getParameterAnnotations());
-	}
+        putSymmetrically(map, boolean.class, Boolean.class);
+        putSymmetrically(map, byte.class, Byte.class);
+        putSymmetrically(map, short.class, Short.class);
+        putSymmetrically(map, char.class, Character.class);
+        putSymmetrically(map, int.class, Integer.class);
+        putSymmetrically(map, long.class, Long.class);
+        putSymmetrically(map, float.class, Float.class);
+        putSymmetrically(map, double.class, Double.class);
 
-	private static ArrayList<ParameterSignature> signatures(
-			Class<?>[] parameterTypes, Annotation[][] parameterAnnotations) {
-		ArrayList<ParameterSignature> sigs= new ArrayList<ParameterSignature>();
-		for (int i= 0; i < parameterTypes.length; i++) {
-			sigs.add(new ParameterSignature(parameterTypes[i],
-					parameterAnnotations[i]));
-		}
-		return sigs;
-	}
+        return Collections.unmodifiableMap(map);
+    }
+    
+    private static <T> void putSymmetrically(Map<T, T> map, T a, T b) {
+        map.put(a, b);
+        map.put(b, a);
+    }
+    
+    public static ArrayList<ParameterSignature> signatures(Method method) {
+        return signatures(method.getParameterTypes(), method
+                .getParameterAnnotations());
+    }
 
-	private final Class<?> type;
+    public static List<ParameterSignature> signatures(Constructor<?> constructor) {
+        return signatures(constructor.getParameterTypes(), constructor
+                .getParameterAnnotations());
+    }
 
-	private final Annotation[] annotations;
+    private static ArrayList<ParameterSignature> signatures(
+            Class<?>[] parameterTypes, Annotation[][] parameterAnnotations) {
+        ArrayList<ParameterSignature> sigs = new ArrayList<ParameterSignature>();
+        for (int i = 0; i < parameterTypes.length; i++) {
+            sigs.add(new ParameterSignature(parameterTypes[i],
+                    parameterAnnotations[i]));
+        }
+        return sigs;
+    }
 
-	private ParameterSignature(Class<?> type, Annotation[] annotations) {
-		this.type= type;
-		this.annotations= annotations;
-	}
+    private final Class<?> type;
 
-	public boolean canAcceptType(Class<?> candidate) {
-		return type.isAssignableFrom(candidate);
-	}
+    private final Annotation[] annotations;
 
-	public Class<?> getType() {
-		return type;
-	}
+    private ParameterSignature(Class<?> type, Annotation[] annotations) {
+        this.type = type;
+        this.annotations = annotations;
+    }
 
-	public List<Annotation> getAnnotations() {
-		return Arrays.asList(annotations);
-	}
+    public boolean canAcceptValue(Object candidate) {
+        return (candidate == null) ? !type.isPrimitive() : canAcceptType(candidate.getClass());
+    }
 
-	public boolean canAcceptArrayType(Class<?> type) {
-		return type.isArray() && canAcceptType(type.getComponentType());
-	}
+    public boolean canAcceptType(Class<?> candidate) {
+        return type.isAssignableFrom(candidate) ||
+                isAssignableViaTypeConversion(type, candidate);
+    }
+    
+    public boolean canPotentiallyAcceptType(Class<?> candidate) {
+        return candidate.isAssignableFrom(type) ||
+                isAssignableViaTypeConversion(candidate, type) ||
+                canAcceptType(candidate);
+    }
 
-	public boolean hasAnnotation(Class<? extends Annotation> type) {
-		return getAnnotation(type) != null;
-	}
+    private boolean isAssignableViaTypeConversion(Class<?> targetType, Class<?> candidate) {
+        if (CONVERTABLE_TYPES_MAP.containsKey(candidate)) {
+            Class<?> wrapperClass = CONVERTABLE_TYPES_MAP.get(candidate);
+            return targetType.isAssignableFrom(wrapperClass);
+        } else {
+            return false;
+        }
+    }
 
-	public <T extends Annotation> T findDeepAnnotation(Class<T> annotationType) {
-		Annotation[] annotations2= annotations;
-		return findDeepAnnotation(annotations2, annotationType, 3);
-	}
+    public Class<?> getType() {
+        return type;
+    }
 
-	private <T extends Annotation> T findDeepAnnotation(
-			Annotation[] annotations, Class<T> annotationType, int depth) {
-		if (depth == 0)
-			return null;
-		for (Annotation each : annotations) {
-			if (annotationType.isInstance(each))
-				return annotationType.cast(each);
-			Annotation candidate= findDeepAnnotation(each.annotationType()
-					.getAnnotations(), annotationType, depth - 1);
-			if (candidate != null)
-				return annotationType.cast(candidate);
-		}
+    public List<Annotation> getAnnotations() {
+        return Arrays.asList(annotations);
+    }
 
-		return null;
-	}
+    public boolean hasAnnotation(Class<? extends Annotation> type) {
+        return getAnnotation(type) != null;
+    }
 
-	public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
-		for (Annotation each : getAnnotations())
-			if (annotationType.isInstance(each))
-				return annotationType.cast(each);
-		return null;
-	}
+    public <T extends Annotation> T findDeepAnnotation(Class<T> annotationType) {
+        Annotation[] annotations2 = annotations;
+        return findDeepAnnotation(annotations2, annotationType, 3);
+    }
+
+    private <T extends Annotation> T findDeepAnnotation(
+            Annotation[] annotations, Class<T> annotationType, int depth) {
+        if (depth == 0) {
+            return null;
+        }
+        for (Annotation each : annotations) {
+            if (annotationType.isInstance(each)) {
+                return annotationType.cast(each);
+            }
+            Annotation candidate = findDeepAnnotation(each.annotationType()
+                    .getAnnotations(), annotationType, depth - 1);
+            if (candidate != null) {
+                return annotationType.cast(candidate);
+            }
+        }
+
+        return null;
+    }
+
+    public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+        for (Annotation each : getAnnotations()) {
+            if (annotationType.isInstance(each)) {
+                return annotationType.cast(each);
+            }
+        }
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/theories/ParameterSupplier.java b/src/main/java/org/junit/experimental/theories/ParameterSupplier.java
index 9016c91..bac8b34 100644
--- a/src/main/java/org/junit/experimental/theories/ParameterSupplier.java
+++ b/src/main/java/org/junit/experimental/theories/ParameterSupplier.java
@@ -2,7 +2,42 @@
 
 import java.util.List;
 
-
+/**
+ * Abstract parent class for suppliers of input data points for theories. Extend this class to customize how {@link
+ * org.junit.experimental.theories.Theories Theories} runner
+ * finds accepted data points. Then use your class together with <b>&#064;ParametersSuppliedBy</b> on input
+ * parameters for theories.
+ *
+ * <p>
+ * For example, here is a supplier for values between two integers, and an annotation that references it:
+ *
+ * <pre>
+ *     &#064;Retention(RetentionPolicy.RUNTIME)
+ *     <b>&#064;ParametersSuppliedBy</b>(BetweenSupplier.class)
+ *     public @interface Between {
+ *         int first();
+ *
+ *         int last();
+ *     }
+ *
+ *     public static class BetweenSupplier extends <b>ParameterSupplier</b> {
+ *         &#064;Override
+ *         public List&lt;<b>PotentialAssignment</b>&gt; getValueSources(<b>ParameterSignature</b> sig) {
+ *             List&lt;<b>PotentialAssignment</b>&gt; list = new ArrayList&lt;PotentialAssignment&gt;();
+ *             Between annotation = (Between) sig.getSupplierAnnotation();
+ *
+ *             for (int i = annotation.first(); i &lt;= annotation.last(); i++)
+ *                 list.add(<b>PotentialAssignment</b>.forValue("ints", i));
+ *             return list;
+ *         }
+ *     }
+ * </pre>
+ * </p>
+ *
+ * @see org.junit.experimental.theories.ParametersSuppliedBy
+ * @see org.junit.experimental.theories.Theories
+ * @see org.junit.experimental.theories.FromDataPoints
+ */
 public abstract class ParameterSupplier {
-	public abstract List<PotentialAssignment> getValueSources(ParameterSignature sig);
+    public abstract List<PotentialAssignment> getValueSources(ParameterSignature sig) throws Throwable;
 }
diff --git a/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java b/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java
index 8f090ef..15b5d95 100644
--- a/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java
+++ b/src/main/java/org/junit/experimental/theories/ParametersSuppliedBy.java
@@ -1,12 +1,48 @@
 package org.junit.experimental.theories;
 
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.PARAMETER;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
-
+/**
+ * Annotating a {@link org.junit.experimental.theories.Theory Theory} method
+ * parameter with &#064;ParametersSuppliedBy causes it to be supplied with
+ * values from the named
+ * {@link org.junit.experimental.theories.ParameterSupplier ParameterSupplier}
+ * when run as a theory by the {@link org.junit.experimental.theories.Theories
+ * Theories} runner.
+ * 
+ * In addition, annotations themselves can be annotated with
+ * &#064;ParametersSuppliedBy, and then used similarly. ParameterSuppliedBy
+ * annotations on parameters are detected by searching up this heirarchy such
+ * that these act as syntactic sugar, making:
+ * 
+ * <pre>
+ * &#064;ParametersSuppliedBy(Supplier.class)
+ * public &#064;interface SpecialParameter { }
+ * 
+ * &#064;Theory
+ * public void theoryMethod(&#064;SpecialParameter String param) {
+ *   ...
+ * }
+ * </pre>
+ * 
+ * equivalent to:
+ * 
+ * <pre>
+ * &#064;Theory
+ * public void theoryMethod(&#064;ParametersSuppliedBy(Supplier.class) String param) {
+ *   ...
+ * }
+ * </pre>
+ */
 @Retention(RetentionPolicy.RUNTIME)
+@Target({ ANNOTATION_TYPE, PARAMETER })
 public @interface ParametersSuppliedBy {
 
-	Class<? extends ParameterSupplier> value();
+    Class<? extends ParameterSupplier> value();
 
 }
diff --git a/src/main/java/org/junit/experimental/theories/PotentialAssignment.java b/src/main/java/org/junit/experimental/theories/PotentialAssignment.java
index 0c008d0..18ca07a 100644
--- a/src/main/java/org/junit/experimental/theories/PotentialAssignment.java
+++ b/src/main/java/org/junit/experimental/theories/PotentialAssignment.java
@@ -1,31 +1,52 @@
 package org.junit.experimental.theories;
 
-public abstract class PotentialAssignment {
-	public static class CouldNotGenerateValueException extends Exception {
-		private static final long serialVersionUID= 1L;
-	}
-	
-	public static PotentialAssignment forValue(final String name, final Object value) {
-		return new PotentialAssignment() {		
-			@Override
-			public Object getValue() throws CouldNotGenerateValueException {
-				return value;
-			}
-			
-			@Override
-			public String toString() {
-				return String.format("[%s]", value);
-			}
+import static java.lang.String.format;
 
-			@Override
-			public String getDescription()
-					throws CouldNotGenerateValueException {
-				return name;
-			}
-		};
-	}
-	
-	public abstract Object getValue() throws CouldNotGenerateValueException;
-	
-	public abstract String getDescription() throws CouldNotGenerateValueException;
-}
+public abstract class PotentialAssignment {
+    public static class CouldNotGenerateValueException extends Exception {
+        private static final long serialVersionUID = 1L;
+        
+        public CouldNotGenerateValueException() {
+        }
+        
+        public CouldNotGenerateValueException(Throwable e) {
+            super(e);
+        }
+    }
+
+    public static PotentialAssignment forValue(final String name, final Object value) {
+        return new PotentialAssignment() {
+            @Override
+            public Object getValue() {
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                return format("[%s]", value);
+            }
+
+            @Override
+            public String getDescription() {
+                String valueString;
+
+                if (value == null) {
+                    valueString = "null";
+                } else {
+                    try {
+                        valueString = format("\"%s\"", value);
+                    } catch (Throwable e) {
+                        valueString = format("[toString() threw %s: %s]", 
+                                             e.getClass().getSimpleName(), e.getMessage());
+                    }
+                }
+
+                return format("%s <from %s>", valueString, name);
+            }
+        };
+    }
+
+    public abstract Object getValue() throws CouldNotGenerateValueException;
+
+    public abstract String getDescription() throws CouldNotGenerateValueException;
+}
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/theories/Theories.java b/src/main/java/org/junit/experimental/theories/Theories.java
index 82ff98b..817f553 100644
--- a/src/main/java/org/junit/experimental/theories/Theories.java
+++ b/src/main/java/org/junit/experimental/theories/Theories.java
@@ -1,16 +1,14 @@
-/**
- * 
- */
 package org.junit.experimental.theories;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.junit.Assert;
-import org.junit.experimental.theories.PotentialAssignment.CouldNotGenerateValueException;
+import org.junit.Assume;
 import org.junit.experimental.theories.internal.Assignments;
 import org.junit.experimental.theories.internal.ParameterizedAssertionError;
 import org.junit.internal.AssumptionViolatedException;
@@ -20,180 +18,288 @@
 import org.junit.runners.model.Statement;
 import org.junit.runners.model.TestClass;
 
+/**
+ * The Theories runner allows to test a certain functionality against a subset of an infinite set of data points.
+ * <p>
+ * A Theory is a piece of functionality (a method) that is executed against several data inputs called data points.
+ * To make a test method a theory you mark it with <b>&#064;Theory</b>. To create a data point you create a public
+ * field in your test class and mark it with <b>&#064;DataPoint</b>. The Theories runner then executes your test
+ * method as many times as the number of data points declared, providing a different data point as
+ * the input argument on each invocation.
+ * </p>
+ * <p>
+ * A Theory differs from standard test method in that it captures some aspect of the intended behavior in possibly
+ * infinite numbers of scenarios which corresponds to the number of data points declared. Using assumptions and
+ * assertions properly together with covering multiple scenarios with different data points can make your tests more
+ * flexible and bring them closer to scientific theories (hence the name).
+ * </p>
+ * <p>
+ * For example:
+ * <pre>
+ *
+ * &#064;RunWith(<b>Theories.class</b>)
+ * public class UserTest {
+ *      <b>&#064;DataPoint</b>
+ *      public static String GOOD_USERNAME = "optimus";
+ *      <b>&#064;DataPoint</b>
+ *      public static String USERNAME_WITH_SLASH = "optimus/prime";
+ *
+ *      <b>&#064;Theory</b>
+ *      public void filenameIncludesUsername(String username) {
+ *          assumeThat(username, not(containsString("/")));
+ *          assertThat(new User(username).configFileName(), containsString(username));
+ *      }
+ * }
+ * </pre>
+ * This makes it clear that the user's filename should be included in the config file name,
+ * only if it doesn't contain a slash. Another test or theory might define what happens when a username does contain
+ * a slash. <code>UserTest</code> will attempt to run <code>filenameIncludesUsername</code> on every compatible data
+ * point defined in the class. If any of the assumptions fail, the data point is silently ignored. If all of the
+ * assumptions pass, but an assertion fails, the test fails.
+ * <p>
+ * Defining general statements as theories allows data point reuse across a bunch of functionality tests and also
+ * allows automated tools to search for new, unexpected data points that expose bugs.
+ * </p>
+ * <p>
+ * The support for Theories has been absorbed from the Popper project, and more complete documentation can be found
+ * from that projects archived documentation.
+ * </p>
+ *
+ * @see <a href="http://web.archive.org/web/20071012143326/popper.tigris.org/tutorial.html">Archived Popper project documentation</a>
+ * @see <a href="http://web.archive.org/web/20110608210825/http://shareandenjoy.saff.net/tdd-specifications.pdf">Paper on Theories</a>
+ */
 public class Theories extends BlockJUnit4ClassRunner {
-	public Theories(Class<?> klass) throws InitializationError {
-		super(klass);
-	}
+    public Theories(Class<?> klass) throws InitializationError {
+        super(klass);
+    }
 
-	@Override
-	protected void collectInitializationErrors(List<Throwable> errors) {
-		super.collectInitializationErrors(errors);
-		validateDataPointFields(errors);
-	}
-	
-	private void validateDataPointFields(List<Throwable> errors) {
-		Field[] fields= getTestClass().getJavaClass().getDeclaredFields();
-		
-		for (Field each : fields)
-			if (each.getAnnotation(DataPoint.class) != null && !Modifier.isStatic(each.getModifiers()))
-				errors.add(new Error("DataPoint field " + each.getName() + " must be static"));
-	}
-	
-	@Override
-	protected void validateConstructor(List<Throwable> errors) {
-		validateOnlyOneConstructor(errors);
-	}
-	
-	@Override
-	protected void validateTestMethods(List<Throwable> errors) {
-		for (FrameworkMethod each : computeTestMethods())
-			if(each.getAnnotation(Theory.class) != null)
-				each.validatePublicVoid(false, errors);
-			else
-				each.validatePublicVoidNoArg(false, errors);
-	}
-	
-	@Override
-	protected List<FrameworkMethod> computeTestMethods() {
-		List<FrameworkMethod> testMethods= super.computeTestMethods();
-		List<FrameworkMethod> theoryMethods= getTestClass().getAnnotatedMethods(Theory.class);
-		testMethods.removeAll(theoryMethods);
-		testMethods.addAll(theoryMethods);
-		return testMethods;
-	}
+    @Override
+    protected void collectInitializationErrors(List<Throwable> errors) {
+        super.collectInitializationErrors(errors);
+        validateDataPointFields(errors);
+        validateDataPointMethods(errors);
+    }
 
-	@Override
-	public Statement methodBlock(final FrameworkMethod method) {
-		return new TheoryAnchor(method, getTestClass());
-	}
+    private void validateDataPointFields(List<Throwable> errors) {
+        Field[] fields = getTestClass().getJavaClass().getDeclaredFields();
 
-	public static class TheoryAnchor extends Statement {
-		private int successes= 0;
+        for (Field field : fields) {
+            if (field.getAnnotation(DataPoint.class) == null && field.getAnnotation(DataPoints.class) == null) {
+                continue;
+            }
+            if (!Modifier.isStatic(field.getModifiers())) {
+                errors.add(new Error("DataPoint field " + field.getName() + " must be static"));
+            }
+            if (!Modifier.isPublic(field.getModifiers())) {
+                errors.add(new Error("DataPoint field " + field.getName() + " must be public"));
+            }
+        }
+    }
 
-		private FrameworkMethod fTestMethod;
-        private TestClass fTestClass;
+    private void validateDataPointMethods(List<Throwable> errors) {
+        Method[] methods = getTestClass().getJavaClass().getDeclaredMethods();
+        
+        for (Method method : methods) {
+            if (method.getAnnotation(DataPoint.class) == null && method.getAnnotation(DataPoints.class) == null) {
+                continue;
+            }
+            if (!Modifier.isStatic(method.getModifiers())) {
+                errors.add(new Error("DataPoint method " + method.getName() + " must be static"));
+            }
+            if (!Modifier.isPublic(method.getModifiers())) {
+                errors.add(new Error("DataPoint method " + method.getName() + " must be public"));
+            }
+        }
+    }
 
-		private List<AssumptionViolatedException> fInvalidParameters= new ArrayList<AssumptionViolatedException>();
+    @Override
+    protected void validateConstructor(List<Throwable> errors) {
+        validateOnlyOneConstructor(errors);
+    }
 
-		public TheoryAnchor(FrameworkMethod method, TestClass testClass) {
-			fTestMethod= method;
-            fTestClass= testClass;
-		}
+    @Override
+    protected void validateTestMethods(List<Throwable> errors) {
+        for (FrameworkMethod each : computeTestMethods()) {
+            if (each.getAnnotation(Theory.class) != null) {
+                each.validatePublicVoid(false, errors);
+                each.validateNoTypeParametersOnArgs(errors);
+            } else {
+                each.validatePublicVoidNoArg(false, errors);
+            }
+            
+            for (ParameterSignature signature : ParameterSignature.signatures(each.getMethod())) {
+                ParametersSuppliedBy annotation = signature.findDeepAnnotation(ParametersSuppliedBy.class);
+                if (annotation != null) {
+                    validateParameterSupplier(annotation.value(), errors);
+                }
+            }
+        }
+    }
 
-        private TestClass getTestClass() {
-            return fTestClass;
+    private void validateParameterSupplier(Class<? extends ParameterSupplier> supplierClass, List<Throwable> errors) {
+        Constructor<?>[] constructors = supplierClass.getConstructors();
+        
+        if (constructors.length != 1) {
+            errors.add(new Error("ParameterSupplier " + supplierClass.getName() + 
+                                 " must have only one constructor (either empty or taking only a TestClass)"));
+        } else {
+            Class<?>[] paramTypes = constructors[0].getParameterTypes();
+            if (!(paramTypes.length == 0) && !paramTypes[0].equals(TestClass.class)) {
+                errors.add(new Error("ParameterSupplier " + supplierClass.getName() + 
+                                     " constructor must take either nothing or a single TestClass instance"));
+            }
+        }
+    }
+
+    @Override
+    protected List<FrameworkMethod> computeTestMethods() {
+        List<FrameworkMethod> testMethods = new ArrayList<FrameworkMethod>(super.computeTestMethods());
+        List<FrameworkMethod> theoryMethods = getTestClass().getAnnotatedMethods(Theory.class);
+        testMethods.removeAll(theoryMethods);
+        testMethods.addAll(theoryMethods);
+        return testMethods;
+    }
+
+    @Override
+    public Statement methodBlock(final FrameworkMethod method) {
+        return new TheoryAnchor(method, getTestClass());
+    }
+
+    public static class TheoryAnchor extends Statement {
+        private int successes = 0;
+
+        private final FrameworkMethod testMethod;
+        private final TestClass testClass;
+
+        private List<AssumptionViolatedException> fInvalidParameters = new ArrayList<AssumptionViolatedException>();
+
+        public TheoryAnchor(FrameworkMethod testMethod, TestClass testClass) {
+            this.testMethod = testMethod;
+            this.testClass = testClass;
         }
 
-		@Override
-		public void evaluate() throws Throwable {
-			runWithAssignment(Assignments.allUnassigned(
-					fTestMethod.getMethod(), getTestClass()));
+        private TestClass getTestClass() {
+            return testClass;
+        }
 
-			if (successes == 0)
-				Assert
-						.fail("Never found parameters that satisfied method assumptions.  Violated assumptions: "
-								+ fInvalidParameters);
-		}
+        @Override
+        public void evaluate() throws Throwable {
+            runWithAssignment(Assignments.allUnassigned(
+                    testMethod.getMethod(), getTestClass()));
+            
+            //if this test method is not annotated with Theory, then no successes is a valid case
+            boolean hasTheoryAnnotation = testMethod.getAnnotation(Theory.class) != null;
+            if (successes == 0 && hasTheoryAnnotation) {
+                Assert
+                        .fail("Never found parameters that satisfied method assumptions.  Violated assumptions: "
+                                + fInvalidParameters);
+            }
+        }
 
-		protected void runWithAssignment(Assignments parameterAssignment)
-				throws Throwable {
-			if (!parameterAssignment.isComplete()) {
-				runWithIncompleteAssignment(parameterAssignment);
-			} else {
-				runWithCompleteAssignment(parameterAssignment);
-			}
-		}
+        protected void runWithAssignment(Assignments parameterAssignment)
+                throws Throwable {
+            if (!parameterAssignment.isComplete()) {
+                runWithIncompleteAssignment(parameterAssignment);
+            } else {
+                runWithCompleteAssignment(parameterAssignment);
+            }
+        }
 
-		protected void runWithIncompleteAssignment(Assignments incomplete)
-				throws InstantiationException, IllegalAccessException,
-				Throwable {
-			for (PotentialAssignment source : incomplete
-					.potentialsForNextUnassigned()) {
-				runWithAssignment(incomplete.assignNext(source));
-			}
-		}
+        protected void runWithIncompleteAssignment(Assignments incomplete)
+                throws Throwable {
+            for (PotentialAssignment source : incomplete
+                    .potentialsForNextUnassigned()) {
+                runWithAssignment(incomplete.assignNext(source));
+            }
+        }
 
-		protected void runWithCompleteAssignment(final Assignments complete)
-				throws InstantiationException, IllegalAccessException,
-				InvocationTargetException, NoSuchMethodException, Throwable {
-			new BlockJUnit4ClassRunner(getTestClass().getJavaClass()) {
-				@Override
-				protected void collectInitializationErrors(
-						List<Throwable> errors) {
-					// do nothing
-				}
+        protected void runWithCompleteAssignment(final Assignments complete)
+                throws Throwable {
+            new BlockJUnit4ClassRunner(getTestClass().getJavaClass()) {
+                @Override
+                protected void collectInitializationErrors(
+                        List<Throwable> errors) {
+                    // do nothing
+                }
 
-				@Override
-				public Statement methodBlock(FrameworkMethod method) {
-					final Statement statement= super.methodBlock(method);
-					return new Statement() {
-						@Override
-						public void evaluate() throws Throwable {
-							try {
-								statement.evaluate();
-								handleDataPointSuccess();
-							} catch (AssumptionViolatedException e) {
-								handleAssumptionViolation(e);
-							} catch (Throwable e) {
-								reportParameterizedError(e, complete
-										.getArgumentStrings(nullsOk()));
-							}
-						}
+                @Override
+                public Statement methodBlock(FrameworkMethod method) {
+                    final Statement statement = super.methodBlock(method);
+                    return new Statement() {
+                        @Override
+                        public void evaluate() throws Throwable {
+                            try {
+                                statement.evaluate();
+                                handleDataPointSuccess();
+                            } catch (AssumptionViolatedException e) {
+                                handleAssumptionViolation(e);
+                            } catch (Throwable e) {
+                                reportParameterizedError(e, complete
+                                        .getArgumentStrings(nullsOk()));
+                            }
+                        }
 
-					};
-				}
+                    };
+                }
 
-				@Override
-				protected Statement methodInvoker(FrameworkMethod method, Object test) {
-					return methodCompletesWithParameters(method, complete, test);
-				}
+                @Override
+                protected Statement methodInvoker(FrameworkMethod method, Object test) {
+                    return methodCompletesWithParameters(method, complete, test);
+                }
 
-				@Override
-				public Object createTest() throws Exception {
-					return getTestClass().getOnlyConstructor().newInstance(
-							complete.getConstructorArguments(nullsOk()));
-				}
-			}.methodBlock(fTestMethod).evaluate();
-		}
+                @Override
+                public Object createTest() throws Exception {
+                    Object[] params = complete.getConstructorArguments();
+                    
+                    if (!nullsOk()) {
+                        Assume.assumeNotNull(params);
+                    }
+                    
+                    return getTestClass().getOnlyConstructor().newInstance(params);
+                }
+            }.methodBlock(testMethod).evaluate();
+        }
 
-		private Statement methodCompletesWithParameters(
-				final FrameworkMethod method, final Assignments complete, final Object freshInstance) {
-			return new Statement() {
-				@Override
-				public void evaluate() throws Throwable {
-					try {
-						final Object[] values= complete.getMethodArguments(
-								nullsOk());
-						method.invokeExplosively(freshInstance, values);
-					} catch (CouldNotGenerateValueException e) {
-						// ignore
-					}
-				}
-			};
-		}
+        private Statement methodCompletesWithParameters(
+                final FrameworkMethod method, final Assignments complete, final Object freshInstance) {
+            return new Statement() {
+                @Override
+                public void evaluate() throws Throwable {
+                    final Object[] values = complete.getMethodArguments();
+                    
+                    if (!nullsOk()) {
+                        Assume.assumeNotNull(values);
+                    }
+                    
+                    method.invokeExplosively(freshInstance, values);
+                }
+            };
+        }
 
-		protected void handleAssumptionViolation(AssumptionViolatedException e) {
-			fInvalidParameters.add(e);
-		}
+        protected void handleAssumptionViolation(AssumptionViolatedException e) {
+            fInvalidParameters.add(e);
+        }
 
-		protected void reportParameterizedError(Throwable e, Object... params)
-				throws Throwable {
-			if (params.length == 0)
-				throw e;
-			throw new ParameterizedAssertionError(e, fTestMethod.getName(),
-					params);
-		}
+        protected void reportParameterizedError(Throwable e, Object... params)
+                throws Throwable {
+            if (params.length == 0) {
+                throw e;
+            }
+            throw new ParameterizedAssertionError(e, testMethod.getName(),
+                    params);
+        }
 
-		private boolean nullsOk() {
-			Theory annotation= fTestMethod.getMethod().getAnnotation(
-					Theory.class);
-			if (annotation == null)
-				return false;
-			return annotation.nullsAccepted();
-		}
+        private boolean nullsOk() {
+            Theory annotation = testMethod.getMethod().getAnnotation(
+                    Theory.class);
+            if (annotation == null) {
+                return false;
+            }
+            return annotation.nullsAccepted();
+        }
 
-		protected void handleDataPointSuccess() {
-			successes++;
-		}
-	}
+        protected void handleDataPointSuccess() {
+            successes++;
+        }
+    }
 }
diff --git a/src/main/java/org/junit/experimental/theories/Theory.java b/src/main/java/org/junit/experimental/theories/Theory.java
index 134fe9d..0b9f2c4 100644
--- a/src/main/java/org/junit/experimental/theories/Theory.java
+++ b/src/main/java/org/junit/experimental/theories/Theory.java
@@ -1,12 +1,18 @@
-/**
- * 
- */
 package org.junit.experimental.theories;
 
+import static java.lang.annotation.ElementType.METHOD;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
+/**
+ * Marks test methods that should be read as theories by the {@link org.junit.experimental.theories.Theories Theories} runner.
+ *
+ * @see org.junit.experimental.theories.Theories
+ */
 @Retention(RetentionPolicy.RUNTIME)
+@Target(METHOD)
 public @interface Theory {
-	boolean nullsAccepted() default true;
+    boolean nullsAccepted() default true;
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/theories/internal/AllMembersSupplier.java b/src/main/java/org/junit/experimental/theories/internal/AllMembersSupplier.java
index 615cc3e..f15fb28 100644
--- a/src/main/java/org/junit/experimental/theories/internal/AllMembersSupplier.java
+++ b/src/main/java/org/junit/experimental/theories/internal/AllMembersSupplier.java
@@ -1,19 +1,19 @@
-/**
- * 
- */
 package org.junit.experimental.theories.internal;
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 
+import org.junit.Assume;
 import org.junit.experimental.theories.DataPoint;
 import org.junit.experimental.theories.DataPoints;
 import org.junit.experimental.theories.ParameterSignature;
 import org.junit.experimental.theories.ParameterSupplier;
 import org.junit.experimental.theories.PotentialAssignment;
+import org.junit.runners.model.FrameworkField;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.TestClass;
 
@@ -21,107 +21,184 @@
  * Supplies Theory parameters based on all public members of the target class.
  */
 public class AllMembersSupplier extends ParameterSupplier {
-	static class MethodParameterValue extends PotentialAssignment {
-		private final FrameworkMethod fMethod;
+    static class MethodParameterValue extends PotentialAssignment {
+        private final FrameworkMethod method;
 
-		private MethodParameterValue(FrameworkMethod dataPointMethod) {
-			fMethod= dataPointMethod;
-		}
+        private MethodParameterValue(FrameworkMethod dataPointMethod) {
+            method = dataPointMethod;
+        }
 
-		@Override
-		public Object getValue() throws CouldNotGenerateValueException {
-			try {
-				return fMethod.invokeExplosively(null);
-			} catch (IllegalArgumentException e) {
-				throw new RuntimeException(
-						"unexpected: argument length is checked");
-			} catch (IllegalAccessException e) {
-				throw new RuntimeException(
-						"unexpected: getMethods returned an inaccessible method");
-			} catch (Throwable e) {
-				throw new CouldNotGenerateValueException();
-				// do nothing, just look for more values
-			}
-		}
+        @Override
+        public Object getValue() throws CouldNotGenerateValueException {
+            try {
+                return method.invokeExplosively(null);
+            } catch (IllegalArgumentException e) {
+                throw new RuntimeException(
+                        "unexpected: argument length is checked");
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(
+                        "unexpected: getMethods returned an inaccessible method");
+            } catch (Throwable throwable) {
+                DataPoint annotation = method.getAnnotation(DataPoint.class);
+                Assume.assumeTrue(annotation == null || !isAssignableToAnyOf(annotation.ignoredExceptions(), throwable));
+                
+                throw new CouldNotGenerateValueException(throwable);
+            }
+        }
 
-		@Override
-		public String getDescription() throws CouldNotGenerateValueException {
-			return fMethod.getName();
-		}
-	}
+        @Override
+        public String getDescription() throws CouldNotGenerateValueException {
+            return method.getName();
+        }
+    }
+    
+    private final TestClass clazz;
 
-	private final TestClass fClass;
+    /**
+     * Constructs a new supplier for {@code type}
+     */
+    public AllMembersSupplier(TestClass type) {
+        clazz = type;
+    }
 
-	/**
-	 * Constructs a new supplier for {@code type}
-	 */
-	public AllMembersSupplier(TestClass type) {
-		fClass= type;
-	}
+    @Override
+    public List<PotentialAssignment> getValueSources(ParameterSignature sig) throws Throwable {
+        List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
 
-	@Override
-	public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
-		List<PotentialAssignment> list= new ArrayList<PotentialAssignment>();
+        addSinglePointFields(sig, list);
+        addMultiPointFields(sig, list);
+        addSinglePointMethods(sig, list);
+        addMultiPointMethods(sig, list);
 
-		addFields(sig, list);
-		addSinglePointMethods(sig, list);
-		addMultiPointMethods(list);
+        return list;
+    }
 
-		return list;
-	}
+    private void addMultiPointMethods(ParameterSignature sig, List<PotentialAssignment> list) throws Throwable {
+        for (FrameworkMethod dataPointsMethod : getDataPointsMethods(sig)) {
+            Class<?> returnType = dataPointsMethod.getReturnType();
+            
+            if ((returnType.isArray() && sig.canPotentiallyAcceptType(returnType.getComponentType())) ||
+                    Iterable.class.isAssignableFrom(returnType)) {
+                try {
+                    addDataPointsValues(returnType, sig, dataPointsMethod.getName(), list, 
+                            dataPointsMethod.invokeExplosively(null));
+                } catch (Throwable throwable) {
+                    DataPoints annotation = dataPointsMethod.getAnnotation(DataPoints.class);
+                    if (annotation != null && isAssignableToAnyOf(annotation.ignoredExceptions(), throwable)) {
+                        return;
+                    } else {
+                        throw throwable;
+                    }
+                }
+            }
+        }
+    }
 
-	private void addMultiPointMethods(List<PotentialAssignment> list) {
-		for (FrameworkMethod dataPointsMethod : fClass
-				.getAnnotatedMethods(DataPoints.class))
-			try {
-				addArrayValues(dataPointsMethod.getName(), list, dataPointsMethod.invokeExplosively(null));
-			} catch (Throwable e) {
-				// ignore and move on
-			}
-	}
+    private void addSinglePointMethods(ParameterSignature sig, List<PotentialAssignment> list) {
+        for (FrameworkMethod dataPointMethod : getSingleDataPointMethods(sig)) {
+            if (sig.canAcceptType(dataPointMethod.getType())) {
+                list.add(new MethodParameterValue(dataPointMethod));
+            }
+        }
+    }
+    
+    private void addMultiPointFields(ParameterSignature sig, List<PotentialAssignment> list) {
+        for (final Field field : getDataPointsFields(sig)) {
+            Class<?> type = field.getType();
+            addDataPointsValues(type, sig, field.getName(), list, getStaticFieldValue(field));
+        }
+    }
 
-	@SuppressWarnings("deprecation")
-	private void addSinglePointMethods(ParameterSignature sig,
-			List<PotentialAssignment> list) {
-		for (FrameworkMethod dataPointMethod : fClass
-				.getAnnotatedMethods(DataPoint.class)) {
-			Class<?> type= sig.getType();
-			if ((dataPointMethod.producesType(type)))
-				list.add(new MethodParameterValue(dataPointMethod));
-		}
-	}
+    private void addSinglePointFields(ParameterSignature sig, List<PotentialAssignment> list) {
+        for (final Field field : getSingleDataPointFields(sig)) {
+            Object value = getStaticFieldValue(field);
+            
+            if (sig.canAcceptValue(value)) {
+                list.add(PotentialAssignment.forValue(field.getName(), value));
+            }
+        }
+    }
+    
+    private void addDataPointsValues(Class<?> type, ParameterSignature sig, String name, 
+            List<PotentialAssignment> list, Object value) {
+        if (type.isArray()) {
+            addArrayValues(sig, name, list, value);
+        }
+        else if (Iterable.class.isAssignableFrom(type)) {
+            addIterableValues(sig, name, list, (Iterable<?>) value);
+        }
+    }
 
-	private void addFields(ParameterSignature sig,
-			List<PotentialAssignment> list) {
-		for (final Field field : fClass.getJavaClass().getFields()) {
-			if (Modifier.isStatic(field.getModifiers())) {
-				Class<?> type= field.getType();
-				if (sig.canAcceptArrayType(type)
-						&& field.getAnnotation(DataPoints.class) != null) {
-					addArrayValues(field.getName(), list, getStaticFieldValue(field));
-				} else if (sig.canAcceptType(type)
-						&& field.getAnnotation(DataPoint.class) != null) {
-					list.add(PotentialAssignment
-							.forValue(field.getName(), getStaticFieldValue(field)));
-				}
-			}
-		}
-	}
+    private void addArrayValues(ParameterSignature sig, String name, List<PotentialAssignment> list, Object array) {
+        for (int i = 0; i < Array.getLength(array); i++) {
+            Object value = Array.get(array, i);
+            if (sig.canAcceptValue(value)) {
+                list.add(PotentialAssignment.forValue(name + "[" + i + "]", value));
+            }
+        }
+    }
+    
+    private void addIterableValues(ParameterSignature sig, String name, List<PotentialAssignment> list, Iterable<?> iterable) {
+        Iterator<?> iterator = iterable.iterator();
+        int i = 0;
+        while (iterator.hasNext()) {
+            Object value = iterator.next();
+            if (sig.canAcceptValue(value)) {
+                list.add(PotentialAssignment.forValue(name + "[" + i + "]", value));
+            }
+            i += 1;
+        }
+    }
 
-	private void addArrayValues(String name, List<PotentialAssignment> list, Object array) {
-		for (int i= 0; i < Array.getLength(array); i++)
-			list.add(PotentialAssignment.forValue(name + "[" + i + "]", Array.get(array, i)));
-	}
+    private Object getStaticFieldValue(final Field field) {
+        try {
+            return field.get(null);
+        } catch (IllegalArgumentException e) {
+            throw new RuntimeException(
+                    "unexpected: field from getClass doesn't exist on object");
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(
+                    "unexpected: getFields returned an inaccessible field");
+        }
+    }
+    
+    private static boolean isAssignableToAnyOf(Class<?>[] typeArray, Object target) {
+        for (Class<?> type : typeArray) {
+            if (type.isAssignableFrom(target.getClass())) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-	private Object getStaticFieldValue(final Field field) {
-		try {
-			return field.get(null);
-		} catch (IllegalArgumentException e) {
-			throw new RuntimeException(
-					"unexpected: field from getClass doesn't exist on object");
-		} catch (IllegalAccessException e) {
-			throw new RuntimeException(
-					"unexpected: getFields returned an inaccessible field");
-		}
-	}
+    protected Collection<FrameworkMethod> getDataPointsMethods(ParameterSignature sig) {
+        return clazz.getAnnotatedMethods(DataPoints.class);
+    }
+    
+    protected Collection<Field> getSingleDataPointFields(ParameterSignature sig) {
+        List<FrameworkField> fields = clazz.getAnnotatedFields(DataPoint.class);
+        Collection<Field> validFields = new ArrayList<Field>();
+
+        for (FrameworkField frameworkField : fields) {
+            validFields.add(frameworkField.getField());
+        }
+
+        return validFields;
+    }
+    
+    protected Collection<Field> getDataPointsFields(ParameterSignature sig) {
+        List<FrameworkField> fields = clazz.getAnnotatedFields(DataPoints.class);
+        Collection<Field> validFields = new ArrayList<Field>();
+
+        for (FrameworkField frameworkField : fields) {
+            validFields.add(frameworkField.getField());
+        }
+
+        return validFields;
+    }
+    
+    protected Collection<FrameworkMethod> getSingleDataPointMethods(ParameterSignature sig) {
+        return clazz.getAnnotatedMethods(DataPoint.class);
+    }
+
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/theories/internal/Assignments.java b/src/main/java/org/junit/experimental/theories/internal/Assignments.java
index bd94f00..a94c8a5 100644
--- a/src/main/java/org/junit/experimental/theories/internal/Assignments.java
+++ b/src/main/java/org/junit/experimental/theories/internal/Assignments.java
@@ -1,8 +1,8 @@
-/**
- * 
- */
 package org.junit.experimental.theories.internal;
 
+import static java.util.Collections.emptyList;
+
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
@@ -19,115 +19,136 @@
  * parameters
  */
 public class Assignments {
-	private List<PotentialAssignment> fAssigned;
+    private final List<PotentialAssignment> assigned;
 
-	private final List<ParameterSignature> fUnassigned;
+    private final List<ParameterSignature> unassigned;
 
-	private final TestClass fClass;
+    private final TestClass clazz;
 
-	private Assignments(List<PotentialAssignment> assigned,
-			List<ParameterSignature> unassigned, TestClass testClass) {
-		fUnassigned= unassigned;
-		fAssigned= assigned;
-		fClass= testClass;
-	}
+    private Assignments(List<PotentialAssignment> assigned,
+            List<ParameterSignature> unassigned, TestClass clazz) {
+        this.unassigned = unassigned;
+        this.assigned = assigned;
+        this.clazz = clazz;
+    }
 
-	/**
-	 * Returns a new assignment list for {@code testMethod}, with no params
-	 * assigned.
-	 */
-	public static Assignments allUnassigned(Method testMethod,
-			TestClass testClass) throws Exception {
-		List<ParameterSignature> signatures;
-		signatures= ParameterSignature.signatures(testClass
-				.getOnlyConstructor());
-		signatures.addAll(ParameterSignature.signatures(testMethod));
-		return new Assignments(new ArrayList<PotentialAssignment>(),
-				signatures, testClass);
-	}
+    /**
+     * Returns a new assignment list for {@code testMethod}, with no params
+     * assigned.
+     */
+    public static Assignments allUnassigned(Method testMethod,
+            TestClass testClass) {
+        List<ParameterSignature> signatures;
+        signatures = ParameterSignature.signatures(testClass
+                .getOnlyConstructor());
+        signatures.addAll(ParameterSignature.signatures(testMethod));
+        return new Assignments(new ArrayList<PotentialAssignment>(),
+                signatures, testClass);
+    }
 
-	public boolean isComplete() {
-		return fUnassigned.size() == 0;
-	}
+    public boolean isComplete() {
+        return unassigned.size() == 0;
+    }
 
-	public ParameterSignature nextUnassigned() {
-		return fUnassigned.get(0);
-	}
+    public ParameterSignature nextUnassigned() {
+        return unassigned.get(0);
+    }
 
-	public Assignments assignNext(PotentialAssignment source) {
-		List<PotentialAssignment> assigned= new ArrayList<PotentialAssignment>(
-				fAssigned);
-		assigned.add(source);
+    public Assignments assignNext(PotentialAssignment source) {
+        List<PotentialAssignment> assigned = new ArrayList<PotentialAssignment>(
+                this.assigned);
+        assigned.add(source);
 
-		return new Assignments(assigned, fUnassigned.subList(1, fUnassigned
-				.size()), fClass);
-	}
+        return new Assignments(assigned, unassigned.subList(1,
+                unassigned.size()), clazz);
+    }
 
-	public Object[] getActualValues(int start, int stop, boolean nullsOk)
-			throws CouldNotGenerateValueException {
-		Object[] values= new Object[stop - start];
-		for (int i= start; i < stop; i++) {
-			Object value= fAssigned.get(i).getValue();
-			if (value == null && !nullsOk)
-				throw new CouldNotGenerateValueException();
-			values[i - start]= value;
-		}
-		return values;
-	}
+    public Object[] getActualValues(int start, int stop) 
+            throws CouldNotGenerateValueException {
+        Object[] values = new Object[stop - start];
+        for (int i = start; i < stop; i++) {
+            values[i - start] = assigned.get(i).getValue();
+        }
+        return values;
+    }
 
-	public List<PotentialAssignment> potentialsForNextUnassigned()
-			throws InstantiationException, IllegalAccessException {
-		ParameterSignature unassigned= nextUnassigned();
-		return getSupplier(unassigned).getValueSources(unassigned);
-	}
+    public List<PotentialAssignment> potentialsForNextUnassigned()
+            throws Throwable {
+        ParameterSignature unassigned = nextUnassigned();
+        List<PotentialAssignment> assignments = getSupplier(unassigned).getValueSources(unassigned);
+        
+        if (assignments.size() == 0) {
+            assignments = generateAssignmentsFromTypeAlone(unassigned);
+        }
+        
+        return assignments;
+    }
 
-	public ParameterSupplier getSupplier(ParameterSignature unassigned)
-			throws InstantiationException, IllegalAccessException {
-		ParameterSupplier supplier= getAnnotatedSupplier(unassigned);
-		if (supplier != null)
-			return supplier;
+    private List<PotentialAssignment> generateAssignmentsFromTypeAlone(ParameterSignature unassigned) {
+        Class<?> paramType = unassigned.getType();
+        
+        if (paramType.isEnum()) {
+            return new EnumSupplier(paramType).getValueSources(unassigned);  
+        } else if (paramType.equals(Boolean.class) || paramType.equals(boolean.class)) {
+            return new BooleanSupplier().getValueSources(unassigned);
+        } else {
+            return emptyList();
+        }
+    }
 
-		return new AllMembersSupplier(fClass);
-	}
+    private ParameterSupplier getSupplier(ParameterSignature unassigned)
+            throws Exception {
+        ParametersSuppliedBy annotation = unassigned
+                .findDeepAnnotation(ParametersSuppliedBy.class);
+        
+        if (annotation != null) {
+            return buildParameterSupplierFromClass(annotation.value());
+        } else {
+            return new AllMembersSupplier(clazz);
+        }
+    }
 
-	public ParameterSupplier getAnnotatedSupplier(ParameterSignature unassigned)
-			throws InstantiationException, IllegalAccessException {
-		ParametersSuppliedBy annotation= unassigned
-				.findDeepAnnotation(ParametersSuppliedBy.class);
-		if (annotation == null)
-			return null;
-		return annotation.value().newInstance();
-	}
+    private ParameterSupplier buildParameterSupplierFromClass(
+            Class<? extends ParameterSupplier> cls) throws Exception {
+        Constructor<?>[] supplierConstructors = cls.getConstructors();
 
-	public Object[] getConstructorArguments(boolean nullsOk)
-			throws CouldNotGenerateValueException {
-		return getActualValues(0, getConstructorParameterCount(), nullsOk);
-	}
+        for (Constructor<?> constructor : supplierConstructors) {
+            Class<?>[] parameterTypes = constructor.getParameterTypes();
+            if (parameterTypes.length == 1
+                    && parameterTypes[0].equals(TestClass.class)) {
+                return (ParameterSupplier) constructor.newInstance(clazz);
+            }
+        }
 
-	public Object[] getMethodArguments(boolean nullsOk)
-			throws CouldNotGenerateValueException {
-		return getActualValues(getConstructorParameterCount(),
-				fAssigned.size(), nullsOk);
-	}
+        return cls.newInstance();
+    }
 
-	public Object[] getAllArguments(boolean nullsOk)
-			throws CouldNotGenerateValueException {
-		return getActualValues(0, fAssigned.size(), nullsOk);
-	}
+    public Object[] getConstructorArguments()
+            throws CouldNotGenerateValueException {
+        return getActualValues(0, getConstructorParameterCount());
+    }
 
-	private int getConstructorParameterCount() {
-		List<ParameterSignature> signatures= ParameterSignature
-				.signatures(fClass.getOnlyConstructor());
-		int constructorParameterCount= signatures.size();
-		return constructorParameterCount;
-	}
+    public Object[] getMethodArguments() throws CouldNotGenerateValueException {
+        return getActualValues(getConstructorParameterCount(), assigned.size());
+    }
 
-	public Object[] getArgumentStrings(boolean nullsOk)
-			throws CouldNotGenerateValueException {
-		Object[] values= new Object[fAssigned.size()];
-		for (int i= 0; i < values.length; i++) {
-			values[i]= fAssigned.get(i).getDescription();
-		}
-		return values;
-	}
+    public Object[] getAllArguments() throws CouldNotGenerateValueException {
+        return getActualValues(0, assigned.size());
+    }
+
+    private int getConstructorParameterCount() {
+        List<ParameterSignature> signatures = ParameterSignature
+                .signatures(clazz.getOnlyConstructor());
+        int constructorParameterCount = signatures.size();
+        return constructorParameterCount;
+    }
+
+    public Object[] getArgumentStrings(boolean nullsOk)
+            throws CouldNotGenerateValueException {
+        Object[] values = new Object[assigned.size()];
+        for (int i = 0; i < values.length; i++) {
+            values[i] = assigned.get(i).getDescription();
+        }
+        return values;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/theories/internal/BooleanSupplier.java b/src/main/java/org/junit/experimental/theories/internal/BooleanSupplier.java
new file mode 100644
index 0000000..5f7032f
--- /dev/null
+++ b/src/main/java/org/junit/experimental/theories/internal/BooleanSupplier.java
@@ -0,0 +1,18 @@
+package org.junit.experimental.theories.internal;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.experimental.theories.ParameterSignature;
+import org.junit.experimental.theories.ParameterSupplier;
+import org.junit.experimental.theories.PotentialAssignment;
+
+public class BooleanSupplier extends ParameterSupplier {
+
+    @Override
+    public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
+        return Arrays.asList(PotentialAssignment.forValue("true", true),
+                             PotentialAssignment.forValue("false", false));
+    }
+
+}
diff --git a/src/main/java/org/junit/experimental/theories/internal/EnumSupplier.java b/src/main/java/org/junit/experimental/theories/internal/EnumSupplier.java
new file mode 100644
index 0000000..1f3ab90
--- /dev/null
+++ b/src/main/java/org/junit/experimental/theories/internal/EnumSupplier.java
@@ -0,0 +1,30 @@
+package org.junit.experimental.theories.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.experimental.theories.ParameterSignature;
+import org.junit.experimental.theories.ParameterSupplier;
+import org.junit.experimental.theories.PotentialAssignment;
+
+public class EnumSupplier extends ParameterSupplier {
+
+    private Class<?> enumType;
+
+    public EnumSupplier(Class<?> enumType) {
+        this.enumType = enumType;
+    }
+
+    @Override
+    public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
+        Object[] enumValues = enumType.getEnumConstants();
+        
+        List<PotentialAssignment> assignments = new ArrayList<PotentialAssignment>();
+        for (Object value : enumValues) {
+            assignments.add(PotentialAssignment.forValue(value.toString(), value));
+        }
+        
+        return assignments;
+    }
+
+}
diff --git a/src/main/java/org/junit/experimental/theories/internal/ParameterizedAssertionError.java b/src/main/java/org/junit/experimental/theories/internal/ParameterizedAssertionError.java
index 285bc3a..5b9e947 100644
--- a/src/main/java/org/junit/experimental/theories/internal/ParameterizedAssertionError.java
+++ b/src/main/java/org/junit/experimental/theories/internal/ParameterizedAssertionError.java
@@ -1,49 +1,50 @@
-/**
- * 
- */
 package org.junit.experimental.theories.internal;
 
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 
+public class ParameterizedAssertionError extends AssertionError {
+    private static final long serialVersionUID = 1L;
 
-public class ParameterizedAssertionError extends RuntimeException {
-	private static final long serialVersionUID = 1L;
+    public ParameterizedAssertionError(Throwable targetException,
+            String methodName, Object... params) {
+        super(String.format("%s(%s)", methodName, join(", ", params)));
+        this.initCause(targetException);
+    }
 
-	public ParameterizedAssertionError(Throwable targetException,
-			String methodName, Object... params) {
-		super(String.format("%s(%s)", methodName, join(", ", params)),
-				targetException);
-	}
+    @Override
+    public boolean equals(Object obj) {
+        return obj instanceof ParameterizedAssertionError && toString().equals(obj.toString());
+    }
 
-	@Override public boolean equals(Object obj) {
-		return toString().equals(obj.toString());
-	}
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
 
-	public static String join(String delimiter, Object... params) {
-		return join(delimiter, Arrays.asList(params));
-	}
+    public static String join(String delimiter, Object... params) {
+        return join(delimiter, Arrays.asList(params));
+    }
 
-	public static String join(String delimiter,
-			Collection<Object> values) {
-		StringBuffer buffer = new StringBuffer();
-		Iterator<Object> iter = values.iterator();
-		while (iter.hasNext()) {
-			Object next = iter.next();
-			buffer.append(stringValueOf(next));
-			if (iter.hasNext()) {
-				buffer.append(delimiter);
-			}
-		}
-		return buffer.toString();
-	}
+    public static String join(String delimiter, Collection<Object> values) {
+        StringBuilder sb = new StringBuilder();
+        Iterator<Object> iter = values.iterator();
+        while (iter.hasNext()) {
+            Object next = iter.next();
+            sb.append(stringValueOf(next));
+            if (iter.hasNext()) {
+                sb.append(delimiter);
+            }
+        }
+        return sb.toString();
+    }
 
-	private static String stringValueOf(Object next) {
-		try {
-			return String.valueOf(next);
-		} catch (Throwable e) {
-			return "[toString failed]";
-		}
-	}
+    private static String stringValueOf(Object next) {
+        try {
+            return String.valueOf(next);
+        } catch (Throwable e) {
+            return "[toString failed]";
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/experimental/theories/internal/SpecificDataPointsSupplier.java b/src/main/java/org/junit/experimental/theories/internal/SpecificDataPointsSupplier.java
new file mode 100644
index 0000000..7b571e3
--- /dev/null
+++ b/src/main/java/org/junit/experimental/theories/internal/SpecificDataPointsSupplier.java
@@ -0,0 +1,90 @@
+package org.junit.experimental.theories.internal;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.experimental.theories.DataPoint;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.FromDataPoints;
+import org.junit.experimental.theories.ParameterSignature;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.TestClass;
+
+public class SpecificDataPointsSupplier extends AllMembersSupplier {
+
+    public SpecificDataPointsSupplier(TestClass testClass) {
+        super(testClass);
+    }
+    
+    @Override
+    protected Collection<Field> getSingleDataPointFields(ParameterSignature sig) {
+        Collection<Field> fields = super.getSingleDataPointFields(sig);        
+        String requestedName = sig.getAnnotation(FromDataPoints.class).value();
+
+        List<Field> fieldsWithMatchingNames = new ArrayList<Field>();
+        
+        for (Field field : fields) {
+            String[] fieldNames = field.getAnnotation(DataPoint.class).value();
+            if (Arrays.asList(fieldNames).contains(requestedName)) {
+                fieldsWithMatchingNames.add(field);
+            }
+        }
+        
+        return fieldsWithMatchingNames;
+    }
+    
+    @Override
+    protected Collection<Field> getDataPointsFields(ParameterSignature sig) {
+        Collection<Field> fields = super.getDataPointsFields(sig);        
+        String requestedName = sig.getAnnotation(FromDataPoints.class).value();
+        
+        List<Field> fieldsWithMatchingNames = new ArrayList<Field>();
+        
+        for (Field field : fields) {
+            String[] fieldNames = field.getAnnotation(DataPoints.class).value();
+            if (Arrays.asList(fieldNames).contains(requestedName)) {
+                fieldsWithMatchingNames.add(field);
+            }
+        }
+        
+        return fieldsWithMatchingNames;
+    }
+    
+    @Override
+    protected Collection<FrameworkMethod> getSingleDataPointMethods(ParameterSignature sig) {
+        Collection<FrameworkMethod> methods = super.getSingleDataPointMethods(sig);
+        String requestedName = sig.getAnnotation(FromDataPoints.class).value();
+        
+        List<FrameworkMethod> methodsWithMatchingNames = new ArrayList<FrameworkMethod>();
+        
+        for (FrameworkMethod method : methods) {
+            String[] methodNames = method.getAnnotation(DataPoint.class).value();
+            if (Arrays.asList(methodNames).contains(requestedName)) {
+                methodsWithMatchingNames.add(method);
+            }
+        }
+        
+        return methodsWithMatchingNames;
+    }
+    
+    @Override
+    protected Collection<FrameworkMethod> getDataPointsMethods(ParameterSignature sig) {
+        Collection<FrameworkMethod> methods = super.getDataPointsMethods(sig);
+        String requestedName = sig.getAnnotation(FromDataPoints.class).value();
+        
+        List<FrameworkMethod> methodsWithMatchingNames = new ArrayList<FrameworkMethod>();
+        
+        for (FrameworkMethod method : methods) {
+            String[] methodNames = method.getAnnotation(DataPoints.class).value();
+            if (Arrays.asList(methodNames).contains(requestedName)) {
+                methodsWithMatchingNames.add(method);
+            }
+        }
+        
+        return methodsWithMatchingNames;
+    }
+
+}
diff --git a/src/main/java/org/junit/experimental/theories/suppliers/TestedOn.java b/src/main/java/org/junit/experimental/theories/suppliers/TestedOn.java
index d6ede64..a19f20a 100644
--- a/src/main/java/org/junit/experimental/theories/suppliers/TestedOn.java
+++ b/src/main/java/org/junit/experimental/theories/suppliers/TestedOn.java
@@ -1,13 +1,31 @@
 package org.junit.experimental.theories.suppliers;
 
+import static java.lang.annotation.ElementType.PARAMETER;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 import org.junit.experimental.theories.ParametersSuppliedBy;
 
-
+/**
+ * Annotating a {@link org.junit.experimental.theories.Theory Theory} method int
+ * parameter with &#064;TestedOn causes it to be supplied with values from the
+ * ints array given when run as a theory by the
+ * {@link org.junit.experimental.theories.Theories Theories} runner. For
+ * example, the below method would be called three times by the Theories runner,
+ * once with each of the int parameters specified.
+ * 
+ * <pre>
+ * &#064;Theory
+ * public void shouldPassForSomeInts(&#064;TestedOn(ints={1, 2, 3}) int param) {
+ *     ...
+ * }
+ * </pre>
+ */
 @ParametersSuppliedBy(TestedOnSupplier.class)
 @Retention(RetentionPolicy.RUNTIME)
+@Target(PARAMETER)
 public @interface TestedOn {
-	int[] ints();
+    int[] ints();
 }
diff --git a/src/main/java/org/junit/experimental/theories/suppliers/TestedOnSupplier.java b/src/main/java/org/junit/experimental/theories/suppliers/TestedOnSupplier.java
index f80298f..dc3d0c9 100644
--- a/src/main/java/org/junit/experimental/theories/suppliers/TestedOnSupplier.java
+++ b/src/main/java/org/junit/experimental/theories/suppliers/TestedOnSupplier.java
@@ -1,23 +1,25 @@
 package org.junit.experimental.theories.suppliers;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import org.junit.experimental.theories.ParameterSignature;
 import org.junit.experimental.theories.ParameterSupplier;
 import org.junit.experimental.theories.PotentialAssignment;
 
-
-
+/**
+ * @see org.junit.experimental.theories.suppliers.TestedOn
+ * @see org.junit.experimental.theories.ParameterSupplier
+ */
 public class TestedOnSupplier extends ParameterSupplier {
-	@Override public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
-		List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
-		TestedOn testedOn = sig.getAnnotation(TestedOn.class);
-		int[] ints = testedOn.ints();
-		for (final int i : ints) {
-			list.add(PotentialAssignment.forValue(Arrays.asList(ints).toString(), i));
-		}
-		return list;
-	}
+    @Override
+    public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
+        List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
+        TestedOn testedOn = sig.getAnnotation(TestedOn.class);
+        int[] ints = testedOn.ints();
+        for (final int i : ints) {
+            list.add(PotentialAssignment.forValue("ints", i));
+        }
+        return list;
+    }
 }
diff --git a/src/main/java/org/junit/internal/ArrayComparisonFailure.java b/src/main/java/org/junit/internal/ArrayComparisonFailure.java
index 08851de..8627d6e 100644
--- a/src/main/java/org/junit/internal/ArrayComparisonFailure.java
+++ b/src/main/java/org/junit/internal/ArrayComparisonFailure.java
@@ -7,53 +7,61 @@
 
 /**
  * Thrown when two array elements differ
+ *
  * @see Assert#assertArrayEquals(String, Object[], Object[])
  */
 public class ArrayComparisonFailure extends AssertionError {
 
-	private static final long serialVersionUID= 1L;
-	
-	private List<Integer> fIndices= new ArrayList<Integer>();
-	private final String fMessage;
-	private final AssertionError fCause;
+    private static final long serialVersionUID = 1L;
 
-	/**
-	 * Construct a new <code>ArrayComparisonFailure</code> with an error text and the array's
-	 * dimension that was not equal
-	 * @param cause the exception that caused the array's content to fail the assertion test 
-	 * @param index the array position of the objects that are not equal.
-	 * @see Assert#assertArrayEquals(String, Object[], Object[])
-	 */
-	public ArrayComparisonFailure(String message, AssertionError cause, int index) {
-		fMessage= message;
-		fCause= cause;
-		addDimension(index);
-	}
+    /*
+     * We have to use the f prefix until the next major release to ensure
+     * serialization compatibility. 
+     * See https://github.com/junit-team/junit/issues/976
+     */
+    private final List<Integer> fIndices = new ArrayList<Integer>();
+    private final String fMessage;
 
-	public void addDimension(int index) {
-		fIndices.add(0, index);
-	}
+    /**
+     * Construct a new <code>ArrayComparisonFailure</code> with an error text and the array's
+     * dimension that was not equal
+     *
+     * @param cause the exception that caused the array's content to fail the assertion test
+     * @param index the array position of the objects that are not equal.
+     * @see Assert#assertArrayEquals(String, Object[], Object[])
+     */
+    public ArrayComparisonFailure(String message, AssertionError cause, int index) {
+        this.fMessage = message;
+        initCause(cause);
+        addDimension(index);
+    }
 
-	@Override
-	public String getMessage() {
-		StringBuilder builder= new StringBuilder();
-		if (fMessage != null)
-			builder.append(fMessage);
-		builder.append("arrays first differed at element ");
-		for (int each : fIndices) {
-			builder.append("[");
-			builder.append(each);
-			builder.append("]");
-		}
-		builder.append("; ");
-		builder.append(fCause.getMessage());
-		return builder.toString();
-	}
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override public String toString() {
-		return getMessage();
-	}
+    public void addDimension(int index) {
+        fIndices.add(0, index);
+    }
+
+    @Override
+    public String getMessage() {
+        StringBuilder sb = new StringBuilder();
+        if (fMessage != null) {
+            sb.append(fMessage);
+        }
+        sb.append("arrays first differed at element ");
+        for (int each : fIndices) {
+            sb.append("[");
+            sb.append(each);
+            sb.append("]");
+        }
+        sb.append("; ");
+        sb.append(getCause().getMessage());
+        return sb.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return getMessage();
+    }
 }
diff --git a/src/main/java/org/junit/internal/AssumptionViolatedException.java b/src/main/java/org/junit/internal/AssumptionViolatedException.java
index 8e11268..880d73f 100644
--- a/src/main/java/org/junit/internal/AssumptionViolatedException.java
+++ b/src/main/java/org/junit/internal/AssumptionViolatedException.java
@@ -5,36 +5,107 @@
 import org.hamcrest.SelfDescribing;
 import org.hamcrest.StringDescription;
 
+/**
+ * An exception class used to implement <i>assumptions</i> (state in which a given test
+ * is meaningful and should or should not be executed). A test for which an assumption
+ * fails should not generate a test case failure.
+ *
+ * @see org.junit.Assume
+ */
 public class AssumptionViolatedException extends RuntimeException implements SelfDescribing {
-	private static final long serialVersionUID= 1L;
+    private static final long serialVersionUID = 2L;
 
-	private final Object fValue;
+    /*
+     * We have to use the f prefix until the next major release to ensure
+     * serialization compatibility. 
+     * See https://github.com/junit-team/junit/issues/976
+     */
+    private final String fAssumption;
+    private final boolean fValueMatcher;
+    private final Object fValue;
+    private final Matcher<?> fMatcher;
 
-	private final Matcher<?> fMatcher;
+    /**
+     * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead.
+     */
+    @Deprecated
+    public AssumptionViolatedException(String assumption, boolean hasValue, Object value, Matcher<?> matcher) {
+        this.fAssumption = assumption;
+        this.fValue = value;
+        this.fMatcher = matcher;
+        this.fValueMatcher = hasValue;
 
-	public AssumptionViolatedException(Object value, Matcher<?> matcher) {
-		super(value instanceof Throwable ? (Throwable) value : null);
-		fValue= value;
-		fMatcher= matcher;
-	}
-	
-	public AssumptionViolatedException(String assumption) {
-		this(assumption, null);
-	}
+        if (value instanceof Throwable) {
+          initCause((Throwable) value);
+        }
+    }
 
-	@Override
-	public String getMessage() {
-		return StringDescription.asString(this);
-	}
+    /**
+     * An assumption exception with the given <i>value</i> (String or
+     * Throwable) and an additional failing {@link Matcher}.
+     *
+     * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead.
+     */
+    @Deprecated
+    public AssumptionViolatedException(Object value, Matcher<?> matcher) {
+        this(null, true, value, matcher);
+    }
 
-	public void describeTo(Description description) {
-		if (fMatcher != null) {
-			description.appendText("got: ");
-			description.appendValue(fValue);
-			description.appendText(", expected: ");
-			description.appendDescriptionOf(fMatcher);
-		} else {
-			description.appendText("failed assumption: " + fValue);
-		}
-	}
-}
\ No newline at end of file
+    /**
+     * An assumption exception with the given <i>value</i> (String or
+     * Throwable) and an additional failing {@link Matcher}.
+     *
+     * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead.
+     */
+    @Deprecated
+    public AssumptionViolatedException(String assumption, Object value, Matcher<?> matcher) {
+        this(assumption, true, value, matcher);
+    }
+
+    /**
+     * An assumption exception with the given message only.
+     *
+     * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead.
+     */
+    @Deprecated
+    public AssumptionViolatedException(String assumption) {
+        this(assumption, false, null, null);
+    }
+
+    /**
+     * An assumption exception with the given message and a cause.
+     *
+     * @deprecated Please use {@link org.junit.AssumptionViolatedException} instead.
+     */
+    @Deprecated
+    public AssumptionViolatedException(String assumption, Throwable e) {
+        this(assumption, false, null, null);
+        initCause(e);
+    }
+
+    @Override
+    public String getMessage() {
+        return StringDescription.asString(this);
+    }
+
+    public void describeTo(Description description) {
+        if (fAssumption != null) {
+            description.appendText(fAssumption);
+        }
+
+        if (fValueMatcher) {
+            // a value was passed in when this instance was constructed; print it
+            if (fAssumption != null) {
+                description.appendText(": ");
+            }
+
+            description.appendText("got: ");
+            description.appendValue(fValue);
+
+            if (fMatcher != null) {
+                description.appendText(", expected: ");
+                description.appendDescriptionOf(fMatcher);
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/junit/internal/Classes.java b/src/main/java/org/junit/internal/Classes.java
new file mode 100644
index 0000000..154603d
--- /dev/null
+++ b/src/main/java/org/junit/internal/Classes.java
@@ -0,0 +1,18 @@
+package org.junit.internal;

+

+import static java.lang.Thread.currentThread;

+

+/**

+ * Miscellaneous functions dealing with classes.

+ */

+public class Classes {

+    /**

+     * Returns Class.forName for {@code className} using the current thread's class loader.

+     *

+     * @param className Name of the class.

+     * @throws ClassNotFoundException

+     */

+    public static Class<?> getClass(String className) throws ClassNotFoundException {

+        return Class.forName(className, true, currentThread().getContextClassLoader());

+    }

+}

diff --git a/src/main/java/org/junit/internal/ComparisonCriteria.java b/src/main/java/org/junit/internal/ComparisonCriteria.java
index e97011d..e6d49a4 100644
--- a/src/main/java/org/junit/internal/ComparisonCriteria.java
+++ b/src/main/java/org/junit/internal/ComparisonCriteria.java
@@ -1,6 +1,7 @@
 package org.junit.internal;
 
 import java.lang.reflect.Array;
+import java.util.Arrays;
 
 import org.junit.Assert;
 
@@ -9,68 +10,74 @@
  * may demand exact equality, or, for example, equality within a given delta.
  */
 public abstract class ComparisonCriteria {
-	/**
-	 * Asserts that two arrays are equal, according to the criteria defined by
-	 * the concrete subclass. If they are not, an {@link AssertionError} is
-	 * thrown with the given message. If <code>expecteds</code> and
-	 * <code>actuals</code> are <code>null</code>, they are considered equal.
-	 * 
-	 * @param message
-	 *            the identifying message for the {@link AssertionError} (
-	 *            <code>null</code> okay)
-	 * @param expecteds
-	 *            Object array or array of arrays (multi-dimensional array) with
-	 *            expected values.
-	 * @param actuals
-	 *            Object array or array of arrays (multi-dimensional array) with
-	 *            actual values
-	 */
-	public void arrayEquals(String message, Object expecteds, Object actuals)
-			throws ArrayComparisonFailure {
-		if (expecteds == actuals)
-			return;
-		String header= message == null ? "" : message + ": ";
+    /**
+     * Asserts that two arrays are equal, according to the criteria defined by
+     * the concrete subclass. If they are not, an {@link AssertionError} is
+     * thrown with the given message. If <code>expecteds</code> and
+     * <code>actuals</code> are <code>null</code>, they are considered equal.
+     *
+     * @param message the identifying message for the {@link AssertionError} (
+     * <code>null</code> okay)
+     * @param expecteds Object array or array of arrays (multi-dimensional array) with
+     * expected values.
+     * @param actuals Object array or array of arrays (multi-dimensional array) with
+     * actual values
+     */
+    public void arrayEquals(String message, Object expecteds, Object actuals)
+            throws ArrayComparisonFailure {
+        if (expecteds == actuals
+            || Arrays.deepEquals(new Object[] {expecteds}, new Object[] {actuals})) {
+            // The reflection-based loop below is potentially very slow, especially for primitive
+            // arrays. The deepEquals check allows us to circumvent it in the usual case where
+            // the arrays are exactly equal.
+            return;
+        }
+        String header = message == null ? "" : message + ": ";
 
-		int expectedsLength= assertArraysAreSameLength(expecteds,
-				actuals, header);
+        int expectedsLength = assertArraysAreSameLength(expecteds,
+                actuals, header);
 
-		for (int i= 0; i < expectedsLength; i++) {
-			Object expected= Array.get(expecteds, i);
-			Object actual= Array.get(actuals, i);
+        for (int i = 0; i < expectedsLength; i++) {
+            Object expected = Array.get(expecteds, i);
+            Object actual = Array.get(actuals, i);
 
-			if (isArray(expected) && isArray(actual)) {
-				try {
-					arrayEquals(message, expected, actual);
-				} catch (ArrayComparisonFailure e) {
-					e.addDimension(i);
-					throw e;
-				}
-			} else
-				try {
-					assertElementsEqual(expected, actual);
-				} catch (AssertionError e) {
-					throw new ArrayComparisonFailure(header, e, i);
-				}
-		}
-	}
+            if (isArray(expected) && isArray(actual)) {
+                try {
+                    arrayEquals(message, expected, actual);
+                } catch (ArrayComparisonFailure e) {
+                    e.addDimension(i);
+                    throw e;
+                }
+            } else {
+                try {
+                    assertElementsEqual(expected, actual);
+                } catch (AssertionError e) {
+                    throw new ArrayComparisonFailure(header, e, i);
+                }
+            }
+        }
+    }
 
-	private boolean isArray(Object expected) {
-		return expected != null && expected.getClass().isArray();
-	}
+    private boolean isArray(Object expected) {
+        return expected != null && expected.getClass().isArray();
+    }
 
-	private int assertArraysAreSameLength(Object expecteds,
-			Object actuals, String header) {
-		if (expecteds == null)
-			Assert.fail(header + "expected array was null");
-		if (actuals == null)
-			Assert.fail(header + "actual array was null");
-		int actualsLength= Array.getLength(actuals);
-		int expectedsLength= Array.getLength(expecteds);
-		if (actualsLength != expectedsLength)
-			Assert.fail(header + "array lengths differed, expected.length="
-					+ expectedsLength + " actual.length=" + actualsLength);
-		return expectedsLength;
-	}
+    private int assertArraysAreSameLength(Object expecteds,
+            Object actuals, String header) {
+        if (expecteds == null) {
+            Assert.fail(header + "expected array was null");
+        }
+        if (actuals == null) {
+            Assert.fail(header + "actual array was null");
+        }
+        int actualsLength = Array.getLength(actuals);
+        int expectedsLength = Array.getLength(expecteds);
+        if (actualsLength != expectedsLength) {
+            Assert.fail(header + "array lengths differed, expected.length="
+                    + expectedsLength + " actual.length=" + actualsLength);
+        }
+        return expectedsLength;
+    }
 
-	protected abstract void assertElementsEqual(Object expected, Object actual);
+    protected abstract void assertElementsEqual(Object expected, Object actual);
 }
diff --git a/src/main/java/org/junit/internal/ExactComparisonCriteria.java b/src/main/java/org/junit/internal/ExactComparisonCriteria.java
index 0a632ff..a267f7f 100644
--- a/src/main/java/org/junit/internal/ExactComparisonCriteria.java
+++ b/src/main/java/org/junit/internal/ExactComparisonCriteria.java
@@ -3,8 +3,8 @@
 import org.junit.Assert;
 
 public class ExactComparisonCriteria extends ComparisonCriteria {
-	@Override
-	protected void assertElementsEqual(Object expected, Object actual) {
-		Assert.assertEquals(expected, actual);
-	}
+    @Override
+    protected void assertElementsEqual(Object expected, Object actual) {
+        Assert.assertEquals(expected, actual);
+    }
 }
diff --git a/src/main/java/org/junit/internal/InexactComparisonCriteria.java b/src/main/java/org/junit/internal/InexactComparisonCriteria.java
index ef3d7ff..16e804b 100644
--- a/src/main/java/org/junit/internal/InexactComparisonCriteria.java
+++ b/src/main/java/org/junit/internal/InexactComparisonCriteria.java
@@ -3,17 +3,22 @@
 import org.junit.Assert;
 
 public class InexactComparisonCriteria extends ComparisonCriteria {
-	public double fDelta;
+    public Object fDelta;
 
-	public InexactComparisonCriteria(double delta) {
-		fDelta= delta;
-	}
+    public InexactComparisonCriteria(double delta) {
+        fDelta = delta;
+    }
 
-	@Override
-	protected void assertElementsEqual(Object expected, Object actual) {
-		if (expected instanceof Double)
-			Assert.assertEquals((Double)expected, (Double)actual, fDelta);
-		else
-			Assert.assertEquals((Float)expected, (Float)actual, fDelta);
-	}
+    public InexactComparisonCriteria(float delta) {
+        fDelta = delta;
+    }
+
+    @Override
+    protected void assertElementsEqual(Object expected, Object actual) {
+        if (expected instanceof Double) {
+            Assert.assertEquals((Double) expected, (Double) actual, (Double) fDelta);
+        } else {
+            Assert.assertEquals((Float) expected, (Float) actual, (Float) fDelta);
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/JUnitSystem.java b/src/main/java/org/junit/internal/JUnitSystem.java
index 6d9c242..cf0f2c0 100644
--- a/src/main/java/org/junit/internal/JUnitSystem.java
+++ b/src/main/java/org/junit/internal/JUnitSystem.java
@@ -3,6 +3,12 @@
 import java.io.PrintStream;
 
 public interface JUnitSystem {
-	void exit(int i);
-	PrintStream out();
+
+    /**
+     * Will be removed in the next major release
+     */
+    @Deprecated
+    void exit(int code);
+
+    PrintStream out();
 }
diff --git a/src/main/java/org/junit/internal/MethodSorter.java b/src/main/java/org/junit/internal/MethodSorter.java
new file mode 100644
index 0000000..d8e661a
--- /dev/null
+++ b/src/main/java/org/junit/internal/MethodSorter.java
@@ -0,0 +1,72 @@
+package org.junit.internal;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.junit.FixMethodOrder;
+
+public class MethodSorter {
+    /**
+     * DEFAULT sort order
+     */
+    public static final Comparator<Method> DEFAULT = new Comparator<Method>() {
+        public int compare(Method m1, Method m2) {
+            int i1 = m1.getName().hashCode();
+            int i2 = m2.getName().hashCode();
+            if (i1 != i2) {
+                return i1 < i2 ? -1 : 1;
+            }
+            return NAME_ASCENDING.compare(m1, m2);
+        }
+    };
+
+    /**
+     * Method name ascending lexicographic sort order, with {@link Method#toString()} as a tiebreaker
+     */
+    public static final Comparator<Method> NAME_ASCENDING = new Comparator<Method>() {
+        public int compare(Method m1, Method m2) {
+            final int comparison = m1.getName().compareTo(m2.getName());
+            if (comparison != 0) {
+                return comparison;
+            }
+            return m1.toString().compareTo(m2.toString());
+        }
+    };
+
+    /**
+     * Gets declared methods of a class in a predictable order, unless @FixMethodOrder(MethodSorters.JVM) is specified.
+     *
+     * Using the JVM order is unwise since the Java platform does not
+     * specify any particular order, and in fact JDK 7 returns a more or less
+     * random order; well-written test code would not assume any order, but some
+     * does, and a predictable failure is better than a random failure on
+     * certain platforms. By default, uses an unspecified but deterministic order.
+     *
+     * @param clazz a class
+     * @return same as {@link Class#getDeclaredMethods} but sorted
+     * @see <a href="http://bugs.sun.com/view_bug.do?bug_id=7023180">JDK
+     *      (non-)bug #7023180</a>
+     */
+    public static Method[] getDeclaredMethods(Class<?> clazz) {
+        Comparator<Method> comparator = getSorter(clazz.getAnnotation(FixMethodOrder.class));
+
+        Method[] methods = clazz.getDeclaredMethods();
+        if (comparator != null) {
+            Arrays.sort(methods, comparator);
+        }
+
+        return methods;
+    }
+
+    private MethodSorter() {
+    }
+
+    private static Comparator<Method> getSorter(FixMethodOrder fixMethodOrder) {
+        if (fixMethodOrder == null) {
+            return DEFAULT;
+        }
+
+        return fixMethodOrder.value().getComparator();
+    }
+}
diff --git a/src/main/java/org/junit/internal/RealSystem.java b/src/main/java/org/junit/internal/RealSystem.java
index 1067c6d..e64e1fe 100644
--- a/src/main/java/org/junit/internal/RealSystem.java
+++ b/src/main/java/org/junit/internal/RealSystem.java
@@ -4,12 +4,16 @@
 
 public class RealSystem implements JUnitSystem {
 
-	public void exit(int code) {
-		System.exit(code);
-	}
+    /**
+     * Will be removed in the next major release
+     */
+    @Deprecated
+    public void exit(int code) {
+        System.exit(code);
+    }
 
-	public PrintStream out() {
-		return System.out;
-	}
+    public PrintStream out() {
+        return System.out;
+    }
 
 }
diff --git a/src/main/java/org/junit/internal/TextListener.java b/src/main/java/org/junit/internal/TextListener.java
index 2b1c679..9aa56c7 100644
--- a/src/main/java/org/junit/internal/TextListener.java
+++ b/src/main/java/org/junit/internal/TextListener.java
@@ -11,88 +11,91 @@
 
 public class TextListener extends RunListener {
 
-	private final PrintStream fWriter;
+    private final PrintStream writer;
 
-	public TextListener(JUnitSystem system) {
-		this(system.out());
-	}
+    public TextListener(JUnitSystem system) {
+        this(system.out());
+    }
 
-	public TextListener(PrintStream writer) {
-		this.fWriter= writer;
-	}
+    public TextListener(PrintStream writer) {
+        this.writer = writer;
+    }
 
-	@Override
-	public void testRunFinished(Result result) {
-		printHeader(result.getRunTime());
-		printFailures(result);
-		printFooter(result);
-	}
+    @Override
+    public void testRunFinished(Result result) {
+        printHeader(result.getRunTime());
+        printFailures(result);
+        printFooter(result);
+    }
 
-	@Override
-	public void testStarted(Description description) {
-		fWriter.append('.');
-	}
+    @Override
+    public void testStarted(Description description) {
+        writer.append('.');
+    }
 
-	@Override
-	public void testFailure(Failure failure) {
-		fWriter.append('E');
-	}
+    @Override
+    public void testFailure(Failure failure) {
+        writer.append('E');
+    }
 
-	@Override
-	public void testIgnored(Description description) {
-		fWriter.append('I');
-	}
+    @Override
+    public void testIgnored(Description description) {
+        writer.append('I');
+    }
 
-	/*
-	 * Internal methods
-	 */
+    /*
+      * Internal methods
+      */
 
-	private PrintStream getWriter() {
-		return fWriter;
-	}
+    private PrintStream getWriter() {
+        return writer;
+    }
 
-	protected void printHeader(long runTime) {
-		getWriter().println();
-		getWriter().println("Time: " + elapsedTimeAsString(runTime));
-	}
+    protected void printHeader(long runTime) {
+        getWriter().println();
+        getWriter().println("Time: " + elapsedTimeAsString(runTime));
+    }
 
-	protected void printFailures(Result result) {
-		List<Failure> failures= result.getFailures();
-		if (failures.size() == 0)
-			return;
-		if (failures.size() == 1)
-			getWriter().println("There was " + failures.size() + " failure:");
-		else
-			getWriter().println("There were " + failures.size() + " failures:");
-		int i= 1;
-		for (Failure each : failures)
-			printFailure(each, "" + i++);
-	}
+    protected void printFailures(Result result) {
+        List<Failure> failures = result.getFailures();
+        if (failures.size() == 0) {
+            return;
+        }
+        if (failures.size() == 1) {
+            getWriter().println("There was " + failures.size() + " failure:");
+        } else {
+            getWriter().println("There were " + failures.size() + " failures:");
+        }
+        int i = 1;
+        for (Failure each : failures) {
+            printFailure(each, "" + i++);
+        }
+    }
 
-	protected void printFailure(Failure each, String prefix) {
-		getWriter().println(prefix + ") " + each.getTestHeader());
-		getWriter().print(each.getTrace());
-	}
+    protected void printFailure(Failure each, String prefix) {
+        getWriter().println(prefix + ") " + each.getTestHeader());
+        getWriter().print(each.getTrace());
+    }
 
-	protected void printFooter(Result result) {
-		if (result.wasSuccessful()) {
-			getWriter().println();
-			getWriter().print("OK");
-			getWriter().println(" (" + result.getRunCount() + " test" + (result.getRunCount() == 1 ? "" : "s") + ")");
+    protected void printFooter(Result result) {
+        if (result.wasSuccessful()) {
+            getWriter().println();
+            getWriter().print("OK");
+            getWriter().println(" (" + result.getRunCount() + " test" + (result.getRunCount() == 1 ? "" : "s") + ")");
 
-		} else {
-			getWriter().println();
-			getWriter().println("FAILURES!!!");
-			getWriter().println("Tests run: " + result.getRunCount() + ",  Failures: " + result.getFailureCount());
-		}
-		getWriter().println();
-	}
+        } else {
+            getWriter().println();
+            getWriter().println("FAILURES!!!");
+            getWriter().println("Tests run: " + result.getRunCount() + ",  Failures: " + result.getFailureCount());
+        }
+        getWriter().println();
+    }
 
-	/**
-	 * Returns the formatted string of the elapsed time. Duplicated from
-	 * BaseTestRunner. Fix it.
-	 */
-	protected String elapsedTimeAsString(long runTime) {
-		return NumberFormat.getInstance().format((double) runTime / 1000);
-	}
+    /**
+     * Returns the formatted string of the elapsed time. Duplicated from
+     * BaseTestRunner. Fix it.
+     */
+    protected String elapsedTimeAsString(long runTime) {
+        return NumberFormat.getInstance().format((double) runTime / 1000);
+    }
 }
diff --git a/src/main/java/org/junit/internal/Throwables.java b/src/main/java/org/junit/internal/Throwables.java
new file mode 100644
index 0000000..86dceef
--- /dev/null
+++ b/src/main/java/org/junit/internal/Throwables.java
@@ -0,0 +1,42 @@
+package org.junit.internal;
+
+/**
+ * Miscellaneous functions dealing with {@code Throwable}.
+ *
+ * @author kcooney@google.com (Kevin Cooney)
+ * @since 4.12
+ */
+public final class Throwables {
+
+    private Throwables() {
+    }
+
+    /**
+     * Rethrows the given {@code Throwable}, allowing the caller to
+     * declare that it throws {@code Exception}. This is useful when
+     * your callers have nothing reasonable they can do when a
+     * {@code Throwable} is thrown. This is declared to return {@code Exception}
+     * so it can be used in a {@code throw} clause:
+     * <pre>
+     * try {
+     *   doSomething();
+     * } catch (Throwable e} {
+     *   throw Throwables.rethrowAsException(e);
+     * }
+     * doSomethingLater();
+     * </pre>
+     *
+     * @param e exception to rethrow
+     * @return does not return anything
+     * @since 4.12
+     */
+    public static Exception rethrowAsException(Throwable e) throws Exception {
+        Throwables.<Exception>rethrow(e);
+        return null; // we never get here
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T extends Throwable> void rethrow(Throwable e) throws T {
+        throw (T) e;
+    }
+}
diff --git a/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java b/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java
index d3bd50a..d86ec95 100644
--- a/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java
+++ b/src/main/java/org/junit/internal/builders/AllDefaultPossibilitiesBuilder.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package org.junit.internal.builders;
 
 import java.util.Arrays;
@@ -10,48 +7,50 @@
 import org.junit.runners.model.RunnerBuilder;
 
 public class AllDefaultPossibilitiesBuilder extends RunnerBuilder {
-	private final boolean fCanUseSuiteMethod;
+    private final boolean canUseSuiteMethod;
 
-	public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) {
-		fCanUseSuiteMethod= canUseSuiteMethod;
-	}
+    public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) {
+        this.canUseSuiteMethod = canUseSuiteMethod;
+    }
 
-	@Override
-	public Runner runnerForClass(Class<?> testClass) throws Throwable {
-		List<RunnerBuilder> builders= Arrays.asList(
-				ignoredBuilder(),
-				annotatedBuilder(),
-				suiteMethodBuilder(),
-				junit3Builder(),
-				junit4Builder());
+    @Override
+    public Runner runnerForClass(Class<?> testClass) throws Throwable {
+        List<RunnerBuilder> builders = Arrays.asList(
+                ignoredBuilder(),
+                annotatedBuilder(),
+                suiteMethodBuilder(),
+                junit3Builder(),
+                junit4Builder());
 
-		for (RunnerBuilder each : builders) {
-			Runner runner= each.safeRunnerForClass(testClass);
-			if (runner != null)
-				return runner;
-		}
-		return null;
-	}
+        for (RunnerBuilder each : builders) {
+            Runner runner = each.safeRunnerForClass(testClass);
+            if (runner != null) {
+                return runner;
+            }
+        }
+        return null;
+    }
 
-	protected JUnit4Builder junit4Builder() {
-		return new JUnit4Builder();
-	}
+    protected JUnit4Builder junit4Builder() {
+        return new JUnit4Builder();
+    }
 
-	protected JUnit3Builder junit3Builder() {
-		return new JUnit3Builder();
-	}
+    protected JUnit3Builder junit3Builder() {
+        return new JUnit3Builder();
+    }
 
-	protected AnnotatedBuilder annotatedBuilder() {
-		return new AnnotatedBuilder(this);
-	}
+    protected AnnotatedBuilder annotatedBuilder() {
+        return new AnnotatedBuilder(this);
+    }
 
-	protected IgnoredBuilder ignoredBuilder() {
-		return new IgnoredBuilder();
-	}
+    protected IgnoredBuilder ignoredBuilder() {
+        return new IgnoredBuilder();
+    }
 
-	protected RunnerBuilder suiteMethodBuilder() {
-		if (fCanUseSuiteMethod)
-			return new SuiteMethodBuilder();
-		return new NullBuilder();
-	}
+    protected RunnerBuilder suiteMethodBuilder() {
+        if (canUseSuiteMethod) {
+            return new SuiteMethodBuilder();
+        }
+        return new NullBuilder();
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java b/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java
index 8ed9ca7..04d7a68 100644
--- a/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java
+++ b/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.builders;
 
 import org.junit.runner.RunWith;
@@ -8,38 +5,112 @@
 import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.RunnerBuilder;
 
+import java.lang.reflect.Modifier;
+
+
+/**
+ * The {@code AnnotatedBuilder} is a strategy for constructing runners for test class that have been annotated with the
+ * {@code @RunWith} annotation. All tests within this class will be executed using the runner that was specified within
+ * the annotation.
+ * <p>
+ * If a runner supports inner member classes, the member classes will inherit the runner from the enclosing class, e.g.:
+ * <pre>
+ * &#064;RunWith(MyRunner.class)
+ * public class MyTest {
+ *     // some tests might go here
+ *
+ *     public class MyMemberClass {
+ *         &#064;Test
+ *         public void thisTestRunsWith_MyRunner() {
+ *             // some test logic
+ *         }
+ *
+ *         // some more tests might go here
+ *     }
+ *
+ *     &#064;RunWith(AnotherRunner.class)
+ *     public class AnotherMemberClass {
+ *         // some tests might go here
+ *
+ *         public class DeepInnerClass {
+ *             &#064;Test
+ *             public void thisTestRunsWith_AnotherRunner() {
+ *                 // some test logic
+ *             }
+ *         }
+ *
+ *         public class DeepInheritedClass extends SuperTest {
+ *             &#064;Test
+ *             public void thisTestRunsWith_SuperRunner() {
+ *                 // some test logic
+ *             }
+ *         }
+ *     }
+ * }
+ *
+ * &#064;RunWith(SuperRunner.class)
+ * public class SuperTest {
+ *     // some tests might go here
+ * }
+ * </pre>
+ * The key points to note here are:
+ * <ul>
+ *     <li>If there is no RunWith annotation, no runner will be created.</li>
+ *     <li>The resolve step is inside-out, e.g. the closest RunWith annotation wins</li>
+ *     <li>RunWith annotations are inherited and work as if the class was annotated itself.</li>
+ *     <li>The default JUnit runner does not support inner member classes,
+ *         so this is only valid for custom runners that support inner member classes.</li>
+ *     <li>Custom runners with support for inner classes may or may not support RunWith annotations for member
+ *         classes. Please refer to the custom runner documentation.</li>
+ * </ul>
+ *
+ * @see org.junit.runners.model.RunnerBuilder
+ * @see org.junit.runner.RunWith
+ * @since 4.0
+ */
 public class AnnotatedBuilder extends RunnerBuilder {
-	private static final String CONSTRUCTOR_ERROR_FORMAT= "Custom runner class %s should have a public constructor with signature %s(Class testClass)";
+    private static final String CONSTRUCTOR_ERROR_FORMAT = "Custom runner class %s should have a public constructor with signature %s(Class testClass)";
 
-	private RunnerBuilder fSuiteBuilder;
+    private final RunnerBuilder suiteBuilder;
 
-	public AnnotatedBuilder(RunnerBuilder suiteBuilder) {
-		fSuiteBuilder= suiteBuilder;
-	}
+    public AnnotatedBuilder(RunnerBuilder suiteBuilder) {
+        this.suiteBuilder = suiteBuilder;
+    }
 
-	@Override
-	public Runner runnerForClass(Class<?> testClass) throws Exception {
-		RunWith annotation= testClass.getAnnotation(RunWith.class);
-		if (annotation != null)
-			return buildRunner(annotation.value(), testClass);
-		return null;
-	}
+    @Override
+    public Runner runnerForClass(Class<?> testClass) throws Exception {
+        for (Class<?> currentTestClass = testClass; currentTestClass != null;
+             currentTestClass = getEnclosingClassForNonStaticMemberClass(currentTestClass)) {
+            RunWith annotation = currentTestClass.getAnnotation(RunWith.class);
+            if (annotation != null) {
+                return buildRunner(annotation.value(), testClass);
+            }
+        }
 
-	public Runner buildRunner(Class<? extends Runner> runnerClass,
-			Class<?> testClass) throws Exception {
-		try {
-			return runnerClass.getConstructor(Class.class).newInstance(
-					new Object[] { testClass });
-		} catch (NoSuchMethodException e) {
-			try {
-				return runnerClass.getConstructor(Class.class,
-						RunnerBuilder.class).newInstance(
-						new Object[] { testClass, fSuiteBuilder });
-			} catch (NoSuchMethodException e2) {
-				String simpleName= runnerClass.getSimpleName();
-				throw new InitializationError(String.format(
-						CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName));
-			}
-		}
-	}
+        return null;
+    }
+
+    private Class<?> getEnclosingClassForNonStaticMemberClass(Class<?> currentTestClass) {
+        if (currentTestClass.isMemberClass() && !Modifier.isStatic(currentTestClass.getModifiers())) {
+            return currentTestClass.getEnclosingClass();
+        } else {
+            return null;
+        }
+    }
+
+    public Runner buildRunner(Class<? extends Runner> runnerClass,
+            Class<?> testClass) throws Exception {
+        try {
+            return runnerClass.getConstructor(Class.class).newInstance(testClass);
+        } catch (NoSuchMethodException e) {
+            try {
+                return runnerClass.getConstructor(Class.class,
+                        RunnerBuilder.class).newInstance(testClass, suiteBuilder);
+            } catch (NoSuchMethodException e2) {
+                String simpleName = runnerClass.getSimpleName();
+                throw new InitializationError(String.format(
+                        CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName));
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/builders/IgnoredBuilder.java b/src/main/java/org/junit/internal/builders/IgnoredBuilder.java
index 6be342c..71940c8 100644
--- a/src/main/java/org/junit/internal/builders/IgnoredBuilder.java
+++ b/src/main/java/org/junit/internal/builders/IgnoredBuilder.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.builders;
 
 import org.junit.Ignore;
@@ -8,10 +5,11 @@
 import org.junit.runners.model.RunnerBuilder;
 
 public class IgnoredBuilder extends RunnerBuilder {
-	@Override
-	public Runner runnerForClass(Class<?> testClass) {
-		if (testClass.getAnnotation(Ignore.class) != null)
-			return new IgnoredClassRunner(testClass);
-		return null;
-	}
+    @Override
+    public Runner runnerForClass(Class<?> testClass) {
+        if (testClass.getAnnotation(Ignore.class) != null) {
+            return new IgnoredClassRunner(testClass);
+        }
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java b/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java
index b4200a8..7c8926b 100644
--- a/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java
+++ b/src/main/java/org/junit/internal/builders/IgnoredClassRunner.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.builders;
 
 import org.junit.runner.Description;
@@ -8,19 +5,19 @@
 import org.junit.runner.notification.RunNotifier;
 
 public class IgnoredClassRunner extends Runner {
-	private final Class<?> fTestClass;
+    private final Class<?> clazz;
 
-	public IgnoredClassRunner(Class<?> testClass) {
-		fTestClass= testClass;
-	}
+    public IgnoredClassRunner(Class<?> testClass) {
+        clazz = testClass;
+    }
 
-	@Override
-	public void run(RunNotifier notifier) {
-		notifier.fireTestIgnored(getDescription());
-	}
+    @Override
+    public void run(RunNotifier notifier) {
+        notifier.fireTestIgnored(getDescription());
+    }
 
-	@Override
-	public Description getDescription() {
-		return Description.createSuiteDescription(fTestClass);
-	}
+    @Override
+    public Description getDescription() {
+        return Description.createSuiteDescription(clazz);
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/builders/JUnit3Builder.java b/src/main/java/org/junit/internal/builders/JUnit3Builder.java
index ddb070b..8b6b371 100644
--- a/src/main/java/org/junit/internal/builders/JUnit3Builder.java
+++ b/src/main/java/org/junit/internal/builders/JUnit3Builder.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.builders;
 
 import org.junit.internal.runners.JUnit38ClassRunner;
@@ -8,14 +5,15 @@
 import org.junit.runners.model.RunnerBuilder;
 
 public class JUnit3Builder extends RunnerBuilder {
-	@Override
-	public Runner runnerForClass(Class<?> testClass) throws Throwable {
-		if (isPre4Test(testClass))
-			return new JUnit38ClassRunner(testClass);
-		return null;
-	}
+    @Override
+    public Runner runnerForClass(Class<?> testClass) throws Throwable {
+        if (isPre4Test(testClass)) {
+            return new JUnit38ClassRunner(testClass);
+        }
+        return null;
+    }
 
-	boolean isPre4Test(Class<?> testClass) {
-		return junit.framework.TestCase.class.isAssignableFrom(testClass);
-	}
+    boolean isPre4Test(Class<?> testClass) {
+        return junit.framework.TestCase.class.isAssignableFrom(testClass);
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/builders/JUnit4Builder.java b/src/main/java/org/junit/internal/builders/JUnit4Builder.java
index 4380db7..6a00678 100644
--- a/src/main/java/org/junit/internal/builders/JUnit4Builder.java
+++ b/src/main/java/org/junit/internal/builders/JUnit4Builder.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.builders;
 
 import org.junit.runner.Runner;
@@ -8,8 +5,8 @@
 import org.junit.runners.model.RunnerBuilder;
 
 public class JUnit4Builder extends RunnerBuilder {
-	@Override
-	public Runner runnerForClass(Class<?> testClass) throws Throwable {
-		return new BlockJUnit4ClassRunner(testClass);
-	}
+    @Override
+    public Runner runnerForClass(Class<?> testClass) throws Throwable {
+        return new BlockJUnit4ClassRunner(testClass);
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/builders/NullBuilder.java b/src/main/java/org/junit/internal/builders/NullBuilder.java
index 9d43d69..c8d306e 100644
--- a/src/main/java/org/junit/internal/builders/NullBuilder.java
+++ b/src/main/java/org/junit/internal/builders/NullBuilder.java
@@ -1,14 +1,11 @@
-/**
- * 
- */
 package org.junit.internal.builders;
 
 import org.junit.runner.Runner;
 import org.junit.runners.model.RunnerBuilder;
 
 public class NullBuilder extends RunnerBuilder {
-	@Override
-	public Runner runnerForClass(Class<?> each) throws Throwable {
-		return null;
-	}
+    @Override
+    public Runner runnerForClass(Class<?> each) throws Throwable {
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java b/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java
index 659bf31..953e6cf 100644
--- a/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java
+++ b/src/main/java/org/junit/internal/builders/SuiteMethodBuilder.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.builders;
 
 import org.junit.internal.runners.SuiteMethod;
@@ -8,19 +5,20 @@
 import org.junit.runners.model.RunnerBuilder;
 
 public class SuiteMethodBuilder extends RunnerBuilder {
-	@Override
-	public Runner runnerForClass(Class<?> each) throws Throwable {
-		if (hasSuiteMethod(each))
-			return new SuiteMethod(each);
-		return null;
-	}
+    @Override
+    public Runner runnerForClass(Class<?> each) throws Throwable {
+        if (hasSuiteMethod(each)) {
+            return new SuiteMethod(each);
+        }
+        return null;
+    }
 
-	public boolean hasSuiteMethod(Class<?> testClass) {
-		try {
-			testClass.getMethod("suite");
-		} catch (NoSuchMethodException e) {
-			return false;
-		}
-		return true;
-	}
+    public boolean hasSuiteMethod(Class<?> testClass) {
+        try {
+            testClass.getMethod("suite");
+        } catch (NoSuchMethodException e) {
+            return false;
+        }
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/requests/ClassRequest.java b/src/main/java/org/junit/internal/requests/ClassRequest.java
index 53bf520..3d6b100 100644
--- a/src/main/java/org/junit/internal/requests/ClassRequest.java
+++ b/src/main/java/org/junit/internal/requests/ClassRequest.java
@@ -1,26 +1,39 @@
 package org.junit.internal.requests;
 
-
 import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
 import org.junit.runner.Request;
 import org.junit.runner.Runner;
 
 public class ClassRequest extends Request {
-	private final Class<?> fTestClass;
+    private final Object runnerLock = new Object();
 
-	private boolean fCanUseSuiteMethod;
+    /*
+     * We have to use the f prefix, because IntelliJ's JUnit4IdeaTestRunner uses
+     * reflection to access this field. See
+     * https://github.com/junit-team/junit/issues/960
+     */
+    private final Class<?> fTestClass;
+    private final boolean canUseSuiteMethod;
+    private volatile Runner runner;
 
-	public ClassRequest(Class<?> testClass, boolean canUseSuiteMethod) {
-		fTestClass= testClass;
-		fCanUseSuiteMethod= canUseSuiteMethod;
-	}
+    public ClassRequest(Class<?> testClass, boolean canUseSuiteMethod) {
+        this.fTestClass = testClass;
+        this.canUseSuiteMethod = canUseSuiteMethod;
+    }
 
-	public ClassRequest(Class<?> testClass) {
-		this(testClass, true);
-	}
+    public ClassRequest(Class<?> testClass) {
+        this(testClass, true);
+    }
 
-	@Override
-	public Runner getRunner() {
-		return new AllDefaultPossibilitiesBuilder(fCanUseSuiteMethod).safeRunnerForClass(fTestClass);
-	}
+    @Override
+    public Runner getRunner() {
+        if (runner == null) {
+            synchronized (runnerLock) {
+                if (runner == null) {
+                    runner = new AllDefaultPossibilitiesBuilder(canUseSuiteMethod).safeRunnerForClass(fTestClass);
+                }
+            }
+        }
+        return runner;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/requests/FilterRequest.java b/src/main/java/org/junit/internal/requests/FilterRequest.java
index e5d98d1..066cba3 100644
--- a/src/main/java/org/junit/internal/requests/FilterRequest.java
+++ b/src/main/java/org/junit/internal/requests/FilterRequest.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.requests;
 
 import org.junit.internal.runners.ErrorReportingRunner;
@@ -13,30 +10,36 @@
  * A filtered {@link Request}.
  */
 public final class FilterRequest extends Request {
-	private final Request fRequest;
-	private final Filter fFilter;
+    private final Request request;
+    /*
+     * We have to use the f prefix, because IntelliJ's JUnit4IdeaTestRunner uses
+     * reflection to access this field. See
+     * https://github.com/junit-team/junit/issues/960
+     */
+    private final Filter fFilter;
 
-	/**
-	 * Creates a filtered Request
-	 * @param classRequest a {@link Request} describing your Tests
-	 * @param filter {@link Filter} to apply to the Tests described in 
-	 * <code>classRequest</code>
-	 */
-	public FilterRequest(Request classRequest, Filter filter) {
-		fRequest= classRequest;
-		fFilter= filter;
-	}
+    /**
+     * Creates a filtered Request
+     *
+     * @param request a {@link Request} describing your Tests
+     * @param filter {@link Filter} to apply to the Tests described in
+     * <code>request</code>
+     */
+    public FilterRequest(Request request, Filter filter) {
+        this.request = request;
+        this.fFilter = filter;
+    }
 
-	@Override 
-	public Runner getRunner() {
-		try {
-			Runner runner= fRequest.getRunner();
-			fFilter.apply(runner);
-			return runner;
-		} catch (NoTestsRemainException e) {
-			return new ErrorReportingRunner(Filter.class, new Exception(String
-					.format("No tests found matching %s from %s", fFilter
-							.describe(), fRequest.toString())));
-		}
-	}
+    @Override
+    public Runner getRunner() {
+        try {
+            Runner runner = request.getRunner();
+            fFilter.apply(runner);
+            return runner;
+        } catch (NoTestsRemainException e) {
+            return new ErrorReportingRunner(Filter.class, new Exception(String
+                    .format("No tests found matching %s from %s", fFilter
+                            .describe(), request.toString())));
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/requests/SortingRequest.java b/src/main/java/org/junit/internal/requests/SortingRequest.java
index 3c6f4f5..77061da 100644
--- a/src/main/java/org/junit/internal/requests/SortingRequest.java
+++ b/src/main/java/org/junit/internal/requests/SortingRequest.java
@@ -8,18 +8,18 @@
 import org.junit.runner.manipulation.Sorter;
 
 public class SortingRequest extends Request {
-	private final Request fRequest;
-	private final Comparator<Description> fComparator;
+    private final Request request;
+    private final Comparator<Description> comparator;
 
-	public SortingRequest(Request request, Comparator<Description> comparator) {
-		fRequest= request;
-		fComparator= comparator;
-	}
+    public SortingRequest(Request request, Comparator<Description> comparator) {
+        this.request = request;
+        this.comparator = comparator;
+    }
 
-	@Override
-	public Runner getRunner() {
-		Runner runner= fRequest.getRunner();
-		new Sorter(fComparator).apply(runner);
-		return runner;
-	}
+    @Override
+    public Runner getRunner() {
+        Runner runner = request.getRunner();
+        new Sorter(comparator).apply(runner);
+        return runner;
+    }
 }
diff --git a/src/main/java/org/junit/internal/runners/ClassRoadie.java b/src/main/java/org/junit/internal/runners/ClassRoadie.java
index 1f77d37..df1b453 100644
--- a/src/main/java/org/junit/internal/runners/ClassRoadie.java
+++ b/src/main/java/org/junit/internal/runners/ClassRoadie.java
@@ -4,6 +4,7 @@
 import java.lang.reflect.Method;
 import java.util.List;
 
+import org.junit.internal.AssumptionViolatedException;
 import org.junit.runner.Description;
 import org.junit.runner.notification.Failure;
 import org.junit.runner.notification.RunNotifier;
@@ -11,69 +12,70 @@
 
 /**
  * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
- *             removed in the next release. Please use
+ *             removed in the next major release. Please use
  *             {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
  */
 @Deprecated
-public
-class ClassRoadie {
-	private RunNotifier fNotifier;
-	private TestClass fTestClass;
-	private Description fDescription;
-	private final Runnable fRunnable;
-	
-	public ClassRoadie(RunNotifier notifier, TestClass testClass,
-			Description description, Runnable runnable) {
-		fNotifier= notifier;
-		fTestClass= testClass;
-		fDescription= description;
-		fRunnable= runnable;
-	}
+public class ClassRoadie {
+    private RunNotifier notifier;
+    private TestClass testClass;
+    private Description description;
+    private final Runnable runnable;
 
-	protected void runUnprotected() {
-		fRunnable.run();
-	};
+    public ClassRoadie(RunNotifier notifier, TestClass testClass,
+            Description description, Runnable runnable) {
+        this.notifier = notifier;
+        this.testClass = testClass;
+        this.description = description;
+        this.runnable = runnable;
+    }
 
-	protected void addFailure(Throwable targetException) {
-		fNotifier.fireTestFailure(new Failure(fDescription, targetException));
-	}
+    protected void runUnprotected() {
+        runnable.run();
+    }
 
-	public void runProtected() {
-		try {
-			runBefores();
-			runUnprotected();
-		} catch (FailedBefore e) {
-		} finally {
-			runAfters();
-		}
-	}
+    protected void addFailure(Throwable targetException) {
+        notifier.fireTestFailure(new Failure(description, targetException));
+    }
 
-	private void runBefores() throws FailedBefore {
-		try {
-			try {
-				List<Method> befores= fTestClass.getBefores();
-				for (Method before : befores)
-					before.invoke(null);
-			} catch (InvocationTargetException e) {
-				throw e.getTargetException();
-			}
-		} catch (org.junit.internal.AssumptionViolatedException e) {
-			throw new FailedBefore();
-		} catch (Throwable e) {
-			addFailure(e);
-			throw new FailedBefore();
-		}
-	}
+    public void runProtected() {
+        try {
+            runBefores();
+            runUnprotected();
+        } catch (FailedBefore e) {
+        } finally {
+            runAfters();
+        }
+    }
 
-	private void runAfters() {
-		List<Method> afters= fTestClass.getAfters();
-		for (Method after : afters)
-			try {
-				after.invoke(null);
-			} catch (InvocationTargetException e) {
-				addFailure(e.getTargetException());
-			} catch (Throwable e) {
-				addFailure(e); // Untested, but seems impossible
-			}
-	}
-}
\ No newline at end of file
+    private void runBefores() throws FailedBefore {
+        try {
+            try {
+                List<Method> befores = testClass.getBefores();
+                for (Method before : befores) {
+                    before.invoke(null);
+                }
+            } catch (InvocationTargetException e) {
+                throw e.getTargetException();
+            }
+        } catch (AssumptionViolatedException e) {
+            throw new FailedBefore();
+        } catch (Throwable e) {
+            addFailure(e);
+            throw new FailedBefore();
+        }
+    }
+
+    private void runAfters() {
+        List<Method> afters = testClass.getAfters();
+        for (Method after : afters) {
+            try {
+                after.invoke(null);
+            } catch (InvocationTargetException e) {
+                addFailure(e.getTargetException());
+            } catch (Throwable e) {
+                addFailure(e); // Untested, but seems impossible
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java
index 200b6f0..1d32beb 100644
--- a/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java
+++ b/src/main/java/org/junit/internal/runners/ErrorReportingRunner.java
@@ -11,50 +11,58 @@
 import org.junit.runners.model.InitializationError;
 
 public class ErrorReportingRunner extends Runner {
-	private final List<Throwable> fCauses;
+    private final List<Throwable> causes;
 
-	private final Class<?> fTestClass;
+    private final Class<?> testClass;
 
-	public ErrorReportingRunner(Class<?> testClass, Throwable cause) {
-		fTestClass= testClass;
-		fCauses= getCauses(cause);
-	}
+    public ErrorReportingRunner(Class<?> testClass, Throwable cause) {
+        if (testClass == null) {
+            throw new NullPointerException("Test class cannot be null");
+        }
+        this.testClass = testClass;
+        causes = getCauses(cause);
+    }
 
-	@Override
-	public Description getDescription() {
-		Description description= Description.createSuiteDescription(fTestClass);
-		for (Throwable each : fCauses)
-			description.addChild(describeCause(each));
-		return description;
-	}
+    @Override
+    public Description getDescription() {
+        Description description = Description.createSuiteDescription(testClass);
+        for (Throwable each : causes) {
+            description.addChild(describeCause(each));
+        }
+        return description;
+    }
 
-	@Override
-	public void run(RunNotifier notifier) {
-		for (Throwable each : fCauses)
-			runCause(each, notifier);
-	}
+    @Override
+    public void run(RunNotifier notifier) {
+        for (Throwable each : causes) {
+            runCause(each, notifier);
+        }
+    }
 
-	@SuppressWarnings("deprecation")
-	private List<Throwable> getCauses(Throwable cause) {
-		if (cause instanceof InvocationTargetException)
-			return getCauses(cause.getCause());
-		if (cause instanceof InitializationError)
-			return ((InitializationError) cause).getCauses();
-		if (cause instanceof org.junit.internal.runners.InitializationError)
-			return ((org.junit.internal.runners.InitializationError) cause)
-					.getCauses();
-		return Arrays.asList(cause);
-	}
+    @SuppressWarnings("deprecation")
+    private List<Throwable> getCauses(Throwable cause) {
+        if (cause instanceof InvocationTargetException) {
+            return getCauses(cause.getCause());
+        }
+        if (cause instanceof InitializationError) {
+            return ((InitializationError) cause).getCauses();
+        }
+        if (cause instanceof org.junit.internal.runners.InitializationError) {
+            return ((org.junit.internal.runners.InitializationError) cause)
+                    .getCauses();
+        }
+        return Arrays.asList(cause);
+    }
 
-	private Description describeCause(Throwable child) {
-		return Description.createTestDescription(fTestClass,
-				"initializationError");
-	}
+    private Description describeCause(Throwable child) {
+        return Description.createTestDescription(testClass,
+                "initializationError");
+    }
 
-	private void runCause(Throwable child, RunNotifier notifier) {
-		Description description= describeCause(child);
-		notifier.fireTestStarted(description);
-		notifier.fireTestFailure(new Failure(description, child));
-		notifier.fireTestFinished(description);
-	}
-}
\ No newline at end of file
+    private void runCause(Throwable child, RunNotifier notifier) {
+        Description description = describeCause(child);
+        notifier.fireTestStarted(description);
+        notifier.fireTestFailure(new Failure(description, child));
+        notifier.fireTestFinished(description);
+    }
+}
diff --git a/src/main/java/org/junit/internal/runners/FailedBefore.java b/src/main/java/org/junit/internal/runners/FailedBefore.java
index 29dcba4..1036cb6 100644
--- a/src/main/java/org/junit/internal/runners/FailedBefore.java
+++ b/src/main/java/org/junit/internal/runners/FailedBefore.java
@@ -2,13 +2,12 @@
 
 import org.junit.runners.BlockJUnit4ClassRunner;
 
-
 /**
  * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
- *             removed in the next release. Please use
+ *             removed in the next major release. Please use
  *             {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
  */
 @Deprecated
 class FailedBefore extends Exception {
-	private static final long serialVersionUID= 1L;
+    private static final long serialVersionUID = 1L;
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/InitializationError.java b/src/main/java/org/junit/internal/runners/InitializationError.java
index 5715ec5..52065ec 100644
--- a/src/main/java/org/junit/internal/runners/InitializationError.java
+++ b/src/main/java/org/junit/internal/runners/InitializationError.java
@@ -3,28 +3,35 @@
 import java.util.Arrays;
 import java.util.List;
 
-@Deprecated
 /**
- * Use the published version: {@link org.junit.runners.InitializationError}
+ * Use the published version:
+ * {@link org.junit.runners.model.InitializationError}
  * This may disappear as soon as 1 April 2009
  */
+@Deprecated
 public class InitializationError extends Exception {
-	private static final long serialVersionUID= 1L;
-	private final List<Throwable> fErrors;
+    private static final long serialVersionUID = 1L;
 
-	public InitializationError(List<Throwable> errors) {
-		fErrors= errors;
-	}
+    /*
+     * We have to use the f prefix until the next major release to ensure
+     * serialization compatibility. 
+     * See https://github.com/junit-team/junit/issues/976
+     */
+    private final List<Throwable> fErrors;
 
-	public InitializationError(Throwable... errors) {
-		this(Arrays.asList(errors));
-	}
-	
-	public InitializationError(String string) {
-		this(new Exception(string));
-	}
+    public InitializationError(List<Throwable> errors) {
+        this.fErrors = errors;
+    }
 
-	public List<Throwable> getCauses() {
-		return fErrors;
-	}
+    public InitializationError(Throwable... errors) {
+        this(Arrays.asList(errors));
+    }
+
+    public InitializationError(String string) {
+        this(new Exception(string));
+    }
+
+    public List<Throwable> getCauses() {
+        return fErrors;
+    }
 }
diff --git a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java
index 0028d0c..631fcf2 100644
--- a/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java
+++ b/src/main/java/org/junit/internal/runners/JUnit38ClassRunner.java
@@ -17,142 +17,164 @@
 import org.junit.runner.manipulation.Sorter;
 import org.junit.runner.notification.Failure;
 import org.junit.runner.notification.RunNotifier;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
 
 public class JUnit38ClassRunner extends Runner implements Filterable, Sortable {
-	private final class OldTestClassAdaptingListener implements
-			TestListener {
-		private final RunNotifier fNotifier;
+    private static final class OldTestClassAdaptingListener implements
+            TestListener {
+        private final RunNotifier notifier;
 
-		private OldTestClassAdaptingListener(RunNotifier notifier) {
-			fNotifier= notifier;
-		}
+        private OldTestClassAdaptingListener(RunNotifier notifier) {
+            this.notifier = notifier;
+        }
 
-		public void endTest(Test test) {
-			fNotifier.fireTestFinished(asDescription(test));
-		}
+        public void endTest(Test test) {
+            notifier.fireTestFinished(asDescription(test));
+        }
 
-		public void startTest(Test test) {
-			fNotifier.fireTestStarted(asDescription(test));
-		}
+        public void startTest(Test test) {
+            notifier.fireTestStarted(asDescription(test));
+        }
 
-		// Implement junit.framework.TestListener
-		public void addError(Test test, Throwable t) {
-			Failure failure= new Failure(asDescription(test), t);
-			fNotifier.fireTestFailure(failure);
-		}
+        // Implement junit.framework.TestListener
+        public void addError(Test test, Throwable e) {
+            Failure failure = new Failure(asDescription(test), e);
+            notifier.fireTestFailure(failure);
+        }
 
-		private Description asDescription(Test test) {
-			if (test instanceof Describable) {
-				Describable facade= (Describable) test;
-				return facade.getDescription();
-			}
-			return Description.createTestDescription(getEffectiveClass(test), getName(test));
-		}
+        private Description asDescription(Test test) {
+            if (test instanceof Describable) {
+                Describable facade = (Describable) test;
+                return facade.getDescription();
+            }
+            return Description.createTestDescription(getEffectiveClass(test), getName(test));
+        }
 
-		private Class<? extends Test> getEffectiveClass(Test test) {
-			return test.getClass();
-		}
+        private Class<? extends Test> getEffectiveClass(Test test) {
+            return test.getClass();
+        }
 
-		private String getName(Test test) {
-			if (test instanceof TestCase)
-				return ((TestCase) test).getName();
-			else
-				return test.toString();
-		}
+        private String getName(Test test) {
+            if (test instanceof TestCase) {
+                return ((TestCase) test).getName();
+            } else {
+                return test.toString();
+            }
+        }
 
-		public void addFailure(Test test, AssertionFailedError t) {
-			addError(test, t);
-		}
-	}
+        public void addFailure(Test test, AssertionFailedError t) {
+            addError(test, t);
+        }
+    }
 
-	private Test fTest;
-	
-	public JUnit38ClassRunner(Class<?> klass) {
-		this(new TestSuite(klass.asSubclass(TestCase.class)));
-	}
+    private volatile Test test;
 
-	public JUnit38ClassRunner(Test test) {
-		super();
-		setTest(test);
-	}
+    public JUnit38ClassRunner(Class<?> klass) {
+        this(new TestSuite(klass.asSubclass(TestCase.class)));
+    }
 
-	@Override
-	public void run(RunNotifier notifier) {
-		TestResult result= new TestResult();
-		result.addListener(createAdaptingListener(notifier));
-		getTest().run(result);
-	}
+    public JUnit38ClassRunner(Test test) {
+        super();
+        setTest(test);
+    }
 
-	public TestListener createAdaptingListener(final RunNotifier notifier) {
-		return new OldTestClassAdaptingListener(notifier);
-	}
-	
-	@Override
-	public Description getDescription() {
-		return makeDescription(getTest());
-	}
+    @Override
+    public void run(RunNotifier notifier) {
+        TestResult result = new TestResult();
+        result.addListener(createAdaptingListener(notifier));
+        getTest().run(result);
+    }
 
-	private static Description makeDescription(Test test) {
-		if (test instanceof TestCase) {
-			TestCase tc= (TestCase) test;
-			return Description.createTestDescription(tc.getClass(), tc.getName());
-		} else if (test instanceof TestSuite) {
-			TestSuite ts= (TestSuite) test;
-			String name= ts.getName() == null ? createSuiteDescription(ts) : ts.getName();
-			Description description= Description.createSuiteDescription(name);
-			int n= ts.testCount();
-			for (int i= 0; i < n; i++) {
-				Description made= makeDescription(ts.testAt(i));
-				description.addChild(made);
-			}
-			return description;
-		} else if (test instanceof Describable) {
-			Describable adapter= (Describable) test;
-			return adapter.getDescription();
-		} else if (test instanceof TestDecorator) {
-			TestDecorator decorator= (TestDecorator) test;
-			return makeDescription(decorator.getTest());
-		} else {
-			// This is the best we can do in this case
-			return Description.createSuiteDescription(test.getClass());
-		}
-	}
+    public TestListener createAdaptingListener(final RunNotifier notifier) {
+        return new OldTestClassAdaptingListener(notifier);
+    }
 
-	private static String createSuiteDescription(TestSuite ts) {
-		int count= ts.countTestCases();
-		String example = count == 0 ? "" : String.format(" [example: %s]", ts.testAt(0));
-		return String.format("TestSuite with %s tests%s", count, example);
-	}
+    @Override
+    public Description getDescription() {
+        return makeDescription(getTest());
+    }
 
-	public void filter(Filter filter) throws NoTestsRemainException {
-		if (getTest() instanceof Filterable) {
-			Filterable adapter= (Filterable) getTest();
-			adapter.filter(filter);
-		} else if (getTest() instanceof TestSuite) {
-			TestSuite suite= (TestSuite) getTest();
-			TestSuite filtered= new TestSuite(suite.getName());
-			int n= suite.testCount();
-			for (int i= 0; i < n; i++) {
-				Test test= suite.testAt(i);
-				if (filter.shouldRun(makeDescription(test)))
-					filtered.addTest(test);
-			}
-			setTest(filtered);
-		}
-	}
+    private static Description makeDescription(Test test) {
+        if (test instanceof TestCase) {
+            TestCase tc = (TestCase) test;
+            return Description.createTestDescription(tc.getClass(), tc.getName(),
+                    getAnnotations(tc));
+        } else if (test instanceof TestSuite) {
+            TestSuite ts = (TestSuite) test;
+            String name = ts.getName() == null ? createSuiteDescription(ts) : ts.getName();
+            Description description = Description.createSuiteDescription(name);
+            int n = ts.testCount();
+            for (int i = 0; i < n; i++) {
+                Description made = makeDescription(ts.testAt(i));
+                description.addChild(made);
+            }
+            return description;
+        } else if (test instanceof Describable) {
+            Describable adapter = (Describable) test;
+            return adapter.getDescription();
+        } else if (test instanceof TestDecorator) {
+            TestDecorator decorator = (TestDecorator) test;
+            return makeDescription(decorator.getTest());
+        } else {
+            // This is the best we can do in this case
+            return Description.createSuiteDescription(test.getClass());
+        }
+    }
 
-	public void sort(Sorter sorter) {
-		if (getTest() instanceof Sortable) {
-			Sortable adapter= (Sortable) getTest();
-			adapter.sort(sorter);
-		}
-	}
+    /**
+     * Get the annotations associated with given TestCase.
+     * @param test the TestCase.
+     */
+    private static Annotation[] getAnnotations(TestCase test) {
+        try {
+            Method m = test.getClass().getMethod(test.getName());
+            return m.getDeclaredAnnotations();
+        } catch (SecurityException e) {
+        } catch (NoSuchMethodException e) {
+        }
+        return new Annotation[0];
+    }
 
-	private void setTest(Test test) {
-		fTest = test;
-	}
+    private static String createSuiteDescription(TestSuite ts) {
+        int count = ts.countTestCases();
+        String example = count == 0 ? "" : String.format(" [example: %s]", ts.testAt(0));
+        return String.format("TestSuite with %s tests%s", count, example);
+    }
 
-	private Test getTest() {
-		return fTest;
-	}
+    public void filter(Filter filter) throws NoTestsRemainException {
+        if (getTest() instanceof Filterable) {
+            Filterable adapter = (Filterable) getTest();
+            adapter.filter(filter);
+        } else if (getTest() instanceof TestSuite) {
+            TestSuite suite = (TestSuite) getTest();
+            TestSuite filtered = new TestSuite(suite.getName());
+            int n = suite.testCount();
+            for (int i = 0; i < n; i++) {
+                Test test = suite.testAt(i);
+                if (filter.shouldRun(makeDescription(test))) {
+                    filtered.addTest(test);
+                }
+            }
+            setTest(filtered);
+            if (filtered.testCount() == 0) {
+                throw new NoTestsRemainException();
+            }
+        }
+    }
+
+    public void sort(Sorter sorter) {
+        if (getTest() instanceof Sortable) {
+            Sortable adapter = (Sortable) getTest();
+            adapter.sort(sorter);
+        }
+    }
+
+    private void setTest(Test test) {
+        this.test = test;
+    }
+
+    private Test getTest() {
+        return test;
+    }
 }
diff --git a/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java b/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java
index d732880..69a23c4 100644
--- a/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java
+++ b/src/main/java/org/junit/internal/runners/JUnit4ClassRunner.java
@@ -21,125 +21,127 @@
 
 /**
  * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
- *             removed in the next release. Please use
+ *             removed in the next major release. Please use
  *             {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
- *             
- *             This may disappear as soon as 1 April 2009
  */
 @Deprecated
 public class JUnit4ClassRunner extends Runner implements Filterable, Sortable {
-	private final List<Method> fTestMethods;
-	private TestClass fTestClass;
+    private final List<Method> testMethods;
+    private TestClass testClass;
 
-	public JUnit4ClassRunner(Class<?> klass) throws InitializationError {
-		fTestClass= new TestClass(klass);
-		fTestMethods= getTestMethods();
-		validate();
-	}
-	
-	protected List<Method> getTestMethods() {
-		return fTestClass.getTestMethods();
-	}
-	
-	protected void validate() throws InitializationError {
-		MethodValidator methodValidator= new MethodValidator(fTestClass);
-		methodValidator.validateMethodsForDefaultRunner();
-		methodValidator.assertValid();
-	}
+    public JUnit4ClassRunner(Class<?> klass) throws InitializationError {
+        testClass = new TestClass(klass);
+        testMethods = getTestMethods();
+        validate();
+    }
 
-	@Override
-	public void run(final RunNotifier notifier) {
-		new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() {
-			public void run() {
-				runMethods(notifier);
-			}
-		}).runProtected();
-	}
+    protected List<Method> getTestMethods() {
+        return testClass.getTestMethods();
+    }
 
-	protected void runMethods(final RunNotifier notifier) {
-		for (Method method : fTestMethods)
-			invokeTestMethod(method, notifier);
-	}
+    protected void validate() throws InitializationError {
+        MethodValidator methodValidator = new MethodValidator(testClass);
+        methodValidator.validateMethodsForDefaultRunner();
+        methodValidator.assertValid();
+    }
 
-	@Override
-	public Description getDescription() {
-		Description spec= Description.createSuiteDescription(getName(), classAnnotations());
-		List<Method> testMethods= fTestMethods;
-		for (Method method : testMethods)
-			spec.addChild(methodDescription(method));
-		return spec;
-	}
+    @Override
+    public void run(final RunNotifier notifier) {
+        new ClassRoadie(notifier, testClass, getDescription(), new Runnable() {
+            public void run() {
+                runMethods(notifier);
+            }
+        }).runProtected();
+    }
 
-	protected Annotation[] classAnnotations() {
-		return fTestClass.getJavaClass().getAnnotations();
-	}
+    protected void runMethods(final RunNotifier notifier) {
+        for (Method method : testMethods) {
+            invokeTestMethod(method, notifier);
+        }
+    }
 
-	protected String getName() {
-		return getTestClass().getName();
-	}
-	
-	protected Object createTest() throws Exception {
-		return getTestClass().getConstructor().newInstance();
-	}
+    @Override
+    public Description getDescription() {
+        Description spec = Description.createSuiteDescription(getName(), classAnnotations());
+        List<Method> testMethods = this.testMethods;
+        for (Method method : testMethods) {
+            spec.addChild(methodDescription(method));
+        }
+        return spec;
+    }
 
-	protected void invokeTestMethod(Method method, RunNotifier notifier) {
-		Description description= methodDescription(method);
-		Object test;
-		try {
-			test= createTest();
-		} catch (InvocationTargetException e) {
-			testAborted(notifier, description, e.getCause());
-			return;			
-		} catch (Exception e) {
-			testAborted(notifier, description, e);
-			return;
-		}
-		TestMethod testMethod= wrapMethod(method);
-		new MethodRoadie(test, testMethod, notifier, description).run();
-	}
+    protected Annotation[] classAnnotations() {
+        return testClass.getJavaClass().getAnnotations();
+    }
 
-	private void testAborted(RunNotifier notifier, Description description,
-			Throwable e) {
-		notifier.fireTestStarted(description);
-		notifier.fireTestFailure(new Failure(description, e));
-		notifier.fireTestFinished(description);
-	}
+    protected String getName() {
+        return getTestClass().getName();
+    }
 
-	protected TestMethod wrapMethod(Method method) {
-		return new TestMethod(method, fTestClass);
-	}
+    protected Object createTest() throws Exception {
+        return getTestClass().getConstructor().newInstance();
+    }
 
-	protected String testName(Method method) {
-		return method.getName();
-	}
+    protected void invokeTestMethod(Method method, RunNotifier notifier) {
+        Description description = methodDescription(method);
+        Object test;
+        try {
+            test = createTest();
+        } catch (InvocationTargetException e) {
+            testAborted(notifier, description, e.getCause());
+            return;
+        } catch (Exception e) {
+            testAborted(notifier, description, e);
+            return;
+        }
+        TestMethod testMethod = wrapMethod(method);
+        new MethodRoadie(test, testMethod, notifier, description).run();
+    }
 
-	protected Description methodDescription(Method method) {
-		return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), testAnnotations(method));
-	}
+    private void testAborted(RunNotifier notifier, Description description,
+            Throwable e) {
+        notifier.fireTestStarted(description);
+        notifier.fireTestFailure(new Failure(description, e));
+        notifier.fireTestFinished(description);
+    }
 
-	protected Annotation[] testAnnotations(Method method) {
-		return method.getAnnotations();
-	}
+    protected TestMethod wrapMethod(Method method) {
+        return new TestMethod(method, testClass);
+    }
 
-	public void filter(Filter filter) throws NoTestsRemainException {
-		for (Iterator<Method> iter= fTestMethods.iterator(); iter.hasNext();) {
-			Method method= iter.next();
-			if (!filter.shouldRun(methodDescription(method)))
-				iter.remove();
-		}
-		if (fTestMethods.isEmpty())
-			throw new NoTestsRemainException();
-	}
+    protected String testName(Method method) {
+        return method.getName();
+    }
 
-	public void sort(final Sorter sorter) {
-		Collections.sort(fTestMethods, new Comparator<Method>() {
-			public int compare(Method o1, Method o2) {
-				return sorter.compare(methodDescription(o1), methodDescription(o2));
-			}
-		});
-	}
+    protected Description methodDescription(Method method) {
+        return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), testAnnotations(method));
+    }
 
-	protected TestClass getTestClass() {
-		return fTestClass;
-	}
+    protected Annotation[] testAnnotations(Method method) {
+        return method.getAnnotations();
+    }
+
+    public void filter(Filter filter) throws NoTestsRemainException {
+        for (Iterator<Method> iter = testMethods.iterator(); iter.hasNext(); ) {
+            Method method = iter.next();
+            if (!filter.shouldRun(methodDescription(method))) {
+                iter.remove();
+            }
+        }
+        if (testMethods.isEmpty()) {
+            throw new NoTestsRemainException();
+        }
+    }
+
+    public void sort(final Sorter sorter) {
+        Collections.sort(testMethods, new Comparator<Method>() {
+            public int compare(Method o1, Method o2) {
+                return sorter.compare(methodDescription(o1), methodDescription(o2));
+            }
+        });
+    }
+
+    protected TestClass getTestClass() {
+        return testClass;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/MethodRoadie.java b/src/main/java/org/junit/internal/runners/MethodRoadie.java
index 4407821..01a476b 100644
--- a/src/main/java/org/junit/internal/runners/MethodRoadie.java
+++ b/src/main/java/org/junit/internal/runners/MethodRoadie.java
@@ -15,143 +15,149 @@
 import org.junit.runner.notification.Failure;
 import org.junit.runner.notification.RunNotifier;
 import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.TestTimedOutException;
 
 /**
  * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
- *             removed in the next release. Please use
+ *             removed in the next major release. Please use
  *             {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
  */
 @Deprecated
 public class MethodRoadie {
-	private final Object fTest;
-	private final RunNotifier fNotifier;
-	private final Description fDescription;
-	private TestMethod fTestMethod;
+    private final Object test;
+    private final RunNotifier notifier;
+    private final Description description;
+    private TestMethod testMethod;
 
-	public MethodRoadie(Object test, TestMethod method, RunNotifier notifier, Description description) {
-		fTest= test;
-		fNotifier= notifier;
-		fDescription= description;
-		fTestMethod= method;
-	}
+    public MethodRoadie(Object test, TestMethod method, RunNotifier notifier, Description description) {
+        this.test = test;
+        this.notifier = notifier;
+        this.description = description;
+        testMethod = method;
+    }
 
-	public void run() {
-		if (fTestMethod.isIgnored()) {
-			fNotifier.fireTestIgnored(fDescription);
-			return;
-		}
-		fNotifier.fireTestStarted(fDescription);
-		try {
-			long timeout= fTestMethod.getTimeout();
-			if (timeout > 0)
-				runWithTimeout(timeout);
-			else
-				runTest();
-		} finally {
-			fNotifier.fireTestFinished(fDescription);
-		}
-	}
+    public void run() {
+        if (testMethod.isIgnored()) {
+            notifier.fireTestIgnored(description);
+            return;
+        }
+        notifier.fireTestStarted(description);
+        try {
+            long timeout = testMethod.getTimeout();
+            if (timeout > 0) {
+                runWithTimeout(timeout);
+            } else {
+                runTest();
+            }
+        } finally {
+            notifier.fireTestFinished(description);
+        }
+    }
 
-	private void runWithTimeout(final long timeout) {
-		runBeforesThenTestThenAfters(new Runnable() {
-		
-			public void run() {
-				ExecutorService service= Executors.newSingleThreadExecutor();
-				Callable<Object> callable= new Callable<Object>() {
-					public Object call() throws Exception {
-						runTestMethod();
-						return null;
-					}
-				};
-				Future<Object> result= service.submit(callable);
-				service.shutdown();
-				try {
-					boolean terminated= service.awaitTermination(timeout,
-							TimeUnit.MILLISECONDS);
-					if (!terminated)
-						service.shutdownNow();
-					result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation
-				} catch (TimeoutException e) {
-					addFailure(new Exception(String.format("test timed out after %d milliseconds", timeout)));
-				} catch (Exception e) {
-					addFailure(e);
-				}				
-			}
-		});
-	}
-	
-	public void runTest() {
-		runBeforesThenTestThenAfters(new Runnable() {
-			public void run() {
-				runTestMethod();
-			}
-		});
-	}
+    private void runWithTimeout(final long timeout) {
+        runBeforesThenTestThenAfters(new Runnable() {
 
-	public void runBeforesThenTestThenAfters(Runnable test) {
-		try {
-			runBefores();
-			test.run();
-		} catch (FailedBefore e) {
-		} catch (Exception e) {
-			throw new RuntimeException("test should never throw an exception to this level");
-		} finally {
-			runAfters();
-		}		
-	}
-	
-	protected void runTestMethod() {
-		try {
-			fTestMethod.invoke(fTest);
-			if (fTestMethod.expectsException())
-				addFailure(new AssertionError("Expected exception: " + fTestMethod.getExpectedException().getName()));
-		} catch (InvocationTargetException e) {
-			Throwable actual= e.getTargetException();
-			if (actual instanceof AssumptionViolatedException)
-				return;
-			else if (!fTestMethod.expectsException())
-				addFailure(actual);
-			else if (fTestMethod.isUnexpected(actual)) {
-				String message= "Unexpected exception, expected<" + fTestMethod.getExpectedException().getName() + "> but was<"
-					+ actual.getClass().getName() + ">";
-				addFailure(new Exception(message, actual));
-			}
-		} catch (Throwable e) {
-			addFailure(e);
-		}
-	}
-	
-	private void runBefores() throws FailedBefore {
-		try {
-			try {
-				List<Method> befores= fTestMethod.getBefores();
-				for (Method before : befores)
-					before.invoke(fTest);
-			} catch (InvocationTargetException e) {
-				throw e.getTargetException();
-			}
-		} catch (AssumptionViolatedException e) {
-			throw new FailedBefore();
-		} catch (Throwable e) {
-			addFailure(e);
-			throw new FailedBefore();
-		}
-	}
+            public void run() {
+                ExecutorService service = Executors.newSingleThreadExecutor();
+                Callable<Object> callable = new Callable<Object>() {
+                    public Object call() throws Exception {
+                        runTestMethod();
+                        return null;
+                    }
+                };
+                Future<Object> result = service.submit(callable);
+                service.shutdown();
+                try {
+                    boolean terminated = service.awaitTermination(timeout,
+                            TimeUnit.MILLISECONDS);
+                    if (!terminated) {
+                        service.shutdownNow();
+                    }
+                    result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation
+                } catch (TimeoutException e) {
+                    addFailure(new TestTimedOutException(timeout, TimeUnit.MILLISECONDS));
+                } catch (Exception e) {
+                    addFailure(e);
+                }
+            }
+        });
+    }
 
-	private void runAfters() {
-		List<Method> afters= fTestMethod.getAfters();
-		for (Method after : afters)
-			try {
-				after.invoke(fTest);
-			} catch (InvocationTargetException e) {
-				addFailure(e.getTargetException());
-			} catch (Throwable e) {
-				addFailure(e); // Untested, but seems impossible
-			}
-	}
+    public void runTest() {
+        runBeforesThenTestThenAfters(new Runnable() {
+            public void run() {
+                runTestMethod();
+            }
+        });
+    }
 
-	protected void addFailure(Throwable e) {
-		fNotifier.fireTestFailure(new Failure(fDescription, e));
-	}
+    public void runBeforesThenTestThenAfters(Runnable test) {
+        try {
+            runBefores();
+            test.run();
+        } catch (FailedBefore e) {
+        } catch (Exception e) {
+            throw new RuntimeException("test should never throw an exception to this level");
+        } finally {
+            runAfters();
+        }
+    }
+
+    protected void runTestMethod() {
+        try {
+            testMethod.invoke(test);
+            if (testMethod.expectsException()) {
+                addFailure(new AssertionError("Expected exception: " + testMethod.getExpectedException().getName()));
+            }
+        } catch (InvocationTargetException e) {
+            Throwable actual = e.getTargetException();
+            if (actual instanceof AssumptionViolatedException) {
+                return;
+            } else if (!testMethod.expectsException()) {
+                addFailure(actual);
+            } else if (testMethod.isUnexpected(actual)) {
+                String message = "Unexpected exception, expected<" + testMethod.getExpectedException().getName() + "> but was<"
+                        + actual.getClass().getName() + ">";
+                addFailure(new Exception(message, actual));
+            }
+        } catch (Throwable e) {
+            addFailure(e);
+        }
+    }
+
+    private void runBefores() throws FailedBefore {
+        try {
+            try {
+                List<Method> befores = testMethod.getBefores();
+                for (Method before : befores) {
+                    before.invoke(test);
+                }
+            } catch (InvocationTargetException e) {
+                throw e.getTargetException();
+            }
+        } catch (AssumptionViolatedException e) {
+            throw new FailedBefore();
+        } catch (Throwable e) {
+            addFailure(e);
+            throw new FailedBefore();
+        }
+    }
+
+    private void runAfters() {
+        List<Method> afters = testMethod.getAfters();
+        for (Method after : afters) {
+            try {
+                after.invoke(test);
+            } catch (InvocationTargetException e) {
+                addFailure(e.getTargetException());
+            } catch (Throwable e) {
+                addFailure(e); // Untested, but seems impossible
+            }
+        }
+    }
+
+    protected void addFailure(Throwable e) {
+        notifier.fireTestFailure(new Failure(description, e));
+    }
 }
 
diff --git a/src/main/java/org/junit/internal/runners/MethodValidator.java b/src/main/java/org/junit/internal/runners/MethodValidator.java
index cadc93f..ba9c9d1 100644
--- a/src/main/java/org/junit/internal/runners/MethodValidator.java
+++ b/src/main/java/org/junit/internal/runners/MethodValidator.java
@@ -15,77 +15,83 @@
 
 /**
  * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
- *             removed in the next release. Please use
+ *             removed in the next major release. Please use
  *             {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
  */
 @Deprecated
 public class MethodValidator {
 
-	private final List<Throwable> fErrors= new ArrayList<Throwable>();
+    private final List<Throwable> errors = new ArrayList<Throwable>();
 
-	private TestClass fTestClass;
+    private TestClass testClass;
 
-	public MethodValidator(TestClass testClass) {
-		fTestClass = testClass;
-	}
+    public MethodValidator(TestClass testClass) {
+        this.testClass = testClass;
+    }
 
-	public void validateInstanceMethods() {
-		validateTestMethods(After.class, false);
-		validateTestMethods(Before.class, false);
-		validateTestMethods(Test.class, false);
-		
-		List<Method> methods= fTestClass.getAnnotatedMethods(Test.class);
-		if (methods.size() == 0)
-			fErrors.add(new Exception("No runnable methods"));
-	}
+    public void validateInstanceMethods() {
+        validateTestMethods(After.class, false);
+        validateTestMethods(Before.class, false);
+        validateTestMethods(Test.class, false);
 
-	public void validateStaticMethods() {
-		validateTestMethods(BeforeClass.class, true);
-		validateTestMethods(AfterClass.class, true);
-	}
-	
-	public List<Throwable> validateMethodsForDefaultRunner() {
-		validateNoArgConstructor();
-		validateStaticMethods();
-		validateInstanceMethods();
-		return fErrors;
-	}
-	
-	public void assertValid() throws InitializationError {
-		if (!fErrors.isEmpty())
-			throw new InitializationError(fErrors);
-	}
+        List<Method> methods = testClass.getAnnotatedMethods(Test.class);
+        if (methods.size() == 0) {
+            errors.add(new Exception("No runnable methods"));
+        }
+    }
 
-	public void validateNoArgConstructor() {
-		try {
-			fTestClass.getConstructor();
-		} catch (Exception e) {
-			fErrors.add(new Exception("Test class should have public zero-argument constructor", e));
-		}
-	}
+    public void validateStaticMethods() {
+        validateTestMethods(BeforeClass.class, true);
+        validateTestMethods(AfterClass.class, true);
+    }
 
-	private void validateTestMethods(Class<? extends Annotation> annotation,
-			boolean isStatic) {
-		List<Method> methods= fTestClass.getAnnotatedMethods(annotation);
-		
-		for (Method each : methods) {
-			if (Modifier.isStatic(each.getModifiers()) != isStatic) {
-				String state= isStatic ? "should" : "should not";
-				fErrors.add(new Exception("Method " + each.getName() + "() "
+    public List<Throwable> validateMethodsForDefaultRunner() {
+        validateNoArgConstructor();
+        validateStaticMethods();
+        validateInstanceMethods();
+        return errors;
+    }
+
+    public void assertValid() throws InitializationError {
+        if (!errors.isEmpty()) {
+            throw new InitializationError(errors);
+        }
+    }
+
+    public void validateNoArgConstructor() {
+        try {
+            testClass.getConstructor();
+        } catch (Exception e) {
+            errors.add(new Exception("Test class should have public zero-argument constructor", e));
+        }
+    }
+
+    private void validateTestMethods(Class<? extends Annotation> annotation,
+            boolean isStatic) {
+        List<Method> methods = testClass.getAnnotatedMethods(annotation);
+
+        for (Method each : methods) {
+            if (Modifier.isStatic(each.getModifiers()) != isStatic) {
+                String state = isStatic ? "should" : "should not";
+                errors.add(new Exception("Method " + each.getName() + "() "
 						+ state + " be static"));
-			}
-			if (!Modifier.isPublic(each.getDeclaringClass().getModifiers()))
-				fErrors.add(new Exception("Class " + each.getDeclaringClass().getName()
+            }
+            if (!Modifier.isPublic(each.getDeclaringClass().getModifiers())) {
+                errors.add(new Exception("Class " + each.getDeclaringClass().getName()
 						+ " should be public"));
-			if (!Modifier.isPublic(each.getModifiers()))
-				fErrors.add(new Exception("Method " + each.getName()
+            }
+            if (!Modifier.isPublic(each.getModifiers())) {
+                errors.add(new Exception("Method " + each.getName()
 						+ " should be public"));
-			if (each.getReturnType() != Void.TYPE)
-				fErrors.add(new Exception("Method " + each.getName()
+            }
+            if (each.getReturnType() != Void.TYPE) {
+                errors.add(new Exception("Method " + each.getName()
 						+ " should be void"));
-			if (each.getParameterTypes().length != 0)
-				fErrors.add(new Exception("Method " + each.getName()
+            }
+            if (each.getParameterTypes().length != 0) {
+                errors.add(new Exception("Method " + each.getName()
 						+ " should have no parameters"));
-		}
-	}
+            }
+        }
+    }
 }
diff --git a/src/main/java/org/junit/internal/runners/SuiteMethod.java b/src/main/java/org/junit/internal/runners/SuiteMethod.java
index 4e8bebc..e336983 100644
--- a/src/main/java/org/junit/internal/runners/SuiteMethod.java
+++ b/src/main/java/org/junit/internal/runners/SuiteMethod.java
@@ -6,7 +6,8 @@
 
 import junit.framework.Test;
 
-/** Runner for use with JUnit 3.8.x-style AllTests classes
+/**
+ * Runner for use with JUnit 3.8.x-style AllTests classes
  * (those that only implement a static <code>suite()</code>
  * method). For example:
  * <pre>
@@ -19,22 +20,22 @@
  * </pre>
  */
 public class SuiteMethod extends JUnit38ClassRunner {
-	public SuiteMethod(Class<?> klass) throws Throwable {
-		super(testFromSuiteMethod(klass));
-	}
+    public SuiteMethod(Class<?> klass) throws Throwable {
+        super(testFromSuiteMethod(klass));
+    }
 
-	public static Test testFromSuiteMethod(Class<?> klass) throws Throwable {
-		Method suiteMethod= null;
-		Test suite= null;
-		try {
-			suiteMethod= klass.getMethod("suite");
-			if (! Modifier.isStatic(suiteMethod.getModifiers())) {
-				throw new Exception(klass.getName() + ".suite() must be static");
-			}
-			suite= (Test) suiteMethod.invoke(null); // static method
-		} catch (InvocationTargetException e) {
-			throw e.getCause();
-		}
-		return suite;
-	}
+    public static Test testFromSuiteMethod(Class<?> klass) throws Throwable {
+        Method suiteMethod = null;
+        Test suite = null;
+        try {
+            suiteMethod = klass.getMethod("suite");
+            if (!Modifier.isStatic(suiteMethod.getModifiers())) {
+                throw new Exception(klass.getName() + ".suite() must be static");
+            }
+            suite = (Test) suiteMethod.invoke(null); // static method
+        } catch (InvocationTargetException e) {
+            throw e.getCause();
+        }
+        return suite;
+    }
 }
diff --git a/src/main/java/org/junit/internal/runners/TestClass.java b/src/main/java/org/junit/internal/runners/TestClass.java
index 1ca2b9d..1abaeea 100644
--- a/src/main/java/org/junit/internal/runners/TestClass.java
+++ b/src/main/java/org/junit/internal/runners/TestClass.java
@@ -11,92 +11,99 @@
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.junit.internal.MethodSorter;
 import org.junit.runners.BlockJUnit4ClassRunner;
 
 /**
  * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
- *             removed in the next release. Please use
+ *             removed in the next major release. Please use
  *             {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
  */
 @Deprecated
 public class TestClass {
-	private final Class<?> fClass;
-	
-	public TestClass(Class<?> klass) {
-		fClass= klass;
-	}
+    private final Class<?> klass;
 
-	public List<Method> getTestMethods() {
-		return getAnnotatedMethods(Test.class);
-	}
+    public TestClass(Class<?> klass) {
+        this.klass = klass;
+    }
 
-	List<Method> getBefores() {
-		return getAnnotatedMethods(BeforeClass.class);
-	}
+    public List<Method> getTestMethods() {
+        return getAnnotatedMethods(Test.class);
+    }
 
-	List<Method> getAfters() {
-		return getAnnotatedMethods(AfterClass.class);
-	}
-	
-	public List<Method> getAnnotatedMethods(Class<? extends Annotation> annotationClass) {
-		List<Method> results= new ArrayList<Method>();
-		for (Class<?> eachClass : getSuperClasses(fClass)) {
-			Method[] methods= eachClass.getDeclaredMethods();
-			for (Method eachMethod : methods) {
-				Annotation annotation= eachMethod.getAnnotation(annotationClass);
-				if (annotation != null && ! isShadowed(eachMethod, results)) 
-					results.add(eachMethod);
-			}
-		}
-		if (runsTopToBottom(annotationClass))
-			Collections.reverse(results);
-		return results;
-	}
+    List<Method> getBefores() {
+        return getAnnotatedMethods(BeforeClass.class);
+    }
 
-	private boolean runsTopToBottom(Class< ? extends Annotation> annotation) {
-		return annotation.equals(Before.class) || annotation.equals(BeforeClass.class);
-	}
-	
-	private boolean isShadowed(Method method, List<Method> results) {
-		for (Method each : results) {
-			if (isShadowed(method, each))
-				return true;
-		}
-		return false;
-	}
+    List<Method> getAfters() {
+        return getAnnotatedMethods(AfterClass.class);
+    }
 
-	private boolean isShadowed(Method current, Method previous) {
-		if (! previous.getName().equals(current.getName()))
-			return false;
-		if (previous.getParameterTypes().length != current.getParameterTypes().length)
-			return false;
-		for (int i= 0; i < previous.getParameterTypes().length; i++) {
-			if (! previous.getParameterTypes()[i].equals(current.getParameterTypes()[i]))
-				return false;
-		}
-		return true;
-	}
+    public List<Method> getAnnotatedMethods(Class<? extends Annotation> annotationClass) {
+        List<Method> results = new ArrayList<Method>();
+        for (Class<?> eachClass : getSuperClasses(klass)) {
+            Method[] methods = MethodSorter.getDeclaredMethods(eachClass);
+            for (Method eachMethod : methods) {
+                Annotation annotation = eachMethod.getAnnotation(annotationClass);
+                if (annotation != null && !isShadowed(eachMethod, results)) {
+                    results.add(eachMethod);
+                }
+            }
+        }
+        if (runsTopToBottom(annotationClass)) {
+            Collections.reverse(results);
+        }
+        return results;
+    }
 
-	private List<Class<?>> getSuperClasses(Class< ?> testClass) {
-		ArrayList<Class<?>> results= new ArrayList<Class<?>>();
-		Class<?> current= testClass;
-		while (current != null) {
-			results.add(current);
-			current= current.getSuperclass();
-		}
-		return results;
-	}
+    private boolean runsTopToBottom(Class<? extends Annotation> annotation) {
+        return annotation.equals(Before.class) || annotation.equals(BeforeClass.class);
+    }
 
-	public Constructor<?> getConstructor() throws SecurityException, NoSuchMethodException {
-		return fClass.getConstructor();
-	}
+    private boolean isShadowed(Method method, List<Method> results) {
+        for (Method each : results) {
+            if (isShadowed(method, each)) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-	public Class<?> getJavaClass() {
-		return fClass;
-	}
+    private boolean isShadowed(Method current, Method previous) {
+        if (!previous.getName().equals(current.getName())) {
+            return false;
+        }
+        if (previous.getParameterTypes().length != current.getParameterTypes().length) {
+            return false;
+        }
+        for (int i = 0; i < previous.getParameterTypes().length; i++) {
+            if (!previous.getParameterTypes()[i].equals(current.getParameterTypes()[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-	public String getName() {
-		return fClass.getName();
-	}
+    private List<Class<?>> getSuperClasses(Class<?> testClass) {
+        ArrayList<Class<?>> results = new ArrayList<Class<?>>();
+        Class<?> current = testClass;
+        while (current != null) {
+            results.add(current);
+            current = current.getSuperclass();
+        }
+        return results;
+    }
+
+    public Constructor<?> getConstructor() throws SecurityException, NoSuchMethodException {
+        return klass.getConstructor();
+    }
+
+    public Class<?> getJavaClass() {
+        return klass;
+    }
+
+    public String getName() {
+        return klass.getName();
+    }
 
 }
diff --git a/src/main/java/org/junit/internal/runners/TestMethod.java b/src/main/java/org/junit/internal/runners/TestMethod.java
index a06213c..821e193 100644
--- a/src/main/java/org/junit/internal/runners/TestMethod.java
+++ b/src/main/java/org/junit/internal/runners/TestMethod.java
@@ -13,57 +13,59 @@
 
 /**
  * @deprecated Included for backwards compatibility with JUnit 4.4. Will be
- *             removed in the next release. Please use
+ *             removed in the next major release. Please use
  *             {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}.
  */
 @Deprecated
 public class TestMethod {
-	private final Method fMethod;
-	private TestClass fTestClass;
+    private final Method method;
+    private TestClass testClass;
 
-	public TestMethod(Method method, TestClass testClass) {
-		fMethod= method;
-		fTestClass= testClass;
-	}
+    public TestMethod(Method method, TestClass testClass) {
+        this.method = method;
+        this.testClass = testClass;
+    }
 
-	public boolean isIgnored() {
-		return fMethod.getAnnotation(Ignore.class) != null;
-	}
+    public boolean isIgnored() {
+        return method.getAnnotation(Ignore.class) != null;
+    }
 
-	public long getTimeout() {
-		Test annotation= fMethod.getAnnotation(Test.class);
-		if (annotation == null)
-			return 0;
-		long timeout= annotation.timeout();
-		return timeout;
-	}
+    public long getTimeout() {
+        Test annotation = method.getAnnotation(Test.class);
+        if (annotation == null) {
+            return 0;
+        }
+        long timeout = annotation.timeout();
+        return timeout;
+    }
 
-	protected Class<? extends Throwable> getExpectedException() {
-		Test annotation= fMethod.getAnnotation(Test.class);
-		if (annotation == null || annotation.expected() == None.class)
-			return null;
-		else
-			return annotation.expected();
-	}
+    protected Class<? extends Throwable> getExpectedException() {
+        Test annotation = method.getAnnotation(Test.class);
+        if (annotation == null || annotation.expected() == None.class) {
+            return null;
+        } else {
+            return annotation.expected();
+        }
+    }
 
-	boolean isUnexpected(Throwable exception) {
-		return ! getExpectedException().isAssignableFrom(exception.getClass());
-	}
+    boolean isUnexpected(Throwable exception) {
+        return !getExpectedException().isAssignableFrom(exception.getClass());
+    }
 
-	boolean expectsException() {
-		return getExpectedException() != null;
-	}
+    boolean expectsException() {
+        return getExpectedException() != null;
+    }
 
-	List<Method> getBefores() {
-		return fTestClass.getAnnotatedMethods(Before.class);
-	}
+    List<Method> getBefores() {
+        return testClass.getAnnotatedMethods(Before.class);
+    }
 
-	List<Method> getAfters() {
-		return fTestClass.getAnnotatedMethods(After.class);
-	}
+    List<Method> getAfters() {
+        return testClass.getAnnotatedMethods(After.class);
+    }
 
-	public void invoke(Object test) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-		fMethod.invoke(test);
-	}
+    public void invoke(Object test) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+        method.invoke(test);
+    }
 
 }
diff --git a/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java b/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java
index a7d534c..e094809 100644
--- a/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java
+++ b/src/main/java/org/junit/internal/runners/model/EachTestNotifier.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.runners.model;
 
 import org.junit.internal.AssumptionViolatedException;
@@ -10,42 +7,42 @@
 import org.junit.runners.model.MultipleFailureException;
 
 public class EachTestNotifier {
-	private final RunNotifier fNotifier;
+    private final RunNotifier notifier;
 
-	private final Description fDescription;
+    private final Description description;
 
-	public EachTestNotifier(RunNotifier notifier, Description description) {
-		fNotifier= notifier;
-		fDescription= description;
-	}
+    public EachTestNotifier(RunNotifier notifier, Description description) {
+        this.notifier = notifier;
+        this.description = description;
+    }
 
-	public void addFailure(Throwable targetException) {
-		if (targetException instanceof MultipleFailureException) {
-			addMultipleFailureException((MultipleFailureException) targetException);
-		} else {
-			fNotifier
-					.fireTestFailure(new Failure(fDescription, targetException));
-		}
-	}
+    public void addFailure(Throwable targetException) {
+        if (targetException instanceof MultipleFailureException) {
+            addMultipleFailureException((MultipleFailureException) targetException);
+        } else {
+            notifier.fireTestFailure(new Failure(description, targetException));
+        }
+    }
 
-	private void addMultipleFailureException(MultipleFailureException mfe) {
-		for (Throwable each : mfe.getFailures())
-			addFailure(each);
-	}
+    private void addMultipleFailureException(MultipleFailureException mfe) {
+        for (Throwable each : mfe.getFailures()) {
+            addFailure(each);
+        }
+    }
 
-	public void addFailedAssumption(AssumptionViolatedException e) {
-		fNotifier.fireTestAssumptionFailed(new Failure(fDescription, e));
-	}
+    public void addFailedAssumption(AssumptionViolatedException e) {
+        notifier.fireTestAssumptionFailed(new Failure(description, e));
+    }
 
-	public void fireTestFinished() {
-		fNotifier.fireTestFinished(fDescription);
-	}
+    public void fireTestFinished() {
+        notifier.fireTestFinished(description);
+    }
 
-	public void fireTestStarted() {
-		fNotifier.fireTestStarted(fDescription);
-	}
+    public void fireTestStarted() {
+        notifier.fireTestStarted(description);
+    }
 
-	public void fireTestIgnored() {
-		fNotifier.fireTestIgnored(fDescription);
-	}
+    public void fireTestIgnored() {
+        notifier.fireTestIgnored(description);
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java b/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java
index 3316806..054f042 100644
--- a/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java
+++ b/src/main/java/org/junit/internal/runners/model/MultipleFailureException.java
@@ -4,9 +4,9 @@
 
 @Deprecated
 public class MultipleFailureException extends org.junit.runners.model.MultipleFailureException {
-	private static final long serialVersionUID= 1L;
+    private static final long serialVersionUID = 1L;
 
-	public MultipleFailureException(List<Throwable> errors) {
-		super(errors);
-	}
+    public MultipleFailureException(List<Throwable> errors) {
+        super(errors);
+    }
 }
diff --git a/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java b/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java
index 9150d90..79d5c05 100644
--- a/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java
+++ b/src/main/java/org/junit/internal/runners/model/ReflectiveCallable.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.runners.model;
 
 import java.lang.reflect.InvocationTargetException;
@@ -10,13 +7,13 @@
  * wrapping it in an InvocationTargetException.
  */
 public abstract class ReflectiveCallable {
-	public Object run() throws Throwable {
-		try {
-			return runReflectiveCall();
-		} catch (InvocationTargetException e) {
-			throw e.getTargetException();
-		}
-	}
+    public Object run() throws Throwable {
+        try {
+            return runReflectiveCall();
+        } catch (InvocationTargetException e) {
+            throw e.getTargetException();
+        }
+    }
 
-	protected abstract Object runReflectiveCall() throws Throwable;
+    protected abstract Object runReflectiveCall() throws Throwable;
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java b/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java
deleted file mode 100644
index e7df8bf..0000000
--- a/src/main/java/org/junit/internal/runners/rules/RuleFieldValidator.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package org.junit.internal.runners.rules;
-
-import java.lang.annotation.Annotation;
-import java.util.List;
-
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.rules.TestRule;
-import org.junit.runners.model.FrameworkField;
-import org.junit.runners.model.TestClass;
-
-/**
- * A RuleFieldValidator validates the rule fields of a
- * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the
- * {@code TestClass} are written to a list of errors.
- * 
- * There are two slightly different validators. The {@link #CLASS_RULE_VALIDATOR}
- * validates fields with a {@link ClassRule} annotation and the
- * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation.
- */
-public enum RuleFieldValidator {
-	/**
-	 * Validates fields with a {@link ClassRule} annotation.
-	 */
-	CLASS_RULE_VALIDATOR(ClassRule.class, true),
-	/**
-	 * Validates fields with a {@link Rule} annotation.
-	 */
-	RULE_VALIDATOR(Rule.class, false);
-
-	private final Class<? extends Annotation> fAnnotation;
-
-	private final boolean fOnlyStaticFields;
-
-	private RuleFieldValidator(Class<? extends Annotation> annotation,
-			boolean onlyStaticFields) {
-		this.fAnnotation= annotation;
-		this.fOnlyStaticFields= onlyStaticFields;
-	}
-
-	/**
-	 * Validate the {@link org.junit.runners.model.TestClass} and adds reasons
-	 * for rejecting the class to a list of errors.
-	 * @param target the {@code TestClass} to validate.
-	 * @param errors the list of errors.
-	 */
-	public void validate(TestClass target, List<Throwable> errors) {
-		List<FrameworkField> fields= target.getAnnotatedFields(fAnnotation);
-		for (FrameworkField each : fields)
-			validateField(each, errors);
-	}
-
-	private void validateField(FrameworkField field, List<Throwable> errors) {
-		optionallyValidateStatic(field, errors);
-		validatePublic(field, errors);
-		validateTestRuleOrMethodRule(field, errors);
-	}
-
-	private void optionallyValidateStatic(FrameworkField field,
-			List<Throwable> errors) {
-		if (fOnlyStaticFields && !field.isStatic())
-			addError(errors, field, "must be static.");
-	}
-
-	private void validatePublic(FrameworkField field, List<Throwable> errors) {
-		if (!field.isPublic())
-			addError(errors, field, "must be public.");
-	}
-
-	private void validateTestRuleOrMethodRule(FrameworkField field,
-			List<Throwable> errors) {
-		if (!isMethodRule(field) && !isTestRule(field))
-			addError(errors, field, "must implement MethodRule or TestRule.");
-	}
-
-	private boolean isTestRule(FrameworkField target) {
-		return TestRule.class.isAssignableFrom(target.getType());
-	}
-
-	@SuppressWarnings("deprecation")
-	private boolean isMethodRule(FrameworkField target) {
-		return org.junit.rules.MethodRule.class.isAssignableFrom(target
-				.getType());
-	}
-
-	private void addError(List<Throwable> errors, FrameworkField field,
-			String suffix) {
-		String message= "The @" + fAnnotation.getSimpleName() + " '"
-				+ field.getName() + "' " + suffix;
-		errors.add(new Exception(message));
-	}
-}
diff --git a/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java b/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java
index 23f921b..36de4f1 100644
--- a/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java
+++ b/src/main/java/org/junit/internal/runners/rules/RuleMemberValidator.java
@@ -1,91 +1,279 @@
 package org.junit.internal.runners.rules;
 
-import java.lang.annotation.Annotation;
-import java.util.List;
 import org.junit.ClassRule;
 import org.junit.Rule;
+import org.junit.rules.MethodRule;
 import org.junit.rules.TestRule;
-import org.junit.runners.model.FrameworkField;
+import org.junit.runners.model.FrameworkMember;
 import org.junit.runners.model.TestClass;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
- * A RuleFieldValidator validates the rule fields of a
- * {@link TestClass}. All reasons for rejecting the
+ * A RuleMemberValidator validates the rule fields/methods of a
+ * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the
  * {@code TestClass} are written to a list of errors.
  *
- * There are two slightly different validators. The {@link #CLASS_RULE_VALIDATOR}
+ * <p>There are four slightly different validators. The {@link #CLASS_RULE_VALIDATOR}
  * validates fields with a {@link ClassRule} annotation and the
- * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation.
+ * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation.</p>
+ *
+ * <p>The {@link #CLASS_RULE_METHOD_VALIDATOR}
+ * validates methods with a {@link ClassRule} annotation and the
+ * {@link #RULE_METHOD_VALIDATOR} validates methods with a {@link Rule} annotation.</p>
  */
-public enum RuleMemberValidator {
-	/**
-	 * Validates fields with a {@link ClassRule} annotation.
-	 */
-	CLASS_RULE_VALIDATOR(ClassRule.class, true),
-	/**
-	 * Validates fields with a {@link Rule} annotation.
-	 */
-	RULE_VALIDATOR(Rule.class, false);
+public class RuleMemberValidator {
+    /**
+     * Validates fields with a {@link ClassRule} annotation.
+     */
+    public static final RuleMemberValidator CLASS_RULE_VALIDATOR =
+            classRuleValidatorBuilder()
+            .withValidator(new DeclaringClassMustBePublic())
+            .withValidator(new MemberMustBeStatic())
+            .withValidator(new MemberMustBePublic())
+            .withValidator(new FieldMustBeATestRule())
+            .build();
+    /**
+     * Validates fields with a {@link Rule} annotation.
+     */
+    public static final RuleMemberValidator RULE_VALIDATOR =
+            testRuleValidatorBuilder()
+            .withValidator(new MemberMustBeNonStaticOrAlsoClassRule())
+            .withValidator(new MemberMustBePublic())
+            .withValidator(new FieldMustBeARule())
+            .build();
+    /**
+     * Validates methods with a {@link ClassRule} annotation.
+     */
+    public static final RuleMemberValidator CLASS_RULE_METHOD_VALIDATOR =
+            classRuleValidatorBuilder()
+            .forMethods()
+            .withValidator(new DeclaringClassMustBePublic())
+            .withValidator(new MemberMustBeStatic())
+            .withValidator(new MemberMustBePublic())
+            .withValidator(new MethodMustBeATestRule())
+            .build();
 
-	private final Class<? extends Annotation> fAnnotation;
+    /**
+     * Validates methods with a {@link Rule} annotation.
+     */
+    public static final RuleMemberValidator RULE_METHOD_VALIDATOR =
+            testRuleValidatorBuilder()
+            .forMethods()
+            .withValidator(new MemberMustBeNonStaticOrAlsoClassRule())
+            .withValidator(new MemberMustBePublic())
+            .withValidator(new MethodMustBeARule())
+            .build();
 
-	private final boolean fOnlyStaticFields;
+    private final Class<? extends Annotation> annotation;
+    private final boolean methods;
+    private final List<RuleValidator> validatorStrategies;
 
-	private RuleMemberValidator(Class<? extends Annotation> annotation,
-			boolean onlyStaticFields) {
-		this.fAnnotation= annotation;
-		this.fOnlyStaticFields= onlyStaticFields;
-	}
+    RuleMemberValidator(Builder builder) {
+        this.annotation = builder.annotation;
+        this.methods = builder.methods;
+        this.validatorStrategies = builder.validators;
+    }
 
-	/**
-	 * Validate the {@link TestClass} and adds reasons
-	 * for rejecting the class to a list of errors.
-	 * @param target the {@code TestClass} to validate.
-	 * @param errors the list of errors.
-	 */
-	public void validate(TestClass target, List<Throwable> errors) {
-		List<FrameworkField> fields= target.getAnnotatedFields(fAnnotation);
-		for (FrameworkField each : fields)
-			validateField(each, errors);
-	}
+    /**
+     * Validate the {@link org.junit.runners.model.TestClass} and adds reasons
+     * for rejecting the class to a list of errors.
+     *
+     * @param target the {@code TestClass} to validate.
+     * @param errors the list of errors.
+     */
+    public void validate(TestClass target, List<Throwable> errors) {
+        List<? extends FrameworkMember<?>> members = methods ? target.getAnnotatedMethods(annotation)
+                : target.getAnnotatedFields(annotation);
 
-	private void validateField(FrameworkField field, List<Throwable> errors) {
-		optionallyValidateStatic(field, errors);
-		validatePublic(field, errors);
-		validateTestRuleOrMethodRule(field, errors);
-	}
+        for (FrameworkMember<?> each : members) {
+            validateMember(each, errors);
+        }
+    }
 
-	private void optionallyValidateStatic(FrameworkField field,
-			List<Throwable> errors) {
-		if (fOnlyStaticFields && !field.isStatic())
-			addError(errors, field, "must be static.");
-	}
+    private void validateMember(FrameworkMember<?> member, List<Throwable> errors) {
+        for (RuleValidator strategy : validatorStrategies) {
+            strategy.validate(member, annotation, errors);
+        }
+    }
 
-	private void validatePublic(FrameworkField field, List<Throwable> errors) {
-		if (!field.isPublic())
-			addError(errors, field, "must be public.");
-	}
+    private static Builder classRuleValidatorBuilder() {
+        return new Builder(ClassRule.class);
+    }
 
-	private void validateTestRuleOrMethodRule(FrameworkField field,
-			List<Throwable> errors) {
-		if (!isMethodRule(field) && !isTestRule(field))
-			addError(errors, field, "must implement MethodRule or TestRule.");
-	}
+    private static Builder testRuleValidatorBuilder() {
+        return new Builder(Rule.class);
+    }
 
-	private boolean isTestRule(FrameworkField target) {
-		return TestRule.class.isAssignableFrom(target.getType());
-	}
+    private static class Builder {
+        private final Class<? extends Annotation> annotation;
+        private boolean methods;
+        private final List<RuleValidator> validators;
 
-	@SuppressWarnings("deprecation")
-	private boolean isMethodRule(FrameworkField target) {
-		return org.junit.rules.MethodRule.class.isAssignableFrom(target
-				.getType());
-	}
+        private Builder(Class<? extends Annotation> annotation) {
+            this.annotation = annotation;
+            this.methods = false;
+            this.validators = new ArrayList<RuleValidator>();
+        }
 
-	private void addError(List<Throwable> errors, FrameworkField field,
-			String suffix) {
-		String message= "The @" + fAnnotation.getSimpleName() + " '"
-				+ field.getName() + "' " + suffix;
-		errors.add(new Exception(message));
-	}
+        Builder forMethods() {
+            methods = true;
+            return this;
+        }
+
+        Builder withValidator(RuleValidator validator) {
+            validators.add(validator);
+            return this;
+        }
+
+        RuleMemberValidator build() {
+            return new RuleMemberValidator(this);
+        }
+    }
+
+    private static boolean isRuleType(FrameworkMember<?> member) {
+        return isMethodRule(member) || isTestRule(member);
+    }
+
+    private static boolean isTestRule(FrameworkMember<?> member) {
+        return TestRule.class.isAssignableFrom(member.getType());
+    }
+
+    private static boolean isMethodRule(FrameworkMember<?> member) {
+        return MethodRule.class.isAssignableFrom(member.getType());
+    }
+
+    /**
+     * Encapsulates a single piece of validation logic, used to determine if {@link org.junit.Rule} and
+     * {@link org.junit.ClassRule} annotations have been used correctly
+     */
+    interface RuleValidator {
+        /**
+         * Examine the given member and add any violations of the strategy's validation logic to the given list of errors
+         * @param member The member (field or member) to examine
+         * @param annotation The type of rule annotation on the member
+         * @param errors The list of errors to add validation violations to
+         */
+        void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors);
+    }
+
+    /**
+     * Requires the validated member to be non-static
+     */
+    private static final class MemberMustBeNonStaticOrAlsoClassRule implements RuleValidator {
+        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {
+            boolean isMethodRuleMember = isMethodRule(member);
+            boolean isClassRuleAnnotated = (member.getAnnotation(ClassRule.class) != null);
+
+            // We disallow:
+            //  - static MethodRule members
+            //  - static @Rule annotated members
+            //    - UNLESS they're also @ClassRule annotated
+            // Note that MethodRule cannot be annotated with @ClassRule
+            if (member.isStatic() && (isMethodRuleMember || !isClassRuleAnnotated)) {
+                String message;
+                if (isMethodRule(member)) {
+                    message = "must not be static.";
+                } else {
+                    message = "must not be static or it must be annotated with @ClassRule.";
+                }
+                errors.add(new ValidationError(member, annotation, message));
+            }
+        }
+    }
+
+    /**
+     * Requires the member to be static
+     */
+    private static final class MemberMustBeStatic implements RuleValidator {
+        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {
+            if (!member.isStatic()) {
+                errors.add(new ValidationError(member, annotation,
+                        "must be static."));
+            }
+        }
+    }
+
+    /**
+     * Requires the member's declaring class to be public
+     */
+    private static final class DeclaringClassMustBePublic implements RuleValidator {
+        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {
+            if (!isDeclaringClassPublic(member)) {
+                errors.add(new ValidationError(member, annotation,
+                        "must be declared in a public class."));
+            }
+        }
+
+        private boolean isDeclaringClassPublic(FrameworkMember<?> member) {
+            return Modifier.isPublic(member.getDeclaringClass().getModifiers());
+        }
+    }
+
+    /**
+     * Requires the member to be public
+     */
+    private static final class MemberMustBePublic implements RuleValidator {
+        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {
+            if (!member.isPublic()) {
+                errors.add(new ValidationError(member, annotation,
+                        "must be public."));
+            }
+        }
+    }
+
+    /**
+     * Requires the member is a field implementing {@link org.junit.rules.MethodRule} or {@link org.junit.rules.TestRule}
+     */
+    private static final class FieldMustBeARule implements RuleValidator {
+        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {
+            if (!isRuleType(member)) {
+                errors.add(new ValidationError(member, annotation,
+                        "must implement MethodRule or TestRule."));
+            }
+        }
+    }
+
+    /**
+     * Require the member to return an implementation of {@link org.junit.rules.MethodRule} or
+     * {@link org.junit.rules.TestRule}
+     */
+    private static final class MethodMustBeARule implements RuleValidator {
+        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {
+            if (!isRuleType(member)) {
+                errors.add(new ValidationError(member, annotation,
+                        "must return an implementation of MethodRule or TestRule."));
+            }
+        }
+    }
+    
+    /**
+     * Require the member to return an implementation of {@link org.junit.rules.TestRule}
+     */
+    private static final class MethodMustBeATestRule implements RuleValidator {
+        public void validate(FrameworkMember<?> member,
+                Class<? extends Annotation> annotation, List<Throwable> errors) {
+            if (!isTestRule(member)) {
+                errors.add(new ValidationError(member, annotation, 
+                        "must return an implementation of TestRule."));
+            }
+        }
+    }
+    
+    /**
+     * Requires the member is a field implementing {@link org.junit.rules.TestRule}
+     */
+    private static final class FieldMustBeATestRule implements RuleValidator {
+
+        public void validate(FrameworkMember<?> member,
+                Class<? extends Annotation> annotation, List<Throwable> errors) {
+            if (!isTestRule(member)) {
+                errors.add(new ValidationError(member, annotation,
+                        "must implement TestRule."));
+            }
+        }
+    }
 }
diff --git a/src/main/java/org/junit/internal/runners/rules/ValidationError.java b/src/main/java/org/junit/internal/runners/rules/ValidationError.java
new file mode 100644
index 0000000..d1af8ae
--- /dev/null
+++ b/src/main/java/org/junit/internal/runners/rules/ValidationError.java
@@ -0,0 +1,11 @@
+package org.junit.internal.runners.rules;
+
+import org.junit.runners.model.FrameworkMember;
+
+import java.lang.annotation.Annotation;
+
+class ValidationError extends Exception {
+    public ValidationError(FrameworkMember<?> member, Class<? extends Annotation> annotation, String suffix) {
+        super(String.format("The @%s '%s' %s", annotation.getSimpleName(), member.getName(), suffix));
+    }
+}
diff --git a/src/main/java/org/junit/internal/runners/statements/ExpectException.java b/src/main/java/org/junit/internal/runners/statements/ExpectException.java
index ddfef07..d0636bd 100644
--- a/src/main/java/org/junit/internal/runners/statements/ExpectException.java
+++ b/src/main/java/org/junit/internal/runners/statements/ExpectException.java
@@ -1,38 +1,36 @@
-/**
- * 
- */
 package org.junit.internal.runners.statements;
 
 import org.junit.internal.AssumptionViolatedException;
 import org.junit.runners.model.Statement;
 
 public class ExpectException extends Statement {
-	private Statement fNext;
-	private final Class<? extends Throwable> fExpected;
-	
-	public ExpectException(Statement next, Class<? extends Throwable> expected) {
-		fNext= next;
-		fExpected= expected;
-	}
-	
-	@Override
-	public void evaluate() throws Exception {
-		boolean complete = false;
-		try {
-			fNext.evaluate();
-			complete = true;
-		} catch (AssumptionViolatedException e) {
-			throw e;
-		} catch (Throwable e) {
-			if (!fExpected.isAssignableFrom(e.getClass())) {
-				String message= "Unexpected exception, expected<"
-							+ fExpected.getName() + "> but was<"
-							+ e.getClass().getName() + ">";
-				throw new Exception(message, e);
-			}
-		}
-		if (complete)
-			throw new AssertionError("Expected exception: "
-					+ fExpected.getName());
-	}
+    private final Statement next;
+    private final Class<? extends Throwable> expected;
+
+    public ExpectException(Statement next, Class<? extends Throwable> expected) {
+        this.next = next;
+        this.expected = expected;
+    }
+
+    @Override
+    public void evaluate() throws Exception {
+        boolean complete = false;
+        try {
+            next.evaluate();
+            complete = true;
+        } catch (AssumptionViolatedException e) {
+            throw e;
+        } catch (Throwable e) {
+            if (!expected.isAssignableFrom(e.getClass())) {
+                String message = "Unexpected exception, expected<"
+                        + expected.getName() + "> but was<"
+                        + e.getClass().getName() + ">";
+                throw new Exception(message, e);
+            }
+        }
+        if (complete) {
+            throw new AssertionError("Expected exception: "
+                    + expected.getName());
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/statements/Fail.java b/src/main/java/org/junit/internal/runners/statements/Fail.java
index e7d0d5c..e55875c 100644
--- a/src/main/java/org/junit/internal/runners/statements/Fail.java
+++ b/src/main/java/org/junit/internal/runners/statements/Fail.java
@@ -2,16 +2,15 @@
 
 import org.junit.runners.model.Statement;
 
-
 public class Fail extends Statement {
-	private final Throwable fError;
+    private final Throwable error;
 
-	public Fail(Throwable e) {
-		fError= e;
-	}
+    public Fail(Throwable e) {
+        error = e;
+    }
 
-	@Override
-	public void evaluate() throws Throwable {
-		throw fError;
-	}
+    @Override
+    public void evaluate() throws Throwable {
+        throw error;
+    }
 }
diff --git a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java
index bff7c72..9fad35b 100644
--- a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java
+++ b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java
@@ -1,71 +1,161 @@
-/**
- * 
- */
 package org.junit.internal.runners.statements;
 
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
 import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestTimedOutException;
 
 public class FailOnTimeout extends Statement {
-	private final Statement fOriginalStatement;
+    private final Statement originalStatement;
+    private final TimeUnit timeUnit;
+    private final long timeout;
 
-	private final long fTimeout;
+    /**
+     * Returns a new builder for building an instance.
+     *
+     * @since 4.12
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
 
-	public FailOnTimeout(Statement originalStatement, long timeout) {
-		fOriginalStatement= originalStatement;
-		fTimeout= timeout;
-	}
+    /**
+     * Creates an instance wrapping the given statement with the given timeout in milliseconds.
+     *
+     * @param statement the statement to wrap
+     * @param timeoutMillis the timeout in milliseconds
+     * @deprecated use {@link #builder()} instead.
+     */
+    @Deprecated
+    public FailOnTimeout(Statement statement, long timeoutMillis) {
+        this(builder().withTimeout(timeoutMillis, TimeUnit.MILLISECONDS), statement);
+    }
 
-	@Override
-	public void evaluate() throws Throwable {
-		StatementThread thread= evaluateStatement();
-		if (!thread.fFinished)
-			throwExceptionForUnfinishedThread(thread);
-	}
+    private FailOnTimeout(Builder builder, Statement statement) {
+        originalStatement = statement;
+        timeout = builder.timeout;
+        timeUnit = builder.unit;
+    }
 
-	private StatementThread evaluateStatement() throws InterruptedException {
-		StatementThread thread= new StatementThread(fOriginalStatement);
-		thread.start();
-		thread.join(fTimeout);
-		thread.interrupt();
-		return thread;
-	}
+    /**
+     * Builder for {@link FailOnTimeout}.
+     *
+     * @since 4.12
+     */
+    public static class Builder {
+        private long timeout = 0;
+        private TimeUnit unit = TimeUnit.SECONDS;
 
-	private void throwExceptionForUnfinishedThread(StatementThread thread)
-			throws Throwable {
-		if (thread.fExceptionThrownByOriginalStatement != null)
-			throw thread.fExceptionThrownByOriginalStatement;
-		else
-			throwTimeoutException(thread);
-	}
+        private Builder() {
+        }
 
-	private void throwTimeoutException(StatementThread thread) throws Exception {
-		Exception exception= new Exception(String.format(
-				"test timed out after %d milliseconds", fTimeout));
-		exception.setStackTrace(thread.getStackTrace());
-		throw exception;
-	}
+        /**
+         * Specifies the time to wait before timing out the test.
+         *
+         * <p>If this is not called, or is called with a {@code timeout} of
+         * {@code 0}, the returned {@code Statement} will wait forever for the
+         * test to complete, however the test will still launch from a separate
+         * thread. This can be useful for disabling timeouts in environments
+         * where they are dynamically set based on some property.
+         *
+         * @param timeout the maximum time to wait
+         * @param unit the time unit of the {@code timeout} argument
+         * @return {@code this} for method chaining.
+         */
+        public Builder withTimeout(long timeout, TimeUnit unit) {
+            if (timeout < 0) {
+                throw new IllegalArgumentException("timeout must be non-negative");
+            }
+            if (unit == null) {
+                throw new NullPointerException("TimeUnit cannot be null");
+            }
+            this.timeout = timeout;
+            this.unit = unit;
+            return this;
+        }
 
-	private static class StatementThread extends Thread {
-		private final Statement fStatement;
+        /**
+         * Builds a {@link FailOnTimeout} instance using the values in this builder,
+         * wrapping the given statement.
+         *
+         * @param statement
+         */
+        public FailOnTimeout build(Statement statement) {
+            if (statement == null) {
+                throw new NullPointerException("statement cannot be null");
+            }
+            return new FailOnTimeout(this, statement);
+        }
+    }
 
-		private boolean fFinished= false;
+    @Override
+    public void evaluate() throws Throwable {
+        CallableStatement callable = new CallableStatement();
+        FutureTask<Throwable> task = new FutureTask<Throwable>(callable);
+        Thread thread = new Thread(task, "Time-limited test");
+        thread.setDaemon(true);
+        thread.start();
+        callable.awaitStarted();
+        Throwable throwable = getResult(task, thread);
+        if (throwable != null) {
+            throw throwable;
+        }
+    }
 
-		private Throwable fExceptionThrownByOriginalStatement= null;
+    /**
+     * Wait for the test task, returning the exception thrown by the test if the
+     * test failed, an exception indicating a timeout if the test timed out, or
+     * {@code null} if the test passed.
+     */
+    private Throwable getResult(FutureTask<Throwable> task, Thread thread) {
+        try {
+            if (timeout > 0) {
+                return task.get(timeout, timeUnit);
+            } else {
+                return task.get();
+            }
+        } catch (InterruptedException e) {
+            return e; // caller will re-throw; no need to call Thread.interrupt()
+        } catch (ExecutionException e) {
+            // test failed; have caller re-throw the exception thrown by the test
+            return e.getCause();
+        } catch (TimeoutException e) {
+            return createTimeoutException(thread);
+        }
+    }
 
-		public StatementThread(Statement statement) {
-			fStatement= statement;
-		}
+    private Exception createTimeoutException(Thread thread) {
+        StackTraceElement[] stackTrace = thread.getStackTrace();
+        Exception currThreadException = new TestTimedOutException(timeout, timeUnit);
+        if (stackTrace != null) {
+            currThreadException.setStackTrace(stackTrace);
+            thread.interrupt();
+        }
+        return currThreadException;
+    }
 
-		@Override
-		public void run() {
-			try {
-				fStatement.evaluate();
-				fFinished= true;
-			} catch (InterruptedException e) {
-				//don't log the InterruptedException
-			} catch (Throwable e) {
-				fExceptionThrownByOriginalStatement= e;
-			}
-		}
-	}
-}
\ No newline at end of file
+    private class CallableStatement implements Callable<Throwable> {
+        private final CountDownLatch startLatch = new CountDownLatch(1);
+
+        public Throwable call() throws Exception {
+            try {
+                startLatch.countDown();
+                originalStatement.evaluate();
+            } catch (Exception e) {
+                throw e;
+            } catch (Throwable e) {
+                return e;
+            }
+            return null;
+        }
+
+        public void awaitStarted() throws InterruptedException {
+            startLatch.await();
+        }
+    }
+}
diff --git a/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java b/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java
index e2e81e1..68c0545 100644
--- a/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java
+++ b/src/main/java/org/junit/internal/runners/statements/InvokeMethod.java
@@ -1,22 +1,19 @@
-/**
- * 
- */
 package org.junit.internal.runners.statements;
 
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.Statement;
 
 public class InvokeMethod extends Statement {
-	private final FrameworkMethod fTestMethod;
-	private Object fTarget;
-	
-	public InvokeMethod(FrameworkMethod testMethod, Object target) {
-		fTestMethod= testMethod;
-		fTarget= target;
-	}
-	
-	@Override
-	public void evaluate() throws Throwable {
-		fTestMethod.invokeExplosively(fTarget);
-	}
+    private final FrameworkMethod testMethod;
+    private final Object target;
+
+    public InvokeMethod(FrameworkMethod testMethod, Object target) {
+        this.testMethod = testMethod;
+        this.target = target;
+    }
+
+    @Override
+    public void evaluate() throws Throwable {
+        testMethod.invokeExplosively(target);
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/statements/RunAfters.java b/src/main/java/org/junit/internal/runners/statements/RunAfters.java
index 475ec72..7512a7d 100644
--- a/src/main/java/org/junit/internal/runners/statements/RunAfters.java
+++ b/src/main/java/org/junit/internal/runners/statements/RunAfters.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.runners.statements;
 
 import java.util.ArrayList;
@@ -11,33 +8,34 @@
 import org.junit.runners.model.Statement;
 
 public class RunAfters extends Statement {
-	private final Statement fNext;
+    private final Statement next;
 
-	private final Object fTarget;
+    private final Object target;
 
-	private final List<FrameworkMethod> fAfters;
-	
-	public RunAfters(Statement next, List<FrameworkMethod> afters, Object target) {
-		fNext= next;
-		fAfters= afters;
-		fTarget= target;
-	}
+    private final List<FrameworkMethod> afters;
 
-	@Override
-	public void evaluate() throws Throwable {
-		List<Throwable> errors = new ArrayList<Throwable>();
-		try {
-			fNext.evaluate();
-		} catch (Throwable e) {
-			errors.add(e);
-		} finally {
-			for (FrameworkMethod each : fAfters)
-				try {
-					each.invokeExplosively(fTarget);
-				} catch (Throwable e) {
-					errors.add(e);
-				}
-		}
-		MultipleFailureException.assertEmpty(errors);
-	}
+    public RunAfters(Statement next, List<FrameworkMethod> afters, Object target) {
+        this.next = next;
+        this.afters = afters;
+        this.target = target;
+    }
+
+    @Override
+    public void evaluate() throws Throwable {
+        List<Throwable> errors = new ArrayList<Throwable>();
+        try {
+            next.evaluate();
+        } catch (Throwable e) {
+            errors.add(e);
+        } finally {
+            for (FrameworkMethod each : afters) {
+                try {
+                    each.invokeExplosively(target);
+                } catch (Throwable e) {
+                    errors.add(e);
+                }
+            }
+        }
+        MultipleFailureException.assertEmpty(errors);
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/internal/runners/statements/RunBefores.java b/src/main/java/org/junit/internal/runners/statements/RunBefores.java
index 66a34e1..238fbe7 100644
--- a/src/main/java/org/junit/internal/runners/statements/RunBefores.java
+++ b/src/main/java/org/junit/internal/runners/statements/RunBefores.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.internal.runners.statements;
 
 import java.util.List;
@@ -9,22 +6,23 @@
 import org.junit.runners.model.Statement;
 
 public class RunBefores extends Statement {
-	private final Statement fNext;
+    private final Statement next;
 
-	private final Object fTarget;
+    private final Object target;
 
-	private final List<FrameworkMethod> fBefores;
+    private final List<FrameworkMethod> befores;
 
-	public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) {
-		fNext= next;
-		fBefores= befores;
-		fTarget= target;
-	}
+    public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) {
+        this.next = next;
+        this.befores = befores;
+        this.target = target;
+    }
 
-	@Override
-	public void evaluate() throws Throwable {
-		for (FrameworkMethod before : fBefores)
-			before.invokeExplosively(fTarget);
-		fNext.evaluate();
-	}
+    @Override
+    public void evaluate() throws Throwable {
+        for (FrameworkMethod before : befores) {
+            before.invokeExplosively(target);
+        }
+        next.evaluate();
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/matchers/JUnitMatchers.java b/src/main/java/org/junit/matchers/JUnitMatchers.java
index 837ed33..847a347 100644
--- a/src/main/java/org/junit/matchers/JUnitMatchers.java
+++ b/src/main/java/org/junit/matchers/JUnitMatchers.java
@@ -79,5 +79,5 @@
 	 */
 	public static <T> CombinableMatcher<T> either(Matcher<T> matcher) {
 		return new CombinableMatcher<T>(matcher);
-	}	
+	}
 }
diff --git a/src/main/java/org/junit/package-info.java b/src/main/java/org/junit/package-info.java
index bb60d0d..fb12f25 100644
--- a/src/main/java/org/junit/package-info.java
+++ b/src/main/java/org/junit/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Provides JUnit core classes and annotations. 
- * 
+ *
  * Corresponds to junit.framework in Junit 3.x.
  *
  * @since 4.0
diff --git a/src/main/java/org/junit/rules/ErrorCollector.java b/src/main/java/org/junit/rules/ErrorCollector.java
index 3522a65..8c6600e 100644
--- a/src/main/java/org/junit/rules/ErrorCollector.java
+++ b/src/main/java/org/junit/rules/ErrorCollector.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.rules;
 
 import static org.junit.Assert.assertThat;
@@ -16,70 +13,72 @@
  * The ErrorCollector rule allows execution of a test to continue after the
  * first problem is found (for example, to collect _all_ the incorrect rows in a
  * table, and report them all at once):
- * 
+ *
  * <pre>
  * public static class UsesErrorCollectorTwice {
  * 	&#064;Rule
  * 	public ErrorCollector collector= new ErrorCollector();
- * 
- * 	&#064;Test
- * 	public void example() {
- * 		collector.addError(new Throwable(&quot;first thing went wrong&quot;));
- * 		collector.addError(new Throwable(&quot;second thing went wrong&quot;));
- * 		collector.checkThat(getResult(), not(containsString(&quot;ERROR!&quot;)));
- * 		// all lines will run, and then a combined failure logged at the end.
- * 	}
+ *
+ * &#064;Test
+ * public void example() {
+ *      collector.addError(new Throwable(&quot;first thing went wrong&quot;));
+ *      collector.addError(new Throwable(&quot;second thing went wrong&quot;));
+ *      collector.checkThat(getResult(), not(containsString(&quot;ERROR!&quot;)));
+ *      // all lines will run, and then a combined failure logged at the end.
+ *     }
  * }
  * </pre>
+ *
+ * @since 4.7
  */
 public class ErrorCollector extends Verifier {
-	private List<Throwable> errors= new ArrayList<Throwable>();
+    private List<Throwable> errors = new ArrayList<Throwable>();
 
-	@Override
-	protected void verify() throws Throwable {
-		MultipleFailureException.assertEmpty(errors);
-	}
+    @Override
+    protected void verify() throws Throwable {
+        MultipleFailureException.assertEmpty(errors);
+    }
 
-	/**
-	 * Adds a Throwable to the table.  Execution continues, but the test will fail at the end.
-	 */
-	public void addError(Throwable error) {
-		errors.add(error);
-	}
+    /**
+     * Adds a Throwable to the table.  Execution continues, but the test will fail at the end.
+     */
+    public void addError(Throwable error) {
+        errors.add(error);
+    }
 
-	/**
-	 * Adds a failure to the table if {@code matcher} does not match {@code value}.  
-	 * Execution continues, but the test will fail at the end if the match fails.
-	 */
-	public <T> void checkThat(final T value, final Matcher<T> matcher) {
-		checkThat("", value, matcher);
-	}
+    /**
+     * Adds a failure to the table if {@code matcher} does not match {@code value}.
+     * Execution continues, but the test will fail at the end if the match fails.
+     */
+    public <T> void checkThat(final T value, final Matcher<T> matcher) {
+        checkThat("", value, matcher);
+    }
 
-	/**
-	 * Adds a failure with the given {@code reason}
-	 * to the table if {@code matcher} does not match {@code value}.
-	 * Execution continues, but the test will fail at the end if the match fails.
-	 */
-	public <T> void checkThat(final String reason, final T value, final Matcher<T> matcher) {
-		checkSucceeds(new Callable<Object>() {
-			public Object call() throws Exception {
-				assertThat(reason, value, matcher);
-				return value;
-			}
-		});
-	}
+    /**
+     * Adds a failure with the given {@code reason}
+     * to the table if {@code matcher} does not match {@code value}.
+     * Execution continues, but the test will fail at the end if the match fails.
+     */
+    public <T> void checkThat(final String reason, final T value, final Matcher<T> matcher) {
+        checkSucceeds(new Callable<Object>() {
+            public Object call() throws Exception {
+                assertThat(reason, value, matcher);
+                return value;
+            }
+        });
+    }
 
-	/**
-	 * Adds to the table the exception, if any, thrown from {@code callable}.
-	 * Execution continues, but the test will fail at the end if
-	 * {@code callable} threw an exception.
-	 */
-	public Object checkSucceeds(Callable<Object> callable) {
-		try {
-			return callable.call();
-		} catch (Throwable e) {
-			addError(e);
-			return null;
-		}
-	}
-}
\ No newline at end of file
+    /**
+     * Adds to the table the exception, if any, thrown from {@code callable}.
+     * Execution continues, but the test will fail at the end if
+     * {@code callable} threw an exception.
+     */
+    public <T> T checkSucceeds(Callable<T> callable) {
+        try {
+            return callable.call();
+        } catch (Throwable e) {
+            addError(e);
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/org/junit/rules/ExternalResource.java b/src/main/java/org/junit/rules/ExternalResource.java
index 1fe3719..71ca287 100644
--- a/src/main/java/org/junit/rules/ExternalResource.java
+++ b/src/main/java/org/junit/rules/ExternalResource.java
@@ -7,62 +7,65 @@
  * A base class for Rules (like TemporaryFolder) that set up an external
  * resource before a test (a file, socket, server, database connection, etc.),
  * and guarantee to tear it down afterward:
- * 
+ *
  * <pre>
  * public static class UsesExternalResource {
- * 	Server myServer= new Server();
- * 
- * 	&#064;Rule
- * 	public ExternalResource resource= new ExternalResource() {
- * 		&#064;Override
- * 		protected void before() throws Throwable {
- * 			myServer.connect();
- * 		};
- * 
- * 		&#064;Override
- * 		protected void after() {
- * 			myServer.disconnect();
- * 		};
- * 	};
- * 
- * 	&#064;Test
- * 	public void testFoo() {
- * 		new Client().run(myServer);
- * 	}
+ *  Server myServer= new Server();
+ *
+ *  &#064;Rule
+ *  public ExternalResource resource= new ExternalResource() {
+ *      &#064;Override
+ *      protected void before() throws Throwable {
+ *          myServer.connect();
+ *         };
+ *
+ *      &#064;Override
+ *      protected void after() {
+ *          myServer.disconnect();
+ *         };
+ *     };
+ *
+ *  &#064;Test
+ *  public void testFoo() {
+ *      new Client().run(myServer);
+ *     }
  * }
  * </pre>
+ *
+ * @since 4.7
  */
 public abstract class ExternalResource implements TestRule {
-	public Statement apply(Statement base, Description description) {
-		return statement(base);
-	}
+    public Statement apply(Statement base, Description description) {
+        return statement(base);
+    }
 
-	private Statement statement(final Statement base) {
-		return new Statement() {
-			@Override
-			public void evaluate() throws Throwable {
-				before();
-				try {
-					base.evaluate();
-				} finally {
-					after();
-				}
-			}
-		};
-	}
+    private Statement statement(final Statement base) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                before();
+                try {
+                    base.evaluate();
+                } finally {
+                    after();
+                }
+            }
+        };
+    }
 
-	/**
-	 * Override to set up your specific external resource.
-	 * @throws if setup fails (which will disable {@code after}
-	 */
-	protected void before() throws Throwable {
-		// do nothing
-	}
+    /**
+     * Override to set up your specific external resource.
+     *
+     * @throws Throwable if setup fails (which will disable {@code after}
+     */
+    protected void before() throws Throwable {
+        // do nothing
+    }
 
-	/**
-	 * Override to tear down your specific external resource.
-	 */
-	protected void after() {
-		// do nothing
-	}
+    /**
+     * Override to tear down your specific external resource.
+     */
+    protected void after() {
+        // do nothing
+    }
 }
diff --git a/src/main/java/org/junit/rules/MethodRule.java b/src/main/java/org/junit/rules/MethodRule.java
index 5167672..823ee78 100644
--- a/src/main/java/org/junit/rules/MethodRule.java
+++ b/src/main/java/org/junit/rules/MethodRule.java
@@ -12,7 +12,7 @@
  * {@link Statement}, which is passed to the next {@link Rule}, if any. For
  * examples of how this can be useful, see these provided MethodRules,
  * or write your own:
- * 
+ *
  * <ul>
  *   <li>{@link ErrorCollector}: collect multiple errors in one test method</li>
  *   <li>{@link ExpectedException}: make flexible assertions about thrown exceptions</li>
@@ -23,18 +23,22 @@
  *   <li>{@link Timeout}: cause test to fail after a set time</li>
  *   <li>{@link Verifier}: fail test if object state ends up incorrect</li>
  * </ul>
+ *
+ * Note that {@link MethodRule} has been replaced by {@link TestRule},
+ * which has the added benefit of supporting class rules.
+ *
+ * @since 4.7
  */
-@Deprecated
 public interface MethodRule {
-	/**
-	 * Modifies the method-running {@link Statement} to implement an additional
-	 * test-running rule.
-	 * 
-	 * @param base The {@link Statement} to be modified
-	 * @param method The method to be run
-	 * @param target The object on with the method will be run.
-	 * @return a new statement, which may be the same as {@code base},
-	 * a wrapper around {@code base}, or a completely new Statement.
-	 */
-	Statement apply(Statement base, FrameworkMethod method, Object target);
-}
\ No newline at end of file
+    /**
+     * Modifies the method-running {@link Statement} to implement an additional
+     * test-running rule.
+     *
+     * @param base The {@link Statement} to be modified
+     * @param method The method to be run
+     * @param target The object on which the method will be run.
+     * @return a new statement, which may be the same as {@code base},
+     *         a wrapper around {@code base}, or a completely new Statement.
+     */
+    Statement apply(Statement base, FrameworkMethod method, Object target);
+}
diff --git a/src/main/java/org/junit/rules/RuleChain.java b/src/main/java/org/junit/rules/RuleChain.java
index 8af3c05..f43d8f5 100644
--- a/src/main/java/org/junit/rules/RuleChain.java
+++ b/src/main/java/org/junit/rules/RuleChain.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package org.junit.rules;
 
 import java.util.ArrayList;
@@ -14,24 +11,24 @@
  * The RuleChain rule allows ordering of TestRules. You create a
  * {@code RuleChain} with {@link #outerRule(TestRule)} and subsequent calls of
  * {@link #around(TestRule)}:
- * 
+ *
  * <pre>
  * public static class UseRuleChain {
  * 	&#064;Rule
- * 	public TestRule chain= RuleChain
+ * 	public RuleChain chain= RuleChain
  * 	                       .outerRule(new LoggingRule("outer rule")
  * 	                       .around(new LoggingRule("middle rule")
  * 	                       .around(new LoggingRule("inner rule");
- * 
+ *
  * 	&#064;Test
  * 	public void example() {
  * 		assertTrue(true);
- * 	}
+ *     }
  * }
  * </pre>
- * 
+ *
  * writes the log
- * 
+ *
  * <pre>
  * starting outer rule
  * starting middle rule
@@ -40,60 +37,61 @@
  * finished middle rule
  * finished outer rule
  * </pre>
+ *
+ * @since 4.10
  */
 public class RuleChain implements TestRule {
-	private static final RuleChain EMPTY_CHAIN= new RuleChain(
-			Collections.<TestRule> emptyList());
+    private static final RuleChain EMPTY_CHAIN = new RuleChain(
+            Collections.<TestRule>emptyList());
 
-	private List<TestRule> rulesStartingWithInnerMost;
+    private List<TestRule> rulesStartingWithInnerMost;
 
-	/**
-	 * Returns a {@code RuleChain} without a {@link TestRule}. This method may
-	 * be the starting point of a {@code RuleChain}.
-	 * 
-	 * @return a {@code RuleChain} without a {@link TestRule}.
-	 */
-	public static RuleChain emptyRuleChain() {
-		return EMPTY_CHAIN;
-	}
+    /**
+     * Returns a {@code RuleChain} without a {@link TestRule}. This method may
+     * be the starting point of a {@code RuleChain}.
+     *
+     * @return a {@code RuleChain} without a {@link TestRule}.
+     */
+    public static RuleChain emptyRuleChain() {
+        return EMPTY_CHAIN;
+    }
 
-	/**
-	 * Returns a {@code RuleChain} with a single {@link TestRule}. This method
-	 * is the usual starting point of a {@code RuleChain}.
-	 * 
-	 * @param outerRule
-	 *            the outer rule of the {@code RuleChain}.
-	 * @return a {@code RuleChain} with a single {@link TestRule}.
-	 */
-	public static RuleChain outerRule(TestRule outerRule) {
-		return emptyRuleChain().around(outerRule);
-	}
+    /**
+     * Returns a {@code RuleChain} with a single {@link TestRule}. This method
+     * is the usual starting point of a {@code RuleChain}.
+     *
+     * @param outerRule the outer rule of the {@code RuleChain}.
+     * @return a {@code RuleChain} with a single {@link TestRule}.
+     */
+    public static RuleChain outerRule(TestRule outerRule) {
+        return emptyRuleChain().around(outerRule);
+    }
 
-	private RuleChain(List<TestRule> rules) {
-		this.rulesStartingWithInnerMost= rules;
-	}
+    private RuleChain(List<TestRule> rules) {
+        this.rulesStartingWithInnerMost = rules;
+    }
 
-	/**
-	 * Create a new {@code RuleChain}, which encloses the {@code nextRule} with
-	 * the rules of the current {@code RuleChain}.
-	 * 
-	 * @param enclosedRule
-	 *            the rule to enclose.
-	 * @return a new {@code RuleChain}.
-	 */
-	public RuleChain around(TestRule enclosedRule) {
-		List<TestRule> rulesOfNewChain= new ArrayList<TestRule>();
-		rulesOfNewChain.add(enclosedRule);
-		rulesOfNewChain.addAll(rulesStartingWithInnerMost);
-		return new RuleChain(rulesOfNewChain);
-	}
+    /**
+     * Create a new {@code RuleChain}, which encloses the {@code nextRule} with
+     * the rules of the current {@code RuleChain}.
+     *
+     * @param enclosedRule the rule to enclose.
+     * @return a new {@code RuleChain}.
+     */
+    public RuleChain around(TestRule enclosedRule) {
+        List<TestRule> rulesOfNewChain = new ArrayList<TestRule>();
+        rulesOfNewChain.add(enclosedRule);
+        rulesOfNewChain.addAll(rulesStartingWithInnerMost);
+        return new RuleChain(rulesOfNewChain);
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	public Statement apply(Statement base, Description description) {
-		for (TestRule each : rulesStartingWithInnerMost)
-			base= each.apply(base, description);
-		return base;
-	}
+    /**
+     * {@inheritDoc}
+     */
+    public Statement apply(Statement base, Description description) {
+        for (TestRule each : rulesStartingWithInnerMost) {
+            base = each.apply(base, description);
+        }
+        return base;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/rules/RunRules.java b/src/main/java/org/junit/rules/RunRules.java
index d5905b9..131fc1f 100644
--- a/src/main/java/org/junit/rules/RunRules.java
+++ b/src/main/java/org/junit/rules/RunRules.java
@@ -5,23 +5,26 @@
 
 /**
  * Runs a collection of rules on a statement.
+ *
+ * @since 4.9
  */
 public class RunRules extends Statement {
-	private final Statement statement;
+    private final Statement statement;
 
-	public RunRules(Statement base, Iterable<TestRule> rules, Description description) {
-		statement= applyAll(base, rules, description);
-	}
-	
-	@Override
-	public void evaluate() throws Throwable {
-		statement.evaluate();
-	}
+    public RunRules(Statement base, Iterable<TestRule> rules, Description description) {
+        statement = applyAll(base, rules, description);
+    }
 
-	private static Statement applyAll(Statement result, Iterable<TestRule> rules,
-			Description description) {
-		for (TestRule each : rules)
-			result= each.apply(result, description);
-		return result;
-	}
+    @Override
+    public void evaluate() throws Throwable {
+        statement.evaluate();
+    }
+
+    private static Statement applyAll(Statement result, Iterable<TestRule> rules,
+            Description description) {
+        for (TestRule each : rules) {
+            result = each.apply(result, description);
+        }
+        return result;
+    }
 }
diff --git a/src/main/java/org/junit/rules/Stopwatch.java b/src/main/java/org/junit/rules/Stopwatch.java
new file mode 100644
index 0000000..5d34e7f
--- /dev/null
+++ b/src/main/java/org/junit/rules/Stopwatch.java
@@ -0,0 +1,183 @@
+package org.junit.rules;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The Stopwatch Rule notifies one of its own protected methods of the time spent by a test.
+ *
+ * <p>Override them to get the time in nanoseconds. For example, this class will keep logging the
+ * time spent by each passed, failed, skipped, and finished test:
+ *
+ * <pre>
+ * public static class StopwatchTest {
+ *     private static final Logger logger = Logger.getLogger(&quot;&quot;);
+ *
+ *     private static void logInfo(Description description, String status, long nanos) {
+ *         String testName = description.getMethodName();
+ *         logger.info(String.format(&quot;Test %s %s, spent %d microseconds&quot;,
+ *                                   testName, status, TimeUnit.NANOSECONDS.toMicros(nanos)));
+ *     }
+ *
+ *     &#064;Rule
+ *     public Stopwatch stopwatch = new Stopwatch() {
+ *         &#064;Override
+ *         protected void succeeded(long nanos, Description description) {
+ *             logInfo(description, &quot;succeeded&quot;, nanos);
+ *         }
+ *
+ *         &#064;Override
+ *         protected void failed(long nanos, Throwable e, Description description) {
+ *             logInfo(description, &quot;failed&quot;, nanos);
+ *         }
+ *
+ *         &#064;Override
+ *         protected void skipped(long nanos, AssumptionViolatedException e, Description description) {
+ *             logInfo(description, &quot;skipped&quot;, nanos);
+ *         }
+ *
+ *         &#064;Override
+ *         protected void finished(long nanos, Description description) {
+ *             logInfo(description, &quot;finished&quot;, nanos);
+ *         }
+ *     };
+ *
+ *     &#064;Test
+ *     public void succeeds() {
+ *     }
+ *
+ *     &#064;Test
+ *     public void fails() {
+ *         fail();
+ *     }
+ *
+ *     &#064;Test
+ *     public void skips() {
+ *         assumeTrue(false);
+ *     }
+ * }
+ * </pre>
+ *
+ * An example to assert runtime:
+ * <pre>
+ * &#064;Test
+ * public void performanceTest() throws InterruptedException {
+ *     long delta = 30;
+ *     Thread.sleep(300L);
+ *     assertEquals(300d, stopwatch.runtime(MILLISECONDS), delta);
+ *     Thread.sleep(500L);
+ *     assertEquals(800d, stopwatch.runtime(MILLISECONDS), delta);
+ * }
+ * </pre>
+ *
+ * @author tibor17
+ * @since 4.12
+ */
+public abstract class Stopwatch implements TestRule {
+    private final Clock clock;
+    private volatile long startNanos;
+    private volatile long endNanos;
+
+    public Stopwatch() {
+        this(new Clock());
+    }
+
+    Stopwatch(Clock clock) {
+        this.clock = clock;
+    }
+
+    /**
+     * Gets the runtime for the test.
+     *
+     * @param unit time unit for returned runtime
+     * @return runtime measured during the test
+     */
+    public long runtime(TimeUnit unit) {
+        return unit.convert(getNanos(), TimeUnit.NANOSECONDS);
+    }
+
+    /**
+     * Invoked when a test succeeds
+     */
+    protected void succeeded(long nanos, Description description) {
+    }
+
+    /**
+     * Invoked when a test fails
+     */
+    protected void failed(long nanos, Throwable e, Description description) {
+    }
+
+    /**
+     * Invoked when a test is skipped due to a failed assumption.
+     */
+    protected void skipped(long nanos, AssumptionViolatedException e, Description description) {
+    }
+
+    /**
+     * Invoked when a test method finishes (whether passing or failing)
+     */
+    protected void finished(long nanos, Description description) {
+    }
+
+    private long getNanos() {
+        if (startNanos == 0) {
+            throw new IllegalStateException("Test has not started");
+        }
+        long currentEndNanos = endNanos; // volatile read happens here
+        if (currentEndNanos == 0) {
+          currentEndNanos = clock.nanoTime();
+        }
+
+        return currentEndNanos - startNanos;
+    }
+
+    private void starting() {
+        startNanos = clock.nanoTime();
+        endNanos = 0;
+    }
+
+    private void stopping() {
+        endNanos = clock.nanoTime();
+    }
+
+    public final Statement apply(Statement base, Description description) {
+        return new InternalWatcher().apply(base, description);
+    }
+
+    private class InternalWatcher extends TestWatcher {
+
+        @Override protected void starting(Description description) {
+            Stopwatch.this.starting();
+        }
+
+        @Override protected void finished(Description description) {
+            Stopwatch.this.finished(getNanos(), description);
+        }
+
+        @Override protected void succeeded(Description description) {
+            Stopwatch.this.stopping();
+            Stopwatch.this.succeeded(getNanos(), description);
+        }
+
+        @Override protected void failed(Throwable e, Description description) {
+            Stopwatch.this.stopping();
+            Stopwatch.this.failed(getNanos(), e, description);
+        }
+
+        @Override protected void skipped(AssumptionViolatedException e, Description description) {
+            Stopwatch.this.stopping();
+            Stopwatch.this.skipped(getNanos(), e, description);
+        }
+    }
+
+    static class Clock {
+
+        public long nanoTime() {
+            return System.nanoTime();
+        }
+    }
+}
diff --git a/src/main/java/org/junit/rules/TemporaryFolder.java b/src/main/java/org/junit/rules/TemporaryFolder.java
index a7c82aa..dc75c93 100644
--- a/src/main/java/org/junit/rules/TemporaryFolder.java
+++ b/src/main/java/org/junit/rules/TemporaryFolder.java
@@ -6,108 +6,165 @@
 import org.junit.Rule;
 
 /**
- * The TemporaryFolder Rule allows creation of files and folders that are
- * guaranteed to be deleted when the test method finishes (whether it passes or
- * fails):
- * 
+ * The TemporaryFolder Rule allows creation of files and folders that should
+ * be deleted when the test method finishes (whether it passes or
+ * fails). Whether the deletion is successful or not is not checked by this rule.
+ * No exception will be thrown in case the deletion fails.
+ *
+ * <p>Example of usage:
  * <pre>
  * public static class HasTempFolder {
- * 	&#064;Rule
- * 	public TemporaryFolder folder= new TemporaryFolder();
- * 
- * 	&#064;Test
- * 	public void testUsingTempFolder() throws IOException {
- * 		File createdFile= folder.newFile(&quot;myfile.txt&quot;);
- * 		File createdFolder= folder.newFolder(&quot;subfolder&quot;);
- * 		// ...
- * 	}
+ *  &#064;Rule
+ *  public TemporaryFolder folder= new TemporaryFolder();
+ *
+ *  &#064;Test
+ *  public void testUsingTempFolder() throws IOException {
+ *      File createdFile= folder.newFile(&quot;myfile.txt&quot;);
+ *      File createdFolder= folder.newFolder(&quot;subfolder&quot;);
+ *      // ...
+ *     }
  * }
  * </pre>
+ *
+ * @since 4.7
  */
 public class TemporaryFolder extends ExternalResource {
-	private File folder;
+    private final File parentFolder;
+    private File folder;
 
-	@Override
-	protected void before() throws Throwable {
-		create();
-	}
+    public TemporaryFolder() {
+        this(null);
+    }
 
-	@Override
-	protected void after() {
-		delete();
-	}
+    public TemporaryFolder(File parentFolder) {
+        this.parentFolder = parentFolder;
+    }
 
-	// testing purposes only
-	/**
-	 * for testing purposes only.  Do not use.
-	 */
-	public void create() throws IOException {
-		folder= newFolder();
-	}
+    @Override
+    protected void before() throws Throwable {
+        create();
+    }
 
-	/**
-	 * Returns a new fresh file with the given name under the temporary folder.
-	 */
-	public File newFile(String fileName) throws IOException {
-		File file= new File(getRoot(), fileName);
-		file.createNewFile();
-		return file;
-	}
+    @Override
+    protected void after() {
+        delete();
+    }
 
-	/**
-	 * Returns a new fresh file with a random name under the temporary folder.
-	 */
-	public File newFile() throws IOException {
-		return File.createTempFile("junit", null, folder);
-	}
+    // testing purposes only
 
-	/**
-	 * Returns a new fresh folder with the given name under the temporary folder.
-	 */
-	public File newFolder(String... folderNames) {
-		File file = getRoot();
-		for (String folderName : folderNames) {
-			file = new File(file, folderName);
-			file.mkdir();
-		}
-		return file;
-	}
+    /**
+     * for testing purposes only. Do not use.
+     */
+    public void create() throws IOException {
+        folder = createTemporaryFolderIn(parentFolder);
+    }
 
-	/**
-	 * Returns a new fresh folder with a random name under the temporary
-	 * folder.
-	 */
-	public File newFolder() throws IOException {
-		File createdFolder= File.createTempFile("junit", "", folder);
-		createdFolder.delete();
-		createdFolder.mkdir();
-		return createdFolder;
-	}
+    /**
+     * Returns a new fresh file with the given name under the temporary folder.
+     */
+    public File newFile(String fileName) throws IOException {
+        File file = new File(getRoot(), fileName);
+        if (!file.createNewFile()) {
+            throw new IOException(
+                    "a file with the name \'" + fileName + "\' already exists in the test folder");
+        }
+        return file;
+    }
 
-	/**
-	 * @return the location of this temporary folder.
-	 */
-	public File getRoot() {
-		if (folder == null) {
-			throw new IllegalStateException("the temporary folder has not yet been created");
-		}
-		return folder;
-	}
+    /**
+     * Returns a new fresh file with a random name under the temporary folder.
+     */
+    public File newFile() throws IOException {
+        return File.createTempFile("junit", null, getRoot());
+    }
 
-	/**
-	 * Delete all files and folders under the temporary folder.
-	 * Usually not called directly, since it is automatically applied
-	 * by the {@link Rule}
-	 */
-	public void delete() {
-		recursiveDelete(folder);
-	}
+    /**
+     * Returns a new fresh folder with the given name under the temporary
+     * folder.
+     */
+    public File newFolder(String folder) throws IOException {
+        return newFolder(new String[]{folder});
+    }
 
-	private void recursiveDelete(File file) {
-		File[] files= file.listFiles();
-		if (files != null)
-			for (File each : files)
-				recursiveDelete(each);
-		file.delete();
-	}
+    /**
+     * Returns a new fresh folder with the given name(s) under the temporary
+     * folder.
+     */
+    public File newFolder(String... folderNames) throws IOException {
+        File file = getRoot();
+        for (int i = 0; i < folderNames.length; i++) {
+            String folderName = folderNames[i];
+            validateFolderName(folderName);
+            file = new File(file, folderName);
+            if (!file.mkdir() && isLastElementInArray(i, folderNames)) {
+                throw new IOException(
+                        "a folder with the name \'" + folderName + "\' already exists");
+            }
+        }
+        return file;
+    }
+    
+    /**
+     * Validates if multiple path components were used while creating a folder.
+     * 
+     * @param folderName
+     *            Name of the folder being created
+     */
+    private void validateFolderName(String folderName) throws IOException {
+        File tempFile = new File(folderName);
+        if (tempFile.getParent() != null) {
+            String errorMsg = "Folder name cannot consist of multiple path components separated by a file separator."
+                    + " Please use newFolder('MyParentFolder','MyFolder') to create hierarchies of folders";
+            throw new IOException(errorMsg);
+        }
+    }
+
+    private boolean isLastElementInArray(int index, String[] array) {
+        return index == array.length - 1;
+    }
+
+    /**
+     * Returns a new fresh folder with a random name under the temporary folder.
+     */
+    public File newFolder() throws IOException {
+        return createTemporaryFolderIn(getRoot());
+    }
+
+    private File createTemporaryFolderIn(File parentFolder) throws IOException {
+        File createdFolder = File.createTempFile("junit", "", parentFolder);
+        createdFolder.delete();
+        createdFolder.mkdir();
+        return createdFolder;
+    }
+
+    /**
+     * @return the location of this temporary folder.
+     */
+    public File getRoot() {
+        if (folder == null) {
+            throw new IllegalStateException(
+                    "the temporary folder has not yet been created");
+        }
+        return folder;
+    }
+
+    /**
+     * Delete all files and folders under the temporary folder. Usually not
+     * called directly, since it is automatically applied by the {@link Rule}
+     */
+    public void delete() {
+        if (folder != null) {
+            recursiveDelete(folder);
+        }
+    }
+
+    private void recursiveDelete(File file) {
+        File[] files = file.listFiles();
+        if (files != null) {
+            for (File each : files) {
+                recursiveDelete(each);
+            }
+        }
+        file.delete();
+    }
 }
diff --git a/src/main/java/org/junit/rules/TestName.java b/src/main/java/org/junit/rules/TestName.java
index c4ab9ce..bf72602 100644
--- a/src/main/java/org/junit/rules/TestName.java
+++ b/src/main/java/org/junit/rules/TestName.java
@@ -4,36 +4,38 @@
 
 /**
  * The TestName Rule makes the current test name available inside test methods:
- * 
+ *
  * <pre>
  * public class TestNameTest {
- * 	&#064;Rule
- * 	public TestName name= new TestName();
- * 
- * 	&#064;Test
- * 	public void testA() {
- * 		assertEquals(&quot;testA&quot;, name.getMethodName());
- * 	}
- * 
- * 	&#064;Test
- * 	public void testB() {
- * 		assertEquals(&quot;testB&quot;, name.getMethodName());
- * 	}
+ *  &#064;Rule
+ *  public TestName name= new TestName();
+ *
+ *  &#064;Test
+ *  public void testA() {
+ *      assertEquals(&quot;testA&quot;, name.getMethodName());
+ *     }
+ *
+ *  &#064;Test
+ *  public void testB() {
+ *      assertEquals(&quot;testB&quot;, name.getMethodName());
+ *     }
  * }
  * </pre>
+ *
+ * @since 4.7
  */
 public class TestName extends TestWatcher {
-	private String fName;
+    private String name;
 
-	@Override
-	protected void starting(Description d) {
-		fName= d.getMethodName();
-	}
+    @Override
+    protected void starting(Description d) {
+        name = d.getMethodName();
+    }
 
-	/**
-	 * @return the name of the currently-running test method
-	 */
-	public String getMethodName() {
-		return fName;
-	}
+    /**
+     * @return the name of the currently-running test method
+     */
+    public String getMethodName() {
+        return name;
+    }
 }
diff --git a/src/main/java/org/junit/rules/TestRule.java b/src/main/java/org/junit/rules/TestRule.java
index b7760c4..53e2f70 100644
--- a/src/main/java/org/junit/rules/TestRule.java
+++ b/src/main/java/org/junit/rules/TestRule.java
@@ -9,16 +9,16 @@
  * a test that would otherwise fail to pass, or it may perform necessary setup or
  * cleanup for tests, or it may observe test execution to report it elsewhere.
  * {@link TestRule}s can do everything that could be done previously with
- * methods annotated with {@link org.junit.Before}, 
- * {@link org.junit.After}, {@link org.junit.BeforeClass}, or 
- * {@link org.junit.AfterClass}, but they are more powerful, and more easily 
+ * methods annotated with {@link org.junit.Before},
+ * {@link org.junit.After}, {@link org.junit.BeforeClass}, or
+ * {@link org.junit.AfterClass}, but they are more powerful, and more easily
  * shared
  * between projects and classes.
- * 
+ *
  * The default JUnit test runners for suites and
  * individual test cases recognize {@link TestRule}s introduced in two different
- * ways.  {@link org.junit.Rule} annotates method-level 
- * {@link TestRule}s, and {@link org.junit.ClassRule} 
+ * ways.  {@link org.junit.Rule} annotates method-level
+ * {@link TestRule}s, and {@link org.junit.ClassRule}
  * annotates class-level {@link TestRule}s.  See Javadoc for those annotations
  * for more information.
  *
@@ -28,7 +28,7 @@
  * {@link Statement}, which is passed to the next {@link org.junit.Rule}, if any. For
  * examples of how this can be useful, see these provided TestRules,
  * or write your own:
- * 
+ *
  * <ul>
  *   <li>{@link ErrorCollector}: collect multiple errors in one test method</li>
  *   <li>{@link ExpectedException}: make flexible assertions about thrown exceptions</li>
@@ -39,16 +39,18 @@
  *   <li>{@link Timeout}: cause test to fail after a set time</li>
  *   <li>{@link Verifier}: fail test if object state ends up incorrect</li>
  * </ul>
+ *
+ * @since 4.9
  */
 public interface TestRule {
-	/**
-	 * Modifies the method-running {@link Statement} to implement this
-	 * test-running rule.
-	 * 
-	 * @param base The {@link Statement} to be modified
-	 * @param description A {@link Description} of the test implemented in {@code base}
-	 * @return a new statement, which may be the same as {@code base},
-	 * a wrapper around {@code base}, or a completely new Statement.
-	 */
-	Statement apply(Statement base, Description description);
+    /**
+     * Modifies the method-running {@link Statement} to implement this
+     * test-running rule.
+     *
+     * @param base The {@link Statement} to be modified
+     * @param description A {@link Description} of the test implemented in {@code base}
+     * @return a new statement, which may be the same as {@code base},
+     *         a wrapper around {@code base}, or a completely new Statement.
+     */
+    Statement apply(Statement base, Description description);
 }
diff --git a/src/main/java/org/junit/rules/TestWatcher.java b/src/main/java/org/junit/rules/TestWatcher.java
index 351b449..5492b6b 100644
--- a/src/main/java/org/junit/rules/TestWatcher.java
+++ b/src/main/java/org/junit/rules/TestWatcher.java
@@ -1,94 +1,166 @@
 package org.junit.rules;
 
-import org.junit.internal.AssumptionViolatedException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.AssumptionViolatedException;
 import org.junit.runner.Description;
+import org.junit.runners.model.MultipleFailureException;
 import org.junit.runners.model.Statement;
 
 /**
  * TestWatcher is a base class for Rules that take note of the testing
  * action, without modifying it. For example, this class will keep a log of each
  * passing and failing test:
- * 
+ *
  * <pre>
  * public static class WatchmanTest {
- * 	private static String watchedLog;
- * 
- * 	&#064;Rule
- * 	public MethodRule watchman= new TestWatcher() {
- * 		&#064;Override
- * 		protected void failed(Description d) {
- * 			watchedLog+= d + &quot;\n&quot;;
- * 		}
- * 
- * 		&#064;Override
- * 		protected void succeeded(Description d) {
- * 			watchedLog+= d + &quot; &quot; + &quot;success!\n&quot;;
- * 		}
- * 	};
- * 
- * 	&#064;Test
- * 	public void fails() {
- * 		fail();
- * 	}
- * 
- * 	&#064;Test
- * 	public void succeeds() {
- * 	}
+ *  private static String watchedLog;
+ *
+ *  &#064;Rule
+ *  public TestWatcher watchman= new TestWatcher() {
+ *      &#064;Override
+ *      protected void failed(Throwable e, Description description) {
+ *          watchedLog+= description + &quot;\n&quot;;
+ *      }
+ *
+ *      &#064;Override
+ *      protected void succeeded(Description description) {
+ *          watchedLog+= description + &quot; &quot; + &quot;success!\n&quot;;
+ *         }
+ *     };
+ *
+ *  &#064;Test
+ *  public void fails() {
+ *      fail();
+ *  }
+ *
+ *  &#064;Test
+ *  public void succeeds() {
+ *     }
  * }
  * </pre>
+ *
+ * @since 4.9
  */
 public abstract class TestWatcher implements TestRule {
-	public Statement apply(final Statement base, final Description description) {
-		return new Statement() {
-			@Override
-			public void evaluate() throws Throwable {
-				starting(description);
-				try {
-					base.evaluate();
-					succeeded(description);
-				} catch (AssumptionViolatedException e) {
-					throw e;
-				} catch (Throwable t) {
-					failed(t, description);
-					throw t;
-				} finally {
-					finished(description);
-				}
-			}
-		};
-	}
+    public Statement apply(final Statement base, final Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                List<Throwable> errors = new ArrayList<Throwable>();
 
-	/**
-	 * Invoked when a test succeeds
-	 * 
-	 * @param description
-	 */
-	protected void succeeded(Description description) {
-	}
+                startingQuietly(description, errors);
+                try {
+                    base.evaluate();
+                    succeededQuietly(description, errors);
+                } catch (@SuppressWarnings("deprecation") org.junit.internal.AssumptionViolatedException  e) {
+                    errors.add(e);
+                    skippedQuietly(e, description, errors);
+                } catch (Throwable e) {
+                    errors.add(e);
+                    failedQuietly(e, description, errors);
+                } finally {
+                    finishedQuietly(description, errors);
+                }
 
-	/**
-	 * Invoked when a test fails
-	 * 
-	 * @param e 
-	 * @param description
-	 */
-	protected void failed(Throwable e, Description description) {
-	}
+                MultipleFailureException.assertEmpty(errors);
+            }
+        };
+    }
 
-	/**
-	 * Invoked when a test is about to start
-	 * 
-	 * @param description  
-	 */
-	protected void starting(Description description) {
-	}
+    private void succeededQuietly(Description description,
+            List<Throwable> errors) {
+        try {
+            succeeded(description);
+        } catch (Throwable e) {
+            errors.add(e);
+        }
+    }
 
+    private void failedQuietly(Throwable e, Description description,
+            List<Throwable> errors) {
+        try {
+            failed(e, description);
+        } catch (Throwable e1) {
+            errors.add(e1);
+        }
+    }
 
-	/**
-	 * Invoked when a test method finishes (whether passing or failing)
-	 * 
-	 * @param description  
-	 */
-	protected void finished(Description description) {
-	}
+    @SuppressWarnings("deprecation")
+    private void skippedQuietly(
+            org.junit.internal.AssumptionViolatedException e, Description description,
+            List<Throwable> errors) {
+        try {
+            if (e instanceof AssumptionViolatedException) {
+                skipped((AssumptionViolatedException) e, description);
+            } else {
+                skipped(e, description);
+            }
+        } catch (Throwable e1) {
+            errors.add(e1);
+        }
+    }
+
+    private void startingQuietly(Description description,
+            List<Throwable> errors) {
+        try {
+            starting(description);
+        } catch (Throwable e) {
+            errors.add(e);
+        }
+    }
+
+    private void finishedQuietly(Description description,
+            List<Throwable> errors) {
+        try {
+            finished(description);
+        } catch (Throwable e) {
+            errors.add(e);
+        }
+    }
+
+    /**
+     * Invoked when a test succeeds
+     */
+    protected void succeeded(Description description) {
+    }
+
+    /**
+     * Invoked when a test fails
+     */
+    protected void failed(Throwable e, Description description) {
+    }
+
+    /**
+     * Invoked when a test is skipped due to a failed assumption.
+     */
+    @SuppressWarnings("deprecation")
+    protected void skipped(AssumptionViolatedException e, Description description) {
+        // For backwards compatibility with JUnit 4.11 and earlier, call the legacy version
+        org.junit.internal.AssumptionViolatedException asInternalException = e;
+        skipped(asInternalException, description);
+    }
+
+    /**
+     * Invoked when a test is skipped due to a failed assumption.
+     *
+     * @deprecated use {@link #skipped(AssumptionViolatedException, Description)}
+     */
+    @Deprecated
+    protected void skipped(
+            org.junit.internal.AssumptionViolatedException e, Description description) {
+    }
+
+    /**
+     * Invoked when a test is about to start
+     */
+    protected void starting(Description description) {
+    }
+
+    /**
+     * Invoked when a test method finishes (whether passing or failing)
+     */
+    protected void finished(Description description) {
+    }
 }
diff --git a/src/main/java/org/junit/rules/TestWatchman.java b/src/main/java/org/junit/rules/TestWatchman.java
index 15daa64..c8d6c71 100644
--- a/src/main/java/org/junit/rules/TestWatchman.java
+++ b/src/main/java/org/junit/rules/TestWatchman.java
@@ -8,93 +8,84 @@
  * TestWatchman is a base class for Rules that take note of the testing
  * action, without modifying it. For example, this class will keep a log of each
  * passing and failing test:
- * 
+ *
  * <pre>
  * public static class WatchmanTest {
- * 	private static String watchedLog;
- * 
- * 	&#064;Rule
- * 	public MethodRule watchman= new TestWatchman() {
- * 		&#064;Override
- * 		public void failed(Throwable e, FrameworkMethod method) {
- * 			watchedLog+= method.getName() + &quot; &quot; + e.getClass().getSimpleName()
- * 					+ &quot;\n&quot;;
- * 		}
- * 
- * 		&#064;Override
- * 		public void succeeded(FrameworkMethod method) {
- * 			watchedLog+= method.getName() + &quot; &quot; + &quot;success!\n&quot;;
- * 		}
- * 	};
- * 
- * 	&#064;Test
- * 	public void fails() {
- * 		fail();
- * 	}
- * 
- * 	&#064;Test
- * 	public void succeeds() {
- * 	}
+ *  private static String watchedLog;
+ *
+ *  &#064;Rule
+ *  public MethodRule watchman= new TestWatchman() {
+ *      &#064;Override
+ *      public void failed(Throwable e, FrameworkMethod method) {
+ *          watchedLog+= method.getName() + &quot; &quot; + e.getClass().getSimpleName()
+ *                  + &quot;\n&quot;;
+ *         }
+ *
+ *      &#064;Override
+ *      public void succeeded(FrameworkMethod method) {
+ *          watchedLog+= method.getName() + &quot; &quot; + &quot;success!\n&quot;;
+ *         }
+ *     };
+ *
+ *  &#064;Test
+ *  public void fails() {
+ *      fail();
+ *     }
+ *
+ *  &#064;Test
+ *  public void succeeds() {
+ *     }
  * }
  * </pre>
- * 
- * @deprecated {@link MethodRule} is deprecated.  
- *             Use {@link TestWatcher} implements {@link TestRule} instead.
+ *
+ * @since 4.7
+ * @deprecated Use {@link TestWatcher} (which implements {@link TestRule}) instead.
  */
 @Deprecated
 public class TestWatchman implements MethodRule {
-	public Statement apply(final Statement base, final FrameworkMethod method,
-			Object target) {
-		return new Statement() {
-			@Override
-			public void evaluate() throws Throwable {
-				starting(method);
-				try {
-					base.evaluate();
-					succeeded(method);
-				} catch (AssumptionViolatedException e) {
-					throw e;
-				} catch (Throwable t) {
-					failed(t, method);
-					throw t;
-				} finally {
-					finished(method);
-				}
-			}
-		};
-	}
+    public Statement apply(final Statement base, final FrameworkMethod method,
+            Object target) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                starting(method);
+                try {
+                    base.evaluate();
+                    succeeded(method);
+                } catch (AssumptionViolatedException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    failed(e, method);
+                    throw e;
+                } finally {
+                    finished(method);
+                }
+            }
+        };
+    }
 
-	/**
-	 * Invoked when a test method succeeds
-	 * 
-	 * @param method
-	 */
-	public void succeeded(FrameworkMethod method) {
-	}
+    /**
+     * Invoked when a test method succeeds
+     */
+    public void succeeded(FrameworkMethod method) {
+    }
 
-	/**
-	 * Invoked when a test method fails
-	 * 
-	 * @param e 
-	 * @param method
-	 */
-	public void failed(Throwable e, FrameworkMethod method) {
-	}
+    /**
+     * Invoked when a test method fails
+     */
+    public void failed(Throwable e, FrameworkMethod method) {
+    }
 
-	/**
-	 * Invoked when a test method is about to start
-	 * 
-	 * @param method  
-	 */
-	public void starting(FrameworkMethod method) {
-	}
+    /**
+     * Invoked when a test method is about to start
+     */
+    public void starting(FrameworkMethod method) {
+    }
 
 
-	/**
-	 * Invoked when a test method finishes (whether passing or failing)
-	 * 
-	 * @param method  
-	 */
-	public void finished(FrameworkMethod method) {
-	}
+    /**
+     * Invoked when a test method finishes (whether passing or failing)
+     */
+    public void finished(FrameworkMethod method) {
+    }
 }
diff --git a/src/main/java/org/junit/rules/Timeout.java b/src/main/java/org/junit/rules/Timeout.java
index 85ce6d6..8d382df 100644
--- a/src/main/java/org/junit/rules/Timeout.java
+++ b/src/main/java/org/junit/rules/Timeout.java
@@ -1,49 +1,200 @@
-/**
- * 
- */
 package org.junit.rules;
 
 import org.junit.internal.runners.statements.FailOnTimeout;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * The Timeout Rule applies the same timeout to all test methods in a class:
- * 
  * <pre>
- * public static class HasGlobalTimeout {
- * 	public static String log;
- * 
- * 	&#064;Rule
- * 	public MethodRule globalTimeout= new Timeout(20);
- * 
- * 	&#064;Test
- * 	public void testInfiniteLoop1() {
- * 		log+= &quot;ran1&quot;;
- * 		for (;;) {
- * 		}
- * 	}
- * 
- * 	&#064;Test
- * 	public void testInfiniteLoop2() {
- * 		log+= &quot;ran2&quot;;
- * 		for (;;) {
- * 		}
- * 	}
+ * public static class HasGlobalLongTimeout {
+ *
+ *  &#064;Rule
+ *  public Timeout globalTimeout= new Timeout(20);
+ *
+ *  &#064;Test
+ *  public void run1() throws InterruptedException {
+ *      Thread.sleep(100);
+ *  }
+ *
+ *  &#064;Test
+ *  public void infiniteLoop() {
+ *      while (true) {}
+ *  }
  * }
  * </pre>
+ * <p>
+ * Each test is run in a new thread. If the specified timeout elapses before
+ * the test completes, its execution is interrupted via {@link Thread#interrupt()}.
+ * This happens in interruptable I/O and locks, and methods in {@link Object}
+ * and {@link Thread} throwing {@link InterruptedException}.
+ * <p>
+ * A specified timeout of 0 will be interpreted as not set, however tests will
+ * still launch from separate threads. This can be useful for disabling timeouts
+ * in environments where they are dynamically set based on some property.
+ *
+ * @since 4.7
  */
 public class Timeout implements TestRule {
-	private final int fMillis;
+    private final long timeout;
+    private final TimeUnit timeUnit;
 
-	/**
-	 * @param millis the millisecond timeout
-	 */
-	public Timeout(int millis) {
-		fMillis= millis;
-	}
+    /**
+     * Returns a new builder for building an instance.
+     *
+     * @since 4.12
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
 
-	public Statement apply(Statement base, Description description) {
-		return new FailOnTimeout(base, fMillis);
-	}
-}
\ No newline at end of file
+    /**
+     * Create a {@code Timeout} instance with the timeout specified
+     * in milliseconds.
+     * <p>
+     * This constructor is deprecated.
+     * <p>
+     * Instead use {@link #Timeout(long, java.util.concurrent.TimeUnit)},
+     * {@link Timeout#millis(long)}, or {@link Timeout#seconds(long)}.
+     *
+     * @param millis the maximum time in milliseconds to allow the
+     * test to run before it should timeout
+     */
+    @Deprecated
+    public Timeout(int millis) {
+        this(millis, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Create a {@code Timeout} instance with the timeout specified
+     * at the timeUnit of granularity of the provided {@code TimeUnit}.
+     *
+     * @param timeout the maximum time to allow the test to run
+     * before it should timeout
+     * @param timeUnit the time unit for the {@code timeout}
+     * @since 4.12
+     */
+    public Timeout(long timeout, TimeUnit timeUnit) {
+        this.timeout = timeout;
+        this.timeUnit = timeUnit;
+    }
+
+    /**
+     * Create a {@code Timeout} instance initialized with values form
+     * a builder.
+     *
+     * @since 4.12
+     */
+    protected Timeout(Builder builder) {
+        timeout = builder.getTimeout();
+        timeUnit = builder.getTimeUnit();
+    }
+
+    /**
+     * Creates a {@link Timeout} that will timeout a test after the
+     * given duration, in milliseconds.
+     *
+     * @since 4.12
+     */
+    public static Timeout millis(long millis) {
+        return new Timeout(millis, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Creates a {@link Timeout} that will timeout a test after the
+     * given duration, in seconds.
+     *
+     * @since 4.12
+     */
+    public static Timeout seconds(long seconds) {
+        return new Timeout(seconds, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Gets the timeout configured for this rule, in the given units.
+     *
+     * @since 4.12
+     */
+    protected final long getTimeout(TimeUnit unit) {
+        return unit.convert(timeout, timeUnit);
+    }
+
+    /**
+     * Creates a {@link Statement} that will run the given
+     * {@code statement}, and timeout the operation based
+     * on the values configured in this rule. Subclasses
+     * can override this method for different behavior.
+     *
+     * @since 4.12
+     */
+    protected Statement createFailOnTimeoutStatement(
+            Statement statement) throws Exception {
+        return FailOnTimeout.builder()
+            .withTimeout(timeout, timeUnit)
+            .build(statement);
+    }
+
+    public Statement apply(Statement base, Description description) {
+        try {
+            return createFailOnTimeoutStatement(base);
+        } catch (final Exception e) {
+            return new Statement() {
+                @Override public void evaluate() throws Throwable {
+                    throw new RuntimeException("Invalid parameters for Timeout", e);
+                }
+            };
+        }
+    }
+
+    /**
+     * Builder for {@link Timeout}.
+     *
+     * @since 4.12
+     */
+    public static class Builder {
+        private boolean lookForStuckThread = false;
+        private long timeout = 0;
+        private TimeUnit timeUnit = TimeUnit.SECONDS;
+
+        protected Builder() {
+        }
+
+        /**
+         * Specifies the time to wait before timing out the test.
+         *
+         * <p>If this is not called, or is called with a
+         * {@code timeout} of {@code 0}, the returned {@code Timeout}
+         * rule instance will cause the tests to wait forever to
+         * complete, however the tests will still launch from a
+         * separate thread. This can be useful for disabling timeouts
+         * in environments where they are dynamically set based on
+         * some property.
+         *
+         * @param timeout the maximum time to wait
+         * @param unit the time unit of the {@code timeout} argument
+         * @return {@code this} for method chaining.
+         */
+        public Builder withTimeout(long timeout, TimeUnit unit) {
+            this.timeout = timeout;
+            this.timeUnit = unit;
+            return this;
+        }
+
+        protected long getTimeout() {
+            return timeout;
+        }
+
+        protected TimeUnit getTimeUnit()  {
+            return timeUnit;
+        }
+
+        /**
+         * Builds a {@link Timeout} instance using the values in this builder.,
+         */
+        public Timeout build() {
+            return new Timeout(this);
+        }
+    }
+}
diff --git a/src/main/java/org/junit/rules/Verifier.java b/src/main/java/org/junit/rules/Verifier.java
index be1a55e..7a03b0c 100644
--- a/src/main/java/org/junit/rules/Verifier.java
+++ b/src/main/java/org/junit/rules/Verifier.java
@@ -7,39 +7,41 @@
  * Verifier is a base class for Rules like ErrorCollector, which can turn
  * otherwise passing test methods into failing tests if a verification check is
  * failed
- * 
+ *
  * <pre>
- *     public static class ErrorLogVerifier() {
+ *     public static class ErrorLogVerifier {
  *        private ErrorLog errorLog = new ErrorLog();
- *     
+ *
  *        &#064;Rule
- *        public MethodRule verifier = new Verifier() {
+ *        public Verifier verifier = new Verifier() {
  *           &#064;Override public void verify() {
  *              assertTrue(errorLog.isEmpty());
  *           }
  *        }
- *        
+ *
  *        &#064;Test public void testThatMightWriteErrorLog() {
  *           // ...
  *        }
  *     }
  * </pre>
+ *
+ * @since 4.7
  */
-public class Verifier implements TestRule {
-	public Statement apply(final Statement base, Description description) {
-		return new Statement() {
-			@Override
-			public void evaluate() throws Throwable {
-				base.evaluate();
-				verify();
-			}
-		};
-	}
+public abstract class Verifier implements TestRule {
+    public Statement apply(final Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                base.evaluate();
+                verify();
+            }
+        };
+    }
 
-	/**
-	 * Override this to add verification logic. Overrides should throw an
-	 * exception to indicate that verification failed.
-	 */
-	protected void verify() throws Throwable {
-	}
+    /**
+     * Override this to add verification logic. Overrides should throw an
+     * exception to indicate that verification failed.
+     */
+    protected void verify() throws Throwable {
+    }
 }
diff --git a/src/main/java/org/junit/runner/Computer.java b/src/main/java/org/junit/runner/Computer.java
index 939f702..8bb4b20 100644
--- a/src/main/java/org/junit/runner/Computer.java
+++ b/src/main/java/org/junit/runner/Computer.java
@@ -8,33 +8,35 @@
  * Represents a strategy for computing runners and suites.
  * WARNING: this class is very likely to undergo serious changes in version 4.8 and
  * beyond.
+ *
+ * @since 4.6
  */
 public class Computer {
-	/**
-	 * Returns a new default computer, which runs tests in serial order
-	 */
-	public static Computer serial() {
-		return new Computer();
-	}
+    /**
+     * Returns a new default computer, which runs tests in serial order
+     */
+    public static Computer serial() {
+        return new Computer();
+    }
 
-	/**
-	 * Create a suite for {@code classes}, building Runners with {@code builder}.
-	 * Throws an InitializationError if Runner construction fails
-	 */
-	public Runner getSuite(final RunnerBuilder builder,
-			Class<?>[] classes) throws InitializationError {
-		return new Suite(new RunnerBuilder() {
-			@Override
-			public Runner runnerForClass(Class<?> testClass) throws Throwable {
-				return getRunner(builder, testClass);
-			}
-		}, classes);
-	}
+    /**
+     * Create a suite for {@code classes}, building Runners with {@code builder}.
+     * Throws an InitializationError if Runner construction fails
+     */
+    public Runner getSuite(final RunnerBuilder builder,
+            Class<?>[] classes) throws InitializationError {
+        return new Suite(new RunnerBuilder() {
+            @Override
+            public Runner runnerForClass(Class<?> testClass) throws Throwable {
+                return getRunner(builder, testClass);
+            }
+        }, classes);
+    }
 
-	/**
-	 * Create a single-class runner for {@code testClass}, using {@code builder}
-	 */
-	protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
-		return builder.runnerForClass(testClass);
-	}
+    /**
+     * Create a single-class runner for {@code testClass}, using {@code builder}
+     */
+    protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
+        return builder.runnerForClass(testClass);
+    }
 }
diff --git a/src/main/java/org/junit/runner/Describable.java b/src/main/java/org/junit/runner/Describable.java
index e59cc01..1514141 100644
--- a/src/main/java/org/junit/runner/Describable.java
+++ b/src/main/java/org/junit/runner/Describable.java
@@ -3,10 +3,12 @@
 
 /**
  * Represents an object that can describe itself
+ *
+ * @since 4.5
  */
 public interface Describable {
-	/**
-	 * @return a {@link Description} showing the tests to be run by the receiver
-	 */
-	public abstract Description getDescription();
+    /**
+     * @return a {@link Description} showing the tests to be run by the receiver
+     */
+    public abstract Description getDescription();
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/runner/FilterFactories.java b/src/main/java/org/junit/runner/FilterFactories.java
new file mode 100644
index 0000000..020d394
--- /dev/null
+++ b/src/main/java/org/junit/runner/FilterFactories.java
@@ -0,0 +1,82 @@
+package org.junit.runner;

+

+import org.junit.internal.Classes;

+import org.junit.runner.FilterFactory.FilterNotCreatedException;

+import org.junit.runner.manipulation.Filter;

+

+/**

+ * Utility class whose methods create a {@link FilterFactory}.

+ */

+class FilterFactories {

+    /**

+     * Creates a {@link Filter}.

+     *

+     * A filter specification is of the form "package.of.FilterFactory=args-to-filter-factory" or

+     * "package.of.FilterFactory".

+     *

+     * @param request the request that will be filtered

+     * @param filterSpec the filter specification

+     * @throws org.junit.runner.FilterFactory.FilterNotCreatedException

+     */

+    public static Filter createFilterFromFilterSpec(Request request, String filterSpec)

+            throws FilterFactory.FilterNotCreatedException {

+        Description topLevelDescription = request.getRunner().getDescription();

+        String[] tuple;

+

+        if (filterSpec.contains("=")) {

+            tuple = filterSpec.split("=", 2);

+        } else {

+            tuple = new String[]{ filterSpec, "" };

+        }

+

+        return createFilter(tuple[0], new FilterFactoryParams(topLevelDescription, tuple[1]));

+    }

+

+    /**

+     * Creates a {@link Filter}.

+     *

+     * @param filterFactoryFqcn The fully qualified class name of the {@link FilterFactory}

+     * @param params The arguments to the {@link FilterFactory}

+     */

+    public static Filter createFilter(String filterFactoryFqcn, FilterFactoryParams params)

+            throws FilterFactory.FilterNotCreatedException {

+        FilterFactory filterFactory = createFilterFactory(filterFactoryFqcn);

+

+        return filterFactory.createFilter(params);

+    }

+

+    /**

+     * Creates a {@link Filter}.

+     *

+     * @param filterFactoryClass The class of the {@link FilterFactory}

+     * @param params             The arguments to the {@link FilterFactory}

+     *

+     */

+    public static Filter createFilter(Class<? extends FilterFactory> filterFactoryClass, FilterFactoryParams params)

+            throws FilterFactory.FilterNotCreatedException {

+        FilterFactory filterFactory = createFilterFactory(filterFactoryClass);

+

+        return filterFactory.createFilter(params);

+    }

+

+    static FilterFactory createFilterFactory(String filterFactoryFqcn) throws FilterNotCreatedException {

+        Class<? extends FilterFactory> filterFactoryClass;

+

+        try {

+            filterFactoryClass = Classes.getClass(filterFactoryFqcn).asSubclass(FilterFactory.class);

+        } catch (Exception e) {

+            throw new FilterNotCreatedException(e);

+        }

+

+        return createFilterFactory(filterFactoryClass);

+    }

+

+    static FilterFactory createFilterFactory(Class<? extends FilterFactory> filterFactoryClass)

+            throws FilterNotCreatedException {

+        try {

+            return filterFactoryClass.getConstructor().newInstance();

+        } catch (Exception e) {

+            throw new FilterNotCreatedException(e);

+        }

+    }

+}

diff --git a/src/main/java/org/junit/runner/FilterFactory.java b/src/main/java/org/junit/runner/FilterFactory.java
new file mode 100644
index 0000000..57b4eaa
--- /dev/null
+++ b/src/main/java/org/junit/runner/FilterFactory.java
@@ -0,0 +1,24 @@
+package org.junit.runner;

+

+import org.junit.runner.manipulation.Filter;

+

+/**

+ * Extend this class to create a factory that creates {@link Filter}.

+ */

+public interface FilterFactory {

+    /**

+     * Creates a {@link Filter} given a {@link FilterFactoryParams} argument.

+     *

+     * @param params Parameters needed to create the {@link Filter}

+     */

+    Filter createFilter(FilterFactoryParams params) throws FilterNotCreatedException;

+

+    /**

+     * Exception thrown if the {@link Filter} cannot be created.

+     */

+    public static class FilterNotCreatedException extends Exception {

+        public FilterNotCreatedException(Exception exception) {

+            super(exception.getMessage(), exception);

+        }

+    }

+}

diff --git a/src/main/java/org/junit/runner/FilterFactoryParams.java b/src/main/java/org/junit/runner/FilterFactoryParams.java
new file mode 100644
index 0000000..1e74ab9
--- /dev/null
+++ b/src/main/java/org/junit/runner/FilterFactoryParams.java
@@ -0,0 +1,23 @@
+package org.junit.runner;

+

+public final class FilterFactoryParams {

+    private final Description topLevelDescription;

+    private final String args;

+

+    public FilterFactoryParams(Description topLevelDescription, String args) {

+        if (args == null || topLevelDescription == null) {

+            throw new NullPointerException();

+        }

+

+        this.topLevelDescription = topLevelDescription;

+        this.args = args;

+    }

+

+    public String getArgs() {

+        return args;

+    }

+

+    public Description getTopLevelDescription() {

+        return topLevelDescription;

+    }

+}

diff --git a/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
new file mode 100644
index 0000000..434157c
--- /dev/null
+++ b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java
@@ -0,0 +1,149 @@
+package org.junit.runner;

+

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.List;

+

+import org.junit.internal.Classes;

+import org.junit.runner.FilterFactory.FilterNotCreatedException;

+import org.junit.runner.manipulation.Filter;

+import org.junit.runners.model.InitializationError;

+

+class JUnitCommandLineParseResult {

+    private final List<String> filterSpecs = new ArrayList<String>();

+    private final List<Class<?>> classes = new ArrayList<Class<?>>();

+    private final List<Throwable> parserErrors = new ArrayList<Throwable>();

+

+    /**

+     * Do not use. Testing purposes only.

+     */

+    JUnitCommandLineParseResult() {}

+

+    /**

+     * Returns filter specs parsed from command line.

+     */

+    public List<String> getFilterSpecs() {

+        return Collections.unmodifiableList(filterSpecs);

+    }

+

+    /**

+     * Returns test classes parsed from command line.

+     */

+    public List<Class<?>> getClasses() {

+        return Collections.unmodifiableList(classes);

+    }

+

+    /**

+     * Parses the arguments.

+     *

+     * @param args Arguments

+     */

+    public static JUnitCommandLineParseResult parse(String[] args) {

+        JUnitCommandLineParseResult result = new JUnitCommandLineParseResult();

+

+        result.parseArgs(args);

+

+        return result;

+    }

+

+    private void parseArgs(String[] args) {

+        parseParameters(parseOptions(args));

+    }

+

+    String[] parseOptions(String... args) {

+        for (int i = 0; i != args.length; ++i) {

+            String arg = args[i];

+

+            if (arg.equals("--")) {

+                return copyArray(args, i + 1, args.length);

+            } else if (arg.startsWith("--")) {

+                if (arg.startsWith("--filter=") || arg.equals("--filter")) {

+                    String filterSpec;

+                    if (arg.equals("--filter")) {

+                        ++i;

+

+                        if (i < args.length) {

+                            filterSpec = args[i];

+                        } else {

+                            parserErrors.add(new CommandLineParserError(arg + " value not specified"));

+                            break;

+                        }

+                    } else {

+                        filterSpec = arg.substring(arg.indexOf('=') + 1);

+                    }

+

+                    filterSpecs.add(filterSpec);

+                } else {

+                    parserErrors.add(new CommandLineParserError("JUnit knows nothing about the " + arg + " option"));

+                }

+            } else {

+                return copyArray(args, i, args.length);

+            }

+        }

+

+        return new String[]{};

+    }

+

+    private String[] copyArray(String[] args, int from, int to) {

+        ArrayList<String> result = new ArrayList<String>();

+

+        for (int j = from; j != to; ++j) {

+            result.add(args[j]);

+        }

+

+        return result.toArray(new String[result.size()]);

+    }

+

+    void parseParameters(String[] args) {

+        for (String arg : args) {

+            try {

+                classes.add(Classes.getClass(arg));

+            } catch (ClassNotFoundException e) {

+                parserErrors.add(new IllegalArgumentException("Could not find class [" + arg + "]", e));

+            }

+        }

+    }

+

+    private Request errorReport(Throwable cause) {

+        return Request.errorReport(JUnitCommandLineParseResult.class, cause);

+    }

+

+    /**

+     * Creates a {@link Request}.

+     *

+     * @param computer {@link Computer} to be used.

+     */

+    public Request createRequest(Computer computer) {

+        if (parserErrors.isEmpty()) {

+            Request request = Request.classes(

+                    computer, classes.toArray(new Class<?>[classes.size()]));

+            return applyFilterSpecs(request);

+        } else {

+            return errorReport(new InitializationError(parserErrors));

+        }

+    }

+

+    private Request applyFilterSpecs(Request request) {

+        try {

+            for (String filterSpec : filterSpecs) {

+                Filter filter = FilterFactories.createFilterFromFilterSpec(

+                        request, filterSpec);

+                request = request.filterWith(filter);

+            }

+            return request;

+        } catch (FilterNotCreatedException e) {

+            return errorReport(e);

+        }

+    }

+

+    /**

+     * Exception used if there's a problem parsing the command line.

+     */

+    public static class CommandLineParserError extends Exception {

+        private static final long serialVersionUID= 1L;

+

+        public CommandLineParserError(String message) {

+            super(message);

+        }

+    }

+}

diff --git a/src/main/java/org/junit/runner/JUnitCore.java b/src/main/java/org/junit/runner/JUnitCore.java
index 2fcd3b3..c1479e0 100644
--- a/src/main/java/org/junit/runner/JUnitCore.java
+++ b/src/main/java/org/junit/runner/JUnitCore.java
@@ -1,186 +1,167 @@
 package org.junit.runner;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import junit.runner.Version;
 import org.junit.internal.JUnitSystem;
 import org.junit.internal.RealSystem;
 import org.junit.internal.TextListener;
 import org.junit.internal.runners.JUnit38ClassRunner;
-import org.junit.runner.notification.Failure;
 import org.junit.runner.notification.RunListener;
 import org.junit.runner.notification.RunNotifier;
 
 /**
- * <code>JUnitCore</code> is a facade for running tests. It supports running JUnit 4 tests, 
- * JUnit 3.8.x tests, and mixtures. To run tests from the command line, run 
+ * <code>JUnitCore</code> is a facade for running tests. It supports running JUnit 4 tests,
+ * JUnit 3.8.x tests, and mixtures. To run tests from the command line, run
  * <code>java org.junit.runner.JUnitCore TestClass1 TestClass2 ...</code>.
- * For one-shot test runs, use the static method {@link #runClasses(Class[])}. 
+ * For one-shot test runs, use the static method {@link #runClasses(Class[])}.
  * If you want to add special listeners,
  * create an instance of {@link org.junit.runner.JUnitCore} first and use it to run the tests.
- * 
+ *
  * @see org.junit.runner.Result
  * @see org.junit.runner.notification.RunListener
  * @see org.junit.runner.Request
+ * @since 4.0
  */
 public class JUnitCore {
-	private RunNotifier fNotifier;
+    private final RunNotifier notifier = new RunNotifier();
 
-	/**
-	 * Create a new <code>JUnitCore</code> to run tests.
-	 */
-	public JUnitCore() {
-		fNotifier= new RunNotifier();
-	}
+    /**
+     * Run the tests contained in the classes named in the <code>args</code>.
+     * If all tests run successfully, exit with a status of 0. Otherwise exit with a status of 1.
+     * Write feedback while tests are running and write
+     * stack traces for all failed tests after the tests all complete.
+     *
+     * @param args names of classes in which to find tests to run
+     */
+    public static void main(String... args) {
+        Result result = new JUnitCore().runMain(new RealSystem(), args);
+        System.exit(result.wasSuccessful() ? 0 : 1);
+    }
 
-	/**
-	 * Run the tests contained in the classes named in the <code>args</code>.
-	 * If all tests run successfully, exit with a status of 0. Otherwise exit with a status of 1.
-	 * Write feedback while tests are running and write
-	 * stack traces for all failed tests after the tests all complete.
-	 * @param args names of classes in which to find tests to run
-	 */
-	public static void main(String... args) {
-		runMainAndExit(new RealSystem(), args);
-	}
+    /**
+     * Run the tests contained in <code>classes</code>. Write feedback while the tests
+     * are running and write stack traces for all failed tests after all tests complete. This is
+     * similar to {@link #main(String[])}, but intended to be used programmatically.
+     *
+     * @param classes Classes in which to find tests
+     * @return a {@link Result} describing the details of the test run and the failed tests.
+     */
+    public static Result runClasses(Class<?>... classes) {
+        return runClasses(defaultComputer(), classes);
+    }
 
-	/**
-	 * Do not use. Testing purposes only.
-	 * @param system 
-	 */
-	public static void runMainAndExit(JUnitSystem system, String... args) {
-		Result result= new JUnitCore().runMain(system, args);
-		system.exit(result.wasSuccessful() ? 0 : 1);
-	}
+    /**
+     * Run the tests contained in <code>classes</code>. Write feedback while the tests
+     * are running and write stack traces for all failed tests after all tests complete. This is
+     * similar to {@link #main(String[])}, but intended to be used programmatically.
+     *
+     * @param computer Helps construct Runners from classes
+     * @param classes  Classes in which to find tests
+     * @return a {@link Result} describing the details of the test run and the failed tests.
+     */
+    public static Result runClasses(Computer computer, Class<?>... classes) {
+        return new JUnitCore().run(computer, classes);
+    }
 
-	/**
-	 * Run the tests contained in <code>classes</code>. Write feedback while the tests
-	 * are running and write stack traces for all failed tests after all tests complete. This is
-	 * similar to {@link #main(String[])}, but intended to be used programmatically.
-	 * @param computer Helps construct Runners from classes
-	 * @param classes Classes in which to find tests
-	 * @return a {@link Result} describing the details of the test run and the failed tests.
-	 */
-	public static Result runClasses(Computer computer, Class<?>... classes) {
-		return new JUnitCore().run(computer, classes);
-	}
-	/**
-	 * Run the tests contained in <code>classes</code>. Write feedback while the tests
-	 * are running and write stack traces for all failed tests after all tests complete. This is
-	 * similar to {@link #main(String[])}, but intended to be used programmatically.
-	 * @param classes Classes in which to find tests
-	 * @return a {@link Result} describing the details of the test run and the failed tests.
-	 */
-	public static Result runClasses(Class<?>... classes) {
-		return new JUnitCore().run(defaultComputer(), classes);
-	}
-	
-	/**
-	 * Do not use. Testing purposes only.
-	 * @param system 
-	 */
-	public Result runMain(JUnitSystem system, String... args) {
-		system.out().println("JUnit version " + Version.id());
-		List<Class<?>> classes= new ArrayList<Class<?>>();
-		List<Failure> missingClasses= new ArrayList<Failure>();
-		for (String each : args)
-			try {
-				classes.add(Class.forName(each));
-			} catch (ClassNotFoundException e) {
-				system.out().println("Could not find class: " + each);
-				Description description= Description.createSuiteDescription(each);
-				Failure failure= new Failure(description, e);
-				missingClasses.add(failure);
-			}
-		RunListener listener= new TextListener(system);
-		addListener(listener);
-		Result result= run(classes.toArray(new Class[0]));
-		for (Failure each : missingClasses)
-			result.getFailures().add(each);
-		return result;
-	}
+    /**
+     * @param system
+     * @param args from main()
+     */
+    Result runMain(JUnitSystem system, String... args) {
+        system.out().println("JUnit version " + Version.id());
 
-	/**
-	 * @return the version number of this release
-	 */
-	public String getVersion() {
-		return Version.id();
-	}
-	
-	/**
-	 * Run all the tests in <code>classes</code>.
-	 * @param classes the classes containing tests
-	 * @return a {@link Result} describing the details of the test run and the failed tests.
-	 */
-	public Result run(Class<?>... classes) {
-		return run(Request.classes(defaultComputer(), classes));
-	}
+        JUnitCommandLineParseResult jUnitCommandLineParseResult = JUnitCommandLineParseResult.parse(args);
 
-	/**
-	 * Run all the tests in <code>classes</code>.
-	 * @param computer Helps construct Runners from classes
-	 * @param classes the classes containing tests
-	 * @return a {@link Result} describing the details of the test run and the failed tests.
-	 */
-	public Result run(Computer computer, Class<?>... classes) {
-		return run(Request.classes(computer, classes));
-	}
+        RunListener listener = new TextListener(system);
+        addListener(listener);
 
-	/**
-	 * Run all the tests contained in <code>request</code>.
-	 * @param request the request describing tests
-	 * @return a {@link Result} describing the details of the test run and the failed tests.
-	 */
-	public Result run(Request request) {
-		return run(request.getRunner());
-	}
+        return run(jUnitCommandLineParseResult.createRequest(defaultComputer()));
+    }
 
-	/**
-	 * Run all the tests contained in JUnit 3.8.x <code>test</code>. Here for backward compatibility.
-	 * @param test the old-style test
-	 * @return a {@link Result} describing the details of the test run and the failed tests.
-	 */
-	public Result run(junit.framework.Test test) { 
-		return run(new JUnit38ClassRunner(test));
-	}
-	
-	/**
-	 * Do not use. Testing purposes only.
-	 */
-	public Result run(Runner runner) {
-		Result result= new Result();
-		RunListener listener= result.createListener();
-		fNotifier.addFirstListener(listener);
-		try {
-			fNotifier.fireTestRunStarted(runner.getDescription());
-			runner.run(fNotifier);
-			fNotifier.fireTestRunFinished(result);
-		} finally {
-			removeListener(listener);
-		}
-		return result;
-	}
-	
-	/**
-	 * Add a listener to be notified as the tests run.
-	 * @param listener the listener to add
-	 * @see org.junit.runner.notification.RunListener
-	 */
-	public void addListener(RunListener listener) {
-		fNotifier.addListener(listener);
-	}
+    /**
+     * @return the version number of this release
+     */
+    public String getVersion() {
+        return Version.id();
+    }
 
-	/**
-	 * Remove a listener.
-	 * @param listener the listener to remove
-	 */
-	public void removeListener(RunListener listener) {
-		fNotifier.removeListener(listener);
-	}
-	
-	static Computer defaultComputer() {
-		return new Computer();
-	}
+    /**
+     * Run all the tests in <code>classes</code>.
+     *
+     * @param classes the classes containing tests
+     * @return a {@link Result} describing the details of the test run and the failed tests.
+     */
+    public Result run(Class<?>... classes) {
+        return run(defaultComputer(), classes);
+    }
 
+    /**
+     * Run all the tests in <code>classes</code>.
+     *
+     * @param computer Helps construct Runners from classes
+     * @param classes the classes containing tests
+     * @return a {@link Result} describing the details of the test run and the failed tests.
+     */
+    public Result run(Computer computer, Class<?>... classes) {
+        return run(Request.classes(computer, classes));
+    }
+
+    /**
+     * Run all the tests contained in <code>request</code>.
+     *
+     * @param request the request describing tests
+     * @return a {@link Result} describing the details of the test run and the failed tests.
+     */
+    public Result run(Request request) {
+        return run(request.getRunner());
+    }
+
+    /**
+     * Run all the tests contained in JUnit 3.8.x <code>test</code>. Here for backward compatibility.
+     *
+     * @param test the old-style test
+     * @return a {@link Result} describing the details of the test run and the failed tests.
+     */
+    public Result run(junit.framework.Test test) {
+        return run(new JUnit38ClassRunner(test));
+    }
+
+    /**
+     * Do not use. Testing purposes only.
+     */
+    public Result run(Runner runner) {
+        Result result = new Result();
+        RunListener listener = result.createListener();
+        notifier.addFirstListener(listener);
+        try {
+            notifier.fireTestRunStarted(runner.getDescription());
+            runner.run(notifier);
+            notifier.fireTestRunFinished(result);
+        } finally {
+            removeListener(listener);
+        }
+        return result;
+    }
+
+    /**
+     * Add a listener to be notified as the tests run.
+     *
+     * @param listener the listener to add
+     * @see org.junit.runner.notification.RunListener
+     */
+    public void addListener(RunListener listener) {
+        notifier.addListener(listener);
+    }
+
+    /**
+     * Remove a listener.
+     *
+     * @param listener the listener to remove
+     */
+    public void removeListener(RunListener listener) {
+        notifier.removeListener(listener);
+    }
+
+    static Computer defaultComputer() {
+        return new Computer();
+    }
 }
diff --git a/src/main/java/org/junit/runner/Request.java b/src/main/java/org/junit/runner/Request.java
index 310b915..79c0f1e 100644
--- a/src/main/java/org/junit/runner/Request.java
+++ b/src/main/java/org/junit/runner/Request.java
@@ -11,151 +11,160 @@
 import org.junit.runners.model.InitializationError;
 
 /**
- * <p>A <code>Request</code> is an abstract description of tests to be run. Older versions of 
+ * A <code>Request</code> is an abstract description of tests to be run. Older versions of
  * JUnit did not need such a concept--tests to be run were described either by classes containing
  * tests or a tree of {@link  org.junit.Test}s. However, we want to support filtering and sorting,
  * so we need a more abstract specification than the tests themselves and a richer
- * specification than just the classes.</p>
- * 
- * <p>The flow when JUnit runs tests is that a <code>Request</code> specifies some tests to be run ->
- * a {@link org.junit.runner.Runner} is created for each class implied by the <code>Request</code> -> 
- * the {@link org.junit.runner.Runner} returns a detailed {@link org.junit.runner.Description} 
- * which is a tree structure of the tests to be run.</p>
+ * specification than just the classes.
+ *
+ * <p>The flow when JUnit runs tests is that a <code>Request</code> specifies some tests to be run -&gt;
+ * a {@link org.junit.runner.Runner} is created for each class implied by the <code>Request</code> -&gt;
+ * the {@link org.junit.runner.Runner} returns a detailed {@link org.junit.runner.Description}
+ * which is a tree structure of the tests to be run.
+ *
+ * @since 4.0
  */
 public abstract class Request {
-	/**
-	 * Create a <code>Request</code> that, when processed, will run a single test.
-	 * This is done by filtering out all other tests. This method is used to support rerunning
-	 * single tests.
-	 * @param clazz the class of the test
-	 * @param methodName the name of the test
-	 * @return a <code>Request</code> that will cause a single test be run
-	 */
-	public static Request method(Class<?> clazz, String methodName) {
-		Description method= Description.createTestDescription(clazz, methodName);
-		return Request.aClass(clazz).filterWith(method);
-	}
+    /**
+     * Create a <code>Request</code> that, when processed, will run a single test.
+     * This is done by filtering out all other tests. This method is used to support rerunning
+     * single tests.
+     *
+     * @param clazz the class of the test
+     * @param methodName the name of the test
+     * @return a <code>Request</code> that will cause a single test be run
+     */
+    public static Request method(Class<?> clazz, String methodName) {
+        Description method = Description.createTestDescription(clazz, methodName);
+        return Request.aClass(clazz).filterWith(method);
+    }
 
-	/**
-	 * Create a <code>Request</code> that, when processed, will run all the tests
-	 * in a class. The odd name is necessary because <code>class</code> is a reserved word.
-	 * @param clazz the class containing the tests
-	 * @return a <code>Request</code> that will cause all tests in the class to be run
-	 */
-	public static Request aClass(Class<?> clazz) {
-		return new ClassRequest(clazz);
-	}
+    /**
+     * Create a <code>Request</code> that, when processed, will run all the tests
+     * in a class. The odd name is necessary because <code>class</code> is a reserved word.
+     *
+     * @param clazz the class containing the tests
+     * @return a <code>Request</code> that will cause all tests in the class to be run
+     */
+    public static Request aClass(Class<?> clazz) {
+        return new ClassRequest(clazz);
+    }
 
-	/**
-	 * Create a <code>Request</code> that, when processed, will run all the tests
-	 * in a class. If the class has a suite() method, it will be ignored.
-	 * @param clazz the class containing the tests
-	 * @return a <code>Request</code> that will cause all tests in the class to be run
-	 */
-	public static Request classWithoutSuiteMethod(Class<?> clazz) {
-		return new ClassRequest(clazz, false);
-	}
+    /**
+     * Create a <code>Request</code> that, when processed, will run all the tests
+     * in a class. If the class has a suite() method, it will be ignored.
+     *
+     * @param clazz the class containing the tests
+     * @return a <code>Request</code> that will cause all tests in the class to be run
+     */
+    public static Request classWithoutSuiteMethod(Class<?> clazz) {
+        return new ClassRequest(clazz, false);
+    }
 
-	/**
-	 * Create a <code>Request</code> that, when processed, will run all the tests
-	 * in a set of classes.
-	 * @param computer Helps construct Runners from classes
-	 * @param classes the classes containing the tests
-	 * @return a <code>Request</code> that will cause all tests in the classes to be run
-	 */
-	public static Request classes(Computer computer, Class<?>... classes) {
-		try {
-			AllDefaultPossibilitiesBuilder builder= new AllDefaultPossibilitiesBuilder(true);
-			Runner suite= computer.getSuite(builder, classes);
-			return runner(suite);
-		} catch (InitializationError e) {
-			throw new RuntimeException(
-					"Bug in saff's brain: Suite constructor, called as above, should always complete");
-		}
-	}
+    /**
+     * Create a <code>Request</code> that, when processed, will run all the tests
+     * in a set of classes.
+     *
+     * @param computer Helps construct Runners from classes
+     * @param classes the classes containing the tests
+     * @return a <code>Request</code> that will cause all tests in the classes to be run
+     */
+    public static Request classes(Computer computer, Class<?>... classes) {
+        try {
+            AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
+            Runner suite = computer.getSuite(builder, classes);
+            return runner(suite);
+        } catch (InitializationError e) {
+            throw new RuntimeException(
+                    "Bug in saff's brain: Suite constructor, called as above, should always complete");
+        }
+    }
 
-	/**
-	 * Create a <code>Request</code> that, when processed, will run all the tests
-	 * in a set of classes with the default <code>Computer</code>.
-	 * @param classes the classes containing the tests
-	 * @return a <code>Request</code> that will cause all tests in the classes to be run
-	 */
-	public static Request classes(Class<?>... classes) {
-		return classes(JUnitCore.defaultComputer(), classes);
-	}
-	
+    /**
+     * Create a <code>Request</code> that, when processed, will run all the tests
+     * in a set of classes with the default <code>Computer</code>.
+     *
+     * @param classes the classes containing the tests
+     * @return a <code>Request</code> that will cause all tests in the classes to be run
+     */
+    public static Request classes(Class<?>... classes) {
+        return classes(JUnitCore.defaultComputer(), classes);
+    }
 
-	/**
-	 * Not used within JUnit.  Clients should simply instantiate ErrorReportingRunner themselves
-	 */
-	@Deprecated	
-	public static Request errorReport(Class<?> klass, Throwable cause) {
-		return runner(new ErrorReportingRunner(klass, cause));
-	}
 
-	/**
-	 * @param runner the runner to return
-	 * @return a <code>Request</code> that will run the given runner when invoked
-	 */
-	public static Request runner(final Runner runner) {
-		return new Request(){
-			@Override
-			public Runner getRunner() {
-				return runner;
-			}		
-		};
-	}
+    /**
+     * Creates a {@link Request} that, when processed, will report an error for the given
+     * test class with the given cause.
+     */
+    public static Request errorReport(Class<?> klass, Throwable cause) {
+        return runner(new ErrorReportingRunner(klass, cause));
+    }
 
-	/**
-	 * Returns a {@link Runner} for this Request
-	 * @return corresponding {@link Runner} for this Request
-	 */
-	public abstract Runner getRunner();
+    /**
+     * @param runner the runner to return
+     * @return a <code>Request</code> that will run the given runner when invoked
+     */
+    public static Request runner(final Runner runner) {
+        return new Request() {
+            @Override
+            public Runner getRunner() {
+                return runner;
+            }
+        };
+    }
 
-	/**
-	 * Returns a Request that only contains those tests that should run when
-	 * <code>filter</code> is applied
-	 * @param filter The {@link Filter} to apply to this Request
-	 * @return the filtered Request
-	 */
-	public Request filterWith(Filter filter) {
-		return new FilterRequest(this, filter);
-	}
+    /**
+     * Returns a {@link Runner} for this Request
+     *
+     * @return corresponding {@link Runner} for this Request
+     */
+    public abstract Runner getRunner();
 
-	/**
-	 * Returns a Request that only runs contains tests whose {@link Description}
-	 * equals <code>desiredDescription</code>
-	 * @param desiredDescription {@link Description} of those tests that should be run
-	 * @return the filtered Request
-	 */
-	public Request filterWith(final Description desiredDescription) {
-		return filterWith(Filter.matchMethodDescription(desiredDescription));
-	}
+    /**
+     * Returns a Request that only contains those tests that should run when
+     * <code>filter</code> is applied
+     *
+     * @param filter The {@link Filter} to apply to this Request
+     * @return the filtered Request
+     */
+    public Request filterWith(Filter filter) {
+        return new FilterRequest(this, filter);
+    }
 
-	/**
-	 * Returns a Request whose Tests can be run in a certain order, defined by 
-	 * <code>comparator</code>
-	 * 
-	 * For example, here is code to run a test suite in alphabetical order:
-	 * 
-	 * <pre>
-	private static Comparator<Description> forward() {
-		return new Comparator<Description>() {
-			public int compare(Description o1, Description o2) {
-				return o1.getDisplayName().compareTo(o2.getDisplayName());
-			}
-		};
-	}
-	
-	public static main() {
-		new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
-	}
-	 * </pre>
-	 * 
-	 * @param comparator definition of the order of the tests in this Request
-	 * @return a Request with ordered Tests
-	 */
-	public Request sortWith(Comparator<Description> comparator) {
-		return new SortingRequest(this, comparator);
-	}
+    /**
+     * Returns a Request that only runs contains tests whose {@link Description}
+     * equals <code>desiredDescription</code>
+     *
+     * @param desiredDescription {@link Description} of those tests that should be run
+     * @return the filtered Request
+     */
+    public Request filterWith(final Description desiredDescription) {
+        return filterWith(Filter.matchMethodDescription(desiredDescription));
+    }
+
+    /**
+     * Returns a Request whose Tests can be run in a certain order, defined by
+     * <code>comparator</code>
+     * <p>
+     * For example, here is code to run a test suite in alphabetical order:
+     * <pre>
+     * private static Comparator&lt;Description&gt; forward() {
+     * return new Comparator&lt;Description&gt;() {
+     * public int compare(Description o1, Description o2) {
+     * return o1.getDisplayName().compareTo(o2.getDisplayName());
+     * }
+     * };
+     * }
+     *
+     * public static main() {
+     * new JUnitCore().run(Request.aClass(AllTests.class).sortWith(forward()));
+     * }
+     * </pre>
+     *
+     * @param comparator definition of the order of the tests in this Request
+     * @return a Request with ordered Tests
+     */
+    public Request sortWith(Comparator<Description> comparator) {
+        return new SortingRequest(this, comparator);
+    }
 }
diff --git a/src/main/java/org/junit/runner/Result.java b/src/main/java/org/junit/runner/Result.java
index edfb97c..73ad059 100644
--- a/src/main/java/org/junit/runner/Result.java
+++ b/src/main/java/org/junit/runner/Result.java
@@ -1,106 +1,196 @@
 package org.junit.runner;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.ObjectStreamField;
 import java.io.Serializable;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 
 import org.junit.runner.notification.Failure;
 import org.junit.runner.notification.RunListener;
 
 /**
- * A <code>Result</code> collects and summarizes information from running multiple
- * tests. Since tests are expected to run correctly, successful tests are only noted in
- * the count of tests that ran.
+ * A <code>Result</code> collects and summarizes information from running multiple tests.
+ * All tests are counted -- additional information is collected from tests that fail.
+ *
+ * @since 4.0
  */
 public class Result implements Serializable {
-	private static final long serialVersionUID = 1L;
-	private AtomicInteger fCount = new AtomicInteger();
-	private AtomicInteger fIgnoreCount= new AtomicInteger();
-	private final List<Failure> fFailures= Collections.synchronizedList(new ArrayList<Failure>());
-	private long fRunTime= 0;
-	private long fStartTime;
+    private static final long serialVersionUID = 1L;
+    private static final ObjectStreamField[] serialPersistentFields =
+            ObjectStreamClass.lookup(SerializedForm.class).getFields();
+    private final AtomicInteger count;
+    private final AtomicInteger ignoreCount;
+    private final CopyOnWriteArrayList<Failure> failures;
+    private final AtomicLong runTime;
+    private final AtomicLong startTime;
 
-	/**
-	 * @return the number of tests run
-	 */
-	public int getRunCount() {
-		return fCount.get();
-	}
+    /** Only set during deserialization process. */
+    private SerializedForm serializedForm;
 
-	/**
-	 * @return the number of tests that failed during the run
-	 */
-	public int getFailureCount() {
-		return fFailures.size();
-	}
+    public Result() {
+        count = new AtomicInteger();
+        ignoreCount = new AtomicInteger();
+        failures = new CopyOnWriteArrayList<Failure>();
+        runTime = new AtomicLong();
+        startTime = new AtomicLong();
+    }
 
-	/**
-	 * @return the number of milliseconds it took to run the entire suite to run
-	 */
-	public long getRunTime() {
-		return fRunTime;
-	}
+    private Result(SerializedForm serializedForm) {
+        count = serializedForm.fCount;
+        ignoreCount = serializedForm.fIgnoreCount;
+        failures = new CopyOnWriteArrayList<Failure>(serializedForm.fFailures);
+        runTime = new AtomicLong(serializedForm.fRunTime);
+        startTime = new AtomicLong(serializedForm.fStartTime);
+    }
 
-	/**
-	 * @return the {@link Failure}s describing tests that failed and the problems they encountered
-	 */
-	public List<Failure> getFailures() {
-		return fFailures;
-	}
+    /**
+     * @return the number of tests run
+     */
+    public int getRunCount() {
+        return count.get();
+    }
 
-	/**
-	 * @return the number of tests ignored during the run
-	 */
-	public int getIgnoreCount() {
-		return fIgnoreCount.get();
-	}
+    /**
+     * @return the number of tests that failed during the run
+     */
+    public int getFailureCount() {
+        return failures.size();
+    }
 
-	/**
-	 * @return <code>true</code> if all tests succeeded
-	 */
-	public boolean wasSuccessful() {
-		return getFailureCount() == 0;
-	}
+    /**
+     * @return the number of milliseconds it took to run the entire suite to run
+     */
+    public long getRunTime() {
+        return runTime.get();
+    }
 
-	private class Listener extends RunListener {
-		@Override
-		public void testRunStarted(Description description) throws Exception {
-			fStartTime= System.currentTimeMillis();
-		}
+    /**
+     * @return the {@link Failure}s describing tests that failed and the problems they encountered
+     */
+    public List<Failure> getFailures() {
+        return failures;
+    }
 
-		@Override
-		public void testRunFinished(Result result) throws Exception {
-			long endTime= System.currentTimeMillis();
-			fRunTime+= endTime - fStartTime;
-		}
+    /**
+     * @return the number of tests ignored during the run
+     */
+    public int getIgnoreCount() {
+        return ignoreCount.get();
+    }
 
-		@Override
-		public void testFinished(Description description) throws Exception {
-			fCount.getAndIncrement();
-		}
+    /**
+     * @return <code>true</code> if all tests succeeded
+     */
+    public boolean wasSuccessful() {
+        return getFailureCount() == 0;
+    }
 
-		@Override
-		public void testFailure(Failure failure) throws Exception {
-			fFailures.add(failure);
-		}
+    private void writeObject(ObjectOutputStream s) throws IOException {
+        SerializedForm serializedForm = new SerializedForm(this);
+        serializedForm.serialize(s);
+    }
 
-		@Override
-		public void testIgnored(Description description) throws Exception {
-			fIgnoreCount.getAndIncrement();
-		}
+    private void readObject(ObjectInputStream s)
+            throws ClassNotFoundException, IOException {
+        serializedForm = SerializedForm.deserialize(s);
+    }
 
-		@Override
-		public void testAssumptionFailure(Failure failure) {
-			// do nothing: same as passing (for 4.5; may change in 4.6)
-		}
-	}
+    private Object readResolve()  {
+        return new Result(serializedForm);
+    }
 
-	/**
-	 * Internal use only.
-	 */
-	public RunListener createListener() {
-		return new Listener();
-	}
+    @RunListener.ThreadSafe
+    private class Listener extends RunListener {
+        @Override
+        public void testRunStarted(Description description) throws Exception {
+            startTime.set(System.currentTimeMillis());
+        }
+
+        @Override
+        public void testRunFinished(Result result) throws Exception {
+            long endTime = System.currentTimeMillis();
+            runTime.addAndGet(endTime - startTime.get());
+        }
+
+        @Override
+        public void testFinished(Description description) throws Exception {
+            count.getAndIncrement();
+        }
+
+        @Override
+        public void testFailure(Failure failure) throws Exception {
+            failures.add(failure);
+        }
+
+        @Override
+        public void testIgnored(Description description) throws Exception {
+            ignoreCount.getAndIncrement();
+        }
+
+        @Override
+        public void testAssumptionFailure(Failure failure) {
+            // do nothing: same as passing (for 4.5; may change in 4.6)
+        }
+    }
+
+    /**
+     * Internal use only.
+     */
+    public RunListener createListener() {
+        return new Listener();
+    }
+
+    /**
+     * Represents the serialized output of {@code Result}. The fields on this
+     * class match the files that {@code Result} had in JUnit 4.11.
+     */
+    private static class SerializedForm implements Serializable {
+        private static final long serialVersionUID = 1L;
+        private final AtomicInteger fCount;
+        private final AtomicInteger fIgnoreCount;
+        private final List<Failure> fFailures;
+        private final long fRunTime;
+        private final long fStartTime;
+
+        public SerializedForm(Result result) {
+            fCount = result.count;
+            fIgnoreCount = result.ignoreCount;
+            fFailures = Collections.synchronizedList(new ArrayList<Failure>(result.failures));
+            fRunTime = result.runTime.longValue();
+            fStartTime = result.startTime.longValue();
+        }
+
+        @SuppressWarnings("unchecked")
+        private SerializedForm(ObjectInputStream.GetField fields) throws IOException {
+            fCount = (AtomicInteger) fields.get("fCount", null);
+            fIgnoreCount = (AtomicInteger) fields.get("fIgnoreCount", null);
+            fFailures = (List<Failure>) fields.get("fFailures", null);
+            fRunTime = fields.get("fRunTime", 0L);
+            fStartTime = fields.get("fStartTime", 0L);
+        }
+
+        public void serialize(ObjectOutputStream s) throws IOException {
+            ObjectOutputStream.PutField fields = s.putFields();
+            fields.put("fCount", fCount);
+            fields.put("fIgnoreCount", fIgnoreCount);
+            fields.put("fFailures", fFailures);
+            fields.put("fRunTime", fRunTime);
+            fields.put("fStartTime", fStartTime);
+            s.writeFields();
+        }
+
+        public static SerializedForm deserialize(ObjectInputStream s)
+                throws ClassNotFoundException, IOException {
+            ObjectInputStream.GetField fields = s.readFields();
+            return new SerializedForm(fields);
+        }
+    }
 }
diff --git a/src/main/java/org/junit/runner/RunWith.java b/src/main/java/org/junit/runner/RunWith.java
index 602edf0..3428ee2 100644
--- a/src/main/java/org/junit/runner/RunWith.java
+++ b/src/main/java/org/junit/runner/RunWith.java
@@ -7,28 +7,30 @@
 import java.lang.annotation.Target;
 
 /**
- * When a class is annotated with <code>&#064;RunWith</code> or extends a class annotated 
- * with <code>&#064;RunWith</code>, JUnit will invoke the class it references to run the 
- * tests in that class instead of the runner built into JUnit. We added this feature late 
- * in development. While it seems powerful we expect the runner API to change as we learn 
- * how people really use it. Some of the classes that are currently internal will likely 
+ * When a class is annotated with <code>&#064;RunWith</code> or extends a class annotated
+ * with <code>&#064;RunWith</code>, JUnit will invoke the class it references to run the
+ * tests in that class instead of the runner built into JUnit. We added this feature late
+ * in development. While it seems powerful we expect the runner API to change as we learn
+ * how people really use it. Some of the classes that are currently internal will likely
  * be refined and become public.
- * 
+ *
  * For example, suites in JUnit 4 are built using RunWith, and a custom runner named Suite:
- * 
+ *
  * <pre>
  * &#064;RunWith(Suite.class)
  * &#064;SuiteClasses({ATest.class, BTest.class, CTest.class})
  * public class ABCSuite {
  * }
  * </pre>
+ *
+ * @since 4.0
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 @Inherited
 public @interface RunWith {
-	/**
-	 * @return a Runner class (must have a constructor that takes a single Class to run)
-	 */
-	Class<? extends Runner> value();
+    /**
+     * @return a Runner class (must have a constructor that takes a single Class to run)
+     */
+    Class<? extends Runner> value();
 }
diff --git a/src/main/java/org/junit/runner/Runner.java b/src/main/java/org/junit/runner/Runner.java
index 39e424f..d728dd8 100644
--- a/src/main/java/org/junit/runner/Runner.java
+++ b/src/main/java/org/junit/runner/Runner.java
@@ -9,32 +9,35 @@
  * a custom runner, in addition to implementing the abstract methods here you must
  * also provide a constructor that takes as an argument the {@link Class} containing
  * the tests.
- * <p/>
- * The default runner implementation guarantees that the instances of the test case
+ *
+ * <p>The default runner implementation guarantees that the instances of the test case
  * class will be constructed immediately before running the test and that the runner
- * will retain no reference to the test case instances, generally making them 
+ * will retain no reference to the test case instances, generally making them
  * available for garbage collection.
- * 
+ *
  * @see org.junit.runner.Description
  * @see org.junit.runner.RunWith
+ * @since 4.0
  */
 public abstract class Runner implements Describable {
-	/* (non-Javadoc)
-	 * @see org.junit.runner.Describable#getDescription()
-	 */
-	public abstract Description getDescription();
+    /*
+     * (non-Javadoc)
+     * @see org.junit.runner.Describable#getDescription()
+     */
+    public abstract Description getDescription();
 
-	/**
-	 * Run the tests for this runner.
-	 * @param notifier will be notified of events while tests are being run--tests being 
-	 * started, finishing, and failing
-	 */
-	public abstract void run(RunNotifier notifier);
-	
-	/**
-	 * @return the number of tests to be run by the receiver
-	 */
-	public int testCount() {
-		return getDescription().testCount();
-	}
-}
\ No newline at end of file
+    /**
+     * Run the tests for this runner.
+     *
+     * @param notifier will be notified of events while tests are being run--tests being
+     * started, finishing, and failing
+     */
+    public abstract void run(RunNotifier notifier);
+
+    /**
+     * @return the number of tests to be run by the receiver
+     */
+    public int testCount() {
+        return getDescription().testCount();
+    }
+}
diff --git a/src/main/java/org/junit/runner/manipulation/Filter.java b/src/main/java/org/junit/runner/manipulation/Filter.java
index c0f31b0..0287351 100644
--- a/src/main/java/org/junit/runner/manipulation/Filter.java
+++ b/src/main/java/org/junit/runner/manipulation/Filter.java
@@ -7,108 +7,116 @@
  * The canonical case of filtering is when you want to run a single test method in a class. Rather
  * than introduce runner API just for that one case, JUnit provides a general filtering mechanism.
  * If you want to filter the tests to be run, extend <code>Filter</code> and apply an instance of
- * your filter to the {@link org.junit.runner.Request} before running it (see 
- * {@link org.junit.runner.JUnitCore#run(Request)}. Alternatively, apply a <code>Filter</code> to 
- * a {@link org.junit.runner.Runner} before running tests (for example, in conjunction with 
+ * your filter to the {@link org.junit.runner.Request} before running it (see
+ * {@link org.junit.runner.JUnitCore#run(Request)}. Alternatively, apply a <code>Filter</code> to
+ * a {@link org.junit.runner.Runner} before running tests (for example, in conjunction with
  * {@link org.junit.runner.RunWith}.
+ *
+ * @since 4.0
  */
 public abstract class Filter {
-	/**
-	 * A null <code>Filter</code> that passes all tests through.
-	 */
-	public static Filter ALL= new Filter() {
-		@Override
-		public boolean shouldRun(Description description) {
-			return true;
-		}
+    /**
+     * A null <code>Filter</code> that passes all tests through.
+     */
+    public static final Filter ALL = new Filter() {
+        @Override
+        public boolean shouldRun(Description description) {
+            return true;
+        }
 
-		@Override
-		public String describe() {
-			return "all tests";
-		}
-		
-		@Override
-		public void apply(Object child) throws NoTestsRemainException {
-			// do nothing
-		}
+        @Override
+        public String describe() {
+            return "all tests";
+        }
 
-		@Override
-		public Filter intersect(Filter second) {
-			return second;
-		}
-	};
-	
-	/**
-	 * Returns a {@code Filter} that only runs the single method described by
-	 * {@code desiredDescription}
-	 */
-	public static Filter matchMethodDescription(final Description desiredDescription) {
-		return new Filter() {
-			@Override
-			public boolean shouldRun(Description description) {
-				if (description.isTest())
-					return desiredDescription.equals(description);
-				
-				// explicitly check if any children want to run
-				for (Description each : description.getChildren())
-					if (shouldRun(each))
-						return true;
-				return false;					
-			}
+        @Override
+        public void apply(Object child) throws NoTestsRemainException {
+            // do nothing
+        }
 
-			@Override
-			public String describe() {
-				return String.format("Method %s", desiredDescription.getDisplayName());
-			}
-		};
-	}
+        @Override
+        public Filter intersect(Filter second) {
+            return second;
+        }
+    };
+
+    /**
+     * Returns a {@code Filter} that only runs the single method described by
+     * {@code desiredDescription}
+     */
+    public static Filter matchMethodDescription(final Description desiredDescription) {
+        return new Filter() {
+            @Override
+            public boolean shouldRun(Description description) {
+                if (description.isTest()) {
+                    return desiredDescription.equals(description);
+                }
+
+                // explicitly check if any children want to run
+                for (Description each : description.getChildren()) {
+                    if (shouldRun(each)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public String describe() {
+                return String.format("Method %s", desiredDescription.getDisplayName());
+            }
+        };
+    }
 
 
-	/**
-	 * @param description the description of the test to be run
-	 * @return <code>true</code> if the test should be run
-	 */
-	public abstract boolean shouldRun(Description description);
+    /**
+     * @param description the description of the test to be run
+     * @return <code>true</code> if the test should be run
+     */
+    public abstract boolean shouldRun(Description description);
 
-	/**
-	 * Returns a textual description of this Filter
-	 * @return a textual description of this Filter
-	 */
-	public abstract String describe();
+    /**
+     * Returns a textual description of this Filter
+     *
+     * @return a textual description of this Filter
+     */
+    public abstract String describe();
 
-	/**
-	 * Invoke with a {@link org.junit.runner.Runner} to cause all tests it intends to run
-	 * to first be checked with the filter. Only those that pass the filter will be run.
-	 * @param child the runner to be filtered by the receiver
-	 * @throws NoTestsRemainException if the receiver removes all tests
-	 */
-	public void apply(Object child) throws NoTestsRemainException {
-		if (!(child instanceof Filterable))
-			return;
-		Filterable filterable= (Filterable) child;
-		filterable.filter(this);
-	}
+    /**
+     * Invoke with a {@link org.junit.runner.Runner} to cause all tests it intends to run
+     * to first be checked with the filter. Only those that pass the filter will be run.
+     *
+     * @param child the runner to be filtered by the receiver
+     * @throws NoTestsRemainException if the receiver removes all tests
+     */
+    public void apply(Object child) throws NoTestsRemainException {
+        if (!(child instanceof Filterable)) {
+            return;
+        }
+        Filterable filterable = (Filterable) child;
+        filterable.filter(this);
+    }
 
-	/**
-	 * Returns a new Filter that accepts the intersection of the tests accepted
-	 * by this Filter and {@code second}
-	 */
-	public Filter intersect(final Filter second) {
-		if (second == this || second == ALL) {
-			return this;
-		}
-		final Filter first= this;
-		return new Filter() {
-			@Override
-			public boolean shouldRun(Description description) {
-				return first.shouldRun(description)
-						&& second.shouldRun(description);
-			}
+    /**
+     * Returns a new Filter that accepts the intersection of the tests accepted
+     * by this Filter and {@code second}
+     */
+    public Filter intersect(final Filter second) {
+        if (second == this || second == ALL) {
+            return this;
+        }
+        final Filter first = this;
+        return new Filter() {
+            @Override
+            public boolean shouldRun(Description description) {
+                return first.shouldRun(description)
+                        && second.shouldRun(description);
+            }
 
-			@Override
-			public String describe() {
-				return first.describe() + " and " + second.describe();
-			}
-		};
-	}
+            @Override
+            public String describe() {
+                return first.describe() + " and " + second.describe();
+            }
+        };
+    }
 }
diff --git a/src/main/java/org/junit/runner/manipulation/Filterable.java b/src/main/java/org/junit/runner/manipulation/Filterable.java
index 782c0f7..d605027 100644
--- a/src/main/java/org/junit/runner/manipulation/Filterable.java
+++ b/src/main/java/org/junit/runner/manipulation/Filterable.java
@@ -3,14 +3,17 @@
 /**
  * Runners that allow filtering should implement this interface. Implement {@link #filter(Filter)}
  * to remove tests that don't pass the filter.
+ *
+ * @since 4.0
  */
 public interface Filterable {
 
-	/**
-	 * Remove tests that don't pass the parameter <code>filter</code>.
-	 * @param filter the {@link Filter} to apply
-	 * @throws NoTestsRemainException if all tests are filtered out
-	 */
-	void filter(Filter filter) throws NoTestsRemainException;
+    /**
+     * Remove tests that don't pass the parameter <code>filter</code>.
+     *
+     * @param filter the {@link Filter} to apply
+     * @throws NoTestsRemainException if all tests are filtered out
+     */
+    void filter(Filter filter) throws NoTestsRemainException;
 
 }
diff --git a/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java b/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java
index 03fb3bf..21935bd 100644
--- a/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java
+++ b/src/main/java/org/junit/runner/manipulation/NoTestsRemainException.java
@@ -2,7 +2,9 @@
 
 /**
  * Thrown when a filter removes all tests from a runner.
+ *
+ * @since 4.0
  */
 public class NoTestsRemainException extends Exception {
-	private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 1L;
 }
diff --git a/src/main/java/org/junit/runner/manipulation/Sortable.java b/src/main/java/org/junit/runner/manipulation/Sortable.java
index fec1d0c..9ac864c 100644
--- a/src/main/java/org/junit/runner/manipulation/Sortable.java
+++ b/src/main/java/org/junit/runner/manipulation/Sortable.java
@@ -5,13 +5,16 @@
  * failed first, you can reduce the average time to the first test failing. Test sorting should not be used to
  * cope with order dependencies between tests. Tests that are isolated from each other are less
  * expensive to maintain and can be run individually.
+ *
+ * @since 4.0
  */
 public interface Sortable {
 
-	/**
-	 * Sorts the tests using <code>sorter</code>
-	 * @param sorter the {@link Sorter} to use for sorting the tests
-	 */
-	public void sort(Sorter sorter);
+    /**
+     * Sorts the tests using <code>sorter</code>
+     *
+     * @param sorter the {@link Sorter} to use for sorting the tests
+     */
+    public void sort(Sorter sorter);
 
 }
diff --git a/src/main/java/org/junit/runner/manipulation/Sorter.java b/src/main/java/org/junit/runner/manipulation/Sorter.java
index 242df14..20192d0 100644
--- a/src/main/java/org/junit/runner/manipulation/Sorter.java
+++ b/src/main/java/org/junit/runner/manipulation/Sorter.java
@@ -7,40 +7,42 @@
 /**
  * A <code>Sorter</code> orders tests. In general you will not need
  * to use a <code>Sorter</code> directly. Instead, use {@link org.junit.runner.Request#sortWith(Comparator)}.
- * 
- * 
+ *
+ * @since 4.0
  */
 public class Sorter implements Comparator<Description> {
-	/**
-	 * NULL is a <code>Sorter</code> that leaves elements in an undefined order
-	 */
-	public static Sorter NULL= new Sorter(new Comparator<Description>() {
-		public int compare(Description o1, Description o2) {
-			return 0;
-		}});
-	private final Comparator<Description> fComparator;
+    /**
+     * NULL is a <code>Sorter</code> that leaves elements in an undefined order
+     */
+    public static final Sorter NULL = new Sorter(new Comparator<Description>() {
+        public int compare(Description o1, Description o2) {
+            return 0;
+        }
+    });
 
-	/**
-	 * Creates a <code>Sorter</code> that uses <code>comparator</code>
-	 * to sort tests
-	 * @param comparator the {@link Comparator} to use when sorting tests
-	 */
-	public Sorter(Comparator<Description> comparator) {
-		fComparator= comparator;
-	}
+    private final Comparator<Description> comparator;
 
-	/**
-	 * Sorts the test in <code>runner</code> using <code>comparator</code>
-	 * @param object
-	 */
-	public void apply(Object object) {
-		if (object instanceof Sortable) {
-			Sortable sortable = (Sortable) object;
-			sortable.sort(this);
-		}
-	}
+    /**
+     * Creates a <code>Sorter</code> that uses <code>comparator</code>
+     * to sort tests
+     *
+     * @param comparator the {@link Comparator} to use when sorting tests
+     */
+    public Sorter(Comparator<Description> comparator) {
+        this.comparator = comparator;
+    }
 
-	public int compare(Description o1, Description o2) {
-		return fComparator.compare(o1, o2);
-	}
+    /**
+     * Sorts the test in <code>runner</code> using <code>comparator</code>
+     */
+    public void apply(Object object) {
+        if (object instanceof Sortable) {
+            Sortable sortable = (Sortable) object;
+            sortable.sort(this);
+        }
+    }
+
+    public int compare(Description o1, Description o2) {
+        return comparator.compare(o1, o2);
+    }
 }
diff --git a/src/main/java/org/junit/runner/notification/Failure.java b/src/main/java/org/junit/runner/notification/Failure.java
index 501caa5..c03b4c1 100644
--- a/src/main/java/org/junit/runner/notification/Failure.java
+++ b/src/main/java/org/junit/runner/notification/Failure.java
@@ -12,68 +12,76 @@
  * will be of a single test. However, if problems are encountered while constructing the
  * test (for example, if a {@link org.junit.BeforeClass} method is not static), it may describe
  * something other than a single test.
+ *
+ * @since 4.0
  */
 public class Failure implements Serializable {
-	private static final long serialVersionUID = 1L;
-	private final Description fDescription;
-	private final Throwable fThrownException;
+    private static final long serialVersionUID = 1L;
 
-	/**
-	 * Constructs a <code>Failure</code> with the given description and exception.
-	 * @param description a {@link org.junit.runner.Description} of the test that failed
-	 * @param thrownException the exception that was thrown while running the test
-	 */
-	public Failure(Description description, Throwable thrownException) {
-		fThrownException = thrownException;
-		fDescription= description;
-	}
+    /*
+     * We have to use the f prefix until the next major release to ensure
+     * serialization compatibility. 
+     * See https://github.com/junit-team/junit/issues/976
+     */
+    private final Description fDescription;
+    private final Throwable fThrownException;
 
-	/**
-	 * @return a user-understandable label for the test
-	 */
-	public String getTestHeader() {
-		return fDescription.getDisplayName();
-	}
+    /**
+     * Constructs a <code>Failure</code> with the given description and exception.
+     *
+     * @param description a {@link org.junit.runner.Description} of the test that failed
+     * @param thrownException the exception that was thrown while running the test
+     */
+    public Failure(Description description, Throwable thrownException) {
+        this.fThrownException = thrownException;
+        this.fDescription = description;
+    }
 
-	/**
-	 * @return the raw description of the context of the failure.
-	 */
-	public Description getDescription() {
-		return fDescription;
-	}
+    /**
+     * @return a user-understandable label for the test
+     */
+    public String getTestHeader() {
+        return fDescription.getDisplayName();
+    }
 
-	/**
-	 * @return the exception thrown
-	 */
+    /**
+     * @return the raw description of the context of the failure.
+     */
+    public Description getDescription() {
+        return fDescription;
+    }
 
-	public Throwable getException() {
-	    return fThrownException;
-	}
+    /**
+     * @return the exception thrown
+     */
 
-	@Override
-	public String toString() {
-	    StringBuffer buffer= new StringBuffer();
-	    buffer.append(getTestHeader() + ": "+fThrownException.getMessage());
-	    return buffer.toString();
-	}
+    public Throwable getException() {
+        return fThrownException;
+    }
 
-	/**
-	 * Convenience method
-	 * @return the printed form of the exception
-	 */
-	public String getTrace() {
-		StringWriter stringWriter= new StringWriter();
-		PrintWriter writer= new PrintWriter(stringWriter);
-		getException().printStackTrace(writer);
-		StringBuffer buffer= stringWriter.getBuffer();
-		return buffer.toString();
-	}
+    @Override
+    public String toString() {
+        return getTestHeader() + ": " + fThrownException.getMessage();
+    }
 
-	/**
-	 * Convenience method
-	 * @return the message of the thrown exception
-	 */
-	public String getMessage() {
-		return getException().getMessage();
-	}
+    /**
+     * Convenience method
+     *
+     * @return the printed form of the exception
+     */
+    public String getTrace() {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter writer = new PrintWriter(stringWriter);
+        getException().printStackTrace(writer);
+        return stringWriter.toString();
+    }
+
+    /**
+     * Convenience method
+     *
+     * @return the message of the thrown exception
+     */
+    public String getMessage() {
+        return getException().getMessage();
+    }
 }
diff --git a/src/main/java/org/junit/runner/notification/RunListener.java b/src/main/java/org/junit/runner/notification/RunListener.java
index ffe8134..db9d8c1 100644
--- a/src/main/java/org/junit/runner/notification/RunListener.java
+++ b/src/main/java/org/junit/runner/notification/RunListener.java
@@ -1,15 +1,21 @@
 package org.junit.runner.notification;
 
-import org.junit.internal.AssumptionViolatedException;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
 import org.junit.runner.Description;
 import org.junit.runner.Result;
 
 /**
- * <p>If you need to respond to the events during a test run, extend <code>RunListener</code>
- * and override the appropriate methods. If a listener throws an exception while processing a 
- * test event, it will be removed for the remainder of the test run.</p>
- * 
- * <p>For example, suppose you have a <code>Cowbell</code>
+ * Register an instance of this class with {@link RunNotifier} to be notified
+ * of events that occur during a test run. All of the methods in this class
+ * are abstract and have no implementation; override one or more methods to
+ * receive events.
+ * <p>
+ * For example, suppose you have a <code>Cowbell</code>
  * class that you want to make a noise whenever a test fails. You could write:
  * <pre>
  * public class RingingListener extends RunListener {
@@ -18,9 +24,8 @@
  *    }
  * }
  * </pre>
- * </p>
- * 
- * <p>To invoke your listener, you need to run your tests through <code>JUnitCore</code>.
+ * <p>
+ * To invoke your listener, you need to run your tests through <code>JUnitCore</code>.
  * <pre>
  * public void main(String... args) {
  *    JUnitCore core= new JUnitCore();
@@ -28,66 +33,108 @@
  *    core.run(MyTestClass.class);
  * }
  * </pre>
- * </p>
+ * <p>
+ * If a listener throws an exception for a test event, the other listeners will
+ * have their {@link RunListener#testFailure(Failure)} called with a {@code Description}
+ * of {@link Description#TEST_MECHANISM} to indicate the failure.
+ * <p>
+ * By default, JUnit will synchronize calls to your listener. If your listener
+ * is thread-safe and you want to allow JUnit to call your listener from
+ * multiple threads when tests are run in parallel, you can annotate your
+ * test class with {@link RunListener.ThreadSafe}.
+ * <p>
+ * Listener methods will be called from the same thread as is running
+ * the test, unless otherwise indicated by the method Javadoc
+ *
  * @see org.junit.runner.JUnitCore
+ * @since 4.0
  */
 public class RunListener {
 
-	/**
-	 * Called before any tests have been run.
-	 * @param description describes the tests to be run
-	 */
-	public void testRunStarted(Description description) throws Exception {
-	}
-	
-	/**
-	 * Called when all tests have finished
-	 * @param result the summary of the test run, including all the tests that failed
-	 */
-	public void testRunFinished(Result result) throws Exception {
-	}
-	
-	/**
-	 * Called when an atomic test is about to be started.
-	 * @param description the description of the test that is about to be run 
-	 * (generally a class and method name)
-	 */
-	public void testStarted(Description description) throws Exception {
-	}
+    /**
+     * Called before any tests have been run. This may be called on an
+     * arbitrary thread.
+     *
+     * @param description describes the tests to be run
+     */
+    public void testRunStarted(Description description) throws Exception {
+    }
 
-	/**
-	 * Called when an atomic test has finished, whether the test succeeds or fails.
-	 * @param description the description of the test that just ran
-	 */
-	public void testFinished(Description description) throws Exception {
-	}
+    /**
+     * Called when all tests have finished. This may be called on an
+     * arbitrary thread.
+     *
+     * @param result the summary of the test run, including all the tests that failed
+     */
+    public void testRunFinished(Result result) throws Exception {
+    }
 
-	/** 
-	 * Called when an atomic test fails.
-	 * @param failure describes the test that failed and the exception that was thrown
-	 */
-	public void testFailure(Failure failure) throws Exception {
-	}
+    /**
+     * Called when an atomic test is about to be started.
+     *
+     * @param description the description of the test that is about to be run
+     * (generally a class and method name)
+     */
+    public void testStarted(Description description) throws Exception {
+    }
 
-	/**
-	 * Called when an atomic test flags that it assumes a condition that is
-	 * false
-	 * 
-	 * @param failure
-	 *            describes the test that failed and the
-	 *            {@link AssumptionViolatedException} that was thrown
-	 */
-	public void testAssumptionFailure(Failure failure) {
-	}
+    /**
+     * Called when an atomic test has finished, whether the test succeeds or fails.
+     *
+     * @param description the description of the test that just ran
+     */
+    public void testFinished(Description description) throws Exception {
+    }
 
-	/**
-	 * Called when a test will not be run, generally because a test method is annotated 
-	 * with {@link org.junit.Ignore}.
-	 * 
-	 * @param description describes the test that will not be run
-	 */
-	public void testIgnored(Description description) throws Exception {
-	}
+    /**
+     * Called when an atomic test fails, or when a listener throws an exception.
+     *
+     * <p>In the case of a failure of an atomic test, this method will be called
+     * with the same {@code Description} passed to
+     * {@link #testStarted(Description)}, from the same thread that called
+     * {@link #testStarted(Description)}.
+     *
+     * <p>In the case of a listener throwing an exception, this will be called with
+     * a {@code Description} of {@link Description#TEST_MECHANISM}, and may be called
+     * on an arbitrary thread.
+     *
+     * @param failure describes the test that failed and the exception that was thrown
+     */
+    public void testFailure(Failure failure) throws Exception {
+    }
+
+    /**
+     * Called when an atomic test flags that it assumes a condition that is
+     * false
+     *
+     * @param failure describes the test that failed and the
+     * {@link org.junit.AssumptionViolatedException} that was thrown
+     */
+    public void testAssumptionFailure(Failure failure) {
+    }
+
+    /**
+     * Called when a test will not be run, generally because a test method is annotated
+     * with {@link org.junit.Ignore}.
+     *
+     * @param description describes the test that will not be run
+     */
+    public void testIgnored(Description description) throws Exception {
+    }
+
+
+    /**
+     * Indicates a {@code RunListener} that can have its methods called
+     * concurrently. This implies that the class is thread-safe (i.e. no set of
+     * listener calls can put the listener into an invalid state, even if those
+     * listener calls are being made by multiple threads without
+     * synchronization).
+     *
+     * @since 4.12
+     */
+    @Documented
+    @Target(ElementType.TYPE)
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface ThreadSafe {
+    }
 }
-
-
diff --git a/src/main/java/org/junit/runner/notification/RunNotifier.java b/src/main/java/org/junit/runner/notification/RunNotifier.java
index d0f6c85..6875f76 100644
--- a/src/main/java/org/junit/runner/notification/RunNotifier.java
+++ b/src/main/java/org/junit/runner/notification/RunNotifier.java
@@ -1,166 +1,214 @@
 package org.junit.runner.notification;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
+import static java.util.Arrays.asList;
 
-import org.junit.internal.AssumptionViolatedException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
 import org.junit.runner.Description;
 import org.junit.runner.Result;
 
 /**
  * If you write custom runners, you may need to notify JUnit of your progress running tests.
  * Do this by invoking the <code>RunNotifier</code> passed to your implementation of
- * {@link org.junit.runner.Runner#run(RunNotifier)}. Future evolution of this class is likely to 
+ * {@link org.junit.runner.Runner#run(RunNotifier)}. Future evolution of this class is likely to
  * move {@link #fireTestRunStarted(Description)} and {@link #fireTestRunFinished(Result)}
  * to a separate class since they should only be called once per run.
+ *
+ * @since 4.0
  */
 public class RunNotifier {
-	private final List<RunListener> fListeners= 
-		Collections.synchronizedList(new ArrayList<RunListener>());
-	private boolean fPleaseStop= false;
-	
-	/** Internal use only
-	 */
-	public void addListener(RunListener listener) {
-		fListeners.add(listener);
-	}
+    private final List<RunListener> listeners = new CopyOnWriteArrayList<RunListener>();
+    private volatile boolean pleaseStop = false;
 
-	/** Internal use only
-	 */
-	public void removeListener(RunListener listener) {
-		fListeners.remove(listener);
+    /**
+     * Internal use only
+     */
+    public void addListener(RunListener listener) {
+        if (listener == null) {
+            throw new NullPointerException("Cannot add a null listener");
+        }
+        listeners.add(wrapIfNotThreadSafe(listener));
     }
 
-	private abstract class SafeNotifier {
-		void run() {
-			synchronized (fListeners) {
-				for (Iterator<RunListener> all= fListeners.iterator(); all.hasNext();)
-					try {
-						notifyListener(all.next());
-					} catch (Exception e) {
-						all.remove(); // Remove the offending listener first to avoid an infinite loop
-						fireTestFailure(new Failure(Description.TEST_MECHANISM, e));
-					}
-			}
-		}
-		
-		abstract protected void notifyListener(RunListener each) throws Exception;
-	}
-	
-	/**
-	 * Do not invoke. 
-	 */
-	public void fireTestRunStarted(final Description description) {
-		new SafeNotifier() {
-			@Override
-			protected void notifyListener(RunListener each) throws Exception {
-				each.testRunStarted(description);
-			};
-		}.run();
-	}
-	
-	/**
-	 * Do not invoke.
-	 */
-	public void fireTestRunFinished(final Result result) {
-		new SafeNotifier() {
-			@Override
-			protected void notifyListener(RunListener each) throws Exception {
-				each.testRunFinished(result);
-			};
-		}.run();
-	}
-	
-	/**
-	 * Invoke to tell listeners that an atomic test is about to start.
-	 * @param description the description of the atomic test (generally a class and method name)
-	 * @throws StoppedByUserException thrown if a user has requested that the test run stop
-	 */
-	public void fireTestStarted(final Description description) throws StoppedByUserException {
-		if (fPleaseStop)
-			throw new StoppedByUserException();
-		new SafeNotifier() {
-			@Override
-			protected void notifyListener(RunListener each) throws Exception {
-				each.testStarted(description);
-			};
-		}.run();
-	}
+    /**
+     * Internal use only
+     */
+    public void removeListener(RunListener listener) {
+        if (listener == null) {
+            throw new NullPointerException("Cannot remove a null listener");
+        }
+        listeners.remove(wrapIfNotThreadSafe(listener));
+    }
 
-	/**
-	 * Invoke to tell listeners that an atomic test failed.
-	 * @param failure the description of the test that failed and the exception thrown
-	 */
-	public void fireTestFailure(final Failure failure) {
-		new SafeNotifier() {
-			@Override
-			protected void notifyListener(RunListener each) throws Exception {
-				each.testFailure(failure);
-			};
-		}.run();
-	}
+    /**
+     * Wraps the given listener with {@link SynchronizedRunListener} if
+     * it is not annotated with {@link RunListener.ThreadSafe}.
+     */
+    RunListener wrapIfNotThreadSafe(RunListener listener) {
+        return listener.getClass().isAnnotationPresent(RunListener.ThreadSafe.class) ?
+                listener : new SynchronizedRunListener(listener, this);
+    }
 
-	/**
-	 * Invoke to tell listeners that an atomic test flagged that it assumed
-	 * something false.
-	 * 
-	 * @param failure
-	 *            the description of the test that failed and the
-	 *            {@link AssumptionViolatedException} thrown
-	 */
-	public void fireTestAssumptionFailed(final Failure failure) {
-		new SafeNotifier() {
-			@Override
-			protected void notifyListener(RunListener each) throws Exception {
-				each.testAssumptionFailure(failure);
-			};
-		}.run();
-	}
 
-	/**
-	 * Invoke to tell listeners that an atomic test was ignored.
-	 * @param description the description of the ignored test
-	 */
-	public void fireTestIgnored(final Description description) {
-		new SafeNotifier() {
-			@Override
-			protected void notifyListener(RunListener each) throws Exception {
-				each.testIgnored(description);
-			}
-		}.run();
-	}
+    private abstract class SafeNotifier {
+        private final List<RunListener> currentListeners;
 
-	/**
-	 * Invoke to tell listeners that an atomic test finished. Always invoke 
-	 * {@link #fireTestFinished(Description)} if you invoke {@link #fireTestStarted(Description)} 
-	 * as listeners are likely to expect them to come in pairs.
-	 * @param description the description of the test that finished
-	 */
-	public void fireTestFinished(final Description description) {
-		new SafeNotifier() {
-			@Override
-			protected void notifyListener(RunListener each) throws Exception {
-				each.testFinished(description);
-			};
-		}.run();
-	}
-	
-	/**
-	 * Ask that the tests run stop before starting the next test. Phrased politely because
-	 * the test currently running will not be interrupted. It seems a little odd to put this
-	 * functionality here, but the <code>RunNotifier</code> is the only object guaranteed 
-	 * to be shared amongst the many runners involved.
-	 */
-	public void pleaseStop() {
-		fPleaseStop= true;
-	}
+        SafeNotifier() {
+            this(listeners);
+        }
 
-	/**
-	 * Internal use only. The Result's listener must be first.
-	 */
-	public void addFirstListener(RunListener listener) {
-		fListeners.add(0, listener);
-	}
-}
\ No newline at end of file
+        SafeNotifier(List<RunListener> currentListeners) {
+            this.currentListeners = currentListeners;
+        }
+
+        void run() {
+            int capacity = currentListeners.size();
+            ArrayList<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
+            ArrayList<Failure> failures = new ArrayList<Failure>(capacity);
+            for (RunListener listener : currentListeners) {
+                try {
+                    notifyListener(listener);
+                    safeListeners.add(listener);
+                } catch (Exception e) {
+                    failures.add(new Failure(Description.TEST_MECHANISM, e));
+                }
+            }
+            fireTestFailures(safeListeners, failures);
+        }
+
+        abstract protected void notifyListener(RunListener each) throws Exception;
+    }
+
+    /**
+     * Do not invoke.
+     */
+    public void fireTestRunStarted(final Description description) {
+        new SafeNotifier() {
+            @Override
+            protected void notifyListener(RunListener each) throws Exception {
+                each.testRunStarted(description);
+            }
+        }.run();
+    }
+
+    /**
+     * Do not invoke.
+     */
+    public void fireTestRunFinished(final Result result) {
+        new SafeNotifier() {
+            @Override
+            protected void notifyListener(RunListener each) throws Exception {
+                each.testRunFinished(result);
+            }
+        }.run();
+    }
+
+    /**
+     * Invoke to tell listeners that an atomic test is about to start.
+     *
+     * @param description the description of the atomic test (generally a class and method name)
+     * @throws StoppedByUserException thrown if a user has requested that the test run stop
+     */
+    public void fireTestStarted(final Description description) throws StoppedByUserException {
+        if (pleaseStop) {
+            throw new StoppedByUserException();
+        }
+        new SafeNotifier() {
+            @Override
+            protected void notifyListener(RunListener each) throws Exception {
+                each.testStarted(description);
+            }
+        }.run();
+    }
+
+    /**
+     * Invoke to tell listeners that an atomic test failed.
+     *
+     * @param failure the description of the test that failed and the exception thrown
+     */
+    public void fireTestFailure(Failure failure) {
+        fireTestFailures(listeners, asList(failure));
+    }
+
+    private void fireTestFailures(List<RunListener> listeners,
+            final List<Failure> failures) {
+        if (!failures.isEmpty()) {
+            new SafeNotifier(listeners) {
+                @Override
+                protected void notifyListener(RunListener listener) throws Exception {
+                    for (Failure each : failures) {
+                        listener.testFailure(each);
+                    }
+                }
+            }.run();
+        }
+    }
+
+    /**
+     * Invoke to tell listeners that an atomic test flagged that it assumed
+     * something false.
+     *
+     * @param failure the description of the test that failed and the
+     * {@link org.junit.AssumptionViolatedException} thrown
+     */
+    public void fireTestAssumptionFailed(final Failure failure) {
+        new SafeNotifier() {
+            @Override
+            protected void notifyListener(RunListener each) throws Exception {
+                each.testAssumptionFailure(failure);
+            }
+        }.run();
+    }
+
+    /**
+     * Invoke to tell listeners that an atomic test was ignored.
+     *
+     * @param description the description of the ignored test
+     */
+    public void fireTestIgnored(final Description description) {
+        new SafeNotifier() {
+            @Override
+            protected void notifyListener(RunListener each) throws Exception {
+                each.testIgnored(description);
+            }
+        }.run();
+    }
+
+    /**
+     * Invoke to tell listeners that an atomic test finished. Always invoke
+     * this method if you invoke {@link #fireTestStarted(Description)}
+     * as listeners are likely to expect them to come in pairs.
+     *
+     * @param description the description of the test that finished
+     */
+    public void fireTestFinished(final Description description) {
+        new SafeNotifier() {
+            @Override
+            protected void notifyListener(RunListener each) throws Exception {
+                each.testFinished(description);
+            }
+        }.run();
+    }
+
+    /**
+     * Ask that the tests run stop before starting the next test. Phrased politely because
+     * the test currently running will not be interrupted. It seems a little odd to put this
+     * functionality here, but the <code>RunNotifier</code> is the only object guaranteed
+     * to be shared amongst the many runners involved.
+     */
+    public void pleaseStop() {
+        pleaseStop = true;
+    }
+
+    /**
+     * Internal use only. The Result's listener must be first.
+     */
+    public void addFirstListener(RunListener listener) {
+        if (listener == null) {
+            throw new NullPointerException("Cannot add a null listener");
+        }
+        listeners.add(0, wrapIfNotThreadSafe(listener));
+    }
+}
diff --git a/src/main/java/org/junit/runner/notification/StoppedByUserException.java b/src/main/java/org/junit/runner/notification/StoppedByUserException.java
index 89be3ba..f5490f7 100644
--- a/src/main/java/org/junit/runner/notification/StoppedByUserException.java
+++ b/src/main/java/org/junit/runner/notification/StoppedByUserException.java
@@ -1,11 +1,12 @@
 package org.junit.runner.notification;
 
 /**
- * Thrown when a user has requested that the test run stop. Writers of 
+ * Thrown when a user has requested that the test run stop. Writers of
  * test running GUIs should be prepared to catch a <code>StoppedByUserException</code>.
- * 
+ *
  * @see org.junit.runner.notification.RunNotifier
+ * @since 4.0
  */
 public class StoppedByUserException extends RuntimeException {
-	private static final long serialVersionUID= 1L;
+    private static final long serialVersionUID = 1L;
 }
diff --git a/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java b/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
new file mode 100644
index 0000000..c53c1ee
--- /dev/null
+++ b/src/main/java/org/junit/runner/notification/SynchronizedRunListener.java
@@ -0,0 +1,103 @@
+package org.junit.runner.notification;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+
+/**
+ * Thread-safe decorator for {@link RunListener} implementations that synchronizes
+ * calls to the delegate.
+ *
+ * <p>This class synchronizes all listener calls on a RunNotifier instance. This is done because
+ * prior to JUnit 4.12, all listeners were called in a synchronized block in RunNotifier,
+ * so no two listeners were ever called concurrently. If we instead made the methods here
+ * sychronized, clients that added multiple listeners that called common code might see
+ * issues due to the reduced synchronization.
+ *
+ * @author Tibor Digana (tibor17)
+ * @author Kevin Cooney (kcooney)
+ * @since 4.12
+ *
+ * @see RunNotifier
+ */
+@RunListener.ThreadSafe
+final class SynchronizedRunListener extends RunListener {
+    private final RunListener listener;
+    private final Object monitor;
+
+    SynchronizedRunListener(RunListener listener, Object monitor) {
+        this.listener = listener;
+        this.monitor = monitor;
+    }
+
+    @Override
+    public void testRunStarted(Description description) throws Exception {
+        synchronized (monitor) {
+            listener.testRunStarted(description);
+        }
+    }
+
+    @Override
+    public void testRunFinished(Result result) throws Exception {
+        synchronized (monitor) {
+            listener.testRunFinished(result);
+        }
+    }
+
+    @Override
+    public void testStarted(Description description) throws Exception {
+        synchronized (monitor) {
+            listener.testStarted(description);
+        }
+    }
+
+    @Override
+    public void testFinished(Description description) throws Exception {
+        synchronized (monitor) {
+            listener.testFinished(description);
+        }
+    }
+
+    @Override
+    public void testFailure(Failure failure) throws Exception {
+        synchronized (monitor) {
+            listener.testFailure(failure);
+        }
+    }
+
+    @Override
+    public void testAssumptionFailure(Failure failure) {
+        synchronized (monitor) {
+            listener.testAssumptionFailure(failure);
+        }
+    }
+
+    @Override
+    public void testIgnored(Description description) throws Exception {
+        synchronized (monitor) {
+            listener.testIgnored(description);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return listener.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof SynchronizedRunListener)) {
+            return false;
+        }
+        SynchronizedRunListener that = (SynchronizedRunListener) other;
+        
+        return listener.equals(that.listener);
+    }
+
+    @Override
+    public String toString() {
+        return listener.toString() + " (with synchronization wrapper)";
+    }
+}
diff --git a/src/main/java/org/junit/runners/AllTests.java b/src/main/java/org/junit/runners/AllTests.java
index 50c02db..416c99d 100644
--- a/src/main/java/org/junit/runners/AllTests.java
+++ b/src/main/java/org/junit/runners/AllTests.java
@@ -2,7 +2,8 @@
 
 import org.junit.internal.runners.SuiteMethod;
 
-/** Runner for use with JUnit 3.8.x-style AllTests classes
+/**
+ * Runner for use with JUnit 3.8.x-style AllTests classes
  * (those that only implement a static <code>suite()</code>
  * method). For example:
  * <pre>
@@ -13,12 +14,14 @@
  *    }
  * }
  * </pre>
+ *
+ * @since 4.0
  */
 public class AllTests extends SuiteMethod {
-	/**
-	 * Only called reflectively. Do not use programmatically.
-	 */
-	public AllTests(Class<?> klass) throws Throwable {
-		super(klass);
-	}
+    /**
+     * Only called reflectively. Do not use programmatically.
+     */
+    public AllTests(Class<?> klass) throws Throwable {
+        super(klass);
+    }
 }
diff --git a/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java b/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java
index 397da19..4d06199 100644
--- a/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java
+++ b/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java
@@ -1,8 +1,11 @@
 package org.junit.runners;
 
-import static org.junit.internal.runners.rules.RuleFieldValidator.RULE_VALIDATOR;
+import static org.junit.internal.runners.rules.RuleMemberValidator.RULE_METHOD_VALIDATOR;
+import static org.junit.internal.runners.rules.RuleMemberValidator.RULE_VALIDATOR;
 
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 
 import org.junit.After;
 import org.junit.Before;
@@ -17,6 +20,7 @@
 import org.junit.internal.runners.statements.InvokeMethod;
 import org.junit.internal.runners.statements.RunAfters;
 import org.junit.internal.runners.statements.RunBefores;
+import org.junit.rules.MethodRule;
 import org.junit.rules.RunRules;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -31,377 +35,403 @@
  * annotations in the org.junit package. Many users will never notice this
  * class: it is now the default test class runner, but it should have exactly
  * the same behavior as the old test class runner ({@code JUnit4ClassRunner}).
- * 
+ * <p>
  * BlockJUnit4ClassRunner has advantages for writers of custom JUnit runners
  * that are slight changes to the default behavior, however:
- * 
+ *
  * <ul>
  * <li>It has a much simpler implementation based on {@link Statement}s,
  * allowing new operations to be inserted into the appropriate point in the
  * execution flow.
- * 
+ *
  * <li>It is published, and extension and reuse are encouraged, whereas {@code
  * JUnit4ClassRunner} was in an internal package, and is now deprecated.
  * </ul>
+ * <p>
+ * In turn, in 2009 we introduced {@link Rule}s.  In many cases where extending
+ * BlockJUnit4ClassRunner was necessary to add new behavior, {@link Rule}s can
+ * be used, which makes the extension more reusable and composable.
+ *
+ * @since 4.5
  */
 public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
-	/**
-	 * Creates a BlockJUnit4ClassRunner to run {@code klass}
-	 * 
-	 * @throws InitializationError
-	 *             if the test class is malformed.
-	 */
-	public BlockJUnit4ClassRunner(Class<?> klass) throws InitializationError {
-		super(klass);
-	}
+    private final ConcurrentHashMap<FrameworkMethod, Description> methodDescriptions = new ConcurrentHashMap<FrameworkMethod, Description>();
+    /**
+     * Creates a BlockJUnit4ClassRunner to run {@code klass}
+     *
+     * @throws InitializationError if the test class is malformed.
+     */
+    public BlockJUnit4ClassRunner(Class<?> klass) throws InitializationError {
+        super(klass);
+    }
 
-	//
-	// Implementation of ParentRunner
-	// 
+    //
+    // Implementation of ParentRunner
+    //
 
-	@Override
-	protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
-		Description description= describeChild(method);
-		if (method.getAnnotation(Ignore.class) != null) {
-			notifier.fireTestIgnored(description);
-		} else {
-			runLeaf(methodBlock(method), description, notifier);
-		}
-	}
+    @Override
+    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
+        Description description = describeChild(method);
+        if (isIgnored(method)) {
+            notifier.fireTestIgnored(description);
+        } else {
+            runLeaf(methodBlock(method), description, notifier);
+        }
+    }
+    
+    /**
+     * Evaluates whether {@link FrameworkMethod}s are ignored based on the
+     * {@link Ignore} annotation.
+     */
+    @Override
+    protected boolean isIgnored(FrameworkMethod child) {
+        return child.getAnnotation(Ignore.class) != null;
+    }
 
-	@Override
-	protected Description describeChild(FrameworkMethod method) {
-		return Description.createTestDescription(getTestClass().getJavaClass(),
-				testName(method), method.getAnnotations());
-	}
+    @Override
+    protected Description describeChild(FrameworkMethod method) {
+        Description description = methodDescriptions.get(method);
 
-	@Override
-	protected List<FrameworkMethod> getChildren() {
-		return computeTestMethods();
-	}
+        if (description == null) {
+            description = Description.createTestDescription(getTestClass().getJavaClass(),
+                    testName(method), method.getAnnotations());
+            methodDescriptions.putIfAbsent(method, description);
+        }
 
-	//
-	// Override in subclasses
-	//
+        return description;
+    }
 
-	/**
-	 * Returns the methods that run tests. Default implementation returns all
-	 * methods annotated with {@code @Test} on this class and superclasses that
-	 * are not overridden.
-	 */
-	protected List<FrameworkMethod> computeTestMethods() {
-		return getTestClass().getAnnotatedMethods(Test.class);
-	}
+    @Override
+    protected List<FrameworkMethod> getChildren() {
+        return computeTestMethods();
+    }
 
-	@Override
-	protected void collectInitializationErrors(List<Throwable> errors) {
-		super.collectInitializationErrors(errors);
+    //
+    // Override in subclasses
+    //
 
-		validateNoNonStaticInnerClass(errors);
-		validateConstructor(errors);
-		validateInstanceMethods(errors);
-		validateFields(errors);
-	}
+    /**
+     * Returns the methods that run tests. Default implementation returns all
+     * methods annotated with {@code @Test} on this class and superclasses that
+     * are not overridden.
+     */
+    protected List<FrameworkMethod> computeTestMethods() {
+        return getTestClass().getAnnotatedMethods(Test.class);
+    }
 
-	protected void validateNoNonStaticInnerClass(List<Throwable> errors) {
-		if (getTestClass().isANonStaticInnerClass()) {
-			String gripe= "The inner class " + getTestClass().getName()
-					+ " is not static.";
-			errors.add(new Exception(gripe));
-		}
-	}
+    @Override
+    protected void collectInitializationErrors(List<Throwable> errors) {
+        super.collectInitializationErrors(errors);
 
-	/**
-	 * Adds to {@code errors} if the test class has more than one constructor,
-	 * or if the constructor takes parameters. Override if a subclass requires
-	 * different validation rules.
-	 */
-	protected void validateConstructor(List<Throwable> errors) {
-		validateOnlyOneConstructor(errors);
-		validateZeroArgConstructor(errors);
-	}
+        validateNoNonStaticInnerClass(errors);
+        validateConstructor(errors);
+        validateInstanceMethods(errors);
+        validateFields(errors);
+        validateMethods(errors);
+    }
 
-	/**
-	 * Adds to {@code errors} if the test class has more than one constructor
-	 * (do not override)
-	 */
-	protected void validateOnlyOneConstructor(List<Throwable> errors) {
-		if (!hasOneConstructor()) {
-			String gripe= "Test class should have exactly one public constructor";
-			errors.add(new Exception(gripe));
-		}
-	}
+    protected void validateNoNonStaticInnerClass(List<Throwable> errors) {
+        if (getTestClass().isANonStaticInnerClass()) {
+            String gripe = "The inner class " + getTestClass().getName()
+                    + " is not static.";
+            errors.add(new Exception(gripe));
+        }
+    }
 
-	/**
-	 * Adds to {@code errors} if the test class's single constructor takes
-	 * parameters (do not override)
-	 */
-	protected void validateZeroArgConstructor(List<Throwable> errors) {
-		if (!getTestClass().isANonStaticInnerClass()
-				&& hasOneConstructor()
-				&& (getTestClass().getOnlyConstructor().getParameterTypes().length != 0)) {
-			String gripe= "Test class should have exactly one public zero-argument constructor";
-			errors.add(new Exception(gripe));
-		}
-	}
+    /**
+     * Adds to {@code errors} if the test class has more than one constructor,
+     * or if the constructor takes parameters. Override if a subclass requires
+     * different validation rules.
+     */
+    protected void validateConstructor(List<Throwable> errors) {
+        validateOnlyOneConstructor(errors);
+        validateZeroArgConstructor(errors);
+    }
 
-	private boolean hasOneConstructor() {
-		return getTestClass().getJavaClass().getConstructors().length == 1;
-	}
+    /**
+     * Adds to {@code errors} if the test class has more than one constructor
+     * (do not override)
+     */
+    protected void validateOnlyOneConstructor(List<Throwable> errors) {
+        if (!hasOneConstructor()) {
+            String gripe = "Test class should have exactly one public constructor";
+            errors.add(new Exception(gripe));
+        }
+    }
 
-	/**
-	 * Adds to {@code errors} for each method annotated with {@code @Test},
-	 * {@code @Before}, or {@code @After} that is not a public, void instance
-	 * method with no arguments.
-	 * 
-	 * @deprecated unused API, will go away in future version
-	 */
-	@Deprecated
-	protected void validateInstanceMethods(List<Throwable> errors) {
-		validatePublicVoidNoArgMethods(After.class, false, errors);
-		validatePublicVoidNoArgMethods(Before.class, false, errors);
-		validateTestMethods(errors);
+    /**
+     * Adds to {@code errors} if the test class's single constructor takes
+     * parameters (do not override)
+     */
+    protected void validateZeroArgConstructor(List<Throwable> errors) {
+        if (!getTestClass().isANonStaticInnerClass()
+                && hasOneConstructor()
+                && (getTestClass().getOnlyConstructor().getParameterTypes().length != 0)) {
+            String gripe = "Test class should have exactly one public zero-argument constructor";
+            errors.add(new Exception(gripe));
+        }
+    }
 
-		if (computeTestMethods().size() == 0)
-			errors.add(new Exception("No runnable methods"));
-	}
+    private boolean hasOneConstructor() {
+        return getTestClass().getJavaClass().getConstructors().length == 1;
+    }
 
-	protected void validateFields(List<Throwable> errors) {
-		RULE_VALIDATOR.validate(getTestClass(), errors);
-	}
+    /**
+     * Adds to {@code errors} for each method annotated with {@code @Test},
+     * {@code @Before}, or {@code @After} that is not a public, void instance
+     * method with no arguments.
+     */
+    @Deprecated
+    protected void validateInstanceMethods(List<Throwable> errors) {
+        validatePublicVoidNoArgMethods(After.class, false, errors);
+        validatePublicVoidNoArgMethods(Before.class, false, errors);
+        validateTestMethods(errors);
 
-	/**
-	 * Adds to {@code errors} for each method annotated with {@code @Test}that
-	 * is not a public, void instance method with no arguments.
-	 */
-	protected void validateTestMethods(List<Throwable> errors) {
-		validatePublicVoidNoArgMethods(Test.class, false, errors);
-	}
+        if (computeTestMethods().size() == 0) {
+            errors.add(new Exception("No runnable methods"));
+        }
+    }
 
-	/**
-	 * Returns a new fixture for running a test. Default implementation executes
-	 * the test class's no-argument constructor (validation should have ensured
-	 * one exists).
-	 */
-	protected Object createTest() throws Exception {
-		return getTestClass().getOnlyConstructor().newInstance();
-	}
+    protected void validateFields(List<Throwable> errors) {
+        RULE_VALIDATOR.validate(getTestClass(), errors);
+    }
 
-	/**
-	 * Returns the name that describes {@code method} for {@link Description}s.
-	 * Default implementation is the method's name
-	 */
-	protected String testName(FrameworkMethod method) {
-		return method.getName();
-	}
+    private void validateMethods(List<Throwable> errors) {
+        RULE_METHOD_VALIDATOR.validate(getTestClass(), errors);
+    }
 
-	/**
-	 * Returns a Statement that, when executed, either returns normally if
-	 * {@code method} passes, or throws an exception if {@code method} fails.
-	 * 
-	 * Here is an outline of the default implementation:
-	 * 
-	 * <ul>
-	 * <li>Invoke {@code method} on the result of {@code createTest()}, and
-	 * throw any exceptions thrown by either operation.
-	 * <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
-	 * expecting} attribute, return normally only if the previous step threw an
-	 * exception of the correct type, and throw an exception otherwise.
-	 * <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
-	 * timeout} attribute, throw an exception if the previous step takes more
-	 * than the specified number of milliseconds.
-	 * <li>ALWAYS run all non-overridden {@code @Before} methods on this class
-	 * and superclasses before any of the previous steps; if any throws an
-	 * Exception, stop execution and pass the exception on.
-	 * <li>ALWAYS run all non-overridden {@code @After} methods on this class
-	 * and superclasses after any of the previous steps; all After methods are
-	 * always executed: exceptions thrown by previous steps are combined, if
-	 * necessary, with exceptions from After methods into a
-	 * {@link MultipleFailureException}.
-	 * <li>ALWAYS allow {@code @Rule} fields to modify the execution of the
-	 * above steps. A {@code Rule} may prevent all execution of the above steps,
-	 * or add additional behavior before and after, or modify thrown exceptions.
-	 * For more information, see {@link TestRule}
-	 * </ul>
-	 * 
-	 * This can be overridden in subclasses, either by overriding this method,
-	 * or the implementations creating each sub-statement.
-	 */
-	protected Statement methodBlock(FrameworkMethod method) {
-		Object test;
-		try {
-			test= new ReflectiveCallable() {
-				@Override
-				protected Object runReflectiveCall() throws Throwable {
-					return createTest();
-				}
-			}.run();
-		} catch (Throwable e) {
-			return new Fail(e);
-		}
+    /**
+     * Adds to {@code errors} for each method annotated with {@code @Test}that
+     * is not a public, void instance method with no arguments.
+     */
+    protected void validateTestMethods(List<Throwable> errors) {
+        validatePublicVoidNoArgMethods(Test.class, false, errors);
+    }
 
-		Statement statement= methodInvoker(method, test);
-		statement= possiblyExpectingExceptions(method, test, statement);
-		statement= withPotentialTimeout(method, test, statement);
-		statement= withBefores(method, test, statement);
-		statement= withAfters(method, test, statement);
-		statement= withRules(method, test, statement);
-		return statement;
-	}
+    /**
+     * Returns a new fixture for running a test. Default implementation executes
+     * the test class's no-argument constructor (validation should have ensured
+     * one exists).
+     */
+    protected Object createTest() throws Exception {
+        return getTestClass().getOnlyConstructor().newInstance();
+    }
 
-	//
-	// Statement builders
-	//
+    /**
+     * Returns the name that describes {@code method} for {@link Description}s.
+     * Default implementation is the method's name
+     */
+    protected String testName(FrameworkMethod method) {
+        return method.getName();
+    }
 
-	/**
-	 * Returns a {@link Statement} that invokes {@code method} on {@code test}
-	 */
-	protected Statement methodInvoker(FrameworkMethod method, Object test) {
-		return new InvokeMethod(method, test);
-	}
+    /**
+     * Returns a Statement that, when executed, either returns normally if
+     * {@code method} passes, or throws an exception if {@code method} fails.
+     *
+     * Here is an outline of the default implementation:
+     *
+     * <ul>
+     * <li>Invoke {@code method} on the result of {@code createTest()}, and
+     * throw any exceptions thrown by either operation.
+     * <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
+     * expecting} attribute, return normally only if the previous step threw an
+     * exception of the correct type, and throw an exception otherwise.
+     * <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
+     * timeout} attribute, throw an exception if the previous step takes more
+     * than the specified number of milliseconds.
+     * <li>ALWAYS run all non-overridden {@code @Before} methods on this class
+     * and superclasses before any of the previous steps; if any throws an
+     * Exception, stop execution and pass the exception on.
+     * <li>ALWAYS run all non-overridden {@code @After} methods on this class
+     * and superclasses after any of the previous steps; all After methods are
+     * always executed: exceptions thrown by previous steps are combined, if
+     * necessary, with exceptions from After methods into a
+     * {@link MultipleFailureException}.
+     * <li>ALWAYS allow {@code @Rule} fields to modify the execution of the
+     * above steps. A {@code Rule} may prevent all execution of the above steps,
+     * or add additional behavior before and after, or modify thrown exceptions.
+     * For more information, see {@link TestRule}
+     * </ul>
+     *
+     * This can be overridden in subclasses, either by overriding this method,
+     * or the implementations creating each sub-statement.
+     */
+    protected Statement methodBlock(FrameworkMethod method) {
+        Object test;
+        try {
+            test = new ReflectiveCallable() {
+                @Override
+                protected Object runReflectiveCall() throws Throwable {
+                    return createTest();
+                }
+            }.run();
+        } catch (Throwable e) {
+            return new Fail(e);
+        }
 
-	/**
-	 * Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
-	 * has the {@code expecting} attribute, return normally only if {@code next}
-	 * throws an exception of the correct type, and throw an exception
-	 * otherwise.
-	 * 
-	 * @deprecated Will be private soon: use Rules instead
-	 */
-	@Deprecated
-	protected Statement possiblyExpectingExceptions(FrameworkMethod method,
-			Object test, Statement next) {
-		Test annotation= method.getAnnotation(Test.class);
-		return expectsException(annotation) ? new ExpectException(next,
-				getExpectedException(annotation)) : next;
-	}
+        Statement statement = methodInvoker(method, test);
+        statement = possiblyExpectingExceptions(method, test, statement);
+        statement = withPotentialTimeout(method, test, statement);
+        statement = withBefores(method, test, statement);
+        statement = withAfters(method, test, statement);
+        statement = withRules(method, test, statement);
+        return statement;
+    }
 
-	/**
-	 * Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
-	 * has the {@code timeout} attribute, throw an exception if {@code next}
-	 * takes more than the specified number of milliseconds.
-	 * 
-	 * @deprecated Will be private soon: use Rules instead
-	 */
-	@Deprecated
-	protected Statement withPotentialTimeout(FrameworkMethod method,
-			Object test, Statement next) {
-		long timeout= getTimeout(method.getAnnotation(Test.class));
-		return timeout > 0 ? new FailOnTimeout(next, timeout) : next;
-	}
+    //
+    // Statement builders
+    //
 
-	/**
-	 * Returns a {@link Statement}: run all non-overridden {@code @Before}
-	 * methods on this class and superclasses before running {@code next}; if
-	 * any throws an Exception, stop execution and pass the exception on.
-	 * 
-	 * @deprecated Will be private soon: use Rules instead
-	 */
-	@Deprecated
-	protected Statement withBefores(FrameworkMethod method, Object target,
-			Statement statement) {
-		List<FrameworkMethod> befores= getTestClass().getAnnotatedMethods(
-				Before.class);
-		return befores.isEmpty() ? statement : new RunBefores(statement,
-				befores, target);
-	}
+    /**
+     * Returns a {@link Statement} that invokes {@code method} on {@code test}
+     */
+    protected Statement methodInvoker(FrameworkMethod method, Object test) {
+        return new InvokeMethod(method, test);
+    }
 
-	/**
-	 * Returns a {@link Statement}: run all non-overridden {@code @After}
-	 * methods on this class and superclasses before running {@code next}; all
-	 * After methods are always executed: exceptions thrown by previous steps
-	 * are combined, if necessary, with exceptions from After methods into a
-	 * {@link MultipleFailureException}.
-	 * 
-	 * @deprecated Will be private soon: use Rules instead
-	 */
-	@Deprecated
-	protected Statement withAfters(FrameworkMethod method, Object target,
-			Statement statement) {
-		List<FrameworkMethod> afters= getTestClass().getAnnotatedMethods(
-				After.class);
-		return afters.isEmpty() ? statement : new RunAfters(statement, afters,
-				target);
-	}
+    /**
+     * Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
+     * has the {@code expecting} attribute, return normally only if {@code next}
+     * throws an exception of the correct type, and throw an exception
+     * otherwise.
+     */
+    protected Statement possiblyExpectingExceptions(FrameworkMethod method,
+            Object test, Statement next) {
+        Test annotation = method.getAnnotation(Test.class);
+        return expectsException(annotation) ? new ExpectException(next,
+                getExpectedException(annotation)) : next;
+    }
 
-	private Statement withRules(FrameworkMethod method, Object target,
-			Statement statement) {
-		Statement result= statement;
-		result= withMethodRules(method, target, result);
-		result= withTestRules(method, target, result);
-		return result;
-	}
+    /**
+     * Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
+     * has the {@code timeout} attribute, throw an exception if {@code next}
+     * takes more than the specified number of milliseconds.
+     */
+    @Deprecated
+    protected Statement withPotentialTimeout(FrameworkMethod method,
+            Object test, Statement next) {
+        long timeout = getTimeout(method.getAnnotation(Test.class));
+        if (timeout <= 0) {
+            return next;
+        }
+        return FailOnTimeout.builder()
+               .withTimeout(timeout, TimeUnit.MILLISECONDS)
+               .build(next);
+    }
 
-	@SuppressWarnings("deprecation")
-	private Statement withMethodRules(FrameworkMethod method, Object target,
-			Statement result) {
-		List<TestRule> testRules= getTestRules(target);
-		for (org.junit.rules.MethodRule each : getMethodRules(target))
-			if (! testRules.contains(each))
-				result= each.apply(result, method, target);
-		return result;
-	}
+    /**
+     * Returns a {@link Statement}: run all non-overridden {@code @Before}
+     * methods on this class and superclasses before running {@code next}; if
+     * any throws an Exception, stop execution and pass the exception on.
+     */
+    protected Statement withBefores(FrameworkMethod method, Object target,
+            Statement statement) {
+        List<FrameworkMethod> befores = getTestClass().getAnnotatedMethods(
+                Before.class);
+        return befores.isEmpty() ? statement : new RunBefores(statement,
+                befores, target);
+    }
 
-	@SuppressWarnings("deprecation")
-	private List<org.junit.rules.MethodRule> getMethodRules(Object target) {
-		return rules(target);
-	}
+    /**
+     * Returns a {@link Statement}: run all non-overridden {@code @After}
+     * methods on this class and superclasses before running {@code next}; all
+     * After methods are always executed: exceptions thrown by previous steps
+     * are combined, if necessary, with exceptions from After methods into a
+     * {@link MultipleFailureException}.
+     */
+    protected Statement withAfters(FrameworkMethod method, Object target,
+            Statement statement) {
+        List<FrameworkMethod> afters = getTestClass().getAnnotatedMethods(
+                After.class);
+        return afters.isEmpty() ? statement : new RunAfters(statement, afters,
+                target);
+    }
 
-	/**
-	 * @param target
-	 *            the test case instance
-	 * @return a list of MethodRules that should be applied when executing this
-	 *         test
-	 * @deprecated {@link org.junit.rules.MethodRule} is a deprecated interface. Port to
-	 *             {@link TestRule} and
-	 *             {@link BlockJUnit4ClassRunner#getTestRules(Object)}
-	 */
-	@Deprecated
-	protected List<org.junit.rules.MethodRule> rules(Object target) {
-		return getTestClass().getAnnotatedFieldValues(target, Rule.class,
-				org.junit.rules.MethodRule.class);
-	}
+    private Statement withRules(FrameworkMethod method, Object target,
+            Statement statement) {
+        List<TestRule> testRules = getTestRules(target);
+        Statement result = statement;
+        result = withMethodRules(method, testRules, target, result);
+        result = withTestRules(method, testRules, result);
 
-	/**
-	 * Returns a {@link Statement}: apply all non-static {@link Value} fields
-	 * annotated with {@link Rule}.
-	 *
-	 * @param statement The base statement
-	 * @return a RunRules statement if any class-level {@link Rule}s are
-	 *         found, or the base statement
-	 */
-	private Statement withTestRules(FrameworkMethod method, Object target,
-			Statement statement) {
-		List<TestRule> testRules= getTestRules(target);
-		return testRules.isEmpty() ? statement :
-			new RunRules(statement, testRules, describeChild(method));
-	}
+        return result;
+    }
 
-	/**
-	 * @param target
-	 *            the test case instance
-	 * @return a list of TestRules that should be applied when executing this
-	 *         test
-	 */
-	protected List<TestRule> getTestRules(Object target) {
-		return getTestClass().getAnnotatedFieldValues(target,
-				Rule.class, TestRule.class);
-	}
+    private Statement withMethodRules(FrameworkMethod method, List<TestRule> testRules,
+            Object target, Statement result) {
+        for (org.junit.rules.MethodRule each : getMethodRules(target)) {
+            if (!testRules.contains(each)) {
+                result = each.apply(result, method, target);
+            }
+        }
+        return result;
+    }
 
-	private Class<? extends Throwable> getExpectedException(Test annotation) {
-		if (annotation == null || annotation.expected() == None.class)
-			return null;
-		else
-			return annotation.expected();
-	}
+    private List<org.junit.rules.MethodRule> getMethodRules(Object target) {
+        return rules(target);
+    }
 
-	private boolean expectsException(Test annotation) {
-		return getExpectedException(annotation) != null;
-	}
+    /**
+     * @param target the test case instance
+     * @return a list of MethodRules that should be applied when executing this
+     *         test
+     */
+    protected List<MethodRule> rules(Object target) {
+        List<MethodRule> rules = getTestClass().getAnnotatedMethodValues(target, 
+                Rule.class, MethodRule.class);
+        
+        rules.addAll(getTestClass().getAnnotatedFieldValues(target,
+                Rule.class, MethodRule.class));
+        
+        return rules;
+    }
 
-	private long getTimeout(Test annotation) {
-		if (annotation == null)
-			return 0;
-		return annotation.timeout();
-	}
+    /**
+     * Returns a {@link Statement}: apply all non-static fields
+     * annotated with {@link Rule}.
+     *
+     * @param statement The base statement
+     * @return a RunRules statement if any class-level {@link Rule}s are
+     *         found, or the base statement
+     */
+    private Statement withTestRules(FrameworkMethod method, List<TestRule> testRules,
+            Statement statement) {
+        return testRules.isEmpty() ? statement :
+                new RunRules(statement, testRules, describeChild(method));
+    }
+
+    /**
+     * @param target the test case instance
+     * @return a list of TestRules that should be applied when executing this
+     *         test
+     */
+    protected List<TestRule> getTestRules(Object target) {
+        List<TestRule> result = getTestClass().getAnnotatedMethodValues(target,
+                Rule.class, TestRule.class);
+
+        result.addAll(getTestClass().getAnnotatedFieldValues(target,
+                Rule.class, TestRule.class));
+
+        return result;
+    }
+
+    private Class<? extends Throwable> getExpectedException(Test annotation) {
+        if (annotation == null || annotation.expected() == None.class) {
+            return null;
+        } else {
+            return annotation.expected();
+        }
+    }
+
+    private boolean expectsException(Test annotation) {
+        return getExpectedException(annotation) != null;
+    }
+
+    private long getTimeout(Test annotation) {
+        if (annotation == null) {
+            return 0;
+        }
+        return annotation.timeout();
+    }
 }
diff --git a/src/main/java/org/junit/runners/JUnit4.java b/src/main/java/org/junit/runners/JUnit4.java
index 1e1f347..6ba28c2 100644
--- a/src/main/java/org/junit/runners/JUnit4.java
+++ b/src/main/java/org/junit/runners/JUnit4.java
@@ -11,12 +11,14 @@
  * This is the only way this class should be used--any extension that
  * depends on the implementation details of this class is likely to break
  * in future versions.
+ *
+ * @since 4.5
  */
 public final class JUnit4 extends BlockJUnit4ClassRunner {
-	/**
-	 * Constructs a new instance of the default runner
-	 */
-	public JUnit4(Class<?> klass) throws InitializationError {
-		super(klass);
-	}
+    /**
+     * Constructs a new instance of the default runner
+     */
+    public JUnit4(Class<?> klass) throws InitializationError {
+        super(klass);
+    }
 }
diff --git a/src/main/java/org/junit/runners/MethodSorters.java b/src/main/java/org/junit/runners/MethodSorters.java
new file mode 100644
index 0000000..5821892
--- /dev/null
+++ b/src/main/java/org/junit/runners/MethodSorters.java
@@ -0,0 +1,41 @@
+package org.junit.runners;
+
+import java.lang.reflect.Method;
+import java.util.Comparator;
+
+import org.junit.internal.MethodSorter;
+
+/**
+ * Sort the methods into a specified execution order.
+ * Defines common {@link MethodSorter} implementations.
+ *
+ * @since 4.11
+ */
+public enum MethodSorters {
+    /**
+     * Sorts the test methods by the method name, in lexicographic order,
+     * with {@link Method#toString()} used as a tiebreaker
+     */
+    NAME_ASCENDING(MethodSorter.NAME_ASCENDING),
+
+    /**
+     * Leaves the test methods in the order returned by the JVM.
+     * Note that the order from the JVM may vary from run to run
+     */
+    JVM(null),
+
+    /**
+     * Sorts the test methods in a deterministic, but not predictable, order
+     */
+    DEFAULT(MethodSorter.DEFAULT);
+
+    private final Comparator<Method> comparator;
+
+    private MethodSorters(Comparator<Method> comparator) {
+        this.comparator = comparator;
+    }
+
+    public Comparator<Method> getComparator() {
+        return comparator;
+    }
+}
diff --git a/src/main/java/org/junit/runners/Parameterized.java b/src/main/java/org/junit/runners/Parameterized.java
index 3ebfead..829c8f0 100644
--- a/src/main/java/org/junit/runners/Parameterized.java
+++ b/src/main/java/org/junit/runners/Parameterized.java
@@ -1,167 +1,351 @@
 package org.junit.runners;
 
-import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.lang.reflect.Modifier;
+import java.text.MessageFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
 import org.junit.runner.Runner;
-import org.junit.runner.notification.RunNotifier;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.Statement;
 import org.junit.runners.model.TestClass;
+import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory;
+import org.junit.runners.parameterized.ParametersRunnerFactory;
+import org.junit.runners.parameterized.TestWithParameters;
 
 /**
- * <p>
  * The custom runner <code>Parameterized</code> implements parameterized tests.
  * When running a parameterized test class, instances are created for the
  * cross-product of the test methods and the test data elements.
- * </p>
- * 
+ * <p>
  * For example, to test a Fibonacci function, write:
- * 
  * <pre>
  * &#064;RunWith(Parameterized.class)
  * public class FibonacciTest {
- * 	&#064;Parameters
- * 	public static List&lt;Object[]&gt; data() {
- * 		return Arrays.asList(new Object[][] {
- * 			{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
- * 		});
- * 	}
- * 
- * 	private int fInput;
- * 
- * 	private int fExpected;
- * 
- * 	public FibonacciTest(int input, int expected) {
- * 		fInput= input;
- * 		fExpected= expected;
- * 	}
- * 
- * 	&#064;Test
- * 	public void test() {
- * 		assertEquals(fExpected, Fibonacci.compute(fInput));
- * 	}
+ *     &#064;Parameters(name= &quot;{index}: fib[{0}]={1}&quot;)
+ *     public static Iterable&lt;Object[]&gt; data() {
+ *         return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
+ *                 { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
+ *     }
+ *
+ *     private int fInput;
+ *
+ *     private int fExpected;
+ *
+ *     public FibonacciTest(int input, int expected) {
+ *         fInput= input;
+ *         fExpected= expected;
+ *     }
+ *
+ *     &#064;Test
+ *     public void test() {
+ *         assertEquals(fExpected, Fibonacci.compute(fInput));
+ *     }
  * }
  * </pre>
- * 
  * <p>
  * Each instance of <code>FibonacciTest</code> will be constructed using the
  * two-argument constructor and the data values in the
  * <code>&#064;Parameters</code> method.
- * </p>
+ * <p>
+ * In order that you can easily identify the individual tests, you may provide a
+ * name for the <code>&#064;Parameters</code> annotation. This name is allowed
+ * to contain placeholders, which are replaced at runtime. The placeholders are
+ * <dl>
+ * <dt>{index}</dt>
+ * <dd>the current parameter index</dd>
+ * <dt>{0}</dt>
+ * <dd>the first parameter value</dd>
+ * <dt>{1}</dt>
+ * <dd>the second parameter value</dd>
+ * <dt>...</dt>
+ * <dd>...</dd>
+ * </dl>
+ * <p>
+ * In the example given above, the <code>Parameterized</code> runner creates
+ * names like <code>[1: fib(3)=2]</code>. If you don't use the name parameter,
+ * then the current parameter index is used as name.
+ * <p>
+ * You can also write:
+ * <pre>
+ * &#064;RunWith(Parameterized.class)
+ * public class FibonacciTest {
+ *  &#064;Parameters
+ *  public static Iterable&lt;Object[]&gt; data() {
+ *      return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
+ *                 { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
+ *  }
+ *  
+ *  &#064;Parameter(0)
+ *  public int fInput;
+ *
+ *  &#064;Parameter(1)
+ *  public int fExpected;
+ *
+ *  &#064;Test
+ *  public void test() {
+ *      assertEquals(fExpected, Fibonacci.compute(fInput));
+ *  }
+ * }
+ * </pre>
+ * <p>
+ * Each instance of <code>FibonacciTest</code> will be constructed with the default constructor
+ * and fields annotated by <code>&#064;Parameter</code>  will be initialized
+ * with the data values in the <code>&#064;Parameters</code> method.
+ *
+ * <p>
+ * The parameters can be provided as an array, too:
+ * 
+ * <pre>
+ * &#064;Parameters
+ * public static Object[][] data() {
+ * 	return new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 },
+ * 			{ 5, 5 }, { 6, 8 } };
+ * }
+ * </pre>
+ * 
+ * <h3>Tests with single parameter</h3>
+ * <p>
+ * If your test needs a single parameter only, you don't have to wrap it with an
+ * array. Instead you can provide an <code>Iterable</code> or an array of
+ * objects.
+ * <pre>
+ * &#064;Parameters
+ * public static Iterable&lt;? extends Object&gt; data() {
+ * 	return Arrays.asList(&quot;first test&quot;, &quot;second test&quot;);
+ * }
+ * </pre>
+ * <p>
+ * or
+ * <pre>
+ * &#064;Parameters
+ * public static Object[] data() {
+ * 	return new Object[] { &quot;first test&quot;, &quot;second test&quot; };
+ * }
+ * </pre>
+ *
+ * <h3>Create different runners</h3>
+ * <p>
+ * By default the {@code Parameterized} runner creates a slightly modified
+ * {@link BlockJUnit4ClassRunner} for each set of parameters. You can build an
+ * own {@code Parameterized} runner that creates another runner for each set of
+ * parameters. Therefore you have to build a {@link ParametersRunnerFactory}
+ * that creates a runner for each {@link TestWithParameters}. (
+ * {@code TestWithParameters} are bundling the parameters and the test name.)
+ * The factory must have a public zero-arg constructor.
+ *
+ * <pre>
+ * public class YourRunnerFactory implements ParameterizedRunnerFactory {
+ *     public Runner createRunnerForTestWithParameters(TestWithParameters test)
+ *             throws InitializationError {
+ *         return YourRunner(test);
+ *     }
+ * }
+ * </pre>
+ * <p>
+ * Use the {@link UseParametersRunnerFactory} to tell the {@code Parameterized}
+ * runner that it should use your factory.
+ *
+ * <pre>
+ * &#064;RunWith(Parameterized.class)
+ * &#064;UseParametersRunnerFactory(YourRunnerFactory.class)
+ * public class YourTest {
+ *     ...
+ * }
+ * </pre>
+ *
+ * @since 4.0
  */
 public class Parameterized extends Suite {
-	/**
-	 * Annotation for a method which provides parameters to be injected into the
-	 * test class constructor by <code>Parameterized</code>
-	 */
-	@Retention(RetentionPolicy.RUNTIME)
-	@Target(ElementType.METHOD)
-	public static @interface Parameters {
-	}
+    /**
+     * Annotation for a method which provides parameters to be injected into the
+     * test class constructor by <code>Parameterized</code>. The method has to
+     * be public and static.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.METHOD)
+    public static @interface Parameters {
+        /**
+         * Optional pattern to derive the test's name from the parameters. Use
+         * numbers in braces to refer to the parameters or the additional data
+         * as follows:
+         * <pre>
+         * {index} - the current parameter index
+         * {0} - the first parameter value
+         * {1} - the second parameter value
+         * etc...
+         * </pre>
+         * <p>
+         * Default value is "{index}" for compatibility with previous JUnit
+         * versions.
+         *
+         * @return {@link MessageFormat} pattern string, except the index
+         *         placeholder.
+         * @see MessageFormat
+         */
+        String name() default "{index}";
+    }
 
-	private class TestClassRunnerForParameters extends
-			BlockJUnit4ClassRunner {
-		private final int fParameterSetNumber;
+    /**
+     * Annotation for fields of the test class which will be initialized by the
+     * method annotated by <code>Parameters</code>.
+     * By using directly this annotation, the test class constructor isn't needed.
+     * Index range must start at 0.
+     * Default value is 0.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.FIELD)
+    public static @interface Parameter {
+        /**
+         * Method that returns the index of the parameter in the array
+         * returned by the method annotated by <code>Parameters</code>.
+         * Index range must start at 0.
+         * Default value is 0.
+         *
+         * @return the index of the parameter.
+         */
+        int value() default 0;
+    }
 
-		private final List<Object[]> fParameterList;
+    /**
+     * Add this annotation to your test class if you want to generate a special
+     * runner. You have to specify a {@link ParametersRunnerFactory} class that
+     * creates such runners. The factory must have a public zero-arg
+     * constructor.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Inherited
+    @Target(ElementType.TYPE)
+    public @interface UseParametersRunnerFactory {
+        /**
+         * @return a {@link ParametersRunnerFactory} class (must have a default
+         *         constructor)
+         */
+        Class<? extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class;
+    }
 
-		TestClassRunnerForParameters(Class<?> type,
-				List<Object[]> parameterList, int i) throws InitializationError {
-			super(type);
-			fParameterList= parameterList;
-			fParameterSetNumber= i;
-		}
+    private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory();
 
-		@Override
-		public Object createTest() throws Exception {
-			return getTestClass().getOnlyConstructor().newInstance(
-					computeParams());
-		}
+    private static final List<Runner> NO_RUNNERS = Collections.<Runner>emptyList();
 
-		private Object[] computeParams() throws Exception {
-			try {
-				return fParameterList.get(fParameterSetNumber);
-			} catch (ClassCastException e) {
-				throw new Exception(String.format(
-						"%s.%s() must return a Collection of arrays.",
-						getTestClass().getName(), getParametersMethod(
-								getTestClass()).getName()));
-			}
-		}
+    private final List<Runner> runners;
 
-		@Override
-		protected String getName() {
-			return String.format("[%s]", fParameterSetNumber);
-		}
+    /**
+     * Only called reflectively. Do not use programmatically.
+     */
+    public Parameterized(Class<?> klass) throws Throwable {
+        super(klass, NO_RUNNERS);
+        ParametersRunnerFactory runnerFactory = getParametersRunnerFactory(
+                klass);
+        Parameters parameters = getParametersMethod().getAnnotation(
+                Parameters.class);
+        runners = Collections.unmodifiableList(createRunnersForParameters(
+                allParameters(), parameters.name(), runnerFactory));
+    }
 
-		@Override
-		protected String testName(final FrameworkMethod method) {
-			return String.format("%s[%s]", method.getName(),
-					fParameterSetNumber);
-		}
+    private ParametersRunnerFactory getParametersRunnerFactory(Class<?> klass)
+            throws InstantiationException, IllegalAccessException {
+        UseParametersRunnerFactory annotation = klass
+                .getAnnotation(UseParametersRunnerFactory.class);
+        if (annotation == null) {
+            return DEFAULT_FACTORY;
+        } else {
+            Class<? extends ParametersRunnerFactory> factoryClass = annotation
+                    .value();
+            return factoryClass.newInstance();
+        }
+    }
 
-		@Override
-		protected void validateConstructor(List<Throwable> errors) {
-			validateOnlyOneConstructor(errors);
-		}
+    @Override
+    protected List<Runner> getChildren() {
+        return runners;
+    }
 
-		@Override
-		protected Statement classBlock(RunNotifier notifier) {
-			return childrenInvoker(notifier);
-		}
-		
-		@Override
-		protected Annotation[] getRunnerAnnotations() {
-			return new Annotation[0];
-		}
-	}
+    private TestWithParameters createTestWithNotNormalizedParameters(
+            String pattern, int index, Object parametersOrSingleParameter) {
+        Object[] parameters= (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter
+            : new Object[] { parametersOrSingleParameter };
+        return createTestWithParameters(getTestClass(), pattern, index,
+                parameters);
+    }
 
-	private final ArrayList<Runner> runners= new ArrayList<Runner>();
+    @SuppressWarnings("unchecked")
+    private Iterable<Object> allParameters() throws Throwable {
+        Object parameters = getParametersMethod().invokeExplosively(null);
+        if (parameters instanceof Iterable) {
+            return (Iterable<Object>) parameters;
+        } else if (parameters instanceof Object[]) {
+            return Arrays.asList((Object[]) parameters);
+        } else {
+            throw parametersMethodReturnedWrongType();
+        }
+    }
 
-	/**
-	 * Only called reflectively. Do not use programmatically.
-	 */
-	public Parameterized(Class<?> klass) throws Throwable {
-		super(klass, Collections.<Runner>emptyList());
-		List<Object[]> parametersList= getParametersList(getTestClass());
-		for (int i= 0; i < parametersList.size(); i++)
-			runners.add(new TestClassRunnerForParameters(getTestClass().getJavaClass(),
-					parametersList, i));
-	}
+    private FrameworkMethod getParametersMethod() throws Exception {
+        List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(
+                Parameters.class);
+        for (FrameworkMethod each : methods) {
+            if (each.isStatic() && each.isPublic()) {
+                return each;
+            }
+        }
 
-	@Override
-	protected List<Runner> getChildren() {
-		return runners;
-	}
+        throw new Exception("No public static parameters method on class "
+                + getTestClass().getName());
+    }
 
-	@SuppressWarnings("unchecked")
-	private List<Object[]> getParametersList(TestClass klass)
-			throws Throwable {
-		return (List<Object[]>) getParametersMethod(klass).invokeExplosively(
-				null);
-	}
+    private List<Runner> createRunnersForParameters(
+            Iterable<Object> allParameters, String namePattern,
+            ParametersRunnerFactory runnerFactory)
+            throws InitializationError,
+            Exception {
+        try {
+            List<TestWithParameters> tests = createTestsForParameters(
+                    allParameters, namePattern);
+            List<Runner> runners = new ArrayList<Runner>();
+            for (TestWithParameters test : tests) {
+                runners.add(runnerFactory
+                        .createRunnerForTestWithParameters(test));
+            }
+            return runners;
+        } catch (ClassCastException e) {
+            throw parametersMethodReturnedWrongType();
+        }
+    }
 
-	private FrameworkMethod getParametersMethod(TestClass testClass)
-			throws Exception {
-		List<FrameworkMethod> methods= testClass
-				.getAnnotatedMethods(Parameters.class);
-		for (FrameworkMethod each : methods) {
-			int modifiers= each.getMethod().getModifiers();
-			if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))
-				return each;
-		}
+    private List<TestWithParameters> createTestsForParameters(
+            Iterable<Object> allParameters, String namePattern)
+            throws Exception {
+        int i = 0;
+        List<TestWithParameters> children = new ArrayList<TestWithParameters>();
+        for (Object parametersOfSingleTest : allParameters) {
+            children.add(createTestWithNotNormalizedParameters(namePattern,
+                    i++, parametersOfSingleTest));
+        }
+        return children;
+    }
 
-		throw new Exception("No public static parameters method on class "
-				+ testClass.getName());
-	}
+    private Exception parametersMethodReturnedWrongType() throws Exception {
+        String className = getTestClass().getName();
+        String methodName = getParametersMethod().getName();
+        String message = MessageFormat.format(
+                "{0}.{1}() must return an Iterable of arrays.",
+                className, methodName);
+        return new Exception(message);
+    }
 
+    private static TestWithParameters createTestWithParameters(
+            TestClass testClass, String pattern, int index, Object[] parameters) {
+        String finalPattern = pattern.replaceAll("\\{index\\}",
+                Integer.toString(index));
+        String name = MessageFormat.format(finalPattern, parameters);
+        return new TestWithParameters("[" + name + "]", testClass,
+                Arrays.asList(parameters));
+    }
 }
diff --git a/src/main/java/org/junit/runners/ParentRunner.java b/src/main/java/org/junit/runners/ParentRunner.java
old mode 100644
new mode 100755
index a41aad3..92641bf
--- a/src/main/java/org/junit/runners/ParentRunner.java
+++ b/src/main/java/org/junit/runners/ParentRunner.java
@@ -1,10 +1,13 @@
 package org.junit.runners;
 
-import static org.junit.internal.runners.rules.RuleFieldValidator.CLASS_RULE_VALIDATOR;
+import static org.junit.internal.runners.rules.RuleMemberValidator.CLASS_RULE_METHOD_VALIDATOR;
+import static org.junit.internal.runners.rules.RuleMemberValidator.CLASS_RULE_VALIDATOR;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
@@ -13,6 +16,7 @@
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.internal.AssumptionViolatedException;
 import org.junit.internal.runners.model.EachTestNotifier;
@@ -31,10 +35,12 @@
 import org.junit.runner.notification.StoppedByUserException;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.MultipleFailureException;
 import org.junit.runners.model.RunnerScheduler;
 import org.junit.runners.model.Statement;
 import org.junit.runners.model.TestClass;
+import org.junit.validator.AnnotationsValidator;
+import org.junit.validator.PublicClassValidator;
+import org.junit.validator.TestClassValidator;
 
 /**
  * Provides most of the functionality specific to a Runner that implements a
@@ -43,336 +49,404 @@
  * {@link Method} . For {@link Suite}, {@code T} is {@link Class}.) Subclasses
  * must implement finding the children of the node, describing each child, and
  * running each child. ParentRunner will filter and sort children, handle
- * {@code @BeforeClass} and {@code @AfterClass} methods, 
+ * {@code @BeforeClass} and {@code @AfterClass} methods,
  * handle annotated {@link ClassRule}s, create a composite
  * {@link Description}, and run children sequentially.
+ *
+ * @since 4.5
  */
 public abstract class ParentRunner<T> extends Runner implements Filterable,
-		Sortable {
-	private final TestClass fTestClass;
+        Sortable {
+    private static final List<TestClassValidator> VALIDATORS = Arrays.asList(
+            new AnnotationsValidator(), new PublicClassValidator());
 
-	private Sorter fSorter= Sorter.NULL;
+    private final Object childrenLock = new Object();
+    private final TestClass testClass;
 
-	private List<T> fFilteredChildren= null;
-	
-	private RunnerScheduler fScheduler= new RunnerScheduler() {	
-		public void schedule(Runnable childStatement) {
-			childStatement.run();
-		}
-	
-		public void finished() {
-			// do nothing
-		}
-	};
+    // Guarded by childrenLock
+    private volatile Collection<T> filteredChildren = null;
 
-	/**
-	 * Constructs a new {@code ParentRunner} that will run {@code @TestClass}
-	 * @throws InitializationError 
-	 */
-	protected ParentRunner(Class<?> testClass) throws InitializationError {
-		fTestClass= new TestClass(testClass);
-		validate();
-	}
+    private volatile RunnerScheduler scheduler = new RunnerScheduler() {
+        public void schedule(Runnable childStatement) {
+            childStatement.run();
+        }
 
-	//
-	// Must be overridden
-	//
+        public void finished() {
+            // do nothing
+        }
+    };
 
-	/**
-	 * Returns a list of objects that define the children of this Runner.
-	 */
-	protected abstract List<T> getChildren();
+    /**
+     * Constructs a new {@code ParentRunner} that will run {@code @TestClass}
+     */
+    protected ParentRunner(Class<?> testClass) throws InitializationError {
+        this.testClass = createTestClass(testClass);
+        validate();
+    }
 
-	/**
-	 * Returns a {@link Description} for {@code child}, which can be assumed to
-	 * be an element of the list returned by {@link ParentRunner#getChildren()}
-	 */
-	protected abstract Description describeChild(T child);
+    protected TestClass createTestClass(Class<?> testClass) {
+        return new TestClass(testClass);
+    }
 
-	/**
-	 * Runs the test corresponding to {@code child}, which can be assumed to be
-	 * an element of the list returned by {@link ParentRunner#getChildren()}.
-	 * Subclasses are responsible for making sure that relevant test events are
-	 * reported through {@code notifier}
-	 */
-	protected abstract void runChild(T child, RunNotifier notifier);
-		
-	//
-	// May be overridden
-	//
-	
-	/**
-	 * Adds to {@code errors} a throwable for each problem noted with the test class (available from {@link #getTestClass()}).
-	 * Default implementation adds an error for each method annotated with
-	 * {@code @BeforeClass} or {@code @AfterClass} that is not
-	 * {@code public static void} with no arguments.
-	 */
-	protected void collectInitializationErrors(List<Throwable> errors) {
-		validatePublicVoidNoArgMethods(BeforeClass.class, true, errors);
-		validatePublicVoidNoArgMethods(AfterClass.class, true, errors);
-		validateClassRules(errors);
-	}
+    //
+    // Must be overridden
+    //
 
-	/**
-	 * Adds to {@code errors} if any method in this class is annotated with
-	 * {@code annotation}, but:
-	 * <ul>
-	 * <li>is not public, or
-	 * <li>takes parameters, or
-	 * <li>returns something other than void, or
-	 * <li>is static (given {@code isStatic is false}), or
-	 * <li>is not static (given {@code isStatic is true}).
-	 */
-	protected void validatePublicVoidNoArgMethods(Class<? extends Annotation> annotation,
-			boolean isStatic, List<Throwable> errors) {
-		List<FrameworkMethod> methods= getTestClass().getAnnotatedMethods(annotation);
+    /**
+     * Returns a list of objects that define the children of this Runner.
+     */
+    protected abstract List<T> getChildren();
 
-		for (FrameworkMethod eachTestMethod : methods)
-			eachTestMethod.validatePublicVoidNoArg(isStatic, errors);
-	}
+    /**
+     * Returns a {@link Description} for {@code child}, which can be assumed to
+     * be an element of the list returned by {@link ParentRunner#getChildren()}
+     */
+    protected abstract Description describeChild(T child);
 
-	private void validateClassRules(List<Throwable> errors) {
-		CLASS_RULE_VALIDATOR.validate(getTestClass(), errors);
-	}
+    /**
+     * Runs the test corresponding to {@code child}, which can be assumed to be
+     * an element of the list returned by {@link ParentRunner#getChildren()}.
+     * Subclasses are responsible for making sure that relevant test events are
+     * reported through {@code notifier}
+     */
+    protected abstract void runChild(T child, RunNotifier notifier);
 
-	/** 
-	 * Constructs a {@code Statement} to run all of the tests in the test class. Override to add pre-/post-processing. 
-	 * Here is an outline of the implementation:
-	 * <ul>
-	 * <li>Call {@link #runChild(Object, RunNotifier)} on each object returned by {@link #getChildren()} (subject to any imposed filter and sort).</li>
-	 * <li>ALWAYS run all non-overridden {@code @BeforeClass} methods on this class
-	 * and superclasses before the previous step; if any throws an
-	 * Exception, stop execution and pass the exception on.
-	 * <li>ALWAYS run all non-overridden {@code @AfterClass} methods on this class
-	 * and superclasses before any of the previous steps; all AfterClass methods are
-	 * always executed: exceptions thrown by previous steps are combined, if
-	 * necessary, with exceptions from AfterClass methods into a
-	 * {@link MultipleFailureException}.
-	 * </ul>
-	 * @param notifier
-	 * @return {@code Statement}
-	 */
-	protected Statement classBlock(final RunNotifier notifier) {
-		Statement statement= childrenInvoker(notifier);
-		statement= withBeforeClasses(statement);
-		statement= withAfterClasses(statement);
-		statement= withClassRules(statement);
-		return statement;
-	}
+    //
+    // May be overridden
+    //
 
-	/**
-	 * Returns a {@link Statement}: run all non-overridden {@code @BeforeClass} methods on this class
-	 * and superclasses before executing {@code statement}; if any throws an
-	 * Exception, stop execution and pass the exception on.
-	 */
-	protected Statement withBeforeClasses(Statement statement) {
-		List<FrameworkMethod> befores= fTestClass
-				.getAnnotatedMethods(BeforeClass.class);
-		return befores.isEmpty() ? statement :
-			new RunBefores(statement, befores, null);
-	}
+    /**
+     * Adds to {@code errors} a throwable for each problem noted with the test class (available from {@link #getTestClass()}).
+     * Default implementation adds an error for each method annotated with
+     * {@code @BeforeClass} or {@code @AfterClass} that is not
+     * {@code public static void} with no arguments.
+     */
+    protected void collectInitializationErrors(List<Throwable> errors) {
+        validatePublicVoidNoArgMethods(BeforeClass.class, true, errors);
+        validatePublicVoidNoArgMethods(AfterClass.class, true, errors);
+        validateClassRules(errors);
+        applyValidators(errors);
+    }
 
-	/**
-	 * Returns a {@link Statement}: run all non-overridden {@code @AfterClass} methods on this class
-	 * and superclasses before executing {@code statement}; all AfterClass methods are
-	 * always executed: exceptions thrown by previous steps are combined, if
-	 * necessary, with exceptions from AfterClass methods into a
-	 * {@link MultipleFailureException}.
-	 */
-	protected Statement withAfterClasses(Statement statement) {
-		List<FrameworkMethod> afters= fTestClass
-				.getAnnotatedMethods(AfterClass.class);
-		return afters.isEmpty() ? statement : 
-			new RunAfters(statement, afters, null);
-	}
+    private void applyValidators(List<Throwable> errors) {
+        if (getTestClass().getJavaClass() != null) {
+            for (TestClassValidator each : VALIDATORS) {
+                errors.addAll(each.validateTestClass(getTestClass()));
+            }
+        }
+    }
 
-	/**
-	 * Returns a {@link Statement}: apply all 
-	 * static fields assignable to {@link TestRule}
-	 * annotated with {@link ClassRule}.
-	 *
-	 * @param statement
-	 *            the base statement
-	 * @return a RunRules statement if any class-level {@link Rule}s are
-	 *         found, or the base statement
-	 */
-	private Statement withClassRules(Statement statement) {
-		List<TestRule> classRules= classRules();
-		return classRules.isEmpty() ? statement :
-		    new RunRules(statement, classRules, getDescription());
-	}
+    /**
+     * Adds to {@code errors} if any method in this class is annotated with
+     * {@code annotation}, but:
+     * <ul>
+     * <li>is not public, or
+     * <li>takes parameters, or
+     * <li>returns something other than void, or
+     * <li>is static (given {@code isStatic is false}), or
+     * <li>is not static (given {@code isStatic is true}).
+     * </ul>
+     */
+    protected void validatePublicVoidNoArgMethods(Class<? extends Annotation> annotation,
+            boolean isStatic, List<Throwable> errors) {
+        List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(annotation);
 
-	/**
-	 * @return the {@code ClassRule}s that can transform the block that runs
-	 *         each method in the tested class.
-	 */
-	protected List<TestRule> classRules() {
-		return fTestClass.getAnnotatedFieldValues(null, ClassRule.class, TestRule.class);
-	}
+        for (FrameworkMethod eachTestMethod : methods) {
+            eachTestMethod.validatePublicVoidNoArg(isStatic, errors);
+        }
+    }
 
-	/**
-	 * Returns a {@link Statement}: Call {@link #runChild(Object, RunNotifier)}
-	 * on each object returned by {@link #getChildren()} (subject to any imposed
-	 * filter and sort)
-	 */
-	protected Statement childrenInvoker(final RunNotifier notifier) {
-		return new Statement() {
-			@Override
-			public void evaluate() {
-				runChildren(notifier);
-			}
-		};
-	}
+    private void validateClassRules(List<Throwable> errors) {
+        CLASS_RULE_VALIDATOR.validate(getTestClass(), errors);
+        CLASS_RULE_METHOD_VALIDATOR.validate(getTestClass(), errors);
+    }
 
-	private void runChildren(final RunNotifier notifier) {
-		for (final T each : getFilteredChildren())
- 			fScheduler.schedule(new Runnable() {			
-				public void run() {
-					ParentRunner.this.runChild(each, notifier);
-				}
-			});
-		fScheduler.finished();
-	}
+    /**
+     * Constructs a {@code Statement} to run all of the tests in the test class.
+     * Override to add pre-/post-processing. Here is an outline of the
+     * implementation:
+     * <ol>
+     * <li>Determine the children to be run using {@link #getChildren()}
+     * (subject to any imposed filter and sort).</li>
+     * <li>If there are any children remaining after filtering and ignoring,
+     * construct a statement that will:
+     * <ol>
+     * <li>Apply all {@code ClassRule}s on the test-class and superclasses.</li>
+     * <li>Run all non-overridden {@code @BeforeClass} methods on the test-class
+     * and superclasses; if any throws an Exception, stop execution and pass the
+     * exception on.</li>
+     * <li>Run all remaining tests on the test-class.</li>
+     * <li>Run all non-overridden {@code @AfterClass} methods on the test-class
+     * and superclasses: exceptions thrown by previous steps are combined, if
+     * necessary, with exceptions from AfterClass methods into a
+     * {@link org.junit.runners.model.MultipleFailureException}.</li>
+     * </ol>
+     * </li>
+     * </ol>
+     *
+     * @return {@code Statement}
+     */
+    protected Statement classBlock(final RunNotifier notifier) {
+        Statement statement = childrenInvoker(notifier);
+        if (!areAllChildrenIgnored()) {
+            statement = withBeforeClasses(statement);
+            statement = withAfterClasses(statement);
+            statement = withClassRules(statement);
+        }
+        return statement;
+    }
 
-	/**
-	 * Returns a name used to describe this Runner
-	 */
-	protected String getName() {
-		return fTestClass.getName();
-	}
+    private boolean areAllChildrenIgnored() {
+        for (T child : getFilteredChildren()) {
+            if (!isIgnored(child)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-	//
-	// Available for subclasses
-	//
+    /**
+     * Returns a {@link Statement}: run all non-overridden {@code @BeforeClass} methods on this class
+     * and superclasses before executing {@code statement}; if any throws an
+     * Exception, stop execution and pass the exception on.
+     */
+    protected Statement withBeforeClasses(Statement statement) {
+        List<FrameworkMethod> befores = testClass
+                .getAnnotatedMethods(BeforeClass.class);
+        return befores.isEmpty() ? statement :
+                new RunBefores(statement, befores, null);
+    }
 
-	/**
-	 * Returns a {@link TestClass} object wrapping the class to be executed.
-	 */
-	public final TestClass getTestClass() {
-		return fTestClass;
-	}
+    /**
+     * Returns a {@link Statement}: run all non-overridden {@code @AfterClass} methods on this class
+     * and superclasses before executing {@code statement}; all AfterClass methods are
+     * always executed: exceptions thrown by previous steps are combined, if
+     * necessary, with exceptions from AfterClass methods into a
+     * {@link org.junit.runners.model.MultipleFailureException}.
+     */
+    protected Statement withAfterClasses(Statement statement) {
+        List<FrameworkMethod> afters = testClass
+                .getAnnotatedMethods(AfterClass.class);
+        return afters.isEmpty() ? statement :
+                new RunAfters(statement, afters, null);
+    }
 
-	/**
-	 * Runs a {@link Statement} that represents a leaf (aka atomic) test.
-	 */
-	protected final void runLeaf(Statement statement, Description description,
-			RunNotifier notifier) {
-		EachTestNotifier eachNotifier= new EachTestNotifier(notifier, description);
-		eachNotifier.fireTestStarted();
-		try {
-		    statement.evaluate();
-		} catch (AssumptionViolatedException e) {
-			eachNotifier.addFailedAssumption(e);
-		} catch (Throwable e) {
-			eachNotifier.addFailure(e);
-		} finally {
-			eachNotifier.fireTestFinished();
-		}
-	}
-	
-	/**
-	 * @return the annotations that should be attached to this runner's 
-	 * description.
-	 */
-	protected Annotation[] getRunnerAnnotations() {
-		return fTestClass.getAnnotations();
-	}
-	
-	//
-	// Implementation of Runner
-	// 
-	
-	@Override
-	public Description getDescription() {
-		Description description= Description.createSuiteDescription(getName(),
-				getRunnerAnnotations());
-		for (T child : getFilteredChildren())
-			description.addChild(describeChild(child));
-		return description;
-	}
+    /**
+     * Returns a {@link Statement}: apply all
+     * static fields assignable to {@link TestRule}
+     * annotated with {@link ClassRule}.
+     *
+     * @param statement the base statement
+     * @return a RunRules statement if any class-level {@link Rule}s are
+     *         found, or the base statement
+     */
+    private Statement withClassRules(Statement statement) {
+        List<TestRule> classRules = classRules();
+        return classRules.isEmpty() ? statement :
+                new RunRules(statement, classRules, getDescription());
+    }
 
-	@Override
-	public void run(final RunNotifier notifier) {
-		EachTestNotifier testNotifier= new EachTestNotifier(notifier,
-				getDescription());
-		try {
-			Statement statement= classBlock(notifier);
-			statement.evaluate();
-		} catch (AssumptionViolatedException e) {
-			testNotifier.fireTestIgnored();
-		} catch (StoppedByUserException e) {
-			throw e;
-		} catch (Throwable e) {
-			testNotifier.addFailure(e);
-		}
-	}
+    /**
+     * @return the {@code ClassRule}s that can transform the block that runs
+     *         each method in the tested class.
+     */
+    protected List<TestRule> classRules() {
+        List<TestRule> result = testClass.getAnnotatedMethodValues(null, ClassRule.class, TestRule.class);
+        result.addAll(testClass.getAnnotatedFieldValues(null, ClassRule.class, TestRule.class));
+        return result;
+    }
 
-	//
-	// Implementation of Filterable and Sortable
-	//
+    /**
+     * Returns a {@link Statement}: Call {@link #runChild(Object, RunNotifier)}
+     * on each object returned by {@link #getChildren()} (subject to any imposed
+     * filter and sort)
+     */
+    protected Statement childrenInvoker(final RunNotifier notifier) {
+        return new Statement() {
+            @Override
+            public void evaluate() {
+                runChildren(notifier);
+            }
+        };
+    }
 
-	public void filter(Filter filter) throws NoTestsRemainException {
-		for (Iterator<T> iter = getFilteredChildren().iterator(); iter.hasNext(); ) {
-			T each = iter.next();
-			if (shouldRun(filter, each))
-				try {
-					filter.apply(each);
-				} catch (NoTestsRemainException e) {
-					iter.remove();
-				}
-			else
-				iter.remove();
-		}
-	    if (getFilteredChildren().isEmpty()) {
-	        throw new NoTestsRemainException();
-	    }
-	}
+    /**
+     * Evaluates whether a child is ignored. The default implementation always
+     * returns <code>false</code>.
+     * 
+     * <p>{@link BlockJUnit4ClassRunner}, for example, overrides this method to
+     * filter tests based on the {@link Ignore} annotation.
+     */
+    protected boolean isIgnored(T child) {
+        return false;
+    }
 
-	public void sort(Sorter sorter) {
-		fSorter= sorter;
-		for (T each : getFilteredChildren())
-			sortChild(each);
-		Collections.sort(getFilteredChildren(), comparator());
-	}
+    private void runChildren(final RunNotifier notifier) {
+        final RunnerScheduler currentScheduler = scheduler;
+        try {
+            for (final T each : getFilteredChildren()) {
+                currentScheduler.schedule(new Runnable() {
+                    public void run() {
+                        ParentRunner.this.runChild(each, notifier);
+                    }
+                });
+            }
+        } finally {
+            currentScheduler.finished();
+        }
+    }
 
-	//
-	// Private implementation
-	// 
+    /**
+     * Returns a name used to describe this Runner
+     */
+    protected String getName() {
+        return testClass.getName();
+    }
 
-	private void validate() throws InitializationError {
-		List<Throwable> errors= new ArrayList<Throwable>();
-		collectInitializationErrors(errors);
-		if (!errors.isEmpty())
-			throw new InitializationError(errors);
-	}
+    //
+    // Available for subclasses
+    //
 
-	private List<T> getFilteredChildren() {
-		if (fFilteredChildren == null)
-			fFilteredChildren = new ArrayList<T>(getChildren());
-		return fFilteredChildren;
-	}
+    /**
+     * Returns a {@link TestClass} object wrapping the class to be executed.
+     */
+    public final TestClass getTestClass() {
+        return testClass;
+    }
 
-	private void sortChild(T child) {
-		fSorter.apply(child);
-	}
+    /**
+     * Runs a {@link Statement} that represents a leaf (aka atomic) test.
+     */
+    protected final void runLeaf(Statement statement, Description description,
+            RunNotifier notifier) {
+        EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
+        eachNotifier.fireTestStarted();
+        try {
+            statement.evaluate();
+        } catch (AssumptionViolatedException e) {
+            eachNotifier.addFailedAssumption(e);
+        } catch (Throwable e) {
+            eachNotifier.addFailure(e);
+        } finally {
+            eachNotifier.fireTestFinished();
+        }
+    }
 
-	private boolean shouldRun(Filter filter, T each) {
-		return filter.shouldRun(describeChild(each));
-	}
+    /**
+     * @return the annotations that should be attached to this runner's
+     *         description.
+     */
+    protected Annotation[] getRunnerAnnotations() {
+        return testClass.getAnnotations();
+    }
 
-	private Comparator<? super T> comparator() {
-		return new Comparator<T>() {
-			public int compare(T o1, T o2) {
-				return fSorter.compare(describeChild(o1), describeChild(o2));
-			}
-		};
-	}
+    //
+    // Implementation of Runner
+    //
 
-	/**
-	 * Sets a scheduler that determines the order and parallelization
-	 * of children.  Highly experimental feature that may change.
-	 */
-	public void setScheduler(RunnerScheduler scheduler) {
-		this.fScheduler = scheduler;
-	}
+    @Override
+    public Description getDescription() {
+        Description description = Description.createSuiteDescription(getName(),
+                getRunnerAnnotations());
+        for (T child : getFilteredChildren()) {
+            description.addChild(describeChild(child));
+        }
+        return description;
+    }
+
+    @Override
+    public void run(final RunNotifier notifier) {
+        EachTestNotifier testNotifier = new EachTestNotifier(notifier,
+                getDescription());
+        try {
+            Statement statement = classBlock(notifier);
+            statement.evaluate();
+        } catch (AssumptionViolatedException e) {
+            testNotifier.addFailedAssumption(e);
+        } catch (StoppedByUserException e) {
+            throw e;
+        } catch (Throwable e) {
+            testNotifier.addFailure(e);
+        }
+    }
+
+    //
+    // Implementation of Filterable and Sortable
+    //
+
+    public void filter(Filter filter) throws NoTestsRemainException {
+        synchronized (childrenLock) {
+            List<T> children = new ArrayList<T>(getFilteredChildren());
+            for (Iterator<T> iter = children.iterator(); iter.hasNext(); ) {
+                T each = iter.next();
+                if (shouldRun(filter, each)) {
+                    try {
+                        filter.apply(each);
+                    } catch (NoTestsRemainException e) {
+                        iter.remove();
+                    }
+                } else {
+                    iter.remove();
+                }
+            }
+            filteredChildren = Collections.unmodifiableCollection(children);
+            if (filteredChildren.isEmpty()) {
+                throw new NoTestsRemainException();
+            }
+        }
+    }
+
+    public void sort(Sorter sorter) {
+        synchronized (childrenLock) {
+            for (T each : getFilteredChildren()) {
+                sorter.apply(each);
+            }
+            List<T> sortedChildren = new ArrayList<T>(getFilteredChildren());
+            Collections.sort(sortedChildren, comparator(sorter));
+            filteredChildren = Collections.unmodifiableCollection(sortedChildren);
+        }
+    }
+
+    //
+    // Private implementation
+    //
+
+    private void validate() throws InitializationError {
+        List<Throwable> errors = new ArrayList<Throwable>();
+        collectInitializationErrors(errors);
+        if (!errors.isEmpty()) {
+            throw new InitializationError(errors);
+        }
+    }
+
+    private Collection<T> getFilteredChildren() {
+        if (filteredChildren == null) {
+            synchronized (childrenLock) {
+                if (filteredChildren == null) {
+                    filteredChildren = Collections.unmodifiableCollection(getChildren());
+                }
+            }
+        }
+        return filteredChildren;
+    }
+
+    private boolean shouldRun(Filter filter, T each) {
+        return filter.shouldRun(describeChild(each));
+    }
+
+    private Comparator<? super T> comparator(final Sorter sorter) {
+        return new Comparator<T>() {
+            public int compare(T o1, T o2) {
+                return sorter.compare(describeChild(o1), describeChild(o2));
+            }
+        };
+    }
+
+    /**
+     * Sets a scheduler that determines the order and parallelization
+     * of children.  Highly experimental feature that may change.
+     */
+    public void setScheduler(RunnerScheduler scheduler) {
+        this.scheduler = scheduler;
+    }
 }
diff --git a/src/main/java/org/junit/runners/Suite.java b/src/main/java/org/junit/runners/Suite.java
index 1b3bb48..b37179f 100644
--- a/src/main/java/org/junit/runners/Suite.java
+++ b/src/main/java/org/junit/runners/Suite.java
@@ -5,6 +5,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.Collections;
 import java.util.List;
 
 import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
@@ -20,111 +21,110 @@
  * static {@link junit.framework.Test} <code>suite()</code> method. To use it, annotate a class
  * with <code>@RunWith(Suite.class)</code> and <code>@SuiteClasses({TestClass1.class, ...})</code>.
  * When you run this class, it will run all the tests in all the suite classes.
+ *
+ * @since 4.0
  */
 public class Suite extends ParentRunner<Runner> {
-	/**
-	 * Returns an empty suite.
-	 */
-	public static Runner emptySuite() {
-		try {
-			return new Suite((Class<?>)null, new Class<?>[0]);
-		} catch (InitializationError e) {
-			throw new RuntimeException("This shouldn't be possible");
-		}
-	}
-	
-	/**
-	 * The <code>SuiteClasses</code> annotation specifies the classes to be run when a class
-	 * annotated with <code>@RunWith(Suite.class)</code> is run.
-	 */
-	@Retention(RetentionPolicy.RUNTIME)
-	@Target(ElementType.TYPE)
-	@Inherited
-	public @interface SuiteClasses {
-		/**
-		 * @return the classes to be run
-		 */
-		public Class<?>[] value();
-	}
-	
-	private static Class<?>[] getAnnotatedClasses(Class<?> klass) throws InitializationError {
-		SuiteClasses annotation= klass.getAnnotation(SuiteClasses.class);
-		if (annotation == null)
-			throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName()));
-		return annotation.value();
-	}
+    /**
+     * Returns an empty suite.
+     */
+    public static Runner emptySuite() {
+        try {
+            return new Suite((Class<?>) null, new Class<?>[0]);
+        } catch (InitializationError e) {
+            throw new RuntimeException("This shouldn't be possible");
+        }
+    }
 
-	private final List<Runner> fRunners;
+    /**
+     * The <code>SuiteClasses</code> annotation specifies the classes to be run when a class
+     * annotated with <code>@RunWith(Suite.class)</code> is run.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.TYPE)
+    @Inherited
+    public @interface SuiteClasses {
+        /**
+         * @return the classes to be run
+         */
+        public Class<?>[] value();
+    }
 
-	/**
-	 * Called reflectively on classes annotated with <code>@RunWith(Suite.class)</code>
-	 * 
-	 * @param klass the root class
-	 * @param builder builds runners for classes in the suite
-	 * @throws InitializationError
-	 */
-	public Suite(Class<?> klass, RunnerBuilder builder) throws InitializationError {
-		this(builder, klass, getAnnotatedClasses(klass));
-	}
+    private static Class<?>[] getAnnotatedClasses(Class<?> klass) throws InitializationError {
+        SuiteClasses annotation = klass.getAnnotation(SuiteClasses.class);
+        if (annotation == null) {
+            throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName()));
+        }
+        return annotation.value();
+    }
 
-	/**
-	 * Call this when there is no single root class (for example, multiple class names
-	 * passed on the command line to {@link org.junit.runner.JUnitCore}
-	 * 
-	 * @param builder builds runners for classes in the suite
-	 * @param classes the classes in the suite
-	 * @throws InitializationError 
-	 */
-	public Suite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
-		this(null, builder.runners(null, classes));
-	}
-	
-	/**
-	 * Call this when the default builder is good enough. Left in for compatibility with JUnit 4.4.
-	 * @param klass the root of the suite
-	 * @param suiteClasses the classes in the suite
-	 * @throws InitializationError
-	 */
-	protected Suite(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
-		this(new AllDefaultPossibilitiesBuilder(true), klass, suiteClasses);
-	}
-	
-	/**
-	 * Called by this class and subclasses once the classes making up the suite have been determined
-	 * 
-	 * @param builder builds runners for classes in the suite
-	 * @param klass the root of the suite
-	 * @param suiteClasses the classes in the suite
-	 * @throws InitializationError
-	 */
-	protected Suite(RunnerBuilder builder, Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
-		this(klass, builder.runners(klass, suiteClasses));
-	}
-	
-	/**
-	 * Called by this class and subclasses once the runners making up the suite have been determined
-	 * 
-	 * @param klass root of the suite
-	 * @param runners for each class in the suite, a {@link Runner}
-	 * @throws InitializationError 
-	 */
-	protected Suite(Class<?> klass, List<Runner> runners) throws InitializationError {
-		super(klass);
-		fRunners = runners;
-	}
-	
-	@Override
-	protected List<Runner> getChildren() {
-		return fRunners;
-	}
-	
-	@Override
-	protected Description describeChild(Runner child) {
-		return child.getDescription();
-	}
+    private final List<Runner> runners;
 
-	@Override
-	protected void runChild(Runner runner, final RunNotifier notifier) {
-		runner.run(notifier);
-	}
+    /**
+     * Called reflectively on classes annotated with <code>@RunWith(Suite.class)</code>
+     *
+     * @param klass the root class
+     * @param builder builds runners for classes in the suite
+     */
+    public Suite(Class<?> klass, RunnerBuilder builder) throws InitializationError {
+        this(builder, klass, getAnnotatedClasses(klass));
+    }
+
+    /**
+     * Call this when there is no single root class (for example, multiple class names
+     * passed on the command line to {@link org.junit.runner.JUnitCore}
+     *
+     * @param builder builds runners for classes in the suite
+     * @param classes the classes in the suite
+     */
+    public Suite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
+        this(null, builder.runners(null, classes));
+    }
+
+    /**
+     * Call this when the default builder is good enough. Left in for compatibility with JUnit 4.4.
+     *
+     * @param klass the root of the suite
+     * @param suiteClasses the classes in the suite
+     */
+    protected Suite(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
+        this(new AllDefaultPossibilitiesBuilder(true), klass, suiteClasses);
+    }
+
+    /**
+     * Called by this class and subclasses once the classes making up the suite have been determined
+     *
+     * @param builder builds runners for classes in the suite
+     * @param klass the root of the suite
+     * @param suiteClasses the classes in the suite
+     */
+    protected Suite(RunnerBuilder builder, Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
+        this(klass, builder.runners(klass, suiteClasses));
+    }
+
+    /**
+     * Called by this class and subclasses once the runners making up the suite have been determined
+     *
+     * @param klass root of the suite
+     * @param runners for each class in the suite, a {@link Runner}
+     */
+    protected Suite(Class<?> klass, List<Runner> runners) throws InitializationError {
+        super(klass);
+        this.runners = Collections.unmodifiableList(runners);
+    }
+
+    @Override
+    protected List<Runner> getChildren() {
+        return runners;
+    }
+
+    @Override
+    protected Description describeChild(Runner child) {
+        return child.getDescription();
+    }
+
+    @Override
+    protected void runChild(Runner runner, final RunNotifier notifier) {
+        runner.run(notifier);
+    }
 }
diff --git a/src/main/java/org/junit/runners/model/Annotatable.java b/src/main/java/org/junit/runners/model/Annotatable.java
new file mode 100644
index 0000000..8eff6fd
--- /dev/null
+++ b/src/main/java/org/junit/runners/model/Annotatable.java
@@ -0,0 +1,20 @@
+package org.junit.runners.model;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * A model element that may have annotations.
+ * 
+ * @since 4.12
+ */
+public interface Annotatable {
+    /**
+     * Returns the model elements' annotations.
+     */
+    Annotation[] getAnnotations();
+
+    /**
+     * Returns the annotation on the model element of the given type, or @code{null}
+     */
+    <T extends Annotation> T getAnnotation(Class<T> annotationType);
+}
diff --git a/src/main/java/org/junit/runners/model/FrameworkField.java b/src/main/java/org/junit/runners/model/FrameworkField.java
index 4a4d4a4..945e389 100644
--- a/src/main/java/org/junit/runners/model/FrameworkField.java
+++ b/src/main/java/org/junit/runners/model/FrameworkField.java
@@ -2,64 +2,79 @@
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
 
 import org.junit.runners.BlockJUnit4ClassRunner;
 
 /**
  * Represents a field on a test class (currently used only for Rules in
  * {@link BlockJUnit4ClassRunner}, but custom runners can make other uses)
+ *
+ * @since 4.7
  */
 public class FrameworkField extends FrameworkMember<FrameworkField> {
-	private final Field fField;
+    private final Field field;
 
-	FrameworkField(Field field) {
-		fField= field;
-	}
+    FrameworkField(Field field) {
+        if (field == null) {
+            throw new NullPointerException(
+                    "FrameworkField cannot be created without an underlying field.");
+        }
+        this.field = field;
+    }
 
-	public String getName() {
-		return getField().getName();
-	}
+    @Override
+    public String getName() {
+        return getField().getName();
+    }
 
-	@Override
-	public Annotation[] getAnnotations() {
-		return fField.getAnnotations();
-	}
+    public Annotation[] getAnnotations() {
+        return field.getAnnotations();
+    }
 
-	public boolean isPublic() {
-		int modifiers= fField.getModifiers();
-		return Modifier.isPublic(modifiers);
-	}
+    public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+        return field.getAnnotation(annotationType);
+    }
 
-	@Override
-	public boolean isShadowedBy(FrameworkField otherMember) {
-		return otherMember.getName().equals(getName());
-	}
+    @Override
+    public boolean isShadowedBy(FrameworkField otherMember) {
+        return otherMember.getName().equals(getName());
+    }
 
-	public boolean isStatic() {
-		int modifiers= fField.getModifiers();
-		return Modifier.isStatic(modifiers);
-	}
+    @Override
+    protected int getModifiers() {
+        return field.getModifiers();
+    }
 
-	/**
-	 * @return the underlying java Field
-	 */
-	public Field getField() {
-		return fField;
-	}
+    /**
+     * @return the underlying java Field
+     */
+    public Field getField() {
+        return field;
+    }
 
-	/**
-	 * @return the underlying Java Field type
-	 * @see java.lang.reflect.Field#getType()
-	 */
-	public Class<?> getType() {
-		return fField.getType();
-	}
+    /**
+     * @return the underlying Java Field type
+     * @see java.lang.reflect.Field#getType()
+     */
+    @Override
+    public Class<?> getType() {
+        return field.getType();
+    }
+    
+    @Override
+    public Class<?> getDeclaringClass() {
+        return field.getDeclaringClass();
+    }
 
-	/**
-	 * Attempts to retrieve the value of this field on {@code target}
-	 */
-	public Object get(Object target) throws IllegalArgumentException, IllegalAccessException {
-		return fField.get(target);
-	}
+    /**
+     * Attempts to retrieve the value of this field on {@code target}
+     */
+    public Object get(Object target) throws IllegalArgumentException, IllegalAccessException {
+        return field.get(target);
+    }
+
+    @Override
+    public String toString() {
+        return field.toString();
+    }
 }
diff --git a/src/main/java/org/junit/runners/model/FrameworkMember.java b/src/main/java/org/junit/runners/model/FrameworkMember.java
index 9cccd4b..724f096 100644
--- a/src/main/java/org/junit/runners/model/FrameworkMember.java
+++ b/src/main/java/org/junit/runners/model/FrameworkMember.java
@@ -1,20 +1,45 @@
 package org.junit.runners.model;
 
-import java.lang.annotation.Annotation;
+import java.lang.reflect.Modifier;
 import java.util.List;
 
-abstract class FrameworkMember<T extends FrameworkMember<T>> {
-	/**
-	 * Returns the annotations on this method
-	 */
-	abstract Annotation[] getAnnotations();
+/**
+ * Parent class for {@link FrameworkField} and {@link FrameworkMethod}
+ *
+ * @since 4.7
+ */
+public abstract class FrameworkMember<T extends FrameworkMember<T>> implements
+        Annotatable {
+    abstract boolean isShadowedBy(T otherMember);
 
-	abstract boolean isShadowedBy(T otherMember);
+    boolean isShadowedBy(List<T> members) {
+        for (T each : members) {
+            if (isShadowedBy(each)) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-	boolean isShadowedBy(List<T> members) {
-		for (T each : members)
-			if (isShadowedBy(each))
-				return true;
-		return false;
-	}
+    protected abstract int getModifiers();
+
+    /**
+     * Returns true if this member is static, false if not.
+     */
+    public boolean isStatic() {
+        return Modifier.isStatic(getModifiers());
+    }
+
+    /**
+     * Returns true if this member is public, false if not.
+     */
+    public boolean isPublic() {
+        return Modifier.isPublic(getModifiers());
+    }
+
+    public abstract String getName();
+
+    public abstract Class<?> getType();
+
+    public abstract Class<?> getDeclaringClass();
 }
diff --git a/src/main/java/org/junit/runners/model/FrameworkMethod.java b/src/main/java/org/junit/runners/model/FrameworkMethod.java
index 81c8963..3580052 100644
--- a/src/main/java/org/junit/runners/model/FrameworkMethod.java
+++ b/src/main/java/org/junit/runners/model/FrameworkMethod.java
@@ -1,9 +1,8 @@
-package org.junit.runners.model; 
+package org.junit.runners.model;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.List;
 
@@ -12,145 +11,192 @@
 /**
  * Represents a method on a test class to be invoked at the appropriate point in
  * test execution. These methods are usually marked with an annotation (such as
- * {@code @Test}, {@code @Before}, {@code @After}, {@code @BeforeClass}, 
+ * {@code @Test}, {@code @Before}, {@code @After}, {@code @BeforeClass},
  * {@code @AfterClass}, etc.)
+ *
+ * @since 4.5
  */
 public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
-	final Method fMethod;
+    private final Method method;
 
-	/**
-	 * Returns a new {@code FrameworkMethod} for {@code method}
-	 */
-	public FrameworkMethod(Method method) {
-		fMethod= method;
-	}
+    /**
+     * Returns a new {@code FrameworkMethod} for {@code method}
+     */
+    public FrameworkMethod(Method method) {
+        if (method == null) {
+            throw new NullPointerException(
+                    "FrameworkMethod cannot be created without an underlying method.");
+        }
+        this.method = method;
+    }
 
-	/**
-	 * Returns the underlying Java method
-	 */
-	public Method getMethod() {
-		return fMethod;
-	}
+    /**
+     * Returns the underlying Java method
+     */
+    public Method getMethod() {
+        return method;
+    }
 
-	/**
-	 * Returns the result of invoking this method on {@code target} with
-	 * parameters {@code params}. {@link InvocationTargetException}s thrown are
-	 * unwrapped, and their causes rethrown.
-	 */
-	public Object invokeExplosively(final Object target, final Object... params)
-			throws Throwable {
-		return new ReflectiveCallable() {
-			@Override
-			protected Object runReflectiveCall() throws Throwable {
-				return fMethod.invoke(target, params);
-			}
-		}.run();
-	}
+    /**
+     * Returns the result of invoking this method on {@code target} with
+     * parameters {@code params}. {@link InvocationTargetException}s thrown are
+     * unwrapped, and their causes rethrown.
+     */
+    public Object invokeExplosively(final Object target, final Object... params)
+            throws Throwable {
+        return new ReflectiveCallable() {
+            @Override
+            protected Object runReflectiveCall() throws Throwable {
+                return method.invoke(target, params);
+            }
+        }.run();
+    }
 
-	/**
-	 * Returns the method's name
-	 */
-	public String getName() {
-		return fMethod.getName();
-	}
+    /**
+     * Returns the method's name
+     */
+    @Override
+    public String getName() {
+        return method.getName();
+    }
 
-	/**
-	 * Adds to {@code errors} if this method:
-	 * <ul>
-	 * <li>is not public, or
-	 * <li>takes parameters, or
-	 * <li>returns something other than void, or
-	 * <li>is static (given {@code isStatic is false}), or
-	 * <li>is not static (given {@code isStatic is true}).
-	 */
-	public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
-		validatePublicVoid(isStatic, errors);
-		if (fMethod.getParameterTypes().length != 0)
-			errors.add(new Exception("Method " + fMethod.getName() + " should have no parameters"));
-	}
+    /**
+     * Adds to {@code errors} if this method:
+     * <ul>
+     * <li>is not public, or
+     * <li>takes parameters, or
+     * <li>returns something other than void, or
+     * <li>is static (given {@code isStatic is false}), or
+     * <li>is not static (given {@code isStatic is true}).
+     * </ul>
+     */
+    public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
+        validatePublicVoid(isStatic, errors);
+        if (method.getParameterTypes().length != 0) {
+            errors.add(new Exception("Method " + method.getName() + " should have no parameters"));
+        }
+    }
 
 
-	/**
-	 * Adds to {@code errors} if this method:
-	 * <ul>
-	 * <li>is not public, or
-	 * <li>returns something other than void, or
-	 * <li>is static (given {@code isStatic is false}), or
-	 * <li>is not static (given {@code isStatic is true}).
-	 */
-	public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {
-		if (Modifier.isStatic(fMethod.getModifiers()) != isStatic) {
-			String state= isStatic ? "should" : "should not";
-			errors.add(new Exception("Method " + fMethod.getName() + "() " + state + " be static"));
-		}
-		if (!Modifier.isPublic(fMethod.getDeclaringClass().getModifiers()))
-			errors.add(new Exception("Class " + fMethod.getDeclaringClass().getName() + " should be public"));
-		if (!Modifier.isPublic(fMethod.getModifiers()))
-			errors.add(new Exception("Method " + fMethod.getName() + "() should be public"));
-		if (fMethod.getReturnType() != Void.TYPE)
-			errors.add(new Exception("Method " + fMethod.getName() + "() should be void"));
-	}
+    /**
+     * Adds to {@code errors} if this method:
+     * <ul>
+     * <li>is not public, or
+     * <li>returns something other than void, or
+     * <li>is static (given {@code isStatic is false}), or
+     * <li>is not static (given {@code isStatic is true}).
+     * </ul>
+     */
+    public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {
+        if (isStatic() != isStatic) {
+            String state = isStatic ? "should" : "should not";
+            errors.add(new Exception("Method " + method.getName() + "() " + state + " be static"));
+        }
+        if (!isPublic()) {
+            errors.add(new Exception("Method " + method.getName() + "() should be public"));
+        }
+        if (method.getReturnType() != Void.TYPE) {
+            errors.add(new Exception("Method " + method.getName() + "() should be void"));
+        }
+    }
 
-	public void validateNoTypeParametersOnArgs(List<Throwable> errors) {
-		new NoGenericTypeParametersValidator(fMethod).validate(errors);
-	}
+    @Override
+    protected int getModifiers() {
+        return method.getModifiers();
+    }
 
-	@Override
-	public boolean isShadowedBy(FrameworkMethod other) {
-		if (!other.getName().equals(getName()))
-			return false;
-		if (other.getParameterTypes().length != getParameterTypes().length)
-			return false;
-		for (int i= 0; i < other.getParameterTypes().length; i++)
-			if (!other.getParameterTypes()[i].equals(getParameterTypes()[i]))
-				return false;
-		return true;
-	}
+    /**
+     * Returns the return type of the method
+     */
+    public Class<?> getReturnType() {
+        return method.getReturnType();
+    }
 
-	@Override
-	public boolean equals(Object obj) {
-		if (!FrameworkMethod.class.isInstance(obj))
-			return false;
-		return ((FrameworkMethod) obj).fMethod.equals(fMethod);
-	}
+    /**
+     * Returns the return type of the method
+     */
+    @Override
+    public Class<?> getType() {
+        return getReturnType();
+    }
 
-	@Override
-	public int hashCode() {
-		return fMethod.hashCode();
-	}
+    /**
+     * Returns the class where the method is actually declared
+     */
+    @Override
+    public Class<?> getDeclaringClass() {
+        return method.getDeclaringClass();
+    }
 
-	/**
-	 * Returns true iff this is a no-arg method that returns a value assignable
-	 * to {@code type}
-	 *
-	 * @deprecated This is used only by the Theories runner, and does not
-	 * use all the generic type info that it ought to. It will be replaced
-	 * with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod)
-	 * once Theories moves to junit-contrib.
-	 */
-	@Deprecated
-	public boolean producesType(Type type) {
-		return getParameterTypes().length == 0 && type instanceof Class<?>
-		    && ((Class<?>) type).isAssignableFrom(fMethod.getReturnType());
-	}
+    public void validateNoTypeParametersOnArgs(List<Throwable> errors) {
+        new NoGenericTypeParametersValidator(method).validate(errors);
+    }
 
-	private Class<?>[] getParameterTypes() {
-		return fMethod.getParameterTypes();
-	}
+    @Override
+    public boolean isShadowedBy(FrameworkMethod other) {
+        if (!other.getName().equals(getName())) {
+            return false;
+        }
+        if (other.getParameterTypes().length != getParameterTypes().length) {
+            return false;
+        }
+        for (int i = 0; i < other.getParameterTypes().length; i++) {
+            if (!other.getParameterTypes()[i].equals(getParameterTypes()[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-	/**
-	 * Returns the annotations on this method
-	 */
-	@Override
-	public Annotation[] getAnnotations() {
-		return fMethod.getAnnotations();
-	}
+    @Override
+    public boolean equals(Object obj) {
+        if (!FrameworkMethod.class.isInstance(obj)) {
+            return false;
+        }
+        return ((FrameworkMethod) obj).method.equals(method);
+    }
 
-	/**
-	 * Returns the annotation of type {@code annotationType} on this method, if
-	 * one exists.
-	 */
-	public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
-		return fMethod.getAnnotation(annotationType);
-	}
+    @Override
+    public int hashCode() {
+        return method.hashCode();
+    }
+
+    /**
+     * Returns true if this is a no-arg method that returns a value assignable
+     * to {@code type}
+     *
+     * @deprecated This is used only by the Theories runner, and does not
+     *             use all the generic type info that it ought to. It will be replaced
+     *             with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod)
+     *             once Theories moves to junit-contrib.
+     */
+    @Deprecated
+    public boolean producesType(Type type) {
+        return getParameterTypes().length == 0 && type instanceof Class<?>
+                && ((Class<?>) type).isAssignableFrom(method.getReturnType());
+    }
+
+    private Class<?>[] getParameterTypes() {
+        return method.getParameterTypes();
+    }
+
+    /**
+     * Returns the annotations on this method
+     */
+    public Annotation[] getAnnotations() {
+        return method.getAnnotations();
+    }
+
+    /**
+     * Returns the annotation of type {@code annotationType} on this method, if
+     * one exists.
+     */
+    public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+        return method.getAnnotation(annotationType);
+    }
+
+    @Override
+    public String toString() {
+        return method.toString();
+    }
 }
diff --git a/src/main/java/org/junit/runners/model/InitializationError.java b/src/main/java/org/junit/runners/model/InitializationError.java
index 4de9ea7..841b565 100644
--- a/src/main/java/org/junit/runners/model/InitializationError.java
+++ b/src/main/java/org/junit/runners/model/InitializationError.java
@@ -5,35 +5,43 @@
 
 /**
  * Represents one or more problems encountered while initializing a Runner
+ *
+ * @since 4.5
  */
 public class InitializationError extends Exception {
-	private static final long serialVersionUID= 1L;
-	private final List<Throwable> fErrors;
+    private static final long serialVersionUID = 1L;
 
-	/**
-	 * Construct a new {@code InitializationError} with one or more
-	 * errors {@code errors} as causes
-	 */
-	public InitializationError(List<Throwable> errors) {
-		fErrors= errors;
-	}
-	
-	public InitializationError(Throwable error) {
-		this(Arrays.asList(error));
-	}
-	
-	/**
-	 * Construct a new {@code InitializationError} with one cause
-	 * with message {@code string}
-	 */
-	public InitializationError(String string) {
-		this(new Exception(string));
-	}
+    /*
+     * We have to use the f prefix until the next major release to ensure
+     * serialization compatibility. 
+     * See https://github.com/junit-team/junit/issues/976
+     */
+    private final List<Throwable> fErrors;
 
-	/**
-	 * Returns one or more Throwables that led to this initialization error.
-	 */
-	public List<Throwable> getCauses() {
-		return fErrors;
-	}
+    /**
+     * Construct a new {@code InitializationError} with one or more
+     * errors {@code errors} as causes
+     */
+    public InitializationError(List<Throwable> errors) {
+        this.fErrors = errors;
+    }
+
+    public InitializationError(Throwable error) {
+        this(Arrays.asList(error));
+    }
+
+    /**
+     * Construct a new {@code InitializationError} with one cause
+     * with message {@code string}
+     */
+    public InitializationError(String string) {
+        this(new Exception(string));
+    }
+
+    /**
+     * Returns one or more Throwables that led to this initialization error.
+     */
+    public List<Throwable> getCauses() {
+        return fErrors;
+    }
 }
diff --git a/src/main/java/org/junit/runners/model/MultipleFailureException.java b/src/main/java/org/junit/runners/model/MultipleFailureException.java
index 6d70ca0..325c645 100644
--- a/src/main/java/org/junit/runners/model/MultipleFailureException.java
+++ b/src/main/java/org/junit/runners/model/MultipleFailureException.java
@@ -1,60 +1,69 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-
 package org.junit.runners.model;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.junit.internal.Throwables;
+
 /**
  * Collects multiple {@code Throwable}s into one exception.
+ *
+ * @since 4.9
  */
 public class MultipleFailureException extends Exception {
-	private static final long serialVersionUID= 1L;
-	
-	private final List<Throwable> fErrors;
+    private static final long serialVersionUID = 1L;
 
-	public MultipleFailureException(List<Throwable> errors) {
-		fErrors= new ArrayList<Throwable>(errors);
-	}
+    /*
+     * We have to use the f prefix until the next major release to ensure
+     * serialization compatibility. 
+     * See https://github.com/junit-team/junit/issues/976
+     */
+    private final List<Throwable> fErrors;
 
-	public List<Throwable> getFailures() {
-		return Collections.unmodifiableList(fErrors);
-	}
+    public MultipleFailureException(List<Throwable> errors) {
+        this.fErrors = new ArrayList<Throwable>(errors);
+    }
 
-	@Override
-	public String getMessage() {
-		StringBuilder sb = new StringBuilder(
-				String.format("There were %d errors:", fErrors.size()));
-		for (Throwable e : fErrors) {
-			sb.append(String.format("\n  %s(%s)", e.getClass().getName(), e.getMessage()));
-		}
-		return sb.toString();
-	}
+    public List<Throwable> getFailures() {
+        return Collections.unmodifiableList(fErrors);
+    }
 
-	/**
-	 * Asserts that a list of throwables is empty. If it isn't empty,
-	 * will throw {@link MultipleFailureException} (if there are
-	 * multiple throwables in the list) or the first element in the list
-	 * (if there is only one element).
-	 * 
-	 * @param errors list to check
-	 * @throws Throwable if the list is not empty
-	 */
-	@SuppressWarnings("deprecation")
-	public static void assertEmpty(List<Throwable> errors) throws Throwable {
-		if (errors.isEmpty())
-			return;
-		if (errors.size() == 1)
-			throw errors.get(0);
+    @Override
+    public String getMessage() {
+        StringBuilder sb = new StringBuilder(
+                String.format("There were %d errors:", fErrors.size()));
+        for (Throwable e : fErrors) {
+            sb.append(String.format("\n  %s(%s)", e.getClass().getName(), e.getMessage()));
+        }
+        return sb.toString();
+    }
 
-		/*
-		 * Many places in the code are documented to throw
-		 * org.junit.internal.runners.model.MultipleFailureException.
-		 * That class now extends this one, so we throw the internal
-		 * exception in case developers have tests that catch
-		 * MultipleFailureException.
-		 */
-		throw new org.junit.internal.runners.model.MultipleFailureException(errors);
-	}
+    /**
+     * Asserts that a list of throwables is empty. If it isn't empty,
+     * will throw {@link MultipleFailureException} (if there are
+     * multiple throwables in the list) or the first element in the list
+     * (if there is only one element).
+     *
+     * @param errors list to check
+     * @throws Exception or Error if the list is not empty
+     */
+    @SuppressWarnings("deprecation")
+    public static void assertEmpty(List<Throwable> errors) throws Exception {
+        if (errors.isEmpty()) {
+            return;
+        }
+        if (errors.size() == 1) {
+            throw Throwables.rethrowAsException(errors.get(0));
+        }
+
+        /*
+           * Many places in the code are documented to throw
+           * org.junit.internal.runners.model.MultipleFailureException.
+           * That class now extends this one, so we throw the internal
+           * exception in case developers have tests that catch
+           * MultipleFailureException.
+           */
+        throw new org.junit.internal.runners.model.MultipleFailureException(errors);
+    }
 }
diff --git a/src/main/java/org/junit/runners/model/NoGenericTypeParametersValidator.java b/src/main/java/org/junit/runners/model/NoGenericTypeParametersValidator.java
index 77662b8..386b7ff 100644
--- a/src/main/java/org/junit/runners/model/NoGenericTypeParametersValidator.java
+++ b/src/main/java/org/junit/runners/model/NoGenericTypeParametersValidator.java
@@ -9,45 +9,50 @@
 import java.util.List;
 
 class NoGenericTypeParametersValidator {
-	private final Method fMethod;
+    private final Method method;
 
-	NoGenericTypeParametersValidator(Method method) {
-		this.fMethod = method;
-	}
+    NoGenericTypeParametersValidator(Method method) {
+        this.method = method;
+    }
 
-	void validate(List<Throwable> errors) {
-		for (Type each : fMethod.getGenericParameterTypes())
-			validateNoTypeParameterOnType(each, errors);
-	}
+    void validate(List<Throwable> errors) {
+        for (Type each : method.getGenericParameterTypes()) {
+            validateNoTypeParameterOnType(each, errors);
+        }
+    }
 
-	private void validateNoTypeParameterOnType(Type type, List<Throwable> errors) {
-		if (type instanceof TypeVariable<?>) {
-			errors.add(new Exception("Method " + fMethod.getName()
-					+ "() contains unresolved type variable " + type));
-		} else if (type instanceof ParameterizedType)
-			validateNoTypeParameterOnParameterizedType((ParameterizedType) type, errors);
-		else if (type instanceof WildcardType)
-			validateNoTypeParameterOnWildcardType((WildcardType) type, errors);
-		else if (type instanceof GenericArrayType)
-			validateNoTypeParameterOnGenericArrayType((GenericArrayType) type, errors);
-	}
+    private void validateNoTypeParameterOnType(Type type, List<Throwable> errors) {
+        if (type instanceof TypeVariable<?>) {
+            errors.add(new Exception("Method " + method.getName()
+                    + "() contains unresolved type variable " + type));
+        } else if (type instanceof ParameterizedType) {
+            validateNoTypeParameterOnParameterizedType((ParameterizedType) type, errors);
+        } else if (type instanceof WildcardType) {
+            validateNoTypeParameterOnWildcardType((WildcardType) type, errors);
+        } else if (type instanceof GenericArrayType) {
+            validateNoTypeParameterOnGenericArrayType((GenericArrayType) type, errors);
+        }
+    }
 
-	private void validateNoTypeParameterOnParameterizedType(ParameterizedType parameterized,
-			List<Throwable> errors) {
-		for (Type each : parameterized.getActualTypeArguments())
-			validateNoTypeParameterOnType(each, errors);
-	}
+    private void validateNoTypeParameterOnParameterizedType(ParameterizedType parameterized,
+            List<Throwable> errors) {
+        for (Type each : parameterized.getActualTypeArguments()) {
+            validateNoTypeParameterOnType(each, errors);
+        }
+    }
 
-	private void validateNoTypeParameterOnWildcardType(WildcardType wildcard,
-			List<Throwable> errors) {
-		for (Type each : wildcard.getUpperBounds())
-		    validateNoTypeParameterOnType(each, errors);
-		for (Type each : wildcard.getLowerBounds())
-		    validateNoTypeParameterOnType(each, errors);
-	}
+    private void validateNoTypeParameterOnWildcardType(WildcardType wildcard,
+            List<Throwable> errors) {
+        for (Type each : wildcard.getUpperBounds()) {
+            validateNoTypeParameterOnType(each, errors);
+        }
+        for (Type each : wildcard.getLowerBounds()) {
+            validateNoTypeParameterOnType(each, errors);
+        }
+    }
 
-	private void validateNoTypeParameterOnGenericArrayType(
-			GenericArrayType arrayType, List<Throwable> errors) {
-		validateNoTypeParameterOnType(arrayType.getGenericComponentType(), errors);
-	}
+    private void validateNoTypeParameterOnGenericArrayType(
+            GenericArrayType arrayType, List<Throwable> errors) {
+        validateNoTypeParameterOnType(arrayType.getGenericComponentType(), errors);
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/runners/model/RunnerBuilder.java b/src/main/java/org/junit/runners/model/RunnerBuilder.java
index 3a334be..7d3eee3 100644
--- a/src/main/java/org/junit/runners/model/RunnerBuilder.java
+++ b/src/main/java/org/junit/runners/model/RunnerBuilder.java
@@ -9,21 +9,21 @@
 import org.junit.runner.Runner;
 
 /**
- * A RunnerBuilder is a strategy for constructing runners for classes. 
- * 
+ * A RunnerBuilder is a strategy for constructing runners for classes.
+ *
  * Only writers of custom runners should use <code>RunnerBuilder</code>s.  A custom runner class with a constructor taking
- * a <code>RunnerBuilder</code> parameter will be passed the instance of <code>RunnerBuilder</code> used to build that runner itself.  
+ * a <code>RunnerBuilder</code> parameter will be passed the instance of <code>RunnerBuilder</code> used to build that runner itself.
  * For example,
  * imagine a custom runner that builds suites based on a list of classes in a text file:
- * 
+ *
  * <pre>
  * \@RunWith(TextFileSuite.class)
  * \@SuiteSpecFile("mysuite.txt")
  * class MySuite {}
  * </pre>
- * 
+ *
  * The implementation of TextFileSuite might include:
- * 
+ *
  * <pre>
  * public TextFileSuite(Class testClass, RunnerBuilder builder) {
  *   // ...
@@ -32,73 +32,77 @@
  *   // ...
  * }
  * </pre>
- * 
+ *
  * @see org.junit.runners.Suite
+ * @since 4.5
  */
 public abstract class RunnerBuilder {
-	private final Set<Class<?>> parents= new HashSet<Class<?>>();
+    private final Set<Class<?>> parents = new HashSet<Class<?>>();
 
-	/**
-	 * Override to calculate the correct runner for a test class at runtime.
-	 * 
-	 * @param testClass class to be run
-	 * @return a Runner
-	 * @throws Throwable if a runner cannot be constructed
-	 */
-	public abstract Runner runnerForClass(Class<?> testClass) throws Throwable;
+    /**
+     * Override to calculate the correct runner for a test class at runtime.
+     *
+     * @param testClass class to be run
+     * @return a Runner
+     * @throws Throwable if a runner cannot be constructed
+     */
+    public abstract Runner runnerForClass(Class<?> testClass) throws Throwable;
 
-	/**
-	 * Always returns a runner, even if it is just one that prints an error instead of running tests.
-	 * @param testClass class to be run
-	 * @return a Runner
-	 */
-	public Runner safeRunnerForClass(Class<?> testClass) {
-		try {
-			return runnerForClass(testClass);
-		} catch (Throwable e) {
-			return new ErrorReportingRunner(testClass, e);
-		}
-	}
+    /**
+     * Always returns a runner, even if it is just one that prints an error instead of running tests.
+     *
+     * @param testClass class to be run
+     * @return a Runner
+     */
+    public Runner safeRunnerForClass(Class<?> testClass) {
+        try {
+            return runnerForClass(testClass);
+        } catch (Throwable e) {
+            return new ErrorReportingRunner(testClass, e);
+        }
+    }
 
-	Class<?> addParent(Class<?> parent) throws InitializationError {
-		if (!parents.add(parent))
-			throw new InitializationError(String.format("class '%s' (possibly indirectly) contains itself as a SuiteClass", parent.getName()));
-		return parent;
-	}
+    Class<?> addParent(Class<?> parent) throws InitializationError {
+        if (!parents.add(parent)) {
+            throw new InitializationError(String.format("class '%s' (possibly indirectly) contains itself as a SuiteClass", parent.getName()));
+        }
+        return parent;
+    }
 
-	void removeParent(Class<?> klass) {
-		parents.remove(klass);
-	}
+    void removeParent(Class<?> klass) {
+        parents.remove(klass);
+    }
 
-	/**
-	 * Constructs and returns a list of Runners, one for each child class in
-	 * {@code children}.  Care is taken to avoid infinite recursion:
-	 * this builder will throw an exception if it is requested for another
-	 * runner for {@code parent} before this call completes.
-	 */
-	public List<Runner> runners(Class<?> parent, Class<?>[] children)
-			throws InitializationError {
-		addParent(parent);
+    /**
+     * Constructs and returns a list of Runners, one for each child class in
+     * {@code children}.  Care is taken to avoid infinite recursion:
+     * this builder will throw an exception if it is requested for another
+     * runner for {@code parent} before this call completes.
+     */
+    public List<Runner> runners(Class<?> parent, Class<?>[] children)
+            throws InitializationError {
+        addParent(parent);
 
-		try {
-			return runners(children);
-		} finally {
-			removeParent(parent);
-		}
-	}
-	
-	public List<Runner> runners(Class<?> parent, List<Class<?>> children)
-			throws InitializationError {
-		return runners(parent, children.toArray(new Class<?>[0]));
-	}
-	
-	private List<Runner> runners(Class<?>[] children) {
-		ArrayList<Runner> runners= new ArrayList<Runner>();
-		for (Class<?> each : children) {
-			Runner childRunner= safeRunnerForClass(each);
-			if (childRunner != null)
-				runners.add(childRunner);
-		}
-		return runners;
-	}
+        try {
+            return runners(children);
+        } finally {
+            removeParent(parent);
+        }
+    }
+
+    public List<Runner> runners(Class<?> parent, List<Class<?>> children)
+            throws InitializationError {
+        return runners(parent, children.toArray(new Class<?>[0]));
+    }
+
+    private List<Runner> runners(Class<?>[] children) {
+        ArrayList<Runner> runners = new ArrayList<Runner>();
+        for (Class<?> each : children) {
+            Runner childRunner = safeRunnerForClass(each);
+            if (childRunner != null) {
+                runners.add(childRunner);
+            }
+        }
+        return runners;
+    }
 }
diff --git a/src/main/java/org/junit/runners/model/RunnerScheduler.java b/src/main/java/org/junit/runners/model/RunnerScheduler.java
index fbc25a4..db43308 100644
--- a/src/main/java/org/junit/runners/model/RunnerScheduler.java
+++ b/src/main/java/org/junit/runners/model/RunnerScheduler.java
@@ -3,19 +3,21 @@
 /**
  * Represents a strategy for scheduling when individual test methods
  * should be run (in serial or parallel)
- * 
+ *
  * WARNING: still experimental, may go away.
+ *
+ * @since 4.7
  */
 public interface RunnerScheduler {
-	/**
-	 * Schedule a child statement to run
-	 */
-	void schedule(Runnable childStatement);
-	
-	/**
-	 * Override to implement any behavior that must occur
-	 * after all children have been scheduled (for example,
-	 * waiting for them all to finish)
-	 */
-	void finished();
+    /**
+     * Schedule a child statement to run
+     */
+    void schedule(Runnable childStatement);
+
+    /**
+     * Override to implement any behavior that must occur
+     * after all children have been scheduled (for example,
+     * waiting for them all to finish)
+     */
+    void finished();
 }
diff --git a/src/main/java/org/junit/runners/model/Statement.java b/src/main/java/org/junit/runners/model/Statement.java
index a7c5478..fa53fa1 100644
--- a/src/main/java/org/junit/runners/model/Statement.java
+++ b/src/main/java/org/junit/runners/model/Statement.java
@@ -1,16 +1,15 @@
-/**
- * 
- */
 package org.junit.runners.model;
 
 
 /**
  * Represents one or more actions to be taken at runtime in the course
  * of running a JUnit test suite.
+ *
+ * @since 4.5
  */
 public abstract class Statement {
-	/**
-	 * Run the action, throwing a {@code Throwable} if anything goes wrong.
-	 */
-	public abstract void evaluate() throws Throwable;
+    /**
+     * Run the action, throwing a {@code Throwable} if anything goes wrong.
+     */
+    public abstract void evaluate() throws Throwable;
 }
\ No newline at end of file
diff --git a/src/main/java/org/junit/runners/model/TestClass.java b/src/main/java/org/junit/runners/model/TestClass.java
old mode 100644
new mode 100755
index 362a13a..c8a544d
--- a/src/main/java/org/junit/runners/model/TestClass.java
+++ b/src/main/java/org/junit/runners/model/TestClass.java
@@ -1,159 +1,313 @@
 package org.junit.runners.model;
 
 import static java.lang.reflect.Modifier.isStatic;
+import static org.junit.internal.MethodSorter.NAME_ASCENDING;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.internal.MethodSorter;
 
 /**
  * Wraps a class to be run, providing method validation and annotation searching
+ *
+ * @since 4.5
  */
-public class TestClass {
-	private final Class<?> fClass;
+public class TestClass implements Annotatable {
+    private static final FieldComparator FIELD_COMPARATOR = new FieldComparator();
+    private static final MethodComparator METHOD_COMPARATOR = new MethodComparator();
 
-	private Map<Class<?>, List<FrameworkMethod>> fMethodsForAnnotations= new HashMap<Class<?>, List<FrameworkMethod>>();
+    private final Class<?> clazz;
+    private final Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations;
+    private final Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations;
 
-	private Map<Class<?>, List<FrameworkField>> fFieldsForAnnotations= new HashMap<Class<?>, List<FrameworkField>>();
+    /**
+     * Creates a {@code TestClass} wrapping {@code clazz}. Each time this
+     * constructor executes, the class is scanned for annotations, which can be
+     * an expensive process (we hope in future JDK's it will not be.) Therefore,
+     * try to share instances of {@code TestClass} where possible.
+     */
+    public TestClass(Class<?> clazz) {
+        this.clazz = clazz;
+        if (clazz != null && clazz.getConstructors().length > 1) {
+            throw new IllegalArgumentException(
+                    "Test class can only have one constructor");
+        }
 
-	/**
-	 * Creates a {@code TestClass} wrapping {@code klass}. Each time this
-	 * constructor executes, the class is scanned for annotations, which can be
-	 * an expensive process (we hope in future JDK's it will not be.) Therefore,
-	 * try to share instances of {@code TestClass} where possible.
-	 */
-	public TestClass(Class<?> klass) {
-		fClass= klass;
-		if (klass != null && klass.getConstructors().length > 1)
-			throw new IllegalArgumentException(
-					"Test class can only have one constructor");
+        Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations =
+                new LinkedHashMap<Class<? extends Annotation>, List<FrameworkMethod>>();
+        Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations =
+                new LinkedHashMap<Class<? extends Annotation>, List<FrameworkField>>();
 
-		for (Class<?> eachClass : getSuperClasses(fClass)) {
-			for (Method eachMethod : eachClass.getDeclaredMethods())
-				addToAnnotationLists(new FrameworkMethod(eachMethod),
-						fMethodsForAnnotations);
-			for (Field eachField : eachClass.getDeclaredFields())
-				addToAnnotationLists(new FrameworkField(eachField),
-						fFieldsForAnnotations);
-		}
-	}
+        scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);
 
-	private <T extends FrameworkMember<T>> void addToAnnotationLists(T member,
-			Map<Class<?>, List<T>> map) {
-		for (Annotation each : member.getAnnotations()) {
-			Class<? extends Annotation> type= each.annotationType();
-			List<T> members= getAnnotatedMembers(map, type);
-			if (member.isShadowedBy(members))
-				return;
-			if (runsTopToBottom(type))
-				members.add(0, member);
-			else
-				members.add(member);
-		}
-	}
+        this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);
+        this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations);
+    }
 
-	/**
-	 * Returns, efficiently, all the non-overridden methods in this class and
-	 * its superclasses that are annotated with {@code annotationClass}.
-	 */
-	public List<FrameworkMethod> getAnnotatedMethods(
-			Class<? extends Annotation> annotationClass) {
-		return getAnnotatedMembers(fMethodsForAnnotations, annotationClass);
-	}
+    protected void scanAnnotatedMembers(Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations, Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations) {
+        for (Class<?> eachClass : getSuperClasses(clazz)) {
+            for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) {
+                addToAnnotationLists(new FrameworkMethod(eachMethod), methodsForAnnotations);
+            }
+            // ensuring fields are sorted to make sure that entries are inserted
+            // and read from fieldForAnnotations in a deterministic order
+            for (Field eachField : getSortedDeclaredFields(eachClass)) {
+                addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations);
+            }
+        }
+    }
 
-	/**
-	 * Returns, efficiently, all the non-overridden fields in this class and its
-	 * superclasses that are annotated with {@code annotationClass}.
-	 */
-	public List<FrameworkField> getAnnotatedFields(
-			Class<? extends Annotation> annotationClass) {
-		return getAnnotatedMembers(fFieldsForAnnotations, annotationClass);
-	}
+    private static Field[] getSortedDeclaredFields(Class<?> clazz) {
+        Field[] declaredFields = clazz.getDeclaredFields();
+        Arrays.sort(declaredFields, FIELD_COMPARATOR);
+        return declaredFields;
+    }
 
-	private <T> List<T> getAnnotatedMembers(Map<Class<?>, List<T>> map,
-			Class<? extends Annotation> type) {
-		if (!map.containsKey(type))
-			map.put(type, new ArrayList<T>());
-		return map.get(type);
-	}
+    protected static <T extends FrameworkMember<T>> void addToAnnotationLists(T member,
+            Map<Class<? extends Annotation>, List<T>> map) {
+        for (Annotation each : member.getAnnotations()) {
+            Class<? extends Annotation> type = each.annotationType();
+            List<T> members = getAnnotatedMembers(map, type, true);
+            if (member.isShadowedBy(members)) {
+                return;
+            }
+            if (runsTopToBottom(type)) {
+                members.add(0, member);
+            } else {
+                members.add(member);
+            }
+        }
+    }
 
-	private boolean runsTopToBottom(Class<? extends Annotation> annotation) {
-		return annotation.equals(Before.class)
-				|| annotation.equals(BeforeClass.class);
-	}
+    private static <T extends FrameworkMember<T>> Map<Class<? extends Annotation>, List<T>>
+            makeDeeplyUnmodifiable(Map<Class<? extends Annotation>, List<T>> source) {
+        LinkedHashMap<Class<? extends Annotation>, List<T>> copy =
+                new LinkedHashMap<Class<? extends Annotation>, List<T>>();
+        for (Map.Entry<Class<? extends Annotation>, List<T>> entry : source.entrySet()) {
+            copy.put(entry.getKey(), Collections.unmodifiableList(entry.getValue()));
+        }
+        return Collections.unmodifiableMap(copy);
+    }
 
-	private List<Class<?>> getSuperClasses(Class<?> testClass) {
-		ArrayList<Class<?>> results= new ArrayList<Class<?>>();
-		Class<?> current= testClass;
-		while (current != null) {
-			results.add(current);
-			current= current.getSuperclass();
-		}
-		return results;
-	}
+    /**
+     * Returns, efficiently, all the non-overridden methods in this class and
+     * its superclasses that are annotated}.
+     * 
+     * @since 4.12
+     */
+    public List<FrameworkMethod> getAnnotatedMethods() {
+        List<FrameworkMethod> methods = collectValues(methodsForAnnotations);
+        Collections.sort(methods, METHOD_COMPARATOR);
+        return methods;
+    }
 
-	/**
-	 * Returns the underlying Java class.
-	 */
-	public Class<?> getJavaClass() {
-		return fClass;
-	}
+    /**
+     * Returns, efficiently, all the non-overridden methods in this class and
+     * its superclasses that are annotated with {@code annotationClass}.
+     */
+    public List<FrameworkMethod> getAnnotatedMethods(
+            Class<? extends Annotation> annotationClass) {
+        return Collections.unmodifiableList(getAnnotatedMembers(methodsForAnnotations, annotationClass, false));
+    }
 
-	/**
-	 * Returns the class's name.
-	 */
-	public String getName() {
-		if (fClass == null)
-			return "null";
-		return fClass.getName();
-	}
+    /**
+     * Returns, efficiently, all the non-overridden fields in this class and its
+     * superclasses that are annotated.
+     * 
+     * @since 4.12
+     */
+    public List<FrameworkField> getAnnotatedFields() {
+        return collectValues(fieldsForAnnotations);
+    }
 
-	/**
-	 * Returns the only public constructor in the class, or throws an {@code
-	 * AssertionError} if there are more or less than one.
-	 */
+    /**
+     * Returns, efficiently, all the non-overridden fields in this class and its
+     * superclasses that are annotated with {@code annotationClass}.
+     */
+    public List<FrameworkField> getAnnotatedFields(
+            Class<? extends Annotation> annotationClass) {
+        return Collections.unmodifiableList(getAnnotatedMembers(fieldsForAnnotations, annotationClass, false));
+    }
 
-	public Constructor<?> getOnlyConstructor() {
-		Constructor<?>[] constructors= fClass.getConstructors();
-		Assert.assertEquals(1, constructors.length);
-		return constructors[0];
-	}
+    private <T> List<T> collectValues(Map<?, List<T>> map) {
+        Set<T> values = new LinkedHashSet<T>();
+        for (List<T> additionalValues : map.values()) {
+            values.addAll(additionalValues);
+        }
+        return new ArrayList<T>(values);
+    }
 
-	/**
-	 * Returns the annotations on this class
-	 */
-	public Annotation[] getAnnotations() {
-		if (fClass == null)
-			return new Annotation[0];
-		return fClass.getAnnotations();
-	}
+    private static <T> List<T> getAnnotatedMembers(Map<Class<? extends Annotation>, List<T>> map,
+            Class<? extends Annotation> type, boolean fillIfAbsent) {
+        if (!map.containsKey(type) && fillIfAbsent) {
+            map.put(type, new ArrayList<T>());
+        }
+        List<T> members = map.get(type);
+        return members == null ? Collections.<T>emptyList() : members;
+    }
 
-	public <T> List<T> getAnnotatedFieldValues(Object test,
-			Class<? extends Annotation> annotationClass, Class<T> valueClass) {
-		List<T> results= new ArrayList<T>();
-		for (FrameworkField each : getAnnotatedFields(annotationClass)) {
-			try {
-				Object fieldValue= each.get(test);
-				if (valueClass.isInstance(fieldValue))
-					results.add(valueClass.cast(fieldValue));
-			} catch (IllegalAccessException e) {
-				throw new RuntimeException(
-						"How did getFields return a field we couldn't access?", e);
-			}
-		}
-		return results;
-	}
+    private static boolean runsTopToBottom(Class<? extends Annotation> annotation) {
+        return annotation.equals(Before.class)
+                || annotation.equals(BeforeClass.class);
+    }
 
-	public boolean isANonStaticInnerClass() {
-		return fClass.isMemberClass() && !isStatic(fClass.getModifiers());
-	}
+    private static List<Class<?>> getSuperClasses(Class<?> testClass) {
+        ArrayList<Class<?>> results = new ArrayList<Class<?>>();
+        Class<?> current = testClass;
+        while (current != null) {
+            results.add(current);
+            current = current.getSuperclass();
+        }
+        return results;
+    }
+
+    /**
+     * Returns the underlying Java class.
+     */
+    public Class<?> getJavaClass() {
+        return clazz;
+    }
+
+    /**
+     * Returns the class's name.
+     */
+    public String getName() {
+        if (clazz == null) {
+            return "null";
+        }
+        return clazz.getName();
+    }
+
+    /**
+     * Returns the only public constructor in the class, or throws an {@code
+     * AssertionError} if there are more or less than one.
+     */
+
+    public Constructor<?> getOnlyConstructor() {
+        Constructor<?>[] constructors = clazz.getConstructors();
+        Assert.assertEquals(1, constructors.length);
+        return constructors[0];
+    }
+
+    /**
+     * Returns the annotations on this class
+     */
+    public Annotation[] getAnnotations() {
+        if (clazz == null) {
+            return new Annotation[0];
+        }
+        return clazz.getAnnotations();
+    }
+
+    public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+        if (clazz == null) {
+            return null;
+        }
+        return clazz.getAnnotation(annotationType);
+    }
+
+    public <T> List<T> getAnnotatedFieldValues(Object test,
+            Class<? extends Annotation> annotationClass, Class<T> valueClass) {
+        List<T> results = new ArrayList<T>();
+        for (FrameworkField each : getAnnotatedFields(annotationClass)) {
+            try {
+                Object fieldValue = each.get(test);
+                if (valueClass.isInstance(fieldValue)) {
+                    results.add(valueClass.cast(fieldValue));
+                }
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(
+                        "How did getFields return a field we couldn't access?", e);
+            }
+        }
+        return results;
+    }
+
+    public <T> List<T> getAnnotatedMethodValues(Object test,
+            Class<? extends Annotation> annotationClass, Class<T> valueClass) {
+        List<T> results = new ArrayList<T>();
+        for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) {
+            try {
+                /*
+                 * A method annotated with @Rule may return a @TestRule or a @MethodRule,
+                 * we cannot call the method to check whether the return type matches our
+                 * expectation i.e. subclass of valueClass. If we do that then the method 
+                 * will be invoked twice and we do not want to do that. So we first check
+                 * whether return type matches our expectation and only then call the method
+                 * to fetch the MethodRule
+                 */
+                if (valueClass.isAssignableFrom(each.getReturnType())) {
+                    Object fieldValue = each.invokeExplosively(test);
+                    results.add(valueClass.cast(fieldValue));
+                }
+            } catch (Throwable e) {
+                throw new RuntimeException(
+                        "Exception in " + each.getName(), e);
+            }
+        }
+        return results;
+    }
+
+    public boolean isPublic() {
+        return Modifier.isPublic(clazz.getModifiers());
+    }
+
+    public boolean isANonStaticInnerClass() {
+        return clazz.isMemberClass() && !isStatic(clazz.getModifiers());
+    }
+
+    @Override
+    public int hashCode() {
+        return (clazz == null) ? 0 : clazz.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TestClass other = (TestClass) obj;
+        return clazz == other.clazz;
+    }
+
+    /**
+     * Compares two fields by its name.
+     */
+    private static class FieldComparator implements Comparator<Field> {
+        public int compare(Field left, Field right) {
+            return left.getName().compareTo(right.getName());
+        }
+    }
+
+    /**
+     * Compares two methods by its name.
+     */
+    private static class MethodComparator implements
+            Comparator<FrameworkMethod> {
+        public int compare(FrameworkMethod left, FrameworkMethod right) {
+            return NAME_ASCENDING.compare(left.getMethod(), right.getMethod());
+        }
+    }
 }
diff --git a/src/main/java/org/junit/runners/model/TestTimedOutException.java b/src/main/java/org/junit/runners/model/TestTimedOutException.java
new file mode 100644
index 0000000..60e1a8a
--- /dev/null
+++ b/src/main/java/org/junit/runners/model/TestTimedOutException.java
@@ -0,0 +1,44 @@
+package org.junit.runners.model;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Exception thrown when a test fails on timeout.
+ * 
+ * @since 4.12
+ * 
+ */
+public class TestTimedOutException extends Exception {
+
+    private static final long serialVersionUID = 31935685163547539L;
+
+    private final TimeUnit timeUnit;
+    private final long timeout;
+
+    /**
+     * Creates exception with a standard message "test timed out after [timeout] [timeUnit]"
+     * 
+     * @param timeout the amount of time passed before the test was interrupted
+     * @param timeUnit the time unit for the timeout value
+     */
+    public TestTimedOutException(long timeout, TimeUnit timeUnit) {
+        super(String.format("test timed out after %d %s", 
+                timeout, timeUnit.name().toLowerCase()));
+        this.timeUnit = timeUnit;
+        this.timeout = timeout;
+    }
+
+    /**
+     * Gets the time passed before the test was interrupted
+     */
+    public long getTimeout() {
+        return timeout;
+    }
+
+    /**
+     * Gets the time unit for the timeout value
+     */
+    public TimeUnit getTimeUnit() {
+        return timeUnit;
+    }
+}
diff --git a/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java
new file mode 100644
index 0000000..1c49f84
--- /dev/null
+++ b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java
@@ -0,0 +1,143 @@
+package org.junit.runners.parameterized;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.List;
+
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.model.FrameworkField;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+
+/**
+ * A {@link BlockJUnit4ClassRunner} with parameters support. Parameters can be
+ * injected via constructor or into annotated fields.
+ */
+public class BlockJUnit4ClassRunnerWithParameters extends
+        BlockJUnit4ClassRunner {
+    private final Object[] parameters;
+
+    private final String name;
+
+    public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test)
+            throws InitializationError {
+        super(test.getTestClass().getJavaClass());
+        parameters = test.getParameters().toArray(
+                new Object[test.getParameters().size()]);
+        name = test.getName();
+    }
+
+    @Override
+    public Object createTest() throws Exception {
+        if (fieldsAreAnnotated()) {
+            return createTestUsingFieldInjection();
+        } else {
+            return createTestUsingConstructorInjection();
+        }
+    }
+
+    private Object createTestUsingConstructorInjection() throws Exception {
+        return getTestClass().getOnlyConstructor().newInstance(parameters);
+    }
+
+    private Object createTestUsingFieldInjection() throws Exception {
+        List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
+        if (annotatedFieldsByParameter.size() != parameters.length) {
+            throw new Exception(
+                    "Wrong number of parameters and @Parameter fields."
+                            + " @Parameter fields counted: "
+                            + annotatedFieldsByParameter.size()
+                            + ", available parameters: " + parameters.length
+                            + ".");
+        }
+        Object testClassInstance = getTestClass().getJavaClass().newInstance();
+        for (FrameworkField each : annotatedFieldsByParameter) {
+            Field field = each.getField();
+            Parameter annotation = field.getAnnotation(Parameter.class);
+            int index = annotation.value();
+            try {
+                field.set(testClassInstance, parameters[index]);
+            } catch (IllegalArgumentException iare) {
+                throw new Exception(getTestClass().getName()
+                        + ": Trying to set " + field.getName()
+                        + " with the value " + parameters[index]
+                        + " that is not the right type ("
+                        + parameters[index].getClass().getSimpleName()
+                        + " instead of " + field.getType().getSimpleName()
+                        + ").", iare);
+            }
+        }
+        return testClassInstance;
+    }
+
+    @Override
+    protected String getName() {
+        return name;
+    }
+
+    @Override
+    protected String testName(FrameworkMethod method) {
+        return method.getName() + getName();
+    }
+
+    @Override
+    protected void validateConstructor(List<Throwable> errors) {
+        validateOnlyOneConstructor(errors);
+        if (fieldsAreAnnotated()) {
+            validateZeroArgConstructor(errors);
+        }
+    }
+
+    @Override
+    protected void validateFields(List<Throwable> errors) {
+        super.validateFields(errors);
+        if (fieldsAreAnnotated()) {
+            List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
+            int[] usedIndices = new int[annotatedFieldsByParameter.size()];
+            for (FrameworkField each : annotatedFieldsByParameter) {
+                int index = each.getField().getAnnotation(Parameter.class)
+                        .value();
+                if (index < 0 || index > annotatedFieldsByParameter.size() - 1) {
+                    errors.add(new Exception("Invalid @Parameter value: "
+                            + index + ". @Parameter fields counted: "
+                            + annotatedFieldsByParameter.size()
+                            + ". Please use an index between 0 and "
+                            + (annotatedFieldsByParameter.size() - 1) + "."));
+                } else {
+                    usedIndices[index]++;
+                }
+            }
+            for (int index = 0; index < usedIndices.length; index++) {
+                int numberOfUse = usedIndices[index];
+                if (numberOfUse == 0) {
+                    errors.add(new Exception("@Parameter(" + index
+                            + ") is never used."));
+                } else if (numberOfUse > 1) {
+                    errors.add(new Exception("@Parameter(" + index
+                            + ") is used more than once (" + numberOfUse + ")."));
+                }
+            }
+        }
+    }
+
+    @Override
+    protected Statement classBlock(RunNotifier notifier) {
+        return childrenInvoker(notifier);
+    }
+
+    @Override
+    protected Annotation[] getRunnerAnnotations() {
+        return new Annotation[0];
+    }
+
+    private List<FrameworkField> getAnnotatedFieldsByParameter() {
+        return getTestClass().getAnnotatedFields(Parameter.class);
+    }
+
+    private boolean fieldsAreAnnotated() {
+        return !getAnnotatedFieldsByParameter().isEmpty();
+    }
+}
diff --git a/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParametersFactory.java b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParametersFactory.java
new file mode 100644
index 0000000..ae49ef4
--- /dev/null
+++ b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParametersFactory.java
@@ -0,0 +1,18 @@
+package org.junit.runners.parameterized;
+
+import org.junit.runner.Runner;
+import org.junit.runners.model.InitializationError;
+
+/**
+ * A {@link ParametersRunnerFactory} that creates
+ * {@link BlockJUnit4ClassRunnerWithParameters}.
+ * 
+ * @since 4.12
+ */
+public class BlockJUnit4ClassRunnerWithParametersFactory implements
+        ParametersRunnerFactory {
+    public Runner createRunnerForTestWithParameters(TestWithParameters test)
+            throws InitializationError {
+        return new BlockJUnit4ClassRunnerWithParameters(test);
+    }
+}
diff --git a/src/main/java/org/junit/runners/parameterized/ParametersRunnerFactory.java b/src/main/java/org/junit/runners/parameterized/ParametersRunnerFactory.java
new file mode 100644
index 0000000..16ea1f3
--- /dev/null
+++ b/src/main/java/org/junit/runners/parameterized/ParametersRunnerFactory.java
@@ -0,0 +1,21 @@
+package org.junit.runners.parameterized;
+
+import org.junit.runner.Runner;
+import org.junit.runners.model.InitializationError;
+
+/**
+ * A {@code ParameterizedRunnerFactory} creates a runner for a single
+ * {@link TestWithParameters}.
+ * 
+ * @since 4.12
+ */
+public interface ParametersRunnerFactory {
+    /**
+     * Returns a runner for the specified {@link TestWithParameters}.
+     * 
+     * @throws InitializationError
+     *             if the runner could not be created.
+     */
+    Runner createRunnerForTestWithParameters(TestWithParameters test)
+            throws InitializationError;
+}
diff --git a/src/main/java/org/junit/runners/parameterized/TestWithParameters.java b/src/main/java/org/junit/runners/parameterized/TestWithParameters.java
new file mode 100644
index 0000000..1b86644
--- /dev/null
+++ b/src/main/java/org/junit/runners/parameterized/TestWithParameters.java
@@ -0,0 +1,82 @@
+package org.junit.runners.parameterized;
+
+import static java.util.Collections.unmodifiableList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.runners.model.TestClass;
+
+/**
+ * A {@code TestWithParameters} keeps the data together that are needed for
+ * creating a runner for a single data set of a parameterized test. It has a
+ * name, the test class and a list of parameters.
+ * 
+ * @since 4.12
+ */
+public class TestWithParameters {
+    private final String name;
+
+    private final TestClass testClass;
+
+    private final List<Object> parameters;
+
+    public TestWithParameters(String name, TestClass testClass,
+            List<Object> parameters) {
+        notNull(name, "The name is missing.");
+        notNull(testClass, "The test class is missing.");
+        notNull(parameters, "The parameters are missing.");
+        this.name = name;
+        this.testClass = testClass;
+        this.parameters = unmodifiableList(new ArrayList<Object>(parameters));
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TestClass getTestClass() {
+        return testClass;
+    }
+
+    public List<Object> getParameters() {
+        return parameters;
+    }
+
+    @Override
+    public int hashCode() {
+        int prime = 14747;
+        int result = prime + name.hashCode();
+        result = prime * result + testClass.hashCode();
+        return prime * result + parameters.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TestWithParameters other = (TestWithParameters) obj;
+        return name.equals(other.name)
+                && parameters.equals(other.parameters)
+                && testClass.equals(other.testClass);
+    }
+
+    @Override
+    public String toString() {
+        return testClass.getName() + " '" + name + "' with parameters "
+                + parameters;
+    }
+
+    private static void notNull(Object value, String message) {
+        if (value == null) {
+            throw new NullPointerException(message);
+        }
+    }
+}
diff --git a/src/main/java/org/junit/validator/AnnotationValidator.java b/src/main/java/org/junit/validator/AnnotationValidator.java
new file mode 100644
index 0000000..8a53adf
--- /dev/null
+++ b/src/main/java/org/junit/validator/AnnotationValidator.java
@@ -0,0 +1,60 @@
+package org.junit.validator;
+
+import org.junit.runners.model.FrameworkField;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.TestClass;
+
+import static java.util.Collections.emptyList;
+
+import java.util.List;
+
+/**
+ * Validates annotations on classes and methods. To be validated,
+ * an annotation should be annotated with {@link ValidateWith}
+ *
+ * Instances of this class are shared by multiple test runners, so they should
+ * be immutable and thread-safe.
+ *
+ * @since 4.12
+ */
+public abstract class AnnotationValidator {
+
+    private static final List<Exception> NO_VALIDATION_ERRORS = emptyList();
+
+    /**
+     * Validates annotation on the given class.
+     *
+     * @param testClass that is being validated
+     * @return A list of exceptions. Default behavior is to return an empty list.
+     *
+     * @since 4.12
+     */
+    public List<Exception> validateAnnotatedClass(TestClass testClass) {
+        return NO_VALIDATION_ERRORS;
+    }
+
+    /**
+     * Validates annotation on the given field.
+     *
+     * @param field that is being validated
+     * @return A list of exceptions. Default behavior is to return an empty list.
+     *
+     * @since 4.12
+     */
+    public List<Exception> validateAnnotatedField(FrameworkField field) {
+        return NO_VALIDATION_ERRORS;
+
+    }
+
+    /**
+     * Validates annotation on the given method.
+     *
+     * @param method that is being validated
+     * @return A list of exceptions. Default behavior is to return an empty list.
+     *
+     * @since 4.12
+     */
+    public List<Exception> validateAnnotatedMethod(FrameworkMethod method) {
+        return NO_VALIDATION_ERRORS;
+    }
+}
diff --git a/src/main/java/org/junit/validator/AnnotationValidatorFactory.java b/src/main/java/org/junit/validator/AnnotationValidatorFactory.java
new file mode 100644
index 0000000..7309fdd
--- /dev/null
+++ b/src/main/java/org/junit/validator/AnnotationValidatorFactory.java
@@ -0,0 +1,42 @@
+package org.junit.validator;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Creates instances of Annotation Validators.
+ *
+ * @since 4.12
+ */
+public class AnnotationValidatorFactory {
+    private static final ConcurrentHashMap<ValidateWith, AnnotationValidator> VALIDATORS_FOR_ANNOTATION_TYPES =
+            new ConcurrentHashMap<ValidateWith, AnnotationValidator>();
+
+    /**
+     * Creates the AnnotationValidator specified by the value in
+     * {@link org.junit.validator.ValidateWith}. Instances are
+     * cached.
+     *
+     * @return An instance of the AnnotationValidator.
+     *
+     * @since 4.12
+     */
+    public AnnotationValidator createAnnotationValidator(ValidateWith validateWithAnnotation) {
+        AnnotationValidator validator = VALIDATORS_FOR_ANNOTATION_TYPES.get(validateWithAnnotation);
+        if (validator != null) {
+            return validator;
+        }
+
+        Class<? extends AnnotationValidator> clazz = validateWithAnnotation.value();
+        if (clazz == null) {
+            throw new IllegalArgumentException("Can't create validator, value is null in annotation " + validateWithAnnotation.getClass().getName());
+        }
+        try {
+            AnnotationValidator annotationValidator = clazz.newInstance();
+            VALIDATORS_FOR_ANNOTATION_TYPES.putIfAbsent(validateWithAnnotation, annotationValidator);
+            return VALIDATORS_FOR_ANNOTATION_TYPES.get(validateWithAnnotation);
+        } catch (Exception e) {
+            throw new RuntimeException("Exception received when creating AnnotationValidator class " + clazz.getName(), e);
+        }
+    }
+
+}
diff --git a/src/main/java/org/junit/validator/AnnotationsValidator.java b/src/main/java/org/junit/validator/AnnotationsValidator.java
new file mode 100644
index 0000000..30f54a6
--- /dev/null
+++ b/src/main/java/org/junit/validator/AnnotationsValidator.java
@@ -0,0 +1,120 @@
+package org.junit.validator;
+
+import static java.util.Collections.singletonList;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.runners.model.Annotatable;
+import org.junit.runners.model.FrameworkField;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.TestClass;
+
+/**
+ * An {@code AnnotationsValidator} validates all annotations of a test class,
+ * including its annotated fields and methods.
+ * 
+ * @since 4.12
+ */
+public final class AnnotationsValidator implements TestClassValidator {
+    private static final List<AnnotatableValidator<?>> VALIDATORS = Arrays.<AnnotatableValidator<?>>asList(
+            new ClassValidator(), new MethodValidator(), new FieldValidator());
+
+    /**
+     * Validate all annotations of the specified test class that are be
+     * annotated with {@link ValidateWith}.
+     * 
+     * @param testClass
+     *            the {@link TestClass} that is validated.
+     * @return the errors found by the validator.
+     */
+    public List<Exception> validateTestClass(TestClass testClass) {
+        List<Exception> validationErrors= new ArrayList<Exception>();
+        for (AnnotatableValidator<?> validator : VALIDATORS) {
+            List<Exception> additionalErrors= validator
+                    .validateTestClass(testClass);
+            validationErrors.addAll(additionalErrors);
+        }
+        return validationErrors;
+    }
+
+    private static abstract class AnnotatableValidator<T extends Annotatable> {
+        private static final AnnotationValidatorFactory ANNOTATION_VALIDATOR_FACTORY = new AnnotationValidatorFactory();
+
+        abstract Iterable<T> getAnnotatablesForTestClass(TestClass testClass);
+
+        abstract List<Exception> validateAnnotatable(
+                AnnotationValidator validator, T annotatable);
+
+        public List<Exception> validateTestClass(TestClass testClass) {
+            List<Exception> validationErrors= new ArrayList<Exception>();
+            for (T annotatable : getAnnotatablesForTestClass(testClass)) {
+                List<Exception> additionalErrors= validateAnnotatable(annotatable);
+                validationErrors.addAll(additionalErrors);
+            }
+            return validationErrors;
+        }
+
+        private List<Exception> validateAnnotatable(T annotatable) {
+            List<Exception> validationErrors= new ArrayList<Exception>();
+            for (Annotation annotation : annotatable.getAnnotations()) {
+                Class<? extends Annotation> annotationType = annotation
+                        .annotationType();
+                ValidateWith validateWith = annotationType
+                        .getAnnotation(ValidateWith.class);
+                if (validateWith != null) {
+                    AnnotationValidator annotationValidator = ANNOTATION_VALIDATOR_FACTORY
+                            .createAnnotationValidator(validateWith);
+                    List<Exception> errors= validateAnnotatable(
+                            annotationValidator, annotatable);
+                    validationErrors.addAll(errors);
+                }
+            }
+            return validationErrors;
+        }
+    }
+
+    private static class ClassValidator extends AnnotatableValidator<TestClass> {
+        @Override
+        Iterable<TestClass> getAnnotatablesForTestClass(TestClass testClass) {
+            return singletonList(testClass);
+        }
+
+        @Override
+        List<Exception> validateAnnotatable(
+                AnnotationValidator validator, TestClass testClass) {
+            return validator.validateAnnotatedClass(testClass);
+        }
+    }
+
+    private static class MethodValidator extends
+            AnnotatableValidator<FrameworkMethod> {
+        @Override
+        Iterable<FrameworkMethod> getAnnotatablesForTestClass(
+                TestClass testClass) {
+            return testClass.getAnnotatedMethods();
+        }
+
+        @Override
+        List<Exception> validateAnnotatable(
+                AnnotationValidator validator, FrameworkMethod method) {
+            return validator.validateAnnotatedMethod(method);
+        }
+    }
+
+    private static class FieldValidator extends
+            AnnotatableValidator<FrameworkField> {
+        @Override
+        Iterable<FrameworkField> getAnnotatablesForTestClass(TestClass testClass) {
+            return testClass.getAnnotatedFields();
+        }
+
+        @Override
+        List<Exception> validateAnnotatable(
+                AnnotationValidator validator, FrameworkField field) {
+            return validator.validateAnnotatedField(field);
+        }
+    };
+}
diff --git a/src/main/java/org/junit/validator/PublicClassValidator.java b/src/main/java/org/junit/validator/PublicClassValidator.java
new file mode 100644
index 0000000..fe3f185
--- /dev/null
+++ b/src/main/java/org/junit/validator/PublicClassValidator.java
@@ -0,0 +1,33 @@
+package org.junit.validator;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+
+import java.util.List;
+
+import org.junit.runners.model.TestClass;
+
+/**
+ * Validates that a {@link TestClass} is public.
+ * 
+ * @since 4.12
+ */
+public class PublicClassValidator implements TestClassValidator {
+    private static final List<Exception> NO_VALIDATION_ERRORS = emptyList();
+
+    /**
+     * Validate that the specified {@link TestClass} is public.
+     * 
+     * @param testClass the {@link TestClass} that is validated.
+     * @return an empty list if the class is public or a list with a single
+     *         exception otherwise.
+     */
+    public List<Exception> validateTestClass(TestClass testClass) {
+        if (testClass.isPublic()) {
+            return NO_VALIDATION_ERRORS;
+        } else {
+            return singletonList(new Exception("The class "
+                    + testClass.getName() + " is not public."));
+        }
+    }
+}
diff --git a/src/main/java/org/junit/validator/TestClassValidator.java b/src/main/java/org/junit/validator/TestClassValidator.java
new file mode 100644
index 0000000..43cb787
--- /dev/null
+++ b/src/main/java/org/junit/validator/TestClassValidator.java
@@ -0,0 +1,21 @@
+package org.junit.validator;
+
+import java.util.List;
+
+import org.junit.runners.model.TestClass;
+
+/**
+ * Validates a single facet of a test class.
+ * 
+ * @since 4.12
+ */
+public interface TestClassValidator {
+    /**
+     * Validate a single facet of a test class.
+     * 
+     * @param testClass
+     *            the {@link TestClass} that is validated.
+     * @return the validation errors found by the validator.
+     */
+    public List<Exception> validateTestClass(TestClass testClass);
+}
diff --git a/src/main/java/org/junit/validator/ValidateWith.java b/src/main/java/org/junit/validator/ValidateWith.java
new file mode 100644
index 0000000..03d7906
--- /dev/null
+++ b/src/main/java/org/junit/validator/ValidateWith.java
@@ -0,0 +1,19 @@
+package org.junit.validator;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Allows for an {@link AnnotationValidator} to be attached to an annotation.
+ *
+ * <p>When attached to an annotation, the validator will be instantiated and invoked
+ * by the {@link org.junit.runners.ParentRunner}.</p>
+ *
+ * @since 4.12
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ValidateWith {
+    Class<? extends AnnotationValidator> value();
+}