diff options
Diffstat (limited to '')
-rw-r--r-- | forms/qa/integration/forms/MasterDetailForms.java | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/forms/qa/integration/forms/MasterDetailForms.java b/forms/qa/integration/forms/MasterDetailForms.java new file mode 100644 index 000000000..4b448fe04 --- /dev/null +++ b/forms/qa/integration/forms/MasterDetailForms.java @@ -0,0 +1,400 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package integration.forms; + +import com.sun.star.beans.NamedValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XIndexContainer; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.container.XNameContainer; +import com.sun.star.embed.XComponentSupplier; +import com.sun.star.form.XGridColumnFactory; +import com.sun.star.form.XGridFieldDataSupplier; +import com.sun.star.form.XLoadable; +import com.sun.star.lang.XComponent; +import com.sun.star.sdb.CommandType; +import com.sun.star.sdb.XFormDocumentsSupplier; +import com.sun.star.sdbc.SQLException; +import com.sun.star.sdbc.XColumnLocate; +import com.sun.star.ucb.Command; +import com.sun.star.ucb.OpenMode; +import com.sun.star.ucb.XCommandProcessor; +import com.sun.star.uno.Type; +import com.sun.star.util.XModifiable; +import connectivity.tools.CRMDatabase; +import connectivity.tools.HsqlColumnDescriptor; +import connectivity.tools.HsqlDatabase; +import connectivity.tools.HsqlTableDescriptor; +import org.openoffice.complex.forms.tools.ResultSet; + + +public class MasterDetailForms extends complexlib.ComplexTestCase implements com.sun.star.form.XLoadListener +{ + private XMultiServiceFactory m_orb; + + private XPropertySet m_masterForm; + private XPropertySet m_detailForm; + private ResultSet m_masterResult; + private ResultSet m_detailResult; + + final private Object m_waitForLoad = new Object(); + private boolean m_loaded = false; + + /* ------------------------------------------------------------------ */ + @Override + public String[] getTestMethodNames() + { + return new String[] { + "checkMultipleKeys", + "checkDetailFormDefaults" + }; + } + + /* ------------------------------------------------------------------ */ + public void before() + { + m_orb = param.getMSF(); + } + + /* ------------------------------------------------------------------ */ + @Override + public String getTestObjectName() + { + return "Form Control Spreadsheet Cell Binding Test"; + } + + /* ------------------------------------------------------------------ */ + /** creates the table structure needed for the test + */ + private void impl_createTableStructure( final HsqlDatabase _databaseDocument ) throws SQLException + { + HsqlColumnDescriptor[] masterColumns = { + new HsqlColumnDescriptor( "ID1", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "ID2", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "value", "VARCHAR(50)" ), + }; + HsqlColumnDescriptor[] detailColumns = { + new HsqlColumnDescriptor( "ID", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "FK_ID1", "INTEGER", HsqlColumnDescriptor.REQUIRED, "master", "ID1" ), + new HsqlColumnDescriptor( "FK_ID2", "INTEGER", HsqlColumnDescriptor.REQUIRED, "master", "ID2" ), + new HsqlColumnDescriptor( "name", "VARCHAR(50)" ), + }; + _databaseDocument.createTable( new HsqlTableDescriptor( "master", masterColumns ) ); + _databaseDocument.createTable( new HsqlTableDescriptor( "detail", detailColumns ) ); + + _databaseDocument.executeSQL( "INSERT INTO \"master\" VALUES ( 1, 1, 'First Record' )" ); + _databaseDocument.executeSQL( "INSERT INTO \"master\" VALUES ( 1, 2, 'Second Record' )" ); + _databaseDocument.executeSQL( "INSERT INTO \"detail\" VALUES ( 1, 1, 1, 'record 1.1 (1)')"); + _databaseDocument.executeSQL( "INSERT INTO \"detail\" VALUES ( 2, 1, 1, 'record 1.1 (2)')"); + _databaseDocument.executeSQL( "INSERT INTO \"detail\" VALUES ( 3, 1, 2, 'record 1.2 (1)')"); + + _databaseDocument.defaultConnection().refreshTables(); + } + + /* ------------------------------------------------------------------ */ + private void impl_createForms( final HsqlDatabase _databaseDocument ) throws com.sun.star.uno.Exception + { + m_masterForm = dbfTools.queryPropertySet( m_orb.createInstance( "com.sun.star.form.component.DataForm" ) ); + m_masterForm.setPropertyValue( "ActiveConnection", _databaseDocument.defaultConnection().getXConnection() ); + m_masterForm.setPropertyValue( "CommandType", Integer.valueOf( com.sun.star.sdb.CommandType.TABLE ) ); + m_masterForm.setPropertyValue( "Command", "master" ); + + m_masterResult = new ResultSet( m_masterForm ); + + m_detailForm = dbfTools.queryPropertySet( m_orb.createInstance( "com.sun.star.form.component.DataForm" ) ); + m_detailForm.setPropertyValue( "ActiveConnection", _databaseDocument.defaultConnection().getXConnection() ); + m_detailForm.setPropertyValue( "CommandType", Integer.valueOf( com.sun.star.sdb.CommandType.TABLE ) ); + m_detailForm.setPropertyValue( "Command", "detail" ); + + m_detailResult = new ResultSet( m_detailForm ); + + XNameContainer masterContainer = UnoRuntime.queryInterface( XNameContainer.class, m_masterForm ); + masterContainer.insertByName( "slave", m_detailForm ); + } + + /* ------------------------------------------------------------------ */ + /** checks if master-detail relationships including multiple keys work + */ + public void checkMultipleKeys() throws com.sun.star.uno.Exception, java.lang.Exception + { + HsqlDatabase databaseDocument = null; + try + { + databaseDocument = new HsqlDatabase( m_orb ); + impl_createTableStructure( databaseDocument ); + impl_createForms( databaseDocument ); + + m_detailForm.setPropertyValue( "MasterFields", new String[] { "ID1", "ID2" } ); + m_detailForm.setPropertyValue( "DetailFields", new String[] { "FK_ID1", "FK_ID2" } ); + + XLoadable loadMaster = UnoRuntime.queryInterface( XLoadable.class, m_masterForm ); + XLoadable loadDetail = UnoRuntime.queryInterface( XLoadable.class, m_detailForm ); + loadDetail.addLoadListener( this ); + + // wait until the detail form is loaded + loadMaster.load(); + impl_waitForLoadedEvent(); + + // okay, now the master form should be on the first record + assure( "wrong form state after loading (ID1)", m_masterResult.getInt(1) == 1 ); + assure( "wrong form state after loading (ID2)", m_masterResult.getInt(2) == 1 ); + assure( "wrong form state after loading (value)", m_masterResult.getString(3).equals( "First Record" ) ); + + // the values in the linked fields should be identical + int expectedDetailRowCounts[] = { 2, 1 }; + do + { + verifyColumnValueIdentity( "ID1", "FK_ID1" ); + verifyColumnValueIdentity( "ID2", "FK_ID2" ); + + m_detailResult.last(); + int masterPos = m_masterResult.getRow(); + assure( "wrong number of records in detail form, for master form at pos " + masterPos, + ((Integer)m_detailForm.getPropertyValue( "RowCount" )).intValue() == expectedDetailRowCounts[ masterPos - 1 ] ); + + if (!m_masterResult.next()) + return; + impl_waitForLoadedEvent(); + } + while ( !m_masterResult.isAfterLast() ); + assure( "wrong number of records in master form", 2 == ((Integer)m_masterForm.getPropertyValue( "RowCount" )).intValue() ); + } + finally + { + if ( databaseDocument != null ) + databaseDocument.closeAndDelete(); + impl_cleanUpStep(); + } + } + + /* ------------------------------------------------------------------ */ + private final void impl_cleanUpStep() + { + if ( m_masterForm != null ) + dbfTools.disposeComponent( m_masterForm ); + if ( m_detailForm != null ) + dbfTools.disposeComponent( m_detailForm ); + m_masterForm = m_detailForm = null; + } + + /* ------------------------------------------------------------------ */ + /** checks whether default values in detail forms work as expected. + * + * Effectively, this test case verifies the issues #i106574# and #i105235# did not creep back in. + */ + public void checkDetailFormDefaults() throws Exception + { + CRMDatabase database = null; + XCommandProcessor subComponentCommands = null; + try + { + // create our standard CRM database document + database = new CRMDatabase( m_orb, true ); + + // create a form document therein + XFormDocumentsSupplier formDocSupp = UnoRuntime.queryInterface( XFormDocumentsSupplier.class, database.getDatabase().getModel() ); + XMultiServiceFactory formFactory = UnoRuntime.queryInterface( XMultiServiceFactory.class, formDocSupp.getFormDocuments() ); + NamedValue[] loadArgs = new NamedValue[] { + new NamedValue( "ActiveConnection", database.getConnection().getXConnection() ), + new NamedValue( "MediaType", "application/vnd.oasis.opendocument.text" ) + }; + + subComponentCommands = UnoRuntime.queryInterface( + XCommandProcessor.class, + formFactory.createInstanceWithArguments( "com.sun.star.sdb.DocumentDefinition", loadArgs ) ); + Command command = new Command(); + command.Name = "openDesign"; + command.Argument = Short.valueOf( OpenMode.DOCUMENT ); + + DocumentHelper subDocument = new DocumentHelper( m_orb, + UnoRuntime.queryInterface( XComponent.class, + subComponentCommands.execute( command, subComponentCommands.createCommandIdentifier(), null ) + ) + ); + FormLayer formLayer = new FormLayer( subDocument ); + XPropertySet controlModel = formLayer.insertControlLine( "DatabaseNumericField", "ID", "", 10 ); + formLayer.insertControlLine( "DatabaseTextField", "Name", "", 20 ); + formLayer.insertControlLine( "DatabaseTextField", "Description", "", 30 ); + + m_masterForm = (XPropertySet)dbfTools.getParent( controlModel, XPropertySet.class ); + m_masterForm.setPropertyValue( "Command", "categories" ); + m_masterForm.setPropertyValue( "CommandType", Integer.valueOf( CommandType.TABLE ) ); + + // create a detail form + m_detailForm = UnoRuntime.queryInterface( XPropertySet.class, subDocument.createSubForm( m_masterForm, "products" ) ); + m_detailForm.setPropertyValue( "Command", "SELECT \"ID\", \"Name\", \"CategoryID\" FROM \"products\"" ); + m_detailForm.setPropertyValue( "CommandType", Integer.valueOf( CommandType.COMMAND ) ); + m_detailForm.setPropertyValue( "MasterFields", new String[] { "ID" } ); + m_detailForm.setPropertyValue( "DetailFields", new String[] { "CategoryID" } ); + + // create a grid control in the detail form, with some columns + XPropertySet gridControlModel = formLayer.createControlAndShape( "GridControl", 20, 40, 130, 50, m_detailForm ); + gridControlModel.setPropertyValue( "Name", "product list" ); + XIndexContainer gridColumns = UnoRuntime.queryInterface( XIndexContainer.class, gridControlModel ); + impl_createGridColumn( gridColumns, "TextField", "ID" ); + XPropertySet nameColumn = impl_createGridColumn( gridColumns, "TextField", "Name" ); + nameColumn.setPropertyValue( "Width", Integer.valueOf( 600 ) ); // 6 cm + nameColumn.setPropertyValue( "DefaultText", "default text" ); + + // go live + m_masterResult = new ResultSet( m_masterForm ); + m_detailResult = new ResultSet( m_detailForm ); + + XLoadable loadDetail = UnoRuntime.queryInterface( XLoadable.class, m_detailForm ); + loadDetail.addLoadListener( this ); + + subDocument.getCurrentView().toggleFormDesignMode(); + impl_waitForLoadedEvent(); + + // now that we set up this, do the actual tests + // First, https://bz.apache.org/ooo/show_bug.cgi?id=105235 described the problem + // that default values in the sub form didn't work when the master form was navigated to a row + // for which no detail records were present, and the default of the column/control is the same + // as the last known value. + + // so, take the current value of the "Name" column, and set it as default value ... + String defaultValue = m_detailResult.getString( 2 ); + nameColumn.setPropertyValue( "DefaultText", defaultValue ); + // ... then move to the second main form row ... + m_masterResult.absolute( 2 ); + impl_waitForLoadedEvent(); + // ... which should result in an empty sub form ... + assure( "test precondition not met: The second master form record is expected to have no detail records, " + + "else the test becomes meaningless", impl_isNewRecord( m_detailForm ) ); + // ... and in the "Name" column having the proper text + String actualValue = (String)nameColumn.getPropertyValue( "Text" ); + assureEquals( "#i105235#: default value in sub form not working (not propagated to column model)", defaultValue, actualValue ); + // However, checking the column model's value alone is not enough - we need to ensure it is properly + // propagated to the control. + XGridFieldDataSupplier gridData = subDocument.getCurrentView().getControl( + gridControlModel, XGridFieldDataSupplier.class ); + actualValue = (String)(gridData.queryFieldData( 0, Type.STRING )[1]); + assureEquals( "#i105235#: default value in sub form not working (not propagated to column)", defaultValue, actualValue ); + } + finally + { + if ( subComponentCommands != null ) + { + XComponentSupplier componentSupplier = UnoRuntime.queryInterface( XComponentSupplier.class, subComponentCommands ); + XModifiable modifySubComponent = UnoRuntime.queryInterface( XModifiable.class, componentSupplier.getComponent() ); + modifySubComponent.setModified( false ); + Command command = new Command(); + command.Name = "close"; + subComponentCommands.execute( command, subComponentCommands.createCommandIdentifier(), null ); + } + + if ( database != null ) + database.saveAndClose(); + + impl_cleanUpStep(); + } + } + + /* ------------------------------------------------------------------ */ + private boolean impl_isNewRecord( final XPropertySet _rowSet ) + { + boolean isNew = false; + try + { + isNew = ((Boolean)_rowSet.getPropertyValue( "IsNew" )).booleanValue(); + } + catch ( Exception ex ) + { + failed( "obtaining the IsNew property failed" ); + } + return isNew; + } + + /* ------------------------------------------------------------------ */ + private XPropertySet impl_createGridColumn( final XIndexContainer _gridModel, final String _columnType, final String _boundField ) throws Exception + { + final XGridColumnFactory columnFactory = UnoRuntime.queryInterface( XGridColumnFactory.class, _gridModel ); + XPropertySet column = columnFactory.createColumn( _columnType ); + column.setPropertyValue( "DataField", _boundField ); + column.setPropertyValue( "Name", _boundField ); + column.setPropertyValue( "Label", _boundField ); + _gridModel.insertByIndex( _gridModel.getCount(), column ); + return column; + } + + private void impl_waitForLoadedEvent() + { + synchronized( m_waitForLoad ) + { + while ( !m_loaded ) + { + try { m_waitForLoad.wait(); } + catch( InterruptedException e ) { } + } + // reset the flag for the next time + m_loaded = false; + } + } + + /** assures that the (integer) values in the given columns of our master and detail forms are identical + */ + private void verifyColumnValueIdentity( final String masterColName, final String detailColName ) throws SQLException + { + XColumnLocate locateMasterCols = UnoRuntime.queryInterface( XColumnLocate.class, m_masterForm ); + XColumnLocate locateDetailCols = UnoRuntime.queryInterface( XColumnLocate.class, m_detailForm ); + + int masterValue = m_masterResult.getInt( locateMasterCols.findColumn( masterColName ) ); + int detailValue = m_detailResult.getInt( locateDetailCols.findColumn( detailColName ) ); + + assure( "values in linked column pair " + detailColName + "->" + masterColName + " (" + + detailValue + "->" + masterValue + ") do not match (master position: " + m_masterResult.getRow() + ")!", + masterValue == detailValue ); + } + + public void disposing(com.sun.star.lang.EventObject eventObject) + { + } + + public void loaded(com.sun.star.lang.EventObject eventObject) + { + synchronized( m_waitForLoad ) + { + m_loaded = true; + m_waitForLoad.notify(); + } + } + + public void reloaded(com.sun.star.lang.EventObject eventObject) + { + synchronized( m_waitForLoad ) + { + m_loaded = true; + m_waitForLoad.notify(); + } + } + + public void reloading(com.sun.star.lang.EventObject eventObject) + { + } + + public void unloaded(com.sun.star.lang.EventObject eventObject) + { + } + + public void unloading(com.sun.star.lang.EventObject eventObject) + { + } +} |