//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// import StdlibUnittest public protocol Wrapper { associatedtype Base init(wrapping base: Base) var base: Base { get set } } public protocol LoggingType: Wrapper { associatedtype Log : AnyObject } extension LoggingType { public var log: Log.Type { return Log.self } public var selfType: Any.Type { return type(of: self) } } //===----------------------------------------------------------------------===// // Iterator //===----------------------------------------------------------------------===// public class IteratorLog { public static func dispatchTester( _ iterator: I ) -> LoggingIterator> { return LoggingIterator(wrapping: LoggingIterator(wrapping: iterator)) } public static var next = TypeIndexed(0) } public struct LoggingIterator { public var base: Base } extension LoggingIterator: LoggingType { public typealias Log = IteratorLog public init(wrapping base: Base) { self.base = base } } extension LoggingIterator: IteratorProtocol { public mutating func next() -> Base.Element? { Log.next[selfType] += 1 return base.next() } } //===----------------------------------------------------------------------===// // Sequence and Collection logs //===----------------------------------------------------------------------===// // FIXME: it's not clear if it's really worth this hierarchy. the // test.log pattern requires all the static properties be at the top // since Log is an associated type that cannot be refined in extensions // that add functionality. public class SequenceLogBase { // Sequence public static var makeIterator = TypeIndexed(0) public static var underestimatedCount = TypeIndexed(0) public static var dropFirst = TypeIndexed(0) public static var dropLast = TypeIndexed(0) public static var dropWhile = TypeIndexed(0) public static var prefixWhile = TypeIndexed(0) public static var prefixMaxLength = TypeIndexed(0) public static var suffixMaxLength = TypeIndexed(0) public static var withContiguousStorageIfAvailable = TypeIndexed(0) public static var _customContainsEquatableElement = TypeIndexed(0) public static var _copyToContiguousArray = TypeIndexed(0) public static var _copyContents = TypeIndexed(0) // Collection public static var startIndex = TypeIndexed(0) public static var endIndex = TypeIndexed(0) public static var subscriptIndex = TypeIndexed(0) public static var subscriptRange = TypeIndexed(0) public static var _failEarlyRangeCheckIndex = TypeIndexed(0) public static var _failEarlyRangeCheckRange = TypeIndexed(0) public static var successor = TypeIndexed(0) public static var formSuccessor = TypeIndexed(0) public static var indices = TypeIndexed(0) public static var isEmpty = TypeIndexed(0) public static var count = TypeIndexed(0) public static var _customIndexOfEquatableElement = TypeIndexed(0) public static var advance = TypeIndexed(0) public static var advanceLimit = TypeIndexed(0) public static var distance = TypeIndexed(0) // BidirectionalCollection public static var predecessor = TypeIndexed(0) public static var formPredecessor = TypeIndexed(0) // MutableCollection public static var subscriptIndexSet = TypeIndexed(0) public static var subscriptRangeSet = TypeIndexed(0) public static var partitionBy = TypeIndexed(0) public static var _withUnsafeMutableBufferPointerIfSupported = TypeIndexed(0) public static var _withUnsafeMutableBufferPointerIfSupportedNonNilReturns = TypeIndexed(0) public static var withContiguousMutableStorageIfAvailable = TypeIndexed(0) public static var withContiguousMutableStorageIfAvailableNonNilReturns = TypeIndexed(0) // RangeReplaceableCollection public static var init_ = TypeIndexed(0) public static var initRepeating = TypeIndexed(0) public static var initWithSequence = TypeIndexed(0) public static var _customRemoveLast = TypeIndexed(0) public static var _customRemoveLastN = TypeIndexed(0) public static var append = TypeIndexed(0) public static var appendContentsOf = TypeIndexed(0) public static var insert = TypeIndexed(0) public static var insertContentsOf = TypeIndexed(0) public static var removeAll = TypeIndexed(0) public static var removeAt = TypeIndexed(0) public static var removeFirst = TypeIndexed(0) public static var removeFirstN = TypeIndexed(0) public static var removeSubrange = TypeIndexed(0) public static var replaceSubrange = TypeIndexed(0) public static var reserveCapacity = TypeIndexed(0) } public class SequenceLog : SequenceLogBase { public static func dispatchTester( _ s: S ) -> LoggingSequence> { return LoggingSequence(wrapping: LoggingSequence(wrapping: s)) } } public class CollectionLog : SequenceLogBase { public static func dispatchTester( _ c: C ) -> LoggingCollection> { return LoggingCollection(wrapping: LoggingCollection(wrapping: c)) } } public class BidirectionalCollectionLog : SequenceLogBase { public static func dispatchTester( _ c: C ) -> LoggingBidirectionalCollection> { return LoggingBidirectionalCollection( wrapping: LoggingBidirectionalCollection(wrapping: c)) } } public class MutableCollectionLog : SequenceLogBase { public static func dispatchTester( _ c: C ) -> LoggingMutableCollection> { return LoggingMutableCollection( wrapping: LoggingMutableCollection(wrapping: c)) } } /// Data container to keep track of how many times each `Base` type calls methods /// of `RangeReplaceableCollection`. /// /// Each static variable is a mapping of Type -> Number of calls. public class RangeReplaceableCollectionLog : SequenceLogBase { public static func dispatchTester( _ rrc: C ) -> LoggingRangeReplaceableCollection> { return LoggingRangeReplaceableCollection( wrapping: LoggingRangeReplaceableCollection(wrapping: rrc) ) } } //===----------------------------------------------------------------------===// // Sequence and Collection that count method calls //===----------------------------------------------------------------------===// /// Interposes between `Sequence` method calls to increment each method's /// counter. public struct LoggingSequence { public var base: Base } extension LoggingSequence: LoggingType { // I know, I know. It doesn't matter though. The benefit of the whole logging // class hierarchy is unclear... public typealias Log = SequenceLog public init(wrapping base: Base) { self.base = base } } extension LoggingSequence: Sequence { public typealias Element = Base.Element public typealias Iterator = LoggingIterator public func makeIterator() -> Iterator { SequenceLog.makeIterator[selfType] += 1 return LoggingIterator(wrapping: base.makeIterator()) } public var underestimatedCount: Int { SequenceLog.underestimatedCount[selfType] += 1 return base.underestimatedCount } public func withContiguousStorageIfAvailable( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R? { SequenceLog.withContiguousStorageIfAvailable[selfType] += 1 return try base.withContiguousStorageIfAvailable(body) } public func _customContainsEquatableElement(_ element: Element) -> Bool? { SequenceLog._customContainsEquatableElement[selfType] += 1 return base._customContainsEquatableElement(element) } /// Create a native array buffer containing the elements of `self`, /// in the same order. public func _copyToContiguousArray() -> ContiguousArray { SequenceLog._copyToContiguousArray[selfType] += 1 return base._copyToContiguousArray() } /// Copy a Sequence into an array. public func _copyContents( initializing buffer: UnsafeMutableBufferPointer ) -> (Iterator,UnsafeMutableBufferPointer.Index) { SequenceLog._copyContents[selfType] += 1 let (it,idx) = base._copyContents(initializing: buffer) return (Iterator(wrapping: it),idx) } } public typealias LoggingCollection = LoggingSequence extension LoggingCollection: Collection { public typealias Index = Base.Index public typealias Indices = Base.Indices public typealias SubSequence = Base.SubSequence public var startIndex: Index { CollectionLog.startIndex[selfType] += 1 return base.startIndex } public var endIndex: Index { CollectionLog.endIndex[selfType] += 1 return base.endIndex } public subscript(position: Index) -> Element { CollectionLog.subscriptIndex[selfType] += 1 return base[position] } public subscript(bounds: Range) -> SubSequence { CollectionLog.subscriptRange[selfType] += 1 return base[bounds] } public func _failEarlyRangeCheck(_ index: Index, bounds: Range) { CollectionLog._failEarlyRangeCheckIndex[selfType] += 1 base._failEarlyRangeCheck(index, bounds: bounds) } public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { CollectionLog._failEarlyRangeCheckRange[selfType] += 1 base._failEarlyRangeCheck(range, bounds: bounds) } public func index(after i: Index) -> Index { CollectionLog.successor[selfType] += 1 return base.index(after: i) } public func formIndex(after i: inout Index) { CollectionLog.formSuccessor[selfType] += 1 base.formIndex(after: &i) } public var indices: Indices { CollectionLog.indices[selfType] += 1 return base.indices } public var isEmpty: Bool { CollectionLog.isEmpty[selfType] += 1 return base.isEmpty } public var count: Int { CollectionLog.count[selfType] += 1 return base.count } public func _customIndexOfEquatableElement( _ element: Element ) -> Index?? { CollectionLog._customIndexOfEquatableElement[selfType] += 1 return base._customIndexOfEquatableElement(element) } public func index(_ i: Index, offsetBy n: Int) -> Index { CollectionLog.advance[selfType] += 1 return base.index(i, offsetBy: n) } public func index( _ i: Index, offsetBy n: Int, limitedBy limit: Index ) -> Index? { CollectionLog.advanceLimit[selfType] += 1 return base.index(i, offsetBy: n, limitedBy: limit) } public func distance(from start: Index, to end: Index) -> Int { CollectionLog.distance[selfType] += 1 return base.distance(from: start, to: end) } } public typealias LoggingBidirectionalCollection< Base: BidirectionalCollection > = LoggingCollection extension LoggingBidirectionalCollection: BidirectionalCollection { public func index(before i: Index) -> Index { BidirectionalCollectionLog.predecessor[selfType] += 1 return base.index(before: i) } public func formIndex(before i: inout Index) { BidirectionalCollectionLog.formPredecessor[selfType] += 1 base.formIndex(before: &i) } } public typealias LoggingRandomAccessCollection = LoggingBidirectionalCollection extension LoggingRandomAccessCollection: RandomAccessCollection { } public typealias LoggingMutableCollection = LoggingCollection extension LoggingMutableCollection: MutableCollection { public subscript(position: Index) -> Element { get { MutableCollectionLog.subscriptIndex[selfType] += 1 return base[position] } set { MutableCollectionLog.subscriptIndexSet[selfType] += 1 base[position] = newValue } } public subscript(bounds: Range) -> SubSequence { get { MutableCollectionLog.subscriptRange[selfType] += 1 return base[bounds] } set { MutableCollectionLog.subscriptRangeSet[selfType] += 1 base[bounds] = newValue } } public mutating func partition( by belongsInSecondPartition: (Iterator.Element) throws -> Bool ) rethrows -> Index { Log.partitionBy[selfType] += 1 return try base.partition(by: belongsInSecondPartition) } @available(*, deprecated, renamed: "withContiguousMutableStorageIfAvailable") public mutating func _withUnsafeMutableBufferPointerIfSupported( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R? { MutableCollectionLog._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 let result = try base._withUnsafeMutableBufferPointerIfSupported(body) if result != nil { Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 } return result } public mutating func withContiguousMutableStorageIfAvailable( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R? { MutableCollectionLog.withContiguousMutableStorageIfAvailable[selfType] += 1 let result = try base.withContiguousMutableStorageIfAvailable(body) if result != nil { Log.withContiguousMutableStorageIfAvailableNonNilReturns[selfType] += 1 } return result } } public typealias LoggingMutableBidirectionalCollection< Base: MutableCollection & BidirectionalCollection > = LoggingMutableCollection public typealias LoggingMutableRandomAccessCollection< Base: MutableCollection & RandomAccessCollection > = LoggingMutableCollection public typealias LoggingRangeReplaceableCollection< Base: RangeReplaceableCollection > = LoggingCollection extension LoggingRangeReplaceableCollection: RangeReplaceableCollection { public init() { self.base = Base() RangeReplaceableCollectionLog.init_[selfType] += 1 } public init(repeating repeatedValue: Element, count: Int) { self.base = Base(repeating: repeatedValue, count: count) RangeReplaceableCollectionLog.initRepeating[selfType] += 1 } public init(_ elements: S) where S.Element == Element { self.base = Base(elements) RangeReplaceableCollectionLog.initWithSequence[selfType] += 1 } public mutating func _customRemoveLast() -> Element? { RangeReplaceableCollectionLog._customRemoveLast[selfType] += 1 return base._customRemoveLast() } public mutating func _customRemoveLast(_ n: Int) -> Bool { RangeReplaceableCollectionLog._customRemoveLastN[selfType] += 1 return base._customRemoveLast(n) } public mutating func append(_ newElement: Element) { RangeReplaceableCollectionLog.append[selfType] += 1 base.append(newElement) } public mutating func append( contentsOf newElements: S ) where S.Element == Element { RangeReplaceableCollectionLog.appendContentsOf[selfType] += 1 base.append(contentsOf: newElements) } public mutating func insert( _ newElement: Element, at i: Index ) { RangeReplaceableCollectionLog.insert[selfType] += 1 base.insert(newElement, at: i) } public mutating func insert( contentsOf newElements: C, at i: Index ) where C.Element == Element { RangeReplaceableCollectionLog.insertContentsOf[selfType] += 1 base.insert(contentsOf: newElements, at: i) } public mutating func removeAll(keepingCapacity keepCapacity: Bool) { RangeReplaceableCollectionLog.removeAll[selfType] += 1 base.removeAll(keepingCapacity: keepCapacity) } @discardableResult public mutating func remove(at index: Index) -> Element { RangeReplaceableCollectionLog.removeAt[selfType] += 1 return base.remove(at: index) } @discardableResult public mutating func removeFirst() -> Element { RangeReplaceableCollectionLog.removeFirst[selfType] += 1 return base.removeFirst() } public mutating func removeFirst(_ n: Int) { RangeReplaceableCollectionLog.removeFirstN[selfType] += 1 base.removeFirst(n) } public mutating func removeSubrange(_ bounds: Range) { RangeReplaceableCollectionLog.removeSubrange[selfType] += 1 base.removeSubrange(bounds) } public mutating func replaceSubrange( _ bounds: Range, with newElements: C ) where C.Element == Element { RangeReplaceableCollectionLog.replaceSubrange[selfType] += 1 base.replaceSubrange(bounds, with: newElements) } public mutating func reserveCapacity(_ n: Int) { RangeReplaceableCollectionLog.reserveCapacity[selfType] += 1 base.reserveCapacity(n) } } public typealias LoggingRangeReplaceableBidirectionalCollection< Base: BidirectionalCollection & RangeReplaceableCollection > = LoggingRangeReplaceableCollection public typealias LoggingRangeReplaceableRandomAccessCollection< Base: RandomAccessCollection & RangeReplaceableCollection > = LoggingRangeReplaceableCollection //===----------------------------------------------------------------------===// // Collections that count calls to `withContiguousMutableStorageIfAvailable` //===----------------------------------------------------------------------===// /// Interposes between `withContiguousMutableStorageIfAvailable` method calls /// to increment a counter. Calls to this method from within dispatched methods /// are uncounted by the standard logging collection wrapper. public struct BufferAccessLoggingMutableCollection< Base : MutableCollection > { public var base: Base } extension BufferAccessLoggingMutableCollection: LoggingType { public typealias Log = MutableCollectionLog public init(wrapping base: Base) { self.base = base } } extension BufferAccessLoggingMutableCollection: Sequence { public typealias SubSequence = Base.SubSequence public typealias Iterator = Base.Iterator public typealias Element = Base.Element public func makeIterator() -> Iterator { return base.makeIterator() } } extension BufferAccessLoggingMutableCollection: MutableCollection { public typealias Index = Base.Index public typealias Indices = Base.Indices public var startIndex: Index { return base.startIndex } public var endIndex: Index { return base.endIndex } public var indices: Indices { return base.indices } public subscript(position: Index) -> Element { get { return base[position] } set { base[position] = newValue } } public subscript(bounds: Range) -> SubSequence { get { return base[bounds] } set { base[bounds] = newValue } } public func index(after i: Index) -> Index { return base.index(after: i) } public func index(_ i: Index, offsetBy n: Int) -> Index { return base.index(i, offsetBy: n) } public func distance(from start: Index, to end: Index) -> Int { return base.distance(from: start, to: end) } @available(*, deprecated, renamed: "withContiguousMutableStorageIfAvailable") public mutating func _withUnsafeMutableBufferPointerIfSupported( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R? { Log._withUnsafeMutableBufferPointerIfSupported[selfType] += 1 let result = try base._withUnsafeMutableBufferPointerIfSupported(body) if result != nil { Log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[selfType] += 1 } return result } public mutating func withContiguousMutableStorageIfAvailable( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R? { Log.withContiguousMutableStorageIfAvailable[selfType] += 1 let result = try base.withContiguousMutableStorageIfAvailable(body) if result != nil { Log.withContiguousMutableStorageIfAvailableNonNilReturns[selfType] += 1 } return result } } public typealias BufferAccessLoggingMutableBidirectionalCollection< Base: MutableCollection & BidirectionalCollection > = BufferAccessLoggingMutableCollection extension BufferAccessLoggingMutableBidirectionalCollection: BidirectionalCollection { public func index(before i: Index) -> Index { return base.index(before: i) } } public typealias BufferAccessLoggingMutableRandomAccessCollection< Base: MutableCollection & RandomAccessCollection > = BufferAccessLoggingMutableCollection //===----------------------------------------------------------------------===// // Custom assertions //===----------------------------------------------------------------------===// public func expectCustomizable( _: T, _ counters: TypeIndexed, _ message: @autoclosure () -> String = "", stackTrace: SourceLocStack = SourceLocStack(), showFrame: Bool = true, file: String = #file, line: UInt = #line ) where T.Base : LoggingType, T.Log == T.Base.Log { let newTrace = stackTrace.pushIf(showFrame, file: file, line: line) expectNotEqual(0, counters[T.self], message(), stackTrace: newTrace) expectEqual( counters[T.self], counters[T.Base.self], message(), stackTrace: newTrace) } public func expectNotCustomizable( _: T, _ counters: TypeIndexed, _ message: @autoclosure () -> String = "", stackTrace: SourceLocStack = SourceLocStack(), showFrame: Bool = true, file: String = #file, line: UInt = #line ) where T.Base : LoggingType, T.Log == T.Base.Log { let newTrace = stackTrace.pushIf(showFrame, file: file, line: line) expectNotEqual(0, counters[T.self], message(), stackTrace: newTrace) expectEqual(0, counters[T.Base.self], message(), stackTrace: newTrace) }