| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2019 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 |
| |
| // Please keep this import statement as-is; this file is also used by the GenerateTestFixtures project, which doesn't have TestImports.swift. |
| |
| #if DEPLOYMENT_RUNTIME_SWIFT && (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) |
| import SwiftFoundation |
| #else |
| import Foundation |
| #endif |
| |
| // ----- |
| |
| extension Calendar { |
| static var neutral: Calendar { |
| var calendar = Calendar(identifier: .gregorian) |
| calendar.timeZone = TimeZone(secondsFromGMT: 0)! |
| calendar.locale = NSLocale.system |
| return calendar |
| } |
| } |
| |
| enum Fixtures { |
| static let mutableAttributedString = TypedFixture<NSMutableAttributedString>("NSMutableAttributedString") { |
| let string = NSMutableAttributedString(string: "0123456789") |
| // Should have: .xyyzzxyx. |
| |
| let attrs1: [NSAttributedString.Key: Any] = [.init("Font"): "Helvetica", .init("Size"): 123] |
| let attrs2: [NSAttributedString.Key: Any] = [.init("Font"): "Times", .init("Size"): 456] |
| |
| let attrs3NS = attrs2 as NSDictionary |
| let attrs3Maybe: [NSAttributedString.Key: Any]? |
| if let attrs3Swift = attrs3NS as? [String: Any] { |
| attrs3Maybe = Dictionary(attrs3Swift.map { (NSAttributedString.Key($0.key), $0.value) }, uniquingKeysWith: { $1 }) |
| } else { |
| attrs3Maybe = nil |
| } |
| |
| let attrs3 = try XCTUnwrap(attrs3Maybe) |
| |
| string.setAttributes(attrs1, range: NSMakeRange(1, string.length - 2)) |
| string.setAttributes(attrs2, range: NSMakeRange(2, 2)) |
| string.setAttributes(attrs3, range: NSMakeRange(4, 2)) |
| string.setAttributes(attrs2, range: NSMakeRange(8, 1)) |
| |
| return string |
| } |
| |
| static let attributedString = TypedFixture<NSAttributedString>("NSAttributedString") { |
| return NSAttributedString(attributedString: try Fixtures.mutableAttributedString.make()) |
| } |
| |
| // ===== ByteCountFormatter ===== |
| |
| static let byteCountFormatterDefault = TypedFixture<ByteCountFormatter>("ByteCountFormatter-Default") { |
| return ByteCountFormatter() |
| } |
| |
| static let byteCountFormatterAllFieldsSet = TypedFixture<ByteCountFormatter>("ByteCountFormatter-AllFieldsSet") { |
| let f = ByteCountFormatter() |
| |
| f.allowedUnits = [.useBytes, .useKB] |
| f.countStyle = .decimal |
| f.formattingContext = .beginningOfSentence |
| |
| f.zeroPadsFractionDigits = true |
| f.includesCount = true |
| |
| f.allowsNonnumericFormatting = false |
| f.includesUnit = false |
| f.includesCount = false |
| f.isAdaptive = false |
| |
| return f |
| } |
| |
| // ===== DateIntervalFormatter ===== |
| |
| static let dateIntervalFormatterDefault = TypedFixture<DateIntervalFormatter>("DateIntervalFormatter-Default") { |
| let dif = DateIntervalFormatter() |
| |
| let calendar = Calendar.neutral |
| |
| dif.calendar = calendar |
| dif.timeZone = calendar.timeZone |
| dif.locale = calendar.locale |
| |
| return dif |
| } |
| |
| static let dateIntervalFormatterValuesSetWithoutTemplate = TypedFixture<DateIntervalFormatter>("DateIntervalFormatter-ValuesSetWithoutTemplate") { |
| let dif = DateIntervalFormatter() |
| |
| var calendar = Calendar.neutral |
| calendar.locale = Locale(identifier: "ja-JP") |
| |
| dif.calendar = calendar |
| dif.timeZone = calendar.timeZone |
| dif.locale = calendar.locale |
| dif.dateStyle = .long |
| dif.timeStyle = .none |
| dif.timeZone = TimeZone(secondsFromGMT: 60 * 60) |
| |
| return dif |
| } |
| |
| static let dateIntervalFormatterValuesSetWithTemplate = TypedFixture<DateIntervalFormatter>("DateIntervalFormatter-ValuesSetWithTemplate") { |
| let dif = DateIntervalFormatter() |
| |
| var calendar = Calendar.neutral |
| calendar.locale = Locale(identifier: "ja-JP") |
| |
| dif.calendar = calendar |
| dif.timeZone = calendar.timeZone |
| dif.locale = calendar.locale |
| dif.dateTemplate = "dd mm yyyy HH:MM" |
| dif.timeZone = TimeZone(secondsFromGMT: 60 * 60) |
| |
| return dif |
| } |
| |
| // ===== ISO8601DateFormatter ===== |
| |
| static let iso8601FormatterDefault = TypedFixture<ISO8601DateFormatter>("ISO8601DateFormatter-Default") { |
| let idf = ISO8601DateFormatter() |
| idf.timeZone = Calendar.neutral.timeZone |
| |
| return idf |
| } |
| |
| static let iso8601FormatterOptionsSet = TypedFixture<ISO8601DateFormatter>("ISO8601DateFormatter-OptionsSet") { |
| let idf = ISO8601DateFormatter() |
| idf.timeZone = Calendar.neutral.timeZone |
| |
| idf.formatOptions = [ .withDay, .withWeekOfYear, .withMonth, .withTimeZone, .withColonSeparatorInTimeZone, .withDashSeparatorInDate ] |
| |
| return idf |
| } |
| |
| // ===== NSTextCheckingResult ===== |
| |
| static let textCheckingResultSimpleRegex = TypedFixture<NSTextCheckingResult>("NSTextCheckingResult-SimpleRegex") { |
| let string = "aaa" |
| let regexp = try NSRegularExpression(pattern: "aaa", options: []) |
| let result = try XCTUnwrap(regexp.matches(in: string, range: NSRange(string.startIndex ..< string.endIndex, in: string)).first) |
| |
| return result |
| } |
| |
| |
| static let textCheckingResultExtendedRegex = TypedFixture<NSTextCheckingResult>("NSTextCheckingResult-ExtendedRegex") { |
| let string = "aaaaaa" |
| let regexp = try NSRegularExpression(pattern: "a(a(a(a(a(a)))))", options: []) |
| let result = try XCTUnwrap(regexp.matches(in: string, range: NSRange(string.startIndex ..< string.endIndex, in: string)).first) |
| |
| return result |
| } |
| |
| static let textCheckingResultComplexRegex = TypedFixture<NSTextCheckingResult>("NSTextCheckingResult-ComplexRegex") { |
| let string = "aaaaaaaaa" |
| let regexp = try NSRegularExpression(pattern: "a(a(a(a(a(a(a(a(a))))))))", options: []) |
| let result = try XCTUnwrap(regexp.matches(in: string, range: NSRange(string.startIndex ..< string.endIndex, in: string)).first) |
| |
| return result |
| } |
| |
| // ===== NSIndexSet ===== |
| |
| static let indexSetEmpty = TypedFixture<NSIndexSet>("NSIndexSet-Empty") { |
| return NSIndexSet(indexesIn: NSMakeRange(0, 0)) |
| } |
| |
| static let indexSetOneRange = TypedFixture<NSIndexSet>("NSIndexSet-OneRange") { |
| return NSIndexSet(indexesIn: NSMakeRange(0, 50)) |
| } |
| |
| static let indexSetManyRanges = TypedFixture<NSIndexSet>("NSIndexSet-ManyRanges") { |
| let indexSet = NSMutableIndexSet() |
| indexSet.add(in: NSMakeRange(0, 50)) |
| indexSet.add(in: NSMakeRange(100, 50)) |
| indexSet.add(in: NSMakeRange(1000, 50)) |
| indexSet.add(in: NSMakeRange(Int.max - 50, 50)) |
| return indexSet.copy() as! NSIndexSet |
| } |
| |
| static let mutableIndexSetEmpty = TypedFixture<NSMutableIndexSet>("NSMutableIndexSet-Empty") { |
| return (try Fixtures.indexSetEmpty.make()).mutableCopy() as! NSMutableIndexSet |
| } |
| |
| static let mutableIndexSetOneRange = TypedFixture<NSMutableIndexSet>("NSMutableIndexSet-OneRange") { |
| return (try Fixtures.indexSetOneRange.make()).mutableCopy() as! NSMutableIndexSet |
| } |
| |
| static let mutableIndexSetManyRanges = TypedFixture<NSMutableIndexSet>("NSMutableIndexSet-ManyRanges") { |
| return (try Fixtures.indexSetManyRanges.make()).mutableCopy() as! NSMutableIndexSet |
| } |
| |
| // ===== NSIndexPath ===== |
| |
| static let indexPathEmpty = TypedFixture<NSIndexPath>("NSIndexPath-Empty") { |
| return NSIndexPath() |
| } |
| |
| static let indexPathOneIndex = TypedFixture<NSIndexPath>("NSIndexPath-OneIndex") { |
| return NSIndexPath(index: 52) |
| } |
| |
| static let indexPathManyIndices = TypedFixture<NSIndexPath>("NSIndexPath-ManyIndices") { |
| var indexPath = IndexPath() |
| indexPath.append([4, 8, 15, 16, 23, 42]) |
| return indexPath as NSIndexPath |
| } |
| |
| // ===== NSSet, NSMutableSet ===== |
| |
| static let setOfNumbers = TypedFixture<NSSet>("NSSet-Numbers") { |
| let numbers = [1, 2, 3, 4, 5].map { NSNumber(value: $0) } |
| return NSSet(array: numbers) |
| } |
| |
| static let setEmpty = TypedFixture<NSSet>("NSSet-Empty") { |
| return NSSet() |
| } |
| |
| static let mutableSetOfNumbers = TypedFixture<NSMutableSet>("NSMutableSet-Numbers") { |
| let numbers = [1, 2, 3, 4, 5].map { NSNumber(value: $0) } |
| return NSMutableSet(array: numbers) |
| } |
| |
| static let mutableSetEmpty = TypedFixture<NSMutableSet>("NSMutableSet-Empty") { |
| return NSMutableSet() |
| } |
| |
| // ===== NSCountedSet ===== |
| |
| static let countedSetOfNumbersAppearingOnce = TypedFixture<NSCountedSet>("NSCountedSet-NumbersAppearingOnce") { |
| let numbers = [1, 2, 3, 4, 5].map { NSNumber(value: $0) } |
| return NSCountedSet(array: numbers) |
| } |
| |
| static let countedSetOfNumbersAppearingSeveralTimes = TypedFixture<NSCountedSet>("NSCountedSet-NumbersAppearingSeveralTimes") { |
| let numbers = [1, 2, 3, 4, 5].map { NSNumber(value: $0) } |
| let set = NSCountedSet() |
| for _ in 0 ..< 5 { |
| for number in numbers { |
| set.add(number) |
| } |
| } |
| return set |
| } |
| |
| static let countedSetEmpty = TypedFixture<NSCountedSet>("NSCountedSet-Empty") { |
| return NSCountedSet() |
| } |
| |
| // ===== NSCharacterSet, NSMutableCharacterSet ===== |
| |
| static let characterSetEmpty = TypedFixture<NSCharacterSet>("NSCharacterSet-Empty") { |
| return NSCharacterSet() |
| } |
| |
| static let characterSetRange = TypedFixture<NSCharacterSet>("NSCharacterSet-Range") { |
| return NSCharacterSet(range: NSMakeRange(0, 255)) |
| } |
| |
| static let characterSetString = TypedFixture<NSCharacterSet>("NSCharacterSet-String") { |
| return NSCharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyz") |
| } |
| |
| static let characterSetBitmap = TypedFixture<NSCharacterSet>("NSCharacterSet-Bitmap") { |
| let someSet = NSCharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyz") |
| return NSCharacterSet(bitmapRepresentation: someSet.bitmapRepresentation) |
| } |
| |
| static let characterSetBuiltin = TypedFixture<NSCharacterSet>("NSCharacterSet-Builtin") { |
| return NSCharacterSet.alphanumerics as NSCharacterSet |
| } |
| |
| // ===== NSOrderedSet, NSMutableOrderedSet ===== |
| |
| static let orderedSetOfNumbers = TypedFixture<NSOrderedSet>("NSOrderedSet-Numbers") { |
| let numbers = [1, 2, 3, 4, 5].map { NSNumber(value: $0) } |
| return NSOrderedSet(array: numbers) |
| } |
| |
| static let orderedSetEmpty = TypedFixture<NSOrderedSet>("NSOrderedSet-Empty") { |
| return NSOrderedSet() |
| } |
| |
| static let mutableOrderedSetOfNumbers = TypedFixture<NSMutableOrderedSet>("NSMutableOrderedSet-Numbers") { |
| let numbers = [1, 2, 3, 4, 5].map { NSNumber(value: $0) } |
| return NSMutableOrderedSet(array: numbers) |
| } |
| |
| static let mutableOrderedSetEmpty = TypedFixture<NSMutableOrderedSet>("NSMutableOrderedSet-Empty") { |
| return NSMutableOrderedSet() |
| } |
| |
| // ===== NSMeasurement ===== |
| |
| static let zeroMeasurement = TypedFixture<NSMeasurement>("NSMeasurement-Zero") { |
| let noUnit = Unit(symbol: "") |
| return NSMeasurement(doubleValue: 0, unit: noUnit) |
| } |
| |
| static let lengthMeasurement = TypedFixture<NSMeasurement>("NSMeasurement-Length") { |
| return NSMeasurement(doubleValue: 45, unit: UnitLength.miles) |
| } |
| |
| static let frequencyMeasurement = TypedFixture<NSMeasurement>("NSMeasurement-Frequency") { |
| return NSMeasurement(doubleValue: 1400, unit: UnitFrequency.megahertz) |
| } |
| |
| static let angleMeasurement = TypedFixture<NSMeasurement>("NSMeasurement-Angle") { |
| return NSMeasurement(doubleValue: 90, unit: UnitAngle.degrees) |
| } |
| |
| // ===== Fixture list ===== |
| |
| static let _listOfAllFixtures: [AnyFixture] = [ |
| AnyFixture(Fixtures.mutableAttributedString), |
| AnyFixture(Fixtures.attributedString), |
| AnyFixture(Fixtures.byteCountFormatterDefault), |
| AnyFixture(Fixtures.byteCountFormatterAllFieldsSet), |
| AnyFixture(Fixtures.dateIntervalFormatterDefault), |
| AnyFixture(Fixtures.dateIntervalFormatterValuesSetWithTemplate), |
| AnyFixture(Fixtures.dateIntervalFormatterValuesSetWithoutTemplate), |
| AnyFixture(Fixtures.iso8601FormatterDefault), |
| AnyFixture(Fixtures.iso8601FormatterOptionsSet), |
| AnyFixture(Fixtures.textCheckingResultSimpleRegex), |
| AnyFixture(Fixtures.textCheckingResultExtendedRegex), |
| AnyFixture(Fixtures.textCheckingResultComplexRegex), |
| AnyFixture(Fixtures.indexSetEmpty), |
| AnyFixture(Fixtures.indexSetOneRange), |
| AnyFixture(Fixtures.indexSetManyRanges), |
| AnyFixture(Fixtures.mutableIndexSetEmpty), |
| AnyFixture(Fixtures.mutableIndexSetOneRange), |
| AnyFixture(Fixtures.mutableIndexSetManyRanges), |
| AnyFixture(Fixtures.indexPathEmpty), |
| AnyFixture(Fixtures.indexPathOneIndex), |
| AnyFixture(Fixtures.indexPathManyIndices), |
| AnyFixture(Fixtures.setOfNumbers), |
| AnyFixture(Fixtures.setEmpty), |
| AnyFixture(Fixtures.mutableSetOfNumbers), |
| AnyFixture(Fixtures.mutableSetEmpty), |
| AnyFixture(Fixtures.countedSetOfNumbersAppearingOnce), |
| AnyFixture(Fixtures.countedSetOfNumbersAppearingSeveralTimes), |
| AnyFixture(Fixtures.countedSetEmpty), |
| AnyFixture(Fixtures.characterSetEmpty), |
| AnyFixture(Fixtures.characterSetRange), |
| AnyFixture(Fixtures.characterSetString), |
| AnyFixture(Fixtures.characterSetBitmap), |
| AnyFixture(Fixtures.characterSetBuiltin), |
| AnyFixture(Fixtures.orderedSetOfNumbers), |
| AnyFixture(Fixtures.orderedSetEmpty), |
| AnyFixture(Fixtures.mutableOrderedSetOfNumbers), |
| AnyFixture(Fixtures.mutableOrderedSetEmpty), |
| AnyFixture(Fixtures.zeroMeasurement), |
| AnyFixture(Fixtures.lengthMeasurement), |
| AnyFixture(Fixtures.frequencyMeasurement), |
| AnyFixture(Fixtures.angleMeasurement), |
| ] |
| |
| // This ensures that we do not have fixtures with duplicate identifiers: |
| |
| static var all: [AnyFixture] { |
| return Array(Fixtures.allFixturesByIdentifier.values) |
| } |
| |
| static var allFixturesByIdentifier: [String: AnyFixture] = { |
| let keysAndValues = Fixtures._listOfAllFixtures.map { ($0.identifier, $0) } |
| return Dictionary(keysAndValues, uniquingKeysWith: { _, _ in fatalError("No two keys should be the same in fixtures. Double-check keys in FixtureValues.swift to make sure they're all unique.") }) |
| }() |
| } |
| |
| // ----- |
| |
| // Support for the above: |
| |
| enum FixtureVariant: String, CaseIterable { |
| case macOS10_14 = "macOS-10.14" |
| |
| func url(fixtureRepository: URL) -> URL { |
| return URL(fileURLWithPath: self.rawValue, relativeTo: fixtureRepository) |
| } |
| } |
| |
| protocol Fixture { |
| associatedtype ValueType |
| var identifier: String { get } |
| func make() throws -> ValueType |
| var supportsSecureCoding: Bool { get } |
| } |
| |
| struct TypedFixture<ValueType: NSObject & NSCoding>: Fixture { |
| var identifier: String |
| private var creationHandler: () throws -> ValueType |
| |
| init(_ identifier: String, creationHandler: @escaping () throws -> ValueType) { |
| self.identifier = identifier |
| self.creationHandler = creationHandler |
| } |
| |
| func make() throws -> ValueType { |
| return try creationHandler() |
| } |
| |
| var supportsSecureCoding: Bool { |
| let kind: Any.Type |
| if let made = try? make() { |
| kind = type(of: made) |
| } else { |
| kind = ValueType.self |
| } |
| |
| return (kind as? NSSecureCoding.Type)?.supportsSecureCoding == true |
| } |
| } |
| |
| struct AnyFixture: Fixture { |
| var identifier: String |
| private var creationHandler: () throws -> NSObject & NSCoding |
| let supportsSecureCoding: Bool |
| |
| init<T: Fixture>(_ fixture: T) { |
| self.identifier = fixture.identifier |
| self.creationHandler = { return try fixture.make() as! (NSObject & NSCoding) } |
| self.supportsSecureCoding = fixture.supportsSecureCoding |
| } |
| |
| func make() throws -> NSObject & NSCoding { |
| return try creationHandler() |
| } |
| } |
| |
| enum FixtureError: Error { |
| case noneFound |
| } |
| |
| extension Fixture where ValueType: NSObject & NSCoding { |
| func load(fixtureRepository: URL, variant: FixtureVariant) throws -> ValueType? { |
| let data = try Data(contentsOf: url(inFixtureRepository: fixtureRepository, variant: variant)) |
| let unarchiver = NSKeyedUnarchiver(forReadingWith: data) |
| unarchiver.requiresSecureCoding = self.supportsSecureCoding |
| unarchiver.decodingFailurePolicy = .setErrorAndReturn |
| |
| let value = unarchiver.decodeObject(of: ValueType.self, forKey: NSKeyedArchiveRootObjectKey) |
| if let error = unarchiver.error { |
| throw error |
| } |
| return value |
| } |
| |
| func url(inFixtureRepository fixtureRepository: URL, variant: FixtureVariant) -> URL { |
| return variant.url(fixtureRepository: fixtureRepository) |
| .appendingPathComponent(identifier) |
| .appendingPathExtension("archive") |
| } |
| |
| func loadEach(fixtureRepository: URL, handler: (ValueType, FixtureVariant) throws -> Void) throws { |
| var foundAny = false |
| |
| for variant in FixtureVariant.allCases { |
| let fileURL = url(inFixtureRepository: fixtureRepository, variant: variant) |
| guard (try? fileURL.checkResourceIsReachable()) == true else { continue } |
| |
| foundAny = true |
| |
| if let value = try load(fixtureRepository: fixtureRepository, variant: variant) { |
| try handler(value, variant) |
| } |
| } |
| |
| guard foundAny else { throw FixtureError.noneFound } |
| } |
| } |