Migrate to GitHub Actions (#151)

This commit is contained in:
Rick Newton-Rogers 2024-11-06 13:32:20 +00:00 committed by GitHub
parent 569db3a632
commit d720898dbf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 451 additions and 699 deletions

8
.editorconfig Normal file
View File

@ -0,0 +1,8 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

14
.github/release.yml vendored Normal file
View File

@ -0,0 +1,14 @@
changelog:
categories:
- title: SemVer Major
labels:
- ⚠️ semver/major
- title: SemVer Minor
labels:
- semver/minor
- title: SemVer Patch
labels:
- semver/patch
- title: Other Changes
labels:
- semver/none

18
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Main
on:
push:
branches: [main]
schedule:
- cron: "0 8,20 * * *"
jobs:
unit-tests:
name: Unit tests
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
with:
linux_5_9_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_5_10_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"

26
.github/workflows/pull_request.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: PR
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
soundness:
name: Soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
with:
license_header_check_project_name: "Swift Metrics API"
unit-tests:
name: Unit tests
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
with:
linux_5_9_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_5_10_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
cxx-interop:
name: Cxx interop
uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main

View File

@ -0,0 +1,18 @@
name: PR label
on:
pull_request:
types: [labeled, unlabeled, opened, reopened, synchronize]
jobs:
semver-label-check:
name: Semantic Version label check
runs-on: ubuntu-latest
timeout-minutes: 1
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Check for Semantic Version label
uses: apple/swift-nio/.github/actions/pull_request_semver_label_checker@main

35
.licenseignore Normal file
View File

@ -0,0 +1,35 @@
.gitignore
**/.gitignore
.licenseignore
.gitattributes
.git-blame-ignore-revs
.mailfilter
.mailmap
.spi.yml
.swift-format
.editorconfig
.github/*
*.md
*.txt
*.yml
*.yaml
*.json
Package.swift
**/Package.swift
Package@-*.swift
**/Package@-*.swift
Package.resolved
**/Package.resolved
Makefile
*.modulemap
**/*.modulemap
**/*.docc/*
*.xcprivacy
**/*.xcprivacy
*.symlink
**/*.symlink
Dockerfile
**/Dockerfile
Snippets/*
dev/git.commit.template
.unacceptablelanguageignore

68
.swift-format Normal file
View File

@ -0,0 +1,68 @@
{
"version" : 1,
"indentation" : {
"spaces" : 4
},
"tabWidth" : 4,
"fileScopedDeclarationPrivacy" : {
"accessLevel" : "private"
},
"spacesAroundRangeFormationOperators" : false,
"indentConditionalCompilationBlocks" : false,
"indentSwitchCaseLabels" : false,
"lineBreakAroundMultilineExpressionChainComponents" : false,
"lineBreakBeforeControlFlowKeywords" : false,
"lineBreakBeforeEachArgument" : true,
"lineBreakBeforeEachGenericRequirement" : true,
"lineLength" : 120,
"maximumBlankLines" : 1,
"respectsExistingLineBreaks" : true,
"prioritizeKeepingFunctionOutputTogether" : true,
"noAssignmentInExpressions" : {
"allowedFunctions" : [
"XCTAssertNoThrow",
"XCTAssertThrowsError"
]
},
"rules" : {
"AllPublicDeclarationsHaveDocumentation" : false,
"AlwaysUseLiteralForEmptyCollectionInit" : false,
"AlwaysUseLowerCamelCase" : false,
"AmbiguousTrailingClosureOverload" : true,
"BeginDocumentationCommentWithOneLineSummary" : false,
"DoNotUseSemicolons" : true,
"DontRepeatTypeInStaticProperties" : true,
"FileScopedDeclarationPrivacy" : true,
"FullyIndirectEnum" : true,
"GroupNumericLiterals" : true,
"IdentifiersMustBeASCII" : true,
"NeverForceUnwrap" : false,
"NeverUseForceTry" : false,
"NeverUseImplicitlyUnwrappedOptionals" : false,
"NoAccessLevelOnExtensionDeclaration" : true,
"NoAssignmentInExpressions" : true,
"NoBlockComments" : true,
"NoCasesWithOnlyFallthrough" : true,
"NoEmptyTrailingClosureParentheses" : true,
"NoLabelsInCasePatterns" : true,
"NoLeadingUnderscores" : false,
"NoParensAroundConditions" : true,
"NoVoidReturnOnFunctionSignature" : true,
"OmitExplicitReturns" : true,
"OneCasePerLine" : true,
"OneVariableDeclarationPerLine" : true,
"OnlyOneTrailingClosureArgument" : true,
"OrderedImports" : true,
"ReplaceForEachWithForLoop" : true,
"ReturnVoidInsteadOfEmptyTuple" : true,
"UseEarlyExits" : false,
"UseExplicitNilCheckInConditions" : false,
"UseLetInEveryBoundCaseVariable" : false,
"UseShorthandTypeNames" : true,
"UseSingleLinePropertyGetter" : false,
"UseSynthesizedInitializer" : false,
"UseTripleSlashForDocumentationComments" : true,
"UseWhereClausesInForLoops" : false,
"ValidateDocumentationComments" : false
}
}

View File

@ -1,16 +0,0 @@
# file options
--swiftversion 5.0
--exclude .build
# format options
--self insert
--patternlet inline
--stripunusedargs unnamed-only
--ifdef no-indent
# Configure the placement of an extension's access control keyword.
--extensionacl on-declarations
# rules

View File

@ -60,36 +60,9 @@ We require that your commit messages match our template. The easiest way to do t
git config commit.template dev/git.commit.template
### Make sure Tests work on Linux
### Run CI checks locally
SwiftMetrics uses XCTest to run tests on both macOS and Linux. While the macOS version of XCTest is able to use the Objective-C runtime to discover tests at execution time, the Linux version is not.
For this reason, whenever you add new tests **you have to run a script** that generates the hooks needed to run those tests on Linux, or our CI will complain that the tests are not all present on Linux. To do this, merely execute `ruby ./scripts/generate_linux_tests.rb` at the root of the package and check the changes it made.
### Run `./scripts/soundness.sh`
The scripts directory contains a [soundness.sh script](https://github.com/apple/swift-metrics/blob/main/scripts/soundness.sh)
that enforces additional checks, like license headers and formatting style.
Please make sure to `./scripts/soundness.sh` before pushing a change upstream, otherwise it is likely the PR validation will fail
on minor changes such as a missing `self.` or similar formatting issues.
> The script also executes the above mentioned `generate_linux_tests.rb`.
For frequent contributors, we recommend adding the script as a [git pre-push hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks), which you can do via executing the following command in the project root directory:
```bash
cat << EOF > .git/hooks/pre-push
#!/bin/bash
if [[ -f "scripts/soundness.sh" ]]; then
scripts/soundness.sh
fi
EOF
```
Which makes the script execute, and only allow the `git push` to complete if the check has passed.
In the case of formatting issues, you can then `git add` the formatting changes, and attempt the push again.
You can run the Github Actions workflows locally using [act](https://github.com/nektos/act). For detailed steps on how to do this please see [https://github.com/swiftlang/github-workflows?tab=readme-ov-file#running-workflows-locally](https://github.com/swiftlang/github-workflows?tab=readme-ov-file#running-workflows-locally).
## How to contribute your work

View File

@ -22,10 +22,6 @@ let package = Package(
.library(name: "Metrics", targets: ["Metrics"]),
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
],
dependencies: [
// ~~~ SwiftPM Plugins ~~~
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
],
targets: [
.target(
name: "CoreMetrics"

View File

@ -48,7 +48,7 @@ public final class Counter {
/// Increment the counter.
///
/// - parameters:
/// - by: Amount to increment by.
/// - amount: Amount to increment by.
@inlinable
public func increment<DataType: BinaryInteger>(by amount: DataType) {
self._handler.increment(by: Int64(amount))
@ -88,7 +88,7 @@ extension Counter {
extension Counter: CustomStringConvertible {
public var description: String {
return "Counter(\(self.label), dimensions: \(self.dimensions))"
"Counter(\(self.label), dimensions: \(self.dimensions))"
}
}
@ -127,7 +127,7 @@ public final class FloatingPointCounter {
/// Increment the FloatingPointCounter.
///
/// - parameters:
/// - by: Amount to increment by.
/// - amount: Amount to increment by.
@inlinable
public func increment<DataType: BinaryFloatingPoint>(by amount: DataType) {
self._handler.increment(by: Double(amount))
@ -167,7 +167,7 @@ extension FloatingPointCounter {
extension FloatingPointCounter: CustomStringConvertible {
public var description: String {
return "FloatingPointCounter(\(self.label), dimensions: \(self.dimensions))"
"FloatingPointCounter(\(self.label), dimensions: \(self.dimensions))"
}
}
@ -235,7 +235,7 @@ public final class Meter {
/// Increment the Meter.
///
/// - parameters:
/// - by: Amount to increment by.
/// - amount: Amount to increment by.
@inlinable
public func increment<DataType: BinaryFloatingPoint>(by amount: DataType) {
self._handler.increment(by: Double(amount))
@ -250,7 +250,7 @@ public final class Meter {
/// Decrement the Meter.
///
/// - parameters:
/// - by: Amount to decrement by.
/// - amount: Amount to decrement by.
@inlinable
public func decrement<DataType: BinaryFloatingPoint>(by amount: DataType) {
self._handler.decrement(by: Double(amount))
@ -284,7 +284,7 @@ extension Meter {
extension Meter: CustomStringConvertible {
public var description: String {
return "\(type(of: self))(\(self.label), dimensions: \(self.dimensions))"
"\(type(of: self))(\(self.label), dimensions: \(self.dimensions))"
}
}
@ -368,7 +368,7 @@ extension Recorder {
extension Recorder: CustomStringConvertible {
public var description: String {
return "\(type(of: self))(\(self.label), dimensions: \(self.dimensions), aggregate: \(self.aggregate))"
"\(type(of: self))(\(self.label), dimensions: \(self.dimensions), aggregate: \(self.aggregate))"
}
}
@ -397,9 +397,18 @@ public struct TimeUnit: Equatable, Sendable {
public static let nanoseconds = TimeUnit(code: .nanoseconds, scaleFromNanoseconds: 1)
public static let microseconds = TimeUnit(code: .microseconds, scaleFromNanoseconds: 1000)
public static let milliseconds = TimeUnit(code: .milliseconds, scaleFromNanoseconds: 1000 * TimeUnit.microseconds.scaleFromNanoseconds)
public static let seconds = TimeUnit(code: .seconds, scaleFromNanoseconds: 1000 * TimeUnit.milliseconds.scaleFromNanoseconds)
public static let minutes = TimeUnit(code: .minutes, scaleFromNanoseconds: 60 * TimeUnit.seconds.scaleFromNanoseconds)
public static let milliseconds = TimeUnit(
code: .milliseconds,
scaleFromNanoseconds: 1000 * TimeUnit.microseconds.scaleFromNanoseconds
)
public static let seconds = TimeUnit(
code: .seconds,
scaleFromNanoseconds: 1000 * TimeUnit.milliseconds.scaleFromNanoseconds
)
public static let minutes = TimeUnit(
code: .minutes,
scaleFromNanoseconds: 60 * TimeUnit.seconds.scaleFromNanoseconds
)
public static let hours = TimeUnit(code: .hours, scaleFromNanoseconds: 60 * TimeUnit.minutes.scaleFromNanoseconds)
public static let days = TimeUnit(code: .days, scaleFromNanoseconds: 24 * TimeUnit.hours.scaleFromNanoseconds)
}
@ -436,7 +445,7 @@ public final class Timer {
/// Record a duration in nanoseconds.
///
/// - parameters:
/// - value: Duration to record.
/// - duration: Duration to record.
@inlinable
public func recordNanoseconds(_ duration: Int64) {
self._handler.recordNanoseconds(duration)
@ -445,7 +454,7 @@ public final class Timer {
/// Record a duration in nanoseconds.
///
/// - parameters:
/// - value: Duration to record.
/// - duration: Duration to record.
@inlinable
public func recordNanoseconds<DataType: BinaryInteger>(_ duration: DataType) {
self.recordNanoseconds(duration >= Int64.max ? Int64.max : Int64(duration))
@ -454,7 +463,7 @@ public final class Timer {
/// Record a duration in microseconds.
///
/// - parameters:
/// - value: Duration to record.
/// - duration: Duration to record.
@inlinable
public func recordMicroseconds<DataType: BinaryInteger>(_ duration: DataType) {
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
@ -470,7 +479,7 @@ public final class Timer {
/// Record a duration in microseconds.
///
/// - parameters:
/// - value: Duration to record.
/// - duration: Duration to record.
@inlinable
public func recordMicroseconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
self.recordNanoseconds(Double(duration * 1000) < Double(Int64.max) ? Int64(duration * 1000) : Int64.max)
@ -479,7 +488,7 @@ public final class Timer {
/// Record a duration in milliseconds.
///
/// - parameters:
/// - value: Duration to record.
/// - duration: Duration to record.
@inlinable
public func recordMilliseconds<DataType: BinaryInteger>(_ duration: DataType) {
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
@ -495,16 +504,18 @@ public final class Timer {
/// Record a duration in milliseconds.
///
/// - parameters:
/// - value: Duration to record.
/// - duration: Duration to record.
@inlinable
public func recordMilliseconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
self.recordNanoseconds(Double(duration * 1_000_000) < Double(Int64.max) ? Int64(duration * 1_000_000) : Int64.max)
self.recordNanoseconds(
Double(duration * 1_000_000) < Double(Int64.max) ? Int64(duration * 1_000_000) : Int64.max
)
}
/// Record a duration in seconds.
///
/// - parameters:
/// - value: Duration to record.
/// - duration: Duration to record.
@inlinable
public func recordSeconds<DataType: BinaryInteger>(_ duration: DataType) {
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
@ -520,10 +531,12 @@ public final class Timer {
/// Record a duration in seconds.
///
/// - parameters:
/// - value: Duration to record.
/// - duration: Duration to record.
@inlinable
public func recordSeconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
self.recordNanoseconds(Double(duration * 1_000_000_000) < Double(Int64.max) ? Int64(duration * 1_000_000_000) : Int64.max)
self.recordNanoseconds(
Double(duration * 1_000_000_000) < Double(Int64.max) ? Int64(duration * 1_000_000_000) : Int64.max
)
}
}
@ -544,7 +557,11 @@ extension Timer {
/// - label: The label for the `Timer`.
/// - dimensions: The dimensions for the `Timer`.
/// - displayUnit: A hint to the backend responsible for presenting the data of the preferred display unit. This is not guaranteed to be supported by all backends.
public convenience init(label: String, dimensions: [(String, String)] = [], preferredDisplayUnit displayUnit: TimeUnit) {
public convenience init(
label: String,
dimensions: [(String, String)] = [],
preferredDisplayUnit displayUnit: TimeUnit
) {
let handler = MetricsSystem.factory.makeTimer(label: label, dimensions: dimensions)
handler.preferDisplayUnit(displayUnit)
self.init(label: label, dimensions: dimensions, handler: handler)
@ -560,7 +577,7 @@ extension Timer {
extension Timer: CustomStringConvertible {
public var description: String {
return "Timer(\(self.label), dimensions: \(self.dimensions))"
"Timer(\(self.label), dimensions: \(self.dimensions))"
}
}
@ -589,7 +606,7 @@ public enum MetricsSystem {
/// Returns a reference to the configured factory.
public static var factory: MetricsFactory {
return self._factory.underlying
self._factory.underlying
}
/// Acquire a writer lock for the duration of the given block.
@ -597,7 +614,7 @@ public enum MetricsSystem {
/// - Parameter body: The block to execute while holding the lock.
/// - Returns: The value returned by the block.
public static func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
return try self._factory.withWriterLock(body)
try self._factory.withWriterLock(body)
}
// This can be `@unchecked Sendable` because we're manually gating access to mutable state with a lock.
@ -612,20 +629,23 @@ public enum MetricsSystem {
func replaceFactory(_ factory: MetricsFactory, validate: Bool) {
self.lock.withWriterLock {
precondition(!validate || !self.initialized, "metrics system can only be initialized once per process. currently used factory: \(self._underlying)")
precondition(
!validate || !self.initialized,
"metrics system can only be initialized once per process. currently used factory: \(self._underlying)"
)
self._underlying = factory
self.initialized = true
}
}
var underlying: MetricsFactory {
return self.lock.withReaderLock {
return self._underlying
self.lock.withReaderLock {
self._underlying
}
}
func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
return try self.lock.withWriterLock(body)
try self.lock.withWriterLock(body)
}
}
}
@ -891,7 +911,7 @@ extension MetricsFactory {
/// - label: The label for the `FloatingPointCounterHandler`.
/// - dimensions: The dimensions for the `FloatingPointCounterHandler`.
public func makeFloatingPointCounter(label: String, dimensions: [(String, String)]) -> FloatingPointCounterHandler {
return AccumulatingRoundingFloatingPointCounter(label: label, dimensions: dimensions)
AccumulatingRoundingFloatingPointCounter(label: label, dimensions: dimensions)
}
/// Invoked when the corresponding `FloatingPointCounter`'s `destroy()` function is invoked.
@ -915,7 +935,7 @@ extension MetricsFactory {
/// - label: The label for the `MeterHandler`.
/// - dimensions: The dimensions for the `MeterHandler`.
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
return AccumulatingMeter(label: label, dimensions: dimensions)
AccumulatingMeter(label: label, dimensions: dimensions)
}
/// Invoked when the corresponding `Meter`'s `destroy()` function is invoked.
@ -1051,7 +1071,7 @@ public protocol TimerHandler: AnyObject, _SwiftMetricsSendableProtocol {
/// Record a duration in nanoseconds.
///
/// - parameters:
/// - value: Duration to record.
/// - duration: Duration to record.
func recordNanoseconds(_ duration: Int64)
/// Set the preferred display unit for this TimerHandler.
@ -1077,23 +1097,23 @@ public final class MultiplexMetricsHandler: MetricsFactory {
}
public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
return MuxCounter(factories: self.factories, label: label, dimensions: dimensions)
MuxCounter(factories: self.factories, label: label, dimensions: dimensions)
}
public func makeFloatingPointCounter(label: String, dimensions: [(String, String)]) -> FloatingPointCounterHandler {
return MuxFloatingPointCounter(factories: self.factories, label: label, dimensions: dimensions)
MuxFloatingPointCounter(factories: self.factories, label: label, dimensions: dimensions)
}
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
return MuxMeter(factories: self.factories, label: label, dimensions: dimensions)
MuxMeter(factories: self.factories, label: label, dimensions: dimensions)
}
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
return MuxRecorder(factories: self.factories, label: label, dimensions: dimensions, aggregate: aggregate)
MuxRecorder(factories: self.factories, label: label, dimensions: dimensions, aggregate: aggregate)
}
public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
return MuxTimer(factories: self.factories, label: label, dimensions: dimensions)
MuxTimer(factories: self.factories, label: label, dimensions: dimensions)
}
public func destroyCounter(_ handler: CounterHandler) {
@ -1133,11 +1153,11 @@ public final class MultiplexMetricsHandler: MetricsFactory {
}
func increment(by amount: Int64) {
self.counters.forEach { $0.increment(by: amount) }
for counter in self.counters { counter.increment(by: amount) }
}
func reset() {
self.counters.forEach { $0.reset() }
for counter in self.counters { counter.reset() }
}
}
@ -1148,11 +1168,11 @@ public final class MultiplexMetricsHandler: MetricsFactory {
}
func increment(by amount: Double) {
self.counters.forEach { $0.increment(by: amount) }
for counter in self.counters { counter.increment(by: amount) }
}
func reset() {
self.counters.forEach { $0.reset() }
for counter in self.counters { counter.reset() }
}
}
@ -1163,34 +1183,36 @@ public final class MultiplexMetricsHandler: MetricsFactory {
}
func set(_ value: Int64) {
self.meters.forEach { $0.set(value) }
for meter in self.meters { meter.set(value) }
}
func set(_ value: Double) {
self.meters.forEach { $0.set(value) }
for meter in self.meters { meter.set(value) }
}
func increment(by amount: Double) {
self.meters.forEach { $0.increment(by: amount) }
for meter in self.meters { meter.increment(by: amount) }
}
func decrement(by amount: Double) {
self.meters.forEach { $0.decrement(by: amount) }
for meter in self.meters { meter.decrement(by: amount) }
}
}
private final class MuxRecorder: RecorderHandler {
let recorders: [RecorderHandler]
public init(factories: [MetricsFactory], label: String, dimensions: [(String, String)], aggregate: Bool) {
self.recorders = factories.map { $0.makeRecorder(label: label, dimensions: dimensions, aggregate: aggregate) }
self.recorders = factories.map {
$0.makeRecorder(label: label, dimensions: dimensions, aggregate: aggregate)
}
}
func record(_ value: Int64) {
self.recorders.forEach { $0.record(value) }
for recorder in self.recorders { recorder.record(value) }
}
func record(_ value: Double) {
self.recorders.forEach { $0.record(value) }
for recorder in self.recorders { recorder.record(value) }
}
}
@ -1201,39 +1223,41 @@ public final class MultiplexMetricsHandler: MetricsFactory {
}
func recordNanoseconds(_ duration: Int64) {
self.timers.forEach { $0.recordNanoseconds(duration) }
for timer in self.timers { timer.recordNanoseconds(duration) }
}
func preferDisplayUnit(_ unit: TimeUnit) {
self.timers.forEach { $0.preferDisplayUnit(unit) }
for timer in self.timers { timer.preferDisplayUnit(unit) }
}
}
}
/// Ships with the metrics module, used for initial bootstrapping.
public final class NOOPMetricsHandler: MetricsFactory, CounterHandler, FloatingPointCounterHandler, MeterHandler, RecorderHandler, TimerHandler {
public final class NOOPMetricsHandler: MetricsFactory, CounterHandler, FloatingPointCounterHandler, MeterHandler,
RecorderHandler, TimerHandler
{
public static let instance = NOOPMetricsHandler()
private init() {}
public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
return self
self
}
public func makeFloatingPointCounter(label: String, dimensions: [(String, String)]) -> FloatingPointCounterHandler {
return self
self
}
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
return self
self
}
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
return self
self
}
public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
return self
self
}
public func destroyCounter(_: CounterHandler) {}

View File

@ -13,9 +13,10 @@
//===----------------------------------------------------------------------===//
@_exported import CoreMetrics
@_exported import class CoreMetrics.Timer
import Foundation
@_exported import class CoreMetrics.Timer
extension Timer {
/// Convenience for measuring duration of a closure.
///
@ -24,7 +25,11 @@ extension Timer {
/// - dimensions: The dimensions for the Timer.
/// - body: Closure to run & record.
@inlinable
public static func measure<T>(label: String, dimensions: [(String, String)] = [], body: @escaping () throws -> T) rethrows -> T {
public static func measure<T>(
label: String,
dimensions: [(String, String)] = [],
body: @escaping () throws -> T
) rethrows -> T {
let timer = Timer(label: label, dimensions: dimensions)
let start = DispatchTime.now().uptimeNanoseconds
defer {
@ -88,7 +93,7 @@ extension Timer {
}
extension Timer {
/// Convenience for recording a duration based on ``Duration``.
/// Convenience for recording a duration based on `Duration`.
///
/// `Duration` will be converted to an `Int64` number of nanoseconds, and then recorded with nanosecond precision.
///

View File

@ -69,7 +69,7 @@ public final class TestMetrics: MetricsFactory {
}
public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
return self.lock.withLock { () -> CounterHandler in
self.lock.withLock { () -> CounterHandler in
if let existing = self._counters[.init(label: label, dimensions: dimensions)] {
return existing
}
@ -80,7 +80,7 @@ public final class TestMetrics: MetricsFactory {
}
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
return self.lock.withLock { () -> MeterHandler in
self.lock.withLock { () -> MeterHandler in
if let existing = self._meters[.init(label: label, dimensions: dimensions)] {
return existing
}
@ -91,7 +91,7 @@ public final class TestMetrics: MetricsFactory {
}
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
return self.lock.withLock { () -> RecorderHandler in
self.lock.withLock { () -> RecorderHandler in
if let existing = self._recorders[.init(label: label, dimensions: dimensions)] {
return existing
}
@ -102,7 +102,7 @@ public final class TestMetrics: MetricsFactory {
}
public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
return self.lock.withLock { () -> TimerHandler in
self.lock.withLock { () -> TimerHandler in
if let existing = self._timers[.init(label: label, dimensions: dimensions)] {
return existing
}
@ -148,15 +148,15 @@ public final class TestMetrics: MetricsFactory {
extension TestMetrics.FullKey: Hashable {
public func hash(into hasher: inout Hasher) {
self.label.hash(into: &hasher)
self.dimensions.forEach { dim in
for dim in self.dimensions {
dim.0.hash(into: &hasher)
dim.1.hash(into: &hasher)
}
}
public static func == (lhs: TestMetrics.FullKey, rhs: TestMetrics.FullKey) -> Bool {
return lhs.label == rhs.label &&
Dictionary(uniqueKeysWithValues: lhs.dimensions) == Dictionary(uniqueKeysWithValues: rhs.dimensions)
lhs.label == rhs.label
&& Dictionary(uniqueKeysWithValues: lhs.dimensions) == Dictionary(uniqueKeysWithValues: rhs.dimensions)
}
}
@ -193,11 +193,11 @@ extension TestMetrics {
// MARK: - Gauge
public func expectGauge(_ metric: Gauge) throws -> TestRecorder {
return try self.expectRecorder(metric)
try self.expectRecorder(metric)
}
public func expectGauge(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestRecorder {
return try self.expectRecorder(label, dimensions)
try self.expectRecorder(label, dimensions)
}
// MARK: - Meter
@ -299,7 +299,7 @@ public final class TestCounter: TestMetric, CounterHandler, Equatable {
public let dimensions: [(String, String)]
public var key: TestMetrics.FullKey {
return TestMetrics.FullKey(label: self.label, dimensions: self.dimensions)
TestMetrics.FullKey(label: self.label, dimensions: self.dimensions)
}
let lock = NSLock()
@ -318,33 +318,33 @@ public final class TestCounter: TestMetric, CounterHandler, Equatable {
}
public func reset() {
return self.lock.withLock {
self.lock.withLock {
self._values = []
}
}
public var lastValue: Int64? {
return self.last?.1
self.last?.1
}
public var totalValue: Int64 {
return self.values.reduce(0, +)
self.values.reduce(0, +)
}
public var last: (Date, Int64)? {
return self.lock.withLock {
self.lock.withLock {
self._values.last
}
}
public var values: [Int64] {
return self.lock.withLock {
self.lock.withLock {
self._values.map { $0.1 }
}
}
public static func == (lhs: TestCounter, rhs: TestCounter) -> Bool {
return lhs.id == rhs.id
lhs.id == rhs.id
}
}
@ -354,7 +354,7 @@ public final class TestMeter: TestMetric, MeterHandler, Equatable {
public let dimensions: [(String, String)]
public var key: TestMetrics.FullKey {
return TestMetrics.FullKey(label: self.label, dimensions: self.dimensions)
TestMetrics.FullKey(label: self.label, dimensions: self.dimensions)
}
let lock = NSLock()
@ -430,23 +430,23 @@ public final class TestMeter: TestMetric, MeterHandler, Equatable {
}
public var lastValue: Double? {
return self.last?.1
self.last?.1
}
public var last: (Date, Double)? {
return self.lock.withLock {
self.lock.withLock {
self._values.last
}
}
public var values: [Double] {
return self.lock.withLock {
self.lock.withLock {
self._values.map { $0.1 }
}
}
public static func == (lhs: TestMeter, rhs: TestMeter) -> Bool {
return lhs.id == rhs.id
lhs.id == rhs.id
}
}
@ -457,7 +457,7 @@ public final class TestRecorder: TestMetric, RecorderHandler, Equatable {
public let aggregate: Bool
public var key: TestMetrics.FullKey {
return TestMetrics.FullKey(label: self.label, dimensions: self.dimensions)
TestMetrics.FullKey(label: self.label, dimensions: self.dimensions)
}
let lock = NSLock()
@ -482,23 +482,23 @@ public final class TestRecorder: TestMetric, RecorderHandler, Equatable {
}
public var lastValue: Double? {
return self.last?.1
self.last?.1
}
public var last: (Date, Double)? {
return self.lock.withLock {
self.lock.withLock {
self._values.last
}
}
public var values: [Double] {
return self.lock.withLock {
self.lock.withLock {
self._values.map { $0.1 }
}
}
public static func == (lhs: TestRecorder, rhs: TestRecorder) -> Bool {
return lhs.id == rhs.id
lhs.id == rhs.id
}
}
@ -509,7 +509,7 @@ public final class TestTimer: TestMetric, TimerHandler, Equatable {
public let dimensions: [(String, String)]
public var key: TestMetrics.FullKey {
return TestMetrics.FullKey(label: self.label, dimensions: self.dimensions)
TestMetrics.FullKey(label: self.label, dimensions: self.dimensions)
}
let lock = NSLock()
@ -543,23 +543,23 @@ public final class TestTimer: TestMetric, TimerHandler, Equatable {
}
public var lastValue: Int64? {
return self.last?.1
self.last?.1
}
public var values: [Int64] {
return self.lock.withLock {
return self._values.map { $0.1 }
self.lock.withLock {
self._values.map { $0.1 }
}
}
public var last: (Date, Int64)? {
return self.lock.withLock {
return self._values.last
self.lock.withLock {
self._values.last
}
}
public static func == (lhs: TestTimer, rhs: TestTimer) -> Bool {
return lhs.id == rhs.id
lhs.id == rhs.id
}
}

View File

@ -12,10 +12,11 @@
//
//===----------------------------------------------------------------------===//
@testable import CoreMetrics
import MetricsTestKit
import XCTest
@testable import CoreMetrics
class MetricsTests: XCTestCase {
func testCounters() throws {
// bootstrap with our test metrics
@ -25,12 +26,12 @@ class MetricsTests: XCTestCase {
let name = "counter-\(UUID().uuidString)"
let counter = Counter(label: name)
let testCounter = try metrics.expectCounter(counter)
let total = Int.random(in: 500 ... 1000)
for _ in 0 ..< total {
let total = Int.random(in: 500...1000)
for _ in 0..<total {
group.enter()
DispatchQueue(label: "\(name)-queue").async {
defer { group.leave() }
counter.increment(by: Int.random(in: 0 ... 1000))
counter.increment(by: Int.random(in: 0...1000))
}
}
group.wait()
@ -45,7 +46,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
// run the test
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)
let counter = try metrics.expectCounter(name)
XCTAssertEqual(counter.values.count, 1, "expected number of entries to match")
@ -154,12 +155,12 @@ class MetricsTests: XCTestCase {
let name = "recorder-\(UUID().uuidString)"
let recorder = Recorder(label: name)
let testRecorder = try metrics.expectRecorder(recorder)
let total = Int.random(in: 500 ... 1000)
for _ in 0 ..< total {
let total = Int.random(in: 500...1000)
for _ in 0..<total {
group.enter()
DispatchQueue(label: "\(name)-queue").async {
defer { group.leave() }
recorder.record(Int.random(in: Int.min ... Int.max))
recorder.record(Int.random(in: Int.min...Int.max))
}
}
group.wait()
@ -172,12 +173,12 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
let recorder = Recorder(label: "test-recorder")
let testRecorder = try metrics.expectRecorder(recorder)
let values = (0 ... 999).map { _ in Int32.random(in: Int32.min ... Int32.max) }
for i in 0 ..< values.count {
let values = (0...999).map { _ in Int32.random(in: Int32.min...Int32.max) }
for i in 0..<values.count {
recorder.record(values[i])
}
XCTAssertEqual(values.count, testRecorder.values.count, "expected number of entries to match")
for i in 0 ..< values.count {
for i in 0..<values.count {
XCTAssertEqual(Int32(testRecorder.values[i]), values[i], "expected value #\(i) to match.")
}
}
@ -188,12 +189,12 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
let recorder = Recorder(label: "test-recorder")
let testRecorder = try metrics.expectRecorder(recorder)
let values = (0 ... 999).map { _ in Float.random(in: Float(Int32.min) ... Float(Int32.max)) }
for i in 0 ..< values.count {
let values = (0...999).map { _ in Float.random(in: Float(Int32.min)...Float(Int32.max)) }
for i in 0..<values.count {
recorder.record(values[i])
}
XCTAssertEqual(values.count, testRecorder.values.count, "expected number of entries to match")
for i in 0 ..< values.count {
for i in 0..<values.count {
XCTAssertEqual(Float(testRecorder.values[i]), values[i], "expected value #\(i) to match.")
}
}
@ -204,7 +205,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
// run the test
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)
let recorder = try metrics.expectRecorder(name)
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
@ -219,12 +220,12 @@ class MetricsTests: XCTestCase {
let name = "timer-\(UUID().uuidString)"
let timer = Timer(label: name)
let testTimer = try metrics.expectTimer(timer)
let total = Int.random(in: 500 ... 1000)
for _ in 0 ..< total {
let total = Int.random(in: 500...1000)
for _ in 0..<total {
group.enter()
DispatchQueue(label: "\(name)-queue").async {
defer { group.leave() }
timer.recordNanoseconds(Int64.random(in: Int64.min ... Int64.max))
timer.recordNanoseconds(Int64.random(in: Int64.min...Int64.max))
}
}
group.wait()
@ -237,7 +238,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
// run the test
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)
let timer = try metrics.expectTimer(name)
XCTAssertEqual(timer.values.count, 1, "expected number of entries to match")
@ -252,22 +253,22 @@ class MetricsTests: XCTestCase {
let timer = Timer(label: "test-timer")
let testTimer = try metrics.expectTimer(timer)
// nano
let nano = Int64.random(in: 0 ... 5)
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], nano, "expected value to match")
// micro
let micro = Int64.random(in: 0 ... 5)
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], micro * 1000, "expected value to match")
// milli
let milli = Int64.random(in: 0 ... 5)
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], milli * 1_000_000, "expected value to match")
// seconds
let sec = Int64.random(in: 0 ... 5)
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], sec * 1_000_000_000, "expected value to match")
@ -341,7 +342,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
// run the test
let name = "gauge-\(UUID().uuidString)"
let value = Double.random(in: -1000 ... 1000)
let value = Double.random(in: -1000...1000)
let gauge = Gauge(label: name)
gauge.record(value)
let recorder = try metrics.expectRecorder(gauge)
@ -355,7 +356,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
// run the test
let name = "gauge-\(UUID().uuidString)"
let value = Double.random(in: -1000 ... 1000)
let value = Double.random(in: -1000...1000)
Gauge(label: name).record(value)
let recorder = try metrics.expectRecorder(name)
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
@ -368,7 +369,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
// run the test
let name = "meter-\(UUID().uuidString)"
let value = Double.random(in: -1000 ... 1000)
let value = Double.random(in: -1000...1000)
let meter = Meter(label: name)
meter.set(value)
let testMeter = try metrics.expectMeter(meter)
@ -382,7 +383,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
// run the test
let name = "meter-\(UUID().uuidString)"
let value = Double.random(in: -1000 ... 1000)
let value = Double.random(in: -1000...1000)
Meter(label: name).set(value)
let testMeter = try metrics.expectMeter(name)
XCTAssertEqual(testMeter.values.count, 1, "expected number of entries to match")
@ -395,12 +396,12 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
let meter = Meter(label: name)
let testMeter = try metrics.expectMeter(meter)
let values = (0 ... 999).map { _ in Int32.random(in: Int32.min ... Int32.max) }
for i in 0 ..< values.count {
let values = (0...999).map { _ in Int32.random(in: Int32.min...Int32.max) }
for i in 0..<values.count {
meter.set(values[i])
}
XCTAssertEqual(values.count, testMeter.values.count, "expected number of entries to match")
for i in 0 ..< values.count {
for i in 0..<values.count {
XCTAssertEqual(Int32(testMeter.values[i]), values[i], "expected value #\(i) to match.")
}
}
@ -411,12 +412,12 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
let meter = Meter(label: name)
let testMeter = try metrics.expectMeter(meter)
let values = (0 ... 999).map { _ in Float.random(in: Float(Int32.min) ... Float(Int32.max)) }
for i in 0 ..< values.count {
let values = (0...999).map { _ in Float.random(in: Float(Int32.min)...Float(Int32.max)) }
for i in 0..<values.count {
meter.set(values[i])
}
XCTAssertEqual(values.count, testMeter.values.count, "expected number of entries to match")
for i in 0 ..< values.count {
for i in 0..<values.count {
XCTAssertEqual(Float(testMeter.values[i]), values[i], "expected value #\(i) to match.")
}
}
@ -430,8 +431,8 @@ class MetricsTests: XCTestCase {
let name = "meter-\(UUID().uuidString)"
let meter = Meter(label: name)
let testMeter = try metrics.expectMeter(meter)
let values = (500 ... 1000).map { _ in Double.random(in: 0 ... Double(Int32.max)) }
for i in 0 ..< values.count {
let values = (500...1000).map { _ in Double.random(in: 0...Double(Int32.max)) }
for i in 0..<values.count {
group.enter()
DispatchQueue(label: "\(name)-queue").async {
defer { group.leave() }
@ -453,8 +454,8 @@ class MetricsTests: XCTestCase {
let meter = Meter(label: name)
let testMeter = try metrics.expectMeter(meter)
let values = (500 ... 1000).map { _ in Double.random(in: 0 ... Double(Int32.max)) }
for i in 0 ..< values.count {
let values = (500...1000).map { _ in Double.random(in: 0...Double(Int32.max)) }
for i in 0..<values.count {
group.enter()
DispatchQueue(label: "\(name)-queue").async {
defer { group.leave() }
@ -523,17 +524,17 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(MultiplexMetricsHandler(factories: factories))
// run the test
let name = UUID().uuidString
let value = Int.random(in: Int.min ... Int.max)
let value = Int.random(in: Int.min...Int.max)
let muxCounter = Counter(label: name)
muxCounter.increment(by: value)
factories.forEach { factory in
for factory in factories {
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
for factory in factories {
let counter = factory.counters.first
XCTAssertEqual(counter?.values.count, 0, "expected number of entries to match")
}
@ -545,10 +546,10 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(MultiplexMetricsHandler(factories: factories))
// run the test
let name = UUID().uuidString
let value = Double.random(in: 0 ... 1)
let value = Double.random(in: 0...1)
let muxMeter = Meter(label: name)
muxMeter.set(value)
factories.forEach { factory in
for factory in factories {
let meter = factory.meters.first
XCTAssertEqual(meter?.label, name, "expected label to match")
XCTAssertEqual(meter?.values.count, 1, "expected number of entries to match")
@ -562,10 +563,10 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(MultiplexMetricsHandler(factories: factories))
// run the test
let name = UUID().uuidString
let value = Double.random(in: 0 ... 1)
let value = Double.random(in: 0...1)
let muxRecorder = Recorder(label: name)
muxRecorder.record(value)
factories.forEach { factory in
for factory in factories {
let recorder = factory.recorders.first
XCTAssertEqual(recorder?.label, name, "expected label to match")
XCTAssertEqual(recorder?.values.count, 1, "expected number of entries to match")
@ -579,16 +580,20 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(MultiplexMetricsHandler(factories: factories))
// run the test
let name = UUID().uuidString
let seconds = Int.random(in: 1 ... 10)
let seconds = Int.random(in: 1...10)
let muxTimer = Timer(label: name, preferredDisplayUnit: .minutes)
muxTimer.recordSeconds(seconds)
factories.forEach { factory in
for factory in factories {
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?.valueInPreferredUnit(atIndex: 0), Double(seconds) / 60.0, "seconds should be returned as minutes")
XCTAssertEqual(
timer?.valueInPreferredUnit(atIndex: 0),
Double(seconds) / 60.0,
"seconds should be returned as minutes"
)
}
}
@ -609,7 +614,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
let name = "gauge-\(UUID().uuidString)"
let value = Double.random(in: -1000 ... 1000)
let value = Double.random(in: -1000...1000)
let gauge = Gauge(label: name)
gauge.record(value)
@ -631,7 +636,11 @@ class MetricsTests: XCTestCase {
XCTAssertEqual(recorderAgain.values.first, -value, "expected value to match")
let identityAgain = ObjectIdentifier(recorderAgain)
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"
)
}
func testDestroyingMeter() throws {
@ -639,7 +648,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
let name = "meter-\(UUID().uuidString)"
let value = Double.random(in: -1000 ... 1000)
let value = Double.random(in: -1000...1000)
let meter = Meter(label: name)
meter.set(value)
@ -661,7 +670,11 @@ class MetricsTests: XCTestCase {
XCTAssertEqual(testMeterAgain.values.first, -value, "expected value to match")
let identityAgain = ObjectIdentifier(testMeterAgain)
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"
)
}
func testDestroyingCounter() throws {
@ -669,7 +682,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
let name = "counter-\(UUID().uuidString)"
let value = Int.random(in: 0 ... 1000)
let value = Int.random(in: 0...1000)
let counter = Counter(label: name)
counter.increment(by: value)
@ -691,7 +704,11 @@ class MetricsTests: XCTestCase {
XCTAssertEqual(testCounterAgain.values.first, Int64(value), "expected value to match")
let identityAgain = ObjectIdentifier(counterAgain)
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"
)
}
func testDestroyingTimer() throws {
@ -699,7 +716,7 @@ class MetricsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
let name = "timer-\(UUID().uuidString)"
let value = Int64.random(in: 0 ... 1000)
let value = Int64.random(in: 0...1000)
let timer = Timer(label: name)
timer.recordNanoseconds(value)
@ -720,7 +737,11 @@ class MetricsTests: XCTestCase {
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")
XCTAssertNotEqual(
identity,
identityAgain,
"since the cached metric was released, the created a new should have a different identity"
)
}
func testDescriptions() throws {

View File

@ -12,11 +12,12 @@
//
//===----------------------------------------------------------------------===//
@testable import CoreMetrics
@testable import Metrics
import MetricsTestKit
import XCTest
@testable import CoreMetrics
@testable import Metrics
class MetricsExtensionsTests: XCTestCase {
func testTimerBlock() throws {
// bootstrap with our test metrics
@ -40,7 +41,7 @@ class MetricsExtensionsTests: XCTestCase {
// run the test
let timer = Timer(label: "test-timer")
let testTimer = try metrics.expectTimer(timer)
let timeInterval = TimeInterval(Double.random(in: 1 ... 500))
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], Int64(timeInterval * 1_000_000_000), "expected value to match")
@ -54,22 +55,22 @@ class MetricsExtensionsTests: XCTestCase {
let timer = Timer(label: "test-timer")
let testTimer = try metrics.expectTimer(timer)
// nano
let nano = DispatchTimeInterval.nanoseconds(Int.random(in: 1 ... 500))
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]), nano.nano(), "expected value to match")
// micro
let micro = DispatchTimeInterval.microseconds(Int.random(in: 1 ... 500))
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]), micro.nano(), "expected value to match")
// milli
let milli = DispatchTimeInterval.milliseconds(Int.random(in: 1 ... 500))
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]), milli.nano(), "expected value to match")
// seconds
let sec = DispatchTimeInterval.seconds(Int.random(in: 1 ... 500))
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]), sec.nano(), "expected value to match")
@ -92,7 +93,11 @@ class MetricsExtensionsTests: XCTestCase {
let testTimer = try metrics.expectTimer(timer)
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
XCTAssertEqual(UInt64(testTimer.values.first!), 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")
}
@ -129,7 +134,7 @@ class MetricsExtensionsTests: XCTestCase {
MetricsSystem.bootstrapInternal(metrics)
let name = "timer-\(UUID().uuidString)"
let value = Int64.random(in: 0 ... 1000)
let value = Int64.random(in: 0...1000)
let timer = Timer(label: name)
timer.recordNanoseconds(value)
@ -140,7 +145,7 @@ class MetricsExtensionsTests: XCTestCase {
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
let secondsName = "timer-seconds-\(UUID().uuidString)"
let secondsValue = Int64.random(in: 0 ... 1000)
let secondsValue = Int64.random(in: 0...1000)
let secondsTimer = Timer(label: secondsName, preferredDisplayUnit: .seconds)
secondsTimer.recordSeconds(secondsValue)
@ -153,32 +158,67 @@ class MetricsExtensionsTests: XCTestCase {
let metrics = TestMetrics()
MetricsSystem.bootstrapInternal(metrics)
let value = Double.random(in: 0 ... 1000)
let value = Double.random(in: 0...1000)
let timer = Timer(label: "test", preferredDisplayUnit: .seconds)
timer.recordSeconds(value)
let testTimer = try metrics.expectTimer(timer)
testTimer.preferDisplayUnit(.nanoseconds)
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value * 1000 * 1000 * 1000, accuracy: 1.0, "expected value to match")
XCTAssertEqual(
testTimer.valueInPreferredUnit(atIndex: 0),
value * 1000 * 1000 * 1000,
accuracy: 1.0,
"expected value to match"
)
testTimer.preferDisplayUnit(.microseconds)
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value * 1000 * 1000, accuracy: 0.001, "expected value to match")
XCTAssertEqual(
testTimer.valueInPreferredUnit(atIndex: 0),
value * 1000 * 1000,
accuracy: 0.001,
"expected value to match"
)
testTimer.preferDisplayUnit(.milliseconds)
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value * 1000, accuracy: 0.000001, "expected value to match")
XCTAssertEqual(
testTimer.valueInPreferredUnit(atIndex: 0),
value * 1000,
accuracy: 0.000001,
"expected value to match"
)
testTimer.preferDisplayUnit(.seconds)
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value, accuracy: 0.000000001, "expected value to match")
XCTAssertEqual(
testTimer.valueInPreferredUnit(atIndex: 0),
value,
accuracy: 0.000000001,
"expected value to match"
)
testTimer.preferDisplayUnit(.minutes)
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value / 60, accuracy: 0.000000001, "expected value to match")
XCTAssertEqual(
testTimer.valueInPreferredUnit(atIndex: 0),
value / 60,
accuracy: 0.000000001,
"expected value to match"
)
testTimer.preferDisplayUnit(.hours)
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value / (60 * 60), accuracy: 0.000000001, "expected value to match")
XCTAssertEqual(
testTimer.valueInPreferredUnit(atIndex: 0),
value / (60 * 60),
accuracy: 0.000000001,
"expected value to match"
)
testTimer.preferDisplayUnit(.days)
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value / (60 * 60 * 24), accuracy: 0.000000001, "expected value to match")
XCTAssertEqual(
testTimer.valueInPreferredUnit(atIndex: 0),
value / (60 * 60 * 24),
accuracy: 0.000000001,
"expected value to match"
)
}
}

View File

@ -12,11 +12,12 @@
//
//===----------------------------------------------------------------------===//
@testable import CoreMetrics
import Dispatch
import MetricsTestKit
import XCTest
@testable import CoreMetrics
class SendableTest: XCTestCase {
func testSendableMetrics() async throws {
// bootstrap with our test metrics
@ -25,7 +26,7 @@ class SendableTest: XCTestCase {
do {
let name = "counter-\(UUID().uuidString)"
let value = Int.random(in: 0 ... 1000)
let value = Int.random(in: 0...1000)
let counter = Counter(label: name)
let task = Task.detached { () -> [Int64] in
@ -40,7 +41,7 @@ class SendableTest: XCTestCase {
do {
let name = "floating-point-counter-\(UUID().uuidString)"
let value = Double.random(in: 0 ... 0.9999)
let value = Double.random(in: 0...0.9999)
let counter = FloatingPointCounter(label: name)
let task = Task.detached { () -> Double in
@ -54,7 +55,7 @@ class SendableTest: XCTestCase {
do {
let name = "recorder-\(UUID().uuidString)"
let value = Double.random(in: -1000 ... 1000)
let value = Double.random(in: -1000...1000)
let recorder = Recorder(label: name)
let task = Task.detached { () -> [Double] in
@ -69,7 +70,7 @@ class SendableTest: XCTestCase {
do {
let name = "meter-\(UUID().uuidString)"
let value = Double.random(in: -1000 ... 1000)
let value = Double.random(in: -1000...1000)
let meter = Meter(label: name)
let task = Task.detached { () -> [Double] in
@ -84,7 +85,7 @@ class SendableTest: XCTestCase {
do {
let name = "timer-\(UUID().uuidString)"
let value = Int64.random(in: 0 ... 1000)
let value = Int64.random(in: 0...1000)
let timer = Timer(label: name)
let task = Task.detached { () -> [Int64] in

View File

@ -1,31 +0,0 @@
ARG swift_version=5.7
ARG ubuntu_version=focal
ARG base_image=swift:$swift_version-$ubuntu_version
FROM $base_image
# needed to do again after FROM due to docker limitation
ARG swift_version
ARG ubuntu_version
# set as UTF-8
RUN apt-get update && apt-get install -y locales locales-all
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
# dependencies
RUN apt-get update && apt-get install -y wget
RUN apt-get update && apt-get install -y lsof dnsutils netcat-openbsd net-tools curl jq # used by integration tests
# ruby and jazzy for docs generation
RUN apt-get update && apt-get install -y ruby ruby-dev libsqlite3-dev build-essential
# tools
RUN mkdir -p $HOME/.tools
RUN echo 'export PATH="$HOME/.tools:$PATH"' >> $HOME/.profile
# swiftformat (until part of the toolchain)
ARG swiftformat_version=0.49.6
RUN if [ "${swift_version}" = "5.7" ]; then git clone --branch $swiftformat_version --depth 1 https://github.com/nicklockwood/SwiftFormat $HOME/.tools/swift-format; fi
RUN if [ "${swift_version}" = "5.7" ]; then cd $HOME/.tools/swift-format && swift build -c release; fi
RUN if [ "${swift_version}" = "5.7" ]; then ln -s $HOME/.tools/swift-format/.build/release/swiftformat $HOME/.tools/swiftformat; fi

View File

@ -1,18 +0,0 @@
version: "3"
services:
runtime-setup:
image: swift-metrics:20.04-5.8
build:
args:
ubuntu_version: "focal"
swift_version: "5.8"
test:
image: swift-metrics:20.04-5.8
environment: []
#- SANITIZER_ARG=--sanitize=thread
shell:
image: swift-metrics:20.04-5.8

View File

@ -1,17 +0,0 @@
version: "3"
services:
runtime-setup:
image: swift-metrics:22.04-5.10
build:
args:
base_image: "swiftlang/swift:nightly-5.10-jammy"
test:
image: swift-metrics:22.04-5.10
environment: []
#- SANITIZER_ARG=--sanitize=thread
shell:
image: swift-metrics:22.04-5.10

View File

@ -1,18 +0,0 @@
version: "3"
services:
runtime-setup:
image: swift-metrics:22.04-5.9
build:
args:
ubuntu_version: "jammy"
swift_version: "5.9"
test:
image: swift-metrics:22.04-5.9
environment: []
#- SANITIZER_ARG=--sanitize=thread
shell:
image: swift-metrics:22.04-5.9

View File

@ -1,17 +0,0 @@
version: "3"
services:
runtime-setup:
image: swift-metrics:22.04-main
build:
args:
base_image: "swiftlang/swift:nightly-main-jammy"
test:
image: swift-metrics:22.04-main
environment: []
#- SANITIZER_ARG=--sanitize=thread
shell:
image: swift-metrics:22.04-main

View File

@ -1,37 +0,0 @@
# this file is not designed to be run directly
# instead, use the docker-compose.<os>.<swift> files
# eg docker-compose -f docker/docker-compose.yaml -f docker/docker-compose.1804.50.yaml run test
version: "3"
services:
runtime-setup:
image: swift-metrics:default
build:
context: .
dockerfile: Dockerfile
common: &common
image: swift-metrics:default
depends_on: [runtime-setup]
volumes:
- ~/.ssh:/root/.ssh
- ..:/code:z
working_dir: /code
cap_drop:
- CAP_NET_RAW
- CAP_NET_BIND_SERVICE
soundness:
<<: *common
command: /bin/bash -xcl "./scripts/soundness.sh"
test:
<<: *common
command: /bin/bash -xcl "swift test -Xswiftc -warnings-as-errors $${SANITIZER_ARG-}"
# util
shell:
<<: *common
entrypoint: /bin/bash -l

View File

@ -1,94 +0,0 @@
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift Metrics API open source project
##
## Copyright (c) 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
##
##===----------------------------------------------------------------------===##
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of SwiftNIO project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -eu
function usage() {
echo "$0 [-u] version"
echo
echo "OPTIONS:"
echo " -u: Additionally upload the podspec"
}
upload=false
while getopts ":u" opt; do
case $opt in
u)
upload=true
;;
\?)
usage
exit 1
;;
esac
done
shift "$((OPTIND-1))"
if [[ $# -eq 0 ]]; then
echo "Must provide target version"
exit 1
fi
version=$1
name="SwiftMetrics"
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
tmpdir=$(mktemp -d /tmp/.build_podspecsXXXXXX)
echo "Building podspec in $tmpdir"
cat >> "${tmpdir}/${name}.podspec" <<- EOF
Pod::Spec.new do |s|
s.name = '$name'
s.version = '$version'
s.license = { :type => 'Apache 2.0', :file => 'LICENSE.txt' }
s.summary = 'A Metrics API for Swift.'
s.homepage = 'https://github.com/apple/swift-metrics'
s.author = 'Apple Inc.'
s.source = { :git => 'https://github.com/apple/swift-metrics.git', :tag => s.version.to_s }
s.documentation_url = 'https://apple.github.io/swift-metrics'
s.module_name = '$name'
s.swift_version = '4.2'
s.cocoapods_version = '>=1.6.0'
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.9'
s.tvos.deployment_target = '9.0'
s.watchos.deployment_target = '2.0'
s.source_files = 'Sources/CoreMetrics/**/*.swift'
end
EOF
if $upload; then
echo "Uploading ${tmpdir}/${name}.podspec"
pod trunk push "${tmpdir}/${name}.podspec"
else
echo "Linting ${tmpdir}/${name}.podspec"
pod spec lint "${tmpdir}/${name}.podspec"
fi

View File

@ -1,68 +0,0 @@
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift Metrics API open source project
##
## Copyright (c) 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
##
##===----------------------------------------------------------------------===##
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) 2017-2020 Apple Inc. and the SwiftNIO project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of SwiftNIO project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -eu
function usage() {
echo >&2 "Usage: $0 REPO-GITHUB-URL NEW-VERSION OLD-VERSIONS..."
echo >&2
echo >&2 "This script requires a Swift 5.2+ toolchain."
echo >&2
echo >&2 "Examples:"
echo >&2
echo >&2 "Check between main and tag 2.1.1 of swift-nio:"
echo >&2 " $0 https://github.com/apple/swift-nio main 2.1.1"
echo >&2
echo >&2 "Check between HEAD and commit 64cf63d7 using the provided toolchain:"
echo >&2 " xcrun --toolchain org.swift.5120190702a $0 ../some-local-repo HEAD 64cf63d7"
}
if [[ $# -lt 3 ]]; then
usage
exit 1
fi
tmpdir=$(mktemp -d /tmp/.check-api_XXXXXX)
repo_url=$1
new_tag=$2
shift 2
repodir="$tmpdir/repo"
git clone "$repo_url" "$repodir"
git -C "$repodir" fetch -q origin '+refs/pull/*:refs/remotes/origin/pr/*'
cd "$repodir"
git checkout -q "$new_tag"
for old_tag in "$@"; do
echo "Checking public API breakages from $old_tag to $new_tag"
swift package diagnose-api-breaking-changes "$old_tag"
done
echo done

View File

@ -1,16 +0,0 @@
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## 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
##
##===----------------------------------------------------------------------===##
swift package --disable-sandbox preview-documentation --target "$1"

View File

@ -1,163 +0,0 @@
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## 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
##
##===----------------------------------------------------------------------===##
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of SwiftNIO project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -eu
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
function replace_acceptable_years() {
# this needs to replace all acceptable forms with 'YEARS'
sed -e 's/20[12][789012]-20[12][89012]/YEARS/' -e 's/20[12][89012]/YEARS/'
}
printf "=> Checking for unacceptable language... "
# This greps for unacceptable terminology. The square bracket[s] are so that
# "git grep" doesn't find the lines that greps :).
unacceptable_terms=(
-e blacklis[t]
-e whitelis[t]
-e slav[e]
-e sanit[y]
)
if git grep --color=never -i "${unacceptable_terms[@]}" > /dev/null; then
printf "\033[0;31mUnacceptable language found.\033[0m\n"
git grep -i "${unacceptable_terms[@]}"
exit 1
fi
printf "\033[0;32mokay.\033[0m\n"
printf "=> Checking format... "
if [[ ! -x $(which swiftformat) ]]; then
printf "\033[0;31mswiftformat not found!\033[0m\n"
exit 1
fi
FIRST_OUT="$(git status --porcelain)"
swiftformat . > /dev/null 2>&1
SECOND_OUT="$(git status --porcelain)"
if [[ "$FIRST_OUT" != "$SECOND_OUT" ]]; then
printf "\033[0;31mformatting issues!\033[0m\n"
git --no-pager diff
exit 1
else
printf "\033[0;32mokay.\033[0m\n"
fi
printf "=> Checking license headers\n"
tmp=$(mktemp /tmp/.swift-metrics-soundness_XXXXXX)
for language in swift-or-c bash dtrace; do
printf " * $language... "
declare -a matching_files
declare -a exceptions
expections=( )
matching_files=( -name '*' )
case "$language" in
swift-or-c)
exceptions=( -name Package.swift -o -name "Package@swift-*.swift" )
matching_files=( -name '*.swift' -o -name '*.c' -o -name '*.h' )
cat > "$tmp" <<"EOF"
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Metrics API open source project
//
// Copyright (c) YEARS 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
//
//===----------------------------------------------------------------------===//
EOF
;;
bash)
matching_files=( -name '*.sh' )
cat > "$tmp" <<"EOF"
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift Metrics API open source project
##
## Copyright (c) YEARS 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
##
##===----------------------------------------------------------------------===##
EOF
;;
dtrace)
matching_files=( -name '*.d' )
cat > "$tmp" <<"EOF"
#!/usr/sbin/dtrace -q -s
/*===----------------------------------------------------------------------===*
*
* This source file is part of the Swift Metrics API open source project
*
* Copyright (c) YEARS 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
*
*===----------------------------------------------------------------------===*/
EOF
;;
*)
echo >&2 "ERROR: unknown language '$language'"
;;
esac
expected_lines=$(cat "$tmp" | wc -l)
expected_sha=$(cat "$tmp" | shasum)
(
cd "$here/.."
find . \
\( \! -path './.build/*' -a \
\( "${matching_files[@]}" \) -a \
\( \! \( "${exceptions[@]}" \) \) \) | while read line; do
if [[ "$(cat "$line" | replace_acceptable_years | head -n $expected_lines | shasum)" != "$expected_sha" ]]; then
printf "\033[0;31mmissing headers in file '$line'!\033[0m\n"
diff -u <(cat "$line" | replace_acceptable_years | head -n $expected_lines) "$tmp"
exit 1
fi
done
printf "\033[0;32mokay.\033[0m\n"
)
done
rm "$tmp"