runtime/str-builtins-test.cpp (1,917 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include "str-builtins.h" #include "gtest/gtest.h" #include "builtins.h" #include "handles.h" #include "objects.h" #include "runtime.h" #include "set-builtins.h" #include "test-utils.h" namespace py { namespace testing { using StrBuiltinsTest = RuntimeFixture; using StrIterTest = RuntimeFixture; using StrIteratorBuiltinsTest = RuntimeFixture; using StringIterTest = RuntimeFixture; TEST_F(StrBuiltinsTest, BuiltinBase) { HandleScope scope(thread_); Type small_str(&scope, runtime_->typeAt(LayoutId::kSmallStr)); EXPECT_EQ(small_str.builtinBase(), LayoutId::kStr); Type large_str(&scope, runtime_->typeAt(LayoutId::kLargeStr)); EXPECT_EQ(large_str.builtinBase(), LayoutId::kStr); Type str(&scope, runtime_->typeAt(LayoutId::kStr)); EXPECT_EQ(str.builtinBase(), LayoutId::kStr); } TEST_F(StrBuiltinsTest, RichCompareStringEQ) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "magic string" if (a == "magic string"): result = "foo" else: result = "bar" )") .isError()); EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "foo")); } TEST_F(StrBuiltinsTest, RichCompareStringEQWithSubClass) { ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass a = SubStr("magic string") if (a == "magic string"): result = "foo" else: result = "bar" )") .isError()); EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "foo")); } TEST_F(StrBuiltinsTest, RichCompareStringNE) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "magic string" result = "bar" if (a != "magic string"): result = "foo" )") .isError()); EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "bar")); } TEST_F(StrBuiltinsTest, RichCompareStringNEWithSubClass) { ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass a = SubStr("apple") result = "bar" if (a != "apple"): result = "foo" )") .isError()); EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "bar")); } TEST_F(StrBuiltinsTest, RichCompareSingleCharLE) { ASSERT_FALSE(runFromCStr(runtime_, R"( a_le_b = 'a' <= 'b' b_le_a = 'a' >= 'b' a_le_a = 'a' <= 'a' )") .isError()); HandleScope scope(thread_); Object a_le_b(&scope, mainModuleAt(runtime_, "a_le_b")); EXPECT_EQ(*a_le_b, Bool::trueObj()); Object b_le_a(&scope, mainModuleAt(runtime_, "b_le_a")); EXPECT_EQ(*b_le_a, Bool::falseObj()); Object a_le_a(&scope, mainModuleAt(runtime_, "a_le_a")); EXPECT_EQ(*a_le_a, Bool::trueObj()); } TEST_F(StrBuiltinsTest, RichCompareSingleCharLEWithSubClass) { ASSERT_FALSE(runFromCStr(runtime_, R"( class S(str): pass a_le_b = S('a') <= S('b') b_le_a = S('a') >= S('b') a_le_a = S('a') <= S('a') )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "a_le_b"), Bool::trueObj()); EXPECT_EQ(mainModuleAt(runtime_, "b_le_a"), Bool::falseObj()); EXPECT_EQ(mainModuleAt(runtime_, "a_le_a"), Bool::trueObj()); } TEST_F(StrBuiltinsTest, DunderNewCallsDunderStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __str__(self): return "foo" a = str.__new__(str, Foo()) )") .isError()); HandleScope scope(thread_); Object a(&scope, mainModuleAt(runtime_, "a")); EXPECT_TRUE(isStrEqualsCStr(*a, "foo")); } TEST_F(StrBuiltinsTest, DunderNewCallsReprIfNoDunderStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: pass f = Foo() a = str.__new__(str, f) b = repr(f) )") .isError()); HandleScope scope(thread_); Object a(&scope, mainModuleAt(runtime_, "a")); Object b(&scope, mainModuleAt(runtime_, "b")); EXPECT_TRUE(isStrEquals(a, b)); } TEST_F(StrBuiltinsTest, DunderNewWithNoArgsExceptTypeReturnsEmptyString) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = str.__new__(str) )") .isError()); HandleScope scope(thread_); Object a(&scope, mainModuleAt(runtime_, "a")); EXPECT_TRUE(isStrEqualsCStr(*a, "")); } TEST_F(StrBuiltinsTest, DunderNewWithStrReturnsSameStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = str.__new__(str, "hello") )") .isError()); HandleScope scope(thread_); Object a(&scope, mainModuleAt(runtime_, "a")); EXPECT_TRUE(isStrEqualsCStr(*a, "hello")); } TEST_F(StrBuiltinsTest, DunderNewWithTypeCallsTypeDunderStr) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, "a = str.__new__(str, int)").isError()); Object a(&scope, mainModuleAt(runtime_, "a")); EXPECT_TRUE(isStrEqualsCStr(*a, "<class 'int'>")); } TEST_F(StrBuiltinsTest, DunderNewWithNoArgsRaisesTypeError) { EXPECT_TRUE(raisedWithStr( runFromCStr(runtime_, "str.__new__()"), LayoutId::kTypeError, "'str.__new__' takes min 1 positional arguments but 0 given")); } TEST_F(StrBuiltinsTest, DunderNewWithTooManyArgsRaisesTypeError) { EXPECT_TRUE(raisedWithStr( runFromCStr(runtime_, "str.__new__(str, 1, 2, 3, 4)"), LayoutId::kTypeError, "'str.__new__' takes max 4 positional arguments but 5 given")); } TEST_F(StrBuiltinsTest, DunderNewWithNonSubtypeArgRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, "str.__new__(object)"), LayoutId::kTypeError, "'__new__': 'object' is not a subclass of 'str'")); } TEST_F(StrBuiltinsTest, DunderAddWithTwoStringsReturnsConcatenatedString) { HandleScope scope(thread_); Object str1(&scope, runtime_->newStrFromCStr("hello")); Object str2(&scope, runtime_->newStrFromCStr("world")); Object result(&scope, runBuiltin(METH(str, __add__), str1, str2)); EXPECT_TRUE(isStrEqualsCStr(*result, "helloworld")); } TEST_F(StrBuiltinsTest, DunderAddWithTwoStringsOfSubClassReturnsConcatenatedString) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass str1 = SubStr("hello") str2 = SubStr("world") )") .isError()); Object str1(&scope, mainModuleAt(runtime_, "str1")); Object str2(&scope, mainModuleAt(runtime_, "str2")); Object result(&scope, runBuiltin(METH(str, __add__), str1, str2)); EXPECT_TRUE(isStrEqualsCStr(*result, "helloworld")); } TEST_F(StrBuiltinsTest, DunderAddWithLeftEmptyAndReturnsRight) { HandleScope scope(thread_); Object str1(&scope, Str::empty()); Object str2(&scope, runtime_->newStrFromCStr("world")); Object result(&scope, runBuiltin(METH(str, __add__), str1, str2)); EXPECT_TRUE(isStrEqualsCStr(*result, "world")); } TEST_F(StrBuiltinsTest, DunderAddWithRightEmptyAndReturnsRight) { HandleScope scope(thread_); Object str1(&scope, runtime_->newStrFromCStr("hello")); Object str2(&scope, Str::empty()); Object result(&scope, runBuiltin(METH(str, __add__), str1, str2)); EXPECT_TRUE(isStrEqualsCStr(*result, "hello")); } TEST_F(StrBuiltinsTest, PlusOperatorOnStringsEqualsDunderAdd) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "hello" b = "world" c = a + b d = a.__add__(b) )") .isError()); HandleScope scope(thread_); Object c(&scope, mainModuleAt(runtime_, "c")); Object d(&scope, mainModuleAt(runtime_, "d")); EXPECT_TRUE(isStrEqualsCStr(*c, "helloworld")); EXPECT_TRUE(isStrEqualsCStr(*d, "helloworld")); } TEST_F(StrBuiltinsTest, DunderBoolWithEmptyStringReturnsFalse) { HandleScope scope(thread_); Str str(&scope, Str::empty()); EXPECT_EQ(runBuiltin(METH(str, __bool__), str), Bool::falseObj()); } TEST_F(StrBuiltinsTest, DunderBoolWithNonEmptyStringReturnsTrue) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("hello")); EXPECT_EQ(runBuiltin(METH(str, __bool__), str), Bool::trueObj()); } TEST_F(StrBuiltinsTest, DunderBoolWithNonEmptyStringOfSubClassReturnsTrue) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass substr = SubStr("hello") )") .isError()); Object substr(&scope, mainModuleAt(runtime_, "substr")); EXPECT_EQ(runBuiltin(METH(str, __bool__), substr), Bool::trueObj()); } TEST_F(StrBuiltinsTest, DunderLenReturnsLength) { ASSERT_FALSE(runFromCStr(runtime_, R"( l1 = len("aloha") l2 = str.__len__("aloha") l3 = "aloha".__len__() )") .isError()); HandleScope scope(thread_); Object l1(&scope, mainModuleAt(runtime_, "l1")); Object l2(&scope, mainModuleAt(runtime_, "l2")); Object l3(&scope, mainModuleAt(runtime_, "l3")); EXPECT_TRUE(isIntEqualsWord(*l1, 5)); EXPECT_TRUE(isIntEqualsWord(*l2, 5)); EXPECT_TRUE(isIntEqualsWord(*l3, 5)); } TEST_F(StrBuiltinsTest, StringLenWithEmptyString) { ASSERT_FALSE(runFromCStr(runtime_, "l = len('')").isError()); HandleScope scope(thread_); Object length(&scope, mainModuleAt(runtime_, "l")); EXPECT_TRUE(isIntEqualsWord(*length, 0)); } TEST_F(StrBuiltinsTest, DunderLenWithNonAsciiReturnsCodePointLength) { ASSERT_FALSE(runFromCStr(runtime_, "l = len('\xc3\xa9')").isError()); HandleScope scope(thread_); SmallInt length(&scope, mainModuleAt(runtime_, "l")); EXPECT_TRUE(isIntEqualsWord(*length, 1)); } TEST_F(StrBuiltinsTest, DunderLenWithExtraArgumentRaisesTypeError) { EXPECT_TRUE(raisedWithStr( runFromCStr(runtime_, "l = 'aloha'.__len__('arg')"), LayoutId::kTypeError, "'str.__len__' takes max 1 positional arguments but 2 given")); } TEST_F(StrBuiltinsTest, DunderMulWithNonIntRaisesTypeError) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); Object count(&scope, runtime_->newList()); EXPECT_TRUE(raisedWithStr( runBuiltin(METH(str, __mul__), self, count), LayoutId::kTypeError, "'list' object cannot be interpreted as an integer")); } TEST_F(StrBuiltinsTest, DunderMulWithDunderIndexReturnsRepeatedStr) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return 2 count = C() )") .isError()); Object count(&scope, mainModuleAt(runtime_, "count")); Object result(&scope, runBuiltin(METH(str, __mul__), self, count)); EXPECT_TRUE(isStrEqualsCStr(*result, "foofoo")); } TEST_F(StrBuiltinsTest, DunderMulWithBadDunderIndexRaisesTypeError) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return "foo" count = C() )") .isError()); Object count(&scope, mainModuleAt(runtime_, "count")); EXPECT_TRUE(raisedWithStr(runBuiltin(METH(str, __mul__), self, count), LayoutId::kTypeError, "__index__ returned non-int (type str)")); } TEST_F(StrBuiltinsTest, DunderMulPropagatesDunderIndexError) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): raise ArithmeticError("called __index__") count = C() )") .isError()); Object count(&scope, mainModuleAt(runtime_, "count")); EXPECT_TRUE(raisedWithStr(runBuiltin(METH(str, __mul__), self, count), LayoutId::kArithmeticError, "called __index__")); } TEST_F(StrBuiltinsTest, DunderMulWithLargeIntRaisesOverflowError) { HandleScope scope(thread_); Object self(&scope, Str::empty()); const uword digits[] = {1, 1}; Object count(&scope, runtime_->newLargeIntWithDigits(digits)); EXPECT_TRUE(raisedWithStr(runBuiltin(METH(str, __mul__), self, count), LayoutId::kOverflowError, "cannot fit 'int' into an index-sized integer")); } TEST_F(StrBuiltinsTest, DunderMulWithOverflowRaisesOverflowError) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); Object count(&scope, SmallInt::fromWord(SmallInt::kMaxValue / 2)); EXPECT_TRUE(raisedWithStr(runBuiltin(METH(str, __mul__), self, count), LayoutId::kOverflowError, "repeated string is too long")); } TEST_F(StrBuiltinsTest, DunderMulWithEmptyBytesReturnsEmptyStr) { HandleScope scope(thread_); Object self(&scope, Str::empty()); Object count(&scope, runtime_->newInt(10)); Object result(&scope, runBuiltin(METH(str, __mul__), self, count)); EXPECT_TRUE(isStrEqualsCStr(*result, "")); } TEST_F(StrBuiltinsTest, DunderMulWithNegativeReturnsEmptyStr) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); Object count(&scope, SmallInt::fromWord(-5)); Object result(&scope, runBuiltin(METH(str, __mul__), self, count)); EXPECT_TRUE(isStrEqualsCStr(*result, "")); } TEST_F(StrBuiltinsTest, DunderMulWithZeroReturnsEmptyStr) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); Object count(&scope, SmallInt::fromWord(0)); Object result(&scope, runBuiltin(METH(str, __mul__), self, count)); EXPECT_TRUE(isStrEqualsCStr(*result, "")); } TEST_F(StrBuiltinsTest, DunderMulWithOneReturnsSamStr) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); Object count(&scope, SmallInt::fromWord(1)); Object result(&scope, runBuiltin(METH(str, __mul__), self, count)); EXPECT_TRUE(isStrEqualsCStr(*result, "foo")); } TEST_F(StrBuiltinsTest, DunderMulWithSmallStrReturnsRepeatedSmallStr) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); Object count(&scope, SmallInt::fromWord(2)); Object result(&scope, runBuiltin(METH(str, __mul__), self, count)); EXPECT_TRUE(isStrEqualsCStr(*result, "foofoo")); } TEST_F(StrBuiltinsTest, DunderMulWithSmallStrReturnsRepeatedLargeStr) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foo")); Object count(&scope, SmallInt::fromWord(3)); Object result(&scope, runBuiltin(METH(str, __mul__), self, count)); EXPECT_TRUE(isStrEqualsCStr(*result, "foofoofoo")); } TEST_F(StrBuiltinsTest, DunderMulWithLargeStrReturnsRepeatedLargeStr) { HandleScope scope(thread_); Object self(&scope, runtime_->newStrFromCStr("foobarbaz")); Object count(&scope, SmallInt::fromWord(2)); Object result(&scope, runBuiltin(METH(str, __mul__), self, count)); EXPECT_TRUE(isStrEqualsCStr(*result, "foobarbazfoobarbaz")); } TEST_F(StrBuiltinsTest, DunderRmulCallsDunderMul) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, "result = 3 * 'foo'").isError()); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_TRUE(isStrEqualsCStr(*result, "foofoofoo")); } TEST_F(StrBuiltinsTest, HasPrefixWithPrefixPastEndOfStrReturnsFalse) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello")); Str needle(&scope, runtime_->newStrFromCStr("hel")); EXPECT_TRUE(strHasPrefix(haystack, needle, 0)); EXPECT_FALSE(strHasPrefix(haystack, needle, 1)); EXPECT_FALSE(strHasPrefix(haystack, needle, 3)); } TEST_F(StrBuiltinsTest, HasPrefixWithNonPrefixPastEndOfStrReturnsFalse) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello")); Str needle(&scope, runtime_->newStrFromCStr("lop")); EXPECT_FALSE(strHasPrefix(haystack, needle, 0)); EXPECT_FALSE(strHasPrefix(haystack, needle, 1)); EXPECT_FALSE(strHasPrefix(haystack, needle, 3)); } TEST_F(StrBuiltinsTest, HasPrefixWithEmptyNeedleReturnsTrue) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello")); Str empty(&scope, Str::empty()); EXPECT_TRUE(strHasPrefix(empty, empty, 0)); EXPECT_FALSE(strHasPrefix(empty, empty, 1)); EXPECT_TRUE(strHasPrefix(haystack, empty, 0)); EXPECT_TRUE(strHasPrefix(haystack, empty, 5)); EXPECT_FALSE(strHasPrefix(haystack, empty, 6)); } TEST_F(StrBuiltinsTest, HasSurrogateWithNonSurrogateReturnsFalse) { HandleScope scope(thread_); Str s1(&scope, SmallStr::fromCStr("")); EXPECT_FALSE(strHasSurrogate(s1)); Str s2(&scope, SmallStr::fromCStr("a")); EXPECT_FALSE(strHasSurrogate(s2)); Str s3(&scope, SmallStr::fromCStr("b10\x04-U.")); EXPECT_FALSE(strHasSurrogate(s3)); Str s4(&scope, SmallStr::fromCStr("pav\u00e9")); EXPECT_FALSE(strHasSurrogate(s4)); Str s5(&scope, runtime_->newStrFromCStr("Hello world!")); EXPECT_FALSE(strHasSurrogate(s5)); Str s6(&scope, runtime_->newStrFromCStr("1234567890")); EXPECT_FALSE(strHasSurrogate(s6)); Str s7(&scope, runtime_->newStrFromCStr("\u00c9tudes Op. 10")); EXPECT_FALSE(strHasSurrogate(s7)); } TEST_F(StrBuiltinsTest, HasSurrogateWithSurrogateReturnsTrue) { HandleScope scope(thread_); Str s1(&scope, SmallStr::fromCodePoint(0xD800)); EXPECT_TRUE(strHasSurrogate(s1)); Str s2(&scope, SmallStr::fromCodePoint(0xDFFF)); EXPECT_TRUE(strHasSurrogate(s2)); Str s3(&scope, SmallStr::fromCodePoint(0xD1E9)); EXPECT_TRUE(strHasSurrogate(s3)); const int32_t view4[] = {'p', 'a', 'd', 'd', 'i', 'n', 'g', 0xD800}; Str s4(&scope, runtime_->newStrFromUTF32(view4)); EXPECT_TRUE(strHasSurrogate(s4)); const int32_t view5[] = {'p', 'a', 'd', 'd', 'i', 'n', 'g', 0xDC81}; Str s5(&scope, runtime_->newStrFromUTF32(view5)); EXPECT_TRUE(strHasSurrogate(s5)); const int32_t view6[] = {'p', 'a', 'd', 0xD800, 0xDFFF}; Str s6(&scope, runtime_->newStrFromUTF32(view6)); EXPECT_TRUE(strHasSurrogate(s6)); const int32_t view7[] = {'p', 'a', 'd', 0xDC00, 0xD910}; Str s7(&scope, runtime_->newStrFromUTF32(view7)); EXPECT_TRUE(strHasSurrogate(s7)); } TEST_F(StrBuiltinsTest, InternStringsInTupleInternsItems) { HandleScope scope(thread_); Str str0(&scope, runtime_->newStrFromCStr("a")); Str str1(&scope, runtime_->newStrFromCStr("hello world")); Str str2(&scope, runtime_->newStrFromCStr("hello world foobar")); EXPECT_TRUE(runtime_->isInternedStr(thread_, str0)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str1)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str2)); Tuple tuple(&scope, runtime_->newTupleWith3(str0, str1, str2)); strInternInTuple(thread_, tuple); str0 = tuple.at(0); str1 = tuple.at(1); str2 = tuple.at(2); EXPECT_TRUE(runtime_->isInternedStr(thread_, str0)); EXPECT_TRUE(runtime_->isInternedStr(thread_, str1)); EXPECT_TRUE(runtime_->isInternedStr(thread_, str2)); } TEST_F(StrBuiltinsTest, InternStringConstantsInternsAlphanumericStringsInTuple) { HandleScope scope(thread_); Str str0(&scope, runtime_->newStrFromCStr("_")); Str str1(&scope, runtime_->newStrFromCStr("hello world")); Str str2(&scope, runtime_->newStrFromCStr("helloworldfoobar")); Str str3(&scope, runtime_->newStrFromCStr("hello_world")); EXPECT_TRUE(runtime_->isInternedStr(thread_, str0)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str1)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str2)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str3)); Tuple tuple(&scope, runtime_->newTupleWith4(str0, str1, str2, str3)); strInternConstants(thread_, tuple); str0 = tuple.at(0); str1 = tuple.at(1); str2 = tuple.at(2); str3 = tuple.at(3); EXPECT_TRUE(runtime_->isInternedStr(thread_, str0)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str1)); EXPECT_TRUE(runtime_->isInternedStr(thread_, str2)); EXPECT_TRUE(runtime_->isInternedStr(thread_, str3)); } TEST_F(StrBuiltinsTest, InternStringConstantsInternsStringsInNestedTuples) { HandleScope scope(thread_); Str str0(&scope, runtime_->newStrFromCStr("_")); Str str1(&scope, runtime_->newStrFromCStr("hello world")); Str str2(&scope, runtime_->newStrFromCStr("helloworldfoobar")); EXPECT_TRUE(runtime_->isInternedStr(thread_, str0)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str1)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str2)); Int int0(&scope, SmallInt::fromWord(0)); Int int1(&scope, SmallInt::fromWord(1)); Tuple inner(&scope, runtime_->newTupleWith3(str0, str1, str2)); Tuple outer(&scope, runtime_->newTupleWith3(int0, int1, inner)); strInternConstants(thread_, outer); str0 = inner.at(0); str1 = inner.at(1); str2 = inner.at(2); EXPECT_TRUE(runtime_->isInternedStr(thread_, str0)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str1)); EXPECT_TRUE(runtime_->isInternedStr(thread_, str2)); } TEST_F(StrBuiltinsTest, InternStringConstantsInternsStringsInFrozenSetsInTuples) { HandleScope scope(thread_); Str str0(&scope, runtime_->newStrFromCStr("alpharomeo")); Str str1(&scope, runtime_->newStrFromCStr("hello world")); Str str2(&scope, runtime_->newStrFromCStr("helloworldfoobar")); EXPECT_FALSE(runtime_->isInternedStr(thread_, str0)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str1)); EXPECT_FALSE(runtime_->isInternedStr(thread_, str2)); Int int0(&scope, SmallInt::fromWord(0)); Int int1(&scope, SmallInt::fromWord(1)); FrozenSet inner(&scope, runtime_->newFrozenSet()); Tuple outer(&scope, runtime_->newTupleWith3(int0, int1, inner)); setHashAndAdd(thread_, inner, str0); setHashAndAdd(thread_, inner, str1); setHashAndAdd(thread_, inner, str2); strInternConstants(thread_, outer); inner = outer.at(2); bool all_interned = true; bool some_interned = false; Object value(&scope, NoneType::object()); for (word idx = 0; setNextItem(inner, &idx, &value);) { Str obj(&scope, *value); bool interned = Runtime::isInternedStr(thread_, obj); all_interned &= interned; some_interned |= interned; } EXPECT_FALSE(all_interned); EXPECT_TRUE(some_interned); } TEST_F(StrBuiltinsTest, DunderReprWithPrintableASCIIReturnsStr) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("hello")); Object repr(&scope, runBuiltin(METH(str, __repr__), str)); EXPECT_TRUE(isStrEqualsCStr(*repr, "'hello'")); } TEST_F(StrBuiltinsTest, DunderReprWithStrSubclassReturnsStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass substr = SubStr("hello") )") .isError()); HandleScope scope(thread_); Object substr(&scope, mainModuleAt(runtime_, "substr")); Object repr(&scope, runBuiltin(METH(str, __repr__), substr)); EXPECT_TRUE(isStrEqualsCStr(*repr, "'hello'")); } TEST_F(StrBuiltinsTest, DunderReprWithNonPrintableASCIIReturnsStr) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("\x06")); // ACK character Object repr(&scope, runBuiltin(METH(str, __repr__), str)); EXPECT_TRUE(isStrEqualsCStr(*repr, R"('\x06')")); } TEST_F(StrBuiltinsTest, DunderReprWithDoubleQuotesReturnsStr) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("hello \"world\"")); Object repr(&scope, runBuiltin(METH(str, __repr__), str)); EXPECT_TRUE(isStrEqualsCStr(*repr, R"('hello "world"')")); } TEST_F(StrBuiltinsTest, DunderReprWithSingleQuotesReturnsStr) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("hello 'world'")); Object repr(&scope, runBuiltin(METH(str, __repr__), str)); EXPECT_TRUE(isStrEqualsCStr(*repr, R"("hello 'world'")")); } TEST_F(StrBuiltinsTest, DunderReprWithBothQuotesReturnsStr) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("'hello' \"world\"")); Object repr(&scope, runBuiltin(METH(str, __repr__), str)); EXPECT_TRUE(isStrEqualsCStr(*repr, R"('\'hello\' "world"')")); } TEST_F(StrBuiltinsTest, DunderReprWithNestedQuotesReturnsStr) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr( R"(hello 'world, "I am 'your "father"'"')")); Object repr(&scope, runBuiltin(METH(str, __repr__), str)); EXPECT_TRUE( isStrEqualsCStr(*repr, R"('hello \'world, "I am \'your "father"\'"\'')")); } TEST_F(StrBuiltinsTest, DunderReprOnCommonEscapeSequences) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("\n \t \r \\")); Object repr(&scope, runBuiltin(METH(str, __repr__), str)); EXPECT_TRUE(isStrEqualsCStr(*repr, R"('\n \t \r \\')")); } TEST_F(StrBuiltinsTest, DunderReprWithUnicodeReturnsStr) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("foo\U0001d4eb\U0001d4ea\U0001d4fb")); Object repr(&scope, runBuiltin(METH(str, __repr__), str)); EXPECT_TRUE(isStrEqualsCStr(*repr, "'foo\U0001d4eb\U0001d4ea\U0001d4fb'")); } TEST_F(StrBuiltinsTest, DunderStr) { const char* src = R"( result = 'Hello, World!'.__str__() )"; HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_TRUE(isStrEqualsCStr(*result, "Hello, World!")); } TEST_F(StrBuiltinsTest, SplitWithOneCharSeparator) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "hello".split("e") b = "hello".split("l") )") .isError()); HandleScope scope(thread_); List a(&scope, mainModuleAt(runtime_, "a")); ASSERT_EQ(a.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(a.at(0), "h")); EXPECT_TRUE(isStrEqualsCStr(a.at(1), "llo")); List b(&scope, mainModuleAt(runtime_, "b")); ASSERT_EQ(b.numItems(), 3); EXPECT_TRUE(isStrEqualsCStr(b.at(0), "he")); EXPECT_TRUE(isStrEqualsCStr(b.at(1), "")); EXPECT_TRUE(isStrEqualsCStr(b.at(2), "o")); } TEST_F(StrBuiltinsTest, SplitWithEmptySelfReturnsSingleEmptyString) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "".split("a") )") .isError()); HandleScope scope(thread_); List a(&scope, mainModuleAt(runtime_, "a")); ASSERT_EQ(a.numItems(), 1); EXPECT_TRUE(isStrEqualsCStr(a.at(0), "")); } TEST_F(StrBuiltinsTest, SplitWithMultiCharSeparator) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "hello".split("el") b = "hello".split("ll") c = "hello".split("hello") d = "hellllo".split("ll") )") .isError()); HandleScope scope(thread_); List a(&scope, mainModuleAt(runtime_, "a")); ASSERT_EQ(a.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(a.at(0), "h")); EXPECT_TRUE(isStrEqualsCStr(a.at(1), "lo")); List b(&scope, mainModuleAt(runtime_, "b")); ASSERT_EQ(b.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(b.at(0), "he")); EXPECT_TRUE(isStrEqualsCStr(b.at(1), "o")); List c(&scope, mainModuleAt(runtime_, "c")); ASSERT_EQ(c.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(c.at(0), "")); EXPECT_TRUE(isStrEqualsCStr(c.at(1), "")); List d(&scope, mainModuleAt(runtime_, "d")); ASSERT_EQ(d.numItems(), 3); EXPECT_TRUE(isStrEqualsCStr(d.at(0), "he")); EXPECT_TRUE(isStrEqualsCStr(d.at(1), "")); EXPECT_TRUE(isStrEqualsCStr(d.at(2), "o")); } TEST_F(StrBuiltinsTest, SplitWithMaxSplitZeroReturnsList) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "hello".split("x", 0) b = "hello".split("l", 0) )") .isError()); HandleScope scope(thread_); Object a_obj(&scope, mainModuleAt(runtime_, "a")); Object b_obj(&scope, mainModuleAt(runtime_, "b")); ASSERT_TRUE(a_obj.isList()); ASSERT_TRUE(b_obj.isList()); List a(&scope, *a_obj); List b(&scope, *b_obj); ASSERT_EQ(a.numItems(), 1); ASSERT_EQ(b.numItems(), 1); EXPECT_TRUE(isStrEqualsCStr(a.at(0), "hello")); EXPECT_TRUE(isStrEqualsCStr(a.at(0), "hello")); } TEST_F(StrBuiltinsTest, SplitWithMaxSplitBelowNumPartsStopsEarly) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "hello".split("l", 1) b = "1,2,3,4".split(",", 2) )") .isError()); HandleScope scope(thread_); List a(&scope, mainModuleAt(runtime_, "a")); ASSERT_EQ(a.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(a.at(0), "he")); EXPECT_TRUE(isStrEqualsCStr(a.at(1), "lo")); List b(&scope, mainModuleAt(runtime_, "b")); ASSERT_EQ(b.numItems(), 3); EXPECT_TRUE(isStrEqualsCStr(b.at(0), "1")); EXPECT_TRUE(isStrEqualsCStr(b.at(1), "2")); EXPECT_TRUE(isStrEqualsCStr(b.at(2), "3,4")); } TEST_F(StrBuiltinsTest, SplitWithMaxSplitGreaterThanNumParts) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "hello".split("l", 2) b = "1,2,3,4".split(",", 5) )") .isError()); HandleScope scope(thread_); List a(&scope, mainModuleAt(runtime_, "a")); ASSERT_EQ(a.numItems(), 3); EXPECT_TRUE(isStrEqualsCStr(a.at(0), "he")); EXPECT_TRUE(isStrEqualsCStr(a.at(1), "")); EXPECT_TRUE(isStrEqualsCStr(a.at(2), "o")); List b(&scope, mainModuleAt(runtime_, "b")); ASSERT_EQ(b.numItems(), 4); EXPECT_TRUE(isStrEqualsCStr(b.at(0), "1")); EXPECT_TRUE(isStrEqualsCStr(b.at(1), "2")); EXPECT_TRUE(isStrEqualsCStr(b.at(2), "3")); EXPECT_TRUE(isStrEqualsCStr(b.at(3), "4")); } TEST_F(StrBuiltinsTest, SplitEmptyStringWithNoSepReturnsEmptyList) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "".split() )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "result")); EXPECT_EQ(result.numItems(), 0); } TEST_F(StrBuiltinsTest, SplitWhitespaceStringWithNoSepReturnsEmptyList) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = " \t\n ".split() )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "result")); EXPECT_EQ(result.numItems(), 0); } TEST_F(StrBuiltinsTest, SplitWhitespaceReturnsComponentParts) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = " \t\n hello\t\n world".split() )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {"hello", "world"}); } TEST_F(StrBuiltinsTest, SplitWhitespaceWithMaxsplitEqualsNegativeOneReturnsAllResults) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = " \t\n hello\t\n world".split(maxsplit=-1) )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {"hello", "world"}); } TEST_F(StrBuiltinsTest, SplitWhitespaceWithMaxsplitEqualsZeroReturnsOneElementList) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = " \t\n hello world ".split(maxsplit=0) )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {"hello world "}); } TEST_F(StrBuiltinsTest, SplitWhitespaceWithMaxsplitEqualsOneReturnsTwoElementList) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = " \t\n hello world ".split(maxsplit=1) )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "result")); EXPECT_EQ(result.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "hello")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "world ")); } TEST_F(StrBuiltinsTest, SplitlinesSplitsOnLineBreaks) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello\nworld\rwhats\r\nup".splitlines() )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {"hello", "world", "whats", "up"}); } TEST_F(StrBuiltinsTest, SplitlinesWithKeependsKeepsLineBreaks) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello\nworld\rwhats\r\nup".splitlines(keepends=True) )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {"hello\n", "world\r", "whats\r\n", "up"}); } TEST_F(StrBuiltinsTest, SplitlinesWithNoNewlinesReturnsIdEqualString) { ASSERT_FALSE(runFromCStr(runtime_, R"( s = "hello world foo bar" [result] = s.splitlines() )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "s"), mainModuleAt(runtime_, "result")); } TEST_F(StrBuiltinsTest, SplitlinesWithMultiByteNewlineSplitsLine) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello\u2028world".splitlines() )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {"hello", "world"}); } TEST_F(StrBuiltinsTest, SplitlinesWithMultiByteNewlineAndKeependsSplitsLine) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello\u2028world".splitlines(keepends=True) )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {u8"hello\u2028", "world"}); } TEST_F(StrBuiltinsTest, RsplitWithOneCharSeparatorSplitsCorrectly) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "hello".rsplit("e") )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "h")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "llo")); } TEST_F(StrBuiltinsTest, RsplitWithRepeatedOneCharSeparatorSplitsCorrectly) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "hello".rsplit("l") )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 3); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "he")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "")); EXPECT_TRUE(isStrEqualsCStr(result.at(2), "o")); } TEST_F(StrBuiltinsTest, RsplitWithEmptySelfReturnsSingleEmptyString) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "".rsplit("a") )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 1); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "")); } TEST_F(StrBuiltinsTest, RsplitWithMultiCharSeparatorSplitsFromRight) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "hello".rsplit("el") )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "h")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "lo")); } TEST_F(StrBuiltinsTest, RsplitWithRepeatedCharSeparatorSplitsFromRight) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "hello".rsplit("ll") )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "he")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "o")); } TEST_F(StrBuiltinsTest, RsplitWithSeparatorSameAsInputSplitsIntoEmptyComponents) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "hello".rsplit("hello") )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "")); } TEST_F(StrBuiltinsTest, RsplitWithMultiCharSeparatorWithMultipleAppearancesSplitsCorrectly) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "hellllo".rsplit("ll") )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 3); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "he")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "")); EXPECT_TRUE(isStrEqualsCStr(result.at(2), "o")); } TEST_F(StrBuiltinsTest, RsplitWithMaxSplitZeroReturnsList) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = "hello".rsplit("x", 0) b = "hello".rsplit("l", 0) )") .isError()); HandleScope scope(thread_); Object a_obj(&scope, mainModuleAt(runtime_, "a")); Object b_obj(&scope, mainModuleAt(runtime_, "b")); ASSERT_TRUE(a_obj.isList()); ASSERT_TRUE(b_obj.isList()); List a(&scope, *a_obj); List b(&scope, *b_obj); ASSERT_EQ(a.numItems(), 1); ASSERT_EQ(b.numItems(), 1); EXPECT_TRUE(isStrEqualsCStr(a.at(0), "hello")); EXPECT_TRUE(isStrEqualsCStr(a.at(0), "hello")); } TEST_F(StrBuiltinsTest, RsplitWithRepeatedCharAndMaxSplitBelowNumPartsStopsEarly) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "hello".rsplit("l", 1) )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "hel")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "o")); } TEST_F(StrBuiltinsTest, RsplitWithMaxSplitBelowNumPartsStopsEarly) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "1,2,3,4".rsplit(",", 2) )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 3); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "1,2")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "3")); EXPECT_TRUE(isStrEqualsCStr(result.at(2), "4")); } TEST_F(StrBuiltinsTest, RsplitWithRepeatedCharAndMaxSplitGreaterThanNumPartsSplitsCorrectly) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "hello".rsplit("l", 2) )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 3); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "he")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "")); EXPECT_TRUE(isStrEqualsCStr(result.at(2), "o")); } TEST_F(StrBuiltinsTest, RsplitWithMaxSplitGreaterThanNumPartsSplitsCorrectly) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = "1,2,3,4".rsplit(",", 5) )") .isError()); HandleScope scope(thread_); List result(&scope, mainModuleAt(runtime_, "l")); ASSERT_EQ(result.numItems(), 4); EXPECT_TRUE(isStrEqualsCStr(result.at(0), "1")); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "2")); EXPECT_TRUE(isStrEqualsCStr(result.at(2), "3")); EXPECT_TRUE(isStrEqualsCStr(result.at(3), "4")); } TEST_F(StrBuiltinsTest, StrStripWithInvalidCharsRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( "test".strip(1) )"), LayoutId::kTypeError, "str.strip() arg must be None or str")); } TEST_F(StrBuiltinsTest, StrLStripWithInvalidCharsRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( "test".lstrip(1) )"), LayoutId::kTypeError, "str.lstrip() arg must be None or str")); } TEST_F(StrBuiltinsTest, StrRStripWithInvalidCharsRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( "test".rstrip(1) )"), LayoutId::kTypeError, "str.rstrip() arg must be None or str")); } TEST_F(StrBuiltinsTest, StripWithNoneArgStripsBoth) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr(" Hello World ")); Object none(&scope, NoneType::object()); Object result(&scope, runBuiltin(METH(str, strip), str, none)); EXPECT_TRUE(isStrEqualsCStr(*result, "Hello World")); } TEST_F(StrBuiltinsTest, LStripWithNoneArgStripsLeft) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr(" Hello World ")); Object none(&scope, NoneType::object()); Object result(&scope, runBuiltin(METH(str, lstrip), str, none)); EXPECT_TRUE(isStrEqualsCStr(*result, "Hello World ")); } TEST_F(StrBuiltinsTest, LStripWithSubClassAndNonArgStripsLeft) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass substr = SubStr(" Hello World ") )") .isError()); Object str(&scope, mainModuleAt(runtime_, "substr")); Object none(&scope, NoneType::object()); Object result(&scope, runBuiltin(METH(str, lstrip), str, none)); EXPECT_TRUE(isStrEqualsCStr(*result, "Hello World ")); } TEST_F(StrBuiltinsTest, RStripWithNoneArgStripsRight) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr(" Hello World ")); Object none(&scope, NoneType::object()); Object result(&scope, runBuiltin(METH(str, rstrip), str, none)); EXPECT_TRUE(isStrEqualsCStr(*result, " Hello World")); } TEST_F(StrBuiltinsTest, RStripWithSubClassAndNoneArgStripsRight) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass substr = SubStr(" Hello World ") )") .isError()); Object str(&scope, mainModuleAt(runtime_, "substr")); Object none(&scope, NoneType::object()); Object result(&scope, runBuiltin(METH(str, rstrip), str, none)); EXPECT_TRUE(isStrEqualsCStr(*result, " Hello World")); } TEST_F(StrBuiltinsTest, StripWithoutArgsStripsBoth) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr(" \n\tHello World\n\t ")); Object none(&scope, NoneType::object()); Object result(&scope, runBuiltin(METH(str, strip), str, none)); EXPECT_TRUE(isStrEqualsCStr(*result, "Hello World")); } TEST_F(StrBuiltinsTest, StripWithSubClassAndWithoutArgsStripsBoth) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass substr = SubStr(" \n\tHello World\n\t ") )") .isError()); Object str(&scope, mainModuleAt(runtime_, "substr")); Object none(&scope, NoneType::object()); Object result(&scope, runBuiltin(METH(str, strip), str, none)); EXPECT_TRUE(isStrEqualsCStr(*result, "Hello World")); } TEST_F(StrBuiltinsTest, LStripWithoutArgsStripsLeft) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr(" \n\tHello World\n\t ")); Object none(&scope, NoneType::object()); Object result(&scope, runBuiltin(METH(str, lstrip), str, none)); EXPECT_TRUE(isStrEqualsCStr(*result, "Hello World\n\t ")); } TEST_F(StrBuiltinsTest, RStripWithoutArgsStripsRight) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr(" \n\tHello World\n\t ")); Object none(&scope, NoneType::object()); Object result(&scope, runBuiltin(METH(str, rstrip), str, none)); EXPECT_TRUE(isStrEqualsCStr(*result, " \n\tHello World")); } TEST_F(StrBuiltinsTest, StripWithCharsStripsChars) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("bcaHello Worldcab")); Object chars(&scope, runtime_->newStrFromCStr("abc")); Object result(&scope, runBuiltin(METH(str, strip), str, chars)); EXPECT_TRUE(isStrEqualsCStr(*result, "Hello World")); } TEST_F(StrBuiltinsTest, LStripWithCharsStripsCharsToLeft) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("bcaHello Worldcab")); Object chars(&scope, runtime_->newStrFromCStr("abc")); Object result(&scope, runBuiltin(METH(str, lstrip), str, chars)); EXPECT_TRUE(isStrEqualsCStr(*result, "Hello Worldcab")); } TEST_F(StrBuiltinsTest, RStripWithCharsStripsCharsToRight) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("bcaHello Worldcab")); Object chars(&scope, runtime_->newStrFromCStr("abc")); Object result(&scope, runBuiltin(METH(str, rstrip), str, chars)); EXPECT_TRUE(isStrEqualsCStr(*result, "bcaHello World")); } TEST_F(StrBuiltinsTest, ReplaceWithDefaultCountReplacesAll) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "a1a1a1a".replace("a", "b") )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_TRUE(result.isStr()); EXPECT_TRUE(isStrEqualsCStr(*result, "b1b1b1b")); } TEST_F(StrBuiltinsTest, ReplaceWithCountReplacesCountedOccurrences) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "a1a1a1a".replace("a", "b", 2) )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_TRUE(result.isStr()); EXPECT_TRUE(isStrEqualsCStr(*result, "b1b1a1a")); } TEST_F(StrBuiltinsTest, ReplaceWithCountOfIndexTypeReplacesCountedOccurrences) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "a1a1a1a".replace("a", "b", True) )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_TRUE(result.isStr()); EXPECT_TRUE(isStrEqualsCStr(*result, "b1a1a1a")); } TEST_F(StrBuiltinsTest, ReplaceWithNonMatchingReturnsSameObject) { ASSERT_FALSE(runFromCStr(runtime_, R"( s = "a" result = s is s.replace("z", "b") )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_EQ(*result, Bool::trueObj()); } TEST_F(StrBuiltinsTest, ReplaceWithMissingArgRaisesTypeError) { EXPECT_TRUE(raisedWithStr( runFromCStr(runtime_, "'aa'.replace('a')"), LayoutId::kTypeError, "'str.replace' takes min 3 positional arguments but 2 given")); } TEST_F(StrBuiltinsTest, ReplaceWithNonIntCountRaisesTypeError) { EXPECT_TRUE( raisedWithStr(runFromCStr(runtime_, "'aa'.replace('a', 'a', 'a')"), LayoutId::kTypeError, "'str' object cannot be interpreted as an integer")); } TEST_F(StrBuiltinsTest, DunderIterReturnsStrIter) { HandleScope scope(thread_); Str empty_str(&scope, Str::empty()); Object iter(&scope, runBuiltin(METH(str, __iter__), empty_str)); ASSERT_TRUE(iter.isStrIterator()); } TEST_F(StrBuiltinsTest, DunderIterWithSubClassReturnsStrIterator) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass substr = SubStr("") )") .isError()); Object empty_str(&scope, mainModuleAt(runtime_, "substr")); Object iter(&scope, runBuiltin(METH(str, __iter__), empty_str)); EXPECT_TRUE(iter.isStrIterator()); } TEST_F(StrIteratorBuiltinsTest, CallDunderNextReadsAsciiCharactersSequentially) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("ab")); Object iter(&scope, runBuiltin(METH(str, __iter__), str)); ASSERT_TRUE(iter.isStrIterator()); Object item0(&scope, runBuiltin(METH(str_iterator, __next__), iter)); EXPECT_TRUE(isStrEqualsCStr(*item0, "a")); Object item1(&scope, runBuiltin(METH(str_iterator, __next__), iter)); EXPECT_TRUE(isStrEqualsCStr(*item1, "b")); } TEST_F(StrIteratorBuiltinsTest, CallDunderNextReadsUnicodeCharactersSequentially) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr(u8"a\u00E4b")); Object iter(&scope, runBuiltin(METH(str, __iter__), str)); ASSERT_TRUE(iter.isStrIterator()); Object item0(&scope, runBuiltin(METH(str_iterator, __next__), iter)); EXPECT_TRUE(isStrEqualsCStr(*item0, "a")); Object item1(&scope, runBuiltin(METH(str_iterator, __next__), iter)); EXPECT_EQ(*item1, SmallStr::fromCodePoint(0xe4)); Object item2(&scope, runBuiltin(METH(str_iterator, __next__), iter)); EXPECT_TRUE(isStrEqualsCStr(*item2, "b")); } TEST_F(StrIteratorBuiltinsTest, DunderIterReturnsSelf) { HandleScope scope(thread_); Str empty_str(&scope, Str::empty()); Object iter(&scope, runBuiltin(METH(str, __iter__), empty_str)); ASSERT_TRUE(iter.isStrIterator()); // Now call __iter__ on the iterator object Object result(&scope, runBuiltin(METH(str_iterator, __iter__), iter)); ASSERT_EQ(*result, *iter); } TEST_F(StrIteratorBuiltinsTest, DunderLengthHintOnEmptyStrIteratorReturnsZero) { HandleScope scope(thread_); Str empty_str(&scope, Str::empty()); Object iter(&scope, runBuiltin(METH(str, __iter__), empty_str)); ASSERT_TRUE(iter.isStrIterator()); Object length_hint(&scope, runBuiltin(METH(str_iterator, __length_hint__), iter)); EXPECT_TRUE(isIntEqualsWord(*length_hint, 0)); } TEST_F(StrIteratorBuiltinsTest, DunderLengthHintOnConsumedStrIteratorReturnsZero) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("a")); Object iter(&scope, runBuiltin(METH(str, __iter__), str)); ASSERT_TRUE(iter.isStrIterator()); Object length_hint1(&scope, runBuiltin(METH(str_iterator, __length_hint__), iter)); EXPECT_TRUE(isIntEqualsWord(*length_hint1, 1)); // Consume the iterator Object item1(&scope, runBuiltin(METH(str_iterator, __next__), iter)); ASSERT_TRUE(item1.isStr()); ASSERT_EQ(item1, runtime_->newStrFromCStr("a")); Object length_hint2(&scope, runBuiltin(METH(str_iterator, __length_hint__), iter)); EXPECT_TRUE(isIntEqualsWord(*length_hint2, 0)); } TEST_F(StrBuiltinsTest, StripSpaceWithEmptyStrIsIdentity) { HandleScope scope(thread_); Str empty_str(&scope, Str::empty()); Str lstripped_empty_str(&scope, strStripSpaceLeft(thread_, empty_str)); EXPECT_EQ(*empty_str, *lstripped_empty_str); Str rstripped_empty_str(&scope, strStripSpaceRight(thread_, empty_str)); EXPECT_EQ(*empty_str, *rstripped_empty_str); Str stripped_empty_str(&scope, strStripSpace(thread_, empty_str)); EXPECT_EQ(*empty_str, *stripped_empty_str); } TEST_F(StrBuiltinsTest, StripSpaceWithUnstrippableStrIsIdentity) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("Nothing to strip here")); ASSERT_TRUE(str.isLargeStr()); Str lstripped_str(&scope, strStripSpaceLeft(thread_, str)); EXPECT_EQ(*str, *lstripped_str); Str rstripped_str(&scope, strStripSpaceRight(thread_, str)); EXPECT_EQ(*str, *rstripped_str); Str stripped_str(&scope, strStripSpace(thread_, str)); EXPECT_EQ(*str, *stripped_str); } TEST_F(StrBuiltinsTest, StripSpaceWithUnstrippableSmallStrIsIdentity) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("nostrip")); ASSERT_TRUE(str.isSmallStr()); Str lstripped_str(&scope, strStripSpaceLeft(thread_, str)); EXPECT_EQ(*str, *lstripped_str); Str rstripped_str(&scope, strStripSpaceRight(thread_, str)); EXPECT_EQ(*str, *rstripped_str); Str stripped_str(&scope, strStripSpace(thread_, str)); EXPECT_EQ(*str, *stripped_str); } TEST_F(StrBuiltinsTest, StripSpaceWithFullyStrippableUnicodeStrReturnsEmptyStr) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr(u8"\n\r\t\f \u3000 \u202f \n\t\r\f")); Str lstripped_str(&scope, strStripSpaceLeft(thread_, str)); EXPECT_EQ(lstripped_str.length(), 0); Str rstripped_str(&scope, strStripSpaceRight(thread_, str)); EXPECT_EQ(rstripped_str.length(), 0); Str stripped_str(&scope, strStripSpace(thread_, str)); EXPECT_EQ(stripped_str.length(), 0); } TEST_F(StrBuiltinsTest, StripSpaceLeft) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr(" strp ")); ASSERT_TRUE(str.isSmallStr()); Str lstripped_str(&scope, strStripSpaceLeft(thread_, str)); ASSERT_TRUE(lstripped_str.isSmallStr()); EXPECT_TRUE(isStrEqualsCStr(*lstripped_str, "strp ")); Str str1(&scope, runtime_->newStrFromCStr(" \n \n\tLot of leading space ")); ASSERT_TRUE(str1.isLargeStr()); Str lstripped_str1(&scope, strStripSpaceLeft(thread_, str1)); EXPECT_TRUE(isStrEqualsCStr(*lstripped_str1, "Lot of leading space ")); Str str2(&scope, runtime_->newStrFromCStr(u8"\n\n\n \u2005 \ntest")); ASSERT_TRUE(str2.isLargeStr()); Str lstripped_str2(&scope, strStripSpaceLeft(thread_, str2)); ASSERT_TRUE(lstripped_str2.isSmallStr()); EXPECT_TRUE(isStrEqualsCStr(*lstripped_str2, "test")); } TEST_F(StrBuiltinsTest, StripSpaceRight) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr(" strp ")); ASSERT_TRUE(str.isSmallStr()); Str rstripped_str(&scope, strStripSpaceRight(thread_, str)); ASSERT_TRUE(rstripped_str.isSmallStr()); EXPECT_TRUE(isStrEqualsCStr(*rstripped_str, " strp")); Str str1(&scope, runtime_->newStrFromCStr(" Lot of trailing space\t\n \n ")); ASSERT_TRUE(str1.isLargeStr()); Str rstripped_str1(&scope, strStripSpaceRight(thread_, str1)); EXPECT_TRUE(isStrEqualsCStr(*rstripped_str1, " Lot of trailing space")); Str str2(&scope, runtime_->newStrFromCStr(u8"test\n \u2004 \n\n")); ASSERT_TRUE(str2.isLargeStr()); Str rstripped_str2(&scope, strStripSpaceRight(thread_, str2)); ASSERT_TRUE(rstripped_str2.isSmallStr()); EXPECT_TRUE(isStrEqualsCStr(*rstripped_str2, "test")); } TEST_F(StrBuiltinsTest, StripSpaceBoth) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr(" strp ")); ASSERT_TRUE(str.isSmallStr()); Str stripped_str(&scope, strStripSpace(thread_, str)); ASSERT_TRUE(stripped_str.isSmallStr()); EXPECT_TRUE(isStrEqualsCStr(*stripped_str, "strp")); Str str1(&scope, runtime_->newStrFromCStr( "\n \n \n\tLot of leading and trailing space\n \n ")); ASSERT_TRUE(str1.isLargeStr()); Str stripped_str1(&scope, strStripSpace(thread_, str1)); EXPECT_TRUE( isStrEqualsCStr(*stripped_str1, "Lot of leading and trailing space")); Str str2(&scope, runtime_->newStrFromCStr(u8"\n\u00a0\ttest\t \u1680 \n\n\n")); ASSERT_TRUE(str2.isLargeStr()); Str stripped_str2(&scope, strStripSpace(thread_, str2)); ASSERT_TRUE(stripped_str2.isSmallStr()); EXPECT_TRUE(isStrEqualsCStr(*stripped_str2, "test")); } TEST_F(StrBuiltinsTest, StripWithEmptyStrIsIdentity) { HandleScope scope(thread_); Str empty_str(&scope, Str::empty()); Str chars(&scope, runtime_->newStrFromCStr("abc")); Str lstripped_empty_str(&scope, strStripLeft(thread_, empty_str, chars)); EXPECT_EQ(*empty_str, *lstripped_empty_str); Str rstripped_empty_str(&scope, strStripRight(thread_, empty_str, chars)); EXPECT_EQ(*empty_str, *rstripped_empty_str); Str stripped_empty_str(&scope, strStrip(thread_, empty_str, chars)); EXPECT_EQ(*empty_str, *stripped_empty_str); } TEST_F(StrBuiltinsTest, StripWithFullyStrippableStrReturnsEmptyStr) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("bbbbaaaaccccdddd")); Str chars(&scope, runtime_->newStrFromCStr("abcd")); Str lstripped_str(&scope, strStripLeft(thread_, str, chars)); EXPECT_EQ(lstripped_str.length(), 0); Str rstripped_str(&scope, strStripRight(thread_, str, chars)); EXPECT_EQ(rstripped_str.length(), 0); Str stripped_str(&scope, strStrip(thread_, str, chars)); EXPECT_EQ(stripped_str.length(), 0); } TEST_F(StrBuiltinsTest, StripWithEmptyCharsIsIdentity) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr(" Just another string ")); Str chars(&scope, Str::empty()); Str lstripped_str(&scope, strStripLeft(thread_, str, chars)); EXPECT_EQ(*str, *lstripped_str); Str rstripped_str(&scope, strStripRight(thread_, str, chars)); EXPECT_EQ(*str, *rstripped_str); Str stripped_str(&scope, strStrip(thread_, str, chars)); EXPECT_EQ(*str, *stripped_str); } TEST_F(StrBuiltinsTest, StripBoth) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("bcdHello Worldcab")); Str chars(&scope, runtime_->newStrFromCStr("abcd")); Str stripped_str(&scope, strStrip(thread_, str, chars)); EXPECT_TRUE(isStrEqualsCStr(*stripped_str, "Hello Worl")); } TEST_F(StrBuiltinsTest, StripLeft) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("bcdHello Worldcab")); Str chars(&scope, runtime_->newStrFromCStr("abcd")); Str lstripped_str(&scope, strStripLeft(thread_, str, chars)); EXPECT_TRUE(isStrEqualsCStr(*lstripped_str, "Hello Worldcab")); } TEST_F(StrBuiltinsTest, StripRight) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("bcdHello Worldcab")); Str chars(&scope, runtime_->newStrFromCStr("abcd")); Str rstripped_str(&scope, strStripRight(thread_, str, chars)); EXPECT_TRUE(isStrEqualsCStr(*rstripped_str, "bcdHello Worl")); } TEST_F(StrBuiltinsTest, CountWithNeedleLargerThanHaystackReturnsZero) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("h")); Str needle(&scope, runtime_->newStrFromCStr("hello")); EXPECT_TRUE(isIntEqualsWord(strCount(haystack, needle, 0, kMaxWord), 0)); } TEST_F(StrBuiltinsTest, CountWithSmallNegativeStartIndexesFromEnd) { // Index from the end if abs(start) < len(haystack) HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello")); Str needle(&scope, runtime_->newStrFromCStr("h")); EXPECT_TRUE(isIntEqualsWord(strCount(haystack, needle, -1, kMaxWord), 0)); } TEST_F(StrBuiltinsTest, CountWithLargeNegativeStartIndexesFromStart) { // Default to 0 if abs(start) >= len(haystack) HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello")); Str needle(&scope, runtime_->newStrFromCStr("h")); EXPECT_TRUE(isIntEqualsWord(strCount(haystack, needle, -10, kMaxWord), 1)); } TEST_F(StrBuiltinsTest, CountWithSmallNegativeEndIndexesFromEnd) { // Index from the end if abs(end) < len(haystack) HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello")); Str needle(&scope, runtime_->newStrFromCStr("o")); EXPECT_TRUE(isIntEqualsWord(strCount(haystack, needle, 0, -2), 0)); } TEST_F(StrBuiltinsTest, CountWithLargeNegativeEndIndexesFromStart) { // Default to 0 if abs(end) >= len(haystack) HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello")); Str needle(&scope, runtime_->newStrFromCStr("o")); EXPECT_TRUE(isIntEqualsWord(strCount(haystack, needle, 0, -10), 0)); } TEST_F(StrBuiltinsTest, CountWithSingleCharNeedleFindsNeedle) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("oooo")); Str needle(&scope, runtime_->newStrFromCStr("o")); EXPECT_TRUE(isIntEqualsWord(strCount(haystack, needle, 0, kMaxWord), 4)); } TEST_F(StrBuiltinsTest, CountWithMultiCharNeedleFindsNeedle) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("oooo")); Str needle(&scope, runtime_->newStrFromCStr("oo")); EXPECT_TRUE(isIntEqualsWord(strCount(haystack, needle, 0, kMaxWord), 2)); } TEST_F(StrBuiltinsTest, CountWithUnicodeNeedleReturnsCount) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr( u8"\u20ac10 Cr\u00e8me Cr\u00e8me br\u00fbl\u00e9e")); Str needle(&scope, runtime_->newStrFromCStr(u8"Cr\u00e8me")); EXPECT_TRUE(isIntEqualsWord(strCount(haystack, needle, 0, kMaxWord), 2)); } TEST_F(StrBuiltinsTest, CountWithNonNormalizedUTF8StringFindsChar) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr(u8"\u0061\u0308\u0304")); fprintf(stderr, "'%s'\n", unique_c_ptr<char>(haystack.toCStr()).get()); Str needle(&scope, runtime_->newStrFromCStr("a")); EXPECT_TRUE(isIntEqualsWord(strCount(haystack, needle, 0, kMaxWord), 1)); } TEST_F(StrBuiltinsTest, FindWithEmptyHaystackAndEmptyNeedleReturnsZero) { HandleScope scope(thread_); Str haystack(&scope, Str::empty()); Str needle(&scope, Str::empty()); EXPECT_EQ(strFind(haystack, needle), 0); } TEST_F(StrBuiltinsTest, FindWithEmptyHaystackAndNonEmptyNeedleReturnsNegativeOne) { HandleScope scope(thread_); Str haystack(&scope, Str::empty()); Str needle(&scope, runtime_->newStrFromCStr("hello")); EXPECT_EQ(strFind(haystack, needle), -1); } TEST_F(StrBuiltinsTest, FindWithNonEmptyHaystackAndEmptyNeedleReturnsZero) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello")); Str needle(&scope, Str::empty()); EXPECT_EQ(strFind(haystack, needle), 0); } TEST_F(StrBuiltinsTest, FindWithNonExistentNeedleReturnsNegativeOne) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello")); Str needle(&scope, runtime_->newStrFromCStr("a")); EXPECT_EQ(strFind(haystack, needle), -1); } TEST_F(StrBuiltinsTest, FindReturnsIndexOfFirstOccurrence) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("helloworldhelloworld")); Str needle(&scope, runtime_->newStrFromCStr("world")); EXPECT_EQ(strFind(haystack, needle), 5); } TEST_F(StrBuiltinsTest, FindFirstNonWhitespaceWithEmptyStringReturnsZero) { HandleScope scope(thread_); Str str(&scope, Str::empty()); EXPECT_EQ(strFindFirstNonWhitespace(str), 0); } TEST_F(StrBuiltinsTest, FindFirstNonWhitespaceWithOnlyWhitespaceReturnsLength) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr(u8" \u205f ")); EXPECT_EQ(strFindFirstNonWhitespace(str), str.length()); } TEST_F(StrBuiltinsTest, FindFirstNonWhitespaceFindsFirstNonWhitespaceChar) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr(u8" \u3000 foo ")); EXPECT_EQ(strFindFirstNonWhitespace(str), 5); } TEST_F(StrBuiltinsTest, FindWithEmptyNeedleReturnsZero) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".find("") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 0)); } TEST_F(StrBuiltinsTest, FindWithEmptyNeedleReturnsNegativeOne) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".find("", 8) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), -1)); } TEST_F(StrBuiltinsTest, FindWithEmptyNeedleAndSliceReturnsStart) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".find("", 3, 5) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 3)); } TEST_F(StrBuiltinsTest, FindWithEmptyNeedleAndEmptySliceReturnsStart) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".find("", 3, 3) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 3)); } TEST_F(StrBuiltinsTest, FindWithNegativeStartClipsToZero) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".find("h", -5, 1) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 0)); } TEST_F(StrBuiltinsTest, FindWithEndPastEndOfStringClipsToLength) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".find("h", 0, 100) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 0)); } TEST_F(StrBuiltinsTest, FindCallsDunderIndexOnStart) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return 4 result = "bbbbbbbb".find("b", C()) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 4)); } TEST_F(StrBuiltinsTest, FindCallsDunderIndexOnEnd) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return 5 result = "aaaabbbb".find("b", 0, C()) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 4)); } TEST_F(StrBuiltinsTest, FindClampsStartReturningBigNumber) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return 46116860184273879030 result = "aaaabbbb".find("b", C()) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), -1)); } TEST_F(StrBuiltinsTest, FindClampsEndReturningBigNumber) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return 46116860184273879030 result = "aaaabbbb".find("b", 0, C()) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 4)); } TEST_F(StrBuiltinsTest, FindClampsEndReturningBigNegativeNumber) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return -46116860184273879030 result = "aaaabbbb".find("b", 0, C()) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), -1)); } TEST_F(StrBuiltinsTest, FindWithUnicodeReturnsCodePointIndex) { ASSERT_FALSE(runFromCStr(runtime_, R"( s = "Cr\u00e8me br\u00fbl\u00e9e" result = s.find("e") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 4)); } TEST_F(StrBuiltinsTest, FindWithStartAfterUnicodeCodePoint) { ASSERT_FALSE(runFromCStr(runtime_, R"( s = "\u20ac10 Cr\u00e8me br\u00fbl\u00e9e" result = s.find("e", 4) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 8)); } TEST_F(StrBuiltinsTest, FindWithDifferentSizeCodePoints) { ASSERT_FALSE(runFromCStr(runtime_, R"( s = "Cr\u00e8me \u10348 \u29D98 br\u00fbl\u00e9e" result = s.find("\u29D98") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 9)); } TEST_F(StrBuiltinsTest, FindWithOneCharStringFindsChar) { ASSERT_FALSE(runFromCStr(runtime_, R"( result1 = "hello".find("h") result2 = "hello".find("e") result3 = "hello".find("z") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result1"), 0)); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result2"), 1)); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result3"), -1)); } TEST_F(StrBuiltinsTest, FindWithSlicePreservesIndices) { ASSERT_FALSE(runFromCStr(runtime_, R"( result1 = "hello".find("h", 1) result2 = "hello".find("e", 1) result3 = "hello".find("o", 0, 2) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result1"), -1)); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result2"), 1)); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result3"), -1)); } TEST_F(StrBuiltinsTest, FindWithMultiCharStringFindsSubstring) { ASSERT_FALSE(runFromCStr(runtime_, R"( result1 = "hello".find("he") result2 = "hello".find("el") result3 = "hello".find("ze") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result1"), 0)); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result2"), 1)); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result3"), -1)); } TEST_F(StrBuiltinsTest, RfindWithOneCharStringFindsChar) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".rfind("l") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 3)); } TEST_F(StrBuiltinsTest, RfindCharWithUnicodeReturnsCodePointIndex) { ASSERT_FALSE(runFromCStr(runtime_, R"( s = "Cr\u00e8me br\u00fbl\u00e9e" result = s.rfind("e") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 11)); } TEST_F(StrBuiltinsTest, RfindCharWithStartAfterUnicodeCodePoint) { ASSERT_FALSE(runFromCStr(runtime_, R"( s = "\u20ac10 Cr\u00e8me br\u00fbl\u00e9e" result = s.rfind("e", 4) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 15)); } TEST_F(StrBuiltinsTest, RfindCharWithDifferentSizeCodePoints) { ASSERT_FALSE(runFromCStr(runtime_, R"( s = "Cr\u00e8me \u10348 \u29D98 br\u00fbl\u00e9e\u2070E\u29D98 " result = s.rfind("\u29D98") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 20)); } TEST_F(StrBuiltinsTest, RfindWithMultiCharStringFindsSubstring) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "aabbaa".rfind("aa") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 4)); } TEST_F(StrBuiltinsTest, RfindCharWithNegativeStartClipsToZero) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".rfind("h", -5, 1) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 0)); } TEST_F(StrBuiltinsTest, RfindCharWithEndPastEndOfStringClipsToLength) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".rfind("h", 0, 100) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 0)); } TEST_F(StrBuiltinsTest, RfindWithEndLessThanLengthStartsAtEnd) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "aaaabb".rfind("b", 0, 5) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 4)); } TEST_F(StrBuiltinsTest, RfindCallsDunderIndexOnEnd) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return 5 result = "aaaabbbb".rfind("b", 0, C()) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 4)); } TEST_F(StrBuiltinsTest, RfindClampsStartReturningBigNumber) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return 46116860184273879030 result = "aaaabbbb".rfind("b", C()) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), -1)); } TEST_F(StrBuiltinsTest, RfindClampsEndReturningBigNumber) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return 46116860184273879030 result = "aaaabbbb".rfind("b", 0, C()) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 7)); } TEST_F(StrBuiltinsTest, RfindClampsEndReturningBigNegativeNumber) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __index__(self): return -46116860184273879030 result = "aaaabbbb".rfind("b", 0, C()) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), -1)); } TEST_F(StrBuiltinsTest, RfindWithEmptyHaystackAndNeedleReturnsZero) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "".rfind("") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 0)); } TEST_F(StrBuiltinsTest, RfindWithEmptyHaystackAndNeedleAndBoundsReturnsZero) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "".rfind("", 0, 5) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 0)); } TEST_F(StrBuiltinsTest, RfindCharWithEmptyNeedleReturnsLength) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".rfind("") )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 5)); } TEST_F(StrBuiltinsTest, RfindCharWithEmptyNeedleReturnsNegativeOne) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".rfind("", 8) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), -1)); } TEST_F(StrBuiltinsTest, RfindCharWithEmptyNeedleAndSliceReturnsEnd) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".rfind("", 3, 5) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 5)); } TEST_F(StrBuiltinsTest, RfindWithEmptyNeedleAndEmptySliceReturnsEnd) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "hello".rfind("", 3, 3) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 3)); } TEST_F(StrBuiltinsTest, IndexWithPresentSubstringReturnsIndex) { ASSERT_FALSE(runFromCStr(runtime_, R"( s = "\u20ac10 Cr\u00e8me br\u00fbl\u00e9e" result = s.index("e", 4) )") .isError()); EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 8)); } TEST_F(StrBuiltinsTest, IndexWithMissingSubstringRaisesValueError) { EXPECT_TRUE( raised(runFromCStr(runtime_, "'h'.index('q')"), LayoutId::kValueError)); } TEST_F(StrBuiltinsTest, DunderHashReturnsSmallInt) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("hello world")); EXPECT_TRUE(runBuiltin(METH(str, __hash__), str).isSmallInt()); } TEST_F(StrBuiltinsTest, DunderHashSmallStringReturnsSmallInt) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("h")); EXPECT_TRUE(runBuiltin(METH(str, __hash__), str).isSmallInt()); } TEST_F(StrBuiltinsTest, DunderHashWithEquivalentStringsReturnsSameHash) { HandleScope scope(thread_); Str str1(&scope, runtime_->newStrFromCStr("hello world foobar")); Str str2(&scope, runtime_->newStrFromCStr("hello world foobar")); EXPECT_NE(*str1, *str2); Object result1(&scope, runBuiltin(METH(str, __hash__), str1)); Object result2(&scope, runBuiltin(METH(str, __hash__), str2)); EXPECT_TRUE(result1.isSmallInt()); EXPECT_TRUE(result2.isSmallInt()); EXPECT_EQ(*result1, *result2); } TEST_F(StrBuiltinsTest, DunderHashWithSubclassReturnsSameHash) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C(str): pass i0 = C("abc") i1 = "abc" )") .isError()); Object i0(&scope, mainModuleAt(runtime_, "i0")); Object i1(&scope, mainModuleAt(runtime_, "i1")); Object result0(&scope, runBuiltin(METH(str, __hash__), i0)); Object result1(&scope, runBuiltin(METH(str, __hash__), i1)); EXPECT_TRUE(result0.isSmallInt()); EXPECT_TRUE(result1.isSmallInt()); EXPECT_EQ(result0, result1); } TEST_F(StringIterTest, SimpleIter) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("test")); EXPECT_TRUE(str.equalsCStr("test")); StrIterator iter(&scope, runtime_->newStrIterator(str)); Object ch(&scope, strIteratorNext(thread_, iter)); ASSERT_TRUE(ch.isStr()); EXPECT_TRUE(Str::cast(*ch).equalsCStr("t")); ch = strIteratorNext(thread_, iter); ASSERT_TRUE(ch.isStr()); EXPECT_TRUE(Str::cast(*ch).equalsCStr("e")); ch = strIteratorNext(thread_, iter); ASSERT_TRUE(ch.isStr()); EXPECT_TRUE(Str::cast(*ch).equalsCStr("s")); ch = strIteratorNext(thread_, iter); ASSERT_TRUE(ch.isStr()); EXPECT_TRUE(Str::cast(*ch).equalsCStr("t")); ch = strIteratorNext(thread_, iter); ASSERT_TRUE(ch.isError()); } TEST_F(StringIterTest, SetIndex) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("test")); EXPECT_TRUE(str.equalsCStr("test")); StrIterator iter(&scope, runtime_->newStrIterator(str)); iter.setIndex(1); Object ch(&scope, strIteratorNext(thread_, iter)); ASSERT_TRUE(ch.isStr()); EXPECT_TRUE(Str::cast(*ch).equalsCStr("e")); iter.setIndex(5); ch = strIteratorNext(thread_, iter); // Index should not have advanced. ASSERT_EQ(iter.index(), 5); ASSERT_TRUE(ch.isError()); } TEST_F(StrBuiltinsTest, DunderContainsWithNonStrSelfRaisesTypeError) { EXPECT_TRUE(raised(runFromCStr(runtime_, "str.__contains__(3, 'foo')"), LayoutId::kTypeError)); } TEST_F(StrBuiltinsTest, DunderContainsWithNonStrOtherRaisesTypeError) { EXPECT_TRUE(raised(runFromCStr(runtime_, "str.__contains__('foo', 3)"), LayoutId::kTypeError)); } TEST_F(StrBuiltinsTest, DunderContainsWithPresentSubstrReturnsTrue) { ASSERT_FALSE( runFromCStr(runtime_, "result = str.__contains__('foo', 'f')").isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_EQ(*result, Bool::trueObj()); } TEST_F(StrBuiltinsTest, DunderContainsWithNotPresentSubstrReturnsTrue) { ASSERT_FALSE( runFromCStr(runtime_, "result = str.__contains__('foo', 'q')").isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_EQ(*result, Bool::falseObj()); } TEST_F(StrBuiltinsTest, CapitalizeReturnsCapitalizedStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "foo".capitalize() )") .isError()); EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "Foo")); } TEST_F(StrBuiltinsTest, CapitalizeUpperCaseReturnsUnmodifiedStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "Foo".capitalize() )") .isError()); EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "Foo")); } TEST_F(StrBuiltinsTest, CapitalizeAllUppercaseReturnsCapitalizedStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "FOO".capitalize() )") .isError()); EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "Foo")); } TEST_F(StrBuiltinsTest, CapitalizeWithEmptyStrReturnsEmptyStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = "".capitalize() )") .isError()); EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "")); } TEST_F(StrBuiltinsTest, StrUnderlyingWithStrReturnsSameStr) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("hello")); Object underlying(&scope, strUnderlying(*str)); EXPECT_EQ(*str, *underlying); } TEST_F(StrBuiltinsTest, StrUnderlyingWithSubClassReturnsUnderlyingStr) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class SubStr(str): pass substr = SubStr("some string") )") .isError()); Object substr(&scope, mainModuleAt(runtime_, "substr")); ASSERT_FALSE(substr.isStr()); Object underlying(&scope, strUnderlying(*substr)); EXPECT_TRUE(isStrEqualsCStr(*underlying, "some string")); } } // namespace testing } // namespace py