diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index fa3ad03df96b..41d7551234d9 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -28,7 +28,6 @@ import com.intellij.openapi.extensions.*; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.StreamUtil; -import com.intellij.openapi.util.io.ZipFileCache; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.*; import com.intellij.util.containers.MultiMap; @@ -614,7 +613,7 @@ public class PluginManagerCore { String fileURL = StringUtil.replace(file.toURI().toASCIIString(), "!", "%21"); URL jarURL = new URL("jar:" + fileURL + "!/META-INF/" + fileName); - ZipFile zipFile = ZipFileCache.acquire(file.getPath()); + ZipFile zipFile = new ZipFile(file); try { ZipEntry entry = zipFile.getEntry("META-INF/" + fileName); if (entry != null) { @@ -625,7 +624,7 @@ public class PluginManagerCore { } } finally { - ZipFileCache.release(zipFile); + zipFile.close(); } } catch (XmlSerializationException e) { diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/ZipHandler.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/ZipHandler.java index fc4844ec2f18..6672674d9e14 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/ZipHandler.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/ZipHandler.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. @@ -18,7 +18,6 @@ package com.intellij.openapi.vfs.impl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.io.ZipFileCache; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; import gnu.trove.THashMap; @@ -43,7 +42,7 @@ public class ZipHandler extends ArchiveHandler { Map map = new THashMap(); map.put("", createRootEntry()); - ZipFile zip = getZipFile(); + ZipFile zip = new ZipFile(getFileToUse()); try { Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { @@ -51,7 +50,7 @@ public class ZipHandler extends ArchiveHandler { } } finally { - ZipFileCache.release(zip); + zip.close(); } return map; @@ -62,11 +61,6 @@ public class ZipHandler extends ArchiveHandler { return getFile(); } - @NotNull - private ZipFile getZipFile() throws IOException { - return ZipFileCache.acquire(getFileToUse().getPath()); - } - @NotNull private EntryInfo getOrCreate(ZipEntry entry, Map map, ZipFile zip) { boolean isDirectory = entry.isDirectory(); @@ -117,7 +111,7 @@ public class ZipHandler extends ArchiveHandler { @NotNull @Override public byte[] contentsToByteArray(@NotNull String relativePath) throws IOException { - ZipFile zip = getZipFile(); + ZipFile zip = new ZipFile(getFileToUse()); try { ZipEntry entry = zip.getEntry(relativePath); if (entry != null) { @@ -133,7 +127,7 @@ public class ZipHandler extends ArchiveHandler { } } finally { - ZipFileCache.release(zip); + zip.close(); } return ArrayUtil.EMPTY_BYTE_ARRAY; diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java index f1cf7487bde4..5826e6de5159 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java @@ -17,6 +17,7 @@ package com.intellij.execution.console; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupManager; +import com.intellij.codeInsight.lookup.impl.LookupImpl; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -76,7 +77,8 @@ public class ConsoleExecuteAction extends DumbAwareAction { if (enabled) { Lookup lookup = LookupManager.getActiveLookup(editor); // we should check getCurrentItem() also - fast typing could produce outdated lookup, such lookup reports isCompletion() true - enabled = lookup == null || !lookup.isCompletion() || lookup.getCurrentItem() == null; + enabled = lookup == null || !lookup.isCompletion() || lookup.getCurrentItem() == null || + (lookup instanceof LookupImpl && ((LookupImpl)lookup).getFocusDegree() == LookupImpl.FocusDegree.UNFOCUSED); } e.getPresentation().setEnabled(enabled); diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshQueue.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshQueue.java index b7a3736bbbf8..6948e19193fc 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshQueue.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshQueue.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. @@ -44,7 +44,7 @@ public abstract class RefreshQueue { refresh(async, recursive, finishRunnable, getDefaultModalityState(), files); } - public final void refresh(boolean async, boolean recursive, @Nullable Runnable finishRunnable, @NotNull Collection files) { + public final void refresh(boolean async, boolean recursive, @Nullable Runnable finishRunnable, @NotNull Collection files) { refresh(async, recursive, finishRunnable, getDefaultModalityState(), files); } @@ -62,7 +62,7 @@ public abstract class RefreshQueue { boolean recursive, @Nullable Runnable finishRunnable, @NotNull ModalityState state, - @NotNull Collection files) { + @NotNull Collection files) { RefreshSession session = createSession(async, recursive, finishRunnable, state); session.addAllFiles(files); session.launch(); diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshSession.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshSession.java index 78f0daaa7cc7..e0f4d599a98d 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshSession.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/RefreshSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -33,7 +33,7 @@ public abstract class RefreshSession { public abstract void addFile(@NotNull VirtualFile file); - public abstract void addAllFiles(@NotNull Collection files); + public abstract void addAllFiles(@NotNull Collection files); public void addAllFiles(@NotNull VirtualFile... files) { addAllFiles(Arrays.asList(files)); diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/IntelliJCheckBoxUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/IntelliJCheckBoxUI.java index d13551d1d2e4..4492a8bbb4ff 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/IntelliJCheckBoxUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/IntelliJCheckBoxUI.java @@ -37,15 +37,17 @@ public class IntelliJCheckBoxUI extends DarculaCheckBoxUI { g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); g.setStroke(new BasicStroke(JBUI.scale(1) *2.0f, BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)); - g.setPaint(getShadowColor(enabled, true)); final int x1 = JBUI.scale(5); final int y1 = JBUI.scale(9); final int x2 = JBUI.scale(7); final int y2 = JBUI.scale(11); - g.drawLine(x1, y1, x2, y2); - g.drawLine(x2, y2, w-JBUI.scale(2) - 1, JBUI.scale(5)); + if (enabled) { + g.setPaint(getShadowColor(true, true)); + g.drawLine(x1, y1, x2, y2); + g.drawLine(x2, y2, w - JBUI.scale(2) - 1, JBUI.scale(5)); + } g.setPaint(getCheckSignColor(enabled, true)); g.drawLine(x1, y1 - 2, x2, y2 - 2); - g.drawLine(x2, y2 - 2, w-JBUI.scale(2) - 1, JBUI.scale(5) - 2); + g.drawLine(x2, y2 - 2, w - JBUI.scale(2) - 1, JBUI.scale(5) - 2); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshSessionImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshSessionImpl.java index 220d86b09162..e760233798d1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshSessionImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/RefreshSessionImpl.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. @@ -77,7 +77,7 @@ public class RefreshSessionImpl extends RefreshSession { } @Override - public void addAllFiles(@NotNull Collection files) { + public void addAllFiles(@NotNull Collection files) { for (VirtualFile file : files) { if (file == null) { LOG.error("null passed among " + files); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java index 77825b188d01..059a8611cd6f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java @@ -21,7 +21,6 @@ import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.io.ZipFileCache; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VFileProperty; @@ -297,7 +296,7 @@ public class VfsImplUtil { } private static class InvalidationState { - private Map rootsToRefresh; + private Set myRootsToRefresh; @Nullable public static InvalidationState invalidate(@Nullable InvalidationState state, final String path) { @@ -323,19 +322,18 @@ public class VfsImplUtil { private void registerPathToRefresh(String path, ArchiveFileSystem vfs) { NewVirtualFile root = ManagingFS.getInstance().findRoot(vfs.composeRootPath(path), vfs); if (root != null) { - if (rootsToRefresh == null) rootsToRefresh = ContainerUtil.newHashMap(); - rootsToRefresh.put(path, root); + if (myRootsToRefresh == null) myRootsToRefresh = ContainerUtil.newHashSet(); + myRootsToRefresh.add(root); } } public void scheduleRefresh() { - if (rootsToRefresh != null) { - for (VirtualFile root : rootsToRefresh.values()) { - ((NewVirtualFile)root).markDirtyRecursively(); + if (myRootsToRefresh != null) { + for (NewVirtualFile root : myRootsToRefresh) { + root.markDirtyRecursively(); } - ZipFileCache.reset(rootsToRefresh.keySet()); boolean async = !ApplicationManager.getApplication().isUnitTestMode(); - RefreshQueue.getInstance().refresh(async, true, null, rootsToRefresh.values()); + RefreshQueue.getInstance().refresh(async, true, null, myRootsToRefresh); } } } diff --git a/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java b/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java index b7b0638185bf..4ea30d0a2a4b 100644 --- a/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java +++ b/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java @@ -259,6 +259,11 @@ public abstract class AbstractExpandableItemsHandler attributes = msg.getAttributes(); + fireRootPresentationAdded(attributes.get("name"), attributes.get("comment")); + } else { GeneralToSMTRunnerEventsConvertor.logProblem(LOG, "Unexpected service message:" + name, myTestFrameworkName); } diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java index bdf9d9050542..62abe559587b 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java @@ -740,6 +740,9 @@ public class SMTestProxy extends AbstractTestProxy { public static class SMRootTestProxy extends SMTestProxy { private boolean myTestsReporterAttached; // false by default + private String myPresentation; + private String myComment; + public SMRootTestProxy() { super("[root]", true, null); } @@ -752,6 +755,22 @@ public class SMTestProxy extends AbstractTestProxy { return myTestsReporterAttached; } + public String getPresentation() { + return myPresentation; + } + + public void setPresentation(String presentation) { + myPresentation = presentation; + } + + public void setComment(String comment) { + myComment = comment; + } + + public String getComment() { + return myComment; + } + @Override protected AbstractState determineSuiteStateOnFinished() { if (isLeaf() && !isTestsReporterAttached()) { diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/TestsPresentationUtil.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/TestsPresentationUtil.java index d36250191b09..ed7789e236a9 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/TestsPresentationUtil.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/TestsPresentationUtil.java @@ -145,7 +145,10 @@ public class TestsPresentationUtil { final TestStateInfo.Magnitude magnitude = testProxy.getMagnitudeInfo(); final String text; - if (magnitude == TestStateInfo.Magnitude.RUNNING_INDEX) { + final String presentableName = testProxy.getPresentation(); + if (presentableName != null) { + text = presentableName; + } else if (magnitude == TestStateInfo.Magnitude.RUNNING_INDEX) { text = SMTestsRunnerBundle.message("sm.test.runner.ui.tests.tree.presentation.labels.running.tests"); } else if (magnitude == TestStateInfo.Magnitude.TERMINATED_INDEX) { text = SMTestsRunnerBundle.message("sm.test.runner.ui.tests.tree.presentation.labels.was.terminated"); @@ -153,6 +156,10 @@ public class TestsPresentationUtil { text = SMTestsRunnerBundle.message("sm.test.runner.ui.tests.tree.presentation.labels.test.results"); } renderer.append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); + final String comment = testProxy.getComment(); + if (comment != null) { + renderer.append(" (" + comment + ")", SimpleTextAttributes.GRAY_ATTRIBUTES); + } } public static void formatRootNodeWithoutChildren(final SMTestProxy.SMRootTestProxy testProxy, diff --git a/platform/util/src/com/intellij/openapi/util/io/ZipFileCache.java b/platform/util/src/com/intellij/openapi/util/io/ZipFileCache.java index 89aa9dddb0f3..4b1873e48297 100644 --- a/platform/util/src/com/intellij/openapi/util/io/ZipFileCache.java +++ b/platform/util/src/com/intellij/openapi/util/io/ZipFileCache.java @@ -15,259 +15,26 @@ */ package com.intellij.openapi.util.io; -import com.intellij.openapi.diagnostic.LogUtil; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.util.ConcurrencyUtil; -import com.intellij.util.Function; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.io.File; import java.io.IOException; import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import java.util.zip.ZipFile; -/** - *

Utility class which tries to keep frequently requested .zip files open - * to avoid time loss on closing/reopening ZipFile instances.

- * - *

Clients obtain a file by calling {@link #acquire(String)} - * and indicate the loss of interest to it via {@link #release(ZipFile)}. - * Released files are closed after some period of time (about 30 seconds), - * unless requested again within the period.

- * - *

Since ZipFiles are read-only objects allowing concurrent access, - * a same instance may be returned to a different threads requesting a same path. - * A file may be closed only after being released by all applicants.

- * - *

The class does not expect .zip files on a disk to be changed, - * so it may return an outdated instance of ZipFile (reading from it - * may return inaccurate data or even cause an exceptions to happen). - * It's a clients' responsibility to keep a track of .zip files - * and call the {@link #reset(Collection)} method for a paths - * which are possibly changed. Reset paths are removed from the cache - * and are closed immediately after being released.

- */ +/** @deprecated use {@link ZipFile#ZipFile(String)} and {@link ZipFile#close()} instead (to be removed in IDEA 17) */ +@SuppressWarnings("unused") public class ZipFileCache { - private static final int PERIOD = 10000; // disposer schedule, ms - private static final int TIMEOUT = 30000; // released file close delay, ms - - private static class CacheRecord { - private final String path; - private final ZipFile file; - private int count = 1; - private long released = 0; - - private CacheRecord(@NotNull String path, @NotNull ZipFile file) throws IOException { - this.path = path; - this.file = file; - } - } - - private static final Object ourLock = new Object(); - private static final Map ourPathCache = ContainerUtil.newTroveMap(FileUtil.PATH_HASHING_STRATEGY); - private static final Map ourFileCache = ContainerUtil.newHashMap(); - private static final Map ourQueue = ContainerUtil.newHashMap(); - - private static final ScheduledThreadPoolExecutor ourExecutor; - - static { - ourExecutor = ConcurrencyUtil.newSingleScheduledThreadExecutor("ZipFileCache Dispose", Thread.MIN_PRIORITY); - ourExecutor.scheduleWithFixedDelay(new Runnable() { - @Override - public void run() { - List toClose = getFilesToClose(0, System.currentTimeMillis() - TIMEOUT); - if (toClose != null) { - close(toClose); - } - } - }, PERIOD, PERIOD, TimeUnit.MILLISECONDS); - } - @NotNull public static ZipFile acquire(@NotNull String path) throws IOException { - path = toCanonicalPath(path); - - synchronized (ourLock) { - CacheRecord record = ourPathCache.get(path); - if (record != null) { - record.count++; - return record.file; - } - } - - CacheRecord record; - ZipFile file = tryOpen(path); - - synchronized (ourLock) { - record = ourPathCache.get(path); - if (record == null) { - record = new CacheRecord(path, file); - ourPathCache.put(path, record); - ourFileCache.put(file, record); - return file; - } - else { - record.count++; - } - } - - close(file); - return record.file; - } - - private static String toCanonicalPath(@NotNull String path) { - path = FileUtil.toSystemDependentName(path); - - File file = new File(path); - try { - return file.getCanonicalPath(); - } - catch (IOException e) { - return file.getAbsolutePath(); - } - } - - private static ZipFile tryOpen(String path) throws IOException { - debug("opening %s", path); - try { - return new ZipFile(path); - } - catch (IOException e) { - String reason = e.getMessage(); - if ("too many open files".equalsIgnoreCase(reason) && tryCloseFiles() > 0) { - return new ZipFile(path); - } - else { - throw e; - } - } - } - - private static int tryCloseFiles() { - List toClose = getFilesToClose(5, 0); - if (toClose == null) return 0; - close(toClose); - logger().warn("too many open files, closed: " + toClose.size()); - return toClose.size(); - } - - @Nullable - private static List getFilesToClose(int limit, long timeout) { - List toClose = null; - - synchronized (ourLock) { - Iterator i = ourPathCache.values().iterator(); - while (i.hasNext() && (limit == 0 || toClose == null || toClose.size() < limit)) { - CacheRecord record = i.next(); - if (record.count <= 0 && (timeout == 0 || record.released <= timeout)) { - i.remove(); - ourFileCache.remove(record.file); - if (toClose == null) toClose = ContainerUtil.newArrayList(); - toClose.add(record.file); - } - } - } - - return toClose; + return new ZipFile(path); } public static void release(@NotNull ZipFile file) { - synchronized (ourLock) { - CacheRecord record = ourFileCache.get(file); - if (record != null) { - record.count--; - record.released = System.currentTimeMillis(); - logger().assertTrue(record.count >= 0, record.path); - return; - } - - Integer count = ourQueue.get(file); - if (count != null) { - count--; - if (count == 0) { - ourQueue.remove(file); - close(file); - } - else { - ourQueue.put(file, count); - } - return; - } - } - - logger().warn(new IllegalArgumentException("stray file: " + file.getName())); - close(file); + try { file.close(); } + catch (IOException ignored) { } } - public static void reset(@NotNull Collection paths) { - debug("resetting %s", paths); + public static void reset(Collection paths) { } - List toClose = ContainerUtil.newSmartList(); - - paths = ContainerUtil.map(paths, new Function() { - @Override - public String fun(String path) { - return toCanonicalPath(path); - } - }); - - synchronized (ourLock) { - for (String path : paths) { - CacheRecord record = ourPathCache.remove(path); - if (record != null) { - ourFileCache.remove(record.file); - if (record.count > 0) { - ourQueue.put(record.file, record.count); - } - else { - toClose.add(record.file); - } - } - } - } - - close(toClose); - } - - private static void close(@NotNull List files) { - for (ZipFile file : files) { - close(file); - } - } - - private static void close(@NotNull ZipFile file) { - debug("closing %s", file.getName()); - try { - file.close(); - } - catch (IOException e) { - logger().info(file.getName(), e); - } - } - - private static Logger logger() { - return Logger.getInstance(ZipFileCache.class); - } - - private static void debug(@NotNull String format, Object... args) { - LogUtil.debug(logger(), format, args); - } - - /** - * ZipFileCache maintains a background thread. In server environments, this thread may run indefinitely and prevent the class loader from - * being gc-ed. Thus it's necessary to invoke this method to stop that thread and let the classes be garbage-collected. - */ - @SuppressWarnings("unused") - public static void stopBackgroundThread() { - if (ourExecutor != null) { - ourExecutor.shutdown(); - } - } + public static void stopBackgroundThread() { } } diff --git a/platform/util/src/com/intellij/util/lang/JarLoader.java b/platform/util/src/com/intellij/util/lang/JarLoader.java index 4a040e53ef88..0c5499f4c2d5 100644 --- a/platform/util/src/com/intellij/util/lang/JarLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarLoader.java @@ -16,15 +16,15 @@ package com.intellij.util.lang; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.io.ZipFileCache; 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.URISyntaxException; import java.net.URL; import java.util.Enumeration; import java.util.zip.ZipEntry; @@ -32,15 +32,14 @@ import java.util.zip.ZipFile; class JarLoader extends Loader { private final URL myURL; - private final boolean myCanLockJar; private SoftReference myMemoryLoader; - JarLoader(URL url, boolean canLockJar, int index, boolean preloadJarContents) throws IOException { + // 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; - myCanLockJar = canLockJar; - ZipFile zipFile = acquireZipFile(); + ZipFile zipFile = new ZipFile(getFileUrl()); try { if (preloadJarContents) { JarMemoryLoader loader = JarMemoryLoader.load(zipFile, getBaseURL()); @@ -50,29 +49,23 @@ class JarLoader extends Loader { } } finally { - releaseZipFile(zipFile); - } - } - - private ZipFile acquireZipFile() throws IOException { - String path = FileUtil.unquote(myURL.getFile()); - //noinspection IOResourceOpenedButNotSafelyClosed - return myCanLockJar ? ZipFileCache.acquire(path) : new ZipFile(path); - } - - private void releaseZipFile(ZipFile zipFile) throws IOException { - if (myCanLockJar) { - ZipFileCache.release(zipFile); - } - else { zipFile.close(); } } + private File getFileUrl() throws IOException { + try { + return new File(myURL.toURI()); + } + catch (URISyntaxException e) { + throw new IOException(e); + } + } + @NotNull @Override public ClasspathCache.LoaderData buildData() throws IOException { - ZipFile zipFile = acquireZipFile(); + ZipFile zipFile = new ZipFile(getFileUrl()); try { ClasspathCache.LoaderData loaderData = new ClasspathCache.LoaderData(); Enumeration entries = zipFile.entries(); @@ -85,7 +78,7 @@ class JarLoader extends Loader { return loaderData; } finally { - releaseZipFile(zipFile); + zipFile.close(); } } @@ -99,7 +92,7 @@ class JarLoader extends Loader { } try { - ZipFile zipFile = acquireZipFile(); + ZipFile zipFile = new ZipFile(getFileUrl()); try { ZipEntry entry = zipFile.getEntry(name); if (entry != null) { @@ -107,7 +100,7 @@ class JarLoader extends Loader { } } finally { - releaseZipFile(zipFile); + zipFile.close(); } } catch (Exception e) { diff --git a/plugins/junit/src/META-INF/plugin.xml b/plugins/junit/src/META-INF/plugin.xml index 43d2dceeb5c4..0ca3bc1d7ed9 100644 --- a/plugins/junit/src/META-INF/plugin.xml +++ b/plugins/junit/src/META-INF/plugin.xml @@ -55,6 +55,9 @@ + + + diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java index dd6e52cf8f2a..a71e2cfcf17c 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java @@ -25,6 +25,9 @@ import org.junit.runner.Result; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public class JUnitTreeByDescriptionHierarchyTest { @Test @@ -35,32 +38,96 @@ public class JUnitTreeByDescriptionHierarchyTest { @Test public void test2Parameterized() throws Exception { final Description root = Description.createSuiteDescription("root"); + final ArrayList tests = new ArrayList(); for (String className : new String[]{"a.TestA", "a.TestB"}) { final Description aTestClass = Description.createSuiteDescription(className); root.addChild(aTestClass); - for (String paramName : new String[]{"[0]", "[1]"}) { - final Description param1 = Description.createSuiteDescription(paramName); - aTestClass.addChild(param1); - param1.addChild(Description.createTestDescription(className, "testName" + paramName)); - } - + attachParameterizedTests(className, aTestClass, tests); } - doTest(root, "##teamcity[suiteTreeStarted name='TestA' locationHint='java:suite://a.TestA']\n" + - "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestA.|[0|]']\n" + - "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + - "##teamcity[suiteTreeEnded name='|[0|]']\n" + - "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://a.TestA.|[1|]']\n" + - "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + - "##teamcity[suiteTreeEnded name='|[1|]']\n" + - "##teamcity[suiteTreeEnded name='TestA']\n" + - "##teamcity[suiteTreeStarted name='TestB' locationHint='java:suite://a.TestB']\n" + - "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestB.|[0|]']\n" + - "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestB.testName|[0|]']\n" + - "##teamcity[suiteTreeEnded name='|[0|]']\n" + - "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://a.TestB.|[1|]']\n" + - "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + - "##teamcity[suiteTreeEnded name='|[1|]']\n" + - "##teamcity[suiteTreeEnded name='TestB']\n"); + doTest(root, tests, + "##teamcity[suiteTreeStarted name='TestA' locationHint='java:suite://a.TestA']\n" + + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestA.|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[suiteTreeEnded name='|[0|]']\n" + + "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://a.TestA.|[1|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + + "##teamcity[suiteTreeEnded name='|[1|]']\n" + + "##teamcity[suiteTreeEnded name='TestA']\n" + + "##teamcity[suiteTreeStarted name='TestB' locationHint='java:suite://a.TestB']\n" + + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestB.|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestB.testName|[0|]']\n" + + "##teamcity[suiteTreeEnded name='|[0|]']\n" + + "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://a.TestB.|[1|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + + "##teamcity[suiteTreeEnded name='|[1|]']\n" + + "##teamcity[suiteTreeEnded name='TestB']\n", + + + "##teamcity[enteredTheMatrix]\n" + + "\n" + + "##teamcity[rootName name = 'root']\n" + + "\n" + + "##teamcity[testSuiteFinished name='root']\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[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[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[testStarted name='testName|[1|]' locationHint='java:test://a.TestB.testName|[1|]']\n" + + "\n" + + "##teamcity[testFinished name='testName|[1|]']\n" + + "##teamcity[testSuiteFinished name='|[1|]']\n" + + "\n" + + "##teamcity[testSuiteFinished name='TestB']\n" + + "\n"); + } + + @Test + public void testSingleParameterizedClass() throws Exception { + final String className = "a.TestA"; + final Description aTestClassDescription = Description.createSuiteDescription(className); + final ArrayList tests = new ArrayList(); + attachParameterizedTests(className, aTestClassDescription, tests); + doTest(aTestClassDescription, tests, + //tree + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://a.TestA.|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://a.TestA.testName|[0|]']\n" + + "##teamcity[suiteTreeEnded name='|[0|]']\n" + + "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://a.TestA.|[1|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + + "##teamcity[suiteTreeEnded name='|[1|]']\n", + //start + "##teamcity[enteredTheMatrix]\n" + + "\n" + + "##teamcity[rootName name = 'TestA' comment = 'a']\n" + + "\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[testStarted name='testName|[1|]' locationHint='java:test://a.TestA.testName|[1|]']\n" + + "\n" + + "##teamcity[testFinished name='testName|[1|]']\n" + + "##teamcity[testSuiteFinished name='|[1|]']\n" + + "\n" + + "##teamcity[testSuiteFinished name='TestA']\n" + + "\n"); } @Test @@ -68,17 +135,76 @@ public class JUnitTreeByDescriptionHierarchyTest { final Description root = Description.createSuiteDescription("root"); final String className = "ATest"; final String methodName = "test1"; + final List tests = new ArrayList(); for( String suiteName : new String[] {"ASuite1", "ASuite2"}) { final Description aSuite = Description.createSuiteDescription(suiteName); root.addChild(aSuite); final Description aTest = Description.createSuiteDescription(className); aSuite.addChild(aTest); - aTest.addChild(Description.createTestDescription(className, methodName)); + final Description testDescription = Description.createTestDescription(className, methodName); + tests.add(testDescription); + aTest.addChild(testDescription); } - - doTest(root, "##teamcity[suiteTreeStarted name='ATest' locationHint='java:suite://ATest']\n" + - "##teamcity[suiteTreeNode name='test1' locationHint='java:test://ATest.test1']\n" + - "##teamcity[suiteTreeEnded name='ATest']\n"); + + doTest(root, tests, + //expected tree + "##teamcity[suiteTreeStarted name='ASuite1' locationHint='java:suite://ASuite1']\n" + + "##teamcity[suiteTreeStarted name='ATest' locationHint='java:suite://ATest']\n" + + "##teamcity[suiteTreeNode name='test1' locationHint='java:test://ATest.test1']\n" + + "##teamcity[suiteTreeEnded name='ATest']\n" + + "##teamcity[suiteTreeEnded name='ASuite1']\n" + + "##teamcity[suiteTreeStarted name='ASuite2' locationHint='java:suite://ASuite2']\n" + + "##teamcity[suiteTreeStarted name='ATest' locationHint='java:suite://ATest']\n" + + "##teamcity[suiteTreeNode name='test1' locationHint='java:test://ATest.test1']\n" + + "##teamcity[suiteTreeEnded name='ATest']\n" + + "##teamcity[suiteTreeEnded name='ASuite2']\n", + + //started + "##teamcity[enteredTheMatrix]\n" + + "\n" + + "##teamcity[rootName name = 'root']\n" + + "\n" + + "##teamcity[testSuiteFinished name='root']\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[testStarted name='test1' locationHint='java:test://ATest.test1']\n" + + "\n" + + "##teamcity[testFinished name='test1']\n" + + "##teamcity[testSuiteFinished name='ATest']\n" + + "\n" + + "##teamcity[testSuiteFinished name='ASuite2']\n" + + "\n"); + } + + 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() { + @Override + public void write(int b) throws IOException { + buf.append(new String(new byte[]{(byte)b})); + } + })); + sender.sendTree(root); + + Assert.assertEquals("output: " + buf, expectedTree, StringUtil.convertLineSeparators(buf.toString())); + + buf.setLength(0); + + sender.testRunStarted(root); + for (Description test : tests) { + sender.testStarted(test); + sender.testFinished(test); + } + sender.testRunFinished(new Result()); + + Assert.assertEquals("output: " + buf, expectedStart, StringUtil.convertLineSeparators(buf.toString())); } @Test @@ -92,27 +218,70 @@ public class JUnitTreeByDescriptionHierarchyTest { final Description root = Description.createSuiteDescription("root"); final Description aTestClass = Description.createSuiteDescription("ATest"); root.addChild(aTestClass); - for (String paramName : new String[]{"[0]", "[1]"}) { - final Description param1 = Description.createSuiteDescription(paramName); - aTestClass.addChild(param1); - param1.addChild(Description.createTestDescription("ATest", "testName" + paramName)); - } + final ArrayList tests = new ArrayList(); + attachParameterizedTests("ATest", aTestClass, tests); final Description suiteDescription = Description.createSuiteDescription("suite"); root.addChild(suiteDescription); final Description aTestClassWithJUnit3Test = Description.createSuiteDescription("ATest"); suiteDescription.addChild(aTestClassWithJUnit3Test); - aTestClassWithJUnit3Test.addChild(Description.createTestDescription("ATest", "test")); - doTest(root, "##teamcity[suiteTreeStarted name='ATest' locationHint='java:suite://ATest']\n" + - "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://ATest.|[0|]']\n" + - "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + - "##teamcity[suiteTreeEnded name='|[0|]']\n" + - "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://ATest.|[1|]']\n" + - "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + - "##teamcity[suiteTreeEnded name='|[1|]']\n" + - "##teamcity[suiteTreeNode name='test' locationHint='java:test://ATest.test']\n" + - "##teamcity[suiteTreeEnded name='ATest']\n"); + final Description testDescription = Description.createTestDescription("ATest", "test"); + aTestClassWithJUnit3Test.addChild(testDescription); + tests.add(testDescription); + doTest(root, tests, + "##teamcity[suiteTreeStarted name='ATest' locationHint='java:suite://ATest']\n" + + "##teamcity[suiteTreeStarted name='|[0|]' locationHint='java:suite://ATest.|[0|]']\n" + + "##teamcity[suiteTreeNode name='testName|[0|]' locationHint='java:test://ATest.testName|[0|]']\n" + + "##teamcity[suiteTreeEnded name='|[0|]']\n" + + "##teamcity[suiteTreeStarted name='|[1|]' locationHint='java:suite://ATest.|[1|]']\n" + + "##teamcity[suiteTreeNode name='testName|[1|]' locationHint='java:test://ATest.testName|[1|]']\n" + + "##teamcity[suiteTreeEnded name='|[1|]']\n" + + "##teamcity[suiteTreeEnded name='ATest']\n" + + "##teamcity[suiteTreeStarted name='suite' locationHint='java:suite://suite']\n" + + "##teamcity[suiteTreeStarted name='ATest' locationHint='java:suite://ATest']\n" + + "##teamcity[suiteTreeNode name='test' locationHint='java:test://ATest.test']\n" + + "##teamcity[suiteTreeEnded name='ATest']\n" + + "##teamcity[suiteTreeEnded name='suite']\n", + + //start + "##teamcity[enteredTheMatrix]\n" + + "\n" + + "##teamcity[rootName name = 'root']\n" + + "\n" + + "##teamcity[testSuiteFinished name='root']\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[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[testStarted name='test' locationHint='java:test://ATest.test']\n" + + "\n" + + "##teamcity[testFinished name='test']\n" + + "##teamcity[testSuiteFinished name='ATest']\n" + + "\n" + + "##teamcity[testSuiteFinished name='suite']\n" + + "\n"); } + + private static void attachParameterizedTests(String className, Description aTestClass, List tests) { + for (String paramName : new String[]{"[0]", "[1]"}) { + final Description param1 = Description.createSuiteDescription(paramName); + aTestClass.addChild(param1); + final Description testDescription = Description.createTestDescription(className, "testName" + paramName); + tests.add(testDescription); + param1.addChild(testDescription); + } + } + private static void doTest(Description description, String expected) { final StringBuffer buf = new StringBuffer(); new SMTestSender(new PrintStream(new OutputStream() { @@ -130,31 +299,18 @@ public class JUnitTreeByDescriptionHierarchyTest { final Description description = Description.createSuiteDescription("TestA"); final Description emptyDescription = Description.createTestDescription(SMTestSender.EMPTY_SUITE_NAME, SMTestSender.EMPTY_SUITE_WARNING); description.addChild(emptyDescription); + doTest(description, Collections.singletonList(emptyDescription), + "##teamcity[suiteTreeNode name='warning' locationHint='java:test://TestA.warning']\n", - final StringBuffer buf = new StringBuffer(); - final PrintStream printStream = new PrintStream(new OutputStream() { - @Override - public void write(int b) throws IOException { - buf.append(new String(new byte[]{(byte)b})); - } - }); - final SMTestSender sender = new SMTestSender(printStream); - - sender.sendTree(description); - - sender.testRunStarted(description); - sender.testStarted(emptyDescription); - sender.testFinished(emptyDescription); - sender.testRunFinished(new Result()); - - Assert.assertEquals("output: " + buf, "##teamcity[suiteTreeNode name='warning' locationHint='java:test://junit.framework.TestSuite$1.warning']\n" + - "##teamcity[enteredTheMatrix]\n" + - "\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" + - "\n", StringUtil.convertLineSeparators(buf.toString())); + "##teamcity[enteredTheMatrix]\n" + + "\n" + + "##teamcity[rootName name = 'TestA']\n" + + "\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" + + "\n"); } } diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java index 19ad2bff1af6..fcdb82291eba 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4IdeaTestRunner.java @@ -52,7 +52,7 @@ public class JUnit4IdeaTestRunner implements IdeaTestRunner { description = getFilteredDescription(request, description); } TreeSender.sendTree(this, description, sendTree); - if (myTestsListener instanceof SMTestSender) { + if (myTestsListener instanceof SMTestSender && sendTree) { ((SMTestSender)myTestsListener).sendTree(description); } } diff --git a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java index f82a6077cc96..e5b831eaa3fd 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java +++ b/plugins/junit_rt/src/com/intellij/junit4/SMTestSender.java @@ -41,10 +41,14 @@ public class SMTestSender extends RunListener { 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 PrintStream myPrintStream = System.out; + private final Map myParents = new HashMap(); + private final Map mySuites = new HashMap(); + public SMTestSender() {} @@ -58,6 +62,20 @@ public class SMTestSender extends RunListener { public void testRunStarted(Description description) throws Exception { myPrintStream.println("##teamcity[enteredTheMatrix]\n"); + if (myCurrentClassName != null && !myCurrentClassName.startsWith("[")) { + int lastPointIdx = myCurrentClassName.lastIndexOf('.'); + String name = myCurrentClassName; + String comment = null; + if (lastPointIdx >= 0) { + name = myCurrentClassName.substring(lastPointIdx + 1); + comment = myCurrentClassName.substring(0, lastPointIdx); + } + + myPrintStream.println("##teamcity[rootName name = \'" + escapeName(name) + + (comment != null ? ("\' comment = \'" + escapeName(comment)) : "") + + "\']\n"); + myCurrentClassName = getShortName(myCurrentClassName); + } } public void testRunFinished(Result result) throws Exception { @@ -67,6 +85,9 @@ public class SMTestSender extends RunListener { if (myCurrentClassName != null) { myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(myCurrentClassName) + "\']\n"); } + if (myCurrentSuiteName != null) { + myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']\n"); + } } public void testStarted(Description description) throws Exception { @@ -77,10 +98,35 @@ public class SMTestSender extends RunListener { 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); + + if (descriptors.isEmpty()) { + currentSuite = (String)suites.get(1); + descriptors = (List)mySuites.get(currentSuite); + } + + if (!currentSuite.equals(myCurrentSuiteName)) { + finishCurrentSuite(); + myCurrentSuiteName = currentSuite; + myPrintStream.println("##teamcity[testSuiteStarted name =\'" + escapeName(getShortName(myCurrentSuiteName)) + "\']"); + } + + 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) + "\']"); @@ -99,6 +145,16 @@ public class SMTestSender extends RunListener { 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)) + "\']"); } @@ -194,31 +250,49 @@ public class SMTestSender extends RunListener { private final List myEmptyTests = new ArrayList(); - private void sendTree(Description description, Map groups, Description parent) { - final String className = JUnit4ReflectionUtil.getClassName(description); + private void sendTree(Description description, Description parent, Description suiteParent) { + String className = JUnit4ReflectionUtil.getClassName(description); if (description.getChildren().isEmpty()) { final String methodName = JUnit4ReflectionUtil.getMethodName((Description)description); if (methodName != null) { - myPrintStream.println("##teamcity[suiteTreeNode name=\'" + escapeName(methodName) + "\' " + getTestMethodLocation(methodName, className) + "]"); if (isWarning(methodName, className)) { - myEmptyTests.add(getShortName(JUnit4ReflectionUtil.getClassName(parent))); + 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)) { + List parents = (List)myParents.get(description); + if (parents == null) { + parents = new ArrayList(); + 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); } } + return; } - List tests = (List)groups.get(description); - if (isParameter(description)) { - tests = description.getChildren(); - } - if (tests == null) { - return; - } + + List tests = description.getChildren(); boolean pass = false; for (Iterator iterator = tests.iterator(); iterator.hasNext(); ) { final Object next = iterator.next(); - final List childTests = ((Description)next).getChildren(); final Description nextDescription = (Description)next; - if (((myCurrentClassName == null || !myCurrentClassName.equals(getShortName(className))) && childTests.isEmpty() && JUnit4ReflectionUtil.getMethodName(nextDescription) != null || isParameter(nextDescription)) && !pass) { + final List childTests = nextDescription.getChildren(); + if ((myCurrentClassName == null || !myCurrentClassName.equals(className)) && !pass) { pass = true; String locationHint = className; if (isParameter((Description)description)) { @@ -231,14 +305,12 @@ public class SMTestSender extends RunListener { } } } - myPrintStream.println("##teamcity[suiteTreeStarted name=\'" + escapeName(getShortName(className)) + - "\' locationHint=\'java:suite://" + escapeName(locationHint) + "\']"); + myPrintStream.println("##teamcity[suiteTreeStarted name=\'" + escapeName(getShortName(className)) + "\' locationHint=\'java:suite://" + escapeName(locationHint) + "\']"); } - sendTree(nextDescription, groups, description); + sendTree(nextDescription, description, isParameter(description) ? null : childTests.isEmpty() && parent != null ? parent : description); } if (pass) { myPrintStream.println("##teamcity[suiteTreeEnded name=\'" + escapeName(getShortName(JUnit4ReflectionUtil.getClassName((Description)description))) + "\']"); - groups.remove(description); } } @@ -250,34 +322,14 @@ public class SMTestSender extends RunListener { return "locationHint=\'java:test://" + escapeName(className + "." + methodName) + "\'"; } - private static void groupTests(Object description, Map found) { - if (!isParameter((Description)description)) { - ArrayList childTests = ((Description)description).getChildren(); - List children = (List)found.get(description); - if (children == null) { - children = new ArrayList(); - found.put(description, children); - } else { - childTests = new ArrayList(childTests); - childTests.removeAll(children); - } - children.addAll(childTests); - for (Iterator iterator = childTests.iterator(); iterator.hasNext(); ) { - groupTests(iterator.next(), found); - } - } - } - private static boolean isParameter(Description description) { String displayName = description.getDisplayName(); return displayName.startsWith("[") && displayName.endsWith("]"); } public void sendTree(Description description) { - myCurrentClassName = getShortName(JUnit4ReflectionUtil.getClassName((Description)description)); - final HashMap group = new HashMap(); - groupTests(description, group); - sendTree(description, group, null); + myCurrentClassName = JUnit4ReflectionUtil.getClassName((Description)description); + sendTree(description, null, null); } private static String getShortName(String fqName) { diff --git a/python/resources/icons/com/jetbrains/python/debug/StepIntoMyCode.png b/python/resources/icons/com/jetbrains/python/debug/StepIntoMyCode.png index 6872566a1f0e..a5cd4415d07b 100644 Binary files a/python/resources/icons/com/jetbrains/python/debug/StepIntoMyCode.png and b/python/resources/icons/com/jetbrains/python/debug/StepIntoMyCode.png differ diff --git a/python/resources/icons/com/jetbrains/python/debug/StepIntoMyCode@2x.png b/python/resources/icons/com/jetbrains/python/debug/StepIntoMyCode@2x.png index fdd640d0518f..48fe75e5d895 100644 Binary files a/python/resources/icons/com/jetbrains/python/debug/StepIntoMyCode@2x.png and b/python/resources/icons/com/jetbrains/python/debug/StepIntoMyCode@2x.png differ