@testable import CoreMetrics @testable import protocol CoreMetrics.Timer import Foundation internal class TestMetrics: MetricsHandler { private let lock = NSLock() // TODO: consider lock per cache? var counters = [Counter]() var recorders = [Recorder]() var timers = [Timer]() public func makeCounter(label: String, dimensions: [(String, String)]) -> Counter { return self.make(label: label, dimensions: dimensions, registry: &self.counters, maker: TestCounter.init) } public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> Recorder { let maker = { (label: String, dimensions: [(String, String)]) -> Recorder in TestRecorder(label: label, dimensions: dimensions, aggregate: aggregate) } return self.make(label: label, dimensions: dimensions, registry: &self.recorders, maker: maker) } public func makeTimer(label: String, dimensions: [(String, String)]) -> Timer { return self.make(label: label, dimensions: dimensions, registry: &self.timers, maker: TestTimer.init) } private func make(label: String, dimensions: [(String, String)], registry: inout [Item], maker: (String, [(String, String)]) -> Item) -> Item { let item = maker(label, dimensions) return self.lock.withLock { registry.append(item) return item } } } internal class TestCounter: Counter, 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 = NSUUID().uuidString self.label = label self.dimensions = dimensions } func increment(_ value: DataType) { self.lock.withLock { self.values.append((Date(), Int64(value))) } print("adding \(value) to \(self.label)") } public static func == (lhs: TestCounter, rhs: TestCounter) -> Bool { return lhs.id == rhs.id } } internal class TestRecorder: Recorder, 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: DataType) { self.record(Double(value)) } func record(_ value: DataType) { self.lock.withLock { // this may loose percision but good enough as an example values.append((Date(), Double(value))) } print("recoding \(value) in \(self.label)") } public static func == (lhs: TestRecorder, rhs: TestRecorder) -> Bool { return lhs.id == rhs.id } } internal class TestTimer: Timer, 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 = NSUUID().uuidString self.label = label self.dimensions = dimensions } func recordNanoseconds(_ duration: Int64) { self.lock.withLock { values.append((Date(), duration)) } print("recoding \(duration) \(self.label)") } public static func == (lhs: TestTimer, rhs: TestTimer) -> Bool { return lhs.id == rhs.id } } private extension NSLock { func withLock(_ body: () -> T) -> T { self.lock() defer { self.unlock() } return body() } }