Skip to content

Commit

Permalink
Tabulator support (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
Baraujo25 committed Feb 19, 2020
1 parent 3456181 commit 4572601
Show file tree
Hide file tree
Showing 28 changed files with 525 additions and 245 deletions.
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

This project implements a JMeter plugin to **support RTE (Remote Terminal Emulation) protocols** by providing a recorder for automatic test plan creation, and config and sampler for protocol interactions.

Nowadays the plugin supports **IBM protocol's TN5250 and TN3270** by using embedded [xtn5250](https://sourceforge.net/projects/xtn5250/) and [dm3270](http://dmolony.github.io/) emulators with modifications ([xtn5250 fork](https://github.com/abstracta/xtn5250) and [dm3270 fork](https://github.com/abstracta/dm3270)) to better accommodate to the plugin usage (exception handling, logging, external dependencies, etc).
Nowadays the plugin supports **IBM protocol's TN5250, TN3270 and VT420** by using embedded [xtn5250](https://sourceforge.net/projects/xtn5250/), [dm3270](http://dmolony.github.io/) and [jvt220](https://github.com/jawi/jVT220) emulators with modifications [xtn5250 fork](https://github.com/abstracta/xtn5250), [dm3270 fork](https://github.com/abstracta/dm3270) and [jvt220 fork](https://github.com/Blazemeter/jVT220) to better accommodate to the plugin usage (exception handling, logging, external dependencies, etc).

People who usually work with these IBM servers interact with them, basically, by sending keystrokes from the terminal keyboard (or emulator) to fill forms or call processes. The plugin provides a [recording controller](#a-recording-controller-rte-recorder), which allows the user to interact through a terminal emulator, recording every interaction (samplers) with the mainframe application. Additionally, the plugin allows for manual test plan creation, providing a config element for setting connection parameters and a sampler to set fields on screen and attention key to send to the mainframe application. Besides, the sampler allows to simulate the existing attention keys on the terminal keyboard like ENTER, F1, F2, F3..., ATTN, CLEAR, etc.

Expand Down Expand Up @@ -68,7 +68,7 @@ If more than one RTE Config element is used at the same level of the Test Plan,

#### Sampler (RTE Sampler)

![alt text](docs/send-keys.png "RTE Sampler GUI")
![alt text](docs/rte-sampler.png "RTE Sampler GUI")

Connections are shared by RTE Samplers in same thread created by a thread group (different threads use separate connections). The RTE Sampler element checks if a connection exists to send the packets, if none exists, it uses the RTE Config data to establish a new one. Connections are automatically closed (unless Jmeter property `RTEConnectionConfig.reuseConnections=true` is specified in *jmeter.properties*) at the end of each thread iteration.

Expand All @@ -83,7 +83,12 @@ The RTE Sampler fields are:
- *Disconnect*. This option allows to explicitly close the connection to the terminal server. This allows to restart the emulator status by re connecting to the server en following samplers.
> As previously stated, connections are (by default) automatically closed at the end of each thread iteration, so is not required to add a sampler with disconnect action at the end of each thread loop iteration.
- *RTE Message*. When "Send keys" action is selected it is possible to specify fields to send and attention key to use:
- *Payload*. Contains a grid in which user can specify Coordinates (row and column) or Label (which precedes from a terminal screen label) of a field in the screen, and the value (string) to send in both cases. Rows and columns start from Row 1, Column 1 (are 1 indexed).
- *Payload*. Contains a grid where the user can add different types of input.
- Input by Tabulator: It will make as many tabulations as specified to the mainframe, before
sending the _"value"_. It is useful to avoid using coordinates.
- Input by Label: It precedes from a terminal screen label. It will send to the mainframe application the already set value to a field which matches the selected label.
- Input by Coordinates (row and column): It will send the text introduced in _"value"_ to the mainframe application at that position.
> For more information about inputs and how they work in a normal flow, check this [examples](docs/recorder/terminal-emulator/terminal-emulator.md#Input By Label Usage).
- *Attention Keys*. These buttons trigger the attention keys to be sent to the server on each sample. They all represent a key from a terminal's keyboard.
- *Wait for*. When using "Connect" or "Send keys" action it is possible to wait for a specific condition. If this condition is not reached after a specific time (defined in *Timeout* value), the sampler returns timeout error. There are four defined waiters:
- *Sync*. Waits for the system to return from X SYSTEM or Input Inhibited mode. Default value is checked, as it's recommended to always check that the system is not in Input Inhibited Mode after a sample (and before the next one) in order to get the correct screen in the sample result (and to ensure that the next sampler is executed from the desired screen). On the other hand, the sampler does an implicit "Wait for sync" each time it connects to a server, which means that if *Connect* mode is used, then it's not needed to check the *Wait for sync* function, unless you want to change the default timeout.
Expand Down Expand Up @@ -124,7 +129,7 @@ The RTE Config element should specify the server url in *Server* field, and the

Finally, the second sampler should use "Send keys" action (the default option) and specify in Payload grid the position of the username on the screen, the label (in this case 'Password') and the values for both *user* and *password* fields. Besides, the attention key *ENTER* (the default one) should be selected to simulate the user pressing that key after filling the fields. Finally, an assert post processor should be added to check for the "Login Successful" message.

![alt text](docs/send-keys.png "RTE Sampler 2")
![alt text](docs/rte-sampler.png "RTE Sampler 2")

#### Waiters usage
As explained previously, the RTE Sampler has 4 types of waiters which work as synchronization functions, in order to ensure that the response shown by the sampler is the screen that the server wants to show. It's recommended to always have at least one waiter checked on each sampler.
Expand Down
6 changes: 5 additions & 1 deletion docs/class-diagram.puml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ package com.blazemeter.jmeter.rte {
class LabelInput extends Input{
String label
}


class TabulatorInput extends Input{
int offset
}

class Position {
int row
int column
Expand Down
Binary file added docs/rte-sampler.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/send-keys.png
Binary file not shown.
2 changes: 1 addition & 1 deletion jmeter-plugins-build/scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ ORIGINAL_VERSION=$(mvn -q -Dexec.executable="echo" -Dexec.args='${project.versio
git checkout production && git reset --hard origin/production
git merge master
mvn versions:set -DremoveSnapshot
/execute-on-vnc.sh mvn --batch-mode clean verify
/execute-on-vnc.sh mvn --batch-mode clean verify -Prelease
mvn --batch-mode scm:checkin -Dmessage="[RELEASE][skip ci] Fix release version \${project.version}" -Dincludes=pom.xml
mvn --batch-mode scm:check-local-modification -DpushChanges=false
mvn --batch-mode scm:tag -Dtag="\${project.version}"
Expand Down
39 changes: 35 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
<groupId>com.blazemeter.jmeter</groupId>
<artifactId>jmeter-bzm-rte</artifactId>
<packaging>jar</packaging>
<version>2.2.1</version>
<version>2.3</version>
<name>RTEPlugin Sampler as JMeter plugin</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<jmeter.version>3.2</jmeter.version>
<jmeter.version>3.1</jmeter.version>
</properties>

<repositories>
Expand All @@ -31,7 +31,7 @@
</scm>

<dependencies>

<dependency>
<groupId>com.github.blazemeter</groupId>
<artifactId>xtn5250</artifactId>
Expand Down Expand Up @@ -330,6 +330,37 @@
</plugins>
</build>
</profile>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M3</version>
<executions>
<execution>
<id>enforce-no-snapshots</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireReleaseDeps>
<message>No Snapshots Allowed!</message>
<excludes>
<exclude>org.apache.maven:maven-core</exclude>
<exclude>org.apache.maven.plugins:*</exclude>
</excludes>
</requireReleaseDeps>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ protected SocketFactory getSocketFactory(SSLType sslType, String server) throws
}

@Override
public void send(List<Input> input, AttentionKey attentionKey) throws RteIOException {
public void send(List<Input> input, AttentionKey attentionKey)
throws RteIOException {
input.forEach(this::setField);
sendAttentionKey(attentionKey);
exceptionHandler.throwAnyPendingError();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ void await(List<WaitCondition> waitConditions)

void removeTerminalStateListener(TerminalStateListener terminalStateListener);

void send(List<Input> input, AttentionKey attentionKey) throws RteIOException;
void send(List<Input> input, AttentionKey attentionKey)
throws RteIOException;

Screen getScreen();

Expand Down
46 changes: 46 additions & 0 deletions src/main/java/com/blazemeter/jmeter/rte/core/TabulatorInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.blazemeter.jmeter.rte.core;

import java.util.Objects;

public class TabulatorInput extends Input {

//offset is the number of tabs that will be sent
private final int offset;

public TabulatorInput(int offset, String value) {
super(value);
this.offset = offset;
}

@Override
public String getCsv() {
return "<TAB*" + offset + ">\t" + getInput();
}

@Override
public String toString() {
return offset + " tab/s:" + getInput();
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TabulatorInput that = (TabulatorInput) o;
return Objects.equals(offset, that.offset) &&
Objects.equals(getInput(), that.getInput());
}

public int getOffset() {
return offset;
}

@Override
public int hashCode() {
return Objects.hash(offset, getInput());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.blazemeter.jmeter.rte.core.exceptions;

import java.util.NoSuchElementException;

public class ScreenWithoutFieldException extends NoSuchElementException {

public ScreenWithoutFieldException() {
super("Screen not composed by fields. None found.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import com.blazemeter.jmeter.rte.core.LabelInput;
import com.blazemeter.jmeter.rte.core.Position;
import com.blazemeter.jmeter.rte.core.Screen;
import com.blazemeter.jmeter.rte.core.TabulatorInput;
import com.blazemeter.jmeter.rte.core.TerminalType;
import com.blazemeter.jmeter.rte.core.exceptions.ConnectionClosedException;
import com.blazemeter.jmeter.rte.core.exceptions.InvalidFieldLabelException;
import com.blazemeter.jmeter.rte.core.exceptions.InvalidFieldPositionException;
import com.blazemeter.jmeter.rte.core.exceptions.RteIOException;
import com.blazemeter.jmeter.rte.core.exceptions.ScreenWithoutFieldException;
import com.blazemeter.jmeter.rte.core.listener.ExceptionHandler;
import com.blazemeter.jmeter.rte.core.listener.TerminalStateListener;
import com.blazemeter.jmeter.rte.core.ssl.SSLType;
Expand Down Expand Up @@ -40,6 +42,7 @@
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -178,6 +181,7 @@ public void removeTerminalStateListener(TerminalStateListener listener) {
client.removeKeyboardStatusListener(listenerProxy);
client.removeCursorMoveListener(listenerProxy);
}

}

private void setFieldByCoord(CoordInput i) {
Expand All @@ -195,11 +199,21 @@ protected void setField(Input i) {
setFieldByCoord((CoordInput) i);
} else if (i instanceof LabelInput) {
setFieldByLabel((LabelInput) i);
} else if (i instanceof TabulatorInput) {
setFieldByTabulator((TabulatorInput) i);
} else {
throw new IllegalArgumentException("Invalid input type: " + i.getClass());
}
}

private void setFieldByTabulator(TabulatorInput i) {
try {
client.setTabulatedInput(i.getInput(), i.getOffset());
} catch (NoSuchElementException e) {
throw new ScreenWithoutFieldException();
}
}

private void setFieldByLabel(LabelInput i) {
try {
client.setFieldTextByLabel(i.getLabel(), i.getInput());
Expand Down Expand Up @@ -248,9 +262,8 @@ public Screen getScreen() {
}
}

private Screen buildScreenFromText(String screenText) {
Dimension size = getScreenSize();
Screen ret = new Screen(size);
public Screen buildScreenFromText(String screenText) {
Screen ret = new Screen(getScreenSize());
int lastNonBlankPosition = screenText.length() - 1;
while (lastNonBlankPosition >= 0 && (screenText.charAt(lastNonBlankPosition) == ' '
|| screenText.charAt(lastNonBlankPosition) == '\u0000')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void keyboardStatusChanged(KeyboardStatusChangedEvent keyboardStatusChang

@Override
public void cursorMoved(int i, int i1, Field field) {
handleReceivedEvent("screenChanged");
handleReceivedEvent("cursorMoved");
}

@Override
Expand All @@ -51,8 +51,7 @@ protected boolean getCurrentConditionState() {
}

private void handleReceivedEvent(String event) {
/*
we are updating over here because
/*we are updating over here because
silent does not really have a
condition. Then always when some event
arrives we need to startStablePeriod again.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import com.blazemeter.jmeter.rte.core.LabelInput;
import com.blazemeter.jmeter.rte.core.Position;
import com.blazemeter.jmeter.rte.core.Screen;
import com.blazemeter.jmeter.rte.core.TabulatorInput;
import com.blazemeter.jmeter.rte.core.TerminalType;
import com.blazemeter.jmeter.rte.core.exceptions.ConnectionClosedException;
import com.blazemeter.jmeter.rte.core.exceptions.InvalidFieldLabelException;
import com.blazemeter.jmeter.rte.core.exceptions.InvalidFieldPositionException;
import com.blazemeter.jmeter.rte.core.exceptions.RteIOException;
import com.blazemeter.jmeter.rte.core.exceptions.ScreenWithoutFieldException;
import com.blazemeter.jmeter.rte.core.listener.ExceptionHandler;
import com.blazemeter.jmeter.rte.core.listener.TerminalStateListener;
import com.blazemeter.jmeter.rte.core.ssl.SSLType;
Expand All @@ -35,6 +37,7 @@
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -147,11 +150,22 @@ protected void setField(Input i) {
setFieldByCoord((CoordInput) i);
} else if (i instanceof LabelInput) {
setFieldByLabel((LabelInput) i);
} else if (i instanceof TabulatorInput) {
setFieldByTabulator((TabulatorInput) i);
} else {
throw new IllegalArgumentException("Invalid input type: " + i.getClass());
}
}

private void setFieldByTabulator(TabulatorInput i) {
try {
client
.setFieldTextByTabulator(i.getOffset(), i.getInput());
} catch (NoSuchElementException e) {
throw new ScreenWithoutFieldException();
}
}

private void setFieldByCoord(CoordInput i) {
try {
client.setFieldTextByCoord(i.getPosition().getRow(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import com.blazemeter.jmeter.rte.core.listener.ExceptionHandler;
import com.blazemeter.jmeter.rte.core.wait.TextWaitCondition;
import com.blazemeter.jmeter.rte.protocols.tn5250.Tn5250Client;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import net.infordata.em.tn5250.XI5250EmulatorEvent;

public class ScreenTextListener extends Tn5250ConditionWaiter<TextWaitCondition> {


private static final List<String> EVENT_NAMES = getEventNames();

public ScreenTextListener(TextWaitCondition condition, Tn5250Client client,
ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) {
super(condition, client, stableTimeoutExecutor, exceptionHandler);
Expand Down Expand Up @@ -54,6 +57,6 @@ protected boolean getCurrentConditionState() {
}

private void handleReceivedEvent(XI5250EmulatorEvent event) {
updateConditionState(event.toString());
updateConditionState(EVENT_NAMES.get(event.getID()));
}
}
Loading

0 comments on commit 4572601

Please sign in to comment.