Compare commits
14 Commits
eb5ccea1c4
...
199b458a9f
| Author | SHA1 | Date |
|---|---|---|
|
|
199b458a9f | |
|
|
cbd39ceaca | |
|
|
029e902273 | |
|
|
58f390a873 | |
|
|
53de3bfa9a | |
|
|
5e63558d12 | |
|
|
726392cf4e | |
|
|
4a491b5ad5 | |
|
|
58e2968a7f | |
|
|
4ec5a219dc | |
|
|
c1976209b5 | |
|
|
cffefdc627 | |
|
|
d720898dbf | |
|
|
569db3a632 |
|
|
@ -0,0 +1,8 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
|
@ -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
|
||||
|
|
@ -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: "--explicit-target-dependency-import-check error"
|
||||
linux_5_10_arguments_override: "--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_next_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"
|
||||
|
|
@ -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: "--explicit-target-dependency-import-check error"
|
||||
linux_5_10_arguments_override: "--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_next_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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
16
.swiftformat
16
.swiftformat
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// swift-tools-version:5.8
|
||||
// swift-tools-version:5.9
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift Metrics API open source project
|
||||
|
|
@ -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"
|
||||
|
|
@ -44,3 +40,25 @@ let package = Package(
|
|||
),
|
||||
]
|
||||
)
|
||||
|
||||
for target in package.targets {
|
||||
var settings = target.swiftSettings ?? []
|
||||
settings.append(.enableExperimentalFeature("StrictConcurrency=complete"))
|
||||
target.swiftSettings = settings
|
||||
}
|
||||
|
||||
// --- STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- //
|
||||
for target in package.targets {
|
||||
switch target.type {
|
||||
case .regular, .test, .executable:
|
||||
var settings = target.swiftSettings ?? []
|
||||
// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md
|
||||
settings.append(.enableUpcomingFeature("MemberImportVisibility"))
|
||||
target.swiftSettings = settings
|
||||
case .macro, .plugin, .system, .binary:
|
||||
() // not applicable
|
||||
@unknown default:
|
||||
() // we don't know what to do here, do nothing
|
||||
}
|
||||
}
|
||||
// --- END: STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- //
|
||||
|
|
|
|||
|
|
@ -90,12 +90,18 @@ This API was designed with the contributors to the Swift on Server community and
|
|||
|
||||
### Metric types
|
||||
|
||||
The API supports four metric types:
|
||||
The API supports six metric types:
|
||||
|
||||
`Counter`: A counter is a cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart. For example, you can use a counter to represent the number of requests served, tasks completed, or errors.
|
||||
|
||||
```swift
|
||||
counter.increment(by: 100)
|
||||
```
|
||||
|
||||
- `FloatingPointCounter`: A variation of a `Counter` that records a floating point value, instead of an integer.
|
||||
|
||||
```swift
|
||||
floatingPointCounter.increment(by: 10.5)
|
||||
```
|
||||
|
||||
`Gauge`: A Gauge is a metric that represents a single numerical value that can arbitrarily go up and down. Gauges are typically used for measured values like temperatures or current memory usage, but also "counts" that can go up and down, like the number of active threads. Gauges are modeled as a `Recorder` with a sample size of 1 that does not perform any aggregation.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,6 @@ with the all the required detail.
|
|||
and in the Server → Security Updates category on the [Swift forums][swift-forums-sec].
|
||||
|
||||
[sswg]: https://github.com/swift-server/sswg
|
||||
[sswg-security]: https://github.com/swift-server/sswg/blob/main/security/README.md
|
||||
[sswg-security]: https://www.swift.org/sswg/security/
|
||||
[swift-forums-sec]: https://forums.swift.org/c/server/security-updates/
|
||||
[mitre]: https://cveform.mitre.org/
|
||||
|
|
|
|||
|
|
@ -91,7 +91,9 @@ This API was designed with the contributors to the Swift on Server community and
|
|||
### Metric types
|
||||
|
||||
- ``Counter``
|
||||
- ``FloatingPointCounter``
|
||||
- ``Meter``
|
||||
- ``Recorder``
|
||||
- ``Gauge``
|
||||
- ``Timer``
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ import Darwin
|
|||
import WinSDK
|
||||
#elseif canImport(Glibc)
|
||||
import Glibc
|
||||
#elseif canImport(Android)
|
||||
import Android
|
||||
#elseif canImport(Musl)
|
||||
import Musl
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
///
|
||||
|
|
@ -23,8 +24,15 @@ extension Timer {
|
|||
/// - label: The label for the Timer.
|
||||
/// - dimensions: The dimensions for the Timer.
|
||||
/// - body: Closure to run & record.
|
||||
#if compiler(>=6.0)
|
||||
@available(*, deprecated, message: "Please use non-static version on an already created Timer")
|
||||
#endif
|
||||
@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 +96,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.
|
||||
///
|
||||
|
|
@ -107,4 +115,44 @@ extension Timer {
|
|||
|
||||
self.recordNanoseconds(nanoseconds.partialValue)
|
||||
}
|
||||
|
||||
#if compiler(>=6.0)
|
||||
/// Convenience for measuring duration of a closure.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - clock: The clock used for measuring the duration. Defaults to the continuous clock.
|
||||
/// - body: The closure to record the duration of.
|
||||
@inlinable
|
||||
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||
public func measure<Result, Failure: Error, Clock: _Concurrency.Clock>(
|
||||
clock: Clock = .continuous,
|
||||
body: () throws(Failure) -> Result
|
||||
) throws(Failure) -> Result where Clock.Duration == Duration {
|
||||
let start = clock.now
|
||||
defer {
|
||||
self.record(duration: start.duration(to: clock.now))
|
||||
}
|
||||
return try body()
|
||||
}
|
||||
|
||||
/// Convenience for measuring duration of a closure.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - clock: The clock used for measuring the duration. Defaults to the continuous clock.
|
||||
/// - isolation: The isolation of the method. Defaults to the isolation of the caller.
|
||||
/// - body: The closure to record the duration of.
|
||||
@inlinable
|
||||
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||
public func measure<Result, Failure: Error, Clock: _Concurrency.Clock>(
|
||||
clock: Clock = .continuous,
|
||||
isolation: isolated (any Actor)? = #isolation,
|
||||
body: () async throws(Failure) -> sending Result
|
||||
) async throws(Failure) -> sending Result where Clock.Duration == Duration {
|
||||
let start = clock.now
|
||||
defer {
|
||||
self.record(duration: start.duration(to: clock.now))
|
||||
}
|
||||
return try await body()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -12,12 +12,14 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@testable import CoreMetrics
|
||||
@testable import Metrics
|
||||
import MetricsTestKit
|
||||
import XCTest
|
||||
|
||||
@testable import CoreMetrics
|
||||
@testable import Metrics
|
||||
|
||||
class MetricsExtensionsTests: XCTestCase {
|
||||
@available(*, deprecated)
|
||||
func testTimerBlock() throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
|
|
@ -40,7 +42,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 +56,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 +94,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 +135,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 +146,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,33 +159,105 @@ 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"
|
||||
)
|
||||
}
|
||||
|
||||
#if compiler(>=6.0)
|
||||
func testTimerMeasure() async throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
// run the test
|
||||
let name = "timer-\(UUID().uuidString)"
|
||||
let delay = Duration.milliseconds(5)
|
||||
let timer = Timer(label: name)
|
||||
try await timer.measure {
|
||||
try await Task.sleep(for: delay)
|
||||
}
|
||||
|
||||
let expectedTimer = try metrics.expectTimer(name)
|
||||
XCTAssertEqual(1, expectedTimer.values.count, "expected number of entries to match")
|
||||
XCTAssertGreaterThan(expectedTimer.values[0], delay.nanosecondsClamped, "expected delay to match")
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func testTimerMeasureFromMainActor() async throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
// run the test
|
||||
let name = "timer-\(UUID().uuidString)"
|
||||
let delay = Duration.milliseconds(5)
|
||||
let timer = Timer(label: name)
|
||||
try await timer.measure {
|
||||
try await Task.sleep(for: delay)
|
||||
}
|
||||
|
||||
let expectedTimer = try metrics.expectTimer(name)
|
||||
XCTAssertEqual(1, expectedTimer.values.count, "expected number of entries to match")
|
||||
XCTAssertGreaterThan(expectedTimer.values[0], delay.nanosecondsClamped, "expected delay to match")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// https://bugs.swift.org/browse/SR-6310
|
||||
|
|
@ -211,3 +289,25 @@ extension DispatchTimeInterval {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.7)
|
||||
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||
extension Swift.Duration {
|
||||
fileprivate var nanosecondsClamped: Int64 {
|
||||
let components = self.components
|
||||
|
||||
let secondsComponentNanos = components.seconds.multipliedReportingOverflow(by: 1_000_000_000)
|
||||
let attosCompononentNanos = components.attoseconds / 1_000_000_000
|
||||
let combinedNanos = secondsComponentNanos.partialValue.addingReportingOverflow(attosCompononentNanos)
|
||||
|
||||
guard
|
||||
!secondsComponentNanos.overflow,
|
||||
!combinedNanos.overflow
|
||||
else {
|
||||
return .max
|
||||
}
|
||||
|
||||
return combinedNanos.partialValue
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,39 +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
|
||||
##
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
set -eu
|
||||
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
contributors=$( cd "$here"/.. && git shortlog -es | cut -f2 | sed 's/^/- /' )
|
||||
|
||||
cat > "$here/../CONTRIBUTORS.txt" <<- EOF
|
||||
For the purpose of tracking copyright, this is the list of individuals and
|
||||
organizations who have contributed source code to the Swift Metrics API.
|
||||
|
||||
For employees of an organization/company where the copyright of work done
|
||||
by employees of that company is held by the company itself, only the company
|
||||
needs to be listed here.
|
||||
|
||||
## COPYRIGHT HOLDERS
|
||||
|
||||
- Apple Inc. (all contributors with '@apple.com')
|
||||
|
||||
### Contributors
|
||||
|
||||
$contributors
|
||||
|
||||
**Updating this list**
|
||||
|
||||
Please do not edit this file manually. It is generated using \`./scripts/generate_contributors_list.sh\`. If a name is misspelled or appearing multiple times: add an entry in \`./.mailmap\`
|
||||
EOF
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
Loading…
Reference in New Issue