// RUN: %target-run-simple-swift // REQUIRES: executable_test // UNSUPPORTED: freestanding import StdlibUnittest var StringIndexTests = TestSuite("StringIndexTests") enum SimpleString: String { case smallASCII = "abcdefg" case smallUnicode = "abรฉร๐“€€" case largeASCII = "012345678901234567890" case largeUnicode = "abรฉร012345678901234567890๐“€€" case emoji = "๐Ÿ˜€๐Ÿ˜ƒ๐Ÿคข๐Ÿคฎ๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽค๐Ÿง›๐Ÿปโ€โ™‚๏ธ๐Ÿง›๐Ÿปโ€โ™‚๏ธ๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ" } let simpleStrings: [String] = [ SimpleString.smallASCII.rawValue, SimpleString.smallUnicode.rawValue, SimpleString.largeASCII.rawValue, SimpleString.largeUnicode.rawValue, SimpleString.emoji.rawValue, "", ] StringIndexTests.test("basic sanity checks") { for s in simpleStrings { let utf8 = Array(s.utf8) let subUTF8 = Array(s[...].utf8) let utf16 = Array(s.utf16) let subUTF16 = Array(s[...].utf16) let utf32 = Array(s.unicodeScalars.map { $0.value }) let subUTF32 = Array(s[...].unicodeScalars.map { $0.value }) expectEqual(s, String(decoding: utf8, as: UTF8.self)) expectEqual(s, String(decoding: subUTF8, as: UTF8.self)) expectEqual(s, String(decoding: utf16, as: UTF16.self)) expectEqual(s, String(decoding: subUTF16, as: UTF16.self)) expectEqual(s, String(decoding: utf32, as: UTF32.self)) expectEqual(s, String(decoding: subUTF32, as: UTF32.self)) } } StringIndexTests.test("view counts") { func validateViewCount( _ view: View, for string: String, stackTrace: SourceLocStack = SourceLocStack(), showFrame: Bool = true, file: String = #file, line: UInt = #line ) where View.Element: Equatable, View.Index == String.Index { var stackTrace = stackTrace.pushIf(showFrame, file: file, line: line) let count = view.count func expect(_ i: Int, file: String = #file, line: UInt = #line ) { expectEqual(count, i, "for String: \(string)", stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), showFrame: false) } let reversedView = view.reversed() expect(Array(view).count) expect(view.indices.count) expect(view.indices.reversed().count) expect(reversedView.indices.count) expect(view.distance(from: view.startIndex, to: view.endIndex)) expect(reversedView.distance( from: reversedView.startIndex, to: reversedView.endIndex)) // Access the elements from the indices expectEqual(Array(view), view.indices.map { view[$0] }) expectEqual( Array(reversedView), reversedView.indices.map { reversedView[$0] }) let indicesArray = Array(view.indices) for i in 0..= 0b1100_0000 { expectEqual(idx, idx.samePosition(in: s.unicodeScalars)) expectEqual(idx, idx.samePosition(in: s.utf16)) // ASCII if char <= 0x7F { expectEqual(UInt16(char), s.utf16[idx]) expectEqual(UInt32(char), s.unicodeScalars[idx].value) } } else { // Continuation code unit assert(char & 0b1100_0000 == 0b1000_0000) expectNil(idx.samePosition(in: s)) expectNil(idx.samePosition(in: s.utf16)) expectNil(idx.samePosition(in: s.unicodeScalars)) } } } for s in simpleStrings { validateIndices(s) } } StringIndexTests.test("UTF-16 Offsets") { func validateOffsets(_ s: String) { let end = s.endIndex let utf16Count = s.utf16.count expectEqual(end, String.Index(utf16Offset: utf16Count, in: s)) expectEqual(end, String.Index(utf16Offset: utf16Count, in: s[...])) let pastEnd = String.Index(utf16Offset: utf16Count+1, in: s) expectNotEqual(end, pastEnd) expectEqual(pastEnd, String.Index(utf16Offset: utf16Count+1, in: s[...])) expectEqual(pastEnd, String.Index(utf16Offset: utf16Count+2, in: s)) expectEqual(pastEnd, String.Index(utf16Offset: -1, in: s)) expectEqual( pastEnd, String.Index(utf16Offset: Swift.max(1, utf16Count), in: s.dropFirst())) let utf16Indices = Array(s.utf16.indices) expectEqual(utf16Count, utf16Indices.count) for i in 0.. String.Index { var idx = idx while str.utf8[idx] & 0xC0 == 0x80 { str.utf8.formIndex(before: &idx) } return idx } StringIndexTests.test("Scalar Align UTF-8 indices") { // TODO: Test a new aligning API when we add it. For now, we // test scalar-aligning UTF-8 indices let str = "a๐Ÿ˜‡" let subScalarIdx = str.utf8.index(str.utf8.startIndex, offsetBy: 2) let roundedIdx = swift5ScalarAlign(subScalarIdx, in: str) expectEqual(1, roundedIdx.utf16Offset(in: str)) let roundedIdx2 = str.utf8[...subScalarIdx].lastIndex { $0 & 0xC0 != 0x80 } expectEqual(roundedIdx, roundedIdx2) var roundedIdx3 = subScalarIdx while roundedIdx3.samePosition(in: str.unicodeScalars) == nil { str.utf8.formIndex(before: &roundedIdx3) } expectEqual(roundedIdx, roundedIdx3) } #if _runtime(_ObjC) import Foundation StringIndexTests.test("String.Index(_:within) / Range(_:in:)") { guard #available(SwiftStdlib 5.1, *) else { return } let str = simpleStrings.joined() let substr = str[...] for idx in str.utf8.indices { expectEqual( String.Index(idx, within: str), String.Index(idx, within: substr)) } expectNil(String.Index(str.startIndex, within: str.dropFirst())) expectNil(String.Index(str.endIndex, within: str.dropLast())) expectNotNil(String.Index(str.startIndex, within: str)) expectNotNil(String.Index(str.endIndex, within: str)) let utf16Count = str.utf16.count let utf16Indices = Array(str.utf16.indices) + [str.utf16.endIndex] for location in 0..(nsRange, in: str) let substrRange = Range(nsRange, in: substr) expectEqual(strRange, substrRange) guard strLB != nil && strUB != nil else { expectNil(strRange) continue } expectEqual(strRange, Range(uncheckedBounds: (strLB!, strUB!))) } } } StringIndexTests.test("Misaligned") { // Misaligned indices were fixed in 5.1 guard _hasSwift_5_1() else { return } func doIt(_ str: String) { let characterIndices = Array(str.indices) let scalarIndices = Array(str.unicodeScalars.indices) + [str.endIndex] let utf8Indices = Array(str.utf8.indices) var lastScalarI = 0 for i in 1.. Bool, _ message: String = "", file: String = #file, line: UInt = #line ) { expectTrue(condition(), message, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line) } var curCharIdx = str.startIndex var curScalarIdx = str.startIndex var curUTF8Idx = str.startIndex var curUTF16Idx = str.startIndex while curCharIdx < str.endIndex { let curChar = str[curCharIdx] expect(curChar == str[curScalarIdx]) expect(curChar == str[curUTF8Idx]) expect(curChar == str[curUTF16Idx]) // Advance the character index once and have the scalar index catch up str.formIndex(after: &curCharIdx) let scalarStartIdx = curScalarIdx defer { let sub = str[scalarStartIdx.. utf8StartIdx { str.utf8.formIndex(before: &utf8RevIdx) expect(curScalar == str.unicodeScalars[utf8RevIdx]) expect(curSubChar == str[utf8RevIdx]) expect(!UTF16.isTrailSurrogate(str.utf16[utf8RevIdx])) expect(utf8StartIdx == str[utf8RevIdx...].startIndex) expect(str[utf8StartIdx.. utf16StartIdx { str.utf16.formIndex(before: &utf16RevIdx) expect(curScalar == str.unicodeScalars[utf16RevIdx]) expect(curSubChar == str[utf16RevIdx]) expect(!UTF8.isContinuation(str.utf8[utf16RevIdx])) expect(utf16StartIdx == str[utf16RevIdx...].startIndex) expect(str[utf16StartIdx..