package org.elixir_lang.jps;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.util.io.FileSystemUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.util.io.TestFileSystemBuilder;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.api.CanceledStatus;
import org.jetbrains.jps.builders.impl.BuildDataPathsImpl;
import org.jetbrains.jps.builders.impl.BuildRootIndexImpl;
import org.jetbrains.jps.builders.impl.BuildTargetIndexImpl;
import org.jetbrains.jps.builders.impl.BuildTargetRegistryImpl;
import org.jetbrains.jps.builders.logging.BuildLoggingManager;
import org.jetbrains.jps.builders.storage.BuildDataPaths;
import org.jetbrains.jps.cmdline.ClasspathBootstrap;
import org.jetbrains.jps.cmdline.ProjectDescriptor;
import org.jetbrains.jps.incremental.BuilderRegistry;
import org.jetbrains.jps.incremental.IncProjectBuilder;
import org.jetbrains.jps.incremental.RebuildRequestedException;
import org.jetbrains.jps.incremental.fs.BuildFSState;
import org.jetbrains.jps.incremental.storage.BuildDataManager;
import org.jetbrains.jps.incremental.storage.BuildTargetsState;
import org.jetbrains.jps.incremental.storage.ProjectTimestamps;
import org.jetbrains.jps.indices.ModuleExcludeIndex;
import org.jetbrains.jps.indices.impl.IgnoredFileIndexImpl;
import org.jetbrains.jps.indices.impl.ModuleExcludeIndexImpl;
import org.jetbrains.jps.model.*;
import org.jetbrains.jps.model.java.*;
import org.jetbrains.jps.model.library.JpsOrderRootType;
import org.jetbrains.jps.model.library.JpsTypedLibrary;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.model.library.sdk.JpsSdkReference;
import org.jetbrains.jps.model.library.sdk.JpsSdkType;
import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.model.module.JpsModuleType;
import org.jetbrains.jps.model.module.JpsSdkReferencesTable;
import org.jetbrains.jps.model.serialization.JpsProjectLoader;
import org.jetbrains.jps.model.serialization.PathMacroUtil;
import org.jetbrains.jps.util.JpsPathUtil;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Created by zyuyou on 15/7/17.
*/
public abstract class JpsBuildTestCase extends UsefulTestCase {
private File myProjectDir;
private JpsSdk<JpsDummyElement> myJdk;
private TestProjectBuilderLogger myLogger;
protected JpsProject myProject;
protected JpsModel myModel;
protected File myDataStorageRoot;
protected Map<String, String> myBuildParams;
@Override
protected void setUp() throws Exception {
super.setUp();
myModel = JpsElementFactory.getInstance().createModel();
myProject = myModel.getProject();
myDataStorageRoot = FileUtil.createTempDirectory("compile-server-" + getProjectName(), null);
myLogger = new TestProjectBuilderLogger();
myBuildParams = new HashMap<String, String>();
}
@Override
protected void tearDown() throws Exception {
myProjectDir = null;
super.tearDown();
}
protected static void assertOutput(String outputPath, TestFileSystemBuilder expected){
expected.build().assertDirectoryEqual(new File(FileUtil.toSystemIndependentName(outputPath)));
}
protected static void change(String filePath){
change(filePath, null);
}
protected static void change(String filePath, @Nullable String newContent){
try{
File file = new File(FileUtil.toSystemDependentName(filePath));
assertTrue("File " + file.getAbsolutePath() + " doesn't exist", file.exists());
if(newContent != null){
FileUtil.writeToFile(file, newContent);
}
long oldTimeStamp = FileSystemUtil.lastModified(file);
long time = System.currentTimeMillis();
setLastModified(file, time);
if(FileSystemUtil.lastModified(file) <= oldTimeStamp){
setLastModified(file, time + 1);
long newTimeStamp = FileSystemUtil.lastModified(file);
if(newTimeStamp <= oldTimeStamp){
// Mac OS and som versions of Linux truncates timestamp to nearest second
setLastModified(file, time + 1000);
newTimeStamp = FileSystemUtil.lastModified(file);
assertTrue("Failed to change timestamp for " + file.getAbsolutePath(), newTimeStamp > oldTimeStamp);
}
sleepUntil(newTimeStamp);
}
} catch (IOException e) {
e.printStackTrace();
}
}
protected static void sleepUntil(long time){
// we need this to ensure that the file won't be treated as changed by user during complication
// and therefore marked for recompilation
long delta;
while ((delta = time - System.currentTimeMillis()) > 0){
try{
// noinspection BusyWait
Thread.sleep(delta);
} catch (InterruptedException ignored) {
}
}
}
private static void setLastModified(File file, long time){
boolean updated = file.setLastModified(time);
assertTrue("Cannot modify timestamp for " + file.getAbsolutePath(), updated);
}
protected JpsSdk<JpsDummyElement> addJdk(String name){
try{
return addJdk(name, FileUtil.toSystemIndependentName(ClasspathBootstrap.getResourceFile(Object.class).getCanonicalPath()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected JpsSdk<JpsDummyElement> addJdk(String name, String path){
String homePath = System.getProperty("java.home");
String versionString = System.getProperty("java.version");
JpsTypedLibrary<JpsSdk<JpsDummyElement>> jdk = myModel.getGlobal().addSdk(name, homePath, versionString, JpsJavaSdkType.INSTANCE);
jdk.addRoot(JpsPathUtil.pathToUrl(path), JpsOrderRootType.COMPILED);
return jdk.getProperties();
}
protected String getProjectName(){
return StringUtil.decapitalize(StringUtil.trimStart(getName(), "test"));
}
protected ProjectDescriptor createProjectDescriptor(BuildLoggingManager buildLoggingManager){
try{
BuildTargetRegistryImpl targetRegistry = new BuildTargetRegistryImpl(myModel);
ModuleExcludeIndex index = new ModuleExcludeIndexImpl(myModel);
IgnoredFileIndexImpl ignoredFileIndex = new IgnoredFileIndexImpl(myModel);
BuildDataPaths dataPaths = new BuildDataPathsImpl(myDataStorageRoot);
BuildRootIndexImpl buildRootIndex = new BuildRootIndexImpl(targetRegistry, myModel, index, dataPaths, ignoredFileIndex);
BuildTargetIndexImpl targetIndex = new BuildTargetIndexImpl(targetRegistry, buildRootIndex);
BuildTargetsState targetsState = new BuildTargetsState(dataPaths, myModel, buildRootIndex);
ProjectTimestamps timestamps = new ProjectTimestamps(myDataStorageRoot, targetsState);
BuildDataManager dataManager = new BuildDataManager(dataPaths, targetsState, true);
return new ProjectDescriptor(myModel, new BuildFSState(true), timestamps, dataManager, buildLoggingManager, index,
targetsState, targetIndex, buildRootIndex, ignoredFileIndex);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected void loadProject(String projectPath, Map<String, String> pathVariables){
try{
String testDataRootPath = getTestDataRootPath();
String fullProjectPath = FileUtil.toSystemDependentName(testDataRootPath != null ? testDataRootPath + "/" + projectPath : projectPath);
Map<String, String> allPathVariables = new HashMap<String, String>(pathVariables.size() + 1);
allPathVariables.putAll(pathVariables);
allPathVariables.put(PathMacroUtil.APPLICATION_HOME_DIR, PathManager.getHomePath());
JpsProjectLoader.loadProject(myProject, allPathVariables, fullProjectPath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Nullable
protected String getTestDataRootPath(){
return null;
}
protected <T extends JpsElement>JpsModule addModule(String moduleName,
String[] srcPaths,
@Nullable String outputPath,
@Nullable String testOutputPath,
JpsSdk<T> sdk){
return addModule(moduleName, srcPaths, outputPath, testOutputPath, sdk, JpsJavaModuleType.INSTANCE);
}
protected <T extends JpsElement, M extends JpsModuleType & JpsElementTypeWithDefaultProperties> JpsModule addModule(
String moduleName, String[] srcPaths, @Nullable String outputPath, @Nullable String testOutputPath,
JpsSdk<T> sdk, M moduleType ){
JpsModule module = myProject.addModule(moduleName, moduleType);
JpsSdkType<T> sdkType = sdk.getSdkType();
JpsSdkReferencesTable sdkTable = module.getSdkReferencesTable();
sdkTable.setSdkReference(sdkType, sdk.createReference());
if(sdkType instanceof JpsJavaSdkTypeWrapper){
JpsSdkReference<T> wrapperRef = sdk.createReference();
sdkTable.setSdkReference(JpsJavaSdkType.INSTANCE, JpsJavaExtensionService.getInstance().createWrappedJavaSdkReference((JpsJavaSdkTypeWrapper)sdkType, wrapperRef));
}
module.getDependenciesList().addSdkDependency(sdkType);
if(srcPaths.length > 0 || outputPath != null){
for(String srcPath : srcPaths){
module.getContentRootsList().addUrl(JpsPathUtil.pathToUrl(srcPath));
module.addSourceRoot(JpsPathUtil.pathToUrl(srcPath), JavaSourceRootType.SOURCE);
}
JpsJavaModuleExtension extension = JpsJavaExtensionService.getInstance().getOrCreateModuleExtension(module);
if(outputPath != null){
extension.setOutputUrl(JpsPathUtil.pathToUrl(outputPath));
if(!StringUtil.isEmpty(testOutputPath)){
extension.setTestOutputUrl(JpsPathUtil.pathToUrl(testOutputPath));
}else {
extension.setTestOutputUrl(extension.getOutputUrl());
}
}else{
extension.setInheritOutput(true);
}
}
return module;
}
protected void rebuildAll(){
doBuild(CompileScopeTestBuilder.rebuild().all()).assertSuccessful();
}
protected BuildResult makeAll(){
return doBuild(CompileScopeTestBuilder.make().all());
}
protected BuildResult doBuild(CompileScopeTestBuilder scope){
ProjectDescriptor descriptor = createProjectDescriptor(new BuildLoggingManager((myLogger)));
try{
myLogger.clear();
return doBuild(descriptor, scope);
}finally {
descriptor.release();
}
}
protected BuildResult doBuild(ProjectDescriptor descriptor, CompileScopeTestBuilder scopeTestBuilder){
IncProjectBuilder builder = new IncProjectBuilder(descriptor, BuilderRegistry.getInstance(), myBuildParams, CanceledStatus.NULL, null, true);
BuildResult result = new BuildResult();
builder.addMessageHandler(result);
try {
builder.build(scopeTestBuilder.build(), false);
}catch(RebuildRequestedException e){
throw new RuntimeException(e);
}
return result;
}
public String createFile(String relativePath, String text){
try{
File file = new File(getOrCreateProjectDir(), relativePath);
FileUtil.writeToFile(file, text);
return FileUtil.toSystemIndependentName(file.getAbsolutePath());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public File getOrCreateProjectDir(){
if(myProjectDir == null){
try{
myProjectDir = doGetProjectDir();
}catch (IOException e){
throw new RuntimeException(e);
}
}
return myProjectDir;
}
protected File doGetProjectDir() throws IOException{
return FileUtil.createTempDirectory("prj", null);
}
public String getAbsolutePath(String pathRelativeToProjectRoot){
return FileUtil.toSystemIndependentName(new File(getOrCreateProjectDir(), pathRelativeToProjectRoot).getAbsolutePath());
}
public JpsModule addModule(String moduleName, String... srcPaths){
if(myJdk == null){
myJdk = addJdk("1.8");
}
return addModule(moduleName, srcPaths, getAbsolutePath("out/production/" + moduleName), null, myJdk);
}
}