Exemplo n.º 1
0
  public XDebuggerTree(
      final @NotNull Project project,
      final @NotNull XDebuggerEditorsProvider editorsProvider,
      final @Nullable XSourcePosition sourcePosition,
      final @NotNull String popupActionGroupId,
      @Nullable XValueMarkers<?, ?> valueMarkers) {
    myValueMarkers = valueMarkers;
    myProject = project;
    myEditorsProvider = editorsProvider;
    mySourcePosition = sourcePosition;
    myTreeModel = new DefaultTreeModel(null);
    setModel(myTreeModel);
    setCellRenderer(new XDebuggerTreeRenderer());
    new TreeLinkMouseListener(new XDebuggerTreeRenderer()) {
      @Override
      protected boolean doCacheLastNode() {
        return false;
      }

      @Override
      protected void handleTagClick(@Nullable Object tag, @NotNull MouseEvent event) {
        if (tag instanceof XDebuggerTreeNodeHyperlink) {
          ((XDebuggerTreeNodeHyperlink) tag).onClick(event);
        }
      }
    }.installOn(this);
    setRootVisible(false);
    setShowsRootHandles(true);

    new DoubleClickListener() {
      @Override
      protected boolean onDoubleClick(MouseEvent e) {
        return expandIfEllipsis();
      }
    }.installOn(this);

    addKeyListener(
        new KeyAdapter() {
          @Override
          public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            if (key == KeyEvent.VK_ENTER || key == KeyEvent.VK_SPACE || key == KeyEvent.VK_RIGHT) {
              expandIfEllipsis();
            }
          }
        });

    if (Boolean.valueOf(System.getProperty("xdebugger.variablesView.rss"))) {
      new XDebuggerTreeSpeedSearch(this, SPEED_SEARCH_CONVERTER);
    } else {
      new TreeSpeedSearch(this, SPEED_SEARCH_CONVERTER);
    }

    final ActionManager actionManager = ActionManager.getInstance();
    addMouseListener(
        new PopupHandler() {
          @Override
          public void invokePopup(final Component comp, final int x, final int y) {
            ActionGroup group = (ActionGroup) actionManager.getAction(popupActionGroupId);
            actionManager
                .createActionPopupMenu(ActionPlaces.UNKNOWN, group)
                .getComponent()
                .show(comp, x, y);
          }
        });
    registerShortcuts();

    setTransferHandler(DEFAULT_TRANSFER_HANDLER);

    addComponentListener(myMoveListener);
  }
/** @author yole */
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public class PlatformTestUtil {
  public static final boolean COVERAGE_ENABLED_BUILD =
      "true".equals(System.getProperty("idea.coverage.enabled.build"));
  public static final byte[] EMPTY_JAR_BYTES = {
    0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  };

  public static <T> void registerExtension(
      final ExtensionPointName<T> name, final T t, final Disposable parentDisposable) {
    registerExtension(Extensions.getRootArea(), name, t, parentDisposable);
  }

  public static <T> void registerExtension(
      final ExtensionsArea area,
      final ExtensionPointName<T> name,
      final T t,
      final Disposable parentDisposable) {
    final ExtensionPoint<T> extensionPoint = area.getExtensionPoint(name.getName());
    extensionPoint.registerExtension(t);
    Disposer.register(
        parentDisposable,
        new Disposable() {
          @Override
          public void dispose() {
            extensionPoint.unregisterExtension(t);
          }
        });
  }

  @Nullable
  protected static String toString(@Nullable Object node, @Nullable Queryable.PrintInfo printInfo) {
    if (node instanceof AbstractTreeNode) {
      if (printInfo != null) {
        return ((AbstractTreeNode) node).toTestString(printInfo);
      } else {
        @SuppressWarnings({"deprecation", "UnnecessaryLocalVariable"})
        final String presentation = ((AbstractTreeNode) node).getTestPresentation();
        return presentation;
      }
    }
    if (node == null) {
      return "NULL";
    }
    return node.toString();
  }

  public static String print(JTree tree, boolean withSelection) {
    return print(tree, withSelection, null);
  }

  public static String print(
      JTree tree, boolean withSelection, @Nullable Condition<String> nodePrintCondition) {
    StringBuilder buffer = new StringBuilder();
    final Collection<String> strings = printAsList(tree, withSelection, nodePrintCondition);
    for (String string : strings) {
      buffer.append(string).append("\n");
    }
    return buffer.toString();
  }

  public static Collection<String> printAsList(
      JTree tree, boolean withSelection, @Nullable Condition<String> nodePrintCondition) {
    Collection<String> strings = new ArrayList<String>();
    Object root = tree.getModel().getRoot();
    printImpl(tree, root, strings, 0, withSelection, nodePrintCondition);
    return strings;
  }

  private static void printImpl(
      JTree tree,
      Object root,
      Collection<String> strings,
      int level,
      boolean withSelection,
      @Nullable Condition<String> nodePrintCondition) {
    DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode) root;

    final Object userObject = defaultMutableTreeNode.getUserObject();
    String nodeText;
    if (userObject != null) {
      nodeText = toString(userObject, null);
    } else {
      nodeText = "null";
    }

    if (nodePrintCondition != null && !nodePrintCondition.value(nodeText)) return;

    final StringBuilder buff = new StringBuilder();
    StringUtil.repeatSymbol(buff, ' ', level);

    final boolean expanded = tree.isExpanded(new TreePath(defaultMutableTreeNode.getPath()));
    if (!defaultMutableTreeNode.isLeaf()) {
      buff.append(expanded ? "-" : "+");
    }

    final boolean selected =
        tree.getSelectionModel().isPathSelected(new TreePath(defaultMutableTreeNode.getPath()));
    if (withSelection && selected) {
      buff.append("[");
    }

    buff.append(nodeText);

    if (withSelection && selected) {
      buff.append("]");
    }

    strings.add(buff.toString());

    int childCount = tree.getModel().getChildCount(root);
    if (expanded) {
      for (int i = 0; i < childCount; i++) {
        printImpl(
            tree,
            tree.getModel().getChild(root, i),
            strings,
            level + 1,
            withSelection,
            nodePrintCondition);
      }
    }
  }

  public static void assertTreeEqual(JTree tree, @NonNls String expected) {
    assertTreeEqual(tree, expected, false);
  }

  public static void assertTreeEqualIgnoringNodesOrder(JTree tree, @NonNls String expected) {
    assertTreeEqualIgnoringNodesOrder(tree, expected, false);
  }

  public static void assertTreeEqual(JTree tree, String expected, boolean checkSelected) {
    String treeStringPresentation = print(tree, checkSelected);
    assertEquals(expected, treeStringPresentation);
  }

  public static void assertTreeEqualIgnoringNodesOrder(
      JTree tree, String expected, boolean checkSelected) {
    final Collection<String> actualNodesPresentation = printAsList(tree, checkSelected, null);
    final List<String> expectedNodes = StringUtil.split(expected, "\n");
    UsefulTestCase.assertSameElements(actualNodesPresentation, expectedNodes);
  }

  @TestOnly
  public static void waitForAlarm(final int delay) throws InterruptedException {
    assert !ApplicationManager.getApplication().isWriteAccessAllowed()
        : "It's a bad idea to wait for an alarm under the write action. Somebody creates an alarm which requires read action and you are deadlocked.";
    assert ApplicationManager.getApplication().isDispatchThread();

    final AtomicBoolean invoked = new AtomicBoolean();
    final Alarm alarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
    alarm.addRequest(
        new Runnable() {
          @Override
          public void run() {
            ApplicationManager.getApplication()
                .invokeLater(
                    new Runnable() {
                      @Override
                      public void run() {
                        alarm.addRequest(
                            new Runnable() {
                              @Override
                              public void run() {
                                invoked.set(true);
                              }
                            },
                            delay);
                      }
                    });
          }
        },
        delay);

    UIUtil.dispatchAllInvocationEvents();

    boolean sleptAlready = false;
    while (!invoked.get()) {
      UIUtil.dispatchAllInvocationEvents();
      //noinspection BusyWait
      Thread.sleep(sleptAlready ? 10 : delay);
      sleptAlready = true;
    }
    UIUtil.dispatchAllInvocationEvents();
  }

  @TestOnly
  public static void dispatchAllInvocationEventsInIdeEventQueue() throws InterruptedException {
    assert SwingUtilities.isEventDispatchThread() : Thread.currentThread();
    final EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
    while (true) {
      AWTEvent event = eventQueue.peekEvent();
      if (event == null) break;
      AWTEvent event1 = eventQueue.getNextEvent();
      if (event1 instanceof InvocationEvent) {
        IdeEventQueue.getInstance().dispatchEvent(event1);
      }
    }
  }

  private static Date raidDate(Bombed bombed) {
    final Calendar instance = Calendar.getInstance();
    instance.set(Calendar.YEAR, bombed.year());
    instance.set(Calendar.MONTH, bombed.month());
    instance.set(Calendar.DAY_OF_MONTH, bombed.day());
    instance.set(Calendar.HOUR_OF_DAY, bombed.time());
    instance.set(Calendar.MINUTE, 0);

    return instance.getTime();
  }

  public static boolean bombExplodes(Bombed bombedAnnotation) {
    Date now = new Date();
    return now.after(raidDate(bombedAnnotation));
  }

  public static boolean isRotten(Bombed bomb) {
    long bombRotPeriod = 30L * 24 * 60 * 60 * 1000; // month
    return new Date().after(new Date(raidDate(bomb).getTime() + bombRotPeriod));
  }

  public static StringBuilder print(
      AbstractTreeStructure structure,
      Object node,
      int currentLevel,
      @Nullable Comparator comparator,
      int maxRowCount,
      char paddingChar,
      @Nullable Queryable.PrintInfo printInfo) {
    StringBuilder buffer = new StringBuilder();
    doPrint(
        buffer, currentLevel, node, structure, comparator, maxRowCount, 0, paddingChar, printInfo);
    return buffer;
  }

  private static int doPrint(
      StringBuilder buffer,
      int currentLevel,
      Object node,
      AbstractTreeStructure structure,
      @Nullable Comparator comparator,
      int maxRowCount,
      int currentLine,
      char paddingChar,
      @Nullable Queryable.PrintInfo printInfo) {
    if (currentLine >= maxRowCount && maxRowCount != -1) return currentLine;

    StringUtil.repeatSymbol(buffer, paddingChar, currentLevel);
    buffer.append(toString(node, printInfo)).append("\n");
    currentLine++;
    Object[] children = structure.getChildElements(node);

    if (comparator != null) {
      ArrayList<?> list = new ArrayList<Object>(Arrays.asList(children));
      @SuppressWarnings({"UnnecessaryLocalVariable", "unchecked"})
      Comparator<Object> c = comparator;
      Collections.sort(list, c);
      children = ArrayUtil.toObjectArray(list);
    }
    for (Object child : children) {
      currentLine =
          doPrint(
              buffer,
              currentLevel + 1,
              child,
              structure,
              comparator,
              maxRowCount,
              currentLine,
              paddingChar,
              printInfo);
    }

    return currentLine;
  }

  public static String print(Object[] objects) {
    return print(Arrays.asList(objects));
  }

  public static String print(Collection c) {
    StringBuilder result = new StringBuilder();
    for (Iterator iterator = c.iterator(); iterator.hasNext(); ) {
      Object each = iterator.next();
      result.append(toString(each, null));
      if (iterator.hasNext()) {
        result.append("\n");
      }
    }

    return result.toString();
  }

  public static String print(ListModel model) {
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < model.getSize(); i++) {
      result.append(toString(model.getElementAt(i), null));
      result.append("\n");
    }
    return result.toString();
  }

  public static String print(JTree tree) {
    return print(tree, false);
  }

  public static void assertTreeStructureEquals(
      final AbstractTreeStructure treeStructure, final String expected) {
    assertEquals(
        expected,
        print(treeStructure, treeStructure.getRootElement(), 0, null, -1, ' ', null).toString());
  }

  public static void invokeNamedAction(final String actionId) {
    final AnAction action = ActionManager.getInstance().getAction(actionId);
    assertNotNull(action);
    final Presentation presentation = new Presentation();
    @SuppressWarnings("deprecation")
    final DataContext context = DataManager.getInstance().getDataContext();
    final AnActionEvent event =
        new AnActionEvent(null, context, "", presentation, ActionManager.getInstance(), 0);
    action.update(event);
    Assert.assertTrue(presentation.isEnabled());
    action.actionPerformed(event);
  }

  public static void assertTiming(final String message, final long expectedMs, final long actual) {
    if (COVERAGE_ENABLED_BUILD) return;

    final long expectedOnMyMachine =
        Math.max(1, expectedMs * Timings.MACHINE_TIMING / Timings.ETALON_TIMING);

    // Allow 10% more in case of test machine is busy.
    String logMessage = message;
    if (actual > expectedOnMyMachine) {
      int percentage = (int) (100.0 * (actual - expectedOnMyMachine) / expectedOnMyMachine);
      logMessage += ". Operation took " + percentage + "% longer than expected";
    }
    logMessage +=
        ". Expected on my machine: "
            + expectedOnMyMachine
            + "."
            + " Actual: "
            + actual
            + "."
            + " Expected on Standard machine: "
            + expectedMs
            + ";"
            + " Actual on Standard: "
            + actual * Timings.ETALON_TIMING / Timings.MACHINE_TIMING
            + ";"
            + " Timings: CPU="
            + Timings.CPU_TIMING
            + ", I/O="
            + Timings.IO_TIMING
            + "."
            + " ("
            + (int) (Timings.MACHINE_TIMING * 1.0 / Timings.ETALON_TIMING * 100)
            + "% of the Standard)"
            + ".";
    final double acceptableChangeFactor = 1.1;
    if (actual < expectedOnMyMachine) {
      System.out.println(logMessage);
      TeamCityLogger.info(logMessage);
    } else if (actual < expectedOnMyMachine * acceptableChangeFactor) {
      TeamCityLogger.warning(logMessage, null);
    } else {
      // throw AssertionFailedError to try one more time
      throw new AssertionFailedError(logMessage);
    }
  }

  /**
   * example usage: startPerformanceTest("calculating pi",100,
   * testRunnable).cpuBound().assertTiming();
   */
  public static TestInfo startPerformanceTest(
      @NonNls @NotNull String message, int expectedMs, @NotNull ThrowableRunnable test) {
    return new TestInfo(test, expectedMs, message);
  }

  // calculates average of the median values in the selected part of the array. E.g. for part=3
  // returns average in the middle third.
  public static long averageAmongMedians(@NotNull long[] time, int part) {
    assert part >= 1;
    int n = time.length;
    Arrays.sort(time);
    long total = 0;
    for (int i = n / 2 - n / part / 2; i < n / 2 + n / part / 2; i++) {
      total += time[i];
    }
    return total / (n / part);
  }

  public static boolean canRunTest(@NotNull Class testCaseClass) {
    if (GraphicsEnvironment.isHeadless()) {
      for (Class<?> clazz = testCaseClass; clazz != null; clazz = clazz.getSuperclass()) {
        if (clazz.getAnnotation(SkipInHeadlessEnvironment.class) != null) {
          System.out.println(
              "Class '"
                  + testCaseClass.getName()
                  + "' is skipped because it requires working UI environment");
          return false;
        }
      }
    }
    return true;
  }

  public static void assertPathsEqual(@Nullable String expected, @Nullable String actual) {
    if (expected != null) expected = FileUtil.toSystemIndependentName(expected);
    if (actual != null) actual = FileUtil.toSystemIndependentName(actual);
    assertEquals(expected, actual);
  }

  public static class TestInfo {
    private final ThrowableRunnable test; // runnable to measure
    private final int expectedMs; // millis the test is expected to run
    private ThrowableRunnable setup; // to run before each test
    private boolean usesAllCPUCores; // true if the test runs faster on multi-core
    private int attempts = 4; // number of retries if performance failed
    private final String message; // to print on fail
    private boolean adjustForIO =
        true; // true if test uses IO, timings need to be re-calibrated according to this agent disk
              // performance
    private boolean adjustForCPU =
        true; // true if test uses CPU, timings need to be re-calibrated according to this agent CPU
              // speed

    private TestInfo(@NotNull ThrowableRunnable test, int expectedMs, String message) {
      this.test = test;
      this.expectedMs = expectedMs;
      assert expectedMs > 0 : "Expected must be > 0. Was: " + expectedMs;
      this.message = message;
    }

    public TestInfo setup(@NotNull ThrowableRunnable setup) {
      assert this.setup == null;
      this.setup = setup;
      return this;
    }

    public TestInfo usesAllCPUCores() {
      assert adjustForCPU : "This test configured to be io-bound, it cannot use all cores";
      usesAllCPUCores = true;
      return this;
    }

    public TestInfo cpuBound() {
      adjustForIO = false;
      adjustForCPU = true;
      return this;
    }

    public TestInfo ioBound() {
      adjustForIO = true;
      adjustForCPU = false;
      return this;
    }

    public TestInfo attempts(int attempts) {
      this.attempts = attempts;
      return this;
    }

    public void assertTiming() {
      assert expectedMs != 0 : "Must call .expect() before run test";
      if (COVERAGE_ENABLED_BUILD) return;

      while (true) {
        attempts--;
        long start;
        try {
          if (setup != null) setup.run();
          start = System.currentTimeMillis();
          test.run();
        } catch (Throwable throwable) {
          throw new RuntimeException(throwable);
        }
        long finish = System.currentTimeMillis();
        long duration = finish - start;

        int expectedOnMyMachine = expectedMs;
        if (adjustForCPU) {
          expectedOnMyMachine =
              adjust(expectedOnMyMachine, Timings.CPU_TIMING, Timings.ETALON_CPU_TIMING);

          expectedOnMyMachine =
              usesAllCPUCores
                  ? expectedOnMyMachine * 8 / JobSchedulerImpl.CORES_COUNT
                  : expectedOnMyMachine;
        }
        if (adjustForIO) {
          expectedOnMyMachine =
              adjust(expectedOnMyMachine, Timings.IO_TIMING, Timings.ETALON_IO_TIMING);
        }

        // Allow 10% more in case of test machine is busy.
        String logMessage = message;
        if (duration > expectedOnMyMachine) {
          int percentage = (int) (100.0 * (duration - expectedOnMyMachine) / expectedOnMyMachine);
          logMessage += ": " + percentage + "% longer";
        }
        logMessage +=
            ". Expected: "
                + formatTime(expectedOnMyMachine)
                + ". Actual: "
                + formatTime(duration)
                + "."
                + Timings.getStatistics();
        final double acceptableChangeFactor = 1.1;
        if (duration < expectedOnMyMachine) {
          int percentage = (int) (100.0 * (expectedOnMyMachine - duration) / expectedOnMyMachine);
          logMessage = percentage + "% faster. " + logMessage;

          TeamCityLogger.info(logMessage);
          System.out.println("SUCCESS: " + logMessage);
        } else if (duration < expectedOnMyMachine * acceptableChangeFactor) {
          TeamCityLogger.warning(logMessage, null);
          System.out.println("WARNING: " + logMessage);
        } else {
          // try one more time
          if (attempts == 0) {
            // try {
            //  Object result =
            // Class.forName("com.intellij.util.ProfilingUtil").getMethod("captureCPUSnapshot").invoke(null);
            //  System.err.println("CPU snapshot captured in '"+result+"'");
            // }
            // catch (Exception e) {
            // }

            throw new AssertionFailedError(logMessage);
          }
          System.gc();
          System.gc();
          System.gc();
          String s = "Another epic fail (remaining attempts: " + attempts + "): " + logMessage;
          TeamCityLogger.warning(s, null);
          System.err.println(s);
          // if (attempts == 1) {
          //  try {
          //
          // Class.forName("com.intellij.util.ProfilingUtil").getMethod("startCPUProfiling").invoke(null);
          //  }
          //  catch (Exception e) {
          //  }
          // }
          continue;
        }
        break;
      }
    }

    private static String formatTime(long millis) {
      String hint = "";
      DecimalFormat format = new DecimalFormat("#.0", DecimalFormatSymbols.getInstance(Locale.US));
      if (millis >= 60 * 1000) hint = format.format(millis / 60 / 1000.f) + "m";
      if (millis >= 1000)
        hint += (hint.isEmpty() ? "" : " ") + format.format(millis / 1000.f) + "s";
      String result = millis + "ms";
      if (!hint.isEmpty()) {
        result = result + " (" + hint + ")";
      }
      return result;
    }

    private static int adjust(int expectedOnMyMachine, long thisTiming, long ethanolTiming) {
      // most of our algorithms are quadratic. sad but true.
      double speed = 1.0 * thisTiming / ethanolTiming;
      double delta = speed < 1 ? 0.9 + Math.pow(speed - 0.7, 2) : 0.45 + Math.pow(speed - 0.25, 2);
      expectedOnMyMachine *= delta;
      return expectedOnMyMachine;
    }
  }

  public static void assertTiming(
      String message, long expected, @NotNull Runnable actionToMeasure) {
    assertTiming(message, expected, 4, actionToMeasure);
  }

  public static long measure(@NotNull Runnable actionToMeasure) {
    long start = System.currentTimeMillis();
    actionToMeasure.run();
    long finish = System.currentTimeMillis();
    return finish - start;
  }

  public static void assertTiming(
      String message, long expected, int attempts, @NotNull Runnable actionToMeasure) {
    while (true) {
      attempts--;
      long duration = measure(actionToMeasure);
      try {
        assertTiming(message, expected, duration);
        break;
      } catch (AssertionFailedError e) {
        if (attempts == 0) throw e;
        System.gc();
        System.gc();
        System.gc();
        String s = "Another epic fail (remaining attempts: " + attempts + "): " + e.getMessage();
        TeamCityLogger.warning(s, null);
        System.err.println(s);
      }
    }
  }

  private static HashMap<String, VirtualFile> buildNameToFileMap(
      VirtualFile[] files, @Nullable VirtualFileFilter filter) {
    HashMap<String, VirtualFile> map = new HashMap<String, VirtualFile>();
    for (VirtualFile file : files) {
      if (filter != null && !filter.accept(file)) continue;
      map.put(file.getName(), file);
    }
    return map;
  }

  public static void assertDirectoriesEqual(VirtualFile dirAfter, VirtualFile dirBefore)
      throws IOException {
    assertDirectoriesEqual(dirAfter, dirBefore, null);
  }

  @SuppressWarnings("UnsafeVfsRecursion")
  public static void assertDirectoriesEqual(
      VirtualFile dirAfter, VirtualFile dirBefore, @Nullable VirtualFileFilter fileFilter)
      throws IOException {
    FileDocumentManager.getInstance().saveAllDocuments();

    VirtualFile[] childrenAfter = dirAfter.getChildren();

    if (dirAfter.isInLocalFileSystem()
        && dirAfter.getFileSystem() != TempFileSystem.getInstance()) {
      File[] ioAfter = new File(dirAfter.getPath()).listFiles();
      shallowCompare(childrenAfter, ioAfter);
    }

    VirtualFile[] childrenBefore = dirBefore.getChildren();
    if (dirBefore.isInLocalFileSystem()
        && dirBefore.getFileSystem() != TempFileSystem.getInstance()) {
      File[] ioBefore = new File(dirBefore.getPath()).listFiles();
      shallowCompare(childrenBefore, ioBefore);
    }

    HashMap<String, VirtualFile> mapAfter = buildNameToFileMap(childrenAfter, fileFilter);
    HashMap<String, VirtualFile> mapBefore = buildNameToFileMap(childrenBefore, fileFilter);

    Set<String> keySetAfter = mapAfter.keySet();
    Set<String> keySetBefore = mapBefore.keySet();
    assertEquals(dirAfter.getPath(), keySetAfter, keySetBefore);

    for (String name : keySetAfter) {
      VirtualFile fileAfter = mapAfter.get(name);
      VirtualFile fileBefore = mapBefore.get(name);
      if (fileAfter.isDirectory()) {
        assertDirectoriesEqual(fileAfter, fileBefore, fileFilter);
      } else {
        assertFilesEqual(fileAfter, fileBefore);
      }
    }
  }

  private static void shallowCompare(VirtualFile[] vfs, @Nullable File[] io) {
    List<String> vfsPaths = new ArrayList<String>();
    for (VirtualFile file : vfs) {
      vfsPaths.add(file.getPath());
    }

    List<String> ioPaths = new ArrayList<String>();
    if (io != null) {
      for (File file : io) {
        ioPaths.add(file.getPath().replace(File.separatorChar, '/'));
      }
    }

    assertEquals(sortAndJoin(vfsPaths), sortAndJoin(ioPaths));
  }

  private static String sortAndJoin(List<String> strings) {
    Collections.sort(strings);
    StringBuilder buf = new StringBuilder();
    for (String string : strings) {
      buf.append(string);
      buf.append('\n');
    }
    return buf.toString();
  }

  public static void assertFilesEqual(VirtualFile fileAfter, VirtualFile fileBefore)
      throws IOException {
    try {
      assertJarFilesEqual(
          VfsUtilCore.virtualToIoFile(fileAfter), VfsUtilCore.virtualToIoFile(fileBefore));
    } catch (IOException e) {
      FileDocumentManager manager = FileDocumentManager.getInstance();

      Document docBefore = manager.getDocument(fileBefore);
      boolean canLoadBeforeText =
          !fileBefore.getFileType().isBinary() || fileBefore.getFileType() == FileTypes.UNKNOWN;
      String textB =
          docBefore != null
              ? docBefore.getText()
              : !canLoadBeforeText
                  ? null
                  : LoadTextUtil.getTextByBinaryPresentation(
                          fileBefore.contentsToByteArray(false), fileBefore)
                      .toString();

      Document docAfter = manager.getDocument(fileAfter);
      boolean canLoadAfterText =
          !fileBefore.getFileType().isBinary() || fileBefore.getFileType() == FileTypes.UNKNOWN;
      String textA =
          docAfter != null
              ? docAfter.getText()
              : !canLoadAfterText
                  ? null
                  : LoadTextUtil.getTextByBinaryPresentation(
                          fileAfter.contentsToByteArray(false), fileAfter)
                      .toString();

      if (textA != null && textB != null) {
        assertEquals(fileAfter.getPath(), textA, textB);
      } else {
        Assert.assertArrayEquals(
            fileAfter.getPath(), fileAfter.contentsToByteArray(), fileBefore.contentsToByteArray());
      }
    }
  }

  public static void assertJarFilesEqual(File file1, File file2) throws IOException {
    final File tempDirectory1;
    final File tempDirectory2;

    final JarFile jarFile1 = new JarFile(file1);
    try {
      final JarFile jarFile2 = new JarFile(file2);
      try {
        tempDirectory1 = PlatformTestCase.createTempDir("tmp1");
        tempDirectory2 = PlatformTestCase.createTempDir("tmp2");
        ZipUtil.extract(jarFile1, tempDirectory1, null);
        ZipUtil.extract(jarFile2, tempDirectory2, null);
      } finally {
        jarFile2.close();
      }
    } finally {
      jarFile1.close();
    }

    final VirtualFile dirAfter =
        LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tempDirectory1);
    assertNotNull(tempDirectory1.toString(), dirAfter);
    final VirtualFile dirBefore =
        LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tempDirectory2);
    assertNotNull(tempDirectory2.toString(), dirBefore);
    ApplicationManager.getApplication()
        .runWriteAction(
            new Runnable() {
              @Override
              public void run() {
                dirAfter.refresh(false, true);
                dirBefore.refresh(false, true);
              }
            });
    assertDirectoriesEqual(dirAfter, dirBefore);
  }

  public static void assertElementsEqual(final Element expected, final Element actual)
      throws IOException {
    if (!JDOMUtil.areElementsEqual(expected, actual)) {
      junit.framework.Assert.assertEquals(printElement(expected), printElement(actual));
    }
  }

  public static String printElement(final Element element) throws IOException {
    final StringWriter writer = new StringWriter();
    JDOMUtil.writeElement(element, writer, "\n");
    return writer.getBuffer().toString();
  }

  public static String getCommunityPath() {
    final String homePath = PathManager.getHomePath();
    if (new File(homePath, "community").exists()) {
      return homePath + File.separatorChar + "community";
    }
    return homePath;
  }

  public static Comparator<AbstractTreeNode> createComparator(final Queryable.PrintInfo printInfo) {
    return new Comparator<AbstractTreeNode>() {
      @Override
      public int compare(final AbstractTreeNode o1, final AbstractTreeNode o2) {
        String displayText1 = o1.toTestString(printInfo);
        String displayText2 = o2.toTestString(printInfo);
        return Comparing.compare(displayText1, displayText2);
      }
    };
  }

  @NotNull
  public static <T> T notNull(@Nullable T t) {
    assertNotNull(t);
    return t;
  }

  @NotNull
  public static String loadFileText(@NotNull String fileName) throws IOException {
    return StringUtil.convertLineSeparators(FileUtil.loadFile(new File(fileName)));
  }

  public static void tryGcSoftlyReachableObjects() {
    List<Object> list = ContainerUtil.newArrayList();
    for (int i = 0; i < 100; i++) {
      list.add(new SoftReference<byte[]>(new byte[(int) Runtime.getRuntime().freeMemory() / 2]));
    }
  }

  public static void withEncoding(@NotNull String encoding, @NotNull final Runnable r) {
    withEncoding(
        encoding,
        new ThrowableRunnable() {
          @Override
          public void run() throws Throwable {
            r.run();
          }
        });
  }

  public static void withEncoding(@NotNull String encoding, @NotNull ThrowableRunnable r) {
    Charset oldCharset = Charset.defaultCharset();
    try {
      try {
        patchSystemFileEncoding(encoding);
        r.run();
      } finally {
        patchSystemFileEncoding(oldCharset.name());
      }
    } catch (Throwable t) {
      throw new RuntimeException(t);
    }
  }

  private static void patchSystemFileEncoding(String encoding)
      throws NoSuchFieldException, IllegalAccessException {
    Field charset = Charset.class.getDeclaredField("defaultCharset");
    charset.setAccessible(true);
    charset.set(Charset.class, null);
    System.setProperty("file.encoding", encoding);
  }
}