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
;
65 import java
.io
.IOException
;
66 import java
.util
.ArrayList
;
67 import java
.util
.List
;
72 public class GroovyCompilerTest
extends JavaCodeInsightFixtureTestCase
{
73 private TempDirTestFixture myMainOutput
;
76 protected void setUp() throws Exception
{
77 myMainOutput
= new TempDirTestFixtureImpl();
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());
96 protected void tearDown() throws Exception
{
97 myMainOutput
.tearDown();
102 public void testPlainGroovy() throws Throwable
{
103 myFixture
.addFileToProject("A.groovy", "println '239'");
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());" +
114 myFixture
.addFileToProject("Bar.groovy", "class Bar {" +
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());" +
129 final String barText
= "class Bar {" + " def foo() { 239 }" + "}";
130 final PsiFile file
= myFixture
.addFileToProject("Bar.groovy", barText
);
132 assertOutput("Foo", "239");
134 setFileText(file
, "class Bar {}");
137 fail("Make should fail");
139 catch (RuntimeException e
) {
140 if (!(e
.getCause() instanceof AssertionFailedError
)) {
145 setFileText(file
, barText
);
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());" +
158 myFixture
.addFileToProject("Bar.groovy", "public class Bar {" + "public int foo() { " + " return 239;" + "}" + "}");
161 assertOutput("Foo", "239");
163 new WriteCommandAction(getProject()) {
164 protected void run(Result result
) throws Throwable
{
165 bar
.setName("Bar.java");
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; }" +
178 final PsiFile bar
= myFixture
.addFileToProject("Bar.groovy", "class Bar {" +
180 "public static void main(String[] args) { " +
181 " System.out.println(new Foo().foo());" +
185 assertOutput("Bar", "239");
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" +
198 " public int foo() { return 239; }\n" +
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());" +
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; }" +
220 final PsiFile bar
= myFixture
.addFileToProject("Bar.groovy", "class Bar {" +
222 "public static void main(String[] args) { " +
223 " System.out.println(new Foo().foo());" +
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);" +
246 assertOutput("Bar", "239");
248 touch(bar
.getVirtualFile());
251 assertOutput("Bar", "239");
254 public void testMakeInTests() throws Throwable
{
256 myFixture
.addFileToProject("tests/Super.groovy", "class Super {}");
259 myFixture
.addFileToProject("tests/Sub.groovy", "class Sub {\n" +
260 " Super xxx() {}\n" +
261 " static void main(String[] args) {" +
265 myFixture
.addFileToProject("tests/Java.java", "public class Java {}");
267 assertOutput("Sub", "hello");
270 public void testTestsDependOnProduction() throws Throwable
{
272 myFixture
.addFileToProject("src/com/Bar.groovy", "package com\n" +
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 {}");
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);
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 {}");
303 public void testDontApplyTransformsFromSameModule() throws Exception
{
306 myFixture
.addClass("public class JavaClassToGenerateStubs {}");
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" +
328 myFixture
.addFileToProject("Foo.groovy", "class Foo {\n" +
329 "static def autoImported() { 239 }\n" +
332 CompilerConfiguration
.getInstance(getProject()).addResourceFilePattern("*.ASTTransformation");
334 myFixture
.addFileToProject("META-INF/services/org.codehaus.groovy.transform.ASTTransformation", "Transf");
337 public void testApplyTransformsFromDependencies() throws Exception
{
340 myFixture
.addFileToProject("dependent/Bar.groovy", "class Bar {\n" +
341 " static Object zzz = autoImported()\n" +
342 " static void main(String[] args) {\n" +
347 myFixture
.addFileToProject("dependent/AJavaClass.java", "class AJavaClass {}");
349 Module dep
= addDependentModule();
351 addGroovyLibrary(dep
);
354 assertOutput("Bar", "239", dep
);
357 private Module
addDependentModule() {
358 Module dep
= new WriteCommandAction
<Module
>(getProject()) {
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);
376 result
.setResult(dep
);
378 }.execute().getResultObject();
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);
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() {
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();
414 final ErrorReportingCallback callback
= new ErrorReportingCallback(semaphore
);
415 CompilerManager
.getInstance(getProject()).make(callback
);
417 callback
.throwException();
418 return callback
.getMessages();
422 private void compile(VirtualFile... files) {
423 final Semaphore semaphore = new Semaphore();
425 final ErrorReportingCallback callback = new ErrorReportingCallback(semaphore);
426 CompilerManager.getInstance(getProject()).compile(files, new ErrorReportingCallback(semaphore), false);
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();
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());
460 public void processTerminated(ProcessEvent event
) {
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
) {
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
);
491 fail("Compiler errors occurred! " + StringUtil
.join(myMessages
, "\n"));
494 catch (Throwable t
) {
502 void throwException() {
503 if (myError
!= null) {
504 throw new RuntimeException(myError
);
508 public List
<String
> getMessages() {