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: /*
043: * EditorTestCase.java
044: *
045: * Created on 24. srpen 2004, 12:32
046: */
047:
048: package lib;
049:
050: import java.awt.datatransfer.DataFlavor;
051: import java.awt.datatransfer.Transferable;
052: import java.awt.datatransfer.UnsupportedFlavorException;
053: import java.io.File;
054: import java.io.IOException;
055: import java.util.StringTokenizer;
056: import javax.swing.text.BadLocationException;
057: import javax.swing.text.Document;
058: import org.netbeans.jellytools.EditorOperator;
059: import org.netbeans.jellytools.JellyTestCase;
060: import org.netbeans.jellytools.NbDialogOperator;
061: import org.netbeans.jellytools.ProjectsTabOperator;
062: import org.netbeans.jellytools.actions.OpenAction;
063: import org.netbeans.jellytools.nodes.Node;
064: import org.netbeans.jellytools.nodes.ProjectRootNode;
065: import org.netbeans.jemmy.JemmyException;
066: import org.netbeans.jemmy.JemmyProperties;
067: import org.netbeans.jemmy.TimeoutExpiredException;
068: import org.netbeans.jemmy.Waitable;
069: import org.netbeans.jemmy.Waiter;
070: import org.netbeans.jemmy.operators.ComponentOperator;
071: import org.netbeans.jemmy.operators.JEditorPaneOperator;
072: import org.netbeans.jemmy.operators.JTextFieldOperator; //retouche:
073: import org.netbeans.junit.ide.ProjectSupport;
074:
075: //retouche:
076: //import org.netbeans.junit.ide.ProjectSupport;
077:
078: /**
079: *
080: * @author Petr Felenda, Martin Roskanin
081: */
082: public class EditorTestCase extends JellyTestCase {
083:
084: private static final int OPENED_PROJECT_ACCESS_TIMEOUT = 1000;
085:
086: /** Default name of project is used if not specified in openProject method. */
087: private String defaultProjectName = "editor_test";
088: private String defaultSamplePackage = "dummy";
089: private String defaultSampleName = "sample1";
090:
091: private static final char treeSeparator = '|';
092: private final String defaultPackageNameTreePath = "Source packages"
093: + treeSeparator + "dummy";
094: private final String defaultFileName = "sample1";
095: private String projectName = null;
096: private String treeSubPackagePathToFile = null;
097: private String fileName = null;
098: private final String dialogSaveTitle = "Save"; // I18N
099: public static final int WAIT_MAX_MILIS_FOR_CLIPBOARD = 4000;
100:
101: /**
102: * Creates a new instance of EditorTestCase.
103: *
104: * <p>
105: * Initializes default sample file name (package and name)
106: * so that {@link #openDefaultSampleFile()} can be used.
107: * <br>
108: * The rule for naming is the same like for golden files
109: * i.e. package corresponds to the class name and the file
110: * name corresponds to test method name.
111: *
112: * @param testMethodName name of the test method
113: * that should be executed.
114: */
115: public EditorTestCase(String testMethodName) {
116: super (testMethodName);
117:
118: defaultSamplePackage = getClass().getName();
119: defaultSampleName = getName();
120: }
121:
122: /**
123: * Split class full name into package name and class name.
124: *
125: * @param full name of the class
126: * @return array containing package name and the class name.
127: */
128: /* public static String[] splitClassName(String classFullName) {
129: int lastDotIndex = classFullName.lastIndexOf('.');
130: return new String[] {
131: (lastDotIndex >= 0) ? classFullName.substring(0, lastDotIndex) : "", // pkg name
132: classFullName.substring(lastDotIndex + 1) // class name
133: };
134: }
135: */
136:
137: /** Open project. Before opening the project is checked opened projects.
138: * @param projectName is name of the project stored in .../editor/test/qa-functional/data/ directory.
139: */
140: public void openProject(String projectName) {
141: this .projectName = projectName;
142: File projectPath = new File(this .getDataDir() + "/projects",
143: projectName);
144: log("data dir = " + this .getDataDir().toString());
145:
146: /* 1. check if project is open */
147: ProjectsTabOperator pto = new ProjectsTabOperator();
148: pto.invoke();
149: boolean isOpen = true;
150: try {
151: JemmyProperties.setCurrentTimeout(
152: "JTreeOperator.WaitNextNodeTimeout",
153: OPENED_PROJECT_ACCESS_TIMEOUT);
154: ProjectRootNode prn = pto.getProjectRootNode(projectName);
155: } catch (TimeoutExpiredException ex) {
156: // This excpeiton is ok, project is not open;
157: //ex.printStackTrace();
158: isOpen = false;
159: }
160:
161: if (isOpen) {
162: log("Project is open!");
163: return;
164: }
165:
166: /* 2. open project */
167: //retouche:
168: Object prj = ProjectSupport.openProject(projectPath);
169:
170: }
171:
172: /**
173: * Get the default project name to be used
174: * in {@link openDefaultProject()}.
175: * <br>
176: * The default value is "editor_test".
177: *
178: * @return default project name
179: */
180: protected String getDefaultProjectName() {
181: return defaultProjectName;
182: }
183:
184: /**
185: * Set the default project name to be used
186: * in {@link openDefaultProject()}.
187: *
188: * @param defaultProjectName new default project name.
189: */
190: /*
191: protected void setDefaultProjectName(String defaultProjectName) {
192: this.defaultProjectName = defaultProjectName;
193: }
194: */
195:
196: /**
197: * Open default project determined
198: * by {@link #getDefaultProjectName()}.
199: */
200: protected void openDefaultProject() {
201: openProject(getDefaultProjectName());
202: }
203:
204: /**
205: * Close the default project.
206: */
207: protected void closeDefaultProject() {
208: closeProject(getDefaultProjectName());
209: }
210:
211: protected void closeProject(String projectName) {
212: //retouche:
213: // ProjectSupport.closeProject(projectName);
214: }
215:
216: /** Open file in open project
217: * @param treeSubPath e.g. "Source Packages|test","sample1" */
218: public void openFile(String treeSubPackagePathToFile,
219: String fileName) {
220: // debug info, to be removed
221: this .treeSubPackagePathToFile = treeSubPackagePathToFile;
222: ProjectsTabOperator pto = new ProjectsTabOperator();
223: pto.invoke();
224: ProjectRootNode prn = pto.getProjectRootNode(projectName);
225: prn.select();
226:
227: // fix of issue #51191
228: // each of nodes is checked by calling method waitForChildNode
229: // before they are actually opened
230: StringTokenizer st = new StringTokenizer(
231: treeSubPackagePathToFile, treeSeparator + "");
232: String token = "";
233: String oldtoken = "";
234: // if there are more then one tokens process each of them
235: if (st.countTokens() > 1) {
236: token = st.nextToken();
237: String fullpath = token;
238: while (st.hasMoreTokens()) {
239: token = st.nextToken();
240: waitForChildNode(fullpath, token);
241: fullpath += treeSeparator + token;
242: }
243: }
244: // last node
245: waitForChildNode(treeSubPackagePathToFile, fileName);
246: // end of fix of issue #51191
247:
248: Node node = new Node(prn, treeSubPackagePathToFile
249: + treeSeparator + fileName);
250: //node.performPopupAction("Open");
251: new OpenAction().performAPI(node); //should be more stable then performing open action from popup
252:
253: }
254:
255: /**
256: * Waits for a child node to be shown in the IDE. Needed for test
257: * stabilization on slow machines.
258: * @param parentPath full path for parent, | used as a delimiter
259: * @param childName name of the child node
260: */
261: public void waitForChildNode(String parentPath, String childName) {
262: ProjectsTabOperator pto = new ProjectsTabOperator();
263: ProjectRootNode prn = pto.getProjectRootNode(projectName);
264: prn.select();
265: Node parent = new Node(prn, parentPath);
266: final String finalFileName = childName;
267: try {
268: // wait for max. 30 seconds for the file node to appear
269: JemmyProperties.setCurrentTimeout("Waiter.WaitingTime",
270: 30000);
271: new Waiter(new Waitable() {
272: public Object actionProduced(Object parent) {
273: return ((Node) parent)
274: .isChildPresent(finalFileName) ? Boolean.TRUE
275: : null;
276: }
277:
278: public String getDescription() {
279: return ("Waiting for the tree to load.");
280: }
281: }).waitAction(parent);
282: } catch (InterruptedException e) {
283: throw new JemmyException("Interrupted.", e);
284: }
285: }
286:
287: /** Open the default file in open project */
288: public void openFile() {
289: openFile(defaultPackageNameTreePath, defaultFileName);
290: }
291:
292: /** Close file in open project.
293: */
294: public void closeFile() {
295: try {
296: new EditorOperator(fileName).close();
297: } catch (TimeoutExpiredException ex) {
298: log(ex.getMessage());
299: log("Can't close the file");
300: }
301: }
302:
303: /** Close file in open project.
304: */
305: public void closeFileWithSave() {
306: try {
307: new EditorOperator(fileName).close(true);
308: } catch (TimeoutExpiredException ex) {
309: log(ex.getMessage());
310: log("Can't close the file");
311: }
312: }
313:
314: /** Close file in open project.
315: */
316: public void closeFileWithDiscard() {
317: try {
318: new EditorOperator(fileName).closeDiscard();
319: } catch (TimeoutExpiredException ex) {
320: log(ex.getMessage());
321: log("Can't close the file");
322: }
323: }
324:
325: /** Close dialog with added title
326: * @param title dialog title */
327: public void closeDialog(String title) {
328: NbDialogOperator dialog = new NbDialogOperator(title);
329: dialog.closeByButton();
330: }
331:
332: /**
333: * Write the text of the passed document to the ref file
334: * and compare the created .ref file with the golden file.
335: * <br>
336: * If the two files differ the test fails and generates the diff file.
337: *
338: * @param testDoc document to be written to the .ref file and compared.
339: */
340: protected void compareReferenceFiles(Document testDoc) {
341: try {
342: ref(testDoc.getText(0, testDoc.getLength()));
343: compareReferenceFiles();
344: } catch (BadLocationException e) {
345: e.printStackTrace(getLog());
346: fail();
347: }
348: }
349:
350: /**
351: * Open a source file located in the "Source packages" in the editor.
352: *
353: * @param dir directory path with "|" separator.
354: * @param srcName source name without suffix.
355: */
356: protected void openSourceFile(String dir, String srcName) {
357: openFile(org.netbeans.jellytools.Bundle.getString(
358: "org.netbeans.modules.java.j2seproject.Bundle",
359: "NAME_src.dir")
360: + treeSeparator + dir, srcName);
361: }
362:
363: protected final String getDefaultSamplePackage() {
364: return defaultSamplePackage;
365: }
366:
367: protected final String getDefaultSampleName() {
368: return defaultSampleName;
369: }
370:
371: protected void openDefaultSampleFile() {
372: openSourceFile(defaultSamplePackage, defaultSampleName);
373: }
374:
375: protected EditorOperator getDefaultSampleEditorOperator() {
376: return new EditorOperator(defaultSampleName);
377: }
378:
379: /** Method will wait max. <code> maxMiliSeconds </code> miliseconds for the <code> requiredValue </code>
380: * gathered by <code> resolver </code>.
381: *
382: * @param maxMiliSeconds maximum time to wait for requiredValue
383: * @param resolver resolver, which is gathering an actual value
384: * @param requiredValue if resolver value equals requiredValue the wait cycle is finished
385: *
386: * @return false if the given maxMiliSeconds time elapsed and the requiredValue wasn't obtained
387: */
388: protected boolean waitMaxMilisForValue(int maxMiliSeconds,
389: ValueResolver resolver, Object requiredValue) {
390: int time = (int) maxMiliSeconds / 100;
391: while (time > 0) {
392: Object resolvedValue = resolver.getValue();
393: if (requiredValue == null && resolvedValue == null) {
394: return true;
395: }
396: if (requiredValue != null
397: && requiredValue.equals(resolvedValue)) {
398: return true;
399: }
400: try {
401: Thread.currentThread().sleep(100);
402: } catch (InterruptedException ex) {
403: time = 0;
404: }
405: time--;
406: }
407: return false;
408: }
409:
410: /** Interface for value resolver needed for i.e. waitMaxMilisForValue method.
411: * For more details, please look at {@link #waitMaxMilisForValue()}.
412: */
413: public static interface ValueResolver {
414: /** Returns checked value */
415: Object getValue();
416: }
417:
418: protected ValueResolver getClipboardResolver(
419: final JEditorPaneOperator txtOper, final int key,
420: final int mod, final String oldVal) {
421:
422: ValueResolver clipboardValueResolver = new ValueResolver() {
423: public Object getValue() {
424: txtOper.pushKey(key, mod);
425: Transferable newClipValue = txtOper.getToolkit()
426: .getSystemClipboard().getContents(txtOper);
427: String newVal = getClipBoardContent(newClipValue);
428: log("newClipValue:" + newVal);
429: return (newVal.equals(oldVal)) ? Boolean.TRUE
430: : Boolean.FALSE;
431: }
432: };
433:
434: return clipboardValueResolver;
435: }
436:
437: protected ValueResolver getTextFieldResolver(
438: final JTextFieldOperator oper, final String newValue) {
439: ValueResolver textFieldResolver = new ValueResolver() {
440: public Object getValue() {
441: String actVal = oper.getText();
442: log("actual value:" + actVal);
443: return (newValue.equals(actVal)) ? Boolean.TRUE
444: : Boolean.FALSE;
445: }
446: };
447:
448: return textFieldResolver;
449: }
450:
451: private String getClipBoardContent(Transferable clip) {
452: DataFlavor[] df = clip.getTransferDataFlavors();
453: for (int i = 0; i < df.length; i++) {
454: DataFlavor dataFlavor = df[i];
455: log("Mime: " + dataFlavor.getMimeType());
456: log("Class: "
457: + dataFlavor.getRepresentationClass().getName());
458: log("Class2: "
459: + dataFlavor.getDefaultRepresentationClass()
460: .getName());
461: try {
462: log(clip.getTransferData(dataFlavor).getClass()
463: .getName());
464: } catch (IOException ioe) {
465:
466: } catch (UnsupportedFlavorException ufe) {
467:
468: }
469: }
470: if (df.length == 0)
471: return "n/a";
472: String val = null;
473: try {
474: val = (String) clip.getTransferData(new DataFlavor(
475: "text/plain; class=java.lang.String"));
476: } catch (UnsupportedFlavorException ex) {
477: fail(ex);
478: } catch (IOException ex) {
479: fail(ex);
480: } catch (ClassNotFoundException ex) {
481: fail(ex);
482: }
483: return val;
484: }
485:
486: protected void cutCopyViaStrokes(JEditorPaneOperator txtOper,
487: int key, int mod) {
488: Transferable oldClipValue = txtOper.getToolkit()
489: .getSystemClipboard().getContents(txtOper);
490: String oldVal = getClipBoardContent(oldClipValue);
491: log("");
492: log("oldClipValue:" + oldVal);
493: txtOper.requestFocus();
494: txtOper.pushKey(key, mod);
495: // give max WAIT_MAX_MILIS_FOR_CLIPBOARD milis for clipboard to change
496: boolean success = waitMaxMilisForValue(
497: WAIT_MAX_MILIS_FOR_CLIPBOARD, getClipboardResolver(
498: txtOper, key, mod, oldVal), Boolean.FALSE);
499: if (success == false) {
500: // give it one more chance. maybe selection was not ready at the time of
501: // copying
502: log("!!!! ONCE AGAIN");
503: txtOper.pushKey(key, mod);
504: // give max WAIT_MAX_MILIS_FOR_CLIPBOARD milis for clipboard to change
505: waitMaxMilisForValue(WAIT_MAX_MILIS_FOR_CLIPBOARD,
506: getClipboardResolver(txtOper, key, mod, oldVal),
507: Boolean.FALSE);
508: }
509: }
510:
511: protected void pasteViaStrokes(ComponentOperator compOper, int key,
512: int mod, ValueResolver resolver) {
513: compOper.pushKey(key, mod);
514: // give max WAIT_MAX_MILIS_FOR_CLIPBOARD milis for clipboard to change
515: if (resolver != null) {
516: boolean success = waitMaxMilisForValue(
517: WAIT_MAX_MILIS_FOR_CLIPBOARD, resolver,
518: Boolean.TRUE);
519: if (success == false) {
520: // give it one more chance.
521: compOper.pushKey(key, mod);
522: // give max WAIT_MAX_MILIS_FOR_CLIPBOARD milis for clipboard to change
523: waitMaxMilisForValue(WAIT_MAX_MILIS_FOR_CLIPBOARD,
524: resolver, Boolean.TRUE);
525: }
526: }
527: }
528:
529: }
|