From 345618159c825e1c8993bad5687c9a8d5f3dc632 Mon Sep 17 00:00:00 2001 From: Joaquin Araujo <48018971+Baraujo25@users.noreply.github.com> Date: Wed, 15 Jan 2020 11:55:48 -0300 Subject: [PATCH] wait conditions refactored, readme updated, directories organized (#9) --- README.md | 20 +++--- docs/{ => extractor}/extractor-component.gif | Bin docs/{ => extractor}/extractor-usage.gif | Bin docs/{ => extractor}/response-headers.png | Bin docs/{ => extractor}/rte-extractor.md | 0 docs/{ => extractor}/rte-extractor.png | Bin docs/{ => extractor}/variable-prefix.png | Bin .../recorder-panel.png} | Bin .../recorder-template.png} | Bin .../recording-filled-up.png} | Bin .../terminal-emulator}/assertion-usage.gif | Bin .../terminal-emulator}/emulator-login.png | Bin .../input-by-label-usage.gif | Bin .../rte-recorder-emulator.png | Bin .../terminal-emulator/terminal-emulator.md | 31 ++++++++++ .../wait-conditions}/regex-wait-for-text.png | Bin .../unsucessful-text-wait-condition.png | Bin .../wait-conditions-recording.md | 4 +- .../wait-conditions}/wait-for-text-usage.gif | Bin .../view-result-tree.png} | Bin docs/terminal-emulator.md | 31 ---------- pom.xml | 14 ++--- .../jmeter/rte/core/wait/ConditionWaiter.java | 34 +++++++++- .../rte/core/wait/CursorWaitCondition.java | 2 +- .../rte/core/wait/TextWaitCondition.java | 3 +- .../jmeter/rte/extractor/RTEExtractor.java | 15 ++--- .../tn3270/listeners/ScreenTextListener.java | 37 +++-------- .../tn3270/listeners/SilenceListener.java | 27 ++++---- .../listeners/Tn3270ConditionWaiter.java | 3 +- .../tn3270/listeners/UnlockListener.java | 31 +++------- .../listeners/VisibleCursorListener.java | 21 ++----- .../tn5250/listeners/ScreenTextListener.java | 35 +++-------- .../tn5250/listeners/SilenceListener.java | 38 +++++++++--- .../listeners/Tn5250ConditionWaiter.java | 1 + .../tn5250/listeners/UnlockListener.java | 26 ++------ .../listeners/VisibleCursorListener.java | 20 ++---- src/main/resources/recorder-help.html | 6 +- .../rte/core/listeners/ConditionWaiterIT.java | 14 ++--- .../rte/protocols/RteProtocolClientIT.java | 2 + .../rte/protocols/tn3270/Tn3270ClientIT.java | 12 ++++ .../listeners/ScreenTextListenerIT.java | 8 +-- .../tn3270/listeners/UnlockListenerIT.java | 1 + .../rte/protocols/tn5250/Tn5250ClientIT.java | 10 +++ .../listeners/ScreenTextListenerIT.java | 13 +--- .../protocols/tn3270/login-flash-screen.html | 27 ++++++++ .../login-with-multiple-flash-screen.yml | 58 ++++++++++++++++++ .../login-with-multiple-flash-screen.yml | 27 ++++++++ 47 files changed, 335 insertions(+), 236 deletions(-) rename docs/{ => extractor}/extractor-component.gif (100%) rename docs/{ => extractor}/extractor-usage.gif (100%) rename docs/{ => extractor}/response-headers.png (100%) rename docs/{ => extractor}/rte-extractor.md (100%) rename docs/{ => extractor}/rte-extractor.png (100%) rename docs/{ => extractor}/variable-prefix.png (100%) rename docs/{RecorderPanel.png => recorder/recorder-panel.png} (100%) rename docs/{RecorderTemplate.png => recorder/recorder-template.png} (100%) rename docs/{RecordingFilledUp.png => recorder/recording-filled-up.png} (100%) rename docs/{ => recorder/terminal-emulator}/assertion-usage.gif (100%) rename docs/{ => recorder/terminal-emulator}/emulator-login.png (100%) rename docs/{ => recorder/terminal-emulator}/input-by-label-usage.gif (100%) rename docs/{ => recorder/terminal-emulator}/rte-recorder-emulator.png (100%) create mode 100644 docs/recorder/terminal-emulator/terminal-emulator.md rename docs/{ => recorder/terminal-emulator/wait-conditions}/regex-wait-for-text.png (100%) rename docs/{ => recorder/terminal-emulator/wait-conditions}/unsucessful-text-wait-condition.png (100%) rename docs/{ => recorder/terminal-emulator/wait-conditions}/wait-conditions-recording.md (94%) rename docs/{ => recorder/terminal-emulator/wait-conditions}/wait-for-text-usage.gif (100%) rename docs/{View-Result-Tree.png => recorder/view-result-tree.png} (100%) delete mode 100644 docs/terminal-emulator.md create mode 100644 src/test/resources/com/blazemeter/jmeter/rte/protocols/tn3270/login-flash-screen.html create mode 100644 src/test/resources/com/blazemeter/jmeter/rte/protocols/tn3270/login-with-multiple-flash-screen.yml create mode 100644 src/test/resources/com/blazemeter/jmeter/rte/protocols/tn5250/login-with-multiple-flash-screen.yml diff --git a/README.md b/README.md index 8014dc0f..992a6618 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ People who usually work with these IBM servers interact with them, basically, by The plugin adds four different elements to JMeter: #### A Recording Controller (RTE-Recorder) -![alt text](docs/RecorderPanel.png "RTE Recorder") +![alt text](docs/recorder/recorder-panel.png "RTE Recorder") ##### Configuring the RTE Recorder First all is necessary to add a template which is going to contain all the elements required to begin with recording. @@ -25,19 +25,19 @@ Go to Files -> Templates -> and select _Recording RTE_ ##### Usage of RTE Recorder -To start recording, the user should specify the _Server_, _Port_, _Protocol_, _Terminal Type_, _SSL Type_, _Timeout_ and _Timeout Threshold_. These configurations are the same ones detailed in [RTE-Config](#a-config-element-rte-config). Details about Timeout Threshold field and how the wait conditions works can be found [here](docs/wait-conditions-recording.md). - ![alt_text](docs/RecordingFilledUp.png) +To start recording, the user should specify the _Server_, _Port_, _Protocol_, _Terminal Type_, _SSL Type_, _Timeout_ and _Timeout Threshold_. These configurations are the same ones detailed in [RTE-Config](#a-config-element-rte-config). Details about Timeout Threshold field and how the wait conditions works can be found [here](docs/recorder/terminal-emulator/wait-conditions/wait-conditions-recording.md). + ![alt_text](docs/recorder/recording-filled-up.png) > RTE recorder buttons purpose: > - *Start*: This button allows the user to begin with the recording and to connect to the mainframe application through a terminal emulator. Additionally, after button is pressed, an RTE-Config and Connect Sampler will be added to the test plan. > - *Stop*: This button allows the user to stop current recording. Once this button is pressed, the recording will be stopped and Disconnect Sampler will be added to the test plan followed by the closure of the terminal emulator. > - *Restart*: This button is the equivalent to pressing stop and start buttons. - > - *Timeout Threshold*: This field will set the timeout which later on the [waits conditions ](docs/wait-conditions-recording.md)will use to set the proper time out for the conditions. + > - *Timeout Threshold*: This field will set the timeout which later on the [waits conditions ](docs/recorder/terminal-emulator/wait-conditions/wait-conditions-recording.md)will use to set the proper time out for the conditions. -Once everything is configured, the user proceeds to start the recording session, pressing START button. After the connection to the mainframe application is established (supposing configurations are right), the **[Terminal Emulator](/docs/terminal-emulator.md)** will show up. +Once everything is configured, the user proceeds to start the recording session, pressing START button. After the connection to the mainframe application is established (supposing configurations are right), the **[Terminal Emulator](/docs/recorder/terminal-emulator/terminal-emulator.md)** will show up. -![alt_text](docs/emulator-login.png) +![alt_text](docs/recorder/terminal-emulator/emulator-login.png) ->Click [here](/docs/terminal-emulator.md) to see all the information you must know about terminal emulator. +>Click [here](/docs/recorder/terminal-emulator/terminal-emulator.md) to see all the information you must know about terminal emulator. Now we are able to interact with our client through RTE-Emulator. Every interaction will be automatically saved in samplers (_[check out everything about samplers ](#sampler-rte-sampler)_). Once we have ended the flow that we want to record, we can easily close the terminal emulator or press STOP button to stop our recording. @@ -49,7 +49,7 @@ Once we have ended the flow that we want to record, we can easily close the term When a View Results Tree JMeter test element is included as a child of the RTE recorder, then all interactions between the Terminal Emulator and the mainframe application will be showed on the View Results Tree and allow for proper analysis and validation of all information sent and received from the mainframe application. -![alter_text](docs/View-Result-Tree.png) +![alter_text](docs/recorder/view-result-tree.png) #### A Config Element (RTE Config) @@ -96,10 +96,10 @@ All the "waiters" use a stable timeout value (in milliseconds) which specifies t > Warning: both Stable Timeout and Silent Interval should be shorter than Timeout value, otherwise the sampler will always return a timeout error. #### RTE-Extractor -![alt_text](docs/rte-extractor.png) +![alt_text](docs/extractor/rte-extractor.png) RTE-Extractor is a post-processor which its main purpose is to extract positions from response headers to be used later as a JMeter variable. -> Check [here](/docs/rte-extractor.md) for more information. +> Check [here](/docs/extractor/rte-extractor.md) for more information. ### Example diff --git a/docs/extractor-component.gif b/docs/extractor/extractor-component.gif similarity index 100% rename from docs/extractor-component.gif rename to docs/extractor/extractor-component.gif diff --git a/docs/extractor-usage.gif b/docs/extractor/extractor-usage.gif similarity index 100% rename from docs/extractor-usage.gif rename to docs/extractor/extractor-usage.gif diff --git a/docs/response-headers.png b/docs/extractor/response-headers.png similarity index 100% rename from docs/response-headers.png rename to docs/extractor/response-headers.png diff --git a/docs/rte-extractor.md b/docs/extractor/rte-extractor.md similarity index 100% rename from docs/rte-extractor.md rename to docs/extractor/rte-extractor.md diff --git a/docs/rte-extractor.png b/docs/extractor/rte-extractor.png similarity index 100% rename from docs/rte-extractor.png rename to docs/extractor/rte-extractor.png diff --git a/docs/variable-prefix.png b/docs/extractor/variable-prefix.png similarity index 100% rename from docs/variable-prefix.png rename to docs/extractor/variable-prefix.png diff --git a/docs/RecorderPanel.png b/docs/recorder/recorder-panel.png similarity index 100% rename from docs/RecorderPanel.png rename to docs/recorder/recorder-panel.png diff --git a/docs/RecorderTemplate.png b/docs/recorder/recorder-template.png similarity index 100% rename from docs/RecorderTemplate.png rename to docs/recorder/recorder-template.png diff --git a/docs/RecordingFilledUp.png b/docs/recorder/recording-filled-up.png similarity index 100% rename from docs/RecordingFilledUp.png rename to docs/recorder/recording-filled-up.png diff --git a/docs/assertion-usage.gif b/docs/recorder/terminal-emulator/assertion-usage.gif similarity index 100% rename from docs/assertion-usage.gif rename to docs/recorder/terminal-emulator/assertion-usage.gif diff --git a/docs/emulator-login.png b/docs/recorder/terminal-emulator/emulator-login.png similarity index 100% rename from docs/emulator-login.png rename to docs/recorder/terminal-emulator/emulator-login.png diff --git a/docs/input-by-label-usage.gif b/docs/recorder/terminal-emulator/input-by-label-usage.gif similarity index 100% rename from docs/input-by-label-usage.gif rename to docs/recorder/terminal-emulator/input-by-label-usage.gif diff --git a/docs/rte-recorder-emulator.png b/docs/recorder/terminal-emulator/rte-recorder-emulator.png similarity index 100% rename from docs/rte-recorder-emulator.png rename to docs/recorder/terminal-emulator/rte-recorder-emulator.png diff --git a/docs/recorder/terminal-emulator/terminal-emulator.md b/docs/recorder/terminal-emulator/terminal-emulator.md new file mode 100644 index 00000000..c34aba17 --- /dev/null +++ b/docs/recorder/terminal-emulator/terminal-emulator.md @@ -0,0 +1,31 @@ +# Terminal Emulator +![alt_text](rte-recorder-emulator.png) + +- Pressing ![alt_text](/src/main/resources/dark-theme/copy.png) you are able to copy from the emulator, also using the standard keyboard shortcuts. +- Pressing ![alt_text](/src/main/resources/dark-theme/paste.png) you are able to paste in cursor position on the emulator, also using standard shortcuts. +- You can select a screen area to be used as input field label, press ![alt_text](/src/main/resources/dark-theme/inputByLabel.png) and then set the input field text, to record a test plan that uses the provided label to locate the input field on the screen and fill the field with provided text. + > Input by label allows to find the field on the screen regardless of changes of field positioning, which makes recorded test plans more robust (than using default input by coord). + + >[Here](#input-by-label-usage) is a small example of input by label usage. +- You can select a screen area to be used as wait for text, then press ![alt_text](/src/main/resources/dark-theme/waitForText.png) and a new *Text Wait Condition* will be added to your sampler. + + >[Here](wait-conditions/wait-conditions-recording.md#text-wait-condition) is more information about wait for text, how it works and a little usage example. + +- You can press assertion button ![alt_text](/src/main/resources/dark-theme/assertion.png) when you want to make sure that a part of the screen has appeared in the screen. This assertion has the same behaviour as JMeter Assertions. To assert for a part of the screen you just have to select a part of the screen and press the button. An assertion will be added to corresponding sampler. + >[Here](#recorder-screen-assertion-usage) is an example of usage. + +- Clicking on ![alter_text](/src/main/resources/dark-theme/not-visible-credentials.png) / ![alter_text](/src/main/resources/dark-theme/visible-credentials.png) you will be able to show/hide credentials. + +- If you click on the ![alter_text](/src/main/resources/dark-theme/help.png) icon in the emulator, a pop up window will be displayed with general help information on the emulator: shortcuts, explanation about indicators on the screen, etc. + +- **Sample name:** As the label says, you can specify the name of the sample in current screen. + +### Input By Label Usage + +![alt_text](input-by-label-usage.gif) + + + +### Recorder Screen Assertion Usage + +![alt_text](assertion-usage.gif) \ No newline at end of file diff --git a/docs/regex-wait-for-text.png b/docs/recorder/terminal-emulator/wait-conditions/regex-wait-for-text.png similarity index 100% rename from docs/regex-wait-for-text.png rename to docs/recorder/terminal-emulator/wait-conditions/regex-wait-for-text.png diff --git a/docs/unsucessful-text-wait-condition.png b/docs/recorder/terminal-emulator/wait-conditions/unsucessful-text-wait-condition.png similarity index 100% rename from docs/unsucessful-text-wait-condition.png rename to docs/recorder/terminal-emulator/wait-conditions/unsucessful-text-wait-condition.png diff --git a/docs/wait-conditions-recording.md b/docs/recorder/terminal-emulator/wait-conditions/wait-conditions-recording.md similarity index 94% rename from docs/wait-conditions-recording.md rename to docs/recorder/terminal-emulator/wait-conditions/wait-conditions-recording.md index fdd34451..b24801c3 100644 --- a/docs/wait-conditions-recording.md +++ b/docs/recorder/terminal-emulator/wait-conditions/wait-conditions-recording.md @@ -8,7 +8,7 @@ To begin with the explication of how Wait Conditions works in RTE-Recorder, lets *Text wait*: It will wait until the text appears on the screen. -[Here](../README.md#waiters-usage) there is more information about Wait Conditions in general. +[Here](/README.md#waiters-usage) there is more information about Wait Conditions in general. ## Wait conditions recording resolution @@ -18,7 +18,7 @@ Wait conditions recording resolution is currently determined by the following 4 **Case 2**: Consider now that you have received multiple keyboards status changes, so the keyboard has been unlocked and locked few/many times but the difference between those locks and unlocks is greater than **Stable Period**, then the plugin will add a Silent Wait Condition alongside a warning stating this behavior and a possible workaround in case the recorded behavior is not the expected one. -> [Here](../README.md#stable-period) for a description of stable period +> [Here](/README.md#stable-period) for a description of stable period **Case 3**: In this case your keyboard state has been changed once or several times but now the difference between those status changes is lower than stable period and also the difference between last keyboard unlock and last event occurred (like attention keys, inputs, etc) is lower than Stable Period. Then a Sync Wait Condition will be added to that sampler. diff --git a/docs/wait-for-text-usage.gif b/docs/recorder/terminal-emulator/wait-conditions/wait-for-text-usage.gif similarity index 100% rename from docs/wait-for-text-usage.gif rename to docs/recorder/terminal-emulator/wait-conditions/wait-for-text-usage.gif diff --git a/docs/View-Result-Tree.png b/docs/recorder/view-result-tree.png similarity index 100% rename from docs/View-Result-Tree.png rename to docs/recorder/view-result-tree.png diff --git a/docs/terminal-emulator.md b/docs/terminal-emulator.md deleted file mode 100644 index 9288efeb..00000000 --- a/docs/terminal-emulator.md +++ /dev/null @@ -1,31 +0,0 @@ -# Terminal Emulator -![alt_text](rte-recorder-emulator.png) - -- Pressing ![alt_text](../src/main/resources/dark-theme/copy.png) you are able to copy from the emulator, also using the standard keyboard shortcuts. -- Pressing ![alt_text](../src/main/resources/dark-theme/paste.png) you are able to paste in cursor position on the emulator, also using standard shortcuts. -- You can select a screen area to be used as input field label, press ![alt_text](../src/main/resources/dark-theme/inputByLabel.png) and then set the input field text, to record a test plan that uses the provided label to locate the input field on the screen and fill the field with provided text. - > Input by label allows to find the field on the screen regardless of changes of field positioning, which makes recorded test plans more robust (than using default input by coord). - - >[Here](#input-by-label-usage) is a small example of input by label usage. -- You can select a screen area to be used as wait for text, then press ![alt_text](../src/main/resources/dark-theme/waitForText.png) and a new *Text Wait Condition* will be added to your sampler. - - >[Here](wait-conditions-recording.md#text-wait-condition) is more information about wait for text, how it works and a little usage example. - -- You can press assertion button ![alt_text](../src/main/resources/dark-theme/assertion.png) when you want to make sure that a part of the screen has appeared in the screen. This assertion has the same behaviour as JMeter Assertions. To assert for a part of the screen you just have to select a part of the screen and press the button. An assertion will be added to corresponding sampler. - >[Here](#recorder-screen-assertion-usage) is an example of usage. - -- Clicking on ![alter_text](../src/main/resources/dark-theme/not-visible-credentials.png) / ![alter_text](../src/main/resources/dark-theme/visible-credentials.png) you will be able to show/hide credentials. - -- If you click on the ![alter_text](../src/main/resources/dark-theme/help.png) icon in the emulator, a pop up window will be displayed with general help information on the emulator: shortcuts, explanation about indicators on the screen, etc. - -- **Sample name:** As the label says, you can specify the name of the sample in current screen. - -### Input By Label Usage - -![alt_text](input-by-label-usage.gif) - - - -### Recorder Screen Assertion Usage - -![alt_text](assertion-usage.gif) \ No newline at end of file diff --git a/pom.xml b/pom.xml index a07125d7..f664ca5e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,14 +6,14 @@ com.blazemeter.jmeter jmeter-bzm-rte jar - 2.2 + 2.2.1 RTEPlugin Sampler as JMeter plugin UTF-8 UTF-8 - 3.1 + 3.2 @@ -24,10 +24,10 @@ - scm:git:git@github.com:Blazemeter/RTEPlugin.git - scm:git:git@github.com:Blazemeter/RTEPlugin.git + scm:git:git@gitlab.abstracta.us:BZ-plugins/jmeter-rte-plugin.git + scm:git:git@gitlab.abstracta.us:BZ-plugins/jmeter-rte-plugin.git - https://github.com/Blazemeter/RTEPlugin + http://gitlab.abstracta.us/BZ-plugins/jmeter-rte-plugin @@ -35,12 +35,12 @@ com.github.blazemeter xtn5250 - 3.1 + 3.2 com.github.blazemeter dm3270 - 0.11.1-lib + 0.12.1-lib diff --git a/src/main/java/com/blazemeter/jmeter/rte/core/wait/ConditionWaiter.java b/src/main/java/com/blazemeter/jmeter/rte/core/wait/ConditionWaiter.java index 2096cfd0..6ee97cc6 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/core/wait/ConditionWaiter.java +++ b/src/main/java/com/blazemeter/jmeter/rte/core/wait/ConditionWaiter.java @@ -8,13 +8,18 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class ConditionWaiter implements ExceptionListener { + private static final Logger LOG = LoggerFactory.getLogger(ConditionWaiter.class); + protected final T condition; - private ExceptionHandler exceptionHandler; + protected boolean lastConditionState; private final CountDownLatch lock = new CountDownLatch(1); private final ScheduledExecutorService stableTimeoutExecutor; + private ExceptionHandler exceptionHandler; private ScheduledFuture stableTimeoutTask; private boolean ended; @@ -26,7 +31,7 @@ public ConditionWaiter(T condition, ScheduledExecutorService stableTimeoutExecut exceptionHandler.addListener(this); } - protected synchronized void startStablePeriod() { + private synchronized void startStablePeriod() { if (ended) { return; } @@ -35,7 +40,7 @@ protected synchronized void startStablePeriod() { .schedule(lock::countDown, condition.getStableTimeoutMillis(), TimeUnit.MILLISECONDS); } - protected synchronized void endStablePeriod() { + private synchronized void endStablePeriod() { if (stableTimeoutTask != null) { stableTimeoutTask.cancel(false); } @@ -72,4 +77,27 @@ public void stop() { exceptionHandler.removeListener(this); } + protected void updateConditionState(String event) { + boolean currentConditionState = getCurrentConditionState(); + if (lastConditionState != currentConditionState) { + lastConditionState = currentConditionState; + if (currentConditionState) { + LOG.debug("Stable period restarted because event {} arrived", event); + startStablePeriod(); + } else { + LOG.debug("Stable period cancelled. Since {} arrived condition does not meet", event); + endStablePeriod(); + } + } + } + + protected abstract boolean getCurrentConditionState(); + + protected void initialVerificationOfCondition() { + if (getCurrentConditionState()) { + LOG.debug("Start stable period since condition was already met"); + startStablePeriod(); + lastConditionState = true; + } + } } diff --git a/src/main/java/com/blazemeter/jmeter/rte/core/wait/CursorWaitCondition.java b/src/main/java/com/blazemeter/jmeter/rte/core/wait/CursorWaitCondition.java index 9160029f..11936d43 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/core/wait/CursorWaitCondition.java +++ b/src/main/java/com/blazemeter/jmeter/rte/core/wait/CursorWaitCondition.java @@ -7,7 +7,7 @@ * {@link WaitCondition} to wait for the cursor to be visible on the desired position. */ public class CursorWaitCondition extends WaitCondition { - + private final Position position; public CursorWaitCondition(Position position, long timeoutMillis, long stableTimeoutMillis) { diff --git a/src/main/java/com/blazemeter/jmeter/rte/core/wait/TextWaitCondition.java b/src/main/java/com/blazemeter/jmeter/rte/core/wait/TextWaitCondition.java index bd623e84..13820069 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/core/wait/TextWaitCondition.java +++ b/src/main/java/com/blazemeter/jmeter/rte/core/wait/TextWaitCondition.java @@ -13,9 +13,8 @@ * {@link WaitCondition} to wait for certain text to be in terminal screen. */ public class TextWaitCondition extends WaitCondition { - + private static final Logger LOG = LoggerFactory.getLogger(TextWaitCondition.class); - private final Pattern regex; private final PatternMatcher matcher; private final Area searchArea; diff --git a/src/main/java/com/blazemeter/jmeter/rte/extractor/RTEExtractor.java b/src/main/java/com/blazemeter/jmeter/rte/extractor/RTEExtractor.java index 3485c5fe..ee7a84bd 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/extractor/RTEExtractor.java +++ b/src/main/java/com/blazemeter/jmeter/rte/extractor/RTEExtractor.java @@ -26,7 +26,8 @@ public class RTEExtractor extends AbstractScopedTestElement implements PostProce private static final String VARIABLE_PREFIX_PROPERTY = "RTEExtractor.variablePrefix"; private static final String POSITION_TYPE_PROPERTY = "RTEExtractor.positionType"; private static final PositionType DEFAULT_POSITION_TYPE = PositionType.CURSOR_POSITION; - + private static final int UNSPECIFIED_COORDS = 1; + private static final int UNSPECIFIED_OFFSET = 0; private JMeterContext context; public RTEExtractor() { @@ -101,7 +102,7 @@ private Position extractFieldPosition(String responseHeaders, String requestHead } private int getOffsetAsInt() { - return Integer.parseInt(getOffset()); + return getOffset().isEmpty() ? UNSPECIFIED_OFFSET : Integer.parseInt(getOffset()); } private boolean isGivenFieldPositionValid(String requestHeaders) { @@ -149,11 +150,11 @@ private String extractTerminalType(String requestHeaders) { } private int getRowAsInt() { - return Integer.parseInt(getRow()); + return getRow().isEmpty() ? UNSPECIFIED_COORDS : Integer.parseInt(getRow()); } private int getColumnAsInt() { - return Integer.parseInt(getColumn()); + return getColumn().isEmpty() ? UNSPECIFIED_COORDS : Integer.parseInt(getColumn()); } public String getRow() { @@ -161,7 +162,7 @@ public String getRow() { } public void setRow(String row) { - setProperty(ROW_PROPERTY, row); + setProperty(ROW_PROPERTY, row, "1"); } public String getColumn() { @@ -169,7 +170,7 @@ public String getColumn() { } public void setColumn(String column) { - setProperty(COLUMN_PROPERTY, column); + setProperty(COLUMN_PROPERTY, column, "1"); } public String getOffset() { @@ -177,7 +178,7 @@ public String getOffset() { } public void setOffset(String offset) { - setProperty(OFFSET_PROPERTY, offset); + setProperty(OFFSET_PROPERTY, offset, "0"); } public String getVariablePrefix() { diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/ScreenTextListener.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/ScreenTextListener.java index 0d0c78a6..e2c31094 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/ScreenTextListener.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/ScreenTextListener.java @@ -10,56 +10,32 @@ import com.bytezone.dm3270.display.ScreenChangeListener; import com.bytezone.dm3270.display.ScreenWatcher; import java.util.concurrent.ScheduledExecutorService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class ScreenTextListener extends Tn3270ConditionWaiter implements KeyboardStatusListener, CursorMoveListener, ScreenChangeListener { - private static final Logger LOG = LoggerFactory.getLogger(ScreenTextListener.class); - - private boolean matched; - public ScreenTextListener(TextWaitCondition condition, Tn3270Client client, ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) { super(condition, client, stableTimeoutExecutor, exceptionHandler); client.addCursorMoveListener(this); client.addKeyboardStatusListener(this); client.addScreenChangeListener(this); - checkIfScreenMatchesCondition(); - if (matched) { - startStablePeriod(); - } } @Override public void keyboardStatusChanged(KeyboardStatusChangedEvent keyboardStatusChangedEvent) { - handleReceivedEvent("keyboardStatusChanged"); + updateConditionState(keyboardStatusChangedEvent.getClass().getSimpleName()); } @Override public void cursorMoved(int i, int i1, Field field) { - handleReceivedEvent("cursorMoved"); + updateConditionState("cursor moved"); + } @Override public void screenChanged(ScreenWatcher screenWatcher) { - checkIfScreenMatchesCondition(); - handleReceivedEvent("screenChanged"); - } - - private void handleReceivedEvent(String event) { - if (matched) { - LOG.debug("Restart screen text stable period since received event {}", event); - startStablePeriod(); - } - } - - private void checkIfScreenMatchesCondition() { - if (condition.matchesScreen(client.getScreen())) { - LOG.debug("Found matching text in screen, now waiting for silent period."); - matched = true; - } + updateConditionState(screenWatcher.getClass().getSimpleName()); } @Override @@ -70,4 +46,9 @@ public void stop() { client.removeScreenChangeListener(this); } + @Override + protected boolean getCurrentConditionState() { + return condition.matchesScreen(client.getScreen()); + } + } diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/SilenceListener.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/SilenceListener.java index a3b2bdbf..3fc478d0 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/SilenceListener.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/SilenceListener.java @@ -10,26 +10,16 @@ import com.bytezone.dm3270.display.ScreenChangeListener; import com.bytezone.dm3270.display.ScreenWatcher; import java.util.concurrent.ScheduledExecutorService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class SilenceListener extends Tn3270ConditionWaiter implements KeyboardStatusListener, CursorMoveListener, ScreenChangeListener { - private static final Logger LOG = LoggerFactory.getLogger(SilenceListener.class); - public SilenceListener(SilentWaitCondition condition, Tn3270Client client, ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) { super(condition, client, stableTimeoutExecutor, exceptionHandler); client.addCursorMoveListener(this); client.addKeyboardStatusListener(this); client.addScreenChangeListener(this); - startStablePeriod(); - } - - private void handleReceivedEvent(String event) { - LOG.debug("Restarting silent period since event received {}", event); - startStablePeriod(); } @Override @@ -39,7 +29,7 @@ public void keyboardStatusChanged(KeyboardStatusChangedEvent keyboardStatusChang @Override public void cursorMoved(int i, int i1, Field field) { - handleReceivedEvent("cursorMoved"); + handleReceivedEvent("screenChanged"); } @Override @@ -55,4 +45,19 @@ public void stop() { client.removeScreenChangeListener(this); } + @Override + protected boolean getCurrentConditionState() { + return true; + } + + private void handleReceivedEvent(String event) { + /* + we are updating over here because + silent does not really have a + condition. Then always when some event + arrives we need to startStablePeriod again. + */ + lastConditionState = false; + updateConditionState(event); + } } diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/Tn3270ConditionWaiter.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/Tn3270ConditionWaiter.java index f75dc175..4c2c8d57 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/Tn3270ConditionWaiter.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/Tn3270ConditionWaiter.java @@ -10,11 +10,12 @@ public abstract class Tn3270ConditionWaiter extends ConditionWaiter { protected Tn3270Client client; - + public Tn3270ConditionWaiter(T condition, Tn3270Client client, ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) { super(condition, stableTimeoutExecutor, exceptionHandler); this.client = client; + initialVerificationOfCondition(); } } diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/UnlockListener.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/UnlockListener.java index 2e0d6113..a6217e8b 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/UnlockListener.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/UnlockListener.java @@ -6,42 +6,20 @@ import com.bytezone.dm3270.application.KeyboardStatusChangedEvent; import com.bytezone.dm3270.application.KeyboardStatusListener; import java.util.concurrent.ScheduledExecutorService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class UnlockListener extends Tn3270ConditionWaiter implements KeyboardStatusListener { - private static final Logger LOG = LoggerFactory.getLogger( - UnlockListener.class); - private boolean isInputInhibited; - public UnlockListener(SyncWaitCondition condition, Tn3270Client client, ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) { super(condition, client, stableTimeoutExecutor, exceptionHandler); client.addKeyboardStatusListener(this); - isInputInhibited = client.isInputInhibited(); - if (!isInputInhibited) { - LOG.debug("Start stable period since input is not inhibited"); - startStablePeriod(); - } } @Override public void keyboardStatusChanged(KeyboardStatusChangedEvent keyboardStatusChangedEvent) { - LOG.debug("keyboardStatusChanged {}", keyboardStatusChangedEvent.toString()); - - boolean wasInputInhibited = isInputInhibited; - isInputInhibited = keyboardStatusChangedEvent.keyboardLocked; - if (isInputInhibited != wasInputInhibited) { - if (isInputInhibited) { - LOG.debug("Cancel stable period since input has been inhibited"); - endStablePeriod(); - } else { - LOG.debug("Start stable period since input is no longer inhibited"); - startStablePeriod(); - } - } + updateConditionState( + "keyboardStatusChanged: " + keyboardStatusChangedEvent.toString()); } @Override @@ -50,4 +28,9 @@ public void stop() { client.removeKeyboardStatusListener(this); } + @Override + protected boolean getCurrentConditionState() { + return !client.isInputInhibited(); + } + } diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/VisibleCursorListener.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/VisibleCursorListener.java index aae3e937..f3a751bb 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/VisibleCursorListener.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/VisibleCursorListener.java @@ -7,22 +7,14 @@ import com.bytezone.dm3270.display.CursorMoveListener; import com.bytezone.dm3270.display.Field; import java.util.concurrent.ScheduledExecutorService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class VisibleCursorListener extends Tn3270ConditionWaiter implements CursorMoveListener { - private static final Logger LOG = LoggerFactory.getLogger(VisibleCursorListener.class); - public VisibleCursorListener(CursorWaitCondition condition, Tn3270Client client, ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) { super(condition, client, stableTimeoutExecutor, exceptionHandler); client.addCursorMoveListener(this); - if (condition.getPosition().equals(getCursorPosition())) { - LOG.debug("Cursor is in expected position, now waiting for it to remain for stable period"); - startStablePeriod(); - } } private Position getCursorPosition() { @@ -31,13 +23,7 @@ private Position getCursorPosition() { @Override public void cursorMoved(int i, int i1, Field field) { - if (condition.getPosition().equals(getCursorPosition())) { - LOG.debug("Cursor is in expected position, now waiting for it to remain for stable period"); - startStablePeriod(); - } else { - LOG.debug("Cursor is not in expected position, canceling any stable period"); - endStablePeriod(); - } + updateConditionState("cursor moved"); } @Override @@ -46,4 +32,9 @@ public void stop() { client.removeCursorMoveListener(this); } + @Override + protected boolean getCurrentConditionState() { + return condition.getPosition().equals(getCursorPosition()); + } + } diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/ScreenTextListener.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/ScreenTextListener.java index e2b11e4a..07bb40ab 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/ScreenTextListener.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/ScreenTextListener.java @@ -5,22 +5,12 @@ import com.blazemeter.jmeter.rte.protocols.tn5250.Tn5250Client; import java.util.concurrent.ScheduledExecutorService; import net.infordata.em.tn5250.XI5250EmulatorEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class ScreenTextListener extends Tn5250ConditionWaiter { - - private static final Logger LOG = LoggerFactory.getLogger(ScreenTextListener.class); - - private boolean matched; - + public ScreenTextListener(TextWaitCondition condition, Tn5250Client client, ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) { super(condition, client, stableTimeoutExecutor, exceptionHandler); - checkIfScreenMatchesCondition(); - if (matched) { - startStablePeriod(); - } } @Override @@ -28,13 +18,6 @@ public void connecting(XI5250EmulatorEvent event) { handleReceivedEvent(event); } - private void handleReceivedEvent(XI5250EmulatorEvent event) { - if (matched) { - LOG.debug("Restart silent stable period since received event {}", event); - startStablePeriod(); - } - } - @Override public void connected(XI5250EmulatorEvent event) { handleReceivedEvent(event); @@ -52,17 +35,9 @@ public void stateChanged(XI5250EmulatorEvent event) { @Override public void newPanelReceived(XI5250EmulatorEvent event) { - checkIfScreenMatchesCondition(); handleReceivedEvent(event); } - private void checkIfScreenMatchesCondition() { - if (condition.matchesScreen(client.getScreen())) { - LOG.debug("Found matching text in screen, now waiting for silent period."); - matched = true; - } - } - @Override public void fieldsRemoved(XI5250EmulatorEvent event) { handleReceivedEvent(event); @@ -73,4 +48,12 @@ public void dataSended(XI5250EmulatorEvent event) { handleReceivedEvent(event); } + @Override + protected boolean getCurrentConditionState() { + return condition.matchesScreen(client.getScreen()); + } + + private void handleReceivedEvent(XI5250EmulatorEvent event) { + updateConditionState(event.toString()); + } } diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/SilenceListener.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/SilenceListener.java index 6a7f9b29..921429ff 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/SilenceListener.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/SilenceListener.java @@ -3,10 +3,13 @@ import com.blazemeter.jmeter.rte.core.listener.ExceptionHandler; import com.blazemeter.jmeter.rte.core.wait.SilentWaitCondition; import com.blazemeter.jmeter.rte.protocols.tn5250.Tn5250Client; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Collectors; import net.infordata.em.tn5250.XI5250EmulatorEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * A {@link Tn5250ConditionWaiter} which allows waiting until the terminal does not receive events @@ -14,12 +17,19 @@ */ public class SilenceListener extends Tn5250ConditionWaiter { - private static final Logger LOG = LoggerFactory.getLogger(SilenceListener.class); + private static final List EVENT_NAMES = getEventNames(); public SilenceListener(SilentWaitCondition condition, Tn5250Client client, ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) { super(condition, client, stableTimeoutExecutor, exceptionHandler); - startStablePeriod(); + } + + private static List getEventNames() { + Field[] declaredFields = XI5250EmulatorEvent.class.getDeclaredFields(); + return Arrays.stream(declaredFields) + .filter(f -> Modifier.isStatic(f.getModifiers()) && Modifier.isPublic(f.getModifiers())) + .map(Field::getName) + .collect(Collectors.toList()); } @Override @@ -27,11 +37,6 @@ public void connecting(XI5250EmulatorEvent event) { handleReceivedEvent(event); } - private void handleReceivedEvent(XI5250EmulatorEvent event) { - LOG.debug("Restarting silent period since event received {}", event); - startStablePeriod(); - } - @Override public void connected(XI5250EmulatorEvent event) { handleReceivedEvent(event); @@ -62,4 +67,19 @@ public void dataSended(XI5250EmulatorEvent event) { handleReceivedEvent(event); } + @Override + protected boolean getCurrentConditionState() { + return true; + } + + private void handleReceivedEvent(XI5250EmulatorEvent event) { + /* + we are updating over here because + silent does not really have a + condition. Then always when some event + arrives we need to startStablePeriod again. + */ + lastConditionState = false; + updateConditionState(EVENT_NAMES.get(event.getID())); + } } diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/Tn5250ConditionWaiter.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/Tn5250ConditionWaiter.java index d9c09584..bb273025 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/Tn5250ConditionWaiter.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/Tn5250ConditionWaiter.java @@ -22,6 +22,7 @@ public Tn5250ConditionWaiter(T condition, Tn5250Client client, super(condition, stableTimeoutExecutor, exceptionHandler); client.addEmulatorListener(this); this.client = client; + initialVerificationOfCondition(); } @Override diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/UnlockListener.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/UnlockListener.java index 4f9ee1b0..781a55f0 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/UnlockListener.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/UnlockListener.java @@ -5,40 +5,24 @@ import com.blazemeter.jmeter.rte.protocols.tn5250.Tn5250Client; import java.util.concurrent.ScheduledExecutorService; import net.infordata.em.tn5250.XI5250EmulatorEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * A {@link Tn5250ConditionWaiter} which allows waiting until the terminal is unlocked. */ public class UnlockListener extends Tn5250ConditionWaiter { - private static final Logger LOG = LoggerFactory.getLogger(UnlockListener.class); - private boolean isInputInhibited; - public UnlockListener(SyncWaitCondition condition, Tn5250Client client, ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) { super(condition, client, stableTimeoutExecutor, exceptionHandler); - isInputInhibited = client.isInputInhibited(); - if (!isInputInhibited) { - LOG.debug("Start stable period since input is not inhibited"); - startStablePeriod(); - } } @Override public synchronized void stateChanged(XI5250EmulatorEvent event) { - boolean wasInputInhibited = isInputInhibited; - isInputInhibited = client.isInputInhibited(); - if (isInputInhibited != wasInputInhibited) { - if (isInputInhibited) { - LOG.debug("Cancel stable period since input has been inhibited"); - endStablePeriod(); - } else { - LOG.debug("Start stable period since input is no longer inhibited"); - startStablePeriod(); - } - } + updateConditionState(event.toString()); } + @Override + protected boolean getCurrentConditionState() { + return !client.isInputInhibited(); + } } diff --git a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/VisibleCursorListener.java b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/VisibleCursorListener.java index 8a29cacd..fc8d3a50 100644 --- a/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/VisibleCursorListener.java +++ b/src/main/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/VisibleCursorListener.java @@ -5,8 +5,6 @@ import com.blazemeter.jmeter.rte.protocols.tn5250.Tn5250Client; import java.util.concurrent.ScheduledExecutorService; import net.infordata.em.tn5250.XI5250EmulatorEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * A {@link Tn5250ConditionWaiter} which allows waiting until the cursor shows up on the desired @@ -14,26 +12,18 @@ */ public class VisibleCursorListener extends Tn5250ConditionWaiter { - private static final Logger LOG = LoggerFactory.getLogger(VisibleCursorListener.class); - public VisibleCursorListener(CursorWaitCondition condition, Tn5250Client client, ScheduledExecutorService stableTimeoutExecutor, ExceptionHandler exceptionHandler) { super(condition, client, stableTimeoutExecutor, exceptionHandler); - if (condition.getPosition().equals(client.getCursorPosition().orElse(null))) { - LOG.debug("Cursor is in expected position, now waiting for it to remain for stable period"); - startStablePeriod(); - } } @Override public synchronized void stateChanged(XI5250EmulatorEvent event) { - if (condition.getPosition().equals(client.getCursorPosition().orElse(null))) { - LOG.debug("Cursor is in expected position, now waiting for it to remain for stable period"); - startStablePeriod(); - } else { - LOG.debug("Cursor is not in expected position, canceling any stable period"); - endStablePeriod(); - } + updateConditionState(event.toString()); } + @Override + protected boolean getCurrentConditionState() { + return condition.getPosition().equals(client.getCursorPosition().orElse(null)); + } } diff --git a/src/main/resources/recorder-help.html b/src/main/resources/recorder-help.html index d5bc6f76..33569162 100644 --- a/src/main/resources/recorder-help.html +++ b/src/main/resources/recorder-help.html @@ -118,7 +118,11 @@

Status bar

- Allow using a selected part of the screen as wait condition + Allow using a selected part of the screen as text wait condition + + + + Allow using a selected part of the screen in a Response Assertion

Credits

diff --git a/src/test/java/com/blazemeter/jmeter/rte/core/listeners/ConditionWaiterIT.java b/src/test/java/com/blazemeter/jmeter/rte/core/listeners/ConditionWaiterIT.java index bb17fa98..2f403779 100644 --- a/src/test/java/com/blazemeter/jmeter/rte/core/listeners/ConditionWaiterIT.java +++ b/src/test/java/com/blazemeter/jmeter/rte/core/listeners/ConditionWaiterIT.java @@ -18,14 +18,13 @@ public abstract class ConditionWaiterIT> { protected static final long TIMEOUT_MILLIS = 3000; protected static final long STABLE_MILLIS = 1000; + private static final int INITIAL_DELAY = 500; protected ScheduledExecutorService stableTimeoutExecutor; - private ScheduledExecutorService eventGeneratorExecutor; - @Mock protected ExceptionHandler exceptionHandler; - protected T listener; + private ScheduledExecutorService eventGeneratorExecutor; @Before public void setup() throws Exception { @@ -40,11 +39,10 @@ public void setup() throws Exception { @Test public void shouldUnblockAfterReceivingException() throws Exception { when(exceptionHandler.hasPendingError()).thenReturn(true); - long unlockDelayMillis = 500; Stopwatch waitTime = Stopwatch.createStarted(); - startSingleEventGenerator(unlockDelayMillis, buildOnExceptionEventGenerator()); + startSingleEventGenerator(INITIAL_DELAY, buildOnExceptionEventGenerator()); listener.await(); - assertThat(waitTime.elapsed(TimeUnit.MILLISECONDS)).isGreaterThanOrEqualTo(unlockDelayMillis); + assertThat(waitTime.elapsed(TimeUnit.MILLISECONDS)).isGreaterThanOrEqualTo(INITIAL_DELAY); } private Runnable buildOnExceptionEventGenerator() { @@ -64,7 +62,9 @@ protected void startSingleEventGenerator(long delayMillis, Runnable eventGenerat } protected void startPeriodicEventGenerator(Runnable eventGenerator) { - eventGeneratorExecutor.scheduleAtFixedRate(eventGenerator, 500, 500, TimeUnit.MILLISECONDS); + eventGeneratorExecutor + .scheduleAtFixedRate(eventGenerator, INITIAL_DELAY, Long.divideUnsigned(STABLE_MILLIS, 2), + TimeUnit.MILLISECONDS); } diff --git a/src/test/java/com/blazemeter/jmeter/rte/protocols/RteProtocolClientIT.java b/src/test/java/com/blazemeter/jmeter/rte/protocols/RteProtocolClientIT.java index 66bddfa3..9cd9240b 100644 --- a/src/test/java/com/blazemeter/jmeter/rte/protocols/RteProtocolClientIT.java +++ b/src/test/java/com/blazemeter/jmeter/rte/protocols/RteProtocolClientIT.java @@ -85,4 +85,6 @@ protected Screen buildScreenFromHtmlFile(String fileName) throws IOException { } protected abstract List buildExpectedFields(); + + } diff --git a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/Tn3270ClientIT.java b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/Tn3270ClientIT.java index 638e275e..0ee5ea40 100644 --- a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/Tn3270ClientIT.java +++ b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/Tn3270ClientIT.java @@ -35,6 +35,7 @@ import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.util.JMeterUtils; import org.apache.oro.text.regex.Perl5Compiler; import org.apache.oro.text.regex.Perl5Matcher; import org.junit.Test; @@ -360,4 +361,15 @@ public void shouldValidateSecretFieldsOnScreenWhenBuildScreen() throws Exception assertThat(currentSegments).isEqualTo(buildExpectedFields()); } + + @Test(expected = TimeoutException.class) + public void shouldThrowTimeoutExceptionWhenMatchedScreenChangedBeforeStablePeriod() + throws Exception { + loadFlow("login-with-multiple-flash-screen.yml"); + client.connect(VIRTUAL_SERVER_HOST, server.getPort(), SSLType.NONE, getDefaultTerminalType(), + TIMEOUT_MILLIS); + + client.await(Collections.singletonList(new TextWaitCondition(JMeterUtils.getPattern("AAAAA"), + JMeterUtils.getMatcher(), Area.fromTopLeftBottomRight(1, 1, Position.UNSPECIFIED_INDEX, Position.UNSPECIFIED_INDEX), TIMEOUT_MILLIS, STABLE_TIMEOUT_MILLIS))); + } } diff --git a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/ScreenTextListenerIT.java b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/ScreenTextListenerIT.java index 8ac9b950..fe57d57c 100644 --- a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/ScreenTextListenerIT.java +++ b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/ScreenTextListenerIT.java @@ -88,8 +88,8 @@ public void shouldThrowTimeoutExceptionWhenReceivedScreenNotMatchingRegexInArea( listener.await(); } - @Test(expected = TimeoutException.class) - public void shouldThrowTimeoutExceptionWhenReceivedExpectedScreenButKeepGettingStateChanges() + @Test + public void shouldUnlockWhenReceivingMultipleStateChangesButScreenDoesNotChange() throws Exception { setupScreenWithText(EXPECTED_SCREEN); buildScreenStateChangeGenerator().run(); @@ -97,8 +97,8 @@ public void shouldThrowTimeoutExceptionWhenReceivedExpectedScreenButKeepGettingS listener.await(); } - @Test(expected = TimeoutException.class) - public void shouldThrowTimeoutExceptionWhenReceivedExpectedScreenButKeepGettingScreens() + @Test + public void shouldUnlockWhenReceivingMultipleExpectedScreen() throws Exception { setupScreenWithText(EXPECTED_SCREEN); startPeriodicEventGenerator(buildScreenStateChangeGenerator()); diff --git a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/UnlockListenerIT.java b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/UnlockListenerIT.java index b0293749..549847da 100644 --- a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/UnlockListenerIT.java +++ b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn3270/listeners/UnlockListenerIT.java @@ -30,6 +30,7 @@ protected Tn3270ConditionWaiter buildConditionWaiter() { @Test public void shouldUnblockAfterReceivingUnlockStateChange() throws Exception { + when(client.isInputInhibited()).thenReturn(false); KeyboardStatusChangedEvent keyboardEvent = new KeyboardStatusChangedEvent(false, false, ""); long unlockDelayMillis = 500; Stopwatch waitTime = Stopwatch.createStarted(); diff --git a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn5250/Tn5250ClientIT.java b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn5250/Tn5250ClientIT.java index 089837a7..8d7566cc 100644 --- a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn5250/Tn5250ClientIT.java +++ b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn5250/Tn5250ClientIT.java @@ -34,6 +34,7 @@ import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.util.JMeterUtils; import org.apache.oro.text.regex.Perl5Compiler; import org.apache.oro.text.regex.Perl5Matcher; import org.junit.Test; @@ -329,4 +330,13 @@ public void shouldValidateSecretFieldsOnScreenWhenBuildScreen() throws Exception Collectors.toList()); assertThat(currentFields).isEqualTo(buildExpectedFields()); } + + @Test(expected = TimeoutException.class) + public void shouldFailWhenMatchedScreenChangedBeforeStablePeriod() throws Exception { + loadFlow("login-with-multiple-flash-screen.yml"); + client.connect(VIRTUAL_SERVER_HOST, server.getPort(), SSLType.NONE, getDefaultTerminalType(), + TIMEOUT_MILLIS); + client.await(Collections.singletonList(new TextWaitCondition(JMeterUtils.getPattern("Sign on"), + JMeterUtils.getMatcher(), Area.fromTopLeftBottomRight(1,1,24,80), 10000, 1000))); + } } diff --git a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/ScreenTextListenerIT.java b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/ScreenTextListenerIT.java index ee591b72..0079c2f6 100644 --- a/src/test/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/ScreenTextListenerIT.java +++ b/src/test/java/com/blazemeter/jmeter/rte/protocols/tn5250/listeners/ScreenTextListenerIT.java @@ -85,17 +85,8 @@ public void shouldThrowTimeoutExceptionWhenReceivedScreenNotMatchingRegexInArea( listener.await(); } - @Test(expected = TimeoutException.class) - public void shouldThrowTimeoutExceptionWhenReceivedExpectedScreenButKeepGettingStateChanges() - throws Exception { - setupScreenWithText(EXPECTED_SCREEN); - buildNewPanelGenerator().run(); - startPeriodicEventGenerator(buildStateChangeGenerator()); - listener.await(); - } - - @Test(expected = TimeoutException.class) - public void shouldThrowTimeoutExceptionWhenReceivedExpectedScreenButKeepGettingScreens() + @Test + public void shouldUnlockWhenReceivingMultipleExpectedScreen() throws Exception { setupScreenWithText(EXPECTED_SCREEN); startPeriodicEventGenerator(buildNewPanelGenerator()); diff --git a/src/test/resources/com/blazemeter/jmeter/rte/protocols/tn3270/login-flash-screen.html b/src/test/resources/com/blazemeter/jmeter/rte/protocols/tn3270/login-flash-screen.html new file mode 100644 index 00000000..a41150a6 --- /dev/null +++ b/src/test/resources/com/blazemeter/jmeter/rte/protocols/tn3270/login-flash-screen.html @@ -0,0 +1,27 @@ + +
 
THIS IS A "TEST" LOGIN -
 
                                                      
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                                
+                                                                               
 
+
+ diff --git a/src/test/resources/com/blazemeter/jmeter/rte/protocols/tn3270/login-with-multiple-flash-screen.yml b/src/test/resources/com/blazemeter/jmeter/rte/protocols/tn3270/login-with-multiple-flash-screen.yml new file mode 100644 index 00000000..1c6de866 --- /dev/null +++ b/src/test/resources/com/blazemeter/jmeter/rte/protocols/tn3270/login-with-multiple-flash-screen.yml @@ -0,0 +1,58 @@ +# Do TN3270E +- !server {data: FFFD28, delayMillis: 342} +# Won't TN3270E +- !client {data: FFFC28} +# Do Terminal Type +- !server {data: FFFD18, delayMillis: 196} +# Will Terminal Type +- !client {data: FFFB18} +# Send your Terminal Type +- !server {data: 00FFFA1801FFF0, delayMillis: 196} +# terminal-type: IBM-3278-2-E +- !client {data: FFFA180049424D2D333237382D322D45FFF0} +# Do End of Record +- !server {data: FFFD19, delayMillis: 197} +# Will End of Record +- !server {data: FFFB19} +# Will End of Record +- !client {data: FFFB19} +# Do Binary Transmission + Will Binary Transmission +- !server {data: FFFD00FFFB00, delayMillis: 198} +# Do End of Record +- !client {data: FFFD19} +# Will Binary Transmission + Do Binary Transmission +- !client {data: FFFB00FFFD00} +# restore keyboard + user input screen + cursor=2,1 +- !server {data: 05C1115D7F1D401140401DC8C1C1C1C1C1C1C1C1C140C5D5E3C5D940E4E2C5D9C9C440601D4011C15013FFEF} +- !server {data: 01C2FFEF, delayMillis: 150} +- !server {data: 05C1115D7F1D401140401DC8E3C8C9E240C9E240C1407FE3C5E2E37F40D3D6C7C9D5401D4011C15013FFEF} +- !server {data: 01C2FFEF} +# user: testusr + enter +- !client {data: 7D406111405AA385A2A3A4A299FFEF} +# reset + cursor=1,1 +- !server {data: 05C1115D7F1D4011404013FFEF, delayMillis: 289} +# restore keyboard +- !server {data: 05D7FFEF} +# restore keyboard + password input screen + cursor=8,20 +- !server {data: 05C31140403C4040401140401DE86060606060606060606060606060606060606060606060606060606060606040E3E2D661C540D3D6C7D6D54060606060606060606060606060606060606060606060606060606060606060606060606011C1501DE8404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404011C2601DE84040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040115B601DE8D7C6F161D7C6F1F3407E7E6E40C885939740404040D7C6F361D7C6F1F5407E7E6E40D3968796868640404040D7C1F1407E7E6E40C1A3A38595A389969540404040D7C1F2407E7E6E40D985A28896A6115CF01DE8E896A4409481A840998598A485A2A340A29785838986898340888593974089958696999481A38996954082A8408595A385998995874081407D6F7D408995408195A8408595A399A840868985938411C3F31DE8C595A3859940D3D6C7D6D540978199819485A38599A24082859396A67A11C4E31DE8D9C1C3C640D3D6C7D6D540978199819485A38599A27A11C6D21D6040E4A285998984404040407E7E7E6E11C6E21DE8E3C5E2E3E4E2D9401DF011C8F21D6040D781A2A2A696998440407E7E7E6E11C9C21D4C00000000000000001DF0114DF21D6040C18383A340D5948299407E7E7E6E114EC21DC8F1F0F0F0F0F0F00000000000000000000000000000000000000000000000000000000000000000001DF0114BD21D6040D79996838584A49985407E7E7E6E114BE21DC8D7D9D6C3F0F0F01DF01150D21D6040E289A9854040404040407E7E7E6E1150E21DC8F4F0F9F60000001DF011D2F21D6040D78599869699944040407E7E7E6E11D3C21DC80000001DF0114CC21D6040C79996A49740C9848595A340407E7E7E6E114CD51DC800000000000000001DF011C9E21D6040D585A640D781A2A2A6969984407E7E7E6E11C9F51D4C00000000000000001DF011D7F31DE8C595A38599408195407DE27D408285869699854085818388409697A3899695408485A2899985844082859396A67A1D6011D9C71DE80011D9C91DC8401DF060D596948189931D6011D9D71DE80011D9D91DC8401DF060D5969596A38983851D6011D9E81DE80011D96A1DC8001DF060D985839695958583A31D6011D97A1DE80011D97C1DC8401DF060D6C9C483819984401D6011D5D21D6040C39694948195844040407E7E7E6E11D5E21DC840404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040401DF011C7C21D7C40E28583938182859340404040407E7E7E6E11C7D51D7C40404040404040401DF011C9C313FFEF, + delayMillis: 197} +# password: testpsw + enter +- !client {data: 7DC94A11C9C3A385A2A397A2A640FFEF} +# Server shows stream of data +- !server {data: 05C1115D7F1D4011404013FFEF, delayMillis: 198} +- !server {data: 05C111404013FFEF} +- !server {data: 01C11140403C404000FFEF01401140401DC8E3E2E2F7F0F0F0C940E3C5E2E3E4E2D940D381A2A360E4A2858440F0F540C1979940F1F840F1F67AF1F240E2A8A2A385947EE7C5F4F940C68183899389A3A87EE3E2D61D4011C1501DC8E3E2E2F7F0F0F1C940C396A495A37EF0F0F0F0F340D49684857EC681899340D3968392A38994857ED596958540D58194857EE3C5E2E3C9D5C740E4E2C5D9F11D4011C26013FFEF014011C2601DC8C9D2D1F5F6F4F5F5C940E3C5E2E3E4E2D940D3D6C7D6D540C9D540D7D9D6C7D9C5E2E240C1E340F1F67AF1F77AF4F540D6D540C1D7D9C9D340F56B40F2F0F1F81D4011C3F013FFEF, + delayMillis: 196} +- !server {data: 014011C3F01DC8405C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C1D4011C5401DC8405C4040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040405C1D4011C6501DC8405C4040404040404040E685938396948540A39640E3E2D661C540969540D4E5E240D489958940E2A8A2A3859440404040404040404040404040404040405C1D4011C7601DC8405C40E2D4C6C9C44DE7C5F4F95D4040D1C5E2F2D5D6C4C54DE4E2C9F2F4F9D4C55D4040E3E2D6C1D7D7D34DC1F4F9C9D6E3E2D65D4040404040404040405C1D4011C8F01DC8405C4040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040405C1D40114A4013FFEF, + delayMillis: 2439} +- !server {data: 0140114A401DC8405C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C1D40114B5013FFEF0140114B501DC8401D40114C601DC85CD3D6C7D6D5F3F9F47A40E3E2D661C9E2D7C640E2C5E2E2C9D6D540E3C1C9D3D6D9C9D5C740E2C5D3C5C3E3C9D6D540C9D540D7D9D6C7D9C5E2E25C1D40114DF013FFEF0140114DF01DC85CD5D6E3C57A407DE3C5E2E3E4E2D94BC9E2D7C6D7C1D9D44BC3D3C9E2E37D40D5D6E340C6D6E4D5C46B40C4C5C6C1E4D3E3E240E3C1D2C5D55C1D40114F401DC85CD5D6E3C57A407DE2E8E2F24BC3D3C9E2E34DC9E2D7C6D7C1D9D45D7D40D4C1E840C2C540E4E2C5C440C1E240C140D4D6C4C5D35C1D4011505013FFEF} +- !server {data: 01401150501DC85C40C1D3D3D6C3C1E3C9D6D5E240C6D6D940C9E2D7C640E7E7E7E7E7E740C9D540D7D9D6C7D9C5E2E25C1D4011D16013FFEF, + delayMillis: 299} +- !server {data: 014011D1601DC85CD5D6E3C57A40C4C5C6C1E4D3E340D3C9C2D9C1D9E840E2E3D9E4C3E3E4D9C540E2C5D3C5C3E3C5C47A40404DE2D4D7C540D9C1C3C640C9D7C3E240E2C4E2C65D5C1D4011D2F013FFEF014011D2F01DC85CD5D6E3C57A40E2E8E2D7D9D6C340C8C1E240C2C5C5D540D9C560C1D3D3D6C3C1E3C5C45C1D4011D44013FFEF014011D4401DC85CD5D6E3C57A40E2E8E2C8C5D3D740C8C1E240C2C5C5D540D9C560C1D3D3D6C3C1E3C5C45C1D4011D55013FFEF, + delayMillis: 275} +- !server {data: 014011D5501DC8401D4011D6601DC840C5D5E3C5D9407DC9E2D7E2E3C1D9E37D40C6D6D940C3D6D4D7E4E3C5D940C1E2E2D6C3C9C1E3C5E240D4C1E2E3C5D940D7C1D5C5D31D4011D7F01DC840404040D6D9407DD7C4C67D404040404040C6D6D940D4E5E240D7D9D6C7D9C1D440C4C5E5C5D3D6D7D4C5D5E340C6C1C3C9D3C9E3E8404DD7C4C65D1D4011D9401DC8401D40115A5013FFEF, + delayMillis: 568} +- !server {data: 0140115A501DC8115AD1D9C5C1C4E8401D4013115B6013FFEF, delayMillis: 255} +- !server {data: 01C2FFEF} +- !server {data: FFFF} +- !client {data: FFFF} \ No newline at end of file diff --git a/src/test/resources/com/blazemeter/jmeter/rte/protocols/tn5250/login-with-multiple-flash-screen.yml b/src/test/resources/com/blazemeter/jmeter/rte/protocols/tn5250/login-with-multiple-flash-screen.yml new file mode 100644 index 00000000..f8bee4ca --- /dev/null +++ b/src/test/resources/com/blazemeter/jmeter/rte/protocols/tn5250/login-with-multiple-flash-screen.yml @@ -0,0 +1,27 @@ +- !server {data: FFFD27FFFD18, delayMillis: 2195} +- !client {data: FFFC27} +- !client {data: FFFB18} +- !server {data: FFFA1801FFF0} +- !client {data: FFFA1800} +- !client {data: 49424D2D333137392D32FFF0} +- !server {data: FFFD19FFFB19FFFD00FFFB00} +- !client {data: FFFB19} +- !client {data: FFFD19FFFB00FFFD00} +# Server welcome screen +- !server {data: 01F512A00000040000030440041100180107000000190000001101162240404040404040404040404040E289879540D695020137402011022F20E2A8A2A3859440404B404B404B404B404B407A2011024520E3C5E2E3E2E8E2402011032F20E2A482A2A8A2A38594404B404B404B404B407A2011034520E3C5E2E3E240404040402011042F20C489A2979381A8404B404B404B404B404B407A2011044520E3C5E2E3C4C9E2D7D3E82011061020E4A2859940404B404B404B404B404B404B404B404B404B404B404B404B404B404B201106341D402024000A000000000000000000001107341D402027000A11081020D799968799819461979996838584A49985404B404B404B404B404B404B404B404B201108341D400024000A0000000000000000000011091020D48595A440404B404B404B404B404B404B404B404B404B404B404B404B404B404B201109341D400024000A00000000000000000000110A1020C3A499998595A340938982998199A8404B404B404B404B404B404B404B404B404B20110A341D400024000A0000000000000000000011071020D781A2A2A696998440404B404B404B404B404B404B404B404B404B404B404B404B201107342702073E0011182722404DC35D40C3D6D7E8D9C9C7C8E340C9C2D440C3D6D9D74B40F1F9F8F06B40F2F0F1F34B404040402004520000FFEF, + delayMillis: 500} +- !server {data: 01F512A00000040000030440041100180107000000190000001101162240404040404040404040404040E6859383969485020137402011022F20E2A8A2A3859440404B404B404B404B404B407A2011024520E3C5E2E3E2E8E2402011032F20E2A482A2A8A2A38594404B404B404B404B407A2011034520E3C5E2E3E240404040402011042F20C489A2979381A8404B404B404B404B404B407A2011044520E3C5E2E3C4C9E2D7D3E82011061020E4A2859940404B404B404B404B404B404B404B404B404B404B404B404B404B404B201106341D402024000A000000000000000000001107341D402027000A11081020D799968799819461979996838584A49985404B404B404B404B404B404B404B404B201108341D400024000A0000000000000000000011091020D48595A440404B404B404B404B404B404B404B404B404B404B404B404B404B404B201109341D400024000A00000000000000000000110A1020C3A499998595A340938982998199A8404B404B404B404B404B404B404B404B404B20110A341D400024000A0000000000000000000011071020D781A2A2A696998440404B404B404B404B404B404B404B404B404B404B404B404B201107342702073E0011182722404DC35D40C3D6D7E8D9C9C7C8E340C9C2D440C3D6D9D74B40F1F9F8F06B40F2F0F1F34B404040402004520000FFEF, + delayMillis: 5} +# User sends user and password +- !client {data: 002112A0000004000000073CF1110635E3C5E2E3E4E2D9110735E3C5E2E3D7E2E6FFEF} +- !server {data: 001112A000000400000304F30005D97000FFEF, delayMillis: 1522} +- !client {data: 004712A0000004000000000088003AD9708006000101000000000000000000000000000000000001F5F2F5F1F0F1F1020000000000000100000000014300000000000000000000FFEF} +- !server {data: 000A12A000000400000CFFEF} +# Server menu screen +- !server {data: 028C12A00000040000030440041100001114061D50002000991101013AD4C1C9D5000000000000200000000000000000000000000000000000000022C9C2D4408940D481899540D48595A420110202274020400000400000274020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020E2A8A2A385947A200020E3C5E2E3E2E8E200201103013AE285938583A34096958540968640A38885408696939396A68995877A2011050620F14B00E4A2859940A381A292A22011060620F24B00D6868689838540A381A292A22011080620F44B00C6899385A26B409389829981998985A26B4081958440869693848599A220110A0620F64B00C3969494A495898381A3899695A220110C0620F84B00D799968293859440888195849389958720110D0620F94B00C489A2979381A8408140948595A420110E0520F1F04B00C9958696999481A389969540C1A2A289A2A38195A3409697A3899695A220110F0520F1F14B00C9C2D4408940C1838385A2A240A381A292A22011110520F9F04B00E2898795409686862011130120E285938583A3899695409699408396949481958420111401207E7E7E6E24111550201116013AC6F37EC5A789A320003AC6F47ED799969497A320003AC6F97ED985A3998985A58520003AC6F1F27EC3819583859320003AC6F1F37EC9958696999481A389969540C1A2A289A2A38195A3201117013AC6F2F37EE285A340899589A389819340948595A420111801224DC35D40C3D6D7E8D9C9C7C8E340C9C2D440C3D6D9D74B40F1F9F8F06B40F2F0F1F34B00000000000000000000000000000000000000000000000000000000000000000000000000000000002200201314070411001C04520000FFEF, + delayMillis: 89} +# User selects tasks menu item +- !client {data: 001112A00000040000001408F1111407F1FFEF} +# Server tasks menu screen +- !server {data: 02F012A00000040000030440041100001114061D50002000991101013AE4E2C5D9000000000000200000000000000000000000000000000000000000000022E4A2859940E381A292A220110202274020400000400000274020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020E2A8A2A385947A200020E3C5E2E3E2E8E200201103013AE285938583A34096958540968640A38885408696939396A68995877A2011050620F14B00C489A2979381A84096994083888195878540A896A499409196822011060620F24B00C489A2979381A8409485A2A2818785A22011070620F34B00E28595844081409485A2A28187852011080620F44B00E2A4829489A34081409196822011090620F54B00E696999240A689A38840A896A49940A29796969385844096A4A397A4A34086899385A220110A0620F64B00E696999240A689A38840A896A499408281A3838840919682A220110B0620F74B00C489A2979381A84096994083888195878540A896A49940938982998199A8409389A2A320110C0620F84B00C3888195878540A896A499409781A2A2A696998420110D0620F94B00C3888195878540A896A49940A4A28599409799968689938520110F0520F6F04B00D496998540A4A2859940A381A292409697A3899695A22011110520F9F04B00E2898795409686862011130120E285938583A3899695409699408396949481958420111401207E7E7E6E24111550201116013AC6F37EC5A789A320003AC6F47ED799969497A320003AC6F97ED985A3998985A58520003AC6F1F27EC3819583859320003AC6F1F37EC9958696999481A389969540C1A2A289A2A38195A3201117013AC6F1F67EE2A8A2A3859440D481899540948595A420111801224DC35D40C3D6D7E8D9C9C7C8E340C9C2D440C3D6D9D74B40F1F9F8F06B40F2F0F1F34B00000000000000000000000000000000000000000000000000000000000000000000000000000000002200201314070411001804520000FFEF, + delayMillis: 2}