tests: Consolidate version (2 vs. 3) and platform (CPython vs. PyPy) checks (#2376)

Fix logic in test_bytes_to_string

Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
diff --git a/tests/conftest.py b/tests/conftest.py
index d317c49..45a264a 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -205,7 +205,11 @@
         from pybind11_tests.eigen import have_eigen
     except ImportError:
         have_eigen = False
-    pypy = platform.python_implementation() == "PyPy"
+
+    # Provide simple `six`-like aliases.
+    pytest.PY2 = (sys.version_info.major == 2)
+    pytest.CPYTHON = (platform.python_implementation() == "CPython")
+    pytest.PYPY = (platform.python_implementation() == "PyPy")
 
     skipif = pytest.mark.skipif
     pytest.suppress = suppress
@@ -215,13 +219,13 @@
                                              reason="eigen and/or numpy are not installed")
     pytest.requires_eigen_and_scipy = skipif(
         not have_eigen or not scipy, reason="eigen and/or scipy are not installed")
-    pytest.unsupported_on_pypy = skipif(pypy, reason="unsupported on PyPy")
-    pytest.bug_in_pypy = pytest.mark.xfail(pypy, reason="bug in PyPy")
-    pytest.unsupported_on_pypy3 = skipif(pypy and sys.version_info.major >= 3,
+    pytest.unsupported_on_pypy = skipif(pytest.PYPY, reason="unsupported on PyPy")
+    pytest.bug_in_pypy = pytest.mark.xfail(pytest.PYPY, reason="bug in PyPy")
+    pytest.unsupported_on_pypy3 = skipif(pytest.PYPY and not pytest.PY2,
                                          reason="unsupported on PyPy3")
-    pytest.unsupported_on_pypy_lt_6 = skipif(pypy and sys.pypy_version_info[0] < 6,
+    pytest.unsupported_on_pypy_lt_6 = skipif(pytest.PYPY and sys.pypy_version_info[0] < 6,
                                              reason="unsupported on PyPy<6")
-    pytest.unsupported_on_py2 = skipif(sys.version_info.major < 3,
+    pytest.unsupported_on_py2 = skipif(pytest.PY2,
                                        reason="unsupported on Python 2.x")
     pytest.gc_collect = gc_collect
 
diff --git a/tests/test_buffers.py b/tests/test_buffers.py
index e264311..db1871e 100644
--- a/tests/test_buffers.py
+++ b/tests/test_buffers.py
@@ -1,15 +1,12 @@
 # -*- coding: utf-8 -*-
 import io
 import struct
-import sys
 
 import pytest
 
 from pybind11_tests import buffers as m
 from pybind11_tests import ConstructorStats
 
-PY3 = sys.version_info[0] >= 3
-
 pytestmark = pytest.requires_numpy
 
 with pytest.suppress(ImportError):
@@ -98,7 +95,7 @@
 def test_readonly_buffer():
     buf = m.BufferReadOnly(0x64)
     view = memoryview(buf)
-    assert view[0] == 0x64 if PY3 else b'd'
+    assert view[0] == b'd' if pytest.PY2 else 0x64
     assert view.readonly
 
 
@@ -106,7 +103,7 @@
 def test_selective_readonly_buffer():
     buf = m.BufferReadOnlySelect()
 
-    memoryview(buf)[0] = 0x64 if PY3 else b'd'
+    memoryview(buf)[0] = b'd' if pytest.PY2 else 0x64
     assert buf.value == 0x64
 
     io.BytesIO(b'A').readinto(buf)
@@ -114,6 +111,6 @@
 
     buf.readonly = True
     with pytest.raises(TypeError):
-        memoryview(buf)[0] = 0 if PY3 else b'\0'
+        memoryview(buf)[0] = b'\0' if pytest.PY2 else 0
     with pytest.raises(TypeError):
         io.BytesIO(b'1').readinto(buf)
diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py
index af44bda..c905766 100644
--- a/tests/test_builtin_casters.py
+++ b/tests/test_builtin_casters.py
@@ -115,13 +115,19 @@
     """Tests the ability to pass bytes to C++ string-accepting functions.  Note that this is
     one-way: the only way to return bytes to Python is via the pybind11::bytes class."""
     # Issue #816
-    import sys
-    byte = bytes if sys.version_info[0] < 3 else str
 
-    assert m.strlen(byte("hi")) == 2
-    assert m.string_length(byte("world")) == 5
-    assert m.string_length(byte("a\x00b")) == 3
-    assert m.strlen(byte("a\x00b")) == 1  # C-string limitation
+    def to_bytes(s):
+        if pytest.PY2:
+            b = s
+        else:
+            b = s.encode("utf8")
+        assert isinstance(b, bytes)
+        return b
+
+    assert m.strlen(to_bytes("hi")) == 2
+    assert m.string_length(to_bytes("world")) == 5
+    assert m.string_length(to_bytes("a\x00b")) == 3
+    assert m.strlen(to_bytes("a\x00b")) == 1  # C-string limitation
 
     # passing in a utf8 encoded string should work
     assert m.string_length(u'💩'.encode("utf8")) == 4
@@ -187,12 +193,11 @@
 
 def test_integer_casting():
     """Issue #929 - out-of-range integer values shouldn't be accepted"""
-    import sys
     assert m.i32_str(-1) == "-1"
     assert m.i64_str(-1) == "-1"
     assert m.i32_str(2000000000) == "2000000000"
     assert m.u32_str(2000000000) == "2000000000"
-    if sys.version_info < (3,):
+    if pytest.PY2:
         assert m.i32_str(long(-1)) == "-1"  # noqa: F821 undefined name 'long'
         assert m.i64_str(long(-1)) == "-1"  # noqa: F821 undefined name 'long'
         assert m.i64_str(long(-999999999999)) == "-999999999999"  # noqa: F821 undefined name
@@ -214,7 +219,7 @@
         m.i32_str(3000000000)
     assert "incompatible function arguments" in str(excinfo.value)
 
-    if sys.version_info < (3,):
+    if pytest.PY2:
         with pytest.raises(TypeError) as excinfo:
             m.u32_str(long(-1))  # noqa: F821 undefined name 'long'
         assert "incompatible function arguments" in str(excinfo.value)
diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py
index dad40db..df354ad 100644
--- a/tests/test_kwargs_and_defaults.py
+++ b/tests/test_kwargs_and_defaults.py
@@ -2,11 +2,6 @@
 import pytest
 from pybind11_tests import kwargs_and_defaults as m
 
-import platform
-import sys
-
-pypy = platform.python_implementation() == "PyPy"
-
 
 def test_function_signatures(doc):
     assert doc(m.kw_func0) == "kw_func0(arg0: int, arg1: int) -> str"
@@ -151,7 +146,7 @@
     """
 
 
-@pytest.mark.xfail(pypy and sys.version_info < (3, 0),
+@pytest.mark.xfail(pytest.PYPY and pytest.PY2,
                    reason="PyPy2 doesn't seem to double count")
 def test_args_refcount():
     """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular
diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py
index 1c7b1dd..e5d8355 100644
--- a/tests/test_pytypes.py
+++ b/tests/test_pytypes.py
@@ -113,7 +113,7 @@
     assert m.bytes_from_str().decode() == "bar"
 
     assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format(
-        "bytes" if sys.version_info[0] == 3 else "str"
+        "str" if pytest.PY2 else "bytes"
     )
 
 
@@ -324,7 +324,7 @@
     view = method(*args)
     assert isinstance(view, memoryview)
     assert view.format == fmt
-    if isinstance(expected_view, bytes) or sys.version_info[0] >= 3:
+    if isinstance(expected_view, bytes) or not pytest.PY2:
         view_as_list = list(view)
     else:
         # Using max to pick non-zero byte (big-endian vs little-endian).
@@ -352,7 +352,7 @@
     view = m.test_memoryview_from_buffer_empty_shape()
     assert isinstance(view, memoryview)
     assert view.format == 'B'
-    if sys.version_info.major < 3:
+    if pytest.PY2:
         # Python 2 behavior is weird, but Python 3 (the future) is fine.
         # PyPy3 has <memoryview, while CPython 2 has <memory
         assert bytes(view).startswith(b'<memory')
@@ -366,14 +366,14 @@
 
 
 def test_test_memoryview_from_buffer_nullptr():
-    if sys.version_info.major < 3:
+    if pytest.PY2:
         m.test_memoryview_from_buffer_nullptr()
     else:
         with pytest.raises(ValueError):
             m.test_memoryview_from_buffer_nullptr()
 
 
-@pytest.mark.skipif(sys.version_info.major < 3, reason='API not available')
+@pytest.unsupported_on_py2
 def test_memoryview_from_memory():
     view = m.test_memoryview_from_memory()
     assert isinstance(view, memoryview)
diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py
index baa0f4e..4cb4591 100644
--- a/tests/test_stl_binders.py
+++ b/tests/test_stl_binders.py
@@ -1,6 +1,5 @@
 # -*- coding: utf-8 -*-
 import pytest
-import sys
 from pybind11_tests import stl_binders as m
 
 with pytest.suppress(ImportError):
@@ -77,7 +76,7 @@
     assert v[1] == 2
     v[2] = 5
     mv = memoryview(v)  # We expose the buffer interface
-    if sys.version_info.major > 2:
+    if not pytest.PY2:
         assert mv[2] == 5
         mv[2] = 6
     else:
@@ -85,7 +84,7 @@
         mv[2] = '\x06'
     assert v[2] == 6
 
-    if sys.version_info.major > 2:
+    if not pytest.PY2:
         mv = memoryview(b)
         v = m.VectorUChar(mv[::2])
         assert v[1] == 3