diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java index 13ef8d37cd5c..3bd67509e586 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -1769,7 +1769,12 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb ApplicationManager.getApplication().assertIsDispatchThread(); //TODO: remove this requirement ((XDebugSessionImpl)getXdebugProcess().getSession()).reset(); myState.set(STATE_INITIAL); - myRequestManager.processDetached(this, false); + getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + myRequestManager.processDetached(DebugProcessImpl.this, false); + } + }); myConnection = environment.getRemoteConnection(); getManagerThread().restartIfNeeded(); createVirtualMachine(environment.getSessionName(), environment.isPollConnection()); diff --git a/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java b/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java index cb6c6a95c88e..9fb6e4cf989b 100644 --- a/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java +++ b/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java @@ -16,6 +16,7 @@ package com.intellij.execution; import com.intellij.ExtensionPoints; +import com.intellij.debugger.impl.GenericDebuggerRunnerSettings; import com.intellij.execution.configurations.*; import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessAdapter; @@ -216,11 +217,16 @@ public abstract class JavaTestFrameworkRunnableState" + "Please specify single working directory, or change test scope to single module."); @@ -228,7 +234,7 @@ public abstract class JavaTestFrameworkRunnableStatePlease change fork mode to <none> to " + actionName.toLowerCase( Locale.ENGLISH) + "."); diff --git a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java index d4e529772a72..ac4c581f15e9 100644 --- a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java +++ b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocator.java @@ -22,10 +22,11 @@ import com.intellij.execution.junit2.info.MethodLocation; import com.intellij.execution.testframework.sm.runner.SMTestLocator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiManager; import com.intellij.psi.PsiMethod; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.ClassUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -50,32 +51,27 @@ public class JavaTestLocator implements SMTestLocator { path = path.substring(0, idx); } - JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project); if (SUITE_PROTOCOL.equals(protocol)) { path = StringUtil.trimEnd(path, "."); - PsiClass[] classes = javaPsiFacade.findClasses(path, scope); - if (classes.length > 0) { + PsiClass aClass = ClassUtil.findPsiClass(PsiManager.getInstance(project), path, null, true, scope); + if (aClass != null) { results = ContainerUtil.newSmartList(); - for (PsiClass aClass : classes) { - results.add(paramName != null ? PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName) - : new PsiLocation(project, aClass)); - } + results.add(paramName != null ? PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName) + : new PsiLocation(project, aClass)); } } else if (TEST_PROTOCOL.equals(protocol)) { String className = StringUtil.getPackageName(path); if (!StringUtil.isEmpty(className)) { String methodName = StringUtil.getShortName(path); - PsiClass[] classes = javaPsiFacade.findClasses(className, scope); - if (classes.length > 0) { + PsiClass aClass = ClassUtil.findPsiClass(PsiManager.getInstance(project), className, null, true, scope); + if (aClass != null) { results = ContainerUtil.newSmartList(); - for (PsiClass aClass : classes) { - PsiMethod[] methods = aClass.findMethodsByName(methodName, true); - if (methods.length > 0) { - for (PsiMethod method : methods) { - results.add(paramName != null ? new PsiMemberParameterizedLocation(project, method, aClass, paramName) - : MethodLocation.elementInClass(method, aClass)); - } + PsiMethod[] methods = aClass.findMethodsByName(methodName, true); + if (methods.length > 0) { + for (PsiMethod method : methods) { + results.add(paramName != null ? new PsiMemberParameterizedLocation(project, method, aClass, paramName) + : MethodLocation.elementInClass(method, aClass)); } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java index 6f503534cb9c..b4ca43f4cc0b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java @@ -109,7 +109,8 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } if (ExceptionUtil.getThrownCheckedExceptions(new PsiElement[] {body}).isEmpty()) { - if (isCollectCall(body, statement.getIterationParameter())) { + if (!(iteratedValueType instanceof PsiClassType && ((PsiClassType)iteratedValueType).isRaw()) && + isCollectCall(body, statement.getIterationParameter())) { holder.registerProblem(iteratedValue, "Can be replaced with collect call", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new ReplaceWithCollectCallFix()); } else if (!isTrivial(body, statement.getIterationParameter())) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeRawCollection.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeRawCollection.java new file mode 100644 index 000000000000..fd51db79f84e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeRawCollection.java @@ -0,0 +1,22 @@ +// "Replace with forEach" "false" +import java.util.*; +import java.util.stream.Collectors + +class A { + public static List toTypedList1(Collection collection, Class aClass) { + if (collection.size() == 0) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(collection.size()); + + for (Object obj : collection) { + result.add(aClass.cast(obj)); + } + + return result; + } + + + +} \ No newline at end of file diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java index c8a8361b020b..a00ffe6c7f4d 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -286,7 +286,6 @@ public abstract class HTMLComposerImpl extends HTMLComposer { @Override public void appendListItem(StringBuffer buf, RefElement refElement) { startListItem(buf); - buf.append(CLOSE_TAG); appendElementReference(buf, refElement, true); appendAdditionalListItemInfo(buf, refElement); doneListItem(buf); diff --git a/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java b/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java index 6e313990e112..9dab9c62b3a3 100644 --- a/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java +++ b/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java @@ -58,7 +58,7 @@ public class BootstrapClassLoaderUtil extends ClassUtilCore { UrlClassLoader.Builder builder = UrlClassLoader.build() .urls(filterClassPath(new ArrayList(classpath))) - .allowLock(false) + .allowLock() .usePersistentClasspathIndexForLocalClassDirectories() .useCache(); if (Boolean.valueOf(System.getProperty(PROPERTY_ALLOW_BOOTSTRAP_RESOURCES, "true"))) { diff --git a/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java b/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java index 49a400022a99..63aabc7bf446 100644 --- a/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java +++ b/platform/diff-impl/src/com/intellij/diff/impl/CacheDiffRequestChainProcessor.java @@ -21,9 +21,9 @@ import com.intellij.diff.chains.DiffRequestProducer; import com.intellij.diff.chains.DiffRequestProducerException; import com.intellij.diff.requests.*; import com.intellij.diff.tools.util.SoftHardCacheMap; +import com.intellij.diff.util.DiffTaskQueue; import com.intellij.diff.util.DiffUserDataKeys; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; -import com.intellij.diff.util.WaitingBackgroundableTaskExecutor; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -33,9 +33,11 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.util.Consumer; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.Convertor; +import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.CalledInBackground; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,7 +53,7 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso @NotNull private final SoftHardCacheMap myRequestCache = new SoftHardCacheMap(5, 5); - @NotNull private final WaitingBackgroundableTaskExecutor myTaskExecutor = new WaitingBackgroundableTaskExecutor(); + @NotNull private final DiffTaskQueue myQueue = new DiffTaskQueue(); public CacheDiffRequestChainProcessor(@Nullable Project project, @NotNull DiffRequestChain requestChain) { super(project, requestChain); @@ -62,6 +64,7 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso // Update // + @CalledInAwt public void updateRequest(final boolean force, @Nullable final ScrollToPolicy scrollToChangePolicy) { List requests = myRequestChain.getRequests(); int index = myRequestChain.getIndex(); @@ -78,12 +81,13 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso return; } - myTaskExecutor.execute( - new Convertor() { + myQueue.executeAndTryWait( + new Function() { @Override - public Runnable convert(ProgressIndicator indicator) { + public Runnable fun(ProgressIndicator indicator) { final DiffRequest request = loadRequest(producer, indicator); return new Runnable() { + @CalledInAwt @Override public void run() { myRequestCache.put(producer, request); @@ -131,9 +135,10 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso // @Override + @CalledInAwt protected void onDispose() { super.onDispose(); - myTaskExecutor.abort(); + Disposer.dispose(myQueue); myRequestCache.clear(); } diff --git a/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java b/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java index 2b6fbcb3a798..74674c4f83ab 100644 --- a/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java +++ b/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java @@ -299,6 +299,7 @@ public abstract class DiffRequestProcessor implements Disposable { protected void onAfterNavigate() { } + @CalledInAwt protected void onDispose() { } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java b/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java index 16dd6a0fd80c..8e03481e1c7a 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java @@ -20,7 +20,7 @@ import com.intellij.diff.FrameDiffTool; import com.intellij.diff.FrameDiffTool.DiffViewer; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.tools.util.DiffDataKeys; -import com.intellij.diff.util.WaitingBackgroundableTaskExecutor; +import com.intellij.diff.util.DiffTaskQueue; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataProvider; @@ -31,8 +31,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; -import com.intellij.util.Alarm; -import com.intellij.util.containers.Convertor; +import com.intellij.util.Function; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.*; @@ -47,9 +46,7 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { @NotNull protected final DiffContext myContext; @NotNull protected final ContentDiffRequest myRequest; - @NotNull private final WaitingBackgroundableTaskExecutor myTaskExecutor = new WaitingBackgroundableTaskExecutor(); - @NotNull private final Alarm myAlarm = new Alarm(); - + @NotNull private final DiffTaskQueue myTaskExecutor = new DiffTaskQueue(); @NotNull private final AtomicBoolean myDisposed = new AtomicBoolean(false); public DiffViewerBase(@NotNull DiffContext context, @NotNull ContentDiffRequest request) { @@ -75,15 +72,11 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { public final void dispose() { if (!myDisposed.compareAndSet(false, true)) return; - Disposer.dispose(myAlarm); - abortRediff(); - onDispose(); UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { - abortRediff(); onDisposeAwt(); } }); @@ -92,14 +85,10 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { @CalledInAwt public final void scheduleRediff() { if (myDisposed.get()) return; - myTaskExecutor.abort(); - final int modificationStamp = myTaskExecutor.getModificationStamp(); - myAlarm.cancelAllRequests(); - myAlarm.addRequest(new Runnable() { + myTaskExecutor.abortAndSchedule(new Runnable() { @Override public void run() { - if (modificationStamp != myTaskExecutor.getModificationStamp()) return; rediff(); } }, ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS); @@ -127,10 +116,10 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { int waitMillis = trySync || tryRediffSynchronously() ? ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS : 0; - myTaskExecutor.execute( - new Convertor() { + myTaskExecutor.executeAndTryWait( + new Function() { @Override - public Runnable convert(ProgressIndicator indicator) { + public Runnable fun(ProgressIndicator indicator) { return performRediff(indicator); } }, @@ -199,6 +188,7 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider { protected abstract Runnable performRediff(@NotNull ProgressIndicator indicator); protected void onDispose() { + Disposer.dispose(myTaskExecutor); } @CalledInAwt diff --git a/platform/diff-impl/src/com/intellij/diff/util/WaitingBackgroundableTaskExecutor.java b/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java similarity index 63% rename from platform/diff-impl/src/com/intellij/diff/util/WaitingBackgroundableTaskExecutor.java rename to platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java index 3a95b5c60b9f..9cf267a06075 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/WaitingBackgroundableTaskExecutor.java +++ b/platform/diff-impl/src/com/intellij/diff/util/BackgroundTaskUtil.java @@ -21,7 +21,7 @@ import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.EmptyRunnable; -import com.intellij.util.containers.Convertor; +import com.intellij.util.Function; import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,59 +34,38 @@ import java.util.concurrent.atomic.AtomicReference; * Executor to perform long operations on pooled thread * Is is used to reduce blinking, in case of fast end of background task. */ -public class WaitingBackgroundableTaskExecutor { +public class BackgroundTaskUtil { private static final Runnable TOO_SLOW_OPERATION = new EmptyRunnable(); - private int myModificationStamp = 0; - @Nullable private ProgressIndicator myProgressIndicator; - @CalledInAwt - public int getModificationStamp() { - return myModificationStamp; + @NotNull + public static ProgressIndicator executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis) { + return executeAndTryWait(backgroundTask, onSlowAction, waitMillis, false); } @CalledInAwt - public void abort() { - if (myProgressIndicator != null) { - myProgressIndicator.cancel(); - myProgressIndicator = null; - myModificationStamp++; - } - } - - @CalledInAwt - public void execute(@NotNull final Convertor backgroundTask, - @Nullable final Runnable onSlowAction, - final int waitMillis) { - execute(backgroundTask, onSlowAction, waitMillis, false); - } - - @CalledInAwt - public void execute(@NotNull final Convertor backgroundTask, - @Nullable final Runnable onSlowAction, - final int waitMillis, - final boolean forceEDT) { - abort(); - - myModificationStamp++; - final int modificationStamp = myModificationStamp; - + @NotNull + public static ProgressIndicator executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis, + final boolean forceEDT) { final ModalityState modality = ModalityState.current(); - myProgressIndicator = new EmptyProgressIndicator() { + final ProgressIndicator indicator = new EmptyProgressIndicator() { @NotNull @Override public ModalityState getModalityState() { return modality; } }; - final ProgressIndicator indicator = myProgressIndicator; final Semaphore semaphore = new Semaphore(0); final AtomicReference resultRef = new AtomicReference(); if (forceEDT) { - Runnable result = backgroundTask.convert(indicator); - finish(result, modificationStamp, indicator); + Runnable callback = backgroundTask.fun(indicator); + finish(callback, indicator); } else { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @@ -95,18 +74,18 @@ public class WaitingBackgroundableTaskExecutor { ProgressManager.getInstance().executeProcessUnderProgress(new Runnable() { @Override public void run() { - final Runnable result = backgroundTask.convert(indicator); + final Runnable callback = backgroundTask.fun(indicator); if (indicator.isCanceled()) { semaphore.release(); return; } - if (!resultRef.compareAndSet(null, result)) { + if (!resultRef.compareAndSet(null, callback)) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - finish(result, modificationStamp, indicator); + finish(callback, indicator); } }, modality); } @@ -123,19 +102,20 @@ public class WaitingBackgroundableTaskExecutor { } if (!resultRef.compareAndSet(null, TOO_SLOW_OPERATION)) { // update presentation in the same thread to reduce blinking, caused by 'invokeLater' and fast background operation - finish(resultRef.get(), modificationStamp, indicator); + finish(resultRef.get(), indicator); } else { if (onSlowAction != null) onSlowAction.run(); } } + + return indicator; } @CalledInAwt - private void finish(@NotNull Runnable result, int modificationStamp, @NotNull ProgressIndicator indicator) { + private static void finish(@NotNull Runnable result, @NotNull ProgressIndicator indicator) { if (indicator.isCanceled()) return; - if (myModificationStamp != modificationStamp) return; - result.run(); + indicator.stop(); } } diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java b/platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java new file mode 100644 index 000000000000..96027e2a7356 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffTaskQueue.java @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.util; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.Alarm; +import com.intellij.util.Function; +import org.jetbrains.annotations.*; + +import java.util.concurrent.atomic.AtomicReference; + +public class DiffTaskQueue implements Disposable { + @NotNull private final Object LOCK = new Object(); + @NotNull private final Alarm myAlarm = new Alarm(); + + private boolean myDisposed; + @NotNull private final AtomicReference myProgressIndicator = new AtomicReference(); + + @CalledInAny + public void dispose() { + // if EDT is awaiting for background progress in executeAndTryWait - it holds LOCK. + // so we want to try cancel indicator before getting the lock. + cancelProgress(); + synchronized (LOCK) { + if (myDisposed) return; + myDisposed = true; + cancelProgress(); + Disposer.dispose(myAlarm); + } + } + + @CalledInAwt + public void abort() { + synchronized (LOCK) { + cancelProgress(); + myAlarm.cancelAllRequests(); + } + } + + private void cancelProgress() { + ProgressIndicator indicator = myProgressIndicator.getAndSet(null); + if (indicator != null) indicator.cancel(); + } + + @CalledInAwt + public void abortAndSchedule(@NotNull final Runnable task, int millis) { + synchronized (LOCK) { + if (myDisposed) return; + abort(); + + myAlarm.addRequest(new Runnable() { + @Override + public void run() { + synchronized (LOCK) { + if (myDisposed) return; + } + task.run(); + } + }, millis); + } + } + + @CalledInAwt + public void executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis) { + executeAndTryWait(backgroundTask, onSlowAction, waitMillis, false); + } + + @CalledInAwt + public void executeAndTryWait(@NotNull final Function backgroundTask, + @Nullable final Runnable onSlowAction, + final int waitMillis, + final boolean forceEDT) { + synchronized (LOCK) { + if (myDisposed) return; + abort(); + + Function function = new Function() { + @Override + @CalledInBackground + public Runnable fun(final ProgressIndicator indicator) { + final Runnable callback = backgroundTask.fun(indicator); + return new Runnable() { + @Override + @CalledInAwt + public void run() { + synchronized (LOCK) { + if (myDisposed) return; + indicator.checkCanceled(); + } + callback.run(); + } + }; + } + }; + + myProgressIndicator.set(BackgroundTaskUtil.executeAndTryWait(function, onSlowAction, waitMillis, forceEDT)); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java index 7516febe596b..c5b459261fa8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -556,7 +556,7 @@ public class FileWatcher { case CREATE: case DELETE: for (String p : paths) { - myDirtyPaths.dirtyPaths.add(p); + myDirtyPaths.dirtyPathsRecursive.add(p); String parentPath = new File(p).getParent(); if (parentPath != null) { myDirtyPaths.dirtyPaths.add(parentPath); diff --git a/platform/platform-tests/testData/badPlugins/plugins/BadPlugin2.jar b/platform/platform-tests/testData/badPlugins/plugins/BadPlugin2.jar index 5ff3678492ce..56df54520e2e 100644 Binary files a/platform/platform-tests/testData/badPlugins/plugins/BadPlugin2.jar and b/platform/platform-tests/testData/badPlugins/plugins/BadPlugin2.jar differ diff --git a/platform/platform-tests/testSrc/com/intellij/application/BadPluginTest.java b/platform/platform-tests/testSrc/com/intellij/application/BadPluginTest.java index 8c2c7f3236be..6bb618d3970f 100644 --- a/platform/platform-tests/testSrc/com/intellij/application/BadPluginTest.java +++ b/platform/platform-tests/testSrc/com/intellij/application/BadPluginTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.application; import com.intellij.ide.plugins.IdeaPluginDescriptor; @@ -6,7 +21,7 @@ import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.testFramework.PlatformLangTestCase; +import com.intellij.testFramework.PlatformTestCase; import com.intellij.testFramework.PlatformTestUtil; import java.io.File; @@ -15,24 +30,22 @@ import java.util.List; /** * @author Dmitry Avdeev - * Date: 1/9/13 + * @since 9.01.2013 */ -public class BadPluginTest extends PlatformLangTestCase { - - private static final String COM_YOURCOMPANY_UNIQUE_PLUGIN_ID = "com.yourcompany.unique.plugin.id"; +public class BadPluginTest extends PlatformTestCase { + private static final String COM_YOUR_COMPANY_UNIQUE_PLUGIN_ID = "com.your.company.unique.plugin.id"; public void testBadPlugin() throws Exception { - IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId(COM_YOURCOMPANY_UNIQUE_PLUGIN_ID)); + IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId(COM_YOUR_COMPANY_UNIQUE_PLUGIN_ID)); if (plugin == null) return; List disabledPlugins = new ArrayList(); PluginManagerCore.loadDisabledPlugins(PathManager.getConfigPath(), disabledPlugins); assertEquals(1, disabledPlugins.size()); - assertEquals(COM_YOURCOMPANY_UNIQUE_PLUGIN_ID, disabledPlugins.get(0)); + assertEquals(COM_YOUR_COMPANY_UNIQUE_PLUGIN_ID, disabledPlugins.get(0)); } @Override protected void setUp() throws Exception { - String path = PlatformTestUtil.getCommunityPath() + "/platform/platform-tests/testData/badPlugins"; File directory = createTempDirectory(false); FileUtil.copyDir(new File(path), directory); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java index 8baa5a78c50f..7f52a1df5ff6 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java @@ -714,6 +714,33 @@ public class FileWatcherTest extends PlatformLangTestCase { } } + public void testDisplacementByIsomorphicTree() throws Exception { + File top = createTestDir("top"); + File up = createTestDir(top, "up"); + File middle = createTestDir(up, "middle"); + File file = createTestFile(middle, "file.txt", "original content"); + File up_copy = new File(top, "up_copy"); + FileUtil.copyDir(up, up_copy); + FileUtil.writeToFile(file, "new content"); + + VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + assertNotNull(vFile); + assertEquals("new content", VfsUtilCore.loadText(vFile)); + + LocalFileSystem.WatchRequest request = watch(up); + try { + myAccept = true; + FileUtil.rename(up, new File(top, "up.bak")); + FileUtil.rename(up_copy, up); + assertEvent(VFileContentChangeEvent.class, file.getPath()); + assertTrue(vFile.isValid()); + assertEquals("original content", VfsUtilCore.loadText(vFile)); + } + finally { + unwatch(request); + } + } + @NotNull private LocalFileSystem.WatchRequest watch(File watchFile) { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java index 218cc4c865d6..12aa12edd134 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; +import com.intellij.testFramework.LightPlatformTestCase; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; @@ -33,7 +34,37 @@ import java.util.Set; import static com.intellij.openapi.util.io.IoTestUtil.*; import static com.intellij.testFramework.PlatformTestUtil.assertPathsEqual; -public class SymlinkHandlingTest extends SymlinkTestCase { +public class SymlinkHandlingTest extends LightPlatformTestCase { + protected LocalFileSystem myFileSystem; + protected File myTempDir; + + @Override + protected void setUp() throws Exception { + super.setUp(); + myFileSystem = LocalFileSystem.getInstance(); + myTempDir = createTestDir("temp"); + } + + @Override + protected void tearDown() throws Exception { + try { + super.tearDown(); + } + finally { + delete(myTempDir); + } + } + + @Override + protected void runTest() throws Throwable { + if (SystemInfo.areSymLinksSupported) { + super.runTest(); + } + else { + System.err.println("Skipped: " + getName()); + } + } + public void testMissingLink() throws Exception { File missingFile = new File(myTempDir, "missing_file"); assertTrue(missingFile.getPath(), !missingFile.exists() || missingFile.delete()); @@ -362,6 +393,17 @@ public class SymlinkHandlingTest extends SymlinkTestCase { return myFileSystem.findFileByPath(ioFile.getPath()); } + protected void refresh() { + assertTrue(myTempDir.getPath(), myTempDir.isDirectory() || myTempDir.mkdirs()); + + VirtualFile tempDir = myFileSystem.refreshAndFindFileByIoFile(myTempDir); + assertNotNull(myTempDir.getPath(), tempDir); + + tempDir.getChildren(); + tempDir.refresh(false, true); + VfsUtilCore.visitChildrenRecursively(tempDir, new VirtualFileVisitor() { }); + } + private static void assertBrokenLink(@NotNull VirtualFile link) { assertTrue(link.is(VFileProperty.SYMLINK)); assertEquals(0, link.getLength()); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkTestCase.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkTestCase.java deleted file mode 100644 index 999fa434481c..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkTestCase.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.vfs.local; - -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.io.IoTestUtil; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileVisitor; -import com.intellij.testFramework.LightPlatformLangTestCase; - -import java.io.File; - -import static com.intellij.openapi.util.io.IoTestUtil.createTestDir; - -public abstract class SymlinkTestCase extends LightPlatformLangTestCase { - protected LocalFileSystem myFileSystem; - protected File myTempDir; - - @Override - protected void setUp() throws Exception { - super.setUp(); - myFileSystem = LocalFileSystem.getInstance(); - myTempDir = createTestDir("temp"); - } - - @Override - protected void tearDown() throws Exception { - try { - super.tearDown(); - } - finally { - IoTestUtil.delete(myTempDir); - } - } - - @Override - protected void runTest() throws Throwable { - if (SystemInfo.areSymLinksSupported) { - super.runTest(); - } - else { - System.err.println("Skipped: " + getName()); - } - } - - protected void refresh() { - assertTrue(myTempDir.getPath(), myTempDir.isDirectory() || myTempDir.mkdirs()); - - VirtualFile tempDir = myFileSystem.refreshAndFindFileByIoFile(myTempDir); - assertNotNull(myTempDir.getPath(), tempDir); - - tempDir.getChildren(); - tempDir.refresh(false, true); - VfsUtilCore.visitChildrenRecursively(tempDir, new VirtualFileVisitor() { }); - } -} diff --git a/platform/testFramework/src/com/intellij/testFramework/CompositeException.java b/platform/testFramework/src/com/intellij/testFramework/CompositeException.java index 59e73bf718d4..4ec1c8b21107 100644 --- a/platform/testFramework/src/com/intellij/testFramework/CompositeException.java +++ b/platform/testFramework/src/com/intellij/testFramework/CompositeException.java @@ -130,7 +130,8 @@ public class CompositeException extends Exception { sb.append(line); line = exceptionProcessor.fun(exception); - if (!line.endsWith("\n")) line += '\n'; + if (line == null) line = "null\n"; + else if (!line.endsWith("\n")) line += '\n'; stringProcessor.process(line); sb.append(line); } diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java index d4bc09129695..670bcfd7bd43 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java @@ -458,15 +458,18 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro result.add(e); } - //cleanTheWorld(); try { - myEditorListenerTracker.checkListenersLeak(); + if (myEditorListenerTracker != null) { + myEditorListenerTracker.checkListenersLeak(); + } } catch (AssertionError error) { result.add(error); } try { - myThreadTracker.checkLeak(); + if (myThreadTracker != null) { + myThreadTracker.checkLeak(); + } } catch (AssertionError error) { result.add(error); @@ -477,9 +480,6 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro catch (Throwable error) { result.add(error); } - //if (directoryIndex != null) { - // directoryIndex.assertAncestorConsistent(); - //} } finally { myProjectManager = null; diff --git a/platform/testFramework/testFramework.iml b/platform/testFramework/testFramework.iml index 19a4097b1f15..bb99b4dbda56 100644 --- a/platform/testFramework/testFramework.iml +++ b/platform/testFramework/testFramework.iml @@ -14,12 +14,12 @@ - + - + @@ -27,5 +27,4 @@ - - + \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java index 672a4b6190f5..947f144e8b3a 100644 --- a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java +++ b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java @@ -86,8 +86,12 @@ public class PatienceIntLCS { int matched = matching[0].length; assert matched > 0; + c1 = matching[0][0]; + c2 = matching[1][0]; + + checkReduction(c1, c2); PatienceIntLCS patienceDiff = - new PatienceIntLCS(myFirst, mySecond, start1, matching[0][0], start2, matching[1][0], myChanges1, myChanges2); + new PatienceIntLCS(myFirst, mySecond, start1, c1, start2, c2, myChanges1, myChanges2); patienceDiff.execute(); for (int i = 1; i < matching[0].length; i++) { diff --git a/platform/util/src/com/intellij/util/lang/JarLoader.java b/platform/util/src/com/intellij/util/lang/JarLoader.java index 68c0bfb5479e..17b8181c619b 100644 --- a/platform/util/src/com/intellij/util/lang/JarLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarLoader.java @@ -17,11 +17,11 @@ package com.intellij.util.lang; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.io.URLUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import sun.misc.Resource; +import java.io.File; import java.io.IOException; import java.lang.ref.SoftReference; import java.net.URL; @@ -30,15 +30,18 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; class JarLoader extends Loader { - private final URL myURL; + private final File myCanonicalFile; + private final boolean myCanLockJar; // true implies that the zipfile will not be modified in the lifetime of the JarLoader private SoftReference myMemoryLoader; + private volatile SoftReference myZipFileSoftReference; // Used only when myCanLockJar==true - // todo drop unused parameter JarLoader(URL url, @SuppressWarnings("unused") boolean canLockJar, int index, boolean preloadJarContents) throws IOException { - super(new URL(URLUtil.JAR_PROTOCOL, "", -1, url + "!/"), index); - myURL = url; + super(new URL("jar", "", -1, url + "!/"), index); - ZipFile zipFile = new ZipFile(getFileUrl()); + myCanonicalFile = new File(FileUtil.unquote(url.getFile())).getCanonicalFile(); + myCanLockJar = canLockJar; + + ZipFile zipFile = getZipFile(); // IOException from opening is propagated to caller if zip file isn't valid, try { if (preloadJarContents) { JarMemoryLoader loader = JarMemoryLoader.load(zipFile, getBaseURL()); @@ -48,18 +51,14 @@ class JarLoader extends Loader { } } finally { - zipFile.close(); + releaseZipFile(zipFile); } } - private String getFileUrl() throws IOException { - return FileUtil.unquote(myURL.getFile()); - } - @NotNull @Override public ClasspathCache.LoaderData buildData() throws IOException { - ZipFile zipFile = new ZipFile(getFileUrl()); + ZipFile zipFile = getZipFile(); try { ClasspathCache.LoaderData loaderData = new ClasspathCache.LoaderData(); Enumeration entries = zipFile.entries(); @@ -72,21 +71,21 @@ class JarLoader extends Loader { return loaderData; } finally { - zipFile.close(); + releaseZipFile(zipFile); } } @Override @Nullable Resource getResource(String name, boolean flag) { - JarMemoryLoader loader = com.intellij.reference.SoftReference.dereference(myMemoryLoader); + JarMemoryLoader loader = myMemoryLoader != null? myMemoryLoader.get() : null; if (loader != null) { Resource resource = loader.getResource(name); if (resource != null) return resource; } try { - ZipFile zipFile = new ZipFile(getFileUrl()); + ZipFile zipFile = getZipFile(); try { ZipEntry entry = zipFile.getEntry(name); if (entry != null) { @@ -94,18 +93,58 @@ class JarLoader extends Loader { } } finally { - zipFile.close(); + releaseZipFile(zipFile); } } catch (Exception e) { - Logger.getInstance(JarLoader.class).error("url: " + myURL, e); + error("file: " + myCanonicalFile, e); } return null; } + protected void error(String message, Throwable t) { + //Logger.getLogger(JarLoader.class.getName()).log(Level.SEVERE, message, t); + Logger.getInstance(JarLoader.class).error(message, t); + } + + private void releaseZipFile(ZipFile zipFile) throws IOException { + // Closing of zip file when myCanLockJar=true happens in ZipFile.finalize + if (!myCanLockJar) { + zipFile.close(); + } + } + + @NotNull + private ZipFile getZipFile() throws IOException { + // This code is executed at least 100K times (O(number of classes needed to load)) and it takes considerable time to open ZipFile's + // such number of times so we store reference to ZipFile if we allowed to lock the file (assume it isn't changed) + if (myCanLockJar) { + SoftReference zipFileSoftReference = myZipFileSoftReference; + if(zipFileSoftReference != null) { + ZipFile existingZipFile = zipFileSoftReference.get(); + if (existingZipFile != null) return existingZipFile; + } + synchronized (ourLock) { + zipFileSoftReference = myZipFileSoftReference; + if(zipFileSoftReference != null) { + ZipFile existingZipFile = zipFileSoftReference.get(); + if (existingZipFile != null) return existingZipFile; + } + // ZipFile's native implementation (ZipFile.c, zip_util.c) has path -> file descriptor cache + ZipFile zipFile = new ZipFile(myCanonicalFile); + myZipFileSoftReference = new SoftReference(zipFile); + return zipFile; + } + } else { + return new ZipFile(myCanonicalFile); + } + } + @Override public String toString() { - return "JarLoader [" + myURL + "]"; + return "JarLoader [" + myCanonicalFile + "]"; } + + private static final Object ourLock = new Object(); } diff --git a/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java b/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java index f012f1fc6526..497db669ba7f 100644 --- a/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java @@ -15,9 +15,7 @@ */ package com.intellij.util.lang; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.containers.hash.HashMap; -import com.intellij.util.io.zip.ZipShort; +import com.intellij.openapi.util.io.FileUtilRt; import org.jetbrains.annotations.Nullable; import sun.misc.Resource; @@ -26,6 +24,7 @@ import java.io.IOException; import java.net.URL; import java.util.Collections; import java.util.Enumeration; +import java.util.HashMap; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -64,8 +63,8 @@ public class JarMemoryLoader { ZipEntry sizeEntry = entries.nextElement(); if (sizeEntry == null || !sizeEntry.getName().equals(SIZE_ENTRY)) return null; - byte[] bytes = FileUtil.loadBytes(zipFile.getInputStream(sizeEntry), 2); - int size = ZipShort.getValue(bytes); + byte[] bytes = FileUtilRt.loadBytes(zipFile.getInputStream(sizeEntry), 2); + int size = ((bytes[1] & 0xFF) << 8) + (bytes[0] & 0xFF); JarMemoryLoader loader = new JarMemoryLoader(); for (int i = 0; i < size && entries.hasMoreElements(); i++) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java index 2df8cad37e62..fd0b781bd06a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java @@ -19,9 +19,9 @@ import com.intellij.diff.chains.DiffRequestProducerException; import com.intellij.diff.impl.DiffRequestProcessor; import com.intellij.diff.requests.*; import com.intellij.diff.tools.util.SoftHardCacheMap; +import com.intellij.diff.util.DiffTaskQueue; import com.intellij.diff.util.DiffUserDataKeys; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; -import com.intellij.diff.util.WaitingBackgroundableTaskExecutor; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -31,10 +31,11 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.changes.actions.diff.ChangeDiffRequestProducer; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.Convertor; import org.jetbrains.annotations.*; import java.util.Collections; @@ -48,7 +49,7 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { @Nullable private Change myCurrentChange; - @NotNull private final WaitingBackgroundableTaskExecutor myTaskExecutor = new WaitingBackgroundableTaskExecutor(); + @NotNull private final DiffTaskQueue myQueue = new DiffTaskQueue(); public CacheChangeProcessor(@NotNull Project project) { super(project); @@ -74,21 +75,25 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { // Update // + @CalledInAwt public void updateRequest(final boolean force, @Nullable final ScrollToPolicy scrollToChangePolicy) { final Change change = myCurrentChange; + DiffRequest cachedRequest = loadRequestFast(change); if (cachedRequest != null) { applyRequest(cachedRequest, force, scrollToChangePolicy); return; } - myTaskExecutor.execute( - new Convertor() { + // TODO: check if current loading change is the same as we want to load now? (and not interrupt loading) + myQueue.executeAndTryWait( + new Function() { @Override - public Runnable convert(ProgressIndicator indicator) { + public Runnable fun(ProgressIndicator indicator) { final DiffRequest request = loadRequest(change, indicator); return new Runnable() { @Override + @CalledInAwt public void run() { myRequestCache.put(change, Pair.create(change, request)); applyRequest(request, force, scrollToChangePolicy); @@ -107,6 +112,7 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { } @Nullable + @CalledInAwt @Contract("null -> !null") protected DiffRequest loadRequestFast(@Nullable Change change) { if (change == null) return NoDiffRequest.INSTANCE; @@ -152,9 +158,10 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { // @Override + @CalledInAwt protected void onDispose() { super.onDispose(); - myTaskExecutor.abort(); + Disposer.dispose(myQueue); myRequestCache.clear(); } diff --git a/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java new file mode 100644 index 000000000000..ece7f752ddc7 --- /dev/null +++ b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/ExternalChangesDetectionVcsTest.java @@ -0,0 +1,211 @@ +package com.intellij.openapi.vcs.changes.committed; + +import com.intellij.ide.startup.impl.StartupManagerImpl; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TempDirTestFixture; +import com.intellij.testFramework.vcs.AbstractJunitVcsTestCase; +import com.intellij.util.Processor; +import com.intellij.util.ui.UIUtil; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @author irengrig + */ +public class ExternalChangesDetectionVcsTest extends AbstractJunitVcsTestCase { + private MockAbstractVcs myVcs; + private ProjectLevelVcsManagerImpl myVcsManager; + private LocalFileSystem myLFS; + private ChangeListManager myChangeListManager; + private VcsDirtyScopeManager myVcsDirtyScopeManager; + private TempDirTestFixture myTempDirTestFixture; + private File myClientRoot; + + @Before + public void setUp() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); + myTempDirTestFixture = fixtureFactory.createTempDirTestFixture(); + myTempDirTestFixture.setUp(); + + myClientRoot = new File(myTempDirTestFixture.getTempDirPath(), "clientroot"); + myClientRoot.mkdir(); + + initProject(myClientRoot, ExternalChangesDetectionVcsTest.this.getTestName()); + + ((StartupManagerImpl)StartupManager.getInstance(myProject)).runPostStartupActivities(); + + myVcs = new MockAbstractVcs(myProject); + myVcs.setChangeProvider(new MyMockChangeProvider()); + myVcsManager = (ProjectLevelVcsManagerImpl)ProjectLevelVcsManager.getInstance(myProject); + myVcsManager.registerVcs(myVcs); + myVcsManager.setDirectoryMapping("", myVcs.getName()); + + myLFS = LocalFileSystem.getInstance(); + myChangeListManager = ChangeListManager.getInstance(myProject); + ((ProjectComponent)myChangeListManager).projectOpened(); + myVcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); + ((ProjectComponent)myVcsDirtyScopeManager).projectOpened(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @After + public void tearDown() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + myVcsManager.unregisterVcs(myVcs); + ((ProjectComponent)myChangeListManager).projectClosed(); + ((ProjectComponent)myVcsDirtyScopeManager).projectClosed(); + + tearDownProject(); + if (myTempDirTestFixture != null) { + myTempDirTestFixture.tearDown(); + myTempDirTestFixture = null; + } + FileUtil.delete(myClientRoot); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @Test + public void testDeletion() throws Exception { + final File f = new File(myClientRoot, "f.txt"); + f.createNewFile(); + final VirtualFile vf = myLFS.refreshAndFindFileByIoFile(f); + myChangeListManager.ensureUpToDate(false); + ((ChangeListManagerImpl) myChangeListManager).getUnversionedFiles().contains(vf); + FileUtil.delete(f); + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + ((ChangeListManagerImpl) myChangeListManager).getUnversionedFiles().isEmpty(); + } + + @Test + public void testGeneration() throws Exception { + for (int i = 0; i < 100; i++) { + final File f = new File(myClientRoot, "f" + i + ".txt"); + f.createNewFile(); + } + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + final List unversionedFiles = ((ChangeListManagerImpl)myChangeListManager).getUnversionedFiles(); + final Pattern pattern = Pattern.compile("f([0-9])+\\.txt"); + int cnt = 0; + for (VirtualFile unversionedFile : unversionedFiles) { + if (VfsUtil.isAncestor(myWorkingCopyDir, unversionedFile, true)) { + ++ cnt; + Assert.assertTrue(pattern.matcher(unversionedFile.getName()).matches()); + } + } + Assert.assertEquals(100, cnt); + } + + /*@Test + public void testMoveDir() throws Exception { + final File root = new File(myClientRoot, "was"); + root.mkdir(); + for (int i = 0; i < 10; i++) { + final File dir = new File(root, "dir" + i); + dir.mkdir(); + for (int j = 0; j < 10; j++) { + final File f = new File(dir, "f" + j + ".txt"); + f.createNewFile(); + } + } + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + List unversionedFiles = ((ChangeListManagerImpl)myChangeListManager).getUnversionedFiles(); + final Pattern pattern = Pattern.compile("f([0-9])+\\.txt"); + final Pattern patternDir = Pattern.compile("dir([0-9])+"); + int cnt = 0; + for (VirtualFile unversionedFile : unversionedFiles) { + if (VfsUtil.isAncestor(myWorkingCopyDir, unversionedFile, true) && (! "was".equals(unversionedFile.getName()))) { + ++ cnt; + Assert.assertTrue(pattern.matcher(unversionedFile.getName()).matches() || patternDir.matcher(unversionedFile.getName()).matches()); + } + } + Assert.assertEquals(110, cnt); + final File renamedFile = new File(myClientRoot, "newName"); + FileUtil.copyDir(root, renamedFile); + FileUtil.delete(root); + + myWorkingCopyDir.refresh(false, true); + myChangeListManager.ensureUpToDate(false); + unversionedFiles = ((ChangeListManagerImpl)myChangeListManager).getUnversionedFiles(); + + cnt = 0; + for (VirtualFile unversionedFile : unversionedFiles) { + if (VfsUtil.isAncestor(myWorkingCopyDir, unversionedFile, true) && (! "newName".equals(unversionedFile.getName()))) { + ++ cnt; + Assert.assertTrue((pattern.matcher(unversionedFile.getName()).matches() || patternDir.matcher(unversionedFile.getName()).matches())); + if (unversionedFile.getPath().indexOf("newName") == -1) { + System.out.println("wrong " + unversionedFile.getPath()); + } + } + } + Assert.assertEquals(110, cnt); + }*/ + + private static class MyMockChangeProvider implements ChangeProvider { + @Override + public void doCleanup(List files) { + } + + @Override + public void getChanges(VcsDirtyScope dirtyScope, + final ChangelistBuilder builder, + ProgressIndicator progress, + ChangeListManagerGate addGate) throws VcsException { + for (FilePath path : dirtyScope.getDirtyFiles()) { + builder.processUnversionedFile(path.getVirtualFile()); + } + final Processor processor = new Processor() { + @Override + public boolean process(final VirtualFile vf) { + builder.processUnversionedFile(vf); + return true; + } + }; + for (FilePath dir : dirtyScope.getRecursivelyDirtyDirectories()) { + VfsUtil.processFilesRecursively(dir.getVirtualFile(), processor); + } + } + + @Override + public boolean isModifiedDocumentTrackingRequired() { + return false; + } + } +} diff --git a/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java new file mode 100644 index 000000000000..f24ad1fed284 --- /dev/null +++ b/platform/vcs-impl/testSrc/com/intellij/openapi/vcs/changes/committed/VcsEventsListenerTest.java @@ -0,0 +1,355 @@ +package com.intellij.openapi.vcs.changes.committed; + +import com.intellij.ide.startup.impl.StartupManagerImpl; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeList; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.checkin.CheckinEnvironment; +import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; +import com.intellij.openapi.vcs.rollback.RollbackEnvironment; +import com.intellij.openapi.vcs.rollback.RollbackProgressListener; +import com.intellij.openapi.vcs.ui.RefreshableOnComponent; +import com.intellij.openapi.vcs.update.SequentialUpdatesContext; +import com.intellij.openapi.vcs.update.UpdateEnvironment; +import com.intellij.openapi.vcs.update.UpdateSession; +import com.intellij.openapi.vcs.update.UpdatedFiles; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TempDirTestFixture; +import com.intellij.testFramework.vcs.AbstractJunitVcsTestCase; +import com.intellij.util.NullableFunction; +import com.intellij.util.PairConsumer; +import com.intellij.util.ui.UIUtil; +import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * @author irengrig + * Date: 12/21/10 + * Time: 2:03 PM + */ +public class VcsEventsListenerTest extends AbstractJunitVcsTestCase { + private AbstractVcs myVcs; + private ProjectLevelVcsManagerImpl myVcsManager; + private ChangeListManager myChangeListManager; + private TempDirTestFixture myTempDirFixture; + private File myClientRoot; + + @Before + public void setUp() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); + myTempDirFixture = fixtureFactory.createTempDirTestFixture(); + myTempDirFixture.setUp(); + + myClientRoot = new File(myTempDirFixture.getTempDirPath(), "clientroot"); + myClientRoot.mkdir(); + + initProject(myClientRoot, VcsEventsListenerTest.this.getTestName()); + + ((StartupManagerImpl)StartupManager.getInstance(myProject)).runPostStartupActivities(); + + myChangeListManager = ChangeListManager.getInstance(myProject); + myVcs = VcsActiveEnvironmentsProxy.proxyVcs(new MyVcs(myProject, "mock")); + myVcsManager = (ProjectLevelVcsManagerImpl)ProjectLevelVcsManager.getInstance(myProject); + myVcsManager.registerVcs(myVcs); + myVcsManager.setDirectoryMapping(myWorkingCopyDir.getPath(), myVcs.getName()); + + ((ProjectComponent)myChangeListManager).projectOpened(); + } + catch (Exception e) { + tearDown(); + throw new RuntimeException(e); + } + } + }); + } + + @After + public void tearDown() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + if (myVcsManager != null && myVcs != null) { + myVcsManager.unregisterVcs(myVcs); + } + + ((ProjectComponent)myChangeListManager).projectClosed(); + tearDownProject(); + if (myTempDirFixture != null) { + myTempDirFixture.tearDown(); + myTempDirFixture = null; + } + FileUtil.delete(myClientRoot); + } + catch (Exception e) { + throw new RuntimeException(e); + } + finally { + try { + UsefulTestCase.clearFields(this); + } + catch (IllegalAccessException e) { + //noinspection ThrowFromFinallyBlock + throw new RuntimeException(e); + } + } + } + }); + } + + @Test + public void testSimpleListeningWithProxy() throws Exception { + final VcsEventsListenerManager manager = myVcsManager.getVcsEventsListenerManager(); + final List list = Arrays.asList(new VirtualFile[]{myWorkingCopyDir}); + final MyCheckinListener listener = new MyCheckinListener(list); + final Object key = manager.addCheckin(new ForwardingListener(listener)); + + myVcs.getCheckinEnvironment().scheduleUnversionedFilesForAddition(list); + + Assert.assertTrue(listener.isChecked()); + listener.assertCheckOk(); + + listener.reset(); + + manager.removeCheckin(key); + listener.reset(); + myVcs.getCheckinEnvironment().scheduleUnversionedFilesForAddition(list); + Assert.assertFalse(listener.isChecked()); + } + + @Test + public void testSimpleListeningWithoutProxy() throws Exception { + myVcsManager.setDirectoryMapping(myWorkingCopyDir.getPath(), "svn"); + + testSimpleListeningWithProxy(); + } + + private static class MyVcs extends MockAbstractVcs { + private MyVcs(Project project, String name) { + super(project, name); + } + + @Override + protected UpdateEnvironment createUpdateEnvironment() { + return new UpdateEnvironment() { + @Override + public void fillGroups(UpdatedFiles updatedFiles) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @NotNull + @Override + public UpdateSession updateDirectories(@NotNull FilePath[] contentRoots, + UpdatedFiles updatedFiles, + ProgressIndicator progressIndicator, + @NotNull Ref context) throws ProcessCanceledException { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Configurable createConfigurable(Collection files) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean validateOptions(Collection roots) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + }; + } + + @Override + protected RollbackEnvironment createRollbackEnvironment() { + return new RollbackEnvironment() { + @Override + public String getRollbackOperationName() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackChanges(List changes, + List vcsExceptions, + @NotNull RollbackProgressListener listener) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackMissingFileDeletion(List files, + List exceptions, + RollbackProgressListener listener) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackModifiedWithoutCheckout(List files, + List exceptions, + RollbackProgressListener listener) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void rollbackIfUnchanged(VirtualFile file) { + //To change body of implemented methods use File | Settings | File Templates. + } + }; + } + + @Override + protected CheckinEnvironment createCheckinEnvironment() { + return new CheckinEnvironment() { + @Override + public RefreshableOnComponent createAdditionalOptionsPanel(CheckinProjectPanel panel, + PairConsumer additionalDataConsumer) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getDefaultMessageFor(FilePath[] filesToCheckin) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getHelpId() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getCheckinOperationName() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List commit(List changes, String preparedComment) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List commit(List changes, + String preparedComment, + @NotNull NullableFunction parametersHolder, Set feedback) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List scheduleMissingFileForDeletion(List files) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public List scheduleUnversionedFilesForAddition(List files) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean keepChangeListAfterCommit(ChangeList changeList) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isRefreshAfterCommitNeeded() { + return true; + } + }; + } + } + + private static class MyCheckinListener implements CheckinEnvironment { + private boolean myChecked; + private boolean myAssertOk; + private final List myCheckList; + + public MyCheckinListener(final List checkList) { + myCheckList = checkList; + myChecked = false; + myAssertOk = true; + } + + @Override + public RefreshableOnComponent createAdditionalOptionsPanel(CheckinProjectPanel panel, + PairConsumer additionalDataConsumer) { + return null; + } + @Override + public String getDefaultMessageFor(FilePath[] filesToCheckin) { + return null; + } + @Override + public String getHelpId() { + return null; + } + @Override + public String getCheckinOperationName() { + return null; + } + @Override + public List commit(List changes, String preparedComment) { + return null; + } + + @Override + public List commit(List changes, + String preparedComment, + @NotNull NullableFunction parametersHolder, Set feedback) { + return null; + } + + @Override + public List scheduleMissingFileForDeletion(List files) { + return null; + } + + @Override + public List scheduleUnversionedFilesForAddition(List files) { + myChecked = true; + myAssertOk = myCheckList.equals(files); + return null; + } + + @Override + public boolean keepChangeListAfterCommit(ChangeList changeList) { + return false; + } + + @Override + public boolean isRefreshAfterCommitNeeded() { + return true; + } + + public boolean isChecked() { + return myChecked; + } + + public void assertCheckOk() { + Assert.assertTrue(myAssertOk); + } + + public void reset() { + myAssertOk = true; + myChecked = false; + } + } +} diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java similarity index 100% rename from platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java rename to platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractJunitVcsTestCase.java diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java similarity index 100% rename from platform/testFramework/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java rename to platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java index 476bab69af06..d76862ffb3d9 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java @@ -10,7 +10,10 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; -import com.intellij.vcs.log.*; +import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.VcsShortCommitDetails; import com.intellij.vcs.log.data.LoadingDetails; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.data.VisiblePack; @@ -157,6 +160,7 @@ public class GraphTableModel extends AbstractTableModel { /** * Requests the proper data provider to load more data from the log & recreate the model. + * * @param onLoaded will be called upon task completion on the EDT. */ public void requestToLoadMore(@NotNull Runnable onLoaded) { @@ -183,7 +187,9 @@ public class GraphTableModel extends AbstractTableModel { return ""; } else { - return data.getAuthor().getName() + (data.getAuthor().equals(data.getCommitter()) ? "" : "*"); + String authorString = data.getAuthor().getName(); + if (authorString.isEmpty()) authorString = data.getAuthor().getEmail(); + return authorString + (data.getAuthor().equals(data.getCommitter()) ? "" : "*"); } case DATE_COLUMN: if (data == null || data.getAuthorTime() < 0) { @@ -208,6 +214,7 @@ public class GraphTableModel extends AbstractTableModel { * Returns Changes for commits at selected rows.
* Rows are given in the order as they appear in the table, i. e. in reverse chronological order.
* Changes can be returned as-is, i.e. with duplicate changes for a single file. + * * @return Changes selected in all rows, or null if this data is not ready yet. */ @Nullable diff --git a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java index 50ba95a046fb..68d5aba3f6e0 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java @@ -15,6 +15,7 @@ */ package git4idea.actions; +import com.intellij.dvcs.repo.Repository; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -114,19 +115,18 @@ public abstract class GitRepositoryAction extends DumbAwareAction { if (files != null) { for (VirtualFile file : files) { GitRepositoryManager manager = GitUtil.getRepositoryManager(project); - if (manager == null) { - return false; - } - final GitRepository repositoryForFile = manager.getRepositoryForFile(file); - if (repositoryForFile != null && repositoryForFile.getState() == GitRepository.State.REBASING) { - return true; - } + if (isRebasing(manager.getRepositoryForFile(file))) return true; } } + if (isRebasing(GitBranchUtil.getCurrentRepository(project))) return true; } return false; } + private static boolean isRebasing(@Nullable GitRepository repository) { + return repository != null && repository.getState() == Repository.State.REBASING; + } + /** * Get git roots for the project. The method shows dialogs in the case when roots cannot be retrieved, so it should be called * from the event dispatch thread. diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java index f0273b478608..4a270259d8c0 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java @@ -130,4 +130,23 @@ public class JUnitRerunFailedTestsTest extends LightCodeInsightFixtureTestCase }); assertNull(TestMethods.getTestPresentation(testProxy, project, searchScope)); } + + public void testInnerClass() throws Exception { + myFixture.addClass("public class TestClass {\n" + + " public static class Tests extends junit.framework.TestCase {\n" + + " public void testFoo() throws Exception {}\n" + + " }\n" + + "}"); + + final SMTestProxy testProxy = new SMTestProxy("testFoo", false, "java:test://TestClass$Tests.testFoo"); + final Project project = getProject(); + final GlobalSearchScope searchScope = GlobalSearchScope.projectScope(project); + testProxy.setLocator(JavaTestLocator.INSTANCE); + Location location = testProxy.getLocation(project, searchScope); + assertNotNull(location); + PsiElement element = location.getPsiElement(); + assertTrue(element instanceof PsiMethod); + String name = ((PsiMethod)element).getName(); + assertEquals(name, "testFoo"); + } } diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java index d46dff96d663..50f7dcf6db7d 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java @@ -15,7 +15,7 @@ */ package com.intellij.execution.junit; -import com.intellij.junit4.SMTestSender; +import com.intellij.junit4.JUnit4TestListener; import com.intellij.openapi.util.text.StringUtil; import junit.framework.Assert; import org.junit.Test; @@ -65,26 +65,25 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + - "##teamcity[testSuiteFinished name='root']\n" + - "##teamcity[testSuiteStarted name ='TestA']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='TestA']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n" + "##teamcity[testSuiteFinished name='TestA']\n" + - "##teamcity[testSuiteStarted name ='TestB']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='TestB']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestB.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + @@ -109,17 +108,16 @@ public class JUnitTreeByDescriptionHierarchyTest { //start "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'TestA' comment = 'a' location = 'java:suite://a.TestA']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + - "##teamcity[testSuiteFinished name='|[1|]']\n" + - "##teamcity[testSuiteFinished name='TestA']\n"); + "##teamcity[testSuiteFinished name='|[1|]']\n"); } @Test @@ -154,16 +152,15 @@ public class JUnitTreeByDescriptionHierarchyTest { //started "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + - "##teamcity[testSuiteFinished name='root']\n" + - "##teamcity[testSuiteStarted name ='ASuite1']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + + "##teamcity[testSuiteStarted name='ASuite1']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testStarted name='test1' locationHint='java:test://ATest.test1']\n" + "\n" + "##teamcity[testFinished name='test1']\n" + "##teamcity[testSuiteFinished name='ATest']\n" + "##teamcity[testSuiteFinished name='ASuite1']\n" + - "##teamcity[testSuiteStarted name ='ASuite2']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + + "##teamcity[testSuiteStarted name='ASuite2']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testStarted name='test1' locationHint='java:test://ATest.test1']\n" + "\n" + "##teamcity[testFinished name='test1']\n" + @@ -173,7 +170,7 @@ public class JUnitTreeByDescriptionHierarchyTest { private static void doTest(Description root, List tests, String expectedTree, String expectedStart) throws Exception { final StringBuffer buf = new StringBuffer(); - final SMTestSender sender = new SMTestSender(new PrintStream(new OutputStream() { + final JUnit4TestListener sender = new JUnit4TestListener(new PrintStream(new OutputStream() { @Override public void write(int b) throws IOException { buf.append(new String(new byte[]{(byte)b})); @@ -204,8 +201,37 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[rootName name = 'TestA' location = 'java:suite://TestA']\n" + "##teamcity[testStarted name='testName' locationHint='java:test://TestA.testName']\n" + "\n" + - "##teamcity[testFinished name='testName']\n" + - "##teamcity[testSuiteFinished name='TestA']\n"); + "##teamcity[testFinished name='testName']\n"); + } + + @Test + public void testParameterizedTestsUpsideDown() throws Exception { + final Description aTestClass = Description.createSuiteDescription("ATest"); + final ArrayList tests = new ArrayList(); + final Description testMethod = Description.createSuiteDescription("testName"); + aTestClass.addChild(testMethod); + for (String paramName : new String[]{"[0]", "[1]"}) { + final Description testDescription = Description.createTestDescription("ATest", "testName" + paramName); + tests.add(testDescription); + testMethod.addChild(testDescription); + } + doTest(aTestClass, tests, + "##teamcity[suiteTreeStarted name='testName' locationHint='java:suite://testName']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + + "##teamcity[suiteTreeEnded name='testName']\n", + + + "##teamcity[enteredTheMatrix]\n" + + "##teamcity[rootName name = 'ATest' location = 'java:suite://ATest']\n" + + "##teamcity[testSuiteStarted name='testName']\n" + + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + + "\n" + + "##teamcity[testFinished name='testName|[0|]']\n" + + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + + "\n" + + "##teamcity[testFinished name='testName|[1|]']\n" + + "##teamcity[testSuiteFinished name='testName']\n"); } @Test @@ -240,21 +266,20 @@ public class JUnitTreeByDescriptionHierarchyTest { //start "##teamcity[enteredTheMatrix]\n" + "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" + - "##teamcity[testSuiteFinished name='root']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + - "##teamcity[testSuiteStarted name ='|[0|]']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + + "##teamcity[testSuiteStarted name='|[0|]']\n" + "##teamcity[testStarted name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + "\n" + "##teamcity[testFinished name='testName|[0|]']\n" + "##teamcity[testSuiteFinished name='|[0|]']\n" + - "##teamcity[testSuiteStarted name ='|[1|]']\n" + + "##teamcity[testSuiteStarted name='|[1|]']\n" + "##teamcity[testStarted name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + "\n" + "##teamcity[testFinished name='testName|[1|]']\n" + "##teamcity[testSuiteFinished name='|[1|]']\n" + "##teamcity[testSuiteFinished name='ATest']\n" + - "##teamcity[testSuiteStarted name ='suite']\n" + - "##teamcity[testSuiteStarted name ='ATest']\n" + + "##teamcity[testSuiteStarted name='suite']\n" + + "##teamcity[testSuiteStarted name='ATest']\n" + "##teamcity[testStarted name='test' locationHint='java:test://ATest.test']\n" + "\n" + "##teamcity[testFinished name='test']\n" + @@ -275,7 +300,7 @@ public class JUnitTreeByDescriptionHierarchyTest { private static void doTest(Description description, String expected) { final StringBuffer buf = new StringBuffer(); - new SMTestSender(new PrintStream(new OutputStream() { + new JUnit4TestListener(new PrintStream(new OutputStream() { @Override public void write(int b) throws IOException { buf.append(new String(new byte[]{(byte)b})); @@ -288,7 +313,7 @@ public class JUnitTreeByDescriptionHierarchyTest { @Test public void testProcessEmptyTestCase() throws Exception { final Description description = Description.createSuiteDescription("TestA"); - final Description emptyDescription = Description.createTestDescription(SMTestSender.EMPTY_SUITE_NAME, SMTestSender.EMPTY_SUITE_WARNING); + final Description emptyDescription = Description.createTestDescription(JUnit4TestListener.EMPTY_SUITE_NAME, JUnit4TestListener.EMPTY_SUITE_WARNING); description.addChild(emptyDescription); doTest(description, Collections.singletonList(emptyDescription), "##teamcity[suiteTreeNode name='warning' locationHint='java:test://TestA.warning']\n", @@ -298,7 +323,6 @@ public class JUnitTreeByDescriptionHierarchyTest { "##teamcity[rootName name = 'TestA' location = 'java:suite://TestA']\n" + "##teamcity[testStarted name='warning' locationHint='java:test://junit.framework.TestSuite$1.warning']\n" + "\n" + - "##teamcity[testFinished name='warning']\n" + - "##teamcity[testSuiteFinished name='TestA']\n"); + "##teamcity[testFinished name='warning']\n"); } } diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java index 4f68466cdac3..348607c84024 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java @@ -52,9 +52,9 @@ public class JUnit4IdeaTestRunner implements IdeaTestRunner { description = getFilteredDescription(request, description); } - if (myTestsListener instanceof SMTestSender) { + if (myTestsListener instanceof JUnit4TestListener) { if (sendTree) { - ((SMTestSender)myTestsListener).sendTree(description); + ((JUnit4TestListener)myTestsListener).sendTree(description); } sendTree = false; } else { @@ -189,7 +189,7 @@ public class JUnit4IdeaTestRunner implements IdeaTestRunner { public void setStreams(Object segmentedOut, Object segmentedErr, int lastIdx) { if (JUnitStarter.SM_RUNNER) { - myTestsListener = new SMTestSender(); + myTestsListener = new JUnit4TestListener(); } else { myRegistry = new JUnit4OutputObjectRegistry((PacketProcessor)segmentedOut, lastIdx); myTestsListener = new JUnit4TestResultsSender(myRegistry); diff --git a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java similarity index 65% rename from plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java rename to plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java index 453a60e6fe7c..ff16fed6e210 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java @@ -32,27 +32,27 @@ import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.*; -public class SMTestSender extends RunListener { +public class JUnit4TestListener extends RunListener { private static final String MESSAGE_LENGTH_FOR_PATTERN_MATCHING = "idea.junit.message.length.threshold"; private static final String JUNIT_FRAMEWORK_COMPARISON_NAME = ComparisonFailure.class.getName(); private static final String ORG_JUNIT_COMPARISON_NAME = "org.junit.ComparisonFailure"; public static final String EMPTY_SUITE_NAME = "junit.framework.TestSuite$1"; public static final String EMPTY_SUITE_WARNING = "warning"; - private String myCurrentSuiteName; - private String myCurrentClassName; - private String myParamName; + private List myStartedSuites = new ArrayList(); + private Map myParents = new HashMap(); + private final PrintStream myPrintStream; + private String myRootName; - private PrintStream myPrintStream = System.out; - private final Map myParents = new HashMap(); - private final Map mySuites = new HashMap(); + public JUnit4TestListener() { + myPrintStream = System.out; + } - - public SMTestSender() {} - - public SMTestSender(PrintStream printStream) { + public JUnit4TestListener(PrintStream printStream) { myPrintStream = printStream; } @@ -62,112 +62,90 @@ public class SMTestSender extends RunListener { public void testRunStarted(Description description) throws Exception { myPrintStream.println("##teamcity[enteredTheMatrix]"); - if (myCurrentClassName != null && !myCurrentClassName.startsWith("[")) { - int lastPointIdx = myCurrentClassName.lastIndexOf('.'); - String name = myCurrentClassName; + if (myRootName != null && !myRootName.startsWith("[")) { + int lastPointIdx = myRootName.lastIndexOf('.'); + String name = myRootName; String comment = null; if (lastPointIdx >= 0) { - name = myCurrentClassName.substring(lastPointIdx + 1); - comment = myCurrentClassName.substring(0, lastPointIdx); + name = myRootName.substring(lastPointIdx + 1); + comment = myRootName.substring(0, lastPointIdx); } myPrintStream.println("##teamcity[rootName name = \'" + escapeName(name) + (comment != null ? ("\' comment = \'" + escapeName(comment)) : "") + "\'" + - " location = \'java:suite://" + escapeName(myCurrentClassName) + + " location = \'java:suite://" + escapeName(myRootName) + "\']"); - myCurrentClassName = getShortName(myCurrentClassName); + myRootName = getShortName(myRootName); } } public void testRunFinished(Result result) throws Exception { - if (myParamName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myParamName) + "\']"); - } - if (myCurrentClassName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']"); - } - if (myCurrentSuiteName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); + for (int i = myStartedSuites.size() - 1; i>= 0; i--) { + Object parent = myStartedSuites.get(i); + myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName((String)parent) + "\']"); } + myStartedSuites.clear(); } public void testStarted(Description description) throws Exception { final String methodName = JUnit4ReflectionUtil.getMethodName(description); final String classFQN = JUnit4ReflectionUtil.getClassName(description); - final int paramStart = methodName.indexOf('['); - if (myParamName != null){ - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myParamName) + "\']"); - myParamName = null; - } - final List suites = (List)myParents.get(description); - if (suites != null && !suites.isEmpty()) { - String currentSuite = (String)suites.get(0); - List descriptors = (List)mySuites.get(currentSuite); + final List parents = (List)myParents.get(description); + if (parents != null) { - if (descriptors.isEmpty()) { - currentSuite = (String)suites.get(1); - descriptors = (List)mySuites.get(currentSuite); + List parentsHierarchy = (List)parents.remove(0); + + int idx = 0; + String currentClass; + String currentParent; + while (idx < myStartedSuites.size() && idx < parentsHierarchy.size()) { + currentClass = (String)myStartedSuites.get(idx); + currentParent = getShortName(JUnit4ReflectionUtil.getClassName((Description)parentsHierarchy.get(parentsHierarchy.size() - 1 - idx))); + if (!currentClass.equals(currentParent)) break; + idx++; } - if (!currentSuite.equals(myCurrentSuiteName)) { - finishCurrentSuite(); - myCurrentSuiteName = currentSuite; - myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); + for (int i = myStartedSuites.size() - 1; i >= idx; i--) { + currentClass = (String)myStartedSuites.remove(i); + myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(currentClass) + "\']"); } - descriptors.remove(description); - } - else if (myCurrentSuiteName != null){ - finishCurrentSuite(); - myCurrentSuiteName = null; - } - - String className = getShortName(classFQN); - if (!myEmptyTests.isEmpty() && isWarning(methodName, classFQN)) { - className = (String)myEmptyTests.remove(0); - } - - if (!className.equals(myCurrentClassName)) { - if (myCurrentClassName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']"); - } - myCurrentClassName = className; - myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(myCurrentClassName) + "\']"); - } - if (paramStart > -1) { - final String paramName = methodName.substring(paramStart, methodName.length()); - if (!paramName.equals(myParamName)) { - myParamName = paramName; - myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(myParamName) + "\']"); + for (int i = idx; i < parentsHierarchy.size(); i++) { + Description parent = (Description)parentsHierarchy.get(parentsHierarchy.size() - 1 - i); + final String className = getShortName(JUnit4ReflectionUtil.getClassName(parent)); + if (!className.equals(myRootName)) { + myPrintStream.println("##teamcity[testSuiteStarted name=\'" + escapeName(className) + "\']"); + myStartedSuites.add(className); + } } } + myPrintStream.println("##teamcity[testStarted name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, classFQN) + "]"); } - private void finishCurrentSuite() { - if (myCurrentClassName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']"); - myCurrentClassName = null; - } - if (myCurrentSuiteName != null) { - myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); - } - } - public void testFinished(Description description) throws Exception { myPrintStream.println("\n##teamcity[testFinished name=\'" + escapeName(JUnit4ReflectionUtil.getMethodName(description)) + "\']"); } public void testFailure(Failure failure) throws Exception { - final String failureMessage = failure.getMessage(); - final String trace = failure.getTrace(); final Map attrs = new HashMap(); attrs.put("name", JUnit4ReflectionUtil.getMethodName(failure.getDescription())); - final ComparisonFailureData notification = createExceptionNotification(failure.getException()); - ComparisonFailureData.registerSMAttributes(notification, trace, failureMessage, attrs); - myPrintStream.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); + try { + final String trace = failure.getTrace(); + final ComparisonFailureData notification = createExceptionNotification(failure.getException()); + ComparisonFailureData.registerSMAttributes(notification, trace, failure.getMessage(), attrs); + } + catch (Throwable e) { + final StringWriter stringWriter = new StringWriter(); + final PrintWriter writer = new PrintWriter(stringWriter); + e.printStackTrace(writer); + ComparisonFailureData.registerSMAttributes(null, stringWriter.toString(), e.getMessage(), attrs); + } + finally { + myPrintStream.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); + } } public void testAssumptionFailure(Failure failure) { @@ -249,39 +227,29 @@ public class SMTestSender extends RunListener { return messageLength < threshold; } - private final List myEmptyTests = new ArrayList(); - - private void sendTree(Description description, Description parent, Description suiteParent) { + private void sendTree(Description description, Description parent, List currentParents) { + List pParents = new ArrayList(3); + pParents.addAll(currentParents); + if (parent != null && !myRootName.equals(JUnit4ReflectionUtil.getClassName(parent))) { + pParents.add(0, parent); + } + String className = JUnit4ReflectionUtil.getClassName(description); if (description.getChildren().isEmpty()) { final String methodName = JUnit4ReflectionUtil.getMethodName((Description)description); if (methodName != null) { - if (isWarning(methodName, className)) { - className = JUnit4ReflectionUtil.getClassName(parent); - myEmptyTests.add(getShortName(className)); - } - myPrintStream.println("##teamcity[suiteTreeNode name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, className) + "]"); - } - - if (suiteParent != null ) { - final String parentFQName = JUnit4ReflectionUtil.getClassName(suiteParent); - if (!myCurrentClassName.equals(parentFQName)) { + if (parent != null) { List parents = (List)myParents.get(description); if (parents == null) { - parents = new ArrayList(); + parents = new ArrayList(1); myParents.put(description, parents); } - if (!parents.contains(parentFQName)) { - parents.add(parentFQName); - } - - List descriptors = (List)mySuites.get(parentFQName); - if (descriptors == null) { - descriptors = new ArrayList(); - mySuites.put(parentFQName, descriptors); - } - descriptors.add(description); + parents.add(pParents); } + if (isWarning(methodName, className)) { + className = JUnit4ReflectionUtil.getClassName(parent); + } + myPrintStream.println("##teamcity[suiteTreeNode name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, className) + "]"); } return; @@ -292,8 +260,7 @@ public class SMTestSender extends RunListener { for (Iterator iterator = tests.iterator(); iterator.hasNext(); ) { final Object next = iterator.next(); final Description nextDescription = (Description)next; - final List childTests = nextDescription.getChildren(); - if ((myCurrentClassName == null || !myCurrentClassName.equals(className)) && !pass) { + if ((myRootName == null || !myRootName.equals(className)) && !pass) { pass = true; String locationHint = className; if (isParameter((Description)description)) { @@ -308,7 +275,7 @@ public class SMTestSender extends RunListener { } myPrintStream.println("##teamcity[suiteTreeStarted name=\'" + escapeName(getShortName(className)) + "\' locationHint=\'java:suite://" + escapeName(locationHint) + "\']"); } - sendTree(nextDescription, description, isParameter(description) ? null : childTests.isEmpty() && parent != null ? parent : description); + sendTree(nextDescription, description, pParents); } if (pass) { myPrintStream.println("##teamcity[suiteTreeEnded name=\'" + escapeName(getShortName(JUnit4ReflectionUtil.getClassName((Description)description))) + "\']"); @@ -329,8 +296,8 @@ public class SMTestSender extends RunListener { } public void sendTree(Description description) { - myCurrentClassName = JUnit4ReflectionUtil.getClassName((Description)description); - sendTree(description, null, null); + myRootName = JUnit4ReflectionUtil.getClassName((Description)description); + sendTree(description, null, new ArrayList()); } private static String getShortName(String fqName) { diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestResultsSender.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestResultsSender.java index 1285e5abc1b5..d397acb45bea 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestResultsSender.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestResultsSender.java @@ -29,8 +29,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class JUnit4TestResultsSender extends RunListener { private static final String JUNIT_FRAMEWORK_COMPARISON_NAME = ComparisonFailure.class.getName(); @@ -108,7 +106,7 @@ public class JUnit4TestResultsSender extends RunListener { private static PacketFactory createExceptionNotification(Throwable assertion) { if (assertion instanceof KnownException) return ((KnownException)assertion).getPacketFactory(); - final ComparisonFailureData notification = SMTestSender.createExceptionNotification(assertion); + final ComparisonFailureData notification = JUnit4TestListener.createExceptionNotification(assertion); if (notification != null) { return ComparisonDetailsExtractor.create(assertion, notification.getExpected(), notification.getActual()); } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloTask.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloTask.java index 57a28c08b95b..7f36ddd8c4dd 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloTask.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloTask.java @@ -120,4 +120,10 @@ public class TrelloTask extends Task { public TaskRepository getRepository() { return myRepository; } + + @NotNull + @Override + public String getNumber() { + return myCard.getIdShort(); + } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/model/TrelloCard.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/model/TrelloCard.java index 668ab361e25d..137c82a9fb70 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/model/TrelloCard.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/model/TrelloCard.java @@ -36,9 +36,9 @@ import static com.intellij.tasks.trello.model.TrelloLabel.LabelColor; @SuppressWarnings("UnusedDeclaration") public class TrelloCard extends TrelloModel { - public static final String REQUIRED_FIELDS = "closed,desc,idMembers,idBoard,idList,labels,name,url,dateLastActivity"; + public static final String REQUIRED_FIELDS = "closed,desc,idMembers,idBoard,idList,idShort,labels,name,url,dateLastActivity"; - private String idBoard, idList; + private String idBoard, idList, idShort; private List idMembers; private String name; @SerializedName("desc") @@ -77,6 +77,11 @@ public class TrelloCard extends TrelloModel { return idList; } + @NotNull + public String getIdShort() { + return idShort; + } + @NotNull public List getIdMembers() { return idMembers; diff --git a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java index 3f98528cc533..6204eea43a0e 100644 --- a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java +++ b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java @@ -18,12 +18,8 @@ package org.testng; import jetbrains.buildServer.messages.serviceMessages.ServiceMessage; import org.testng.collections.Lists; -import org.testng.xml.XmlClass; -import org.testng.xml.XmlInclude; import org.testng.xml.XmlSuite; -import org.testng.xml.XmlTest; -import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -51,27 +47,6 @@ public class IDEARemoteTestNG extends TestNG { int testCount= 0; - for (XmlSuite suite : suites) { - System.out.println("##teamcity[suiteTreeStarted name=\'" + suite.getName() + "\' locationHint=\'java:suite://" + suite.getName() + "\']"); - final List tests = suite.getTests(); - for (XmlTest test : tests) { - for (XmlClass aClass : test.getXmlClasses()) { - final String shortName = IDEATestNGRemoteListener.getShortName(aClass.getName()); - System.out.println("##teamcity[suiteTreeStarted name=\'" + - shortName + "\' locationHint=\'java:suite://" + aClass.getName() + "\']"); - if (myParam != null) { - for (XmlInclude include : aClass.getIncludedMethods()) { - aClass.setIncludedMethods(Arrays.asList(new XmlInclude(include.getName(), Arrays.asList(Integer.parseInt(myParam)), 0))); - } - } - - System.out.println("##teamcity[suiteTreeEnded name=\'" + shortName + "\']"); - } - testCount += test.getClasses().size(); - } - System.out.println("##teamcity[suiteTreeEnded name=\'" + suite.getName() + "\']"); - } - final HashMap map = new HashMap(); map.put("count", String.valueOf(testCount)); System.out.println(ServiceMessage.asString("testCount", map)); diff --git a/python/helpers/profiler/prof_util.py b/python/helpers/profiler/prof_util.py index ebdc5be6da62..ca7ff3cab43e 100644 --- a/python/helpers/profiler/prof_util.py +++ b/python/helpers/profiler/prof_util.py @@ -2,6 +2,7 @@ __author__ = 'traff' import threading import os +import tempfile from _prof_imports import Stats, FuncStat, Function class ProfDaemonThread(threading.Thread): @@ -16,9 +17,12 @@ class ProfDaemonThread(threading.Thread): def OnRun(self): pass -def generate_snapshot_filepath(basepath): +def generate_snapshot_filepath(basepath, local_temp_dir=False): if basepath is None: - return None + basepath = 'snapshot' + if local_temp_dir: + basepath = os.path.join(tempfile.gettempdir(), os.path.basename(basepath)) + n = 0 path = basepath + '.pstat' while os.path.exists(path): diff --git a/python/helpers/profiler/run_profiler.py b/python/helpers/profiler/run_profiler.py index 060efaa698a0..b8f707dee994 100644 --- a/python/helpers/profiler/run_profiler.py +++ b/python/helpers/profiler/run_profiler.py @@ -67,7 +67,7 @@ class Profiler(object): def process(self, message): if hasattr(message, 'save_snapshot'): - self.save_snapshot(message.id, generate_snapshot_filepath(message.save_snapshot.filepath) if not remote_run else None) + self.save_snapshot(message.id, generate_snapshot_filepath(message.save_snapshot.filepath, remote_run), remote_run) else: raise AssertionError("Unknown request %s" % dir(message)) @@ -84,7 +84,7 @@ class Profiler(object): pydev_imports.execfile(file, globals, globals) # execute the script self.stop_profiling() - self.save_snapshot(0, generate_snapshot_filepath(base_snapshot_path) if not remote_run else None) + self.save_snapshot(0, generate_snapshot_filepath(base_snapshot_path, remote_run), remote_run) def start_profiling(self): self.profiling_backend.enable() @@ -104,11 +104,13 @@ class Profiler(object): self.profiling_backend.dump_stats(filename) return filename - def save_snapshot(self, id, filename): + def save_snapshot(self, id, filename, send_stat=False): self.stop_profiling() if filename is not None: filename = self.dump_snapshot(filename) print('Snapshot saved to %s' % filename) + + if not send_stat: response = ProfilerResponse(id=id, snapshot_filepath=filename) else: response = ProfilerResponse(id=id) diff --git a/python/helpers/profiler/yappi_profiler.py b/python/helpers/profiler/yappi_profiler.py index c1024c4b0e9d..2be2d2f74836 100644 --- a/python/helpers/profiler/yappi_profiler.py +++ b/python/helpers/profiler/yappi_profiler.py @@ -24,8 +24,7 @@ class YappiProfile(object): self.stats = yappi.convert2pstats(yappi.get_func_stats()).stats def getstats(self): - if self.stats is None: - self.create_stats() + self.create_stats() return self.stats diff --git a/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java b/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java index 16a7b86261b3..b460e0f03de9 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java +++ b/python/src/com/jetbrains/python/codeInsight/PyTypingAnnotationInjector.java @@ -24,16 +24,27 @@ import com.jetbrains.python.psi.PyStringLiteralExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.regex.Pattern; + /** * @author vlan */ public class PyTypingAnnotationInjector extends PyInjectorBase { + public static final Pattern RE_TYPING_ANNOTATION = Pattern.compile("\\s*\\S+(\\[.*\\])?\\s*"); + @Nullable @Override public Language getInjectedLanguage(@NotNull PsiElement context) { - if (context instanceof PyStringLiteralExpression && PsiTreeUtil.getParentOfType(context, PyAnnotation.class, true) != null) { - return PyDocstringLanguageDialect.getInstance(); + if (context instanceof PyStringLiteralExpression) { + final PyStringLiteralExpression expr = (PyStringLiteralExpression)context; + if (PsiTreeUtil.getParentOfType(context, PyAnnotation.class, true) != null && isTypingAnnotation(expr.getStringValue())) { + return PyDocstringLanguageDialect.getInstance(); + } } return null; } + + private static boolean isTypingAnnotation(@NotNull String s) { + return RE_TYPING_ANNOTATION.matcher(s).matches(); + } } diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 4d104f1c2629..5ec8c8d0e16b 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -23,6 +23,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.scratch.ScratchFileService; import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.lang.ASTFactory; import com.intellij.lang.ASTNode; @@ -1803,6 +1804,16 @@ public class PyUtil { return type == builtinCache.getObjectType() || type == builtinCache.getOldstyleClassobjType(); } + public static boolean isInScratchFile(@NotNull PsiElement element) { + final ScratchFileService service = ScratchFileService.getInstance(); + final PsiFile file = element.getContainingFile(); + if (file != null) { + final VirtualFile virtualFile = file.getVirtualFile(); + return service != null && virtualFile != null && service.getRootType(virtualFile) != null; + } + return false; + } + /** * This helper class allows to collect various information about AST nodes composing {@link PyStringLiteralExpression}. */ diff --git a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java index b073db8c6955..e0ce6f2dfc1a 100644 --- a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java +++ b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameResolverImpl.java @@ -37,6 +37,7 @@ import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil; import com.jetbrains.python.console.PydevConsoleRunner; import com.jetbrains.python.facet.PythonPathContributingFacet; import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyImportResolver; import com.jetbrains.python.sdk.PySdkUtil; @@ -84,7 +85,7 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso @Override public QualifiedNameResolver fromElement(@NotNull PsiElement foothold) { myContext.setFromElement(foothold); - if (PydevConsoleRunner.isInPydevConsole(foothold)) { + if (PydevConsoleRunner.isInPydevConsole(foothold) || PyUtil.isInScratchFile(foothold)) { withAllModules(); Sdk sdk = PydevConsoleRunner.getConsoleSdk(foothold); if (sdk != null) { diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index 6ac1fdfd5c2e..b6126e9abeb6 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -15,6 +15,15 @@ */ package com.jetbrains.python; +import com.intellij.lang.injection.InjectedLanguageManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.testFramework.LightProjectDescriptor; import com.jetbrains.python.documentation.PythonDocumentationProvider; import com.jetbrains.python.fixtures.PyTestCase; @@ -25,6 +34,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * Tests for a type system based on mypy's typing module. * @@ -306,6 +317,59 @@ public class PyTypingTest extends PyTestCase { " pass\n"); } + public void testStringLiteralInjection() { + doTestInjectedText("class C:\n" + + " def foo(self, expr: 'C'):\n" + + " pass\n", + "C"); + } + + public void testStringLiteralInjectionParameterizedType() { + doTestInjectedText("from typing import Union, List\n" + + "\n" + + "class C:\n" + + " def foo(self, expr: 'Union[List[C], C]'):\n" + + " pass\n", + "Union[List[C], C]"); + } + + // PY-15810 + public void testNoStringLiteralInjectionForNonTypingStrings() { + doTestNoInjectedText("class C:\n" + + " def foo(self, expr: 'foo bar'):\n" + + " pass\n"); + } + + private void doTestNoInjectedText(@NotNull String text) { + myFixture.configureByText(PythonFileType.INSTANCE, text); + final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject()); + final PsiLanguageInjectionHost host = languageManager.getInjectionHost(getElementAtCaret()); + assertNull(host); + } + + private void doTestInjectedText(@NotNull String text, @NotNull String expected) { + myFixture.configureByText(PythonFileType.INSTANCE, text); + final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject()); + final PsiLanguageInjectionHost host = languageManager.getInjectionHost(getElementAtCaret()); + assertNotNull(host); + final List> files = languageManager.getInjectedPsiFiles(host); + assertNotNull(files); + assertFalse(files.isEmpty()); + final PsiElement injected = files.get(0).getFirst(); + assertEquals(expected, injected.getText()); + } + + @NotNull + private PsiElement getElementAtCaret() { + final Editor editor = myFixture.getEditor(); + final Document document = editor.getDocument(); + final PsiFile file = PsiDocumentManager.getInstance(myFixture.getProject()).getPsiFile(document); + assertNotNull(file); + final PsiElement element = file.findElementAt(myFixture.getCaretOffset()); + assertNotNull(element); + return element; + } + private void doTest(@NotNull String expectedType, @NotNull String text) { myFixture.copyDirectoryToProject("typing", ""); myFixture.configureByText(PythonFileType.INSTANCE, text);