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(
|
||||
name: "MetricsTests",
|
||||
dependencies: ["Metrics"]
|
||||
dependencies: ["Metrics", "MetricsTestKit"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
|||
),
|
||||
.testTarget(
|
||||
name: "MetricsTests",
|
||||
dependencies: ["Metrics"]
|
||||
dependencies: ["Metrics", "MetricsTestKit"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
|||
),
|
||||
.testTarget(
|
||||
name: "MetricsTests",
|
||||
dependencies: ["Metrics"]
|
||||
dependencies: ["Metrics", "MetricsTestKit"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
|||
),
|
||||
.testTarget(
|
||||
name: "MetricsTests",
|
||||
dependencies: ["Metrics"]
|
||||
dependencies: ["Metrics", "MetricsTestKit"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
|||
),
|
||||
.testTarget(
|
||||
name: "MetricsTests",
|
||||
dependencies: ["Metrics"]
|
||||
dependencies: ["Metrics", "MetricsTestKit"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
|||
),
|
||||
.testTarget(
|
||||
name: "MetricsTests",
|
||||
dependencies: ["Metrics"]
|
||||
dependencies: ["Metrics", "MetricsTestKit"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
|||
),
|
||||
.testTarget(
|
||||
name: "MetricsTests",
|
||||
dependencies: ["Metrics"]
|
||||
dependencies: ["Metrics", "MetricsTestKit"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ let package = Package(
|
|||
),
|
||||
.testTarget(
|
||||
name: "MetricsTests",
|
||||
dependencies: ["Metrics"]
|
||||
dependencies: ["Metrics", "MetricsTestKit"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -48,10 +48,10 @@ public final class TestMetrics: MetricsFactory {
|
|||
let dimensions: [(String, String)]
|
||||
}
|
||||
|
||||
private var counters = [FullKey: CounterHandler]()
|
||||
private var meters = [FullKey: MeterHandler]()
|
||||
private var recorders = [FullKey: RecorderHandler]()
|
||||
private var timers = [FullKey: TimerHandler]()
|
||||
private var _counters = [FullKey: TestCounter]()
|
||||
private var _meters = [FullKey: TestMeter]()
|
||||
private var _recorders = [FullKey: TestRecorder]()
|
||||
private var _timers = [FullKey: TestTimer]()
|
||||
|
||||
public init() {
|
||||
// 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.
|
||||
public func reset() {
|
||||
self.lock.withLock {
|
||||
self.counters = [:]
|
||||
self.meters = [:]
|
||||
self.recorders = [:]
|
||||
self.timers = [:]
|
||||
self._counters = [:]
|
||||
self._recorders = [:]
|
||||
self._meters = [:]
|
||||
self._timers = [:]
|
||||
}
|
||||
}
|
||||
|
||||
public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
public func destroyCounter(_ handler: CounterHandler) {
|
||||
if let testCounter = handler as? TestCounter {
|
||||
self.lock.withLock { () in
|
||||
self.counters.removeValue(forKey: testCounter.key)
|
||||
self.lock.withLock {
|
||||
self._counters.removeValue(forKey: testCounter.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -123,23 +123,23 @@ public final class TestMetrics: MetricsFactory {
|
|||
public func destroyMeter(_ handler: MeterHandler) {
|
||||
if let testMeter = handler as? TestMeter {
|
||||
self.lock.withLock { () in
|
||||
self.meters.removeValue(forKey: testMeter.key)
|
||||
self._meters.removeValue(forKey: testMeter.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func destroyRecorder(_ handler: RecorderHandler) {
|
||||
if let testRecorder = handler as? TestRecorder {
|
||||
self.lock.withLock { () in
|
||||
self.recorders.removeValue(forKey: testRecorder.key)
|
||||
self.lock.withLock {
|
||||
self._recorders.removeValue(forKey: testRecorder.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func destroyTimer(_ handler: TimerHandler) {
|
||||
if let testTimer = handler as? TestTimer {
|
||||
self.lock.withLock { () in
|
||||
self.timers.removeValue(forKey: testTimer.key)
|
||||
self.lock.withLock {
|
||||
self._timers.removeValue(forKey: testTimer.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -174,17 +174,22 @@ extension TestMetrics {
|
|||
|
||||
public func expectCounter(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestCounter {
|
||||
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)
|
||||
}
|
||||
guard let testCounter = maybeCounter as? TestCounter else {
|
||||
throw TestMetricsError.illegalMetricType(metric: maybeCounter, expected: "\(TestCounter.self)")
|
||||
}
|
||||
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
|
||||
|
||||
public func expectGauge(_ metric: Gauge) throws -> TestRecorder {
|
||||
|
|
@ -206,17 +211,22 @@ extension TestMetrics {
|
|||
|
||||
public func expectMeter(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestMeter {
|
||||
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)
|
||||
}
|
||||
guard let testMeter = maybeMeter as? TestMeter else {
|
||||
throw TestMetricsError.illegalMetricType(metric: maybeMeter, expected: "\(TestMeter.self)")
|
||||
}
|
||||
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
|
||||
|
||||
public func expectRecorder(_ metric: Recorder) throws -> TestRecorder {
|
||||
|
|
@ -228,17 +238,22 @@ extension TestMetrics {
|
|||
|
||||
public func expectRecorder(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestRecorder {
|
||||
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)
|
||||
}
|
||||
guard let testRecorder = maybeRecorder as? TestRecorder else {
|
||||
throw TestMetricsError.illegalMetricType(metric: maybeRecorder, expected: "\(TestRecorder.self)")
|
||||
}
|
||||
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
|
||||
|
||||
public func expectTimer(_ metric: CoreMetrics.Timer) throws -> TestTimer {
|
||||
|
|
@ -250,16 +265,21 @@ extension TestMetrics {
|
|||
|
||||
public func expectTimer(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestTimer {
|
||||
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)
|
||||
}
|
||||
guard let testTimer = maybeTimer as? TestTimer else {
|
||||
throw TestMetricsError.illegalMetricType(metric: maybeTimer, expected: "\(TestTimer.self)")
|
||||
}
|
||||
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
|
||||
|
|
@ -283,7 +303,7 @@ public final class TestCounter: TestMetric, CounterHandler, Equatable {
|
|||
}
|
||||
|
||||
let lock = NSLock()
|
||||
private var values = [(Date, Int64)]()
|
||||
private var _values = [(Date, Int64)]()
|
||||
|
||||
init(label: String, dimensions: [(String, String)]) {
|
||||
self.id = UUID().uuidString
|
||||
|
|
@ -293,31 +313,31 @@ public final class TestCounter: TestMetric, CounterHandler, Equatable {
|
|||
|
||||
public func increment(by amount: Int64) {
|
||||
self.lock.withLock {
|
||||
self.values.append((Date(), amount))
|
||||
self._values.append((Date(), amount))
|
||||
}
|
||||
}
|
||||
|
||||
public func reset() {
|
||||
return self.lock.withLock {
|
||||
self.values = []
|
||||
self._values = []
|
||||
}
|
||||
}
|
||||
|
||||
public var lastValue: Int64? {
|
||||
return self.lock.withLock {
|
||||
return values.last?.1
|
||||
}
|
||||
return self.values.last?.1
|
||||
}
|
||||
|
||||
public var totalValue: Int64 {
|
||||
return self.lock.withLock {
|
||||
return values.map { $0.1 }.reduce(0, +)
|
||||
}
|
||||
return self.values.map { $0.1 }.reduce(0, +)
|
||||
}
|
||||
|
||||
public var last: (Date, Int64)? {
|
||||
return self.values.last
|
||||
}
|
||||
|
||||
var values: [(Date, Int64)] {
|
||||
return self.lock.withLock {
|
||||
values.last
|
||||
self._values
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -336,7 +356,7 @@ public final class TestMeter: TestMetric, MeterHandler, Equatable {
|
|||
}
|
||||
|
||||
let lock = NSLock()
|
||||
private var values = [(Date, Double)]()
|
||||
private var _values = [(Date, Double)]()
|
||||
|
||||
init(label: String, dimensions: [(String, String)]) {
|
||||
self.id = NSUUID().uuidString
|
||||
|
|
@ -350,36 +370,38 @@ public final class TestMeter: TestMetric, MeterHandler, Equatable {
|
|||
|
||||
public func set(_ value: Double) {
|
||||
self.lock.withLock {
|
||||
// this may loose precision but good enough as an example
|
||||
values.append((Date(), Double(value)))
|
||||
// this may lose precision but good enough as an example
|
||||
_values.append((Date(), Double(value)))
|
||||
}
|
||||
}
|
||||
|
||||
public func increment(by amount: Double) {
|
||||
self.lock.withLock {
|
||||
let lastValue = self.values.last?.1 ?? 0
|
||||
let lastValue = self._values.last?.1 ?? 0
|
||||
let newValue = lastValue - amount
|
||||
values.append((Date(), Double(newValue)))
|
||||
_values.append((Date(), Double(newValue)))
|
||||
}
|
||||
}
|
||||
|
||||
public func decrement(by amount: Double) {
|
||||
self.lock.withLock {
|
||||
let lastValue = self.values.last?.1 ?? 0
|
||||
let lastValue = self._values.last?.1 ?? 0
|
||||
let newValue = lastValue - amount
|
||||
values.append((Date(), Double(newValue)))
|
||||
_values.append((Date(), Double(newValue)))
|
||||
}
|
||||
}
|
||||
|
||||
public var lastValue: Double? {
|
||||
return self.lock.withLock {
|
||||
values.last?.1
|
||||
}
|
||||
return self.last?.1
|
||||
}
|
||||
|
||||
public var last: (Date, Double)? {
|
||||
return self.values.last
|
||||
}
|
||||
|
||||
var values: [(Date, Double)] {
|
||||
return self.lock.withLock {
|
||||
values.last
|
||||
self._values
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -399,7 +421,7 @@ public final class TestRecorder: TestMetric, RecorderHandler, Equatable {
|
|||
}
|
||||
|
||||
let lock = NSLock()
|
||||
private var values = [(Date, Double)]()
|
||||
private var _values = [(Date, Double)]()
|
||||
|
||||
init(label: String, dimensions: [(String, String)], aggregate: Bool) {
|
||||
self.id = NSUUID().uuidString
|
||||
|
|
@ -414,20 +436,22 @@ public final class TestRecorder: TestMetric, RecorderHandler, Equatable {
|
|||
|
||||
public func record(_ value: Double) {
|
||||
self.lock.withLock {
|
||||
// this may loose precision but good enough as an example
|
||||
values.append((Date(), Double(value)))
|
||||
// this may lose precision but good enough as an example
|
||||
_values.append((Date(), Double(value)))
|
||||
}
|
||||
}
|
||||
|
||||
public var lastValue: Double? {
|
||||
return self.lock.withLock {
|
||||
values.last?.1
|
||||
}
|
||||
return self.last?.1
|
||||
}
|
||||
|
||||
public var last: (Date, Double)? {
|
||||
return self.values.last
|
||||
}
|
||||
|
||||
var values: [(Date, Double)] {
|
||||
return self.lock.withLock {
|
||||
values.last
|
||||
self._values
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -480,19 +504,19 @@ public final class TestTimer: TestMetric, TimerHandler, Equatable {
|
|||
|
||||
public var lastValue: Int64? {
|
||||
return self.lock.withLock {
|
||||
return _values.last?.1
|
||||
return self._values.last?.1
|
||||
}
|
||||
}
|
||||
|
||||
public var values: [Int64] {
|
||||
return self.lock.withLock {
|
||||
return _values.map { $0.1 }
|
||||
return self._values.map { $0.1 }
|
||||
}
|
||||
}
|
||||
|
||||
public var last: (Date, Int64)? {
|
||||
return self.lock.withLock {
|
||||
return _values.last
|
||||
return self._values.last
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@testable import CoreMetrics
|
||||
@testable import MetricsTestKit
|
||||
import XCTest
|
||||
|
||||
class MetricsTests: XCTestCase {
|
||||
|
|
@ -46,7 +47,7 @@ class MetricsTests: XCTestCase {
|
|||
let name = "counter-\(UUID().uuidString)"
|
||||
let value = Int.random(in: Int.min ... Int.max)
|
||||
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[0].1, Int64(value), "expected value to match")
|
||||
counter.reset()
|
||||
|
|
@ -59,7 +60,7 @@ class MetricsTests: XCTestCase {
|
|||
MetricsSystem.bootstrapInternal(metrics)
|
||||
let label = "\(#function)-fp-counter-\(UUID())"
|
||||
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.signalingNaN)
|
||||
XCTAssertEqual(counter.values.count, 0, "expected nan values to be ignored")
|
||||
|
|
@ -71,7 +72,7 @@ class MetricsTests: XCTestCase {
|
|||
MetricsSystem.bootstrapInternal(metrics)
|
||||
let label = "\(#function)-fp-counter-\(UUID())"
|
||||
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)
|
||||
XCTAssertEqual(counter.values.count, 0, "expected infinite values to be ignored")
|
||||
|
|
@ -83,7 +84,7 @@ class MetricsTests: XCTestCase {
|
|||
MetricsSystem.bootstrapInternal(metrics)
|
||||
let label = "\(#function)-fp-counter-\(UUID())"
|
||||
let fpCounter = FloatingPointCounter(label: label)
|
||||
let counter = metrics.counters[label] as! TestCounter
|
||||
let counter = try metrics.expectCounter(label)
|
||||
fpCounter.increment(by: -100)
|
||||
XCTAssertEqual(counter.values.count, 0, "expected negative values to be ignored")
|
||||
}
|
||||
|
|
@ -94,19 +95,19 @@ class MetricsTests: XCTestCase {
|
|||
MetricsSystem.bootstrapInternal(metrics)
|
||||
let label = "\(#function)-fp-counter-\(UUID())"
|
||||
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)
|
||||
XCTAssertEqual(counter.values.count, 0, "expected zero values to be ignored")
|
||||
}
|
||||
|
||||
func testDefaultFloatingPointCounter_ceilsExtremeValues() {
|
||||
func testDefaultFloatingPointCounter_ceilsExtremeValues() throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
let label = "\(#function)-fp-counter-\(UUID())"
|
||||
let fpCounter = FloatingPointCounter(label: label)
|
||||
let counter = metrics.counters[label] as! TestCounter
|
||||
let counter = try metrics.expectCounter(label)
|
||||
// Just larger than Int64
|
||||
fpCounter.increment(by: Double(sign: .plus, exponent: 63, significand: 1))
|
||||
// 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")
|
||||
}
|
||||
|
||||
func testDefaultFloatingPointCounter_accumulatesFloatingPointDecimalValues() {
|
||||
func testDefaultFloatingPointCounter_accumulatesFloatingPointDecimalValues() throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
let label = "\(#function)-fp-counter-\(UUID())"
|
||||
let fpCounter = FloatingPointCounter(label: label)
|
||||
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)
|
||||
fpCounter.increment(by: 0.75)
|
||||
|
|
@ -205,9 +206,9 @@ class MetricsTests: XCTestCase {
|
|||
let name = "recorder-\(UUID().uuidString)"
|
||||
let value = Double.random(in: Double(Int.min) ... Double(Int.max))
|
||||
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[0].1, value, "expected value to match")
|
||||
XCTAssertEqual(recorder.lastValue, value, "expected value to match")
|
||||
}
|
||||
|
||||
func testTimers() throws {
|
||||
|
|
@ -238,9 +239,9 @@ class MetricsTests: XCTestCase {
|
|||
let name = "timer-\(UUID().uuidString)"
|
||||
let value = Int64.random(in: Int64.min ... Int64.max)
|
||||
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[0].1, value, "expected value to match")
|
||||
XCTAssertEqual(timer.values[0], value, "expected value to match")
|
||||
}
|
||||
|
||||
func testTimerVariants() throws {
|
||||
|
|
@ -254,22 +255,22 @@ class MetricsTests: XCTestCase {
|
|||
let nano = Int64.random(in: 0 ... 5)
|
||||
timer.recordNanoseconds(nano)
|
||||
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
|
||||
let micro = Int64.random(in: 0 ... 5)
|
||||
timer.recordMicroseconds(micro)
|
||||
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
|
||||
let milli = Int64.random(in: 0 ... 5)
|
||||
timer.recordMilliseconds(milli)
|
||||
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
|
||||
let sec = Int64.random(in: 0 ... 5)
|
||||
timer.recordSeconds(sec)
|
||||
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 {
|
||||
|
|
@ -282,31 +283,31 @@ class MetricsTests: XCTestCase {
|
|||
// nano (integer)
|
||||
timer.recordNanoseconds(Int64.max)
|
||||
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)
|
||||
timer.recordMicroseconds(Int64.max)
|
||||
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)
|
||||
timer.recordMicroseconds(Double(Int64.max) + 1)
|
||||
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)
|
||||
timer.recordMilliseconds(Int64.max)
|
||||
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)
|
||||
timer.recordMilliseconds(Double(Int64.max) + 1)
|
||||
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)
|
||||
timer.recordSeconds(Int64.max)
|
||||
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)
|
||||
timer.recordSeconds(Double(Int64.max) * 1)
|
||||
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 {
|
||||
|
|
@ -319,19 +320,19 @@ class MetricsTests: XCTestCase {
|
|||
// nano
|
||||
timer.recordNanoseconds(UInt64.max)
|
||||
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
|
||||
timer.recordMicroseconds(UInt64.max)
|
||||
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
|
||||
timer.recordMilliseconds(UInt64.max)
|
||||
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
|
||||
timer.recordSeconds(UInt64.max)
|
||||
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 {
|
||||
|
|
@ -345,7 +346,7 @@ class MetricsTests: XCTestCase {
|
|||
gauge.record(value)
|
||||
let recorder = gauge._handler as! TestRecorder
|
||||
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 {
|
||||
|
|
@ -356,9 +357,9 @@ class MetricsTests: XCTestCase {
|
|||
let name = "gauge-\(UUID().uuidString)"
|
||||
let value = Double.random(in: -1000 ... 1000)
|
||||
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[0].1, value, "expected value to match")
|
||||
XCTAssertEqual(recorder.lastValue, value, "expected value to match")
|
||||
}
|
||||
|
||||
func testMeter() throws {
|
||||
|
|
@ -383,7 +384,7 @@ class MetricsTests: XCTestCase {
|
|||
let name = "meter-\(NSUUID().uuidString)"
|
||||
let value = Double.random(in: -1000 ... 1000)
|
||||
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[0].1, value, "expected value to match")
|
||||
}
|
||||
|
|
@ -398,15 +399,15 @@ class MetricsTests: XCTestCase {
|
|||
let muxCounter = Counter(label: name)
|
||||
muxCounter.increment(by: value)
|
||||
factories.forEach { factory in
|
||||
let counter = factory.counters.first?.1 as! TestCounter
|
||||
XCTAssertEqual(counter.label, name, "expected label to match")
|
||||
XCTAssertEqual(counter.values.count, 1, "expected number of entries to match")
|
||||
XCTAssertEqual(counter.values[0].1, Int64(value), "expected value to match")
|
||||
let counter = factory.counters.first
|
||||
XCTAssertEqual(counter?.label, name, "expected label to match")
|
||||
XCTAssertEqual(counter?.values.count, 1, "expected number of entries to match")
|
||||
XCTAssertEqual(counter?.lastValue, Int64(value), "expected value to match")
|
||||
}
|
||||
muxCounter.reset()
|
||||
factories.forEach { factory in
|
||||
let counter = factory.counters.first?.1 as! TestCounter
|
||||
XCTAssertEqual(counter.values.count, 0, "expected number of entries to match")
|
||||
let counter = factory.counters.first
|
||||
XCTAssertEqual(counter?.values.count, 0, "expected number of entries to match")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -420,10 +421,10 @@ class MetricsTests: XCTestCase {
|
|||
let muxMeter = Meter(label: name)
|
||||
muxMeter.set(value)
|
||||
factories.forEach { factory in
|
||||
let meter = factory.meters.first?.1 as! TestMeter
|
||||
XCTAssertEqual(meter.label, name, "expected label to match")
|
||||
XCTAssertEqual(meter.values.count, 1, "expected number of entries to match")
|
||||
XCTAssertEqual(meter.values[0].1, value, "expected value to match")
|
||||
let meter = factory.meters.first
|
||||
XCTAssertEqual(meter?.label, name, "expected label to match")
|
||||
XCTAssertEqual(meter?.values.count, 1, "expected number of entries to match")
|
||||
XCTAssertEqual(meter?.values[0].1, value, "expected value to match")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -437,10 +438,10 @@ class MetricsTests: XCTestCase {
|
|||
let muxRecorder = Recorder(label: name)
|
||||
muxRecorder.record(value)
|
||||
factories.forEach { factory in
|
||||
let recorder = factory.recorders.first?.1 as! TestRecorder
|
||||
XCTAssertEqual(recorder.label, name, "expected label to match")
|
||||
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
||||
XCTAssertEqual(recorder.values[0].1, value, "expected value to match")
|
||||
let recorder = factory.recorders.first
|
||||
XCTAssertEqual(recorder?.label, name, "expected label to match")
|
||||
XCTAssertEqual(recorder?.values.count, 1, "expected number of entries 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)
|
||||
muxTimer.recordSeconds(seconds)
|
||||
factories.forEach { factory in
|
||||
let timer = factory.timers.first?.1 as! TestTimer
|
||||
XCTAssertEqual(timer.label, name, "expected label 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.displayUnit, .minutes, "expected value to match")
|
||||
XCTAssertEqual(timer.retrieveValueInPreferredUnit(atIndex: 0), Double(seconds) / 60.0, "seconds should be returned as minutes")
|
||||
let timer = factory.timers.first
|
||||
XCTAssertEqual(timer?.label, name, "expected label to match")
|
||||
XCTAssertEqual(timer?.values.count, 1, "expected number of entries 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?.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
|
||||
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")
|
||||
|
||||
let identity = ObjectIdentifier(timer)
|
||||
|
|
@ -588,7 +589,7 @@ class MetricsTests: XCTestCase {
|
|||
timerAgain.recordNanoseconds(value)
|
||||
let testTimerAgain = timerAgain._handler as! TestTimer
|
||||
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)
|
||||
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 Metrics
|
||||
@testable import MetricsTestKit
|
||||
import XCTest
|
||||
|
||||
class MetricsExtensionsTests: XCTestCase {
|
||||
|
|
@ -27,9 +28,9 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
Timer.measure(label: name) {
|
||||
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")
|
||||
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 {
|
||||
|
|
@ -42,7 +43,7 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
let timeInterval = TimeInterval(Double.random(in: 1 ... 500))
|
||||
timer.record(timeInterval)
|
||||
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 {
|
||||
|
|
@ -56,26 +57,26 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
let nano = DispatchTimeInterval.nanoseconds(Int.random(in: 1 ... 500))
|
||||
timer.record(nano)
|
||||
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
|
||||
let micro = DispatchTimeInterval.microseconds(Int.random(in: 1 ... 500))
|
||||
timer.record(micro)
|
||||
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
|
||||
let milli = DispatchTimeInterval.milliseconds(Int.random(in: 1 ... 500))
|
||||
timer.record(milli)
|
||||
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
|
||||
let sec = DispatchTimeInterval.seconds(Int.random(in: 1 ... 500))
|
||||
timer.record(sec)
|
||||
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
|
||||
timer.record(DispatchTimeInterval.never)
|
||||
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() {
|
||||
|
|
@ -91,7 +92,7 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
|
||||
let testTimer = timer._handler as! TestTimer
|
||||
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")
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +108,7 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
|
||||
let testTimer = timer._handler as! TestTimer
|
||||
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")
|
||||
|
||||
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
|
||||
import Dispatch
|
||||
@testable import MetricsTestKit
|
||||
import XCTest
|
||||
|
||||
class SendableTest: XCTestCase {
|
||||
|
|
@ -90,7 +91,7 @@ class SendableTest: XCTestCase {
|
|||
let task = Task.detached { () -> [Int64] in
|
||||
timer.recordNanoseconds(value)
|
||||
let handler = timer._handler as! TestTimer
|
||||
return handler.values.map { $0.1 }
|
||||
return handler.values
|
||||
}
|
||||
let values = await task.value
|
||||
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
||||
|
|
|
|||
Loading…
Reference in New Issue