blob: bfe042bd071f61ec027a7109e78df0afedb21f48 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.openide.text;
import java.awt.GraphicsEnvironment;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.netbeans.junit.NbTestCase;
import org.netbeans.junit.RandomlyFails;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.Task;
import org.openide.util.UserQuestionException;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
/** Testing different features of CloneableEditorSupport
*
* @author Jaroslav Tulach
*/
public class CloneableEditorSupportTest extends NbTestCase
implements CloneableEditorSupport.Env {
public static Test suite() {
return GraphicsEnvironment.isHeadless() ? new TestSuite() : new TestSuite(CloneableEditorSupportTest.class);
}
static {
System.setProperty("org.openide.windows.DummyWindowManager.VISIBLE", "false");
System.setProperty("assertgc.paths", "0");
}
/** the support to work with */
private CloneableEditorSupport support;
/** the content of lookup of support */
private InstanceContent ic;
/** Delay in miliseconds to simulate delay between closing stream and file modification time. */
private long delay = 0L;
public void testSaveAfterLoadOfFileThatWasModifiedInFuture() throws Exception {
content = "Ahoj\nMyDoc";
// simulate the the just opened file has modification time in future
// intentionally extremly big to make it work even in debugger
date = new Date(System.currentTimeMillis() + 86400000);
javax.swing.text.Document doc = support.openDocument ();
assertNotNull (doc);
doc.remove(0, doc.getLength());
support.saveDocument();
doc.getProperty(Object.class);
assertEquals("Saved ok and empty", "", content);
}
public void testDocCanBeGCdWhenNotModifiedButOpened() throws Exception {
content = "Ahoj\nMyDoc";
javax.swing.text.Document doc = support.openDocument ();
assertNotNull (doc);
WeakReference<Object> ref = new WeakReference<Object>(doc);
doc = null;
assertGC ("Document can dissapear", ref, Collections.singleton(support));
assertTrue ("Can be closed without problems", support.close ());
assertFalse ("Document is not loaded", support.isDocumentLoaded ());
}
public void testDocumentIsNotGCedIfModified () throws Exception {
content = "Ahoj\nMyDoc";
javax.swing.text.Document doc = support.openDocument ();
assertNotNull (doc);
doc.insertString (0, "Zmena", null);
assertTrue ("Is modified", support.isModified ());
WeakReference<Object> ref = new WeakReference<Object>(doc);
doc = null;
boolean ok;
try {
assertGC ("Should fail", ref);
ok = false;
} catch (AssertionFailedError expected) {
ok = true;
}
if (!ok) {
fail ("Document should not disappear, as it is modified");
}
assertTrue ("Document remains loaded", support.isDocumentLoaded ());
}
@RandomlyFails // http://deadlock.netbeans.org/hudson/job/NB-Core-Build/9880/testReport/
public void testDocumentIsNotGCedIfOpenedInEditor () throws Exception {
content = "Ahoj\nMyDoc";
javax.swing.text.Document doc = support.openDocument ();
assertNotNull (doc);
support.open();
class R implements Runnable {
JEditorPane[] arr;
public void run() {
arr = support.getOpenedPanes();
}
public JEditorPane[] getArr() throws Exception {
SwingUtilities.invokeAndWait(this);
return arr;
}
}
R panes = new R();
assertNotNull("There is one pane", panes.getArr());
assertFalse("Not modified", support.isModified ());
WeakReference<Object> ref = new WeakReference<Object>(doc);
doc = null;
boolean ok;
try {
assertGC ("Should fail", ref);
ok = false;
} catch (AssertionFailedError expected) {
ok = true;
}
if (!ok) {
fail ("Document should not disappear, as it is modified");
}
assertTrue ("Document remains loaded", support.isDocumentLoaded ());
support.close();
assertNull("There is no pane", panes.getArr());
assertGC ("Should succeed with GC now", ref);
}
// Env variables
private String content = "";
private boolean valid = true;
private boolean modified = false;
/** if not null contains message why this document cannot be modified */
private String cannotBeModified;
private java.util.Date date = new java.util.Date ();
private java.util.List<java.beans.PropertyChangeListener> propL = new ArrayList<PropertyChangeListener>();
private java.beans.VetoableChangeListener vetoL;
public CloneableEditorSupportTest(java.lang.String testName) {
super(testName);
}
@Override
protected void setUp () {
ic = new InstanceContent ();
support = new CES (this, new AbstractLookup (ic));
}
public void testDocumentCanBeRead () throws Exception {
content = "Ahoj\nMyDoc";
javax.swing.text.Document doc = support.openDocument ();
assertNotNull (doc);
String s = doc.getText (0, doc.getLength ());
assertEquals ("Same text as in the stream", content, s);
assertFalse ("No redo", support.getUndoRedo ().canRedo ());
assertFalse ("No undo", support.getUndoRedo ().canUndo ());
}
public void testLineLookupIsPropagated () throws Exception {
content = "Line1\nLine2\n";
Integer template = new Integer (1);
ic.add (template); // put anything into the lookup
// in order to set.getLines() work correctly, the document has to be loaded
support.openDocument();
Line.Set set = support.getLineSet();
java.util.List list = set.getLines();
assertEquals ("Three lines", 3, list.size ());
Line l = (Line)list.get (0);
Integer i = l.getLookup().lookup(Integer.class);
assertEquals ("The original integer", template, i);
ic.remove (template);
i = l.getLookup().lookup(Integer.class);
assertNull ("Lookup is dynamic, so now there is nothing", i);
}
public void testGetInputStream () throws Exception {
content = "goes\nto\nInputStream";
String added = "added before\n";
javax.swing.text.Document doc = support.openDocument ();
assertNotNull (doc);
// modify the document
doc.insertString(0, added, null);
compareStreamWithString(support.getInputStream(), added + content);
}
public void testGetInputStreamWhenClosed () throws Exception {
content = "basic\ncontent";
compareStreamWithString(support.getInputStream(), content);
// we should be doing this with the document still closed
assertNull("The document is supposed to be still closed", support.getDocument ());
}
public void testDocumentCannotBeModified () throws Exception {
content = "Ahoj\nMyDoc";
cannotBeModified = "No, you cannot modify this document in this test";
javax.swing.text.Document doc = support.openDocument ();
assertNotNull (doc);
assertFalse ("Nothing to undo", support.getUndoRedo ().canUndo ());
// this should not be allowed
try {
doc.insertString (0, "Kuk", null);
fail("Modification should not proceed");
} catch (BadLocationException ex) {
// Expected
}
String s = doc.getText (0, doc.getLength ());
assertEquals ("The document is now the same as at the begining", content, s);
assertEquals ("Message has been shown to user in status bar", cannotBeModified, org.openide.awt.StatusDisplayer.getDefault ().getStatusText ());
}
public void testDocumentCanBeGarbageCollectedWhenClosed () throws Exception {
content = "Ahoj\nMyDoc";
javax.swing.text.Document doc = support.openDocument ();
assertNotNull (doc);
assertTrue ("Document is loaded", support.isDocumentLoaded ());
assertTrue ("Can be closed without problems", support.close ());
assertFalse ("Document is not loaded", support.isDocumentLoaded ());
WeakReference<Object> ref = new WeakReference<Object>(doc);
doc = null;
assertGC ("Document can dissapear", ref);
}
/**
* Tests that the wrapEditorComponent() method returns the passed
* parameter (doesn't wrap the passed component in some additional UI).
*/
public void testWrapEditorComponent() {
javax.swing.JPanel panel = new javax.swing.JPanel();
assertSame(support.wrapEditorComponent(panel), panel);
}
public void testSaveWhenNoDocumentOpen() throws IOException {
modified = true;
support.saveDocument();
}
public void testGetEditorKit() {
EditorKit kit = CloneableEditorSupport.getEditorKit("text/plain");
assertNotNull("EditorKit should never be null", kit);
// There shouldn't be any EK registered and we should get the default one
assertEquals("Wrong default EditorKit", "org.openide.text.CloneableEditorSupport$PlainEditorKit", kit.getClass().getName());
}
/** Tests that UserQuestionException is thrown when saving externally modified document. */
public void testSaveExternallyModified() throws Exception {
content = "Ahoj\nMyDoc";
support.openDocument();
modified = true;
// simulate external modification
date = new Date(System.currentTimeMillis() + 100);
try {
support.saveDocument();
fail("UserQuestionException should be thrown because of external modification.");
} catch (UserQuestionException e) {
// OK, exception expected.
}
}
/** Tests that UserQuestionException is NOT thrown when document is not externally
* modified but there is some delay between closing stream and setting file modification time.
* See issue 149069.
*/
public void testSaveNotExternallyModified() throws Exception {
content = "Ahoj\nMyDoc";
support.openDocument();
modified = true;
// intentionally extremly big to make it work even in debugger
delay = 86400000;
support.saveDocument();
modified = true;
try {
support.saveDocument();
} catch (UserQuestionException e) {
fail("UserQuestionException should NOT be thrown (see #149069).");
}
}
private void compareStreamWithString(InputStream is, String s) throws Exception{
int i;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((i = is.read()) != -1) {
baos.write(i);
}
byte b1[] = baos.toByteArray();
byte b2[] = s.getBytes();
assertTrue("Same bytes as would result from the string: " + s, Arrays.equals(b1, b2));
}
public void testDocumentBeforeSaveRunnableProcessed() throws Exception {
content = "Ahoj\nMyDoc";
final javax.swing.text.Document doc = support.openDocument ();
assertNotNull (doc);
final boolean[] processed = { false };
doc.putProperty("beforeSaveRunnable", new Runnable() {
public void run() {
// See CloneableEditorSupport for property explanation
Runnable beforeSaveStart = (Runnable) doc.getProperty("beforeSaveStart");
if (beforeSaveStart != null) {
beforeSaveStart.run();
}
processed[0] = true;
// See CloneableEditorSupport for property explanation
Runnable beforeSaveEnd = (Runnable) doc.getProperty("beforeSaveEnd");
if (beforeSaveEnd != null) {
beforeSaveEnd.run();
}
}
});
doc.insertString(0, "Nazdar", null); // Modify doc to allow save
support.saveDocument();
assertTrue("CES.saveDocument() did not execute a runnable in \"beforeSaveRunnable\" document property",
processed[0]);
}
public void testPrepareDocument() throws Exception {
content = "Ahoj\nMyDoc";
Task task = support.prepareDocument();
task.waitFinished();
try {
Object o = new Object();
assertGC("", new WeakReference<Object>(o));
} catch (AssertionFailedError e) {
// ignore, intentional
}
Document doc = support.getDoc();
assertNotNull("Document should not be GCed while its loading task exists", doc);
Task task2 = support.prepareDocument();
assertTrue("Loading task should be finished", task2.isFinished());
assertNotSame("Expecting different task instance", task, task2);
task2 = null;
Reference<Task> taskRef = new WeakReference<Task>(task);
Reference<Document> docRef = new WeakReference<Document>(doc);
task = null;
doc = null;
assertGC("Can't GC document loading task", taskRef);
assertGC("Can't GC document", docRef);
}
//
// Implementation of the CloneableEditorSupport.Env
//
public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener l) {
propL.add (l);
}
public synchronized void removePropertyChangeListener(java.beans.PropertyChangeListener l) {
propL.remove (l);
}
public synchronized void addVetoableChangeListener(java.beans.VetoableChangeListener l) {
assertNull ("This is the first veto listener", vetoL);
vetoL = l;
}
public void removeVetoableChangeListener(java.beans.VetoableChangeListener l) {
assertEquals ("Removing the right veto one", vetoL, l);
vetoL = null;
}
public org.openide.windows.CloneableOpenSupport findCloneableOpenSupport() {
return support;
}
public String getMimeType() {
return "text/plain";
}
public java.util.Date getTime() {
return date;
}
public java.io.InputStream inputStream() throws java.io.IOException {
return new java.io.ByteArrayInputStream (content.getBytes ());
}
public java.io.OutputStream outputStream() throws java.io.IOException {
class ContentStream extends java.io.ByteArrayOutputStream {
@Override
public void close() throws java.io.IOException {
super.close ();
content = new String (toByteArray ());
date = new Date(System.currentTimeMillis() + delay);
}
}
return new ContentStream ();
}
public boolean isValid() {
return valid;
}
public boolean isModified() {
return modified;
}
public void markModified() throws java.io.IOException {
if (cannotBeModified != null) {
final String notify = cannotBeModified;
IOException e = new IOException () {
public String getLocalizedMessage () {
return notify;
}
};
Exceptions.attachLocalizedMessage(e, cannotBeModified);
throw e;
}
modified = true;
}
public void unmarkModified() {
modified = false;
}
/** Implementation of the CES */
private static final class CES extends CloneableEditorSupport {
public CES (Env env, Lookup l) {
super (env, l);
}
protected String messageName() {
return "Name";
}
protected String messageOpened() {
return "Opened";
}
protected String messageOpening() {
return "Opening";
}
protected String messageSave() {
return "Save";
}
protected String messageToolTip() {
return "ToolTip";
}
}
}