| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See http://swift.org/LICENSE.txt for license information |
| // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| |
| |
| #if DEPLOYMENT_RUNTIME_OBJC || os(Linux) |
| import Foundation |
| import XCTest |
| #else |
| import SwiftFoundation |
| import SwiftXCTest |
| #endif |
| |
| import CoreFoundation |
| |
| #if os(OSX) || os(iOS) |
| internal let kCFStringEncodingMacRoman = CFStringBuiltInEncodings.macRoman.rawValue |
| internal let kCFStringEncodingWindowsLatin1 = CFStringBuiltInEncodings.windowsLatin1.rawValue |
| internal let kCFStringEncodingISOLatin1 = CFStringBuiltInEncodings.isoLatin1.rawValue |
| internal let kCFStringEncodingNextStepLatin = CFStringBuiltInEncodings.nextStepLatin.rawValue |
| internal let kCFStringEncodingASCII = CFStringBuiltInEncodings.ASCII.rawValue |
| internal let kCFStringEncodingUnicode = CFStringBuiltInEncodings.unicode.rawValue |
| internal let kCFStringEncodingUTF8 = CFStringBuiltInEncodings.UTF8.rawValue |
| internal let kCFStringEncodingNonLossyASCII = CFStringBuiltInEncodings.nonLossyASCII.rawValue |
| internal let kCFStringEncodingUTF16 = CFStringBuiltInEncodings.UTF16.rawValue |
| internal let kCFStringEncodingUTF16BE = CFStringBuiltInEncodings.UTF16BE.rawValue |
| internal let kCFStringEncodingUTF16LE = CFStringBuiltInEncodings.UTF16LE.rawValue |
| internal let kCFStringEncodingUTF32 = CFStringBuiltInEncodings.UTF32.rawValue |
| internal let kCFStringEncodingUTF32BE = CFStringBuiltInEncodings.UTF32BE.rawValue |
| internal let kCFStringEncodingUTF32LE = CFStringBuiltInEncodings.UTF32LE.rawValue |
| #endif |
| |
| |
| class TestNSString : XCTestCase { |
| |
| static var allTests: [(String, (TestNSString) -> () throws -> Void)] { |
| return [ |
| ("test_boolValue", test_boolValue ), |
| ("test_BridgeConstruction", test_BridgeConstruction ), |
| ("test_integerValue", test_integerValue ), |
| ("test_intValue", test_intValue ), |
| ("test_isEqualToStringWithSwiftString", test_isEqualToStringWithSwiftString ), |
| ("test_isEqualToObjectWithNSString", test_isEqualToObjectWithNSString ), |
| ("test_isNotEqualToObjectWithNSNumber", test_isNotEqualToObjectWithNSNumber ), |
| ("test_FromASCIIData", test_FromASCIIData ), |
| ("test_FromUTF8Data", test_FromUTF8Data ), |
| // Swift3 updates broke the expectations of this test. disabling for now |
| // ("test_FromMalformedUTF8Data", test_FromMalformedUTF8Data ), |
| ("test_FromASCIINSData", test_FromASCIINSData ), |
| ("test_FromUTF8NSData", test_FromUTF8NSData ), |
| // Swift3 updates broke the expectations of this test. disabling for now |
| // ("test_FromMalformedUTF8NSData", test_FromMalformedUTF8NSData ), |
| ("test_FromNullTerminatedCStringInASCII", test_FromNullTerminatedCStringInASCII ), |
| ("test_FromNullTerminatedCStringInUTF8", test_FromNullTerminatedCStringInUTF8 ), |
| // Swift3 updates broke the expectations of this test. disabling for now |
| // ("test_FromMalformedNullTerminatedCStringInUTF8", test_FromMalformedNullTerminatedCStringInUTF8 ), |
| ("test_uppercaseString", test_uppercaseString ), |
| ("test_lowercaseString", test_lowercaseString ), |
| ("test_capitalizedString", test_capitalizedString ), |
| ("test_longLongValue", test_longLongValue ), |
| ("test_rangeOfCharacterFromSet", test_rangeOfCharacterFromSet ), |
| ("test_CFStringCreateMutableCopy", test_CFStringCreateMutableCopy), |
| ("test_FromContentsOfURL",test_FromContentsOfURL), |
| ("test_FromContentOfFile",test_FromContentOfFile), |
| ("test_swiftStringUTF16", test_swiftStringUTF16), |
| // This test takes forever on build servers; it has been seen up to 1852.084 seconds |
| // ("test_completePathIntoString", test_completePathIntoString), |
| ("test_stringByTrimmingCharactersInSet", test_stringByTrimmingCharactersInSet), |
| ("test_initializeWithFormat", test_initializeWithFormat), |
| ("test_initializeWithFormat2", test_initializeWithFormat2), |
| ("test_initializeWithFormat3", test_initializeWithFormat3), |
| ("test_deletingLastPathComponent", test_deletingLastPathComponent), |
| ("test_getCString_simple", test_getCString_simple), |
| ("test_getCString_nonASCII_withASCIIAccessor", test_getCString_nonASCII_withASCIIAccessor), |
| ("test_NSHomeDirectoryForUser", test_NSHomeDirectoryForUser), |
| ("test_resolvingSymlinksInPath", test_resolvingSymlinksInPath), |
| ("test_expandingTildeInPath", test_expandingTildeInPath), |
| ("test_standardizingPath", test_standardizingPath), |
| ("test_addingPercentEncoding", test_addingPercentEncoding), |
| ("test_removingPercentEncodingInLatin", test_removingPercentEncodingInLatin), |
| ("test_removingPercentEncodingInNonLatin", test_removingPercentEncodingInNonLatin), |
| ("test_removingPersentEncodingWithoutEncoding", test_removingPersentEncodingWithoutEncoding), |
| ("test_addingPercentEncodingAndBack", test_addingPercentEncodingAndBack), |
| ("test_stringByAppendingPathExtension", test_stringByAppendingPathExtension), |
| ("test_deletingPathExtension", test_deletingPathExtension), |
| ("test_ExternalRepresentation", test_ExternalRepresentation), |
| ("test_mutableStringConstructor", test_mutableStringConstructor), |
| ("test_PrefixSuffix", test_PrefixSuffix), |
| ("test_utf16StringRangeCount", test_StringUTF16ViewIndexStrideableRange), |
| ("test_reflection", { _ in test_reflection }), |
| ("test_replacingOccurrences", test_replacingOccurrences), |
| ("test_getLineStart", test_getLineStart), |
| ] |
| } |
| |
| func test_boolValue() { |
| let trueStrings: [NSString] = ["t", "true", "TRUE", "tRuE", "yes", "YES", "1", "+000009"] |
| for string in trueStrings { |
| XCTAssert(string.boolValue) |
| } |
| let falseStrings: [NSString] = ["false", "FALSE", "fAlSe", "no", "NO", "0", "<true>", "_true", "-00000"] |
| for string in falseStrings { |
| XCTAssertFalse(string.boolValue) |
| } |
| } |
| |
| func test_BridgeConstruction() { |
| let literalConversion: NSString = "literal" |
| XCTAssertEqual(literalConversion.length, 7) |
| |
| let nonLiteralConversion = NSString(string: "test\(self)") |
| XCTAssertTrue(nonLiteralConversion.length > 4) |
| |
| let nonLiteral2 = NSString(string: String(4)) |
| let t = nonLiteral2.character(at: 0) |
| XCTAssertTrue(t == 52) |
| |
| let externalString: NSString = NSString(string: String.localizedName(of: String.defaultCStringEncoding)) |
| XCTAssertTrue(externalString.length >= 4) |
| |
| let cluster: NSString = "✌🏾" |
| XCTAssertEqual(cluster.length, 3) |
| } |
| |
| func test_integerValue() { |
| let string1: NSString = "123" |
| XCTAssertEqual(string1.integerValue, 123) |
| |
| let string2: NSString = "123a" |
| XCTAssertEqual(string2.integerValue, 123) |
| |
| let string3: NSString = "-123a" |
| XCTAssertEqual(string3.integerValue, -123) |
| |
| let string4: NSString = "a123" |
| XCTAssertEqual(string4.integerValue, 0) |
| |
| let string5: NSString = "+123" |
| XCTAssertEqual(string5.integerValue, 123) |
| |
| let string6: NSString = "++123" |
| XCTAssertEqual(string6.integerValue, 0) |
| |
| let string7: NSString = "-123" |
| XCTAssertEqual(string7.integerValue, -123) |
| |
| let string8: NSString = "--123" |
| XCTAssertEqual(string8.integerValue, 0) |
| |
| let string9: NSString = "999999999999999999999999999999" |
| XCTAssertEqual(string9.integerValue, Int.max) |
| |
| let string10: NSString = "-999999999999999999999999999999" |
| XCTAssertEqual(string10.integerValue, Int.min) |
| } |
| |
| func test_intValue() { |
| let string1: NSString = "123" |
| XCTAssertEqual(string1.intValue, 123) |
| |
| let string2: NSString = "123a" |
| XCTAssertEqual(string2.intValue, 123) |
| |
| let string3: NSString = "-123a" |
| XCTAssertEqual(string3.intValue, -123) |
| |
| let string4: NSString = "a123" |
| XCTAssertEqual(string4.intValue, 0) |
| |
| let string5: NSString = "+123" |
| XCTAssertEqual(string5.intValue, 123) |
| |
| let string6: NSString = "++123" |
| XCTAssertEqual(string6.intValue, 0) |
| |
| let string7: NSString = "-123" |
| XCTAssertEqual(string7.intValue, -123) |
| |
| let string8: NSString = "--123" |
| XCTAssertEqual(string8.intValue, 0) |
| |
| let string9: NSString = "999999999999999999999999999999" |
| XCTAssertEqual(string9.intValue, Int32.max) |
| |
| let string10: NSString = "-999999999999999999999999999999" |
| XCTAssertEqual(string10.intValue, Int32.min) |
| } |
| |
| func test_isEqualToStringWithSwiftString() { |
| let string: NSString = "literal" |
| let swiftString = "literal" |
| XCTAssertTrue(string.isEqual(to: swiftString)) |
| } |
| |
| func test_isEqualToObjectWithNSString() { |
| let string1: NSString = "literal" |
| let string2: NSString = "literal" |
| XCTAssertTrue(string1.isEqual(string2)) |
| } |
| |
| func test_isNotEqualToObjectWithNSNumber() { |
| let string: NSString = "5" |
| let number: NSNumber = 5 |
| XCTAssertFalse(string.isEqual(number)) |
| } |
| |
| internal let mockASCIIStringBytes: [UInt8] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x53, 0x77, 0x69, 0x66, 0x74, 0x21] |
| internal let mockASCIIString = "Hello Swift!" |
| internal let mockUTF8StringBytes: [UInt8] = [0x49, 0x20, 0xE2, 0x9D, 0xA4, 0xEF, 0xB8, 0x8F, 0x20, 0x53, 0x77, 0x69, 0x66, 0x74] |
| internal let mockUTF8String = "I ❤️ Swift" |
| internal let mockMalformedUTF8StringBytes: [UInt8] = [0xFF] |
| |
| func test_FromASCIIData() { |
| let bytes = mockASCIIStringBytes |
| let string = NSString(bytes: bytes, length: bytes.count, encoding: String.Encoding.ascii.rawValue) |
| XCTAssertNotNil(string) |
| XCTAssertTrue(string?.isEqual(to: mockASCIIString) ?? false) |
| } |
| |
| func test_FromUTF8Data() { |
| let bytes = mockUTF8StringBytes |
| let string = NSString(bytes: bytes, length: bytes.count, encoding: String.Encoding.utf8.rawValue) |
| XCTAssertNotNil(string) |
| XCTAssertTrue(string?.isEqual(to: mockUTF8String) ?? false) |
| } |
| |
| func test_FromMalformedUTF8Data() { |
| let bytes = mockMalformedUTF8StringBytes |
| let string = NSString(bytes: bytes, length: bytes.count, encoding: String.Encoding.utf8.rawValue) |
| XCTAssertNil(string) |
| } |
| |
| func test_FromASCIINSData() { |
| let bytes = mockASCIIStringBytes |
| let data = Data(bytes: bytes, count: bytes.count) |
| let string = NSString(data: data, encoding: String.Encoding.ascii.rawValue) |
| XCTAssertNotNil(string) |
| XCTAssertTrue(string?.isEqual(to: mockASCIIString) ?? false) |
| } |
| |
| func test_FromUTF8NSData() { |
| let bytes = mockUTF8StringBytes |
| let data = Data(bytes: bytes, count: bytes.count) |
| let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue) |
| XCTAssertNotNil(string) |
| XCTAssertTrue(string?.isEqual(to: mockUTF8String) ?? false) |
| } |
| |
| func test_FromMalformedUTF8NSData() { |
| let bytes = mockMalformedUTF8StringBytes |
| let data = Data(bytes: bytes, count: bytes.count) |
| let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue) |
| XCTAssertNil(string) |
| } |
| |
| func test_FromNullTerminatedCStringInASCII() { |
| let bytes = mockASCIIStringBytes + [0x00] |
| let string = NSString(CString: bytes.map { Int8(bitPattern: $0) }, encoding: String.Encoding.ascii.rawValue) |
| XCTAssertNotNil(string) |
| XCTAssertTrue(string?.isEqual(to: mockASCIIString) ?? false) |
| } |
| |
| func test_FromNullTerminatedCStringInUTF8() { |
| let bytes = mockUTF8StringBytes + [0x00] |
| let string = NSString(CString: bytes.map { Int8(bitPattern: $0) }, encoding: String.Encoding.utf8.rawValue) |
| XCTAssertNotNil(string) |
| XCTAssertTrue(string?.isEqual(to: mockUTF8String) ?? false) |
| } |
| |
| func test_FromMalformedNullTerminatedCStringInUTF8() { |
| let bytes = mockMalformedUTF8StringBytes + [0x00] |
| let string = NSString(CString: bytes.map { Int8(bitPattern: $0) }, encoding: String.Encoding.utf8.rawValue) |
| XCTAssertNil(string) |
| } |
| |
| func test_FromContentsOfURL() { |
| guard let testFileURL = testBundle().url(forResource: "NSStringTestData", withExtension: "txt") else { |
| XCTFail("URL for NSStringTestData.txt is nil") |
| return |
| } |
| |
| do { |
| let string = try NSString(contentsOf: testFileURL, encoding: String.Encoding.utf8.rawValue) |
| XCTAssertEqual(string, "swift-corelibs-foundation") |
| } catch { |
| XCTFail("Unable to init NSString from contentsOf:encoding:") |
| } |
| do { |
| let string = try NSString(contentsOf: testFileURL, encoding: String.Encoding.utf16.rawValue) |
| XCTAssertNotEqual(string, "swift-corelibs-foundation", "Wrong result when reading UTF-8 file with UTF-16 encoding in contentsOf:encoding") |
| } catch { |
| XCTFail("Unable to init NSString from contentsOf:encoding:") |
| } |
| } |
| |
| func test_FromContentOfFile() { |
| let testFilePath = testBundle().path(forResource: "NSStringTestData", ofType: "txt") |
| XCTAssertNotNil(testFilePath) |
| |
| do { |
| let str = try NSString(contentsOfFile: testFilePath!, encoding: String.Encoding.utf8.rawValue) |
| XCTAssertEqual(str, "swift-corelibs-foundation") |
| } catch { |
| XCTFail("Unable to init NSString from contentsOfFile:encoding:") |
| } |
| } |
| |
| func test_uppercaseString() { |
| XCTAssertEqual(NSString(stringLiteral: "abcd").uppercased, "ABCD") |
| XCTAssertEqual(NSString(stringLiteral: "abcd").uppercased, "ABCD") // full-width |
| XCTAssertEqual(NSString(stringLiteral: "абВГ").uppercased, "АБВГ") |
| XCTAssertEqual(NSString(stringLiteral: "たちつてと").uppercased, "たちつてと") |
| |
| // Special casing (see swift/validation-tests/stdlib/NSStringAPI.swift) |
| XCTAssertEqual(NSString(stringLiteral: "\u{0069}").uppercased(with: Locale(identifier: "en")), "\u{0049}") |
| // Currently fails; likely there are locale loading issues that are preventing this from functioning correctly |
| // XCTAssertEqual(NSString(stringLiteral: "\u{0069}").uppercased(with: NSLocale(localeIdentifier: "tr")), "\u{0130}") |
| XCTAssertEqual(NSString(stringLiteral: "\u{00df}").uppercased, "\u{0053}\u{0053}") |
| XCTAssertEqual(NSString(stringLiteral: "\u{fb01}").uppercased, "\u{0046}\u{0049}") |
| } |
| |
| func test_lowercaseString() { |
| XCTAssertEqual(NSString(stringLiteral: "abCD").lowercased, "abcd") |
| XCTAssertEqual(NSString(stringLiteral: "ABCD").lowercased, "abcd") // full-width |
| XCTAssertEqual(NSString(stringLiteral: "aБВГ").lowercased, "aбвг") |
| XCTAssertEqual(NSString(stringLiteral: "たちつてと").lowercased, "たちつてと") |
| |
| // Special casing (see swift/validation-tests/stdlib/NSStringAPI.swift) |
| XCTAssertEqual(NSString(stringLiteral: "\u{0130}").lowercased(with: Locale(identifier: "en")), "\u{0069}\u{0307}") |
| // Currently fails; likely there are locale loading issues that are preventing this from functioning correctly |
| // XCTAssertEqual(NSString(stringLiteral: "\u{0130}").lowercased(with: NSLocale(localeIdentifier: "tr")), "\u{0069}") |
| XCTAssertEqual(NSString(stringLiteral: "\u{0049}\u{0307}").lowercased(with: Locale(identifier: "en")), "\u{0069}\u{0307}") |
| // Currently fails; likely there are locale loading issues that are preventing this from functioning correctly |
| // XCTAssertEqual(NSString(stringLiteral: "\u{0049}\u{0307}").lowercaseStringWithLocale(NSLocale(localeIdentifier: "tr")), "\u{0069}") |
| } |
| |
| func test_capitalizedString() { |
| XCTAssertEqual(NSString(stringLiteral: "foo Foo fOO FOO").capitalized, "Foo Foo Foo Foo") |
| XCTAssertEqual(NSString(stringLiteral: "жжж").capitalized, "Жжж") |
| } |
| |
| func test_longLongValue() { |
| let string1: NSString = "123" |
| XCTAssertEqual(string1.longLongValue, 123) |
| |
| let string2: NSString = "123a" |
| XCTAssertEqual(string2.longLongValue, 123) |
| |
| let string3: NSString = "-123a" |
| XCTAssertEqual(string3.longLongValue, -123) |
| |
| let string4: NSString = "a123" |
| XCTAssertEqual(string4.longLongValue, 0) |
| |
| let string5: NSString = "+123" |
| XCTAssertEqual(string5.longLongValue, 123) |
| |
| let string6: NSString = "++123" |
| XCTAssertEqual(string6.longLongValue, 0) |
| |
| let string7: NSString = "-123" |
| XCTAssertEqual(string7.longLongValue, -123) |
| |
| let string8: NSString = "--123" |
| XCTAssertEqual(string8.longLongValue, 0) |
| |
| let string9: NSString = "999999999999999999999999999999" |
| XCTAssertEqual(string9.longLongValue, Int64.max) |
| |
| let string10: NSString = "-999999999999999999999999999999" |
| XCTAssertEqual(string10.longLongValue, Int64.min) |
| } |
| |
| func test_rangeOfCharacterFromSet() { |
| let string: NSString = "0Az" |
| let letters = CharacterSet.letters |
| let decimalDigits = CharacterSet.decimalDigits |
| XCTAssertEqual(string.rangeOfCharacter(from: letters).location, 1) |
| XCTAssertEqual(string.rangeOfCharacter(from: decimalDigits).location, 0) |
| XCTAssertEqual(string.rangeOfCharacter(from: letters, options: .backwards).location, 2) |
| XCTAssertEqual(string.rangeOfCharacter(from: letters, options: [], range: NSMakeRange(2, 1)).location, 2) |
| } |
| |
| func test_CFStringCreateMutableCopy() { |
| let nsstring: NSString = "абВГ" |
| let mCopy = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, unsafeBitCast(nsstring, to: CFString.self)) |
| let str = unsafeBitCast(mCopy, to: NSString.self) |
| XCTAssertEqual(nsstring, str) |
| } |
| |
| // This test verifies that CFStringGetBytes with a UTF16 encoding works on an NSString backed by a Swift string |
| func test_swiftStringUTF16() { |
| #if os(OSX) || os(iOS) |
| let kCFStringEncodingUTF16 = CFStringBuiltInEncodings.UTF16.rawValue |
| #endif |
| |
| let testString = "hello world" |
| let string = NSString(string: testString) |
| let cfString = unsafeBitCast(string, to: CFString.self) |
| |
| // Get the bytes as UTF16 |
| let reservedLength = 50 |
| var buf : [UInt8] = [] |
| buf.reserveCapacity(reservedLength) |
| var usedLen : CFIndex = 0 |
| let _ = buf.withUnsafeMutableBufferPointer { p in |
| CFStringGetBytes(cfString, CFRangeMake(0, CFStringGetLength(cfString)), CFStringEncoding(kCFStringEncodingUTF16), 0, false, p.baseAddress, reservedLength, &usedLen) |
| } |
| |
| // Make a new string out of it |
| let newCFString = CFStringCreateWithBytes(nil, buf, usedLen, CFStringEncoding(kCFStringEncodingUTF16), false) |
| let newString = unsafeBitCast(newCFString, to: NSString.self) |
| |
| XCTAssertTrue(newString.isEqual(to: testString)) |
| } |
| |
| func test_completePathIntoString() { |
| let fileNames = [ |
| NSTemporaryDirectory() + "Test_completePathIntoString_01", |
| NSTemporaryDirectory() + "test_completePathIntoString_02", |
| NSTemporaryDirectory() + "test_completePathIntoString_01.txt", |
| NSTemporaryDirectory() + "test_completePathIntoString_01.dat", |
| NSTemporaryDirectory() + "test_completePathIntoString_03.DAT" |
| ] |
| |
| guard ensureFiles(fileNames) else { |
| XCTAssert(false, "Could not create temp files for testing.") |
| return |
| } |
| |
| let tmpPath = { (path: String) -> String in |
| return NSTemporaryDirectory() + "\(path)" |
| } |
| |
| do { |
| let path: String = tmpPath("") |
| var outName: String = "" |
| var matches: [String] = [] |
| _ = path.completePath(into: &outName, caseSensitive: false, matchesInto: &matches, filterTypes: nil) |
| _ = try FileManager.default.contentsOfDirectory(at: URL(string: path)!, includingPropertiesForKeys: nil, options: []) |
| XCTAssert(outName == "/", "If NSString is valid path to directory which has '/' suffix then outName is '/'.") |
| // This assert fails on CI; https://bugs.swift.org/browse/SR-389 |
| // XCTAssert(matches.count == content.count && matches.count == count, "If NSString is valid path to directory then matches contain all content of directory. expected \(content) but got \(matches)") |
| } catch { |
| XCTAssert(false, "Could not finish test due to error") |
| } |
| |
| do { |
| let path: String = "/tmp" |
| var outName: String = "" |
| var matches: [String] = [] |
| _ = path.completePath(into: &outName, caseSensitive: false, matchesInto: &matches, filterTypes: nil) |
| let urlToTmp = URL(fileURLWithPath: "/private/tmp/").standardized |
| _ = try FileManager.default.contentsOfDirectory(at: urlToTmp, includingPropertiesForKeys: nil, options: []) |
| XCTAssert(outName == "/tmp/", "If path could be completed to existing directory then outName is a string itself plus '/'.") |
| // This assert fails on CI; https://bugs.swift.org/browse/SR-389 |
| // XCTAssert(matches.count == content.count && matches.count == count, "If NSString is valid path to directory then matches contain all content of directory. expected \(content) but got \(matches)") |
| } catch { |
| XCTAssert(false, "Could not finish test due to error") |
| } |
| |
| let fileNames2 = [ |
| NSTemporaryDirectory() + "ABC/", |
| NSTemporaryDirectory() + "ABCD/", |
| NSTemporaryDirectory() + "abcde" |
| ] |
| |
| guard ensureFiles(fileNames2) else { |
| XCTAssert(false, "Could not create temp files for testing.") |
| return |
| } |
| |
| do { |
| let path: String = tmpPath("ABC") |
| var outName: String = "" |
| var matches: [String] = [] |
| let count = path.completePath(into: &outName, caseSensitive: false, matchesInto: &matches, filterTypes: nil) |
| XCTAssert(stringsAreCaseInsensitivelyEqual(outName, path), "If NSString is valid path to directory then outName is string itself.") |
| XCTAssert(matches.count == count && count == fileNames2.count, "") |
| } |
| |
| do { |
| let path: String = tmpPath("Test_completePathIntoString_01") |
| var outName: String = "" |
| var matches: [String] = [] |
| let count = path.completePath(into: &outName, caseSensitive: true, matchesInto: &matches, filterTypes: nil) |
| XCTAssert(outName == path, "If NSString is valid path to file and search is case sensitive then outName is string itself.") |
| XCTAssert(matches.count == 1 && count == 1 && stringsAreCaseInsensitivelyEqual(matches[0], path), "If NSString is valid path to file and search is case sensitive then matches contain that file path only") |
| } |
| |
| do { |
| let path: String = tmpPath("Test_completePathIntoString_01") |
| var outName: String = "" |
| var matches: [String] = [] |
| let count = path.completePath(into: &outName, caseSensitive: false, matchesInto: &matches, filterTypes: nil) |
| XCTAssert(stringsAreCaseInsensitivelyEqual(outName, path), "If NSString is valid path to file and search is case insensitive then outName is string equal to self.") |
| XCTAssert(matches.count == 3 && count == 3, "Matches contain all files with similar name.") |
| } |
| |
| do { |
| let path = tmpPath(NSUUID().uuidString) |
| var outName: String = "" |
| var matches: [String] = [] |
| let count = path.completePath(into: &outName, caseSensitive: false, matchesInto: &matches, filterTypes: nil) |
| XCTAssert(outName == "", "If no matches found then outName is nil.") |
| XCTAssert(matches.count == 0 && count == 0, "If no matches found then return 0 and matches is empty.") |
| } |
| |
| do { |
| let path: String = "" |
| var outName: String = "" |
| var matches: [String] = [] |
| let count = path.completePath(into: &outName, caseSensitive: false, matchesInto: &matches, filterTypes: nil) |
| XCTAssert(outName == "", "If no matches found then outName is nil.") |
| XCTAssert(matches.count == 0 && count == 0, "If no matches found then return 0 and matches is empty.") |
| } |
| |
| do { |
| let path: String = tmpPath("test_c") |
| var outName: String = "" |
| var matches: [String] = [] |
| // case insensetive |
| let count = path.completePath(into: &outName, caseSensitive: false, matchesInto: &matches, filterTypes: nil) |
| XCTAssert(stringsAreCaseInsensitivelyEqual(outName, tmpPath("Test_completePathIntoString_0")), "If there are matches then outName should be longest common prefix of all matches.") |
| XCTAssert(matches.count == fileNames.count && count == fileNames.count, "If there are matches then matches array contains them.") |
| } |
| |
| do { |
| let path: String = tmpPath("test_c") |
| var outName: String = "" |
| var matches: [String] = [] |
| // case sensetive |
| let count = path.completePath(into: &outName, caseSensitive: true, matchesInto: &matches, filterTypes: nil) |
| XCTAssert(outName == tmpPath("test_completePathIntoString_0"), "If there are matches then outName should be longest common prefix of all matches.") |
| XCTAssert(matches.count == 4 && count == 4, "Supports case sensetive search") |
| } |
| |
| do { |
| let path: String = tmpPath("test_c") |
| var outName: String = "" |
| var matches: [String] = [] |
| // case sensetive |
| let count = path.completePath(into: &outName, caseSensitive: true, matchesInto: &matches, filterTypes: ["DAT"]) |
| XCTAssert(outName == tmpPath("test_completePathIntoString_03.DAT"), "If there are matches then outName should be longest common prefix of all matches.") |
| XCTAssert(matches.count == 1 && count == 1, "Supports case sensetive search by extensions") |
| } |
| |
| do { |
| let path: String = tmpPath("test_c") |
| var outName: String = "" |
| var matches: [String] = [] |
| // type by filter |
| let count = path.completePath(into: &outName, caseSensitive: false, matchesInto: &matches, filterTypes: ["txt", "dat"]) |
| XCTAssert(stringsAreCaseInsensitivelyEqual(outName, tmpPath("test_completePathIntoString_0")), "If there are matches then outName should be longest common prefix of all matches.") |
| XCTAssert(matches.count == 3 && count == 3, "Supports filtration by type") |
| } |
| |
| do { |
| // will be resolved against current working directory that is directory there results of build process are stored |
| let path: String = "TestFoundation" |
| var outName: String = "" |
| var matches: [String] = [] |
| let count = path.completePath(into: &outName, caseSensitive: false, matchesInto: &matches, filterTypes: nil) |
| // Build directory at least contains executable itself and *.swiftmodule directory |
| XCTAssert(matches.count == count && count >= 2, "Supports relative paths.") |
| XCTAssert(startWith(path, strings: matches), "For relative paths matches are relative too.") |
| } |
| |
| // Next check has no sense on Linux due to case sensitive file system. |
| #if os(OSX) |
| guard ensureFiles([NSTemporaryDirectory() + "ABC/temp.txt"]) else { |
| XCTAssert(false, "Could not create temp files for testing.") |
| return |
| } |
| |
| do { |
| let path: String = tmpPath("aBc/t") |
| var outName: String = "" |
| var matches: [String] = [] |
| // type by filter |
| let count = path.completePath(into: &outName, caseSensitive: true, matchesInto: &matches, filterTypes: ["txt", "dat"]) |
| XCTAssert(outName == tmpPath("aBc/temp.txt"), "outName starts with receiver.") |
| XCTAssert(matches.count >= 1 && count >= 1, "There are matches") |
| } |
| #endif |
| } |
| |
| private func startWith(_ prefix: String, strings: [String]) -> Bool { |
| for item in strings { |
| guard item.hasPrefix(prefix) else { |
| return false |
| } |
| } |
| |
| return true |
| } |
| |
| private func stringsAreCaseInsensitivelyEqual(_ lhs: String, _ rhs: String) -> Bool { |
| return lhs.compare(rhs, options: .caseInsensitive) == .orderedSame |
| } |
| |
| func test_stringByTrimmingCharactersInSet() { |
| let characterSet = CharacterSet.whitespaces |
| let string: NSString = " abc " |
| XCTAssertEqual(string.trimmingCharacters(in: characterSet), "abc") |
| |
| let emojiString: NSString = " \u{1F62C} " |
| XCTAssertEqual(emojiString.trimmingCharacters(in: characterSet), "\u{1F62C}") |
| } |
| |
| func test_initializeWithFormat() { |
| let argument: [CVarArg] = [42, 42.0] |
| withVaList(argument) { |
| pointer in |
| let string = NSString(format: "Value is %d (%.1f)", arguments: pointer) |
| XCTAssertEqual(string, "Value is 42 (42.0)") |
| } |
| } |
| |
| func test_initializeWithFormat2() { |
| let argument: UInt8 = 75 |
| let string = NSString(format: "%02X", argument) |
| XCTAssertEqual(string, "4B") |
| } |
| |
| func test_initializeWithFormat3() { |
| let argument: [CVarArg] = [1000, 42.0] |
| |
| withVaList(argument) { |
| pointer in |
| let string = NSString(format: "Default value is %d (%.1f)", locale: nil, arguments: pointer) |
| XCTAssertEqual(string, "Default value is 1000 (42.0)") |
| } |
| |
| #if false // these two tests expose bugs in icu4c's localization on some linux builds (disable until we can get a uniform fix for this) |
| withVaList(argument) { |
| pointer in |
| let string = NSString(format: "en_GB value is %d (%.1f)", locale: Locale.init(localeIdentifier: "en_GB"), arguments: pointer) |
| XCTAssertEqual(string, "en_GB value is 1,000 (42.0)") |
| } |
| |
| withVaList(argument) { |
| pointer in |
| let string = NSString(format: "de_DE value is %d (%.1f)", locale: Locale.init(localeIdentifier: "de_DE"), arguments: pointer) |
| XCTAssertEqual(string, "de_DE value is 1.000 (42,0)") |
| } |
| #endif |
| |
| withVaList(argument) { |
| pointer in |
| let loc: NSDictionary = ["NSDecimalSeparator" as NSString : "&" as NSString] |
| let string = NSString(format: "NSDictionary value is %d (%.1f)", locale: loc, arguments: pointer) |
| XCTAssertEqual(string, "NSDictionary value is 1000 (42&0)") |
| } |
| } |
| |
| func test_deletingLastPathComponent() { |
| do { |
| let path: NSString = "/tmp/scratch.tiff" |
| let result = path.deletingLastPathComponent |
| XCTAssertEqual(result, "/tmp") |
| } |
| |
| do { |
| let path: NSString = "/tmp/lock/" |
| let result = path.deletingLastPathComponent |
| XCTAssertEqual(result, "/tmp") |
| } |
| |
| do { |
| let path: NSString = "/tmp/" |
| let result = path.deletingLastPathComponent |
| XCTAssertEqual(result, "/") |
| } |
| |
| do { |
| let path: NSString = "/tmp" |
| let result = path.deletingLastPathComponent |
| XCTAssertEqual(result, "/") |
| } |
| |
| do { |
| let path: NSString = "/" |
| let result = path.deletingLastPathComponent |
| XCTAssertEqual(result, "/") |
| } |
| |
| do { |
| let path: NSString = "scratch.tiff" |
| let result = path.deletingLastPathComponent |
| XCTAssertEqual(result, "") |
| } |
| |
| do { |
| let path: NSString = "foo/bar" |
| let result = path.deletingLastPathComponent |
| XCTAssertEqual(result, "foo", "Relative path stays relative.") |
| } |
| } |
| |
| func test_resolvingSymlinksInPath() { |
| do { |
| let path: NSString = "foo/bar" |
| let result = path.resolvingSymlinksInPath |
| XCTAssertEqual(result, "foo/bar", "For relative paths, symbolic links that can’t be resolved are left unresolved in the returned string.") |
| } |
| |
| do { |
| let path: NSString = "/tmp/.." |
| let result = path.resolvingSymlinksInPath |
| |
| #if os(OSX) |
| let expected = "/private" |
| #else |
| let expected = "/" |
| #endif |
| |
| XCTAssertEqual(result, expected, "For absolute paths, all symbolic links are guaranteed to be removed.") |
| } |
| |
| do { |
| let path: NSString = "tmp/.." |
| let result = path.resolvingSymlinksInPath |
| XCTAssertEqual(result, "tmp/..", "Parent links could be resolved for absolute paths only.") |
| } |
| |
| do { |
| let path: NSString = "/tmp/" |
| let result = path.resolvingSymlinksInPath |
| XCTAssertEqual(result, "/tmp", "Result doesn't contain trailing slash.") |
| } |
| |
| do { |
| let path: NSString = "http://google.com/search/.." |
| let result = path.resolvingSymlinksInPath |
| XCTAssertEqual(result, "http:/google.com/search/..", "resolvingSymlinksInPath treats receiver as file path always") |
| } |
| |
| do { |
| let path: NSString = "file:///tmp/.." |
| let result = path.resolvingSymlinksInPath |
| XCTAssertEqual(result, "file:/tmp/..", "resolvingSymlinksInPath treats receiver as file path always") |
| } |
| } |
| |
| func test_getCString_simple() { |
| let str: NSString = "foo" |
| var chars = [Int8](repeating:0xF, count:4) |
| let count = chars.count |
| let expected: [Int8] = [102, 111, 111, 0] |
| var res: Bool = false |
| chars.withUnsafeMutableBufferPointer() { |
| let ptr = $0.baseAddress! |
| res = str.getCString(ptr, maxLength: count, encoding: String.Encoding.ascii.rawValue) |
| } |
| XCTAssertTrue(res, "getCString should work on simple strings with ascii string encoding") |
| XCTAssertEqual(chars, expected, "getCString on \(str) should have resulted in \(expected) but got \(chars)") |
| } |
| |
| func test_getCString_nonASCII_withASCIIAccessor() { |
| let str: NSString = "ƒoo" |
| var chars = [Int8](repeating:0xF, count:5) |
| let expected: [Int8] = [-58, -110, 111, 111, 0] |
| let count = chars.count |
| var res: Bool = false |
| chars.withUnsafeMutableBufferPointer() { |
| let ptr = $0.baseAddress! |
| res = str.getCString(ptr, maxLength: count, encoding: String.Encoding.ascii.rawValue) |
| } |
| XCTAssertFalse(res, "getCString should not work on non ascii strings accessing as ascii string encoding") |
| chars.withUnsafeMutableBufferPointer() { |
| let ptr = $0.baseAddress! |
| res = str.getCString(ptr, maxLength: count, encoding: String.Encoding.utf8.rawValue) |
| } |
| XCTAssertTrue(res, "getCString should work on UTF8 encoding") |
| XCTAssertEqual(chars, expected, "getCString on \(str) should have resulted in \(expected) but got \(chars)") |
| } |
| |
| func test_NSHomeDirectoryForUser() { |
| let homeDir = NSHomeDirectoryForUser(nil) |
| let userName = NSUserName() |
| let homeDir2 = NSHomeDirectoryForUser(userName) |
| let homeDir3 = NSHomeDirectory() |
| XCTAssert(homeDir != nil && homeDir == homeDir2 && homeDir == homeDir3, "Could get user' home directory") |
| } |
| |
| func test_expandingTildeInPath() { |
| do { |
| let path: NSString = "~" |
| let result = path.expandingTildeInPath |
| XCTAssert(result == NSHomeDirectory(), "Could resolve home directory for current user") |
| XCTAssertFalse(result.hasSuffix("/"), "Result have no trailing path separator") |
| } |
| |
| do { |
| let path: NSString = "~/" |
| let result = path.expandingTildeInPath |
| XCTAssert(result == NSHomeDirectory(), "Could resolve home directory for current user") |
| XCTAssertFalse(result.hasSuffix("/"), "Result have no trailing path separator") |
| } |
| |
| do { |
| let path = NSString(string: "~\(NSUserName())") |
| let result = path.expandingTildeInPath |
| XCTAssert(result == NSHomeDirectory(), "Could resolve home directory for specific user") |
| XCTAssertFalse(result.hasSuffix("/"), "Result have no trailing path separator") |
| } |
| |
| do { |
| let userName = NSUUID().uuidString |
| let path = NSString(string: "~\(userName)/") |
| let result = path.expandingTildeInPath |
| // next assert fails in VirtualBox because home directory for unknown user resolved to /var/run/vboxadd |
| XCTAssert(result == "~\(userName)", "Return copy of reciver if home directory could no be resolved.") |
| } |
| } |
| |
| func test_standardizingPath() { |
| |
| // tmp is special because it is symlinked to /private/tmp and this /private prefix should be dropped, |
| // so tmp is tmp. On Linux tmp is not symlinked so it would be the same. |
| do { |
| let path: NSString = "/.//tmp/ABC/.." |
| let result = path.standardizingPath |
| XCTAssertEqual(result, "/tmp", "standardizingPath removes extraneous path components and resolve symlinks.") |
| } |
| |
| do { |
| let path: NSString = "~" |
| let result = path.standardizingPath |
| let expected = NSHomeDirectory() |
| XCTAssertEqual(result, expected, "standardizingPath expanding initial tilde.") |
| } |
| |
| do { |
| let path: NSString = "~/foo/bar/" |
| let result = path.standardizingPath |
| let expected = NSHomeDirectory() + "/foo/bar" |
| XCTAssertEqual(result, expected, "standardizingPath expanding initial tilde.") |
| } |
| |
| // relative file paths depend on file path standardizing that is not yet implemented |
| do { |
| let path: NSString = "foo/bar" |
| let result = path.standardizingPath |
| XCTAssertEqual(NSString(string: result), path, "standardizingPath doesn't resolve relative paths") |
| } |
| |
| // tmp is symlinked on OS X only |
| #if os(OSX) |
| do { |
| let path: NSString = "/tmp/.." |
| let result = path.standardizingPath |
| XCTAssertEqual(result, "/private") |
| } |
| #endif |
| |
| do { |
| let path: NSString = "/tmp/ABC/.." |
| let result = path.standardizingPath |
| XCTAssertEqual(result, "/tmp", "parent links could be resolved for absolute paths") |
| } |
| |
| do { |
| let path: NSString = "tmp/ABC/.." |
| let result = path.standardizingPath |
| XCTAssertEqual(NSString(string: result), path, "parent links could not be resolved for relative paths") |
| } |
| } |
| |
| func test_addingPercentEncoding() { |
| let s1 = "a b".addingPercentEncoding(withAllowedCharacters: .alphanumerics) |
| XCTAssertEqual(s1, "a%20b") |
| |
| let s2 = "\u{0434}\u{043E}\u{043C}".addingPercentEncoding(withAllowedCharacters: .alphanumerics) |
| XCTAssertEqual(s2, "%D0%B4%D0%BE%D0%BC") |
| } |
| |
| func test_removingPercentEncodingInLatin() { |
| let s1 = "a%20b".removingPercentEncoding |
| XCTAssertEqual(s1, "a b") |
| let s2 = "a%1 b".removingPercentEncoding |
| XCTAssertNil(s2, "returns nil for a string with an invalid percent encoding") |
| } |
| |
| func test_removingPercentEncodingInNonLatin() { |
| let s1 = "\u{043C}\u{043E}\u{0439}%20\u{0434}\u{043E}\u{043C}".removingPercentEncoding |
| XCTAssertEqual(s1, "\u{043C}\u{043E}\u{0439} \u{0434}\u{043E}\u{043C}") |
| |
| let s2 = "%D0%B4%D0%BE%D0%BC".removingPercentEncoding |
| XCTAssertEqual(s2, "\u{0434}\u{043E}\u{043C}") |
| |
| let s3 = "\u{00E0}a%1 b".removingPercentEncoding |
| XCTAssertNil(s3, "returns nil for a string with an invalid percent encoding") |
| } |
| |
| func test_removingPersentEncodingWithoutEncoding() { |
| let cyrillicString = "\u{0434}\u{043E}\u{043C}" |
| let cyrillicEscapedString = cyrillicString.removingPercentEncoding |
| XCTAssertEqual(cyrillicString, cyrillicEscapedString) |
| |
| let chineseString = "\u{623F}\u{5B50}" |
| let chineseEscapedString = chineseString.removingPercentEncoding |
| XCTAssertEqual(chineseString, chineseEscapedString) |
| |
| let arabicString = "\u{0645}\u{0646}\u{0632}\u{0644}" |
| let arabicEscapedString = arabicString.removingPercentEncoding |
| XCTAssertEqual(arabicString, arabicEscapedString) |
| |
| let randomString = "\u{00E0}\u{00E6}" |
| let randomEscapedString = randomString.removingPercentEncoding |
| XCTAssertEqual(randomString, randomEscapedString) |
| |
| let latinString = "home" |
| let latinEscapedString = latinString.removingPercentEncoding |
| XCTAssertEqual(latinString, latinEscapedString) |
| } |
| |
| func test_addingPercentEncodingAndBack() { |
| let latingString = "a b" |
| let escapedLatingString = latingString.addingPercentEncoding(withAllowedCharacters: .alphanumerics) |
| let returnedLatingString = escapedLatingString?.removingPercentEncoding |
| XCTAssertEqual(returnedLatingString, latingString) |
| |
| let cyrillicString = "\u{0434}\u{043E}\u{043C}" |
| let escapedCyrillicString = cyrillicString.addingPercentEncoding(withAllowedCharacters: .alphanumerics) |
| let returnedCyrillicString = escapedCyrillicString?.removingPercentEncoding |
| XCTAssertEqual(returnedCyrillicString, cyrillicString) |
| } |
| |
| func test_stringByAppendingPathExtension() { |
| let values = [ |
| NSString(string: "/tmp/scratch.old") : "/tmp/scratch.old.tiff", |
| NSString(string: "/tmp/scratch.") : "/tmp/scratch..tiff", |
| NSString(string: "/tmp/") : "/tmp.tiff", |
| NSString(string: "/scratch") : "/scratch.tiff", |
| NSString(string: "/~scratch") : "/~scratch.tiff", |
| NSString(string: "scratch") : "scratch.tiff", |
| ] |
| for (fileName, expectedResult) in values { |
| let result = fileName.appendingPathExtension("tiff") |
| XCTAssertEqual(result, expectedResult, "expected \(expectedResult) for \(fileName) but got \(result as Optional)") |
| } |
| } |
| |
| func test_deletingPathExtension() { |
| let values : Dictionary = [ |
| NSString(string: "/tmp/scratch.tiff") : "/tmp/scratch", |
| NSString(string: "/tmp/") : "/tmp", |
| NSString(string: "scratch.bundle") : "scratch", |
| NSString(string: "scratch..tiff") : "scratch.", |
| NSString(string: ".tiff") : ".tiff", |
| NSString(string: "/") : "/", |
| ] |
| for (fileName, expectedResult) in values { |
| let result = fileName.deletingPathExtension |
| XCTAssertEqual(result, expectedResult, "expected \(expectedResult) for \(fileName) but got \(result)") |
| } |
| } |
| |
| func test_ExternalRepresentation() { |
| // Ensure NSString can be used to create an external data representation |
| |
| let UTF8Encoding = CFStringEncoding(kCFStringEncodingUTF8) |
| let UTF16Encoding = CFStringEncoding(kCFStringEncodingUTF16) |
| let ISOLatin1Encoding = CFStringEncoding(kCFStringEncodingISOLatin1) |
| |
| do { |
| let string = unsafeBitCast(NSString(string: "this is an external string that should be representable by data"), to: CFString.self) |
| let UTF8Data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, string, UTF8Encoding, 0) |
| let UTF8Length = CFDataGetLength(UTF8Data) |
| XCTAssertEqual(UTF8Length, 63, "NSString should successfully produce an external UTF8 representation with a length of 63 but got \(UTF8Length) bytes") |
| |
| let UTF16Data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, string, UTF16Encoding, 0) |
| let UTF16Length = CFDataGetLength(UTF16Data) |
| XCTAssertEqual(UTF16Length, 128, "NSString should successfully produce an external UTF16 representation with a length of 128 but got \(UTF16Length) bytes") |
| |
| let ISOLatin1Data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, string, ISOLatin1Encoding, 0) |
| let ISOLatin1Length = CFDataGetLength(ISOLatin1Data) |
| XCTAssertEqual(ISOLatin1Length, 63, "NSString should successfully produce an external ISOLatin1 representation with a length of 63 but got \(ISOLatin1Length) bytes") |
| } |
| |
| do { |
| let string = unsafeBitCast(NSString(string: "🐢 encoding all the way down. 🐢🐢🐢"), to: CFString.self) |
| let UTF8Data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, string, UTF8Encoding, 0) |
| let UTF8Length = CFDataGetLength(UTF8Data) |
| XCTAssertEqual(UTF8Length, 44, "NSString should successfully produce an external UTF8 representation with a length of 44 but got \(UTF8Length) bytes") |
| |
| let UTF16Data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, string, UTF16Encoding, 0) |
| let UTF16Length = CFDataGetLength(UTF16Data) |
| XCTAssertEqual(UTF16Length, 74, "NSString should successfully produce an external UTF16 representation with a length of 74 but got \(UTF16Length) bytes") |
| |
| let ISOLatin1Data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, string, ISOLatin1Encoding, 0) |
| XCTAssertNil(ISOLatin1Data) |
| } |
| } |
| |
| //[SR-1988] Ranges of String.UTF16View.Index have negative count |
| func test_StringUTF16ViewIndexStrideableRange(){ |
| let testStrings = ["", "\u{0000}", "a", "aa", "ab", "\u{007f}", "\u{0430}", "\u{0430}\u{0431}\u{0432}","\u{1f425}"] |
| |
| func checkStrideable<S : Strideable>( |
| instances: [S], |
| distances: [S.Stride], |
| distanceOracle: (Int, Int) -> S.Stride |
| ) { |
| for i in instances.indices { |
| let first = instances[i] |
| for j in instances.indices { |
| let second = instances[j] |
| XCTAssertTrue(distanceOracle(i, j) == first.distance(to: second)) |
| XCTAssertTrue(second == first.advanced(by: distanceOracle(i, j))) |
| } |
| } |
| } |
| testStrings.forEach{ |
| let utf16 = $0.utf16 |
| var indicies = Array(utf16.indices) |
| indicies.append(utf16.indices.endIndex) |
| checkStrideable(instances: indicies, |
| distances: Array(0..<utf16.count), |
| distanceOracle: {$1 - $0}) |
| } |
| } |
| |
| func test_mutableStringConstructor() { |
| let mutableString = NSMutableString(string: "Test") |
| XCTAssertEqual(mutableString, "Test") |
| } |
| |
| func test_getLineStart() { |
| // offset 012345 678901 |
| let twoLines = "line1\nline2\n" |
| var outStartIndex = twoLines.startIndex |
| var outEndIndex = twoLines.startIndex |
| var outContentsEndIndex = twoLines.startIndex |
| |
| twoLines.getLineStart(&outStartIndex, end: &outEndIndex, |
| contentsEnd: &outContentsEndIndex, |
| for: outEndIndex..<outEndIndex) |
| |
| XCTAssertEqual(outStartIndex, twoLines.startIndex) |
| XCTAssertEqual(outContentsEndIndex, twoLines.index(twoLines.startIndex, offsetBy: 5)) |
| XCTAssertEqual(outEndIndex, twoLines.index(twoLines.startIndex, offsetBy: 6)) |
| |
| twoLines.getLineStart(&outStartIndex, end: &outEndIndex, |
| contentsEnd: &outContentsEndIndex, |
| for: outEndIndex..<outEndIndex) |
| |
| XCTAssertEqual(outStartIndex, twoLines.index(twoLines.startIndex, offsetBy: 6)) |
| XCTAssertEqual(outContentsEndIndex, twoLines.index(twoLines.startIndex, offsetBy: 11)) |
| XCTAssertEqual(outEndIndex, twoLines.index(twoLines.startIndex, offsetBy: 12)) |
| } |
| } |
| |
| struct ComparisonTest { |
| let lhs: String |
| let rhs: String |
| let loc: UInt |
| let reason: String |
| |
| var xfail: Bool { |
| return !reason.isEmpty |
| } |
| |
| init( |
| _ lhs: String, _ rhs: String, |
| reason: String = "", line: UInt = #line |
| ) { |
| self.lhs = lhs |
| self.rhs = rhs |
| self.reason = reason |
| self.loc = line |
| } |
| } |
| |
| let comparisonTests = [ |
| ComparisonTest("", ""), |
| ComparisonTest("", "a"), |
| |
| // ASCII cases |
| ComparisonTest("t", "tt"), |
| ComparisonTest("t", "Tt"), |
| ComparisonTest("\u{0}", ""), |
| ComparisonTest("\u{0}", "\u{0}", |
| reason: "https://bugs.swift.org/browse/SR-332"), |
| ComparisonTest("\r\n", "t"), |
| ComparisonTest("\r\n", "\n", |
| reason: "blocked on rdar://problem/19036555"), |
| ComparisonTest("\u{0}", "\u{0}\u{0}", |
| reason: "rdar://problem/19034601"), |
| |
| // Whitespace |
| // U+000A LINE FEED (LF) |
| // U+000B LINE TABULATION |
| // U+000C FORM FEED (FF) |
| // U+0085 NEXT LINE (NEL) |
| // U+2028 LINE SEPARATOR |
| // U+2029 PARAGRAPH SEPARATOR |
| ComparisonTest("\u{0085}", "\n"), |
| ComparisonTest("\u{000b}", "\n"), |
| ComparisonTest("\u{000c}", "\n"), |
| ComparisonTest("\u{2028}", "\n"), |
| ComparisonTest("\u{2029}", "\n"), |
| ComparisonTest("\r\n\r\n", "\r\n"), |
| |
| // U+0301 COMBINING ACUTE ACCENT |
| // U+00E1 LATIN SMALL LETTER A WITH ACUTE |
| ComparisonTest("a\u{301}", "\u{e1}"), |
| ComparisonTest("a", "a\u{301}"), |
| ComparisonTest("a", "\u{e1}"), |
| |
| // U+304B HIRAGANA LETTER KA |
| // U+304C HIRAGANA LETTER GA |
| // U+3099 COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK |
| ComparisonTest("\u{304b}", "\u{304b}"), |
| ComparisonTest("\u{304c}", "\u{304c}"), |
| ComparisonTest("\u{304b}", "\u{304c}"), |
| ComparisonTest("\u{304b}", "\u{304c}\u{3099}"), |
| ComparisonTest("\u{304c}", "\u{304b}\u{3099}"), |
| ComparisonTest("\u{304c}", "\u{304c}\u{3099}"), |
| |
| // U+212B ANGSTROM SIGN |
| // U+030A COMBINING RING ABOVE |
| // U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE |
| ComparisonTest("\u{212b}", "A\u{30a}"), |
| ComparisonTest("\u{212b}", "\u{c5}"), |
| ComparisonTest("A\u{30a}", "\u{c5}"), |
| ComparisonTest("A\u{30a}", "a"), |
| ComparisonTest("A", "A\u{30a}"), |
| |
| // U+2126 OHM SIGN |
| // U+03A9 GREEK CAPITAL LETTER OMEGA |
| ComparisonTest("\u{2126}", "\u{03a9}"), |
| |
| // U+0323 COMBINING DOT BELOW |
| // U+0307 COMBINING DOT ABOVE |
| // U+1E63 LATIN SMALL LETTER S WITH DOT BELOW |
| // U+1E69 LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE |
| ComparisonTest("\u{1e69}", "s\u{323}\u{307}"), |
| ComparisonTest("\u{1e69}", "s\u{307}\u{323}"), |
| ComparisonTest("\u{1e69}", "\u{1e63}\u{307}"), |
| ComparisonTest("\u{1e63}", "s\u{323}"), |
| ComparisonTest("\u{1e63}\u{307}", "s\u{323}\u{307}"), |
| ComparisonTest("\u{1e63}\u{307}", "s\u{307}\u{323}"), |
| ComparisonTest("s\u{323}", "\u{1e69}"), |
| |
| // U+FB01 LATIN SMALL LIGATURE FI |
| ComparisonTest("\u{fb01}", "\u{fb01}"), |
| ComparisonTest("fi", "\u{fb01}"), |
| |
| // U+1F1E7 REGIONAL INDICATOR SYMBOL LETTER B |
| // \u{1F1E7}\u{1F1E7} Flag of Barbados |
| ComparisonTest("\u{1F1E7}", "\u{1F1E7}\u{1F1E7}", |
| reason: "https://bugs.swift.org/browse/SR-367"), |
| |
| // Test that Unicode collation is performed in deterministic mode. |
| // |
| // U+0301 COMBINING ACUTE ACCENT |
| // U+0341 COMBINING ACUTE TONE MARK |
| // U+0954 DEVANAGARI ACUTE ACCENT |
| // |
| // Collation elements from DUCET: |
| // 0301 ; [.0000.0024.0002] # COMBINING ACUTE ACCENT |
| // 0341 ; [.0000.0024.0002] # COMBINING ACUTE TONE MARK |
| // 0954 ; [.0000.0024.0002] # DEVANAGARI ACUTE ACCENT |
| // |
| // U+0301 and U+0954 don't decompose in the canonical decomposition mapping. |
| // U+0341 has a canonical decomposition mapping of U+0301. |
| ComparisonTest("\u{0301}", "\u{0341}", |
| reason: "https://bugs.swift.org/browse/SR-243"), |
| ComparisonTest("\u{0301}", "\u{0954}"), |
| ComparisonTest("\u{0341}", "\u{0954}"), |
| ] |
| |
| enum Stack: Swift.Error { |
| case Stack([UInt]) |
| } |
| |
| func checkHasPrefixHasSuffix(_ lhs: String, _ rhs: String, _ stack: [UInt]) -> Int { |
| if lhs == "" { |
| var failures = 0 |
| failures += lhs.hasPrefix(rhs) ? 1 : 0 |
| failures += lhs.hasSuffix(rhs) ? 1 : 0 |
| return failures |
| } |
| if rhs == "" { |
| var failures = 0 |
| failures += lhs.hasPrefix(rhs) ? 1 : 0 |
| failures += lhs.hasSuffix(rhs) ? 1 : 0 |
| return failures |
| } |
| |
| // To determine the expected results, compare grapheme clusters, |
| // scalar-to-scalar, of the NFD form of the strings. |
| let lhsNFDGraphemeClusters = |
| lhs.decomposedStringWithCanonicalMapping.characters.map { |
| Array(String($0).unicodeScalars) |
| } |
| let rhsNFDGraphemeClusters = |
| rhs.decomposedStringWithCanonicalMapping.characters.map { |
| Array(String($0).unicodeScalars) |
| } |
| let expectHasPrefix = lhsNFDGraphemeClusters.starts( |
| with: rhsNFDGraphemeClusters, by: (==)) |
| let expectHasSuffix = |
| lhsNFDGraphemeClusters.lazy.reversed().starts( |
| with: rhsNFDGraphemeClusters.lazy.reversed(), by: (==)) |
| |
| func testFailure(_ lhs: Bool, _ rhs: Bool, _ stack: [UInt]) -> Int { |
| guard lhs == rhs else { |
| // print(stack) |
| return 1 |
| } |
| return 0 |
| } |
| |
| var failures = 0 |
| failures += testFailure(expectHasPrefix, lhs.hasPrefix(rhs), stack + [#line]) |
| failures += testFailure(expectHasSuffix, lhs.hasSuffix(rhs), stack + [#line]) |
| return failures |
| } |
| |
| extension TestNSString { |
| func test_PrefixSuffix() { |
| #if !_runtime(_ObjC) |
| for test in comparisonTests { |
| var failures = 0 |
| failures += checkHasPrefixHasSuffix(test.lhs, test.rhs, [test.loc, #line]) |
| failures += checkHasPrefixHasSuffix(test.rhs, test.lhs, [test.loc, #line]) |
| |
| let fragment = "abc" |
| let combiner = "\u{0301}" |
| |
| failures += checkHasPrefixHasSuffix(test.lhs + fragment, test.rhs, [test.loc, #line]) |
| failures += checkHasPrefixHasSuffix(fragment + test.lhs, test.rhs, [test.loc, #line]) |
| failures += checkHasPrefixHasSuffix(test.lhs + combiner, test.rhs, [test.loc, #line]) |
| failures += checkHasPrefixHasSuffix(combiner + test.lhs, test.rhs, [test.loc, #line]) |
| |
| let fail = (failures > 0) |
| if fail { |
| // print("Prefix/Suffix case \(test.loc): \(failures) failures") |
| // print("Failures were\(test.xfail ? "" : " not") expected") |
| } |
| XCTAssert(test.xfail == fail, "Unexpected \(test.xfail ?"success":"failure"): \(test.loc)") |
| } |
| #endif |
| } |
| } |
| |
| func test_reflection() { |
| } |
| |
| extension TestNSString { |
| func test_replacingOccurrences() { |
| let testPrefix = "ab" |
| let testSuffix = "cd" |
| let testEmoji = "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}" |
| let testString = testPrefix + testEmoji + testSuffix |
| |
| let testReplacement = "xyz" |
| let testReplacementEmoji = "\u{01F468}\u{200D}\u{002764}\u{00FE0F}\u{200D}\u{01F48B}\u{200D}\u{01F468}" |
| |
| let noChange = testString.replacingOccurrences(of: testReplacement, with: "") |
| XCTAssertEqual(noChange, testString) |
| |
| let removePrefix = testString.replacingOccurrences(of: testPrefix, with: "") |
| XCTAssertEqual(removePrefix, testEmoji + testSuffix) |
| let replacePrefix = testString.replacingOccurrences(of: testPrefix, with: testReplacement) |
| XCTAssertEqual(replacePrefix, testReplacement + testEmoji + testSuffix) |
| |
| let removeSuffix = testString.replacingOccurrences(of: testSuffix, with: "") |
| XCTAssertEqual(removeSuffix, testPrefix + testEmoji) |
| let replaceSuffix = testString.replacingOccurrences(of: testSuffix, with: testReplacement) |
| XCTAssertEqual(replaceSuffix, testPrefix + testEmoji + testReplacement) |
| |
| let removeMultibyte = testString.replacingOccurrences(of: testEmoji, with: "") |
| XCTAssertEqual(removeMultibyte, testPrefix + testSuffix) |
| let replaceMultibyte = testString.replacingOccurrences(of: testEmoji, with: testReplacement) |
| XCTAssertEqual(replaceMultibyte, testPrefix + testReplacement + testSuffix) |
| |
| let replaceMultibyteWithMultibyte = testString.replacingOccurrences(of: testEmoji, with: testReplacementEmoji) |
| XCTAssertEqual(replaceMultibyteWithMultibyte, testPrefix + testReplacementEmoji + testSuffix) |
| |
| let replacePrefixWithMultibyte = testString.replacingOccurrences(of: testPrefix, with: testReplacementEmoji) |
| XCTAssertEqual(replacePrefixWithMultibyte, testReplacementEmoji + testEmoji + testSuffix) |
| |
| let replaceSuffixWithMultibyte = testString.replacingOccurrences(of: testSuffix, with: testReplacementEmoji) |
| XCTAssertEqual(replaceSuffixWithMultibyte, testPrefix + testEmoji + testReplacementEmoji) |
| } |
| } |