diff --git a/Sources/CoreMetrics/Metrics.swift b/Sources/CoreMetrics/Metrics.swift index 9c03a0a..57ecd01 100644 --- a/Sources/CoreMetrics/Metrics.swift +++ b/Sources/CoreMetrics/Metrics.swift @@ -273,6 +273,8 @@ public class Timer { /// - value: Duration to record. @inlinable public func recordMicroseconds(_ duration: DataType) { + guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) } + let result = Int64(duration).multipliedReportingOverflow(by: 1000) if result.overflow { self.recordNanoseconds(Int64.max) @@ -296,6 +298,8 @@ public class Timer { /// - value: Duration to record. @inlinable public func recordMilliseconds(_ duration: DataType) { + guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) } + let result = Int64(duration).multipliedReportingOverflow(by: 1_000_000) if result.overflow { self.recordNanoseconds(Int64.max) @@ -319,6 +323,8 @@ public class Timer { /// - value: Duration to record. @inlinable public func recordSeconds(_ duration: DataType) { + guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) } + let result = Int64(duration).multipliedReportingOverflow(by: 1_000_000_000) if result.overflow { self.recordNanoseconds(Int64.max) diff --git a/Tests/MetricsTests/CoreMetricsTests+XCTest.swift b/Tests/MetricsTests/CoreMetricsTests+XCTest.swift index e667133..2b01d07 100644 --- a/Tests/MetricsTests/CoreMetricsTests+XCTest.swift +++ b/Tests/MetricsTests/CoreMetricsTests+XCTest.swift @@ -35,6 +35,7 @@ extension MetricsTests { ("testTimerBlock", testTimerBlock), ("testTimerVariants", testTimerVariants), ("testTimerOverflow", testTimerOverflow), + ("testTimerHandlesUnsignedOverflow", testTimerHandlesUnsignedOverflow), ("testGauge", testGauge), ("testGaugeBlock", testGaugeBlock), ("testMUX", testMUX), diff --git a/Tests/MetricsTests/CoreMetricsTests.swift b/Tests/MetricsTests/CoreMetricsTests.swift index 234995b..804e956 100644 --- a/Tests/MetricsTests/CoreMetricsTests.swift +++ b/Tests/MetricsTests/CoreMetricsTests.swift @@ -217,6 +217,27 @@ class MetricsTests: XCTestCase { XCTAssertEqual(testTimer.values[3].1, Int64.max, "expected value to match") } + func testTimerHandlesUnsignedOverflow() throws { + // bootstrap with our test metrics + let metrics = TestMetrics() + MetricsSystem.bootstrapInternal(metrics) + // run the test + let timer = Timer(label: "test-timer") + let testTimer = timer.handler as! TestTimer + // micro + timer.recordMicroseconds(UInt64.max) + XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match") + XCTAssertEqual(testTimer.values[0].1, Int64.max, "expected value to match") + // milli + timer.recordMilliseconds(UInt64.max) + XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match") + XCTAssertEqual(testTimer.values[1].1, Int64.max, "expected value to match") + // seconds + timer.recordSeconds(UInt64.max) + XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match") + XCTAssertEqual(testTimer.values[2].1, Int64.max, "expected value to match") + } + func testGauge() throws { // bootstrap with our test metrics let metrics = TestMetrics()