-
-
Notifications
You must be signed in to change notification settings - Fork 410
Expand file tree
/
Copy pathTestDisplayLinkWrapper.swift
More file actions
140 lines (112 loc) · 4.31 KB
/
Copy pathTestDisplayLinkWrapper.swift
File metadata and controls
140 lines (112 loc) · 4.31 KB
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
132
133
134
135
136
137
138
139
140
import Foundation
@_spi(Private) @testable import Sentry
#if os(iOS) || os(tvOS) || os(visionOS)
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)
}
}
@_spi(Private) 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
@_spi(Private) 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
@_spi(Private) 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 ignoreLinkInvocations = false
public var linkInvocations = Invocations<Void>()
public override func link(withTarget target: Any, selector sel: Selector) {
if ignoreLinkInvocations == false {
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 = SentryFramesTracker.slowFrameThreshold(currentFrameRate.rawValue) + timeEpsilon
dateProvider.advance(by: duration)
call()
return duration
}
public func middlingSlowFrameDuration() -> CFTimeInterval {
(frozenFrameThreshold - (SentryFramesTracker.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
}
public func frameWith(delay: Double) {
dateProvider.advance(by: currentFrameRate.tickDuration + delay)
call()
}
/// 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