package com.mobilesorcery.sdk.testing.project;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import com.mobilesorcery.sdk.core.BuildConfiguration;
import com.mobilesorcery.sdk.core.IBuildConfiguration;
import com.mobilesorcery.sdk.core.MoSyncBuilder;
import com.mobilesorcery.sdk.core.MoSyncProject;
import com.mobilesorcery.sdk.core.NameSpacePropertyOwner;
import com.mobilesorcery.sdk.core.PropertyUtil;
import com.mobilesorcery.sdk.testing.TestPlugin;
public class MoSyncProjectTestManager {
private static final String TEST_PREFIX = "test.ns:";
private static final String IS_TEST_PROJECT = TEST_PREFIX + "is.testproject";
public static final String TEST_RESOURCES = TEST_PREFIX + "test.res";
private static final String TEST_LIBRARY = "testify.lib";
private static final String TEST_LIBRARY_DEBUG = "testifyD.lib";
private MoSyncProject project;
private Set<IPath> testResources = new HashSet<IPath>();
private IBuildConfiguration prototypeDebugCfg;
private IBuildConfiguration prototypeReleaseCfg;
public MoSyncProjectTestManager(MoSyncProject project) {
this.project = project;
init();
}
private void init() {
testResources = new HashSet<IPath>(Arrays.asList(PropertyUtil.getPaths(project, TEST_RESOURCES)));
}
/**
* Returns whether the project associated with this manager
* has been configured to execute tests.
* @return
*/
public boolean isTestProject() {
return isTestProject(project);
}
private static boolean isTestProject(MoSyncProject project) {
return PropertyUtil.getBoolean(project, IS_TEST_PROJECT);
}
/**
* Configures a project to become a test project
*/
public void configureProject() {
// TODO: We now use a specific property page to modify this; and this
// property page is specific to the testing plugin. So we kind of assume
// that the only plugin that will ever modify this property is the testing
// plugin. Kind of ugly, so FIXME. Maybe even time to implement path containers...
project.setProperty(MoSyncProject.STANDARD_EXCLUDES_FILTER_KEY, "%" + TEST_RESOURCES + "%");
configureBuildConfiguration(true);
configureBuildConfiguration(false);
}
private void configureBuildConfiguration(boolean isDebug) {
IBuildConfiguration testCfg = ensureHasTestConfigs(isDebug);
// Now we just brutally set the standard excludes to be the test resources
testCfg.getProperties().setProperty(MoSyncProject.STANDARD_EXCLUDES_FILTER_KEY, "%EMPTY%");
PropertyUtil.setPaths(project, TEST_RESOURCES, testResources.toArray(new IPath[0]));
addRequiredLibraries(testCfg.getProperties(), isDebug);
setMinimumMemorySizes(testCfg);
}
private void setMinimumMemorySizes(IBuildConfiguration testCfg) {
// The framework itself requires a certain amount of memory.
setMinimumMemorySize(testCfg.getProperties(), MoSyncBuilder.MEMORY_HEAPSIZE_KB, 128);
setMinimumMemorySize(testCfg.getProperties(), MoSyncBuilder.MEMORY_STACKSIZE_KB, 64);
setMinimumMemorySize(testCfg.getProperties(), MoSyncBuilder.MEMORY_DATASIZE_KB, 256);
}
private void setMinimumMemorySize(NameSpacePropertyOwner properties, String key, int minMemSizeInKb) {
Integer currentMemSize = PropertyUtil.getInteger(properties, key);
if (currentMemSize == null || currentMemSize < minMemSizeInKb) {
PropertyUtil.setInteger(properties, key, minMemSizeInKb);
}
}
private void addRequiredLibraries(NameSpacePropertyOwner properties, boolean isDebug) {
IPath[] libs = PropertyUtil.getPaths(properties, MoSyncBuilder.ADDITIONAL_LIBRARIES);
boolean hasTestLibrary = false;
for (int i = 0; i < libs.length; i++) {
hasTestLibrary |= isTestLibrary(libs[i]);
}
if (!hasTestLibrary) {
IPath[] newLibs = new IPath[libs.length + 1];
System.arraycopy(libs, 0, newLibs, 0, libs.length);
newLibs[libs.length] = new Path(isDebug ? TEST_LIBRARY_DEBUG : TEST_LIBRARY);
PropertyUtil.setPaths(properties, MoSyncBuilder.ADDITIONAL_LIBRARIES, newLibs);
}
}
public static boolean isTestConfig(IBuildConfiguration cfg) {
return cfg != null && cfg.getTypes().contains(TestPlugin.TEST_BUILD_CONFIGURATION_TYPE);
}
private boolean isTestLibrary(IPath lib) {
return TEST_LIBRARY.equalsIgnoreCase(lib.toPortableString()) || TEST_LIBRARY_DEBUG.equalsIgnoreCase(lib.toPortableString());
}
public List<IBuildConfiguration> getTestConfigs(boolean isDebug) {
return new ArrayList<IBuildConfiguration>(project.getBuildConfigurations(project.getBuildConfigurationsOfType(getTypes(isDebug))));
}
public void setPrototypeConfiguration(IBuildConfiguration prototypeCfg, boolean isDebug) {
if (isDebug) {
prototypeDebugCfg = prototypeCfg;
} else {
prototypeReleaseCfg = prototypeCfg;
}
}
private IBuildConfiguration ensureHasTestConfigs(boolean isDebug) {
project.activateBuildConfigurations();
List<IBuildConfiguration> cfgs = getTestConfigs(isDebug);
if (!cfgs.isEmpty()) {
return cfgs.get(0);
}
String cfgName = isDebug ? "Test_Debug" : "Test";
String testCfgId = BuildConfiguration.createUniqueId(project, cfgName);
// We try to create the test configuration to be as close as possible to existing configurations.
IBuildConfiguration prototype = isDebug ? prototypeDebugCfg : prototypeReleaseCfg;
if (prototype == null) {
List<String> prototypes = new ArrayList<String>(project.getBuildConfigurationsOfType(isDebug ? IBuildConfiguration.DEBUG_TYPE : IBuildConfiguration.RELEASE_TYPE));
prototype = prototypes.isEmpty() ? null : project.getBuildConfiguration(prototypes.get(0));
}
String[] types = getTypes(isDebug);
IBuildConfiguration testCfg = null;
if (prototype == null) {
testCfg = project.installBuildConfiguration(testCfgId, types);
} else {
testCfg = prototype.clone(testCfgId);
testCfg.setTypes(Arrays.asList(types));
project.installBuildConfiguration(testCfg);
}
return testCfg;
}
private String[] getTypes(boolean isDebug) {
String baseCfgType = isDebug ? IBuildConfiguration.DEBUG_TYPE : IBuildConfiguration.RELEASE_TYPE;
String[] types = new String[] { baseCfgType , TestPlugin.TEST_BUILD_CONFIGURATION_TYPE };
return types;
}
public void assignTestResource(IPath path, boolean assign) {
if (assign) {
testResources.add(path);
} else {
testResources.remove(path);
}
}
public boolean isTestResource(IResource resource) {
return testResources.contains(resource.getProjectRelativePath());
}
public Set<IPath> getTestResources() {
return Collections.unmodifiableSet(testResources);
}
/**
* Utility method to check whether a project can
* be configured as a test project
* @param project
* @return
*/
public boolean canConfigureProject(IProject project) {
MoSyncProject mosyncProject = MoSyncProject.create(project);
return mosyncProject != null && mosyncProject.areBuildConfigurationsSupported() && !isTestProject(mosyncProject);
}
}