001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.model;
038:
039: import javax.swing.text.BadLocationException;
040: import java.io.File;
041: import java.io.IOException;
042:
043: import java.util.Arrays;
044:
045: import edu.rice.cs.util.FileOpenSelector;
046: import edu.rice.cs.util.Log;
047: import edu.rice.cs.util.OperationCanceledException;
048: import edu.rice.cs.util.StringOps;
049: import edu.rice.cs.util.swing.Utilities;
050:
051: /** Test functions of the single display model.
052: * @version $Id: SingleDisplayModelTest.java 4255 2007-08-28 19:17:37Z mgricken $
053: */
054: public class SingleDisplayModelTest extends GlobalModelTestCase {
055:
056: // _log is inherited from GlobalModelTestCase
057:
058: private Object _readyLock = new Object(); // lock used for wait/notify on interpreterReady event
059:
060: /** Get the instance of the SingleDisplayModel.*/
061: private DefaultGlobalModel getSDModel() {
062: return _model;
063: }
064:
065: protected void assertNotEmpty() {
066: assertTrue("number of documents", getSDModel()
067: .getOpenDefinitionsDocuments().size() > 0);
068: }
069:
070: protected void assertActiveDocument(OpenDefinitionsDocument doc) {
071: assertEquals("active document", doc, getSDModel()
072: .getActiveDocument());
073: }
074:
075: /** Creates and returns a new document, makes sure newFile and activeDocumentChanged events are fired, and then
076: * adds some text.
077: * @return the new modified document
078: */
079: protected OpenDefinitionsDocument setupDocument(String text)
080: throws BadLocationException {
081:
082: assertNotEmpty();
083: SDTestListener listener = new SDTestListener() {
084: public synchronized void newFileCreated(
085: OpenDefinitionsDocument doc) {
086: newCount++;
087: }
088:
089: public synchronized void activeDocumentChanged(
090: OpenDefinitionsDocument doc) {
091: switchCount++;
092: }
093: };
094:
095: getSDModel().addListener(listener);
096:
097: listener.assertSwitchCount(0);
098:
099: // Open a new document
100: int numOpen = getSDModel().getOpenDefinitionsDocuments().size();
101: OpenDefinitionsDocument doc = getSDModel().newFile();
102: assertNumOpenDocs(numOpen + 1);
103:
104: listener.assertNewCount(1);
105: listener.assertSwitchCount(1);
106: assertLength(0, doc);
107: assertModified(false, doc);
108:
109: changeDocumentText(text, doc); // not atomic but no other thread is trying to modify doc
110: getSDModel().removeListener(listener);
111:
112: _log.log("New File " + doc + " created");
113:
114: return doc;
115: }
116:
117: /** Tests the invariant that at least one document is open at time of creation. */
118: public void testNotEmptyOnStartup() throws BadLocationException {
119: // Should be one empty document after creation
120: assertNumOpenDocs(1);
121: OpenDefinitionsDocument doc = getSDModel().getActiveDocument();
122: assertModified(false, doc);
123: assertLength(0, doc);
124: _log.log("testNotEmptyOnStartup completed");
125: }
126:
127: /** Tests the setNext and setPrevious functions, making sure that the activeDocumentChanged event is called. */
128: public void testDocumentSwitching() throws BadLocationException {
129: // Check for proper events
130: SDTestListener listener = new SDTestListener() {
131: public synchronized void newFileCreated(
132: OpenDefinitionsDocument doc) {
133: newCount++;
134: }
135:
136: public synchronized void activeDocumentChanged(
137: OpenDefinitionsDocument doc) {
138: switchCount++;
139: }
140: };
141: getSDModel().addListener(listener);
142:
143: // Set up first document
144: OpenDefinitionsDocument doc3 = getSDModel().getActiveDocument();
145: changeDocumentText(FOO_TEXT, doc3);
146: listener.assertSwitchCount(0);
147:
148: // Set up two more documents
149: OpenDefinitionsDocument doc2 = setupDocument(BAR_TEXT);
150: assertNumOpenDocs(2);
151: listener.assertNewCount(1);
152: listener.assertSwitchCount(1);
153: assertActiveDocument(doc2);
154:
155: OpenDefinitionsDocument doc1 = setupDocument(BAZ_TEXT);
156: assertNumOpenDocs(3);
157: listener.assertNewCount(2);
158: listener.assertSwitchCount(2);
159: assertActiveDocument(doc1);
160:
161: // Make sure setPrevious doesn't move (at start of list)
162: getSDModel().setActivePreviousDocument();
163: listener.assertSwitchCount(3);
164: assertActiveDocument(doc3);
165:
166: // Test setPrevious
167: getSDModel().setActiveNextDocument();
168: listener.assertSwitchCount(4);
169: assertActiveDocument(doc1);
170:
171: // Test setPrevious
172: getSDModel().setActiveNextDocument();
173: listener.assertSwitchCount(5);
174: assertActiveDocument(doc2);
175:
176: getSDModel().setActiveNextDocument();
177: listener.assertSwitchCount(6);
178: assertActiveDocument(doc3);
179:
180: // Make sure setNext doesn't move (at end of list)
181: getSDModel().setActiveNextDocument();
182: listener.assertSwitchCount(7);
183: assertActiveDocument(doc1);
184:
185: // Test setPrevious
186: getSDModel().setActivePreviousDocument();
187: listener.assertSwitchCount(8);
188: assertActiveDocument(doc3);
189:
190: // Test setActive
191: getSDModel().setActiveDocument(doc1);
192: listener.assertSwitchCount(9);
193: assertActiveDocument(doc1);
194:
195: // Make sure number of docs hasn't changed
196: assertNumOpenDocs(3);
197: getSDModel().removeListener(listener);
198: _log.log("testDocumentSwitching completed");
199: }
200:
201: /** Ensures that an unmodified, empty document is closed after a file is opened, while a modified document
202: * is left open.
203: */
204: public void testCloseUnmodifiedAutomatically()
205: throws BadLocationException, IOException,
206: OperationCanceledException, AlreadyOpenException {
207:
208: assertNumOpenDocs(1); // This assertion depends on the active document being set before setUp() is finished
209: OpenDefinitionsDocument doc = getSDModel().getActiveDocument();
210: assertModified(false, doc);
211: assertLength(0, doc);
212:
213: final File tempFile = writeToNewTempFile(BAR_TEXT);
214:
215: // Check for proper events
216: SDTestListener listener = new SDTestListener() {
217: public void fileOpened(OpenDefinitionsDocument doc) {
218: File file = null;
219: try {
220: file = doc.getFile();
221: } catch (FileMovedException fme) {
222: // We know file should exist
223: fail("file does not exist");
224: }
225: try {
226: assertEquals("file to open", tempFile
227: .getCanonicalFile(), file
228: .getCanonicalFile());
229: synchronized (this ) {
230: openCount++;
231: }
232: } catch (IOException ioe) {
233: fail("could not get canonical file");
234: }
235: }
236:
237: public synchronized void fileClosed(
238: OpenDefinitionsDocument doc) {
239: closeCount++;
240: }
241:
242: public synchronized void activeDocumentChanged(
243: OpenDefinitionsDocument doc) {
244: switchCount++;
245: }
246: };
247: getSDModel().addListener(listener);
248:
249: // Open file, should replace the old
250: doc = getSDModel().openFile(new FileSelector(tempFile));
251: listener.assertOpenCount(1);
252: listener.assertCloseCount(1);
253: listener.assertSwitchCount(1);
254: assertNumOpenDocs(1);
255: assertModified(false, doc);
256: assertContents(BAR_TEXT, doc);
257: getSDModel().removeListener(listener);
258: _log.log("testCloseUnmodifiedAutomatically completed");
259: }
260:
261: /** Tests that active document is switched on close, and that a new file is created after the last one is closed. */
262: public void testCloseFiles() throws BadLocationException {
263: // Check for proper events
264: SDTestListener listener = new SDTestListener() {
265: public synchronized boolean canAbandonFile(
266: OpenDefinitionsDocument doc) {
267: canAbandonCount++;
268: return true; // yes allow the abandon
269: }
270:
271: public synchronized void newFileCreated(
272: OpenDefinitionsDocument doc) {
273: newCount++;
274: }
275:
276: public synchronized void fileClosed(
277: OpenDefinitionsDocument doc) {
278: closeCount++;
279: }
280:
281: public synchronized void activeDocumentChanged(
282: OpenDefinitionsDocument doc) {
283: switchCount++;
284: }
285:
286: public synchronized void interpreterReady(File wd) { // closing all files calls resetInteractions
287: // Utilities.show("interpreterReady(" + wd + ") called");
288: // Utilities.show("Traceback is:\n" + StringOps.getStackTrace());
289: interpreterReadyCount++;
290: synchronized (_readyLock) {
291: _readyLock.notify();
292: }
293: }
294: };
295:
296: _model.addListener(listener);
297:
298: // Set up two documents
299: OpenDefinitionsDocument doc1 = _model.getActiveDocument();
300: changeDocumentText(FOO_TEXT, doc1);
301: OpenDefinitionsDocument doc2 = setupDocument(BAR_TEXT);
302: assertActiveDocument(doc2);
303: assertNumOpenDocs(2);
304: listener.assertNewCount(1);
305: listener.assertSwitchCount(1);
306:
307: // Close one
308: _model.closeFile(_model.getActiveDocument());
309: assertNumOpenDocs(1);
310: listener.assertCloseCount(1);
311: listener.assertAbandonCount(1);
312: listener.assertSwitchCount(2);
313: assertActiveDocument(doc1);
314: assertContents(FOO_TEXT, _model.getActiveDocument());
315:
316: // Close the other
317: _model.closeFile(_model.getActiveDocument());
318: listener.assertCloseCount(2);
319: listener.assertAbandonCount(2);
320:
321: // Ensure a new document was created
322: assertNumOpenDocs(1);
323: listener.assertNewCount(2);
324: listener.assertSwitchCount(3);
325: assertLength(0, _model.getActiveDocument());
326:
327: _log.log("Starting second phase of testCloseFiles");
328:
329: // Set up two documents
330: doc1 = _model.getActiveDocument();
331: changeDocumentText(FOO_TEXT, doc1);
332: doc2 = setupDocument(BAR_TEXT);
333: assertNumOpenDocs(2);
334: listener.assertNewCount(3);
335:
336: _log.log("Just before calling _model.closeAllFiles()");
337:
338: // Close all files, ensure new one was created
339: _model.closeAllFiles();
340: Utilities.clearEventQueue();
341: assertNumOpenDocs(1);
342: assertLength(0, _model.getActiveDocument());
343:
344: // wait for interpreter to be ready
345: try {
346: synchronized (_readyLock) {
347: if (listener.getInterpreterReadyCount() == 0)
348: _readyLock.wait(10000); // intentionally not a while
349: }
350: } catch (InterruptedException e) {
351: fail("Wait for interpreterReady event was interrupted by "
352: + e);
353: }
354: listener.assertInterpreterReadyCount(1);
355: listener.assertNewCount(4);
356: listener.assertCloseCount(4);
357: listener.assertAbandonCount(4);
358:
359: _model.removeListener(listener);
360: // _log.log("testCloseFiles completed");
361: }
362:
363: /** Tests the getCompleteFileName method. */
364: public void testCompleteFilename() throws BadLocationException,
365: IOException, OperationCanceledException,
366: AlreadyOpenException {
367: // Untitled
368: OpenDefinitionsDocument doc = _model.getActiveDocument();
369: assertEquals("untitled display filename", "(Untitled)", doc
370: .getCompletePath());
371:
372: // Ends in ".java"
373: File file = File.createTempFile("DrJava-filename-test",
374: ".java", _tempDir).getCanonicalFile();
375: file.deleteOnExit();
376: String name = file.getAbsolutePath();
377: doc = _model.openFile(new FileSelector(file));
378:
379: assertEquals(".java display filename", name, doc
380: .getCompletePath());
381:
382: // Doesn't contain ".java"
383: file = File.createTempFile("DrJava-filename-test", ".txt",
384: _tempDir).getCanonicalFile();
385: file.deleteOnExit();
386: name = file.getAbsolutePath();
387: doc = _model.openFile(new FileSelector(file));
388: assertEquals(".txt display filename", name, doc
389: .getCompletePath());
390:
391: // Modified File
392: file = File.createTempFile("DrJava-filename-test", ".java",
393: _tempDir).getCanonicalFile();
394: file.deleteOnExit();
395: name = file.getAbsolutePath();
396: doc = _model.openFile(new FileSelector(file));
397: changeDocumentText("foo", doc);
398: assertEquals(".java.txt display filename", name + " *", doc
399: .getCompletePath());
400: _log.log("testDisplayFilename completed");
401: }
402:
403: public void testDeleteFileWhileOpen() throws IOException,
404: OperationCanceledException, AlreadyOpenException {
405: String txt = "This is some test text";
406: File f = writeToNewTempFile(txt);
407: OpenDefinitionsDocument doc1 = _model
408: .openFile(new FileSelector(f));
409: OpenDefinitionsDocument doc2 = _model.newFile();
410: f.delete();
411: _model.closeFile(doc1);
412: _log.log("testDeleteFileWhileOpen completed");
413: // TODO: possibly test with more files; test to make sure the
414: // active document get's switched correctly.
415:
416: // Closing one file works. It doesn't work when you are closing
417: // multiple files including the one that doesn't exist on the file system.
418:
419: // Furthermore, if the user clicks "YES" to save to a different location,
420: // it doesn't prompt for the location to save, but immediately procedes with
421: // the closing and runs into the DocumentNotFound exception which ultimately
422: // leads to the View breaking. Solution may lie in whatever is not letting
423: // the file selector show up when clicking "YES" to resave.
424: }
425:
426: public void testDeleteFileBeforeCloseAll() throws IOException,
427: OperationCanceledException, AlreadyOpenException {
428: final File[] files = new File[10];
429: for (int i = 0; i < 10; i++) {
430: String txt = "Text for file " + i;
431: files[i] = writeToNewTempFile(txt);
432: }
433: FileOpenSelector fos = new FileOpenSelector() {
434: public File[] getFiles() throws OperationCanceledException {
435: return files;
436: }
437: };
438: _model.openFiles(fos);
439: _log.log("Opened files " + Arrays.toString(files));
440: OpenDefinitionsDocument doc = _model
441: .getSortedOpenDefinitionsDocuments().get(5);
442: _model.setActiveDocument(doc);
443: _log.log("Active document is: " + doc);
444: files[5].delete();
445: _log.log("Delected document: " + doc);
446: _model.closeAllFiles();
447: _log.log("testDeleteFileBeforeCloseAll completed");
448: }
449:
450: /** A GlobalModelListener for testing. By default it expects no events to be fired. To customize,
451: * subclass and override one or more methods.
452: */
453: public static class SDTestListener extends TestListener implements
454: GlobalModelListener {
455:
456: /** Extra counter for SDTestListener */
457: protected volatile int switchCount;
458:
459: public void resetCounts() {
460: super .resetCounts();
461: switchCount = 0;
462: }
463:
464: public void assertSwitchCount(int i) {
465: assertEquals("number of active document switches", i,
466: switchCount);
467: }
468:
469: public void activeDocumentChanged(OpenDefinitionsDocument doc) {
470: fail("activeDocumentChanged fired unexpectedly");
471: }
472:
473: public void activeDocumentRefreshed(OpenDefinitionsDocument doc) {
474: fail("activeDocumentRefreshed fired unexpectedly");
475: }
476:
477: public int getInterpreterReadyCount() {
478: return interpreterReadyCount;
479: }
480: }
481: }
|