blob: ebcdc88b1cc6cdef7c55d7fe3fc3994d3384fce9 [file] [log] [blame]
// 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 }
}
}