package org.robolectric;

import android.app.Application;

import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;
import org.robolectric.annotation.Config;
import org.robolectric.internal.SdkConfig;
import org.robolectric.internal.bytecode.InstrumentationConfiguration;
import org.robolectric.internal.SdkEnvironment;
import org.robolectric.manifest.AndroidManifest;
import org.robolectric.res.FsFile;
import org.robolectric.util.Transcript;

import java.lang.reflect.Method;

import static org.assertj.core.api.Assertions.fail;
import static org.junit.Assert.assertTrue;
import static org.robolectric.util.TestUtil.resourceFile;

public class TestRunnerSequenceTest {
  public static class StateHolder {
    public static Transcript transcript;
  }

  @Test public void shouldRunThingsInTheRightOrder() throws Exception {
    StateHolder.transcript = new Transcript();
    assertNoFailures(run(new Runner(SimpleTest.class)));
    StateHolder.transcript.assertEventsSoFar(
        "configureShadows",
//                "resetStaticState", // no longer an overridable hook
//                "setupApplicationState", // no longer an overridable hook
        "createApplication",
        "application.onCreate",
        "beforeTest",
        "application.beforeTest",
        "prepareTest",
        "application.prepareTest",
        "TEST!",
        "application.onTerminate",
        "afterTest",
        "application.afterTest"
    );
  }

  @Test public void whenNoAppManifest_shouldRunThingsInTheRightOrder() throws Exception {
    StateHolder.transcript = new Transcript();
    assertNoFailures(run(new Runner(SimpleTest.class) {
      @Override protected AndroidManifest getAppManifest(Config config) {
        return new AndroidManifest(null, null, null, "package") {
          @Override
          public int getTargetSdkVersion() {
            return SdkConfig.FALLBACK_SDK_VERSION;
          }
        };
      }
    }));
    StateHolder.transcript.assertEventsSoFar(
        "configureShadows",
        "createApplication",
        "application.onCreate",
        "beforeTest",
        "application.beforeTest",
        "prepareTest",
        "application.prepareTest",
        "TEST!",
        "application.onTerminate",
        "afterTest",
        "application.afterTest"
    );
  }

  @Test public void shouldReleaseAllStateAfterClassSoWeDontLeakMemory() throws Exception {
    RobolectricTestRunner robolectricTestRunner = new Runner(SimpleTest.class);
    robolectricTestRunner.run(new RunNotifier());
    assertTrue(robolectricTestRunner.allStateIsCleared());
  }

  public static class SimpleTest {
    @Test public void shouldDoNothingMuch() throws Exception {
      StateHolder.transcript.add("TEST!");
    }
  }

  private Result run(Runner runner) throws InitializationError {
    RunNotifier notifier = new RunNotifier();
    Result result = new Result();
    notifier.addListener(result.createListener());
    runner.run(notifier);
    return result;
  }

  private void assertNoFailures(Result result) {
    if (!result.wasSuccessful()) {
      for (Failure failure : result.getFailures()) {
        fail(failure.getMessage(), failure.getException());
      }
    }
  }

  public static class Runner extends RobolectricTestRunner {
    public Runner(Class<?> testClass) throws InitializationError {
      super(testClass);
    }

    @NotNull
    @Override public InstrumentationConfiguration createClassLoaderConfig(Config config) {
      return InstrumentationConfiguration.newBuilder()
          .doNotAcquireClass(StateHolder.class.getName())
          .build();
    }

    @Override
    protected AndroidManifest getAppManifest(Config config) {
      return new AndroidManifest(resourceFile("TestAndroidManifest.xml"), resourceFile("res"), resourceFile("assets"));
    }

    @NotNull
    @Override protected Class<? extends TestLifecycle> getTestLifecycleClass() {
      return MyTestLifecycle.class;
    }

    @Override protected void configureShadows(SdkEnvironment sdkEnvironment, Config config) {
      StateHolder.transcript.add("configureShadows");
      super.configureShadows(sdkEnvironment, config);
    }
  }

  public static class MyTestLifecycle extends DefaultTestLifecycle {
    @Override public Application createApplication(Method method, AndroidManifest appManifest, Config config) {
      StateHolder.transcript.add("createApplication");
      return new MyApplication();
    }

    @Override public void beforeTest(Method method) {
      StateHolder.transcript.add("beforeTest");
      super.beforeTest(method);
    }

    @Override public void prepareTest(Object test) {
      StateHolder.transcript.add("prepareTest");
      super.prepareTest(test);
    }

    @Override public void afterTest(Method method) {
      StateHolder.transcript.add("afterTest");
      super.afterTest(method);
    }

    private static class MyApplication extends Application implements TestLifecycleApplication {
      @Override public void onCreate() {
        StateHolder.transcript.add("application.onCreate");
      }

      @Override public void beforeTest(Method method) {
        StateHolder.transcript.add("application.beforeTest");
      }

      @Override public void prepareTest(Object test) {
        StateHolder.transcript.add("application.prepareTest");
      }

      @Override public void afterTest(Method method) {
        StateHolder.transcript.add("application.afterTest");
      }

      @Override public void onTerminate() {
        StateHolder.transcript.add("application.onTerminate");
      }
    }
  }
}

@@ -51,17 +51,16 @@
 cation;%0A
-%0A
 import o
@@ -417,93 +417,93 @@
 nal.
-bytecode.InstrumentationConfiguration;%0Aimport org.robolectric.internal.SdkEnvironment
+SdkEnvironment;%0Aimport org.robolectric.internal.bytecode.InstrumentationConfiguration
 ;%0Aim
@@ -553,43 +553,8 @@
 st;%0A
-import org.robolectric.res.FsFile;%0A
 impo
@@ -623,16 +623,153 @@
 Method;%0A
+import java.util.Properties;%0A%0Aimport static android.os.Build.VERSION_CODES.JELLY_BEAN;%0Aimport static java.util.Collections.singletonList;
 %0Aimport 
@@ -3629,32 +3629,198 @@
 tClass);%0A    %7D%0A%0A
+    @NotNull%0A    @Override%0A    protected SdkPicker createSdkPicker() %7B%0A      return new SdkPicker(singletonList(new SdkConfig(JELLY_BEAN)), new Properties());%0A    %7D%0A%0A
     @NotNull%0A   

Fix TestRunnerSequenceTest.