Tag: cws_dev300_odbmacros3 User: fs Date: 2008-07-30 20:54:22+0000 Modified: dba/dbaccess/qa/complex/dbaccess/DatabaseDocument.java
Log: #i76128# more event testing - covered everything now except - OnNew: no valid implementation exists which could trigger this - OnSaveAsFailed, OnSaveFailed, OnSaveToFaile - OnTitleChanged: this is unreliable (notified too often) due to a bug in framework's TitleHelper implementation File Changes: Directory: /dba/dbaccess/qa/complex/dbaccess/ ============================================= File [changed]: DatabaseDocument.java Url: http://dba.openoffice.org/source/browse/dba/dbaccess/qa/complex/dbaccess/DatabaseDocument.java?r1=1.1.2.4&r2=1.1.2.5 Delta lines: +365 -64 ---------------------- --- DatabaseDocument.java 2008-07-30 08:50:55+0000 1.1.2.4 +++ DatabaseDocument.java 2008-07-30 20:54:19+0000 1.1.2.5 @@ -7,7 +7,7 @@ * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: DatabaseDocument.java,v $ - * $Revision: 1.1.2.4 $ + * $Revision: 1.1.2.5 $ * * This file is part of OpenOffice.org. * @@ -29,6 +29,8 @@ ************************************************************************/ package complex.dbaccess; +import com.sun.star.awt.XTopWindow; +import com.sun.star.awt.XTopWindow; import com.sun.star.beans.PropertyState; import com.sun.star.document.DocumentEvent; import com.sun.star.lang.XEventListener; @@ -41,10 +43,15 @@ import com.sun.star.beans.XPropertySet; import com.sun.star.container.XSet; import com.sun.star.document.XDocumentEventListener; +import com.sun.star.document.XEventBroadcaster; import com.sun.star.document.XEventsSupplier; import com.sun.star.document.XScriptInvocationContext; +import com.sun.star.frame.DoubleInitializationException; import com.sun.star.lang.XComponent; import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XDispatch; +import com.sun.star.frame.XDispatchProvider; +import com.sun.star.frame.XFrame; import com.sun.star.frame.XLoadable; import com.sun.star.frame.XModel; import com.sun.star.frame.XModel2; @@ -59,7 +66,10 @@ import com.sun.star.sdb.XReportDocumentsSupplier; import com.sun.star.uno.XComponentContext; import com.sun.star.util.CloseVetoException; +import com.sun.star.util.URL; import com.sun.star.util.XCloseable; +import com.sun.star.util.XModifiable; +import com.sun.star.util.XURLTransformer; import connectivity.tools.*; import helper.FileTools; import java.io.File; @@ -69,8 +79,12 @@ import java.util.Iterator; import java.util.Vector; -public class DatabaseDocument extends CRMBasedTestCase +public class DatabaseDocument extends CRMBasedTestCase implements com.sun.star.document.XEventListener { + private XComponent m_callbackFactory = null; + private Vector m_documentEvents = new Vector(); + private Vector m_globalEvents = new Vector(); + /** a helper class which can be used by the Basic scripts in our test documents * to notify us of events in this document */ @@ -78,7 +92,7 @@ { public void documentEventOccured( DocumentEvent _event ) { - onEventCallback( _event ); + onDocumentEvent( _event ); } public void disposing( com.sun.star.lang.EventObject _Event ) @@ -147,15 +161,13 @@ } }; - private XComponent m_callbackFactory = null; - // -------------------------------------------------------------------------------------------------------- public String[] getTestMethodNames() { return new String[] { "testLoadable", - "testStorable" + "testDocumentEvents" }; } @@ -178,12 +190,17 @@ XSet.class, getORB() ); m_callbackFactory = new CallbackComponentFactory(); globalFactory.insert( m_callbackFactory ); + + // register ourself as listener at the global event broadcaster + XEventBroadcaster broadcaster = (XEventBroadcaster)UnoRuntime.queryInterface( + XEventBroadcaster.class, getORB().createInstance( "com.sun.star.frame.GlobalEventBroadcaster" ) ); + broadcaster.addEventListener( this ); } catch( Exception e ) { System.err.println( "could not create the test case, error message:\n" + e.getMessage() ); e.printStackTrace( System.err ); - failed( "failed to created the test case" ); + failed( "failed to create the test case" ); } } @@ -192,9 +209,23 @@ { super.after(); + try + { // dispose our callback factory. This will automatically remove it from our service // factory m_callbackFactory.dispose(); + + // revoke ourself as listener at the global event broadcaster + XEventBroadcaster broadcaster = (XEventBroadcaster) UnoRuntime.queryInterface( + XEventBroadcaster.class, getORB().createInstance( "com.sun.star.frame.GlobalEventBroadcaster" ) ); + broadcaster.removeEventListener( this ); + } + catch ( Exception e ) + { + System.err.println( "could not close the test case, error message:\n" + e.getMessage() ); + e.printStackTrace( System.err ); + failed( "failed to close the test case" ); + } } // -------------------------------------------------------------------------------------------------------- @@ -210,21 +241,6 @@ } // -------------------------------------------------------------------------------------------------------- - public void testStorable() throws Exception - { - Object object = getFactory().createInstance("com.sun.star.frame.Desktop"); - XComponentLoader xComponentLoader = (XComponentLoader)UnoRuntime.queryInterface(XComponentLoader.class, object); - XComponent xComponent = xComponentLoader.loadComponentFromURL( m_database.getDatabase().getDocumentURL(), "_blank", FrameSearchFlag.ALL, new PropertyValue[0] ); - m_database.close(); - - XStorable storable = (XStorable)UnoRuntime.queryInterface(XStorable.class,xComponent); - storable.store(); - - XCloseable close = (XCloseable)UnoRuntime.queryInterface(XCloseable.class,xComponent); - close.close(true); - } - - // -------------------------------------------------------------------------------------------------------- private class UnoMethodDescriptor { public Class unoInterfaceClass = null; @@ -261,35 +277,48 @@ } // -------------------------------------------------------------------------------------------------------- - private String impl_copyTempFile( String sourceURL ) throws IOException, URISyntaxException + private String impl_copyTempFile( String _sourceURL ) throws IOException + { + String targetURL = createTempFileURL(); + try { - File targetFile = File.createTempFile( getTestObjectName(), ".odb" ); - FileTools.copyFile( new File( new URI( sourceURL ) ), targetFile ); - targetFile.deleteOnExit(); - return targetFile.getAbsoluteFile().toURL().toString(); + FileTools.copyFile( new File( new URI( _sourceURL ) ), new File( new URI( targetURL ) ) ); } + catch ( URISyntaxException e ) { } - // -------------------------------------------------------------------------------------------------------- - private XModel impl_cloneDocument( XModel _databaseDoc ) throws CloseVetoException, IOException, URISyntaxException, Exception + if ( ( targetURL.indexOf( "file:/" ) == 0 ) && ( targetURL.indexOf( "file:///" ) == -1 ) ) { - // close the document after rememberinbg its URL - String documentURL = _databaseDoc.getURL(); - XCloseable closeDoc = (XCloseable)UnoRuntime.queryInterface( XCloseable.class, - _databaseDoc ); - closeDoc.close( true ); + // for some reason, the URLs here in Java start with "file:/" only, instead of "file:///" + // Some of the office code doesn't like this ... + targetURL = "file:///" + targetURL.substring( 6 ); + } - // clone the file - documentURL = impl_copyTempFile( documentURL ); + return targetURL; + } + // -------------------------------------------------------------------------------------------------------- + private XModel impl_createDocument( ) throws Exception + { XModel databaseDoc = (XModel)UnoRuntime.queryInterface( XModel.class, getORB().createInstance( "com.sun.star.sdb.OfficeDatabaseDocument" ) ); + + // should not be initialized here - we did neither initNew nor load nor storeAsURL it impl_checkDocumentInitState( databaseDoc, false ); return databaseDoc; } // -------------------------------------------------------------------------------------------------------- - public void testLoadable() throws Exception, IOException, URISyntaxException + private void impl_closeDocument( XModel _databaseDoc ) throws CloseVetoException, IOException, Exception + { + // close the document after rememberinbg its URL + XCloseable closeDoc = (XCloseable)UnoRuntime.queryInterface( XCloseable.class, + _databaseDoc ); + closeDoc.close( true ); + } + + // -------------------------------------------------------------------------------------------------------- + private XModel impl_createEmptyEmbeddedHSQLDocument() throws Exception, IOException { XModel databaseDoc = (XModel)UnoRuntime.queryInterface( XModel.class, getORB().createInstance( "com.sun.star.sdb.OfficeDatabaseDocument" ) ); @@ -308,25 +337,13 @@ assureEquals( "Args are expected to be empty here", 0, args.length ); // and, you should be able to set properties at the data source - { XOfficeDatabaseDocument dataSourceAccess = (XOfficeDatabaseDocument)UnoRuntime.queryInterface( XOfficeDatabaseDocument.class, databaseDoc ); XPropertySet dsProperties = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, dataSourceAccess.getDataSource() ); dsProperties.setPropertyValue( "URL", "sdbc:embedded:hsqldb" ); - } - // there's three methods how you can initialize a database document - // 1. XLoadable::initNew - // 2. XLoadable::load - // 3. XStorable::storeAsURL - // (this is for compatibility reasons, to not break existing code) - - // .................................................................... - // Let's check 3. first - File documentFile = java.io.File.createTempFile( getTestObjectName(), ".odb" ); - documentFile.deleteOnExit(); - String documentURL = documentFile.getAbsoluteFile().toURL().toString(); + String documentURL = createTempFileURL(); storeDoc.storeAsURL( documentURL, new PropertyValue[] {} ); // now that the document is stored, ... @@ -335,10 +352,25 @@ // ... it should be initialized impl_checkDocumentInitState( databaseDoc, true ); + return databaseDoc; + } + + // -------------------------------------------------------------------------------------------------------- + public void testLoadable() throws Exception, IOException + { + XModel databaseDoc = impl_createEmptyEmbeddedHSQLDocument(); + String documentURL = databaseDoc.getURL(); + + // there's three methods how you can initialize a database document: + // .................................................................... - // then 2. - databaseDoc = impl_cloneDocument( databaseDoc ); + // 1. XStorable::storeAsURL + // (this is for compatibility reasons, to not break existing code) + // this test is already made in impl_createEmptyEmbeddedHSQLDocument + // .................................................................... + // 2. XLoadable::load + documentURL = impl_copyTempFile( documentURL ); // load the doc, and verify it's initialized then, and has the proper URL XLoadable loadDoc = (XLoadable)UnoRuntime.queryInterface( XLoadable.class, databaseDoc ); loadDoc.load( new PropertyValue[] { new PropertyValue( "URL", 0, documentURL, PropertyState.DIRECT_VALUE ) } ); @@ -347,16 +379,285 @@ assureEquals( "wrong URL after loading the document", documentURL, databaseDoc.getURL() ); impl_checkDocumentInitState( databaseDoc, true ); + // and while we are here ... initilizing the same document again should not be possible + verifyExpectedException( databaseDoc, XLoadable.class, "initNew", new Object[0], + DoubleInitializationException.class ); + verifyExpectedException( databaseDoc, XLoadable.class, "load", new Object[] { new PropertyValue[0] }, + DoubleInitializationException.class ); + // .................................................................... - // then 1. - databaseDoc = impl_cloneDocument( databaseDoc ); + // 3. XLoadable::initNew + impl_closeDocument( databaseDoc ); + databaseDoc = impl_createDocument(); loadDoc = (XLoadable)UnoRuntime.queryInterface( XLoadable.class, databaseDoc ); loadDoc.initNew(); assureEquals( "wrong URL after initializing the document", "", databaseDoc.getURL() ); impl_checkDocumentInitState( databaseDoc, true ); + + // same as above - initializing the document a second time must fail + verifyExpectedException( databaseDoc, XLoadable.class, "initNew", new Object[0], + DoubleInitializationException.class ); + verifyExpectedException( databaseDoc, XLoadable.class, "load", new Object[] { new PropertyValue[0] }, + DoubleInitializationException.class ); + } + + // -------------------------------------------------------------------------------------------------------- + public void testDocumentEvents() throws Exception, IOException + { + XModel databaseDoc = impl_createEmptyEmbeddedHSQLDocument(); + XStorable storeDoc = (XStorable) UnoRuntime.queryInterface( XStorable.class, + databaseDoc ); + + String oldURL = null, newURL = null, context = null; + + // XStorable.store + oldURL = databaseDoc.getURL(); + context = "store"; + impl_startObservingEvents( context ); + storeDoc.store(); + assureEquals( "store is not expected to change the document URL", databaseDoc.getURL(), oldURL ); + impl_stopObservingEvents( m_globalEvents, new String[] { "OnSave", "OnSaveDone" }, context ); + + // XStorable.storeToURL + context = "storeToURL"; + impl_startObservingEvents( context ); + storeDoc.storeToURL( createTempFileURL(), new PropertyValue[0] ); + assureEquals( "storetoURL is not expected to change the document URL", databaseDoc.getURL(), oldURL ); + impl_stopObservingEvents( m_globalEvents, new String[] { "OnSaveTo", "OnSaveToDone" }, context ); + + // XStorable.storeAsURL + newURL = createTempFileURL(); + context = "storeAsURL"; + impl_startObservingEvents( context ); + storeDoc.storeAsURL( newURL, new PropertyValue[0] ); + assureEquals( "storeAsURL is expected to change the document URL", databaseDoc.getURL(), newURL ); + impl_stopObservingEvents( m_globalEvents, new String[] { "OnSaveAs", "OnSaveAsDone" }, context ); + + // XModifiable.setModified + XModifiable modifyDoc = (XModifiable) UnoRuntime.queryInterface( XModifiable.class, + databaseDoc ); + context = "setModified"; + impl_startObservingEvents( context ); + modifyDoc.setModified( true ); + assureEquals( "setModified didn't work", modifyDoc.isModified(), true ); + impl_stopObservingEvents( m_globalEvents, new String[] { "OnModifyChanged" }, context ); + + // XStorable.store, with implicit reset of the "Modified" flag + context = "store (2)"; + impl_startObservingEvents( context ); + storeDoc.store(); + assureEquals( "'store' should implicitly reset the modified flag", modifyDoc.isModified(), false ); + impl_stopObservingEvents( m_globalEvents, new String[] { "OnSave", "OnSaveDone", "OnModifyChanged" }, context ); + + // XComponentLoader.loadComponentFromURL + newURL = impl_copyTempFile( databaseDoc.getURL() ); + XComponentLoader loader = (XComponentLoader)UnoRuntime.queryInterface( XComponentLoader.class, + getORB().createInstance( "com.sun.star.frame.Desktop" ) ); + context = "loadComponentFromURL"; + impl_startObservingEvents( context ); + databaseDoc = (XModel) UnoRuntime.queryInterface( XModel.class, + loader.loadComponentFromURL( newURL, "_blank", 0, new PropertyValue[0] ) ); + impl_stopObservingEvents( m_globalEvents, + new String[] { "OnLoadFinished", "OnViewCreated", "OnFocus", "OnLoad" }, context ); + + // closing a document by API + XCloseable closeDoc = (XCloseable) UnoRuntime.queryInterface( XCloseable.class, + databaseDoc ); + context = "close (API)"; + impl_startObservingEvents( context ); + closeDoc.close( true ); + impl_stopObservingEvents( m_globalEvents, + new String[] { "OnPrepareUnload", "OnViewClosed", "OnUnload" }, context ); + + // closing a document via UI + context = "close (UI)"; + impl_startObservingEvents( "prepare for '" + context + "'" ); + databaseDoc = (XModel)UnoRuntime.queryInterface( XModel.class, + loader.loadComponentFromURL( newURL, "_blank", 0, new PropertyValue[0] ) ); + impl_waitForEvent( m_globalEvents, "OnLoad", 5000 ); + // wait for all events to arrive - OnLoad should be the last one + + XDispatchProvider dispatchProvider = (XDispatchProvider) UnoRuntime.queryInterface( XDispatchProvider.class, + databaseDoc.getCurrentController().getFrame() ); + URL url = impl_getURL( ".uno:CloseDoc" ); + XDispatch dispatcher = dispatchProvider.queryDispatch( url, "", 0 ); + impl_startObservingEvents( context ); + dispatcher.dispatch( url, new PropertyValue[0] ); + impl_stopObservingEvents( m_globalEvents, + new String[] { "OnPrepareViewClosing", "OnViewClosed", "OnPrepareUnload", "OnUnload" }, context ); + + // creating a new document + databaseDoc = impl_createDocument(); + XLoadable loadDoc = (XLoadable) UnoRuntime.queryInterface( XLoadable.class, + databaseDoc ); + context = "initNew"; + impl_startObservingEvents( context ); + loadDoc.initNew(); + impl_stopObservingEvents( m_globalEvents, new String[] { "OnCreate" }, context ); + + impl_startObservingEvents( context + " (cleanup)" ); + impl_closeDocument( databaseDoc ); + impl_waitForEvent( m_globalEvents, "OnUnload", 5000 ); + + // focus changes + context = "activation"; + // for this, load a database document ... + impl_startObservingEvents( "prepare for '" + context + "'" ); + databaseDoc = (XModel)UnoRuntime.queryInterface( XModel.class, + loader.loadComponentFromURL( newURL, "_blank", 0, new PropertyValue[0] ) ); + impl_waitForEvent( m_globalEvents, "OnLoad", 5000 ); + // ... and another document ... + String otherURL = impl_copyTempFile( databaseDoc.getURL() ); + XModel otherDoc = (XModel)UnoRuntime.queryInterface( XModel.class, + loader.loadComponentFromURL( otherURL, "_blank", 0, new PropertyValue[0] ) ); + impl_raise( otherDoc ); + + // ... and switch between the two + impl_startObservingEvents( context ); + impl_raise( databaseDoc ); + impl_stopObservingEvents( m_globalEvents, new String[] { "OnUnfocus", "OnFocus" }, context ); + + // cleanup + impl_closeDocument( databaseDoc ); + impl_closeDocument( otherDoc ); + } + + // -------------------------------------------------------------------------------------------------------- + private URL impl_getURL( String _completeURL ) throws Exception + { + URL[] url = { new URL() }; + url[0].Complete = _completeURL; + XURLTransformer urlTransformer = (XURLTransformer) UnoRuntime.queryInterface( XURLTransformer.class, + getORB().createInstance( "com.sun.star.util.URLTransformer" ) ); + urlTransformer.parseStrict( url ); + return url[0]; + } + + // -------------------------------------------------------------------------------------------------------- + private void impl_raise( XModel _document ) + { + XFrame frame = _document.getCurrentController().getFrame(); + XTopWindow topWindow = (XTopWindow) UnoRuntime.queryInterface( XTopWindow.class, + frame.getContainerWindow() ); + topWindow.toFront(); + } + + // -------------------------------------------------------------------------------------------------------- + private void impl_startObservingEvents( String _context ) + { + log.println( " " + _context ); + synchronized ( m_documentEvents ) + { + m_documentEvents.clear(); + } + synchronized ( m_globalEvents ) + { + m_globalEvents.clear(); + } + } + + // -------------------------------------------------------------------------------------------------------- + private void impl_stopObservingEvents( Vector _actualEvents, String[] _expectedEvents, String _context ) + { + synchronized ( _actualEvents ) + { + int actualEventCount = _actualEvents.size(); + while ( actualEventCount < _expectedEvents.length ) + { + // well, it's possible not all events already arrived, yet - finally, some of them + // are notified asynchronously + // So, wait a few seconds. + try + { + _actualEvents.wait( 5000 ); + } + catch ( InterruptedException ex ) { } + + if ( actualEventCount == _actualEvents.size() ) + // the above wait was left because of the timeout, *not* because an event + // arrived. Okay, we won't wait any longer, this is a failure. + break; + actualEventCount = _actualEvents.size(); + } + + assureEquals( "wrong event count for '" + _context + "'", + _expectedEvents.length, _actualEvents.size() ); + + for ( int i=0; i<_expectedEvents.length; ++i ) + { + assureEquals( "wrong event at positon " + ( i + 1 ) + " for '" + _context + "'", + _expectedEvents[i], _actualEvents.get(i) ); + } + } + } + + // -------------------------------------------------------------------------------------------------------- + void impl_waitForEvent( Vector _eventQueue, String _expectedEvent, int _maxMilliseconds ) + { + synchronized ( _eventQueue ) + { + int waitedMilliseconds = 0; + + while ( waitedMilliseconds < _maxMilliseconds ) + { + for ( int i=0; i<_eventQueue.size(); ++i ) + { + if ( _expectedEvent.equals( _eventQueue.get(i) ) ) + // found the event in the queue + return; + } + + // wait a little, perhaps the event will still arrive + try + { + _eventQueue.wait( 500 ); + waitedMilliseconds += 500; + } + catch ( InterruptedException e ) { } + } + } + + failed( "expected event '" + _expectedEvent + "' did not arrive after " + _maxMilliseconds + " milliseconds" ); + } + + // -------------------------------------------------------------------------------------------------------- + void onDocumentEvent( DocumentEvent _Event ) + { + if ( _Event.EventName.equals( "OnTitleChanged" ) ) + // OnTitleChanged events are notified too often. This is known, and accepted. + // (the deeper reason is that it's diffult to determine, in the DatabaseDocument implementatin, + // when the title actually changed. In particular, when we do a saveAsURL, and then ask for a + // title *before* the TitleHelper got the document's OnSaveAsDone event, then the wrong (old) + // title is obtained. + return; + + synchronized ( m_documentEvents ) + { + m_documentEvents.add( _Event.EventName ); + m_documentEvents.notifyAll(); + } + + log.println( " document event: " + _Event.EventName ); + } + + // -------------------------------------------------------------------------------------------------------- + public void notifyEvent( com.sun.star.document.EventObject _Event ) + { + if ( _Event.EventName.equals( "OnTitleChanged" ) ) + // ignore. See onDocumentEvent for a justification + return; + + synchronized ( m_globalEvents ) + { + m_globalEvents.add( _Event.EventName ); + m_globalEvents.notifyAll(); + } + + log.println( " global event: " + _Event.EventName ); } - void onEventCallback( DocumentEvent _Event ) + public void disposing( EventObject _Event ) { + throw new UnsupportedOperationException( "Not supported yet." ); } } --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
