001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.text;
043:
044: import java.io.IOException;
045: import javax.swing.text.*;
046: import org.netbeans.junit.*;
047: import org.openide.util.Exceptions;
048: import org.openide.util.RequestProcessor;
049:
050: /**
051: * Emulating old UndoRedo manager deadlock.
052: *
053: * @author Jaroslav Tulach
054: */
055: public class UndoRedoCooperationTest extends NbTestCase implements
056: CloneableEditorSupport.Env {
057: static {
058: System.setProperty(
059: "org.openide.windows.DummyWindowManager.VISIBLE",
060: "false");
061: }
062: /** the support to work with */
063: private CES support;
064: // Env variables
065: private String content = "Hello";
066: private boolean valid = true;
067: private boolean modified = false;
068: /** if not null contains message why this document cannot be modified */
069: private String cannotBeModified;
070: private java.util.Date date = new java.util.Date();
071: private java.util.List/*<java.beans.PropertyChangeListener>*/propL = new java.util.ArrayList();
072: private java.beans.VetoableChangeListener vetoL;
073:
074: /** Creates new TextTest */
075: public UndoRedoCooperationTest(String s) {
076: super (s);
077: }
078:
079: protected javax.swing.text.EditorKit createEditorKit() {
080: return new NbLikeEditorKit();
081: }
082:
083: protected void setUp() {
084: support = new CES(this , org.openide.util.Lookup.EMPTY);
085: }
086:
087: public void testOneThreadDoingEditsOneThreadDoingReverts()
088: throws Exception {
089: final StyledDocument d = support.openDocument();
090: d.insertString(0, "Ahoj\n", null);
091: assertTrue("We can do undo now", support.getUndoRedo()
092: .canUndo());
093:
094: class Blocker implements javax.swing.event.DocumentListener {
095: public void insertUpdate(javax.swing.event.DocumentEvent e) {
096: synchronized (UndoRedoCooperationTest.this ) {
097: UndoRedoCooperationTest.this .notify();
098: try {
099: UndoRedoCooperationTest.this .wait();
100: } catch (InterruptedException ex) {
101: ex.printStackTrace();
102: }
103: }
104: }
105:
106: public void removeUpdate(javax.swing.event.DocumentEvent e) {
107: }
108:
109: public void changedUpdate(javax.swing.event.DocumentEvent e) {
110: }
111: }
112: d.addDocumentListener(new Blocker());
113:
114: class Run implements Runnable {
115: public void run() {
116: try {
117: d.insertString(2, "Kuk", null);
118: } catch (BadLocationException ex) {
119: ex.printStackTrace();
120: }
121: }
122: }
123: class Undo implements Runnable {
124: public void run() {
125: support.getUndoRedo().undo();
126: support.getUndoRedo().undo();
127: assertFalse(support.getUndoRedo().canUndo());
128: assertTrue(support.getUndoRedo().canRedo());
129: }
130: }
131:
132: RequestProcessor.Task t1, t2;
133: synchronized (this ) {
134: t1 = new RequestProcessor("Inserting into document")
135: .post(new Run());
136: wait();
137: // now the inserting thread is blocked in EditThatCanBlockInAddEditMethod
138: t2 = new RequestProcessor("Doing undo").post(new Undo());
139:
140: // wait a while till one of the undos is called
141: Thread.sleep(100);
142: // let the insert into document continue
143: notify();
144: }
145:
146: // there should be a deadlock
147: t1.waitFinished();
148: t2.waitFinished();
149: }
150:
151: public void testDeadlock8692() throws Exception {
152: doTest(0);
153: }
154:
155: public void testUndoRedo() throws Exception {
156: doTest(1000);
157: }
158:
159: private void doTest(int sleep) throws Exception {
160: final StyledDocument d = support.openDocument();
161: d.insertString(0, "Ahoj\n", null);
162: support.saveDocument();
163: assertFalse("Previous save make it non-modified", support
164: .isModified());
165:
166: cannotBeModified = "My reason";
167:
168: class R implements Runnable {
169: private Exception ex;
170:
171: public void run() {
172: try {
173: d.remove(0, 2);
174: } catch (BadLocationException ex) {
175: this .ex = ex;
176: }
177: }
178: }
179:
180: R r = new R();
181: NbDocument.runAtomic(d, r);
182:
183: if (sleep > 0) {
184: Thread.sleep(sleep);
185: }
186:
187: //
188: // anyway we need to wait till all posted AWT tasks are finished
189: javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
190: public void run() {
191: // just wait
192: }
193: });
194:
195: assertEquals("Text contains orignal version", "Aho", d.getText(
196: 0, 3));
197: }
198:
199: public void testUndoMustBePossibleWithPlainDocument()
200: throws Exception {
201: support.plain = true;
202:
203: final StyledDocument d = support.openDocument();
204:
205: assertTrue("Document is not empty", d.getLength() > 0);
206:
207: d.remove(0, d.getLength());
208: String s = d.getText(0, d.getLength());
209: assertEquals("The document is empty", "", s);
210:
211: assertTrue("There is something to undo", support.getUndoRedo()
212: .canUndo());
213: support.getUndoRedo().undo();
214:
215: s = d.getText(0, d.getLength());
216: assertEquals("Contains the original content", content, s);
217: }
218:
219: public void testClearTheMap() throws Exception {
220: support.plain = false;
221:
222: final StyledDocument d = support.openDocument();
223:
224: support.getUndoRedo().discardAllEdits();
225:
226: assertTrue("Document is not empty", d.getLength() > 0);
227:
228: cannotBeModified = "Cannot";
229: try {
230: d.remove(0, d.getLength());
231: } catch (BadLocationException be) { /* expected */
232: }
233:
234: String s = d.getText(0, d.getLength());
235: assertEquals("The document is the same", "Hello", s);
236: }
237:
238: public void testEmptyRunAtomic() throws Exception {
239: content = "";
240: final StyledDocument d = support.openDocument();
241: d.insertString(0, "a", null);
242: assertTrue(support.isModified());
243: // Run empty runnable which should call notifyModify() followed by
244: // notifyUnmodified()
245: NbDocument.runAtomic(d, new Runnable() {
246: public void run() {
247: // Do nothing
248: }
249: });
250: assertTrue(
251: "Empty runAtomic() must not reset the modified flag",
252: support.isModified());
253: }
254:
255: public void testCanUndoDoesNotMarkDocumentUnmodified()
256: throws Exception {
257: content = "";
258: final StyledDocument d = support.openDocument();
259: d.insertString(0, "a", null);
260: assertTrue(support.isModified());
261: assertTrue(support.getUndoRedo().canUndo());
262: assertTrue("canUndo() must not reset the modified flag",
263: support.isModified());
264: }
265:
266: //
267: // Implementation of the CloneableEditorSupport.Env
268: //
269:
270: public synchronized void addPropertyChangeListener(
271: java.beans.PropertyChangeListener l) {
272: propL.add(l);
273: }
274:
275: public synchronized void removePropertyChangeListener(
276: java.beans.PropertyChangeListener l) {
277: propL.remove(l);
278: }
279:
280: public synchronized void addVetoableChangeListener(
281: java.beans.VetoableChangeListener l) {
282: assertNull("This is the first veto listener", vetoL);
283: vetoL = l;
284: }
285:
286: public void removeVetoableChangeListener(
287: java.beans.VetoableChangeListener l) {
288: assertEquals("Removing the right veto one", vetoL, l);
289: vetoL = null;
290: }
291:
292: public org.openide.windows.CloneableOpenSupport findCloneableOpenSupport() {
293: return support;
294: }
295:
296: public String getMimeType() {
297: return "text/plain";
298: }
299:
300: public java.util.Date getTime() {
301: return date;
302: }
303:
304: public java.io.InputStream inputStream() throws java.io.IOException {
305: return new java.io.ByteArrayInputStream(content.getBytes());
306: }
307:
308: public java.io.OutputStream outputStream()
309: throws java.io.IOException {
310: class ContentStream extends java.io.ByteArrayOutputStream {
311: public void close() throws java.io.IOException {
312: super .close();
313: content = new String(toByteArray());
314: }
315: }
316:
317: return new ContentStream();
318: }
319:
320: public boolean isValid() {
321: return valid;
322: }
323:
324: public boolean isModified() {
325: return modified;
326: }
327:
328: public void markModified() throws java.io.IOException {
329: if (cannotBeModified != null) {
330: IOException e = new IOException();
331: Exceptions.attachLocalizedMessage(e, cannotBeModified);
332: throw e;
333: }
334:
335: modified = true;
336: }
337:
338: public void unmarkModified() {
339: modified = false;
340: }
341:
342: /** Implementation of the CES */
343: private final class CES extends CloneableEditorSupport {
344: public boolean plain;
345:
346: public CES(Env env, org.openide.util.Lookup l) {
347: super (env, l);
348: }
349:
350: protected String messageName() {
351: return "Name";
352: }
353:
354: protected String messageOpened() {
355: return "Opened";
356: }
357:
358: protected String messageOpening() {
359: return "Opening";
360: }
361:
362: protected String messageSave() {
363: return "Save";
364: }
365:
366: protected String messageToolTip() {
367: return "ToolTip";
368: }
369:
370: protected javax.swing.text.EditorKit createEditorKit() {
371: if (plain) {
372: return super .createEditorKit();
373: } else {
374: return UndoRedoCooperationTest.this .createEditorKit();
375: }
376: }
377: } // end of CES
378:
379: }
|