diff options
Diffstat (limited to 'dbaccess/qa/complex')
17 files changed, 4069 insertions, 0 deletions
diff --git a/dbaccess/qa/complex/dbaccess/ApplicationController.java b/dbaccess/qa/complex/dbaccess/ApplicationController.java new file mode 100644 index 000000000000..41bf379f7a91 --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/ApplicationController.java @@ -0,0 +1,203 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ApplicationController.java,v $ + * $Revision: 1.1.2.1 $ + * + * 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.dbaccess; + +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XNameAccess; +import com.sun.star.frame.FrameSearchFlag; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XModel; +import com.sun.star.frame.XStorable; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.sdb.XOfficeDatabaseDocument; +import com.sun.star.sdb.application.XDatabaseDocumentUI; +import com.sun.star.sdbcx.XTablesSupplier; +import com.sun.star.uno.Exception; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import connectivity.tools.HsqlColumnDescriptor; +import connectivity.tools.HsqlDatabase; +import connectivity.tools.HsqlTableDescriptor; +import helper.URLHelper; +import java.io.File; +import java.io.IOException; + +/** complex test case for Base's application UI + */ +public class ApplicationController extends complexlib.ComplexTestCase +{ + private HsqlDatabase m_database; + private XOfficeDatabaseDocument m_databaseDocument; + private XDatabaseDocumentUI m_documentUI; + + public ApplicationController() + { + super(); + } + + // -------------------------------------------------------------------------------------------------------- + public String[] getTestMethodNames() + { + return new String[] { + "checkSaveAs" + }; + } + + // -------------------------------------------------------------------------------------------------------- + public String getTestObjectName() + { + return getClass().getName(); + } + + // -------------------------------------------------------------------------------------------------------- + protected final XMultiServiceFactory getORB() + { + return (XMultiServiceFactory)param.getMSF(); + } + + // -------------------------------------------------------------------------------------------------------- + protected final XComponentContext getComponentContext() + { + XComponentContext context = null; + try + { + XPropertySet orbProps = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, getORB() ); + context = (XComponentContext)UnoRuntime.queryInterface( XComponentContext.class, + orbProps.getPropertyValue( "DefaultContext" ) ); + } + catch ( Exception ex ) + { + failed( "could not retrieve the ComponentContext" ); + } + return context; + } + + // -------------------------------------------------------------------------------------------------------- + private void impl_closeDocument() + { + if ( m_database != null ) + { + m_database.close(); + m_database = null; + m_databaseDocument = null; + m_documentUI = null; + } + } + + // -------------------------------------------------------------------------------------------------------- + private void impl_switchToDocument( String _documentURL ) throws java.lang.Exception + { + // close previous database document + impl_closeDocument(); + + // create/load the new database document + m_database = ( _documentURL == null ) + ? new HsqlDatabase( getORB() ) + : new HsqlDatabase( getORB(), _documentURL ); + m_databaseDocument = m_database.getDatabaseDocument(); + + // load it into a frame + Object object = getORB().createInstance( "com.sun.star.frame.Desktop" ); + XComponentLoader xComponentLoader = (XComponentLoader)UnoRuntime.queryInterface( XComponentLoader.class, object ); + XComponent loadedComponent = xComponentLoader.loadComponentFromURL( m_database.getDocumentURL(), "_blank", FrameSearchFlag.ALL, new PropertyValue[0] ); + + assure( "too many document instances!", + UnoRuntime.areSame( loadedComponent, m_databaseDocument ) ); + + // get the controller, which provides access to various UI operations + XModel docModel = (XModel)UnoRuntime.queryInterface( XModel.class, + loadedComponent ); + m_documentUI = (XDatabaseDocumentUI)UnoRuntime.queryInterface( XDatabaseDocumentUI.class, + docModel.getCurrentController() ); + } + + // -------------------------------------------------------------------------------------------------------- + public void before() throws Exception, java.lang.Exception + { + impl_switchToDocument( null ); + } + + // -------------------------------------------------------------------------------------------------------- + public void after() + { + impl_closeDocument(); + } + + // -------------------------------------------------------------------------------------------------------- + public void checkSaveAs() throws Exception, IOException, java.lang.Exception + { + // issue 93737 describes the problem that when you save-as a database document, and do changes to it, + // then those changes are saved in the old document, actually + String oldDocumentURL = m_database.getDocumentURL(); + + File documentFile = java.io.File.createTempFile( getTestObjectName(), ".odb" ); + documentFile.deleteOnExit(); + String newDocumentURL = URLHelper.getFileURLFromSystemPath( documentFile.getAbsoluteFile() ); + + // store the doc in a new location + XStorable storeDoc = (XStorable)UnoRuntime.queryInterface( XStorable.class, + m_databaseDocument ); + storeDoc.storeAsURL( newDocumentURL, new PropertyValue[] {} ); + + // connect + m_documentUI.connect(); + assure( "could not connect to " + m_database.getDocumentURL(), m_documentUI.isConnected() ); + + // create a table in the database + m_database.createTable( new HsqlTableDescriptor( "abc", new HsqlColumnDescriptor[] { + new HsqlColumnDescriptor( "a", "VARCHAR(50)" ), + new HsqlColumnDescriptor( "b", "VARCHAR(50)" ), + new HsqlColumnDescriptor( "c", "VARCHAR(50)" ) + } + ) ); + + // load the old document, and verify there is *no* table therein + impl_switchToDocument( oldDocumentURL ); + m_documentUI.connect(); + assure( "could not connect to " + m_database.getDocumentURL(), m_documentUI.isConnected() ); + XTablesSupplier suppTables = (XTablesSupplier)UnoRuntime.queryInterface( XTablesSupplier.class, + m_documentUI.getActiveConnection() ); + XNameAccess tables = suppTables.getTables(); + assure( "the table was created in the wrong database", !tables.hasByName( "abc" ) ); + + // load the new document, and verify there *is* a table therein + impl_switchToDocument( newDocumentURL ); + m_documentUI.connect(); + assure( "could not connect to " + m_database.getDocumentURL(), m_documentUI.isConnected() ); + + suppTables = (XTablesSupplier)UnoRuntime.queryInterface( XTablesSupplier.class, + m_documentUI.getActiveConnection() ); + tables = suppTables.getTables(); + assure( "the newly created table has not been written", tables.hasByName( "abc" ) ); + } +} diff --git a/dbaccess/qa/complex/dbaccess/CRMBasedTestCase.java b/dbaccess/qa/complex/dbaccess/CRMBasedTestCase.java new file mode 100644 index 000000000000..edcb62579aac --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/CRMBasedTestCase.java @@ -0,0 +1,86 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: CRMBasedTestCase.java,v $ + * $Revision: 1.1.6.6 $ + * + * 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.dbaccess; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.sdb.XSingleSelectQueryComposer; +import com.sun.star.uno.UnoRuntime; +import java.util.logging.Level; +import java.util.logging.Logger; + +public abstract class CRMBasedTestCase extends TestCase +{ + protected CRMDatabase m_database; + + // -------------------------------------------------------------------------------------------------------- + protected void createTestCase() + { + try + { + m_database = new CRMDatabase( getORB() ); + } + catch ( Exception e ) + { + e.printStackTrace( System.err ); + assure( "caught an exception (" + e.getMessage() + ") while creating the test case", false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + public void before() + { + createTestCase(); + } + + // -------------------------------------------------------------------------------------------------------- + public void after() + { + try + { + if ( m_database != null ) + m_database.close(); + } + catch ( Exception ex ) + { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, null, ex ); + } + } + + // -------------------------------------------------------------------------------------------------------- + /** creates a SingleSelectQueryComposer for our connection + */ + protected final XSingleSelectQueryComposer createQueryComposer() throws com.sun.star.uno.Exception + { + XMultiServiceFactory connectionFactory = (XMultiServiceFactory)UnoRuntime.queryInterface( + XMultiServiceFactory.class, m_database.getConnection() ); + return (XSingleSelectQueryComposer)UnoRuntime.queryInterface( + XSingleSelectQueryComposer.class, connectionFactory.createInstance( "com.sun.star.sdb.SingleSelectQueryComposer" ) ); + } +} diff --git a/dbaccess/qa/complex/dbaccess/CRMDatabase.java b/dbaccess/qa/complex/dbaccess/CRMDatabase.java new file mode 100644 index 000000000000..ca28c1c769e7 --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/CRMDatabase.java @@ -0,0 +1,236 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: CRMDatabase.java,v $ + * $Revision: 1.6.2.1 $ + * + * 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.dbaccess; + +import com.sun.star.container.ElementExistException; +import com.sun.star.io.IOException; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.sdb.XSingleSelectQueryComposer; +import com.sun.star.sdbc.SQLException; +import com.sun.star.sdbc.XConnection; +import com.sun.star.sdbcx.XTablesSupplier; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.XRefreshable; +import connectivity.tools.DataSource; +import connectivity.tools.HsqlColumnDescriptor; +import connectivity.tools.HsqlDatabase; +import connectivity.tools.HsqlTableDescriptor; +import connectivity.tools.QueryDefinition; + +/** implements a small Customer Relationship Management database + * + * Not finished, by far. Feel free to add features as you need them. + */ +public class CRMDatabase +{ + private XMultiServiceFactory m_orb; + private HsqlDatabase m_database; + private DataSource m_dataSource; + private XConnection m_connection; + + /** constructs the CRM database + */ + public CRMDatabase( XMultiServiceFactory _orb ) throws Exception + { + m_orb = _orb; + + m_database = new HsqlDatabase( m_orb ); + m_dataSource = m_database.getDataSource(); + m_connection = m_database.defaultConnection(); + createTables(); + createQueries(); + } + + // -------------------------------------------------------------------------------------------------------- + /** returns the database document underlying the CRM database + */ + public final HsqlDatabase getDatabase() + { + return m_database; + } + + // -------------------------------------------------------------------------------------------------------- + /** returns the default connection to the database + */ + public final XConnection getConnection() + { + return m_connection; + } + + // -------------------------------------------------------------------------------------------------------- + public void close() throws SQLException, IOException + { + m_database.store(); + m_database.close(); + } + + // -------------------------------------------------------------------------------------------------------- + private void createTables() throws SQLException + { + HsqlTableDescriptor table = new HsqlTableDescriptor( "categories", + new HsqlColumnDescriptor[] { + new HsqlColumnDescriptor( "ID", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "Name", "VARCHAR(50)" ), + new HsqlColumnDescriptor( "Description", "VARCHAR(1024)" ), + new HsqlColumnDescriptor( "Image", "LONGVARBINARY" ) } ); + m_database.createTable( table, true ); + + m_database.executeSQL( "INSERT INTO \"categories\" ( \"ID\", \"Name\" ) VALUES ( 1, 'Food' )" ); + m_database.executeSQL( "INSERT INTO \"categories\" ( \"ID\", \"Name\" ) VALUES ( 2, 'Furniture' )" ); + + table = new HsqlTableDescriptor( "products", + new HsqlColumnDescriptor[] { + new HsqlColumnDescriptor( "ID", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "Name", "VARCHAR(50)" ), + new HsqlColumnDescriptor( "CategoryID", "INTEGER", HsqlColumnDescriptor.REQUIRED, "categories", "ID" ) } ); + m_database.createTable( table, true ); + + m_database.executeSQL( "INSERT INTO \"products\" VALUES ( 1, 'Oranges', 1 )" ); + m_database.executeSQL( "INSERT INTO \"products\" VALUES ( 2, 'Apples', 1 )" ); + m_database.executeSQL( "INSERT INTO \"products\" VALUES ( 3, 'Pears', 1 )" ); + m_database.executeSQL( "INSERT INTO \"products\" VALUES ( 4, 'Strawberries', 1 )" ); + + table = new HsqlTableDescriptor( "customers", + new HsqlColumnDescriptor[] { + new HsqlColumnDescriptor( "ID", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "Name", "VARCHAR(50)" ), + new HsqlColumnDescriptor( "Address", "VARCHAR(50)" ), + new HsqlColumnDescriptor( "City", "VARCHAR(50)" ), + new HsqlColumnDescriptor( "Postal", "VARCHAR(50)" ) } ); + m_database.createTable( table, true ); + + m_database.executeSQL( "INSERT INTO \"customers\" VALUES(1,'Food, Inc.','Down Under','Melbourne','509') " ); + m_database.executeSQL( "INSERT INTO \"customers\" VALUES(2,'Simply Delicious','Down Under','Melbourne','518') " ); + m_database.executeSQL( "INSERT INTO \"customers\" VALUES(3,'Pure Health','10 Fish St.','San Francisco','94107') " ); + m_database.executeSQL( "INSERT INTO \"customers\" VALUES(4,'Milk And More','Arlington Road 21','Dublin','31021') " ); + + table = new HsqlTableDescriptor( "orders", + new HsqlColumnDescriptor[] { + new HsqlColumnDescriptor( "ID", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "CustomerID", "INTEGER", HsqlColumnDescriptor.REQUIRED, "customers", "ID" ), + new HsqlColumnDescriptor( "OrderDate", "DATE" ), + new HsqlColumnDescriptor( "ShipDate", "DATE" ) } ); + m_database.createTable( table, true ); + + m_database.executeSQL( "INSERT INTO \"orders\" (\"ID\", \"CustomerID\", \"OrderDate\") VALUES(1, 1, {D '2009-01-01'})" ); + m_database.executeSQL( "INSERT INTO \"orders\" VALUES(2, 2, {D '2009-01-01'}, {D '2009-01-23'})" ); + + table = new HsqlTableDescriptor( "orders_details", + new HsqlColumnDescriptor[] { + new HsqlColumnDescriptor( "OrderID", "INTEGER", HsqlColumnDescriptor.PRIMARY, "orders", "ID" ), + new HsqlColumnDescriptor( "ProductID", "INTEGER", HsqlColumnDescriptor.PRIMARY, "products", "ID" ), + new HsqlColumnDescriptor( "Quantity", "INTEGER" ) } ); + m_database.createTable( table, true ); + + m_database.executeSQL( "INSERT INTO \"orders_details\" VALUES(1, 1, 100)" ); + m_database.executeSQL( "INSERT INTO \"orders_details\" VALUES(1, 2, 100)" ); + m_database.executeSQL( "INSERT INTO \"orders_details\" VALUES(2, 2, 2000)" ); + m_database.executeSQL( "INSERT INTO \"orders_details\" VALUES(2, 3, 2000)" ); + m_database.executeSQL( "INSERT INTO \"orders_details\" VALUES(2, 4, 2000)" ); + + // since we created the tables by directly executing the SQL statements, we need to refresh + // the tables container + XTablesSupplier suppTables = (XTablesSupplier)UnoRuntime.queryInterface( + XTablesSupplier.class, m_connection ); + XRefreshable refreshTables = (XRefreshable)UnoRuntime.queryInterface( + XRefreshable.class, suppTables.getTables() ); + refreshTables.refresh(); + } + + // -------------------------------------------------------------------------------------------------------- + private void validateUnparseable() + { + // The "unparseable" query should be indeed be unparseable by OOo (though a valid HSQL query) + XSingleSelectQueryComposer composer = null; + QueryDefinition unparseableQuery = null; + try + { + XMultiServiceFactory factory = (XMultiServiceFactory)UnoRuntime.queryInterface( + XMultiServiceFactory.class, m_database.defaultConnection() ); + composer = (XSingleSelectQueryComposer)UnoRuntime.queryInterface( + XSingleSelectQueryComposer.class, factory.createInstance( "com.sun.star.sdb.SingleSelectQueryComposer" ) ); + unparseableQuery = m_dataSource.getQueryDefinition( "unparseable" ); + } + catch( Exception e ) + { + throw new RuntimeException( "caught an unexpected exception: " + e.getMessage() ); + } + + boolean caughtExpected = false; + try + { + composer.setQuery( unparseableQuery.getCommand() ); + } + catch (WrappedTargetException e) { } + catch( SQLException e ) + { + caughtExpected = true; + } + + if ( !caughtExpected ) + throw new RuntimeException( "Somebody improved the parser! This is bad :), since we need an unparsable query here!" ); + } + + // -------------------------------------------------------------------------------------------------------- + private void createQueries() throws ElementExistException, WrappedTargetException, com.sun.star.lang.IllegalArgumentException + { + m_database.getDataSource().createQuery( + "all orders", + "SELECT \"orders\".\"ID\" AS \"Order No.\", " + + "\"customers\".\"Name\" AS \"Customer Name\", " + + "\"orders\".\"OrderDate\" AS \"Order Date\", " + + "\"orders\".\"ShipDate\" AS \"Ship Date\", " + + "\"orders_details\".\"Quantity\", " + + "\"products\".\"Name\" AS \"Product Name\" " + + "FROM \"orders_details\" AS \"orders_details\", " + + "\"orders\" AS \"orders\", " + + "\"products\" AS \"products\", " + + "\"customers\" AS \"customers\" " + + "WHERE ( \"orders_details\".\"OrderID\" = \"orders\".\"ID\" " + + "AND \"orders_details\".\"ProductID\" = \"products\".\"ID\" " + + "AND \"orders\".\"CustomerID\" = \"customers\".\"ID\" )" + ); + + m_database.getDataSource().createQuery( + "unshipped orders", + "SELECT * " + + "FROM \"all orders\"" + + "WHERE ( \"ShipDate\" IS NULL )" + ); + + m_database.getDataSource().createQuery( "parseable", "SELECT * FROM \"customers\"" ); + m_database.getDataSource().createQuery( "parseable native", "SELECT * FROM INFORMATION_SCHEMA.SYSTEM_VIEWS", false ); + m_database.getDataSource().createQuery( "unparseable", + "SELECT CAST( \"ID\" AS VARCHAR(3) ) AS \"ID_VARCHAR\" FROM \"products\"", false ); + + validateUnparseable(); + } +} diff --git a/dbaccess/qa/complex/dbaccess/DataSource.java b/dbaccess/qa/complex/dbaccess/DataSource.java new file mode 100644 index 000000000000..cb19a1f85d53 --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/DataSource.java @@ -0,0 +1,109 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: DataSource.java,v $ + * $Revision: 1.2 $ + * + * 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.dbaccess; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XNamingService; + +public class DataSource extends complexlib.ComplexTestCase { + + connectivity.tools.HsqlDatabase m_database; + connectivity.tools.DataSource m_dataSource; + + // -------------------------------------------------------------------------------------------------------- + public String[] getTestMethodNames() + { + return new String[] + { + "testRegistrationName" + }; + } + + // -------------------------------------------------------------------------------------------------------- + public String getTestObjectName() + { + return "DataSource"; + } + + // -------------------------------------------------------------------------------------------------------- + private void createTestCase() + { + try + { + if ( m_database == null ) + { + CRMDatabase database = new CRMDatabase( getFactory() ); + m_database = database.getDatabase(); + m_dataSource = m_database.getDataSource(); + } + } + catch( Exception e ) + { + failed( "could not create the test case, error message:\n" + e.getMessage() ); + } + } + + // -------------------------------------------------------------------------------------------------------- + private XMultiServiceFactory getFactory() + { + return (XMultiServiceFactory)param.getMSF(); + } + + // -------------------------------------------------------------------------------------------------------- + public void testRegistrationName() + { + createTestCase(); + + try + { + // 1. check the existing "Bibliography" data source whether it has the proper name + String dataSourceName = "Bibliography"; + connectivity.tools.DataSource bibliography = new connectivity.tools.DataSource( getFactory(), dataSourceName ); + assureEquals( "pre-registered database has a wrong name!", + dataSourceName, bibliography.getName() ); + + // 2. register a newly created data source, and verify it has the proper name + dataSourceName = "someDataSource"; + + XNamingService dataSourceRegistrations = (XNamingService)UnoRuntime.queryInterface( + XNamingService.class, getFactory().createInstance("com.sun.star.sdb.DatabaseContext")); + dataSourceRegistrations.registerObject("someDataSource", m_dataSource.getXDataSource()); + + assureEquals( "registration name of a newly registered data source is wrong", + dataSourceName, m_dataSource.getName() ); + } + catch ( AssureException e ) { throw e; } + catch ( Exception e ) + { + failed( "caught an unexpected exception: " + e.getMessage() ); + } + } +} diff --git a/dbaccess/qa/complex/dbaccess/DatabaseDocument.java b/dbaccess/qa/complex/dbaccess/DatabaseDocument.java new file mode 100644 index 000000000000..145ef40515ae --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/DatabaseDocument.java @@ -0,0 +1,847 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: DatabaseDocument.java,v $ + * $Revision: 1.1.2.9 $ + * + * 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.dbaccess; + +import com.sun.star.awt.XTopWindow; +import com.sun.star.beans.PropertyState; +import com.sun.star.document.DocumentEvent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.script.XStorageBasedLibraryContainer; +import com.sun.star.task.XInteractionRequest; +import com.sun.star.uno.Exception; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XStorable; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XNameContainer; +import com.sun.star.container.XSet; +import com.sun.star.document.XDocumentEventBroadcaster; +import com.sun.star.document.XDocumentEventListener; +import com.sun.star.document.XEmbeddedScripts; +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; +import com.sun.star.frame.XTitle; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.NotInitializedException; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.script.provider.XScriptProviderSupplier; +import com.sun.star.sdb.XFormDocumentsSupplier; +import com.sun.star.sdb.XOfficeDatabaseDocument; +import com.sun.star.sdb.XReportDocumentsSupplier; +import com.sun.star.task.DocumentMacroConfirmationRequest; +import com.sun.star.task.XInteractionApprove; +import com.sun.star.task.XInteractionContinuation; +import com.sun.star.task.XInteractionHandler; +import com.sun.star.uno.XComponentContext; +import com.sun.star.util.CloseVetoException; +import com.sun.star.util.URL; +import com.sun.star.util.XChangesBatch; +import com.sun.star.util.XCloseable; +import com.sun.star.util.XModifiable; +import com.sun.star.util.XURLTransformer; +import connectivity.tools.*; +import java.io.IOException; +import java.util.Iterator; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DatabaseDocument extends TestCase implements com.sun.star.document.XDocumentEventListener +{ + private XComponent m_callbackFactory = null; + private Vector m_documentEvents = new Vector(); + private Vector m_globalEvents = new Vector(); + + // for those states, see testDocumentEvents + private static short STATE_NOT_STARTED = 0; + private static short STATE_LOADING_DOC = 1; + private static short STATE_MACRO_EXEC_APPROVED = 2; + private static short STATE_ON_LOAD_RECEIVED = 3; + private short m_loadDocState = STATE_NOT_STARTED; + + // ======================================================================================================== + /** a helper class which can be used by the Basic scripts in our test documents + * to notify us of events in this document + */ + private class CallbackComponent implements XDocumentEventListener, XTypeProvider + { + public void documentEventOccured( DocumentEvent _event ) + { + onDocumentEvent( _event ); + } + + public void disposing( com.sun.star.lang.EventObject _Event ) + { + // not interested in + } + + public Type[] getTypes() + { + Class interfaces[] = getClass().getInterfaces(); + Type types[] = new Type[interfaces.length]; + for(int i = 0; i < interfaces.length; ++ i) + { + types[i] = new Type(interfaces[i]); + } + return types; + } + + public byte[] getImplementationId() + { + return getClass().toString().getBytes(); + } + }; + + // ======================================================================================================== + private static String getCallbackComponentServiceName() + { + return "org.openoffice.complex.dbaccess.EventCallback"; + } + + // ======================================================================================================== + /** a factory for a CallbackComponent + */ + private class CallbackComponentFactory implements XSingleComponentFactory, XServiceInfo, XComponent + { + private Vector m_eventListeners = new Vector(); + + public Object createInstanceWithContext( XComponentContext _context ) throws Exception + { + return new CallbackComponent(); + } + + public Object createInstanceWithArgumentsAndContext( Object[] arg0, XComponentContext _context ) throws Exception + { + return createInstanceWithContext( _context ); + } + + public String getImplementationName() + { + return "org.openoffice.complex.dbaccess.CallbackComponent"; + } + + public boolean supportsService( String _service ) + { + return _service.equals( getCallbackComponentServiceName() ); + } + + public String[] getSupportedServiceNames() + { + return new String[] { getCallbackComponentServiceName() }; + } + + public void dispose() + { + EventObject event = new EventObject( this ); + + Vector eventListenersCopy = (Vector)m_eventListeners.clone(); + Iterator iter = eventListenersCopy.iterator(); + while ( iter.hasNext() ) + { + ((XEventListener)iter.next()).disposing( event ); + } + } + + public void addEventListener( XEventListener _listener ) + { + if ( _listener != null ) + m_eventListeners.add( _listener ); + } + + public void removeEventListener( XEventListener _listener ) + { + m_eventListeners.remove( _listener ); + } + }; + + // ======================================================================================================== + private class MacroExecutionApprove implements XInteractionHandler + { + private XInteractionHandler m_defaultHandler = null; + + MacroExecutionApprove( XMultiServiceFactory _factory ) + { + try + { + m_defaultHandler = (XInteractionHandler)UnoRuntime.queryInterface( XInteractionHandler.class, + _factory.createInstance( "com.sun.star.sdb.InteractionHandler" ) ); + } + catch ( Exception ex ) + { + Logger.getLogger( DatabaseDocument.class.getName() ).log( Level.SEVERE, null, ex ); + } + } + + public void handle( XInteractionRequest _request ) + { + Object request = _request.getRequest(); + if ( !( request instanceof DocumentMacroConfirmationRequest ) && ( m_defaultHandler != null ) ) + { + m_defaultHandler.handle( _request ); + return; + } + + assureEquals( "interaction handleer called in wrong state", STATE_LOADING_DOC, m_loadDocState ); + + // auto-approve + XInteractionContinuation continuations[] = _request.getContinuations(); + for ( int i=0; i<continuations.length; ++i ) + { + XInteractionApprove approve = (XInteractionApprove)UnoRuntime.queryInterface( XInteractionApprove.class, + continuations[i] ); + if ( approve != null ) + { + approve.select(); + m_loadDocState = STATE_MACRO_EXEC_APPROVED; + break; + } + } + } + }; + + // ======================================================================================================== + // -------------------------------------------------------------------------------------------------------- + public String[] getTestMethodNames() + { + return new String[] + { + "testLoadable", + "testDocumentEvents", + "testGlobalEvents" + }; + } + + // -------------------------------------------------------------------------------------------------------- + public String getTestObjectName() + { + return "DatabaseDocument"; + } + + public void before() + { + super.before(); + + try + { + // at our service factory, insert a new factory for our CallbackComponent + // this will allow the Basic code in our test documents to call back into this test case + // here, by just instantiating this service + XSet globalFactory = (XSet)UnoRuntime.queryInterface( + XSet.class, getORB() ); + m_callbackFactory = new CallbackComponentFactory(); + globalFactory.insert( m_callbackFactory ); + + // register ourself as listener at the global event broadcaster + XDocumentEventBroadcaster broadcaster = (XDocumentEventBroadcaster)UnoRuntime.queryInterface( + XDocumentEventBroadcaster.class, getORB().createInstance( "com.sun.star.frame.GlobalEventBroadcaster" ) ); + broadcaster.addDocumentEventListener( 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 create the test case" ); + } + } + + // -------------------------------------------------------------------------------------------------------- + public void after() + { + 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 + XDocumentEventBroadcaster broadcaster = (XDocumentEventBroadcaster) UnoRuntime.queryInterface( + XDocumentEventBroadcaster.class, getORB().createInstance( "com.sun.star.frame.GlobalEventBroadcaster" ) ); + broadcaster.removeDocumentEventListener( 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" ); + } + } + + // -------------------------------------------------------------------------------------------------------- + private class UnoMethodDescriptor + { + public Class unoInterfaceClass = null; + public String methodName = null; + + public UnoMethodDescriptor( Class _class, String _method ) + { + unoInterfaceClass = _class; + methodName = _method; + } + } + + // -------------------------------------------------------------------------------------------------------- + private void impl_checkDocumentInitState( Object _document, boolean _isInitialized ) + { + // things you cannot do with an uninitialized document: + UnoMethodDescriptor[] unsupportedMethods = new UnoMethodDescriptor[] { + new UnoMethodDescriptor( XStorable.class, "store" ), + new UnoMethodDescriptor( XFormDocumentsSupplier.class, "getFormDocuments" ), + new UnoMethodDescriptor( XReportDocumentsSupplier.class, "getReportDocuments" ), + new UnoMethodDescriptor( XScriptProviderSupplier.class, "getScriptProvider" ), + new UnoMethodDescriptor( XEventsSupplier.class, "getEvents" ), + new UnoMethodDescriptor( XTitle.class, "getTitle" ), + new UnoMethodDescriptor( XModel2.class, "getControllers" ) + // (there's much more than this, but we cannot list all methods here, can we ...) + }; + + for ( int i=0; i<unsupportedMethods.length; ++i) + { + verifyExpectedException( _document, unsupportedMethods[i].unoInterfaceClass, + unsupportedMethods[i].methodName, new Object[]{}, _isInitialized ? null : NotInitializedException.class ); + } + } + + // -------------------------------------------------------------------------------------------------------- + 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; + } + + // -------------------------------------------------------------------------------------------------------- + private void impl_closeDocument( XModel _databaseDoc ) throws CloseVetoException, IOException, Exception + { + 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" ) ); + XStorable storeDoc = (XStorable)UnoRuntime.queryInterface( XStorable.class, databaseDoc ); + + // verify the document rejects API calls which require it to be initialized + impl_checkDocumentInitState( databaseDoc, false ); + + // though the document is not initialized, you can ask for the location, the URL, and the args + String location = storeDoc.getLocation(); + String url = databaseDoc.getURL(); + PropertyValue[] args = databaseDoc.getArgs(); + // they should be all empty at this time + assureEquals( "location is expected to be empty here", "", location ); + assureEquals( "URL is expected to be empty here", "", url ); + 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" ); + + String documentURL = createTempFileURL(); + storeDoc.storeAsURL( documentURL, new PropertyValue[0] ); + + // now that the document is stored, ... + // ... its URL should be correct + assureEquals( "wrong URL after storing the document", documentURL, databaseDoc.getURL() ); + // ... 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: + + // .................................................................... + // 1. XStorable::storeAsURL + // (this is for compatibility reasons, to not break existing code) + // this test is already made in impl_createEmptyEmbeddedHSQLDocument + + // .................................................................... + // 2. XLoadable::load + databaseDoc = (XModel)UnoRuntime.queryInterface( XModel.class, + getORB().createInstance( "com.sun.star.sdb.OfficeDatabaseDocument" ) ); + documentURL = copyToTempFile( 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 ) } ); + databaseDoc.attachResource( documentURL, new PropertyValue[0] ); + + 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 ); + + // .................................................................... + // 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 ); + } + + // -------------------------------------------------------------------------------------------------------- + private PropertyValue[] impl_getDefaultLoadArgs() + { + return new PropertyValue[] { + new PropertyValue( "PickListEntry", 0, false, PropertyState.DIRECT_VALUE ) + }; + } + + // -------------------------------------------------------------------------------------------------------- + private PropertyValue[] impl_getMacroExecLoadArgs() + { + return new PropertyValue[] { + new PropertyValue( "PickListEntry", 0, false, PropertyState.DIRECT_VALUE ), + new PropertyValue( "MacroExecutionMode", 0, com.sun.star.document.MacroExecMode.USE_CONFIG, PropertyState.DIRECT_VALUE ), + new PropertyValue( "InteractionHandler", 0, new MacroExecutionApprove(( getORB() )), PropertyState.DIRECT_VALUE ) + }; + } + + // -------------------------------------------------------------------------------------------------------- + private int impl_setMacroSecurityLevel( int _level ) throws Exception + { + XMultiServiceFactory configProvider = (XMultiServiceFactory)UnoRuntime.queryInterface( XMultiServiceFactory.class, + getORB().createInstance( "com.sun.star.configuration.ConfigurationProvider" ) ); + + PropertyValue[] args = new PropertyValue[] { + new PropertyValue( "nodepath", 0, "/org.openoffice.Office.Common/Security/Scripting", PropertyState.DIRECT_VALUE ) + }; + + XPropertySet securitySettings = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, + configProvider.createInstanceWithArguments( "com.sun.star.configuration.ConfigurationUpdateAccess", args ) ); + int oldValue = ((Integer)securitySettings.getPropertyValue( "MacroSecurityLevel" )).intValue(); + securitySettings.setPropertyValue( "MacroSecurityLevel", new Integer( _level ) ); + + XChangesBatch committer = (XChangesBatch)UnoRuntime.queryInterface( XChangesBatch.class, + securitySettings ); + committer.commitChanges(); + + return oldValue; + } + + // -------------------------------------------------------------------------------------------------------- + public void testDocumentEvents() throws Exception, IOException + { + // create an empty document + XModel databaseDoc = impl_createEmptyEmbeddedHSQLDocument(); + + // create Basic library/module therein + XEmbeddedScripts embeddedScripts = (XEmbeddedScripts) UnoRuntime.queryInterface( XEmbeddedScripts.class, + databaseDoc ); + XStorageBasedLibraryContainer basicLibs = embeddedScripts.getBasicLibraries(); + XNameContainer newLib = basicLibs.createLibrary( "EventHandlers" ); + String eventHandlerCode = + "Option Explicit\n" + + "\n" + + "Sub OnLoad\n" + + " Dim oCallback as Object\n" + + " oCallback = createUnoService( \"" + getCallbackComponentServiceName() + "\" )\n" + + "\n" + + " ' as long as the Document is not passed to the Basic callbacks, we need to create\n" + + " ' one ourself\n" + + " Dim oEvent as new com.sun.star.document.DocumentEvent\n" + + " oEvent.EventName = \"OnLoad\"\n" + + " oEvent.Source = ThisComponent\n" + + "\n" + + " oCallback.documentEventOccured( oEvent )\n" + + "End Sub\n"; + newLib.insertByName( "all", eventHandlerCode ); + + // bind the macro to the OnLoad event + String macroURI = "vnd.sun.star.script:EventHandlers.all.OnLoad?language=Basic&location=document"; + XEventsSupplier eventsSupplier = (XEventsSupplier)UnoRuntime.queryInterface( XEventsSupplier.class, + databaseDoc ); + eventsSupplier.getEvents().replaceByName( "OnLoad", new PropertyValue[] { + new PropertyValue( "EventType", 0, "Script", PropertyState.DIRECT_VALUE ), + new PropertyValue( "Script", 0, macroURI, PropertyState.DIRECT_VALUE ) + } ); + + // store the document, and close it + String documentURL = databaseDoc.getURL(); + documentURL = FileHelper.getOOoCompatibleFileURL( documentURL ); + XStorable storeDoc = (XStorable) UnoRuntime.queryInterface( XStorable.class, + databaseDoc ); + storeDoc.store(); + impl_closeDocument( databaseDoc ); + + // ensure the macro security configuration is "ask the user for document macro execution" + int oldSecurityLevel = impl_setMacroSecurityLevel( 1 ); + + // load it, again + XComponentLoader loader = (XComponentLoader)UnoRuntime.queryInterface( XComponentLoader.class, + getORB().createInstance( "com.sun.star.frame.Desktop" ) ); + + m_loadDocState = STATE_LOADING_DOC; + // expected order of states is: + // STATE_LOADING_DOC - initialized here + // STATE_MACRO_EXEC_APPROVED - done in our interaction handler, which auto-approves the execution of macros + // STATE_ON_LOAD_RECEIVED - done in our callback for the document events + // + // In particular, it is important that the interaction handler (which plays the role of the user confirmation + // here) is called before the OnLoad notification is received - since the latter happens from within + // a Basic macro which is bound to the OnLoad event of the document. + + String context = "OnLoad"; + impl_startObservingEvents( context ); + databaseDoc = (XModel)UnoRuntime.queryInterface( XModel.class, + loader.loadComponentFromURL( documentURL, "_blank", 0, impl_getMacroExecLoadArgs() ) ); + impl_stopObservingEvents( m_documentEvents, new String[] { "OnLoad" }, context ); + + assureEquals( "our provided interaction handler was not called", STATE_ON_LOAD_RECEIVED, m_loadDocState ); + + // restore macro security level + impl_setMacroSecurityLevel( oldSecurityLevel ); + + // close the document + impl_closeDocument( databaseDoc ); + } + + // -------------------------------------------------------------------------------------------------------- + public void testGlobalEvents() 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 = copyToTempFile( 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, impl_getDefaultLoadArgs() ) ); + 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, impl_getDefaultLoadArgs() ) ); + 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, impl_getDefaultLoadArgs() ) ); + int previousOnLoadEventPos = impl_waitForEvent( m_globalEvents, "OnLoad", 5000 ); + // ... and another document ... + String otherURL = copyToTempFile( databaseDoc.getURL() ); + XModel otherDoc = (XModel)UnoRuntime.queryInterface( XModel.class, + loader.loadComponentFromURL( otherURL, "_blank", 0, impl_getDefaultLoadArgs() ) ); + impl_waitForEvent( m_globalEvents, "OnLoad", 5000, previousOnLoadEventPos + 1 ); + 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_startObservingEvents( "cleanup after '" + context + "'" ); + 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) ); + } + } + } + + // -------------------------------------------------------------------------------------------------------- + int impl_waitForEvent( Vector _eventQueue, String _expectedEvent, int _maxMilliseconds ) + { + return impl_waitForEvent( _eventQueue, _expectedEvent, _maxMilliseconds, 0 ); + } + + // -------------------------------------------------------------------------------------------------------- + int impl_waitForEvent( Vector _eventQueue, String _expectedEvent, int _maxMilliseconds, int _firstQueueElementToCheck ) + { + synchronized ( _eventQueue ) + { + int waitedMilliseconds = 0; + + while ( waitedMilliseconds < _maxMilliseconds ) + { + for ( int i=_firstQueueElementToCheck; i<_eventQueue.size(); ++i ) + { + if ( _expectedEvent.equals( _eventQueue.get(i) ) ) + // found the event in the queue + return i; + } + + // 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" ); + return -1; + } + + // -------------------------------------------------------------------------------------------------------- + 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; + + if ( ( _Event.EventName.equals( "OnLoad" ) ) + && ( m_loadDocState != STATE_NOT_STARTED ) + ) + { + assureEquals( "OnLoad event must come *after* invocation of the interaction handler / user!", + m_loadDocState, STATE_MACRO_EXEC_APPROVED ); + m_loadDocState = STATE_ON_LOAD_RECEIVED; + } + + synchronized ( m_documentEvents ) + { + m_documentEvents.add( _Event.EventName ); + m_documentEvents.notifyAll(); + } + + log.println( " document event: " + _Event.EventName ); + } + + // -------------------------------------------------------------------------------------------------------- + public void documentEventOccured( DocumentEvent _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 ); + } + + // -------------------------------------------------------------------------------------------------------- + public void disposing( EventObject _Event ) + { + // not interested in + } +} diff --git a/dbaccess/qa/complex/dbaccess/FileHelper.java b/dbaccess/qa/complex/dbaccess/FileHelper.java new file mode 100644 index 000000000000..fbe5f330b24d --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/FileHelper.java @@ -0,0 +1,46 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Parser.java,v $ + * $Revision: 1.1.6.2 $ + * + * 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.dbaccess; + +public class FileHelper +{ + static public String getOOoCompatibleFileURL( String _javaFileURL ) + { + String returnURL = _javaFileURL; + if ( ( returnURL.indexOf( "file:/" ) == 0 ) && ( returnURL.indexOf( "file:///" ) == -1 ) ) + { + // for some reason, the URLs here in Java start with "file:/" only, instead of "file:///" + // Some of the office code doesn't like this ... + returnURL = "file:///" + returnURL.substring( 6 ); + } + return returnURL; + } +} diff --git a/dbaccess/qa/complex/dbaccess/Parser.java b/dbaccess/qa/complex/dbaccess/Parser.java new file mode 100644 index 000000000000..a4305c031e1b --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/Parser.java @@ -0,0 +1,160 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Parser.java,v $ + * $Revision: 1.1.6.2 $ + * + * 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.dbaccess; + +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XIndexAccess; +import com.sun.star.container.XNameAccess; +import com.sun.star.sdb.XParametersSupplier; +import com.sun.star.sdb.XSingleSelectQueryComposer; +import com.sun.star.sdbc.DataType; +import com.sun.star.sdbc.SQLException; +import com.sun.star.uno.Exception; +import com.sun.star.uno.UnoRuntime; + +public class Parser extends CRMBasedTestCase +{ + // -------------------------------------------------------------------------------------------------------- + public String[] getTestMethodNames() + { + return new String[] { + "checkJoinSyntax", + "checkParameterTypes" + }; + } + + // -------------------------------------------------------------------------------------------------------- + public String getTestObjectName() + { + return "Parser"; + } + + // -------------------------------------------------------------------------------------------------------- + protected void createTestCase() + { + try + { + super.createTestCase(); + m_database.getDatabase().getDataSource().createQuery( "query products", "SELECT * FROM \"products\"" ); + } + catch ( Exception e ) + { + e.printStackTrace( System.err ); + assure( "caught an exception (" + e.getMessage() + ") while creating the test case", false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + /** verifies that aliases for inner queries work as expected + */ + public void checkJoinSyntax() throws Exception + { + XSingleSelectQueryComposer composer = createQueryComposer(); + + // feed the composer with some statements. If any of those cannot be parsed, the composer + // will throw an exception - which is a regression then + composer.setQuery( + "SELECT \"categories\".\"Name\", " + + "\"products\".\"Name\" " + + "FROM \"products\" RIGHT OUTER JOIN \"categories\" AS \"categories\" ON \"products\".\"CategoryID\" = \"categories\".\"ID\"" ); + + // just to be sure the composer *really* parses upon setting the query: feed it with + // an unparseable statement + boolean caughtExpected = false; + try + { + composer.setQuery( "NONSENSE" ); + } + catch( SQLException e ) + { + caughtExpected = true; + } + assure( "pre-condition not met: parser should except on unparseable statements, else the complete" + + "test is bogus!", caughtExpected ); + } + + // -------------------------------------------------------------------------------------------------------- + private void impl_checkParameters( final String _statement, final String[] _expectedParameterNames, final int[] _expectedParameterTypes, String _context ) throws Exception + { + XSingleSelectQueryComposer composer = createQueryComposer(); + composer.setQuery( _statement ); + + assureEquals( "checkParameterTypes: internal error", _expectedParameterNames.length, _expectedParameterTypes.length ); + + XParametersSupplier paramSupp = (XParametersSupplier)UnoRuntime.queryInterface( + XParametersSupplier.class, composer ); + XIndexAccess parameters = paramSupp.getParameters(); + + assureEquals( "(ctx: " + _context + ") unexpected parameter count", _expectedParameterNames.length, parameters.getCount() ); + for ( int i=0; i<parameters.getCount(); ++i ) + { + XPropertySet parameter = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, + parameters.getByIndex(i) ); + + String name = (String)parameter.getPropertyValue( "Name" ); + assureEquals( "(ctx: " + _context + ") unexpected parameter name for parameter number " + ( i + 1 ), _expectedParameterNames[i], name ); + + int type = ((Integer)parameter.getPropertyValue( "Type" )).intValue(); + assureEquals( "(ctx: " + _context + ") unexpected data type for parameter number " + ( i + 1 ), _expectedParameterTypes[i], type ); + } + } + + // -------------------------------------------------------------------------------------------------------- + /** verifies that the parser properly recognizes the types of parameters + */ + public void checkParameterTypes() throws Exception + { + impl_checkParameters( + "SELECT * FROM \"all orders\" " + + "WHERE ( \"Order Date\" >= :order_date ) " + + " AND ( ( \"Customer Name\" LIKE :customer ) " + + " OR ( \"Product Name\" LIKE ? ) " + + " )", + new String[] { "order_date", "customer", "Product Name" }, + new int[] { DataType.DATE, DataType.VARCHAR, DataType.VARCHAR }, + ">= && LIKE" + ); + + impl_checkParameters( + "SELECT * FROM \"categories\" " + + "WHERE \"ID\" BETWEEN :id_lo AND :id_hi", + new String[] { "id_lo", "id_hi" }, + new int[] { DataType.INTEGER, DataType.INTEGER }, + "BETWEEN" + ); + + impl_checkParameters( + "SELECT CONCAT( :prefix, CONCAT( \"Name\", :suffix ) ) FROM \"customers\"", + new String[] { "prefix", "suffix" }, + new int[] { DataType.VARCHAR, DataType.VARCHAR }, + "CONCAT" + ); + } +} diff --git a/dbaccess/qa/complex/dbaccess/PropertyBag.java b/dbaccess/qa/complex/dbaccess/PropertyBag.java new file mode 100644 index 000000000000..5f1747fd94bc --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/PropertyBag.java @@ -0,0 +1,296 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: PropertyBag.java,v $ + * $Revision: 1.5 $ + * + * 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.dbaccess; + +import complexlib.ComplexTestCase; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.beans.*; + +public class PropertyBag extends ComplexTestCase +{ + private XPropertyContainer m_bag; + private XPropertySet m_set; + private XPropertyAccess m_access; + private XMultiServiceFactory m_orb; + + public String[] getTestMethodNames() + { + return new String[] + { + "checkBasics", + "checkSequenceAccess", + "checkDynamicSet" + }; + } + + public String getTestObjectName() + { + return "PropertyBag"; + } + + public void before() + { + m_orb = (XMultiServiceFactory)param.getMSF(); + } + + public void checkBasics() + { + createEmptyBag(); + log.println("testing the basics"); + + // check whether empty property names are rejected + boolean caughtExpected = false; + try + { + m_bag.addProperty( "", PropertyAttribute.BOUND, new Integer( 3 ) ); + } + catch(com.sun.star.lang.IllegalArgumentException e) { caughtExpected = true; } + catch(com.sun.star.uno.Exception e) { } + if ( !caughtExpected ) + failed( "empty property names are not rejected by XPropertyContainer::addProperty" ); + + // check whether duplicate insertions are rejected + caughtExpected = false; + try + { + m_bag.addProperty( "Value", PropertyAttribute.BOUND, new String( "" ) ); + m_bag.addProperty( "Value", PropertyAttribute.BOUND, new String( "" ) ); + } + catch(com.sun.star.beans.PropertyExistException e) { caughtExpected = true; } + catch(com.sun.star.uno.Exception e) { } + if ( !caughtExpected ) + failed( "insertion of duplicate property names is not rejected" ); + + // try removing the property we just added - this should fail, as it does not have + // the REMOVEABLE attribute + caughtExpected = false; + try + { + m_bag.removeProperty( "Value" ); + } + catch(com.sun.star.beans.NotRemoveableException e) { caughtExpected = true; } + catch(com.sun.star.uno.Exception e) { } + if ( !caughtExpected ) + failed( "removing non-removeable properties is expected to fail - but it didn't" ); + + // try removing a non-existent property + caughtExpected = false; + try + { + m_bag.removeProperty( "NonExistent" ); + } + catch(com.sun.star.beans.UnknownPropertyException e) { caughtExpected = true; } + catch(com.sun.star.uno.Exception e) { } + if ( !caughtExpected ) + failed( "removing non-existent properties is expected to fail - but it didn't" ); + + // try writing and reading a value for the one property we have so far + try + { + final String testValue = "someArbitraryValue"; + m_set.setPropertyValue( "Value", testValue ); + String currentValue = (String)m_set.getPropertyValue( "Value" ); + if ( !currentValue.equals( testValue ) ) + failed( "set property is not remembered" ); + } + catch(com.sun.star.uno.Exception e) + { + failed( "setting or getting a property value failed" ); + } + + // try setting an illegal value for the property + caughtExpected = false; + try + { + m_set.setPropertyValue( "Value", new Integer( 3 ) ); + } + catch(com.sun.star.lang.IllegalArgumentException e) { caughtExpected = true; } + catch(com.sun.star.uno.Exception e) { } + if ( !caughtExpected ) + failed( "the bag does not respect the property type we declared for the property" ); + } + + public void checkSequenceAccess() throws com.sun.star.uno.Exception + { + log.println( "checking PropertySetAccess via sequences" ); + createStandardBag( false ); + + // --------------------------------- + // XPropertyAccess.setPropertyValues + final PropertyValue expectedValues[] = + { + new PropertyValue( "BoolValue", -1, new Boolean( false ), PropertyState.DIRECT_VALUE ), + new PropertyValue( "StringValue", -1, "some text", PropertyState.DIRECT_VALUE ), + new PropertyValue( "IntegerValue", -1, new Integer( 3 ), PropertyState.DIRECT_VALUE ), + new PropertyValue( "InterfaceValue", -1, m_bag, PropertyState.DIRECT_VALUE ) + }; + m_access.setPropertyValues( expectedValues ); + + for ( int i=0; i<expectedValues.length; ++i ) + { + Object value = m_set.getPropertyValue( expectedValues[i].Name ); + if ( !value.equals( expectedValues[i].Value ) ) + { + log.println( "property name : " + expectedValues[i].Name ); + log.println( "expected value: " + expectedValues[i].Value.toString() ); + log.println( "current value : " + value.toString() ); + failed( "retrieving a previously set property (" + expectedValues[i].Value.getClass().toString() + ") failed" ); + } + } + + // --------------------------------- + // XPropertyAccess.getPropertyValues + PropertyValue currentValues[] = m_access.getPropertyValues(); + for ( int i=0; i<currentValues.length; ++i ) + { + String name = currentValues[i].Name; + Object value = currentValues[i].Value; + for ( int j=0; j<expectedValues.length; ++j ) + { + if ( expectedValues[j].Name.equals( name ) ) + { + if ( !expectedValues[j].Value.equals( value ) ) + { + log.println( "property name : " + expectedValues[j].Name ); + log.println( "expected value: " + expectedValues[j].Value.toString() ); + log.println( "current value : " + value.toString() ); + failed( "getPropertyValues failed for property '" + name + "' failed" ); + } + break; + } + } + + if ( !m_set.getPropertyValue( name ).equals( value ) ) + failed( "XPropertyAccess::getPropertyValues() and XPropertyset::getPropertyValue results are inconsistent" ); + } + } + + public void checkDynamicSet() throws com.sun.star.uno.Exception + { + log.println( "checking proper dynamic of the set" ); + createStandardBag( false ); + + PropertyValue props[] = + { + new PropertyValue( "BoolValue", -1, new Boolean( false ), PropertyState.DIRECT_VALUE), + new PropertyValue( "StringValue", -1, "test", PropertyState.DIRECT_VALUE ), + new PropertyValue( "SomeOtherStringValue", -1, "string value", PropertyState.DIRECT_VALUE ) + }; + + // try setting some property values which are not existent + boolean caughtExpected = false; + try + { + m_access.setPropertyValues( props ); + } + catch( com.sun.star.beans.UnknownPropertyException e ) { caughtExpected = true; } + catch( com.sun.star.uno.Exception e ) { } + if ( !caughtExpected ) + failed( "the set shouldn't accept unknown property values, if not explicitly told to do so" ); + + // re-create the bag, this time allow it to implicitly add properties + createStandardBag( true ); + boolean success = false; + try { m_access.setPropertyValues( props ); success = true; } + catch( com.sun.star.uno.Exception e ) { } + if ( !success ) + failed( "property bag failed to implicitly add unknown properties" ); + + // see whether this property was really added, and not just ignored + PropertyValue newlyAdded = props[ props.length - 1 ]; + try + { + if ( !m_set.getPropertyValue( newlyAdded.Name ).equals( newlyAdded.Value ) ) + failed( "the new property was not really added, or not added with the proper value" ); + } + catch( com.sun.star.uno.Exception e ) { } + } + + private void createEmptyBag() + { + try + { + m_bag = null; + final String serviceName = "com.sun.star.beans.PropertyBag"; + m_bag = (XPropertyContainer)UnoRuntime.queryInterface( XPropertyContainer.class, + m_orb.createInstance( serviceName ) + ); + if ( m_bag == null ) + failed( "could not create a " + serviceName + " instance" ); + m_set = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, m_bag ); + m_access = (XPropertyAccess)UnoRuntime.queryInterface( XPropertyAccess.class, m_bag ); + } + catch( com.sun.star.uno.Exception e ) + { + } + } + + private void createStandardBag( boolean allowLazyAdding ) + { + try + { + m_bag = null; + + Object initArgs[] = { new NamedValue( "AutomaticAddition", new Boolean( allowLazyAdding ) ) }; + + final String serviceName = "com.sun.star.beans.PropertyBag"; + m_bag = (XPropertyContainer)UnoRuntime.queryInterface( XPropertyContainer.class, + m_orb.createInstanceWithArguments( serviceName, initArgs ) + ); + if ( m_bag == null ) + failed( "could not create a " + serviceName + " instance" ); + m_set = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, m_bag ); + m_access = (XPropertyAccess)UnoRuntime.queryInterface( XPropertyAccess.class, m_bag ); + + Object properties[][] = + { + { "BoolValue", new Boolean( true ) }, + { "StringValue", new String( "" ) }, + { "IntegerValue", new Integer( 3 ) }, + { "InterfaceValue", (XInterface)m_bag } + }; + for ( int i=0; i<properties.length; ++i ) + { + m_bag.addProperty( + (String)properties[i][0], + PropertyAttribute.MAYBEVOID, + properties[i][1] + ); + } + } + catch( com.sun.star.uno.Exception e ) + { + } + } +} diff --git a/dbaccess/qa/complex/dbaccess/Query.java b/dbaccess/qa/complex/dbaccess/Query.java new file mode 100644 index 000000000000..d64e1a7c972e --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/Query.java @@ -0,0 +1,130 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Query.java,v $ + * $Revision: 1.7 $ + * + * 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.dbaccess; + +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XIndexAccess; +import com.sun.star.container.XNameAccess; +import com.sun.star.container.XNamed; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.sdb.XQueriesSupplier; +import com.sun.star.sdbcx.XColumnsSupplier; +import com.sun.star.uno.UnoRuntime; + +public class Query extends complexlib.ComplexTestCase { + + connectivity.tools.HsqlDatabase m_database; + connectivity.tools.DataSource m_dataSource; + + // -------------------------------------------------------------------------------------------------------- + public String[] getTestMethodNames() { + return new String[] + { + "testQueryColumns" + }; + } + + // -------------------------------------------------------------------------------------------------------- + public String getTestObjectName() { + return "Query"; + } + + // -------------------------------------------------------------------------------------------------------- + private void createTestCase() + { + try + { + if ( m_database == null ) + { + CRMDatabase database = new CRMDatabase( getFactory() ); + m_database = database.getDatabase(); + m_dataSource = m_database.getDataSource(); + } + } + catch( Exception e ) + { + System.err.println( "could not create the test case, error message:\n" + e.getMessage() ); + e.printStackTrace( System.err ); + assure( "failed to created the test case", false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + private XMultiServiceFactory getFactory() + { + return (XMultiServiceFactory)param.getMSF(); + } + + // -------------------------------------------------------------------------------------------------------- + public void testQueryColumns() + { + createTestCase(); + + try + { + XQueriesSupplier suppQueries = (XQueriesSupplier)UnoRuntime.queryInterface( + XQueriesSupplier.class, m_database.defaultConnection()); + XNameAccess queries = suppQueries.getQueries(); + + String[] queryNames = new String[] { "parseable", "parseable native", "unparseable" }; + String[][] expectedColumnNames = new String[][] { + new String[] { "ID", "Name", "Address", "City", "Postal" }, + new String[] { "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME", "VIEW_DEFINITION", "CHECK_OPTION", "IS_UPDATABLE", "VALID" }, + new String[] { "ID_VARCHAR" } + }; + + for ( int i = 0; i < queryNames.length; ++i ) + { + XPropertySet query = (XPropertySet)UnoRuntime.queryInterface( + XPropertySet.class, queries.getByName( queryNames[i] ) ); + + XColumnsSupplier suppCols = (XColumnsSupplier)UnoRuntime.queryInterface( + XColumnsSupplier.class, query); + XIndexAccess columns = (XIndexAccess)UnoRuntime.queryInterface( + XIndexAccess.class, suppCols.getColumns()); + + // check whether the columns supplied by the query match what we expected + assure( "invalid column count (found " + columns.getCount() + ", expected: " + expectedColumnNames[i].length + ") for query \"" + queryNames[i] + "\"", + columns.getCount() == expectedColumnNames[i].length ); + for ( int col = 0; col < columns.getCount(); ++col ) + { + XNamed columnName = (XNamed)UnoRuntime.queryInterface( + XNamed.class, columns.getByIndex(col) ); + assure( "column no. " + col + " of query \"" + queryNames[i] + "\" not matching", + columnName.getName().equals( expectedColumnNames[i][col] ) ); + } + } + } + catch ( Exception e ) + { + assure( "caught an unexpected exception: " + e.getMessage(), false ); + } + } +} diff --git a/dbaccess/qa/complex/dbaccess/QueryInQuery.java b/dbaccess/qa/complex/dbaccess/QueryInQuery.java new file mode 100644 index 000000000000..b06d57d05c59 --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/QueryInQuery.java @@ -0,0 +1,194 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: QueryInQuery.java,v $ + * $Revision: 1.6.2.1 $ + * + * 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.dbaccess; + +import com.sun.star.container.ElementExistException; +import com.sun.star.lang.IllegalArgumentException; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.sdb.CommandType; +import com.sun.star.sdbc.SQLException; +import connectivity.tools.HsqlColumnDescriptor; +import connectivity.tools.HsqlTableDescriptor; +import connectivity.tools.RowSet; +import com.sun.star.sdbc.XStatement; +import com.sun.star.sdbc.XResultSet; + +public class QueryInQuery extends CRMBasedTestCase +{ + // -------------------------------------------------------------------------------------------------------- + public String[] getTestMethodNames() + { + return new String[] { + "executeSimpleSelect", + "executeAliasedSelect", + "checkNameCollisions", + "checkCyclicReferences", + "checkStatementQiQSupport" + }; + } + + // -------------------------------------------------------------------------------------------------------- + public String getTestObjectName() + { + return "QueryInQuery"; + } + + // -------------------------------------------------------------------------------------------------------- + protected void createTestCase() + { + try + { + super.createTestCase(); + m_database.getDatabase().getDataSource().createQuery( "query products", "SELECT * FROM \"products\"" ); + } + catch ( Exception e ) + { + e.printStackTrace( System.err ); + assure( "caught an exception (" + e.getMessage() + ") while creating the test case", false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + private void verifyEqualRowSetContent( int _outerCommandType, String _outerCommand, int _innerCommandType, String _innerCommand ) throws SQLException + { + RowSet outerRowSet = m_database.getDatabase().createRowSet( _outerCommandType, _outerCommand ); + outerRowSet.execute(); + + RowSet innerRowSet = m_database.getDatabase().createRowSet( _innerCommandType, _innerCommand ); + innerRowSet.execute(); + + outerRowSet.last(); + innerRowSet.last(); + assure( "wrong record counts", outerRowSet.getRow() == innerRowSet.getRow() ); + + outerRowSet.beforeFirst(); + innerRowSet.beforeFirst(); + assure( "wrong column counts", outerRowSet.getColumnCount() == innerRowSet.getColumnCount() ); + + while ( outerRowSet.next() && innerRowSet.next() ) + { + for ( int i=1; i <= outerRowSet.getColumnCount(); ++i ) + { + assure( "content of column " + i + " of row " + outerRowSet.getRow() + " not identical", + innerRowSet.getString(i).equals( outerRowSet.getString(i) ) ); + } + } + } + + // -------------------------------------------------------------------------------------------------------- + /** executes a SQL statement simply selecting all columns from a query + */ + public void executeSimpleSelect() throws SQLException + { + verifyEqualRowSetContent( + CommandType.COMMAND, "SELECT * FROM \"query products\"", + CommandType.QUERY, "query products" ); + } + + // -------------------------------------------------------------------------------------------------------- + /** verifies that aliases for inner queries work as expected + */ + public void executeAliasedSelect() throws SQLException + { + verifyEqualRowSetContent( + CommandType.COMMAND, "SELECT \"PROD\".\"ID\" FROM \"query products\" AS \"PROD\"", + CommandType.COMMAND, "SELECT \"ID\" FROM \"products\"" ); + verifyEqualRowSetContent( + CommandType.COMMAND, "SELECT \"PROD\".* FROM \"query products\" AS \"PROD\"", + CommandType.QUERY, "query products" ); + } + + // -------------------------------------------------------------------------------------------------------- + /** verifies that aliases for inner queries work as expected + */ + public void checkNameCollisions() + { + // create a query with a name which is used by a table + boolean caughtExpected = false; + try + { + m_database.getDatabase().getDataSource().createQuery( "products", "SELECT * FROM \"products\"" ); + } + catch ( WrappedTargetException e ) { caughtExpected = true; } + catch ( IllegalArgumentException e ) {} + catch ( ElementExistException e ) { caughtExpected = true; } + assure( "creating queries with the name of an existing table should not be possible", + caughtExpected ); + + // create a table with a name which is used by a query + HsqlTableDescriptor table = new HsqlTableDescriptor( "query products", + new HsqlColumnDescriptor[] { + new HsqlColumnDescriptor( "ID", "INTEGER" ), + new HsqlColumnDescriptor( "Name", "VARCHAR(50)" ) } ); + + caughtExpected = false; + try + { + m_database.getDatabase().createTableInSDBCX( table ); + } + catch ( SQLException e ) { caughtExpected = true; } + catch ( ElementExistException ex ) { } + assure( "creating tables with the name of an existing query should not be possible", + caughtExpected ); + } + + // -------------------------------------------------------------------------------------------------------- + public void checkCyclicReferences() throws ElementExistException, WrappedTargetException, IllegalArgumentException + { + // some queries which create a cycle in the sub query tree + m_database.getDatabase().getDataSource().createQuery( "orders level 1", "SELECT * FROM \"orders level 0\"" ); + m_database.getDatabase().getDataSource().createQuery( "orders level 2", "SELECT * FROM \"orders level 1\"" ); + m_database.getDatabase().getDataSource().createQuery( "orders level 3", "SELECT * FROM \"orders level 2\"" ); + m_database.getDatabase().getDataSource().createQuery( "orders level 0", "SELECT * FROM \"orders level 3\"" ); + + RowSet rowSet = m_database.getDatabase().createRowSet( CommandType.QUERY, "orders level 0" ); + + boolean caughtExpected = false; + try { rowSet.execute(); } + catch ( SQLException e ) { caughtExpected = ( e.ErrorCode == -com.sun.star.sdb.ErrorCondition.PARSER_CYCLIC_SUB_QUERIES ); } + + assure( "executing a query with cyclic nested sub queries should fail!", caughtExpected ); + } + + // -------------------------------------------------------------------------------------------------------- + public void checkStatementQiQSupport() + { + try + { + XStatement statement = m_database.getConnection().createStatement(); + XResultSet resultSet = statement.executeQuery( "SELECT * FROM \"query products\"" ); + } + catch( SQLException e ) + { + assure( "SDB level statements do not allow for queries in queries", false ); + } + } +} diff --git a/dbaccess/qa/complex/dbaccess/RowSet.java b/dbaccess/qa/complex/dbaccess/RowSet.java new file mode 100644 index 000000000000..8e0161187c3e --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/RowSet.java @@ -0,0 +1,955 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: RowSet.java,v $ + * $Revision: 1.12 $ + * + * 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.dbaccess; + +import com.sun.star.container.XIndexAccess; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.beans.*; +import com.sun.star.lang.*; +import com.sun.star.sdbcx.*; +import com.sun.star.sdbc.*; +import com.sun.star.sdb.*; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.util.XRefreshable; +import connectivity.tools.HsqlDatabase; + +import complexlib.ComplexTestCase; +import complexlib.Assurance.AssureException; + + +public class RowSet extends ComplexTestCase { + + static final int MAX_TABLE_ROWS = 100; + static final int MAX_FETCH_ROWS = 10; + + HsqlDatabase m_database; + connectivity.tools.DataSource + m_dataSource; + XRowSet m_rowSet; + XResultSet m_resultSet; + XResultSetUpdate m_resultSetUpdate; + XRow m_row; + XRowLocate m_rowLocate; + XPropertySet m_rowSetProperties; + XParametersSupplier m_paramsSupplier; + + // -------------------------------------------------------------------------------------------------------- + class ResultSetMovementStress implements Runnable + { + XResultSet m_resultSet; + XRow m_row; + int m_id; + public ResultSetMovementStress(XResultSet _resultSet,int _id) throws java.lang.Exception { + m_resultSet = _resultSet; + m_row = (XRow)UnoRuntime.queryInterface(XRow.class,m_resultSet); + m_id = _id; + } + public void run() + { + try + { + m_resultSet.beforeFirst(); + for ( int i = 0; m_resultSet.next(); ++i ) + { + int pos = m_resultSet.getRow(); + int val = m_row.getInt(1); +// log.println("Clone Move(" + m_id +") before i: " + (i+1) + " Pos: " + pos + " Val: " + val); + testPosition( m_resultSet, m_row, i + 1, "clone move(" + m_id +")" ); +// val = m_row.getInt(1); +// log.println("Clone Move(" + m_id +") after i: " + (i+1) + " Pos: " + pos + " Val: " + val); + int pos2 = m_resultSet.getRow(); + assure("ResultSetMovementStress wrong position: " + i + " Pos1: " + pos + " Pos2: " + pos2,pos == pos2); + } + }catch(AssureException e){ + }catch(Exception e){ + assure("ResultSetMovementStress(" + m_id + ") failed: " + e,false); + } + } + } + // -------------------------------------------------------------------------------------------------------- + public String[] getTestMethodNames() { + return new String[] + { + "testRowSet", + "testRowSetEvents", + "testDeleteBehavior", + "testCloneMovesPlusDeletions", + "testCloneMovesPlusInsertions", + "testParameters" + }; + } + + // -------------------------------------------------------------------------------------------------------- + public String getTestObjectName() { + return "RowSet"; + } + + // -------------------------------------------------------------------------------------------------------- + private void createTestCase( boolean _defaultRowSet ) + { + if ( m_database == null ) + { + try + { + CRMDatabase database = new CRMDatabase( getFactory() ); + m_database = database.getDatabase(); + m_dataSource = m_database.getDataSource(); + } + catch(Exception e) + { + assure( "could not create the embedded HSQL database: " + e.getMessage(), false ); + } + } + + try + { + createStruture(); + } + catch( SQLException e ) + { + assure( "could not connect to the database/table structure, error message:\n" + e.getMessage(), false ); + } + + if ( _defaultRowSet ) + createRowSet( "TEST1", CommandType.TABLE, true, true ); + } + + + // -------------------------------------------------------------------------------------------------------- + private XMultiServiceFactory getFactory() + { + return (XMultiServiceFactory)param.getMSF(); + } + + // -------------------------------------------------------------------------------------------------------- + /** creates a com.sun.star.sdb.RowSet to use during the test + * @param command + * the command to use for the RowSet + * @param commandType + * the command type to use for the RowSet + * @param execute + * determines whether the RowSet should be executed + */ + private void createRowSet( String command, int commandType, boolean execute ) + { + createRowSet( command, commandType, execute, false ); + } + + // -------------------------------------------------------------------------------------------------------- + /** creates a com.sun.star.sdb.RowSet to use during the test + * @param command + * the command to use for the RowSet + * @param commandType + * the command type to use for the RowSet + * @param limitFetchSize + * determines whether the fetch size of the RowSet should be limited to MAX_FETCH_ROWS + * @param execute + * determines whether the RowSet should be executed + */ + private void createRowSet( String command, int commandType, boolean execute, boolean limitFetchSize ) + { + try + { + m_rowSet = (XRowSet)UnoRuntime.queryInterface(XRowSet.class, + getFactory().createInstance("com.sun.star.sdb.RowSet")); + XPropertySet rowSetProperties = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, m_rowSet ); + rowSetProperties.setPropertyValue( "Command", command ); + rowSetProperties.setPropertyValue( "CommandType", new Integer( commandType ) ); + rowSetProperties.setPropertyValue( "ActiveConnection",m_database.defaultConnection() ); + if ( limitFetchSize ) + rowSetProperties.setPropertyValue( "FetchSize", new Integer( MAX_FETCH_ROWS ) ); + + m_resultSet = (XResultSet)UnoRuntime.queryInterface( XResultSet.class, m_rowSet ); + m_resultSetUpdate = (XResultSetUpdate)UnoRuntime.queryInterface( XResultSetUpdate.class, m_rowSet ); + m_row = (XRow)UnoRuntime.queryInterface( XRow.class, m_rowSet ); + m_rowLocate = (XRowLocate)UnoRuntime.queryInterface( XRowLocate.class, m_resultSet ); + m_rowSetProperties = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, m_rowSet ); + m_paramsSupplier = (XParametersSupplier)UnoRuntime.queryInterface( XParametersSupplier.class, m_rowSet ); + + if ( execute ) + m_rowSet.execute(); + } + catch ( java.lang.Exception e ) + { + assure( "caught an exception while creating the RowSet. Type:\n" + e.getClass().toString() + "\nMessage:\n" + e.getMessage(), false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + public void testRowSet() throws java.lang.Exception { + + log.println("testing testRowSet"); + createTestCase( true ); + + // sequential postioning + m_resultSet.beforeFirst(); + testSequentialPositining(m_resultSet,m_row); + + // absolute positioning + testAbsolutePositioning(m_resultSet,m_row); + + // 3rd test + test3(createClone(),m_resultSet); + // 4th test + test4(m_resultSet); + + // concurrent (multi threaded) access to the row set and its clones + testConcurrentAccess(m_resultSet); + } + + // -------------------------------------------------------------------------------------------------------- + XResultSet createClone() throws SQLException + { + XResultSetAccess rowAcc = (XResultSetAccess)UnoRuntime.queryInterface( XResultSetAccess.class, m_rowSet ); + return rowAcc.createResultSet(); + } + + // -------------------------------------------------------------------------------------------------------- + void createStruture() throws SQLException + { + m_database.executeSQL( "DROP TABLE \"TEST1\" IF EXISTS" ); + m_database.executeSQL( "CREATE TABLE \"TEST1\" (\"ID\" integer not null primary key, \"col2\" varchar(50) )" ); + + XConnection connection = m_database.defaultConnection(); + XPreparedStatement prep = connection.prepareStatement("INSERT INTO \"TEST1\" values (?,?)"); + XParameters para = (XParameters)UnoRuntime.queryInterface(XParameters.class,prep); + for(int i=1 ; i <= MAX_TABLE_ROWS ; ++i){ + para.setInt(1, i ); + para.setString(2, "Test" + i); + prep.executeUpdate(); + } + + XTablesSupplier suppTables = (XTablesSupplier)UnoRuntime.queryInterface( XTablesSupplier.class, connection ); + XRefreshable refresh = (XRefreshable)UnoRuntime.queryInterface( XRefreshable.class, suppTables.getTables() ); + refresh.refresh(); + } + + // -------------------------------------------------------------------------------------------------------- + void testPosition(XResultSet m_resultSet,XRow m_row,int expectedValue,String location) throws SQLException + { + int val = m_row.getInt(1); + int pos = m_resultSet.getRow(); + assure( location + ": value/position do not match: " + pos + " (pos) != " + val + " (val)", val == pos ); + assure( location + ": value/position are not as expected: " + val + " (val) != " + expectedValue + " (expected)", val == expectedValue ); + } + + // -------------------------------------------------------------------------------------------------------- + void testSequentialPositining(XResultSet _resultSet,XRow _row) + { + try + { + // 1st test + int i=1; + while(_resultSet.next()) + { + testPosition( _resultSet, _row, i, "testSequentialPositining" ); + ++i; + } + } + catch(AssureException e) + { + } + catch(Exception e) + { + assure("testSequentialPositining failed: " + e,false); + } + } + + // -------------------------------------------------------------------------------------------------------- + void testAbsolutePositioning(XResultSet _resultSet,XRow _row){ + try{ + for(int i = 1 ; i <= MAX_FETCH_ROWS ; ++i){ + int calcPos = (MAX_TABLE_ROWS % i) + 1; + assure( "testAbsolutePositioning failed", _resultSet.absolute(calcPos) ); + testPosition( _resultSet, _row, calcPos, "testAbsolutePositioning" ); + } + }catch(AssureException e){ + }catch(Exception e){ + assure("testAbsolutePositioning failed: " + e,false); + } + } + + // -------------------------------------------------------------------------------------------------------- + void test3(XResultSet clone,XResultSet _resultSet){ + try{ + XRow _row = (XRow)UnoRuntime.queryInterface(XRow.class,_resultSet); + XRow cloneRow = (XRow)UnoRuntime.queryInterface(XRow.class,clone); + for(int i = 1 ; i <= MAX_FETCH_ROWS ; ++i){ + int calcPos = (MAX_TABLE_ROWS % i) + 1; + if ( clone.absolute(calcPos) ) + { + testPosition( clone, cloneRow, calcPos, "test3" ); + testAbsolutePositioning(_resultSet,_row); + testAbsolutePositioning(clone,cloneRow); + } + } + }catch(AssureException e){ + }catch(Exception e){ + assure("test3 failed: " + e,false); + } + } + + // -------------------------------------------------------------------------------------------------------- + void test4(XResultSet _resultSet){ + try{ + XRow _row = (XRow)UnoRuntime.queryInterface(XRow.class,_resultSet); + _resultSet.beforeFirst(); + + for(int i = 1 ; i <= MAX_TABLE_ROWS ; ++i){ + _resultSet.next(); + XResultSet clone = createClone(); + XRow cloneRow = (XRow)UnoRuntime.queryInterface(XRow.class,clone); + int calcPos = MAX_TABLE_ROWS - 1; + if ( calcPos != 0 && clone.absolute(calcPos) ) + { + testPosition( clone, cloneRow, calcPos, "test4: clone" ); + testPosition( _resultSet, _row, i, "test4: rowset" ); + } + } + }catch(AssureException e){ + }catch(Exception e){ + assure("test4 failed: " + e,false); + } + } + + // -------------------------------------------------------------------------------------------------------- + void testConcurrentAccess(XResultSet _resultSet) + { + log.println("testing Thread"); + try + { + XRow _row = (XRow)UnoRuntime.queryInterface(XRow.class,_resultSet); + _resultSet.beforeFirst(); + + final int numberOfThreads = 10; + + Thread threads[] = new Thread[numberOfThreads]; + for ( int i=0; i<numberOfThreads; ++i ) + { + threads[i] = new Thread( new ResultSetMovementStress( createClone(), i ) ); + System.out.println( "starting thread " + String.valueOf(i+1) + " of " + String.valueOf( numberOfThreads ) ); + threads[i].start(); + } + + for ( int i=0; i<numberOfThreads; ++i ) + threads[i].join(); + } + catch(AssureException e) + { + } + catch(Exception e) + { + e.printStackTrace(); + assure("testConcurrentAccess failed: " + e,false); + } + } + // -------------------------------------------------------------------------------------------------------- + public void testRowSetEvents() throws java.lang.Exception { + log.println("testing RowSet Events"); + createTestCase( true ); + + // first we create our RowSet object + RowSetEventListener pRow = new RowSetEventListener(this); + + XColumnsSupplier colSup = (XColumnsSupplier)UnoRuntime.queryInterface(XColumnsSupplier.class,m_rowSet); + XPropertySet col = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class,colSup.getColumns().getByName("ID")); + col.addPropertyChangeListener("Value", pRow); + m_rowSetProperties.addPropertyChangeListener("IsModified", pRow); + m_rowSetProperties.addPropertyChangeListener("IsNew", pRow); + m_rowSetProperties.addPropertyChangeListener("IsRowCountFinal", pRow); + m_rowSetProperties.addPropertyChangeListener("RowCount", pRow); + + XRowSetApproveBroadcaster xApBroad = (XRowSetApproveBroadcaster)UnoRuntime.queryInterface(XRowSetApproveBroadcaster.class,m_resultSet); + xApBroad.addRowSetApproveListener(pRow); + m_rowSet.addRowSetListener(pRow); + + // do some movements to check if we got all notifications + Class cResSet = java.lang.Class.forName("com.sun.star.sdbc.XResultSet"); + boolean moves[] = new boolean[9]; + for( int i = 0; i < moves.length; ++i) + moves[i] = false; + moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = true; + moves[RowSetEventListener.COLUMN_VALUE] = true; + moves[RowSetEventListener.CURSOR_MOVED] = true; + moves[RowSetEventListener.IS_ROW_COUNT_FINAL] = true; + moves[RowSetEventListener.ROW_COUNT] = true; + + testCursorMove(m_resultSet,cResSet.getMethod("afterLast",(Class[])null),pRow,moves,null); + + moves[RowSetEventListener.IS_ROW_COUNT_FINAL] = false; + moves[RowSetEventListener.ROW_COUNT] = false; + testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null); + testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null); + testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null); + testCursorMove(m_resultSet,cResSet.getMethod("last",(Class[])null),pRow,moves,null); + testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null); + testCursorMove(m_resultSet,cResSet.getMethod("first",(Class[])null),pRow,moves,null); + testCursorMove(m_resultSet,cResSet.getMethod("previous",(Class[])null),pRow,moves,null); + testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null); + moves[RowSetEventListener.IS_MODIFIED] = true; + XRowUpdate updRow = (XRowUpdate)UnoRuntime.queryInterface(XRowUpdate.class,m_resultSet); + updRow.updateString(2,"Test21"); + testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null); + + moves[RowSetEventListener.IS_MODIFIED] = false; + Class cupd = java.lang.Class.forName("com.sun.star.sdbc.XResultSetUpdate"); + XResultSetUpdate upd = (XResultSetUpdate)UnoRuntime.queryInterface(XResultSetUpdate.class,m_resultSet); + testCursorMove(upd,cupd.getMethod("moveToInsertRow",(Class[])null),pRow,moves,null); + + updRow.updateInt(1, MAX_TABLE_ROWS + 2); + updRow.updateString(2, "HHHH"); + moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = false; + moves[RowSetEventListener.CURSOR_MOVED] = false; + moves[RowSetEventListener.IS_MODIFIED] = true; + moves[RowSetEventListener.IS_NEW] = true; + moves[RowSetEventListener.ROW_COUNT] = true; + moves[RowSetEventListener.APPROVE_ROW_CHANGE] = true; + moves[RowSetEventListener.ROW_CHANGED] = true; + testCursorMove(upd,cupd.getMethod("insertRow",(Class[])null),pRow,moves,null); + + moves[RowSetEventListener.IS_NEW] = false; + moves[RowSetEventListener.ROW_COUNT] = false; + m_resultSet.first(); + updRow.updateInt(1, MAX_TABLE_ROWS + 3); + updRow.updateString(2, "__"); + testCursorMove(upd,cupd.getMethod("updateRow",(Class[])null),pRow,moves,null); + + moves[RowSetEventListener.IS_NEW] = true; + moves[RowSetEventListener.ROW_COUNT] = true; + m_resultSet.first(); + testCursorMove(upd,cupd.getMethod("deleteRow",(Class[])null),pRow,moves,null); + + moves[RowSetEventListener.IS_NEW] = false; + moves[RowSetEventListener.COLUMN_VALUE] = true; + moves[RowSetEventListener.ROW_COUNT] = false; + m_resultSet.first(); + updRow.updateString(2,"Test21"); + testCursorMove(m_resultSet,cResSet.getMethod("refreshRow",(Class[])null),pRow,moves,null); + + m_resultSet.first(); + updRow.updateString(2,"Test21"); + testCursorMove(upd,cupd.getMethod("cancelRowUpdates",(Class[])null),pRow,moves,null); + + for( int i = 0; i < moves.length; ++i) + moves[i] = false; + moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = true; + moves[RowSetEventListener.COLUMN_VALUE] = true; + moves[RowSetEventListener.CURSOR_MOVED] = true; + + Class cloc = java.lang.Class.forName("com.sun.star.sdbcx.XRowLocate"); + m_resultSet.first(); + Object bookmark = m_rowLocate.getBookmark(); + m_resultSet.next(); + Object temp[] = new Object[1]; + temp[0] = bookmark; + Class ctemp[] = new Class[1]; + ctemp[0] = Object.class; + testCursorMove(m_rowLocate,cloc.getMethod("moveToBookmark",ctemp),pRow,moves,temp); + + Object temp2[] = new Object[2]; + temp2[0] = bookmark; + temp2[1] = new Integer(1); + Class ctemp2[] = new Class[2]; + ctemp2[0] = Object.class; + ctemp2[1] = int.class; + testCursorMove(m_rowLocate,cloc.getMethod("moveRelativeToBookmark",ctemp2),pRow,moves,temp2); + + for( int i = 0; i < moves.length; ++i) + moves[i] = false; + moves[RowSetEventListener.APPROVE_ROW_CHANGE] = true; + moves[RowSetEventListener.ROW_CHANGED] = true; + moves[RowSetEventListener.ROW_COUNT] = true; + Class cdelRows = java.lang.Class.forName("com.sun.star.sdbcx.XDeleteRows"); + ctemp[0] = Object[].class; + XDeleteRows delRows = (XDeleteRows)UnoRuntime.queryInterface(XDeleteRows.class,m_resultSet); + Object bookmarks[] = new Object[5]; + m_resultSet.first(); + for ( int i = 0; i < bookmarks.length ; ++i ){ + m_resultSet.next(); + bookmarks[i] = m_rowLocate.getBookmark(); + } + + temp[0] = bookmarks; + testCursorMove(delRows,cdelRows.getMethod("deleteRows",ctemp),pRow,moves,temp); + + // now destroy the RowSet + XComponent xComp = (XComponent)UnoRuntime.queryInterface(XComponent.class,m_resultSet); + xComp.dispose(); + } + + // -------------------------------------------------------------------------------------------------------- + private void testCursorMove(Object res + ,java.lang.reflect.Method _method + , RowSetEventListener _evt + , boolean _must[] + , Object args[]) throws java.lang.Exception { + _evt.clearCalling(); + _method.invoke(res,args); + + log.println("testing events for " + _method.getName()); + int calling[] = _evt.getCalling(); + int pos = 1; + assure("Callings are not in the correct order for APPROVE_CURSOR_MOVE " , + ( !_must[RowSetEventListener.APPROVE_CURSOR_MOVE] || calling[RowSetEventListener.APPROVE_CURSOR_MOVE] == -1) || calling[RowSetEventListener.APPROVE_CURSOR_MOVE] == pos++ ); + assure("Callings are not in the correct order for APPROVE_ROW_CHANGE" , + ( !_must[ RowSetEventListener.APPROVE_ROW_CHANGE] || calling[RowSetEventListener.APPROVE_ROW_CHANGE] == -1) || calling[RowSetEventListener.APPROVE_ROW_CHANGE] == pos++); + assure("Callings are not in the correct order for COLUMN_VALUE" , + ( !_must[RowSetEventListener.COLUMN_VALUE] || calling[RowSetEventListener.COLUMN_VALUE] == -1) || calling[RowSetEventListener.COLUMN_VALUE] == pos++); + assure("Callings are not in the correct order for CURSOR_MOVED" , + ( !_must[RowSetEventListener.CURSOR_MOVED] || calling[RowSetEventListener.CURSOR_MOVED] == -1) || calling[RowSetEventListener.CURSOR_MOVED] == pos++); + assure("Callings are not in the correct order for ROW_CHANGED" , + ( !_must[ RowSetEventListener.ROW_CHANGED] || calling[RowSetEventListener.ROW_CHANGED] == -1) || calling[RowSetEventListener.ROW_CHANGED] == pos++); + assure("Callings are not in the correct order for IS_MODIFIED" , + ( !_must[ RowSetEventListener.IS_MODIFIED] || calling[RowSetEventListener.IS_MODIFIED] == -1) || calling[RowSetEventListener.IS_MODIFIED] == pos++); + assure("Callings are not in the correct order for IS_NEW" , + ( !_must[ RowSetEventListener.IS_NEW] || calling[RowSetEventListener.IS_NEW] == -1) || calling[RowSetEventListener.IS_NEW] == pos++); + assure("Callings are not in the correct order for ROW_COUNT" , + ( !_must[ RowSetEventListener.ROW_COUNT] || calling[RowSetEventListener.ROW_COUNT] == -1) || calling[RowSetEventListener.ROW_COUNT] == pos++); + assure("Callings are not in the correct order for IS_ROW_COUNT_FINAL" , + ( !_must[ RowSetEventListener.IS_ROW_COUNT_FINAL] || calling[RowSetEventListener.IS_ROW_COUNT_FINAL] == -1) || calling[RowSetEventListener.IS_ROW_COUNT_FINAL] == pos++); + + _evt.clearCalling(); + } + + // -------------------------------------------------------------------------------------------------------- + /** returns the current row count of the RowSet + */ + private int currentRowCount() throws UnknownPropertyException, WrappedTargetException + { + Integer rowCount = (Integer)m_rowSetProperties.getPropertyValue( "RowCount" ); + return rowCount.intValue(); + } + + // -------------------------------------------------------------------------------------------------------- + /** positions the row set at an arbitrary position between 2 and (current row count - 1) + */ + private int positionRandom() throws SQLException, UnknownPropertyException, WrappedTargetException + { + int position = (new java.util.Random()).nextInt( currentRowCount() - 2 ) + 2; + assure( "sub task failed: could not position to row no. " + (new Integer( position )).toString(), + m_resultSet.absolute( position ) ); + return m_resultSet.getRow(); + } + + // -------------------------------------------------------------------------------------------------------- + /** moves the result set to a random record between 2 and (current row count - 1), and deletes this record + * + * After returning from this method, the row set is still positioned at the deleted record + * @return + * the number/position of the record which has been deleted + */ + private int deleteRandom() throws SQLException, UnknownPropertyException, WrappedTargetException + { + // check if the current position and the row count in the result set is changed by a deletion (it should not) + int positionBefore = positionRandom(); + int rowCountBefore = currentRowCount(); + + m_resultSetUpdate.deleteRow(); + + int positionAfter = m_resultSet.getRow(); + int rowCountAfter = currentRowCount(); + assure( "position changed during |deleteRow| (it should not)", positionAfter == positionBefore ); + assure( "row count changed with a |deleteRow| (it should not)", rowCountBefore == rowCountAfter ); + assure( "RowSet does not report the current row as deleted after |deleteRow|", m_resultSet.rowDeleted() ); + + return positionBefore; + } + + // -------------------------------------------------------------------------------------------------------- + public void testDeleteBehavior() throws Exception + { + createTestCase( true ); + + // ensure that all records are known + m_resultSet.last(); + int initialRowCount = currentRowCount(); + + // delete a random row + int deletedRow = deleteRandom(); + + // ..................................................................................................... + // asking for the bookmark of a deleted row should fail + boolean caughtException = false; + try { m_rowLocate.getBookmark(); } + catch ( SQLException e ) { caughtException = true; } + assure( "asking for the bookmark of a deleted row should throw an exception", caughtException ); + + // ..................................................................................................... + // isXXX methods should return |false| on a deleted row + assure( "one of the isFoo failed after |deleteRow|", !m_resultSet.isBeforeFirst() && !m_resultSet.isAfterLast() && !m_resultSet.isFirst() && !m_resultSet.isLast() ); + // note that we can assume that isFirst / isLast also return |false|, since deleteRandom did + // not position on the first or last record, but inbetween + + // ..................................................................................................... + // check if moving away from this row in either direction yields the expected results + assure( "|previous| after |deleteRow| failed", m_resultSet.previous() ); + int positionPrevious = m_resultSet.getRow(); + assure( "position after |previous| after |deleteRow| is not as expected", positionPrevious == deletedRow - 1 ); + + deletedRow = deleteRandom(); + assure( "|next| after |deleteRow| failed", m_resultSet.next() ); + int positionAfter = m_resultSet.getRow(); + assure( "position after |next| after |deleteRow| is not as expected", positionAfter == deletedRow ); + // since the deleted record "vanishs" as soon as the cursor is moved away from it, the absolute position does + // not change with a |next| call here + + // ..................................................................................................... + // check if the deleted rows really vanished after moving away from them + assure( "row count did not change as expected after two deletions", initialRowCount - 2 == currentRowCount() ); + + // ..................................................................................................... + // check if the deleted row vanishes after moving to the insertion row + int rowCountBefore = currentRowCount(); + int deletedPos = deleteRandom(); + m_resultSetUpdate.moveToInsertRow(); + assure( "moving to the insertion row immediately after |deleteRow| does not adjust the row count", rowCountBefore == currentRowCount() + 1 ); + + m_resultSetUpdate.moveToCurrentRow(); + assure( "|moveToCurrentRow| after |deleteRow| + |moveToInsertRow| results in unexpected position", + ( m_resultSet.getRow() == deletedPos ) && !m_resultSet.rowDeleted() ); + + // the same, but this time with deleting the first row (which is not covered by deleteRandom) + m_resultSet.last(); + m_resultSetUpdate.deleteRow(); + m_resultSetUpdate.moveToInsertRow(); + m_resultSetUpdate.moveToCurrentRow(); + assure( "|last| + |deleteRow| + |moveToInsertRow| + |moveToCurrentRow| results in wrong state", m_resultSet.isAfterLast() ); + + // ..................................................................................................... + // check if deleting a deleted row fails as expected + deleteRandom(); + caughtException = false; + try { m_resultSetUpdate.deleteRow(); } + catch( SQLException e ) { caughtException = true; } + assure( "deleting a deleted row succeeded - it shouldn't", caughtException ); + + // ..................................................................................................... + // check if deleteRows fails if it contains the bookmark of a previously-deleted row + m_resultSet.first(); + Object firstBookmark = m_rowLocate.getBookmark(); + positionRandom(); + Object deleteBookmark = m_rowLocate.getBookmark(); + m_resultSetUpdate.deleteRow(); + XDeleteRows multiDelete = (XDeleteRows)UnoRuntime.queryInterface( XDeleteRows.class, m_resultSet ); + int[] deleteSuccess = multiDelete.deleteRows(new Object[]{firstBookmark, deleteBookmark}); + assure( "XDeleteRows::deleteRows with the bookmark of an already-deleted row failed", + ( deleteSuccess.length == 2 ) && ( deleteSuccess[0] != 0 ) && ( deleteSuccess[1] == 0 ) ); + + // ..................................................................................................... + // check if refreshing a deleted row fails as expected + deleteRandom(); + caughtException = false; + try { m_resultSet.refreshRow(); } + catch( SQLException e ) { caughtException = true; } + assure( "refreshing a deleted row succeeded - it shouldn't", caughtException ); + + // ..................................................................................................... + // rowUpdated/rowDeleted + deleteRandom(); + assure( "rowDeleted and/or rowUpdated are wrong on a deleted row", !m_resultSet.rowUpdated() && !m_resultSet.rowInserted() ); + + // ..................................................................................................... + // updating values in a deleted row should fail + deleteRandom(); + XRowUpdate rowUpdated = (XRowUpdate)UnoRuntime.queryInterface( XRowUpdate.class, m_resultSet ); + caughtException = false; + try { rowUpdated.updateString( 2, "Test21" ); } + catch( SQLException e ) { caughtException = true; } + assure( "updating values in a deleted row should not succeed", caughtException ); + } + + // -------------------------------------------------------------------------------------------------------- + /** checks whether deletions on the main RowSet properly interfere (or don't interfere) with the movement + * on a clone of the RowSet + */ + public void testCloneMovesPlusDeletions() throws SQLException, UnknownPropertyException, WrappedTargetException + { + createTestCase( true ); + // ensure that all records are known + m_resultSet.last(); + + XResultSet clone = createClone(); + XRowLocate cloneRowLocate = (XRowLocate)UnoRuntime.queryInterface( XRowLocate.class, clone ); + + positionRandom(); + + // ..................................................................................................... + // move the clone to the same record as the RowSet, and delete this record + cloneRowLocate.moveToBookmark( m_rowLocate.getBookmark() ); + int clonePosition = clone.getRow(); + m_resultSetUpdate.deleteRow(); + + assure( "clone doesn't know that its current row has been deleted via the RowSet", clone.rowDeleted() ); + assure( "clone's position changed somehow during deletion", clonePosition == clone.getRow() ); + + // ..................................................................................................... + // move the row set away from the deleted record. This should still not touch the state of the clone + m_resultSet.previous(); + + assure( "clone doesn't know (anymore) that its current row has been deleted via the RowSet", clone.rowDeleted() ); + assure( "clone's position changed somehow during deletion and RowSet-movement", clonePosition == clone.getRow() ); + + // ..................................................................................................... + // move the clone away from the deleted record + clone.next(); + assure( "clone still assumes that its row is deleted - but we already moved it", !clone.rowDeleted() ); + + // ..................................................................................................... + // check whether deleting the extremes (first / last) work + m_resultSet.first(); + cloneRowLocate.moveToBookmark( m_rowLocate.getBookmark() ); + m_resultSetUpdate.deleteRow(); + clone.previous(); + assure( "deleting the first record left the clone in a strange state (after |previous|)", clone.isBeforeFirst() ); + clone.next(); + assure( "deleting the first record left the clone in a strange state (after |previous| + |next|)", clone.isFirst() ); + + m_resultSet.last(); + cloneRowLocate.moveToBookmark( m_rowLocate.getBookmark() ); + m_resultSetUpdate.deleteRow(); + clone.next(); + assure( "deleting the last record left the clone in a strange state (after |next|)", clone.isAfterLast() ); + clone.previous(); + assure( "deleting the first record left the clone in a strange state (after |next| + |previous|)", clone.isLast() ); + + // ..................................................................................................... + // check whether movements of the clone interfere with movements of the RowSet, if the latter is on a deleted row + int positionBefore = positionRandom(); + m_resultSetUpdate.deleteRow(); + assure( "|deleteRow|, but no |rowDeleted| (this should have been found much earlier!)", m_resultSet.rowDeleted() ); + clone.beforeFirst(); + while ( clone.next() ) + ; + assure( "row set forgot that the current row is deleted", m_resultSet.rowDeleted() ); + + assure( "moving to the next record after |deleteRow| and clone moves failed", m_resultSet.next() ); + assure( "wrong position after |deleteRow| and clone movement", !m_resultSet.isAfterLast() && !m_resultSet.isBeforeFirst() ); + assure( "wrong absolute position after |deleteRow| and clone movement", m_resultSet.getRow() == positionBefore ); + } + + // -------------------------------------------------------------------------------------------------------- + /** checks whether insertions on the main RowSet properly interfere (or don't interfere) with the movement + * on a clone of the RowSet + */ + public void testCloneMovesPlusInsertions() throws SQLException, UnknownPropertyException, WrappedTargetException, PropertyVetoException, com.sun.star.lang.IllegalArgumentException + { + createTestCase( true ); + // ensure that all records are known + m_rowSetProperties.setPropertyValue( "FetchSize", new Integer( 10 ) ); + + XResultSet clone = createClone(); + XRow cloneRow = (XRow)UnoRuntime.queryInterface( XRow.class, clone ); + + // ..................................................................................................... + // first check the basic scenario without the |moveToInsertRow| |moveToCurrentRow|, to ensure that + // really those are broken, if at all + m_resultSet.last(); + clone.first(); + clone.absolute( 11 ); + clone.first(); + + int rowValue1 = m_row.getInt(1); + int rowPos = m_resultSet.getRow(); + int rowValue2 = m_row.getInt(1); + assure( "repeated query for the same column value delivers different values (" + rowValue1 + " and " + rowValue2 + ") on row: " + rowPos, + rowValue1 == rowValue2 ); + + testPosition( clone, cloneRow, 1, "mixed clone/rowset move: clone check" ); + testPosition( m_resultSet, m_row, MAX_TABLE_ROWS, "mixed clone/rowset move: rowset check" ); + + // ..................................................................................................... + // now the complete scenario + m_resultSet.last(); + m_resultSetUpdate.moveToInsertRow(); + clone.first(); + clone.absolute( 11 ); + clone.first(); + m_resultSetUpdate.moveToCurrentRow(); + + testPosition( clone, cloneRow, 1, "mixed clone/rowset move/insertion: clone check" ); + testPosition( m_resultSet, m_row, 100, "mixed clone/rowset move/insertion: rowset check" ); + } + + // -------------------------------------------------------------------------------------------------------- + private void testTableParameters() + { + // for a row set simply based on a table, there should be not parameters at all + createRowSet( "products", CommandType.TABLE, false ); + try + { + verifyParameters( new String[] {}, "testTableParameters" ); + } + catch( AssureException e ) { throw e; } + catch( Exception e ) + { + assure( "testing the parameters of a table failed" + e.getMessage(), false ); + } + } + // -------------------------------------------------------------------------------------------------------- + private void testParametersAfterNormalExecute() + { + try + { + createRowSet( "SELECT * FROM \"customers\"", CommandType.COMMAND, true ); + m_rowSetProperties.setPropertyValue( "Command", "SELECT * FROM \"customers\" WHERE \"City\" = :city"); + XParameters rowsetParams = (XParameters)UnoRuntime.queryInterface( XParameters.class, + m_rowSet ); + rowsetParams.setString( 1, "London" ); + m_rowSet.execute(); + } + catch( AssureException e ) { throw e; } + catch( Exception e ) + { + assure( "testing the parameters of a table failed" + e.getMessage(), false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + private void verifyParameters( String[] _paramNames, String _context ) throws com.sun.star.uno.Exception + { + XIndexAccess params = m_paramsSupplier.getParameters(); + int expected = _paramNames.length; + int found = params != null ? params.getCount() : 0; + + assure( "wrong number of parameters (expected: " + expected + ", found: " + found + ") in " + _context, + found == expected ); + + if ( found == 0 ) + return; + + for ( int i=0; i<expected; ++i ) + { + XPropertySet parameter = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, + params.getByIndex(i) ); + + String expectedName = _paramNames[i]; + String foundName = (String)parameter.getPropertyValue( "Name" ); + assure( "wrong parameter name (expected: " + expectedName + ", found: " + foundName + ") in" + _context, + expectedName.equals( foundName ) ); + } + } + + // -------------------------------------------------------------------------------------------------------- + private void testParametrizedQuery() + { + try + { + // for a row set based on a parametrized query, those parameters should be properly + // recognized + m_dataSource.createQuery( "products like", "SELECT * FROM \"products\" WHERE \"Name\" LIKE :product_name" ); + createRowSet( "products like", CommandType.QUERY, false ); + verifyParameters( new String[] { "product_name" }, "testParametrizedQuery" ); + } + catch( AssureException e ) { throw e; } + catch( Exception e ) + { + assure( "testing the parameters of a parametrized query failed" + e.getMessage(), false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + private void testParametersInteraction() + { + try + { + createRowSet( "products like", CommandType.QUERY, false ); + + // let's fill in a parameter value via XParameters, and see whether it is respected by the parameters container + XParameters rowsetParams = (XParameters)UnoRuntime.queryInterface( XParameters.class, + m_rowSet ); + rowsetParams.setString( 1, "Apples" ); + + XIndexAccess params = m_paramsSupplier.getParameters(); + XPropertySet firstParam = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex(0) ); + Object firstParamValue = firstParam.getPropertyValue( "Value" ); + + assure( "XParameters and the parameters container do not properly interact", + firstParamValue.equals( "Apples" ) ); + + // let's see whether this also survices an execute of the row set + rowsetParams.setString( 1, "Oranges" ); + m_rowSet.execute(); + { + // TODO: the following would not be necessary if the parameters container would *survive* + // the execution of the row set. It currently doesn't (though the values it represents do). + // It would be nice, but not strictly necessary, if it would. + params = m_paramsSupplier.getParameters(); + firstParam = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex(0) ); + } + firstParamValue = firstParam.getPropertyValue( "Value" ); + assure( "XParameters and the parameters container do not properly interact, after the row set has been executed", + firstParamValue.equals( "Oranges" ) ); + } + catch( AssureException e ) { throw e; } + catch( Exception e ) + { + assure( "could not text the relationship between XParameters and XParametersSupplier" + e.getMessage(), false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + private void testParametersInFilter() + { + try + { + createRowSet( "SELECT * FROM \"customers\"", CommandType.COMMAND, false ); + m_rowSetProperties.setPropertyValue( "Filter", "\"City\" = :city" ); + + m_rowSetProperties.setPropertyValue( "ApplyFilter", new Boolean( true ) ); + verifyParameters( new String[] { "city" }, "testParametersInFilter" ); + + m_rowSetProperties.setPropertyValue( "ApplyFilter", new Boolean( false ) ); + verifyParameters( new String[] {}, "testParametersInFilter" ); + } + catch( AssureException e ) { throw e; } + catch( Exception e ) + { + assure( "testing the parameters within a WHERE clause failed" + e.getMessage(), false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + /** checks the XParametersSupplier functionality of a RowSet + */ + public void testParameters() + { + createTestCase( false ); + // use an own RowSet instance, not the one which is also used for the other cases + + testTableParameters(); + testParametrizedQuery(); + testParametersInFilter(); + + testParametersAfterNormalExecute(); + + testParametersInteraction(); + } +} + diff --git a/dbaccess/qa/complex/dbaccess/RowSetEventListener.java b/dbaccess/qa/complex/dbaccess/RowSetEventListener.java new file mode 100644 index 000000000000..774947ecc2dc --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/RowSetEventListener.java @@ -0,0 +1,116 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: RowSetEventListener.java,v $ + * $Revision: 1.4 $ + * + * 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.dbaccess; + +import com.sun.star.sdb.XRowSetApproveListener; +import com.sun.star.sdbc.XRowSetListener; +import com.sun.star.sdb.RowChangeEvent; +import com.sun.star.lang.EventObject; +import com.sun.star.beans.XPropertyChangeListener; + +public class RowSetEventListener implements XRowSetApproveListener,XRowSetListener,XPropertyChangeListener +{ + public static final int APPROVE_CURSOR_MOVE = 0; + public static final int APPROVE_ROW_CHANGE = 1; + public static final int COLUMN_VALUE = 2; + public static final int CURSOR_MOVED = 3; + public static final int ROW_CHANGED = 4; + public static final int IS_MODIFIED = 5; + public static final int IS_NEW = 6; + public static final int ROW_COUNT = 7; + public static final int IS_ROW_COUNT_FINAL = 8; + + RowSet rowset; + int callPos = 1; + int calling []; + + RowSetEventListener(RowSet _rowset){ + rowset = _rowset; + calling = new int [9]; + clearCalling(); + } + public int[] getCalling(){ + return calling; + } + public void clearCalling(){ + for(int i = 0 ; i < calling.length; ++i){ + calling[i] = -1; + } + callPos = 1; + } + // XEventListener + public void disposing(com.sun.star.lang.EventObject event) + { + } + // XRowSetApproveBroadcaster + public boolean approveCursorMove(EventObject event) + { + calling[APPROVE_CURSOR_MOVE] = callPos++; + return true; + } + public boolean approveRowChange(RowChangeEvent event) + { + calling[APPROVE_ROW_CHANGE] = callPos++; + return true; + } + public boolean approveRowSetChange(EventObject event) + { + return true; + } + + // XRowSetListener + public void cursorMoved(com.sun.star.lang.EventObject event) + { + calling[CURSOR_MOVED] = callPos++; + } + public void rowChanged(com.sun.star.lang.EventObject event) + { + calling[ROW_CHANGED] = callPos++; + } + public void rowSetChanged(com.sun.star.lang.EventObject event) + { + } + + public void propertyChange(com.sun.star.beans.PropertyChangeEvent propertyChangeEvent) { + if ( propertyChangeEvent.PropertyName.equals("Value") ){ + calling[COLUMN_VALUE] = callPos++; + } else if ( propertyChangeEvent.PropertyName.equals("IsModified") ){ + calling[IS_MODIFIED] = callPos++; + } else if ( propertyChangeEvent.PropertyName.equals("IsNew") ){ + calling[IS_NEW] = callPos++; + } else if ( propertyChangeEvent.PropertyName.equals("RowCount") ){ + calling[ROW_COUNT] = callPos++; + } else if ( propertyChangeEvent.PropertyName.equals("IsRowCountFinal") ){ + calling[IS_ROW_COUNT_FINAL] = callPos++; + } + } + +} diff --git a/dbaccess/qa/complex/dbaccess/SingleSelectQueryComposer.java b/dbaccess/qa/complex/dbaccess/SingleSelectQueryComposer.java new file mode 100755 index 000000000000..c73ce6496c88 --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/SingleSelectQueryComposer.java @@ -0,0 +1,314 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SingleSelectQueryComposer.java,v $ + * $Revision: 1.8.14.1 $ + * + * 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.dbaccess; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.beans.*; +import com.sun.star.sdbcx.*; +import com.sun.star.sdbc.*; +import com.sun.star.sdb.*; +import com.sun.star.container.*; +import com.sun.star.lang.XMultiServiceFactory; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class SingleSelectQueryComposer extends CRMBasedTestCase +{ + private XSingleSelectQueryComposer m_composer; + + private final String complexFilter = "( \"ID\" = 1 AND \"Postal\" = '4' )" + + " OR ( \"ID\" = 2 AND \"Postal\" = '5' )" + + " OR ( \"ID\" = '3' AND \"Postal\" = '6' AND \"Address\" = '7' )" + + " OR ( \"Address\" = '8' )" + + " OR ( \"Postal\" = '9' )"; + private final String innerProductsQuery = "products (inner)"; + + // -------------------------------------------------------------------------------------------------------- + public String[] getTestMethodNames() + { + return new String[] { + "testAttributes", + "testSubQueries", + "testParameters", + "testDisjunctiveNormalForm" + }; + } + + // -------------------------------------------------------------------------------------------------------- + public String getTestObjectName() + { + return "SingleSelectQueryComposer"; + } + + // -------------------------------------------------------------------------------------------------------- + private void createQueries() throws Exception + { + m_database.getDatabase().getDataSource().createQuery( innerProductsQuery, "SELECT * FROM \"products\"" ); + } + + // -------------------------------------------------------------------------------------------------------- + protected void createTestCase() + { + try + { + super.createTestCase(); + + createQueries(); + + m_composer = createQueryComposer(); + + } + catch ( Exception e ) + { + assure( "caught an exception (" + e.getMessage() + ") while creating the test case", false ); + } + } + + // -------------------------------------------------------------------------------------------------------- + private void checkAttributeAccess( String _attributeName, String _attributeValue ) + { + log.println( "setting " + _attributeName + " to " + _attributeValue ); + String realValue = null; + try + { + Class composerClass = m_composer.getClass(); + Method attributeGetter = composerClass.getMethod( "get" + _attributeName, new Class[] {} ); + Method attributeSetter = composerClass.getMethod( "set" + _attributeName, new Class[] { String.class } ); + + attributeSetter.invoke( m_composer, new Object[] { _attributeValue } ); + realValue = (String)attributeGetter.invoke( m_composer, new Object[] {} ); + } + catch ( NoSuchMethodException e ) { } + catch ( IllegalAccessException e ) { } + catch ( InvocationTargetException e ) { } + assure( "set/get" + _attributeName + " not working as expected (set: " + _attributeValue + ", get: " + ( realValue != null ? realValue : "null" ) + ")", + realValue.equals( _attributeValue ) ); + log.println( " (results in " + (String)m_composer.getQuery() + ")" ); + } + + /** tests accessing attributes of the composer (order, filter, group by, having) + */ + public void testAttributes() + { + log.println("testing SingleSelectQueryComposer's attributes (order, filter, group by, having)"); + + try + { + log.println("check setQuery"); + final String simpleQuery = "SELECT * FROM \"customers\""; + m_composer.setQuery( simpleQuery ); + assure( "set/getQuery inconsistent", m_composer.getQuery().equals( simpleQuery ) ); + + checkAttributeAccess( "Filter", "\"Name\" = 'oranges'" ); + checkAttributeAccess( "Group", "\"City\"" ); + checkAttributeAccess( "Order", "\"Address\"" ); + checkAttributeAccess( "HavingClause", "\"ID\" <> 4" ); + + XIndexAccess orderColumns = m_composer.getOrderColumns(); + assure( "Order columns doesn't exist: \"Address\"", + orderColumns != null && orderColumns.getCount() == 1 && orderColumns.getByIndex(0) != null ); + + XIndexAccess groupColumns = m_composer.getGroupColumns(); + assure( "Group columns doesn't exist: \"City\"", + groupColumns != null && groupColumns.getCount() == 1 && groupColumns.getByIndex(0) != null ); + + // XColumnsSupplier + XColumnsSupplier xSelectColumns = (XColumnsSupplier) + UnoRuntime.queryInterface(XColumnsSupplier.class,m_composer); + assure( "no select columns, or wrong number of select columns", + xSelectColumns != null && xSelectColumns.getColumns() != null && xSelectColumns.getColumns().getElementNames().length == 5 ); + + // structured filter + m_composer.setQuery("SELECT \"ID\", \"Postal\", \"Address\" FROM \"customers\""); + m_composer.setFilter(complexFilter); + PropertyValue[][] aStructuredFilter = m_composer.getStructuredFilter(); + m_composer.setFilter(""); + m_composer.setStructuredFilter(aStructuredFilter); + assure("Structured Filter not identical" , m_composer.getFilter().equals(complexFilter)); + + // structured having clause + m_composer.setHavingClause(complexFilter); + PropertyValue[][] aStructuredHaving = m_composer.getStructuredHavingClause(); + m_composer.setHavingClause(""); + m_composer.setStructuredHavingClause(aStructuredHaving); + assure("Structured Having Clause not identical" , m_composer.getHavingClause().equals(complexFilter)); + } + catch(Exception e) + { + assure("Exception caught: " + e,false); + } + } + + /** test various sub query related features ("queries in queries") + */ + public void testSubQueries() throws Exception + { + m_composer.setQuery( "SELECT * from \"" + innerProductsQuery + "\"" ); + final XTablesSupplier suppTables = (XTablesSupplier)UnoRuntime.queryInterface( + XTablesSupplier.class, m_composer ); + final XNameAccess tables = suppTables.getTables(); + assure( "a simple SELECT * FROM <query> could not be parsed", + tables != null && tables.hasByName( innerProductsQuery ) ); + + final String sInnerCommand = m_database.getDatabase().getDataSource().getQueryDefinition( innerProductsQuery ).getCommand(); + final String sExecutableQuery = m_composer.getQueryWithSubstitution(); + assure( "simple query containing a sub query improperly parsed to SDBC level statement: \n1. " + sExecutableQuery + + "\n2. " + "SELECT * FROM ( " + sInnerCommand + " ) AS \"" + innerProductsQuery + "\"", + sExecutableQuery.equals( "SELECT * FROM ( " + sInnerCommand + " ) AS \"" + innerProductsQuery + "\"") ); + } + + /** tests the XParametersSupplier functionality + */ + public void testParameters() + { + try + { + // "orders for customers" is a query with a named parameter (based on another query) + m_database.getDatabase().getDataSource().createQuery( "orders for customer", "SELECT * FROM \"all orders\" WHERE \"Customer Name\" LIKE :cname" ); + // "orders for customer and product" is query based on "orders for customers", adding an additional, + // anonymous parameter + m_database.getDatabase().getDataSource().createQuery( "orders for customer and product", "SELECT * FROM \"orders for customer\" WHERE \"Product Name\" LIKE ?" ); + + m_composer.setQuery( m_database.getDatabase().getDataSource().getQueryDefinition( "orders for customer and product" ).getCommand() ); + XParametersSupplier suppParams = (XParametersSupplier)UnoRuntime.queryInterface( + XParametersSupplier.class, m_composer ); + XIndexAccess parameters = suppParams.getParameters(); + + String expectedParamNames[] = { + "cname", + "Product Name" + }; + + int paramCount = parameters.getCount(); + assure( "composer did find wrong number of parameters in the nested queries.", + paramCount == expectedParamNames.length ); + + for ( int i = 0; i < paramCount; ++i ) + { + XPropertySet parameter = (XPropertySet)UnoRuntime.queryInterface( + XPropertySet.class, parameters.getByIndex(i) ); + String paramName = (String)parameter.getPropertyValue( "Name" ); + assure( "wrong parameter name at position " + ( i + 1 ) + " (expected: " + expectedParamNames[i] + ", found: " + paramName + ")", + paramName.equals( expectedParamNames[i] ) ); + + } + } + catch( Exception e ) + { + assure( "caught an exception: " + e, false ); + } + } + + private void impl_testDisjunctiveNormalForm( String _query, PropertyValue[][] _expectedDNF ) + { + try { m_composer.setQuery( _query ); } + catch ( Exception e ) + { + // this is an error: the query is expected to be parseable + assure( "caught an exception: " + e, false ); + } + + PropertyValue[][] disjunctiveNormalForm = m_composer.getStructuredFilter(); + + assureEquals( "DNF: wrong number of rows", _expectedDNF.length, disjunctiveNormalForm.length ); + for ( int i=0; i<_expectedDNF.length; ++i ) + { + assureEquals( "DNF: wrong number of columns in row " + i, _expectedDNF[i].length, disjunctiveNormalForm[i].length ); + for ( int j=0; j<_expectedDNF[i].length; ++j ) + { + assureEquals( "DNF: wrong content in column " + j + ", row " + i, + _expectedDNF[i][j].Name, disjunctiveNormalForm[i][j].Name ); + } + } + } + + /** tests the disjunctive normal form functionality, aka the structured filter, + * of the composer + */ + public void testDisjunctiveNormalForm() + { + // a simple case: WHERE clause simply is a combination of predicates knitted with AND + String query = + "SELECT \"customers\".\"Name\", " + + "\"customers\".\"Address\", " + + "\"customers\".\"City\", " + + "\"customers\".\"Postal\", " + + "\"products\".\"Name\" " + + "FROM \"orders\", \"customers\", \"orders_details\", \"products\" " + + "WHERE ( \"orders\".\"CustomerID\" = \"customers\".\"ID\" " + + "AND \"orders_details\".\"OrderID\" = \"orders\".\"ID\" " + + "AND \"orders_details\".\"ProductID\" = \"products\".\"ID\" " + + ") "; + + impl_testDisjunctiveNormalForm( query, new PropertyValue[][] { + new PropertyValue[] { + new PropertyValue( "CustomerID", SQLFilterOperator.EQUAL, "\"customers\".\"ID\"", PropertyState.DIRECT_VALUE ), + new PropertyValue( "OrderID", SQLFilterOperator.EQUAL, "\"orders\".\"ID\"", PropertyState.DIRECT_VALUE ), + new PropertyValue( "ProductID", SQLFilterOperator.EQUAL, "\"products\".\"ID\"", PropertyState.DIRECT_VALUE ) + } } + ); + + // somewhat more challenging: One of the conjunction terms is a disjunction itself + query = + "SELECT \"customers\".\"Name\", " + + "\"customers\".\"Address\", " + + "\"customers\".\"City\", " + + "\"customers\".\"Postal\", " + + "\"products\".\"Name\" " + + "FROM \"orders\", \"customers\", \"orders_details\", \"products\" " + + "WHERE ( \"orders\".\"CustomerID\" = \"customers\".\"ID\" " + + "AND \"orders_details\".\"OrderID\" = \"orders\".\"ID\" " + + "AND \"orders_details\".\"ProductID\" = \"products\".\"ID\" " + + ") " + + "AND " + + "( \"products\".\"Name\" = 'Apples' " + + "OR \"products\".\"ID\" = 2 " + + ")"; + + impl_testDisjunctiveNormalForm( query, new PropertyValue[][] { + new PropertyValue[] { + new PropertyValue( "CustomerID", SQLFilterOperator.EQUAL, "\"customers\".\"ID\"", PropertyState.DIRECT_VALUE ), + new PropertyValue( "OrderID", SQLFilterOperator.EQUAL, "\"orders\".\"ID\"", PropertyState.DIRECT_VALUE ), + new PropertyValue( "ProductID", SQLFilterOperator.EQUAL, "\"products\".\"ID\"", PropertyState.DIRECT_VALUE ), + new PropertyValue( "Name", SQLFilterOperator.EQUAL, "Apples", PropertyState.DIRECT_VALUE ) + }, + new PropertyValue[] { + new PropertyValue( "CustomerID", SQLFilterOperator.EQUAL, "\"customers\".\"ID\"", PropertyState.DIRECT_VALUE ), + new PropertyValue( "OrderID", SQLFilterOperator.EQUAL, "\"orders\".\"ID\"", PropertyState.DIRECT_VALUE ), + new PropertyValue( "ProductID", SQLFilterOperator.EQUAL, "\"products\".\"ID\"", PropertyState.DIRECT_VALUE ), + new PropertyValue( "ID", SQLFilterOperator.EQUAL, new Integer(2), PropertyState.DIRECT_VALUE ) + } } + ); + + } +} diff --git a/dbaccess/qa/complex/dbaccess/TestCase.java b/dbaccess/qa/complex/dbaccess/TestCase.java new file mode 100644 index 000000000000..7fdc2c3d886b --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/TestCase.java @@ -0,0 +1,149 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: TestCase.java,v $ + * $Revision: 1.1.2.1 $ + * + * 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.dbaccess; + +import com.sun.star.beans.XPropertySet; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import helper.FileTools; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; + +public abstract class TestCase extends complexlib.ComplexTestCase +{ + // -------------------------------------------------------------------------------------------------------- + protected final XMultiServiceFactory getORB() + { + return (XMultiServiceFactory)param.getMSF(); + } + + // -------------------------------------------------------------------------------------------------------- + protected final XComponentContext getComponentContext() + { + XComponentContext context = null; + try + { + XPropertySet orbProps = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, getORB() ); + context = (XComponentContext)UnoRuntime.queryInterface( XComponentContext.class, + orbProps.getPropertyValue( "DefaultContext" ) ); + } + catch ( Exception ex ) + { + failed( "could not retrieve the ComponentContext" ); + } + return context; + } + + // -------------------------------------------------------------------------------------------------------- + public void before() + { + } + + // -------------------------------------------------------------------------------------------------------- + public void after() + { + } + + // -------------------------------------------------------------------------------------------------------- + /** returns the URL of a temporary file which can be used during the test. + * + * The file will be deleted when the process exits + * @return the URL of a temporary file + */ + protected final String createTempFileURL() throws IOException + { + File documentFile = java.io.File.createTempFile( getTestObjectName(), ".odb" ); + documentFile.deleteOnExit(); + return documentFile.getAbsoluteFile().toURL().toString(); + } + + // -------------------------------------------------------------------------------------------------------- + /** + * copies the file given by URL to a temporary file + * @return + * the URL of the new file + */ + protected final String copyToTempFile( String _sourceURL ) throws IOException + { + String targetURL = createTempFileURL(); + try + { + FileTools.copyFile( new File( new URI( _sourceURL ) ), new File( new URI( targetURL ) ) ); + } + catch ( URISyntaxException e ) { } + + return FileHelper.getOOoCompatibleFileURL( targetURL ); + } + + // -------------------------------------------------------------------------------------------------------- + protected void verifyExpectedException( Object _object, Class _unoInterfaceClass, String _methodName, Object[] _methodArgs, + Class _expectedExceptionClass ) + { + verifyExpectedException( UnoRuntime.queryInterface( _unoInterfaceClass, _object ), _methodName, + _methodArgs, _expectedExceptionClass ); + } + + // -------------------------------------------------------------------------------------------------------- + protected void verifyExpectedException( Object _object, String _methodName, Object[] _methodArgs, + Class _expectedExceptionClass ) + { + Class objectClass = _object.getClass(); + Class[] methodArgsClasses = new Class[ _methodArgs.length ]; + for ( int i=0; i<_methodArgs.length; ++i ) + methodArgsClasses[i] = _methodArgs[i].getClass(); + + boolean noExceptionAllowed = _expectedExceptionClass == null; + + boolean caughtExpected = noExceptionAllowed ? true : false; + try + { + Method method = objectClass.getMethod( _methodName, methodArgsClasses ); + method.invoke(_object, _methodArgs ); + } + catch ( InvocationTargetException e ) + { + caughtExpected = noExceptionAllowed + ? false + : ( e.getTargetException().getClass().equals( _expectedExceptionClass ) ); + } + catch( Exception e ) + { + caughtExpected = false; + } + assure( "did not catch the expected exception (" + + ( noExceptionAllowed ? "none" : _expectedExceptionClass.getName() ) + + ") while calling " + _object.getClass().getName() + "." + _methodName, caughtExpected ); + } +} diff --git a/dbaccess/qa/complex/dbaccess/UISettings.java b/dbaccess/qa/complex/dbaccess/UISettings.java new file mode 100644 index 000000000000..a755b39aa63c --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/UISettings.java @@ -0,0 +1,133 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Parser.java,v $ + * $Revision: 1.1.6.2 $ + * + * 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.dbaccess; + +import com.sun.star.awt.FontSlant; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.form.XFormController; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XModel; +import com.sun.star.lang.XComponent; +import com.sun.star.sdb.application.XDatabaseDocumentUI; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.XCloseable; +import helper.URLHelper; +import java.io.File; +import java.net.URI; + +public class UISettings extends TestCase +{ + // -------------------------------------------------------------------------------------------------------- + public String[] getTestMethodNames() + { + return new String[] { + "checkTableFormattingPersistence" + }; + } + + // -------------------------------------------------------------------------------------------------------- + public String getTestObjectName() + { + return "UISettings"; + } + + // -------------------------------------------------------------------------------------------------------- + /** verifies that aliases for inner queries work as expected + */ + public void checkTableFormattingPersistence() throws java.lang.Exception + { + CRMDatabase database = new CRMDatabase( getORB() ); + + // load the document + String docURL = database.getDatabase().getDocumentURL(); + XComponentLoader loader = (XComponentLoader)UnoRuntime.queryInterface( XComponentLoader.class, + getORB().createInstance( "com.sun.star.frame.Desktop" ) ); + XModel doc = (XModel)UnoRuntime.queryInterface( XModel.class, + loader.loadComponentFromURL( docURL, "_blank", 0, new PropertyValue[] {} ) ); + + // establish the connection + XDatabaseDocumentUI docUI = (XDatabaseDocumentUI)UnoRuntime.queryInterface( XDatabaseDocumentUI.class, + doc.getCurrentController() ); + docUI.connect(); + + // display the table + XComponent tableViewComp = docUI.loadComponent( com.sun.star.sdb.application.DatabaseObject.TABLE, "customers", false ); + XFormController tableViewController = (XFormController)UnoRuntime.queryInterface( XFormController.class, + tableViewComp ); + XPropertySet tableControlModel = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, + tableViewController.getCurrentControl().getModel() ); + + // change the table's formatting + tableControlModel.setPropertyValue( "FontName", "Andale Sans UI" ); + tableControlModel.setPropertyValue( "FontHeight", new Float( 20 ) ); + tableControlModel.setPropertyValue( "FontSlant", FontSlant.ITALIC ); + + // close the table + docUI.closeSubComponents(); + + // save and close the database document + database.getDatabase().store(); + database.close(); + + // load a copy of the document + // normally, it should be sufficient to load the same doc. However, there might be objects in the Java VM + // which are not yet freed, and which effectively hold the document alive. More precise: The document (|doc|) + // is certainly disposed, but other objects might hold a reference to one of the many other components + // around the database document, the data source, the connection, etc. As long as those objects are + // not cleaned up, the "database model impl" - the structure holding all document data - will + // stay alive, and subsequent requests to load the doc will just reuse it, without really loading it. + docURL = copyToTempFile( docURL ); + doc = (XModel)UnoRuntime.queryInterface( XModel.class, + loader.loadComponentFromURL( docURL, "_blank", 0, new PropertyValue[] {} ) ); + + docUI = (XDatabaseDocumentUI)UnoRuntime.queryInterface( XDatabaseDocumentUI.class, + doc.getCurrentController() ); + docUI.connect(); + + // display the table, again + tableViewComp = docUI.loadComponent( com.sun.star.sdb.application.DatabaseObject.TABLE, "customers", false ); + tableViewController = (XFormController)UnoRuntime.queryInterface( XFormController.class, + tableViewComp ); + tableControlModel = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, + tableViewController.getCurrentControl().getModel() ); + + // verify the properties + assureEquals( "wrong font name", "Andale Sans UI", (String)tableControlModel.getPropertyValue( "FontName" ) ); + assureEquals( "wrong font height", (float)20, ((Float)tableControlModel.getPropertyValue( "FontHeight" )).floatValue() ); + assureEquals( "wrong font slant", FontSlant.ITALIC, (FontSlant)tableControlModel.getPropertyValue( "FontSlant" ) ); + + // close the doc + docUI.closeSubComponents(); + XCloseable closeDoc = (XCloseable)UnoRuntime.queryInterface( XCloseable.class, + doc ); + closeDoc.close( true ); + } +} diff --git a/dbaccess/qa/complex/dbaccess/dbaccess.sce b/dbaccess/qa/complex/dbaccess/dbaccess.sce new file mode 100644 index 000000000000..9b17f0cd9f84 --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/dbaccess.sce @@ -0,0 +1,10 @@ +-o complex.dbaccess.SingleSelectQueryComposer +-o complex.dbaccess.RowSet +-o complex.dbaccess.PropertyBag +-o complex.dbaccess.Query +-o complex.dbaccess.QueryInQuery +-o complex.dbaccess.DatabaseDocument +-o complex.dbaccess.DataSource +-o complex.dbaccess.Parser +-o complex.dbaccess.ApplicationController +-o complex.dbaccess.UISettings diff --git a/dbaccess/qa/complex/dbaccess/makefile.mk b/dbaccess/qa/complex/dbaccess/makefile.mk new file mode 100755 index 000000000000..fa820f3dbeb3 --- /dev/null +++ b/dbaccess/qa/complex/dbaccess/makefile.mk @@ -0,0 +1,85 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.13.76.1 $ +# +# 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 = DbaComplexTests +PRJNAME = $(TARGET) +PACKAGE = complex$/dbaccess + +# --- Settings ----------------------------------------------------- +.INCLUDE: settings.mk + +.IF "$(SOLAR_JAVA)" == "" +all: + @echo "Java not available. Build skipped" + +.INCLUDE : target.mk +.ELSE + +.IF "$(BUILD_QADEVOOO)" == "YES" +#----- compile .java files ----------------------------------------- + +JARFILES = ridl.jar unoil.jar jurt.jar juh.jar java_uno.jar OOoRunner.jar ConnectivityTools.jar +JAVAFILES := $(shell @$(FIND) ./*.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 + +RUNNER_ARGS = -cp $(CLASSPATH)$(PATH_SEPERATOR)$(SOLARBINDIR)$/OOoRunner.jar org.openoffice.Runner -TestBase java_complex + +# --- Targets ------------------------------------------------------ + +.IF "$(depend)" == "" +ALL : ALLTAR +.ELSE +ALL: ALLDEP +.ENDIF + +.INCLUDE : target.mk + + +run: $(CLASSDIR)$/$(JARTARGET) + +java $(RUNNER_ARGS) -sce dbaccess.sce + +run_%: $(CLASSDIR)$/$(JARTARGET) + +java $(RUNNER_ARGS) -o complex.dbaccess.$(@:s/run_//) + +.ELSE +.INCLUDE : target.mk +.ENDIF # "$(BUILD_QADEVOOO)" == "YES" + +.ENDIF # "$(SOLAR_JAVA)" == "" |