// RUN: %target-build-swift -Xfrontend -disable-access-control -module-name a %s -o %t.out -O // RUN: %target-run %t.out // REQUIRES: executable_test // REQUIRES: CPU=arm64 || CPU=x86_64 // // Tests for small strings // import StdlibUnittest import Foundation var SmallStringTests = TestSuite("SmallStringTests") extension String: Error {} func verifySmallString(_ small: _SmallString, _ input: String) { expectEqual(_SmallString.capacity, small.count + small.unusedCapacity) let tiny = Array(input.utf8) expectEqual(tiny.count, small.count) for (lhs, rhs) in zip(tiny, small) { expectEqual(lhs, rhs) } let smallFromUTF16 = _SmallString(Array(input.utf16)) expectNotNil(smallFromUTF16) expectEqualSequence(small, smallFromUTF16!) // Test slicing // for i in 0..) { guard let smol = codeUnits.withUnsafeBufferPointer({ return _SmallString($0) }) else { return nil } self = smol } init?(_ codeUnits: Array) { let str = codeUnits.withUnsafeBufferPointer { return String._uncheckedFromUTF16($0) } if !str._guts.isSmall { return nil } self.init(str._guts._object) } #if _runtime(_ObjC) init?(_cocoaString ns: NSString) { guard _isObjCTaggedPointer(ns) else { return nil } self.init(taggedCocoa: ns) } #endif func _appending(_ other: _SmallString) -> _SmallString? { return _SmallString(self, appending: other) } func _repeated(_ n: Int) -> _SmallString? { var base = self let toAppend = self for _ in 0..<(n &- 1) { guard let s = _SmallString( base, appending: toAppend) else { return nil } base = s } return base } } SmallStringTests.test("FitsInSmall") { func runTest(_ input: String) throws { let tiny = Array(input.utf8) // Constructed from UTF-8 code units guard let small = _SmallString(tiny) else { throw "Didn't fit" } verifySmallString(small, input) // Constructed from UTF-16 code units guard let fromUTF16Small = _SmallString(Array(input.utf16)) else { throw "Failed from utf-16" } verifySmallString(fromUTF16Small, input) } // Pass tests // expectDoesNotThrow({ try runTest("abπŸ˜‡c") }) expectDoesNotThrow({ try runTest("0123456789abcde") }) expectDoesNotThrow({ try runTest("πŸ‘¨β€πŸ‘¦") }) expectDoesNotThrow({ try runTest("") }) // Fail tests // expectThrows("Didn't fit", { try runTest("0123456789abcdef") }) expectThrows("Didn't fit", { try runTest("πŸ‘©β€πŸ‘¦β€πŸ‘¦") }) for cu in (0 as UInt32)...(0x10FFFF as UInt32) { // TODO: Iterate over all scalars when we support UTF-8, and possibly move // this to validation suite. guard let scalar = Unicode.Scalar(cu) else { continue } guard cu <= 0x7F else { break } expectDoesNotThrow({ try runTest(String(scalar)) }) } } #if _runtime(_ObjC) SmallStringTests.test("Bridging") { // Test bridging retains small string form func bridge(_ small: _SmallString) -> String { return String(_StringGuts(small))._bridgeToObjectiveCImpl() as! String } func runTestSmall(_ input: String) throws { // Constructed through CF guard let fromCocoaSmall = _SmallString( _cocoaString: input as NSString ) else { throw "Didn't fit" } verifySmallString(fromCocoaSmall, input) verifySmallString(fromCocoaSmall, bridge(fromCocoaSmall)) } // Pass tests if #available(macOS 10.10, iOS 9, *) { expectDoesNotThrow({ try runTestSmall("abc") }) expectDoesNotThrow({ try runTestSmall("defghijk") }) expectDoesNotThrow({ try runTestSmall("aaaaaaaaaaa") }) } else { // OS X 10.9, iOS 7/8 did not have tagged strings expectThrows("Didn't fit", { try runTestSmall("abc") }) expectThrows("Didn't fit", { try runTestSmall("defghijk") }) expectThrows("Didn't fit", { try runTestSmall("aaaaaaaaaaa") }) } // Fail tests // expectThrows("Didn't fit", { try runTestSmall("\u{0}") }) expectThrows("Didn't fit", { try runTestSmall("0123456789abcde") }) expectThrows("Didn't fit", { try runTestSmall("πŸ‘¨β€πŸ‘¦abcd") }) expectThrows("Didn't fit", { try runTestSmall("πŸ‘¨β€πŸ‘¦") }) expectThrows("Didn't fit", { try runTestSmall("πŸ‘¨β€πŸ‘©β€πŸ‘¦") }) expectThrows("Didn't fit", { try runTestSmall("πŸ‘¨β€πŸ‘¦abcde") }) } #endif SmallStringTests.test("Append, repeating") { let strings = [ "", "a", "bc", "def", "hijk", "lmnop", "qrstuv", "xyzzzzz", "01234567", "890123456", "7890123456", "78901234567", "890123456789", "0123456789012", "34567890123456", "789012345678901", ] let smallstrings = strings.compactMap { _SmallString(Array($0.utf8)) } expectEqual(strings.count, smallstrings.count) for (small, str) in zip(smallstrings, strings) { verifySmallString(small, str) } for i in 0.. _SmallString.capacity { continue } verifySmallString(lhs._appending(rhs)!, (strings[i] + strings[j])) verifySmallString(rhs._appending(lhs)!, (strings[j] + strings[i])) } } for i in 0.. 15) } } } } runAllTests()