go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/starlark/testdata/string.star (about)

     1  # Tests of Starlark 'string'
     2  # option:set
     3  
     4  load("assert.star", "assert")
     5  
     6  # raw string literals:
     7  assert.eq(r"a\bc", "a\\bc")
     8  
     9  # truth
    10  assert.true("abc")
    11  assert.true(chr(0))
    12  assert.true(not "")
    13  
    14  # str + str
    15  assert.eq("a" + "b" + "c", "abc")
    16  
    17  # str * int,  int * str
    18  assert.eq("abc" * 0, "")
    19  assert.eq("abc" * -1, "")
    20  assert.eq("abc" * 1, "abc")
    21  assert.eq("abc" * 5, "abcabcabcabcabc")
    22  assert.eq(0 * "abc", "")
    23  assert.eq(-1 * "abc", "")
    24  assert.eq(1 * "abc", "abc")
    25  assert.eq(5 * "abc", "abcabcabcabcabc")
    26  assert.fails(lambda: 1.0 * "abc", "unknown.*float \\* str")
    27  assert.fails(lambda: "abc" * (1000000 * 1000000), "repeat count 1000000000000 too large")
    28  assert.fails(lambda: "abc" * 1000000 * 1000000, "excessive repeat \\(3000000 \\* 1000000 elements")
    29  
    30  # len
    31  assert.eq(len("Hello, 世界!"), 14)
    32  assert.eq(len("𐐷"), 4)  # U+10437 has a 4-byte UTF-8 encoding (and a 2-code UTF-16 encoding)
    33  
    34  # chr & ord
    35  assert.eq(chr(65), "A")  # 1-byte UTF-8 encoding
    36  assert.eq(chr(1049), "Й")  # 2-byte UTF-8 encoding
    37  assert.eq(chr(0x1F63F), "😿")  # 4-byte UTF-8 encoding
    38  assert.fails(lambda: chr(-1), "Unicode code point -1 out of range \\(<0\\)")
    39  assert.fails(lambda: chr(0x110000), "Unicode code point U\\+110000 out of range \\(>0x10FFFF\\)")
    40  assert.eq(ord("A"), 0x41)
    41  assert.eq(ord("Й"), 0x419)
    42  assert.eq(ord("世"), 0x4e16)
    43  assert.eq(ord("😿"), 0x1F63F)
    44  assert.eq(ord("Й"[1:]), 0xFFFD)  # = Unicode replacement character
    45  assert.fails(lambda: ord("abc"), "string encodes 3 Unicode code points, want 1")
    46  assert.fails(lambda: ord(""), "string encodes 0 Unicode code points, want 1")
    47  assert.fails(lambda: ord("😿"[1:]), "string encodes 3 Unicode code points, want 1")  # 3 x 0xFFFD
    48  
    49  # string.codepoint_ords
    50  assert.eq(type("abcЙ😿".codepoint_ords()), "string.codepoints")
    51  assert.eq(str("abcЙ😿".codepoint_ords()), '"abcЙ😿".codepoint_ords()')
    52  assert.eq(list("abcЙ😿".codepoint_ords()), [97, 98, 99, 1049, 128575])
    53  assert.eq(list(("A" + "😿Z"[1:]).codepoint_ords()), [ord("A"), 0xFFFD, 0xFFFD, 0xFFFD, ord("Z")])
    54  assert.eq(list("".codepoint_ords()), [])
    55  assert.fails(lambda: "abcЙ😿".codepoint_ords()[2], "unhandled index")  # not indexable
    56  assert.fails(lambda: len("abcЙ😿".codepoint_ords()), "no len")  # unknown length
    57  
    58  # string.codepoints
    59  assert.eq(type("abcЙ😿".codepoints()), "string.codepoints")
    60  assert.eq(str("abcЙ😿".codepoints()), '"abcЙ😿".codepoints()')
    61  assert.eq(list("abcЙ😿".codepoints()), ["a", "b", "c", "Й", "😿"])
    62  assert.eq(list(("A" + "😿Z"[1:]).codepoints()), ["A", "�", "�", "�", "Z"])
    63  assert.eq(list("".codepoints()), [])
    64  assert.fails(lambda: "abcЙ😿".codepoints()[2], "unhandled index")  # not indexable
    65  assert.fails(lambda: len("abcЙ😿".codepoints()), "no len")  # unknown length
    66  
    67  # string.elem_ords
    68  assert.eq(type("abcЙ😿".elem_ords()), "string.elems")
    69  assert.eq(str("abcЙ😿".elem_ords()), '"abcЙ😿".elem_ords()')
    70  assert.eq(list("abcЙ😿".elem_ords()), [97, 98, 99, 208, 153, 240, 159, 152, 191])
    71  assert.eq(list(("A" + "😿Z"[1:]).elem_ords()), [65, 159, 152, 191, 90])
    72  assert.eq(list("".elem_ords()), [])
    73  assert.eq("abcЙ😿".elem_ords()[2], 99)  # indexable
    74  assert.eq(len("abcЙ😿".elem_ords()), 9)  # known length
    75  
    76  # string.elems (1-byte substrings, which are invalid text)
    77  assert.eq(type("abcЙ😿".elems()), "string.elems")
    78  assert.eq(str("abcЙ😿".elems()), '"abcЙ😿".elems()')
    79  assert.eq(
    80      repr(list("abcЙ😿".elems())),
    81      r'["a", "b", "c", "\xd0", "\x99", "\xf0", "\x9f", "\x98", "\xbf"]',
    82  )
    83  assert.eq(
    84      repr(list(("A" + "😿Z"[1:]).elems())),
    85      r'["A", "\x9f", "\x98", "\xbf", "Z"]',
    86  )
    87  assert.eq(list("".elems()), [])
    88  assert.eq("abcЙ😿".elems()[2], "c")  # indexable
    89  assert.eq(len("abcЙ😿".elems()), 9)  # known length
    90  
    91  # indexing, x[i]
    92  assert.eq("Hello, 世界!"[0], "H")
    93  assert.eq(repr("Hello, 世界!"[7]), r'"\xe4"')  # (invalid text)
    94  assert.eq("Hello, 世界!"[13], "!")
    95  assert.fails(lambda: "abc"[-4], "out of range")
    96  assert.eq("abc"[-3], "a")
    97  assert.eq("abc"[-2], "b")
    98  assert.eq("abc"[-1], "c")
    99  assert.eq("abc"[0], "a")
   100  assert.eq("abc"[1], "b")
   101  assert.eq("abc"[2], "c")
   102  assert.fails(lambda: "abc"[4], "out of range")
   103  
   104  # x[i] = ...
   105  def f():
   106      "abc"[1] = "B"
   107  
   108  assert.fails(f, "string.*does not support.*assignment")
   109  
   110  # slicing, x[i:j]
   111  assert.eq("abc"[:], "abc")
   112  assert.eq("abc"[-4:], "abc")
   113  assert.eq("abc"[-3:], "abc")
   114  assert.eq("abc"[-2:], "bc")
   115  assert.eq("abc"[-1:], "c")
   116  assert.eq("abc"[0:], "abc")
   117  assert.eq("abc"[1:], "bc")
   118  assert.eq("abc"[2:], "c")
   119  assert.eq("abc"[3:], "")
   120  assert.eq("abc"[4:], "")
   121  assert.eq("abc"[:-4], "")
   122  assert.eq("abc"[:-3], "")
   123  assert.eq("abc"[:-2], "a")
   124  assert.eq("abc"[:-1], "ab")
   125  assert.eq("abc"[:0], "")
   126  assert.eq("abc"[:1], "a")
   127  assert.eq("abc"[:2], "ab")
   128  assert.eq("abc"[:3], "abc")
   129  assert.eq("abc"[:4], "abc")
   130  assert.eq("abc"[1:2], "b")
   131  assert.eq("abc"[2:1], "")
   132  assert.eq(repr("😿"[:1]), r'"\xf0"')  # (invalid text)
   133  
   134  # non-unit strides
   135  assert.eq("abcd"[0:4:1], "abcd")
   136  assert.eq("abcd"[::2], "ac")
   137  assert.eq("abcd"[1::2], "bd")
   138  assert.eq("abcd"[4:0:-1], "dcb")
   139  assert.eq("banana"[7::-2], "aaa")
   140  assert.eq("banana"[6::-2], "aaa")
   141  assert.eq("banana"[5::-2], "aaa")
   142  assert.eq("banana"[4::-2], "nnb")
   143  assert.eq("banana"[::-1], "ananab")
   144  assert.eq("banana"[None:None:-2], "aaa")
   145  assert.fails(lambda: "banana"[1.0::], "invalid start index: got float, want int")
   146  assert.fails(lambda: "banana"[:"":], "invalid end index: got string, want int")
   147  assert.fails(lambda: "banana"[:"":True], "invalid slice step: got bool, want int")
   148  
   149  # in, not in
   150  assert.true("oo" in "food")
   151  assert.true("ox" not in "food")
   152  assert.true("" in "food")
   153  assert.true("" in "")
   154  assert.fails(lambda: 1 in "", "requires string as left operand")
   155  assert.fails(lambda: "" in 1, "unknown binary op: string in int")
   156  
   157  # ==, !=
   158  assert.eq("hello", "he" + "llo")
   159  assert.ne("hello", "Hello")
   160  
   161  # hash must follow java.lang.String.hashCode.
   162  wanthash = {
   163      "": 0,
   164      "\0" * 100: 0,
   165      "hello": 99162322,
   166      "world": 113318802,
   167      "Hello, 世界!": 417292677,
   168  }
   169  gothash = {s: hash(s) for s in wanthash}
   170  assert.eq(gothash, wanthash)
   171  
   172  # TODO(adonovan): ordered comparisons
   173  
   174  # string % tuple formatting
   175  assert.eq("A %d %x Z" % (123, 456), "A 123 1c8 Z")
   176  assert.eq("A %(foo)d %(bar)s Z" % {"foo": 123, "bar": "hi"}, "A 123 hi Z")
   177  assert.eq("%s %r" % ("hi", "hi"), 'hi "hi"')  # TODO(adonovan): use ''-quotation
   178  assert.eq("%%d %d" % 1, "%d 1")
   179  assert.fails(lambda: "%d %d" % 1, "not enough arguments for format string")
   180  assert.fails(lambda: "%d %d" % (1, 2, 3), "too many arguments for format string")
   181  assert.fails(lambda: "" % 1, "too many arguments for format string")
   182  
   183  # %c
   184  assert.eq("%c" % 65, "A")
   185  assert.eq("%c" % 0x3b1, "α")
   186  assert.eq("%c" % "A", "A")
   187  assert.eq("%c" % "α", "α")
   188  assert.fails(lambda: "%c" % "abc", "requires a single-character string")
   189  assert.fails(lambda: "%c" % "", "requires a single-character string")
   190  assert.fails(lambda: "%c" % 65.0, "requires int or single-character string")
   191  assert.fails(lambda: "%c" % 10000000, "requires a valid Unicode code point")
   192  assert.fails(lambda: "%c" % -1, "requires a valid Unicode code point")
   193  # TODO(adonovan): more tests
   194  
   195  # str.format
   196  assert.eq("a{}b".format(123), "a123b")
   197  assert.eq("a{}b{}c{}d{}".format(1, 2, 3, 4), "a1b2c3d4")
   198  assert.eq("a{{b".format(), "a{b")
   199  assert.eq("a}}b".format(), "a}b")
   200  assert.eq("a{{b}}c".format(), "a{b}c")
   201  assert.eq("a{x}b{y}c{}".format(1, x = 2, y = 3), "a2b3c1")
   202  assert.fails(lambda: "a{z}b".format(x = 1), "keyword z not found")
   203  assert.fails(lambda: "{-1}".format(1), "keyword -1 not found")
   204  assert.fails(lambda: "{-0}".format(1), "keyword -0 not found")
   205  assert.fails(lambda: "{+0}".format(1), "keyword \\+0 not found")
   206  assert.fails(lambda: "{+1}".format(1), "keyword \\+1 not found")  # starlark-go/issues/114
   207  assert.eq("{0000000000001}".format(0, 1), "1")
   208  assert.eq("{012}".format(*range(100)), "12")  # decimal, despite leading zeros
   209  assert.fails(lambda: "{0,1} and {1}".format(1, 2), "keyword 0,1 not found")
   210  assert.fails(lambda: "a{123}b".format(), "tuple index out of range")
   211  assert.fails(lambda: "a{}b{}c".format(1), "tuple index out of range")
   212  assert.eq("a{010}b".format(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), "a10b")  # index is decimal
   213  assert.fails(lambda: "a{}b{1}c".format(1, 2), "cannot switch from automatic field numbering to manual")
   214  assert.eq("a{!s}c".format("b"), "abc")
   215  assert.eq("a{!r}c".format("b"), r'a"b"c')
   216  assert.eq("a{x!r}c".format(x = "b"), r'a"b"c')
   217  assert.fails(lambda: "{x!}".format(x = 1), "unknown conversion")
   218  assert.fails(lambda: "{x!:}".format(x = 1), "unknown conversion")
   219  assert.fails(lambda: "{a.b}".format(1), "syntax x.y is not supported")
   220  assert.fails(lambda: "{a[0]}".format(1), "syntax a\\[i\\] is not supported")
   221  assert.fails(lambda: "{ {} }".format(1), "nested replacement fields not supported")
   222  assert.fails(lambda: "{{}".format(1), "single '}' in format")
   223  assert.fails(lambda: "{}}".format(1), "single '}' in format")
   224  assert.fails(lambda: "}}{".format(1), "unmatched '{' in format")
   225  assert.fails(lambda: "}{{".format(1), "single '}' in format")
   226  
   227  # str.split, str.rsplit
   228  assert.eq("a.b.c.d".split("."), ["a", "b", "c", "d"])
   229  assert.eq("a.b.c.d".rsplit("."), ["a", "b", "c", "d"])
   230  assert.eq("a.b.c.d".split(".", -1), ["a", "b", "c", "d"])
   231  assert.eq("a.b.c.d".rsplit(".", -1), ["a", "b", "c", "d"])
   232  assert.eq("a.b.c.d".split(".", 0), ["a.b.c.d"])
   233  assert.eq("a.b.c.d".rsplit(".", 0), ["a.b.c.d"])
   234  assert.eq("a.b.c.d".split(".", 1), ["a", "b.c.d"])
   235  assert.eq("a.b.c.d".rsplit(".", 1), ["a.b.c", "d"])
   236  assert.eq("a.b.c.d".split(".", 2), ["a", "b", "c.d"])
   237  assert.eq("a.b.c.d".rsplit(".", 2), ["a.b", "c", "d"])
   238  assert.eq("  ".split("."), ["  "])
   239  assert.eq("  ".rsplit("."), ["  "])
   240  
   241  # {,r}split on white space:
   242  assert.eq(" a bc\n  def \t  ghi".split(), ["a", "bc", "def", "ghi"])
   243  assert.eq(" a bc\n  def \t  ghi".split(None), ["a", "bc", "def", "ghi"])
   244  assert.eq(" a bc\n  def \t  ghi".split(None, 0), ["a bc\n  def \t  ghi"])
   245  assert.eq(" a bc\n  def \t  ghi".rsplit(None, 0), [" a bc\n  def \t  ghi"])
   246  assert.eq(" a bc\n  def \t  ghi".split(None, 1), ["a", "bc\n  def \t  ghi"])
   247  assert.eq(" a bc\n  def \t  ghi".rsplit(None, 1), [" a bc\n  def", "ghi"])
   248  assert.eq(" a bc\n  def \t  ghi".split(None, 2), ["a", "bc", "def \t  ghi"])
   249  assert.eq(" a bc\n  def \t  ghi".rsplit(None, 2), [" a bc", "def", "ghi"])
   250  assert.eq(" a bc\n  def \t  ghi".split(None, 3), ["a", "bc", "def", "ghi"])
   251  assert.eq(" a bc\n  def \t  ghi".rsplit(None, 3), [" a", "bc", "def", "ghi"])
   252  assert.eq(" a bc\n  def \t  ghi".split(None, 4), ["a", "bc", "def", "ghi"])
   253  assert.eq(" a bc\n  def \t  ghi".rsplit(None, 4), ["a", "bc", "def", "ghi"])
   254  assert.eq(" a bc\n  def \t  ghi".rsplit(None, 5), ["a", "bc", "def", "ghi"])
   255  
   256  assert.eq(" a bc\n  def \t  ghi ".split(None, 0), ["a bc\n  def \t  ghi "])
   257  assert.eq(" a bc\n  def \t  ghi ".rsplit(None, 0), [" a bc\n  def \t  ghi"])
   258  assert.eq(" a bc\n  def \t  ghi ".split(None, 1), ["a", "bc\n  def \t  ghi "])
   259  assert.eq(" a bc\n  def \t  ghi ".rsplit(None, 1), [" a bc\n  def", "ghi"])
   260  
   261  # Observe the algorithmic difference when splitting on spaces versus other delimiters.
   262  assert.eq("--aa--bb--cc--".split("-", 0), ["--aa--bb--cc--"])  # contrast this
   263  assert.eq("  aa  bb  cc  ".split(None, 0), ["aa  bb  cc  "])  #  with this
   264  assert.eq("--aa--bb--cc--".rsplit("-", 0), ["--aa--bb--cc--"])  # ditto this
   265  assert.eq("  aa  bb  cc  ".rsplit(None, 0), ["  aa  bb  cc"])  #  and this
   266  
   267  #
   268  assert.eq("--aa--bb--cc--".split("-", 1), ["", "-aa--bb--cc--"])
   269  assert.eq("--aa--bb--cc--".rsplit("-", 1), ["--aa--bb--cc-", ""])
   270  assert.eq("  aa  bb  cc  ".split(None, 1), ["aa", "bb  cc  "])
   271  assert.eq("  aa  bb  cc  ".rsplit(None, 1), ["  aa  bb", "cc"])
   272  
   273  #
   274  assert.eq("--aa--bb--cc--".split("-", -1), ["", "", "aa", "", "bb", "", "cc", "", ""])
   275  assert.eq("--aa--bb--cc--".rsplit("-", -1), ["", "", "aa", "", "bb", "", "cc", "", ""])
   276  assert.eq("  aa  bb  cc  ".split(None, -1), ["aa", "bb", "cc"])
   277  assert.eq("  aa  bb  cc  ".rsplit(None, -1), ["aa", "bb", "cc"])
   278  assert.eq("  ".split(None), [])
   279  assert.eq("  ".rsplit(None), [])
   280  
   281  assert.eq("localhost:80".rsplit(":", 1)[-1], "80")
   282  
   283  # str.splitlines
   284  assert.eq("\nabc\ndef".splitlines(), ["", "abc", "def"])
   285  assert.eq("\nabc\ndef".splitlines(True), ["\n", "abc\n", "def"])
   286  assert.eq("\nabc\ndef\n".splitlines(), ["", "abc", "def"])
   287  assert.eq("\nabc\ndef\n".splitlines(True), ["\n", "abc\n", "def\n"])
   288  assert.eq("".splitlines(), [])  #
   289  assert.eq("".splitlines(True), [])  #
   290  assert.eq("a".splitlines(), ["a"])
   291  assert.eq("a".splitlines(True), ["a"])
   292  assert.eq("\n".splitlines(), [""])
   293  assert.eq("\n".splitlines(True), ["\n"])
   294  assert.eq("a\n".splitlines(), ["a"])
   295  assert.eq("a\n".splitlines(True), ["a\n"])
   296  assert.eq("a\n\nb".splitlines(), ["a", "", "b"])
   297  assert.eq("a\n\nb".splitlines(True), ["a\n", "\n", "b"])
   298  assert.eq("a\nb\nc".splitlines(), ["a", "b", "c"])
   299  assert.eq("a\nb\nc".splitlines(True), ["a\n", "b\n", "c"])
   300  assert.eq("a\nb\nc\n".splitlines(), ["a", "b", "c"])
   301  assert.eq("a\nb\nc\n".splitlines(True), ["a\n", "b\n", "c\n"])
   302  
   303  # str.{,l,r}strip
   304  assert.eq(" \tfoo\n ".strip(), "foo")
   305  assert.eq(" \tfoo\n ".lstrip(), "foo\n ")
   306  assert.eq(" \tfoo\n ".rstrip(), " \tfoo")
   307  assert.eq(" \tfoo\n ".strip(""), "foo")
   308  assert.eq(" \tfoo\n ".lstrip(""), "foo\n ")
   309  assert.eq(" \tfoo\n ".rstrip(""), " \tfoo")
   310  assert.eq("blah.h".strip("b.h"), "la")
   311  assert.eq("blah.h".lstrip("b.h"), "lah.h")
   312  assert.eq("blah.h".rstrip("b.h"), "bla")
   313  
   314  # str.count
   315  assert.eq("banana".count("a"), 3)
   316  assert.eq("banana".count("a", 2), 2)
   317  assert.eq("banana".count("a", -4, -2), 1)
   318  assert.eq("banana".count("a", 1, 4), 2)
   319  assert.eq("banana".count("a", 0, -100), 0)
   320  
   321  # str.{starts,ends}with
   322  assert.true("foo".endswith("oo"))
   323  assert.true(not "foo".endswith("x"))
   324  assert.true("foo".startswith("fo"))
   325  assert.true(not "foo".startswith("x"))
   326  assert.fails(lambda: "foo".startswith(1), "got int.*want string")
   327  
   328  #
   329  assert.true("abc".startswith(("a", "A")))
   330  assert.true("ABC".startswith(("a", "A")))
   331  assert.true(not "ABC".startswith(("b", "B")))
   332  assert.fails(lambda: "123".startswith((1, 2)), "got int, for element 0")
   333  assert.fails(lambda: "123".startswith(["3"]), "got list")
   334  
   335  #
   336  assert.true("abc".endswith(("c", "C")))
   337  assert.true("ABC".endswith(("c", "C")))
   338  assert.true(not "ABC".endswith(("b", "B")))
   339  assert.fails(lambda: "123".endswith((1, 2)), "got int, for element 0")
   340  assert.fails(lambda: "123".endswith(["3"]), "got list")
   341  
   342  # start/end
   343  assert.true("abc".startswith("bc", 1))
   344  assert.true(not "abc".startswith("b", 999))
   345  assert.true("abc".endswith("ab", None, -1))
   346  assert.true(not "abc".endswith("b", None, -999))
   347  
   348  # str.replace
   349  assert.eq("banana".replace("a", "o", 1), "bonana")
   350  assert.eq("banana".replace("a", "o"), "bonono")
   351  # TODO(adonovan): more tests
   352  
   353  # str.{,r}find
   354  assert.eq("foofoo".find("oo"), 1)
   355  assert.eq("foofoo".find("ox"), -1)
   356  assert.eq("foofoo".find("oo", 2), 4)
   357  assert.eq("foofoo".rfind("oo"), 4)
   358  assert.eq("foofoo".rfind("ox"), -1)
   359  assert.eq("foofoo".rfind("oo", 1, 4), 1)
   360  assert.eq("foofoo".find(""), 0)
   361  assert.eq("foofoo".rfind(""), 6)
   362  
   363  # str.{,r}partition
   364  assert.eq("foo/bar/wiz".partition("/"), ("foo", "/", "bar/wiz"))
   365  assert.eq("foo/bar/wiz".rpartition("/"), ("foo/bar", "/", "wiz"))
   366  assert.eq("foo/bar/wiz".partition("."), ("foo/bar/wiz", "", ""))
   367  assert.eq("foo/bar/wiz".rpartition("."), ("", "", "foo/bar/wiz"))
   368  assert.fails(lambda: "foo/bar/wiz".partition(""), "empty separator")
   369  assert.fails(lambda: "foo/bar/wiz".rpartition(""), "empty separator")
   370  
   371  assert.eq("?".join(["foo", "a/b/c.go".rpartition("/")[0]]), "foo?a/b")
   372  
   373  # str.is{alpha,...}
   374  def test_predicates():
   375      predicates = ["alnum", "alpha", "digit", "lower", "space", "title", "upper"]
   376      table = {
   377          "Hello, World!": "title",
   378          "hello, world!": "lower",
   379          "base64": "alnum lower",
   380          "HAL-9000": "upper",
   381          "Catch-22": "title",
   382          "": "",
   383          "\n\t\r": "space",
   384          "abc": "alnum alpha lower",
   385          "ABC": "alnum alpha upper",
   386          "123": "alnum digit",
   387          "DŽLJ": "alnum alpha upper",
   388          "DžLj": "alnum alpha",
   389          "Dž Lj": "title",
   390          "džlj": "alnum alpha lower",
   391      }
   392      for str, want in table.items():
   393          got = " ".join([name for name in predicates if getattr(str, "is" + name)()])
   394          if got != want:
   395              assert.fail("%r matched [%s], want [%s]" % (str, got, want))
   396  
   397  test_predicates()
   398  
   399  # Strings are not iterable.
   400  # ok
   401  assert.eq(len("abc"), 3)  # len
   402  assert.true("a" in "abc")  # str in str
   403  assert.eq("abc"[1], "b")  # indexing
   404  
   405  # not ok
   406  def for_string():
   407      for x in "abc":
   408          pass
   409  
   410  def args(*args):
   411      return args
   412  
   413  assert.fails(lambda: args(*"abc"), "must be iterable, not string")  # varargs
   414  assert.fails(lambda: list("abc"), "got string, want iterable")  # list(str)
   415  assert.fails(lambda: tuple("abc"), "got string, want iterable")  # tuple(str)
   416  assert.fails(lambda: set("abc"), "got string, want iterable")  # set(str)
   417  assert.fails(lambda: set() | "abc", "unknown binary op: set | string")  # set union
   418  assert.fails(lambda: enumerate("ab"), "got string, want iterable")  # enumerate
   419  assert.fails(lambda: sorted("abc"), "got string, want iterable")  # sorted
   420  assert.fails(lambda: [].extend("bc"), "got string, want iterable")  # list.extend
   421  assert.fails(lambda: ",".join("abc"), "got string, want iterable")  # string.join
   422  assert.fails(lambda: dict(["ab"]), "not iterable .*string")  # dict
   423  assert.fails(for_string, "string value is not iterable")  # for loop
   424  assert.fails(lambda: [x for x in "abc"], "string value is not iterable")  # comprehension
   425  assert.fails(lambda: all("abc"), "got string, want iterable")  # all
   426  assert.fails(lambda: any("abc"), "got string, want iterable")  # any
   427  assert.fails(lambda: reversed("abc"), "got string, want iterable")  # reversed
   428  assert.fails(lambda: zip("ab", "cd"), "not iterable: string")  # zip
   429  
   430  # str.join
   431  assert.eq(",".join([]), "")
   432  assert.eq(",".join(["a"]), "a")
   433  assert.eq(",".join(["a", "b"]), "a,b")
   434  assert.eq(",".join(["a", "b", "c"]), "a,b,c")
   435  assert.eq(",".join(("a", "b", "c")), "a,b,c")
   436  assert.eq("".join(("a", "b", "c")), "abc")
   437  assert.fails(lambda: "".join(None), "got NoneType, want iterable")
   438  assert.fails(lambda: "".join(["one", 2]), "join: in list, want string, got int")
   439  
   440  # TODO(adonovan): tests for: {,r}index
   441  
   442  # str.capitalize
   443  assert.eq("hElLo, WoRlD!".capitalize(), "Hello, world!")
   444  assert.eq("por qué".capitalize(), "Por qué")
   445  assert.eq("¿Por qué?".capitalize(), "¿por qué?")
   446  
   447  # str.lower
   448  assert.eq("hElLo, WoRlD!".lower(), "hello, world!")
   449  assert.eq("por qué".lower(), "por qué")
   450  assert.eq("¿Por qué?".lower(), "¿por qué?")
   451  assert.eq("LJUBOVIĆ".lower(), "ljubović")
   452  assert.true("dženan ljubović".islower())
   453  
   454  # str.upper
   455  assert.eq("hElLo, WoRlD!".upper(), "HELLO, WORLD!")
   456  assert.eq("por qué".upper(), "POR QUÉ")
   457  assert.eq("¿Por qué?".upper(), "¿POR QUÉ?")
   458  assert.eq("ljubović".upper(), "LJUBOVIĆ")
   459  assert.true("DŽENAN LJUBOVIĆ".isupper())
   460  
   461  # str.title
   462  assert.eq("hElLo, WoRlD!".title(), "Hello, World!")
   463  assert.eq("por qué".title(), "Por Qué")
   464  assert.eq("¿Por qué?".title(), "¿Por Qué?")
   465  assert.eq("ljubović".title(), "Ljubović")
   466  assert.true("Dženan Ljubović".istitle())
   467  assert.true(not "DŽenan LJubović".istitle())
   468  
   469  # method spell check
   470  assert.fails(lambda: "".starts_with, "no .starts_with field.*did you mean .startswith")
   471  assert.fails(lambda: "".StartsWith, "no .StartsWith field.*did you mean .startswith")
   472  assert.fails(lambda: "".fin, "no .fin field.*.did you mean .find")
   473  
   474  
   475  # removesuffix
   476  assert.eq("Apricot".removesuffix("cot"), "Apri")
   477  assert.eq("Apricot".removesuffix("Cot"), "Apricot")
   478  assert.eq("Apricot".removesuffix("t"), "Aprico")
   479  assert.eq("a".removesuffix(""), "a")
   480  assert.eq("".removesuffix(""), "")
   481  assert.eq("".removesuffix("a"), "")
   482  assert.eq("Apricot".removesuffix("co"), "Apricot")
   483  assert.eq("Apricotcot".removesuffix("cot"), "Apricot")
   484  
   485  # removeprefix
   486  assert.eq("Apricot".removeprefix("Apr"), "icot")
   487  assert.eq("Apricot".removeprefix("apr"), "Apricot")
   488  assert.eq("Apricot".removeprefix("A"), "pricot")
   489  assert.eq("a".removeprefix(""), "a")
   490  assert.eq("".removeprefix(""), "")
   491  assert.eq("".removeprefix("a"), "")
   492  assert.eq("Apricot".removeprefix("pr"), "Apricot")
   493  assert.eq("AprApricot".removeprefix("Apr"), "Apricot")