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