/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.it.internal;
import com.google.inject.Module;
import io.nuun.kernel.api.plugin.InitState;
import io.nuun.kernel.api.plugin.PluginException;
import io.nuun.kernel.api.plugin.context.InitContext;
import io.nuun.kernel.api.plugin.request.ClasspathScanRequest;
import org.kametic.specifications.Specification;
import org.seedstack.seed.SeedException;
import org.seedstack.seed.core.SeedRuntime;
import org.seedstack.seed.core.internal.AbstractSeedPlugin;
import org.seedstack.seed.it.ITBind;
import org.seedstack.seed.it.ITInstall;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.nio.file.Files;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* This plugin automatically enable integration tests to be managed by SEED.
*/
public class ITPlugin extends AbstractSeedPlugin {
public static final String IT_CLASS_NAME = "seedstack.it.className";
private static final Logger LOGGER = LoggerFactory.getLogger(ITPlugin.class);
private final Set<Class<?>> itBindClasses = new HashSet<>();
private final Set<Class<? extends Module>> itInstallModules = new HashSet<>();
private File temporaryAppStorage;
private Class<?> testClass;
@SuppressWarnings("unchecked")
private final Specification<Class<?>> itBindingSpec = or(classAnnotatedWith(ITBind.class));
@SuppressWarnings("unchecked")
private final Specification<Class<?>> itInstallSpec = or(classAnnotatedWith(ITInstall.class));
@Override
public String name() {
return "testing";
}
@Override
public Collection<ClasspathScanRequest> classpathScanRequests() {
return classpathScanRequestBuilder().specification(itBindingSpec).specification(itInstallSpec).build();
}
@Override
public void setup(SeedRuntime seedRuntime) {
// Create temporary directory for application storage
try {
temporaryAppStorage = Files.createTempDirectory("seedstack-it-").toFile();
LOGGER.info("Created temporary application storage directory {}", temporaryAppStorage.getAbsolutePath());
seedRuntime.setDefaultConfiguration("application.storage", temporaryAppStorage.getAbsolutePath());
} catch (IOException e) {
LOGGER.warn("Unable to create temporary application storage directory");
}
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public InitState initialize(InitContext initContext) {
String itClassName = initContext.kernelParam(IT_CLASS_NAME);
Map<Specification, Collection<Class<?>>> scannedTypesBySpecification = initContext.scannedTypesBySpecification();
// For @ITBind
Collection<Class<?>> itBindClassCandidates = scannedTypesBySpecification.get(itBindingSpec);
if (itBindClassCandidates != null) {
itBindClasses.addAll(itBindClassCandidates);
}
// For @ITInstall
Collection<Class<?>> itInstallClassCandidates = scannedTypesBySpecification.get(itInstallSpec);
if (itInstallClassCandidates != null) {
for (Class<?> itInstallClassCandidate : itInstallClassCandidates) {
if (Module.class.isAssignableFrom(itInstallClassCandidate)) {
itInstallModules.add((Class<? extends Module>) itInstallClassCandidate);
}
}
}
// For test class binding
if (itClassName != null) {
try {
testClass = Class.forName(itClassName);
} catch (ClassNotFoundException e) {
throw new PluginException("Unable to load the test class " + itClassName, e);
}
}
return InitState.INITIALIZED;
}
@Override
public void stop() {
try {
if (temporaryAppStorage != null) {
deleteRecursively(temporaryAppStorage);
LOGGER.info("Deleted temporary application storage directory {}", temporaryAppStorage.getAbsolutePath());
}
} catch (Exception e) {
LOGGER.warn("Unable to delete temporary application storage directory " + temporaryAppStorage.getAbsolutePath(), e);
}
}
@Override
public Object nativeUnitModule() {
Set<Module> itModules = new HashSet<>();
for (Class<? extends Module> klazz : itInstallModules) {
try {
Constructor<? extends Module> declaredConstructor = klazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
itModules.add(declaredConstructor.newInstance());
} catch (Exception e) {
throw SeedException.wrap(e, ITErrorCode.UNABLE_TO_INSTANTIATE_IT_MODULE).put("moduleClass", klazz.getCanonicalName());
}
}
return new ITModule(testClass, itBindClasses, itModules);
}
private void deleteRecursively(final File file) throws IOException {
if (!file.exists()) {
return;
}
// We do not want to traverse symbolic links to directories, we simply unlink them
if (file.isDirectory() && !Files.isSymbolicLink(file.toPath())) {
File[] files = file.listFiles();
if (files != null) {
for (File c : files) {
deleteRecursively(c);
}
}
}
if (!file.delete()) {
LOGGER.debug("Unable to delete file {}" + file.getAbsolutePath());
}
}
}