diff --git a/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/Zos3270ManagerImpl.java b/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/Zos3270ManagerImpl.java index 7e6e532d4..3a3b8f4cc 100644 --- a/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/Zos3270ManagerImpl.java +++ b/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/Zos3270ManagerImpl.java @@ -9,6 +9,7 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.validation.constraints.NotNull; @@ -53,7 +54,6 @@ public class Zos3270ManagerImpl extends AbstractGherkinManager implements IZos32 private static final Log logger = LogFactory.getLog(Zos3270ManagerImpl.class); - private IConfigurationPropertyStoreService cps; private IDynamicStatusStoreService dss; private IZosManagerSpi zosManager; @@ -249,8 +249,8 @@ private void disconnectTerminal(Zos3270TerminalImpl terminal) { } } - protected IConfigurationPropertyStoreService getCps() { - return this.cps; + protected IConfigurationPropertyStoreService getCps() throws Zos3270ManagerException { + return Zos3270PropertiesSingleton.cps(); } protected IDynamicStatusStoreService getDss() { @@ -260,4 +260,49 @@ protected IDynamicStatusStoreService getDss() { public IZosManagerSpi getZosManager() { return this.zosManager; } + + /** + * Get a CPS property from the zos3270 namespace. + * + * The Gherkin sister-classes need to be able to retrieve properties from the CPS. + * + * @param fullPropertyName the name of the property you want. Including the namespace, which must + * match {@link Zos3270ManagerImpl#NAMESPACE} + */ + public String getCpsProperty(String fullPropertyName) throws Zos3270ManagerException { + + String propertyValue; + + if (!fullPropertyName.startsWith(Zos3270ManagerImpl.NAMESPACE+".")) { + // This manager can only get properties from the zos3270 namespace. + throw new Zos3270ManagerException( + "Program logic error. CPS property name must start with '"+Zos3270ManagerImpl.NAMESPACE+".' for the Zos3270 manager to access it."+ + " Property"+fullPropertyName+" cannot be retrieved."); + } + + try { + + // We get something like "zos3270.gherkin.terminal.rows" as input. + // The cps we are using is already pinned to the zos3270 namespace, so we don't need to + // pass that. It is implicitly given. + + + String[] propNameParts = fullPropertyName.split("\\."); + // Skip the namespace zos3270 part. + String prefix = propNameParts[1]; + String suffix = propNameParts[propNameParts.length-1]; + // allocate space for the infixes. + String [] infixes = new String[propNameParts.length-3]; + System.arraycopy( propNameParts, 2, infixes, 0, propNameParts.length-3 ); + + propertyValue = getCps().getProperty(prefix, suffix, infixes); + logger.info("Property requested:"+fullPropertyName+" value:"+propertyValue); + + } catch (ConfigurationPropertyStoreException ex) { + throw new Zos3270ManagerException("Failed to retrieve the CPS property "+fullPropertyName , ex ); + } + + return propertyValue; + } + } diff --git a/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270Coordinator.java b/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270Coordinator.java index 8c6a844d9..377e7a998 100644 --- a/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270Coordinator.java +++ b/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270Coordinator.java @@ -1,3 +1,8 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ package dev.galasa.zos3270.internal.gherkin; import java.util.*; diff --git a/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270GivenTerminal.java b/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270GivenTerminal.java index 97b6e3047..77b692234 100644 --- a/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270GivenTerminal.java +++ b/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/main/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270GivenTerminal.java @@ -11,6 +11,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import dev.galasa.framework.spi.AbstractManager; import dev.galasa.framework.spi.IGherkinExecutable; import dev.galasa.framework.spi.IStatementOwner; import dev.galasa.framework.spi.language.gherkin.ExecutionMethod; @@ -23,16 +24,22 @@ public class Gherkin3270GivenTerminal implements IStatementOwner { + public static final int DEFAULT_TERMINAL_ROWS = 24; + public static final int DEFAULT_TERMINAL_COLUMNS = 80; + + public static final String DEFAULT_TERMINAL_ROWS_STR = Integer.toString(DEFAULT_TERMINAL_ROWS); + public static final String DEFAULT_TERMINAL_COLUMNS_STR = Integer.toString(DEFAULT_TERMINAL_COLUMNS); + private final static Log logger = LogFactory.getLog(Gherkin3270GivenTerminal.class); private final Gherkin3270Coordinator gerkinCoordinator; private final Zos3270ManagerImpl manager; - + public Gherkin3270GivenTerminal(Gherkin3270Coordinator gerkinCoordinator, Zos3270ManagerImpl manager) { this.gerkinCoordinator = gerkinCoordinator; this.manager = manager; } - @ExecutionMethod(keyword = GherkinKeyword.GIVEN, regex = "a terminal( with id of (\\w+))?( tagged (\\w+))?") + @ExecutionMethod(keyword = GherkinKeyword.GIVEN, regex = "a terminal( with id of (\\w+))?( tagged (\\w+))?( with (\\d+) rows and (\\d+) columns)?") public void allocateTerminal(IGherkinExecutable executable, Map testVariables) throws Zos3270ManagerException { // Ensure we have a connected terminal List groups = executable.getRegexGroups(); @@ -54,12 +61,31 @@ public void allocateTerminal(IGherkinExecutable executable, Map t public void provision(IGherkinExecutable executable) throws Zos3270ManagerException { List groups = executable.getRegexGroups(); + + // Extract the parameters from the step in the scenario. String terminalId = Gherkin3270Coordinator.defaultTerminaId(groups.get(1)); String imageTag = Gherkin3270Coordinator.defaultImageTag(groups.get(3)); + String rowsStepParameter = groups.get(5); + String columnsStepParameter = groups.get(6); + + // Log what we have collected. + { + StringBuffer msg = new StringBuffer(); + msg.append("Provisioning a terminal:"); + msg.append(" id="); + msg.append(terminalId); + msg.append(" imageTag="); + msg.append(imageTag); + msg.append(" rows="); + msg.append(rowsStepParameter); + msg.append(" columns="); + msg.append(columnsStepParameter); + logger.info(msg.toString()); + } Zos3270TerminalImpl newTerminal = this.gerkinCoordinator.getTerminal(terminalId); if (newTerminal == null) { - TerminalSize terminalSize = new TerminalSize(80, 24); + TerminalSize terminalSize = getPreferredTerminalSize(rowsStepParameter , columnsStepParameter); TerminalSize alternateSize = new TerminalSize(0, 0); newTerminal = this.manager.generateTerminal(imageTag, true, terminalSize, alternateSize); @@ -67,4 +93,47 @@ public void provision(IGherkinExecutable executable) throws Zos3270ManagerExcept logger.info("zOS 3270 Terminal id '" + terminalId + "' as been provisioned for image tag '" + imageTag + "'"); } } + + protected TerminalSize getPreferredTerminalSize(String rowsStepParameter, String rowsColumnParameter) throws Zos3270ManagerException { + int columns ; + int rows ; + rows = getNumericCPSProperty("zos3270.gherkin.terminal.rows",DEFAULT_TERMINAL_ROWS,rowsStepParameter); + columns = getNumericCPSProperty("zos3270.gherkin.terminal.columns",DEFAULT_TERMINAL_COLUMNS,rowsColumnParameter); + logger.info("Preferred terminal size is "+Integer.toString(rows)+" x "+Integer.toString(columns)); + return new TerminalSize(columns, rows); + } + + private int getNumericCPSProperty(String propertyName, int defaultConstantValue , String stepDefParameterValue) throws Zos3270ManagerException { + String valueStr ; + int cpsPropValue ; + String notANumberMsg ; + + if (stepDefParameterValue==null || stepDefParameterValue.trim().equals("")) { + // The value wasn't specified in the stepdef, so get it from the CPS property. + valueStr = this.manager.getCpsProperty(propertyName); + notANumberMsg = "Error: CPS property '"+propertyName+"' does not contain a number. Current value is "+valueStr; + } else { + // The value was specified in the stepdef parameter, so use that in preference to the CPS or the default value. + valueStr = stepDefParameterValue ; + notANumberMsg = "Error: value from gherkin statement does not contain a number. Current value is "+valueStr; + } + + if (valueStr == null || valueStr.trim().isBlank()) { + // The property is not there, or it's blank. So use the default value. + cpsPropValue = defaultConstantValue ; + } else { + try { + cpsPropValue = Integer.parseInt(valueStr); + } catch(NumberFormatException ex) { + logger.error(notANumberMsg,ex); + throw new Zos3270ManagerException(notANumberMsg); + } + } + return cpsPropValue; + } + + public static String defaultRows(String rows) { + return AbstractManager.defaultString(rows,DEFAULT_TERMINAL_ROWS_STR); + } + } diff --git a/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/test/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270GivenTerminalTest.java b/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/test/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270GivenTerminalTest.java new file mode 100644 index 000000000..b1736760a --- /dev/null +++ b/galasa-managers-parent/galasa-managers-zos-parent/dev.galasa.zos3270.manager/src/test/java/dev/galasa/zos3270/internal/gherkin/Gherkin3270GivenTerminalTest.java @@ -0,0 +1,154 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package dev.galasa.zos3270.internal.gherkin; + +import java.util.Map; + +import org.junit.Test; + +import dev.galasa.framework.spi.language.gherkin.GherkinTest; +import dev.galasa.zos3270.Zos3270ManagerException; +import dev.galasa.zos3270.common.screens.TerminalSize; +import dev.galasa.zos3270.internal.Zos3270ManagerImpl; + +import static org.assertj.core.api.Assertions.*; + +public class Gherkin3270GivenTerminalTest { + + + public class MockZos3270Manager extends Zos3270ManagerImpl { + private Map cpsProperties; + + public MockZos3270Manager(Map cpsProperties) { + this.cpsProperties = cpsProperties; + } + + public String getCpsProperty(String fullPropertyName) throws Zos3270ManagerException { + return cpsProperties.get(fullPropertyName); + } + } + + public class MockGherkinCoordinator extends Gherkin3270Coordinator { + public MockGherkinCoordinator(Zos3270ManagerImpl manager, GherkinTest gherkinTest) { + super(manager, gherkinTest); + } + } + + @Test + public void testGherkin3270GivenTerminalCanBeCreatedOk() { + + Map cpsProps = Map.of(); + MockZos3270Manager mockManager = new MockZos3270Manager(cpsProps); + MockGherkinCoordinator mockCoordinator = new MockGherkinCoordinator(mockManager, null); + + new Gherkin3270GivenTerminal(mockCoordinator, mockManager); + } + + @Test + public void testGherkin3270CanGetDefaultPreferredTerminalSizeEmptyCPS() throws Exception { + Map cpsProps = Map.of( + // "zos3270.gherkin.terminal.rows","24", + // "zos3270.gherkin.terminal.columns","80" + ); + MockZos3270Manager mockManager = new MockZos3270Manager(cpsProps); + MockGherkinCoordinator mockCoordinator = new MockGherkinCoordinator(mockManager, null); + + Gherkin3270GivenTerminal terminal = new Gherkin3270GivenTerminal(mockCoordinator, mockManager); + TerminalSize preferredTerminalSize = terminal.getPreferredTerminalSize("",""); + + assertThat(preferredTerminalSize.getRows()).isEqualTo(Gherkin3270GivenTerminal.DEFAULT_TERMINAL_ROWS); + assertThat(preferredTerminalSize.getColumns()).isEqualTo(Gherkin3270GivenTerminal.DEFAULT_TERMINAL_COLUMNS); + } + + @Test + public void testGherkin3270CanGetDefaultPreferredTerminalSizeFromCPS() throws Exception { + Map cpsProps = Map.of( + "zos3270.gherkin.terminal.rows","25", + "zos3270.gherkin.terminal.columns","81" + ); + MockZos3270Manager mockManager = new MockZos3270Manager(cpsProps); + MockGherkinCoordinator mockCoordinator = new MockGherkinCoordinator(mockManager, null); + + Gherkin3270GivenTerminal terminal = new Gherkin3270GivenTerminal(mockCoordinator, mockManager); + TerminalSize preferredTerminalSize = terminal.getPreferredTerminalSize("",""); + + assertThat(preferredTerminalSize.getRows()).isEqualTo(25); + assertThat(preferredTerminalSize.getColumns()).isEqualTo(81); + } + + @Test + public void testGherkin3270CanGetDefaultPreferredTerminalSizeFromGherkinStatement() throws Exception { + Map cpsProps = Map.of( + "zos3270.gherkin.terminal.rows","25", + "zos3270.gherkin.terminal.columns","81" + ); + MockZos3270Manager mockManager = new MockZos3270Manager(cpsProps); + MockGherkinCoordinator mockCoordinator = new MockGherkinCoordinator(mockManager, null); + + Gherkin3270GivenTerminal terminal = new Gherkin3270GivenTerminal(mockCoordinator, mockManager); + TerminalSize preferredTerminalSize = terminal.getPreferredTerminalSize("48","64"); + + assertThat(preferredTerminalSize.getRows()).isEqualTo(48); + assertThat(preferredTerminalSize.getColumns()).isEqualTo(64); + } + + @Test + public void testGherkin3270GetDefaultPreferredTerminalSizeFailsIfCPSRowsPropNotANumber() throws Exception { + Map cpsProps = Map.of( + "zos3270.gherkin.terminal.rows","hello" + // "zos3270.gherkin.terminal.columns","81" + ); + MockZos3270Manager mockManager = new MockZos3270Manager(cpsProps); + MockGherkinCoordinator mockCoordinator = new MockGherkinCoordinator(mockManager, null); + + Gherkin3270GivenTerminal terminal = new Gherkin3270GivenTerminal(mockCoordinator, mockManager); + + // When... + Zos3270ManagerException ex = catchThrowableOfType( + () -> terminal.getPreferredTerminalSize("",""), + Zos3270ManagerException.class ); + + assertThat(ex).hasMessageContaining("does not contain a number"); + } + + @Test + public void testGherkin3270GetDefaultPreferredTerminalSizeFailsIfGherkinStatementValueNotANumber() throws Exception { + Map cpsProps = Map.of( + "zos3270.gherkin.terminal.rows","25", + "zos3270.gherkin.terminal.columns","81" + ); + MockZos3270Manager mockManager = new MockZos3270Manager(cpsProps); + MockGherkinCoordinator mockCoordinator = new MockGherkinCoordinator(mockManager, null); + + Gherkin3270GivenTerminal terminal = new Gherkin3270GivenTerminal(mockCoordinator, mockManager); + + // When... + Zos3270ManagerException ex = catchThrowableOfType( + () -> terminal.getPreferredTerminalSize("hello-notanumber","notanumber"), + Zos3270ManagerException.class ); + + assertThat(ex).hasMessageContaining("does not contain a number"); + } + + @Test + public void testGherkin3270GetDefaultPreferredTerminalSizeFailsIfCPSColumnsPropNotANumber() throws Exception { + Map cpsProps = Map.of( + "zos3270.gherkin.terminal.rows","24", + "zos3270.gherkin.terminal.columns","hello" + ); + MockZos3270Manager mockManager = new MockZos3270Manager(cpsProps); + MockGherkinCoordinator mockCoordinator = new MockGherkinCoordinator(mockManager, null); + + Gherkin3270GivenTerminal terminal = new Gherkin3270GivenTerminal(mockCoordinator, mockManager); + + // When... + Zos3270ManagerException ex = catchThrowableOfType( + () -> terminal.getPreferredTerminalSize("",""), + Zos3270ManagerException.class ); + + assertThat(ex).hasMessageContaining("does not contain a number"); + } +}