diff options
Diffstat (limited to 'framework/qa/complex/framework')
10 files changed, 3325 insertions, 0 deletions
diff --git a/framework/qa/complex/framework/autosave/AutoSave.java b/framework/qa/complex/framework/autosave/AutoSave.java new file mode 100644 index 000000000000..015d9771be59 --- /dev/null +++ b/framework/qa/complex/framework/autosave/AutoSave.java @@ -0,0 +1,454 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package complex.framework.autosave; + +import com.sun.star.frame.*; +import com.sun.star.lang.*; +import com.sun.star.util.*; +import com.sun.star.beans.*; +import com.sun.star.uno.*; +import com.sun.star.sheet.*; +import com.sun.star.table.*; + +import java.util.*; + +import complexlib.*; +import helper.*; +import util.*; + +//----------------------------------------------- +/** @short Check some use cases of the AutoSave feature + */ +public class AutoSave extends ComplexTestCase +{ + //------------------------------------------- + class AutoSaveListener implements XStatusListener + { + private XDispatch m_xAutoSave; + private URL m_aRegistration; + private Protocol m_aLog; + + public AutoSaveListener(XMultiServiceFactory xSMGR , + XDispatch xAutoSave, + Protocol aLog ) + { + m_aLog = aLog; + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "create listener for AutoSave notifications ..."); + + try + { + m_xAutoSave = xAutoSave; + + XURLTransformer xParser = (XURLTransformer)UnoRuntime.queryInterface( + XURLTransformer.class, + xSMGR.createInstance("com.sun.star.util.URLTransformer")); + URL[] aURL = new URL[1]; + aURL[0] = new URL(); + aURL[0].Complete = "vnd.sun.star.autorecovery:/doAutoSave"; + xParser.parseStrict(aURL); + m_aRegistration = aURL[0]; + + m_xAutoSave.addStatusListener(this, m_aRegistration); + m_aLog.log(Protocol.TYPE_INFO, "successfully registered as AutoSave listener."); + } + catch(Throwable ex) + { + m_aLog.log(ex); + } + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, ""); + } + + public void disableListener() + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "stop listening for AutoSave notifications ..."); + + XDispatch xAutoSave = null; + URL aRegURL = null; + synchronized (this) + { + xAutoSave = m_xAutoSave; + aRegURL = m_aRegistration; + } + + try + { + if ( + (xAutoSave != null) && + (aRegURL != null) + ) + xAutoSave.removeStatusListener(this, aRegURL); + } + catch(Throwable ex) + { + m_aLog.log(ex); + } + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, ""); + } + + public void statusChanged(FeatureStateEvent aEvent) + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "statusChanged() called from AutoSave ..."); + + m_aLog.log("FeatureURL = \""+aEvent.FeatureURL.Complete+"\"" ); + m_aLog.log("FeatureDescriptor = \""+aEvent.FeatureDescriptor+"\"" ); + m_aLog.log("IsEnabled = \""+aEvent.IsEnabled+"\"" ); + m_aLog.log("Requery = \""+aEvent.Requery+"\"" ); + m_aLog.log("State:" ); + m_aLog.log(aEvent.State ); + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, ""); + } + + public void disposing(com.sun.star.lang.EventObject aEvent) + { + m_aLog.log(Protocol.TYPE_INFO, "disposing() called from AutoSave."); + synchronized(this) + { + m_xAutoSave = null; + m_aRegistration = null; + } + } + } + + //------------------------------------------- + // some const + + //------------------------------------------- + // member + + private Protocol m_aLog; + + /** points to the global uno service manager. */ + private XMultiServiceFactory m_xSMGR = null; + + private SOfficeFactory m_aSOF; + + /** can be used to trigger/enable/disable the AutoSave feature. */ + private XDispatch m_xAutoSave = null; + + /** a test document, which needs some time for saving to simulate concurrent + * save operations. */ + private XStorable m_xTestDoc = null; + + private XURLTransformer m_xURLParser = null; + + //------------------------------------------- + // test environment + + //------------------------------------------- + /** @short A function to tell the framework, + which test functions are available. + + @return All test methods. + @todo Think about selection of tests from outside ... + */ + public String[] getTestMethodNames() + { + return new String[] + { + "checkConcurrentAutoSaveToNormalUISave", + }; + } + + //------------------------------------------- + /** @short Create the environment for following tests. + + @descr create an empty test frame, where we can load + different components inside. + */ + public void before() + { + m_aLog = new Protocol(Protocol.MODE_HTML | Protocol.MODE_STDOUT, Protocol.FILTER_NONE, utils.getUsersTempDir() + "/complex_log_ascii_01.html"); + + try + { + // get uno service manager from global test environment + m_xSMGR = (XMultiServiceFactory)param.getMSF(); + + // get another helper to e.g. create test documents + m_aSOF = SOfficeFactory.getFactory(m_xSMGR); + + // create AutoSave instance + m_xAutoSave = (XDispatch)UnoRuntime.queryInterface( + XDispatch.class, + m_xSMGR.createInstance("com.sun.star.comp.framework.AutoRecovery")); + + // prepare AutoSave + // make sure it will be started every 1 min + ConfigHelper aConfig = new ConfigHelper(m_xSMGR, "org.openoffice.Office.Recovery", false); + aConfig.writeRelativeKey("AutoSave", "Enabled" , Boolean.TRUE ); + aConfig.writeRelativeKey("AutoSave", "TimeIntervall", new Integer(1)); // 1 min + aConfig.flush(); + aConfig = null; + + // is needed to parse dispatch commands + m_xURLParser = (XURLTransformer)UnoRuntime.queryInterface( + XURLTransformer.class, + m_xSMGR.createInstance("com.sun.star.util.URLTransformer")); + + } + catch(java.lang.Throwable ex) + { + m_aLog.log(ex); + failed("Couldn't create test environment"); + } + } + + //------------------------------------------- + /** @short close the environment. + */ + public void after() + { + // ??? + } + + //------------------------------------------- + // create a calc document with content, which needs some time for saving + private XInterface createBigCalcDoc() + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "createBigCalcDoc() started ..."); + try + { + m_aLog.log("Create empty calc document for testing."); + XSpreadsheetDocument xSheetDoc = m_aSOF.createCalcDoc("_default"); + m_aLog.log("Retrieve first sheet from calc document."); + XSpreadsheets xSheets = xSheetDoc.getSheets(); + XSpreadsheet xSheet = (XSpreadsheet)AnyConverter.toObject( + new Type(XSpreadsheet.class), + xSheets.getByName( + xSheets.getElementNames()[0])); + m_aLog.log("Fill two cells with value and formula."); + xSheet.getCellByPosition(0, 0).setValue(1); + xSheet.getCellByPosition(0, 1).setFormula("=a1+1"); + m_aLog.log("Retrieve big range."); + XCellRange xRange = xSheet.getCellRangeByName("A1:Z9999"); + XCellSeries xSeries = (XCellSeries)UnoRuntime.queryInterface( + XCellSeries.class, + xRange); + m_aLog.log("Duplicate cells from top to bottom inside range."); + xSeries.fillAuto(FillDirection.TO_BOTTOM, 2); + m_aLog.log("Duplicate cells from left to right inside range."); + xSeries.fillAuto(FillDirection.TO_RIGHT , 1); + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE | Protocol.TYPE_OK, "createBigCalcDoc() finished."); + return xSheetDoc; + } + catch(Throwable ex) + { + m_aLog.log(ex); + } + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "createBigCalcDoc() finished."); + return null; + } + + //------------------------------------------- + private void saveDoc(XInterface xDoc, + String sURL) + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "saveDoc('"+sURL+"') started ..."); + try + { + URL[] aURL = new URL[1]; + aURL[0] = new URL(); + aURL[0].Complete = ".uno:SaveAs"; + m_xURLParser.parseStrict(aURL); + + XModel xModel = (XModel)UnoRuntime.queryInterface( + XModel.class, + xDoc); + XDispatchProvider xProvider = (XDispatchProvider)UnoRuntime.queryInterface( + XDispatchProvider.class, + xModel.getCurrentController()); + XDispatch xDispatch = xProvider.queryDispatch(aURL[0], "_self", 0); + + PropertyValue[] lArgs = new PropertyValue[3]; + lArgs[0] = new PropertyValue(); + lArgs[0].Name = "URL"; + lArgs[0].Value = sURL; + lArgs[1] = new PropertyValue(); + lArgs[1].Name = "Overwrite"; + lArgs[1].Value = Boolean.TRUE; + lArgs[2] = new PropertyValue(); + lArgs[2].Name = "StoreTo"; + lArgs[2].Value = Boolean.TRUE; + + xDispatch.dispatch(aURL[0], lArgs); + + m_aLog.log(Protocol.TYPE_OK, "saveDoc('"+sURL+"') = OK."); + } +/* + catch(com.sun.star.io.IOException exIO) + { + m_aLog.log(Protocol.TYPE_WARNING , "got IOException on calling doc.store()." ); + m_aLog.log(Protocol.TYPE_WARNING_INFO, "Please check the reason for that more in detail." ); + m_aLog.log(Protocol.TYPE_WARNING_INFO, "A message like \"Concurrent save requests are not allowed.\" was intended and doesnt show an error!"); + m_aLog.log(Protocol.TYPE_WARNING_INFO, "Message of the original exception:" ); + m_aLog.log(Protocol.TYPE_WARNING_INFO, exIO.getMessage()); + } +*/ + catch(Throwable ex) + { + m_aLog.log(ex); + } + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "saveDoc('"+sURL+"') finished."); + } + + //------------------------------------------- + private void closeDoc(XInterface xDoc) + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "closeDoc() started ..."); + + try + { + Random aRandom = new Random(); + int nRetry = 5; + while(nRetry>0) + { + try + { + XCloseable xClose = (XCloseable)UnoRuntime.queryInterface( + XCloseable.class, + xDoc); + if (xClose != null) + { + xClose.close(false); + m_aLog.log(Protocol.TYPE_OK, "closeDoc() = OK."); + nRetry = 0; + } + else + m_aLog.log(Protocol.TYPE_ERROR, "closeDoc() = ERROR. Doc doesnt provide needed interface!"); + } + catch(com.sun.star.util.CloseVetoException exVeto) + { + m_aLog.log(Protocol.TYPE_WARNING , "got CloseVetoException on calling doc.close()." ); + m_aLog.log(Protocol.TYPE_WARNING_INFO, "Please check the reason for that more in detail." ); + m_aLog.log(Protocol.TYPE_WARNING_INFO, "A message like \"Cant close while saving.\" was intended and doesnt show an error!"); + m_aLog.log(Protocol.TYPE_WARNING_INFO, exVeto.getMessage()); + } + + if (nRetry > 0) + { + --nRetry; + long nWait = (long)aRandom.nextInt(30000); // 30 sec. + try + { + m_aLog.log(Protocol.TYPE_INFO, "sleep for "+nWait+" ms"); + synchronized(this) + { + wait(nWait); + } + } + catch(Throwable ex) + { + m_aLog.log(Protocol.TYPE_WARNING , "got exception for wait() !?"); + m_aLog.log(Protocol.TYPE_WARNING_INFO, ex.getMessage()); + } + } + } + } + catch(Throwable ex) + { + m_aLog.log(ex); + } + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "closeDoc() finished."); + } + + class DocThread extends Thread + { + DocThread() + {} + + public void run() + { + impl_checkConcurrentAutoSaveToNormalUISave(); + } + } + + //------------------------------------------- + /** @short check concurrent save requests to the same document + * at the same time. + * + * @descr First we simulate an UI save by dispatching the right URL + * to the document and at the same time we try to trigger an AutoSave + * from another thread. So these operations should be started at the same time. + * It should not crash. The AutoSave request must be postphoned. + */ + public void checkConcurrentAutoSaveToNormalUISave() + { + m_aLog.log(Protocol.TYPE_TESTMARK , "AutoSave"); + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "checkConcurrentAutoSaveToNormalUISave()"); + + AutoSaveListener xListener = new AutoSaveListener(m_xSMGR, m_xAutoSave, m_aLog); + + try + { + DocThread aThread = new DocThread(); + aThread.start(); + aThread.join(); + } + catch(Throwable ex) + {} + + xListener.disableListener(); + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "checkConcurrentAutoSaveToNormalUISave()"); + m_aLog.logStatistics(); + } + + public void impl_checkConcurrentAutoSaveToNormalUISave() + { + Random aRandom = new Random(); + + int i = 0; + int c = 5; + for (i=0; i<c; ++i) + { + XInterface xDoc = createBigCalcDoc(); + try + { + long nWait = (long)aRandom.nextInt(120000); + m_aLog.log(Protocol.TYPE_INFO, "sleep for "+nWait+" ms"); + synchronized(this) + { + wait(nWait); + } + } + catch(Throwable ex) + { + m_aLog.log(Protocol.TYPE_WARNING , "got exception for wait() !?"); + m_aLog.log(Protocol.TYPE_WARNING_INFO, ex.getMessage()); + } + saveDoc(xDoc, utils.getOfficeTemp(m_xSMGR) + "/test_calc.ods"); + closeDoc(xDoc); + } + } +} diff --git a/framework/qa/complex/framework/autosave/ConfigHelper.java b/framework/qa/complex/framework/autosave/ConfigHelper.java new file mode 100644 index 000000000000..3bc4683f4fb5 --- /dev/null +++ b/framework/qa/complex/framework/autosave/ConfigHelper.java @@ -0,0 +1,124 @@ +package complex.framework.autosave; + +import java.lang.*; +import com.sun.star.uno.*; +import com.sun.star.lang.*; +import com.sun.star.container.*; +import com.sun.star.beans.*; +import com.sun.star.util.*; + +class ConfigHelper +{ + private XMultiServiceFactory m_xSMGR = null; + private XHierarchicalNameAccess m_xConfig = null; + + //----------------------------------------------- + public ConfigHelper(XMultiServiceFactory xSMGR , + String sConfigPath , + boolean bReadOnly ) + throws com.sun.star.uno.Exception + { + m_xSMGR = xSMGR; + + XMultiServiceFactory xConfigRoot = (XMultiServiceFactory)UnoRuntime.queryInterface( + XMultiServiceFactory.class, + m_xSMGR.createInstance("com.sun.star.configuration.ConfigurationProvider")); + + PropertyValue[] lParams = new PropertyValue[1]; + lParams[0] = new PropertyValue(); + lParams[0].Name = "nodepath"; + lParams[0].Value = sConfigPath; + + Object aConfig; + if (bReadOnly) + aConfig = xConfigRoot.createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + lParams); + else + aConfig = xConfigRoot.createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationUpdateAccess", + lParams); + + m_xConfig = (XHierarchicalNameAccess)UnoRuntime.queryInterface( + XHierarchicalNameAccess.class, + aConfig); + + if (m_xConfig == null) + throw new com.sun.star.uno.Exception("Could not open configuration \""+sConfigPath+"\""); + } + + //----------------------------------------------- + public Object readRelativeKey(String sRelPath, + String sKey ) + throws com.sun.star.container.NoSuchElementException + { + try + { + XPropertySet xPath = (XPropertySet)UnoRuntime.queryInterface( + XPropertySet.class, + m_xConfig.getByHierarchicalName(sRelPath)); + return xPath.getPropertyValue(sKey); + } + catch(com.sun.star.uno.Exception ex) + { + throw new com.sun.star.container.NoSuchElementException(ex.getMessage()); + } + } + + //----------------------------------------------- + public void writeRelativeKey(String sRelPath, + String sKey , + Object aValue ) + throws com.sun.star.container.NoSuchElementException + { + try + { + XPropertySet xPath = (XPropertySet)UnoRuntime.queryInterface( + XPropertySet.class, + m_xConfig.getByHierarchicalName(sRelPath)); + xPath.setPropertyValue(sKey, aValue); + } + catch(com.sun.star.uno.Exception ex) + { + throw new com.sun.star.container.NoSuchElementException(ex.getMessage()); + } + } + + //----------------------------------------------- + public void flush() + { + try + { + XChangesBatch xBatch = (XChangesBatch)UnoRuntime.queryInterface( + XChangesBatch.class, + m_xConfig); + xBatch.commitChanges(); + } + catch(com.sun.star.uno.Exception ex) + {} + } + + //----------------------------------------------- + public static Object readDirectKey(XMultiServiceFactory xSMGR , + String sConfigFile, + String sRelPath , + String sKey ) + throws com.sun.star.uno.Exception + { + ConfigHelper aConfig = new ConfigHelper(xSMGR, sConfigFile, true); + return aConfig.readRelativeKey(sRelPath, sKey); + } + + //----------------------------------------------- + public static void writeDirectKey(XMultiServiceFactory xSMGR , + String sConfigFile, + String sRelPath , + String sKey , + Object aValue ) + throws com.sun.star.uno.Exception + { + ConfigHelper aConfig = new ConfigHelper(xSMGR, sConfigFile, false); + aConfig.writeRelativeKey(sRelPath, sKey, aValue); + aConfig.flush(); + } +} diff --git a/framework/qa/complex/framework/autosave/Protocol.java b/framework/qa/complex/framework/autosave/Protocol.java new file mode 100644 index 000000000000..11f6c4eeeff3 --- /dev/null +++ b/framework/qa/complex/framework/autosave/Protocol.java @@ -0,0 +1,999 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package complex.framework.autosave; + +// __________ Imports __________ + +// structs, const, ... +import com.sun.star.beans.PropertyValue; +import com.sun.star.bridge.XUnoUrlResolver; + +// exceptions +import com.sun.star.container.NoSuchElementException; +import com.sun.star.uno.Exception; +import com.sun.star.uno.RuntimeException; +import java.io.IOException; +import java.lang.InterruptedException; +import java.net.ConnectException; + +// interfaces +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.Any; + +// helper +import com.sun.star.uno.IBridge; +import com.sun.star.uno.UnoRuntime; + +// others +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import java.lang.*; +import java.io.*; +import java.util.*; +import java.sql.*; + +// __________ Implementation __________ + +/** + * Implements a log mechanism to create a protocol of all steps of e.g. an api test + * It provides the possibility to write the logged meesages to a file and/or + * to stdout/stderr (if neccessary at the same time!). + * + * TODO + * - implement filter, which e.g. supress showing of INFO data + */ +public class Protocol extends JComponent +{ + // ____________________ + /** + * Note: Following values can be combined - they are interpreted as flags. + * + * @const MODE_STDOUT messages are logged to stdout + * @const MODE_STDERR messages are logged to stderr + * @const MODE_ASCII messages are logged to an ascii file + * @const MODE_HTML messages are logged to a html file + * + * @const TYPE_SCOPE_OPEN open, mark or count a new scope for following log statements + * @const TYPE_SCOPE_CLOSE close, mark or count the current scope + * @const TYPE_TESTMARK it marks the beginning of a (sub)test, can be used for statistic purposes + * @const TYPE_OK this protocol line is marked as a OK message + * @const TYPE_ERROR this protocol line is marked as an error + * @const TYPE_WARNING this protocol line is marked as a warning + * @const TYPE_INFO this protocol line represent some debug data for analyzing + */ + public static final int MODE_STDOUT = 1; + public static final int MODE_STDERR = 2; + public static final int MODE_ASCII = 4; + public static final int MODE_HTML = 8; + + public static final int TYPE_OK = 1; + public static final int TYPE_ERROR = 2; + public static final int TYPE_WARNING = 4; + public static final int TYPE_INFO = 8; + public static final int TYPE_SCOPE_OPEN = 16; + public static final int TYPE_SCOPE_CLOSE = 32; + public static final int TYPE_TESTMARK = 64; + public static final int TYPE_ERROR_INFO = 128; + public static final int TYPE_WARNING_INFO = 256; + public static final int TYPE_STATISTIC = 512; + public static final int TYPE_LINK = 1024; + + public static final int FILTER_NONE = 0; + public static final int FILTER_OK = TYPE_OK; + public static final int FILTER_ERROR = TYPE_ERROR; + public static final int FILTER_WARNING = TYPE_WARNING; + public static final int FILTER_INFO = TYPE_INFO; + public static final int FILTER_SCOPES = TYPE_SCOPE_OPEN | TYPE_SCOPE_CLOSE; + public static final int FILTER_TESTMARK = TYPE_TESTMARK; + public static final int FILTER_ERROR_INFO = TYPE_ERROR_INFO; + public static final int FILTER_WARNING_INFO = TYPE_WARNING_INFO; + public static final int FILTER_STATISTIC = TYPE_STATISTIC; + public static final int FILTER_LINK = TYPE_LINK; + + // ____________________ + /** + */ + private static final int MARK_DIFF = 5; + + private static final String BGCOLOR_LINECOL = "#95CC77"; + private static final String FGCOLOR_LINECOL_NORMAL = "#ffffbd"; + private static final String FGCOLOR_LINECOL_MARKED = "#000088"; + + private static final String BGCOLOR_STANDARD = "#ffffff"; + private static final String FGCOLOR_STANDARD = "#000000"; + + private static final String BGCOLOR_SCOPE = "#eeeeee"; + private static final String FGCOLOR_SCOPE = "#000000"; + + private static final String BGCOLOR_TIMESTAMP = "#e0e0e0"; + private static final String FGCOLOR_TIMESTAMP = "#000000"; + + private static final String BGCOLOR_TESTMARK = "#0000ff"; + private static final String FGCOLOR_TESTMARK = "#ffffff"; + + private static final String BGCOLOR_OK = "#88dd88"; + private static final String FGCOLOR_OK = "#ffffff"; + + private static final String BGCOLOR_WARNING = "#ffff00"; + private static final String FGCOLOR_WARNING = "#000000"; + + private static final String BGCOLOR_WARNING_INFO = "#ffffcc"; + private static final String FGCOLOR_WARNING_INFO = "#000000"; + + private static final String BGCOLOR_ERROR = "#ff0000"; + private static final String FGCOLOR_ERROR = "#ffff00"; + + private static final String BGCOLOR_ERROR_INFO = "#ffbbbb"; + private static final String FGCOLOR_ERROR_INFO = "#000000"; + + private static final String BGCOLOR_INFO = "#eeeeee"; + private static final String FGCOLOR_INFO = "#000000"; + + private static final String BGCOLOR_STATISTIC = "#0000ff"; + private static final String FGCOLOR_STATISTIC = "#ffffff"; + + private static final String BGCOLOR_LINK = BGCOLOR_INFO; + private static final String FGCOLOR_LINK = FGCOLOR_INFO; + + // ____________________ + /** + * @member m_nMode the mode, in which this protocol object runs + * @member m_nFilter can be used to filter log messages by type + * @member m_sFileName we need it to open the log file on demand (if nMode require such log file) + * @member m_nLine used as line number for the protocol + * @member m_nScope used to format scopes + * @member m_nErrors count errors in protocol + * @member m_nWarnings count warnings in protocol + * @member m_nTestMarks count test marker in protocol + */ + private int m_nMode ; + private int m_nFilter ; + private String m_sFileName ; + private long m_nLine ; + private long m_nScope ; + private long m_nErrors ; + private long m_nWarnings ; + private long m_nTestMarks; + private Timestamp m_aStartTime; + private Timestamp m_aEndTime ; + + // ____________________ + /** + * special helper class to represent one line of a protocol. + * Such line can be specified as a special one (ERROR, WARNING ...). + * That makes it possible to analyze the whole protocol using tools. + */ + class ProtocolLine + { + /// the line number of this protocol line (size of the vector of all protocol lines cn be used to count such lines!) + private long m_nLine; + /// deepness of the current scope + private long m_nScope; + /// mark line as an error, warning, data entry ... (see const definitions before) + private int m_nType; + /// of course, we have to know the logged message too :-) + private String m_sMessage; + /// and it can be usefull to know the current time, when this line was created + private Timestamp m_aStamp; + + /** ctor for fast initializing of such line */ + public ProtocolLine( long nLine , + long nScope , + int nType , + String sMessage ) + { + m_aStamp = new Timestamp(System.currentTimeMillis()); + m_nLine = nLine ; + m_nScope = nScope ; + m_nType = nType ; + m_sMessage = sMessage; + } + + /** format this line as an ascii string for writing log files */ + public synchronized String toString() + { + StringBuffer sLine = new StringBuffer(1000); + + // insert line number + // Use right bound notation and format 6 digits! + sLine.append("[" ); + if (m_nLine<10) + sLine.append(" "); + else + if (m_nLine<100) + sLine.append(" "); + else + if (m_nLine<1000) + sLine.append(" "); + else + if (m_nLine<10000) + sLine.append(" "); + else + if (m_nLine<100000) + sLine.append(" "); + sLine.append(m_nLine); + sLine.append("] " ); + + // add time stamp + // close with a "TAB" ... because some time stamps are not normalized to + // a well defined string length .-) + sLine.append(m_aStamp.toString()+" \t"); + + // add special line type + if ((m_nType & TYPE_OK) == TYPE_OK) + sLine.append(" OK "); + else + if ((m_nType & TYPE_ERROR) == TYPE_ERROR) + sLine.append(" ERROR "); + else + if ((m_nType & TYPE_ERROR_INFO) == TYPE_ERROR_INFO) + sLine.append(" ERROR INFO "); + else + if ((m_nType & TYPE_WARNING) == TYPE_WARNING) + sLine.append(" WARNING "); + else + if ((m_nType & TYPE_WARNING_INFO) == TYPE_WARNING_INFO) + sLine.append(" WARNING INFO "); + else + if ((m_nType & TYPE_INFO) == TYPE_INFO) + sLine.append(" INFO "); + else + if ((m_nType & TYPE_TESTMARK) == TYPE_TESTMARK) + sLine.append(" TEST "); + else + if ((m_nType & TYPE_LINK) == TYPE_LINK) + sLine.append(" LINK "); + else + if ((m_nType & TYPE_STATISTIC) == TYPE_STATISTIC) + sLine.append(" STATISTIC "); + else + if ( + ((m_nType & TYPE_SCOPE_OPEN ) == TYPE_SCOPE_OPEN ) || + ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE) + ) + sLine.append(" SCOPE "); + else + sLine.append(" "); + + // add scope information + for (int s=0; s<m_nScope; ++s) + sLine.append(" "); + + if ((m_nType & TYPE_SCOPE_OPEN) == TYPE_SCOPE_OPEN) + sLine.append(" { "); + else + if ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE) + sLine.append(" } "); + else + sLine.append(" "); + + // add message + sLine.append(m_sMessage); + sLine.append("\n" ); + + return sLine.toString(); + } + + /** + * format this line as a string for writing log files + * using the html format + */ + public synchronized String toHTML() + { + StringBuffer sLine = new StringBuffer(1000); + sLine.append("<tr>"); + + // insert line number + if (m_nLine % MARK_DIFF == 0) + impl_generateColoredHTMLCell(sLine, new Long(m_nLine).toString(), BGCOLOR_LINECOL, FGCOLOR_LINECOL_MARKED, true); + else + impl_generateColoredHTMLCell(sLine, new Long(m_nLine).toString(), BGCOLOR_LINECOL, FGCOLOR_LINECOL_NORMAL, false); + + // add time stamp + impl_generateColoredHTMLCell(sLine, m_aStamp.toString()+" ", BGCOLOR_TIMESTAMP, FGCOLOR_TIMESTAMP, false); + + // add log type info + boolean bTypeCellFilled = false; + if ((m_nType & TYPE_ERROR_INFO) == TYPE_ERROR_INFO) + { + impl_generateColoredHTMLCell(sLine, "ERROR INFO", BGCOLOR_ERROR_INFO, FGCOLOR_ERROR_INFO, false); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_ERROR) == TYPE_ERROR) + { + impl_generateColoredHTMLCell(sLine, "ERROR", BGCOLOR_ERROR, FGCOLOR_ERROR, true); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_WARNING_INFO) == TYPE_WARNING_INFO) + { + impl_generateColoredHTMLCell(sLine, "WARNING INFO", BGCOLOR_WARNING_INFO, FGCOLOR_WARNING_INFO, false); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_WARNING) == TYPE_WARNING) + { + impl_generateColoredHTMLCell(sLine, "WARNING", BGCOLOR_WARNING, FGCOLOR_WARNING, true); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_OK) == TYPE_OK) + { + impl_generateColoredHTMLCell(sLine, "OK", BGCOLOR_OK, FGCOLOR_OK, true); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_INFO) == TYPE_INFO) + { + impl_generateColoredHTMLCell(sLine, "INFO", BGCOLOR_INFO, FGCOLOR_INFO, false); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_TESTMARK) == TYPE_TESTMARK) + { + impl_generateColoredHTMLCell(sLine, "TEST", BGCOLOR_TESTMARK, FGCOLOR_TESTMARK, true); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_STATISTIC) == TYPE_STATISTIC) + { + impl_generateColoredHTMLCell(sLine, "STATISTIC", BGCOLOR_STATISTIC, FGCOLOR_STATISTIC, false); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_LINK) == TYPE_LINK) + { + impl_generateColoredHTMLCell(sLine, "LINK", BGCOLOR_LINK, FGCOLOR_LINK, false); + bTypeCellFilled = true; + } + else + if ( + ((m_nType & TYPE_SCOPE_OPEN ) == TYPE_SCOPE_OPEN ) || + ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE) + ) + { + impl_generateColoredHTMLCell(sLine, "SCOPE", BGCOLOR_SCOPE, FGCOLOR_SCOPE, false); + bTypeCellFilled = true; + } + + // if no tyope information was added to the current coloum, we must + // write any content into this cell. Otherwise some browser + // shows a strange layout! + if (! bTypeCellFilled) + impl_generateColoredHTMLCell(sLine, " ", BGCOLOR_STANDARD, FGCOLOR_STANDARD, false); + + // add scope information + sLine.append("<td>"); + for (int s=0; s<m_nScope; ++s) + sLine.append(" "); + String sColor = "#000000"; + if ((m_nScope % 2) == 0) + sColor = "#808080"; + if ((m_nType & TYPE_SCOPE_OPEN) == TYPE_SCOPE_OPEN) + sLine.append("<font color=\""+sColor+"\">{ "+m_nScope+"</font>"); + else + if ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE) + sLine.append("<font color=\""+sColor+"\">"+m_nScope+" }</font>"); + sLine.append("</td>\n"); + + // add message + sLine.append("<td>" ); + sLine.append(m_sMessage); + sLine.append("</td>\n" ); + + sLine.append("</tr>\n" ); + + return sLine.toString(); + } + + /** detect, if this line object represent an error */ + public synchronized boolean isError() + { + return ( + ((m_nType & TYPE_ERROR) == TYPE_ERROR) && + ((m_nType & TYPE_INFO ) != TYPE_INFO ) + ); + } + + /** detect, if this line object represent a warning */ + public synchronized boolean isWarning() + { + return ( + ((m_nType & TYPE_WARNING) == TYPE_WARNING) && + ((m_nType & TYPE_INFO ) != TYPE_INFO ) + ); + } + + /** detect, if this line object represent a marked position */ + public synchronized boolean isTestMark() + { + return ((m_nType & TYPE_TESTMARK) == TYPE_TESTMARK); + } + + /** + * create a colored table cell formated as HTML. + * + * @param sCell + * an outside string buffer, which can be + * used to generate the + * needed HTML code there. + * + * @param sContent + * the text content of this cell. + * + * @param sBGColor + * a string, which represent the background color + * coded in HTML. + * + * @param sFGColor + * a string, which represent the foreground color + * coded in HTML. + * + * @param bBold + * enable/disable bold state for the text content. + */ + private void impl_generateColoredHTMLCell(StringBuffer sCell , + String sContent, + String sBGColor, + String sFGColor, + boolean bBold ) + { + sCell.append("<td bgcolor=\""+sBGColor+"\">"); + sCell.append("<font color=\""+sFGColor+"\">"); + if (bBold) + sCell.append("<b>"); + sCell.append(sContent); + if (bBold) + sCell.append("</b>"); + sCell.append("</font></td>\n"); + } + } + + // ____________________ + /** + * ctor + * It creates a new instance of this class and innitialize it in the right mode. + * + * @param nMode + * specify how the log should be generated. + * + * @param nFilter + * can be used to filter log messages by it's type. + * + * @param sFileName + * the name of the log file (if nMode requires a log file) + */ + public Protocol(int nMode , + int nFilter , + String sFileName) + { + m_nMode = nMode; + m_nFilter = nFilter; + m_sFileName = sFileName; + m_nLine = 0; + m_nScope = 1; + m_nWarnings = 0; + m_nErrors = 0; + m_aStartTime = new Timestamp(System.currentTimeMillis()); + } + + // ____________________ + /** + * For some modes it's neccessary, that we write some additional + * informations to the log. E.g. for html we must generate close targets. + */ + public synchronized void finish() + { + // Preferr HTML ... because we can't write ASCII and HTML contents to the same log file! + String sContent; + if ((m_nMode & MODE_HTML) == MODE_HTML) + sContent = impl_generateHTMLFooter(); + else + if ((m_nMode & MODE_ASCII) == MODE_ASCII) + sContent = impl_generateAsciiFooter(); + else + return; + + impl_writeToLogFile(m_sFileName, true, sContent); + } + + // ____________________ + /** + * log an unspecified message. + * + * Sometimes it's not neccessary to set a special type for an message. + * The pure message seams to be enough. The type of such "pure messages" + * will be set to INFO. + * + * @param sMessage + * the pure message + * + * @see log(type,message) + */ + public synchronized void log( /*IN*/ String sMessage ) + { + log(TYPE_INFO, sMessage); + } + + // ____________________ + /** + * log an exception. + * + * It uses all informations available by this exception object + * to generate the log. So exceptions are printed out using a + * standard format. + * + * @param exThrowable + * the exception + */ + public synchronized void log( /*IN*/ Throwable exThrowable ) + { + log(TYPE_SCOPE_OPEN | TYPE_ERROR, "exception \""+exThrowable.getMessage()+"\""); + + StackTraceElement[] lStack = exThrowable.getStackTrace(); + for (int i=0; i<lStack.length; ++i) + log(TYPE_ERROR_INFO, lStack[i].toString()); + + log(TYPE_SCOPE_CLOSE | TYPE_ERROR_INFO, ""); + } + + // ____________________ + /** + * log different property arrays. + * + * @param lProps + * the array of properties + */ + public synchronized void log( /*IN*/ com.sun.star.beans.NamedValue[] lProps ) + { + StringBuffer sValues = new StringBuffer(1000); + impl_logPropertyArray(sValues, lProps); + + log(TYPE_SCOPE_OPEN | TYPE_INFO, "property array ["+lProps.length+"]:"); + log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValues.toString() ); + } + + public synchronized void log( /*IN*/ com.sun.star.beans.PropertyValue[] lProps ) + { + StringBuffer sValues = new StringBuffer(1000); + impl_logPropertyArray(sValues, lProps); + + log(TYPE_SCOPE_OPEN | TYPE_INFO, "property array ["+lProps.length+"]:"); + log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValues.toString() ); + } + + public synchronized void log( /*IN*/ com.sun.star.beans.NamedValue aProp ) + { + StringBuffer sValue = new StringBuffer(1000); + impl_logProperty(sValue, aProp); + + log(TYPE_SCOPE_OPEN | TYPE_INFO, "property:" ); + log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValue.toString()); + } + + public synchronized void log( /*IN*/ com.sun.star.beans.PropertyValue aProp ) + { + StringBuffer sValue = new StringBuffer(1000); + impl_logProperty(sValue, aProp); + + log(TYPE_SCOPE_OPEN | TYPE_INFO, "property:" ); + log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValue.toString()); + } + + public synchronized void log( /*IN*/ Object aAny ) + { + StringBuffer sValue = new StringBuffer(1000); + impl_logAny(sValue, aAny); + + log(TYPE_SCOPE_OPEN | TYPE_INFO, "any:" ); + log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValue.toString()); + } + + // ____________________ + /** + * log a message. + * + * It looks for the internal set mode and decide, how this message + * will be handled. Then it generates a special object which represent + * one protocol line, format it and print it out. + * + * @param nType + * mark a line as a special one or open/close scopes + * + * @param sMessage + * the message, which the outside code wish to be written into the log + */ + public synchronized void log( /*IN*/ int nType , + /*IN*/ String sMessage ) + { + nType = (nType & ~m_nFilter); + if (nType == 0) + return; + + ++m_nLine; + + // it's neccessary to open scopes before creatig the protocol line + // to guarantee right tab handling for new scope value! + if ((nType & TYPE_SCOPE_OPEN) == TYPE_SCOPE_OPEN) + ++m_nScope; + + // create the protocol line + ProtocolLine aLine = new ProtocolLine(m_nLine, m_nScope, nType, sMessage); + String sAsciiLog = aLine.toString(); + String sHTMLLog = aLine.toHTML(); + + // it's neccessary to close scope after creatig the protocol line + // to guarantee right tab handling for old scope value! + if ( + ( m_nScope > 0 ) && + ((nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE) + ) + { + --m_nScope; + } + + // update statistic values + if (aLine.isTestMark()) + ++m_nTestMarks; + if (aLine.isWarning()) + ++m_nWarnings; + if (aLine.isError()) + ++m_nErrors; + + // no else - it's a bit field of possible reactions! + if ((m_nMode & MODE_STDOUT) == MODE_STDOUT) + System.out.print(sAsciiLog); + // no else - it's a bit field of possible reactions! + if ((m_nMode & MODE_STDERR) == MODE_STDERR) + System.err.print(sAsciiLog); + // no else - it's a bit field of possible reactions! + // But these both conditions must be handled together. + // Because we cant generate different types of log contents to the same log file. + // We preferr HTML if both types are set. + if ( + ((m_nMode & MODE_HTML ) == MODE_HTML ) || + ((m_nMode & MODE_ASCII) == MODE_ASCII) + ) + { + boolean bAppend = (m_nLine>1); + String sContent; + if ((m_nMode & MODE_HTML) == MODE_HTML) + { + if (! bAppend) + sContent = impl_generateHTMLHeader()+sHTMLLog; + else + sContent = sHTMLLog; + } + else + { + if (! bAppend) + sContent = impl_generateAsciiHeader()+sAsciiLog; + else + sContent = sAsciiLog; + } + + impl_writeToLogFile(m_sFileName, bAppend, sContent); + } + } + + // ____________________ + public synchronized void defineHyperlink( /*IN*/ String sTarget , + /*IN*/ String sDescription) + { + if ((m_nMode & MODE_HTML) != MODE_HTML) + return; + + StringBuffer sLog = new StringBuffer(1000); + sLog.append("<a href=\""); + sLog.append(sTarget ); + sLog.append("\">" ); + sLog.append(sDescription); + sLog.append("</a>" ); + + log(TYPE_LINK, sLog.toString()); + } + + // ____________________ + /** + * log the current statistic values + * We write it into our protocol buffer and + * reset it. + */ + public synchronized void logStatistics() + { + m_aEndTime = new Timestamp(System.currentTimeMillis()); + Timestamp aDiff = new Timestamp(m_aEndTime.getTime()-m_aStartTime.getTime()); + + int nLogType = TYPE_STATISTIC; + if (m_nErrors > 0) + nLogType = TYPE_ERROR_INFO; + else + if (m_nWarnings > 0) + nLogType = TYPE_WARNING_INFO; + + log(nLogType | TYPE_SCOPE_OPEN , "statistic:" ); + log(nLogType , "tests = "+m_nTestMarks ); + log(nLogType , "errors = "+m_nErrors ); + log(nLogType , "warnings = "+m_nWarnings ); + log(nLogType , "elapsed time = "+aDiff.toString()); + log(nLogType | TYPE_SCOPE_CLOSE, "" ); + + resetStatistics(); + } + + public synchronized void resetStatistics() + { + m_nTestMarks = 0; + m_nWarnings = 0; + m_nErrors = 0; + m_aStartTime = new Timestamp(System.currentTimeMillis()); + } + + // ____________________ + /** + * returns a generic html header for generating html log files + * + * It's used from the method finish() to generate a valid html formated file. + * For that its neccessary to open some special html targets like e.g. <html>. + * + * @return A string, which includes the whole header. + * + * @see finish() + * @see generateHTMLFooter() + */ + private String impl_generateHTMLHeader() + { + return "<html>\n<head>\n<title>"+m_sFileName+"</title>\n</head>\n<body>\n<table>\n"; + } + + private String impl_generateAsciiHeader() + { + return "********************************************************************************\n"; + } + + private String impl_generateHTMLFooter() + { + return "\n</table>\n</body>\n</html>\n"; + } + + private String impl_generateAsciiFooter() + { + return "\n\n"; + } + + // ____________________ + /** + * helper to log different representations of a property(array) + * + * @param sOut + * used to generate the log output there. + * + * @param lProps/aProp + * represent the property(array) to be logged. + */ + private void impl_logPropertyArray( /*OUT*/ StringBuffer sOut , + /*IN */ com.sun.star.beans.PropertyValue[] lProps ) + { + int i = 0; + int c = lProps.length; + + for (i=0; i<c; ++i) + impl_logProperty(sOut, lProps[i]); + } + + private void impl_logPropertyArray( /*OUT*/ StringBuffer sOut , + /*IN */ com.sun.star.beans.NamedValue[] lProps ) + { + int i = 0; + int c = lProps.length; + + for (i=0; i<c; ++i) + impl_logProperty(sOut, lProps[i]); + } + + private void impl_logProperty( /*OUT*/ StringBuffer sOut , + /*IN*/ com.sun.star.beans.NamedValue aProp ) + { + sOut.append("\""+aProp.Name+"\" = "); + impl_logAny(sOut, aProp.Value); + } + + private void impl_logProperty( /*OUT*/ StringBuffer sOut , + /*IN*/ com.sun.star.beans.PropertyValue aProp ) + { + sOut.append("\""+aProp.Name+"\" = "); + impl_logAny(sOut, aProp.Value); + } + + // ____________________ + /** + * it trys to convert the given any into a suitable string notation .-) + */ + private synchronized void impl_logAny( /*OUT*/ StringBuffer sOut , + /*IN */ Object aAny ) + { + try + { + if (com.sun.star.uno.AnyConverter.isVoid(aAny)) + { + sOut.append("[void] {"); + } + else + if (com.sun.star.uno.AnyConverter.isChar(aAny)) + { + sOut.append("[char] {"); + sOut.append(com.sun.star.uno.AnyConverter.toChar(aAny)); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isBoolean(aAny)) + { + sOut.append("[boolean] {"); + if (com.sun.star.uno.AnyConverter.toBoolean(aAny)) + sOut.append("TRUE"); + else + sOut.append("FALSE"); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isByte(aAny)) + { + sOut.append("[byte] {"); + sOut.append(com.sun.star.uno.AnyConverter.toByte(aAny)); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isShort(aAny)) + { + sOut.append("[short] {"); + sOut.append(com.sun.star.uno.AnyConverter.toShort(aAny)); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isInt(aAny)) + { + sOut.append("[int] {"); + sOut.append(com.sun.star.uno.AnyConverter.toInt(aAny)); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isLong(aAny)) + { + sOut.append("[long] {"); + sOut.append(com.sun.star.uno.AnyConverter.toLong(aAny)); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isFloat(aAny)) + { + sOut.append("[float] {"); + sOut.append(com.sun.star.uno.AnyConverter.toFloat(aAny)); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isDouble(aAny)) + { + sOut.append("[double] {"); + sOut.append(com.sun.star.uno.AnyConverter.toDouble(aAny)); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isString(aAny)) + { + sOut.append("[string] {"); + sOut.append(com.sun.star.uno.AnyConverter.toString(aAny)); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isEnum(aAny)) + { + sOut.append("[enum] {"); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isType(aAny)) + { + sOut.append("[type] {"); + sOut.append("}"); + } + else + if (com.sun.star.uno.AnyConverter.isArray(aAny)) + { + if (aAny instanceof java.lang.String[]) + { + sOut.append("[sequence< string >] {"); + sOut.append("}"); + } + else + if (aAny instanceof com.sun.star.beans.PropertyValue[]) + { + sOut.append("[sequence< PropertyValue >] {"); + com.sun.star.beans.PropertyValue[] lSubProps = (com.sun.star.beans.PropertyValue[])com.sun.star.uno.AnyConverter.toArray(aAny); + impl_logPropertyArray(sOut, lSubProps); + sOut.append("}"); + } + else + if (aAny instanceof com.sun.star.beans.NamedValue[]) + { + sOut.append("[sequence< NamedValue >] {"); + com.sun.star.beans.NamedValue[] lSubProps = (com.sun.star.beans.NamedValue[])com.sun.star.uno.AnyConverter.toArray(aAny); + impl_logPropertyArray(sOut, lSubProps); + sOut.append("}"); + } + else + { + sOut.append("[unknown array] {-}"); + } + } + else + if (com.sun.star.uno.AnyConverter.isObject(aAny)) + { + sOut.append("[object] {"); + // TODO + sOut.append("}"); + } + + if ((m_nMode & MODE_HTML) == MODE_HTML) + sOut.append("<br>"); + else + sOut.append("\n"); + } + catch(com.sun.star.lang.IllegalArgumentException exIll) + { + sOut.append("Got exception during property conversion.\n"); + sOut.append(exIll.getMessage()); + sOut.append("\n"); + } + } + + // ____________________ + /** + * Writes the given content to the specified log file. + */ + private void impl_writeToLogFile(String sFileName, + boolean bAppend , + String sContent ) + { + try + { + FileWriter aLogFile = new FileWriter(sFileName, bAppend); + aLogFile.write(sContent); + aLogFile.flush(); + aLogFile.close(); + aLogFile = null; + } + catch (java.io.IOException exIO) + { + System.err.println("Can't dump protocol into log file."); + System.err.println(exIO); + exIO.printStackTrace(); + } + } +} diff --git a/framework/qa/complex/framework/autosave/makefile.mk b/framework/qa/complex/framework/autosave/makefile.mk new file mode 100644 index 000000000000..e903c9ad5f33 --- /dev/null +++ b/framework/qa/complex/framework/autosave/makefile.mk @@ -0,0 +1,89 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* +PRJ = ..$/..$/..$/.. +TARGET = AutoSave +PRJNAME = framework +PACKAGE = complex$/framework$/autosave + +# --- Settings ----------------------------------------------------- +.INCLUDE: settings.mk + + +#----- compile .java files ----------------------------------------- + +JARFILES = mysql.jar ridl.jar unoil.jar jurt.jar juh.jar java_uno.jar \ + OOoRunner.jar mysql.jar +JAVAFILES = AutoSave.java ConfigHelper.java Protocol.java +JAVACLASSFILES = $(foreach,i,$(JAVAFILES) $(CLASSDIR)$/$(PACKAGE)$/$(i:b).class) + +#----- make a jar from compiled files ------------------------------ + +MAXLINELENGTH = 100000 + +JARCLASSDIRS = $(PACKAGE) +JARTARGET = $(TARGET).jar +JARCOMPRESS = TRUE + +# --- Parameters for the test -------------------------------------- + +# start an office if the parameter is set for the makefile +.IF "$(OFFICE)" == "" +CT_APPEXECCOMMAND = +.ELSE +CT_APPEXECCOMMAND = -AppExecutionCommand "$(OFFICE)$/soffice -accept=socket,host=localhost,port=8100;urp;" +.ENDIF + +# test base is java complex +CT_TESTBASE = -TestBase java_complex + +# test looks something like the.full.package.TestName +CT_TEST = -o $(PACKAGE:s\$/\.\).$(JAVAFILES:b) + +# start the runner application +CT_APP = org.openoffice.Runner + +# --- Targets ------------------------------------------------------ + +.IF "$(depend)" == "" +$(CLASSDIR)$/$(PACKAGE)$/$(JAVAFILES:b).props : ALLTAR +.ELSE +$(CLASSDIR)$/$(PACKAGE)$/$(JAVAFILES:b).props : ALLDEP +.ENDIF + +.INCLUDE : target.mk + +#$(CLASSDIR)$/$(PACKAGE)$/$(JAVAFILES:b).props : $(JAVAFILES:b).props +# cp $(JAVAFILES:b).props $(CLASSDIR)$/$(PACKAGE)$/$(JAVAFILES:b).props +# jar uf $(CLASSDIR)$/$(JARTARGET) -C $(CLASSDIR) $(PACKAGE)$/$(JAVAFILES:b).props + +RUN: run + +run: + +java -cp $(CLASSPATH) $(CT_APP) $(CT_TESTBASE) $(CT_APPEXECCOMMAND) $(CT_TEST) + + + diff --git a/framework/qa/complex/framework/recovery/CrashThread.java b/framework/qa/complex/framework/recovery/CrashThread.java new file mode 100644 index 000000000000..616d4baa200e --- /dev/null +++ b/framework/qa/complex/framework/recovery/CrashThread.java @@ -0,0 +1,80 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package complex.framework.recovery; + +import com.sun.star.frame.XController; +import com.sun.star.frame.XDispatch; +import com.sun.star.frame.XDispatchProvider; +import com.sun.star.frame.XModel; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.URL; +import com.sun.star.util.XURLTransformer; + +/** + * Thread to crash the office. This thread dies after the office process + * is nopt longer available. + */ +public class CrashThread extends Thread { + public XComponent xDoc = null; + public XMultiServiceFactory msf = null; + + public CrashThread(XComponent xDoc, XMultiServiceFactory msf) { + this.xDoc = xDoc; + this.msf = msf; + } + + public void run() { + try{ + XModel xModel = (XModel) UnoRuntime.queryInterface(XModel.class, xDoc); + + XController xController = xModel.getCurrentController(); + XDispatchProvider xDispProv = (XDispatchProvider) UnoRuntime.queryInterface( + XDispatchProvider.class, + xController); + XURLTransformer xParser = (XURLTransformer) UnoRuntime.queryInterface( + XURLTransformer.class, + msf.createInstance( + "com.sun.star.util.URLTransformer")); + + // Because it's an in/out parameter we must use an array of URL objects. + URL[] aParseURL = new URL[1]; + aParseURL[0] = new URL(); + aParseURL[0].Complete = ".uno:Crash"; + xParser.parseStrict(aParseURL); + + URL aURL = aParseURL[0]; + XDispatch xDispatcher = xDispProv.queryDispatch(aURL, "", 0); + + if (xDispatcher != null) { + xDispatcher.dispatch(aURL, null); + } + } catch (Exception e){} + } +} diff --git a/framework/qa/complex/framework/recovery/KlickButtonThread.java b/framework/qa/complex/framework/recovery/KlickButtonThread.java new file mode 100644 index 000000000000..426a2e3d2a79 --- /dev/null +++ b/framework/qa/complex/framework/recovery/KlickButtonThread.java @@ -0,0 +1,57 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package complex.framework.recovery; + +import com.sun.star.awt.XWindow; +import com.sun.star.lang.XMultiServiceFactory; +import util.UITools; + +/** + * Thread to crash the office. This thread dies after the office process + * is nopt longer available. + */ +public class KlickButtonThread extends Thread { + private XWindow xWindow = null; + private XMultiServiceFactory xMSF = null; + private String buttonName = null; + + public KlickButtonThread(XMultiServiceFactory xMSF, XWindow xWindow, String buttonName) { + this.xWindow = xWindow; + this.xMSF = xMSF; + this.buttonName = buttonName; + } + + public void run() { + try{ + UITools oUITools = new UITools(xMSF, xWindow); + + oUITools.clickButton(buttonName); + + } catch (Exception e){} + } +} diff --git a/framework/qa/complex/framework/recovery/RecoveryTest.java b/framework/qa/complex/framework/recovery/RecoveryTest.java new file mode 100644 index 000000000000..ee565ff74330 --- /dev/null +++ b/framework/qa/complex/framework/recovery/RecoveryTest.java @@ -0,0 +1,1012 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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; + +public class RecoveryTest extends ComplexTestCase { + + static XMultiServiceFactory xMSF; + static SOfficeFactory SOF; + static RecoveryTools rt; + /** + * If you devid 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 Hpyerlink-Dialog + * @todo: sceond 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: makefile anpassen auf Parameter überprüfen + */ + 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 shure 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("wating 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 StarOffice 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 documenst should be recoverd this variable must be true. If it is fasle + * the recovery process was stoped and the button cancel was klicked. + * @param cancel If the recovery is fifnished, this parameter desicdes to klick 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<documents.length;i++){ + log.println(documents[i]); + } + + 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); + + if (recover){ + + log.println("clicking 'Start Recovery' button..."); + oUITools.clickButton("Start Recovery >"); + + 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 recoverd + } + 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 cklick "Yes" or "No", dependend on the value of the parameter <CODE>Yes</CODE> + * @param yes If value is <CODE>TRUE</CODE> 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 cklick "Yes" or "No", dependend on the value of the parameter <CODE>Yes</CODE> + * @param yes If value is <CODE>TRUE</CODE> 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<documents.length;i++){ + log.println(documents[i]); + } + String tempURL = utils.getOfficeTempDir(xMSF); + + log.println("the destination for saveing is: " + tempURL); + try{ + oUITools.setTextEditFiledText("Save to", tempURL); + } catch (java.lang.Exception e){ + failed("could not set target directory for saving documents at 'Save Documents' dialog", CONTINUE); + } + try{ + oUITools.clickButton("OK"); + } catch (java.lang.Exception e){ + failed("could not click 'OK' at 'Save Documents' dialog", CONTINUE); + } + } + } catch (com.sun.star.accessibility.IllegalAccessibleComponentStateException e){ + failed("Could not handle 'Are you sure' dialog."); + } + } + + /** + * This function gets the current screen size and calculate the first + * quarter of it. This qaurter was used to postion to Office windows. + * Further this function calculates the maximum window size so the window + * is visible if it placed on extreme position. + */ + private void makeWindowPositionRage(){ + Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize(); + Point pos = new Point(); + Size size = new Size(); + + // get the max position of the first quarter of the screen + pos.x = screenDim.width / 2; + pos.y = screenDim.height / 2; + windowMaxPosition = pos; + + // get the max size of the windows while they placed in windowMaxPosition + // range and not outside the visibility + size.Height = screenDim.height; + size.Width = screenDim.width; + windowMaxSize = size; + } + + private void generateDesktop(){ + + // create some documents with content + makeWriterDoc("WriterDoc1", true); +// makeCalcDoc("CalcDoc1", true); +// makeDrawDoc("DrawDoc1", true); +// makeImpressDoc("ImpressDoc1", true); +// makeMathDoc("MathDoc1", true); + + // create some documents without content +// makeMathDoc("_blank_math", false); +// makeDrawDoc("_blank_draw", false); +// makeCalcDoc("_blank_calc", false); +// makeWriterDoc("_blank_writer", false); +// makeImpressDoc("_blank_impress", false); + +// makeMathDoc("MathDocEmpty", false); +// makeDrawDoc("DrawDocEmpty", false); +// makeCalcDoc("CalcDocEmpty", false); + makeWriterDoc("WriterDocEmpty", false); +// makeImpressDoc("ImpressDocEmpty", false); + + log.println("Test object successfully created."); + + } + + private void makeImpressDoc(String frameName, boolean withContent){ + log.println("creating Impress document '" + frameName + "'"); + XComponent xImpressDoc = createNewImpressDoc(frameName); + if (withContent) fillImpressDocWithContent(xImpressDoc); + positioningDocument((XModel) UnoRuntime.queryInterface(XModel.class, + xImpressDoc)); + } + + private void makeDrawDoc(String frameName, boolean withContent){ + log.println("creating Draw document '" + frameName + "'"); + XComponent xDrawDoc = createNewDrawDoc(frameName); + if (withContent) fillDrawDocWithContent(xDrawDoc); + positioningDocument((XModel) UnoRuntime.queryInterface(XModel.class, + xDrawDoc)); + } + + private void makeCalcDoc(String frameName, boolean withContent){ + log.println("creating Calc document '" + frameName + "'"); + XSpreadsheetDocument xSpreadsheetDoc = createNewCalcDoc(frameName); + if (withContent) fillCalcDocWithContent(xSpreadsheetDoc); + positioningDocument((XModel) UnoRuntime.queryInterface(XModel.class, + xSpreadsheetDoc)); + } + + private void positioningDocument(XModel model){ + + XWindow xWindow = model.getCurrentController().getFrame().getContainerWindow(); + String frameName = model.getCurrentController().getFrame().getName(); + + // get randomized position and size + Rectangle posSize = makePosZize(); + + // save position and size + windowsPosSize.put(frameName, posSize); + + xWindow.setPosSize(posSize.X, posSize.Y, posSize.Width, posSize.Height, + com.sun.star.awt.PosSize.POSSIZE); + Rectangle test = xWindow.getPosSize(); + log.println("x: "+test.X+" y:"+test.Y+" width:"+test.Width+" height:"+test.Height); + } + + private Rectangle makePosZize(){ + + Rectangle posSize = new Rectangle(); + Random rand = new Random(); + + // Random integers that range from from 0 to n + posSize.X = rand.nextInt(windowMaxPosition.x + 1); + posSize.Y = rand.nextInt(windowMaxPosition.y + 1); + + int maxHeight = windowMaxSize.Height-posSize.X; + int maxWidth = windowMaxSize.Width-posSize.Y; + int height = rand.nextInt(maxHeight + 1); + int width = rand.nextInt((windowMaxSize.Width-posSize.Y) + 1); + + // be shure that the new size his greater then the half of windowMaxSize + posSize.Height = (height < (maxHeight / 2)) ? height + (maxHeight / 2) : height; + posSize.Width = (width < (maxWidth / 2)) ? width + (maxWidth / 2) : width; + + return posSize; + } + + private void makeMathDoc(String frameName, boolean withContent){ + log.println("creating Math document '" + frameName + "'"); + XComponent xMathDoc = createNewMathDoc(frameName); + if (withContent) fillMathDocWithContent(xMathDoc); + positioningDocument((XModel) UnoRuntime.queryInterface(XModel.class, + xMathDoc)); + } + + private XComponent createNewMathDoc(String frameName){ + XComponent xMathDoc = null; + try{ + xMathDoc = SOF.createMathDoc(frameName); + } catch (com.sun.star.uno.Exception e) { + log.println("Exception occured while creating math document '"+frameName+"':"); + failed("Couldn't create test environment"); + } + return xMathDoc; + } + + private void fillMathDocWithContent(XComponent xMathDoc){ + // setting a formula in document + final String expFormula = "sum a cdot b"; + final XPropertySet xPS = (XPropertySet) UnoRuntime.queryInterface + (XPropertySet.class, xMathDoc); + try { + xPS.setPropertyValue("Formula", expFormula); + } catch(com.sun.star.lang.WrappedTargetException e) { + log.println("Exception occured while filling math document with content."); + failed("Couldn't create test environment"); + } catch(com.sun.star.lang.IllegalArgumentException e) { + log.println("Exception occured while filling math document with content."); + failed("Couldn't create test environment"); + } catch(com.sun.star.beans.PropertyVetoException e) { + log.println("Exception occured while filling math document with content."); + failed("Couldn't create test environment"); + } catch(com.sun.star.beans.UnknownPropertyException e) { + log.println("Exception occured while filling math document with content."); + failed("Couldn't create test environment"); + } + } + + private XComponent createNewImpressDoc(String frameName){ + XComponent xImpressDoc = null; + try{ + xImpressDoc = SOF.createImpressDoc(frameName); + } catch (com.sun.star.uno.Exception e) { + log.println("Exception occured while creating impress document '"+frameName+"':"); + failed("Couldn't create test environment"); + } + return xImpressDoc; + } + + + private void fillImpressDocWithContent(XComponent xImpressDoc){ + + log.println( "get presentation" ); + XPresentationSupplier oPS = (XPresentationSupplier) + UnoRuntime.queryInterface(XPresentationSupplier.class, xImpressDoc); + XInterface oObj = oPS.getPresentation(); + + log.println( "get custom presentation" ); + XCustomPresentationSupplier oCPS = (XCustomPresentationSupplier) + UnoRuntime.queryInterface( + XCustomPresentationSupplier.class, xImpressDoc); + XNameContainer xCP = oCPS.getCustomPresentations(); + + XInterface oInstance = null; + + XSingleServiceFactory oSingleMSF = (XSingleServiceFactory) + UnoRuntime.queryInterface(XSingleServiceFactory.class, xCP); + + try{ + oInstance = (XInterface) oSingleMSF.createInstance(); + } catch (com.sun.star.uno.Exception e) { + log.println("Could not create custom presentation while filling impress document with content."); + failed("Couldn't create test environment"); + } + + try { + xCP.insertByName("FirstPresentation",oInstance); + } catch (com.sun.star.lang.WrappedTargetException e) { + log.println("Could not instert custom presentation while filling impress document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.container.ElementExistException e) { + log.println("Could not instert custom presentation while filling impress document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.lang.IllegalArgumentException e) { + log.println("Could not instert custom presentation while filling impress document with content."); + failed("Couldn't create test environment"); + } + } + + private XComponent createNewDrawDoc(String frameName){ + XComponent xDrawDoc = null; + try{ + xDrawDoc = SOF.createDrawDoc(frameName); + } catch (com.sun.star.uno.Exception e) { + log.println("Exception occured while creating draw document '"+frameName+"':"); + failed("Couldn't create test environment"); + } + return xDrawDoc; + } + + private void fillDrawDocWithContent(XComponent xDrawDoc){ + XDrawPagesSupplier oDPS = (XDrawPagesSupplier) + UnoRuntime.queryInterface(XDrawPagesSupplier.class, xDrawDoc); + XDrawPages oDPn = oDPS.getDrawPages(); + XIndexAccess oDPi = (XIndexAccess) + UnoRuntime.queryInterface(XIndexAccess.class, oDPn); + XDrawPage oDP = null; + try { + oDP = (XDrawPage) AnyConverter.toObject( + new Type(XDrawPage.class),oDPi.getByIndex(0)); + } catch (com.sun.star.lang.WrappedTargetException e) { + log.println("Could not get draw pages while filling draw document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.lang.IndexOutOfBoundsException e) { + log.println("Could not get draw pages while filling draw document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.lang.IllegalArgumentException e) { + log.println("Could not get draw pages while filling draw document with content."); + failed("Couldn't create test environment"); + } + + //get a Shape + log.println( "getting Shape" ); + XShapes oShapes = (XShapes) UnoRuntime.queryInterface + (XShapes.class, oDP); + XInterface oObj = SOF.createShape + (xDrawDoc, 5000, 3500, 7500, 5000, "Rectangle"); + for (int i=0; i < 10; i++) { + oShapes.add( + SOF.createShape(xDrawDoc, + 5000, 3500, 7510 + 10 * i, 5010 + 10 * i, "Rectangle")); + } + XShape oShape = SOF.createShape + (xDrawDoc, 3000, 4500, 15000, 1000, "Ellipse"); + oShapes.add((XShape) oObj); + oShapes.add((XShape) oShape); + + + XPropertySet oShapeProps = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class, oObj); + XStyle aStyle = null; + try { + aStyle = (XStyle) AnyConverter.toObject( + new Type(XStyle.class),oShapeProps.getPropertyValue("Style")); + oShapeProps.setPropertyValue("ZOrder", new Integer(1)); + } catch (com.sun.star.lang.WrappedTargetException e) { + log.println("Exception occured while setting or getting property value while filling draw document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.beans.UnknownPropertyException e) { + log.println("Exception occured while setting or getting property value while filling draw document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.lang.IllegalArgumentException e) { + log.println("Exception occured while setting or getting property value while filling draw document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.beans.PropertyVetoException e) { + log.println("Exception occured while setting or getting property value while filling draw document with content."); + failed("Couldn't create test environment"); + } + } + + private void makeWriterDoc(String frameName, boolean withContent){ + log.println("creating Writer document '" + frameName + "'"); + XTextDocument xTextDoc = createNewWriterDoc(frameName); + if (withContent) fillWriterDocWithContent(xTextDoc); + positioningDocument((XModel) UnoRuntime.queryInterface(XModel.class, + xTextDoc)); + } + + private XTextDocument createNewWriterDoc(String frameName){ + XTextDocument xTextDoc = null; + try { + xTextDoc = SOF.createTextDoc(frameName); + } catch (com.sun.star.uno.Exception e) { + log.println("Exception occured while creating text document '"+frameName+"':"); + failed("Couldn't create test environment"); + } + return xTextDoc; + } + + private void fillWriterDocWithContent(XTextDocument xTextDoc){ + try{ + log.println( "inserting some lines" ); + XText oText = xTextDoc.getText(); + XTextCursor oCursor = oText.createTextCursor(); + for (int i=0; i<5; i++){ + oText.insertString( oCursor,"Paragraph Number: " + i, false); + oText.insertString( oCursor, + " The quick brown fox jumps over the lazy Dog: SwXParagraph", + false); + oText.insertControlCharacter( + oCursor, ControlCharacter.PARAGRAPH_BREAK, false ); + oText.insertString( oCursor, + "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG: SwXParagraph", + false); + oText.insertControlCharacter(oCursor, + ControlCharacter.PARAGRAPH_BREAK, false ); + oText.insertControlCharacter( + oCursor, ControlCharacter.LINE_BREAK, false ); + } + } catch ( com.sun.star.lang.IllegalArgumentException e ){ + log.println("Exception occured while filling text document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.uno.Exception e) { + log.println("Exception occured while filling text document with content."); + failed("Couldn't create test environment"); + } + } + + private XSpreadsheetDocument createNewCalcDoc(String frameName){ + + XSpreadsheetDocument xSheetDoc = null; + + try { + xSheetDoc = SOF.createCalcDoc(frameName); + } catch (com.sun.star.uno.Exception e) { + log.println("Exception occured while creating calc document '"+frameName+"':"); + failed("Couldn't create test environment"); + } + return xSheetDoc; + } + + private void fillCalcDocWithContent(XSpreadsheetDocument xSpreadsheetDoc){ + + try{ + XSpreadsheets oSpreadsheets = xSpreadsheetDoc.getSheets(); + + XSpreadsheet oSheet = (XSpreadsheet) AnyConverter.toObject( + new Type(XSpreadsheet.class), + oSpreadsheets.getByName( + oSpreadsheets.getElementNames()[0])); + + XCellRange testRange = oSheet.getCellRangeByName("$A$1:$D$4"); + + XSheetCellRange testSheetRange = (XSheetCellRange) UnoRuntime.queryInterface( + XSheetCellRange.class, + testRange); + oSheet.getCellByPosition(1, 1).setValue(1); + oSheet.getCellByPosition(4, 5).setValue(1); + oSheet.getCellByPosition(3, 2).setFormula("xTextDoc"); + oSheet.getCellByPosition(3, 3).setFormula("xTextDoc"); + } catch (com.sun.star.lang.WrappedTargetException e) { + log.println("Exception occured while filling calc document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.container.NoSuchElementException e) { + log.println("Exception occured while filling calc document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.lang.IndexOutOfBoundsException e) { + log.println("Exception occured while filling calc document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.lang.IllegalArgumentException e) { + log.println("Exception occured while filling calc document with content."); + failed("Couldn't create test environment"); + } catch (com.sun.star.uno.Exception e) { + log.println("Exception occured while filling calc document with content."); + failed("Couldn't create test environment"); + } + } + + /** + * copies all files from the backup folder into a folder called backup.recoveryTest + * and copies the Recovery.xcu to recovery.xcu.recoeryTest + */ + private void backupRecoveryData() + { + log.println("backup recovery data..."); + try{ + rt.copyRecoveryData(true); + }catch (com.sun.star.io.IOException e){ + failed("could not copy recovery data: " + e.toString()); + }catch (java.io.IOException e){ + failed("could not copy recovery data: " + e.toString()); + } + } + + /** + * copies all files from the backup.recoveryTest folder into the backup folder + * and copies the Recovery.xcu.recoveryTest to recovery.xcu + */ + private void restoreBackupRecoveryData() + { + log.println("restore backup recovery data..."); + try{ + rt.copyRecoveryData(false); + }catch (com.sun.star.io.IOException e){ + failed("could not copy recovery data: " + e.toString()); + }catch (java.io.IOException e){ + failed("could not copy recovery data: " + e.toString()); + } + } + + private void cleanRecoveryData(){ + try{ + log.println("bootstrapping the office to get user path to remove old recovery data..."); + + rt.cleanRecoveryData(); + + } catch (com.sun.star.io.IOException e){ + failed("could not clean recovery data: " + e.toString()); + } + } +} diff --git a/framework/qa/complex/framework/recovery/RecoveryTools.java b/framework/qa/complex/framework/recovery/RecoveryTools.java new file mode 100644 index 000000000000..28936949d8ef --- /dev/null +++ b/framework/qa/complex/framework/recovery/RecoveryTools.java @@ -0,0 +1,366 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package complex.framework.recovery; + +import com.sun.star.awt.XDialog; +import com.sun.star.awt.XExtendedToolkit; +import com.sun.star.awt.XWindow; +import com.sun.star.beans.NamedValue; +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.XModel; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.ucb.XSimpleFileAccess; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.util.URL; +import com.sun.star.util.XURLTransformer; +import helper.FileTools; +import helper.OfficeProvider; +import helper.UnoProvider; +import java.io.File; +import java.io.PrintWriter; +import java.util.HashMap; +import lib.TestParameters; +import share.LogWriter; +import util.PropertyName; +import util.UITools; +import util.utils; + +/** + * this class supports the <CODE>RecoverTest</CODE>. You will find here some helper + * functions. + */ +public class RecoveryTools { + + private final TestParameters param; + private final LogWriter log; + + /** + * Creates new OfficeWatcher + * @param param the test parameter + * @param log a log writer + */ + public RecoveryTools(TestParameters param, LogWriter log) { + this.param = param; + this.log = log; + + } + + /** + * get the active dialog from the top of the desktop + * @param xToolKit xToolKit the <CODE> XExtendedToolkit</CODE> to get the dialog from the top of the desktop. + * @return a <CODE>XDialog</CODE> interface of the dialog + */ + public XDialog getActiveDialog( XMultiServiceFactory xMSF){ + XWindow xWin = getActiveWindow(xMSF); + return (XDialog) UnoRuntime.queryInterface(XDialog.class, xWin); + } + + public XWindow getActiveWindow( XMultiServiceFactory xMSF){ + XInterface xToolKit = null; + try { + xToolKit = (XInterface) xMSF.createInstance("com.sun.star.awt.Toolkit") ; + } catch (com.sun.star.uno.Exception e) { + return null; + } + + XExtendedToolkit tk = (XExtendedToolkit) + UnoRuntime.queryInterface(XExtendedToolkit.class, xToolKit); + Object atw = tk.getActiveTopWindow(); + return (XWindow) UnoRuntime.queryInterface(XWindow.class, atw); + } + + /** + * After a crash the office start with a recovery diaolg. It could be that the office + * is connectable but not all services to get the dialog a loaded. This function + * tries to get the dialog until the <CODE>OfficeWatcher</CODE> kills the office. + * @param xToolKit the <CODE> XExtendedToolkit</CODE> to get the dialog from the top of the desktop. + * @return a <CODE>XDialog</CODE> interface of the dialog + */ + public XDialog getActiveDialogAfterStartup(XMultiServiceFactory xMSF){ + // while the office starts it takes some time to get the dialog. + + // the dialog is accessible AFTER the office has recoverd all documents. + // This could consumes more time then the TimeOut allow. + int counter = 0; + int multi = 5; + int pause = param.getInt(PropertyName.SHORT_WAIT)*10; + int timeOut = param.getInt(PropertyName.THREAD_TIME_OUT)*5; + int maximum = (timeOut / pause) * multi; + + XDialog oDialog = getActiveDialog(xMSF); + + while (oDialog == null && (counter < maximum)){ + log.println("waiting until the office has recoverd... remaining " + (timeOut * multi - pause * counter)/1000 + " seconds"); + pause(pause); + oDialog = getActiveDialog(xMSF); + counter ++; + } + return oDialog; + } + + /** + * halt the thread for some time + */ + public void pause(){ + pause(param.getInt(PropertyName.SHORT_WAIT)); + } + + /** + * halt the thread for some time + */ + public void pause(int sleepTime){ + sleep(sleepTime); + } + + private void sleep(long millis){ + try{ + Thread.sleep(millis); + }catch (java.lang.InterruptedException e){} + } + + /** + * remove the content of the user backup folder and removes the Recovery.xcu. This + * was done from the Office via XSimpleFileAccess + * @param xMSF a <CODE>XMultiServiceFactory</CODE> to get <CODE>XSimpleFileAccess</CODE> + * @throws com.sun.star.io.IOException the exception was thrown if something goes wrong. + */ + public void cleanRecoveryData() + throws com.sun.star.io.IOException + { + try{ + HashMap recFiles = getRecoveryFiles(); + + String recoveryFolder = (String) recFiles.get("recoveryFolder"); + String recoveryXCU = (String) recFiles.get("recoveryXCU"); + + log.println("try to remove content of '" + recoveryFolder + "'"); + + File rf = new File(recoveryFolder); + + boolean success = FileTools.cleanDir(rf); + log.println("removed " + recoveryFolder + ": " + success); + + log.println("try to remove '" + recoveryXCU + "'"); + + File xcu = new File(recoveryXCU); + if (xcu.isFile()){ + success = xcu.delete(); + log.println("removed " + recoveryXCU + " : " + success); + } + + } catch (Exception e){ + throw new com.sun.star.io.IOException("could not remove old recovery data: " + e.toString()); + } + } + + public HashMap getRecoveryFiles() + throws com.sun.star.io.IOException + { + try{ + log.println("try to get UnoProvider..."); + UnoProvider unoProv = new UnoProvider(); + XMultiServiceFactory xMSF = (XMultiServiceFactory) unoProv.getManager(param); + + String userPath = utils.expandMacro(xMSF, "${$ORIGIN/bootstraprc:UserInstallation}"); + System.out.println("userPath:'" + userPath + "'"); + + if (userPath.equals(""))userPath = utils.expandMacro(xMSF, "${$ORIGIN/bootstrap.ini:UserInstallation}"); + System.out.println("userPath:'" + userPath + "'"); + + if (userPath.equals("")) throw new com.sun.star.io.IOException("could not get user path at bootstraping"); + + String recoveryFolder = utils.getSystemURL(userPath + "/user/backup"); + + String recoveryXCU = utils.getSystemURL(userPath + "/user/registry/data/org/openoffice/Office/Recovery.xcu"); + + HashMap recFiles = new HashMap(); + + recFiles.put("recoveryFolder", recoveryFolder); + recFiles.put("recoveryXCU", recoveryXCU); + return recFiles; + + } catch (Exception e){ + throw new com.sun.star.io.IOException("could not get recovery folder: " + e.toString()); + } + + } + /** + * This function close the office while calling terminate on the desktop. If + * this failed, the <CODE>ProcessHandler</CODE> kills the process. + * @param xMSF the <CODE>XMultiServiceFactory</CODE> + * @return <CODE>TRUE</CODE> if no exception was thrown, otherwise <CODE>FALSE</CODE> + */ + public boolean closeOffice(XMultiServiceFactory xMSF) { + try { + XDesktop desk = (XDesktop) UnoRuntime.queryInterface( + XDesktop.class, xMSF.createInstance( + "com.sun.star.frame.Desktop")); + xMSF = null; + + desk.terminate(); + log.println("Waiting until ProcessHandler loose the office..."); + + } + catch (java.lang.Exception e) { + e.printStackTrace(); + return false; + } + waitForClosedOffice(); + return true; + } + + /** + * This function waits until the office is closed. If the closing time reach + * the value of parameter <CODE>THREAD_TIME_OUT</CODE> the office was killed. + */ + public void waitForClosedOffice(){ + // check for the office process + helper.ProcessHandler ph = (helper.ProcessHandler) param.get("AppProvider"); + + int timeOut = param.getInt(PropertyName.THREAD_TIME_OUT)*5; + int pause = param.getInt(PropertyName.SHORT_WAIT)*20; + int multi = 0; + while ((ph != null) && (ph.getExitCode()<0) && (pause*multi < timeOut)) { + log.println("waiting until the office is closed... remaining " + (timeOut - pause * multi)/1000 + " seconds"); + pause(pause); + multi ++; + } + + // be shure that office is closed + if (ph != null) ph.kill(); + } + + public void killOffice(){ + helper.ProcessHandler ph = (helper.ProcessHandler) param.get("AppProvider"); + ph.kill(); + } + + /** + * The office must be started WITH restore and crashreporter functionality. + * Therefore the parmater '<CODE>-norestore</CODE>' and '<CODE>-nocrashreport</CODE>' + * was removed from the <CODE>AppExecutionCommand</CODE> parameter + */ + public void removeParametersFromAppExecutionCommand(){ + + //remove some params to start office + String office = (String) param.get("AppExecutionCommand"); + String[] params = {"-norestore", "-nocrashreport"}; + + for (int i = 0; i < params.length; i++){ + int index = office.indexOf(params[i]); + int length = params[i].length(); + if (index != -1){ + office = office.substring(0, index) + office.substring(index + length); + log.println("removed '" + params[i] + "' from AppExecutionCommand: " + office); + } + } + param.put("AppExecutionCommand", office); + log.println("connect: " + (String) param.get("AppExecutionCommand")); + + } + + /** + * This function uses accessibility to handle modal dialogs like the + * "Are you sure" dialog. + * It cklick the named button given in parameter <CODE>buttonName</CODE> + * @param buttonName the name of the button wich should be chlicked + */ + public void handleModalDialog(XMultiServiceFactory xMSF, String buttonName) + throws com.sun.star.accessibility.IllegalAccessibleComponentStateException + { + + log.println("try to get modal Dialog..."); + + pause(); + + XWindow oDialog = getActiveWindow(xMSF); + + if (oDialog == null) throw new com.sun.star.accessibility.IllegalAccessibleComponentStateException("could not get modal Dialog"); + + + UITools oUITools = new UITools(xMSF, oDialog); + oUITools.printAccessibleTree((PrintWriter) log, param.getBool(PropertyName.DEBUG_IS_ACTIVE)); + + try{ + log.println("click ' " + buttonName + "' button.."); + oUITools.clickButton(buttonName); + } catch ( java.lang.Exception e){ + throw new com.sun.star.accessibility.IllegalAccessibleComponentStateException("Could not klick '"+buttonName +"' at modal dialog: " + e.toString()); + } + pause(); + } + + public void clickThreadButton(XMultiServiceFactory xMSF, XWindow xWindow, String buttonName) + throws com.sun.star.accessibility.IllegalAccessibleComponentStateException + { + KlickButtonThread kbt = new KlickButtonThread(xMSF, xWindow, buttonName); + kbt.start(); + pause(param.getInt(PropertyName.SHORT_WAIT) * 10); + } + + public void copyRecoveryData(boolean backup) + throws com.sun.star.io.IOException, java.io.IOException + { + HashMap recFiles = null; + + try{ + recFiles = getRecoveryFiles(); + } catch ( com.sun.star.io.IOException e){ + throw new com.sun.star.io.IOException("Could not get recovery files: " + e.toString()); + } + + try{ + String recoveryFolder = (String) recFiles.get("recoveryFolder"); + String recoveryXCU = (String) recFiles.get("recoveryXCU"); + + File recFolder = new File(recoveryFolder); + File recFolderBackup = new File(recoveryFolder+".recoveryTest"); + + File recXCU = new File(recoveryXCU); + File recXCUBackup = new File(recoveryXCU + ".recoveryTest"); + + if (backup){ + FileTools.copyDirectory(recFolder, recFolderBackup); + FileTools.copyFile(recXCU, recXCUBackup); + } else { + FileTools.copyDirectory(recFolderBackup, recFolder); + FileTools.copyFile(recXCUBackup, recXCU); + + } + } catch (java.io.IOException e){ + throw new java.io.IOException("Could not copy recovery files: " + e.toString()); + } + } + + +}
\ No newline at end of file diff --git a/framework/qa/complex/framework/recovery/TimeoutThread.java b/framework/qa/complex/framework/recovery/TimeoutThread.java new file mode 100644 index 000000000000..088f4b3d36f6 --- /dev/null +++ b/framework/qa/complex/framework/recovery/TimeoutThread.java @@ -0,0 +1,41 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package complex.framework.recovery; + +public class TimeoutThread extends Thread{ + public int timeOut = 0; + /** Creates a new instance of TimeoutThread */ + public TimeoutThread(int timeOut) { + this.timeOut = timeOut; + } + + public void run(){ + util.utils.shortWait(timeOut); + } + +} diff --git a/framework/qa/complex/framework/recovery/makefile.mk b/framework/qa/complex/framework/recovery/makefile.mk new file mode 100755 index 000000000000..4a9f80b4b2a5 --- /dev/null +++ b/framework/qa/complex/framework/recovery/makefile.mk @@ -0,0 +1,103 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ = ..$/..$/..$/.. +TARGET = RecoveryTest +PRJNAME = framework +PACKAGE = complex$/framework$/recovery + +# --- Settings ----------------------------------------------------- +.INCLUDE: settings.mk + + +#----- compile .java files ----------------------------------------- + +JARFILES = mysql.jar mysql.jar ridl.jar unoil.jar jurt.jar juh.jar java_uno.jar OOoRunner.jar +JAVAFILES = RecoveryTest.java RecoveryTools.java CrashThread.java TimeoutThread.java KlickButtonThread.java +JAVACLASSFILES = $(foreach,i,$(JAVAFILES) $(CLASSDIR)$/$(PACKAGE)$/$(i:b).class) + +#----- make a jar from compiled files ------------------------------ + +MAXLINELENGTH = 100000 + +JARCLASSDIRS = $(PACKAGE) +JARTARGET = $(TARGET).jar +JARCOMPRESS = TRUE + +#------ some information how to run the test ----------------------- + +MYTAR: ALLTAR + @echo + @echo ########################### N O T E ###################################### + @echo + @echo To run the test successfully you have to extend your LD_LIBRARY_PATH + @echo to your office program directory! + @echo Example: + @echo setenv LD_LIBRARY_PATH /myOffice/program:\$$LD_LIBRARY_PATH + @echo + @echo To run the you have to use the parameter cmd: + @echo cmd="PATH_TO_OFFICE_BINARY -accept=socket,host=localhost,port=8100;urp;" + @echo + @echo Example: + @echo dmake run cmd="/myOffice/program/soffice -accept=socket,host=localhost,port=8100;urp;" + @echo + + +# --- Parameters for the test -------------------------------------- + +# start an office if the parameter is set for the makefile +.IF "$(cmd)" == "" +CT_APPEXECCOMMAND = +.ELSE +CT_APPEXECCOMMAND = -cmd "$(cmd)" +#CT_APPEXECCOMMAND = -AppExecutionCommand \ +# "$(OFFICE)$/soffice -accept=socket,host=localhost,port=8100;urp;" +.ENDIF + +# test base is java complex +CT_TESTBASE = -TestBase java_complex + +# replace $/ with . in package name +CT_PACKAGE = -o $(PACKAGE:s\$/\.\) + +# start the runner application +CT_APP = org.openoffice.Runner + +CT_NOOFFICE = -NoOffice true +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +RUN: run +# muss noch angepasst werden: es soll auf -AppExecutionCommand und -NoOffice gepr??ft werden +#.IF "$(OFFICE)" == "" +#run: +# @echo Exit +#.ELSE +run: + +java -cp $(CLASSPATH) $(CT_APP) $(CT_TESTBASE) $(CT_APPEXECCOMMAND) $(CT_NOOFFICE) $(CT_PACKAGE).RecoveryTest + |