84c338e6cc5f78ed344963fae5b0359c1885673b
[idea/community.git] / platform / platform-impl / testSrc / com / intellij / ide / util / treeView / TreeUiTest.java
1 package com.intellij.ide.util.treeView;
2
3 import com.intellij.openapi.diagnostic.Log;
4 import com.intellij.openapi.progress.ProcessCanceledException;
5 import com.intellij.openapi.progress.ProgressIndicator;
6 import com.intellij.openapi.progress.Progressive;
7 import com.intellij.openapi.util.*;
8 import com.intellij.util.Time;
9 import com.intellij.util.WaitFor;
10 import junit.framework.TestSuite;
11 import org.jetbrains.annotations.NotNull;
12
13 import javax.swing.*;
14 import javax.swing.tree.DefaultMutableTreeNode;
15 import javax.swing.tree.TreePath;
16 import java.lang.reflect.InvocationTargetException;
17
18 public class TreeUiTest extends AbstractTreeBuilderTest {
19
20   public TreeUiTest(boolean passthrougth) {
21     super(passthrougth);
22   }
23
24   public TreeUiTest(boolean yieldingUiBuild, boolean bgStructureBuilding) {
25     super(yieldingUiBuild, bgStructureBuilding);
26   }
27
28   public void testEmptyInvisibleRoot() throws Exception {
29     myTree.setRootVisible(false);
30     showTree();
31     assertTree("+/\n");
32
33     updateFromRoot();
34     assertTree("+/\n");
35
36
37     buildNode("/", false);
38     assertTree("+/\n");
39
40     myTree.setRootVisible(true);
41     buildNode("/", false);
42     assertTree("/\n");
43   }
44
45   public void testVisibleRoot() throws Exception {
46     myTree.setRootVisible(true);
47     buildStructure(myRoot);
48     assertTree("+/\n");
49
50     updateFromRoot();
51     assertTree("+/\n");
52   }
53
54   public void testThrowingProcessCancelledInterruptsUpdate() throws Exception {
55     assertInterruption(Interruption.throwProcessCancelled);
56   }
57
58   public void testCancelUpdate() throws Exception {
59     assertInterruption(Interruption.invokeCancel);
60   }
61
62
63   public void testBatchUpdate() throws Exception {
64     buildStructure(myRoot);
65
66     myElementUpdate.clear();
67
68     final NodeElement[] toExpand = new NodeElement[] {
69       new NodeElement("com"),
70       new NodeElement("jetbrains"),
71       new NodeElement("org"),
72       new NodeElement("xunit")
73     };
74
75     final ActionCallback done = new ActionCallback();
76     final Ref<ProgressIndicator> indicatorRef = new Ref<ProgressIndicator>();
77     invokeLaterIfNeeded(new Runnable() {
78       public void run() {
79         getBuilder().batch(new Progressive() {
80           public void run(@NotNull ProgressIndicator indicator) {
81             indicatorRef.set(indicator);
82             expandNext(toExpand, 0, indicator, done);
83           }
84         }).notify(done);
85       }
86     });
87
88
89     waitBuilderToCome(new Condition() {
90       public boolean value(Object o) {
91         return done.isProcessed();
92       }
93     });
94
95     assertTrue(done.isDone());
96
97     assertTree("-/\n" +
98                " -com\n" +
99                "  +intellij\n" +
100                " -jetbrains\n" +
101                "  +fabrique\n" +
102                " -org\n" +
103                "  +eclipse\n" +
104                " -xunit\n" +
105                "  runner\n");
106
107     assertFalse(indicatorRef.get().isCanceled());
108   }
109
110
111   public void testCancelUpdateBatch() throws Exception {
112     buildStructure(myRoot);
113
114     myAlwaysShowPlus.add(new NodeElement("com"));
115     myAlwaysShowPlus.add(new NodeElement("jetbrains"));
116     myAlwaysShowPlus.add(new NodeElement("org"));
117     myAlwaysShowPlus.add(new NodeElement("xunit"));
118
119     final Ref<Boolean> cancelled = new Ref<Boolean>(false);
120     myElementUpdateHook = new ElementUpdateHook() {
121       public void onElementAction(String action, Object element) {
122         NodeElement stopElement = new NodeElement("com");
123
124         if (cancelled.get()) {
125           myCancelRequest = new AssertionError("Not supposed to update after element=" + stopElement);
126           return;
127         }
128
129         if (element.equals(stopElement) && action.equals("getChildren")) {
130           cancelled.set(true);
131           getBuilder().cancelUpdate();
132         }
133       }
134     };
135
136     final NodeElement[] toExpand = new NodeElement[] {
137       new NodeElement("com"),
138       new NodeElement("jetbrains"),
139       new NodeElement("org"),
140       new NodeElement("xunit")
141     };
142
143     final ActionCallback done = new ActionCallback();
144     final Ref<ProgressIndicator> indicatorRef = new Ref<ProgressIndicator>();
145
146     invokeLaterIfNeeded(new Runnable() {
147       public void run() {
148         getBuilder().batch(new Progressive() {
149           public void run(@NotNull ProgressIndicator indicator) {
150             indicatorRef.set(indicator);
151             expandNext(toExpand, 0, indicator, done);
152           }
153         }).notify(done);
154       }
155     });
156
157
158     waitBuilderToCome(new Condition() {
159       public boolean value(Object o) {
160         return done.isProcessed() || myCancelRequest != null;
161       }
162     });
163
164
165     assertNull(myCancelRequest);
166     assertTrue(done.isRejected());
167     assertTrue(indicatorRef.get().isCanceled());
168
169     assertFalse(getBuilder().getUi().isCancelProcessed());
170   }
171   
172
173   public void testExpandAll() throws Exception {
174     buildStructure(myRoot);
175     assertTree("+/\n");
176
177     final Ref<Boolean> done = new Ref<Boolean>();
178     doAndWaitForBuilder(new Runnable() {
179       public void run() {
180         getBuilder().expandAll(new Runnable() {
181           public void run() {
182             done.set(true);
183           }
184         });
185       }
186     });
187
188     assertTree("-/\n"
189                + " -com\n"
190                + "  -intellij\n"
191                + "   openapi\n"
192                + " -jetbrains\n"
193                + "  -fabrique\n"
194                + "   ide\n"
195                + " -org\n"
196                + "  -eclipse\n"
197                + "   rcp\n"
198                + " -xunit\n"
199                + "  runner\n");
200   }
201
202   public void testInvisibleRoot() throws Exception {
203     myTree.setRootVisible(false);
204     buildStructure(myRoot);
205     assertTree("-/\n"
206                + " +com\n"
207                + " +jetbrains\n"
208                + " +org\n"
209                + " +xunit\n");
210
211     collapsePath(new TreePath(myTreeModel.getRoot()));
212     assertTree("+/\n");
213
214     updateFromRoot();
215     assertTree("-/\n"
216                + " +com\n"
217                + " +jetbrains\n"
218                + " +org\n"
219                + " +xunit\n");
220
221     buildNode("com", true);
222     assertTree("-/\n"
223                + " +[com]\n"
224                + " +jetbrains\n"
225                + " +org\n"
226                + " +xunit\n");
227
228     myRoot.removeAll();
229     updateFromRoot();
230
231     assertTree("+/\n");
232
233   }
234
235   public void testAutoExpand() throws Exception {
236     buildStructure(myRoot);
237     assertTree("+/\n");
238
239     myAutoExpand.add(new NodeElement("/"));
240     buildStructure(myRoot);
241
242     assertTree("-/\n"
243                + " +com\n"
244                + " +jetbrains\n"
245                + " +org\n"
246                + " +xunit\n");
247
248
249     myAutoExpand.add(new NodeElement("jetbrains"));
250     updateFromRoot();
251
252     assertTree("-/\n"
253                + " +com\n"
254                + " -jetbrains\n"
255                + "  +fabrique\n"
256                + " +org\n"
257                + " +xunit\n");
258
259     collapsePath(getPath("jetbrains"));
260     assertTree("-/\n"
261                + " +com\n"
262                + " +jetbrains\n"
263                + " +org\n"
264                + " +xunit\n");
265
266     updateFrom(new NodeElement("org"));
267     assertTree("-/\n"
268                + " +com\n"
269                + " +jetbrains\n"
270                + " +org\n"
271                + " +xunit\n");
272
273     updateFrom(new NodeElement("jetbrains"));
274     assertTree("-/\n"
275                + " +com\n"
276                + " -jetbrains\n"
277                + "  +fabrique\n"
278                + " +org\n"
279                + " +xunit\n");
280   }
281
282   public void testAutoExpandDeep() throws Exception {
283     myTree.setRootVisible(false);
284     //myAutoExpand.add(new NodeElement("jetbrains"));
285     myAutoExpand.add(new NodeElement("fabrique"));
286
287
288     buildStructure(myRoot);
289     //assertTree("+/\n");
290
291     expand(getPath("/"));
292     expand(getPath("jetbrains"));
293     assertTree("-/\n"
294                + " +com\n"
295                + " -jetbrains\n"
296                + "  -fabrique\n"
297                + "   ide\n"
298                + " +org\n"
299                + " +xunit\n");
300
301     collapsePath(getPath("/"));
302     assertTree("+/\n");
303
304     expand(getPath("/"));
305     expand(getPath("jetbrains"));
306
307     assertTree("-/\n"
308                + " +com\n"
309                + " -jetbrains\n"
310                + "  -fabrique\n"
311                + "   ide\n"
312                + " +org\n"
313                + " +xunit\n");
314
315     collapsePath(getPath("jetbrains"));
316     assertTree("-/\n"
317                + " +com\n"
318                + " +jetbrains\n"
319                + " +org\n"
320                + " +xunit\n");
321
322     expand(getPath("jetbrains"));
323     assertTree("-/\n"
324                + " +com\n"
325                + " -jetbrains\n"
326                + "  -fabrique\n"
327                + "   ide\n"
328                + " +org\n"
329                + " +xunit\n");
330
331   }
332
333
334   public void testAutoExpandInNonVisibleNode() throws Exception {
335     myAutoExpand.add(new NodeElement("fabrique"));
336     buildStructure(myRoot);
337
338     expand(getPath("/"));
339     assertTree("-/\n"
340                + " +com\n"
341                + " +jetbrains\n"
342                + " +org\n"
343                + " +xunit\n");
344   }
345
346   public void testSmartExpand() throws Exception {
347     mySmartExpand = true;
348     buildStructure(myRoot);
349     assertTree("+/\n");
350
351     expand(getPath("/"));
352     assertTree("-/\n"
353                + " +com\n"
354                + " +jetbrains\n"
355                + " +org\n"
356                + " +xunit\n");
357
358     expand(getPath("jetbrains"));
359     assertTree("-/\n"
360                + " +com\n"
361                + " -jetbrains\n"
362                + "  -fabrique\n"
363                + "   ide\n"
364                + " +org\n"
365                + " +xunit\n");
366
367     collapsePath(getPath("jetbrains"));
368     assertTree("-/\n"
369                + " +com\n"
370                + " +jetbrains\n"
371                + " +org\n"
372                + " +xunit\n");
373
374     updateFromRoot();
375     assertTree("-/\n"
376                + " +com\n"
377                + " +jetbrains\n"
378                + " +org\n"
379                + " +xunit\n");
380
381     mySmartExpand = false;
382     collapsePath(getPath("jetbrains"));
383     assertTree("-/\n"
384                + " +com\n"
385                + " +jetbrains\n"
386                + " +org\n"
387                + " +xunit\n");
388
389     expand(getPath("jetbrains"));
390     assertTree("-/\n"
391                + " +com\n"
392                + " -jetbrains\n"
393                + "  +fabrique\n"
394                + " +org\n"
395                + " +xunit\n");
396   }
397
398
399   public void testClear() throws Exception {
400     getBuilder().getUi().setClearOnHideDelay(10 * Time.SECOND);
401
402     buildStructure(myRoot);
403
404     assertTree("+/\n");
405
406     final DefaultMutableTreeNode openApiNode = findNode("openapi", false);
407     final DefaultMutableTreeNode ideNode = findNode("ide", false);
408     final DefaultMutableTreeNode runnerNode = findNode("runner", false);
409     final DefaultMutableTreeNode rcpNode = findNode("rcp", false);
410
411     assertNull(openApiNode);
412     assertNull(ideNode);
413     assertNull(runnerNode);
414     assertNull(rcpNode);
415
416     buildNode(myOpenApi, true);
417     buildNode(myIde, true);
418     buildNode(myRunner, false);
419
420     hideTree();
421
422     assertNull(findNode("openapi", true));
423     assertNull(findNode("ide", true));
424     assertNull(findNode("runner", false));
425     assertNull(findNode("rcp", false));
426
427
428     showTree();
429
430     assertTree("-/\n" +
431                " -com\n" +
432                "  -intellij\n" +
433                "   [openapi]\n" +
434                " -jetbrains\n" +
435                "  -fabrique\n" +
436                "   [ide]\n" +
437                " +org\n" +
438                " -xunit\n" +
439                "  runner\n");
440
441     getMyBuilder().myWasCleanedUp = false;
442     hideTree();
443     showTree();
444
445     assertTree("-/\n" +
446                " -com\n" +
447                "  -intellij\n" +
448                "   [openapi]\n" +
449                " -jetbrains\n" +
450                "  -fabrique\n" +
451                "   [ide]\n" +
452                " +org\n" +
453                " -xunit\n" +
454                "  runner\n");
455
456
457     buildNode(myFabrique.myElement, true, false);
458     assertTree("-/\n" +
459                " -com\n" +
460                "  -intellij\n" +
461                "   openapi\n" +
462                " -jetbrains\n" +
463                "  -[fabrique]\n" +
464                "   ide\n" +
465                " +org\n" +
466                " -xunit\n" +
467                "  runner\n");
468   }
469
470   public void testUpdateRestoresState() throws Exception {
471     buildStructure(myRoot);
472
473     buildNode(myOpenApi, true);
474     buildNode(myIde, true);
475     buildNode(myRunner, false);
476
477     waitBuilderToCome();
478
479     assertTree("-/\n" +
480                " -com\n" +
481                "  -intellij\n" +
482                "   [openapi]\n" +
483                " -jetbrains\n" +
484                "  -fabrique\n" +
485                "   [ide]\n" +
486                " +org\n" +
487                " -xunit\n" +
488                "  runner\n");
489
490     myRoot.removeAll();
491     myStructure.clear();
492
493     final AbstractTreeBuilderTest.Node newRoot = myRoot.addChild("newRoot");
494
495     buildStructure(newRoot);
496
497     updateFromRoot();
498     assertTree("-/\n" +
499                " -newRoot\n" +
500                "  -com\n" +
501                "   -intellij\n" +
502                "    [openapi]\n" +
503                "  -jetbrains\n" +
504                "   -fabrique\n" +
505                "    [ide]\n" +
506                "  +org\n" +
507                "  -xunit\n" +
508                "   runner\n");
509   }
510
511
512   public void testSelect() throws Exception {
513     buildStructure(myRoot);
514     assertTree(
515       "+/\n");
516
517
518     buildNode(myOpenApi, true);
519     assertTree(
520       "-/\n" +
521       " -com\n" +
522       "  -intellij\n" +
523       "   [openapi]\n" +
524       " +jetbrains\n" +
525       " +org\n" +
526       " +xunit\n");
527
528     buildNode("fabrique", true);
529
530     assertTree(
531       "-/\n" +
532       " -com\n" +
533       "  -intellij\n" +
534       "   [openapi]\n" +
535       " -jetbrains\n" +
536       "  +[fabrique]\n" +
537       " +org\n" +
538       " +xunit\n");
539   }
540
541   public void testCallbackOnceOnSelect() throws Exception {
542     buildStructure(myRoot);
543
544     assertCallbackOnce(new TreeAction() {
545       public void run(Runnable onDone) {
546         getMyBuilder().select(new Object[] {new NodeElement("intellij"), new NodeElement("fabrique")}, onDone);
547       }
548     });
549
550     assertTree(
551       "-/\n" +
552       " -com\n" +
553       "  +[intellij]\n" +
554       " -jetbrains\n" +
555       "  +[fabrique]\n" +
556       " +org\n" +
557       " +xunit\n");
558
559   }
560
561   public void testCallbackOnceOnExpand() throws Exception {
562     buildStructure(myRoot);
563
564     assertCallbackOnce(new TreeAction() {
565       public void run(Runnable onDone) {
566         getMyBuilder().expand(new Object[] {new NodeElement("intellij"), new NodeElement("fabrique")}, onDone);
567       }
568     });
569
570     assertTree(
571       "-/\n" +
572       " -com\n" +
573       "  -intellij\n" +
574       "   openapi\n" +
575       " -jetbrains\n" +
576       "  -fabrique\n" +
577       "   ide\n" +
578       " +org\n" +
579       " +xunit\n");
580
581   }
582
583
584   public void testNoInfiniteAutoExpand() throws Exception {
585     mySmartExpand = false;
586
587     assertNoInfiniteAutoExpand(new Runnable() {
588       public void run() {
589         myAutoExpand.add(new NodeElement("level2"));
590         myAutoExpand.add(new NodeElement("level3"));
591         myAutoExpand.add(new NodeElement("level4"));
592         myAutoExpand.add(new NodeElement("level5"));
593         myAutoExpand.add(new NodeElement("level6"));
594         myAutoExpand.add(new NodeElement("level7"));
595         myAutoExpand.add(new NodeElement("level8"));
596         myAutoExpand.add(new NodeElement("level9"));
597         myAutoExpand.add(new NodeElement("level10"));
598         myAutoExpand.add(new NodeElement("level11"));
599         myAutoExpand.add(new NodeElement("level12"));
600         myAutoExpand.add(new NodeElement("level13"));
601         myAutoExpand.add(new NodeElement("level14"));
602         myAutoExpand.add(new NodeElement("level15"));
603       }
604     });
605   }
606
607   public void testNoInfiniteSmartExpand() throws Exception {
608     mySmartExpand = false;
609
610     assertNoInfiniteAutoExpand(new Runnable() {
611       public void run() {
612         mySmartExpand = true;
613       }
614     });
615   }
616
617   private void assertNoInfiniteAutoExpand(final Runnable enableExpand) throws Exception {
618     class Level extends Node {
619
620       int myLevel;
621
622       Level(Node parent, int level) {
623         super(parent, "level" + level);
624         myLevel = level;
625       }
626
627       @Override
628       public Object[] getChildElements() {
629         if (super.getChildElements().length == 0) {
630           addChild(new Level(this, myLevel + 1));
631         }
632
633         return super.getChildElements();
634       }
635     }
636
637     myRoot.addChild(new Level(myRoot, 0));
638
639     activate();
640     buildNode("level0", false);
641
642     assertTree("-/\n" +
643                " -level0\n" +
644                "  +level1\n");
645
646     enableExpand.run();
647
648     expand(getPath("level1"));
649
650     assertTree("-/\n" +
651                " -level0\n" +
652                "  -level1\n" +
653                "   -level2\n" +
654                "    -level3\n" +
655                "     -level4\n" +
656                "      -level5\n" +
657                "       +level6\n");
658
659     expand(getPath("level6"));
660     assertTree("-/\n" +
661                " -level0\n" +
662                "  -level1\n" +
663                "   -level2\n" +
664                "    -level3\n" +
665                "     -level4\n" +
666                "      -level5\n" +
667                "       -level6\n" +
668                "        -level7\n" +
669                "         -level8\n" +
670                "          -level9\n" +
671                "           -level10\n" + 
672                "            +level11\n");
673   }
674
675   private void assertCallbackOnce(final TreeAction action) {
676     final int[] notifyCount = new int[1];
677     final boolean[] done = new boolean[1];
678     invokeLaterIfNeeded(new Runnable() {
679       public void run() {
680         action.run(new Runnable() {
681           public void run() {
682             notifyCount[0]++;
683             done[0] = true;
684           }
685         });
686       }
687     });
688
689     new WaitFor(60000) {
690       @Override
691       protected boolean condition() {
692         return done[0] && getMyBuilder().getUi().isReady();
693       }
694     };
695
696     assertTrue(done[0]);
697     assertEquals(1, notifyCount[0]);
698   }
699
700   public void testSelectMultiple() throws Exception {
701     buildStructure(myRoot);
702     assertTree(
703       "+/\n");
704
705     select(new Object[] {new NodeElement("openapi"), new NodeElement("fabrique")}, false);
706     assertTree(
707       "-/\n" +
708       " -com\n" +
709       "  -intellij\n" +
710       "   [openapi]\n" +
711       " -jetbrains\n" +
712       "  +[fabrique]\n" +
713       " +org\n" +
714       " +xunit\n");
715   }
716
717   public void testUnsuccessfulSelect() throws Exception {
718     buildStructure(myRoot);
719     select(new Object[] {new NodeElement("openapi"), new NodeElement("fabrique")}, false);
720
721     assertTree(
722       "-/\n" +
723       " -com\n" +
724       "  -intellij\n" +
725       "   [openapi]\n" +
726       " -jetbrains\n" +
727       "  +[fabrique]\n" +
728       " +org\n" +
729       " +xunit\n");
730
731     select(new Object[] {new NodeElement("whatever1"), new NodeElement("whatever2")}, false);
732
733     assertTree(
734       "-/\n" +
735       " -com\n" +
736       "  -intellij\n" +
737       "   [openapi]\n" +
738       " -jetbrains\n" +
739       "  +[fabrique]\n" +
740       " +org\n" +
741       " +xunit\n");
742   }
743
744
745   public void testSelectionWhenChildMoved() throws Exception {
746     buildStructure(myRoot);
747     assertTree("+/\n");
748
749     final Node refactoring = myCom.getChildNode("intellij").addChild("refactoring");
750
751     buildNode("refactoring", true);
752
753     assertTree(
754       "-/\n" +
755       " -com\n" +
756       "  -intellij\n" +
757       "   openapi\n" +
758       "   [refactoring]\n" +
759       " +jetbrains\n" +
760       " +org\n" +
761       " +xunit\n");
762
763     refactoring.delete();
764     myCom.getChildNode("intellij").getChildNode("openapi").addChild("refactoring");
765
766     updateFromRoot();
767
768     assertTree(
769       "-/\n" +
770       " -com\n" +
771       "  -intellij\n" +
772       "   -openapi\n" +
773       "    [refactoring]\n" +
774       " +jetbrains\n" +
775       " +org\n" +
776       " +xunit\n");
777   }
778
779
780   public void testSelectionGoesToParentWhenOnlyChildRemove() throws Exception {
781     buildStructure(myRoot);
782     buildNode("openapi", true);
783
784     assertTree(
785       "-/\n" +
786       " -com\n" +
787       "  -intellij\n" +
788       "   [openapi]\n" +
789       " +jetbrains\n" +
790       " +org\n" +
791       " +xunit\n");
792
793     myCom.getChildNode("intellij").getChildNode("openapi").delete();
794
795     updateFromRoot();
796
797     assertTree(
798       "-/\n" +
799       " -com\n" +
800       "  [intellij]\n" +
801       " +jetbrains\n" +
802       " +org\n" +
803       " +xunit\n");
804   }
805
806   public void testCollapsedPathOnExpandedCallback() throws Exception {
807     Node com = myRoot.addChild("com");
808
809     activate();
810     assertTree("+/\n");
811
812     expand(getPath("/"));
813     assertTree("-/\n" +
814                " com\n");
815
816     com.addChild("intellij");
817
818     collapsePath(getPath("/"));
819
820     final Ref<Boolean> done = new Ref<Boolean>();
821     invokeLaterIfNeeded(new Runnable() {
822       @Override
823       public void run() {
824         getBuilder().expand(new NodeElement("com"), new Runnable() {
825           @Override
826           public void run() {
827             getBuilder().getTree().collapsePath(getPath("com"));
828             done.set(Boolean.TRUE);
829           }
830         });
831       }
832     });
833
834     waitBuilderToCome(new Condition<Object>() {
835       @Override
836       public boolean value(Object o) {
837         return (done.get() != null) && done.get().booleanValue();
838       }
839     });
840
841     assertTree("-/\n" +
842                " +com\n");
843   }
844
845   public void testSelectionGoesToParentWhenOnlyChildMoved() throws Exception {
846     buildStructure(myRoot);
847     buildNode("openapi", true);
848
849     assertTree(
850       "-/\n" +
851       " -com\n" +
852       "  -intellij\n" +
853       "   [openapi]\n" +
854       " +jetbrains\n" +
855       " +org\n" +
856       " +xunit\n");
857
858     myCom.getChildNode("intellij").getChildNode("openapi").delete();
859     myRoot.getChildNode("xunit").addChild("openapi");
860
861     updateFromRoot();
862
863     assertTree(
864       "-/\n" +
865       " -com\n" +
866       "  intellij\n" +
867       " +jetbrains\n" +
868       " +org\n" +
869       " -xunit\n" +
870       "  [openapi]\n" +
871       "  runner\n");
872   }
873
874   public void testSelectionGoesToParentWhenOnlyChildMoved2() throws Exception {
875     buildStructure(myRoot);
876     buildNode("openapi", true);
877
878     assertTree(
879       "-/\n" +
880       " -com\n" +
881       "  -intellij\n" +
882       "   [openapi]\n" +
883       " +jetbrains\n" +
884       " +org\n" +
885       " +xunit\n");
886
887     myCom.getChildNode("intellij").getChildNode("openapi").delete();
888     myRoot.getChildNode("xunit").addChild("openapi");
889
890     getBuilder().addSubtreeToUpdateByElement(new NodeElement("intellij"));
891     getBuilder().addSubtreeToUpdateByElement(new NodeElement("xunit"));
892
893
894     doAndWaitForBuilder(new Runnable() {
895       public void run() {
896         getBuilder().getUpdater().performUpdate();
897       }
898     });
899
900     assertTree(
901       "-/\n" +
902       " -com\n" +
903       "  intellij\n" +
904       " +jetbrains\n" +
905       " +org\n" +
906       " -xunit\n" +
907       "  [openapi]\n" +
908       "  runner\n");
909   }
910
911   public void testSelectionGoesToParentWhenChildrenFold() throws Exception {
912     buildStructure(myRoot);
913     buildNode("openapi", true);
914
915     assertTree(
916       "-/\n" +
917       " -com\n" +
918       "  -intellij\n" +
919       "   [openapi]\n" +
920       " +jetbrains\n" +
921       " +org\n" +
922       " +xunit\n");
923
924
925     final DefaultMutableTreeNode node = findNode("intellij", false);
926     collapsePath(new TreePath(node.getPath()));
927
928     assertTree(
929       "-/\n" +
930       " -com\n" +
931       "  +[intellij]\n" +
932       " +jetbrains\n" +
933       " +org\n" +
934       " +xunit\n");
935   }
936
937
938   public void testDeferredSelection() throws Exception {
939     buildStructure(myRoot, false);
940
941     final Ref<Boolean> queued = new Ref<Boolean>(false);
942     final Ref<Boolean> intellijSelected = new Ref<Boolean>(false);
943     final Ref<Boolean> jetbrainsSelected = new Ref<Boolean>(false);
944     invokeLaterIfNeeded(new Runnable() {
945       @Override
946       public void run() {
947         try {
948           getBuilder().select(new NodeElement("intellij"), new Runnable() {
949             @Override
950             public void run() {
951               intellijSelected.set(true);
952             }
953           }, true);
954           queued.set(true);
955         }
956         catch (Exception e) {
957           e.printStackTrace();
958           fail();
959         }
960       }
961     });
962
963     new WaitFor() {
964       @Override
965       protected boolean condition() {
966         return queued.get();
967       }
968     };
969
970     assertTrue(getBuilder().getUi().isReady());
971     assertTree("+null\n");
972     assertNull(((DefaultMutableTreeNode)getBuilder().getTreeModel().getRoot()).getUserObject());
973
974     invokeLaterIfNeeded(new Runnable() {
975       @Override
976       public void run() {
977         getBuilder().getUi().activate(true);
978         getBuilder().select(new NodeElement("jetbrains"), new Runnable() {
979           @Override
980           public void run() {
981             jetbrainsSelected.set(true);
982           }
983         }, true);
984       }
985     });
986
987     waitBuilderToCome(new Condition<Object>() {
988       @Override
989       public boolean value(Object object) {
990         return intellijSelected.get() && jetbrainsSelected.get();
991       }
992     });
993
994     assertTree("-/\n" +
995                " -com\n" +
996                "  +[intellij]\n" +
997                " +[jetbrains]\n" +
998                " +org\n" +
999                " +xunit\n");
1000   }
1001
1002   private void expandNext(final NodeElement[] elements, final int index, final ProgressIndicator indicator, final ActionCallback callback) {
1003     if (indicator.isCanceled()) {
1004       callback.setRejected();
1005       return;
1006     }
1007
1008     if (index >= elements.length) {
1009       callback.setDone();
1010       return;
1011     }
1012
1013     getBuilder().expand(elements[index], new Runnable() {
1014       public void run() {
1015         expandNext(elements, index + 1, indicator, callback);
1016       }
1017     });
1018   }
1019
1020   public void testSelectAfterCancelledUpdate() throws Exception {
1021     Node intellij = myRoot.addChild("com").addChild("intellij");
1022     myRoot.addChild("jetbrains");
1023     activate();
1024
1025     buildNode(new NodeElement("intellij"), false);
1026     assertTree("-/\n" +
1027                " -com\n" +
1028                "  intellij\n" +
1029                " jetbrains\n");
1030
1031
1032     intellij.addChild("ide");
1033
1034     runAndInterrupt(new MyRunnable() {
1035       @Override
1036       public void runSafe() throws Exception {
1037         updateFromRoot();
1038       }
1039     }, "getChildren", new NodeElement("intellij"), Interruption.invokeCancel);
1040
1041     assertTree("-/\n" +
1042                " -com\n" +
1043                "  intellij\n" +
1044                " jetbrains\n");
1045
1046     select(new NodeElement("ide"), false);
1047
1048     assertTree("-/\n" +
1049                " -com\n" +
1050                "  -intellij\n" +
1051                "   [ide]\n" +
1052                " jetbrains\n");
1053   }
1054
1055   private void assertInterruption(Interruption cancelled) throws Exception {
1056     buildStructure(myRoot);
1057
1058     expand(getPath("/"));
1059     expand(getPath("com"));
1060     expand(getPath("jetbrains"));
1061     expand(getPath("org"));
1062     expand(getPath("xunit"));
1063
1064     assertTree("-/\n" +
1065                " -com\n" +
1066                "  +intellij\n" +
1067                " -jetbrains\n" +
1068                "  +fabrique\n" +
1069                " -org\n" +
1070                "  +eclipse\n" +
1071                " -xunit\n" +
1072                "  runner\n");
1073
1074     runAndInterrupt(new MyRunnable() {
1075       public void runSafe() throws Exception {
1076         updateFrom(new NodeElement("/"));
1077       }
1078     }, "update", new NodeElement("jetbrains"), cancelled);
1079
1080     runAndInterrupt(new MyRunnable() {
1081       @Override
1082       public void runSafe() throws Exception {
1083         updateFrom(new NodeElement("/"));
1084       }
1085     }, "getChildren", new NodeElement("jetbrains"), cancelled);
1086   }
1087
1088
1089   public void testBigTreeUpdate() throws Exception {
1090     Node msg = myRoot.addChild("Messages");
1091
1092     buildSiblings(msg, 0, 1, null, null);
1093
1094     doAndWaitForBuilder(new Runnable() {
1095       public void run() {
1096         getBuilder().getUi().activate(true);
1097       }
1098     });
1099
1100     buildNode("Messages", false);
1101
1102     assertTree("-/\n" +
1103                " -Messages\n" +
1104                "  -File 0\n" +
1105                "   message 1 for 0\n" +
1106                "   message 2 for 0\n" +
1107                "  -File 1\n" +
1108                "   message 1 for 1\n" +
1109                "   message 2 for 1\n");
1110
1111
1112     buildSiblings(msg, 2, 1000, new Runnable() {
1113       public void run() {
1114         getBuilder().queueUpdate();
1115       }
1116     }, null);
1117
1118     waitBuilderToCome();
1119   }
1120
1121   private void buildSiblings(final Node node, final int start, final int end, final Runnable eachRunnable, final Runnable endRunnable) throws InvocationTargetException, InterruptedException {
1122     SwingUtilities.invokeAndWait(new Runnable() {
1123       public void run() {
1124         for (int i = start; i <= end; i++) {
1125           Node eachFile = node.addChild("File " + i);
1126           myAutoExpand.add(eachFile.getElement());
1127           eachFile.addChild("message 1 for " + i);
1128           eachFile.addChild("message 2 for " + i);
1129
1130           if (eachRunnable != null) {
1131             eachRunnable.run();
1132           }
1133         }
1134
1135         if (endRunnable != null) {
1136           endRunnable.run();
1137         }
1138       }
1139     });
1140   }
1141
1142   private enum Interruption {
1143     throwProcessCancelled, invokeCancel
1144   }
1145
1146   private void runAndInterrupt(final Runnable action, final String interruptAction, final Object interruptElement, final Interruption interruption) throws Exception {
1147     myElementUpdate.clear();
1148
1149     final Ref<Thread> thread = new Ref<Thread>();
1150
1151     final boolean[] wasInterrupted = new boolean[] {false};
1152     myElementUpdateHook = new ElementUpdateHook() {
1153       public void onElementAction(String action, Object element) {
1154         if (thread.get() == null) {
1155           thread.set(Thread.currentThread());
1156         }
1157
1158
1159         boolean toInterrupt = element.equals(interruptElement) && action.equals(interruptAction);
1160
1161         if (wasInterrupted[0]) {
1162           if (myCancelRequest == null) {
1163             String status = getBuilder().getUi().getStatus();
1164             myCancelRequest = new AssertionError("Not supposed to be update after interruption request: action=" + action + " element=" + element + " interruptAction=" + interruptAction + " interruptElement=" + interruptElement);
1165           }
1166         } else {
1167           if (toInterrupt) {
1168             wasInterrupted[0] = true;
1169             switch (interruption) {
1170               case throwProcessCancelled:
1171                 throw new ProcessCanceledException();
1172               case invokeCancel:
1173                 getBuilder().cancelUpdate();
1174                 break;
1175             }
1176           }
1177         }
1178       }
1179     };
1180
1181     action.run();
1182
1183     myCancelRequest = null;
1184     myElementUpdateHook = null;
1185   }
1186
1187   public void testQueryStructure() throws Exception {
1188     buildStructure(myRoot);
1189
1190     assertTree("+/\n");
1191     assertUpdates("/: update");
1192
1193     expand(getPath("/"));
1194     assertTree("-/\n" +
1195                " +com\n" +
1196                " +jetbrains\n" +
1197                " +org\n" +
1198                " +xunit\n");
1199     assertUpdates("/: update getChildren\n" +
1200                   "com: update getChildren\n" +
1201                   "eclipse: update\n" +
1202                   "fabrique: update\n" +
1203                   "intellij: update\n" +
1204                   "jetbrains: update getChildren\n" +
1205                   "org: update getChildren\n" +
1206                   "runner: update\n" +
1207                   "xunit: update getChildren");
1208
1209     collapsePath(getPath("/"));
1210     assertTree("+/\n");
1211     assertUpdates("");
1212
1213     expand(getPath("/"));
1214     assertTree("-/\n" +
1215                " +com\n" +
1216                " +jetbrains\n" +
1217                " +org\n" +
1218                " +xunit\n");
1219     assertUpdates("/: update getChildren\n" +
1220                   "com: update getChildren\n" +
1221                   "eclipse: update\n" +
1222                   "fabrique: update\n" +
1223                   "intellij: update\n" +
1224                   "jetbrains: update getChildren\n" +
1225                   "org: update getChildren\n" +
1226                   "runner: update\n" +
1227                   "xunit: update getChildren");
1228
1229     updateFromRoot();
1230     assertTree("-/\n" +
1231                " +com\n" +
1232                " +jetbrains\n" +
1233                " +org\n" +
1234                " +xunit\n");
1235
1236     assertUpdates("/: update getChildren\n" +
1237                   "com: update getChildren\n" +
1238                   "eclipse: update\n" +
1239                   "fabrique: update\n" +
1240                   "intellij: update\n" +
1241                   "jetbrains: update getChildren\n" +
1242                   "org: update getChildren\n" +
1243                   "runner: update\n" +
1244                   "xunit: update getChildren");
1245
1246   }
1247
1248   public void testQueryStructureWhenExpand() throws Exception {
1249     buildStructure(myRoot);
1250
1251     assertTree("+/\n");
1252     assertUpdates("/: update");
1253
1254     buildNode("ide", false);
1255     assertTree("-/\n" +
1256                " +com\n" +
1257                " -jetbrains\n" +
1258                "  -fabrique\n" +
1259                "   ide\n" +
1260                " +org\n" +
1261                " +xunit\n");
1262
1263     assertUpdates("/: update getChildren\n" +
1264                   "com: update getChildren\n" +
1265                   "eclipse: update\n" +
1266                   "fabrique: update (2) getChildren\n" +
1267                   "ide: update getChildren\n" +
1268                   "intellij: update\n" +
1269                   "jetbrains: update getChildren\n" +
1270                   "org: update getChildren\n" +
1271                   "runner: update\n" +
1272                   "xunit: update getChildren");
1273
1274   }
1275
1276   public void testQueryStructureIsAlwaysShowsPlus() throws Exception {
1277     buildStructure(myRoot);
1278     myAlwaysShowPlus.add(new NodeElement("jetbrains"));
1279     myAlwaysShowPlus.add(new NodeElement("ide"));
1280
1281     expand(getPath("/"));
1282     assertTree("-/\n" +
1283                " +com\n" +
1284                " +jetbrains\n" +
1285                " +org\n" +
1286                " +xunit\n");
1287
1288     assertUpdates("/: update (2) getChildren\n" +
1289                   "com: update getChildren\n" +
1290                   "eclipse: update\n" +
1291                   "intellij: update\n" +
1292                   "jetbrains: update\n" +
1293                   "org: update getChildren\n" +
1294                   "runner: update\n" +
1295                   "xunit: update getChildren");
1296
1297     expand(getPath("jetbrains"));
1298     expand(getPath("fabrique"));
1299
1300     assertTree("-/\n" +
1301                " +com\n" +
1302                " -jetbrains\n" +
1303                "  -fabrique\n" +
1304                "   +ide\n" +
1305                " +org\n" +
1306                " +xunit\n");
1307
1308     assertUpdates("fabrique: update getChildren\n" +
1309                   "ide: update\n" +
1310                   "jetbrains: update getChildren");
1311
1312
1313     expand(getPath("ide"));
1314     assertTree("-/\n" +
1315                " +com\n" +
1316                " -jetbrains\n" +
1317                "  -fabrique\n" +
1318                "   ide\n" +
1319                " +org\n" +
1320                " +xunit\n");
1321
1322     assertUpdates("ide: update getChildren");
1323   }
1324
1325   public void testQueryStructureIsAlwaysLeaf() throws Exception {
1326     buildStructure(myRoot);
1327     myStructure.addLeaf(new NodeElement("openapi"));
1328
1329     buildNode("jetbrains", false);
1330     assertTree("-/\n" +
1331                " +com\n" +
1332                " -jetbrains\n" +
1333                "  +fabrique\n" +
1334                " +org\n" +
1335                " +xunit\n");
1336
1337     assertUpdates("/: update (2) getChildren\n" +
1338                   "com: update getChildren\n" +
1339                   "eclipse: update\n" +
1340                   "fabrique: update (2) getChildren\n" +
1341                   "ide: update\n" +
1342                   "intellij: update\n" +
1343                   "jetbrains: update getChildren\n" +
1344                   "org: update getChildren\n" +
1345                   "runner: update\n" +
1346                   "xunit: update getChildren");
1347
1348     expand(getPath("fabrique"));
1349     assertTree("-/\n" +
1350                " +com\n" +
1351                " -jetbrains\n" +
1352                "  -fabrique\n" +
1353                "   ide\n" +
1354                " +org\n" +
1355                " +xunit\n");
1356     assertUpdates("ide: update getChildren");
1357
1358
1359     buildNode("com", false);
1360     assertTree("-/\n" +
1361                " -com\n" +
1362                "  +intellij\n" +
1363                " -jetbrains\n" +
1364                "  -fabrique\n" +
1365                "   ide\n" +
1366                " +org\n" +
1367                " +xunit\n");
1368
1369     myElementUpdate.clear();
1370
1371     expand(getPath("intellij"));
1372     assertTree("-/\n" +
1373                " -com\n" +
1374                "  -intellij\n" +
1375                "   openapi\n" +
1376                " -jetbrains\n" +
1377                "  -fabrique\n" +
1378                "   ide\n" +
1379                " +org\n" +
1380                " +xunit\n");
1381
1382     assertUpdates("");
1383   }
1384
1385   public void testToggleIsAlwaysLeaf() throws Exception {
1386     buildStructure(myRoot);
1387
1388     buildNode("openapi", true);
1389
1390     assertTree("-/\n" +
1391                " -com\n" +
1392                "  -intellij\n" +
1393                "   [openapi]\n" +
1394                " +jetbrains\n" +
1395                " +org\n" +
1396                " +xunit\n");
1397
1398     myStructure.addLeaf(new NodeElement("intellij"));
1399
1400     updateFrom(new NodeElement("com"));
1401
1402     assertTree("-/\n" +
1403                " -com\n" +
1404                "  [intellij]\n" +
1405                " +jetbrains\n" +
1406                " +org\n" +
1407                " +xunit\n");
1408
1409     myStructure.removeLeaf(new NodeElement("intellij"));
1410     updateFrom(new NodeElement("com"));
1411
1412     assertTree("-/\n" +
1413                " -com\n" +
1414                "  +[intellij]\n" +
1415                " +jetbrains\n" +
1416                " +org\n" +
1417                " +xunit\n");
1418
1419     expand(getPath("intellij"));
1420
1421     assertTree("-/\n" +
1422                " -com\n" +
1423                "  -[intellij]\n" +
1424                "   openapi\n" +
1425                " +jetbrains\n" +
1426                " +org\n" +
1427                " +xunit\n");
1428
1429   }
1430
1431
1432   public void testSorting() throws Exception {
1433     buildStructure(myRoot);
1434     assertSorted("");
1435
1436     buildNode("/", false);
1437     assertTree("-/\n" +
1438                " +com\n" +
1439                " +jetbrains\n" +
1440                " +org\n" +
1441                " +xunit\n");
1442
1443     assertSorted("/\n" +
1444                  "com\n" +
1445                  "jetbrains\n" +
1446                  "org\n" +
1447                  "xunit");
1448
1449     updateFromRoot();
1450     assertTree("-/\n" +
1451                " +com\n" +
1452                " +jetbrains\n" +
1453                " +org\n" +
1454                " +xunit\n");
1455     assertSorted("/\n" +
1456                  "com\n" +
1457                  "jetbrains\n" +
1458                  "org\n" +
1459                  "xunit");
1460
1461     updateFrom(new NodeElement("/"), false);
1462     assertTree("-/\n" +
1463                " +com\n" +
1464                " +jetbrains\n" +
1465                " +org\n" +
1466                " +xunit\n");
1467     assertSorted("");
1468
1469
1470     expand(getPath("com"));
1471     assertTree("-/\n" +
1472                " -com\n" +
1473                "  +intellij\n" +
1474                " +jetbrains\n" +
1475                " +org\n" +
1476                " +xunit\n");
1477     assertSorted("intellij");
1478   }
1479
1480   public void testResorting() throws Exception {
1481     final boolean invert[] = new boolean[] {false};
1482     NodeDescriptor.NodeComparator<NodeDescriptor> c = new NodeDescriptor.NodeComparator<NodeDescriptor>() {
1483       public int compare(NodeDescriptor o1, NodeDescriptor o2) {
1484         return invert[0] ? AlphaComparator.INSTANCE.compare(o2, o1) : AlphaComparator.INSTANCE.compare(o1, o2);
1485       }
1486     };
1487
1488     myComparator.setDelegate(c);
1489
1490     buildStructure(myRoot);
1491
1492     buildNode("/", false);
1493     assertTree("-/\n" +
1494                " +com\n" +
1495                " +jetbrains\n" +
1496                " +org\n" +
1497                " +xunit\n");
1498
1499     updateFromRoot();
1500     updateFromRoot();
1501     updateFromRoot();
1502
1503     assertTrue(getMyBuilder().getUi().myOwnComparatorStamp > c.getStamp());
1504     invert[0] = true;
1505     c.incStamp();
1506
1507     updateFrom(new NodeElement("/"), false);
1508     assertTree("-/\n" +
1509                " +xunit\n" +
1510                " +org\n" +
1511                " +jetbrains\n" +
1512                " +com\n");
1513   }
1514
1515   public void testRestoreSelectionOfRemovedElement() throws Exception {
1516     buildStructure(myRoot);
1517     buildNode("openapi", true);
1518     assertTree("-/\n" +
1519                " -com\n" +
1520                "  -intellij\n" +
1521                "   [openapi]\n" +
1522                " +jetbrains\n" +
1523                " +org\n" +
1524                " +xunit\n");
1525
1526
1527     removeFromParentButKeepRef(new NodeElement("openapi"));
1528
1529     updateFromRoot();
1530
1531     assertTree("-/\n" +
1532                " -com\n" +
1533                "  [intellij]\n" +
1534                " +jetbrains\n" +
1535                " +org\n" +
1536                " +xunit\n");
1537   }
1538
1539   public void testElementMove1() throws Exception {
1540     assertMove(new Runnable() {
1541       public void run() {
1542         getBuilder().getUpdater().addSubtreeToUpdateByElement(new NodeElement("com"));
1543         getBuilder().getUpdater().addSubtreeToUpdateByElement(new NodeElement("jetbrains"));
1544       }
1545     });
1546   }
1547
1548   public void testElementMove2() throws Exception {
1549     assertMove(new Runnable() {
1550       public void run() {
1551         getBuilder().getUpdater().addSubtreeToUpdateByElement(new NodeElement("jetbrains"));
1552         getBuilder().getUpdater().addSubtreeToUpdateByElement(new NodeElement("com"));
1553       }
1554     });
1555   }
1556
1557   public void testSelectionOnDelete() throws Exception {
1558     doTestSelectionOnDelete(false);
1559   }
1560
1561   public void testSelectionOnDeleteButKeepRef() throws Exception {
1562     doTestSelectionOnDelete(true);
1563   }
1564
1565   public void testRevalidateStructure() throws Exception {
1566     final NodeElement com = new NodeElement("com");
1567     final NodeElement actionSystem = new NodeElement("actionSystem");
1568     actionSystem.setForcedParent(com);
1569
1570     final NodeElement fabrique = new NodeElement("fabrique");
1571     final NodeElement ide = new NodeElement("ide");
1572     fabrique.setForcedParent(myRoot.getElement());
1573
1574     doAndWaitForBuilder(new Runnable() {
1575       public void run() {
1576         myRoot.addChild(com).addChild(actionSystem);
1577         myRoot.addChild(fabrique).addChild(ide);
1578         getBuilder().getUi().activate(true);
1579       }
1580     });
1581
1582     select(actionSystem, false);
1583     expand(getPath("ide"));
1584
1585     assertTree("-/\n" +
1586                " -com\n" +
1587                "  [actionSystem]\n" +
1588                " -fabrique\n" +
1589                "  ide\n");
1590
1591
1592     removeFromParentButKeepRef(actionSystem);
1593     removeFromParentButKeepRef(fabrique);
1594
1595     final NodeElement newActionSystem = new NodeElement("actionSystem");
1596     final NodeElement newFabrique = new NodeElement("fabrique");
1597
1598     myStructure.getNodeFor(com).addChild("intellij").addChild("openapi").addChild(newActionSystem);
1599     myRoot.addChild("jetbrains").addChild("tools").addChild(newFabrique).addChild("ide");
1600
1601     assertSame(com, myStructure.getParentElement(actionSystem));
1602     assertNotSame(com, newActionSystem);
1603     assertEquals(new NodeElement("openapi"), myStructure.getParentElement(newActionSystem));
1604
1605     assertSame(myRoot.getElement(), myStructure.getParentElement(fabrique));
1606
1607     myStructure.setRevalidator(new Revalidator() {
1608       public AsyncResult<Object> revalidate(NodeElement element) {
1609         if (element == actionSystem) {
1610           return new AsyncResult.Done<Object>(newActionSystem);
1611         } else if (element == fabrique) {
1612           return new AsyncResult.Done<Object>(newFabrique);
1613         }
1614         return null;
1615       }
1616     });
1617
1618     updateFromRoot();
1619
1620     assertTree("-/\n" +
1621                " -com\n" +
1622                "  -intellij\n" +
1623                "   -openapi\n" +
1624                "    [actionSystem]\n" +
1625                " -jetbrains\n" +
1626                "  -tools\n" +
1627                "   -fabrique\n" +
1628                "    ide\n");
1629   }
1630
1631   public void testNoRevalidationIfInvalid() throws Exception {
1632     buildStructure(myRoot);
1633
1634     final NodeElement intellij = new NodeElement("intellij");
1635     buildNode(intellij, true);
1636
1637     assertTree("-/\n" +
1638                " -com\n" +
1639                "  +[intellij]\n" +
1640                " +jetbrains\n" +
1641                " +org\n" +
1642                " +xunit\n");
1643
1644
1645     removeFromParentButKeepRef(new NodeElement("intellij"));
1646     myValidator = new Validator() {
1647       public boolean isValid(Object element) {
1648         return !element.equals(intellij);
1649       }
1650     };
1651     final Ref<Object> revalidatedElement = new Ref<Object>();
1652     myStructure.setRevalidator(new Revalidator() {
1653       public AsyncResult<Object> revalidate(NodeElement element) {
1654         revalidatedElement.set(element);
1655         return null;
1656       }
1657     });
1658
1659     updateFromRoot();
1660
1661     assertTree("-/\n" +
1662                " [com]\n" +
1663                " +jetbrains\n" +
1664                " +org\n" +
1665                " +xunit\n");
1666     assertNull(revalidatedElement.get() != null ? revalidatedElement.get().toString() : null, revalidatedElement.get());
1667   }
1668
1669
1670   private void doTestSelectionOnDelete(boolean keepRef) throws Exception {
1671     myComparator.setDelegate(new NodeDescriptor.NodeComparator<NodeDescriptor>() {
1672       public int compare(NodeDescriptor o1, NodeDescriptor o2) {
1673         boolean isParent1 = myStructure._getChildElements(o1.getElement(), false).length > 0;
1674         boolean isParent2 = myStructure._getChildElements(o2.getElement(), false).length > 0;
1675
1676         int result = AlphaComparator.INSTANCE.compare(o1, o2);
1677
1678         if (isParent1) {
1679           result -= 1000;
1680         }
1681
1682         if (isParent2) {
1683           result += 1000;
1684         }
1685
1686         return result;
1687       }
1688     });
1689
1690     buildStructure(myRoot);
1691     myRoot.addChild("toDelete");
1692
1693     select(new NodeElement("toDelete"), false);
1694     assertTree("-/\n" +
1695                " +com\n" +
1696                " +jetbrains\n" +
1697                " +org\n" +
1698                " +xunit\n" +
1699                " [toDelete]\n");
1700
1701     if (keepRef) {
1702       removeFromParentButKeepRef(new NodeElement("toDelete"));
1703     } else {
1704       myStructure.getNodeFor(new NodeElement("toDelete")).delete();
1705     }
1706
1707     getMyBuilder().addSubtreeToUpdateByElement(new NodeElement("/"));
1708
1709     assertTree("-/\n" +
1710                " +com\n" +
1711                " +jetbrains\n" +
1712                " +org\n" +
1713                " +[xunit]\n");
1714   }
1715
1716   public void testSelectWhenUpdatesArePending() throws Exception {
1717     getBuilder().getUpdater().setDelay(1000);
1718
1719     buildStructure(myRoot);
1720
1721     buildNode("intellij", false);
1722     select(new Object[] {new NodeElement("intellij")}, false);
1723     assertTree("-/\n" +
1724                " -com\n" +
1725                "  -[intellij]\n" +
1726                "   openapi\n" +
1727                " +jetbrains\n" +
1728                " +org\n" +
1729                " +xunit\n");
1730
1731
1732     myIntellij.addChild("ui");
1733
1734     DefaultMutableTreeNode intellijNode = findNode("intellij", false);
1735     assertTrue(myTree.isExpanded(new TreePath(intellijNode.getPath())));
1736     getMyBuilder().addSubtreeToUpdate(intellijNode);
1737     assertFalse(getMyBuilder().getUi().isReady());
1738
1739     select(new Object[] {new NodeElement("ui")}, false);
1740     assertTree("-/\n" +
1741                " -com\n" +
1742                "  -intellij\n" +
1743                "   openapi\n" +
1744                "   [ui]\n" +
1745                " +jetbrains\n" +
1746                " +org\n" +
1747                " +xunit\n");
1748   }
1749
1750   public void testAddNewElementToLeafElementAlwaysShowPlus() throws Exception {
1751     myAlwaysShowPlus.add(new NodeElement("openapi"));
1752
1753     buildStructure(myRoot);
1754     select(new Object[] {new NodeElement("openapi")}, false);
1755
1756     assertTree("-/\n" +
1757                " -com\n" +
1758                "  -intellij\n" +
1759                "   +[openapi]\n" +
1760                " +jetbrains\n" +
1761                " +org\n" +
1762                " +xunit\n");
1763
1764     expand(getPath("openapi"));
1765     assertTree("-/\n" +
1766                " -com\n" +
1767                "  -intellij\n" +
1768                "   [openapi]\n" +
1769                " +jetbrains\n" +
1770                " +org\n" +
1771                " +xunit\n");
1772
1773
1774     myOpenApi.addChild("ui");
1775
1776     getMyBuilder().addSubtreeToUpdate(findNode("openapi", false));
1777
1778     assertTree("-/\n" +
1779                " -com\n" +
1780                "  -intellij\n" +
1781                "   +[openapi]\n" +
1782                " +jetbrains\n" +
1783                " +org\n" +
1784                " +xunit\n");
1785   }
1786
1787   public void testAddNewElementToLeafElement() throws Exception {
1788     buildStructure(myRoot);
1789     select(new Object[] {new NodeElement("openapi")}, false);
1790
1791     assertTree("-/\n" +
1792                " -com\n" +
1793                "  -intellij\n" +
1794                "   [openapi]\n" +
1795                " +jetbrains\n" +
1796                " +org\n" +
1797                " +xunit\n");
1798
1799     expand(getPath("openapi"));
1800     assertTree("-/\n" +
1801                " -com\n" +
1802                "  -intellij\n" +
1803                "   [openapi]\n" +
1804                " +jetbrains\n" +
1805                " +org\n" +
1806                " +xunit\n");
1807
1808
1809     myOpenApi.addChild("ui");
1810
1811     getMyBuilder().addSubtreeToUpdate(findNode("openapi", false));
1812
1813     assertTree("-/\n" +
1814                " -com\n" +
1815                "  -intellij\n" +
1816                "   +[openapi]\n" +
1817                " +jetbrains\n" +
1818                " +org\n" +
1819                " +xunit\n");
1820   }
1821
1822
1823   private void assertMove(Runnable updateRoutine) throws Exception {
1824     buildStructure(myRoot);
1825
1826     buildNode("intellij", true);
1827     assertTree("-/\n" +
1828                " -com\n" +
1829                "  +[intellij]\n" +
1830                " +jetbrains\n" +
1831                " +org\n" +
1832                " +xunit\n");
1833
1834     AbstractTreeBuilderTest.Node intellij = removeFromParentButKeepRef(new NodeElement("intellij"));
1835     myRoot.getChildNode("jetbrains").addChild(intellij);
1836
1837     updateRoutine.run();
1838
1839     assertTree("-/\n" +
1840                " com\n" +
1841                " -jetbrains\n" +
1842                "  +fabrique\n" +
1843                "  +[intellij]\n" +
1844                " +org\n" +
1845                " +xunit\n");
1846
1847   }
1848
1849
1850   public void testChangeRootElement() throws Exception {
1851     buildStructure(myRoot);
1852
1853     select(new NodeElement("com"), false);
1854
1855     assertTree("-/\n" +
1856                " +[com]\n" +
1857                " +jetbrains\n" +
1858                " +org\n" +
1859                " +xunit\n");
1860
1861     myRoot = new Node(null, "root");
1862     myStructure.reinitRoot(myRoot);
1863
1864     myRoot.addChild("com");
1865
1866     updateFromRoot();
1867     assertTree("+root\n");
1868   }
1869
1870   public void testReleaseBuilderDuringUpdate() throws Exception {
1871     assertReleaseDuringBuilding("update", "fabrique", new Runnable() {
1872       public void run() {
1873         try {
1874           select(new NodeElement("ide"), false);
1875         }
1876         catch (Exception e) {
1877           myCancelRequest = e;
1878         }
1879       }
1880     });
1881   }
1882
1883   public void testStickyLoadingNodeIssue() throws Exception {
1884     buildStructure(myRoot);
1885
1886     final boolean[] done = new boolean[] {false};
1887     getBuilder().select(new NodeElement("jetbrains"), new Runnable() {
1888       public void run() {
1889         getBuilder().expand(new NodeElement("fabrique"), new Runnable() {
1890           public void run() {
1891             done[0] = true;
1892           }
1893         });
1894       }
1895     });
1896
1897     waitBuilderToCome(new Condition() {
1898       public boolean value(Object o) {
1899         return done[0];
1900       }
1901     });
1902
1903     assertTree("-/\n" +
1904                " +com\n" +
1905                " -[jetbrains]\n" +
1906                "  -fabrique\n" +
1907                "   ide\n" +
1908                " +org\n" +
1909                " +xunit\n");
1910   }
1911
1912   public void testReleaseBuilderDuringGetChildren() throws Exception {
1913     assertReleaseDuringBuilding("getChildren", "fabrique", new Runnable() {
1914       public void run() {
1915         try {
1916           select(new NodeElement("ide"), false);
1917         }
1918         catch (Exception e) {
1919           myCancelRequest = e;
1920         }
1921       }
1922     });
1923   }
1924
1925   private void assertReleaseDuringBuilding(final String actionAction, final Object actionElement, Runnable buildAction) throws Exception {
1926     buildStructure(myRoot);
1927
1928     myElementUpdateHook = new ElementUpdateHook() {
1929       public void onElementAction(String action, Object element) {
1930         if (!element.toString().equals(actionElement.toString())) return;
1931
1932         Runnable runnable = new Runnable() {
1933           public void run() {
1934             myReadyRequest = true;
1935             Disposer.dispose(getBuilder());
1936           }
1937         };
1938
1939         if (actionAction.equals(action)) {
1940           if (getBuilder().getUi().isPassthroughMode()) {
1941             runnable.run();
1942           } else {
1943             SwingUtilities.invokeLater(runnable);
1944           }
1945         }
1946       }
1947     };
1948
1949     buildAction.run();
1950
1951     boolean released = new WaitFor(15000) {
1952       @Override
1953       protected boolean condition() {
1954         return getBuilder().getUi() == null;
1955       }
1956     }.isConditionRealized();
1957
1958     assertTrue(released);
1959   }
1960
1961   public static class SyncUpdate extends TreeUiTest {
1962     public SyncUpdate() {
1963       super(false, false);
1964     }
1965   }
1966
1967   public static class Passthrough extends TreeUiTest {
1968     public Passthrough() {
1969       super(true);
1970     }
1971
1972     public void testSelectionGoesToParentWhenOnlyChildMoved2() throws Exception {
1973       //todo
1974     }
1975
1976     public void testQueryStructureWhenExpand() throws Exception {
1977       //todo
1978     }
1979
1980
1981     public void testElementMove1() throws Exception {
1982       //todo
1983     }
1984
1985     @Override
1986     public void testClear() throws Exception {
1987       //todo
1988     }
1989
1990     @Override
1991     public void testSelectWhenUpdatesArePending() throws Exception {
1992       // doesn't make sense in pass-through mode
1993     }
1994
1995     @Override
1996     public void testBigTreeUpdate() throws Exception {
1997       // doesn't make sense in pass-thorught mode
1998     }
1999   }
2000
2001   public static class YieldingUpdate extends TreeUiTest {
2002     public YieldingUpdate() {
2003       super(true, false);
2004     }
2005   }
2006
2007   public static class BgLoadingSyncUpdate extends TreeUiTest {
2008     public BgLoadingSyncUpdate() {
2009       super(false, true);
2010     }
2011
2012     @Override
2013     protected int getChildrenLoadingDelay() {
2014       return 300;
2015     }
2016
2017     @Override
2018     protected int getNodeDescriptorUpdateDelay() {
2019       return 300;
2020     }
2021
2022     @Override
2023     public void testNoInfiniteSmartExpand() throws Exception {
2024       //todo
2025     }
2026
2027     @Override
2028     public void testBigTreeUpdate() throws Exception {
2029       //to slow, tested the same in VeryQuickBgLoadingTest
2030     }
2031   }
2032
2033   public static class QuickBgLoadingSyncUpdate extends TreeUiTest {
2034     public QuickBgLoadingSyncUpdate() {
2035       super(false, true);
2036     }
2037
2038
2039     @Override
2040     public void testDeferredSelection() throws Exception {
2041       super.testDeferredSelection();
2042     }
2043
2044     @Override
2045     protected int getNodeDescriptorUpdateDelay() {
2046       return 300;
2047     }
2048
2049     @Override
2050     public void testNoInfiniteSmartExpand() throws Exception {
2051       //todo
2052     }
2053
2054     @Override
2055     public void testBigTreeUpdate() throws Exception {
2056       //to slow, tested the same in VeryQuickBgLoadingTest
2057     }
2058
2059   }
2060
2061   public static class VeryQuickBgLoadingSyncUpdate extends TreeUiTest {
2062     public VeryQuickBgLoadingSyncUpdate() {
2063       super(false, true);
2064     }
2065
2066     @Override
2067     protected int getNodeDescriptorUpdateDelay() {
2068       return 0;
2069     }
2070
2071     @Override
2072     protected int getChildrenLoadingDelay() {
2073       return 0;
2074     }
2075
2076     @Override
2077     public void testNoInfiniteSmartExpand() throws Exception {
2078       // todo;
2079     }
2080
2081     @Override
2082     public void testReleaseBuilderDuringUpdate() throws Exception {
2083       // todo
2084     }
2085
2086     @Override
2087     public void testReleaseBuilderDuringGetChildren() throws Exception {
2088       // todo
2089     }
2090   }
2091
2092   public static TestSuite suite() {
2093     TestSuite suite = new TestSuite();
2094     suite.addTestSuite(Passthrough.class);
2095     suite.addTestSuite(SyncUpdate.class);
2096     suite.addTestSuite(YieldingUpdate.class);
2097     suite.addTestSuite(VeryQuickBgLoadingSyncUpdate.class);
2098     suite.addTestSuite(QuickBgLoadingSyncUpdate.class);
2099     suite.addTestSuite(BgLoadingSyncUpdate.class);
2100     return suite;
2101   }
2102
2103   @Override
2104   protected void tearDown() throws Exception {
2105     AbstractTreeUi ui = getBuilder().getUi();
2106     if (ui != null) {
2107       ui.getReady(this).doWhenProcessed(new Runnable() {
2108         public void run() {
2109           Log.flush();
2110         }
2111       });
2112     } else {
2113       Log.flush();
2114     }
2115     super.tearDown();
2116   }
2117
2118   abstract class MyRunnable implements Runnable {
2119     public final void run() {
2120       try {
2121         runSafe();
2122       }
2123       catch (Exception e) {
2124         throw new RuntimeException(e);
2125       }
2126     }
2127
2128     public abstract void runSafe() throws Exception;
2129   }
2130 }