forked from getsentry/sentry-cocoa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TestDisplayLinkWrapper.swift
131 lines (104 loc) · 3.95 KB
/
TestDisplayLinkWrapper.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import Foundation
#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
public enum GPUFrame {
case normal
case slow
case frozen
}
public enum FrameRate: UInt64 {
case low = 60
case high = 120
public var tickDuration: CFTimeInterval {
return 1 / CFTimeInterval(self.rawValue)
}
}
public class TestDisplayLinkWrapper: SentryDisplayLinkWrapper {
public var target: AnyObject!
public var selector: Selector!
public var currentFrameRate: FrameRate = .low
private let frozenFrameThreshold = 0.7
public let slowestSlowFrameDuration: Double
public let fastestFrozenFrameDuration: Double
public var dateProvider: TestCurrentDateProvider
/// The smallest magnitude of time that is significant to how frames are classified as normal/slow/frozen.
public let timeEpsilon = 0.001
public init(dateProvider: TestCurrentDateProvider? = nil) {
self.dateProvider = dateProvider ?? TestCurrentDateProvider()
// The test date provider converts the duration from UInt64 to a double back and forth.
// Therefore we have rounding issues, and subtract or add the timeEpsilon.
slowestSlowFrameDuration = frozenFrameThreshold - timeEpsilon
fastestFrozenFrameDuration = frozenFrameThreshold + timeEpsilon
}
public var linkInvocations = Invocations<Void>()
public override func link(withTarget target: Any, selector sel: Selector) {
linkInvocations.record(Void())
self.target = target as AnyObject
self.selector = sel
}
public override var timestamp: CFTimeInterval {
return dateProvider.systemTime().toTimeInterval()
}
public override var targetTimestamp: CFTimeInterval {
return dateProvider.systemTime().toTimeInterval() + currentFrameRate.tickDuration
}
public var invalidateInvocations = Invocations<Void>()
public override func invalidate() {
target = nil
selector = nil
invalidateInvocations.record(Void())
}
public func call() {
_ = target.perform(selector)
}
public func changeFrameRate(_ newFrameRate: FrameRate) {
currentFrameRate = newFrameRate
}
public func normalFrame() {
dateProvider.advance(by: currentFrameRate.tickDuration)
call()
}
public func fastestSlowFrame() -> CFTimeInterval {
let duration: Double = slowFrameThreshold(currentFrameRate.rawValue) + timeEpsilon
dateProvider.advance(by: duration)
call()
return duration
}
public func middlingSlowFrameDuration() -> CFTimeInterval {
(frozenFrameThreshold - (slowFrameThreshold(currentFrameRate.rawValue) + timeEpsilon)) / 2.0
}
public func middlingSlowFrame() -> CFTimeInterval {
let duration: Double = middlingSlowFrameDuration()
dateProvider.advance(by: duration)
call()
return duration
}
public func slowestSlowFrame() -> CFTimeInterval {
dateProvider.advance(by: slowestSlowFrameDuration)
call()
return slowestSlowFrameDuration
}
public func fastestFrozenFrame() -> CFTimeInterval {
dateProvider.advance(by: fastestFrozenFrameDuration)
call()
return fastestFrozenFrameDuration
}
/// There's no upper bound for a frozen frame, except maybe for the watchdog time limit.
/// - parameter extraTime: the additional time to add to the frozen frame threshold when simulating a frozen frame.
public func slowerFrozenFrame(extraTime: TimeInterval = 0.1) {
dateProvider.advance(by: frozenFrameThreshold + extraTime)
call()
}
public func renderFrames(_ slow: Int, _ frozen: Int, _ normal: Int) {
self.call()
for _ in 0..<slow {
_ = fastestSlowFrame()
}
for _ in 0..<frozen {
_ = fastestFrozenFrame()
}
for _ in 0..<(normal - 1) {
normalFrame()
}
}
}
#endif