# Test cases for strings (compile and run)

[case testStrBasics]
from typing import Tuple
def f() -> str:
    return 'some string'
def g() -> str:
    return 'some\a \v \t \x7f " \n \0string 🐍'
def tostr(x: int) -> str:
    return str(x)
def booltostr(x: bool) -> str:
    return str(x)
def concat(x: str, y: str) -> str:
    return x + y
def eq(x: str) -> int:
    if x == 'foo':
        return 0
    elif x != 'bar':
        return 1
    return 2
def match(x: str, y: str) -> Tuple[bool, bool]:
    return (x.startswith(y), x.endswith(y))

[file driver.py]
from native import f, g, tostr, booltostr, concat, eq, match
import sys

assert f() == 'some string'
assert f() is sys.intern('some string')
assert g() == 'some\a \v \t \x7f " \n \0string 🐍'
assert tostr(57) == '57'
assert concat('foo', 'bar') == 'foobar'
assert booltostr(True) == 'True'
assert booltostr(False) == 'False'
assert eq('foo') == 0
assert eq('zar') == 1
assert eq('bar') == 2

assert int(tostr(0)) == 0
assert int(tostr(20)) == 20
assert match('', '') == (True, True)
assert match('abc', '') == (True, True)
assert match('abc', 'a') == (True, False)
assert match('abc', 'c') == (False, True)
assert match('', 'abc') == (False, False)

[case testStringOps]
from typing import List, Optional

def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]:
    if sep is not None:
        if max_split is not None:
            return s.split(sep, max_split)
        else:
            return s.split(sep)
    return s.split()

ss = "abc abcd abcde abcdef"

def test_split() -> None:
    assert do_split(ss) == ["abc", "abcd", "abcde", "abcdef"]
    assert do_split(ss, " ") == ["abc", "abcd", "abcde", "abcdef"]
    assert do_split(ss, "-") == ["abc abcd abcde abcdef"]
    assert do_split(ss, " ", -1) == ["abc", "abcd", "abcde", "abcdef"]
    assert do_split(ss, " ", 0) == ["abc abcd abcde abcdef"]
    assert do_split(ss, " ", 1) == ["abc", "abcd abcde abcdef"]
    assert do_split(ss, " ", 2) == ["abc", "abcd", "abcde abcdef"]

def getitem(s: str, index: int) -> str:
    return s[index]

from testutil import assertRaises

s = "abc"

def test_getitem() -> None:
    assert getitem(s, 0) == "a"
    assert getitem(s, 1) == "b"
    assert getitem(s, 2) == "c"
    assert getitem(s, -3) == "a"
    assert getitem(s, -2) == "b"
    assert getitem(s, -1) == "c"
    with assertRaises(IndexError, "string index out of range"):
        getitem(s, 4)
    with assertRaises(IndexError, "string index out of range"):
        getitem(s, -4)

def str_to_int(s: str, base: Optional[int] = None) -> int:
    if base:
        return int(s, base)
    else:
        return int(s)

def test_str_to_int() -> None:
    assert str_to_int("1") == 1
    assert str_to_int("10") == 10
    assert str_to_int("a", 16) == 10
    assert str_to_int("1a", 16) == 26
    with assertRaises(ValueError, "invalid literal for int() with base 10: 'xyz'"):
        str_to_int("xyz")

def test_slicing() -> None:
    # Use dummy adds to avoid constant folding
    zero = int()
    two = zero + 2
    s = "foobar" + str()
    assert s[two:] == "obar"
    assert s[:two] == "fo"
    assert s[two:-two] == "ob"
    assert s[two:two] == ""
    assert s[two:two + 1] == "o"
    assert s[-two:] == "ar"
    assert s[:-two] == "foob"
    assert s[:] == "foobar"
    assert s[two:333] == "obar"
    assert s[333:two] == ""
    assert s[two:-333] == ""
    assert s[-333:two] == "fo"
    big_int: int = 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000
    assert s[1:big_int] == "oobar"
    assert s[big_int:] == ""
    assert s[-big_int:-1] == "fooba"

def test_str_replace() -> None:
    a = "foofoofoo"
    assert a.replace("foo", "bar") == "barbarbar"
    assert a.replace("foo", "bar", -1) == "barbarbar"
    assert a.replace("foo", "bar", 1) == "barfoofoo"
    assert a.replace("foo", "bar", 4) == "barbarbar"
    assert a.replace("aaa", "bar") == "foofoofoo"
    assert a.replace("ofo", "xyzw") == "foxyzwxyzwo"

def is_true(x: str) -> bool:
    if x:
        return True
    else:
        return False

def is_true2(x: str) -> bool:
    return bool(x)

def is_false(x: str) -> bool:
    if not x:
        return True
    else:
        return False

def test_str_to_bool() -> None:
    assert is_false('')
    assert not is_true('')
    assert not is_true2('')
    for x in 'a', 'foo', 'bar', 'some string':
        assert is_true(x)
        assert is_true2(x)
        assert not is_false(x)

def test_str_min_max() -> None:
    x: str = 'aaa'
    y: str = 'bbb'
    z: str = 'aa'
    assert min(x, y) == 'aaa'
    assert min(x, z) == 'aa'
    assert max(x, y) == 'bbb'
    assert max(x, z) == 'aaa'

[case testStringFormattingCStyle]
[typing fixtures/typing-full.pyi]
from typing import Tuple

var = 'mypyc'
num = 20

def test_basics() -> None:
    assert 'Hello %s, this is a test' % var == "Hello mypyc, this is a test"
    assert 'Hello %s %d, this is a test' % (var, num) == "Hello mypyc 20, this is a test"
    t: Tuple[str, int] = (var, num)
    assert 'Hello %s %d, this is a test' % t == "Hello mypyc 20, this is a test"

    large_num = 2**65
    assert 'number: %d' % large_num == 'number: 36893488147419103232'
    neg_num = -3
    assert 'negative integer: %d' % neg_num == 'negative integer: -3'
    assert 'negative integer: %d' % (-large_num) == 'negative integer: -36893488147419103232'

    bool_var1 = True
    bool_var2 = False
    assert 'bool: %s, %s' % (bool_var1, bool_var2) == 'bool: True, False'

    float_num = 123.4
    assert '%f' % float_num == '123.400000'
    assert '%.2f' % float_num == '123.40'
    assert '%.5f' % float_num == '123.40000'
    assert '%10.2f' % float_num == '    123.40'
    assert '%10.5f' % float_num == ' 123.40000'
    assert '%010.5f' % float_num == '0123.40000'
    assert '%015.5f' % float_num == '000000123.40000'
    assert '%e' % float_num == '1.234000e+02'
    large_float = 1.23e30
    large_float2 = 1234123412341234123400000000000000000
    small_float = 1.23e-20
    assert '%f, %f, %f' % (small_float, large_float, large_float2) == \
           '0.000000, 1229999999999999959718843908096.000000, 1234123412341234169005079998930878464.000000'
    assert '%s, %s, %s' % (small_float, large_float, large_float2) == \
           '1.23e-20, 1.23e+30, 1234123412341234123400000000000000000'
    assert '%d, %d, %d' % (small_float, large_float, large_float2) == \
           '0, 1229999999999999959718843908096, 1234123412341234123400000000000000000'

    nan_num = float('nan')
    inf_num = float('inf')
    assert '%s, %s' % (nan_num, inf_num) == 'nan, inf'
    assert '%f, %f' % (nan_num, inf_num) == 'nan, inf'

[case testFStrings]
import decimal
from datetime import datetime

var = 'mypyc'
num = 20

def test_fstring_basics() -> None:
    assert f'Hello {var}, this is a test' == "Hello mypyc, this is a test"

    large_num = 2**65
    assert f'number: {large_num}' == 'number: 36893488147419103232'
    neg_num = -3
    assert f'negative integer: {neg_num}' == 'negative integer: -3'
    assert f'negative integer: {-large_num}' == 'negative integer: -36893488147419103232'

    bool_var1 = True
    bool_var2 = False
    assert f'bool: {bool_var1}, {bool_var2}' == 'bool: True, False'

    x = bytes([1, 2, 3, 4])
    # assert f'bytes: {x}' == "bytes: b'\\x01\\x02\\x03\\x04'"
    # error: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes

    float_num = 123.4
    assert f'{float_num}' == '123.4'
    assert f'{float_num:.2f}' == '123.40'
    assert f'{float_num:.5f}' == '123.40000'
    assert f'{float_num:>10.2f}' == '    123.40'
    assert f'{float_num:>10.5f}' == ' 123.40000'
    assert f'{float_num:>010.5f}' == '0123.40000'
    assert f'{float_num:>015.5f}' == '000000123.40000'
    assert f'{float_num:e}' == '1.234000e+02'

    large_float = 1.23e30
    large_float2 = 1234123412341234123400000000000000000
    small_float = 1.23e-20
    assert f'{small_float}, {large_float}, {large_float2}' == '1.23e-20, 1.23e+30, 1234123412341234123400000000000000000'
    nan_num = float('nan')
    inf_num = float('inf')
    assert f'{nan_num}, {inf_num}' == 'nan, inf'

# F-strings would be translated into ''.join[string literals, format method call, ...] in mypy AST.
# Currently we are using a str.join specializer for f-string speed up. We might not cover all cases
# and the rest ones should fall back to a normal str.join method call.
# TODO: Once we have a new pipeline for f-strings, this test case can be moved to testStringOps.
def test_str_join() -> None:
    var = 'mypyc'
    num = 10
    assert ''.join(['a', 'b', '{}'.format(var), 'c']) == 'abmypycc'
    assert ''.join(['a', 'b', '{:{}}'.format(var, ''), 'c']) == 'abmypycc'
    assert ''.join(['a', 'b', '{:{}}'.format(var, '>10'), 'c']) == 'ab     mypycc'
    assert ''.join(['a', 'b', '{:{}}'.format(var, '>{}'.format(num)), 'c']) == 'ab     mypycc'
    assert var.join(['a', '{:{}}'.format(var, ''), 'b']) == 'amypycmypycmypycb'
    assert ','.join(['a', '{:{}}'.format(var, ''), 'b']) == 'a,mypyc,b'
    assert ''.join(['x', var]) == 'xmypyc'

class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f'{self.name} is {self.age} years old.'

def test_fstring_datatype() -> None:
    u = A('John Doe', 14)
    assert f'{u}' == 'John Doe is 14 years old.'
    d = {'name': 'John Doe', 'age': 14}
    assert f'{d}' == "{'name': 'John Doe', 'age': 14}"

def test_fstring_escape() -> None:
    assert f"{'inside'}" == 'inside'
    assert f'{"inside"}' == 'inside'
    assert f"""inside""" == 'inside'
    assert f'''inside''' == 'inside'
    assert f"\"{'inside'}\"" == '"inside"'
    assert f'\'{"inside"}\'' == "'inside'"

    assert f'{{10}}' == '{10}'
    assert f'{{10 + 10}}' == '{10 + 10}'
    assert f'{{{10 + 10}}}' == '{20}'
    assert f'{{{{10 + 10}}}}' == '{{10 + 10}}'

def test_fstring_conversion() -> None:
    assert f'Hello {var!r}' == "Hello 'mypyc'"
    # repr() is equivalent to !r
    assert f'Hello {repr(var)}' == "Hello 'mypyc'"

    assert f'Hello {var!a}' == "Hello 'mypyc'"
    # ascii() is equivalent to !a
    assert f'Hello {ascii(var)}' == "Hello 'mypyc'"

    tmp_str = """this
    is a new line."""
    assert f'Test: {tmp_str!a}' == "Test: 'this\\n    is a new line.'"

    s = 'test: āĀēĒčČ..šŠūŪžŽ'
    assert f'{s}' == 'test: āĀēĒčČ..šŠūŪžŽ'
    assert f'{s!a}' == "'test: \\u0101\\u0100\\u0113\\u0112\\u010d\\u010c..\\u0161\\u0160\\u016b\\u016a\\u017e\\u017d'"

    assert f'Hello {var!s}' == 'Hello mypyc'
    assert f'Hello {num!s}' == 'Hello 20'

def test_fstring_align() -> None:
    assert f'Hello {var:>20}' == "Hello                mypyc"
    assert f'Hello {var!r:>20}' == "Hello              'mypyc'"
    assert f'Hello {var:>{num}}' == "Hello                mypyc"
    assert f'Hello {var!r:>{num}}' == "Hello              'mypyc'"

def test_fstring_multi() -> None:
    assert f'Hello {var}, hello again {var}' == "Hello mypyc, hello again mypyc"
    a = 'py'
    s = f'my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}'
    assert s == 'mypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypy'

def test_fstring_python_doc() -> None:
    name = 'Fred'
    assert f"He said his name is {name!r}." == "He said his name is 'Fred'."
    assert f"He said his name is {repr(name)}." == "He said his name is 'Fred'."

    width = 10
    precision = 4
    value = decimal.Decimal('12.34567')
    assert f'result: {value:{width}.{precision}}' == 'result:      12.35' # nested field

    today = datetime(year=2017, month=1, day=27)
    assert f'{today:%B %d, %Y}' == 'January 27, 2017'  # using date format specifier

    number = 1024
    assert f'{number:#0x}' == '0x400' # using integer format specifier

[case testStringFormatMethod]
from typing import Tuple

def test_format_method_basics() -> None:
    x = str()
    assert 'x{}'.format(x) == 'x'
    assert 'ā{}'.format(x) == 'ā'
    assert '😀{}'.format(x) == '😀'
    assert ''.format() == ''
    assert 'abc'.format() == 'abc'
    assert '{}{}'.format(1, 2) == '12'

    name = 'Eric'
    age = 14
    assert "My name is {name}, I'm {age}.".format(name=name, age=age) == "My name is Eric, I'm 14."
    assert "My name is {A}, I'm {B}.".format(A=name, B=age) == "My name is Eric, I'm 14."
    assert "My name is {}, I'm {B}.".format(name, B=age) == "My name is Eric, I'm 14."

    bool_var1 = True
    bool_var2 = False
    assert 'bool: {}, {}'.format(bool_var1, bool_var2) == 'bool: True, False'

def test_format_method_empty_braces() -> None:
    name = 'Eric'
    age = 14

    assert 'Hello, {}!'.format(name) == 'Hello, Eric!'
    assert '{}'.format(name) == 'Eric'
    assert '{}! Hi!'.format(name) == 'Eric! Hi!'
    assert '{}, Hi, {}'.format(name, name) == 'Eric, Hi, Eric'
    assert 'Hi! {}'.format(name) == 'Hi! Eric'
    assert "Hi, I'm {}. I'm {}.".format(name, age) == "Hi, I'm Eric. I'm 14."

    assert '{{}}'.format() == '{}'
    assert '{{{{}}}}'.format() == '{{}}'
    assert '{{}}{}'.format(name) == '{}Eric'
    assert 'Hi! {{{}}}'.format(name) == 'Hi! {Eric}'
    assert 'Hi! {{ {}'.format(name) == 'Hi! { Eric'
    assert 'Hi! {{ {} }}}}'.format(name) == 'Hi! { Eric }}'

def test_format_method_numbers() -> None:
    s = 'int: {0:d};  hex: {0:x};  oct: {0:o};  bin: {0:b}'.format(-233)
    assert s == 'int: -233;  hex: -e9;  oct: -351;  bin: -11101001'
    num = 2**65
    s = 'int: {0:d};  hex: {0:x};  oct: {0:o};  bin: {0:b}'.format(num)
    assert s == 'int: 36893488147419103232;  hex: 20000000000000000;  oct: 4000000000000000000000;  bin: 100000000000000000000000000000000000000000000000000000000000000000'
    s = 'int: {0:d};  hex: {0:x};  oct: {0:o};  bin: {0:b}'.format(-num)
    assert s == 'int: -36893488147419103232;  hex: -20000000000000000;  oct: -4000000000000000000000;  bin: -100000000000000000000000000000000000000000000000000000000000000000'

    large_num = 2**65
    assert 'number: {}'.format(large_num) == 'number: 36893488147419103232'
    neg_num = -3
    assert 'negative integer: {}'.format(neg_num) == 'negative integer: -3'
    assert 'negative integer: {}'.format(-large_num) == 'negative integer: -36893488147419103232'

    large_float = 1.23e30
    large_float2 = 1234123412341234123400000000000000000
    small_float = 1.23e-20
    assert '{}, {}, {}'.format(small_float, large_float, large_float2) == '1.23e-20, 1.23e+30, 1234123412341234123400000000000000000'
    nan_num = float('nan')
    inf_num = float('inf')
    assert '{}, {}'.format(nan_num, inf_num) == 'nan, inf'

def format_args(*args: int) -> str:
    return 'x{}y{}'.format(*args)
def format_kwargs(**kwargs: int) -> str:
    return 'c{x}d{y}'.format(**kwargs)
def format_args_self(*args: int) -> str:
    return '{}'.format(args)
def format_kwargs_self(**kwargs: int) -> str:
    return '{}'.format(kwargs)

def test_format_method_args() -> None:
    assert format_args(10, 2) == 'x10y2'
    assert format_args_self(10, 2) == '(10, 2)'
    assert format_kwargs(x=10, y=2) == 'c10d2'
    assert format_kwargs(x=10, y=2, z=1) == 'c10d2'
    assert format_kwargs_self(x=10, y=2, z=1) == "{'x': 10, 'y': 2, 'z': 1}"

def test_format_method_different_kind() -> None:
    s1 = "Literal['😀']"
    assert 'Revealed type is {}'.format(s1) == "Revealed type is Literal['😀']"
    s2 = "Revealed type is"
    assert "{} Literal['😀']".format(s2) == "Revealed type is Literal['😀']"
    s3 = "测试："
    assert "{}{} {}".format(s3, s2, s1) == "测试：Revealed type is Literal['😀']"
    assert "Test: {}{}".format(s3, s1) == "Test: 测试：Literal['😀']"
    assert "Test: {}{}".format(s3, s2) == "Test: 测试：Revealed type is"

def test_format_method_nested() -> None:
    var = 'mypyc'
    num = 10
    assert '{:{}}'.format(var, '') == 'mypyc'
    assert '{:{}}'.format(var, '>10') == '     mypyc'
    assert '{:{}}'.format(var, '>{}'.format(num)) == '     mypyc'

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __str__(self):
        return 'Point({self.x}, {self.y})'.format(self=self)

# Format examples from Python doc
# https://docs.python.org/3/library/string.html#formatexamples
def test_format_method_python_doc() -> None:
    # Accessing arguments by position:
    assert '{0}, {1}, {2}'.format('a', 'b', 'c') == 'a, b, c'
    assert '{}, {}, {}'.format('a', 'b', 'c') == 'a, b, c'
    assert '{2}, {1}, {0}'.format('a', 'b', 'c') == 'c, b, a'
    assert '{2}, {1}, {0}'.format(*'abc') == 'c, b, a'        # unpacking argument sequence
    # assert '{0}{1}{0}'.format('abra', 'cad') = 'abracadabra'  # arguments' indices can be repeated

    # Accessing arguments by name:
    s = 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N', longitude='-115.81W')
    assert s == 'Coordinates: 37.24N, -115.81W'
    coord = {'latitude': '37.24N', 'longitude': '-115.81W'}
    assert 'Coordinates: {latitude}, {longitude}'.format(**coord) == 'Coordinates: 37.24N, -115.81W'

    # Accessing arguments’ attributes:
    assert str(Point(4, 2)) == 'Point(4, 2)'

    # Accessing arguments’ items:
    coord2 = (3, 5)
    assert 'X: {0[0]};  Y: {0[1]}'.format(coord2) == 'X: 3;  Y: 5'

    # Replacing %s and %r:
    s = "repr() shows quotes: {!r}; str() doesn't: {!s}".format('test1', 'test2')
    assert s == "repr() shows quotes: 'test1'; str() doesn't: test2"

    # Aligning the text and specifying a width:
    assert '{:<30}'.format('left aligned') == 'left aligned                  '
    assert '{:>30}'.format('right aligned') == '                 right aligned'
    assert '{:^30}'.format('centered') == '           centered           '
    assert '{:*^30}'.format('centered') == '***********centered***********' # use '*' as a fill char

    # Replacing %+f, %-f, and % f and specifying a sign:
    assert '{:+f}; {:+f}'.format(3.14, -3.14) == '+3.140000; -3.140000' # show it always
    assert '{: f}; {: f}'.format(3.14, -3.14) == ' 3.140000; -3.140000' # show a space for positive numbers
    assert '{:-f}; {:-f}'.format(3.14, -3.14) == '3.140000; -3.140000' # show only the minus -- same as '{:f}; {:f}'

    # Replacing %x and %o and converting the value to different bases:
    s = 'int: {0:d};  hex: {0:x};  oct: {0:o};  bin: {0:b}'.format(42) # format also supports binary numbers
    assert s == 'int: 42;  hex: 2a;  oct: 52;  bin: 101010'
    s = 'int: {0:d};  hex: {0:#x};  oct: {0:#o};  bin: {0:#b}'.format(42) # with 0x, 0o, or 0b as prefix:
    assert s == 'int: 42;  hex: 0x2a;  oct: 0o52;  bin: 0b101010'

    # Using the comma as a thousands separator:
    assert '{:,}'.format(1234567890) == '1,234,567,890'

    # Expressing a percentage:
    points = 19.0
    total = 22.0
    assert 'Correct answers: {:.2%}'.format(points/total) == 'Correct answers: 86.36%'

    # Using type-specific formatting:
    import datetime
    d = datetime.datetime(2010, 7, 4, 12, 15, 58)
    assert '{:%Y-%m-%d %H:%M:%S}'.format(d) == '2010-07-04 12:15:58'

    # Nesting arguments and more complex examples:
    tmp_strs = []
    for align, text in zip('<^>', ['left', 'center', 'right']):
        tmp_strs.append('{0:{fill}{align}16}'.format(text, fill=align, align=align))
    assert tmp_strs == ['left<<<<<<<<<<<<', '^^^^^center^^^^^', '>>>>>>>>>>>right']

    octets = [192, 168, 0, 1]
    assert '{:02X}{:02X}{:02X}{:02X}'.format(*octets) == 'C0A80001'

    width = 5
    tmp_strs = []
    for num in range(5,12):
        tmp_str = ''
        for base in 'dXob':
            tmp_str += ('{0:{width}{base}}'.format(num, base=base, width=width))
        tmp_strs.append(tmp_str)
    assert tmp_strs == ['    5    5    5  101',\
                        '    6    6    6  110',\
                        '    7    7    7  111',\
                        '    8    8   10 1000',\
                        '    9    9   11 1001',\
                        '   10    A   12 1010',\
                        '   11    B   13 1011']

[case testChr]
# Some test cases are from https://docs.python.org/3/howto/unicode.html

def try_invalid(x: int) -> bool:
    try:
        chr(x + int())
        return False
    except ValueError:
        return True

def test_chr() -> None:
    assert chr(57344) == '\ue000'
    assert chr(0) == '\x00'
    assert chr(65) == 'A'
    assert chr(150) == '\x96'
    try:
        chr(-1)
        assert False
    except ValueError:
        pass
    try:
        chr(1114112)
        assert False
    except ValueError:
        pass
    assert chr(1114111) == '\U0010ffff'
    x = 0
    assert chr(x + int()) == '\x00'
    x = 100
    assert chr(x + int()) == 'd'
    x = 150
    assert chr(x + int()) == '\x96'
    x = 257
    assert chr(x + int()) == 'ā'
    x = 65537
    assert chr(x + int()) == '𐀁'
    assert try_invalid(-1)
    assert try_invalid(1114112)

[case testOrd]
from testutil import assertRaises

def test_ord() -> None:
    assert ord(' ') == 32
    assert ord(' ' + str()) == 32
    assert ord('\x00') == 0
    assert ord('\x00' + str()) == 0
    assert ord('\ue000') == 57344
    assert ord('\ue000' + str()) == 57344
    s = "a\xac\u1234\u20ac\U00010000"
    #     ^^^^ two-digit hex escape
    #         ^^^^^^ four-digit Unicode escape
    #                     ^^^^^^^^^^ eight-digit Unicode escape
    l1 = [ord(c) for c in s]
    assert l1 == [97, 172, 4660, 8364, 65536]
    u = 'abcdé'
    assert ord(u[-1]) == 233
    assert ord(b'a') == 97
    assert ord(b'a' + bytes()) == 97
    u2 = '\U0010ffff' + str()
    assert ord(u2) == 1114111
    assert ord('\U0010ffff') == 1114111
    with assertRaises(TypeError, "ord() expected a character, but a string of length 2 found"):
        ord('aa')
    with assertRaises(TypeError):
        ord('')

[case testDecode]
def test_decode() -> None:
    assert "\N{GREEK CAPITAL LETTER DELTA}" == '\u0394'
    assert "\u0394" == "\u0394"
    assert "\U00000394" == '\u0394'
    assert b'\x80abc'.decode('utf-8', 'replace') == '\ufffdabc'
    assert b'\x80abc'.decode('utf-8', 'backslashreplace') == '\\x80abc'
    assert b'abc'.decode() == 'abc'
    assert b'abc'.decode('utf-8') == 'abc'
    assert b'\x80abc'.decode('utf-8', 'ignore') == 'abc'
    assert b'\x80abc'.decode('UTF-8', 'ignore') == 'abc'
    assert b'\x80abc'.decode('Utf-8', 'ignore') == 'abc'
    assert b'\x80abc'.decode('utf_8', 'ignore') == 'abc'
    assert b'\x80abc'.decode('latin1', 'ignore') == '\x80abc'
    assert b'\xd2\xbb\xb6\xfe\xc8\xfd'.decode('gbk', 'ignore') == '一二三'
    assert b'\xd2\xbb\xb6\xfe\xc8\xfd'.decode('latin1', 'ignore') == 'Ò»¶þÈý'
    assert b'Z\xc3\xbcrich'.decode("utf-8") == 'Zürich'
    try:
        b'Z\xc3\xbcrich'.decode('ascii')
        assert False
    except UnicodeDecodeError:
        pass
    assert bytearray(range(5)).decode() == '\x00\x01\x02\x03\x04'
    b = bytearray(b'\xe4\xbd\xa0\xe5\xa5\xbd')
    assert b.decode() == '你好'
    assert b.decode('gbk') == '浣犲ソ'
    assert b.decode('latin1') == 'ä½\xa0å¥½'

[case testEncode]
from testutil import assertRaises

def test_encode() -> None:
    u = chr(40960) + 'abcd' + chr(1972)
    assert u.encode() == b'\xea\x80\x80abcd\xde\xb4'
    assert u.encode('utf-8') == b'\xea\x80\x80abcd\xde\xb4'
    with assertRaises(UnicodeEncodeError):
        u.encode('ascii')
    with assertRaises(LookupError):
        u.encode('aaa')
    assert u.encode('utf-8', 'aaaaaa') == b'\xea\x80\x80abcd\xde\xb4'
    assert u.encode('ascii', 'ignore') == b'abcd'
    assert u.encode('ASCII', 'ignore') == b'abcd'
    assert u.encode('ascii', 'replace') == b'?abcd?'
    assert u.encode('ascii', 'xmlcharrefreplace') == b'&#40960;abcd&#1972;'
    assert u.encode('ascii', 'backslashreplace') == b'\\ua000abcd\\u07b4'
    assert u.encode('ascii', 'namereplace') == b'\\N{YI SYLLABLE IT}abcd\\u07b4'
    assert 'pythön!'.encode() == b'pyth\xc3\xb6n!'
    assert '一二三'.encode('gbk') == b'\xd2\xbb\xb6\xfe\xc8\xfd'
    assert u.encode('UTF-8', 'ignore') == b'\xea\x80\x80abcd\xde\xb4'
    assert u.encode('Utf_8') == b'\xea\x80\x80abcd\xde\xb4'
    assert u.encode('UTF_8') == b'\xea\x80\x80abcd\xde\xb4'
    assert u'\u00E1'.encode('latin1') == b'\xe1'
    with assertRaises(UnicodeEncodeError):
        u.encode('latin1')

[case testUnicodeSurrogate]
def f() -> str:
    return "\ud800"

def test_surrogate() -> None:
    assert ord(f()) == 0xd800
    assert ord("\udfff") == 0xdfff
    assert repr("foobar\x00\xab\ud912\U00012345") == r"'foobar\x00«\ud912𒍅'"
