package org.junit.rules;

import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.junit.experimental.results.PrintableResult.testResult;
import static org.junit.experimental.results.ResultMatchers.isSuccessful;
import static org.junit.internal.matchers.ThrowableCauseMatcher.hasCause;

import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import org.hamcrest.CoreMatchers;
import org.hamcrest.Matcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.TestCouldNotBeSkippedException;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.statements.Fail;
import org.junit.runner.Description;
import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;

public class ExternalResourceRuleTest {
    private static String callSequence;

    public static class UsesExternalResource {
        @Rule
        public ExternalResource resource = new ExternalResource() {
            @Override
            protected void before() throws Throwable {
                callSequence += "before ";
            }

            ;

            @Override
            protected void after() {
                callSequence += "after ";
            }

            ;
        };

        @Test
        public void testFoo() {
            callSequence += "test ";
        }
    }

    @Test
    public void externalResourceGeneratesCorrectSequence() {
        callSequence = "";
        assertThat(testResult(UsesExternalResource.class), isSuccessful());
        assertEquals("before test after ", callSequence);
    }

    @Test
    public void shouldThrowMultipleFailureExceptionWhenTestFailsAndClosingResourceFails() throws Throwable {
        // given
        ExternalResource resourceRule = new ExternalResource() {
            @Override
            protected void after() {
                throw new RuntimeException("simulating resource tear down failure");
            }
        };
        Statement failingTest = new Fail(new RuntimeException("simulated test failure"));
        Description dummyDescription = Description.createTestDescription(
                "dummy test class name", "dummy test name");

        try {
            resourceRule.apply(failingTest, dummyDescription).evaluate();
            fail("ExternalResource should throw");
        } catch (MultipleFailureException e) {
            assertThat(e.getMessage(), allOf(
                    containsString("simulated test failure"),
                    containsString("simulating resource tear down failure")
            ));
        }
    }

    @Test
    public void shouldWrapAssumptionFailuresWhenClosingResourceFails() throws Throwable {
        // given
        final AtomicReference<Throwable> externalResourceException = new AtomicReference<Throwable>();
        ExternalResource resourceRule = new ExternalResource() {
            @Override
            protected void after() {
                RuntimeException runtimeException = new RuntimeException("simulating resource tear down failure");
                externalResourceException.set(runtimeException);
                throw runtimeException;
            }
        };
        final AtomicReference<Throwable> assumptionViolatedException = new AtomicReference<Throwable>();
        Statement skippedTest = new Statement() {
            @Override
            public void evaluate() throws Throwable {
                AssumptionViolatedException assumptionFailure = new AssumptionViolatedException("skip it");
                assumptionViolatedException.set(assumptionFailure);
                throw assumptionFailure;
            }
        };
        Description dummyDescription = Description.createTestDescription(
                "dummy test class name", "dummy test name");

        try {
            resourceRule.apply(skippedTest, dummyDescription).evaluate();
            fail("ExternalResource should throw");
        } catch (MultipleFailureException e) {
            assertThat(e.getFailures(), hasItems(
                    instanceOf(TestCouldNotBeSkippedException.class),
                    sameInstance(externalResourceException.get())
            ));
            assertThat(e.getFailures(), hasItems(
                    hasCause(sameInstance(assumptionViolatedException.get())),
                    sameInstance(externalResourceException.get())
            ));
        }
    }

    @SuppressWarnings("unchecked")
    private Matcher<? super List<Throwable>> hasItems(
            Matcher<? super Throwable> one, Matcher<? super Throwable> two) {
        return CoreMatchers.hasItems(one, two);
    }
}

@@ -581,24 +581,52 @@
 .hasCause;%0A%0A
+import java.util.ArrayList;%0A
 import java.
@@ -986,24 +986,137 @@
 escription;%0A
+import org.junit.runner.JUnitCore;%0Aimport org.junit.runner.Result;%0Aimport org.junit.runner.notification.Failure;%0A
 import org.j
@@ -3035,32 +3035,1389 @@
        %7D%0A    %7D%0A%0A
+    public static class TestFailsAndTwoClosingResourcesFail %7B%0A        @Rule%0A        public ExternalResource resourceRule1 = new ExternalResource() %7B%0A            @Override%0A            protected void after() %7B%0A                throw new RuntimeException(%22simulating resource1 tear down failure%22);%0A            %7D%0A        %7D;%0A%0A        @Rule%0A        public ExternalResource resourceRule2 = new ExternalResource() %7B%0A            @Override%0A            protected void after() %7B%0A                throw new RuntimeException(%22simulating resource2 tear down failure%22);%0A            %7D%0A        %7D;%0A%0A        @Test%0A        public void failingTest() %7B%0A            throw new RuntimeException(%22simulated test failure%22);%0A        %7D%0A    %7D%0A%0A    @Test%0A    public void shouldThrowMultipleFailureExceptionWhenTestFailsAndTwoClosingResourcesFail() %7B%0A        Result result = JUnitCore.runClasses(TestFailsAndTwoClosingResourcesFail.class);%0A        assertEquals(3, result.getFailures().size());%0A        List%3CString%3E messages = new ArrayList%3CString%3E();%0A        for (Failure failure : result.getFailures()) %7B%0A            messages.add(failure.getMessage());%0A        %7D%0A        assertThat(messages, CoreMatchers.hasItems(%0A                %22simulated test failure%22,%0A                %22simulating resource1 tear down failure%22,%0A                %22simulating resource2 tear down failure%22%0A        ));%0A    %7D%0A%0A
     @Test%0A    pu

Test for failures in ExternalResouce.after() when multiple rules (#1452)