Skip to content

Commit ab41f7a

Browse files
Merge pull request #141 from fhennig42/Issue_136_Windows_11_Terminal
Issue 136 Windows 11 terminal
2 parents 048788c + cc73917 commit ab41f7a

File tree

3 files changed

+104
-74
lines changed

3 files changed

+104
-74
lines changed

.github/workflows/run-tests.yml

Lines changed: 67 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,74 +14,74 @@ jobs:
1414
# pub400 closes the connection if there are too many requests coming from the same IP address
1515
max-parallel: 1
1616
matrix:
17-
python-version: ["3.8", "3.12"]
17+
python-version: ["3.8", "3.12", "3.13"]
1818
os: [ubuntu-latest, windows-latest]
1919
fail-fast: false
2020

2121
steps:
22-
- name: Checkout repository
23-
uses: actions/checkout@v3
24-
25-
- name: Set up python
26-
uses: actions/setup-python@v4
27-
with:
28-
python-version: ${{ matrix.python-version }}
29-
30-
- name: Install dependencies linux
31-
if: matrix.os == 'ubuntu-latest'
32-
run: |
33-
sudo apt-get install -y xvfb x3270 locales xterm
34-
sudo locale-gen en_US
35-
36-
- name: Install dependencies windows
37-
if: matrix.os == 'windows-latest'
38-
run: |
39-
choco install wc3270
40-
echo "C:\Program Files\wc3270" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
41-
42-
- name: Install python dependencies
43-
run: |
44-
python -m pip install -r requirements-dev.txt
45-
46-
- name: Lint
47-
run: |
48-
inv lint
49-
50-
- name: Run utests with coverage
51-
run: |
52-
coverage run --branch --source Mainframe3270/ -m pytest --verbose utest/
53-
coverage report
54-
coverage xml
55-
56-
- name: Upload unit test coverage to Codecov
57-
uses: codecov/codecov-action@v3
58-
with:
59-
files: coverage.xml
60-
flags: unit
61-
move_coverage_to_trash: true
62-
63-
- name: Run atests with coverage linux
64-
if: matrix.os == 'ubuntu-latest'
65-
run: |
66-
LANG=en_US.iso88591 xvfb-run coverage run --branch --source Mainframe3270/ -m robot $ROBOT_OPTIONS atest/
67-
coverage report
68-
coverage xml
69-
70-
- name: Run atests with coverage windows
71-
if: matrix.os == 'windows-latest'
72-
run: |
73-
coverage run --branch --source Mainframe3270/ -m robot $ROBOT_OPTIONS atest/
74-
coverage report
75-
coverage xml
76-
77-
- name: Upload acceptance tests coverage to Codecov
78-
uses: codecov/codecov-action@v3
79-
with:
80-
files: coverage.xml
81-
flags: acceptance
82-
83-
- uses: actions/upload-artifact@v3
84-
if: ${{ always() }}
85-
with:
86-
name: Tests results python${{ matrix.python-version }} - ${{ matrix.os }}
87-
path: logs/
22+
- name: Checkout repository
23+
uses: actions/checkout@v3
24+
25+
- name: Set up python
26+
uses: actions/setup-python@v4
27+
with:
28+
python-version: ${{ matrix.python-version }}
29+
30+
- name: Install dependencies linux
31+
if: matrix.os == 'ubuntu-latest'
32+
run: |
33+
sudo apt-get install -y xvfb x3270 locales xterm
34+
sudo locale-gen en_US
35+
36+
- name: Install dependencies windows
37+
if: matrix.os == 'windows-latest'
38+
run: |
39+
choco install wc3270
40+
echo "C:\Program Files\wc3270" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
41+
42+
- name: Install python dependencies
43+
run: |
44+
python -m pip install -r requirements-dev.txt
45+
46+
- name: Lint
47+
run: |
48+
inv lint
49+
50+
- name: Run utests with coverage
51+
run: |
52+
coverage run --branch --source Mainframe3270/ -m pytest --verbose utest/
53+
coverage report
54+
coverage xml
55+
56+
- name: Upload unit test coverage to Codecov
57+
uses: codecov/codecov-action@v3
58+
with:
59+
files: coverage.xml
60+
flags: unit
61+
move_coverage_to_trash: true
62+
63+
- name: Run atests with coverage linux
64+
if: matrix.os == 'ubuntu-latest'
65+
run: |
66+
LANG=en_US.iso88591 xvfb-run coverage run --branch --source Mainframe3270/ -m robot $ROBOT_OPTIONS atest/
67+
coverage report
68+
coverage xml
69+
70+
- name: Run atests with coverage windows
71+
if: matrix.os == 'windows-latest'
72+
run: |
73+
coverage run --branch --source Mainframe3270/ -m robot $ROBOT_OPTIONS atest/
74+
coverage report
75+
coverage xml
76+
77+
- name: Upload acceptance tests coverage to Codecov
78+
uses: codecov/codecov-action@v3
79+
with:
80+
files: coverage.xml
81+
flags: acceptance
82+
83+
- uses: actions/upload-artifact@v4
84+
if: ${{ always() }}
85+
with:
86+
name: Tests results python${{ matrix.python-version }} - ${{ matrix.os }}
87+
path: logs/

Mainframe3270/py3270.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
import socket
66
import subprocess
7+
import sys
78
import time
89
import warnings
910
from abc import ABC, abstractmethod
@@ -206,11 +207,16 @@ def close(self):
206207
self.socket.close()
207208

208209
def spawn_app(self, host):
209-
args = ["start", "/wait", self.executable] + self.args
210+
# check if we need to run a different start command when running in Windows 11
211+
is_win10 = sys.getwindowsversion().build < 22000
212+
if is_win10:
213+
args = ["start", "/wait", self.executable] + self.args
214+
else:
215+
args = ["cmd.exe", "/c", "start", "/wait", "conhost", self.executable] + self.args
210216
args.extend(["-scriptport", str(self.script_port), host])
211217
self.sp = subprocess.Popen(
212218
args,
213-
shell=True,
219+
shell=is_win10,
214220
stdin=subprocess.PIPE,
215221
stdout=subprocess.PIPE,
216222
stderr=subprocess.PIPE,
@@ -500,6 +506,30 @@ def read_all_screen(self):
500506
full_text = ""
501507
for ypos in range(self.model_dimensions["rows"]):
502508
full_text += self.string_get(ypos + 1, 1, self.model_dimensions["columns"])
509+
510+
# The following section is necessary for cross platform compatibility if the host application contains some special characters.
511+
# It replaces the unicode values with the corresponding characters to prevent positioning errors which are caused by the different client implementations (wc3270 vs. x3270)
512+
513+
# Replace various box drawing character patterns (multiple stages for different character types, I added them one by one to catch all cases in out tests)
514+
515+
# Stage 1: Replace specific multi-byte sequences
516+
full_text = full_text.replace(\x94\x80", "-") # Horizontal line
517+
full_text = full_text.replace(\x94\x82", "|") # Vertical line
518+
full_text = full_text.replace(\x94\x8c", "+") # Top-left corner
519+
full_text = full_text.replace(\x94\x90", "+") # Top-right corner
520+
full_text = full_text.replace(\x94\x94", "+") # Bottom-left corner
521+
full_text = full_text.replace(\x94\x98", "+") # Bottom-right corner
522+
523+
# Stage 2: Replace any remaining 'â' characters (not sure if this could cause issues if you really want the 'â' character)
524+
full_text = full_text.replace("â", "-")
525+
526+
# Only replace if full_text is a string (in the unit tests these are provided as an instance of MagicMock)
527+
if isinstance(full_text, str):
528+
# Stage 3: Replace Unicode box drawing range
529+
full_text = re.sub(r"[\u2500-\u257F]", "-", full_text)
530+
531+
# Stage 4: Replace any non-ASCII characters that might be box drawing
532+
full_text = re.sub(r"[^\x20-\x7E]", "-", full_text, flags=re.UNICODE)
503533
return full_text
504534

505535
def delete_field(self):

atest/mainframe.robot

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,15 +270,15 @@ Test Get Current Position
270270

271271
Test Get String Positions
272272
${positions} Get String Positions Welcome
273-
Should Be Equal ${{ [(1, 10)] }} ${positions}
273+
Should Be Equal ${{ [(1, 10), (9, 15)] }} ${positions}
274274

275275
Test Get String Positions Case-Insensitive
276276
${positions} Get String Positions Welcome ignore_case=True
277-
Should Be Equal ${{ [(1, 10), (9, 5)] }} ${positions}
277+
Should Be Equal ${{ [(1, 10), (9, 15)] }} ${positions}
278278

279279
Test Get String Positions As Dict
280280
${positions} Get String Positions Welcome As Dict
281-
Should Be Equal ${{ [{"ypos": 1, "xpos": 10}] }}
281+
Should Be Equal ${{ [{"ypos": 1, "xpos": 10}, {"ypos": 9, "xpos": 15}] }}
282282
... ${positions}
283283

284284
Test Get String Positions Without Result
@@ -297,10 +297,10 @@ Test Get String Positions Only After As Dict
297297

298298
Test Get String Positions Only After Case-Insensitive
299299
${positions} Get String Positions Only After 9 4 Welcome ignore_case=True
300-
Should Be Equal ${{ [(9, 5)] }} ${positions}
300+
Should Be Equal ${{ [(9, 15)] }} ${positions}
301301

302302
Test Get String Positions Only After Without Results
303-
${positions} Get String Positions Only After 9 5 Welcome ignore_case=True
303+
${positions} Get String Positions Only After 9 15 Welcome ignore_case=True
304304
Should Be Empty ${positions}
305305

306306
Test Get String Positions Only Before

0 commit comments

Comments
 (0)