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
|
||||
|
||||
### 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
///
|
||||
|
|
@ -24,7 +25,11 @@ extension Timer {
|
|||
/// - dimensions: The dimensions for the Timer.
|
||||
/// - body: Closure to run & record.
|
||||
@inlinable
|
||||
public static func measure<T>(label: String, dimensions: [(String, String)] = [], body: @escaping () throws -> T) rethrows -> T {
|
||||
public static func measure<T>(
|
||||
label: String,
|
||||
dimensions: [(String, String)] = [],
|
||||
body: @escaping () throws -> T
|
||||
) rethrows -> T {
|
||||
let timer = Timer(label: label, dimensions: dimensions)
|
||||
let start = DispatchTime.now().uptimeNanoseconds
|
||||
defer {
|
||||
|
|
@ -88,7 +93,7 @@ extension Timer {
|
|||
}
|
||||
|
||||
extension Timer {
|
||||
/// Convenience for recording a duration based on ``Duration``.
|
||||
/// Convenience for recording a duration based on `Duration`.
|
||||
///
|
||||
/// `Duration` will be converted to an `Int64` number of nanoseconds, and then recorded with nanosecond precision.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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,11 +12,12 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@testable import CoreMetrics
|
||||
@testable import Metrics
|
||||
import MetricsTestKit
|
||||
import XCTest
|
||||
|
||||
@testable import CoreMetrics
|
||||
@testable import Metrics
|
||||
|
||||
class MetricsExtensionsTests: XCTestCase {
|
||||
func testTimerBlock() throws {
|
||||
// bootstrap with our test metrics
|
||||
|
|
@ -40,7 +41,7 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
// run the test
|
||||
let timer = Timer(label: "test-timer")
|
||||
let testTimer = try metrics.expectTimer(timer)
|
||||
let timeInterval = TimeInterval(Double.random(in: 1 ... 500))
|
||||
let timeInterval = TimeInterval(Double.random(in: 1...500))
|
||||
timer.record(timeInterval)
|
||||
XCTAssertEqual(1, testTimer.values.count, "expected number of entries to match")
|
||||
XCTAssertEqual(testTimer.values[0], Int64(timeInterval * 1_000_000_000), "expected value to match")
|
||||
|
|
@ -54,22 +55,22 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
let timer = Timer(label: "test-timer")
|
||||
let testTimer = try metrics.expectTimer(timer)
|
||||
// nano
|
||||
let nano = DispatchTimeInterval.nanoseconds(Int.random(in: 1 ... 500))
|
||||
let nano = DispatchTimeInterval.nanoseconds(Int.random(in: 1...500))
|
||||
timer.record(nano)
|
||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||
XCTAssertEqual(Int(testTimer.values[0]), nano.nano(), "expected value to match")
|
||||
// micro
|
||||
let micro = DispatchTimeInterval.microseconds(Int.random(in: 1 ... 500))
|
||||
let micro = DispatchTimeInterval.microseconds(Int.random(in: 1...500))
|
||||
timer.record(micro)
|
||||
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
||||
XCTAssertEqual(Int(testTimer.values[1]), micro.nano(), "expected value to match")
|
||||
// milli
|
||||
let milli = DispatchTimeInterval.milliseconds(Int.random(in: 1 ... 500))
|
||||
let milli = DispatchTimeInterval.milliseconds(Int.random(in: 1...500))
|
||||
timer.record(milli)
|
||||
XCTAssertEqual(testTimer.values.count, 3, "expected number of entries to match")
|
||||
XCTAssertEqual(Int(testTimer.values[2]), milli.nano(), "expected value to match")
|
||||
// seconds
|
||||
let sec = DispatchTimeInterval.seconds(Int.random(in: 1 ... 500))
|
||||
let sec = DispatchTimeInterval.seconds(Int.random(in: 1...500))
|
||||
timer.record(sec)
|
||||
XCTAssertEqual(testTimer.values.count, 4, "expected number of entries to match")
|
||||
XCTAssertEqual(Int(testTimer.values[3]), sec.nano(), "expected value to match")
|
||||
|
|
@ -92,7 +93,11 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
|
||||
let testTimer = try metrics.expectTimer(timer)
|
||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||
XCTAssertEqual(UInt64(testTimer.values.first!), end.uptimeNanoseconds - start.uptimeNanoseconds, "expected value to match")
|
||||
XCTAssertEqual(
|
||||
UInt64(testTimer.values.first!),
|
||||
end.uptimeNanoseconds - start.uptimeNanoseconds,
|
||||
"expected value to match"
|
||||
)
|
||||
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +134,7 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
MetricsSystem.bootstrapInternal(metrics)
|
||||
|
||||
let name = "timer-\(UUID().uuidString)"
|
||||
let value = Int64.random(in: 0 ... 1000)
|
||||
let value = Int64.random(in: 0...1000)
|
||||
|
||||
let timer = Timer(label: name)
|
||||
timer.recordNanoseconds(value)
|
||||
|
|
@ -140,7 +145,7 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||
|
||||
let secondsName = "timer-seconds-\(UUID().uuidString)"
|
||||
let secondsValue = Int64.random(in: 0 ... 1000)
|
||||
let secondsValue = Int64.random(in: 0...1000)
|
||||
let secondsTimer = Timer(label: secondsName, preferredDisplayUnit: .seconds)
|
||||
secondsTimer.recordSeconds(secondsValue)
|
||||
|
||||
|
|
@ -153,32 +158,67 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
|
||||
let value = Double.random(in: 0 ... 1000)
|
||||
let value = Double.random(in: 0...1000)
|
||||
let timer = Timer(label: "test", preferredDisplayUnit: .seconds)
|
||||
timer.recordSeconds(value)
|
||||
|
||||
let testTimer = try metrics.expectTimer(timer)
|
||||
|
||||
testTimer.preferDisplayUnit(.nanoseconds)
|
||||
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value * 1000 * 1000 * 1000, accuracy: 1.0, "expected value to match")
|
||||
XCTAssertEqual(
|
||||
testTimer.valueInPreferredUnit(atIndex: 0),
|
||||
value * 1000 * 1000 * 1000,
|
||||
accuracy: 1.0,
|
||||
"expected value to match"
|
||||
)
|
||||
|
||||
testTimer.preferDisplayUnit(.microseconds)
|
||||
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value * 1000 * 1000, accuracy: 0.001, "expected value to match")
|
||||
XCTAssertEqual(
|
||||
testTimer.valueInPreferredUnit(atIndex: 0),
|
||||
value * 1000 * 1000,
|
||||
accuracy: 0.001,
|
||||
"expected value to match"
|
||||
)
|
||||
|
||||
testTimer.preferDisplayUnit(.milliseconds)
|
||||
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value * 1000, accuracy: 0.000001, "expected value to match")
|
||||
XCTAssertEqual(
|
||||
testTimer.valueInPreferredUnit(atIndex: 0),
|
||||
value * 1000,
|
||||
accuracy: 0.000001,
|
||||
"expected value to match"
|
||||
)
|
||||
|
||||
testTimer.preferDisplayUnit(.seconds)
|
||||
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value, accuracy: 0.000000001, "expected value to match")
|
||||
XCTAssertEqual(
|
||||
testTimer.valueInPreferredUnit(atIndex: 0),
|
||||
value,
|
||||
accuracy: 0.000000001,
|
||||
"expected value to match"
|
||||
)
|
||||
|
||||
testTimer.preferDisplayUnit(.minutes)
|
||||
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value / 60, accuracy: 0.000000001, "expected value to match")
|
||||
XCTAssertEqual(
|
||||
testTimer.valueInPreferredUnit(atIndex: 0),
|
||||
value / 60,
|
||||
accuracy: 0.000000001,
|
||||
"expected value to match"
|
||||
)
|
||||
|
||||
testTimer.preferDisplayUnit(.hours)
|
||||
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value / (60 * 60), accuracy: 0.000000001, "expected value to match")
|
||||
XCTAssertEqual(
|
||||
testTimer.valueInPreferredUnit(atIndex: 0),
|
||||
value / (60 * 60),
|
||||
accuracy: 0.000000001,
|
||||
"expected value to match"
|
||||
)
|
||||
|
||||
testTimer.preferDisplayUnit(.days)
|
||||
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value / (60 * 60 * 24), accuracy: 0.000000001, "expected value to match")
|
||||
XCTAssertEqual(
|
||||
testTimer.valueInPreferredUnit(atIndex: 0),
|
||||
value / (60 * 60 * 24),
|
||||
accuracy: 0.000000001,
|
||||
"expected value to match"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,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