runtime/objects-test.cpp (1,560 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include <cstdint> #include "gtest/gtest.h" #include "byteslike.h" #include "runtime.h" #include "str-builtins.h" #include "test-utils.h" namespace py { namespace testing { using BytearrayTest = RuntimeFixture; using LargeBytesTest = RuntimeFixture; using SmallBytesTest = RuntimeFixture; using CodeTest = RuntimeFixture; using ComplexTest = RuntimeFixture; using DequeTest = RuntimeFixture; using DoubleTest = RuntimeFixture; using IntTest = RuntimeFixture; using LargeStrTest = RuntimeFixture; using ListTest = RuntimeFixture; using MmapTest = RuntimeFixture; using ModulesTest = RuntimeFixture; using MutableBytesTest = RuntimeFixture; using MutableTupleTest = RuntimeFixture; using SliceTest = RuntimeFixture; using SmallStrTest = RuntimeFixture; using StrArrayTest = RuntimeFixture; using StrTest = RuntimeFixture; using StringTest = RuntimeFixture; using ValueCellTest = RuntimeFixture; using WeakRefTest = RuntimeFixture; TEST_F(BytearrayTest, DownsizeMaintainsCapacity) { HandleScope scope(thread_); Bytearray array(&scope, runtime_->newBytearray()); const byte byte_array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; runtime_->bytearrayExtend(thread_, array, byte_array); ASSERT_EQ(array.numItems(), 9); word capacity = array.capacity(); array.downsize(5); EXPECT_EQ(array.numItems(), 5); EXPECT_EQ(array.capacity(), capacity); } TEST_F(DequeTest, DequeClearRemovesElements) { HandleScope scope(thread_); Deque self(&scope, runtime_->newDeque()); MutableTuple underlying_tuple(&scope, runtime_->newMutableTuple(5)); underlying_tuple.atPut(0, SmallInt::fromWord(0)); underlying_tuple.atPut(1, SmallInt::fromWord(1)); underlying_tuple.atPut(2, SmallInt::fromWord(2)); self.setItems(*underlying_tuple); self.setNumItems(3); self.clear(); ASSERT_EQ(self.numItems(), 0); ASSERT_EQ(self.left(), 0); EXPECT_EQ(self.at(0), NoneType::object()); EXPECT_EQ(self.at(1), NoneType::object()); EXPECT_EQ(self.at(2), NoneType::object()); } TEST_F(SmallBytesTest, CopyToStartAtCopiesToDestinationStartingAtIndex) { byte src_bytes[] = "hello"; HandleScope scope(thread_); Bytes src(&scope, runtime_->newBytesWithAll(src_bytes)); byte result[5] = {0}; src.copyToStartAt(result, 4, 1); EXPECT_STREQ(reinterpret_cast<char*>(result), "ello"); } TEST_F(SmallBytesTest, FindByteWithZeroLengthReturnsNegativeOne) { byte src_bytes[] = "hello"; HandleScope scope(thread_); SmallBytes bytes(&scope, runtime_->newBytesWithAll(src_bytes)); EXPECT_EQ(bytes.findByte('h', 0, 0), -1); } TEST_F(SmallBytesTest, FindByteWithEndBeforeByteReturnsNegativeOne) { byte src_bytes[] = "hello"; HandleScope scope(thread_); SmallBytes bytes(&scope, runtime_->newBytesWithAll(src_bytes)); EXPECT_EQ(bytes.findByte('o', 1, 2), -1); } TEST_F(SmallBytesTest, FindByteWithByteNotInBytesReturnsNegativeOne) { byte src_bytes[] = "hello"; HandleScope scope(thread_); SmallBytes bytes(&scope, runtime_->newBytesWithAll(src_bytes)); EXPECT_EQ(bytes.findByte('x', 0, bytes.length()), -1); } TEST_F(SmallBytesTest, FindByteWithByteInBytesReturnsIndex) { byte src_bytes[] = "hello"; HandleScope scope(thread_); SmallBytes bytes(&scope, runtime_->newBytesWithAll(src_bytes)); EXPECT_EQ(bytes.findByte('o', 0, bytes.length()), 4); } TEST_F(LargeBytesTest, FindByteWithZerolengthReturnsNegativeOne) { byte src_bytes[] = "hello world this is patrick"; HandleScope scope(thread_); LargeBytes bytes(&scope, runtime_->newBytesWithAll(src_bytes)); EXPECT_EQ(bytes.findByte('h', 0, 0), -1); } TEST_F(LargeBytesTest, FindByteWithEndBeforeByteReturnsNegativeOne) { byte src_bytes[] = "hello world"; HandleScope scope(thread_); LargeBytes bytes(&scope, runtime_->newBytesWithAll(src_bytes)); EXPECT_EQ(bytes.findByte('d', 3, 5), -1); } TEST_F(LargeBytesTest, FindByteWithByteNotInBytesReturnsNegativeOne) { byte src_bytes[] = "hello world"; HandleScope scope(thread_); LargeBytes bytes(&scope, runtime_->newBytesWithAll(src_bytes)); EXPECT_EQ(bytes.findByte('x', 0, bytes.length()), -1); } TEST_F(LargeBytesTest, FindByteWithByteInBytesReturnsIndex) { byte src_bytes[] = "hello world"; HandleScope scope(thread_); LargeBytes bytes(&scope, runtime_->newBytesWithAll(src_bytes)); EXPECT_EQ(bytes.findByte('o', 0, bytes.length()), 4); } TEST_F(LargeBytesTest, FindByteNonZeroStartReturnsIndex) { byte src_bytes[] = "hello world"; HandleScope scope(thread_); LargeBytes bytes(&scope, runtime_->newBytesWithAll(src_bytes)); EXPECT_EQ(bytes.findByte('o', 5, bytes.length() - 5), 7); } TEST_F(LargeBytesTest, CopyToStartAtCopiesToDestinationStartingAtIndex) { byte src_bytes[] = "hello world this is patrick"; HandleScope scope(thread_); LargeBytes src(&scope, runtime_->newBytesWithAll(src_bytes)); byte result[8] = {0}; src.copyToStartAt(result, 7, 20); EXPECT_STREQ(reinterpret_cast<char*>(result), "patrick"); } TEST_F(MutableBytesTest, IndexOfAnyWithDifferentStartReturnsFirstMatchIndexAfterStart) { HandleScope scope(thread_); const byte src_bytes[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}; word src_length = ARRAYSIZE(src_bytes); MutableBytes src(&scope, runtime_->newMutableBytesUninitialized(src_length)); for (word i = 0; i < src_length; i++) { src.byteAtPut(i, src_bytes[i]); } const byte needle[] = "eor"; EXPECT_EQ(src.indexOfAny(needle, 0), 1); EXPECT_EQ(src.indexOfAny(needle, 2), 4); EXPECT_EQ(src.indexOfAny(needle, 5), 7); } TEST_F(MutableBytesTest, IndexOfAnyWithNeedleNoMatchReturnsHaystackLength) { HandleScope scope(thread_); const byte src_bytes[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}; word src_length = ARRAYSIZE(src_bytes); MutableBytes src(&scope, runtime_->newMutableBytesUninitialized(src_length)); for (word i = 0; i < src_length; i++) { src.byteAtPut(i, src_bytes[i]); } const byte needle[] = "abc"; EXPECT_EQ(src.indexOfAny(needle, 0), src_length); EXPECT_EQ(src.indexOfAny(needle, 5), src_length); EXPECT_EQ(src.indexOfAny(needle, 9), src_length); } TEST_F(MutableBytesTest, ReplaceFromWithByteslikeReplacesBytes) { HandleScope scope(thread_); const byte bytes[] = "hello world"; Object value(&scope, runtime_->newBytesWithAll(bytes)); Byteslike byteslike(&scope, thread_, *value); ASSERT_TRUE(byteslike.isValid()); MutableBytes mutable_bytes(&scope, runtime_->newMutableBytesUninitialized(11)); mutable_bytes.replaceFromWithByteslike(0, byteslike, 5); mutable_bytes.replaceFromWithByteslike(5, byteslike, 2); mutable_bytes.replaceFromWithByteslike(11, byteslike, 0); mutable_bytes.replaceFromWithByteslike(7, byteslike, 3); mutable_bytes.byteAtPut(10, '\0'); const byte expected[] = "hellohehel"; EXPECT_TRUE(isMutableBytesEqualsBytes(mutable_bytes, expected)); } TEST_F(MutableBytesTest, ReplaceFromWithByteslikeStartAtReplacesBytes) { HandleScope scope(thread_); const byte bytes[] = "hello world"; Object value(&scope, runtime_->newBytesWithAll(bytes)); Byteslike byteslike(&scope, thread_, *value); ASSERT_TRUE(byteslike.isValid()); MutableBytes mutable_bytes(&scope, runtime_->newMutableBytesUninitialized(10)); mutable_bytes.replaceFromWithByteslikeStartAt(0, byteslike, 5, 0); mutable_bytes.replaceFromWithByteslikeStartAt(2, byteslike, 4, 1); mutable_bytes.replaceFromWithByteslikeStartAt(6, byteslike, 4, 8); mutable_bytes.replaceFromWithByteslikeStartAt(7, byteslike, 0, 11); mutable_bytes.replaceFromWithByteslikeStartAt(10, byteslike, 0, 0); const byte expected[] = "heellorld"; EXPECT_TRUE(isMutableBytesEqualsBytes(mutable_bytes, expected)); } TEST_F(MutableBytesTest, ReplaceFromWithStartAtSelfNoop) { HandleScope scope(thread_); const byte src_bytes[] = "patrick"; word src_length = ARRAYSIZE(src_bytes); MutableBytes src(&scope, runtime_->newMutableBytesUninitialized(src_length)); for (word i = 0; i < src_length; i++) { src.byteAtPut(i, src_bytes[i]); } ASSERT_TRUE(isMutableBytesEqualsBytes(src, src_bytes)); src.replaceFromWithStartAt(0, *src, 3, 0); EXPECT_TRUE(isMutableBytesEqualsBytes(src, src_bytes)); } TEST_F(MutableBytesTest, ReplaceFromWithStartAtSelfBackward) { HandleScope scope(thread_); const byte src_bytes[] = "patrick"; word src_length = ARRAYSIZE(src_bytes); MutableBytes src(&scope, runtime_->newMutableBytesUninitialized(src_length)); for (word i = 0; i < src_length; i++) { src.byteAtPut(i, src_bytes[i]); } ASSERT_TRUE(isMutableBytesEqualsBytes(src, src_bytes)); src.replaceFromWithStartAt(0, *src, 3, 4); const byte expected[] = "ickrick"; EXPECT_TRUE(isMutableBytesEqualsBytes(src, expected)); } TEST_F(MutableBytesTest, ReplaceFromWithStartAtSelfForward) { HandleScope scope(thread_); const byte src_bytes[] = "patrick"; word src_length = ARRAYSIZE(src_bytes); MutableBytes src(&scope, runtime_->newMutableBytesUninitialized(src_length)); for (word i = 0; i < src_length; i++) { src.byteAtPut(i, src_bytes[i]); } ASSERT_TRUE(isMutableBytesEqualsBytes(src, src_bytes)); src.replaceFromWithStartAt(4, *src, 3, 0); const byte expected[] = "patrpat"; EXPECT_TRUE(isMutableBytesEqualsBytes(src, expected)); } TEST_F(MutableBytesTest, ReplaceFromWithStartAtReplacesStartingAtSrcIndex) { byte src_bytes[] = "hello world this is patrick"; HandleScope scope(thread_); LargeBytes src(&scope, runtime_->newBytesWithAll(src_bytes)); MutableBytes dst(&scope, runtime_->newMutableBytesUninitialized(8)); dst.replaceFromWithStartAt(0, *src, 7, 20); const byte expected[] = "patrick"; EXPECT_TRUE(isMutableBytesEqualsBytes(dst, expected)); } TEST_F(CodeTest, OffsetToLineNumReturnsLineNumber) { const char* src = R"( def func(): a = 1 b = 2 print(a, b) )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); HandleScope scope(thread_); // The bytecode for func is roughly: // LOAD_CONST # a = 1 // STORE_FAST // // LOAD_CONST # b = 2 // STORE_FAST // // LOAD_GLOBAL # print(a, b) // LOAD_FAST // LOAD_FAST // CALL_FUNCTION Function func(&scope, mainModuleAt(runtime_, "func")); Code code(&scope, func.code()); ASSERT_EQ(code.firstlineno(), 2); // a = 1 EXPECT_EQ(code.offsetToLineNum(0), 3); EXPECT_EQ(code.offsetToLineNum(2 * kCodeUnitScale), 3); // b = 2 EXPECT_EQ(code.offsetToLineNum(4 * kCodeUnitScale), 4); EXPECT_EQ(code.offsetToLineNum(6 * kCodeUnitScale), 4); // print(a, b) for (word i = 8; i < Bytes::cast(code.code()).length(); i++) { EXPECT_EQ(code.offsetToLineNum(i * kCodeUnitScale), 5); } } TEST_F(DoubleTest, DoubleTest) { RawObject o = runtime_->newFloat(3.14); ASSERT_TRUE(o.isFloat()); RawFloat d = Float::cast(o); EXPECT_EQ(d.value(), 3.14); } TEST_F(ComplexTest, ComplexTest) { RawObject o = runtime_->newComplex(1.0, 2.0); ASSERT_TRUE(o.isComplex()); RawComplex c = Complex::cast(o); EXPECT_EQ(c.real(), 1.0); EXPECT_EQ(c.imag(), 2.0); } TEST_F(IntTest, IntTest) { HandleScope scope(thread_); Object o1(&scope, runtime_->newInt(42)); EXPECT_TRUE(isIntEqualsWord(*o1, 42)); Object o2(&scope, runtime_->newInt(9223372036854775807L)); EXPECT_TRUE(isIntEqualsWord(*o2, 9223372036854775807L)); int stack_val = 123; Int o3(&scope, runtime_->newIntFromCPtr(&stack_val)); EXPECT_EQ(*static_cast<int*>(o3.asCPtr()), 123); Object o4(&scope, runtime_->newInt(kMinWord)); EXPECT_TRUE(isIntEqualsWord(*o4, kMinWord)); uword digits[] = {kMaxUword, 0}; Int o5(&scope, runtime_->newLargeIntWithDigits(digits)); EXPECT_TRUE(o5.isLargeInt()); EXPECT_EQ(o5.bitLength(), kBitsPerWord); uword digits2[] = {kMaxUword, 1}; Int o6(&scope, runtime_->newLargeIntWithDigits(digits2)); EXPECT_TRUE(o6.isLargeInt()); EXPECT_EQ(o6.bitLength(), kBitsPerWord + 1); } TEST_F(IntTest, LargeIntValid) { HandleScope scope(thread_); uword digits[] = {static_cast<uword>(-1234), static_cast<uword>(-1)}; LargeInt i(&scope, newLargeIntWithDigits(digits)); // Redundant sign-extension EXPECT_FALSE(i.isValid()); i.digitAtPut(1, -2); EXPECT_TRUE(i.isValid()); i.digitAtPut(0, 1234); i.digitAtPut(1, 0); // Redundant zero-extension EXPECT_FALSE(i.isValid()); i.digitAtPut(1, 1); EXPECT_TRUE(i.isValid()); } TEST_F(IntTest, IsPositive) { HandleScope scope(thread_); Int zero(&scope, runtime_->newInt(0)); EXPECT_FALSE(zero.isPositive()); Int one(&scope, runtime_->newInt(1)); EXPECT_TRUE(one.isPositive()); Int neg_one(&scope, runtime_->newInt(-1)); EXPECT_FALSE(neg_one.isPositive()); Int max_small_int(&scope, runtime_->newInt(RawSmallInt::kMaxValue)); EXPECT_TRUE(max_small_int.isPositive()); Int min_small_int(&scope, runtime_->newInt(RawSmallInt::kMinValue)); EXPECT_FALSE(min_small_int.isPositive()); Int max_word(&scope, runtime_->newInt(kMaxWord)); EXPECT_TRUE(max_word.isPositive()); Int min_word(&scope, runtime_->newInt(kMinWord)); EXPECT_FALSE(min_word.isPositive()); } TEST_F(IntTest, IsNegative) { HandleScope scope(thread_); Int zero(&scope, runtime_->newInt(0)); EXPECT_FALSE(zero.isNegative()); Int one(&scope, runtime_->newInt(1)); EXPECT_FALSE(one.isNegative()); Int neg_one(&scope, runtime_->newInt(-1)); EXPECT_TRUE(neg_one.isNegative()); Int max_small_int(&scope, runtime_->newInt(RawSmallInt::kMaxValue)); EXPECT_FALSE(max_small_int.isNegative()); Int min_small_int(&scope, runtime_->newInt(RawSmallInt::kMinValue)); EXPECT_TRUE(min_small_int.isNegative()); Int max_word(&scope, runtime_->newInt(kMaxWord)); EXPECT_FALSE(max_word.isNegative()); Int min_word(&scope, runtime_->newInt(kMinWord)); EXPECT_TRUE(min_word.isNegative()); } TEST_F(IntTest, IsZero) { HandleScope scope(thread_); Int zero(&scope, runtime_->newInt(0)); EXPECT_TRUE(zero.isZero()); Int one(&scope, runtime_->newInt(1)); EXPECT_FALSE(one.isZero()); Int neg_one(&scope, runtime_->newInt(-1)); EXPECT_FALSE(neg_one.isZero()); Int max_small_int(&scope, runtime_->newInt(RawSmallInt::kMaxValue)); EXPECT_FALSE(max_small_int.isZero()); Int min_small_int(&scope, runtime_->newInt(RawSmallInt::kMinValue)); EXPECT_FALSE(min_small_int.isZero()); Int max_word(&scope, runtime_->newInt(kMaxWord)); EXPECT_FALSE(max_word.isZero()); Int min_word(&scope, runtime_->newInt(kMinWord)); EXPECT_FALSE(min_word.isZero()); } TEST_F(IntTest, Compare) { HandleScope scope(thread_); Int zero(&scope, runtime_->newInt(0)); Int one(&scope, runtime_->newInt(1)); Int neg_one(&scope, runtime_->newInt(-1)); EXPECT_EQ(zero.compare(*zero), 0); EXPECT_GE(one.compare(*neg_one), 1); EXPECT_LE(neg_one.compare(*one), -1); Int min_small_int(&scope, runtime_->newInt(RawSmallInt::kMinValue)); Int max_small_int(&scope, runtime_->newInt(RawSmallInt::kMaxValue)); EXPECT_GE(max_small_int.compare(*min_small_int), 1); EXPECT_LE(min_small_int.compare(*max_small_int), -1); EXPECT_EQ(min_small_int.compare(*min_small_int), 0); EXPECT_EQ(max_small_int.compare(*max_small_int), 0); Int min_word(&scope, runtime_->newInt(kMinWord)); Int max_word(&scope, runtime_->newInt(kMaxWord)); EXPECT_GE(max_word.compare(*min_word), 1); EXPECT_LE(min_word.compare(*max_word), -1); EXPECT_EQ(min_word.compare(*min_word), 0); EXPECT_EQ(max_word.compare(*max_word), 0); EXPECT_GE(max_word.compare(*max_small_int), 1); EXPECT_LE(min_word.compare(*min_small_int), -1); } TEST_F(IntTest, LargeIntCompare) { HandleScope scope(thread_); const uword digits_great[] = {1, 1}; Int great(&scope, runtime_->newLargeIntWithDigits(digits_great)); const uword digits_small[] = {0, 0, kMaxUword}; Int small(&scope, runtime_->newLargeIntWithDigits(digits_small)); EXPECT_EQ(great.compare(*small), 1); EXPECT_EQ(small.compare(*great), -1); const uword digits_great2[] = {1, 1, 1}; const uword digits_small2[] = {1, 1}; great = runtime_->newLargeIntWithDigits(digits_great2); small = runtime_->newLargeIntWithDigits(digits_small2); EXPECT_EQ(great.compare(*small), 1); EXPECT_EQ(small.compare(*great), -1); const uword digits_great3[] = {kMaxUword - 1, 1}; const uword digits_small3[] = {2, 1}; great = runtime_->newLargeIntWithDigits(digits_great3); small = runtime_->newLargeIntWithDigits(digits_small3); EXPECT_EQ(great.compare(*small), 1); EXPECT_EQ(small.compare(*great), -1); const uword digits_great4[] = {kMaxUword - 1, kMaxUword - 1}; const uword digits_small4[] = {2, kMaxUword - 1}; great = runtime_->newLargeIntWithDigits(digits_great4); small = runtime_->newLargeIntWithDigits(digits_small4); EXPECT_EQ(great.compare(*small), 1); EXPECT_EQ(small.compare(*great), -1); } #define EXPECT_VALID(expr, expected_value) \ { \ auto const result = (expr); \ EXPECT_EQ(result.error, CastError::None); \ EXPECT_EQ(result.value, expected_value); \ } TEST_F(IntTest, AsIntWithZeroReturnsZero) { HandleScope scope(thread_); Int zero(&scope, runtime_->newInt(0)); EXPECT_VALID(zero.asInt<int>(), 0); EXPECT_VALID(zero.asInt<unsigned>(), 0U); EXPECT_VALID(zero.asInt<unsigned long>(), 0UL); EXPECT_VALID(zero.asInt<unsigned long long>(), 0ULL); } TEST_F(IntTest, AsIntReturnsInt) { HandleScope scope(thread_); Int num(&scope, runtime_->newInt(1234)); EXPECT_VALID(num.asInt<int>(), 1234); EXPECT_VALID(num.asInt<long>(), 1234); EXPECT_VALID(num.asInt<unsigned>(), 1234U); EXPECT_VALID(num.asInt<unsigned long>(), 1234UL); } TEST_F(IntTest, AsIntReturnsOverflow) { HandleScope scope(thread_); Int num(&scope, runtime_->newInt(1234)); EXPECT_EQ(num.asInt<byte>().error, CastError::Overflow); EXPECT_EQ(num.asInt<int8_t>().error, CastError::Overflow); Int word_max(&scope, runtime_->newInt(kMaxWord)); EXPECT_EQ(word_max.asInt<int32_t>().error, CastError::Overflow); Int word_min(&scope, runtime_->newInt(kMinWord)); EXPECT_EQ(word_min.asInt<int32_t>().error, CastError::Overflow); } TEST_F(IntTest, AsIntWithNegativeIntReturnsInt) { HandleScope scope(thread_); Int neg_num(&scope, runtime_->newInt(-4567)); EXPECT_VALID(neg_num.asInt<int16_t>(), -4567); Int neg_one(&scope, runtime_->newInt(-1)); EXPECT_VALID(neg_one.asInt<int>(), -1); } TEST_F(IntTest, AsIntReturnsUnderflow) { HandleScope scope(thread_); Int neg_num(&scope, runtime_->newInt(-4567)); EXPECT_EQ(neg_num.asInt<unsigned>().error, CastError::Underflow); EXPECT_EQ(neg_num.asInt<int8_t>().error, CastError::Underflow); Int neg_one(&scope, runtime_->newInt(-1)); EXPECT_EQ(neg_one.asInt<unsigned>().error, CastError::Underflow); Int word_min(&scope, runtime_->newInt(kMinWord)); EXPECT_EQ(word_min.asInt<uword>().error, CastError::Underflow); } TEST_F(IntTest, AsIntWithMaxInt32ReturnsInt) { HandleScope scope(thread_); Int int32_max(&scope, runtime_->newInt(kMaxInt32)); EXPECT_VALID(int32_max.asInt<int32_t>(), kMaxInt32); EXPECT_EQ(int32_max.asInt<int16_t>().error, CastError::Overflow); } TEST_F(IntTest, AsIntWithMaxUwordReturnsInt) { HandleScope scope(thread_); Int uword_max(&scope, runtime_->newIntFromUnsigned(kMaxUword)); EXPECT_VALID(uword_max.asInt<uword>(), kMaxUword); EXPECT_EQ(uword_max.asInt<word>().error, CastError::Overflow); } TEST_F(IntTest, AsIntWithMaxWordReturnsInt) { HandleScope scope(thread_); Int word_max(&scope, runtime_->newInt(kMaxWord)); EXPECT_VALID(word_max.asInt<word>(), kMaxWord); EXPECT_VALID(word_max.asInt<uword>(), uword{kMaxWord}); } TEST_F(IntTest, AsIntWithMinWordReturnsInt) { HandleScope scope(thread_); Int word_min(&scope, runtime_->newInt(kMinWord)); EXPECT_VALID(word_min.asInt<word>(), kMinWord); } TEST_F(IntTest, AsIntWithNegativeLargeIntReturnsUnderflow) { HandleScope scope(thread_); uword digits[] = {0, kMaxUword}; Int negative(&scope, runtime_->newLargeIntWithDigits(digits)); EXPECT_EQ(negative.asInt<word>().error, CastError::Underflow); EXPECT_EQ(negative.asInt<uword>().error, CastError::Underflow); } TEST_F(IntTest, AsIntWithTrueReturnsOne) { HandleScope scope(thread_); Int value(&scope, Bool::trueObj()); EXPECT_VALID(value.asInt<word>(), 1); EXPECT_VALID(value.asInt<uint8_t>(), 1); } TEST_F(IntTest, AsIntWithFalseReturnsZero) { HandleScope scope(thread_); Int value(&scope, Bool::falseObj()); EXPECT_VALID(value.asInt<uword>(), uword{0}); EXPECT_VALID(value.asInt<int32_t>(), 0); } #undef EXPECT_VALID TEST_F(IntTest, SmallIntFromWordTruncatedWithSmallNegativeNumberReturnsSelf) { EXPECT_EQ(SmallInt::fromWord(-1), SmallInt::fromWordTruncated(-1)); } TEST_F(MmapTest, AccessSettersSetValues) { HandleScope scope(thread_); Object obj(&scope, runtime_->newMmap()); ASSERT_TRUE(obj.isMmap()); Mmap mmap_obj(&scope, *obj); mmap_obj.setReadable(); mmap_obj.setWritable(); mmap_obj.setCopyOnWrite(); EXPECT_EQ(mmap_obj.isReadable(), true); EXPECT_EQ(mmap_obj.isWritable(), true); EXPECT_EQ(mmap_obj.isCopyOnWrite(), true); } TEST_F(ModulesTest, TestCreate) { HandleScope scope(thread_); Object name(&scope, runtime_->newStrFromCStr("mymodule")); Module module(&scope, runtime_->newModule(name)); EXPECT_EQ(module.name(), *name); } TEST_F(MutableBytesTest, BecomeStrTurnsObjectIntoSmallStr) { HandleScope scope(thread_); Object test_0(&scope, runtime_->emptyMutableBytes()); ASSERT_TRUE(test_0.isMutableBytes()); Object as_str_0(&scope, MutableBytes::cast(*test_0).becomeStr()); EXPECT_TRUE(test_0.isMutableBytes()); EXPECT_TRUE(as_str_0.isSmallStr()); EXPECT_TRUE(isStrEqualsCStr(*as_str_0, "")); Str str(&scope, runtime_->newStrFromCStr("abcdefghi")); Object test_1(&scope, runtime_->newMutableBytesUninitialized(1)); ASSERT_TRUE(test_1.isMutableBytes()); MutableBytes::cast(*test_1).replaceFromWithStr(0, *str, 1); Object as_str_1(&scope, MutableBytes::cast(*test_1).becomeStr()); EXPECT_TRUE(test_1.isMutableBytes()); EXPECT_TRUE(as_str_1.isSmallStr()); EXPECT_TRUE(isStrEqualsCStr(*as_str_1, "a")); Object test_m(&scope, runtime_->newMutableBytesUninitialized(SmallStr::kMaxLength)); ASSERT_TRUE(test_m.isMutableBytes()); MutableBytes::cast(*test_m).replaceFromWithStr(0, *str, SmallStr::kMaxLength); Object as_str_m(&scope, MutableBytes::cast(*test_m).becomeStr()); EXPECT_TRUE(test_m.isMutableBytes()); EXPECT_TRUE(as_str_m.isSmallStr()); EXPECT_TRUE(isStrEqualsCStr(*as_str_m, "abcdefg")); } TEST_F(MutableBytesTest, BecomeStrTurnsObjectIntoLargeStr) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("hello world!")); Object test(&scope, runtime_->newMutableBytesUninitialized(str.length())); ASSERT_TRUE(test.isMutableBytes()); MutableBytes::cast(*test).replaceFromWithStr(0, *str, str.length()); MutableBytes::cast(*test).becomeStr(); EXPECT_TRUE(test.isLargeStr()); EXPECT_TRUE(isStrEqualsCStr(*test, "hello world!")); } TEST_F(SliceTest, AdjustIndices) { // Test: 0:10:1 on len: 10 word length = 10; word start = 0; word stop = 10; word step = 1; word new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 10); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 10); // Test: 2:10:1 on len: 10 start = 2; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 8); ASSERT_EQ(start, 2); ASSERT_EQ(stop, 10); // Test: -4:10:1 on len: 10 start = -4; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 4); ASSERT_EQ(start, 6); ASSERT_EQ(stop, 10); // Test: 0:2:1 on len: 10 start = 0; stop = 2; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 2); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 2); // Test: 0:-2:1 on len: 10 start = 0; stop = -2; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 8); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 8); // Test: 0:10:2 on len: 10 start = 0; stop = 10; step = 2; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 5); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 10); // Test: 0:10:-2 on len: 10 start = 0; stop = 10; step = -2; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 0); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 9); } TEST_F(SliceTest, AdjustIndicesOutOfBounds) { // Test: 10:5:1 on len: 5 word length = 5; word start = 10; word stop = 5; word step = 1; word new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 0); ASSERT_EQ(start, 5); ASSERT_EQ(stop, 5); // Test: -10:5:1 on len: 5 start = -10; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 5); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 5); // Test: 0:10:1 on len: 5 start = 0; stop = 10; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 5); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 5); // Test: 0:-10:1 on len: 5 stop = -10; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 0); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 0); // Test: 0:5:10 on len: 5 stop = 5; step = 10; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 1); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 5); // Test: 0:5:-10 on len: 5 step = -10; new_length = Slice::adjustIndices(length, &start, &stop, step); ASSERT_EQ(new_length, 0); ASSERT_EQ(start, 0); ASSERT_EQ(stop, 4); } TEST_F(SliceTest, LengthWithNegativeStepAndStopLessThanStartReturnsLength) { EXPECT_EQ(Slice::length(5, 2, -1), 3); } TEST_F(SliceTest, LengthWithNegativeStepAndStartLessThanStopReturnsZero) { EXPECT_EQ(Slice::length(2, 5, -1), 0); } TEST_F(SliceTest, LengthWithNegativeStepAndStartEqualsStopReturnsZero) { EXPECT_EQ(Slice::length(2, 2, -1), 0); } TEST_F(SliceTest, LengthWithPositiveStepAndStartLessThanStopReturnsLength) { EXPECT_EQ(Slice::length(2, 5, 1), 3); } TEST_F(SliceTest, LengthWithPositiveStepAndStopLessThanStartReturnsZero) { EXPECT_EQ(Slice::length(5, 2, 1), 0); } TEST_F(SliceTest, LengthWithPositiveStepAndStartEqualsStopReturnsZero) { EXPECT_EQ(Slice::length(2, 2, 1), 0); } TEST_F(StrArrayTest, OffsetByCodePoints) { HandleScope scope(thread_); StrArray empty(&scope, runtime_->newStrArray()); EXPECT_EQ(empty.numItems(), 0); EXPECT_EQ(empty.offsetByCodePoints(0, 1), 0); EXPECT_EQ(empty.offsetByCodePoints(2, 0), 0); EXPECT_EQ(empty.offsetByCodePoints(2, 1), 0); StrArray ascii(&scope, runtime_->newStrArray()); Str ascii_str(&scope, runtime_->newStrFromCStr("abcd")); runtime_->strArrayAddStr(thread_, ascii, ascii_str); EXPECT_EQ(ascii.numItems(), 4); // for ASCII, each code point is one byte wide EXPECT_EQ(ascii.offsetByCodePoints(0, 0), 0); EXPECT_EQ(ascii.offsetByCodePoints(0, 3), 3); EXPECT_EQ(ascii.offsetByCodePoints(1, 0), 1); EXPECT_EQ(ascii.offsetByCodePoints(2, 0), 2); EXPECT_EQ(ascii.offsetByCodePoints(2, 1), 3); EXPECT_EQ(ascii.offsetByCodePoints(3, 0), 3); // return the length once we reach the end of the string EXPECT_EQ(ascii.offsetByCodePoints(0, 4), 4); EXPECT_EQ(ascii.offsetByCodePoints(0, 5), 4); EXPECT_EQ(ascii.offsetByCodePoints(1, 3), 4); EXPECT_EQ(ascii.offsetByCodePoints(1, 4), 4); EXPECT_EQ(ascii.offsetByCodePoints(2, 2), 4); EXPECT_EQ(ascii.offsetByCodePoints(2, 3), 4); EXPECT_EQ(ascii.offsetByCodePoints(3, 1), 4); EXPECT_EQ(ascii.offsetByCodePoints(3, 2), 4); EXPECT_EQ(ascii.offsetByCodePoints(4, 0), 4); EXPECT_EQ(ascii.offsetByCodePoints(6, 0), 4); StrArray unicode(&scope, runtime_->newStrArray()); Str unicode_str( &scope, runtime_->newStrFromCStr("\xd7\x90pq\xd7\x91\xd7\x92-\xd7\x93")); runtime_->strArrayAddStr(thread_, unicode, unicode_str); EXPECT_EQ(unicode.numItems(), 11); // for Unicode, code points may be more than one byte wide EXPECT_EQ(unicode.offsetByCodePoints(0, 0), 0); EXPECT_EQ(unicode.offsetByCodePoints(0, 1), 2); EXPECT_EQ(unicode.offsetByCodePoints(0, 2), 3); EXPECT_EQ(unicode.offsetByCodePoints(0, 3), 4); EXPECT_EQ(unicode.offsetByCodePoints(0, 4), 6); EXPECT_EQ(unicode.offsetByCodePoints(0, 5), 8); EXPECT_EQ(unicode.offsetByCodePoints(0, 6), 9); EXPECT_EQ(unicode.offsetByCodePoints(2, 0), 2); EXPECT_EQ(unicode.offsetByCodePoints(2, 1), 3); EXPECT_EQ(unicode.offsetByCodePoints(2, 2), 4); EXPECT_EQ(unicode.offsetByCodePoints(2, 3), 6); EXPECT_EQ(unicode.offsetByCodePoints(2, 4), 8); EXPECT_EQ(unicode.offsetByCodePoints(2, 5), 9); EXPECT_EQ(unicode.offsetByCodePoints(2, 6), 11); EXPECT_EQ(unicode.offsetByCodePoints(4, 0), 4); EXPECT_EQ(unicode.offsetByCodePoints(4, 1), 6); EXPECT_EQ(unicode.offsetByCodePoints(6, 0), 6); // return the length once we reach the end of the string EXPECT_EQ(unicode.offsetByCodePoints(0, 7), 11); EXPECT_EQ(unicode.offsetByCodePoints(0, 9), 11); EXPECT_EQ(unicode.offsetByCodePoints(2, 7), 11); EXPECT_EQ(unicode.offsetByCodePoints(3, 6), 11); EXPECT_EQ(unicode.offsetByCodePoints(4, 5), 11); EXPECT_EQ(unicode.offsetByCodePoints(8, 3), 11); EXPECT_EQ(unicode.offsetByCodePoints(12, 0), 11); } TEST_F(StrArrayTest, RotateCodePointWithSameFirstAndLastIsNoop) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); runtime_->strArrayAddASCII(thread_, array, 'H'); runtime_->strArrayAddASCII(thread_, array, 'i'); runtime_->strArrayAddASCII(thread_, array, '!'); EXPECT_TRUE(isStrEqualsCStr(runtime_->strFromStrArray(array), "Hi!")); array.rotateCodePoint(1, 1); EXPECT_TRUE(isStrEqualsCStr(runtime_->strFromStrArray(array), "Hi!")); } TEST_F(StrArrayTest, RotateCodePointWithFirstBeforeLastRotatesCodePoint) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); runtime_->strArrayAddASCII(thread_, array, 'a'); runtime_->strArrayAddASCII(thread_, array, 'b'); runtime_->strArrayAddASCII(thread_, array, 'c'); EXPECT_TRUE(isStrEqualsCStr(runtime_->strFromStrArray(array), "abc")); array.rotateCodePoint(0, 2); EXPECT_TRUE(isStrEqualsCStr(runtime_->strFromStrArray(array), "cab")); } TEST_F(StrArrayTest, RotateCodePointWithNonASCIIRotatesEntireCodePoint) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); Str str(&scope, runtime_->newStrFromCStr("Chopin \u00e9tude")); runtime_->strArrayAddStr(thread_, array, str); array.rotateCodePoint(1, 7); EXPECT_TRUE( isStrEqualsCStr(runtime_->strFromStrArray(array), "C\u00e9hopin tude")); } TEST_F(StrArrayTest, RotateCodePointWithNonASCIIUsesCharIndex) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); Str str(&scope, runtime_->newStrFromCStr("Chopin \u00e9tude")); runtime_->strArrayAddStr(thread_, array, str); array.rotateCodePoint(1, 10); EXPECT_TRUE( isStrEqualsCStr(runtime_->strFromStrArray(array), "Cuhopin \u00e9tde")); } TEST_F(LargeStrTest, CopyTo) { RawObject obj = runtime_->newStrFromCStr("hello world!"); ASSERT_TRUE(obj.isLargeStr()); RawStr str = Str::cast(obj); byte array[5]; memset(array, 'a', ARRAYSIZE(array)); str.copyTo(array, 0); EXPECT_EQ(array[0], 'a'); EXPECT_EQ(array[1], 'a'); EXPECT_EQ(array[2], 'a'); EXPECT_EQ(array[3], 'a'); EXPECT_EQ(array[4], 'a'); memset(array, 'b', ARRAYSIZE(array)); str.copyTo(array, 1); EXPECT_EQ(array[0], 'h'); EXPECT_EQ(array[1], 'b'); EXPECT_EQ(array[2], 'b'); EXPECT_EQ(array[3], 'b'); EXPECT_EQ(array[4], 'b'); memset(array, 'c', ARRAYSIZE(array)); str.copyTo(array, 5); EXPECT_EQ(array[0], 'h'); EXPECT_EQ(array[1], 'e'); EXPECT_EQ(array[2], 'l'); EXPECT_EQ(array[3], 'l'); EXPECT_EQ(array[4], 'o'); } TEST_F(StringTest, CompareSmallStrCStrASCII) { HandleScope scope(thread_); Str small_ascii(&scope, runtime_->newStrFromCStr("sm")); ASSERT_TRUE(small_ascii.isSmallStr()); // Equal EXPECT_EQ(small_ascii.compareCStr("sm"), 0); // Less EXPECT_EQ(small_ascii.compareCStr("sma"), -1); EXPECT_EQ(small_ascii.compareCStr("sn"), -1); // Greater EXPECT_EQ(small_ascii.compareCStr("s"), 1); EXPECT_EQ(small_ascii.compareCStr("sl"), 1); } TEST_F(StringTest, CompareSmallStrWithNulCStrASCII) { HandleScope scope(thread_); const byte data[] = {'s', '\0', 'm'}; Str small_ascii(&scope, runtime_->newStrWithAll(data)); ASSERT_TRUE(small_ascii.isSmallStr()); // Less EXPECT_EQ(small_ascii.compareCStr("t"), -1); // Greater EXPECT_EQ(small_ascii.compareCStr("s"), 1); EXPECT_EQ(small_ascii.compareCStr("a\0m"), 1); } TEST_F(StringTest, CompareLargeStrWithNulCStrASCII) { HandleScope scope(thread_); const byte data[] = {'l', 'a', 'r', 'g', 'e', '\0', 's', 't'}; Str large_ascii(&scope, runtime_->newStrWithAll(data)); ASSERT_TRUE(large_ascii.isLargeStr()); // Less EXPECT_EQ(large_ascii.compareCStr("largz"), -1); // Greater EXPECT_EQ(large_ascii.compareCStr("large"), 1); EXPECT_EQ(large_ascii.compareCStr("larga\0st"), 1); } TEST_F(StringTest, CompareLargeStrCStrASCII) { HandleScope scope(thread_); Str large_ascii(&scope, runtime_->newStrFromCStr("large string")); ASSERT_TRUE(large_ascii.isLargeStr()); // Equal EXPECT_EQ(large_ascii.compareCStr("large string"), 0); // Less EXPECT_EQ(large_ascii.compareCStr("large strings"), -1); EXPECT_EQ(large_ascii.compareCStr("large tbigger"), -1); // Greater EXPECT_EQ(large_ascii.compareCStr("large strin"), 1); EXPECT_EQ(large_ascii.compareCStr("large smaller"), 1); } TEST_F(StringTest, CompareSmallStrCStrUTF8) { HandleScope scope(thread_); Str small_utf8(&scope, runtime_->newStrFromCStr("\xC3\x87")); ASSERT_TRUE(small_utf8.isSmallStr()); // Equal EXPECT_EQ(small_utf8.compareCStr("\xC3\x87"), 0); // Less EXPECT_EQ(small_utf8.compareCStr("\xC3\x87s"), -1); EXPECT_EQ(small_utf8.compareCStr("\xC3\x88"), -1); EXPECT_EQ(small_utf8.compareCStr("\xC3\xA7"), -1); // Greater EXPECT_EQ(small_utf8.compareCStr(""), 1); EXPECT_EQ(small_utf8.compareCStr("\xC3\x86"), 1); EXPECT_EQ(small_utf8.compareCStr("\xC3\x67"), 1); } TEST_F(StringTest, CompareLargeStrCStrUTF8) { HandleScope scope(thread_); Str large_utf8(&scope, runtime_->newStrFromCStr("\xC3\x87 large")); ASSERT_TRUE(large_utf8.isLargeStr()); // Equal EXPECT_EQ(large_utf8.compareCStr("\xC3\x87 large"), 0); // Less EXPECT_EQ(large_utf8.compareCStr("\xC3\x87 larges"), -1); EXPECT_EQ(large_utf8.compareCStr("\xC3\x88 large"), -1); EXPECT_EQ(large_utf8.compareCStr("\xC3\xA7 large"), -1); // Greater EXPECT_EQ(large_utf8.compareCStr("\xC3\x87"), 1); EXPECT_EQ(large_utf8.compareCStr("\xC3\x86 large"), 1); EXPECT_EQ(large_utf8.compareCStr("g large"), 1); } TEST_F(StringTest, CompareSmallStrCStrLatin1) { HandleScope scope(thread_); Str small_latin1(&scope, runtime_->newStrFromCStr("\xDC")); ASSERT_TRUE(small_latin1.isSmallStr()); // Equal EXPECT_EQ(small_latin1.compareCStr("\xDC"), 0); // Less EXPECT_EQ(small_latin1.compareCStr("\xDCs"), -1); EXPECT_EQ(small_latin1.compareCStr("\xDD"), -1); EXPECT_EQ(small_latin1.compareCStr("\xEC"), -1); // Greater EXPECT_EQ(small_latin1.compareCStr(""), 1); EXPECT_EQ(small_latin1.compareCStr("\xDB"), 1); EXPECT_EQ(small_latin1.compareCStr("\xAC"), 1); } TEST_F(StringTest, CompareLargeStrCStrLatin1) { HandleScope scope(thread_); Str large_latin1(&scope, runtime_->newStrFromCStr("\xDClarge str")); ASSERT_TRUE(large_latin1.isLargeStr()); // Equal EXPECT_EQ(large_latin1.compareCStr("\xDClarge str"), 0); // Less EXPECT_EQ(large_latin1.compareCStr("\xDClarge strs"), -1); EXPECT_EQ(large_latin1.compareCStr("\xDDlarge str"), -1); EXPECT_EQ(large_latin1.compareCStr("\xEClarge str"), -1); // Greater EXPECT_EQ(large_latin1.compareCStr("\xDC"), 1); EXPECT_EQ(large_latin1.compareCStr("\xDBlarge str"), 1); EXPECT_EQ(large_latin1.compareCStr("\xBClarge str"), 1); } TEST_F(StringTest, CopyToStartAtWithLargeStrCopiesBytes) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("Hello world!")); byte actual0[5]; LargeStr::cast(*str).copyToStartAt(actual0, 5, 3); EXPECT_EQ(std::memcmp(actual0, "lo", 2), 0); byte actual1[3]; LargeStr::cast(*str).copyToStartAt(actual1, 3, 4); EXPECT_EQ(std::memcmp(actual1, "o w", 3), 0); // zero-sized copies should do nothing. str.copyToStartAt(nullptr, 0, 0); LargeStr::cast(*str).copyToStartAt(nullptr, 0, 12); } TEST_F(StringTest, CopyToStartAtWithSmallStrCopiesBytes) { HandleScope scope(thread_); Str str(&scope, SmallStr::fromCStr("bar")); byte actual0[3]; str.copyToStartAt(actual0, 3, 0); EXPECT_EQ(std::memcmp(actual0, "bar", 3), 0); byte actual1[2]; str.copyToStartAt(actual1, 2, 1); EXPECT_EQ(std::memcmp(actual1, "ar", 2), 0); // zero-sized copies should do nothing. str.copyToStartAt(nullptr, 0, 0); str.copyToStartAt(nullptr, 0, 3); } TEST_F(SmallStrTest, CodePointLengthWithAsciiReturnsLength) { HandleScope scope(thread_); SmallStr len0(&scope, SmallStr::fromCStr("")); EXPECT_EQ(len0.length(), 0); EXPECT_EQ(len0.codePointLength(), 0); SmallStr len1(&scope, SmallStr::fromCStr("1")); EXPECT_EQ(len1.length(), 1); EXPECT_EQ(len1.codePointLength(), 1); SmallStr len2(&scope, SmallStr::fromCStr("12")); EXPECT_EQ(len2.length(), 2); EXPECT_EQ(len2.codePointLength(), 2); SmallStr len3(&scope, SmallStr::fromCStr("123")); EXPECT_EQ(len3.length(), 3); EXPECT_EQ(len3.codePointLength(), 3); } TEST_F(SmallStrTest, CodePointLengthWithOneCodePoint) { HandleScope scope(thread_); SmallStr len1(&scope, SmallStr::fromCStr("\x24")); EXPECT_EQ(len1.length(), 1); EXPECT_EQ(len1.codePointLength(), 1); SmallStr len2(&scope, SmallStr::fromCStr("\xC2\xA2")); EXPECT_EQ(len2.length(), 2); EXPECT_EQ(len2.codePointLength(), 1); SmallStr len3(&scope, SmallStr::fromCStr("\xE0\xA4\xB9")); EXPECT_EQ(len3.length(), 3); EXPECT_EQ(len3.codePointLength(), 1); SmallStr len4(&scope, SmallStr::fromCStr("\xF0\x90\x8D\x88")); EXPECT_EQ(len4.length(), 4); EXPECT_EQ(len4.codePointLength(), 1); } TEST_F(SmallStrTest, CodePointLengthWithTwoCodePoints) { HandleScope scope(thread_); SmallStr len1(&scope, SmallStr::fromCStr("\x24\x65")); EXPECT_EQ(len1.length(), 2); EXPECT_EQ(len1.codePointLength(), 2); SmallStr len2(&scope, SmallStr::fromCStr("\xC2\xA2\xC2\xA3")); EXPECT_EQ(len2.length(), 4); EXPECT_EQ(len2.codePointLength(), 2); SmallStr len3(&scope, SmallStr::fromCStr("\xE0\xA4\xB9\xC2\xA3")); EXPECT_EQ(len3.length(), 5); EXPECT_EQ(len3.codePointLength(), 2); SmallStr len4(&scope, SmallStr::fromCStr("\xF0\x90\x8D\x88\xC2\xA3")); EXPECT_EQ(len4.length(), 6); EXPECT_EQ(len4.codePointLength(), 2); } TEST_F(SmallStrTest, CodePointLengthWithThreeCodePoints) { HandleScope scope(thread_); SmallStr len1(&scope, SmallStr::fromCStr("\x24\x65\x66")); EXPECT_EQ(len1.length(), 3); EXPECT_EQ(len1.codePointLength(), 3); SmallStr len2(&scope, SmallStr::fromCStr("\xC2\xA2\xC2\xA3\xC2\xA4")); EXPECT_EQ(len2.length(), 6); EXPECT_EQ(len2.codePointLength(), 3); SmallStr len3(&scope, SmallStr::fromCStr("\xE0\xA4\xB9\xC2\xA3\xC2\xA4")); EXPECT_EQ(len3.length(), 7); EXPECT_EQ(len3.codePointLength(), 3); SmallStr len4(&scope, SmallStr::fromCStr("\xF0\x90\x8D\x88\x65\xC2\xA3")); EXPECT_EQ(len4.length(), 7); EXPECT_EQ(len4.codePointLength(), 3); } TEST_F(SmallStrTest, CopyToCopiesBytes) { HandleScope scope(thread_); SmallStr str(&scope, SmallStr::fromCStr("AB")); EXPECT_EQ(str.length(), 2); EXPECT_EQ(str.byteAt(0), 'A'); EXPECT_EQ(str.byteAt(1), 'B'); byte array[3]{0, 0, 0}; str.copyTo(array, 2); EXPECT_EQ(array[0], 'A'); EXPECT_EQ(array[1], 'B'); EXPECT_EQ(array[2], 0); } TEST_F(SmallStrTest, FromCodePointOneByte) { HandleScope scope(thread_); SmallStr str(&scope, SmallStr::fromCodePoint(0x24)); ASSERT_EQ(str.length(), 1); EXPECT_EQ(str.byteAt(0), 0x24); } TEST_F(SmallStrTest, FromCodePointTwoByte) { HandleScope scope(thread_); SmallStr str(&scope, SmallStr::fromCodePoint(0xA2)); ASSERT_EQ(str.length(), 2); EXPECT_EQ(str.byteAt(0), 0xC2); EXPECT_EQ(str.byteAt(1), 0xA2); } TEST_F(SmallStrTest, FromCodePointThreeByte) { HandleScope scope(thread_); SmallStr str1(&scope, SmallStr::fromCodePoint(0x0939)); ASSERT_EQ(str1.length(), 3); EXPECT_EQ(str1.byteAt(0), 0xE0); EXPECT_EQ(str1.byteAt(1), 0xA4); EXPECT_EQ(str1.byteAt(2), 0xB9); SmallStr str2(&scope, SmallStr::fromCodePoint(0x20AC)); ASSERT_EQ(str2.length(), 3); EXPECT_EQ(str2.byteAt(0), 0xE2); EXPECT_EQ(str2.byteAt(1), 0x82); EXPECT_EQ(str2.byteAt(2), 0xAC); } TEST_F(SmallStrTest, FromCodePointFourByte) { HandleScope scope(thread_); SmallStr str(&scope, SmallStr::fromCodePoint(0x10348)); ASSERT_EQ(str.length(), 4); EXPECT_EQ(str.byteAt(0), 0xF0); EXPECT_EQ(str.byteAt(1), 0x90); EXPECT_EQ(str.byteAt(2), 0x8D); EXPECT_EQ(str.byteAt(3), 0x88); } TEST_F(SmallStrTest, IncludesLargeStrReturnsFalse) { HandleScope scope(thread_); SmallStr haystack(&scope, SmallStr::fromCStr("abcd")); LargeStr needle(&scope, runtime_->newStrFromCStr("abcdefgh")); EXPECT_FALSE(haystack.includes(*needle)); } TEST_F(SmallStrTest, IncludesSmallStrChecksSubstr) { HandleScope scope(thread_); SmallStr haystack(&scope, SmallStr::fromCStr("abcd")); SmallStr empty(&scope, Str::empty()); SmallStr needle(&scope, SmallStr::fromCStr("bc")); SmallStr not_needle(&scope, SmallStr::fromCStr("abcde")); EXPECT_TRUE(haystack.includes(*empty)); EXPECT_TRUE(haystack.includes(*needle)); EXPECT_TRUE(haystack.includes(*haystack)); EXPECT_FALSE(haystack.includes(*not_needle)); } TEST_F(SmallStrTest, IsASCIIReturnsTrueIfAndOnlyIfAllASCII) { HandleScope scope(thread_); SmallStr all_ascii(&scope, SmallStr::fromCStr("abc")); EXPECT_TRUE(all_ascii.isASCII()); SmallStr some_non_ascii(&scope, SmallStr::fromCStr("a\xC2\xA3g")); EXPECT_FALSE(some_non_ascii.isASCII()); } TEST_F(SmallStrTest, OccurrencesOfWithEmptyStringReturnsZero) { HandleScope scope(thread_); Str haystack(&scope, SmallStr::fromCStr("hello")); Str needle(&scope, runtime_->newStrFromCStr("")); EXPECT_EQ(haystack.occurrencesOf(*needle), 0); } TEST_F(SmallStrTest, OccurrencesOfWithLargeStrReturnsZero) { HandleScope scope(thread_); Str haystack(&scope, SmallStr::fromCStr("abab")); Str needle(&scope, runtime_->newStrFromCStr("ababababab")); EXPECT_EQ(haystack.occurrencesOf(*needle), 0); } TEST_F(SmallStrTest, OccurrencesOfWithNoOccurenceReturnsZero) { HandleScope scope(thread_); Str haystack(&scope, SmallStr::fromCStr("abab")); Str needle(&scope, SmallStr::fromCStr("cd")); EXPECT_EQ(haystack.occurrencesOf(*needle), 0); } TEST_F(SmallStrTest, OccurrencesOfWithSmallStrFindsOccurrenceAtSecondChar) { HandleScope scope(thread_); Str haystack(&scope, SmallStr::fromCStr("abab")); Str needle(&scope, SmallStr::fromCStr("ba")); EXPECT_EQ(haystack.occurrencesOf(*needle), 1); } TEST_F(SmallStrTest, OccurrencesOfWithSmallStrMultipleOccurrencesFindsAll) { HandleScope scope(thread_); Str haystack(&scope, SmallStr::fromCStr("hello")); Str needle(&scope, SmallStr::fromCStr("l")); EXPECT_EQ(haystack.occurrencesOf(*needle), 2); } TEST_F(StrTest, OffsetByCodePoints) { HandleScope scope(thread_); Str empty(&scope, Str::empty()); EXPECT_EQ(empty.length(), 0); EXPECT_EQ(empty.codePointLength(), 0); EXPECT_EQ(empty.offsetByCodePoints(0, 1), 0); EXPECT_EQ(empty.offsetByCodePoints(2, 0), 0); EXPECT_EQ(empty.offsetByCodePoints(2, 1), 0); Str ascii(&scope, runtime_->newStrFromCStr("abcd")); EXPECT_EQ(ascii.length(), 4); EXPECT_EQ(ascii.codePointLength(), 4); // for ASCII, each code point is one byte wide EXPECT_EQ(ascii.offsetByCodePoints(0, 0), 0); EXPECT_EQ(ascii.offsetByCodePoints(0, 3), 3); EXPECT_EQ(ascii.offsetByCodePoints(1, 0), 1); EXPECT_EQ(ascii.offsetByCodePoints(2, 0), 2); EXPECT_EQ(ascii.offsetByCodePoints(2, 1), 3); EXPECT_EQ(ascii.offsetByCodePoints(3, 0), 3); // return the length once we reach the end of the string EXPECT_EQ(ascii.offsetByCodePoints(0, 4), 4); EXPECT_EQ(ascii.offsetByCodePoints(0, 5), 4); EXPECT_EQ(ascii.offsetByCodePoints(1, 3), 4); EXPECT_EQ(ascii.offsetByCodePoints(1, 4), 4); EXPECT_EQ(ascii.offsetByCodePoints(2, 2), 4); EXPECT_EQ(ascii.offsetByCodePoints(2, 3), 4); EXPECT_EQ(ascii.offsetByCodePoints(3, 1), 4); EXPECT_EQ(ascii.offsetByCodePoints(3, 2), 4); EXPECT_EQ(ascii.offsetByCodePoints(4, 0), 4); EXPECT_EQ(ascii.offsetByCodePoints(6, 0), 4); Str unicode(&scope, runtime_->newStrFromCStr("\xd7\x90pq\xd7\x91\xd7\x92-\xd7\x93")); EXPECT_EQ(unicode.length(), 11); EXPECT_EQ(unicode.codePointLength(), 7); // for Unicode, code points may be more than one byte wide EXPECT_EQ(unicode.offsetByCodePoints(0, 0), 0); EXPECT_EQ(unicode.offsetByCodePoints(0, 1), 2); EXPECT_EQ(unicode.offsetByCodePoints(0, 2), 3); EXPECT_EQ(unicode.offsetByCodePoints(0, 3), 4); EXPECT_EQ(unicode.offsetByCodePoints(0, 4), 6); EXPECT_EQ(unicode.offsetByCodePoints(0, 5), 8); EXPECT_EQ(unicode.offsetByCodePoints(0, 6), 9); EXPECT_EQ(unicode.offsetByCodePoints(2, 0), 2); EXPECT_EQ(unicode.offsetByCodePoints(2, 1), 3); EXPECT_EQ(unicode.offsetByCodePoints(2, 2), 4); EXPECT_EQ(unicode.offsetByCodePoints(2, 3), 6); EXPECT_EQ(unicode.offsetByCodePoints(2, 4), 8); EXPECT_EQ(unicode.offsetByCodePoints(2, 5), 9); EXPECT_EQ(unicode.offsetByCodePoints(2, 6), 11); EXPECT_EQ(unicode.offsetByCodePoints(4, 0), 4); EXPECT_EQ(unicode.offsetByCodePoints(4, 1), 6); EXPECT_EQ(unicode.offsetByCodePoints(6, 0), 6); // return the length once we reach the end of the string EXPECT_EQ(unicode.offsetByCodePoints(0, 7), 11); EXPECT_EQ(unicode.offsetByCodePoints(0, 9), 11); EXPECT_EQ(unicode.offsetByCodePoints(2, 7), 11); EXPECT_EQ(unicode.offsetByCodePoints(3, 6), 11); EXPECT_EQ(unicode.offsetByCodePoints(4, 5), 11); EXPECT_EQ(unicode.offsetByCodePoints(8, 3), 11); EXPECT_EQ(unicode.offsetByCodePoints(12, 0), 11); } TEST_F(LargeStrTest, CodePointLengthAscii) { HandleScope scope(thread_); const char* code_units = "01234567012345670"; Str str(&scope, runtime_->newStrFromCStr(code_units)); EXPECT_TRUE(str.isLargeStr()); EXPECT_EQ(str.length(), static_cast<word>(std::strlen(code_units))); EXPECT_EQ(str.codePointLength(), 17); } TEST_F(LargeStrTest, CodePointLength) { HandleScope scope(thread_); const char* code_units = "\xd7\x99\xd7\xa9 \xd7\x9c\xd7\x99 \xd7\x94\xd7\xa8\xd7\x91\xd7\x94 " "\xd7\x90\xd7\x95\xd7\xaa\xd7\x99\xd7\x95\xd7\xaa " "\xd7\xa2\xd7\x9b\xd7\xa9\xd7\x99\xd7\x95"; Str str(&scope, runtime_->newStrFromCStr(code_units)); EXPECT_TRUE(str.isLargeStr()); EXPECT_EQ(str.length(), static_cast<word>(std::strlen(code_units))); EXPECT_EQ(str.codePointLength(), 23); } TEST_F(LargeStrTest, IncludesLargeStrChecksSubstr) { HandleScope scope(thread_); LargeStr haystack(&scope, runtime_->newStrFromCStr("abcdefghijk")); LargeStr needle(&scope, runtime_->newStrFromCStr("bcdefghi")); LargeStr not_needle(&scope, runtime_->newStrFromCStr("aaaaaaaa")); EXPECT_TRUE(haystack.includes(*haystack)); EXPECT_TRUE(haystack.includes(*needle)); EXPECT_FALSE(haystack.includes(*not_needle)); } TEST_F(LargeStrTest, IncludesSmallStrChecksSubstr) { HandleScope scope(thread_); LargeStr haystack(&scope, runtime_->newStrFromCStr("abcdefghijkl")); SmallStr empty(&scope, Str::empty()); SmallStr needle(&scope, SmallStr::fromCStr("fgh")); SmallStr not_needle(&scope, SmallStr::fromCStr("bb")); EXPECT_TRUE(haystack.includes(*empty)); EXPECT_TRUE(haystack.includes(*needle)); EXPECT_FALSE(haystack.includes(*not_needle)); Str chars(&scope, runtime_->newStrFromCStr(".\\[{()*+?^$|")); Str hash(&scope, SmallStr::fromCStr("#")); EXPECT_FALSE(chars.includes(*hash)); } TEST_F(LargeStrTest, IsASCIIReturnsTrueIfAndOnlyIfAllASCII) { HandleScope scope(thread_); Str ascii(&scope, runtime_->newStrFromCStr("01234567012345670")); EXPECT_TRUE(ascii.isLargeStr()); EXPECT_TRUE(ascii.isASCII()); Str unicode(&scope, runtime_->newStrFromCStr("ascii \xd7\x99\xd7\xa9 pad")); EXPECT_TRUE(unicode.isLargeStr()); EXPECT_FALSE(unicode.isASCII()); } TEST_F(LargeStrTest, OccurrencesOfWithEmptyStringReturnsZero) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello world")); Str needle(&scope, runtime_->newStrFromCStr("")); EXPECT_EQ(haystack.occurrencesOf(*needle), 0); } TEST_F(LargeStrTest, OccurrencesOfWithLargeStrFindsOccurrenceAtFirstChar) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello world")); Str needle(&scope, runtime_->newStrFromCStr("hello wor")); EXPECT_EQ(haystack.occurrencesOf(*needle), 1); } TEST_F(LargeStrTest, OccurrencesOfWithLargeStrMultipleOccurrencesFindsAll) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello world hello world")); Str needle(&scope, runtime_->newStrFromCStr("hello world")); EXPECT_EQ(haystack.occurrencesOf(*needle), 2); } TEST_F(LargeStrTest, OccurrencesOfWithNoOccurenceReturnsZero) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello world")); Str needle(&scope, runtime_->newStrFromCStr("is not here")); EXPECT_EQ(haystack.occurrencesOf(*needle), 0); } TEST_F(LargeStrTest, OccurrencesOfWithSmallStrFindsOccurrenceAtThirdChar) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello world")); Str needle(&scope, SmallStr::fromCStr("llo")); EXPECT_EQ(haystack.occurrencesOf(*needle), 1); } TEST_F(LargeStrTest, OccurrencesOfWithSmallStrMultipleOccurrencesFindsAll) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("hello hello")); Str needle(&scope, SmallStr::fromCStr("llo")); EXPECT_EQ(haystack.occurrencesOf(*needle), 2); } TEST_F(LargeStrTest, OccurrencesOfWithSmallStrUsesCorrectEndian) { HandleScope scope(thread_); Str haystack(&scope, runtime_->newStrFromCStr("ababababab")); Str needle(&scope, SmallStr::fromCStr("ab")); EXPECT_EQ(haystack.occurrencesOf(*needle), 5); } TEST_F(StringTest, ReverseOffsetByCodePointsEmptyString) { HandleScope scope(thread_); Str empty(&scope, Str::empty()); word i = empty.length(); ASSERT_EQ(0, i); EXPECT_EQ(-1, empty.offsetByCodePoints(i, -1)); } TEST_F(StringTest, ReverseOffsetByCodePointsStringLength1) { HandleScope scope(thread_); Str str1(&scope, runtime_->newStrFromCStr("1")); word len = str1.length(); ASSERT_EQ(1, len); EXPECT_EQ(1, str1.offsetByCodePoints(len, 0)); EXPECT_EQ(0, str1.offsetByCodePoints(len, -1)); EXPECT_EQ(-1, str1.offsetByCodePoints(len, -2)); } TEST_F(StringTest, ReverseOffsetByCodePointsStringLength3) { HandleScope scope(thread_); Str str3(&scope, runtime_->newStrFromCStr("123")); word len = str3.length(); ASSERT_EQ(3, len); EXPECT_EQ(2, str3.offsetByCodePoints(len, -1)); EXPECT_EQ(1, str3.offsetByCodePoints(len, -2)); EXPECT_EQ(0, str3.offsetByCodePoints(len, -3)); EXPECT_EQ(-1, str3.offsetByCodePoints(len, -4)); EXPECT_EQ(1, str3.offsetByCodePoints(len - 1, -1)); EXPECT_EQ(0, str3.offsetByCodePoints(len - 1, -2)); EXPECT_EQ(-1, str3.offsetByCodePoints(len - 1, -3)); EXPECT_EQ(-1, str3.offsetByCodePoints(len - 1, -4)); EXPECT_EQ(0, str3.offsetByCodePoints(len - 2, -1)); EXPECT_EQ(-1, str3.offsetByCodePoints(len - 2, -2)); EXPECT_EQ(-1, str3.offsetByCodePoints(len - 2, -3)); EXPECT_EQ(-1, str3.offsetByCodePoints(len - 2, -4)); } TEST_F(StringTest, ReverseOffsetByCodePointsUnicodeStringLength5) { HandleScope scope(thread_); Str str5(&scope, runtime_->newStrFromCStr("\x41\xD7\x91\xD7\x92")); word len = str5.length(); ASSERT_EQ(5, len); EXPECT_EQ(3, str5.offsetByCodePoints(len, -1)); EXPECT_EQ(1, str5.offsetByCodePoints(len, -2)); EXPECT_EQ(0, str5.offsetByCodePoints(len, -3)); EXPECT_EQ(-1, str5.offsetByCodePoints(len, -4)); EXPECT_EQ(1, str5.offsetByCodePoints(len - 2, -1)); EXPECT_EQ(0, str5.offsetByCodePoints(len - 2, -2)); EXPECT_EQ(-1, str5.offsetByCodePoints(len - 2, -3)); EXPECT_EQ(-1, str5.offsetByCodePoints(len - 2, -4)); EXPECT_EQ(0, str5.offsetByCodePoints(len - 4, -1)); EXPECT_EQ(-1, str5.offsetByCodePoints(len - 4, -2)); EXPECT_EQ(-1, str5.offsetByCodePoints(len - 4, -3)); EXPECT_EQ(-1, str5.offsetByCodePoints(len - 4, -4)); } TEST_F(StringTest, ToCString) { HandleScope scope(thread_); Str empty(&scope, Str::empty()); char* c_empty = empty.toCStr(); ASSERT_NE(c_empty, nullptr); EXPECT_STREQ(c_empty, ""); std::free(c_empty); Str length1(&scope, runtime_->newStrFromCStr("a")); char* c_length1 = length1.toCStr(); ASSERT_NE(c_length1, nullptr); EXPECT_STREQ(c_length1, "a"); std::free(c_length1); Str length2(&scope, runtime_->newStrFromCStr("ab")); char* c_length2 = length2.toCStr(); ASSERT_NE(c_length2, nullptr); EXPECT_STREQ(c_length2, "ab"); std::free(c_length2); Str length10(&scope, runtime_->newStrFromCStr("1234567890")); char* c_length10 = length10.toCStr(); ASSERT_NE(c_length10, nullptr); EXPECT_STREQ(c_length10, "1234567890"); std::free(c_length10); Str nulchar(&scope, runtime_->newStrFromCStr("wx\0yz")); char* c_nulchar = nulchar.toCStr(); ASSERT_NE(c_nulchar, nullptr); EXPECT_STREQ(c_nulchar, "wx"); std::free(c_nulchar); } TEST_F(StringTest, CompareSmallStr) { HandleScope scope(thread_); Str small(&scope, runtime_->newStrFromCStr("foo")); EXPECT_TRUE(small.isSmallStr()); EXPECT_TRUE(small.equalsCStr("foo")); // This apparently stupid test is in response to a bug where we assumed // that the c-string passed to SmallStr::equalsCStr would always // be small itself. EXPECT_FALSE(small.equalsCStr("123456789")); } TEST_F(StringTest, CompareWithUnicode) { HandleScope scope(thread_); Str small(&scope, runtime_->newStrFromCStr(u8"hello\u2028")); EXPECT_TRUE(small.equalsCStr("hello\u2028")); } TEST_F(ValueCellTest, SetPlaceholderRendersIsPlaceholderToReturnTrue) { HandleScope scope(thread_); ValueCell value_cell(&scope, runtime_->newValueCell()); ASSERT_FALSE(value_cell.isPlaceholder()); value_cell.makePlaceholder(); EXPECT_TRUE(value_cell.isPlaceholder()); } TEST_F(WeakRefTest, EnqueueAndDequeue) { HandleScope scope(thread_); RawObject list = NoneType::object(); Object o0(&scope, runtime_->newList()); Object o1(&scope, runtime_->newList()); Object o2(&scope, runtime_->newList()); WeakRef::enqueue(runtime_->newWeakRef(thread_, o0), &list); WeakRef::enqueue(runtime_->newWeakRef(thread_, o1), &list); WeakRef::enqueue(runtime_->newWeakRef(thread_, o2), &list); WeakRef weak(&scope, WeakRef::dequeue(&list)); EXPECT_EQ(weak.referent(), o0); weak = WeakRef::dequeue(&list); EXPECT_EQ(weak.referent(), o1); weak = WeakRef::dequeue(&list); EXPECT_EQ(weak.referent(), o2); EXPECT_EQ(list, NoneType::object()); } TEST_F(WeakRefTest, SpliceQueue) { HandleScope scope(thread_); RawObject list1 = NoneType::object(); RawObject list2 = NoneType::object(); EXPECT_EQ(WeakRef::spliceQueue(list1, list2), NoneType::object()); Object none(&scope, NoneType::object()); RawObject list3 = runtime_->newWeakRef(thread_, none); WeakRef::cast(list3).setLink(list3); EXPECT_EQ(WeakRef::spliceQueue(list1, list3), list3); EXPECT_EQ(WeakRef::spliceQueue(list3, list2), list3); Object o0(&scope, runtime_->newDict()); Object o1(&scope, runtime_->newDict()); Object o2(&scope, runtime_->newDict()); Object o3(&scope, runtime_->newDict()); WeakRef::enqueue(runtime_->newWeakRef(thread_, o0), &list1); WeakRef::enqueue(runtime_->newWeakRef(thread_, o2), &list2); WeakRef::enqueue(runtime_->newWeakRef(thread_, o1), &list1); WeakRef::enqueue(runtime_->newWeakRef(thread_, o3), &list2); RawObject list = WeakRef::spliceQueue(list1, list2); WeakRef weak(&scope, WeakRef::dequeue(&list)); EXPECT_EQ(weak.referent(), o0); weak = WeakRef::dequeue(&list); EXPECT_EQ(weak.referent(), o1); weak = WeakRef::dequeue(&list); EXPECT_EQ(weak.referent(), o2); weak = WeakRef::dequeue(&list); EXPECT_EQ(weak.referent(), o3); EXPECT_EQ(list, NoneType::object()); } TEST_F(ListTest, ReplaceFromWithReplacesElementsStartingAtZero) { HandleScope scope(thread_); List dst(&scope, runtime_->newList()); Tuple dst_tuple(&scope, runtime_->newMutableTuple(5)); dst.setItems(*dst_tuple); dst.setNumItems(5); List src(&scope, listFromRange(0, 5)); dst.replaceFromWith(0, *src, 2); ASSERT_EQ(dst.numItems(), 5); EXPECT_TRUE(isIntEqualsWord(dst.at(0), 0)); EXPECT_TRUE(isIntEqualsWord(dst.at(1), 1)); EXPECT_EQ(dst.at(2), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(3), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(4), SmallInt::fromWord(0)); } TEST_F(ListTest, ReplaceFromWithReplacesElementsStartingInMiddle) { HandleScope scope(thread_); List dst(&scope, runtime_->newList()); Tuple dst_tuple(&scope, runtime_->newMutableTuple(5)); dst.setItems(*dst_tuple); dst.setNumItems(5); List src(&scope, listFromRange(0, 5)); dst.replaceFromWith(1, *src, 2); ASSERT_EQ(dst.numItems(), 5); EXPECT_EQ(dst.at(0), SmallInt::fromWord(0)); EXPECT_TRUE(isIntEqualsWord(dst.at(1), 0)); EXPECT_TRUE(isIntEqualsWord(dst.at(2), 1)); EXPECT_EQ(dst.at(3), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(4), SmallInt::fromWord(0)); } TEST_F(ListTest, ReplaceFromWithCopiesZeroElements) { HandleScope scope(thread_); List dst(&scope, runtime_->newList()); Tuple dst_tuple(&scope, runtime_->newMutableTuple(5)); dst.setItems(*dst_tuple); dst.setNumItems(5); List src(&scope, listFromRange(0, 5)); dst.replaceFromWith(0, *src, 0); ASSERT_EQ(dst.numItems(), 5); EXPECT_EQ(dst.at(0), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(1), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(2), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(3), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(4), SmallInt::fromWord(0)); } TEST_F(ListTest, ReplaceFromWithCopiesEveryElementFromSrc) { HandleScope scope(thread_); List dst(&scope, runtime_->newList()); Tuple dst_tuple(&scope, runtime_->newMutableTuple(5)); dst.setItems(*dst_tuple); dst.setNumItems(5); List src(&scope, listFromRange(0, 5)); dst.replaceFromWith(0, *src, 5); ASSERT_EQ(dst.numItems(), 5); EXPECT_TRUE(isIntEqualsWord(dst.at(0), 0)); EXPECT_TRUE(isIntEqualsWord(dst.at(1), 1)); EXPECT_TRUE(isIntEqualsWord(dst.at(2), 2)); EXPECT_TRUE(isIntEqualsWord(dst.at(3), 3)); EXPECT_TRUE(isIntEqualsWord(dst.at(4), 4)); } TEST_F(ListTest, ReplaceFromWithStartAtReplacesElementsStartingAtSrcStart) { HandleScope scope(thread_); List dst(&scope, runtime_->newList()); Tuple dst_tuple(&scope, runtime_->newMutableTuple(5)); dst.setItems(*dst_tuple); dst.setNumItems(5); List src(&scope, listFromRange(0, 5)); dst.replaceFromWithStartAt(0, *src, 2, 2); ASSERT_EQ(dst.numItems(), 5); EXPECT_TRUE(isIntEqualsWord(dst.at(0), 2)); EXPECT_TRUE(isIntEqualsWord(dst.at(1), 3)); EXPECT_EQ(dst.at(2), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(3), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(4), SmallInt::fromWord(0)); } TEST_F(ListTest, ReplaceFromWithStartAtWithSelfNoop) { HandleScope scope(thread_); List dst(&scope, listFromRange(0, 5)); dst.replaceFromWithStartAt(0, *dst, 2, 0); EXPECT_PYLIST_EQ(dst, {0, 1, 2, 3, 4}); } TEST_F(ListTest, ReplaceFromWithStartAtWithSelfBackward) { HandleScope scope(thread_); List dst(&scope, listFromRange(0, 5)); dst.replaceFromWithStartAt(0, *dst, 2, 2); EXPECT_PYLIST_EQ(dst, {2, 3, 2, 3, 4}); } TEST_F(ListTest, ReplaceFromWithStartAtWithSelfForward) { HandleScope scope(thread_); List dst(&scope, listFromRange(0, 5)); dst.replaceFromWithStartAt(2, *dst, 2, 0); EXPECT_PYLIST_EQ(dst, {0, 1, 0, 1, 4}); } TEST_F(ListTest, SwapSwapsElementsAtIndices) { HandleScope scope(thread_); List src(&scope, listFromRange(0, 5)); EXPECT_PYLIST_EQ(src, {0, 1, 2, 3, 4}); src.swap(1, 3); EXPECT_PYLIST_EQ(src, {0, 3, 2, 1, 4}); } TEST_F(MutableTupleTest, NoneFillTupleFillsTupleWithNone) { HandleScope scope(thread_); MutableTuple tuple(&scope, runtime_->newMutableTuple(3)); tuple.atPut(0, SmallInt::fromWord(0)); tuple.atPut(1, SmallInt::fromWord(1)); tuple.atPut(2, SmallInt::fromWord(2)); tuple.fill(NoneType::object()); EXPECT_EQ(tuple.at(0), NoneType::object()); EXPECT_EQ(tuple.at(1), NoneType::object()); EXPECT_EQ(tuple.at(2), NoneType::object()); } TEST_F(MutableTupleTest, ReplaceFromWithReplacesElementsStartingAtZero) { HandleScope scope(thread_); MutableTuple dst(&scope, runtime_->newMutableTuple(5)); List src(&scope, listFromRange(0, 5)); Tuple src_items(&scope, src.items()); dst.replaceFromWith(0, *src_items, 2); EXPECT_TRUE(isIntEqualsWord(dst.at(0), 0)); EXPECT_TRUE(isIntEqualsWord(dst.at(1), 1)); EXPECT_EQ(dst.at(2), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(3), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(4), SmallInt::fromWord(0)); } TEST_F(MutableTupleTest, ReplaceFromWithReplacesElementsStartingInMiddle) { HandleScope scope(thread_); MutableTuple dst(&scope, runtime_->newMutableTuple(5)); List src(&scope, listFromRange(0, 5)); Tuple src_items(&scope, src.items()); dst.replaceFromWith(1, *src_items, 2); EXPECT_EQ(dst.at(0), SmallInt::fromWord(0)); EXPECT_TRUE(isIntEqualsWord(dst.at(1), 0)); EXPECT_TRUE(isIntEqualsWord(dst.at(2), 1)); EXPECT_EQ(dst.at(3), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(4), SmallInt::fromWord(0)); } TEST_F(MutableTupleTest, ReplaceFromWithCopiesZeroElements) { HandleScope scope(thread_); MutableTuple dst(&scope, runtime_->newMutableTuple(5)); List src(&scope, listFromRange(0, 5)); Tuple src_items(&scope, src.items()); dst.replaceFromWith(0, *src_items, 0); EXPECT_EQ(dst.at(0), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(1), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(2), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(3), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(4), SmallInt::fromWord(0)); } TEST_F(MutableTupleTest, ReplaceFromWithCopiesEveryElementFromSrc) { HandleScope scope(thread_); MutableTuple dst(&scope, runtime_->newMutableTuple(5)); List src(&scope, listFromRange(0, 5)); Tuple src_items(&scope, src.items()); dst.replaceFromWith(0, *src_items, 5); EXPECT_TRUE(isIntEqualsWord(dst.at(0), 0)); EXPECT_TRUE(isIntEqualsWord(dst.at(1), 1)); EXPECT_TRUE(isIntEqualsWord(dst.at(2), 2)); EXPECT_TRUE(isIntEqualsWord(dst.at(3), 3)); EXPECT_TRUE(isIntEqualsWord(dst.at(4), 4)); } TEST_F(MutableTupleTest, ReplaceFromWithStartAtWithSelfNoop) { HandleScope scope(thread_); List dst_list(&scope, listFromRange(0, 5)); MutableTuple dst(&scope, dst_list.items()); dst.replaceFromWithStartAt(0, *dst, 2, 0); EXPECT_PYLIST_EQ(dst_list, {0, 1, 2, 3, 4}); } TEST_F(MutableTupleTest, ReplaceFromWithStartAtWithSelfBackward) { HandleScope scope(thread_); List dst_list(&scope, listFromRange(0, 5)); MutableTuple dst(&scope, dst_list.items()); dst.replaceFromWithStartAt(0, *dst, 2, 2); EXPECT_PYLIST_EQ(dst_list, {2, 3, 2, 3, 4}); } TEST_F(MutableTupleTest, ReplaceFromWithStartAtWithSelfForward) { HandleScope scope(thread_); List dst_list(&scope, listFromRange(0, 5)); MutableTuple dst(&scope, dst_list.items()); dst.replaceFromWithStartAt(2, *dst, 2, 0); EXPECT_PYLIST_EQ(dst_list, {0, 1, 0, 1, 4}); } TEST_F(MutableTupleTest, ReplaceFromWithStartAtReplacesElementsStartingAtSrcStart) { HandleScope scope(thread_); MutableTuple dst(&scope, runtime_->newMutableTuple(5)); List src_list(&scope, listFromRange(0, 5)); Tuple src(&scope, src_list.items()); dst.replaceFromWithStartAt(0, *src, 2, 2); EXPECT_TRUE(isIntEqualsWord(dst.at(0), 2)); EXPECT_TRUE(isIntEqualsWord(dst.at(1), 3)); EXPECT_EQ(dst.at(2), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(3), SmallInt::fromWord(0)); EXPECT_EQ(dst.at(4), SmallInt::fromWord(0)); } TEST_F(MutableTupleTest, SwapSwapsElementsAtIndices) { HandleScope scope(thread_); List src_list(&scope, listFromRange(0, 5)); MutableTuple src(&scope, src_list.items()); EXPECT_PYLIST_EQ(src_list, {0, 1, 2, 3, 4}); src.swap(1, 3); EXPECT_PYLIST_EQ(src_list, {0, 3, 2, 1, 4}); } TEST(ErrorTest, ErrorIsError) { EXPECT_TRUE(Error::error().isError()); EXPECT_TRUE(Error::exception().isError()); EXPECT_TRUE(Error::exception().isErrorException()); EXPECT_TRUE(Error::notFound().isError()); EXPECT_TRUE(Error::notFound().isErrorNotFound()); EXPECT_TRUE(Error::noMoreItems().isError()); EXPECT_TRUE(Error::noMoreItems().isErrorNoMoreItems()); EXPECT_TRUE(Error::outOfMemory().isError()); EXPECT_TRUE(Error::outOfMemory().isErrorOutOfMemory()); EXPECT_TRUE(Error::outOfBounds().isError()); EXPECT_TRUE(Error::outOfBounds().isErrorOutOfBounds()); } TEST(ErrorTest, ErrorHasCorrectKind) { EXPECT_EQ(Error::error().kind(), ErrorKind::kNone); EXPECT_EQ(Error::exception().kind(), ErrorKind::kException); EXPECT_EQ(Error::notFound().kind(), ErrorKind::kNotFound); EXPECT_EQ(Error::noMoreItems().kind(), ErrorKind::kNoMoreItems); EXPECT_EQ(Error::outOfMemory().kind(), ErrorKind::kOutOfMemory); EXPECT_EQ(Error::outOfBounds().kind(), ErrorKind::kOutOfBounds); } } // namespace testing } // namespace py