/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.idea.tests.gui.framework; import com.android.tools.idea.tests.gui.framework.annotation.IdeGuiTest; import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.lang.UrlClassLoader; import org.junit.After; import org.junit.Before; import org.junit.internal.runners.model.ReflectiveCallable; import org.junit.internal.runners.statements.Fail; import org.junit.internal.runners.statements.RunAfters; import org.junit.internal.runners.statements.RunBefores; import org.junit.runners.BlockJUnit4ClassRunner; 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 java.awt.*; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.List; import static org.fest.assertions.Assertions.assertThat; import static org.fest.reflect.core.Reflection.method; public class GuiTestRunner extends BlockJUnit4ClassRunner { private Class<? extends Annotation> myBeforeClass; private Class<? extends Annotation> myAfterClass; private TestClass myTestClass; public GuiTestRunner(Class<?> testClass) throws InitializationError { super(testClass); try { // A random class which is reachable from module community-main's classpath but not // module android's classpath Class.forName("git4idea.repo.GitConfig"); } catch (ClassNotFoundException e) { throw new InitializationError("Invalid test run configuration. Edit your test configuration and make sure that " + "\"Use classpath of module\" is set to \"community-main\", *NOT* \"android\"!"); } } @Override protected Statement methodBlock(FrameworkMethod method) { if (!canRunGuiTests()) { Class<?> testClass = getTestClass().getJavaClass(); Logger logger = Logger.getInstance(testClass); logger.info("Skipping GUI test " + testClass.getCanonicalName() + " due to headless environment"); return super.methodBlock(method); } FrameworkMethod newMethod; try { loadClassesWithIdeClassLoader(); Method methodFromClassLoader = myTestClass.getJavaClass().getMethod(method.getName()); newMethod = new FrameworkMethod(methodFromClassLoader); } catch (Exception e) { return new Fail(e); } Object test; try { test = new ReflectiveCallable() { @Override protected Object runReflectiveCall() throws Throwable { return createTest(); } }.run(); } catch (Throwable e) { return new Fail(e); } Statement statement = methodInvoker(newMethod, test); List<FrameworkMethod> beforeMethods = myTestClass.getAnnotatedMethods(myBeforeClass); if (!beforeMethods.isEmpty()) { statement = new RunBefores(statement, beforeMethods, test); } List<FrameworkMethod> afterMethods = myTestClass.getAnnotatedMethods(myAfterClass); if (!afterMethods.isEmpty()) { statement = new RunAfters(statement, afterMethods, test); } return statement; } public static boolean canRunGuiTests() { return !GraphicsEnvironment.isHeadless(); } @SuppressWarnings("unchecked") private void loadClassesWithIdeClassLoader() throws Exception { UrlClassLoader ideClassLoader = IdeTestApplication.getInstance().getIdeClassLoader(); Thread.currentThread().setContextClassLoader(ideClassLoader); Class<?> testClass = getTestClass().getJavaClass(); myTestClass = new TestClass(ideClassLoader.loadClass(testClass.getName())); myBeforeClass = (Class<? extends Annotation>)ideClassLoader.loadClass(Before.class.getName()); myAfterClass = (Class<? extends Annotation>)ideClassLoader.loadClass(After.class.getName()); } @Override protected Object createTest() throws Exception { return myTestClass != null ? myTestClass.getJavaClass().newInstance() : super.createTest(); } @Override protected Statement methodInvoker(FrameworkMethod method, Object test) { if (canRunGuiTests()) { try { UrlClassLoader ideClassLoader = IdeTestApplication.getInstance().getIdeClassLoader(); //noinspection unchecked Class<? extends Annotation> ideGuiTestClass = (Class<? extends Annotation>)ideClassLoader.loadClass(IdeGuiTest.class.getCanonicalName()); Annotation annotation = method.getMethod().getAnnotation(ideGuiTestClass); if (annotation != null && Proxy.isProxyClass(annotation.getClass())) { InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation); Method closeProjectBeforeExecutionMethod = ideGuiTestClass.getDeclaredMethod("closeProjectBeforeExecution"); Object result = invocationHandler.invoke(annotation, closeProjectBeforeExecutionMethod, new Object[0]); assertThat(result).isInstanceOfAny(Boolean.class, boolean.class); if ((Boolean)result) { method("closeAllProjects").in(test).invoke(); } } } catch (Throwable e) { return new Fail(e); } } return new MethodInvoker(method, test); } }