/************************************************************** * * 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 complex.framework.recovery; import com.sun.star.accessibility.XAccessible; import com.sun.star.accessibility.XAccessibleContext; import com.sun.star.awt.Rectangle; import com.sun.star.awt.Size; import com.sun.star.awt.XDialog; import com.sun.star.awt.XExtendedToolkit; import com.sun.star.awt.XWindow; import com.sun.star.beans.XPropertySet; import com.sun.star.container.XEnumeration; import com.sun.star.container.XIndexAccess; import com.sun.star.container.XNameContainer; import com.sun.star.drawing.XDrawPage; import com.sun.star.drawing.XDrawPages; import com.sun.star.drawing.XDrawPagesSupplier; import com.sun.star.drawing.XShape; import com.sun.star.drawing.XShapes; import com.sun.star.frame.XController; import com.sun.star.frame.XDesktop; import com.sun.star.frame.XDispatch; import com.sun.star.frame.XDispatchProvider; import com.sun.star.frame.XFrame; import com.sun.star.frame.XModel; import com.sun.star.lang.XComponent; import com.sun.star.lang.XMultiServiceFactory; import com.sun.star.lang.XSingleServiceFactory; import com.sun.star.presentation.XCustomPresentationSupplier; import com.sun.star.presentation.XPresentationSupplier; import com.sun.star.sheet.XSheetCellRange; import com.sun.star.sheet.XSpreadsheet; import com.sun.star.sheet.XSpreadsheetDocument; import com.sun.star.sheet.XSpreadsheets; import com.sun.star.style.XStyle; import com.sun.star.table.XCellRange; import com.sun.star.text.ControlCharacter; import com.sun.star.text.XText; import com.sun.star.text.XTextCursor; import com.sun.star.text.XTextDocument; import com.sun.star.uno.AnyConverter; import com.sun.star.uno.Type; import com.sun.star.uno.UnoRuntime; import com.sun.star.uno.XInterface; import com.sun.star.view.XSelectionSupplier; import complexlib.ComplexTestCase; import helper.OfficeProvider; import helper.OfficeWatcher; import java.awt.Dimension; import java.awt.Point; import java.awt.Toolkit; import java.io.PrintWriter; import java.util.Hashtable; import java.util.Random; import util.DesktopTools; import util.PropertyName; import util.SOfficeFactory; import util.UITools; import util.utils; // ---------- junit imports ----------------- import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.openoffice.test.OfficeConnection; import static org.junit.Assert.*; // ------------------------------------------ public class RecoveryTest extends ComplexTestCase { static XMultiServiceFactory xMSF; static SOfficeFactory SOF; static RecoveryTools rt; /** * If you divide the screen in four parts in the first of them the office * windows should be placed. The range of the first quarter is stored in the variable. */ static Point windowMaxPosition; /** * The office windows starts in the first quarter of the screen. In this variable * the maximum size for the windows was stored so the windows can be placed * visible on the screen. */ static Size windowMaxSize; /** * All office windows will be placed by this test on randomized positions. * This positions was stored in this Hashmap. The keys are the frame names * and the values are com sun.star.awt.Rectangle. * @see com.sun.star.awt.Rectangle */ private Hashtable windowsPosSize = new Hashtable(); /** * A function to tell the framework, which test functions are available. * @return All test methods. * * @todo: hidden documents * @todo: running presentation * @todo: modular dialogs like Hyperlink-Dialog * @todo: second view of a document * @todo: remove recovery data before start test * @todo: after a second start after the crash there should no documents recovered anymore * @todo: enable remove of recovery files * @todo: modify makefile to check for parameters */ public String[] getTestMethodNames() { return new String[]{"testCrash"}; } /** Create the environment for following tests. * Use either a component loader from desktop or * from frame * @throws Exception Exception */ public void normalCrash(){ cleanRecoveryData(); startOffice(); generateDesktop(); makeCrash(); int expectedDocumentCount = windowsPosSize.size() + 1; handleRecoveryDialogAfterCrash(expectedDocumentCount); startOffice(); handleRecoveryDialog_QuickExit(expectedDocumentCount); handleCrashReporterDialog(true, true); checkDocumentCount(expectedDocumentCount); } public void testCrash(){ cleanRecoveryData(); restoreBackupRecoveryData(); startOffice(); int expectedDocumentCount = 3; // handleRecoveryDialog_QuickExit(expectedDocumentCount); handleRecoveryDialog_QuickExitAndSave(expectedDocumentCount); //handleCrashReporterDialog(true, true); //checkDocumentCount(expectedDocumentCount); } public void before() throws Exception { String msg ="\n\n\tPATH TO OFFICE BINARY MISSING!\n"; msg +="\tPlease run your command with the following parameter:\n\n"; msg +="\t-AppExecutionCommand=OFFICEBINARY CONNECTIONSTRING\n\n"; msg +="Example Windows:\n"; msg +="-AppExecutionCommand=C:\\office\\soffice.exe -accept=socket,host=localhost,port=8101;urp;\n\n"; msg +="Example UNIX:\n"; msg +="-AppExecutionCommand=/office/soffice \"-accept=socket,host=localhost,port=8101;urp;\"\n\n"; msg+="NOTE: on UNIX be sure to have the connection string inside quotation mark!\n"; assure(msg, param.get("AppExecutionCommand") != null && ! param.get("AppExecutionCommand").equals("")); System.out.println("HALLO" + param.get("AppExecutionCommand")); msg = "\n\nONE PARAMETER IS MISSING!\n"; msg += "Please append to your command the following parameter:\n\n"; msg += "\t-NoOffice=true"; assure(msg, param.getBool("NoOffice")); rt = new RecoveryTools(param ,log); rt.removeParametersFromAppExecutionCommand(); log.println("start the office to test recovery feature..."); // make window ranges makeWindowPositionRage(); //makeRecoveryData(); } private void makeRecoveryData(){ cleanRecoveryData(); startOffice(); generateDesktop(); makeCrash(); int expectedDocumentCount = windowsPosSize.size() + 1; handleRecoveryDialogAfterCrash(expectedDocumentCount); backupRecoveryData(); cleanRecoveryData(); } private void startOffice(){ assure("Could not connect to office", connect()); log.setWatcher(param.get("Watcher")); } private void checkDocumentCount(int expectedDocumentCount){ XEnumeration allComp = DesktopTools.getAllComponents(xMSF); int documentCount = 0; try{ while (allComp.hasMoreElements()){ allComp.nextElement(); documentCount ++; } } catch ( com.sun.star.container.NoSuchElementException e){} catch ( com.sun.star.lang.WrappedTargetException e){} String msg ="The amount of documents to recover is different form the expected amount:\n"; msg += "\texpected:\t" + expectedDocumentCount + "\n"; msg += "\tto recover:\t" + documentCount; assure(msg, expectedDocumentCount == documentCount); } /** * This function starts an office instance. It uses the AppExecutionCommad parameter. * @return TRUE if office is connected otherwise FALSE */ private boolean connect(){ try { OfficeProvider oProvider = new OfficeProvider(); xMSF = (XMultiServiceFactory)oProvider.getManager(param); SOF = SOfficeFactory.getFactory(xMSF); } catch (java.lang.Exception e) { log.println(e.getClass().getName()); log.println("Message: " + e.getMessage()); failed("Cannot connect the office."); return false; } return true; } /** * While creating the test environment the positions and sizes of the frames * was saved. After the office has recovered the documents, this functions * compares the saved positions and sizes with the current frame. */ private void compareWindowPositions(){ System.out.println("all frames:########"); System.out.println(windowsPosSize.entrySet().toString()); XEnumeration allComp = DesktopTools.getAllComponents(xMSF); String msg=null; while (allComp.hasMoreElements()){ try{ // get all components from the desktop XComponent xComponent = (XComponent) UnoRuntime.queryInterface( XComponent.class, allComp.nextElement()); XModel xModel = (XModel) UnoRuntime.queryInterface(XModel.class, xComponent); String frameName = xModel.getCurrentController().getFrame().getName(); // check if this frame was used in creation of test environment if (windowsPosSize.containsKey(frameName)){ Rectangle oldRect = (Rectangle) windowsPosSize.get(frameName); XWindow xWindow = xModel.getCurrentController().getFrame().getContainerWindow(); Rectangle newRect = xWindow.getPosSize(); boolean ok = oldRect.Height == newRect.Height; ok &= oldRect.Width == newRect.Width; ok &= oldRect.X == newRect.X; ok &= oldRect.Y == newRect.Y; if (!ok){ msg = "The frame '" + frameName + "' has a different position/size:\n"; msg += "original value -> restored value:\n"; msg += "X : " + oldRect.X + " -> " + newRect.X + "\n"; msg += "Y : " + oldRect.Y + " -> " + newRect.Y + "\n"; msg += "Height: " + oldRect.Height + " -> " + newRect.Height + "\n"; msg += "Width : " + oldRect.Width + " -> " + newRect.Width + "\n"; } assure(msg, ok, CONTINUE); } } catch (com.sun.star.container.NoSuchElementException e) { } catch ( com.sun.star.lang.WrappedTargetException e) {} } } /** * This function crashes the office */ private void makeCrash(){ // get all documents Object[] allDocs = DesktopTools.getAllOpenDocuments(xMSF); // get one of them for dispatching XComponent xDoc = (XComponent) allDocs[0]; log.println("make the crash in second thread"); CrashThread crash = new CrashThread(xDoc, xMSF); crash.start(); rt.pause(); rt.pause(); } /** * This function uses accessibility to handle the dialog which appears while the * office is crashed. It click the button "OK" to continue. */ private void handleRecoveryDialogAfterCrash(int expectedDocumentCount){ try{ // if the office crashes, the recovery feature needs some time // to save all docs. Therefore the recovery dialog could need some // time to pop up. log.println("waiting for recovery dialog..."); int counter = 0; int maximum = param.getInt(PropertyName.THREAD_TIME_OUT) / param.getInt(PropertyName.SHORT_WAIT); XDialog oDialog = rt.getActiveDialog(xMSF); while ( oDialog == null && (counter < maximum)) { rt.pause(); oDialog = rt.getActiveDialog(xMSF); counter ++; } assure("could not get Recovery Window",(oDialog != null)); XWindow xWindow = (XWindow) UnoRuntime.queryInterface(XWindow.class, oDialog); UITools oUITools = new UITools(xMSF, xWindow); oUITools.printAccessibleTree((PrintWriter) log, param.getBool(PropertyName.DEBUG_IS_ACTIVE)); String[] documents = oUITools.getListBoxItems("The following files will be recovered"); log.println("there are " + documents.length + " documents to save"); String msg ="The amount of documents to recover is different form the expected amount:\n"; msg += "\texpected:\t" + expectedDocumentCount + "\n"; msg += "\tto recover:\t" + documents.length; assure(msg, expectedDocumentCount == documents.length); log.println("disable automatically launch of Office"); oUITools.setCheckBoxValue("Launch OpenOffice automatically", new Integer(0)); log.println("start saving..."); oUITools.clickButton("OK"); rt.waitForClosedOffice(); } catch (Exception e){ e.printStackTrace(); failed("Could not handle crash-dialog: " + e.toString()); } } private void handleCrashReporterDialog(boolean cancel, boolean YesNo){ try{ log.println("try to get Crash Reporter Dialog..."); XDialog oDialog = rt.getActiveDialog(xMSF); assure("could not get CrashReporter Dialog", oDialog != null); XWindow xWindow = (XWindow) UnoRuntime.queryInterface(XWindow.class, oDialog); log.println(oDialog.getTitle()); UITools oUITools = new UITools(xMSF, xWindow); if (cancel) { log.println("clicking 'Cancel' button..."); try{ rt.clickThreadButton(xMSF, xWindow, "Cancel"); } catch (com.sun.star.accessibility.IllegalAccessibleComponentStateException e){ failed("Could not click 'Cancel' at CrashReporter Dialog"); } } else { log.println("clicking 'Next' button..."); oUITools.clickButton("Next >"); } } catch (Exception e){ failed("Could not handle CrashReporter Dialog: " + e.toString()); } } private void handleRecoveryDialog_QuickExit(int expectedDocumentCount){ log.println("handle Recovery Dialog at restart: quick exit"); handleRecoveryDialogAtRestart(expectedDocumentCount, false, true); handleAreYouSureDialog(true); handleSaveDocumentsDialog(false); } private void handleRecoveryDialog_QuickExitAndSave(int expectedDocumentCount){ log.println("handle Recovery Dialog at restart: quick exit"); handleRecoveryDialogAtRestart(expectedDocumentCount, false, true); handleAreYouSureDialog(true); handleSaveDocumentsDialog(true); } private void handleRecoveryDialog_Recover(int expectedDocumentCount){ } private void handleRecoveryDialog_RecoverAndCrashreporter(int expectedDocumentCount){ } /** * This function uses accessibility to handle the dialog which appears while the * office is started after a crash. It waits until the "Next" button is enabled * and click it then to continue. * @param expectedDocumentCount the amount of documents which must be displayed in the recovery dialog * @param recover If the document should be recovered this variable must be true. If it is false * the recovery process was stopped and the button cancel was clicked. * @param cancel If the recovery is finished, this parameter decides to click the "Next" button * or the click cancel. If the value is true, the cancel button was clicked. */ private void handleRecoveryDialogAtRestart(int expectedDocumentCount, boolean recover, boolean cancel){ try{ log.println("try to get Recovery Dialog..."); XDialog oDialog = null; oDialog = rt.getActiveDialogAfterStartup(xMSF); assure("could not get Recovery Dialog at start of office", (oDialog != null), CONTINUE); XWindow xWindow = (XWindow) UnoRuntime.queryInterface(XWindow.class, oDialog); log.println("got the following dialog: '" +oDialog.getTitle() + "'"); UITools oUITools = new UITools(xMSF, xWindow); String listBoxName = "Status of recovered documents"; String[] documents = oUITools.getListBoxItems(listBoxName); log.println("there are " + documents.length + " documents to recover"); log.println("The following files will be recovered:"); for (int i=0;i"); rt.pause(); //XAccessibleContext oButton = oUITools.getButton("Start Recovery >"); int counter = 0; int maximum = param.getInt(PropertyName.THREAD_TIME_OUT) / param.getInt(PropertyName.SHORT_WAIT); //boolean enabeld = oButton.getAccessibleStateSet().contains(com.sun.star.accessibility.AccessibleStateType.ENABLED); XAccessibleContext oButton = null; while ((oButton == null) && (counter < maximum)){ log.println("recovering..."); try{ oButton = oUITools.getButton("Next >"); } catch (java.lang.NullPointerException e){ // no fault: The title "Start Recovery" switches to "Next" // while all documents are recovered } rt.pause(); counter++; } if (cancel) { log.println("clicking 'Cancel' button..."); try{ rt.clickThreadButton(xMSF, xWindow, "Cancel"); } catch (com.sun.star.accessibility.IllegalAccessibleComponentStateException e){ failed("Could not click 'Cancel' at recovery-dialog."); } } else { log.println("clicking 'Next' button..."); oUITools.clickButton("Next >"); } rt.pause(); } else { log.println("do not recover: clicking 'Cancel' button..."); try{ rt.clickThreadButton(xMSF, xWindow, "Cancel"); } catch (com.sun.star.accessibility.IllegalAccessibleComponentStateException e){ failed("Could not click 'Cancel' at recovery-dialog"); } } } catch (Exception e){ failed("Could not handle recovery-dialog at restart: " + e.toString()); } } /** * This function uses accessibility to handle the dialog "Are you sure". * It click "Yes" or "No", depending on the value of the parameter Yes * @param yes If value is TRUE the button "Yes" was clicked, otherwise the button * "No". */ private void handleAreYouSureDialog(boolean yes) { try{ if (yes){ rt.handleModalDialog(xMSF, "Yes"); } else{ rt.handleModalDialog(xMSF, "Cancel"); } } catch (com.sun.star.accessibility.IllegalAccessibleComponentStateException e){ failed("Could not handle 'Are you sure' dialog."); } } /** * This function uses accessibility to handle the dialog "Are you sure". * It click "Yes" or "No", depending on the value of the parameter Yes * @param saveDocuments If value is TRUE the button "Yes" was clicked, otherwise the button * "No". */ private void handleSaveDocumentsDialog(boolean saveDocuments) { try{ if (!saveDocuments){ rt.handleModalDialog(xMSF, "Cancel"); } else{ XWindow oDialog = null; oDialog = rt.getActiveWindow(xMSF); assure("could not get 'Save Documents' Dialog: ", (oDialog != null), CONTINUE); UITools oUITools = new UITools(xMSF, oDialog); oUITools.printAccessibleTree((PrintWriter) log, param.getBool(PropertyName.DEBUG_IS_ACTIVE)); String listBoxName = "Documents"; String[] documents = null; try{ documents = oUITools.getListBoxItems(listBoxName); } catch (java.lang.Exception e){ failed("could not get the document names from the 'Save Documents' dialog", CONTINUE); } log.println("there are " + documents.length + " documents to save"); log.println("The following documents will be saved:"); for (int i=0;i