Use the MetricsTestKit in the MetricsTests rather than using a copy of the TestMetrics utilities (#128)
Co-authored-by: Hamzah Malik <hamzah_malik@apple.com>
This commit is contained in:
parent
32eef8ae84
commit
9d5ff3d48f
|
|
@ -40,7 +40,7 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MetricsTests",
|
name: "MetricsTests",
|
||||||
dependencies: ["Metrics"]
|
dependencies: ["Metrics", "MetricsTestKit"]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MetricsTests",
|
name: "MetricsTests",
|
||||||
dependencies: ["Metrics"]
|
dependencies: ["Metrics", "MetricsTestKit"]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MetricsTests",
|
name: "MetricsTests",
|
||||||
dependencies: ["Metrics"]
|
dependencies: ["Metrics", "MetricsTestKit"]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MetricsTests",
|
name: "MetricsTests",
|
||||||
dependencies: ["Metrics"]
|
dependencies: ["Metrics", "MetricsTestKit"]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MetricsTests",
|
name: "MetricsTests",
|
||||||
dependencies: ["Metrics"]
|
dependencies: ["Metrics", "MetricsTestKit"]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MetricsTests",
|
name: "MetricsTests",
|
||||||
dependencies: ["Metrics"]
|
dependencies: ["Metrics", "MetricsTestKit"]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MetricsTests",
|
name: "MetricsTests",
|
||||||
dependencies: ["Metrics"]
|
dependencies: ["Metrics", "MetricsTestKit"]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MetricsTests",
|
name: "MetricsTests",
|
||||||
dependencies: ["Metrics"]
|
dependencies: ["Metrics", "MetricsTestKit"]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -48,10 +48,10 @@ public final class TestMetrics: MetricsFactory {
|
||||||
let dimensions: [(String, String)]
|
let dimensions: [(String, String)]
|
||||||
}
|
}
|
||||||
|
|
||||||
private var counters = [FullKey: CounterHandler]()
|
private var _counters = [FullKey: TestCounter]()
|
||||||
private var meters = [FullKey: MeterHandler]()
|
private var _meters = [FullKey: TestMeter]()
|
||||||
private var recorders = [FullKey: RecorderHandler]()
|
private var _recorders = [FullKey: TestRecorder]()
|
||||||
private var timers = [FullKey: TimerHandler]()
|
private var _timers = [FullKey: TestTimer]()
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
|
@ -61,61 +61,61 @@ public final class TestMetrics: MetricsFactory {
|
||||||
/// Invoke this method in between test runs to verify that Counters are created as needed.
|
/// Invoke this method in between test runs to verify that Counters are created as needed.
|
||||||
public func reset() {
|
public func reset() {
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
self.counters = [:]
|
self._counters = [:]
|
||||||
self.meters = [:]
|
self._recorders = [:]
|
||||||
self.recorders = [:]
|
self._meters = [:]
|
||||||
self.timers = [:]
|
self._timers = [:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
|
public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
|
||||||
return self.lock.withLock { () -> CounterHandler in
|
return self.lock.withLock { () -> CounterHandler in
|
||||||
if let existing = self.counters[.init(label: label, dimensions: dimensions)] {
|
if let existing = self._counters[.init(label: label, dimensions: dimensions)] {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
let item = TestCounter(label: label, dimensions: dimensions)
|
let item = TestCounter(label: label, dimensions: dimensions)
|
||||||
self.counters[.init(label: label, dimensions: dimensions)] = item
|
self._counters[.init(label: label, dimensions: dimensions)] = item
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
|
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
|
||||||
return self.lock.withLock { () -> MeterHandler in
|
return self.lock.withLock { () -> MeterHandler in
|
||||||
if let existing = self.meters[.init(label: label, dimensions: dimensions)] {
|
if let existing = self._meters[.init(label: label, dimensions: dimensions)] {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
let item = TestMeter(label: label, dimensions: dimensions)
|
let item = TestMeter(label: label, dimensions: dimensions)
|
||||||
self.meters[.init(label: label, dimensions: dimensions)] = item
|
self._meters[.init(label: label, dimensions: dimensions)] = item
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
|
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
|
||||||
return self.lock.withLock { () -> RecorderHandler in
|
return self.lock.withLock { () -> RecorderHandler in
|
||||||
if let existing = self.recorders[.init(label: label, dimensions: dimensions)] {
|
if let existing = self._recorders[.init(label: label, dimensions: dimensions)] {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
let item = TestRecorder(label: label, dimensions: dimensions, aggregate: aggregate)
|
let item = TestRecorder(label: label, dimensions: dimensions, aggregate: aggregate)
|
||||||
self.recorders[.init(label: label, dimensions: dimensions)] = item
|
self._recorders[.init(label: label, dimensions: dimensions)] = item
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
|
public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
|
||||||
return self.lock.withLock { () -> TimerHandler in
|
return self.lock.withLock { () -> TimerHandler in
|
||||||
if let existing = self.timers[.init(label: label, dimensions: dimensions)] {
|
if let existing = self._timers[.init(label: label, dimensions: dimensions)] {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
let item = TestTimer(label: label, dimensions: dimensions)
|
let item = TestTimer(label: label, dimensions: dimensions)
|
||||||
self.timers[.init(label: label, dimensions: dimensions)] = item
|
self._timers[.init(label: label, dimensions: dimensions)] = item
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func destroyCounter(_ handler: CounterHandler) {
|
public func destroyCounter(_ handler: CounterHandler) {
|
||||||
if let testCounter = handler as? TestCounter {
|
if let testCounter = handler as? TestCounter {
|
||||||
self.lock.withLock { () in
|
self.lock.withLock {
|
||||||
self.counters.removeValue(forKey: testCounter.key)
|
self._counters.removeValue(forKey: testCounter.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,23 +123,23 @@ public final class TestMetrics: MetricsFactory {
|
||||||
public func destroyMeter(_ handler: MeterHandler) {
|
public func destroyMeter(_ handler: MeterHandler) {
|
||||||
if let testMeter = handler as? TestMeter {
|
if let testMeter = handler as? TestMeter {
|
||||||
self.lock.withLock { () in
|
self.lock.withLock { () in
|
||||||
self.meters.removeValue(forKey: testMeter.key)
|
self._meters.removeValue(forKey: testMeter.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func destroyRecorder(_ handler: RecorderHandler) {
|
public func destroyRecorder(_ handler: RecorderHandler) {
|
||||||
if let testRecorder = handler as? TestRecorder {
|
if let testRecorder = handler as? TestRecorder {
|
||||||
self.lock.withLock { () in
|
self.lock.withLock {
|
||||||
self.recorders.removeValue(forKey: testRecorder.key)
|
self._recorders.removeValue(forKey: testRecorder.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func destroyTimer(_ handler: TimerHandler) {
|
public func destroyTimer(_ handler: TimerHandler) {
|
||||||
if let testTimer = handler as? TestTimer {
|
if let testTimer = handler as? TestTimer {
|
||||||
self.lock.withLock { () in
|
self.lock.withLock {
|
||||||
self.timers.removeValue(forKey: testTimer.key)
|
self._timers.removeValue(forKey: testTimer.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -174,17 +174,22 @@ extension TestMetrics {
|
||||||
|
|
||||||
public func expectCounter(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestCounter {
|
public func expectCounter(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestCounter {
|
||||||
let maybeItem = self.lock.withLock {
|
let maybeItem = self.lock.withLock {
|
||||||
self.counters[.init(label: label, dimensions: dimensions)]
|
self._counters[.init(label: label, dimensions: dimensions)]
|
||||||
}
|
}
|
||||||
guard let maybeCounter = maybeItem else {
|
guard let testCounter = maybeItem else {
|
||||||
throw TestMetricsError.missingMetric(label: label, dimensions: dimensions)
|
throw TestMetricsError.missingMetric(label: label, dimensions: dimensions)
|
||||||
}
|
}
|
||||||
guard let testCounter = maybeCounter as? TestCounter else {
|
|
||||||
throw TestMetricsError.illegalMetricType(metric: maybeCounter, expected: "\(TestCounter.self)")
|
|
||||||
}
|
|
||||||
return testCounter
|
return testCounter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All the counters which have been created and not destroyed
|
||||||
|
var counters: [TestCounter] {
|
||||||
|
let counters = self.lock.withLock {
|
||||||
|
self._counters
|
||||||
|
}
|
||||||
|
return Array(counters.values)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Gauge
|
// MARK: - Gauge
|
||||||
|
|
||||||
public func expectGauge(_ metric: Gauge) throws -> TestRecorder {
|
public func expectGauge(_ metric: Gauge) throws -> TestRecorder {
|
||||||
|
|
@ -206,17 +211,22 @@ extension TestMetrics {
|
||||||
|
|
||||||
public func expectMeter(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestMeter {
|
public func expectMeter(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestMeter {
|
||||||
let maybeItem = self.lock.withLock {
|
let maybeItem = self.lock.withLock {
|
||||||
self.meters[.init(label: label, dimensions: dimensions)]
|
self._meters[.init(label: label, dimensions: dimensions)]
|
||||||
}
|
}
|
||||||
guard let maybeMeter = maybeItem else {
|
guard let testMeter = maybeItem else {
|
||||||
throw TestMetricsError.missingMetric(label: label, dimensions: dimensions)
|
throw TestMetricsError.missingMetric(label: label, dimensions: dimensions)
|
||||||
}
|
}
|
||||||
guard let testMeter = maybeMeter as? TestMeter else {
|
|
||||||
throw TestMetricsError.illegalMetricType(metric: maybeMeter, expected: "\(TestMeter.self)")
|
|
||||||
}
|
|
||||||
return testMeter
|
return testMeter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All the meters which have been created and not destroyed
|
||||||
|
var meters: [TestMeter] {
|
||||||
|
let meters = self.lock.withLock {
|
||||||
|
self._meters
|
||||||
|
}
|
||||||
|
return Array(meters.values)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Recorder
|
// MARK: - Recorder
|
||||||
|
|
||||||
public func expectRecorder(_ metric: Recorder) throws -> TestRecorder {
|
public func expectRecorder(_ metric: Recorder) throws -> TestRecorder {
|
||||||
|
|
@ -228,17 +238,22 @@ extension TestMetrics {
|
||||||
|
|
||||||
public func expectRecorder(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestRecorder {
|
public func expectRecorder(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestRecorder {
|
||||||
let maybeItem = self.lock.withLock {
|
let maybeItem = self.lock.withLock {
|
||||||
self.recorders[.init(label: label, dimensions: dimensions)]
|
self._recorders[.init(label: label, dimensions: dimensions)]
|
||||||
}
|
}
|
||||||
guard let maybeRecorder = maybeItem else {
|
guard let testRecorder = maybeItem else {
|
||||||
throw TestMetricsError.missingMetric(label: label, dimensions: dimensions)
|
throw TestMetricsError.missingMetric(label: label, dimensions: dimensions)
|
||||||
}
|
}
|
||||||
guard let testRecorder = maybeRecorder as? TestRecorder else {
|
|
||||||
throw TestMetricsError.illegalMetricType(metric: maybeRecorder, expected: "\(TestRecorder.self)")
|
|
||||||
}
|
|
||||||
return testRecorder
|
return testRecorder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All the recorders which have been created and not destroyed
|
||||||
|
var recorders: [TestRecorder] {
|
||||||
|
let recorders = self.lock.withLock {
|
||||||
|
self._recorders
|
||||||
|
}
|
||||||
|
return Array(recorders.values)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Timer
|
// MARK: - Timer
|
||||||
|
|
||||||
public func expectTimer(_ metric: CoreMetrics.Timer) throws -> TestTimer {
|
public func expectTimer(_ metric: CoreMetrics.Timer) throws -> TestTimer {
|
||||||
|
|
@ -250,16 +265,21 @@ extension TestMetrics {
|
||||||
|
|
||||||
public func expectTimer(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestTimer {
|
public func expectTimer(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestTimer {
|
||||||
let maybeItem = self.lock.withLock {
|
let maybeItem = self.lock.withLock {
|
||||||
self.timers[.init(label: label, dimensions: dimensions)]
|
self._timers[.init(label: label, dimensions: dimensions)]
|
||||||
}
|
}
|
||||||
guard let maybeTimer = maybeItem else {
|
guard let testTimer = maybeItem else {
|
||||||
throw TestMetricsError.missingMetric(label: label, dimensions: dimensions)
|
throw TestMetricsError.missingMetric(label: label, dimensions: dimensions)
|
||||||
}
|
}
|
||||||
guard let testTimer = maybeTimer as? TestTimer else {
|
|
||||||
throw TestMetricsError.illegalMetricType(metric: maybeTimer, expected: "\(TestTimer.self)")
|
|
||||||
}
|
|
||||||
return testTimer
|
return testTimer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All the timers which have been created and not destroyed
|
||||||
|
var timers: [TestTimer] {
|
||||||
|
let timers = self.lock.withLock {
|
||||||
|
self._timers
|
||||||
|
}
|
||||||
|
return Array(timers.values)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Metric type implementations
|
// MARK: - Metric type implementations
|
||||||
|
|
@ -283,7 +303,7 @@ public final class TestCounter: TestMetric, CounterHandler, Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
let lock = NSLock()
|
let lock = NSLock()
|
||||||
private var values = [(Date, Int64)]()
|
private var _values = [(Date, Int64)]()
|
||||||
|
|
||||||
init(label: String, dimensions: [(String, String)]) {
|
init(label: String, dimensions: [(String, String)]) {
|
||||||
self.id = UUID().uuidString
|
self.id = UUID().uuidString
|
||||||
|
|
@ -293,31 +313,31 @@ public final class TestCounter: TestMetric, CounterHandler, Equatable {
|
||||||
|
|
||||||
public func increment(by amount: Int64) {
|
public func increment(by amount: Int64) {
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
self.values.append((Date(), amount))
|
self._values.append((Date(), amount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reset() {
|
public func reset() {
|
||||||
return self.lock.withLock {
|
return self.lock.withLock {
|
||||||
self.values = []
|
self._values = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var lastValue: Int64? {
|
public var lastValue: Int64? {
|
||||||
return self.lock.withLock {
|
return self.values.last?.1
|
||||||
return values.last?.1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var totalValue: Int64 {
|
public var totalValue: Int64 {
|
||||||
return self.lock.withLock {
|
return self.values.map { $0.1 }.reduce(0, +)
|
||||||
return values.map { $0.1 }.reduce(0, +)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var last: (Date, Int64)? {
|
public var last: (Date, Int64)? {
|
||||||
|
return self.values.last
|
||||||
|
}
|
||||||
|
|
||||||
|
var values: [(Date, Int64)] {
|
||||||
return self.lock.withLock {
|
return self.lock.withLock {
|
||||||
values.last
|
self._values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -336,7 +356,7 @@ public final class TestMeter: TestMetric, MeterHandler, Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
let lock = NSLock()
|
let lock = NSLock()
|
||||||
private var values = [(Date, Double)]()
|
private var _values = [(Date, Double)]()
|
||||||
|
|
||||||
init(label: String, dimensions: [(String, String)]) {
|
init(label: String, dimensions: [(String, String)]) {
|
||||||
self.id = NSUUID().uuidString
|
self.id = NSUUID().uuidString
|
||||||
|
|
@ -350,36 +370,38 @@ public final class TestMeter: TestMetric, MeterHandler, Equatable {
|
||||||
|
|
||||||
public func set(_ value: Double) {
|
public func set(_ value: Double) {
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
// this may loose precision but good enough as an example
|
// this may lose precision but good enough as an example
|
||||||
values.append((Date(), Double(value)))
|
_values.append((Date(), Double(value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func increment(by amount: Double) {
|
public func increment(by amount: Double) {
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
let lastValue = self.values.last?.1 ?? 0
|
let lastValue = self._values.last?.1 ?? 0
|
||||||
let newValue = lastValue - amount
|
let newValue = lastValue - amount
|
||||||
values.append((Date(), Double(newValue)))
|
_values.append((Date(), Double(newValue)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func decrement(by amount: Double) {
|
public func decrement(by amount: Double) {
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
let lastValue = self.values.last?.1 ?? 0
|
let lastValue = self._values.last?.1 ?? 0
|
||||||
let newValue = lastValue - amount
|
let newValue = lastValue - amount
|
||||||
values.append((Date(), Double(newValue)))
|
_values.append((Date(), Double(newValue)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var lastValue: Double? {
|
public var lastValue: Double? {
|
||||||
return self.lock.withLock {
|
return self.last?.1
|
||||||
values.last?.1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var last: (Date, Double)? {
|
public var last: (Date, Double)? {
|
||||||
|
return self.values.last
|
||||||
|
}
|
||||||
|
|
||||||
|
var values: [(Date, Double)] {
|
||||||
return self.lock.withLock {
|
return self.lock.withLock {
|
||||||
values.last
|
self._values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -399,7 +421,7 @@ public final class TestRecorder: TestMetric, RecorderHandler, Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
let lock = NSLock()
|
let lock = NSLock()
|
||||||
private var values = [(Date, Double)]()
|
private var _values = [(Date, Double)]()
|
||||||
|
|
||||||
init(label: String, dimensions: [(String, String)], aggregate: Bool) {
|
init(label: String, dimensions: [(String, String)], aggregate: Bool) {
|
||||||
self.id = NSUUID().uuidString
|
self.id = NSUUID().uuidString
|
||||||
|
|
@ -414,20 +436,22 @@ public final class TestRecorder: TestMetric, RecorderHandler, Equatable {
|
||||||
|
|
||||||
public func record(_ value: Double) {
|
public func record(_ value: Double) {
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
// this may loose precision but good enough as an example
|
// this may lose precision but good enough as an example
|
||||||
values.append((Date(), Double(value)))
|
_values.append((Date(), Double(value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var lastValue: Double? {
|
public var lastValue: Double? {
|
||||||
return self.lock.withLock {
|
return self.last?.1
|
||||||
values.last?.1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var last: (Date, Double)? {
|
public var last: (Date, Double)? {
|
||||||
|
return self.values.last
|
||||||
|
}
|
||||||
|
|
||||||
|
var values: [(Date, Double)] {
|
||||||
return self.lock.withLock {
|
return self.lock.withLock {
|
||||||
values.last
|
self._values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -480,19 +504,19 @@ public final class TestTimer: TestMetric, TimerHandler, Equatable {
|
||||||
|
|
||||||
public var lastValue: Int64? {
|
public var lastValue: Int64? {
|
||||||
return self.lock.withLock {
|
return self.lock.withLock {
|
||||||
return _values.last?.1
|
return self._values.last?.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var values: [Int64] {
|
public var values: [Int64] {
|
||||||
return self.lock.withLock {
|
return self.lock.withLock {
|
||||||
return _values.map { $0.1 }
|
return self._values.map { $0.1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var last: (Date, Int64)? {
|
public var last: (Date, Int64)? {
|
||||||
return self.lock.withLock {
|
return self.lock.withLock {
|
||||||
return _values.last
|
return self._values.last
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
@testable import CoreMetrics
|
@testable import CoreMetrics
|
||||||
|
@testable import MetricsTestKit
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class MetricsTests: XCTestCase {
|
class MetricsTests: XCTestCase {
|
||||||
|
|
@ -46,7 +47,7 @@ class MetricsTests: XCTestCase {
|
||||||
let name = "counter-\(UUID().uuidString)"
|
let name = "counter-\(UUID().uuidString)"
|
||||||
let value = Int.random(in: Int.min ... Int.max)
|
let value = Int.random(in: Int.min ... Int.max)
|
||||||
Counter(label: name).increment(by: value)
|
Counter(label: name).increment(by: value)
|
||||||
let counter = metrics.counters[name] as! TestCounter
|
let counter = try metrics.expectCounter(name)
|
||||||
XCTAssertEqual(counter.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(counter.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(counter.values[0].1, Int64(value), "expected value to match")
|
XCTAssertEqual(counter.values[0].1, Int64(value), "expected value to match")
|
||||||
counter.reset()
|
counter.reset()
|
||||||
|
|
@ -59,7 +60,7 @@ class MetricsTests: XCTestCase {
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
let label = "\(#function)-fp-counter-\(UUID())"
|
let label = "\(#function)-fp-counter-\(UUID())"
|
||||||
let fpCounter = FloatingPointCounter(label: label)
|
let fpCounter = FloatingPointCounter(label: label)
|
||||||
let counter = metrics.counters[label] as! TestCounter
|
let counter = try metrics.expectCounter(label)
|
||||||
fpCounter.increment(by: Double.nan)
|
fpCounter.increment(by: Double.nan)
|
||||||
fpCounter.increment(by: Double.signalingNaN)
|
fpCounter.increment(by: Double.signalingNaN)
|
||||||
XCTAssertEqual(counter.values.count, 0, "expected nan values to be ignored")
|
XCTAssertEqual(counter.values.count, 0, "expected nan values to be ignored")
|
||||||
|
|
@ -71,7 +72,7 @@ class MetricsTests: XCTestCase {
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
let label = "\(#function)-fp-counter-\(UUID())"
|
let label = "\(#function)-fp-counter-\(UUID())"
|
||||||
let fpCounter = FloatingPointCounter(label: label)
|
let fpCounter = FloatingPointCounter(label: label)
|
||||||
let counter = metrics.counters[label] as! TestCounter
|
let counter = try metrics.expectCounter(label)
|
||||||
fpCounter.increment(by: Double.infinity)
|
fpCounter.increment(by: Double.infinity)
|
||||||
fpCounter.increment(by: -Double.infinity)
|
fpCounter.increment(by: -Double.infinity)
|
||||||
XCTAssertEqual(counter.values.count, 0, "expected infinite values to be ignored")
|
XCTAssertEqual(counter.values.count, 0, "expected infinite values to be ignored")
|
||||||
|
|
@ -83,7 +84,7 @@ class MetricsTests: XCTestCase {
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
let label = "\(#function)-fp-counter-\(UUID())"
|
let label = "\(#function)-fp-counter-\(UUID())"
|
||||||
let fpCounter = FloatingPointCounter(label: label)
|
let fpCounter = FloatingPointCounter(label: label)
|
||||||
let counter = metrics.counters[label] as! TestCounter
|
let counter = try metrics.expectCounter(label)
|
||||||
fpCounter.increment(by: -100)
|
fpCounter.increment(by: -100)
|
||||||
XCTAssertEqual(counter.values.count, 0, "expected negative values to be ignored")
|
XCTAssertEqual(counter.values.count, 0, "expected negative values to be ignored")
|
||||||
}
|
}
|
||||||
|
|
@ -94,19 +95,19 @@ class MetricsTests: XCTestCase {
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
let label = "\(#function)-fp-counter-\(UUID())"
|
let label = "\(#function)-fp-counter-\(UUID())"
|
||||||
let fpCounter = FloatingPointCounter(label: label)
|
let fpCounter = FloatingPointCounter(label: label)
|
||||||
let counter = metrics.counters[label] as! TestCounter
|
let counter = try metrics.expectCounter(label)
|
||||||
fpCounter.increment(by: 0)
|
fpCounter.increment(by: 0)
|
||||||
fpCounter.increment(by: -0)
|
fpCounter.increment(by: -0)
|
||||||
XCTAssertEqual(counter.values.count, 0, "expected zero values to be ignored")
|
XCTAssertEqual(counter.values.count, 0, "expected zero values to be ignored")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDefaultFloatingPointCounter_ceilsExtremeValues() {
|
func testDefaultFloatingPointCounter_ceilsExtremeValues() throws {
|
||||||
// bootstrap with our test metrics
|
// bootstrap with our test metrics
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
let label = "\(#function)-fp-counter-\(UUID())"
|
let label = "\(#function)-fp-counter-\(UUID())"
|
||||||
let fpCounter = FloatingPointCounter(label: label)
|
let fpCounter = FloatingPointCounter(label: label)
|
||||||
let counter = metrics.counters[label] as! TestCounter
|
let counter = try metrics.expectCounter(label)
|
||||||
// Just larger than Int64
|
// Just larger than Int64
|
||||||
fpCounter.increment(by: Double(sign: .plus, exponent: 63, significand: 1))
|
fpCounter.increment(by: Double(sign: .plus, exponent: 63, significand: 1))
|
||||||
// Much larger than Int64
|
// Much larger than Int64
|
||||||
|
|
@ -116,14 +117,14 @@ class MetricsTests: XCTestCase {
|
||||||
XCTAssertEqual(values, [Int64.max, Int64.max], "expected extremely large values to be replaced with Int64.max")
|
XCTAssertEqual(values, [Int64.max, Int64.max], "expected extremely large values to be replaced with Int64.max")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDefaultFloatingPointCounter_accumulatesFloatingPointDecimalValues() {
|
func testDefaultFloatingPointCounter_accumulatesFloatingPointDecimalValues() throws {
|
||||||
// bootstrap with our test metrics
|
// bootstrap with our test metrics
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
let label = "\(#function)-fp-counter-\(UUID())"
|
let label = "\(#function)-fp-counter-\(UUID())"
|
||||||
let fpCounter = FloatingPointCounter(label: label)
|
let fpCounter = FloatingPointCounter(label: label)
|
||||||
let rawFpCounter = fpCounter._handler as! AccumulatingRoundingFloatingPointCounter
|
let rawFpCounter = fpCounter._handler as! AccumulatingRoundingFloatingPointCounter
|
||||||
let counter = metrics.counters[label] as! TestCounter
|
let counter = try metrics.expectCounter(label)
|
||||||
|
|
||||||
// Increment by a small value (perfectly representable)
|
// Increment by a small value (perfectly representable)
|
||||||
fpCounter.increment(by: 0.75)
|
fpCounter.increment(by: 0.75)
|
||||||
|
|
@ -205,9 +206,9 @@ class MetricsTests: XCTestCase {
|
||||||
let name = "recorder-\(UUID().uuidString)"
|
let name = "recorder-\(UUID().uuidString)"
|
||||||
let value = Double.random(in: Double(Int.min) ... Double(Int.max))
|
let value = Double.random(in: Double(Int.min) ... Double(Int.max))
|
||||||
Recorder(label: name).record(value)
|
Recorder(label: name).record(value)
|
||||||
let recorder = metrics.recorders[name] as! TestRecorder
|
let recorder = try metrics.expectRecorder(name)
|
||||||
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(recorder.values[0].1, value, "expected value to match")
|
XCTAssertEqual(recorder.lastValue, value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimers() throws {
|
func testTimers() throws {
|
||||||
|
|
@ -238,9 +239,9 @@ class MetricsTests: XCTestCase {
|
||||||
let name = "timer-\(UUID().uuidString)"
|
let name = "timer-\(UUID().uuidString)"
|
||||||
let value = Int64.random(in: Int64.min ... Int64.max)
|
let value = Int64.random(in: Int64.min ... Int64.max)
|
||||||
Timer(label: name).recordNanoseconds(value)
|
Timer(label: name).recordNanoseconds(value)
|
||||||
let timer = metrics.timers[name] as! TestTimer
|
let timer = try metrics.expectTimer(name)
|
||||||
XCTAssertEqual(timer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(timer.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(timer.values[0].1, value, "expected value to match")
|
XCTAssertEqual(timer.values[0], value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimerVariants() throws {
|
func testTimerVariants() throws {
|
||||||
|
|
@ -254,22 +255,22 @@ class MetricsTests: XCTestCase {
|
||||||
let nano = Int64.random(in: 0 ... 5)
|
let nano = Int64.random(in: 0 ... 5)
|
||||||
timer.recordNanoseconds(nano)
|
timer.recordNanoseconds(nano)
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[0].1, nano, "expected value to match")
|
XCTAssertEqual(testTimer.values[0], nano, "expected value to match")
|
||||||
// micro
|
// micro
|
||||||
let micro = Int64.random(in: 0 ... 5)
|
let micro = Int64.random(in: 0 ... 5)
|
||||||
timer.recordMicroseconds(micro)
|
timer.recordMicroseconds(micro)
|
||||||
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[1].1, micro * 1000, "expected value to match")
|
XCTAssertEqual(testTimer.values[1], micro * 1000, "expected value to match")
|
||||||
// milli
|
// milli
|
||||||
let milli = Int64.random(in: 0 ... 5)
|
let milli = Int64.random(in: 0 ... 5)
|
||||||
timer.recordMilliseconds(milli)
|
timer.recordMilliseconds(milli)
|
||||||
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[2].1, milli * 1_000_000, "expected value to match")
|
XCTAssertEqual(testTimer.values[2], milli * 1_000_000, "expected value to match")
|
||||||
// seconds
|
// seconds
|
||||||
let sec = Int64.random(in: 0 ... 5)
|
let sec = Int64.random(in: 0 ... 5)
|
||||||
timer.recordSeconds(sec)
|
timer.recordSeconds(sec)
|
||||||
XCTAssertEqual(testTimer.values.count, 4, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 4, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[3].1, sec * 1_000_000_000, "expected value to match")
|
XCTAssertEqual(testTimer.values[3], sec * 1_000_000_000, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimerOverflow() throws {
|
func testTimerOverflow() throws {
|
||||||
|
|
@ -282,31 +283,31 @@ class MetricsTests: XCTestCase {
|
||||||
// nano (integer)
|
// nano (integer)
|
||||||
timer.recordNanoseconds(Int64.max)
|
timer.recordNanoseconds(Int64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[0].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[0], Int64.max, "expected value to match")
|
||||||
// micro (integer)
|
// micro (integer)
|
||||||
timer.recordMicroseconds(Int64.max)
|
timer.recordMicroseconds(Int64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[1].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[1], Int64.max, "expected value to match")
|
||||||
// micro (double)
|
// micro (double)
|
||||||
timer.recordMicroseconds(Double(Int64.max) + 1)
|
timer.recordMicroseconds(Double(Int64.max) + 1)
|
||||||
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[1].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[1], Int64.max, "expected value to match")
|
||||||
// milli (integer)
|
// milli (integer)
|
||||||
timer.recordMilliseconds(Int64.max)
|
timer.recordMilliseconds(Int64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 4, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 4, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[2].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[2], Int64.max, "expected value to match")
|
||||||
// milli (double)
|
// milli (double)
|
||||||
timer.recordMilliseconds(Double(Int64.max) + 1)
|
timer.recordMilliseconds(Double(Int64.max) + 1)
|
||||||
XCTAssertEqual(testTimer.values.count, 5, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 5, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[2].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[2], Int64.max, "expected value to match")
|
||||||
// seconds (integer)
|
// seconds (integer)
|
||||||
timer.recordSeconds(Int64.max)
|
timer.recordSeconds(Int64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 6, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 6, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[3].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[3], Int64.max, "expected value to match")
|
||||||
// seconds (double)
|
// seconds (double)
|
||||||
timer.recordSeconds(Double(Int64.max) * 1)
|
timer.recordSeconds(Double(Int64.max) * 1)
|
||||||
XCTAssertEqual(testTimer.values.count, 7, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 7, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[3].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[3], Int64.max, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimerHandlesUnsignedOverflow() throws {
|
func testTimerHandlesUnsignedOverflow() throws {
|
||||||
|
|
@ -319,19 +320,19 @@ class MetricsTests: XCTestCase {
|
||||||
// nano
|
// nano
|
||||||
timer.recordNanoseconds(UInt64.max)
|
timer.recordNanoseconds(UInt64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[0].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[0], Int64.max, "expected value to match")
|
||||||
// micro
|
// micro
|
||||||
timer.recordMicroseconds(UInt64.max)
|
timer.recordMicroseconds(UInt64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[1].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[1], Int64.max, "expected value to match")
|
||||||
// milli
|
// milli
|
||||||
timer.recordMilliseconds(UInt64.max)
|
timer.recordMilliseconds(UInt64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[2].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[2], Int64.max, "expected value to match")
|
||||||
// seconds
|
// seconds
|
||||||
timer.recordSeconds(UInt64.max)
|
timer.recordSeconds(UInt64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 4, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 4, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[3].1, Int64.max, "expected value to match")
|
XCTAssertEqual(testTimer.values[3], Int64.max, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGauge() throws {
|
func testGauge() throws {
|
||||||
|
|
@ -345,7 +346,7 @@ class MetricsTests: XCTestCase {
|
||||||
gauge.record(value)
|
gauge.record(value)
|
||||||
let recorder = gauge._handler as! TestRecorder
|
let recorder = gauge._handler as! TestRecorder
|
||||||
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(recorder.values[0].1, value, "expected value to match")
|
XCTAssertEqual(recorder.lastValue, value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGaugeBlock() throws {
|
func testGaugeBlock() throws {
|
||||||
|
|
@ -356,9 +357,9 @@ class MetricsTests: XCTestCase {
|
||||||
let name = "gauge-\(UUID().uuidString)"
|
let name = "gauge-\(UUID().uuidString)"
|
||||||
let value = Double.random(in: -1000 ... 1000)
|
let value = Double.random(in: -1000 ... 1000)
|
||||||
Gauge(label: name).record(value)
|
Gauge(label: name).record(value)
|
||||||
let recorder = metrics.recorders[name] as! TestRecorder
|
let recorder = try metrics.expectRecorder(name)
|
||||||
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(recorder.values[0].1, value, "expected value to match")
|
XCTAssertEqual(recorder.lastValue, value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testMeter() throws {
|
func testMeter() throws {
|
||||||
|
|
@ -383,7 +384,7 @@ class MetricsTests: XCTestCase {
|
||||||
let name = "meter-\(NSUUID().uuidString)"
|
let name = "meter-\(NSUUID().uuidString)"
|
||||||
let value = Double.random(in: -1000 ... 1000)
|
let value = Double.random(in: -1000 ... 1000)
|
||||||
Meter(label: name).set(value)
|
Meter(label: name).set(value)
|
||||||
let testMeter = metrics.meters[name] as! TestMeter
|
let testMeter = try metrics.expectMeter(name)
|
||||||
XCTAssertEqual(testMeter.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testMeter.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testMeter.values[0].1, value, "expected value to match")
|
XCTAssertEqual(testMeter.values[0].1, value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
@ -398,15 +399,15 @@ class MetricsTests: XCTestCase {
|
||||||
let muxCounter = Counter(label: name)
|
let muxCounter = Counter(label: name)
|
||||||
muxCounter.increment(by: value)
|
muxCounter.increment(by: value)
|
||||||
factories.forEach { factory in
|
factories.forEach { factory in
|
||||||
let counter = factory.counters.first?.1 as! TestCounter
|
let counter = factory.counters.first
|
||||||
XCTAssertEqual(counter.label, name, "expected label to match")
|
XCTAssertEqual(counter?.label, name, "expected label to match")
|
||||||
XCTAssertEqual(counter.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(counter?.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(counter.values[0].1, Int64(value), "expected value to match")
|
XCTAssertEqual(counter?.lastValue, Int64(value), "expected value to match")
|
||||||
}
|
}
|
||||||
muxCounter.reset()
|
muxCounter.reset()
|
||||||
factories.forEach { factory in
|
factories.forEach { factory in
|
||||||
let counter = factory.counters.first?.1 as! TestCounter
|
let counter = factory.counters.first
|
||||||
XCTAssertEqual(counter.values.count, 0, "expected number of entries to match")
|
XCTAssertEqual(counter?.values.count, 0, "expected number of entries to match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -420,10 +421,10 @@ class MetricsTests: XCTestCase {
|
||||||
let muxMeter = Meter(label: name)
|
let muxMeter = Meter(label: name)
|
||||||
muxMeter.set(value)
|
muxMeter.set(value)
|
||||||
factories.forEach { factory in
|
factories.forEach { factory in
|
||||||
let meter = factory.meters.first?.1 as! TestMeter
|
let meter = factory.meters.first
|
||||||
XCTAssertEqual(meter.label, name, "expected label to match")
|
XCTAssertEqual(meter?.label, name, "expected label to match")
|
||||||
XCTAssertEqual(meter.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(meter?.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(meter.values[0].1, value, "expected value to match")
|
XCTAssertEqual(meter?.values[0].1, value, "expected value to match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -437,10 +438,10 @@ class MetricsTests: XCTestCase {
|
||||||
let muxRecorder = Recorder(label: name)
|
let muxRecorder = Recorder(label: name)
|
||||||
muxRecorder.record(value)
|
muxRecorder.record(value)
|
||||||
factories.forEach { factory in
|
factories.forEach { factory in
|
||||||
let recorder = factory.recorders.first?.1 as! TestRecorder
|
let recorder = factory.recorders.first
|
||||||
XCTAssertEqual(recorder.label, name, "expected label to match")
|
XCTAssertEqual(recorder?.label, name, "expected label to match")
|
||||||
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(recorder?.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(recorder.values[0].1, value, "expected value to match")
|
XCTAssertEqual(recorder?.values[0].1, value, "expected value to match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -454,12 +455,12 @@ class MetricsTests: XCTestCase {
|
||||||
let muxTimer = Timer(label: name, preferredDisplayUnit: .minutes)
|
let muxTimer = Timer(label: name, preferredDisplayUnit: .minutes)
|
||||||
muxTimer.recordSeconds(seconds)
|
muxTimer.recordSeconds(seconds)
|
||||||
factories.forEach { factory in
|
factories.forEach { factory in
|
||||||
let timer = factory.timers.first?.1 as! TestTimer
|
let timer = factory.timers.first
|
||||||
XCTAssertEqual(timer.label, name, "expected label to match")
|
XCTAssertEqual(timer?.label, name, "expected label to match")
|
||||||
XCTAssertEqual(timer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(timer?.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(timer.values[0].1, Int64(seconds * 1_000_000_000), "expected value to match")
|
XCTAssertEqual(timer?.values[0], Int64(seconds * 1_000_000_000), "expected value to match")
|
||||||
XCTAssertEqual(timer.displayUnit, .minutes, "expected value to match")
|
XCTAssertEqual(timer?.displayUnit, .minutes, "expected value to match")
|
||||||
XCTAssertEqual(timer.retrieveValueInPreferredUnit(atIndex: 0), Double(seconds) / 60.0, "seconds should be returned as minutes")
|
XCTAssertEqual(timer?.retrieveValueInPreferredUnit(atIndex: 0), Double(seconds) / 60.0, "seconds should be returned as minutes")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -577,7 +578,7 @@ class MetricsTests: XCTestCase {
|
||||||
|
|
||||||
let testTimer = timer._handler as! TestTimer
|
let testTimer = timer._handler as! TestTimer
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values.first!.1, value, "expected value to match")
|
XCTAssertEqual(testTimer.values.first, value, "expected value to match")
|
||||||
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||||
|
|
||||||
let identity = ObjectIdentifier(timer)
|
let identity = ObjectIdentifier(timer)
|
||||||
|
|
@ -588,7 +589,7 @@ class MetricsTests: XCTestCase {
|
||||||
timerAgain.recordNanoseconds(value)
|
timerAgain.recordNanoseconds(value)
|
||||||
let testTimerAgain = timerAgain._handler as! TestTimer
|
let testTimerAgain = timerAgain._handler as! TestTimer
|
||||||
XCTAssertEqual(testTimerAgain.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimerAgain.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimerAgain.values.first!.1, value, "expected value to match")
|
XCTAssertEqual(testTimerAgain.values.first, value, "expected value to match")
|
||||||
|
|
||||||
let identityAgain = ObjectIdentifier(timerAgain)
|
let identityAgain = ObjectIdentifier(timerAgain)
|
||||||
XCTAssertNotEqual(identity, identityAgain, "since the cached metric was released, the created a new should have a different identity")
|
XCTAssertNotEqual(identity, identityAgain, "since the cached metric was released, the created a new should have a different identity")
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
@testable import CoreMetrics
|
@testable import CoreMetrics
|
||||||
@testable import Metrics
|
@testable import Metrics
|
||||||
|
@testable import MetricsTestKit
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class MetricsExtensionsTests: XCTestCase {
|
class MetricsExtensionsTests: XCTestCase {
|
||||||
|
|
@ -27,9 +28,9 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
Timer.measure(label: name) {
|
Timer.measure(label: name) {
|
||||||
Thread.sleep(forTimeInterval: delay)
|
Thread.sleep(forTimeInterval: delay)
|
||||||
}
|
}
|
||||||
let timer = metrics.timers[name] as! TestTimer
|
let timer = try metrics.expectTimer(name)
|
||||||
XCTAssertEqual(1, timer.values.count, "expected number of entries to match")
|
XCTAssertEqual(1, timer.values.count, "expected number of entries to match")
|
||||||
XCTAssertGreaterThan(timer.values[0].1, Int64(delay * 1_000_000_000), "expected delay to match")
|
XCTAssertGreaterThan(timer.values[0], Int64(delay * 1_000_000_000), "expected delay to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimerWithTimeInterval() throws {
|
func testTimerWithTimeInterval() throws {
|
||||||
|
|
@ -42,7 +43,7 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
let timeInterval = TimeInterval(Double.random(in: 1 ... 500))
|
let timeInterval = TimeInterval(Double.random(in: 1 ... 500))
|
||||||
timer.record(timeInterval)
|
timer.record(timeInterval)
|
||||||
XCTAssertEqual(1, testTimer.values.count, "expected number of entries to match")
|
XCTAssertEqual(1, testTimer.values.count, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[0].1, Int64(timeInterval * 1_000_000_000), "expected value to match")
|
XCTAssertEqual(testTimer.values[0], Int64(timeInterval * 1_000_000_000), "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimerWithDispatchTime() throws {
|
func testTimerWithDispatchTime() throws {
|
||||||
|
|
@ -56,26 +57,26 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
let nano = DispatchTimeInterval.nanoseconds(Int.random(in: 1 ... 500))
|
let nano = DispatchTimeInterval.nanoseconds(Int.random(in: 1 ... 500))
|
||||||
timer.record(nano)
|
timer.record(nano)
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(Int(testTimer.values[0].1), nano.nano(), "expected value to match")
|
XCTAssertEqual(Int(testTimer.values[0]), nano.nano(), "expected value to match")
|
||||||
// micro
|
// micro
|
||||||
let micro = DispatchTimeInterval.microseconds(Int.random(in: 1 ... 500))
|
let micro = DispatchTimeInterval.microseconds(Int.random(in: 1 ... 500))
|
||||||
timer.record(micro)
|
timer.record(micro)
|
||||||
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
||||||
XCTAssertEqual(Int(testTimer.values[1].1), micro.nano(), "expected value to match")
|
XCTAssertEqual(Int(testTimer.values[1]), micro.nano(), "expected value to match")
|
||||||
// milli
|
// milli
|
||||||
let milli = DispatchTimeInterval.milliseconds(Int.random(in: 1 ... 500))
|
let milli = DispatchTimeInterval.milliseconds(Int.random(in: 1 ... 500))
|
||||||
timer.record(milli)
|
timer.record(milli)
|
||||||
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
||||||
XCTAssertEqual(Int(testTimer.values[2].1), milli.nano(), "expected value to match")
|
XCTAssertEqual(Int(testTimer.values[2]), milli.nano(), "expected value to match")
|
||||||
// seconds
|
// seconds
|
||||||
let sec = DispatchTimeInterval.seconds(Int.random(in: 1 ... 500))
|
let sec = DispatchTimeInterval.seconds(Int.random(in: 1 ... 500))
|
||||||
timer.record(sec)
|
timer.record(sec)
|
||||||
XCTAssertEqual(testTimer.values.count, 4, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 4, "expected number of entries to match")
|
||||||
XCTAssertEqual(Int(testTimer.values[3].1), sec.nano(), "expected value to match")
|
XCTAssertEqual(Int(testTimer.values[3]), sec.nano(), "expected value to match")
|
||||||
// never
|
// never
|
||||||
timer.record(DispatchTimeInterval.never)
|
timer.record(DispatchTimeInterval.never)
|
||||||
XCTAssertEqual(testTimer.values.count, 5, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 5, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[4].1, 0, "expected value to match")
|
XCTAssertEqual(testTimer.values[4], 0, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimerWithDispatchTimeInterval() {
|
func testTimerWithDispatchTimeInterval() {
|
||||||
|
|
@ -91,7 +92,7 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
|
|
||||||
let testTimer = timer._handler as! TestTimer
|
let testTimer = timer._handler as! TestTimer
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(UInt64(testTimer.values.first!.1), end.uptimeNanoseconds - start.uptimeNanoseconds, "expected value to match")
|
XCTAssertEqual(UInt64(testTimer.values.first!), end.uptimeNanoseconds - start.uptimeNanoseconds, "expected value to match")
|
||||||
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +108,7 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
|
|
||||||
let testTimer = timer._handler as! TestTimer
|
let testTimer = timer._handler as! TestTimer
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values.first!.1, value, "expected value to match")
|
XCTAssertEqual(testTimer.values.first, value, "expected value to match")
|
||||||
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||||
|
|
||||||
let secondsName = "timer-seconds-\(UUID().uuidString)"
|
let secondsName = "timer-seconds-\(UUID().uuidString)"
|
||||||
|
|
|
||||||
|
|
@ -1,268 +0,0 @@
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// This source file is part of the Swift Metrics API open source project
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018-2019 Apple Inc. and the Swift Metrics API project authors
|
|
||||||
// Licensed under Apache License v2.0
|
|
||||||
//
|
|
||||||
// See LICENSE.txt for license information
|
|
||||||
// See CONTRIBUTORS.txt for the list of Swift Metrics API project authors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
@testable import CoreMetrics
|
|
||||||
@testable import class CoreMetrics.Timer
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Metrics factory which allows inspecting recorded metrics programmatically.
|
|
||||||
/// Only intended for tests of the Metrics API itself.
|
|
||||||
internal final class TestMetrics: MetricsFactory {
|
|
||||||
private let lock = NSLock()
|
|
||||||
var counters = [String: CounterHandler]()
|
|
||||||
var meters = [String: MeterHandler]()
|
|
||||||
var recorders = [String: RecorderHandler]()
|
|
||||||
var timers = [String: TimerHandler]()
|
|
||||||
|
|
||||||
func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
|
|
||||||
return self.make(label: label, dimensions: dimensions, registry: &self.counters, maker: TestCounter.init)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
|
|
||||||
return self.make(label: label, dimensions: dimensions, registry: &self.meters, maker: TestMeter.init)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
|
|
||||||
let maker = { (label: String, dimensions: [(String, String)]) -> RecorderHandler in
|
|
||||||
TestRecorder(label: label, dimensions: dimensions, aggregate: aggregate)
|
|
||||||
}
|
|
||||||
return self.make(label: label, dimensions: dimensions, registry: &self.recorders, maker: maker)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
|
|
||||||
return self.make(label: label, dimensions: dimensions, registry: &self.timers, maker: TestTimer.init)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func make<Item>(label: String, dimensions: [(String, String)], registry: inout [String: Item], maker: (String, [(String, String)]) -> Item) -> Item {
|
|
||||||
let item = maker(label, dimensions)
|
|
||||||
return self.lock.withLock {
|
|
||||||
registry[label] = item
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func destroyCounter(_ handler: CounterHandler) {
|
|
||||||
if let testCounter = handler as? TestCounter {
|
|
||||||
self.lock.withLock { () in
|
|
||||||
self.counters.removeValue(forKey: testCounter.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func destroyMeter(_ handler: MeterHandler) {
|
|
||||||
if let testMeter = handler as? TestMeter {
|
|
||||||
self.lock.withLock { () in
|
|
||||||
self.meters.removeValue(forKey: testMeter.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func destroyRecorder(_ handler: RecorderHandler) {
|
|
||||||
if let testRecorder = handler as? TestRecorder {
|
|
||||||
self.lock.withLock { () in
|
|
||||||
self.recorders.removeValue(forKey: testRecorder.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func destroyTimer(_ handler: TimerHandler) {
|
|
||||||
if let testTimer = handler as? TestTimer {
|
|
||||||
self.lock.withLock { () in
|
|
||||||
self.timers.removeValue(forKey: testTimer.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal final class TestCounter: CounterHandler, Equatable {
|
|
||||||
let id: String
|
|
||||||
let label: String
|
|
||||||
let dimensions: [(String, String)]
|
|
||||||
|
|
||||||
let lock = NSLock()
|
|
||||||
var values = [(Date, Int64)]()
|
|
||||||
|
|
||||||
init(label: String, dimensions: [(String, String)]) {
|
|
||||||
self.id = UUID().uuidString
|
|
||||||
self.label = label
|
|
||||||
self.dimensions = dimensions
|
|
||||||
}
|
|
||||||
|
|
||||||
func increment(by amount: Int64) {
|
|
||||||
self.lock.withLock {
|
|
||||||
self.values.append((Date(), amount))
|
|
||||||
}
|
|
||||||
print("adding \(amount) to \(self.label)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func reset() {
|
|
||||||
self.lock.withLock {
|
|
||||||
self.values = []
|
|
||||||
}
|
|
||||||
print("reseting \(self.label)")
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func == (lhs: TestCounter, rhs: TestCounter) -> Bool {
|
|
||||||
return lhs.id == rhs.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal final class TestMeter: MeterHandler, Equatable {
|
|
||||||
let id: String
|
|
||||||
let label: String
|
|
||||||
let dimensions: [(String, String)]
|
|
||||||
|
|
||||||
let lock = NSLock()
|
|
||||||
var values = [(Date, Double)]()
|
|
||||||
|
|
||||||
init(label: String, dimensions: [(String, String)]) {
|
|
||||||
self.id = NSUUID().uuidString
|
|
||||||
self.label = label
|
|
||||||
self.dimensions = dimensions
|
|
||||||
}
|
|
||||||
|
|
||||||
func set(_ value: Int64) {
|
|
||||||
self.set(Double(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
func set(_ value: Double) {
|
|
||||||
self.lock.withLock {
|
|
||||||
// this may loose precision but good enough as an example
|
|
||||||
values.append((Date(), Double(value)))
|
|
||||||
}
|
|
||||||
print("setting \(value) in \(self.label)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func increment(by amount: Double) {
|
|
||||||
let newValue: Double = self.lock.withLock {
|
|
||||||
let lastValue = self.values.last?.1 ?? 0
|
|
||||||
let newValue = lastValue + amount
|
|
||||||
values.append((Date(), Double(newValue)))
|
|
||||||
return newValue
|
|
||||||
}
|
|
||||||
print("recording \(newValue) in \(self.label)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func decrement(by amount: Double) {
|
|
||||||
let newValue: Double = self.lock.withLock {
|
|
||||||
let lastValue = self.values.last?.1 ?? 0
|
|
||||||
let newValue = lastValue - amount
|
|
||||||
values.append((Date(), Double(newValue)))
|
|
||||||
return newValue
|
|
||||||
}
|
|
||||||
print("recording \(newValue) in \(self.label)")
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func == (lhs: TestMeter, rhs: TestMeter) -> Bool {
|
|
||||||
return lhs.id == rhs.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal final class TestRecorder: RecorderHandler, Equatable {
|
|
||||||
let id: String
|
|
||||||
let label: String
|
|
||||||
let dimensions: [(String, String)]
|
|
||||||
let aggregate: Bool
|
|
||||||
|
|
||||||
let lock = NSLock()
|
|
||||||
var values = [(Date, Double)]()
|
|
||||||
|
|
||||||
init(label: String, dimensions: [(String, String)], aggregate: Bool) {
|
|
||||||
self.id = NSUUID().uuidString
|
|
||||||
self.label = label
|
|
||||||
self.dimensions = dimensions
|
|
||||||
self.aggregate = aggregate
|
|
||||||
}
|
|
||||||
|
|
||||||
func record(_ value: Int64) {
|
|
||||||
self.record(Double(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
func record(_ value: Double) {
|
|
||||||
self.lock.withLock {
|
|
||||||
// this may loose precision but good enough as an example
|
|
||||||
values.append((Date(), Double(value)))
|
|
||||||
}
|
|
||||||
print("recording \(value) in \(self.label)")
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func == (lhs: TestRecorder, rhs: TestRecorder) -> Bool {
|
|
||||||
return lhs.id == rhs.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal final class TestTimer: TimerHandler, Equatable {
|
|
||||||
let id: String
|
|
||||||
let label: String
|
|
||||||
var displayUnit: TimeUnit?
|
|
||||||
let dimensions: [(String, String)]
|
|
||||||
|
|
||||||
let lock = NSLock()
|
|
||||||
var values = [(Date, Int64)]()
|
|
||||||
|
|
||||||
init(label: String, dimensions: [(String, String)]) {
|
|
||||||
self.id = UUID().uuidString
|
|
||||||
self.label = label
|
|
||||||
self.displayUnit = nil
|
|
||||||
self.dimensions = dimensions
|
|
||||||
}
|
|
||||||
|
|
||||||
func preferDisplayUnit(_ unit: TimeUnit) {
|
|
||||||
self.lock.withLock {
|
|
||||||
self.displayUnit = unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func retrieveValueInPreferredUnit(atIndex i: Int) -> Double {
|
|
||||||
return self.lock.withLock {
|
|
||||||
let value = values[i].1
|
|
||||||
guard let displayUnit = self.displayUnit else {
|
|
||||||
return Double(value)
|
|
||||||
}
|
|
||||||
return Double(value) / Double(displayUnit.scaleFromNanoseconds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordNanoseconds(_ duration: Int64) {
|
|
||||||
self.lock.withLock {
|
|
||||||
values.append((Date(), duration))
|
|
||||||
}
|
|
||||||
print("recording \(duration) \(self.label)")
|
|
||||||
}
|
|
||||||
|
|
||||||
static func == (lhs: TestTimer, rhs: TestTimer) -> Bool {
|
|
||||||
return lhs.id == rhs.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NSLock {
|
|
||||||
@discardableResult
|
|
||||||
fileprivate func withLock<T>(_ body: () -> T) -> T {
|
|
||||||
self.lock()
|
|
||||||
defer {
|
|
||||||
self.unlock()
|
|
||||||
}
|
|
||||||
return body()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Sendable support
|
|
||||||
|
|
||||||
#if compiler(>=5.6)
|
|
||||||
// ideally we would not be using @unchecked here, but concurrency-safety checks do not recognize locks
|
|
||||||
extension TestCounter: @unchecked Sendable {}
|
|
||||||
extension TestMeter: @unchecked Sendable {}
|
|
||||||
extension TestRecorder: @unchecked Sendable {}
|
|
||||||
extension TestTimer: @unchecked Sendable {}
|
|
||||||
#endif
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
@testable import CoreMetrics
|
@testable import CoreMetrics
|
||||||
import Dispatch
|
import Dispatch
|
||||||
|
@testable import MetricsTestKit
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class SendableTest: XCTestCase {
|
class SendableTest: XCTestCase {
|
||||||
|
|
@ -90,7 +91,7 @@ class SendableTest: XCTestCase {
|
||||||
let task = Task.detached { () -> [Int64] in
|
let task = Task.detached { () -> [Int64] in
|
||||||
timer.recordNanoseconds(value)
|
timer.recordNanoseconds(value)
|
||||||
let handler = timer._handler as! TestTimer
|
let handler = timer._handler as! TestTimer
|
||||||
return handler.values.map { $0.1 }
|
return handler.values
|
||||||
}
|
}
|
||||||
let values = await task.value
|
let values = await task.value
|
||||||
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue