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

Mintty tests #1

Merged
merged 2 commits into from
Oct 26, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
25 changes: 17 additions & 8 deletions colorama/ansitowin32.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
from io import UnsupportedOperation
import re
import sys
import os
Expand All @@ -19,6 +20,12 @@
winterm = WinTerm()


class FileNameInfo(ctypes.Structure):
"""Struct to get FileNameInfo from the win32api"""
_fields_ = [('FileNameLength', ctypes.c_ulong),
('FileName', ctypes.c_wchar * 40)]


def is_msys_cygwin_tty(stream):
if not hasattr(stream, "fileno"):
return False
Expand All @@ -29,24 +36,26 @@ def is_msys_cygwin_tty(stream):
if msvcrt is None:
return False

fileno = stream.fileno()
handle = msvcrt.get_osfhandle(fileno)
FileNameInfo = 2
try:
fileno = stream.fileno()
except UnsupportedOperation:
# StringIO for example has the fileno attribute but doesn't support calling it
return False

class FILE_NAME_INFO(ctypes.Structure):
_fields_ = [('FileNameLength', ctypes.c_ulong),
('FileName', ctypes.c_wchar * 40)]
handle = msvcrt.get_osfhandle(fileno)
FILE_NAME_INFO = 2

info = FILE_NAME_INFO()
info = FileNameInfo()
ret = ctypes.windll.kernel32.GetFileInformationByHandleEx(handle,
FileNameInfo,
FILE_NAME_INFO,
ctypes.byref(info),
ctypes.sizeof(info))
if ret == 0:
return False

msys_pattern = r"\\msys-[0-9a-f]{16}-pty\d-(to|from)-master"
cygwin_pattern = r"\\cygwin-[0-9a-f]{16}-pty\d-(to|from)-master"

return re.match(msys_pattern, info.FileName) is not None or \
re.match(cygwin_pattern, info.FileName) is not None

Expand Down
72 changes: 69 additions & 3 deletions colorama/tests/isatty_test.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
import sys
from unittest import TestCase, main
from io import StringIO
from unittest import TestCase, main, skipUnless

from ..ansitowin32 import StreamWrapper, AnsiToWin32
from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY
from mock import patch, PropertyMock

from ..ansitowin32 import StreamWrapper, AnsiToWin32, is_msys_cygwin_tty, FileNameInfo
from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY, StreamNonTTYWithFileNo


def is_a_tty(stream):
Expand Down Expand Up @@ -52,6 +55,69 @@ def test_withPycharmStreamWrapped(self):
self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty())
self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty())

@patch("colorama.ansitowin32.is_msys_cygwin_tty", return_value=False)
def test_isattyCorrectForMintty(self, mock_fn):
self.assertFalse(is_a_tty(StreamTTY()))
self.assertFalse(is_a_tty(StreamNonTTY()))
mock_fn.assert_called_once()

@patch("colorama.ansitowin32.is_msys_cygwin_tty", return_value=True)
def test_isattyCorrectForMintty(self, mock_fn):
self.assertTrue(is_a_tty(StreamNonTTY()))
self.assertTrue(is_a_tty(StreamTTY()))
mock_fn.assert_called()

class MinttyTest(TestCase):
"""Tests for the detection of mintty / msys/ cygwin

They're arguably a little brittle to the exact detection implementation, so can be refactored
if the implementation changes.
"""

@patch("colorama.ansitowin32.msvcrt", None)
def test_falseNotOnWindows(self):
self.assertFalse(is_msys_cygwin_tty(StreamNonTTYWithFileNo()))

def test_falseForIoString(self):
self.assertFalse(is_msys_cygwin_tty(StringIO()))

@skipUnless(sys.platform.startswith("win"), "requires Windows")
@patch("ctypes.windll.kernel32", None)
def test_falseIfKernelModuleUnavailable(self):
self.assertFalse(is_msys_cygwin_tty(StreamNonTTYWithFileNo()))

@skipUnless(sys.platform.startswith("win"), "requires Windows")
@patch("ctypes.windll.kernel32.GetFileInformationByHandleEx", return_value=0)
@patch("msvcrt.get_osfhandle", return_value=10)
def test_falseIfWin32CallFails(self, mock_win32_call, mock_handle_call):
self.assertFalse(is_msys_cygwin_tty(StreamNonTTYWithFileNo()))

@skipUnless(sys.platform.startswith("win"), "requires Windows")
@patch("ctypes.windll.kernel32.GetFileInformationByHandleEx", return_value=1)
@patch("msvcrt.get_osfhandle", return_value=1000)
def test_trueForMsys(self, mock_file_call, mock_handle_call):

with patch.object(FileNameInfo, "FileName", new_callable=PropertyMock) as mock_filename_info:
mock_filename_info.return_value = r"\msys-0000000000000000-pty3-to-master"
self.assertTrue(is_msys_cygwin_tty(StreamNonTTYWithFileNo()))

@skipUnless(sys.platform.startswith("win"), "requires Windows")
@patch("ctypes.windll.kernel32.GetFileInformationByHandleEx", return_value=1)
@patch("msvcrt.get_osfhandle", return_value=1000)
def test_trueForCygwin(self, mock_file_call, mock_handle_call):

with patch.object(FileNameInfo, "FileName", new_callable=PropertyMock) as mock_filename_info:
mock_filename_info.return_value = r"\cygwin-0000000000000000-pty3-to-master"
self.assertTrue(is_msys_cygwin_tty(StreamNonTTYWithFileNo()))

@skipUnless(sys.platform.startswith("win"), "requires Windows")
@patch("ctypes.windll.kernel32.GetFileInformationByHandleEx", return_value=1)
@patch("msvcrt.get_osfhandle", return_value=1000)
def test_falseForAnythingElse(self, mock_file_call, mock_handle_call):

with patch.object(FileNameInfo, "FileName", new_callable=PropertyMock) as mock_filename_info:
mock_filename_info.return_value = r"\random-0000000000000000-pty3-to-master"
self.assertFalse(is_msys_cygwin_tty(StreamNonTTYWithFileNo()))

if __name__ == '__main__':
main()
7 changes: 7 additions & 0 deletions colorama/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ class StreamNonTTY(StringIO):
def isatty(self):
return False

class StreamNonTTYWithFileNo(StringIO):
def isatty(self):
return False

def fileno(self):
return 10

@contextmanager
def osname(name):
orig = os.name
Expand Down