package jetbrains.mps.tool.builder.make; /*Generated by MPS */ import jetbrains.mps.tool.common.IMessageFormat; import java.util.Map; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.HashMap; import jetbrains.mps.tool.common.Script; import jetbrains.mps.tool.builder.MpsWorker; import java.io.File; import java.io.IOException; import jetbrains.mps.tool.environment.EnvironmentConfig; import jetbrains.mps.internal.collections.runtime.IMapping; import jetbrains.mps.tool.environment.Environment; import org.apache.log4j.Logger; import jetbrains.mps.project.Project; import java.util.Set; import org.jetbrains.mps.openapi.module.SModule; import java.util.LinkedHashSet; import java.util.Collections; import org.jetbrains.mps.openapi.model.SModel; import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes; import jetbrains.mps.make.MakeSession; import jetbrains.mps.make.script.IScript; import jetbrains.mps.make.script.ScriptBuilder; import jetbrains.mps.make.facet.IFacet; import java.util.ArrayList; import jetbrains.mps.make.script.PropertyPoolInitializer; import jetbrains.mps.internal.make.cfg.MakeFacetInitializer; import jetbrains.mps.vfs.IFile; import jetbrains.mps.make.script.IPropertiesPool; import jetbrains.mps.baseLanguage.tuples.runtime.Tuples; import jetbrains.mps.make.facet.ITarget; import jetbrains.mps.tool.builder.unittest.UnitTestListener; import jetbrains.mps.internal.make.cfg.JavaCompileFacetInitializer; import jetbrains.mps.make.script.IScriptController; import java.util.concurrent.Future; import jetbrains.mps.make.script.IResult; import java.util.concurrent.ExecutionException; import org.jetbrains.mps.openapi.module.ModelAccess; import jetbrains.mps.make.ModuleMaker; import jetbrains.mps.progress.EmptyProgressMonitor; import org.jetbrains.annotations.NotNull; import jetbrains.mps.project.AbstractModule; import java.util.Queue; import jetbrains.mps.internal.collections.runtime.QueueSequence; import java.util.LinkedList; import jetbrains.mps.make.resources.IResource; import jetbrains.mps.baseLanguage.closures.runtime.Wrappers; import jetbrains.mps.internal.collections.runtime.Sequence; import jetbrains.mps.internal.collections.runtime.ITranslator2; import jetbrains.mps.internal.collections.runtime.IWhereFilter; import jetbrains.mps.smodel.Language; import jetbrains.mps.smodel.Generator; import jetbrains.mps.smodel.resources.ModelsToResources; import jetbrains.mps.generator.GenerationFacade; import jetbrains.mps.vfs.FileSystem; import jetbrains.mps.tool.common.TeamCityMessageFormat; import jetbrains.mps.tool.common.ScriptProperties; import jetbrains.mps.messages.IMessageHandler; import jetbrains.mps.messages.IMessage; import jetbrains.mps.tool.builder.unittest.UnitTestAdapter; import jetbrains.mps.tool.builder.unittest.ITestReporter; import jetbrains.mps.tool.builder.unittest.XmlTestReporter; import jetbrains.mps.tool.builder.unittest.ConsoleTestReporter; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import jetbrains.mps.progress.ProgressMonitorBase; import org.jetbrains.mps.openapi.util.SubProgressKind; public class GenTestWorker extends GeneratorWorker { private final GenTestWorker.MyMessageHandler myMessageHandler = new GenTestWorker.MyMessageHandler(); private boolean myTestFailed = false; private IMessageFormat myBuildServerMessageFormat; private Map<String, String> path2tmp = MapSequence.fromMap(new HashMap<String, String>()); private String tmpPath; private GenTestWorker.MyReporter myReporter = new GenTestWorker.MyReporter(); public GenTestWorker(Script whatToDo, MpsWorker.AntLogger logger) { super(whatToDo, logger); myBuildServerMessageFormat = getBuildServerMessageFormat(); File tmpDir; try { tmpDir = File.createTempFile("gentest_", "tmp"); tmpDir.delete(); tmpDir.mkdir(); } catch (IOException ex) { throw new RuntimeException(ex); } this.tmpPath = tmpDir.getAbsolutePath(); } @Override public void work() { myReporter.init(); EnvironmentConfig config = EnvironmentConfig.defaultConfig(); for (String jar : myWhatToDo.getLibraryJars()) { config = config.addLib(jar); } for (IMapping<String, String> macro : MapSequence.fromMap(myWhatToDo.getMacro())) { config = config.addMacro(macro.key(), new File(macro.value())); } Environment environment = new GeneratorWorker.MyEnvironment(config); environment.init(); Logger.getRootLogger().setLevel(myWhatToDo.getLogLevel()); setupEnvironment(); setGenerationProperties(); Project project = createDummyProject(); final Set<SModule> modules = new LinkedHashSet<SModule>(); project.getModelAccess().runWriteAction(new Runnable() { public void run() { collectFromModuleFiles(modules); } }); MpsWorker.ObjectsToProcess go = new MpsWorker.ObjectsToProcess(Collections.EMPTY_SET, modules, Collections.EMPTY_SET); if (go.hasAnythingToGenerate()) { loadAndMake(project, go); generate(project, go); } else { error("Could not find anything to test."); } dispose(); showStatistic(); myReporter.finishRun(); cleanUp(); } @Override protected void generate(Project project, MpsWorker.ObjectsToProcess go) { StringBuffer s = new StringBuffer("Generating:"); for (Project p : go.getProjects()) { s.append("\n "); s.append(p); } for (SModule m : go.getModules()) { s.append("\n "); s.append(m); } for (SModel m : go.getModels()) { s.append("\n "); s.append(m); } info(s.toString()); final _FunctionTypes._void_P1_E0<? super String> startTestFormat = new _FunctionTypes._void_P1_E0<String>() { public void invoke(String msg) { myReporter.testStarted(((msg == null ? null : msg.trim()))); } }; final _FunctionTypes._void_P1_E0<? super String> finishTestFormat = new _FunctionTypes._void_P1_E0<String>() { public void invoke(String msg) { myReporter.testFinished(((msg == null ? null : msg.trim()))); } }; final MakeSession ms = new MakeSession(project, myMessageHandler, true) { @Override public IScript toScript(ScriptBuilder scriptBuilder) { if (isInvokeTestsSet()) { scriptBuilder.withFacetName(new IFacet.Name("jetbrains.mps.tool.gentest.Test")); } if (isShowDiff()) { scriptBuilder.withFacetName(new IFacet.Name("jetbrains.mps.tool.gentest.Diff")); } return scriptBuilder.toScript(); } }; ArrayList<PropertyPoolInitializer> ppi = new ArrayList<PropertyPoolInitializer>(); ppi.add(new MakeFacetInitializer().setPathToFile(new _FunctionTypes._return_P1_E0<IFile, String>() { public IFile invoke(String path) { return tmpFile(path); } })); if (isShowDiff()) { PropertyPoolInitializer diffFacetInit = new PropertyPoolInitializer() { public void populate(IPropertiesPool ppool) { Tuples._2<_FunctionTypes._return_P1_E0<? extends String, ? super IFile>, Set<File>> dparams = (Tuples._2<_FunctionTypes._return_P1_E0<? extends String, ? super IFile>, Set<File>>) ppool.properties(new ITarget.Name("jetbrains.mps.tool.gentest.Diff.diff"), Object.class); if (dparams != null) { dparams._0(new _FunctionTypes._return_P1_E0<String, IFile>() { public String invoke(IFile f) { return pathOfTmpFile(f); } }); dparams._1(myWhatToDo.getExcludedFromDiffFiles()); } } }; ppi.add(diffFacetInit); } if (isInvokeTestsSet()) { PropertyPoolInitializer testFacetInit = new PropertyPoolInitializer() { public void populate(IPropertiesPool ppool) { Tuples._1<UnitTestListener> testParams = (Tuples._1<UnitTestListener>) ppool.properties(new ITarget.Name("jetbrains.mps.tool.gentest.Test.runTests"), Object.class); if (testParams != null) { testParams._0(new GenTestWorker.MyUnitTestAdapter()); } } }; ppi.add(testFacetInit); } ppi.add(new JavaCompileFacetInitializer().setJavaCompileOptions(myJavaCompilerOptions)); IScriptController ctl = new IScriptController.Stub2(ms, ppi.toArray(new PropertyPoolInitializer[ppi.size()])); try { BuildMakeService bms = new BuildMakeService(); myReporter.finishRun(); myReporter.startRun(myWhatToDo.getProperty("ant.project.name")); Future<IResult> result = bms.make(ms, collectResources(project, go.getModules(), go.getModels()), null, ctl, new GenTestWorker.MyProgressMonitorBase(startTestFormat, finishTestFormat)); if (!(result.get().isSucessful())) { myErrors.add("Make was not successful " + result.get().output()); } } catch (InterruptedException e) { myErrors.add(e.toString()); } catch (ExecutionException e) { myErrors.add(e.toString()); } } private void loadAndMake(final Project project, final MpsWorker.ObjectsToProcess go) { ModelAccess access = project.getRepository().getModelAccess(); access.runReadAction(new Runnable() { public void run() { new ModuleMaker().make(go.getModules(), new EmptyProgressMonitor() { @Override public void step(String text) { // silently } @Override public void start(@NotNull String taskName, int work) { // silently } }, myJavaCompilerOptions); } }); access.runWriteAction(new Runnable() { public void run() { // the following updates stub models that could change due to the compilation happened (webr, 3.0 migration case) for (SModule m : project.getRepository().getModules()) { if (!((m instanceof AbstractModule))) { continue; } ((AbstractModule) m).updateModelsSet(); } } }); } private void reportIfStartsWith(String prefix, String work, _FunctionTypes._void_P1_E0<? super String> format) { // This logic looks flawed (how come test name ends with ".Test.Generating"), but as long as GenTestWorker doesn't work, I can't figure out what's right if (work != null && work.startsWith(prefix)) { format.invoke(work.substring(prefix.length()) + ".Test." + ((prefix == null ? null : prefix.trim()))); } } private void cleanUp() { for (Queue<File> dirs = QueueSequence.fromQueueAndArray(new LinkedList<File>(), new File(tmpPath)); QueueSequence.fromQueue(dirs).isNotEmpty();) { File dir = QueueSequence.fromQueue(dirs).removeFirstElement(); dir.deleteOnExit(); for (File f : dir.listFiles()) { if (f.isDirectory()) { QueueSequence.fromQueue(dirs).addLastElement(f); } else { f.deleteOnExit(); } } } this.tmpPath = null; MapSequence.fromMap(path2tmp).clear(); } private Iterable<IResource> collectResources(Project project, final Iterable<SModule> modules, final Iterable<SModel> models) { final Wrappers._T<Iterable<SModel>> result = new Wrappers._T<Iterable<SModel>>(null); project.getRepository().getModelAccess().runReadAction(new Runnable() { public void run() { result.value = Sequence.fromIterable(result.value).concat(Sequence.fromIterable(modules).translate(new ITranslator2<SModule, SModel>() { public Iterable<SModel> translate(SModule m) { return m.getModels(); } })); result.value = Sequence.fromIterable(result.value).concat(Sequence.fromIterable(modules).where(new IWhereFilter<SModule>() { public boolean accept(SModule it) { return it instanceof Language; } }).translate(new ITranslator2<SModule, Generator>() { public Iterable<Generator> translate(SModule it) { return ((Language) it).getGenerators(); } }).translate(new ITranslator2<Generator, SModel>() { public Iterable<SModel> translate(Generator gen) { return gen.getModels(); } })); result.value = Sequence.fromIterable(result.value).concat(Sequence.fromIterable(models)); } }); return new ModelsToResources(Sequence.fromIterable(result.value).where(new IWhereFilter<SModel>() { public boolean accept(SModel smd) { return GenerationFacade.canGenerate(smd); } })).resources(false); } private IFile tmpFile(String path) { if (MapSequence.fromMap(path2tmp).containsKey(path)) { return FileSystem.getInstance().getFileByPath(MapSequence.fromMap(path2tmp).get(path)); } int idx = path.indexOf("/"); if (idx > 0) { throw new IllegalArgumentException("not an absolute path '" + path + "'"); } idx = (idx < 0 ? path.indexOf(File.separator) : idx); if (idx > "C:\\".length() && path.indexOf(":") < 0) { throw new IllegalArgumentException("not an absolute path '" + path + "'"); } String tmp = tmpPath + "/" + ((idx != 0 ? path.replace(":", "_w_") : path.substring(1))); MapSequence.fromMap(path2tmp).put(path, tmp); return FileSystem.getInstance().getFileByPath(tmp); } private String pathOfTmpFile(IFile file) { String p = file.getPath(); if (!(p.startsWith(tmpPath))) { throw new IllegalArgumentException("unknown tmp path '" + file.getParent() + "'"); } p = p.substring(tmpPath.length() + 1); if (p.contains("_w_")) { return FileSystem.getInstance().getFileByPath(p.replace("_w_", ":")).getPath(); } String prefix = (File.separatorChar == '/' ? "/" : "\\\\"); return FileSystem.getInstance().getFileByPath(prefix + p).getPath(); } public IMessageFormat getBuildServerMessageFormat() { if (isRunningOnTeamCity()) { return new TeamCityMessageFormat(); } else { return new ConsoleMessageFormat(); } } private boolean isRunningOnTeamCity() { return myWhatToDo.getProperty("teamcity.version") != null; } private boolean isInvokeTestsSet() { return Boolean.parseBoolean(myWhatToDo.getProperty(ScriptProperties.INVOKE_TESTS)) && isCompileSet(); } private boolean isCompileSet() { return Boolean.parseBoolean(myWhatToDo.getProperty(ScriptProperties.COMPILE)); } private boolean isShowDiff() { return Boolean.parseBoolean(myWhatToDo.getProperty(ScriptProperties.SHOW_DIFF)); } @Override protected void showStatistic() { super.showStatistic(); if (myTestFailed) { throw new RuntimeException("Tests Failed"); } } public static void main(String[] args) { GenTestWorker generator = new GenTestWorker(Script.fromDumpInFile(new File(args[0])), new MpsWorker.SystemOutLogger()); generator.workFromMain(); } private class MyMessageHandler implements IMessageHandler { public MyMessageHandler() { } @Override public void handle(@NotNull IMessage msg) { switch (msg.getKind()) { case ERROR: GenTestWorker.this.error(msg.getText()); myReporter.errorLine("[ERROR] " + msg.getText()); // next code used to be in JobMonitor.reportFeedback, but as long as all feedback is piped to MyMessageHandler, // the code relocated here, and is activated only when there's active test (although I doubt getCurrentTestName() ever gives // reasonable value - mechanism to find out current test looks quite odd, see reportIfStartsWith() String test = myReporter.getCurrentTestName(); if (test != null) { Throwable thr = msg.getException(); String text = msg.getText(); String details = (thr == null ? "(no details)" : String.valueOf(MpsWorker.extractStackTrace(thr))); int eol = text.indexOf("\n"); if (eol >= 0) { details = text.substring(eol + 1) + "\n" + details; text = text.substring(0, eol); } myReporter.testFailed(test, text, details); } break; case WARNING: GenTestWorker.this.warning(msg.getText()); myReporter.outputLine("[WARNING]" + msg.getText()); break; case INFORMATION: GenTestWorker.this.info(msg.getText()); myReporter.outputLine("[INFO]" + msg.getText()); break; default: } } } private class MyUnitTestAdapter extends UnitTestAdapter { private MyUnitTestAdapter() { } @Override public void testStarted(String testName) { myReporter.testStarted(testName); } @Override public void testFailed(String test, String message, String details) { myReporter.testFailed(test, message, details); myTestFailed = true; } @Override public void testFinished(String testName) { myReporter.testFinished(testName); } @Override public void logMessage(String message) { if (message != null && !(message.isEmpty())) { info(message); myReporter.outputLine(message); } } @Override public void logError(String errorMessage) { if (errorMessage != null && !(errorMessage.isEmpty())) { error(errorMessage); myReporter.errorLine(errorMessage); } } } private class MyReporter { private ITestReporter testReporter; private String currentTestName; private File gentestdir; private MyReporter() { } private void init() { if (gentestdir != null) { return; } if (isRunningOnTeamCity()) { String wd = myWhatToDo.getProperty("mps.gentest.reportsDir"); wd = (wd == null ? System.getProperty("user.dir") : wd); gentestdir = new File(wd, ".gentest"); if (!(gentestdir.exists())) { if (!(gentestdir.mkdirs())) { File tmpDir; try { tmpDir = File.createTempFile("gentest", "reports"); tmpDir.delete(); tmpDir.mkdir(); } catch (IOException ex) { throw new RuntimeException(ex); } gentestdir = tmpDir; } } else if (gentestdir.isDirectory()) { for (File f : gentestdir.listFiles()) { f.delete(); } } } } private String getCurrentTestName() { return currentTestName; } private void startRun(String name) { this.testReporter = (isRunningOnTeamCity() ? new XmlTestReporter(name) : new ConsoleTestReporter()); } private void finishRun() { if (testReporter == null) { return; } if (currentTestName != null) { testReporter.testFinished(currentTestName); } testReporter.runFinished(); if (isRunningOnTeamCity()) { BufferedOutputStream os = null; try { File reportFile = File.createTempFile("gentest_report-", ".xml", gentestdir); os = new BufferedOutputStream(new FileOutputStream(reportFile)); ((XmlTestReporter) testReporter).dump(os); System.out.println("##teamcity[importData type='junit' path='" + reportFile.getAbsolutePath() + "']"); } catch (IOException ex) { } finally { if (os != null) { try { os.close(); } catch (IOException ignore) { } } } } this.testReporter = null; } private String normalizeTestName(String name) { return name.replace("@", "_"); } private void testStarted(String testname) { testname = normalizeTestName(testname); if (currentTestName != null) { testReporter.testFinished(currentTestName); } this.currentTestName = testname; testReporter.testStarted(testname); } private void testFinished(String testname) { testname = normalizeTestName(testname); testReporter.testFinished(testname); this.currentTestName = null; } private void testFailed(String testname, String msg, String longmsg) { testname = normalizeTestName(testname); testReporter.testFailed(testname, msg, longmsg); } private void outputLine(String out) { if (currentTestName != null) { testReporter.testOutputLine(currentTestName, out); } else if (testReporter != null) { testReporter.outputLine(out); } else { System.out.println(out); } } private void errorLine(String err) { if (currentTestName != null) { testReporter.testErrorLine(currentTestName, err); } else if (testReporter != null) { testReporter.errorLine(err); } else { System.err.println(err); } } } private class MyProgressMonitorBase extends ProgressMonitorBase { private String prevTitle; private final _FunctionTypes._void_P1_E0<? super String> myStartTestFormat; private final _FunctionTypes._void_P1_E0<? super String> myFinishTestFormat; public MyProgressMonitorBase(_FunctionTypes._void_P1_E0<? super String> startTestFormat, _FunctionTypes._void_P1_E0<? super String> finishTestFormat) { myStartTestFormat = startTestFormat; myFinishTestFormat = finishTestFormat; } @Override protected void update(double p0) { } @Override protected void startInternal(String text) { } @Override protected void doneInternal(String text) { } @Override protected void setTitleInternal(String text) { prevTitle = text; } @Override protected void setStepInternal(String p0) { } @Override public boolean isCanceled() { return false; } @Override public void cancel() { } private ProgressMonitorBase.SubProgressMonitor customSubProgress(ProgressMonitorBase parent, int work, SubProgressKind kind) { if (prevTitle != null && prevTitle.startsWith("Generating :: ")) { return new ProgressMonitorBase.SubProgressMonitor(parent, work, kind) { @Override protected void startInternal(String text) { reportIfStartsWith("Generating ", "Generating " + text, MyProgressMonitorBase.this.myStartTestFormat); } @Override protected void doneInternal(String text) { reportIfStartsWith("Generating ", "Generating " + text, MyProgressMonitorBase.this.myFinishTestFormat); } }; } return new ProgressMonitorBase.SubProgressMonitor(parent, work, kind) { @Override protected ProgressMonitorBase.SubProgressMonitor subTaskInternal(int work, SubProgressKind kind) { return customSubProgress(this, work, kind); } }; } @Override protected ProgressMonitorBase.SubProgressMonitor subTaskInternal(int work, SubProgressKind kind) { return customSubProgress(this, work, kind); } } }