Migrate to GitHub Actions (#151)
This commit is contained in:
parent
569db3a632
commit
d720898dbf
|
|
@ -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: "-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"
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
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.
|
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).
|
||||||
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.
|
|
||||||
|
|
||||||
## How to contribute your work
|
## How to contribute your work
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,6 @@ let package = Package(
|
||||||
.library(name: "Metrics", targets: ["Metrics"]),
|
.library(name: "Metrics", targets: ["Metrics"]),
|
||||||
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
|
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
|
||||||
],
|
],
|
||||||
dependencies: [
|
|
||||||
// ~~~ SwiftPM Plugins ~~~
|
|
||||||
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
|
|
||||||
],
|
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
name: "CoreMetrics"
|
name: "CoreMetrics"
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ public final class Counter {
|
||||||
/// Increment the counter.
|
/// Increment the counter.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - by: Amount to increment by.
|
/// - amount: Amount to increment by.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func increment<DataType: BinaryInteger>(by amount: DataType) {
|
public func increment<DataType: BinaryInteger>(by amount: DataType) {
|
||||||
self._handler.increment(by: Int64(amount))
|
self._handler.increment(by: Int64(amount))
|
||||||
|
|
@ -88,7 +88,7 @@ extension Counter {
|
||||||
|
|
||||||
extension Counter: CustomStringConvertible {
|
extension Counter: CustomStringConvertible {
|
||||||
public var description: String {
|
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.
|
/// Increment the FloatingPointCounter.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - by: Amount to increment by.
|
/// - amount: Amount to increment by.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func increment<DataType: BinaryFloatingPoint>(by amount: DataType) {
|
public func increment<DataType: BinaryFloatingPoint>(by amount: DataType) {
|
||||||
self._handler.increment(by: Double(amount))
|
self._handler.increment(by: Double(amount))
|
||||||
|
|
@ -167,7 +167,7 @@ extension FloatingPointCounter {
|
||||||
|
|
||||||
extension FloatingPointCounter: CustomStringConvertible {
|
extension FloatingPointCounter: CustomStringConvertible {
|
||||||
public var description: String {
|
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.
|
/// Increment the Meter.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - by: Amount to increment by.
|
/// - amount: Amount to increment by.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func increment<DataType: BinaryFloatingPoint>(by amount: DataType) {
|
public func increment<DataType: BinaryFloatingPoint>(by amount: DataType) {
|
||||||
self._handler.increment(by: Double(amount))
|
self._handler.increment(by: Double(amount))
|
||||||
|
|
@ -250,7 +250,7 @@ public final class Meter {
|
||||||
/// Decrement the Meter.
|
/// Decrement the Meter.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - by: Amount to decrement by.
|
/// - amount: Amount to decrement by.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func decrement<DataType: BinaryFloatingPoint>(by amount: DataType) {
|
public func decrement<DataType: BinaryFloatingPoint>(by amount: DataType) {
|
||||||
self._handler.decrement(by: Double(amount))
|
self._handler.decrement(by: Double(amount))
|
||||||
|
|
@ -284,7 +284,7 @@ extension Meter {
|
||||||
|
|
||||||
extension Meter: CustomStringConvertible {
|
extension Meter: CustomStringConvertible {
|
||||||
public var description: String {
|
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 {
|
extension Recorder: CustomStringConvertible {
|
||||||
public var description: String {
|
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 nanoseconds = TimeUnit(code: .nanoseconds, scaleFromNanoseconds: 1)
|
||||||
public static let microseconds = TimeUnit(code: .microseconds, scaleFromNanoseconds: 1000)
|
public static let microseconds = TimeUnit(code: .microseconds, scaleFromNanoseconds: 1000)
|
||||||
public static let milliseconds = TimeUnit(code: .milliseconds, scaleFromNanoseconds: 1000 * TimeUnit.microseconds.scaleFromNanoseconds)
|
public static let milliseconds = TimeUnit(
|
||||||
public static let seconds = TimeUnit(code: .seconds, scaleFromNanoseconds: 1000 * TimeUnit.milliseconds.scaleFromNanoseconds)
|
code: .milliseconds,
|
||||||
public static let minutes = TimeUnit(code: .minutes, scaleFromNanoseconds: 60 * TimeUnit.seconds.scaleFromNanoseconds)
|
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 hours = TimeUnit(code: .hours, scaleFromNanoseconds: 60 * TimeUnit.minutes.scaleFromNanoseconds)
|
||||||
public static let days = TimeUnit(code: .days, scaleFromNanoseconds: 24 * TimeUnit.hours.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.
|
/// Record a duration in nanoseconds.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - duration: Duration to record.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func recordNanoseconds(_ duration: Int64) {
|
public func recordNanoseconds(_ duration: Int64) {
|
||||||
self._handler.recordNanoseconds(duration)
|
self._handler.recordNanoseconds(duration)
|
||||||
|
|
@ -445,7 +454,7 @@ public final class Timer {
|
||||||
/// Record a duration in nanoseconds.
|
/// Record a duration in nanoseconds.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - duration: Duration to record.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func recordNanoseconds<DataType: BinaryInteger>(_ duration: DataType) {
|
public func recordNanoseconds<DataType: BinaryInteger>(_ duration: DataType) {
|
||||||
self.recordNanoseconds(duration >= Int64.max ? Int64.max : Int64(duration))
|
self.recordNanoseconds(duration >= Int64.max ? Int64.max : Int64(duration))
|
||||||
|
|
@ -454,7 +463,7 @@ public final class Timer {
|
||||||
/// Record a duration in microseconds.
|
/// Record a duration in microseconds.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - duration: Duration to record.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func recordMicroseconds<DataType: BinaryInteger>(_ duration: DataType) {
|
public func recordMicroseconds<DataType: BinaryInteger>(_ duration: DataType) {
|
||||||
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
|
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
|
||||||
|
|
@ -470,7 +479,7 @@ public final class Timer {
|
||||||
/// Record a duration in microseconds.
|
/// Record a duration in microseconds.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - duration: Duration to record.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func recordMicroseconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
|
public func recordMicroseconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
|
||||||
self.recordNanoseconds(Double(duration * 1000) < Double(Int64.max) ? Int64(duration * 1000) : Int64.max)
|
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.
|
/// Record a duration in milliseconds.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - duration: Duration to record.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func recordMilliseconds<DataType: BinaryInteger>(_ duration: DataType) {
|
public func recordMilliseconds<DataType: BinaryInteger>(_ duration: DataType) {
|
||||||
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
|
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
|
||||||
|
|
@ -495,16 +504,18 @@ public final class Timer {
|
||||||
/// Record a duration in milliseconds.
|
/// Record a duration in milliseconds.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - duration: Duration to record.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func recordMilliseconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
|
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.
|
/// Record a duration in seconds.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - duration: Duration to record.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func recordSeconds<DataType: BinaryInteger>(_ duration: DataType) {
|
public func recordSeconds<DataType: BinaryInteger>(_ duration: DataType) {
|
||||||
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
|
guard duration <= Int64.max else { return self.recordNanoseconds(Int64.max) }
|
||||||
|
|
@ -520,10 +531,12 @@ public final class Timer {
|
||||||
/// Record a duration in seconds.
|
/// Record a duration in seconds.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - duration: Duration to record.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func recordSeconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
|
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`.
|
/// - label: The label for the `Timer`.
|
||||||
/// - dimensions: The dimensions 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.
|
/// - 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)
|
let handler = MetricsSystem.factory.makeTimer(label: label, dimensions: dimensions)
|
||||||
handler.preferDisplayUnit(displayUnit)
|
handler.preferDisplayUnit(displayUnit)
|
||||||
self.init(label: label, dimensions: dimensions, handler: handler)
|
self.init(label: label, dimensions: dimensions, handler: handler)
|
||||||
|
|
@ -560,7 +577,7 @@ extension Timer {
|
||||||
|
|
||||||
extension Timer: CustomStringConvertible {
|
extension Timer: CustomStringConvertible {
|
||||||
public var description: String {
|
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.
|
/// Returns a reference to the configured factory.
|
||||||
public static var factory: MetricsFactory {
|
public static var factory: MetricsFactory {
|
||||||
return self._factory.underlying
|
self._factory.underlying
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acquire a writer lock for the duration of the given block.
|
/// 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.
|
/// - Parameter body: The block to execute while holding the lock.
|
||||||
/// - Returns: The value returned by the block.
|
/// - Returns: The value returned by the block.
|
||||||
public static func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
|
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.
|
// 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) {
|
func replaceFactory(_ factory: MetricsFactory, validate: Bool) {
|
||||||
self.lock.withWriterLock {
|
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._underlying = factory
|
||||||
self.initialized = true
|
self.initialized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var underlying: MetricsFactory {
|
var underlying: MetricsFactory {
|
||||||
return self.lock.withReaderLock {
|
self.lock.withReaderLock {
|
||||||
return self._underlying
|
self._underlying
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
|
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`.
|
/// - label: The label for the `FloatingPointCounterHandler`.
|
||||||
/// - dimensions: The dimensions for the `FloatingPointCounterHandler`.
|
/// - dimensions: The dimensions for the `FloatingPointCounterHandler`.
|
||||||
public func makeFloatingPointCounter(label: String, dimensions: [(String, String)]) -> 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.
|
/// Invoked when the corresponding `FloatingPointCounter`'s `destroy()` function is invoked.
|
||||||
|
|
@ -915,7 +935,7 @@ extension MetricsFactory {
|
||||||
/// - label: The label for the `MeterHandler`.
|
/// - label: The label for the `MeterHandler`.
|
||||||
/// - dimensions: The dimensions for the `MeterHandler`.
|
/// - dimensions: The dimensions for the `MeterHandler`.
|
||||||
public func makeMeter(label: String, dimensions: [(String, String)]) -> 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.
|
/// Invoked when the corresponding `Meter`'s `destroy()` function is invoked.
|
||||||
|
|
@ -1051,7 +1071,7 @@ public protocol TimerHandler: AnyObject, _SwiftMetricsSendableProtocol {
|
||||||
/// Record a duration in nanoseconds.
|
/// Record a duration in nanoseconds.
|
||||||
///
|
///
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - duration: Duration to record.
|
||||||
func recordNanoseconds(_ duration: Int64)
|
func recordNanoseconds(_ duration: Int64)
|
||||||
|
|
||||||
/// Set the preferred display unit for this TimerHandler.
|
/// 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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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) {
|
public func destroyCounter(_ handler: CounterHandler) {
|
||||||
|
|
@ -1133,11 +1153,11 @@ public final class MultiplexMetricsHandler: MetricsFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
func increment(by amount: Int64) {
|
func increment(by amount: Int64) {
|
||||||
self.counters.forEach { $0.increment(by: amount) }
|
for counter in self.counters { counter.increment(by: amount) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func reset() {
|
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) {
|
func increment(by amount: Double) {
|
||||||
self.counters.forEach { $0.increment(by: amount) }
|
for counter in self.counters { counter.increment(by: amount) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func reset() {
|
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) {
|
func set(_ value: Int64) {
|
||||||
self.meters.forEach { $0.set(value) }
|
for meter in self.meters { meter.set(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func set(_ value: Double) {
|
func set(_ value: Double) {
|
||||||
self.meters.forEach { $0.set(value) }
|
for meter in self.meters { meter.set(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func increment(by amount: Double) {
|
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) {
|
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 {
|
private final class MuxRecorder: RecorderHandler {
|
||||||
let recorders: [RecorderHandler]
|
let recorders: [RecorderHandler]
|
||||||
public init(factories: [MetricsFactory], label: String, dimensions: [(String, String)], aggregate: Bool) {
|
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) {
|
func record(_ value: Int64) {
|
||||||
self.recorders.forEach { $0.record(value) }
|
for recorder in self.recorders { recorder.record(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func record(_ value: Double) {
|
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) {
|
func recordNanoseconds(_ duration: Int64) {
|
||||||
self.timers.forEach { $0.recordNanoseconds(duration) }
|
for timer in self.timers { timer.recordNanoseconds(duration) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func preferDisplayUnit(_ unit: TimeUnit) {
|
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.
|
/// 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()
|
public static let instance = NOOPMetricsHandler()
|
||||||
|
|
||||||
private init() {}
|
private init() {}
|
||||||
|
|
||||||
public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
|
public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
|
||||||
return self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeFloatingPointCounter(label: String, dimensions: [(String, String)]) -> FloatingPointCounterHandler {
|
public func makeFloatingPointCounter(label: String, dimensions: [(String, String)]) -> FloatingPointCounterHandler {
|
||||||
return self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
|
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
|
||||||
return self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
|
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
|
||||||
return self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
|
public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
|
||||||
return self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
public func destroyCounter(_: CounterHandler) {}
|
public func destroyCounter(_: CounterHandler) {}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
@_exported import CoreMetrics
|
@_exported import CoreMetrics
|
||||||
@_exported import class CoreMetrics.Timer
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
@_exported import class CoreMetrics.Timer
|
||||||
|
|
||||||
extension Timer {
|
extension Timer {
|
||||||
/// Convenience for measuring duration of a closure.
|
/// Convenience for measuring duration of a closure.
|
||||||
///
|
///
|
||||||
|
|
@ -24,7 +25,11 @@ extension Timer {
|
||||||
/// - dimensions: The dimensions for the Timer.
|
/// - dimensions: The dimensions for the Timer.
|
||||||
/// - body: Closure to run & record.
|
/// - body: Closure to run & record.
|
||||||
@inlinable
|
@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 timer = Timer(label: label, dimensions: dimensions)
|
||||||
let start = DispatchTime.now().uptimeNanoseconds
|
let start = DispatchTime.now().uptimeNanoseconds
|
||||||
defer {
|
defer {
|
||||||
|
|
@ -88,7 +93,7 @@ extension Timer {
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
/// `Duration` will be converted to an `Int64` number of nanoseconds, and then recorded with nanosecond precision.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ public final class TestMetrics: MetricsFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeCounter(label: String, dimensions: [(String, String)]) -> CounterHandler {
|
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)] {
|
if let existing = self._counters[.init(label: label, dimensions: dimensions)] {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +80,7 @@ public final class TestMetrics: MetricsFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeMeter(label: String, dimensions: [(String, String)]) -> MeterHandler {
|
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)] {
|
if let existing = self._meters[.init(label: label, dimensions: dimensions)] {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +91,7 @@ public final class TestMetrics: MetricsFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler {
|
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)] {
|
if let existing = self._recorders[.init(label: label, dimensions: dimensions)] {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +102,7 @@ public final class TestMetrics: MetricsFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeTimer(label: String, dimensions: [(String, String)]) -> TimerHandler {
|
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)] {
|
if let existing = self._timers[.init(label: label, dimensions: dimensions)] {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
|
|
@ -148,15 +148,15 @@ public final class TestMetrics: MetricsFactory {
|
||||||
extension TestMetrics.FullKey: Hashable {
|
extension TestMetrics.FullKey: Hashable {
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
self.label.hash(into: &hasher)
|
self.label.hash(into: &hasher)
|
||||||
self.dimensions.forEach { dim in
|
for dim in self.dimensions {
|
||||||
dim.0.hash(into: &hasher)
|
dim.0.hash(into: &hasher)
|
||||||
dim.1.hash(into: &hasher)
|
dim.1.hash(into: &hasher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: TestMetrics.FullKey, rhs: TestMetrics.FullKey) -> Bool {
|
public static func == (lhs: TestMetrics.FullKey, rhs: TestMetrics.FullKey) -> Bool {
|
||||||
return lhs.label == rhs.label &&
|
lhs.label == rhs.label
|
||||||
Dictionary(uniqueKeysWithValues: lhs.dimensions) == Dictionary(uniqueKeysWithValues: rhs.dimensions)
|
&& Dictionary(uniqueKeysWithValues: lhs.dimensions) == Dictionary(uniqueKeysWithValues: rhs.dimensions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,11 +193,11 @@ extension TestMetrics {
|
||||||
// MARK: - Gauge
|
// MARK: - Gauge
|
||||||
|
|
||||||
public func expectGauge(_ metric: Gauge) throws -> TestRecorder {
|
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 {
|
public func expectGauge(_ label: String, _ dimensions: [(String, String)] = []) throws -> TestRecorder {
|
||||||
return try self.expectRecorder(label, dimensions)
|
try self.expectRecorder(label, dimensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Meter
|
// MARK: - Meter
|
||||||
|
|
@ -299,7 +299,7 @@ public final class TestCounter: TestMetric, CounterHandler, Equatable {
|
||||||
public let dimensions: [(String, String)]
|
public let dimensions: [(String, String)]
|
||||||
|
|
||||||
public var key: TestMetrics.FullKey {
|
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()
|
let lock = NSLock()
|
||||||
|
|
@ -318,33 +318,33 @@ public final class TestCounter: TestMetric, CounterHandler, Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reset() {
|
public func reset() {
|
||||||
return self.lock.withLock {
|
self.lock.withLock {
|
||||||
self._values = []
|
self._values = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var lastValue: Int64? {
|
public var lastValue: Int64? {
|
||||||
return self.last?.1
|
self.last?.1
|
||||||
}
|
}
|
||||||
|
|
||||||
public var totalValue: Int64 {
|
public var totalValue: Int64 {
|
||||||
return self.values.reduce(0, +)
|
self.values.reduce(0, +)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var last: (Date, Int64)? {
|
public var last: (Date, Int64)? {
|
||||||
return self.lock.withLock {
|
self.lock.withLock {
|
||||||
self._values.last
|
self._values.last
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var values: [Int64] {
|
public var values: [Int64] {
|
||||||
return self.lock.withLock {
|
self.lock.withLock {
|
||||||
self._values.map { $0.1 }
|
self._values.map { $0.1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: TestCounter, rhs: TestCounter) -> Bool {
|
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 let dimensions: [(String, String)]
|
||||||
|
|
||||||
public var key: TestMetrics.FullKey {
|
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()
|
let lock = NSLock()
|
||||||
|
|
@ -430,23 +430,23 @@ public final class TestMeter: TestMetric, MeterHandler, Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public var lastValue: Double? {
|
public var lastValue: Double? {
|
||||||
return self.last?.1
|
self.last?.1
|
||||||
}
|
}
|
||||||
|
|
||||||
public var last: (Date, Double)? {
|
public var last: (Date, Double)? {
|
||||||
return self.lock.withLock {
|
self.lock.withLock {
|
||||||
self._values.last
|
self._values.last
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var values: [Double] {
|
public var values: [Double] {
|
||||||
return self.lock.withLock {
|
self.lock.withLock {
|
||||||
self._values.map { $0.1 }
|
self._values.map { $0.1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: TestMeter, rhs: TestMeter) -> Bool {
|
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 let aggregate: Bool
|
||||||
|
|
||||||
public var key: TestMetrics.FullKey {
|
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()
|
let lock = NSLock()
|
||||||
|
|
@ -482,23 +482,23 @@ public final class TestRecorder: TestMetric, RecorderHandler, Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public var lastValue: Double? {
|
public var lastValue: Double? {
|
||||||
return self.last?.1
|
self.last?.1
|
||||||
}
|
}
|
||||||
|
|
||||||
public var last: (Date, Double)? {
|
public var last: (Date, Double)? {
|
||||||
return self.lock.withLock {
|
self.lock.withLock {
|
||||||
self._values.last
|
self._values.last
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var values: [Double] {
|
public var values: [Double] {
|
||||||
return self.lock.withLock {
|
self.lock.withLock {
|
||||||
self._values.map { $0.1 }
|
self._values.map { $0.1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: TestRecorder, rhs: TestRecorder) -> Bool {
|
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 let dimensions: [(String, String)]
|
||||||
|
|
||||||
public var key: TestMetrics.FullKey {
|
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()
|
let lock = NSLock()
|
||||||
|
|
@ -543,23 +543,23 @@ public final class TestTimer: TestMetric, TimerHandler, Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public var lastValue: Int64? {
|
public var lastValue: Int64? {
|
||||||
return self.last?.1
|
self.last?.1
|
||||||
}
|
}
|
||||||
|
|
||||||
public var values: [Int64] {
|
public var values: [Int64] {
|
||||||
return self.lock.withLock {
|
self.lock.withLock {
|
||||||
return self._values.map { $0.1 }
|
self._values.map { $0.1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var last: (Date, Int64)? {
|
public var last: (Date, Int64)? {
|
||||||
return self.lock.withLock {
|
self.lock.withLock {
|
||||||
return self._values.last
|
self._values.last
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: TestTimer, rhs: TestTimer) -> Bool {
|
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 MetricsTestKit
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
@testable import CoreMetrics
|
||||||
|
|
||||||
class MetricsTests: XCTestCase {
|
class MetricsTests: XCTestCase {
|
||||||
func testCounters() throws {
|
func testCounters() throws {
|
||||||
// bootstrap with our test metrics
|
// bootstrap with our test metrics
|
||||||
|
|
@ -526,14 +527,14 @@ class MetricsTests: XCTestCase {
|
||||||
let value = Int.random(in: Int.min...Int.max)
|
let value = Int.random(in: Int.min...Int.max)
|
||||||
let muxCounter = Counter(label: name)
|
let muxCounter = Counter(label: name)
|
||||||
muxCounter.increment(by: value)
|
muxCounter.increment(by: value)
|
||||||
factories.forEach { factory in
|
for factory in factories {
|
||||||
let counter = factory.counters.first
|
let counter = factory.counters.first
|
||||||
XCTAssertEqual(counter?.label, name, "expected label to match")
|
XCTAssertEqual(counter?.label, name, "expected label to match")
|
||||||
XCTAssertEqual(counter?.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(counter?.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(counter?.lastValue, Int64(value), "expected value to match")
|
XCTAssertEqual(counter?.lastValue, Int64(value), "expected value to match")
|
||||||
}
|
}
|
||||||
muxCounter.reset()
|
muxCounter.reset()
|
||||||
factories.forEach { factory in
|
for factory in factories {
|
||||||
let counter = factory.counters.first
|
let counter = factory.counters.first
|
||||||
XCTAssertEqual(counter?.values.count, 0, "expected number of entries to match")
|
XCTAssertEqual(counter?.values.count, 0, "expected number of entries to match")
|
||||||
}
|
}
|
||||||
|
|
@ -548,7 +549,7 @@ class MetricsTests: XCTestCase {
|
||||||
let value = Double.random(in: 0...1)
|
let value = Double.random(in: 0...1)
|
||||||
let muxMeter = Meter(label: name)
|
let muxMeter = Meter(label: name)
|
||||||
muxMeter.set(value)
|
muxMeter.set(value)
|
||||||
factories.forEach { factory in
|
for factory in factories {
|
||||||
let meter = factory.meters.first
|
let meter = factory.meters.first
|
||||||
XCTAssertEqual(meter?.label, name, "expected label to match")
|
XCTAssertEqual(meter?.label, name, "expected label to match")
|
||||||
XCTAssertEqual(meter?.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(meter?.values.count, 1, "expected number of entries to match")
|
||||||
|
|
@ -565,7 +566,7 @@ class MetricsTests: XCTestCase {
|
||||||
let value = Double.random(in: 0...1)
|
let value = Double.random(in: 0...1)
|
||||||
let muxRecorder = Recorder(label: name)
|
let muxRecorder = Recorder(label: name)
|
||||||
muxRecorder.record(value)
|
muxRecorder.record(value)
|
||||||
factories.forEach { factory in
|
for factory in factories {
|
||||||
let recorder = factory.recorders.first
|
let recorder = factory.recorders.first
|
||||||
XCTAssertEqual(recorder?.label, name, "expected label to match")
|
XCTAssertEqual(recorder?.label, name, "expected label to match")
|
||||||
XCTAssertEqual(recorder?.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(recorder?.values.count, 1, "expected number of entries to match")
|
||||||
|
|
@ -582,13 +583,17 @@ class MetricsTests: XCTestCase {
|
||||||
let seconds = Int.random(in: 1...10)
|
let seconds = Int.random(in: 1...10)
|
||||||
let muxTimer = Timer(label: name, preferredDisplayUnit: .minutes)
|
let muxTimer = Timer(label: name, preferredDisplayUnit: .minutes)
|
||||||
muxTimer.recordSeconds(seconds)
|
muxTimer.recordSeconds(seconds)
|
||||||
factories.forEach { factory in
|
for factory in factories {
|
||||||
let timer = factory.timers.first
|
let timer = factory.timers.first
|
||||||
XCTAssertEqual(timer?.label, name, "expected label to match")
|
XCTAssertEqual(timer?.label, name, "expected label to match")
|
||||||
XCTAssertEqual(timer?.values.count, 1, "expected number of entries 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?.values[0], Int64(seconds * 1_000_000_000), "expected value to match")
|
||||||
XCTAssertEqual(timer?.displayUnit, .minutes, "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"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -631,7 +636,11 @@ class MetricsTests: XCTestCase {
|
||||||
XCTAssertEqual(recorderAgain.values.first, -value, "expected value to match")
|
XCTAssertEqual(recorderAgain.values.first, -value, "expected value to match")
|
||||||
|
|
||||||
let identityAgain = ObjectIdentifier(recorderAgain)
|
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 {
|
func testDestroyingMeter() throws {
|
||||||
|
|
@ -661,7 +670,11 @@ class MetricsTests: XCTestCase {
|
||||||
XCTAssertEqual(testMeterAgain.values.first, -value, "expected value to match")
|
XCTAssertEqual(testMeterAgain.values.first, -value, "expected value to match")
|
||||||
|
|
||||||
let identityAgain = ObjectIdentifier(testMeterAgain)
|
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 {
|
func testDestroyingCounter() throws {
|
||||||
|
|
@ -691,7 +704,11 @@ class MetricsTests: XCTestCase {
|
||||||
XCTAssertEqual(testCounterAgain.values.first, Int64(value), "expected value to match")
|
XCTAssertEqual(testCounterAgain.values.first, Int64(value), "expected value to match")
|
||||||
|
|
||||||
let identityAgain = ObjectIdentifier(counterAgain)
|
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 {
|
func testDestroyingTimer() throws {
|
||||||
|
|
@ -720,7 +737,11 @@ class MetricsTests: XCTestCase {
|
||||||
XCTAssertEqual(testTimerAgain.values.first, value, "expected value to match")
|
XCTAssertEqual(testTimerAgain.values.first, value, "expected value to match")
|
||||||
|
|
||||||
let identityAgain = ObjectIdentifier(timerAgain)
|
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 {
|
func testDescriptions() throws {
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,12 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
@testable import CoreMetrics
|
|
||||||
@testable import Metrics
|
|
||||||
import MetricsTestKit
|
import MetricsTestKit
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
@testable import CoreMetrics
|
||||||
|
@testable import Metrics
|
||||||
|
|
||||||
class MetricsExtensionsTests: XCTestCase {
|
class MetricsExtensionsTests: XCTestCase {
|
||||||
func testTimerBlock() throws {
|
func testTimerBlock() throws {
|
||||||
// bootstrap with our test metrics
|
// bootstrap with our test metrics
|
||||||
|
|
@ -92,7 +93,11 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
|
|
||||||
let testTimer = try metrics.expectTimer(timer)
|
let testTimer = try metrics.expectTimer(timer)
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
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")
|
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,25 +165,60 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
let testTimer = try metrics.expectTimer(timer)
|
let testTimer = try metrics.expectTimer(timer)
|
||||||
|
|
||||||
testTimer.preferDisplayUnit(.nanoseconds)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,12 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
@testable import CoreMetrics
|
|
||||||
import Dispatch
|
import Dispatch
|
||||||
import MetricsTestKit
|
import MetricsTestKit
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
@testable import CoreMetrics
|
||||||
|
|
||||||
class SendableTest: XCTestCase {
|
class SendableTest: XCTestCase {
|
||||||
func testSendableMetrics() async throws {
|
func testSendableMetrics() async throws {
|
||||||
// bootstrap with our test metrics
|
// bootstrap with our test metrics
|
||||||
|
|
|
||||||
|
|
@ -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,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