Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(#76) Open connection with pre configured session #90

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ __pychache__
log.html
output.xml
report.html
screenshot*.html
atest/**/session.*3270
atest/log.html
atest/output.xml
atest/report.html
Expand Down
4 changes: 2 additions & 2 deletions Mainframe3270/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from robotlibcore import DynamicCore

from .version import VERSION
from .x3270 import x3270
from .x3270 import X3270


class Mainframe3270(DynamicCore):
Expand Down Expand Up @@ -74,7 +74,7 @@ def __init__(
self._running_on_failure_keyword = False
self.register_run_on_failure_keyword(run_on_failure_keyword)
libraries = [
x3270(visible, timeout, wait_time, wait_time_after_write, img_folder)
X3270(visible, timeout, wait_time, wait_time_after_write, img_folder)
]
DynamicCore.__init__(self, libraries)

Expand Down
75 changes: 74 additions & 1 deletion Mainframe3270/x3270.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import socket
import time
from datetime import timedelta
from os import name as os_name
from typing import Any, List, Optional, Union

from robot.api import logger
Expand All @@ -13,7 +14,7 @@
from .py3270 import Emulator


class x3270(object):
class X3270(object):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I needed to change this name in order to mock the os_name in the unit tests. Since the only interface to the user is the Mainframe3270 class, I do not consider this to be a breaking change.

def __init__(
self,
visible: bool,
Expand Down Expand Up @@ -129,6 +130,78 @@ def _port_in_extra_args(self, args) -> bool:
return True
return False

@keyword("Open Connection From Session File")
def open_connection_from_session_file(self, session_file: os.PathLike):
"""Create a connection to an IBM3270 mainframe using a [https://x3270.miraheze.org/wiki/Session_file|session file].

The session file contains [https://x3270.miraheze.org/wiki/Category:Resources|resources (settings)] for a specific host session.
The only mandatory setting required to establish the connection is the [https://x3270.miraheze.org/wiki/Hostname_resource|hostname resource].

This keyword is an alternative to `Open Connection`. Please note that the Robot-Framework-Mainframe-3270-Library
currently only supports model "2". Specifying any other model will result in a failure.

For more information on session file syntax and detailed examples, please consult the [https://x3270.miraheze.org/wiki/Session_file|x3270 wiki].

Example:
| Open Connection From Session File | ${CURDIR}/session.wc3270 |

where the content of `session.wc3270` is:
| wc3270.hostname: myhost.com:23
| wc3270.model: 2
"""
if self.mf:
self.close_connection()
self._check_session_file_extension(session_file)
self._check_contains_hostname(session_file)
self._check_model(session_file)
if os_name == "nt" and self.visible:
self.mf = Emulator(self.visible, self.timeout)
self.mf.connect(str(session_file))
else:
self.mf = Emulator(self.visible, self.timeout, [str(session_file)])

def _check_session_file_extension(self, session_file):
file_extension = str(session_file).rsplit(".")[-1]
expected_extensions = {
("nt", True): "wc3270",
("nt", False): "ws3270",
("posix", True): "x3270",
("posix", False): "s3270",
}
expected_extension = expected_extensions.get((os_name, self.visible))
if file_extension != expected_extension:
raise ValueError(
f"Based on the emulator that you are using, "
f'the session file extension has to be ".{expected_extension}", '
f'but it was ".{file_extension}"'
)

def _check_contains_hostname(self, session_file):
with open(session_file) as file:
if "hostname:" not in file.read():
raise ValueError(
"Your session file needs to specify the hostname resource "
"to set up the connection. "
"An example for wc3270 looks like this: \n"
"wc3270.hostname: myhost.com\n"
)

def _check_model(self, 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("Close Connection")
def close_connection(self) -> None:
"""Disconnect from the host."""
Expand Down
31 changes: 26 additions & 5 deletions atest/connection.robot
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
*** Settings ***
Library OperatingSystem
Library ../Mainframe3270/
Resource pub400_variables.robot
Library OperatingSystem
Library ../Mainframe3270/ ${VISIBLE}

Test Teardown Test Teardown


*** Variables ***
${ARGFILE} ${CURDIR}/resources/argfile.txt
${TRACE_FILE} ${CURDIR}/x3270.trace
${ARGFILE} ${CURDIR}/resources/argfile.txt
${SESSION_TEMPLATE} ${CURDIR}/resources/session.template
${TRACE_FILE} ${CURDIR}/x3270.trace


*** Test Cases ***
Test Connection With Extra Args List
${extra_args} Create List -port 992 -trace -tracefile ${TRACE_FILE}
${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}
Expand All @@ -23,8 +24,28 @@ Test Connection With Argfile
Sleep 0.5 s
File Should Exist ${TRACE_FILE}

Test Connection From Session File
${session_file}= Create Session File
Open Connection From Session File ${SESSION_FILE}
Wait Field Detected
Page Should Contain String ${WELCOME}


*** 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}
RETURN ${session_file}

Test Teardown
Close Connection
Sleep 1 second
Expand Down
1 change: 1 addition & 0 deletions atest/pub400_variables.robot
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*** Variables ***
${VISIBLE} True
${HOST} pub400.com
${FOLDER} ${CURDIR}${/}screenshots
# Text to write
Expand Down
2 changes: 2 additions & 0 deletions atest/resources/session.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*hostname: L:pub400.com
*port: 992
4 changes: 2 additions & 2 deletions utest/x3270/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from pytest_mock import MockerFixture

from Mainframe3270.py3270 import Emulator
from Mainframe3270.x3270 import x3270
from Mainframe3270.x3270 import X3270

X3270_DEFAULT_ARGS = {
"visible": True,
Expand All @@ -16,6 +16,6 @@
@pytest.fixture
def under_test(mocker: MockerFixture):
mocker.patch("Mainframe3270.py3270.Emulator.create_app")
under_test = x3270(**X3270_DEFAULT_ARGS)
under_test = X3270(**X3270_DEFAULT_ARGS)
under_test.mf = Emulator(under_test.visible, under_test.timeout)
return under_test
1 change: 1 addition & 0 deletions utest/x3270/resources/session.wc3270
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wc3270.port: 992
10 changes: 5 additions & 5 deletions utest/x3270/test__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from pytest_mock import MockerFixture

from Mainframe3270.x3270 import x3270
from Mainframe3270.x3270 import X3270

from .conftest import X3270_DEFAULT_ARGS


def test_default_args():
under_test = x3270(True, 30, 0.5, 0.0, ".")
under_test = X3270(True, 30, 0.5, 0.0, ".")
assert under_test.visible is True
assert under_test.timeout == 30
assert under_test.wait == 0.5
Expand All @@ -18,7 +18,7 @@ def test_default_args():


def test_import_with_time_string():
under_test = x3270(True, "30 s", "500 milliseconds", "1 minute", ".")
under_test = X3270(True, "30 s", "500 milliseconds", "1 minute", ".")
assert under_test.timeout == 30
assert under_test.wait == 0.5
assert under_test.wait_write == 60
Expand All @@ -29,11 +29,11 @@ def test_output_folder_robotframework_running(mocker: MockerFixture):
"robot.libraries.BuiltIn.BuiltIn.get_variable_value",
return_value="/home/output",
)
under_test = x3270(**X3270_DEFAULT_ARGS)
under_test = X3270(**X3270_DEFAULT_ARGS)

m_get_variable_value.assert_called_with("${OUTPUT DIR}")
assert under_test.output_folder == "/home/output"


def test_output_folder_robotframework_not_running(under_test: x3270):
def test_output_folder_robotframework_not_running(under_test: X3270):
assert under_test.output_folder == os.getcwd()
Loading