// RUN: %target-run-simple-swift // REQUIRES: executable_test // // Tests for the non-Foundation CString-oriented API of String // import StdlibUnittest #if os(WASI) let enableCrashTests = false #else let enableCrashTests = true #endif var CStringTests = TestSuite("CStringTests") func getNullUTF8() -> UnsafeMutablePointer? { return nil } func getASCIIUTF8() -> (UnsafeMutablePointer, dealloc: () -> ()) { let up = UnsafeMutablePointer.allocate(capacity: 100) up[0] = 0x61 up[1] = 0x62 up[2] = 0 return (up, { up.deallocate() }) } func getNonASCIIUTF8() -> (UnsafeMutablePointer, dealloc: () -> ()) { let up = UnsafeMutablePointer.allocate(capacity: 100) up[0] = 0xd0 up[1] = 0xb0 up[2] = 0xd0 up[3] = 0xb1 up[4] = 0 return (UnsafeMutablePointer(up), { up.deallocate() }) } func getIllFormedUTF8String1( ) -> (UnsafeMutablePointer, dealloc: () -> ()) { let up = UnsafeMutablePointer.allocate(capacity: 100) up[0] = 0x41 up[1] = 0xed up[2] = 0xa0 up[3] = 0x80 up[4] = 0x41 up[5] = 0 return (UnsafeMutablePointer(up), { up.deallocate() }) } func getIllFormedUTF8String2( ) -> (UnsafeMutablePointer, dealloc: () -> ()) { let up = UnsafeMutablePointer.allocate(capacity: 100) up[0] = 0x41 up[0] = 0x41 up[1] = 0xed up[2] = 0xa0 up[3] = 0x81 up[4] = 0x41 up[5] = 0 return (UnsafeMutablePointer(up), { up.deallocate() }) } func asCCharArray(_ a: [UInt8]) -> [CChar] { return a.map { CChar(bitPattern: $0) } } func getUTF8Length(_ cString: UnsafePointer) -> Int { var length = 0 while cString[length] != 0 { length += 1 } return length } func bindAsCChar(_ utf8: UnsafePointer) -> UnsafePointer { return UnsafeRawPointer(utf8).bindMemory(to: CChar.self, capacity: getUTF8Length(utf8)) } func expectEqualCString(_ lhs: UnsafePointer, _ rhs: UnsafePointer) { var index = 0 while lhs[index] != 0 { expectEqual(lhs[index], rhs[index]) index += 1 } expectEqual(0, rhs[index]) } func expectEqualCString(_ lhs: UnsafePointer, _ rhs: ContiguousArray) { rhs.withUnsafeBufferPointer { expectEqualCString(lhs, $0.baseAddress!) } } func expectEqualCString(_ lhs: UnsafePointer, _ rhs: ContiguousArray) { rhs.withUnsafeBufferPointer { $0.baseAddress!.withMemoryRebound( to: UInt8.self, capacity: rhs.count) { expectEqualCString(lhs, $0) } } } CStringTests.test("String.init(validatingCString:)") { do { let (s, dealloc) = getASCIIUTF8() expectEqual("ab", String(validatingCString: bindAsCChar(s))) dealloc() } do { let (s, dealloc) = getNonASCIIUTF8() expectEqual("аб", String(validatingCString: bindAsCChar(s))) dealloc() } do { let (s, dealloc) = getIllFormedUTF8String1() expectNil(String(validatingCString: bindAsCChar(s))) dealloc() } } CStringTests.test("String(cString:)") { do { let (s, dealloc) = getASCIIUTF8() let result = String(cString: s) expectEqual("ab", result) let su = bindAsCChar(s) expectEqual("ab", String(cString: su)) dealloc() } do { let (s, dealloc) = getNonASCIIUTF8() let result = String(cString: s) expectEqual("аб", result) let su = bindAsCChar(s) expectEqual("аб", String(cString: su)) dealloc() } do { let (s, dealloc) = getIllFormedUTF8String1() let result = String(cString: s) expectEqual("\u{41}\u{fffd}\u{fffd}\u{fffd}\u{41}", result) let su = bindAsCChar(s) expectEqual("\u{41}\u{fffd}\u{fffd}\u{fffd}\u{41}", String(cString: su)) dealloc() } } CStringTests.test("String.decodeCString") { do { let s = getNullUTF8() let result = String.decodeCString(s, as: UTF8.self) expectNil(result) } do { // repairing let (s, dealloc) = getIllFormedUTF8String1() if let (result, repairsMade) = String.decodeCString( s, as: UTF8.self, repairingInvalidCodeUnits: true) { expectEqual("\u{41}\u{fffd}\u{fffd}\u{fffd}\u{41}", result) expectTrue(repairsMade) } else { expectUnreachable("Expected .some()") } dealloc() } do { // non repairing let (s, dealloc) = getIllFormedUTF8String1() let result = String.decodeCString( s, as: UTF8.self, repairingInvalidCodeUnits: false) expectNil(result) dealloc() } } CStringTests.test("String.utf8CString") { do { let (cstr, dealloc) = getASCIIUTF8() defer { dealloc() } let str = String(cString: cstr) expectEqualCString(cstr, str.utf8CString) } do { let (cstr, dealloc) = getNonASCIIUTF8() defer { dealloc() } let str = String(cString: cstr) expectEqualCString(cstr, str.utf8CString) } } CStringTests.test("String.withCString") { do { let (cstr, dealloc) = getASCIIUTF8() defer { dealloc() } let str = String(cString: cstr) str.withCString { expectEqual(str, String(cString: $0)) } } do { let (cstr, dealloc) = getASCIIUTF8() defer { dealloc() } let str = String(cString: cstr) str.withCString { expectEqual(str, String(cString: $0)) } } } CStringTests.test("Substring.withCString") { do { let (cstr, dealloc) = getASCIIUTF8() defer { dealloc() } let str = String(cString: cstr).dropFirst() str.withCString { expectEqual(str, String(cString: $0)) } } do { let (cstr, dealloc) = getASCIIUTF8() defer { dealloc() } let str = String(cString: cstr).dropFirst() str.withCString { expectEqual(str, String(cString: $0)) } } } CStringTests.test("String.cString.with.Array.UInt8.input") { guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } let cstr = UnsafePointer(u8p) let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) let str = String(cString: Array(buffer)) str.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { expectEqualCString(u8p, $0) } } } guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of String.init(cString:) must be null-terminated" ) _ = String(cString: [] as [UInt8]) expectUnreachable() } CStringTests.test("String.cString.with.Array.CChar.input") { guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } let buffer = UnsafeBufferPointer(start: u8p, count: getUTF8Length(u8p)+1) let str = buffer.withMemoryRebound(to: CChar.self) { String(cString: Array($0)) } str.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { expectEqualCString(u8p, $0) } } } guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of String.init(cString:) must be null-terminated" ) _ = String(cString: [] as [CChar]) expectUnreachable() } CStringTests.test("String.cString.with.String.input") { guard #available(SwiftStdlib 5.7, *) else { return } let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } var str = String(cString: "ab") str.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: getUTF8Length(u8p)+1) { expectEqualCString(u8p, $0) } } str = String(cString: "") expectTrue(str.isEmpty) } CStringTests.test("String.cString.with.inout.UInt8.conversion") { guard #available(SwiftStdlib 5.7, *) else { return } var c = UInt8.zero var str = String(cString: &c) expectTrue(str.isEmpty) c = 100 guard enableCrashTests else { return } expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of String.init(cString:) must be null-terminated" ) str = String(cString: &c) expectUnreachable() } CStringTests.test("String.cString.with.inout.CChar.conversion") { guard #available(SwiftStdlib 5.7, *) else { return } var c = CChar.zero var str = String(cString: &c) expectTrue(str.isEmpty) c = 100 guard enableCrashTests else { return } expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of String.init(cString:) must be null-terminated" ) str = String(cString: &c) expectUnreachable() } CStringTests.test("String.validatingCString.with.Array.input") { guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } let buffer = UnsafeBufferPointer(start: u8p, count: getUTF8Length(u8p)+1) let str = buffer.withMemoryRebound(to: CChar.self) { String(validatingCString: Array($0)) } expectNotNil(str) str?.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { expectEqualCString(u8p, $0) } } } guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of String.init(validatingCString:) must be null-terminated" ) _ = String(validatingCString: []) expectUnreachable() } CStringTests.test("String.validatingCString.with.String.input") { guard #available(SwiftStdlib 5.7, *) else { return } let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } var str = String(validatingCString: "ab") expectNotNil(str) str?.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: getUTF8Length(u8p)+1) { expectEqualCString(u8p, $0) } } str = String(validatingCString: "") expectNotNil(str) expectEqual(str?.isEmpty, true) } CStringTests.test("String.validatingCString.with.inout.conversion") { guard #available(SwiftStdlib 5.7, *) else { return } var c = CChar.zero var str = String(validatingCString: &c) expectNotNil(str) expectEqual(str?.isEmpty, true) c = 100 guard enableCrashTests else { return } expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of String.init(validatingCString:) must be null-terminated" ) str = String(validatingCString: &c) expectUnreachable() } CStringTests.test("String.decodeCString.with.Array.input") { guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } let buffer = UnsafeBufferPointer(start: u8p, count: getUTF8Length(u8p)+1) let result = buffer.withMemoryRebound(to: Unicode.UTF8.CodeUnit.self) { String.decodeCString(Array($0), as: Unicode.UTF8.self) } expectNotNil(result) expectEqual(result?.repairsMade, false) result?.result.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { expectEqualCString(u8p, $0) } } } guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) _ = String.decodeCString([], as: Unicode.UTF8.self) expectUnreachable() } CStringTests.test("String.decodeCString.with.String.input") { guard #available(SwiftStdlib 5.7, *) else { return } let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } var result = String.decodeCString( "ab", as: Unicode.UTF8.self, repairingInvalidCodeUnits: true ) expectNotNil(result) expectEqual(result?.repairsMade, false) result?.result.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: getUTF8Length(u8p)+1) { expectEqualCString(u8p, $0) } } result = String.decodeCString("", as: Unicode.UTF8.self) expectNotNil(result) expectEqual(result?.repairsMade, false) expectEqual(result?.result.isEmpty, true) } CStringTests.test("String.decodeCString.with.inout.conversion") { guard #available(SwiftStdlib 5.7, *) else { return } var c = Unicode.UTF8.CodeUnit.zero var result = String.decodeCString( &c, as: Unicode.UTF8.self, repairingInvalidCodeUnits: true ) expectNotNil(result) expectEqual(result?.result.isEmpty, true) expectEqual(result?.repairsMade, false) c = 100 guard enableCrashTests else { return } expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) result = String.decodeCString(&c, as: Unicode.UTF8.self) expectUnreachable() } CStringTests.test("String.init.decodingCString.with.Array.input") { guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } let buffer = UnsafeBufferPointer(start: u8p, count: getUTF8Length(u8p)+1) let str = buffer.withMemoryRebound(to: Unicode.UTF8.CodeUnit.self) { String(decodingCString: Array($0), as: Unicode.UTF8.self) } str.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { expectEqualCString(u8p, $0) } } } guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) _ = String(decodingCString: [], as: Unicode.UTF8.self) expectUnreachable() } CStringTests.test("String.init.decodingCString.with.String.input") { guard #available(SwiftStdlib 5.7, *) else { return } let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } var str = String(decodingCString: "ab", as: Unicode.UTF8.self) str.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: getUTF8Length(u8p)+1) { expectEqualCString(u8p, $0) } } str = String(decodingCString: "", as: Unicode.UTF8.self) expectTrue(str.isEmpty) } CStringTests.test("String.init.decodingCString.with.inout.conversion") { guard #available(SwiftStdlib 5.7, *) else { return } var c = Unicode.UTF8.CodeUnit.zero var str = String(decodingCString: &c, as: Unicode.UTF8.self) expectEqual(str.isEmpty, true) c = 100 guard enableCrashTests else { return } expectCrashLater( // Workaround for https://github.com/apple/swift/issues/58362 (rdar://91365967) // withMessage: "input of String.init(decodingCString:as:) must be null-terminated" ) str = String(decodingCString: &c, as: Unicode.UTF8.self) expectUnreachable() } runAllTests()