private static void doTest(
      @NonNls final String text, final Parser parser, @NonNls final String expected) {
    final PsiBuilder builder = createBuilder(text);
    final PsiBuilder.Marker rootMarker = builder.mark();
    parser.parse(builder);
    rootMarker.done(ROOT);

    // check light tree composition
    final FlyweightCapableTreeStructure<LighterASTNode> lightTree = builder.getLightTree();
    assertEquals(expected, DebugUtil.lightTreeToString(lightTree, false));
    // verify that light tree can be taken multiple times
    final FlyweightCapableTreeStructure<LighterASTNode> lightTree2 = builder.getLightTree();
    assertEquals(expected, DebugUtil.lightTreeToString(lightTree2, false));

    // check heavy tree composition
    final ASTNode root = builder.getTreeBuilt();
    assertEquals(expected, DebugUtil.nodeTreeToString(root, false));

    // check heavy vs. light tree merging
    final PsiBuilder builder2 = createBuilder(text);
    final PsiBuilder.Marker rootMarker2 = builder2.mark();
    parser.parse(builder2);
    rootMarker2.done(ROOT);
    DiffTree.diff(
        new ASTStructure(root),
        builder2.getLightTree(),
        new ShallowNodeComparator<ASTNode, LighterASTNode>() {
          @Override
          public ThreeState deepEqual(ASTNode oldNode, LighterASTNode newNode) {
            return ThreeState.UNSURE;
          }

          @Override
          public boolean typesEqual(ASTNode oldNode, LighterASTNode newNode) {
            return true;
          }

          @Override
          public boolean hashCodesEqual(ASTNode oldNode, LighterASTNode newNode) {
            return true;
          }
        },
        new DiffTreeChangeBuilder<ASTNode, LighterASTNode>() {
          @Override
          public void nodeReplaced(@NotNull ASTNode oldChild, @NotNull LighterASTNode newChild) {
            fail("replaced(" + oldChild + "," + newChild.getTokenType() + ")");
          }

          @Override
          public void nodeDeleted(@NotNull ASTNode oldParent, @NotNull ASTNode oldNode) {
            fail("deleted(" + oldParent + "," + oldNode + ")");
          }

          @Override
          public void nodeInserted(
              @NotNull ASTNode oldParent, @NotNull LighterASTNode newNode, int pos) {
            fail("inserted(" + oldParent + "," + newNode.getTokenType() + ")");
          }
        });
  }
 public static <T> void diffTrees(
     @NotNull final ASTNode oldRoot,
     @NotNull final DiffTreeChangeBuilder<ASTNode, T> builder,
     @NotNull final ShallowNodeComparator<ASTNode, T> comparator,
     @NotNull final FlyweightCapableTreeStructure<T> newTreeStructure,
     @NotNull ProgressIndicator indicator) {
   TreeUtil.ensureParsedRecursivelyCheckingProgress(oldRoot, indicator);
   DiffTree.diff(
       createInterruptibleASTStructure(oldRoot, indicator), newTreeStructure, comparator, builder);
 }