Added
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 2000, 2005 IBM Corporation and others. |
3 |
* All rights reserved. This program and the accompanying materials |
4 |
* are made available under the terms of the Eclipse Public License v1.0 |
5 |
* which accompanies this distribution, and is available at |
6 |
* http://www.eclipse.org/legal/epl-v10.html |
7 |
* |
8 |
* Contributors: |
9 |
* IBM Corporation - initial API and implementation |
10 |
*******************************************************************************/ |
11 |
package org.eclipse.jdt.ui.tests.leaks; |
12 |
|
13 |
import junit.framework.Test; |
14 |
import junit.framework.TestSuite; |
15 |
|
16 |
import org.eclipse.swt.SWT; |
17 |
import org.eclipse.swt.widgets.Shell; |
18 |
|
19 |
import org.eclipse.jface.text.BadLocationException; |
20 |
import org.eclipse.jface.text.Document; |
21 |
import org.eclipse.jface.text.DocumentUndoManager; |
22 |
import org.eclipse.jface.text.IDocument; |
23 |
import org.eclipse.jface.text.ITextViewer; |
24 |
import org.eclipse.jface.text.IUndoManager; |
25 |
import org.eclipse.jface.text.Position; |
26 |
import org.eclipse.jface.text.SharedDocumentUndoManager; |
27 |
import org.eclipse.jface.text.TextViewer; |
28 |
|
29 |
import org.eclipse.jdt.ui.leaktest.LeakTestCase; |
30 |
import org.eclipse.jdt.ui.leaktest.LeakTestSetup; |
31 |
|
32 |
/** |
33 |
* Test for leaks in SharedDocumentUndoManager. |
34 |
* |
35 |
* @since 3.2 |
36 |
*/ |
37 |
public class SharedUndoManagerLeakTest extends LeakTestCase { |
38 |
|
39 |
/** The maximum undo level. */ |
40 |
private static final int MAX_UNDO_LEVEL = 256; |
41 |
|
42 |
/** The shell. */ |
43 |
private Shell fShell; |
44 |
/** The text viewer. */ |
45 |
private ITextViewer fTextViewer; |
46 |
/** The undo manager. */ |
47 |
private IUndoManager fUndoManager; |
48 |
|
49 |
public static Test suite() { |
50 |
return new LeakTestSetup(new TestSuite(SharedUndoManagerLeakTest.class)); |
51 |
} |
52 |
|
53 |
/* |
54 |
* @see TestCase#TestCase(String) |
55 |
*/ |
56 |
public SharedUndoManagerLeakTest(final String name) { |
57 |
super(name); |
58 |
} |
59 |
|
60 |
/* |
61 |
* @see TestCase#setUp() |
62 |
*/ |
63 |
protected void setUp() { |
64 |
fShell= new Shell(); |
65 |
fUndoManager= new SharedDocumentUndoManager(MAX_UNDO_LEVEL); |
66 |
fTextViewer= new TextViewer(fShell, SWT.NONE); |
67 |
fTextViewer.setUndoManager(fUndoManager); |
68 |
fUndoManager.connect(fTextViewer); |
69 |
} |
70 |
|
71 |
public void testLeak() throws Exception{ |
72 |
// There is always a document instance around, hence a DocumentUndoManager. |
73 |
// So count instances first. |
74 |
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=61432 |
75 |
int instanceCount = getInstanceCount(DocumentUndoManager.class); |
76 |
internalTestConvertLineDelimiters(); |
77 |
internalTestRandomAccess(); |
78 |
internalTestRandomAccessAsCompound(); |
79 |
|
80 |
fUndoManager.disconnect(); |
81 |
fUndoManager= null; |
82 |
|
83 |
fShell.dispose(); |
84 |
fShell= null; |
85 |
|
86 |
fTextViewer= null; |
87 |
|
88 |
assertInstanceCount(TextViewer.class, 0); |
89 |
assertInstanceCount(SharedDocumentUndoManager.class, 0); |
90 |
assertInstanceCount(getSharedUndoManagersInnerClass("TextInputListener"), 0); |
91 |
assertInstanceCount(getSharedUndoManagersInnerClass("DocumentUndoListener"), 0); |
92 |
assertInstanceCount(getSharedUndoManagersInnerClass("KeyAndMouseListener"), 0); |
93 |
|
94 |
assertInstanceCount(DocumentUndoManager.class, instanceCount); |
95 |
assertInstanceCount(getDocumentUndoManagersInnerClass("DocumentListener"), 0); |
96 |
assertInstanceCount(getDocumentUndoManagersInnerClass("HistoryListener"), 0); |
97 |
|
98 |
} |
99 |
|
100 |
private Class getSharedUndoManagersInnerClass(String className) { |
101 |
try { |
102 |
return Class.forName("org.eclipse.jface.text.SharedDocumentUndoManager$" + className, true, getClass().getClassLoader()); |
103 |
} catch (ClassNotFoundException e) { |
104 |
fail(); |
105 |
return null; |
106 |
} |
107 |
} |
108 |
|
109 |
private Class getDocumentUndoManagersInnerClass(String className) { |
110 |
try { |
111 |
return Class.forName("org.eclipse.jface.text.DocumentUndoManager$" + className, true, getClass().getClassLoader()); |
112 |
} catch (ClassNotFoundException e) { |
113 |
fail(); |
114 |
return null; |
115 |
} |
116 |
} |
117 |
|
118 |
|
119 |
/** |
120 |
* Test for line delimiter conversion. |
121 |
*/ |
122 |
private void internalTestConvertLineDelimiters() { |
123 |
final String original= "a\r\nb\r\n"; |
124 |
final IDocument document= new Document(original); |
125 |
fTextViewer.setDocument(document); |
126 |
|
127 |
try { |
128 |
document.replace(1, 2, "\n"); |
129 |
document.replace(3, 2, "\n"); |
130 |
} catch (BadLocationException e) { |
131 |
assertTrue(false); |
132 |
} |
133 |
|
134 |
assertTrue(fUndoManager.undoable()); |
135 |
fUndoManager.undo(); |
136 |
assertTrue(fUndoManager.undoable()); |
137 |
fUndoManager.undo(); |
138 |
|
139 |
final String reverted= document.get(); |
140 |
|
141 |
assertEquals(original, reverted); |
142 |
} |
143 |
|
144 |
/** |
145 |
* Randomly applies document changes. |
146 |
*/ |
147 |
private void internalTestRandomAccess() { |
148 |
final int RANDOM_STRING_LENGTH= 50; |
149 |
final int RANDOM_REPLACE_COUNT= 100; |
150 |
|
151 |
assertTrue(RANDOM_REPLACE_COUNT >= 1); |
152 |
assertTrue(RANDOM_REPLACE_COUNT <= MAX_UNDO_LEVEL); |
153 |
|
154 |
String original= createRandomString(RANDOM_STRING_LENGTH); |
155 |
final IDocument document= new Document(original); |
156 |
fTextViewer.setDocument(document); |
157 |
|
158 |
doChange(document, RANDOM_REPLACE_COUNT); |
159 |
|
160 |
assertTrue(fUndoManager.undoable()); |
161 |
while (fUndoManager.undoable()) |
162 |
fUndoManager.undo(); |
163 |
|
164 |
final String reverted= document.get(); |
165 |
|
166 |
assertEquals(original, reverted); |
167 |
} |
168 |
|
169 |
private void doChange(IDocument document, int count) { |
170 |
try { |
171 |
for (int i= 0; i < count; i++) { |
172 |
final Position position= createRandomPositionPoisson(document.getLength()); |
173 |
final String string= createRandomStringPoisson(4); |
174 |
document.replace(position.getOffset(), position.getLength(), string); |
175 |
} |
176 |
} catch (BadLocationException e) { |
177 |
assertTrue(false); |
178 |
} |
179 |
} |
180 |
|
181 |
private void internalTestRandomAccessAsCompound() { |
182 |
final int RANDOM_STRING_LENGTH= 50; |
183 |
final int RANDOM_REPLACE_COUNT= 100; |
184 |
|
185 |
assertTrue(RANDOM_REPLACE_COUNT >= 1); |
186 |
assertTrue(RANDOM_REPLACE_COUNT <= MAX_UNDO_LEVEL); |
187 |
|
188 |
String original= createRandomString(RANDOM_STRING_LENGTH); |
189 |
final IDocument document= new Document(original); |
190 |
fTextViewer.setDocument(document); |
191 |
|
192 |
fUndoManager.beginCompoundChange(); |
193 |
doChange(document, RANDOM_REPLACE_COUNT); |
194 |
fUndoManager.endCompoundChange(); |
195 |
|
196 |
assertTrue(fUndoManager.undoable()); |
197 |
while (fUndoManager.undoable()) |
198 |
fUndoManager.undo(); |
199 |
assertTrue(!fUndoManager.undoable()); |
200 |
|
201 |
final String reverted= document.get(); |
202 |
|
203 |
assertEquals(original, reverted); |
204 |
} |
205 |
|
206 |
private static String createRandomString(int length) { |
207 |
final StringBuffer buffer= new StringBuffer(); |
208 |
|
209 |
for (int i= 0; i < length; i++) |
210 |
buffer.append(getRandomCharacter()); |
211 |
|
212 |
return buffer.toString(); |
213 |
} |
214 |
|
215 |
private static final char getRandomCharacter() { |
216 |
// return Math.random() < 0.5 |
217 |
// ? '\r' |
218 |
// : '\n'; |
219 |
|
220 |
// XXX must include \r, \n, \t |
221 |
return (char) (32 + 95 * Math.random()); |
222 |
} |
223 |
|
224 |
private static String createRandomStringPoisson(int mean) { |
225 |
final int length= getRandomPoissonValue(2); |
226 |
return createRandomString(length); |
227 |
} |
228 |
|
229 |
private static Position createRandomPositionPoisson(int documentLength) { |
230 |
|
231 |
final float random= (float) Math.random(); |
232 |
final int offset= (int) (random * (documentLength + 1)); |
233 |
|
234 |
int length= getRandomPoissonValue(2); |
235 |
if (offset + length > documentLength) |
236 |
length= documentLength - offset; |
237 |
|
238 |
return new Position(offset, length); |
239 |
} |
240 |
|
241 |
private static int getRandomPoissonValue(int mean) { |
242 |
final int MAX_VALUE= 10; |
243 |
|
244 |
final float random= (float) Math.random(); |
245 |
float probability= 0; |
246 |
int i= 0; |
247 |
while (probability < 1 && i < MAX_VALUE) { |
248 |
probability += getPoissonDistribution(mean, i); |
249 |
if (random <= probability) |
250 |
break; |
251 |
i++; |
252 |
} |
253 |
return i; |
254 |
} |
255 |
|
256 |
private static float getPoissonDistribution(float lambda, int k) { |
257 |
return (float) (Math.exp(-lambda) * Math.pow(lambda, k) / faculty(k)); |
258 |
} |
259 |
|
260 |
/** |
261 |
* Returns the faculty of k. |
262 |
*/ |
263 |
private static final int faculty(int k) { |
264 |
return k == 0 |
265 |
? 1 |
266 |
: k * faculty(k - 1); |
267 |
} |
268 |
|
269 |
} |