don't load AST transformations from the same module and (temporarily) on stub generat...
[fedora-idea.git] / plugins / groovy / test / org / jetbrains / plugins / groovy / lang / GroovyCompilerTest.java
blobeedb0ba565ed3b2f0f574ab306cd9e8cb0c269a5
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.jetbrains.plugins.groovy.lang;
19 import com.intellij.compiler.CompilerConfiguration;
20 import com.intellij.compiler.CompilerManagerImpl;
21 import com.intellij.execution.ExecutionException;
22 import com.intellij.execution.Executor;
23 import com.intellij.execution.application.ApplicationConfiguration;
24 import com.intellij.execution.application.ApplicationConfigurationType;
25 import com.intellij.execution.configurations.RunnerSettings;
26 import com.intellij.execution.executors.DefaultRunExecutor;
27 import com.intellij.execution.impl.DefaultJavaProgramRunner;
28 import com.intellij.execution.process.ProcessAdapter;
29 import com.intellij.execution.process.ProcessEvent;
30 import com.intellij.execution.process.ProcessHandler;
31 import com.intellij.execution.process.ProcessOutputTypes;
32 import com.intellij.execution.runners.ExecutionEnvironment;
33 import com.intellij.execution.runners.ProgramRunner;
34 import com.intellij.execution.ui.RunContentDescriptor;
35 import com.intellij.ide.DataManager;
36 import com.intellij.openapi.application.ApplicationManager;
37 import com.intellij.openapi.application.ModalityState;
38 import com.intellij.openapi.application.PathManager;
39 import com.intellij.openapi.application.Result;
40 import com.intellij.openapi.command.WriteCommandAction;
41 import com.intellij.openapi.compiler.*;
42 import com.intellij.openapi.module.ModifiableModuleModel;
43 import com.intellij.openapi.module.Module;
44 import com.intellij.openapi.module.ModuleManager;
45 import com.intellij.openapi.module.StdModuleTypes;
46 import com.intellij.openapi.roots.*;
47 import com.intellij.openapi.util.JDOMExternalizable;
48 import com.intellij.openapi.util.Key;
49 import com.intellij.openapi.util.text.StringUtil;
50 import com.intellij.openapi.vfs.VfsUtil;
51 import com.intellij.openapi.vfs.VirtualFile;
52 import com.intellij.psi.PsiFile;
53 import com.intellij.testFramework.PsiTestUtil;
54 import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
55 import com.intellij.testFramework.fixtures.TempDirTestFixture;
56 import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl;
57 import com.intellij.util.ObjectUtils;
58 import com.intellij.util.concurrency.Semaphore;
59 import junit.framework.AssertionFailedError;
60 import org.jetbrains.plugins.groovy.compiler.GroovyCompilerLoader;
61 import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
62 import org.jetbrains.plugins.groovy.util.GroovyUtils;
64 import java.io.File;
65 import java.io.IOException;
66 import java.util.ArrayList;
67 import java.util.List;
69 /**
70 * @author peter
72 public class GroovyCompilerTest extends JavaCodeInsightFixtureTestCase {
73 private TempDirTestFixture myMainOutput;
75 @Override
76 protected void setUp() throws Exception {
77 myMainOutput = new TempDirTestFixtureImpl();
78 myMainOutput.setUp();
79 super.setUp();
80 getProject().getComponent(GroovyCompilerLoader.class).projectOpened();
81 CompilerManagerImpl.testSetup();
83 CompilerProjectExtension.getInstance(getProject()).setCompilerOutputUrl(myMainOutput.findOrCreateDir("out").getUrl());
85 addGroovyLibrary(myModule);
88 private static void addGroovyLibrary(final Module to) {
89 final String root = PathManager.getHomePath() + "/community/lib/";
90 final File[] groovyJars = GroovyUtils.getFilesInDirectoryByPattern(root, GroovyConfigUtils.GROOVY_ALL_JAR_PATTERN);
91 assert groovyJars.length == 1;
92 PsiTestUtil.addLibrary(to, "groovy", root, groovyJars[0].getName());
95 @Override
96 protected void tearDown() throws Exception {
97 myMainOutput.tearDown();
98 myMainOutput = null;
99 super.tearDown();
102 public void testPlainGroovy() throws Throwable {
103 myFixture.addFileToProject("A.groovy", "println '239'");
104 assertEmpty(make());
105 assertOutput("A", "239");
108 public void testJavaDependsOnGroovy() throws Throwable {
109 myFixture.addClass("public class Foo {" +
110 "public static void main(String[] args) { " +
111 " System.out.println(new Bar().foo());" +
112 "}" +
113 "}");
114 myFixture.addFileToProject("Bar.groovy", "class Bar {" +
115 " def foo() {" +
116 " 239" +
117 " }" +
118 "}");
119 assertEmpty(make());
120 assertOutput("Foo", "239");
123 public void testCorrectFailAndCorrect() throws Exception {
124 myFixture.addClass("public class Foo {" +
125 "public static void main(String[] args) { " +
126 " System.out.println(new Bar().foo());" +
127 "}" +
128 "}");
129 final String barText = "class Bar {" + " def foo() { 239 }" + "}";
130 final PsiFile file = myFixture.addFileToProject("Bar.groovy", barText);
131 assertEmpty(make());
132 assertOutput("Foo", "239");
134 setFileText(file, "class Bar {}");
135 try {
136 make();
137 fail("Make should fail");
139 catch (RuntimeException e) {
140 if (!(e.getCause() instanceof AssertionFailedError)) {
141 throw e;
145 setFileText(file, barText);
146 assertEmpty(make());
147 assertOutput("Foo", "239");
150 public void testRenameToJava() throws Throwable {
151 myFixture.addClass("public class Foo {" +
152 "public static void main(String[] args) { " +
153 " System.out.println(new Bar().foo());" +
154 "}" +
155 "}");
157 final PsiFile bar =
158 myFixture.addFileToProject("Bar.groovy", "public class Bar {" + "public int foo() { " + " return 239;" + "}" + "}");
160 assertEmpty(make());
161 assertOutput("Foo", "239");
163 new WriteCommandAction(getProject()) {
164 protected void run(Result result) throws Throwable {
165 bar.setName("Bar.java");
167 }.execute();
169 assertEmpty(make());
170 assertOutput("Foo", "239");
173 public void testTransitiveJavaDependency() throws Throwable {
174 final VirtualFile ifoo = myFixture.addClass("public interface IFoo { int foo(); }").getContainingFile().getVirtualFile();
175 myFixture.addClass("public class Foo implements IFoo {" +
176 " public int foo() { return 239; }" +
177 "}");
178 final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar {" +
179 "Foo foo\n" +
180 "public static void main(String[] args) { " +
181 " System.out.println(new Foo().foo());" +
182 "}" +
183 "}");
184 assertEmpty(make());
185 assertOutput("Bar", "239");
187 touch(ifoo);
188 touch(bar.getVirtualFile());
190 assertTrue(assertOneElement(make()).contains("WARNING: Groovyc stub generation failed"));
191 assertOutput("Bar", "239");
194 public void testTransitiveJavaDependencyThroughGroovy() throws Throwable {
195 myFixture.addClass("public class IFoo { void foo() {} }").getContainingFile().getVirtualFile();
196 myFixture.addFileToProject("Foo.groovy", "class Foo {\n" +
197 " static IFoo f\n" +
198 " public int foo() { return 239; }\n" +
199 "}");
200 final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar extends Foo {" +
201 "public static void main(String[] args) { " +
202 " System.out.println(new Foo().foo());" +
203 "}" +
204 "}");
205 assertEmpty(make());
206 assertOutput("Bar", "239");
208 deleteClassFile("IFoo");
209 touch(bar.getVirtualFile());
211 assertTrue(assertOneElement(make()).contains("WARNING: Groovyc error"));
212 assertOutput("Bar", "239");
215 public void testDeleteTransitiveJavaClass() throws Throwable {
216 myFixture.addClass("public interface IFoo { int foo(); }");
217 myFixture.addClass("public class Foo implements IFoo {" +
218 " public int foo() { return 239; }" +
219 "}");
220 final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar {" +
221 "Foo foo\n" +
222 "public static void main(String[] args) { " +
223 " System.out.println(new Foo().foo());" +
224 "}" +
225 "}");
226 assertEmpty(make());
227 assertOutput("Bar", "239");
229 deleteClassFile("IFoo");
230 touch(bar.getVirtualFile());
232 assertTrue(assertOneElement(make()).contains("WARNING: Groovyc stub generation failed"));
233 assertOutput("Bar", "239");
236 public void testGroovyDependsOnGroovy() throws Throwable {
237 myFixture.addClass("public class JustToMakeGroovyGenerateStubs {}");
238 myFixture.addFileToProject("Foo.groovy", "class Foo { }");
239 final PsiFile bar = myFixture.addFileToProject("Bar.groovy", "class Bar {" +
240 "def foo(Foo f) {}\n" +
241 "public static void main(String[] args) { " +
242 " System.out.println(239);" +
243 "}" +
244 "}");
245 assertEmpty(make());
246 assertOutput("Bar", "239");
248 touch(bar.getVirtualFile());
250 assertEmpty(make());
251 assertOutput("Bar", "239");
254 public void testMakeInTests() throws Throwable {
255 setupTestSources();
256 myFixture.addFileToProject("tests/Super.groovy", "class Super {}");
257 assertEmpty(make());
259 myFixture.addFileToProject("tests/Sub.groovy", "class Sub {\n" +
260 " Super xxx() {}\n" +
261 " static void main(String[] args) {" +
262 " println 'hello'" +
263 " }" +
264 "}");
265 myFixture.addFileToProject("tests/Java.java", "public class Java {}");
266 assertEmpty(make());
267 assertOutput("Sub", "hello");
270 public void testTestsDependOnProduction() throws Throwable {
271 setupTestSources();
272 myFixture.addFileToProject("src/com/Bar.groovy", "package com\n" +
273 "class Bar {}");
274 myFixture.addFileToProject("src/com/ToGenerateStubs.java", "package com;\n" +
275 "public class ToGenerateStubs {}");
276 myFixture.addFileToProject("tests/com/BarTest.groovy", "package com\n" +
277 "class BarTest extends Bar {}");
278 assertEmpty(make());
281 private void setupTestSources() {
282 new WriteCommandAction(getProject()) {
283 protected void run(Result result) throws Throwable {
284 final ModuleRootManager rootManager = ModuleRootManager.getInstance(myModule);
285 final ModifiableRootModel rootModel = rootManager.getModifiableModel();
286 final ContentEntry entry = rootModel.getContentEntries()[0];
287 entry.removeSourceFolder(entry.getSourceFolders()[0]);
288 entry.addSourceFolder(myFixture.getTempDirFixture().findOrCreateDir("src"), false);
289 entry.addSourceFolder(myFixture.getTempDirFixture().findOrCreateDir("tests"), true);
290 rootModel.commit();
292 }.execute();
295 public void testStubForGroovyExtendingJava() throws Exception {
296 myFixture.addClass("public class Foo {}");
297 myFixture.addFileToProject("Bar.groovy", "class Bar extends Foo {}");
298 myFixture.addClass("public class Goo extends Bar {}");
300 assertEmpty(make());
303 public void testDontApplyTransformsFromSameModule() throws Exception {
304 addTransform();
306 myFixture.addClass("public class JavaClassToGenerateStubs {}");
308 assertEmpty(make());
312 private void addTransform() throws IOException {
313 myFixture.addFileToProject("Transf.groovy",
314 "import org.codehaus.groovy.ast.*\n" +
315 "import org.codehaus.groovy.control.*\n" +
316 "import org.codehaus.groovy.transform.*\n" +
317 "@GroovyASTTransformation(phase = CompilePhase.CONVERSION)\n" +
318 "public class Transf implements ASTTransformation {\n" +
319 " void visit(ASTNode[] nodes, SourceUnit sourceUnit) {\n" +
320 " ModuleNode module = nodes[0]\n" +
321 " for (clazz in module.classes) {\n" +
322 " if (clazz.name.contains('Bar')) " +
323 " module.addStaticImportClass('Foo', ClassHelper.makeWithoutCaching(Foo.class));\n" +
324 " }\n" +
325 " }\n" +
326 "}");
328 myFixture.addFileToProject("Foo.groovy", "class Foo {\n" +
329 "static def autoImported() { 239 }\n" +
330 "}");
332 CompilerConfiguration.getInstance(getProject()).addResourceFilePattern("*.ASTTransformation");
334 myFixture.addFileToProject("META-INF/services/org.codehaus.groovy.transform.ASTTransformation", "Transf");
337 public void testApplyTransformsFromDependencies() throws Exception {
338 addTransform();
340 myFixture.addFileToProject("dependent/Bar.groovy", "class Bar {\n" +
341 " static Object zzz = autoImported()\n" +
342 " static void main(String[] args) {\n" +
343 " println zzz\n" +
344 " }\n" +
345 "}");
347 myFixture.addFileToProject("dependent/AJavaClass.java", "class AJavaClass {}");
349 Module dep = addDependentModule();
351 addGroovyLibrary(dep);
353 assertEmpty(make());
354 assertOutput("Bar", "239", dep);
357 private Module addDependentModule() {
358 Module dep = new WriteCommandAction<Module>(getProject()) {
359 @Override
360 protected void run(Result<Module> result) throws Throwable {
361 final ModifiableModuleModel moduleModel = ModuleManager.getInstance(getProject()).getModifiableModel();
362 moduleModel.newModule("dependent/dependent.iml", StdModuleTypes.JAVA);
363 moduleModel.commit();
365 final Module dep = ModuleManager.getInstance(getProject()).findModuleByName("dependent");
366 final ModifiableRootModel model = ModuleRootManager.getInstance(dep).getModifiableModel();
367 model.addModuleOrderEntry(myModule);
368 final VirtualFile depRoot = myFixture.getTempDirFixture().getFile("dependent");
369 final ContentEntry entry = model.addContentEntry(depRoot);
370 entry.addSourceFolder(depRoot, false);
371 model.setSdk(ModuleRootManager.getInstance(myModule).getSdk());
373 //model.getModuleExtension(CompilerModuleExtension.class).inheritCompilerOutputPath(true);
375 model.commit();
376 result.setResult(dep);
378 }.execute().getResultObject();
379 return dep;
382 private void deleteClassFile(final String className) throws IOException {
383 new WriteCommandAction(getProject()) {
384 protected void run(Result result) throws Throwable {
385 final CompilerModuleExtension extension = ModuleRootManager.getInstance(myModule).getModuleExtension(CompilerModuleExtension.class);
386 //noinspection ConstantConditions
387 extension.getCompilerOutputPath().findChild(className + ".class").delete(this);
389 }.execute();
392 private static void touch(VirtualFile file) throws IOException {
393 file.setBinaryContent(file.contentsToByteArray(), file.getModificationStamp() + 1, file.getTimeStamp() + 1);
396 private static void setFileText(final PsiFile file, final String barText) throws IOException {
397 Runnable runnable = new Runnable() {
398 public void run() {
399 try {
400 VfsUtil.saveText(ObjectUtils.assertNotNull(file.getVirtualFile()), barText);
402 catch (IOException e) {
403 throw new RuntimeException(e);
407 ApplicationManager.getApplication().invokeAndWait(runnable, ModalityState.NON_MODAL);
411 private List<String> make() {
412 final Semaphore semaphore = new Semaphore();
413 semaphore.down();
414 final ErrorReportingCallback callback = new ErrorReportingCallback(semaphore);
415 CompilerManager.getInstance(getProject()).make(callback);
416 semaphore.waitFor();
417 callback.throwException();
418 return callback.getMessages();
422 private void compile(VirtualFile... files) {
423 final Semaphore semaphore = new Semaphore();
424 semaphore.down();
425 final ErrorReportingCallback callback = new ErrorReportingCallback(semaphore);
426 CompilerManager.getInstance(getProject()).compile(files, new ErrorReportingCallback(semaphore), false);
427 semaphore.waitFor();
428 callback.throwException();
432 private void assertOutput(String className, String output) throws ExecutionException {
433 assertOutput(className, output, myModule);
436 private void assertOutput(String className, String output, final Module module) throws ExecutionException {
437 final ApplicationConfiguration configuration =
438 new ApplicationConfiguration("app", getProject(), ApplicationConfigurationType.getInstance());
439 configuration.setModule(module);
440 configuration.setMainClassName(className);
441 final DefaultRunExecutor extension = Executor.EXECUTOR_EXTENSION_NAME.findExtension(DefaultRunExecutor.class);
442 final ExecutionEnvironment environment = new ExecutionEnvironment(configuration, new RunnerSettings<JDOMExternalizable>(null, null),null, DataManager.getInstance().getDataContext());
443 final DefaultJavaProgramRunner runner = ProgramRunner.PROGRAM_RUNNER_EP.findExtension(DefaultJavaProgramRunner.class);
444 final StringBuffer sb = new StringBuffer();
445 final Semaphore semaphore = new Semaphore();
446 semaphore.down();
447 runner.execute(extension, environment, new ProgramRunner.Callback() {
448 public void processStarted(RunContentDescriptor descriptor) {
449 final ProcessHandler handler = descriptor.getProcessHandler();
451 assert handler != null;
452 handler.addProcessListener(new ProcessAdapter() {
453 public void onTextAvailable(ProcessEvent event, Key outputType) {
454 if (ProcessOutputTypes.SYSTEM != outputType) {
455 sb.append(event.getText());
459 @Override
460 public void processTerminated(ProcessEvent event) {
461 semaphore.up();
466 semaphore.waitFor();
467 assertEquals(output.trim(), StringUtil.convertLineSeparators(sb.toString().trim()));
470 private static class ErrorReportingCallback implements CompileStatusNotification {
471 private final Semaphore mySemaphore;
472 private Throwable myError;
473 private List<String> myMessages = new ArrayList<String>();
475 public ErrorReportingCallback(Semaphore semaphore) {
476 mySemaphore = semaphore;
479 public void finished(boolean aborted, int errors, int warnings, final CompileContext compileContext) {
480 try {
481 assertFalse("Code did not compile!", aborted);
482 for (CompilerMessageCategory category : CompilerMessageCategory.values()) {
483 for (CompilerMessage message : compileContext.getMessages(category)) {
484 final String msg = message.getMessage();
485 if (category != CompilerMessageCategory.INFORMATION || !msg.startsWith("Compilation completed successfully")) {
486 myMessages.add(category + ": " + msg);
490 if (errors > 0) {
491 fail("Compiler errors occurred! " + StringUtil.join(myMessages, "\n"));
494 catch (Throwable t) {
495 myError = t;
497 finally {
498 mySemaphore.up();
502 void throwException() {
503 if (myError != null) {
504 throw new RuntimeException(myError);
508 public List<String> getMessages() {
509 return myMessages;