Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

Allow setting of gherkin terminal size using zos3270.gherkin.terminal.rows and zos3270.gherkin.terminal.columns, or in the gherkin scenario itself. #962

Merged
merged 2 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand All @@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String,Object> testVariables) throws Zos3270ManagerException {
// Ensure we have a connected terminal
List<String> groups = executable.getRegexGroups();
Expand All @@ -54,17 +61,79 @@ public void allocateTerminal(IGherkinExecutable executable, Map<String,Object> t

public void provision(IGherkinExecutable executable) throws Zos3270ManagerException {
List<String> 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);
this.gerkinCoordinator.registerTerminal(terminalId, newTerminal, imageTag);
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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rows and columns might need swapping for this log message - for example with 24 rows and 80 columns, this would read as 24 x 80 instead of 80 x 24

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do wonder whether mainframers use 24x80

This is from host on demand:
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adam Coulthard

Its normally 24 x 80 rows and 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);
}

}
Original file line number Diff line number Diff line change
@@ -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<String,String> cpsProperties;

public MockZos3270Manager(Map<String,String> 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<String,String> 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<String,String> 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<String,String> 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<String,String> 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<String,String> 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<String,String> 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<String,String> 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");
}
}