/*
* 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.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.Suite;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;
import org.junit.runners.model.Statement;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.tools.idea.tests.gui.framework.GuiTests.GUI_TESTS_RUNNING_IN_SUITE_PROPERTY;
import static com.intellij.openapi.util.io.FileUtil.notNullize;
/**
* Test runner that automatically includes all test classes that extend {@link GuiTestCase}.
*/
public class GuiTestSuiteRunner extends Suite {
public GuiTestSuiteRunner(Class<?> suiteClass, RunnerBuilder builder) throws InitializationError {
super(builder, suiteClass, getGuiTestClasses(suiteClass));
System.setProperty(GUI_TESTS_RUNNING_IN_SUITE_PROPERTY, "true");
}
@NotNull
private static Class<?>[] getGuiTestClasses(@NotNull Class<?> suiteClass) throws InitializationError {
List<File> guiTestClassFiles = Lists.newArrayList();
File parentDir = getParentDir(suiteClass);
String packagePath = suiteClass.getPackage().getName().replace('.', File.separatorChar);
int packagePathIndex = parentDir.getPath().indexOf(packagePath);
assert packagePathIndex > -1;
String testDirPath = parentDir.getPath().substring(0, packagePathIndex);
findPotentialGuiTestClassFiles(parentDir, guiTestClassFiles);
List<Class<?>> guiTestClasses = Lists.newArrayList();
ClassLoader classLoader = suiteClass.getClassLoader();
for (File classFile : guiTestClassFiles) {
String path = classFile.getPath();
String className = path.substring(testDirPath.length(), path.indexOf(DOT_CLASS)).replace(File.separatorChar, '.');
try {
Class<?> testClass = classLoader.loadClass(className);
if (GuiTestCase.class.isAssignableFrom(testClass)) {
guiTestClasses.add(testClass);
}
}
catch (ClassNotFoundException e) {
throw new InitializationError(e);
}
}
return guiTestClasses.toArray(new Class<?>[guiTestClasses.size()]);
}
private static void findPotentialGuiTestClassFiles(@NotNull File directory, @NotNull List<File> guiTestClassFiles) {
File[] children = notNullize(directory.listFiles());
for (File child : children) {
if (child.isDirectory()) {
findPotentialGuiTestClassFiles(child, guiTestClassFiles);
continue;
}
if (child.isFile() && !child.isHidden() && child.getName().endsWith("Test.class")) {
guiTestClassFiles.add(child);
}
}
}
@NotNull
private static File getParentDir(@NotNull Class<?> clazz) throws InitializationError {
URL classUrl = clazz.getResource(clazz.getSimpleName() + DOT_CLASS);
try {
return new File(classUrl.toURI()).getParentFile();
}
catch (URISyntaxException e) {
throw new InitializationError(e);
}
}
@Override
@NotNull
protected Statement childrenInvoker(final RunNotifier notifier) {
return new Statement() {
@Override
public void evaluate() {
// Run all the tests and dispose IdeTestApplication at the end.
for (final Runner child : getChildren()) {
runChild(child, notifier);
}
IdeTestApplication.disposeInstance();
}
};
}
}