//===----------------------------------------------------------------------===// // // 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 internal class TestMetrics: MetricsFactory { private let lock = NSLock() // TODO: consider lock per cache? var counters = [String: CounterHandler]() var recorders = [String: RecorderHandler]() var timers = [String: TimerHandler]() public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler { return self.make(label: label, dimensions: dimensions, registry: &self.counters, maker: TestCounter.init) } public 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) } public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler { return self.make(label: label, dimensions: dimensions, registry: &self.timers, maker: TestTimer.init) } private func make(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 } } } internal 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 = NSUUID().uuidString self.label = label self.dimensions = dimensions } func increment(_ value: Int64) { self.lock.withLock { self.values.append((Date(), value)) } print("adding \(value) 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 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("recoding \(value) in \(self.label)") } public static func == (lhs: TestRecorder, rhs: TestRecorder) -> Bool { return lhs.id == rhs.id } } internal class TestTimer: TimerHandler, 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() } }