diff --git a/Mainframe3270/__init__.py b/Mainframe3270/__init__.py
index 9aae65b..384816a 100644
--- a/Mainframe3270/__init__.py
+++ b/Mainframe3270/__init__.py
@@ -8,9 +8,14 @@
from robot.utils import ConnectionCache
from robotlibcore import DynamicCore
-from Mainframe3270.keywords import (AssertionKeywords, CommandKeywords,
- ConnectionKeywords, ReadWriteKeywords,
- ScreenshotKeywords, WaitAndTimeoutKeywords)
+from Mainframe3270.keywords import (
+ AssertionKeywords,
+ CommandKeywords,
+ ConnectionKeywords,
+ ReadWriteKeywords,
+ ScreenshotKeywords,
+ WaitAndTimeoutKeywords,
+)
from Mainframe3270.py3270 import Emulator
from Mainframe3270.utils import convert_timeout
from Mainframe3270.version import VERSION
@@ -68,6 +73,46 @@ class Mainframe3270(DynamicCore):
| Switch Connection second # switchting the ocnnection using the alias
| Page Should Contain String Second String
| [Teardown] Close All Connections
+
+ = Changing the emulator model (experimental) =
+
+ By default, the library uses the emulator model 2, which is 24 rows by 80 columns.
+ You can, however, change the model globally when `importing` the library with the `model` argument
+ set to the model of your choice.
+
+ The basic models are 2, 3, 4, and 5. These models differ in their screen size as illustrated in this table:
+
+ | *3270 Model* | *Rows* | *Columns* |
+ | 2 | 24 | 80 |
+ | 3 | 32 | 80 |
+ | 4 | 43 | 80 |
+ | 5 | 27 | 132 |
+
+
+ They can be combined with the 3278 (monochrome green-screen) or 3279 (color) prefix, e.g. 3278-2 or 3279-2.
+
+ In addition to that, there is a -E suffix that indicates support for the [https://x3270.miraheze.org/wiki/3270_data_stream_protocol#extended|x3270 extended data stream].
+
+ You can find more information on emulator models on the [https://x3270.miraheze.org/wiki/3270_models|x3270 wiki].
+
+ In addition to setting the model globally, you can also set the model on the individual emulator basis by providing the model arguments to the `Open Connection`
+ or `Open Connection From Session File` keywords.
+
+ Here is an example for setting the emulator in the Open Connection keyword:
+
+ | Open Connection pub400.com extra_args=["-xrm", "*model: 4"]
+
+ And this is how you would set the emulator model in the Open Connection From Session File keyword:
+
+ | Open Connection From Session File /path/to/session/file
+
+ Where the content of the session file would be
+
+ | *hostname: pub400.com
+ | *model: 4
+
+
+ Note that this is an experimental feature, so not all models might work as expected.
"""
ROBOT_LIBRARY_SCOPE = "TEST SUITE"
@@ -81,6 +126,7 @@ def __init__(
wait_time_after_write: timedelta = timedelta(seconds=0),
img_folder: str = ".",
run_on_failure_keyword: str = "Take Screenshot",
+ model: str = "2",
) -> None:
"""
By default the emulator visibility is set to visible=True.
@@ -107,6 +153,7 @@ def __init__(
self.img_folder = img_folder
self._running_on_failure_keyword = False
self.register_run_on_failure_keyword(run_on_failure_keyword)
+ self.model = model
self.cache = ConnectionCache()
# When generating the library documentation with libdoc, BuiltIn.get_variable_value throws
# a RobotNotRunningError. Therefore, we catch it here to be able to generate the documentation.
@@ -159,8 +206,6 @@ def run_on_failure(self) -> None:
self._running_on_failure_keyword = True
BuiltIn().run_keyword(self.run_on_failure_keyword)
except Exception as error:
- logger.warn(
- f"Keyword '{self.run_on_failure_keyword}' could not be run on failure: {error}"
- )
+ logger.warn(f"Keyword '{self.run_on_failure_keyword}' could not be run on failure: {error}")
finally:
self._running_on_failure_keyword = False
diff --git a/Mainframe3270/keywords/__init__.py b/Mainframe3270/keywords/__init__.py
index abcccbf..83ce265 100644
--- a/Mainframe3270/keywords/__init__.py
+++ b/Mainframe3270/keywords/__init__.py
@@ -3,5 +3,4 @@
from Mainframe3270.keywords.connection import ConnectionKeywords # noqa: F401
from Mainframe3270.keywords.read_write import ReadWriteKeywords # noqa: F401
from Mainframe3270.keywords.screenshot import ScreenshotKeywords # noqa: F401
-from Mainframe3270.keywords.wait_and_timeout import \
- WaitAndTimeoutKeywords # noqa: F401
+from Mainframe3270.keywords.wait_and_timeout import WaitAndTimeoutKeywords # noqa: F401
diff --git a/Mainframe3270/keywords/assertions.py b/Mainframe3270/keywords/assertions.py
index 42915aa..fda1937 100644
--- a/Mainframe3270/keywords/assertions.py
+++ b/Mainframe3270/keywords/assertions.py
@@ -1,8 +1,10 @@
import re
from typing import List, Optional
+
from robot.api import logger
from robot.api.deco import keyword
from robot.utils import Matcher
+
from Mainframe3270.librarycomponent import LibraryComponent
@@ -109,9 +111,7 @@ def page_should_not_contain_any_string(
| Page Should Not Contain Any Strings | ${list_of_string} | ignore_case=True |
| Page Should Not Contain Any Strings | ${list_of_string} | error_message=New error message |
"""
- self._compare_all_list_with_screen_text(
- list_string, ignore_case, error_message, should_match=False
- )
+ self._compare_all_list_with_screen_text(list_string, ignore_case, error_message, should_match=False)
@keyword("Page Should Contain All Strings")
def page_should_contain_all_strings(
@@ -132,9 +132,7 @@ def page_should_contain_all_strings(
| Page Should Contain All Strings | ${list_of_string} | ignore_case=True |
| Page Should Contain All Strings | ${list_of_string} | error_message=New error message |
"""
- self._compare_all_list_with_screen_text(
- list_string, ignore_case, error_message, should_match=True
- )
+ self._compare_all_list_with_screen_text(list_string, ignore_case, error_message, should_match=True)
@keyword("Page Should Not Contain All Strings")
def page_should_not_contain_all_strings(
diff --git a/Mainframe3270/keywords/commands.py b/Mainframe3270/keywords/commands.py
index 3faef3b..cdcfdd1 100644
--- a/Mainframe3270/keywords/commands.py
+++ b/Mainframe3270/keywords/commands.py
@@ -1,6 +1,8 @@
import time
from typing import Optional
+
from robot.api.deco import keyword
+
from Mainframe3270.librarycomponent import LibraryComponent
diff --git a/Mainframe3270/keywords/connection.py b/Mainframe3270/keywords/connection.py
index 0b7da56..f15ab92 100644
--- a/Mainframe3270/keywords/connection.py
+++ b/Mainframe3270/keywords/connection.py
@@ -3,8 +3,10 @@
import shlex
from os import name as os_name
from typing import List, Optional, Union
+
from robot.api import logger
from robot.api.deco import keyword
+
from Mainframe3270.librarycomponent import LibraryComponent
from Mainframe3270.py3270 import Emulator
@@ -62,7 +64,8 @@ def open_connection(
| Open Connection | Hostname | alias=my_first_connection |
"""
extra_args = self._process_args(extra_args)
- connection = Emulator(self.visible, self.timeout, extra_args)
+ model = self._get_model_from_list_or_file(extra_args)
+ connection = Emulator(self.visible, self.timeout, extra_args, model or self.model)
host_string = f"{LU}@{host}" if LU else host
if self._port_in_extra_args(extra_args):
if port != 23:
@@ -93,6 +96,17 @@ def _process_args(args) -> list:
processed_args.append(arg)
return processed_args
+ @staticmethod
+ def _get_model_from_list_or_file(list_or_file):
+ pattern = re.compile(r"[wcxs3270.*]+model:\s*([327892345E-]+)")
+ match = None
+ if isinstance(list_or_file, list):
+ match = pattern.findall(str(list_or_file))
+ elif isinstance(list_or_file, os.PathLike) or isinstance(list_or_file, str):
+ with open(list_or_file) as file:
+ match = pattern.findall(file.read())
+ return None if not match else match[-1]
+
@staticmethod
def _port_in_extra_args(args) -> bool:
if not args:
@@ -103,9 +117,7 @@ def _port_in_extra_args(args) -> bool:
return False
@keyword("Open Connection From Session File")
- def open_connection_from_session_file(
- self, session_file: os.PathLike, alias: Optional[str] = None
- ) -> int:
+ def open_connection_from_session_file(self, session_file: os.PathLike, alias: Optional[str] = None) -> int:
"""Create a connection to an IBM3270 mainframe
using a [https://x3270.miraheze.org/wiki/Session_file|session file].
@@ -134,12 +146,12 @@ def open_connection_from_session_file(
"""
self._check_session_file_extension(session_file)
self._check_contains_hostname(session_file)
- self._check_model(session_file)
+ model = self._get_model_from_list_or_file(session_file)
if os_name == "nt" and self.visible:
- connection = Emulator(self.visible, self.timeout)
+ connection = Emulator(self.visible, self.timeout, model=model or self.model)
connection.connect(str(session_file))
else:
- connection = Emulator(self.visible, self.timeout, [str(session_file)])
+ connection = Emulator(self.visible, self.timeout, [str(session_file)], model or self.model)
return self.cache.register(connection, alias)
def _check_session_file_extension(self, session_file):
@@ -169,23 +181,6 @@ def _check_contains_hostname(session_file):
"wc3270.hostname: myhost.com\n"
)
- @staticmethod
- def _check_model(session_file):
- with open(session_file) as file:
- pattern = re.compile(r"[wcxs3270.*]+model:\s*([327892345E-]+)")
- match = pattern.findall(file.read())
- if not match:
- return
- elif match[-1] == "2":
- return
- else:
- raise ValueError(
- f'Robot-Framework-Mainframe-3270-Library currently only supports model "2", '
- f'the model you specified in your session file was "{match[-1]}". '
- f'Please change it to "2", using either the session wizard if you are on Windows, '
- f'or by editing the model resource like this "*model: 2"'
- )
-
@keyword("Switch Connection")
def switch_connection(self, alias_or_index: Union[str, int]):
"""Switch the current connection to the one identified by index or alias. Indices are returned from
diff --git a/Mainframe3270/keywords/read_write.py b/Mainframe3270/keywords/read_write.py
index 6ff36f6..5ca1b02 100644
--- a/Mainframe3270/keywords/read_write.py
+++ b/Mainframe3270/keywords/read_write.py
@@ -1,6 +1,8 @@
import time
from typing import Any, Optional
+
from robot.api.deco import keyword
+
from Mainframe3270.librarycomponent import LibraryComponent
diff --git a/Mainframe3270/keywords/screenshot.py b/Mainframe3270/keywords/screenshot.py
index d93fe89..295a68f 100644
--- a/Mainframe3270/keywords/screenshot.py
+++ b/Mainframe3270/keywords/screenshot.py
@@ -1,7 +1,9 @@
import os
import time
+
from robot.api import logger
from robot.api.deco import keyword
+
from Mainframe3270.librarycomponent import LibraryComponent
@@ -42,13 +44,10 @@ def take_screenshot(self, height: int = 410, width: int = 670, filename_prefix:
"""
extension = "html"
filename_sufix = round(time.time() * 1000)
- filepath = os.path.join(
- self.img_folder, "%s_%s.%s" % (filename_prefix, filename_sufix, extension)
- )
+ filepath = os.path.join(self.img_folder, "%s_%s.%s" % (filename_prefix, filename_sufix, extension))
self.mf.save_screen(os.path.join(self.output_folder, filepath))
logger.write(
- ''
- % (filepath.replace("\\", "/"), height, width),
+ '' % (filepath.replace("\\", "/"), height, width),
level="INFO",
html=True,
)
diff --git a/Mainframe3270/keywords/wait_and_timeout.py b/Mainframe3270/keywords/wait_and_timeout.py
index ffe1a02..f42eb60 100644
--- a/Mainframe3270/keywords/wait_and_timeout.py
+++ b/Mainframe3270/keywords/wait_and_timeout.py
@@ -1,7 +1,9 @@
import time
from datetime import timedelta
+
from robot.api.deco import keyword
from robot.utils import secs_to_timestr
+
from Mainframe3270.librarycomponent import LibraryComponent
from Mainframe3270.utils import convert_timeout
@@ -75,9 +77,7 @@ def wait_field_detected(self) -> None:
self.mf.wait_for_field()
@keyword("Wait Until String")
- def wait_until_string(
- self, txt: str, timeout: timedelta = timedelta(seconds=5)
- ) -> str:
+ def wait_until_string(self, txt: str, timeout: timedelta = timedelta(seconds=5)) -> str:
"""Wait until a string exists on the mainframe screen to perform the next step. If the string does not appear
in 5 seconds, the keyword will raise an exception. You can define a different timeout.
diff --git a/Mainframe3270/librarycomponent.py b/Mainframe3270/librarycomponent.py
index 0900856..c714f6f 100644
--- a/Mainframe3270/librarycomponent.py
+++ b/Mainframe3270/librarycomponent.py
@@ -1,4 +1,5 @@
from robot.utils import ConnectionCache
+
from Mainframe3270.py3270 import Emulator
@@ -66,3 +67,7 @@ def mf(self) -> Emulator:
@property
def output_folder(self):
return self.library.output_folder
+
+ @property
+ def model(self):
+ return self.library.model
diff --git a/Mainframe3270/py3270.py b/Mainframe3270/py3270.py
index 0d580ba..a7fac1f 100644
--- a/Mainframe3270/py3270.py
+++ b/Mainframe3270/py3270.py
@@ -8,6 +8,8 @@
from contextlib import closing
from os import name as os_name
+from robot.utils import seq2str
+
log = logging.getLogger(__name__)
"""
Python 3+ note: unicode strings should be used when communicating with the Emulator methods.
@@ -78,9 +80,7 @@ def handle_result(self, result):
if result == "ok":
return
if result != "error":
- raise ValueError(
- 'expected "ok" or "error" result, but received: {0}'.format(result)
- )
+ raise ValueError('expected "ok" or "error" result, but received: {0}'.format(result))
msg = b"[no error message]"
if self.data:
@@ -126,9 +126,8 @@ def executable(self):
def args(self):
pass
- def __init__(self, extra_args=None):
- if extra_args:
- self.args = self.__class__.args + extra_args
+ def __init__(self, extra_args=None, model="2"):
+ self.args = self._get_executable_app_args(extra_args, model)
self.sp = None
self.spawn_app()
@@ -155,6 +154,9 @@ def write(self, data):
def readline(self):
return self.sp.stdout.readline()
+ def _get_executable_app_args(self, extra_args, model):
+ return self.__class__.args + ["-xrm", f"*model: {model}"] + (extra_args or [])
+
class x3270App(ExecutableApp):
executable = "x3270"
@@ -163,7 +165,7 @@ class x3270App(ExecutableApp):
# work around that, when AID commands are sent, there is a 350ms delay
# before the command returns. This arg turns that feature off for
# performance reasons.
- args = ["-xrm", "x3270.unlockDelay: False", "-xrm", "x3270.model: 2", "-script"]
+ args = ["-xrm", "x3270.unlockDelay: False", "-script"]
class s3270App(ExecutableApp):
@@ -179,11 +181,10 @@ class NotConnectedException(Exception):
class wc3270App(ExecutableApp):
executable = "wc3270"
# see notes for args in x3270App
- args = ["-xrm", "wc3270.unlockDelay: False", "-xrm", "wc3270.model: 2"]
+ args = ["-xrm", "wc3270.unlockDelay: False"]
- def __init__(self, extra_args=None):
- if extra_args:
- self.args = wc3270App.args + extra_args
+ def __init__(self, extra_args=None, model="2"):
+ self.args = self._get_executable_app_args(extra_args, model)
self.sp = None
self.socket_fh = None
self.script_port = self._get_free_port()
@@ -254,7 +255,49 @@ class Emulator(object):
with it.
"""
- def __init__(self, visible=False, timeout=30, extra_args=None, app=None):
+ _MODEL_TYPES = {
+ "2": "2",
+ "3278-2": "2",
+ "3278-2-E": "2",
+ "3279-2": "2",
+ "3279-2-E": "2",
+ "3": "3",
+ "3278-3": "3",
+ "3278-3-E": "3",
+ "3279-3": "3",
+ "3279-3-E": "3",
+ "4": "4",
+ "3278-4": "4",
+ "3278-4-E": "4",
+ "3279-4": "4",
+ "3279-4-E": "4",
+ "5": "5",
+ "3278-5": "5",
+ "3278-5-E": "5",
+ "3279-5": "5",
+ "3279-5-E": "5",
+ }
+
+ _MODEL_DIMENSIONS = {
+ "2": {
+ "rows": 24,
+ "columns": 80,
+ },
+ "3": {
+ "rows": 32,
+ "columns": 80,
+ },
+ "4": {
+ "rows": 43,
+ "columns": 80,
+ },
+ "5": {
+ "rows": 27,
+ "columns": 132,
+ },
+ }
+
+ def __init__(self, visible=False, timeout=30, extra_args=None, model="2"):
"""
Create an emulator instance
@@ -263,30 +306,32 @@ def __init__(self, visible=False, timeout=30, extra_args=None, app=None):
to x3270.
`extra_args` allows sending parameters to the emulator executable
"""
- self.app = app or self.create_app(visible, extra_args)
+ self.model = model
+ self.model_dimensions = self._set_model_dimensions(model)
+ self.app = self.create_app(visible, extra_args, model)
self.is_terminated = False
self.status = Status(None)
self.timeout = timeout
self.last_host = None
- def __del__(self):
- """
- Since an emulator creates a process (and sometimes a socket handle), it is good practice
- to clean these up when done. Note, not terminating at this point will usually have no
- ill effect - only Python 3+ on Windows had problems in this regard.
- """
- # self.terminate() # The terminate function is no longer needed in python 3.8
- pass
+ def _set_model_dimensions(self, model):
+ try:
+ model_type = Emulator._MODEL_TYPES[model]
+ except KeyError:
+ raise ValueError(
+ f"Model should be one of {seq2str(Emulator._MODEL_TYPES.keys()).replace('and', 'or')}, "
+ f"but was '{model}'."
+ )
+ return Emulator._MODEL_DIMENSIONS[model_type]
- @staticmethod
- def create_app(visible, extra_args):
+ def create_app(self, visible, extra_args, model):
if os_name == "nt":
if visible:
- return wc3270App(extra_args)
- return ws3270App(extra_args)
+ return wc3270App(extra_args, model)
+ return ws3270App(extra_args, model)
if visible:
- return x3270App(extra_args)
- return s3270App(extra_args)
+ return x3270App(extra_args, model)
+ return s3270App(extra_args, model)
def exec_command(self, cmdstr):
"""
@@ -376,9 +421,7 @@ def wait_for_field(self):
self.exec_command("Wait({0}, InputField)".format(self.timeout).encode("utf-8"))
if self.status.keyboard != b"U":
raise KeyboardStateError(
- "keyboard not unlocked, state was: {0}".format(
- self.status.keyboard.decode("utf-8")
- )
+ "keyboard not unlocked, state was: {0}".format(self.status.keyboard.decode("utf-8"))
)
def move_to(self, ypos, xpos):
@@ -435,16 +478,12 @@ def string_get(self, ypos, xpos, length):
terminal.
"""
self._check_limits(ypos, xpos)
- if (xpos + length) > (80 + 1):
- raise Exception(
- "You have exceeded the x-axis limit of the mainframe screen"
- )
+ if (xpos + length) > (self.model_dimensions["columns"] + 1):
+ raise Exception("You have exceeded the x-axis limit of the mainframe screen")
# the screen's coordinates are 1 based, but the command is 0 based
xpos -= 1
ypos -= 1
- cmd = self.exec_command(
- "ascii({0},{1},{2})".format(ypos, xpos, length).encode("utf-8")
- )
+ cmd = self.exec_command("ascii({0},{1},{2})".format(ypos, xpos, length).encode("utf-8"))
# this usage of utf-8 should only return a single line of data
assert len(cmd.data) == 1, cmd.data
return cmd.data[0].decode("unicode_escape")
@@ -453,8 +492,8 @@ def search_string(self, string, ignore_case=False):
"""
Check if a string exists on the mainframe screen and return True or False.
"""
- for ypos in range(24):
- line = self.string_get(ypos + 1, 1, 80)
+ for ypos in range(self.model_dimensions["rows"]):
+ line = self.string_get(ypos + 1, 1, self.model_dimensions["columns"])
if ignore_case:
line = line.lower()
if string in line:
@@ -466,8 +505,8 @@ def read_all_screen(self):
Read all the mainframe screen and return it in a single string.
"""
full_text = ""
- for ypos in range(24):
- full_text += self.string_get(ypos + 1, 1, 80)
+ for ypos in range(self.model_dimensions["rows"]):
+ full_text += self.string_get(ypos + 1, 1, self.model_dimensions["columns"])
return full_text
def delete_field(self):
@@ -501,13 +540,8 @@ def fill_field(self, ypos, xpos, tosend, length):
def save_screen(self, file_path):
self.exec_command("PrintText(html,file,{0})".format(file_path).encode("utf-8"))
- @staticmethod
- def _check_limits(ypos, xpos):
- if ypos > 24:
- raise Exception(
- "You have exceeded the y-axis limit of the mainframe screen"
- )
- if xpos > 80:
- raise Exception(
- "You have exceeded the x-axis limit of the mainframe screen"
- )
+ def _check_limits(self, ypos, xpos):
+ if ypos > self.model_dimensions["rows"]:
+ raise Exception("You have exceeded the y-axis limit of the mainframe screen")
+ if xpos > self.model_dimensions["columns"]:
+ raise Exception("You have exceeded the x-axis limit of the mainframe screen")
diff --git a/Mainframe3270/utils.py b/Mainframe3270/utils.py
index 06bc29d..ccdd9d4 100644
--- a/Mainframe3270/utils.py
+++ b/Mainframe3270/utils.py
@@ -1,4 +1,5 @@
from datetime import timedelta
+
from robot.utils import timestr_to_secs
diff --git a/atest/HelperLibrary.py b/atest/HelperLibrary.py
new file mode 100644
index 0000000..ebca38d
--- /dev/null
+++ b/atest/HelperLibrary.py
@@ -0,0 +1,43 @@
+import os
+import time
+
+from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError
+
+
+class HelperLibrary:
+ ROBOT_LISTENER_API_VERSION = 2
+
+ def __init__(self):
+ self.built_in = BuiltIn()
+ self.ROBOT_LIBRARY_LISTENER = self
+ try:
+ self.library = self.built_in.get_library_instance("Mainframe3270")
+ except RobotNotRunningError:
+ pass
+
+ def create_session_file(self, *content_lines):
+ extensions = {
+ ("nt", True): "wc3270",
+ ("nt", False): "ws3270",
+ ("posix", True): "x3270",
+ ("posix", False): "s3270",
+ }
+ extension = extensions.get((os.name, self.library.visible))
+ session_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources", f"session.{extension}")
+ with open(session_file, "w", encoding="utf-8") as file:
+ for line in content_lines:
+ file.write(line + "\n")
+ return session_file
+
+ def emulator_model_should_be(self, model):
+ error_message = f'Emulator model should have been "{model}", but was "{self.library.mf.model}"'
+ self.built_in.should_be_equal_as_strings(model, self.library.mf.model, error_message, False)
+
+ def _end_keyword(self, name, attributes):
+ if attributes["kwname"] in [
+ "Open Connection",
+ "Open Connection From Session File",
+ "Close Connection",
+ "Close All Connections",
+ ]:
+ time.sleep(1.5)
diff --git a/atest/connection.robot b/atest/connection.robot
index 49e7787..a1ffca0 100644
--- a/atest/connection.robot
+++ b/atest/connection.robot
@@ -2,6 +2,7 @@
Resource pub400_variables.robot
Library OperatingSystem
Library ../Mainframe3270/ ${VISIBLE}
+Library HelperLibrary.py
Test Teardown Test Teardown
@@ -16,27 +17,23 @@ ${TRACE_FILE} ${CURDIR}/x3270.trace
Test Connection With Extra Args List
${extra_args}= Create List -port 992 -trace -tracefile ${TRACE_FILE}
Open Connection L:${HOST} extra_args=${extra_args}
- Sleep 0.5 s
File Should Exist ${TRACE_FILE}
Test Connection With Argfile
Open Connection ${HOST} extra_args=${ARGFILE}
- Sleep 0.5 s
File Should Exist ${TRACE_FILE}
Test Connection From Session File
- ${session_file}= Create Session File
+ ${session_file}= Create Session File *hostname: L:pub400.com *port: 992
Open Connection From Session File ${SESSION_FILE}
Wait Field Detected
Page Should Contain String ${WELCOME}
Test Concurrent Connections
Open Connection ${HOST} alias=first
- Sleep 0.5 s
Write Bare ABCD
Page Should Contain String ABCD
Open Connection ${HOST} alias=second
- Sleep 0.5 s
Write Bare DEFG
Page Should Contain String DEFG
Page Should Not Contain String ABCD
@@ -47,22 +44,6 @@ Test Concurrent Connections
*** Keywords ***
-Create Session File
- ${os_name}= Evaluate os.name
- IF '${os_name}' == 'nt' and ${VISIBLE}
- ${session_file}= Set Variable ${CURDIR}/resources/session.wc3270
- ELSE IF '${os_name}' == 'nt' and not ${VISIBLE}
- ${session_file}= Set Variable ${CURDIR}/resources/session.ws3270
- ELSE IF '${os_name}' == 'posix' and ${VISIBLE}
- ${session_file}= Set Variable ${CURDIR}/resources/session.x3270
- ELSE IF '${os_name}' == 'posix' and not ${VISIBLE}
- ${session_file}= Set Variable ${CURDIR}/resources/session.s3270
- END
- Copy File ${SESSION_TEMPLATE} ${session_file}
- # Using legacy [Return] for older RF versions
- [Return] ${session_file}
-
Test Teardown
Run Keyword And Ignore Error Close Connection
- Sleep 1 second
Remove File ${TRACE_FILE}
diff --git a/atest/mainframe.robot b/atest/mainframe.robot
index 7fb279c..6e19e02 100644
--- a/atest/mainframe.robot
+++ b/atest/mainframe.robot
@@ -1,9 +1,10 @@
*** Settings ***
Documentation These tests verify that all keywords are working correctly and displaying the expected exception messages.
-Library ../Mainframe3270/ run_on_failure_keyword=None
Library OperatingSystem
Library String
+Library ../Mainframe3270/ run_on_failure_keyword=None
+Library HelperLibrary.py
Resource pub400_variables.robot
Suite Setup Suite Setup
@@ -80,12 +81,12 @@ Exception Test Page Should Contain Match
Verify Pattern Not Found Page Should Contain Match ${STRING_NON_EXISTENT} ignore_case=${True}
Exception Test Page Should Contain String X Times
- Verify String Does Not Appear X Times Page Should Contain String X Times ${TEXT_TO_COUNT} 1 3
+ Verify String Does Not Appear X Times Page Should Contain String X Times ${TEXT_TO_COUNT} 2 1
Verify String Does Not Appear X Times
... Page Should Contain String X Times
... ${TEXT_TO_COUNT_WRONG_CASE}
- ... 1
- ... 5
+ ... 3
+ ... 2
... ignore_case=${True}
Exception Test Page Should Match Regex
@@ -131,8 +132,8 @@ Test Page Should Contain Match
Page Should Contain Match ${TEXT_MATCH_WRONG_CASE} ignore_case=${True}
Test Page Should Contain String X Times
- Page Should Contain String X Times ${TEXT_TO_COUNT} 3
- Page Should Contain String X Times ${TEXT_TO_COUNT_WRONG_CASE} 5 ignore_case=${True}
+ Page Should Contain String X Times ${TEXT_TO_COUNT} 1
+ Page Should Contain String X Times ${TEXT_TO_COUNT_WRONG_CASE} 2 ignore_case=${True}
Test Page Should Match Regex
Page Should Match Regex ${VALID_REGEX}
@@ -233,11 +234,9 @@ Suite Setup
Set Screenshot Folder ${FOLDER}
Change Wait Time 0.4
Change Wait Time After Write 0.4
- Sleep 3s
Suite Teardown
Run Keyword And Ignore Error Close Connection
- Sleep 1s
Verify String Not Found
[Arguments] ${keyword} ${string} ${ignore_case}=${False}
@@ -280,8 +279,8 @@ Verify String Does Not Appear X Times
Run Keyword And Expect Error
... ${expected_error}
... ${keyword}
- ... ${TEXT_TO_COUNT}
- ... 1
+ ... ${string}
+ ... ${wrong_number_of_times}
... ignore_case=${ignore_case}
Verify String Found
diff --git a/atest/models/model_on_import.robot b/atest/models/model_on_import.robot
new file mode 100644
index 0000000..24d456c
--- /dev/null
+++ b/atest/models/model_on_import.robot
@@ -0,0 +1,12 @@
+*** Settings ***
+Library ../../Mainframe3270/ model=4
+Library ../HelperLibrary.py
+Resource ../pub400_variables.robot
+
+Test Teardown Run Keyword And Ignore Error Close Connection
+
+
+*** Test Cases ***
+Should Use Model From Import
+ Open Connection ${HOST}
+ Emulator Model Should Be 4
diff --git a/atest/models/models.robot b/atest/models/models.robot
new file mode 100644
index 0000000..544f663
--- /dev/null
+++ b/atest/models/models.robot
@@ -0,0 +1,28 @@
+*** Settings ***
+Library ../../Mainframe3270/
+Library ../HelperLibrary.py
+Resource ../pub400_variables.robot
+
+Test Teardown Run Keyword And Ignore Error Close All Connections
+
+
+*** Test Cases ***
+Model Should Default To 2
+ Open Connection ${HOST}
+ Emulator Model Should Be 2
+
+Open Connection Can Override Model
+ Open Connection ${HOST} extra_args=["-xrm", "*model: 4"]
+ Emulator Model Should Be 4
+
+Open Connection From Session File Can Override Model
+ ${session_file}= Create Session File *hostname: ${HOST} *model: 5
+ Open Connection From Session File ${session_file}
+ Emulator Model Should Be 5
+
+Can Use Different Models In Different Sessions
+ Open Connection ${HOST} extra_args=["-xrm", "*model: 5"]
+ Emulator Model Should Be 5
+ Sleep 0.5 s
+ Open Connection ${HOST} extra_args=["-xrm", "*model: 4"]
+ Emulator Model Should Be 4
diff --git a/atest/pub400_variables.robot b/atest/pub400_variables.robot
index 664ca73..1e08469 100644
--- a/atest/pub400_variables.robot
+++ b/atest/pub400_variables.robot
@@ -11,7 +11,7 @@ ${WELCOME_TITLE} Welcome to PUB400.COM * your
${MAIN_MENU} IBM i Main Menu
${USER_TASK} User Tasks
${TEXT_MATCH} *PUB???.COM*
-${TEXT_TO_COUNT} PUB400
+${TEXT_TO_COUNT} Server
${TEXT_NOT_MATCH} *PUB???400.COM*
# Texts after write
${TEXT_AFTER_DELETE_CHAR} EST _ëçá
@@ -21,7 +21,7 @@ ${TEXT_AFTER_MOVE_NEXT_FIELD} ${SPACE * 4}
${WELCOME_TITLE_WRONG_CASE} WELCOME TO PUB400.COM * YOUR PUBLIC IBM I SERVER
${WELCOME_WRONG_CASE} WELCOME TO PUB400.COM
${TEXT_MATCH_WRONG_CASE} *pub???.com*
-${TEXT_TO_COUNT_WRONG_CASE} pub400
+${TEXT_TO_COUNT_WRONG_CASE} server
${TEXT_NOT_MATCH_WRONG_CASE} *pub???400.com*
# Regex
${VALID_REGEX} PUB\\d{3}
diff --git a/atest/resources/session.template b/atest/resources/session.template
deleted file mode 100644
index 515e3c5..0000000
--- a/atest/resources/session.template
+++ /dev/null
@@ -1,2 +0,0 @@
-*hostname: L:pub400.com
-*port: 992
diff --git a/atest/run_on_failure/custom_keyword_on_import.robot b/atest/run_on_failure/custom_keyword_on_import.robot
index 5467ce4..697fc80 100644
--- a/atest/run_on_failure/custom_keyword_on_import.robot
+++ b/atest/run_on_failure/custom_keyword_on_import.robot
@@ -1,10 +1,11 @@
*** Settings ***
-Library ../../Mainframe3270/ run_on_failure_keyword=Custom Run On Failure Keyword
Library OperatingSystem
+Library ../../Mainframe3270/ run_on_failure_keyword=Custom Run On Failure Keyword
+Library ../HelperLibrary.py
Resource ../pub400_variables.robot
-Suite Setup Open Mainframe
-Suite Teardown Close Mainframe
+Suite Setup Open Connection ${HOST}
+Suite Teardown Run Keyword And Ignore Error Close Connection
*** Variables ***
@@ -19,10 +20,6 @@ Should Run Custom Keyword
*** Keywords ***
-Open Mainframe
- Open Connection ${HOST}
- Sleep 3 seconds
-
Cause Error
Run Keyword And Expect Error
... The string "${STRING_NON_EXISTENT}" was not found
@@ -30,7 +27,3 @@ Cause Error
Custom Run On Failure Keyword
Create File ${CUSTOM_FILE} An error ocurred
-
-Close Mainframe
- Run Keyword And Ignore Error Close Connection
- Sleep 1 second
diff --git a/atest/run_on_failure/none_on_import.robot b/atest/run_on_failure/none_on_import.robot
index cf3e241..212f6bc 100644
--- a/atest/run_on_failure/none_on_import.robot
+++ b/atest/run_on_failure/none_on_import.robot
@@ -3,8 +3,8 @@ Library ../../Mainframe3270/ run_on_failure_keyword=None
Library OperatingSystem
Resource ../pub400_variables.robot
-Suite Setup Open Mainframe
-Suite Teardown Close Mainframe
+Suite Setup Open Connection ${HOST}
+Suite Teardown Run Keyword And Ignore Error Close Connection
*** Test Cases ***
@@ -14,15 +14,7 @@ None Should Run On Failure
*** Keywords ***
-Open Mainframe
- Open Connection ${HOST}
- Sleep 3 seconds
-
Cause Error
Run Keyword And Expect Error
... The string "${STRING_NON_EXISTENT}" was not found
... Page Should Contain String ${STRING_NON_EXISTENT}
-
-Close Mainframe
- Run Keyword And Ignore Error Close Connection
- Sleep 1 second
diff --git a/atest/run_on_failure/run_on_failure.robot b/atest/run_on_failure/run_on_failure.robot
index 6c97af5..381bc0a 100644
--- a/atest/run_on_failure/run_on_failure.robot
+++ b/atest/run_on_failure/run_on_failure.robot
@@ -3,8 +3,8 @@ Library ../../Mainframe3270/ img_folder=${CURDIR}
Library OperatingSystem
Resource ../pub400_variables.robot
-Suite Setup Open Mainframe
-Suite Teardown Close Mainframe
+Suite Setup Open Connection ${HOST}
+Suite Teardown Run Keyword And Ignore Error Close Connection
*** Variables ***
@@ -30,10 +30,6 @@ Register None To Run On Failure
*** Keywords ***
-Open Mainframe
- Open Connection ${HOST}
- Sleep 3 seconds
-
Cause Error
Run Keyword And Expect Error
... The string "${STRING_NON_EXISTENT}" was not found
@@ -41,7 +37,3 @@ Cause Error
Custom Run On Failure Keyword
Create File ${CUSTOM_FILE} An error ocurred
-
-Close Mainframe
- Run Keyword And Ignore Error Close Connection
- Sleep 1 second
diff --git a/mypy.ini b/mypy.ini
deleted file mode 100644
index 976ba02..0000000
--- a/mypy.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[mypy]
-ignore_missing_imports = True
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..7b3be37
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,15 @@
+[tool.black]
+line-length = 120
+target = ['py37']
+
+[tool.isort]
+profile = 'black'
+line_length = 120
+
+[tool.mypy]
+ignore_missing_imports = true
+
+[tool.robotidy]
+configure = [
+ "ReplaceReturns:enabled=False"
+]
diff --git a/tasks.py b/tasks.py
index 28a3bf8..a03a6c3 100644
--- a/tasks.py
+++ b/tasks.py
@@ -4,16 +4,18 @@
@task
def lint_python(c):
"""Perform python code formatting with black, isort and flake8."""
- c.run("black ./setup.py ./tasks.py Mainframe3270/ utest/")
- c.run("isort ./setup.py ./tasks.py Mainframe3270/ utest/")
- c.run("flake8 ./setup.py ./tasks.py Mainframe3270/ utest/")
+ print("Linting python code with black, isort, flake8 and mypy...")
+ c.run("black ./setup.py ./tasks.py Mainframe3270/ atest/ utest/")
+ c.run("isort ./setup.py ./tasks.py Mainframe3270/ atest/ utest/")
+ c.run("flake8 ./setup.py ./tasks.py Mainframe3270/ atest/ utest/")
c.run("mypy ./setup.py ./tasks.py Mainframe3270/")
@task
def lint_robot(c):
"""Perform robot code formatting with robotidy."""
- c.run("robotidy --configure ReplaceReturns:enabled=False atest/")
+ print("Lingting Robot Framework code with robotidy...")
+ c.run("robotidy atest/")
@task(lint_python, lint_robot)
diff --git a/utest/Mainframe3270/keywords/test_assertions.py b/utest/Mainframe3270/keywords/test_assertions.py
index da7a1e6..e73c6e4 100644
--- a/utest/Mainframe3270/keywords/test_assertions.py
+++ b/utest/Mainframe3270/keywords/test_assertions.py
@@ -14,9 +14,7 @@ def under_test():
return create_test_object_for(AssertionKeywords)
-def test_page_should_contain_string(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_string(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
mocker.patch("robot.api.logger.info")
@@ -25,9 +23,7 @@ def test_page_should_contain_string(
logger.info.assert_called_with('The string "abc" was found')
-def test_page_should_contain_string_ignore_case(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_string_ignore_case(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="aBc")
mocker.patch("robot.api.logger.info")
@@ -36,249 +32,179 @@ def test_page_should_contain_string_ignore_case(
logger.info.assert_called_with('The string "abc" was found')
-def test_page_should_contain_string_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_string_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match='The string "def" was not found'):
under_test.page_should_contain_string("def")
-def test_page_should_contain_string_custom_message(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_string_custom_message(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match="my error message"):
under_test.page_should_contain_string("def", error_message="my error message")
-def test_page_should_not_contain_string(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_string(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
under_test.page_should_not_contain_string("ABC")
-def test_page_should_not_contain_string_ignore_case(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_string_ignore_case(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
under_test.page_should_not_contain_string("def", ignore_case=True)
-def test_page_should_not_contain_string_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_string_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match='The string "ABC" was found'):
under_test.page_should_not_contain_string("ABC", ignore_case=True)
-def test_page_should_not_contain_string_custom_message(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_string_custom_message(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match="my error message"):
- under_test.page_should_not_contain_string(
- "abc", error_message="my error message"
- )
+ under_test.page_should_not_contain_string("abc", error_message="my error message")
-def test_page_should_contain_any_string(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_any_string(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
under_test.page_should_contain_any_string(["abc", "def"])
-def test_page_should_contain_any_string_ignore_case(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_any_string_ignore_case(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
under_test.page_should_contain_any_string(["ABC", "def"], ignore_case=True)
-def test_page_should_contain_any_string_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_any_string_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
- with pytest.raises(
- Exception, match=re.escape("The strings \"['def', 'ghi']\" were not found")
- ):
+ with pytest.raises(Exception, match=re.escape("The strings \"['def', 'ghi']\" were not found")):
under_test.page_should_contain_any_string(["def", "ghi"])
-def test_page_should_contain_any_string_custom_message(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_any_string_custom_message(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match="my error message"):
- under_test.page_should_contain_any_string(
- ["def", "ghi"], error_message="my error message"
- )
+ under_test.page_should_contain_any_string(["def", "ghi"], error_message="my error message")
-def test_page_should_contain_all_strings(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_all_strings(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", side_effect=["abc", "def"])
under_test.page_should_contain_all_strings(["abc", "def"])
-def test_page_should_contain_all_strings_ignore_case(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_all_strings_ignore_case(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", side_effect=["AbC", "DeF"])
under_test.page_should_contain_all_strings(["abc", "def"], ignore_case=True)
-def test_page_should_contain_all_strings_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_all_strings_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value=["def"])
with pytest.raises(Exception, match='The string "ghi" was not found'):
under_test.page_should_contain_all_strings(["def", "ghi"])
-def test_page_should_contain_all_strings_custom_message(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_all_strings_custom_message(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match="my error message"):
- under_test.page_should_contain_all_strings(
- ["abc", "def"], error_message="my error message"
- )
+ under_test.page_should_contain_all_strings(["abc", "def"], error_message="my error message")
-def test_page_should_not_contain_any_string(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_any_string(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
under_test.page_should_not_contain_any_string(["def", "ghi"])
-def test_page_should_not_contain_any_string_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_any_string_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match='The string "abc" was found'):
under_test.page_should_not_contain_any_string(["abc", "def"])
-def test_page_should_not_contain_any_string_ignore_case(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_any_string_ignore_case(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="ABC")
with pytest.raises(Exception, match='The string "abc" was found'):
under_test.page_should_not_contain_any_string(["abc", "def"], ignore_case=True)
-def test_page_should_not_contain_any_string_custom_message(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_any_string_custom_message(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match="my error message"):
- under_test.page_should_not_contain_any_string(
- ["abc", "def"], error_message="my error message"
- )
+ under_test.page_should_not_contain_any_string(["abc", "def"], error_message="my error message")
-def test_page_should_not_contain_all_strings(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_all_strings(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
under_test.page_should_not_contain_all_strings(["def", "ghi"])
-def test_page_should_not_contain_all_strings_ignore_case(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_all_strings_ignore_case(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match='The string "abc" was found'):
under_test.page_should_not_contain_all_strings(["ABC", "def"], ignore_case=True)
-def test_page_should_not_contain_all_strings_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_all_strings_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match='The string "abc" was found'):
under_test.page_should_not_contain_all_strings(["abc", "def"])
-def test_page_should_not_contain_all_strings_custom_message(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_all_strings_custom_message(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match="my error message"):
- under_test.page_should_not_contain_all_strings(
- ["abc", "def"], error_message="my error message"
- )
+ under_test.page_should_not_contain_all_strings(["abc", "def"], error_message="my error message")
-def test_page_should_contain_string_x_times(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_string_x_times(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="a")
under_test.page_should_contain_string_x_times("a", 24)
-def test_page_should_contain_string_x_times_ignore_case(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_string_x_times_ignore_case(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="a")
under_test.page_should_contain_string_x_times("A", 24, ignore_case=True)
-def test_page_should_contain_string_x_times_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_string_x_times_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="a")
- with pytest.raises(
- Exception, match='The string "a" was not found "1" times, it appears "24" times'
- ):
+ with pytest.raises(Exception, match='The string "a" was not found "1" times, it appears "24" times'):
under_test.page_should_contain_string_x_times("a", 1)
- with pytest.raises(
- Exception, match='The string "b" was not found "1" times, it appears "0" times'
- ):
+ with pytest.raises(Exception, match='The string "b" was not found "1" times, it appears "0" times'):
under_test.page_should_contain_string_x_times("b", 1)
-def test_page_should_contain_string_x_times_custom_message(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_string_x_times_custom_message(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="a")
with pytest.raises(Exception, match="my error message"):
- under_test.page_should_contain_string_x_times(
- "b", 1, error_message="my error message"
- )
+ under_test.page_should_contain_string_x_times("b", 1, error_message="my error message")
def test_page_should_match_regex(mocker: MockerFixture, under_test: AssertionKeywords):
@@ -287,108 +213,74 @@ def test_page_should_match_regex(mocker: MockerFixture, under_test: AssertionKey
under_test.page_should_match_regex(r"\w+")
-def test_page_should_match_regex_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_match_regex_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
- with pytest.raises(
- Exception, match=re.escape(r'No matches found for "\d+" pattern')
- ):
+ with pytest.raises(Exception, match=re.escape(r'No matches found for "\d+" pattern')):
under_test.page_should_match_regex(r"\d+")
-def test_page_should_not_match_regex(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_match_regex(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
under_test.page_should_not_match_regex(r"\d+")
-def test_page_should_not_match_regex_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_match_regex_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="a")
- with pytest.raises(
- Exception, match=re.escape('There are matches found for "[a]+" pattern')
- ):
+ with pytest.raises(Exception, match=re.escape('There are matches found for "[a]+" pattern')):
under_test.page_should_not_match_regex("[a]+")
-def test_page_should_contain_match(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_match(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
under_test.page_should_contain_match("*a?c*")
-def test_page_should_contain_match_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_match_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
- with pytest.raises(
- Exception, match=re.escape('No matches found for "*e?g*" pattern')
- ):
+ with pytest.raises(Exception, match=re.escape('No matches found for "*e?g*" pattern')):
under_test.page_should_contain_match("*e?g*")
-def test_page_should_contain_match_ignore_case(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_match_ignore_case(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="ABC")
under_test.page_should_contain_match("*a?c*", ignore_case=True)
-def test_page_should_contain_match_custom_message(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_contain_match_custom_message(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match="my error message"):
under_test.page_should_contain_match("*def*", error_message="my error message")
-def test_page_should_not_contain_match(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_match(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
under_test.page_should_not_contain_match("*def*")
-def test_page_should_not_contain_match_fails(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_match_fails(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
- with pytest.raises(
- Exception, match=re.escape('There are matches found for "*abc*" pattern')
- ):
+ with pytest.raises(Exception, match=re.escape('There are matches found for "*abc*" pattern')):
under_test.page_should_not_contain_match("*abc*")
-def test_page_should_not_contain_match_ignore_case(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_match_ignore_case(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
- with pytest.raises(
- Exception, match=re.escape('There are matches found for "*abc*" pattern')
- ):
+ with pytest.raises(Exception, match=re.escape('There are matches found for "*abc*" pattern')):
under_test.page_should_not_contain_match("*ABC*", ignore_case=True)
-def test_page_should_not_contain_match_custom_message(
- mocker: MockerFixture, under_test: AssertionKeywords
-):
+def test_page_should_not_contain_match_custom_message(mocker: MockerFixture, under_test: AssertionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match="my error message"):
- under_test.page_should_not_contain_match(
- "*abc*", error_message="my error message"
- )
+ under_test.page_should_not_contain_match("*abc*", error_message="my error message")
diff --git a/utest/Mainframe3270/keywords/test_connection.py b/utest/Mainframe3270/keywords/test_connection.py
index e280ff4..ba539f8 100644
--- a/utest/Mainframe3270/keywords/test_connection.py
+++ b/utest/Mainframe3270/keywords/test_connection.py
@@ -1,5 +1,4 @@
import os
-import re
from unittest.mock import mock_open, patch
import pytest
@@ -31,9 +30,7 @@ def test_open_connection(mocker: MockerFixture, under_test: ConnectionKeywords):
assert ConnectionCache.register.call_args[0][1] is None
-def test_open_connection_with_alias(
- mocker: MockerFixture, under_test: ConnectionKeywords
-):
+def test_open_connection_with_alias(mocker: MockerFixture, under_test: ConnectionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.connect")
mocker.patch("robot.utils.ConnectionCache.register")
@@ -44,9 +41,7 @@ def test_open_connection_with_alias(
assert ConnectionCache.register.call_args[0][1] == "myalias"
-def test_open_connection_returns_index(
- mocker: MockerFixture, under_test: ConnectionKeywords
-):
+def test_open_connection_returns_index(mocker: MockerFixture, under_test: ConnectionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.connect")
mocker.patch("robot.utils.ConnectionCache.register", return_value=1)
@@ -63,9 +58,7 @@ def test_open_connection_with_lu(mocker: MockerFixture, under_test: ConnectionKe
Emulator.connect.assert_called_with("lu@myhost:23")
-def test_open_connection_with_port(
- mocker: MockerFixture, under_test: ConnectionKeywords
-):
+def test_open_connection_with_port(mocker: MockerFixture, under_test: ConnectionKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.connect")
under_test.open_connection("myhost", port=2222)
@@ -73,16 +66,14 @@ def test_open_connection_with_port(
Emulator.connect.assert_called_with("myhost:2222")
-def test_open_connection_with_extra_args(
- mocker: MockerFixture, under_test: ConnectionKeywords
-):
+def test_open_connection_with_extra_args(mocker: MockerFixture, under_test: ConnectionKeywords):
extra_args = ["-xrm", "*blankFill: true"]
mocker.patch("Mainframe3270.py3270.Emulator.__init__", return_value=None)
mocker.patch("Mainframe3270.py3270.Emulator.connect")
under_test.open_connection("myhost", extra_args=extra_args)
- Emulator.__init__.assert_called_with(True, 30.0, extra_args)
+ Emulator.__init__.assert_called_with(True, 30.0, extra_args, "2")
def test_open_connection_with_port_from_argument_and_from_extra_args(
@@ -101,6 +92,26 @@ def test_open_connection_with_port_from_argument_and_from_extra_args(
)
+def test_open_connection_with_default_model(mocker: MockerFixture, under_test: ConnectionKeywords):
+ mocker.patch("Mainframe3270.py3270.Emulator.__init__", return_value=None)
+ mocker.patch("Mainframe3270.py3270.Emulator.connect")
+
+ under_test.open_connection("myhost")
+
+ Emulator.__init__.assert_called_with(True, 30.0, [], "2")
+
+
+def test_open_connection_with_model_from_extra_args(mocker: MockerFixture, under_test: ConnectionKeywords):
+ mocker.patch("Mainframe3270.py3270.Emulator.__init__", return_value=None)
+ mocker.patch("Mainframe3270.py3270.Emulator.connect")
+ model = "4"
+ extra_args = ["-xrm", f"*model: {model}"]
+
+ under_test.open_connection("myhost", extra_args=extra_args)
+
+ Emulator.__init__.assert_called_with(True, 30.0, extra_args, model)
+
+
def test_process_args_returns_empty_list(under_test: ConnectionKeywords):
args = None
@@ -151,6 +162,55 @@ def test_process_args_from_multiline_file_with_comments(under_test: ConnectionKe
assert processed_args == args_from_file
+@pytest.mark.parametrize(
+ ("model_arg", "expected_model"),
+ [
+ (["-xrm", "wc3270.model: 2"], "2"),
+ (["-xrm", "ws3270.model: 2"], "2"),
+ (["-xrm", "x3270.model: 2"], "2"),
+ (["-xrm", "s3270.model: 2"], "2"),
+ (["-xrm", "*model: 2"], "2"),
+ (["-xrm", "*model:2"], "2"),
+ (["-xrm", "*model:3"], "3"),
+ (["-xrm", "*model:4"], "4"),
+ (["-xrm", "*model:5"], "5"),
+ (["-xrm", "*model:3278-2"], "3278-2"),
+ (["-xrm", "*model:3278-2-E"], "3278-2-E"),
+ (["-xrm", "*model:3279-2"], "3279-2"),
+ (["-xrm", "*model:3279-2-E"], "3279-2-E"),
+ (["-xrm", "*model:3278-3"], "3278-3"),
+ (["-xrm", "*model:3278-3-E"], "3278-3-E"),
+ (["-xrm", "*model:3279-3"], "3279-3"),
+ (["-xrm", "*model:3279-3-E"], "3279-3-E"),
+ (["-xrm", "*model:3278-4"], "3278-4"),
+ (["-xrm", "*model:3278-4-E"], "3278-4-E"),
+ (["-xrm", "*model:3279-4"], "3279-4"),
+ (["-xrm", "*model:3279-4-E"], "3279-4-E"),
+ (["-xrm", "*model:3278-5"], "3278-5"),
+ (["-xrm", "*model:3278-5-E"], "3278-5-E"),
+ (["-xrm", "*model:3279-5"], "3279-5"),
+ (["-xrm", "*model:3279-5-E"], "3279-5-E"),
+ ],
+)
+def test_get_model_from_list_or_file_with_list(under_test: ConnectionKeywords, model_arg: list, expected_model: str):
+ model = under_test._get_model_from_list_or_file(model_arg)
+
+ assert model == expected_model
+
+
+def test_get_model_from_list_or_file_with_file(mocker: MockerFixture, under_test: ConnectionKeywords):
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension")
+ with patch("builtins.open", mock_open(read_data="*hostname: pub400.com\n*model: 5")):
+ model = under_test._get_model_from_list_or_file("session.x3270")
+
+ assert model == "5"
+
+
+def test_get_model_from_list_or_file_returns_None(under_test: ConnectionKeywords):
+ assert under_test._get_model_from_list_or_file([]) is None
+ assert under_test._get_model_from_list_or_file(None) is None
+
+
@pytest.mark.parametrize(
("args", "expected"),
[
@@ -199,66 +259,91 @@ def test_check_session_file_extension(
def test_contains_hostname_raises_ValueError(under_test: ConnectionKeywords):
- with patch(
- "builtins.open", mock_open(read_data="wc3270.port: 992\n")
- ) as session_file:
+ with patch("builtins.open", mock_open(read_data="wc3270.port: 992\n")):
with pytest.raises(
ValueError,
match="Your session file needs to specify the hostname resource to set up the connection",
):
- under_test._check_contains_hostname(session_file)
+ under_test._check_contains_hostname("wc3270.session")
@pytest.mark.parametrize(
- "model",
+ ("os_name", "visible"),
[
- "wc3270.model: 2",
- "ws3270.model:2",
- "x3270.model: 2",
- "s3270.model: 2",
- "*model:2",
- "",
+ ("nt", False),
+ ("posix", True),
+ ("posix", False),
],
)
-def test_check_model(model: str, under_test: ConnectionKeywords):
- with patch("builtins.open", mock_open(read_data=model)) as session_file:
- under_test._check_model(session_file)
+def test_open_connection_from_session_file_uses_default_model(
+ under_test: ConnectionKeywords, os_name: str, visible: bool, mocker: MockerFixture
+):
+ mocker.patch("Mainframe3270.keywords.connection.os_name", os_name)
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension")
+ mocker.patch("Mainframe3270.py3270.Emulator.__init__", return_value=None)
+ under_test.visible = visible
+
+ with patch("builtins.open", mock_open(read_data="*hostname: pub400.com")):
+ under_test.open_connection_from_session_file("session.s3270")
+
+ Emulator.__init__.assert_called_with(visible, 30.0, ["session.s3270"], "2")
+
+
+def test_open_connection_from_session_file_uses_default_model_for_wc3270(
+ mocker: MockerFixture, under_test: ConnectionKeywords
+):
+ mocker.patch("Mainframe3270.keywords.connection.os_name", "nt")
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension")
+ mocker.patch("Mainframe3270.py3270.Emulator.__init__", return_value=None)
+ mocker.patch("Mainframe3270.py3270.Emulator.connect")
+ under_test.visible = True
+ with patch("builtins.open", mock_open(read_data="*hostname: pub400.com")):
+ under_test.open_connection_from_session_file("session.s3270")
+
+ Emulator.__init__.assert_called_with(True, 30.0, model="2")
@pytest.mark.parametrize(
- ("model_string", "model"),
+ ("os_name", "visible"),
[
- ("wc3270.model: 4", "4"),
- ("ws3270.model:4", "4"),
- ("x3270.model: 5", "5"),
- ("s3270.model: 3279-4-E", "3279-4-E"),
- ("*model: 3278-4", "3278-4"),
+ ("nt", False),
+ ("posix", True),
+ ("posix", False),
],
)
-def test_check_model_raises_ValueError(
- model_string: str, model: SystemError, under_test: ConnectionKeywords
+def test_open_connection_from_session_file_uses_model_from_file(
+ mocker: MockerFixture, os_name: str, visible: bool, under_test: ConnectionKeywords
):
- with patch("builtins.open", mock_open(read_data=model_string)) as session_file:
- with pytest.raises(
- ValueError,
- match=re.escape(
- f'Robot-Framework-Mainframe-3270-Library currently only supports model "2", '
- f'the model you specified in your session file was "{model}". '
- f'Please change it to "2", using either the session wizard if you are on Windows, '
- f'or by editing the model resource like this "*model: 2"'
- ),
- ):
- under_test._check_model(session_file)
+ mocker.patch("Mainframe3270.keywords.connection.os_name", os_name)
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension")
+ mocker.patch("Mainframe3270.py3270.Emulator.__init__", return_value=None)
+ under_test.visible = visible
+
+ with patch("builtins.open", mock_open(read_data="*hostname: pub400.com\n*model: 5")):
+ under_test.open_connection_from_session_file("session.x3270")
+ Emulator.__init__.assert_called_with(visible, 30.0, ["session.x3270"], "5")
-def test_open_connection_from_session_file_registers_connection(
+
+def test_open_connection_from_session_file_uses_model_from_file_for_wc3270(
mocker: MockerFixture, under_test: ConnectionKeywords
):
- mocker.patch(
- "Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension"
- )
+ mocker.patch("Mainframe3270.keywords.connection.os_name", "nt")
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension")
+ mocker.patch("Mainframe3270.py3270.Emulator.__init__", return_value=None)
+ mocker.patch("Mainframe3270.py3270.Emulator.connect")
+ under_test.visible = True
+ with patch("builtins.open", mock_open(read_data="*hostname: pub400.com\nwc3270.model: 5")):
+ under_test.open_connection_from_session_file("session.wc3270")
+
+ Emulator.__init__.assert_called_with(True, 30.0, model="5")
+
+
+def test_open_connection_from_session_file_registers_connection(mocker: MockerFixture, under_test: ConnectionKeywords):
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension")
mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_contains_hostname")
- mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_model")
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._get_model_from_list_or_file", return_value="2")
+ mocker.patch("Mainframe3270.py3270.Emulator.connect")
mocker.patch("robot.utils.ConnectionCache.register")
under_test.open_connection_from_session_file("session.wc3270")
@@ -270,11 +355,10 @@ def test_open_connection_from_session_file_registers_connection(
def test_open_connection_from_session_file_registers_connection_with_alias(
mocker: MockerFixture, under_test: ConnectionKeywords
):
- mocker.patch(
- "Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension"
- )
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension")
mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_contains_hostname")
- mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_model")
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._get_model_from_list_or_file", return_value="3")
+ mocker.patch("Mainframe3270.py3270.Emulator.connect")
mocker.patch("robot.utils.ConnectionCache.register")
under_test.open_connection_from_session_file("session.wc3270", "myalias")
@@ -283,14 +367,11 @@ def test_open_connection_from_session_file_registers_connection_with_alias(
assert ConnectionCache.register.call_args[0][1] == "myalias"
-def test_open_connection_from_session_file_returns_index(
- mocker: MockerFixture, under_test: ConnectionKeywords
-):
- mocker.patch(
- "Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension"
- )
+def test_open_connection_from_session_file_returns_index(mocker: MockerFixture, under_test: ConnectionKeywords):
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_session_file_extension")
mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_contains_hostname")
- mocker.patch("Mainframe3270.keywords.ConnectionKeywords._check_model")
+ mocker.patch("Mainframe3270.keywords.ConnectionKeywords._get_model_from_list_or_file", return_value="4")
+ mocker.patch("Mainframe3270.py3270.Emulator.connect")
mocker.patch("robot.utils.ConnectionCache.register", return_value=1)
index = under_test.open_connection_from_session_file("session.wc3270")
diff --git a/utest/Mainframe3270/keywords/test_read_write.py b/utest/Mainframe3270/keywords/test_read_write.py
index 09a0f43..50b6f6a 100644
--- a/utest/Mainframe3270/keywords/test_read_write.py
+++ b/utest/Mainframe3270/keywords/test_read_write.py
@@ -22,9 +22,7 @@ def test_read(under_test: ReadWriteKeywords, mocker: MockerFixture):
def test_read_all_screen(under_test: ReadWriteKeywords, mocker: MockerFixture):
- mocker.patch(
- "Mainframe3270.py3270.Emulator.read_all_screen", return_value="all screen"
- )
+ mocker.patch("Mainframe3270.py3270.Emulator.read_all_screen", return_value="all screen")
content = under_test.read_all_screen()
diff --git a/utest/Mainframe3270/keywords/test_screenshot.py b/utest/Mainframe3270/keywords/test_screenshot.py
index bf10830..10f8fc2 100644
--- a/utest/Mainframe3270/keywords/test_screenshot.py
+++ b/utest/Mainframe3270/keywords/test_screenshot.py
@@ -22,9 +22,7 @@ def test_set_screenshot_folder(under_test: ScreenshotKeywords):
assert under_test.img_folder == os.getcwd()
-def test_set_screenshot_folder_nonexistent(
- mocker: MockerFixture, under_test: ScreenshotKeywords
-):
+def test_set_screenshot_folder_nonexistent(mocker: MockerFixture, under_test: ScreenshotKeywords):
mocker.patch("robot.api.logger.error")
mocker.patch("robot.api.logger.warn")
path = os.path.join(os.getcwd(), "nonexistent")
@@ -32,9 +30,7 @@ def test_set_screenshot_folder_nonexistent(
under_test.set_screenshot_folder(path)
logger.error.assert_called_with('Given screenshots path "%s" does not exist' % path)
- logger.warn.assert_called_with(
- 'Screenshots will be saved in "%s"' % under_test.img_folder
- )
+ logger.warn.assert_called_with('Screenshots will be saved in "%s"' % under_test.img_folder)
def test_take_screenshot(mocker: MockerFixture, under_test: ScreenshotKeywords):
@@ -55,9 +51,7 @@ def test_take_screenshot(mocker: MockerFixture, under_test: ScreenshotKeywords):
assert filepath == "./screenshot_1000.html"
-def test_take_screenshot_with_filename_prefix(
- mocker: MockerFixture, under_test: ScreenshotKeywords
-):
+def test_take_screenshot_with_filename_prefix(mocker: MockerFixture, under_test: ScreenshotKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.save_screen")
mocker.patch("robot.api.logger.write")
mocker.patch("time.time", return_value=1.0)
diff --git a/utest/Mainframe3270/keywords/test_wait_and_timeout.py b/utest/Mainframe3270/keywords/test_wait_and_timeout.py
index 5856845..fa5bb26 100644
--- a/utest/Mainframe3270/keywords/test_wait_and_timeout.py
+++ b/utest/Mainframe3270/keywords/test_wait_and_timeout.py
@@ -86,27 +86,21 @@ def test_wait_until_string(mocker: MockerFixture, under_test: WaitAndTimeoutKeyw
assert txt == "abc"
-def test_wait_until_string_string_not_found(
- mocker: MockerFixture, under_test: WaitAndTimeoutKeywords
-):
+def test_wait_until_string_string_not_found(mocker: MockerFixture, under_test: WaitAndTimeoutKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match='String "def" not found in 1 second'):
under_test.wait_until_string("def", 1)
-def test_wait_until_string_with_time_time_string(
- mocker: MockerFixture, under_test: WaitAndTimeoutKeywords
-):
+def test_wait_until_string_with_time_time_string(mocker: MockerFixture, under_test: WaitAndTimeoutKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match='String "def" not found in 500 milliseconds'):
under_test.wait_until_string("def", "500 millis")
-def test_wait_until_string_with_time_timer_string(
- mocker: MockerFixture, under_test: WaitAndTimeoutKeywords
-):
+def test_wait_until_string_with_time_timer_string(mocker: MockerFixture, under_test: WaitAndTimeoutKeywords):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="abc")
with pytest.raises(Exception, match='String "def" not found in 500 milliseconds'):
diff --git a/utest/Mainframe3270/test__init__.py b/utest/Mainframe3270/test__init__.py
index 752166c..d0869f9 100644
--- a/utest/Mainframe3270/test__init__.py
+++ b/utest/Mainframe3270/test__init__.py
@@ -12,6 +12,7 @@ def test_default_args():
assert under_test.wait_time == 0.5
assert under_test.wait_time_after_write == 0.0
assert under_test.img_folder == "."
+ assert under_test.model == "2"
under_test.mf is None
diff --git a/utest/Mainframe3270/test_librarycomponent.py b/utest/Mainframe3270/test_librarycomponent.py
index 06f172a..82548f7 100644
--- a/utest/Mainframe3270/test_librarycomponent.py
+++ b/utest/Mainframe3270/test_librarycomponent.py
@@ -21,6 +21,7 @@ def test_librarycomponent_returns_common_attributes():
assert library.cache == under_test.cache
assert library.mf == under_test.mf
assert library.output_folder == under_test.output_folder
+ assert library.model == under_test.model
def test_can_set_visible():
diff --git a/utest/Mainframe3270/test_run_on_failure.py b/utest/Mainframe3270/test_run_on_failure.py
index e4b9c3d..4b6599c 100644
--- a/utest/Mainframe3270/test_run_on_failure.py
+++ b/utest/Mainframe3270/test_run_on_failure.py
@@ -36,6 +36,4 @@ def test_run_on_failure_could_not_be_run(mocker: MockerFixture):
with pytest.raises(Exception, match="my error message"):
under_test.run_keyword("Keyword", None, None)
- logger.warn.assert_called_with(
- "Keyword 'Keyword' could not be run on failure: my error message"
- )
+ logger.warn.assert_called_with("Keyword 'Keyword' could not be run on failure: my error message")
diff --git a/utest/py3270/test_command.py b/utest/py3270/test_command.py
index 1910700..0b89957 100644
--- a/utest/py3270/test_command.py
+++ b/utest/py3270/test_command.py
@@ -4,8 +4,7 @@
from pytest_mock import MockerFixture
# fmt: off
-from Mainframe3270.py3270 import (Command, CommandError, Emulator, s3270App,
- wc3270App, ws3270App, x3270App)
+from Mainframe3270.py3270 import Command, CommandError, Emulator, s3270App, wc3270App, ws3270App, x3270App
# fmt: on
@@ -93,7 +92,5 @@ def test_handle_result_not_ok_or_error(mocker: MockerFixture):
app = wc3270App()
under_test = Command(app, b"abc")
- with pytest.raises(
- ValueError, match='expected "ok" or "error" result, but received: abc'
- ):
+ with pytest.raises(ValueError, match='expected "ok" or "error" result, but received: abc'):
under_test.execute()
diff --git a/utest/py3270/test_emulator.py b/utest/py3270/test_emulator.py
index 1851cfc..d23f2d0 100644
--- a/utest/py3270/test_emulator.py
+++ b/utest/py3270/test_emulator.py
@@ -3,6 +3,7 @@
import pytest
from pytest_mock import MockerFixture
+from Mainframe3270 import py3270
from Mainframe3270.py3270 import Emulator, TerminatedError
@@ -11,7 +12,7 @@ def test_emulator_default_args():
under_test = Emulator()
assert under_test.app.executable == "ws3270"
- assert under_test.app.args == ["-xrm", "ws3270.unlockDelay: False"]
+ assert under_test.app.args == ["-xrm", "ws3270.unlockDelay: False", "-xrm", "*model: 2"]
@pytest.mark.usefixtures("mock_windows")
@@ -23,7 +24,7 @@ def test_emulator_visible():
"-xrm",
"wc3270.unlockDelay: False",
"-xrm",
- "wc3270.model: 2",
+ "*model: 2",
]
@@ -50,23 +51,78 @@ def test_emulator_with_extra_args():
assert under_test.app.args > extra_args
+@pytest.mark.usefixtures("mock_windows")
+def test_emulator_with_model_default_model():
+ under_test = Emulator()
+
+ assert under_test.model == "2", 'default model should be "2"'
+
+
+@pytest.mark.usefixtures("mock_windows")
+def test_emulator_with_model():
+ under_test = Emulator(model="4")
+
+ assert under_test.model == "4"
+
+
+@pytest.mark.usefixtures("mock_windows")
+@pytest.mark.parametrize(
+ ("os_name", "visible", "model"),
+ [
+ ("nt", True, "2"),
+ ("nt", False, "2"),
+ ("nt", True, "3"),
+ ("nt", False, "3"),
+ ("posix", True, "2"),
+ ("posix", False, "2"),
+ ("posix", True, "3"),
+ ("posix", False, "3"),
+ ],
+)
+def test_emulator_ws3270App_has_model_as_last_arg(visible: bool, os_name: str, model: str):
+ py3270.os_name = os_name
+ under_test = Emulator(visible, model=model)
+
+ assert under_test.app.args[-2:] == ["-xrm", f"*model: {model}"]
+
+
+@pytest.mark.usefixtures("mock_windows")
+@pytest.mark.parametrize(
+ ("model", "model_dimensions"),
+ [
+ ("2", {"rows": 24, "columns": 80}),
+ ("3", {"rows": 32, "columns": 80}),
+ ("4", {"rows": 43, "columns": 80}),
+ ("5", {"rows": 27, "columns": 132}),
+ ],
+)
+def test_emulator_sets_model_dimensions(model, model_dimensions):
+ under_test = Emulator(model=model)
+
+ assert under_test.model_dimensions == model_dimensions
+
+
+@pytest.mark.usefixtures("mock_windows")
+def test_set_model_dimensions_raises_ValueError():
+ under_test = Emulator()
+
+ with pytest.raises(ValueError, match=r"Model should be one of .+, but was 'wrong model'"):
+ under_test._set_model_dimensions("wrong model")
+
+
@pytest.mark.usefixtures("mock_windows")
def test_exec_command_when_is_terminated():
under_test = Emulator()
under_test.is_terminated = True
- with pytest.raises(
- TerminatedError, match="This Emulator instance has been terminated"
- ):
+ with pytest.raises(TerminatedError, match="This Emulator instance has been terminated"):
under_test.exec_command(b"abc")
@pytest.mark.usefixtures("mock_windows")
def test_terminate_BrokenPipeError(mocker: MockerFixture):
mocker.patch("Mainframe3270.py3270.wc3270App.close")
- mocker.patch(
- "Mainframe3270.py3270.Emulator.exec_command", side_effect=BrokenPipeError
- )
+ mocker.patch("Mainframe3270.py3270.Emulator.exec_command", side_effect=BrokenPipeError)
under_test = Emulator()
under_test.terminate()
@@ -79,9 +135,7 @@ def test_terminate_socket_error(mocker: MockerFixture):
mock_os_error = OSError()
mock_os_error.errno = errno.ECONNRESET
mocker.patch("Mainframe3270.py3270.wc3270App.close")
- mocker.patch(
- "Mainframe3270.py3270.Emulator.exec_command", side_effect=mock_os_error
- )
+ mocker.patch("Mainframe3270.py3270.Emulator.exec_command", side_effect=mock_os_error)
under_test = Emulator()
under_test.terminate()
@@ -176,13 +230,21 @@ def test_string_get_calls__check_limits(mocker: MockerFixture):
@pytest.mark.usefixtures("mock_windows")
-def test_string_get_exceeds_x_axis(mocker: MockerFixture):
- under_test = Emulator(True)
+@pytest.mark.parametrize(
+ ("model", "length"),
+ [
+ ("2", 72),
+ ("3", 72),
+ ("4", 72),
+ ("5", 124),
+ ],
+)
+def test_string_get_exceeds_x_axis(mocker: MockerFixture, model: str, length: int):
+ mocker.patch("Mainframe3270.py3270.wc3270App.readline")
+ under_test = Emulator(True, model=model)
- with pytest.raises(
- Exception, match="You have exceeded the x-axis limit of the mainframe screen"
- ):
- under_test.string_get(1, 10, 72)
+ with pytest.raises(Exception, match="You have exceeded the x-axis limit of the mainframe screen"):
+ under_test.string_get(1, 10, length)
@pytest.mark.usefixtures("mock_windows")
@@ -209,6 +271,18 @@ def test_search_string_ignoring_case(mocker: MockerFixture):
assert under_test.search_string("abc", True)
+@pytest.mark.usefixtures("mock_windows")
+@pytest.mark.parametrize(("model", "rows", "columns"), [("2", 24, 80), ("3", 32, 80), ("4", 43, 80), ("5", 27, 132)])
+def test_search_string_with_different_model_dimensions(mocker: MockerFixture, model: str, rows: int, columns: int):
+ mocker.patch("Mainframe3270.py3270.Emulator.string_get")
+ under_test = Emulator(model=model)
+
+ under_test.search_string("abc")
+
+ assert Emulator.string_get.call_count == rows
+ Emulator.string_get.assert_called_with(rows, 1, columns)
+
+
@pytest.mark.usefixtures("mock_windows")
def test_read_all_screen(mocker: MockerFixture):
mocker.patch("Mainframe3270.py3270.Emulator.string_get", return_value="a")
@@ -219,6 +293,18 @@ def test_read_all_screen(mocker: MockerFixture):
assert content == "a" * 24
+@pytest.mark.usefixtures("mock_windows")
+@pytest.mark.parametrize(("model", "rows", "columns"), [("2", 24, 80), ("3", 32, 80), ("4", 43, 80), ("5", 27, 132)])
+def test_read_all_screen_with_different_model_dimensions(mocker: MockerFixture, model: str, rows: int, columns: int):
+ mocker.patch("Mainframe3270.py3270.Emulator.string_get")
+ under_test = Emulator(model=model)
+
+ under_test.read_all_screen()
+
+ assert Emulator.string_get.call_count == rows
+ Emulator.string_get.assert_called_with(rows, 1, columns)
+
+
@pytest.mark.usefixtures("mock_windows")
def test_check_limits():
under_test = Emulator()
@@ -228,14 +314,20 @@ def test_check_limits():
@pytest.mark.usefixtures("mock_windows")
@pytest.mark.parametrize(
- ("ypos", "xpos", "expected_error"),
+ ("model", "ypos", "xpos", "expected_error"),
[
- (25, 80, "You have exceeded the y-axis limit of the mainframe screen"),
- (24, 81, "You have exceeded the x-axis limit of the mainframe screen"),
+ ("2", 25, 80, "You have exceeded the y-axis limit of the mainframe screen"),
+ ("3", 33, 80, "You have exceeded the y-axis limit of the mainframe screen"),
+ ("4", 44, 80, "You have exceeded the y-axis limit of the mainframe screen"),
+ ("5", 28, 80, "You have exceeded the y-axis limit of the mainframe screen"),
+ ("2", 24, 81, "You have exceeded the x-axis limit of the mainframe screen"),
+ ("3", 32, 81, "You have exceeded the x-axis limit of the mainframe screen"),
+ ("4", 43, 81, "You have exceeded the x-axis limit of the mainframe screen"),
+ ("5", 27, 133, "You have exceeded the x-axis limit of the mainframe screen"),
],
)
-def test_check_limits_raises_Exception(ypos, xpos, expected_error):
- under_test = Emulator()
+def test_check_limits_raises_Exception(model, ypos, xpos, expected_error):
+ under_test = Emulator(model=model)
with pytest.raises(Exception, match=expected_error):
under_test._check_limits(ypos, xpos)