Compare commits
32 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
4c83e1cdf4 | |
|
|
98d36172c1 | |
|
|
27ecca7ac1 | |
|
|
071d1cac3c | |
|
|
0556b16079 | |
|
|
44491db7cc | |
|
|
3c0f419970 | |
|
|
cbd39ceaca | |
|
|
029e902273 | |
|
|
58f390a873 | |
|
|
53de3bfa9a | |
|
|
5e63558d12 | |
|
|
726392cf4e | |
|
|
4a491b5ad5 | |
|
|
58e2968a7f | |
|
|
4ec5a219dc | |
|
|
c1976209b5 | |
|
|
cffefdc627 | |
|
|
d720898dbf | |
|
|
569db3a632 | |
|
|
e0165b53d4 | |
|
|
d067b0e0f7 | |
|
|
79e5fb4fe4 | |
|
|
ce594e71e9 | |
|
|
0124d9ad5c | |
|
|
eb18581491 | |
|
|
33d3f71e00 | |
|
|
7e53749103 | |
|
|
9c0646af4d | |
|
|
13ea1fe7fc | |
|
|
971ba26378 | |
|
|
bf7ea93e17 |
|
|
@ -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,26 @@
|
||||||
|
name: Main
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
schedule:
|
||||||
|
- cron: "0 8,20 * * *"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
unit-tests:
|
||||||
|
name: Unit tests
|
||||||
|
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
|
||||||
|
with:
|
||||||
|
linux_5_9_arguments_override: "--explicit-target-dependency-import-check error"
|
||||||
|
linux_5_10_arguments_override: "--explicit-target-dependency-import-check error"
|
||||||
|
linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
|
||||||
|
linux_6_1_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
|
||||||
|
linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
|
||||||
|
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
|
||||||
|
|
||||||
|
macos-tests:
|
||||||
|
name: macOS tests
|
||||||
|
uses: apple/swift-nio/.github/workflows/macos_tests.yml@main
|
||||||
|
with:
|
||||||
|
runner_pool: nightly
|
||||||
|
build_scheme: swift-metrics-Package
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: PR
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, reopened, synchronize]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
soundness:
|
||||||
|
name: Soundness
|
||||||
|
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
|
||||||
|
with:
|
||||||
|
license_header_check_project_name: "Swift Metrics API"
|
||||||
|
|
||||||
|
unit-tests:
|
||||||
|
name: Unit tests
|
||||||
|
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
|
||||||
|
with:
|
||||||
|
linux_5_9_arguments_override: "--explicit-target-dependency-import-check error"
|
||||||
|
linux_5_10_arguments_override: "--explicit-target-dependency-import-check error"
|
||||||
|
linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
|
||||||
|
linux_6_1_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
|
||||||
|
linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
|
||||||
|
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
|
||||||
|
|
||||||
|
cxx-interop:
|
||||||
|
name: Cxx interop
|
||||||
|
uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main
|
||||||
|
|
||||||
|
macos-tests:
|
||||||
|
name: macOS tests
|
||||||
|
uses: apple/swift-nio/.github/workflows/macos_tests.yml@main
|
||||||
|
with:
|
||||||
|
runner_pool: general
|
||||||
|
build_scheme: swift-metrics-Package
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,55 +1,3 @@
|
||||||
# Code of Conduct
|
# Code of Conduct
|
||||||
To be a truly great community, SwiftMetrics needs to welcome developers from all walks of life,
|
|
||||||
with different backgrounds, and with a wide range of experience. A diverse and friendly
|
|
||||||
community will have more great ideas, more unique perspectives, and produce more great
|
|
||||||
code. We will work diligently to make the SwiftMetrics community welcoming to everyone.
|
|
||||||
|
|
||||||
To give clarity of what is expected of our members, SwiftMetrics has adopted the code of conduct
|
The code of conduct for this project can be found at https://swift.org/code-of-conduct.
|
||||||
defined by [contributor-covenant.org](https://www.contributor-covenant.org). This document is used across many open source
|
|
||||||
communities, and we think it articulates our values well. The full text is copied below:
|
|
||||||
|
|
||||||
### Contributor Code of Conduct v1.3
|
|
||||||
As contributors and maintainers of this project, and in the interest of fostering an open and
|
|
||||||
welcoming community, we pledge to respect all people who contribute through reporting
|
|
||||||
issues, posting feature requests, updating documentation, submitting pull requests or patches,
|
|
||||||
and other activities.
|
|
||||||
|
|
||||||
We are committed to making participation in this project a harassment-free experience for
|
|
||||||
everyone, regardless of level of experience, gender, gender identity and expression, sexual
|
|
||||||
orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or
|
|
||||||
nationality.
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
- The use of sexualized language or imagery
|
|
||||||
- Personal attacks
|
|
||||||
- Trolling or insulting/derogatory comments
|
|
||||||
- Public or private harassment
|
|
||||||
- Publishing other’s private information, such as physical or electronic addresses, without explicit permission
|
|
||||||
- Other unethical or unprofessional conduct
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or reject comments,
|
|
||||||
commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of
|
|
||||||
Conduct, or to ban temporarily or permanently any contributor for other behaviors that they
|
|
||||||
deem inappropriate, threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
|
|
||||||
consistently applying these principles to every aspect of managing this project. Project
|
|
||||||
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
|
|
||||||
from the project team.
|
|
||||||
|
|
||||||
This code of conduct applies both within project spaces and in public spaces when an
|
|
||||||
individual is representing the project or its community.
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
|
|
||||||
contacting a project maintainer at [swift-server-conduct@group.apple.com](mailto:swift-server-conduct@group.apple.com). All complaints will be reviewed and
|
|
||||||
investigated and will result in a response that is deemed necessary and appropriate to the
|
|
||||||
circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter
|
|
||||||
of an incident.
|
|
||||||
|
|
||||||
*This policy is adapted from the Contributor Code of Conduct [version 1.3.0](https://contributor-covenant.org/version/1/3/0/).*
|
|
||||||
|
|
||||||
### Reporting
|
|
||||||
A working group of community members is committed to promptly addressing any [reported issues](mailto:swift-server-conduct@group.apple.com).
|
|
||||||
Working group members are volunteers appointed by the project lead, with a
|
|
||||||
preference for individuals with varied backgrounds and perspectives. Membership is expected
|
|
||||||
to change regularly, and may grow or shrink.
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// swift-tools-version:5.6
|
// swift-tools-version:5.9
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This source file is part of the Swift Metrics API open source project
|
// This source file is part of the Swift Metrics API open source project
|
||||||
|
|
@ -22,10 +22,6 @@ let package = Package(
|
||||||
.library(name: "Metrics", targets: ["Metrics"]),
|
.library(name: "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"
|
||||||
|
|
@ -44,3 +40,25 @@ let package = Package(
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for target in package.targets {
|
||||||
|
var settings = target.swiftSettings ?? []
|
||||||
|
settings.append(.enableExperimentalFeature("StrictConcurrency=complete"))
|
||||||
|
target.swiftSettings = settings
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- //
|
||||||
|
for target in package.targets {
|
||||||
|
switch target.type {
|
||||||
|
case .regular, .test, .executable:
|
||||||
|
var settings = target.swiftSettings ?? []
|
||||||
|
// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md
|
||||||
|
settings.append(.enableUpcomingFeature("MemberImportVisibility"))
|
||||||
|
target.swiftSettings = settings
|
||||||
|
case .macro, .plugin, .system, .binary:
|
||||||
|
() // not applicable
|
||||||
|
@unknown default:
|
||||||
|
() // we don't know what to do here, do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- END: STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- //
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// swift-tools-version:4.2
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "swift-metrics",
|
|
||||||
products: [
|
|
||||||
.library(name: "CoreMetrics", targets: ["CoreMetrics"]),
|
|
||||||
.library(name: "Metrics", targets: ["Metrics"]),
|
|
||||||
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
.target(
|
|
||||||
name: "CoreMetrics",
|
|
||||||
dependencies: []
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "Metrics",
|
|
||||||
dependencies: ["CoreMetrics"]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "MetricsTestKit",
|
|
||||||
dependencies: ["Metrics"]
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "MetricsTests",
|
|
||||||
dependencies: ["Metrics", "MetricsTestKit"]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// swift-tools-version:5.0
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "swift-metrics",
|
|
||||||
products: [
|
|
||||||
.library(name: "CoreMetrics", targets: ["CoreMetrics"]),
|
|
||||||
.library(name: "Metrics", targets: ["Metrics"]),
|
|
||||||
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
.target(
|
|
||||||
name: "CoreMetrics",
|
|
||||||
dependencies: []
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "Metrics",
|
|
||||||
dependencies: ["CoreMetrics"]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "MetricsTestKit",
|
|
||||||
dependencies: ["Metrics"]
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "MetricsTests",
|
|
||||||
dependencies: ["Metrics", "MetricsTestKit"]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// swift-tools-version:5.1
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "swift-metrics",
|
|
||||||
products: [
|
|
||||||
.library(name: "CoreMetrics", targets: ["CoreMetrics"]),
|
|
||||||
.library(name: "Metrics", targets: ["Metrics"]),
|
|
||||||
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
.target(
|
|
||||||
name: "CoreMetrics",
|
|
||||||
dependencies: []
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "Metrics",
|
|
||||||
dependencies: ["CoreMetrics"]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "MetricsTestKit",
|
|
||||||
dependencies: ["Metrics"]
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "MetricsTests",
|
|
||||||
dependencies: ["Metrics", "MetricsTestKit"]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// swift-tools-version:5.2
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "swift-metrics",
|
|
||||||
products: [
|
|
||||||
.library(name: "CoreMetrics", targets: ["CoreMetrics"]),
|
|
||||||
.library(name: "Metrics", targets: ["Metrics"]),
|
|
||||||
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
.target(
|
|
||||||
name: "CoreMetrics",
|
|
||||||
dependencies: []
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "Metrics",
|
|
||||||
dependencies: ["CoreMetrics"]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "MetricsTestKit",
|
|
||||||
dependencies: ["Metrics"]
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "MetricsTests",
|
|
||||||
dependencies: ["Metrics", "MetricsTestKit"]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// swift-tools-version:5.3
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "swift-metrics",
|
|
||||||
products: [
|
|
||||||
.library(name: "CoreMetrics", targets: ["CoreMetrics"]),
|
|
||||||
.library(name: "Metrics", targets: ["Metrics"]),
|
|
||||||
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
.target(
|
|
||||||
name: "CoreMetrics",
|
|
||||||
dependencies: []
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "Metrics",
|
|
||||||
dependencies: ["CoreMetrics"]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "MetricsTestKit",
|
|
||||||
dependencies: ["Metrics"]
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "MetricsTests",
|
|
||||||
dependencies: ["Metrics", "MetricsTestKit"]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// swift-tools-version:5.4
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "swift-metrics",
|
|
||||||
products: [
|
|
||||||
.library(name: "CoreMetrics", targets: ["CoreMetrics"]),
|
|
||||||
.library(name: "Metrics", targets: ["Metrics"]),
|
|
||||||
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
.target(
|
|
||||||
name: "CoreMetrics",
|
|
||||||
dependencies: []
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "Metrics",
|
|
||||||
dependencies: ["CoreMetrics"]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "MetricsTestKit",
|
|
||||||
dependencies: ["Metrics"]
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "MetricsTests",
|
|
||||||
dependencies: ["Metrics", "MetricsTestKit"]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// swift-tools-version:5.5
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "swift-metrics",
|
|
||||||
products: [
|
|
||||||
.library(name: "CoreMetrics", targets: ["CoreMetrics"]),
|
|
||||||
.library(name: "Metrics", targets: ["Metrics"]),
|
|
||||||
.library(name: "MetricsTestKit", targets: ["MetricsTestKit"]),
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
.target(
|
|
||||||
name: "CoreMetrics",
|
|
||||||
dependencies: []
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "Metrics",
|
|
||||||
dependencies: ["CoreMetrics"]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "MetricsTestKit",
|
|
||||||
dependencies: ["Metrics"]
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "MetricsTests",
|
|
||||||
dependencies: ["Metrics", "MetricsTestKit"]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
@ -90,12 +90,18 @@ This API was designed with the contributors to the Swift on Server community and
|
||||||
|
|
||||||
### Metric types
|
### Metric types
|
||||||
|
|
||||||
The API supports four metric types:
|
The API supports six metric types:
|
||||||
|
|
||||||
`Counter`: A counter is a cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart. For example, you can use a counter to represent the number of requests served, tasks completed, or errors.
|
`Counter`: A counter is a cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart. For example, you can use a counter to represent the number of requests served, tasks completed, or errors.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
counter.increment(by: 100)
|
counter.increment(by: 100)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `FloatingPointCounter`: A variation of a `Counter` that records a floating point value, instead of an integer.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
floatingPointCounter.increment(by: 10.5)
|
||||||
```
|
```
|
||||||
|
|
||||||
`Gauge`: A Gauge is a metric that represents a single numerical value that can arbitrarily go up and down. Gauges are typically used for measured values like temperatures or current memory usage, but also "counts" that can go up and down, like the number of active threads. Gauges are modeled as a `Recorder` with a sample size of 1 that does not perform any aggregation.
|
`Gauge`: A Gauge is a metric that represents a single numerical value that can arbitrarily go up and down. Gauges are typically used for measured values like temperatures or current memory usage, but also "counts" that can go up and down, like the number of active threads. Gauges are modeled as a `Recorder` with a sample size of 1 that does not perform any aggregation.
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,6 @@ with the all the required detail.
|
||||||
and in the Server → Security Updates category on the [Swift forums][swift-forums-sec].
|
and in the Server → Security Updates category on the [Swift forums][swift-forums-sec].
|
||||||
|
|
||||||
[sswg]: https://github.com/swift-server/sswg
|
[sswg]: https://github.com/swift-server/sswg
|
||||||
[sswg-security]: https://github.com/swift-server/sswg/blob/main/security/README.md
|
[sswg-security]: https://www.swift.org/sswg/security/
|
||||||
[swift-forums-sec]: https://forums.swift.org/c/server/security-updates/
|
[swift-forums-sec]: https://forums.swift.org/c/server/security-updates/
|
||||||
[mitre]: https://cveform.mitre.org/
|
[mitre]: https://cveform.mitre.org/
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,9 @@ This API was designed with the contributors to the Swift on Server community and
|
||||||
### Metric types
|
### Metric types
|
||||||
|
|
||||||
- ``Counter``
|
- ``Counter``
|
||||||
|
- ``FloatingPointCounter``
|
||||||
- ``Meter``
|
- ``Meter``
|
||||||
- ``Recorder``
|
- ``Recorder``
|
||||||
|
- ``Gauge``
|
||||||
- ``Timer``
|
- ``Timer``
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@ import Darwin
|
||||||
import WinSDK
|
import WinSDK
|
||||||
#elseif canImport(Glibc)
|
#elseif canImport(Glibc)
|
||||||
import Glibc
|
import Glibc
|
||||||
|
#elseif canImport(Android)
|
||||||
|
import Android
|
||||||
|
#elseif canImport(Musl)
|
||||||
|
import Musl
|
||||||
#else
|
#else
|
||||||
#error("Unsupported runtime")
|
#error("Unsupported runtime")
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -129,6 +133,8 @@ extension Lock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Lock: @unchecked Sendable {}
|
||||||
|
|
||||||
/// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
|
/// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
|
||||||
///
|
///
|
||||||
/// This object provides a lock on top of a single `pthread_rwlock_t`. This kind
|
/// This object provides a lock on top of a single `pthread_rwlock_t`. This kind
|
||||||
|
|
@ -271,3 +277,5 @@ extension ReadWriteLock {
|
||||||
try self.withWriterLock(body)
|
try self.withWriterLock(body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ReadWriteLock: @unchecked Sendable {}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -11,11 +11,15 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
// swift-format-ignore-file
|
||||||
|
// Note: Whitespace changes are used to workaround compiler bug
|
||||||
|
// https://github.com/swiftlang/swift/issues/79285
|
||||||
|
|
||||||
@_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 +28,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 {
|
||||||
|
|
@ -60,6 +68,16 @@ extension Timer {
|
||||||
/// - duration: The duration to record.
|
/// - duration: The duration to record.
|
||||||
@inlinable
|
@inlinable
|
||||||
public func record(_ duration: DispatchTimeInterval) {
|
public func record(_ duration: DispatchTimeInterval) {
|
||||||
|
// This wrapping in a optional is a workaround because DispatchTimeInterval
|
||||||
|
// is a non-frozen public enum and Dispatch is built with library evolution
|
||||||
|
// mode turned on.
|
||||||
|
// This means we should have an `@unknown default` case, but this breaks
|
||||||
|
// on non-Darwin platforms.
|
||||||
|
// Switching over an optional means that the `.none` case will map to
|
||||||
|
// `default` (which means we'll always have a valid case to go into
|
||||||
|
// the default case), but in reality this case will never exist as this
|
||||||
|
// optional will never be nil.
|
||||||
|
let duration = Optional(duration)
|
||||||
switch duration {
|
switch duration {
|
||||||
case .nanoseconds(let value):
|
case .nanoseconds(let value):
|
||||||
self.recordNanoseconds(value)
|
self.recordNanoseconds(value)
|
||||||
|
|
@ -71,6 +89,72 @@ extension Timer {
|
||||||
self.recordSeconds(value)
|
self.recordSeconds(value)
|
||||||
case .never:
|
case .never:
|
||||||
self.record(0)
|
self.record(0)
|
||||||
|
default:
|
||||||
|
self.record(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Timer {
|
||||||
|
/// Convenience for recording a duration based on `Duration`.
|
||||||
|
///
|
||||||
|
/// `Duration` will be converted to an `Int64` number of nanoseconds, and then recorded with nanosecond precision.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - duration: The `Duration` to record.
|
||||||
|
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||||
|
@inlinable
|
||||||
|
public func record(duration: Duration) {
|
||||||
|
// `Duration` doesn't have a nice way to convert it nanoseconds or seconds,
|
||||||
|
// and manual conversion can overflow.
|
||||||
|
let seconds = duration.components.seconds.multipliedReportingOverflow(by: 1_000_000_000)
|
||||||
|
guard !seconds.overflow else { return self.recordNanoseconds(Int64.max) }
|
||||||
|
|
||||||
|
let nanoseconds = seconds.partialValue.addingReportingOverflow(duration.components.attoseconds / 1_000_000_000)
|
||||||
|
guard !nanoseconds.overflow else { return self.recordNanoseconds(Int64.max) }
|
||||||
|
|
||||||
|
self.recordNanoseconds(nanoseconds.partialValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
#if compiler(>=6.0)
|
||||||
|
/// Convenience for measuring duration of a closure.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - clock: The clock used for measuring the duration. Defaults to the continuous clock.
|
||||||
|
/// - body: The closure to record the duration of.
|
||||||
|
@inlinable
|
||||||
|
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||||
|
public func measure<Result, Failure: Error, Clock: _Concurrency.Clock>(
|
||||||
|
clock: Clock = .continuous,
|
||||||
|
// DO NOT FIX THE WHITESPACE IN THE NEXT LINE UNTIL 5.10 IS UNSUPPORTED
|
||||||
|
// https://github.com/swiftlang/swift/issues/79285
|
||||||
|
body: () throws(Failure) -> Result) throws(Failure) -> Result where Clock.Duration == Duration {
|
||||||
|
let start = clock.now
|
||||||
|
defer {
|
||||||
|
self.record(duration: start.duration(to: clock.now))
|
||||||
|
}
|
||||||
|
return try body()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience for measuring duration of a closure.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - clock: The clock used for measuring the duration. Defaults to the continuous clock.
|
||||||
|
/// - isolation: The isolation of the method. Defaults to the isolation of the caller.
|
||||||
|
/// - body: The closure to record the duration of.
|
||||||
|
@inlinable
|
||||||
|
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||||
|
public func measure<Result, Failure: Error, Clock: _Concurrency.Clock>(
|
||||||
|
clock: Clock = .continuous,
|
||||||
|
isolation: isolated (any Actor)? = #isolation,
|
||||||
|
// DO NOT FIX THE WHITESPACE IN THE NEXT LINE UNTIL 5.10 IS UNSUPPORTED
|
||||||
|
// https://github.com/swiftlang/swift/issues/79285
|
||||||
|
body: () async throws(Failure) -> sending Result) async throws(Failure) -> sending Result where Clock.Duration == Duration {
|
||||||
|
let start = clock.now
|
||||||
|
defer {
|
||||||
|
self.record(duration: start.duration(to: clock.now))
|
||||||
|
}
|
||||||
|
return try await body()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public final class TestMetrics: MetricsFactory {
|
||||||
public typealias Label = String
|
public typealias Label = String
|
||||||
public typealias Dimensions = String
|
public typealias Dimensions = String
|
||||||
|
|
||||||
public struct FullKey {
|
public struct FullKey: Sendable {
|
||||||
let label: Label
|
let label: Label
|
||||||
let dimensions: [(String, String)]
|
let dimensions: [(String, String)]
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ public final class TestMetrics: MetricsFactory {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset method to destroy all created ``TestCounter``, ``TestRecorder`` and ``TestTimer``.
|
/// Reset method to destroy all created ``TestCounter``, ``TestMeter``, ``TestRecorder`` and ``TestTimer``.
|
||||||
/// Invoke this method in between test runs to verify that Counters are created as needed.
|
/// Invoke this method in between test runs to verify that Counters are created as needed.
|
||||||
public func reset() {
|
public func reset() {
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
|
|
@ -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,14 +354,14 @@ 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()
|
||||||
private var _values = [(Date, Double)]()
|
private var _values = [(Date, Double)]()
|
||||||
|
|
||||||
init(label: String, dimensions: [(String, String)]) {
|
init(label: String, dimensions: [(String, String)]) {
|
||||||
self.id = NSUUID().uuidString
|
self.id = UUID().uuidString
|
||||||
self.label = label
|
self.label = label
|
||||||
self.dimensions = dimensions
|
self.dimensions = dimensions
|
||||||
}
|
}
|
||||||
|
|
@ -378,39 +378,75 @@ public final class TestMeter: TestMetric, MeterHandler, Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func increment(by amount: Double) {
|
public func increment(by amount: Double) {
|
||||||
|
// Drop illegal values
|
||||||
|
// - cannot increment by NaN
|
||||||
|
guard !amount.isNaN else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// - cannot increment by infinite quantities
|
||||||
|
guard !amount.isInfinite else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// - cannot increment by negative values
|
||||||
|
guard amount.sign == .plus else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// - cannot increment by zero
|
||||||
|
guard !amount.isZero else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
let lastValue = self._values.last?.1 ?? 0
|
let lastValue: Double = self._values.last?.1 ?? 0
|
||||||
let newValue = lastValue - amount
|
let newValue = lastValue + amount
|
||||||
_values.append((Date(), Double(newValue)))
|
_values.append((Date(), newValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func decrement(by amount: Double) {
|
public func decrement(by amount: Double) {
|
||||||
|
// Drop illegal values
|
||||||
|
// - cannot decrement by NaN
|
||||||
|
guard !amount.isNaN else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// - cannot decrement by infinite quantities
|
||||||
|
guard !amount.isInfinite else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// - cannot decrement by negative values
|
||||||
|
guard amount.sign == .plus else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// - cannot decrement by zero
|
||||||
|
guard !amount.isZero else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
let lastValue = self._values.last?.1 ?? 0
|
let lastValue: Double = self._values.last?.1 ?? 0
|
||||||
let newValue = lastValue - amount
|
let newValue = lastValue - amount
|
||||||
_values.append((Date(), Double(newValue)))
|
_values.append((Date(), newValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,14 +457,14 @@ 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()
|
||||||
private var _values = [(Date, Double)]()
|
private var _values = [(Date, Double)]()
|
||||||
|
|
||||||
init(label: String, dimensions: [(String, String)], aggregate: Bool) {
|
init(label: String, dimensions: [(String, String)], aggregate: Bool) {
|
||||||
self.id = NSUUID().uuidString
|
self.id = UUID().uuidString
|
||||||
self.label = label
|
self.label = label
|
||||||
self.dimensions = dimensions
|
self.dimensions = dimensions
|
||||||
self.aggregate = aggregate
|
self.aggregate = aggregate
|
||||||
|
|
@ -446,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -473,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()
|
||||||
|
|
@ -507,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -540,26 +576,16 @@ extension NSLock {
|
||||||
|
|
||||||
// MARK: - Errors
|
// MARK: - Errors
|
||||||
|
|
||||||
#if compiler(>=5.6)
|
|
||||||
public enum TestMetricsError: Error {
|
public enum TestMetricsError: Error {
|
||||||
case missingMetric(label: String, dimensions: [(String, String)])
|
case missingMetric(label: String, dimensions: [(String, String)])
|
||||||
case illegalMetricType(metric: Sendable, expected: String)
|
case illegalMetricType(metric: Sendable, expected: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
public enum TestMetricsError: Error {
|
|
||||||
case missingMetric(label: String, dimensions: [(String, String)])
|
|
||||||
case illegalMetricType(metric: Any, expected: String)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - Sendable support
|
// MARK: - Sendable support
|
||||||
|
|
||||||
#if compiler(>=5.6)
|
|
||||||
// ideally we would not be using @unchecked here, but concurrency-safety checks do not recognize locks
|
// ideally we would not be using @unchecked here, but concurrency-safety checks do not recognize locks
|
||||||
extension TestMetrics: @unchecked Sendable {}
|
extension TestMetrics: @unchecked Sendable {}
|
||||||
extension TestCounter: @unchecked Sendable {}
|
extension TestCounter: @unchecked Sendable {}
|
||||||
extension TestMeter: @unchecked Sendable {}
|
extension TestMeter: @unchecked Sendable {}
|
||||||
extension TestRecorder: @unchecked Sendable {}
|
extension TestRecorder: @unchecked Sendable {}
|
||||||
extension TestTimer: @unchecked Sendable {}
|
extension TestTimer: @unchecked Sendable {}
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// LinuxMain.swift
|
|
||||||
//
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
///
|
|
||||||
/// NOTE: This file was generated by generate_linux_tests.rb
|
|
||||||
///
|
|
||||||
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
|
|
||||||
///
|
|
||||||
|
|
||||||
#if os(Linux) || os(FreeBSD)
|
|
||||||
@testable import MetricsTests
|
|
||||||
|
|
||||||
XCTMain([
|
|
||||||
testCase(MetricsExtensionsTests.allTests),
|
|
||||||
testCase(MetricsTests.allTests),
|
|
||||||
])
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// CoreMetricsTests+XCTest.swift
|
|
||||||
//
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
///
|
|
||||||
/// NOTE: This file was generated by generate_linux_tests.rb
|
|
||||||
///
|
|
||||||
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
|
|
||||||
///
|
|
||||||
|
|
||||||
extension MetricsTests {
|
|
||||||
static var allTests: [(String, (MetricsTests) -> () throws -> Void)] {
|
|
||||||
return [
|
|
||||||
("testCounters", testCounters),
|
|
||||||
("testCounterBlock", testCounterBlock),
|
|
||||||
("testDefaultFloatingPointCounter_ignoresNan", testDefaultFloatingPointCounter_ignoresNan),
|
|
||||||
("testDefaultFloatingPointCounter_ignoresInfinity", testDefaultFloatingPointCounter_ignoresInfinity),
|
|
||||||
("testDefaultFloatingPointCounter_ignoresNegativeValues", testDefaultFloatingPointCounter_ignoresNegativeValues),
|
|
||||||
("testDefaultFloatingPointCounter_ignoresZero", testDefaultFloatingPointCounter_ignoresZero),
|
|
||||||
("testDefaultFloatingPointCounter_ceilsExtremeValues", testDefaultFloatingPointCounter_ceilsExtremeValues),
|
|
||||||
("testDefaultFloatingPointCounter_accumulatesFloatingPointDecimalValues", testDefaultFloatingPointCounter_accumulatesFloatingPointDecimalValues),
|
|
||||||
("testRecorders", testRecorders),
|
|
||||||
("testRecordersInt", testRecordersInt),
|
|
||||||
("testRecordersFloat", testRecordersFloat),
|
|
||||||
("testRecorderBlock", testRecorderBlock),
|
|
||||||
("testTimers", testTimers),
|
|
||||||
("testTimerBlock", testTimerBlock),
|
|
||||||
("testTimerVariants", testTimerVariants),
|
|
||||||
("testTimerOverflow", testTimerOverflow),
|
|
||||||
("testTimerHandlesUnsignedOverflow", testTimerHandlesUnsignedOverflow),
|
|
||||||
("testGauge", testGauge),
|
|
||||||
("testGaugeBlock", testGaugeBlock),
|
|
||||||
("testMeter", testMeter),
|
|
||||||
("testMeterBlock", testMeterBlock),
|
|
||||||
("testMUX_Counter", testMUX_Counter),
|
|
||||||
("testMUX_Meter", testMUX_Meter),
|
|
||||||
("testMUX_Recorder", testMUX_Recorder),
|
|
||||||
("testMUX_Timer", testMUX_Timer),
|
|
||||||
("testCustomFactory", testCustomFactory),
|
|
||||||
("testDestroyingGauge", testDestroyingGauge),
|
|
||||||
("testDestroyingMeter", testDestroyingMeter),
|
|
||||||
("testDestroyingCounter", testDestroyingCounter),
|
|
||||||
("testDestroyingTimer", testDestroyingTimer),
|
|
||||||
("testDescriptions", testDescriptions),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -24,17 +25,17 @@ class MetricsTests: XCTestCase {
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
let name = "counter-\(UUID().uuidString)"
|
let name = "counter-\(UUID().uuidString)"
|
||||||
let counter = Counter(label: name)
|
let counter = Counter(label: name)
|
||||||
let testCounter = counter._handler as! TestCounter
|
let testCounter = try metrics.expectCounter(counter)
|
||||||
let total = Int.random(in: 500...1000)
|
let total = Int.random(in: 500...1000)
|
||||||
for _ in 0 ... total {
|
for _ in 0..<total {
|
||||||
group.enter()
|
group.enter()
|
||||||
DispatchQueue(label: "\(name)-queue").async {
|
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.leave()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
group.wait()
|
group.wait()
|
||||||
XCTAssertEqual(testCounter.values.count - 1, total, "expected number of entries to match")
|
XCTAssertEqual(testCounter.values.count, total, "expected number of entries to match")
|
||||||
testCounter.reset()
|
testCounter.reset()
|
||||||
XCTAssertEqual(testCounter.values.count, 0, "expected number of entries to match")
|
XCTAssertEqual(testCounter.values.count, 0, "expected number of entries to match")
|
||||||
}
|
}
|
||||||
|
|
@ -153,17 +154,17 @@ class MetricsTests: XCTestCase {
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
let name = "recorder-\(UUID().uuidString)"
|
let name = "recorder-\(UUID().uuidString)"
|
||||||
let recorder = Recorder(label: name)
|
let recorder = Recorder(label: name)
|
||||||
let testRecorder = recorder._handler as! TestRecorder
|
let testRecorder = try metrics.expectRecorder(recorder)
|
||||||
let total = Int.random(in: 500...1000)
|
let total = Int.random(in: 500...1000)
|
||||||
for _ in 0 ... total {
|
for _ in 0..<total {
|
||||||
group.enter()
|
group.enter()
|
||||||
DispatchQueue(label: "\(name)-queue").async {
|
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.leave()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
group.wait()
|
group.wait()
|
||||||
XCTAssertEqual(testRecorder.values.count - 1, total, "expected number of entries to match")
|
XCTAssertEqual(testRecorder.values.count, total, "expected number of entries to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRecordersInt() throws {
|
func testRecordersInt() throws {
|
||||||
|
|
@ -171,13 +172,13 @@ class MetricsTests: XCTestCase {
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
let recorder = Recorder(label: "test-recorder")
|
let recorder = Recorder(label: "test-recorder")
|
||||||
let testRecorder = recorder._handler as! TestRecorder
|
let testRecorder = try metrics.expectRecorder(recorder)
|
||||||
let values = (0...999).map { _ in Int32.random(in: Int32.min...Int32.max) }
|
let values = (0...999).map { _ in Int32.random(in: Int32.min...Int32.max) }
|
||||||
for i in 0 ... values.count - 1 {
|
for i in 0..<values.count {
|
||||||
recorder.record(values[i])
|
recorder.record(values[i])
|
||||||
}
|
}
|
||||||
XCTAssertEqual(values.count, testRecorder.values.count, "expected number of entries to match")
|
XCTAssertEqual(values.count, testRecorder.values.count, "expected number of entries to match")
|
||||||
for i in 0 ... values.count - 1 {
|
for i in 0..<values.count {
|
||||||
XCTAssertEqual(Int32(testRecorder.values[i]), values[i], "expected value #\(i) to match.")
|
XCTAssertEqual(Int32(testRecorder.values[i]), values[i], "expected value #\(i) to match.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -187,13 +188,13 @@ class MetricsTests: XCTestCase {
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
let recorder = Recorder(label: "test-recorder")
|
let recorder = Recorder(label: "test-recorder")
|
||||||
let testRecorder = recorder._handler as! TestRecorder
|
let testRecorder = try metrics.expectRecorder(recorder)
|
||||||
let values = (0...999).map { _ in Float.random(in: Float(Int32.min)...Float(Int32.max)) }
|
let values = (0...999).map { _ in Float.random(in: Float(Int32.min)...Float(Int32.max)) }
|
||||||
for i in 0 ... values.count - 1 {
|
for i in 0..<values.count {
|
||||||
recorder.record(values[i])
|
recorder.record(values[i])
|
||||||
}
|
}
|
||||||
XCTAssertEqual(values.count, testRecorder.values.count, "expected number of entries to match")
|
XCTAssertEqual(values.count, testRecorder.values.count, "expected number of entries to match")
|
||||||
for i in 0 ... values.count - 1 {
|
for i in 0..<values.count {
|
||||||
XCTAssertEqual(Float(testRecorder.values[i]), values[i], "expected value #\(i) to match.")
|
XCTAssertEqual(Float(testRecorder.values[i]), values[i], "expected value #\(i) to match.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -218,17 +219,17 @@ class MetricsTests: XCTestCase {
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
let name = "timer-\(UUID().uuidString)"
|
let name = "timer-\(UUID().uuidString)"
|
||||||
let timer = Timer(label: name)
|
let timer = Timer(label: name)
|
||||||
let testTimer = timer._handler as! TestTimer
|
let testTimer = try metrics.expectTimer(timer)
|
||||||
let total = Int.random(in: 500...1000)
|
let total = Int.random(in: 500...1000)
|
||||||
for _ in 0 ... total {
|
for _ in 0..<total {
|
||||||
group.enter()
|
group.enter()
|
||||||
DispatchQueue(label: "\(name)-queue").async {
|
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.leave()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
group.wait()
|
group.wait()
|
||||||
XCTAssertEqual(testTimer.values.count - 1, total, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, total, "expected number of entries to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimerBlock() throws {
|
func testTimerBlock() throws {
|
||||||
|
|
@ -250,7 +251,7 @@ class MetricsTests: XCTestCase {
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
// run the test
|
// run the test
|
||||||
let timer = Timer(label: "test-timer")
|
let timer = Timer(label: "test-timer")
|
||||||
let testTimer = timer._handler as! TestTimer
|
let testTimer = try metrics.expectTimer(timer)
|
||||||
// nano
|
// nano
|
||||||
let nano = Int64.random(in: 0...5)
|
let nano = Int64.random(in: 0...5)
|
||||||
timer.recordNanoseconds(nano)
|
timer.recordNanoseconds(nano)
|
||||||
|
|
@ -279,7 +280,7 @@ class MetricsTests: XCTestCase {
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
// run the test
|
// run the test
|
||||||
let timer = Timer(label: "test-timer")
|
let timer = Timer(label: "test-timer")
|
||||||
let testTimer = timer._handler as! TestTimer
|
let testTimer = try metrics.expectTimer(timer)
|
||||||
// nano (integer)
|
// nano (integer)
|
||||||
timer.recordNanoseconds(Int64.max)
|
timer.recordNanoseconds(Int64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
|
|
@ -316,7 +317,7 @@ class MetricsTests: XCTestCase {
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
// run the test
|
// run the test
|
||||||
let timer = Timer(label: "test-timer")
|
let timer = Timer(label: "test-timer")
|
||||||
let testTimer = timer._handler as! TestTimer
|
let testTimer = try metrics.expectTimer(timer)
|
||||||
// nano
|
// nano
|
||||||
timer.recordNanoseconds(UInt64.max)
|
timer.recordNanoseconds(UInt64.max)
|
||||||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
|
|
@ -344,7 +345,7 @@ class MetricsTests: XCTestCase {
|
||||||
let value = Double.random(in: -1000...1000)
|
let value = Double.random(in: -1000...1000)
|
||||||
let gauge = Gauge(label: name)
|
let gauge = Gauge(label: name)
|
||||||
gauge.record(value)
|
gauge.record(value)
|
||||||
let recorder = gauge._handler as! TestRecorder
|
let recorder = try metrics.expectRecorder(gauge)
|
||||||
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(recorder.lastValue, value, "expected value to match")
|
XCTAssertEqual(recorder.lastValue, value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
@ -367,11 +368,11 @@ class MetricsTests: XCTestCase {
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
// run the test
|
// run the test
|
||||||
let name = "meter-\(NSUUID().uuidString)"
|
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 meter = Meter(label: name)
|
||||||
meter.set(value)
|
meter.set(value)
|
||||||
let testMeter = meter._handler as! TestMeter
|
let testMeter = try metrics.expectMeter(meter)
|
||||||
XCTAssertEqual(testMeter.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testMeter.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testMeter.values[0], value, "expected value to match")
|
XCTAssertEqual(testMeter.values[0], value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
@ -381,7 +382,7 @@ class MetricsTests: XCTestCase {
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
// run the test
|
// run the test
|
||||||
let name = "meter-\(NSUUID().uuidString)"
|
let name = "meter-\(UUID().uuidString)"
|
||||||
let value = Double.random(in: -1000...1000)
|
let value = Double.random(in: -1000...1000)
|
||||||
Meter(label: name).set(value)
|
Meter(label: name).set(value)
|
||||||
let testMeter = try metrics.expectMeter(name)
|
let testMeter = try metrics.expectMeter(name)
|
||||||
|
|
@ -389,6 +390,134 @@ class MetricsTests: XCTestCase {
|
||||||
XCTAssertEqual(testMeter.values[0], value, "expected value to match")
|
XCTAssertEqual(testMeter.values[0], value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testMeterInt() throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
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 {
|
||||||
|
meter.set(values[i])
|
||||||
|
}
|
||||||
|
XCTAssertEqual(values.count, testMeter.values.count, "expected number of entries to match")
|
||||||
|
for i in 0..<values.count {
|
||||||
|
XCTAssertEqual(Int32(testMeter.values[i]), values[i], "expected value #\(i) to match.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMeterFloat() throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
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 {
|
||||||
|
meter.set(values[i])
|
||||||
|
}
|
||||||
|
XCTAssertEqual(values.count, testMeter.values.count, "expected number of entries to match")
|
||||||
|
for i in 0..<values.count {
|
||||||
|
XCTAssertEqual(Float(testMeter.values[i]), values[i], "expected value #\(i) to match.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMeterIncrement() throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
// run the test
|
||||||
|
let group = DispatchGroup()
|
||||||
|
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 {
|
||||||
|
group.enter()
|
||||||
|
DispatchQueue(label: "\(name)-queue").async {
|
||||||
|
defer { group.leave() }
|
||||||
|
meter.increment(by: values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.wait()
|
||||||
|
XCTAssertEqual(testMeter.values.count, values.count, "expected number of entries to match")
|
||||||
|
XCTAssertEqual(testMeter.values.last!, values.reduce(0.0, +), accuracy: 0.1, "expected total value to match")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMeterDecrement() throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
// run the test
|
||||||
|
let group = DispatchGroup()
|
||||||
|
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 {
|
||||||
|
group.enter()
|
||||||
|
DispatchQueue(label: "\(name)-queue").async {
|
||||||
|
defer { group.leave() }
|
||||||
|
meter.decrement(by: values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.wait()
|
||||||
|
XCTAssertEqual(testMeter.values.count, values.count, "expected number of entries to match")
|
||||||
|
XCTAssertEqual(testMeter.values.last!, values.reduce(0.0, -), accuracy: 0.1, "expected total value to match")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDefaultMeterIgnoresNan() throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
// run the test
|
||||||
|
let name = "meter-\(UUID().uuidString)"
|
||||||
|
let meter = Meter(label: name)
|
||||||
|
let testMeter = try metrics.expectMeter(meter)
|
||||||
|
meter.increment(by: Double.nan)
|
||||||
|
meter.increment(by: Double.signalingNaN)
|
||||||
|
XCTAssertEqual(testMeter.values.count, 0, "expected nan values to be ignored")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDefaultMeterIgnoresInfinity() throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
// run the test
|
||||||
|
let name = "meter-\(UUID().uuidString)"
|
||||||
|
let meter = Meter(label: name)
|
||||||
|
let testMeter = try metrics.expectMeter(meter)
|
||||||
|
meter.increment(by: Double.infinity)
|
||||||
|
meter.increment(by: -Double.infinity)
|
||||||
|
XCTAssertEqual(testMeter.values.count, 0, "expected infinite values to be ignored")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDefaultMeterIgnoresNegativeValues() throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
// run the test
|
||||||
|
let name = "meter-\(UUID().uuidString)"
|
||||||
|
let meter = Meter(label: name)
|
||||||
|
let testMeter = try metrics.expectMeter(meter)
|
||||||
|
meter.increment(by: -100)
|
||||||
|
XCTAssertEqual(testMeter.values.count, 0, "expected negative values to be ignored")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDefaultMeterIgnoresZero() throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
// run the test
|
||||||
|
let name = "meter-\(UUID().uuidString)"
|
||||||
|
let meter = Meter(label: name)
|
||||||
|
let testMeter = try metrics.expectMeter(meter)
|
||||||
|
meter.increment(by: 0)
|
||||||
|
meter.increment(by: -0)
|
||||||
|
XCTAssertEqual(testMeter.values.count, 0, "expected zero values to be ignored")
|
||||||
|
}
|
||||||
|
|
||||||
func testMUX_Counter() throws {
|
func testMUX_Counter() throws {
|
||||||
// bootstrap with our test metrics
|
// bootstrap with our test metrics
|
||||||
let factories = [TestMetrics(), TestMetrics(), TestMetrics()]
|
let factories = [TestMetrics(), TestMetrics(), TestMetrics()]
|
||||||
|
|
@ -398,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")
|
||||||
}
|
}
|
||||||
|
|
@ -416,11 +545,11 @@ class MetricsTests: XCTestCase {
|
||||||
let factories = [TestMetrics(), TestMetrics(), TestMetrics()]
|
let factories = [TestMetrics(), TestMetrics(), TestMetrics()]
|
||||||
MetricsSystem.bootstrapInternal(MultiplexMetricsHandler(factories: factories))
|
MetricsSystem.bootstrapInternal(MultiplexMetricsHandler(factories: factories))
|
||||||
// run the test
|
// run the test
|
||||||
let name = NSUUID().uuidString
|
let name = UUID().uuidString
|
||||||
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")
|
||||||
|
|
@ -437,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")
|
||||||
|
|
@ -454,17 +583,21 @@ 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"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCustomFactory() {
|
func testCustomHandler() {
|
||||||
final class CustomHandler: CounterHandler {
|
final class CustomHandler: CounterHandler {
|
||||||
func increment<DataType>(by: DataType) where DataType: BinaryInteger {}
|
func increment<DataType>(by: DataType) where DataType: BinaryInteger {}
|
||||||
func reset() {}
|
func reset() {}
|
||||||
|
|
@ -476,6 +609,79 @@ class MetricsTests: XCTestCase {
|
||||||
XCTAssertTrue(counter2._handler is CustomHandler, "expected custom log handler")
|
XCTAssertTrue(counter2._handler is CustomHandler, "expected custom log handler")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testCustomFactory() {
|
||||||
|
// @unchecked Sendable is okay here - locking is done manually.
|
||||||
|
final class CustomFactory: MetricsFactory, @unchecked Sendable {
|
||||||
|
|
||||||
|
init(handler: CustomHandler) {
|
||||||
|
self.handler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
final class CustomHandler: CounterHandler {
|
||||||
|
func increment<DataType>(by: DataType) where DataType: BinaryInteger {}
|
||||||
|
func reset() {}
|
||||||
|
}
|
||||||
|
private let handler: CustomHandler
|
||||||
|
private let lock: NSLock = NSLock()
|
||||||
|
private var locked_didCallDestroyCounter: Bool = false
|
||||||
|
var didCallDestroyCounter: Bool {
|
||||||
|
self.lock.lock()
|
||||||
|
defer {
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
return self.locked_didCallDestroyCounter
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCounter(label: String, dimensions: [(String, String)]) -> any CoreMetrics.CounterHandler {
|
||||||
|
handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRecorder(
|
||||||
|
label: String,
|
||||||
|
dimensions: [(String, String)],
|
||||||
|
aggregate: Bool
|
||||||
|
) -> any CoreMetrics.RecorderHandler {
|
||||||
|
fatalError("Unsupported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTimer(label: String, dimensions: [(String, String)]) -> any CoreMetrics.TimerHandler {
|
||||||
|
fatalError("Unsupported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func destroyCounter(_ handler: any CoreMetrics.CounterHandler) {
|
||||||
|
XCTAssertTrue(
|
||||||
|
handler === self.handler,
|
||||||
|
"The handler to be destroyed doesn't match the expected handler."
|
||||||
|
)
|
||||||
|
self.lock.lock()
|
||||||
|
defer {
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
self.locked_didCallDestroyCounter = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func destroyRecorder(_ handler: any CoreMetrics.RecorderHandler) {
|
||||||
|
fatalError("Unsupported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func destroyTimer(_ handler: any CoreMetrics.TimerHandler) {
|
||||||
|
fatalError("Unsupported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let handler = CustomFactory.CustomHandler()
|
||||||
|
let factory = CustomFactory(handler: handler)
|
||||||
|
|
||||||
|
XCTAssertFalse(factory.didCallDestroyCounter)
|
||||||
|
do {
|
||||||
|
let counter1 = Counter(label: "foo", factory: factory)
|
||||||
|
XCTAssertTrue(counter1._handler is CustomFactory.CustomHandler, "expected a custom metrics handler")
|
||||||
|
XCTAssertTrue(counter1._factory is CustomFactory, "expected a custom metrics factory")
|
||||||
|
counter1.destroy()
|
||||||
|
}
|
||||||
|
XCTAssertTrue(factory.didCallDestroyCounter)
|
||||||
|
}
|
||||||
|
|
||||||
func testDestroyingGauge() throws {
|
func testDestroyingGauge() throws {
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
|
@ -486,7 +692,7 @@ class MetricsTests: XCTestCase {
|
||||||
let gauge = Gauge(label: name)
|
let gauge = Gauge(label: name)
|
||||||
gauge.record(value)
|
gauge.record(value)
|
||||||
|
|
||||||
let recorder = gauge._handler as! TestRecorder
|
let recorder = try metrics.expectRecorder(gauge)
|
||||||
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(recorder.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(recorder.values.first, value, "expected value to match")
|
XCTAssertEqual(recorder.values.first, value, "expected value to match")
|
||||||
XCTAssertEqual(metrics.recorders.count, 1, "recorder should have been stored")
|
XCTAssertEqual(metrics.recorders.count, 1, "recorder should have been stored")
|
||||||
|
|
@ -498,25 +704,29 @@ class MetricsTests: XCTestCase {
|
||||||
let gaugeAgain = Gauge(label: name)
|
let gaugeAgain = Gauge(label: name)
|
||||||
gaugeAgain.record(-value)
|
gaugeAgain.record(-value)
|
||||||
|
|
||||||
let recorderAgain = gaugeAgain._handler as! TestRecorder
|
let recorderAgain = try metrics.expectRecorder(gaugeAgain)
|
||||||
XCTAssertEqual(recorderAgain.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(recorderAgain.values.count, 1, "expected number of entries to match")
|
||||||
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 {
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
|
||||||
let name = "meter-\(NSUUID().uuidString)"
|
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 meter = Meter(label: name)
|
||||||
meter.set(value)
|
meter.set(value)
|
||||||
|
|
||||||
let testMeter = meter._handler as! TestMeter
|
let testMeter = try metrics.expectMeter(meter)
|
||||||
XCTAssertEqual(testMeter.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testMeter.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testMeter.values.first, value, "expected value to match")
|
XCTAssertEqual(testMeter.values.first, value, "expected value to match")
|
||||||
XCTAssertEqual(metrics.meters.count, 1, "recorder should have been stored")
|
XCTAssertEqual(metrics.meters.count, 1, "recorder should have been stored")
|
||||||
|
|
@ -528,12 +738,16 @@ class MetricsTests: XCTestCase {
|
||||||
let meterAgain = Meter(label: name)
|
let meterAgain = Meter(label: name)
|
||||||
meterAgain.set(-value)
|
meterAgain.set(-value)
|
||||||
|
|
||||||
let testMeterAgain = meterAgain._handler as! TestMeter
|
let testMeterAgain = try metrics.expectMeter(meterAgain)
|
||||||
XCTAssertEqual(testMeterAgain.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testMeterAgain.values.count, 1, "expected number of entries to match")
|
||||||
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 {
|
||||||
|
|
@ -546,7 +760,7 @@ class MetricsTests: XCTestCase {
|
||||||
let counter = Counter(label: name)
|
let counter = Counter(label: name)
|
||||||
counter.increment(by: value)
|
counter.increment(by: value)
|
||||||
|
|
||||||
let testCounter = counter._handler as! TestCounter
|
let testCounter = try metrics.expectCounter(counter)
|
||||||
XCTAssertEqual(testCounter.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testCounter.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(testCounter.values.first, Int64(value), "expected value to match")
|
XCTAssertEqual(testCounter.values.first, Int64(value), "expected value to match")
|
||||||
XCTAssertEqual(metrics.counters.count, 1, "counter should have been stored")
|
XCTAssertEqual(metrics.counters.count, 1, "counter should have been stored")
|
||||||
|
|
@ -558,12 +772,16 @@ class MetricsTests: XCTestCase {
|
||||||
let counterAgain = Counter(label: name)
|
let counterAgain = Counter(label: name)
|
||||||
counterAgain.increment(by: value)
|
counterAgain.increment(by: value)
|
||||||
|
|
||||||
let testCounterAgain = counterAgain._handler as! TestCounter
|
let testCounterAgain = try metrics.expectCounter(counterAgain)
|
||||||
XCTAssertEqual(testCounterAgain.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testCounterAgain.values.count, 1, "expected number of entries to match")
|
||||||
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 {
|
||||||
|
|
@ -576,7 +794,7 @@ class MetricsTests: XCTestCase {
|
||||||
let timer = Timer(label: name)
|
let timer = Timer(label: name)
|
||||||
timer.recordNanoseconds(value)
|
timer.recordNanoseconds(value)
|
||||||
|
|
||||||
let testTimer = timer._handler as! TestTimer
|
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(testTimer.values.first, value, "expected value to match")
|
XCTAssertEqual(testTimer.values.first, value, "expected value to match")
|
||||||
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||||
|
|
@ -587,12 +805,16 @@ class MetricsTests: XCTestCase {
|
||||||
|
|
||||||
let timerAgain = Timer(label: name)
|
let timerAgain = Timer(label: name)
|
||||||
timerAgain.recordNanoseconds(value)
|
timerAgain.recordNanoseconds(value)
|
||||||
let testTimerAgain = timerAgain._handler as! TestTimer
|
let testTimerAgain = try metrics.expectTimer(timerAgain)
|
||||||
XCTAssertEqual(testTimerAgain.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testTimerAgain.values.count, 1, "expected number of entries to match")
|
||||||
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 {
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// MetricsTests+XCTest.swift
|
|
||||||
//
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
///
|
|
||||||
/// NOTE: This file was generated by generate_linux_tests.rb
|
|
||||||
///
|
|
||||||
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
|
|
||||||
///
|
|
||||||
|
|
||||||
extension MetricsExtensionsTests {
|
|
||||||
static var allTests: [(String, (MetricsExtensionsTests) -> () throws -> Void)] {
|
|
||||||
return [
|
|
||||||
("testTimerBlock", testTimerBlock),
|
|
||||||
("testTimerWithTimeInterval", testTimerWithTimeInterval),
|
|
||||||
("testTimerWithDispatchTime", testTimerWithDispatchTime),
|
|
||||||
("testTimerWithDispatchTimeInterval", testTimerWithDispatchTimeInterval),
|
|
||||||
("testTimerUnits", testTimerUnits),
|
|
||||||
("testPreferDisplayUnit", testPreferDisplayUnit),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -39,7 +40,7 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
// run the test
|
// run the test
|
||||||
let timer = Timer(label: "test-timer")
|
let timer = Timer(label: "test-timer")
|
||||||
let testTimer = timer._handler as! TestTimer
|
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)
|
timer.record(timeInterval)
|
||||||
XCTAssertEqual(1, testTimer.values.count, "expected number of entries to match")
|
XCTAssertEqual(1, testTimer.values.count, "expected number of entries to match")
|
||||||
|
|
@ -52,7 +53,7 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
// run the test
|
// run the test
|
||||||
let timer = Timer(label: "test-timer")
|
let timer = Timer(label: "test-timer")
|
||||||
let testTimer = timer._handler as! TestTimer
|
let testTimer = try metrics.expectTimer(timer)
|
||||||
// nano
|
// nano
|
||||||
let nano = DispatchTimeInterval.nanoseconds(Int.random(in: 1...500))
|
let nano = DispatchTimeInterval.nanoseconds(Int.random(in: 1...500))
|
||||||
timer.record(nano)
|
timer.record(nano)
|
||||||
|
|
@ -79,7 +80,7 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
XCTAssertEqual(testTimer.values[4], 0, "expected value to match")
|
XCTAssertEqual(testTimer.values[4], 0, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimerWithDispatchTimeInterval() {
|
func testTimerWithDispatchTimeInterval() throws {
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
|
||||||
|
|
@ -90,9 +91,41 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
let end = DispatchTime(uptimeNanoseconds: start.uptimeNanoseconds + 1000 * 1000 * 1000)
|
let end = DispatchTime(uptimeNanoseconds: start.uptimeNanoseconds + 1000 * 1000 * 1000)
|
||||||
timer.recordInterval(since: start, end: end)
|
timer.recordInterval(since: start, end: end)
|
||||||
|
|
||||||
let testTimer = timer._handler as! TestTimer
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTimerDuration() throws {
|
||||||
|
// Wrapping only the insides of the test case so that the generated
|
||||||
|
// tests on Linux in MetricsTests+XCTest don't complain that the func does not exist.
|
||||||
|
guard #available(iOS 16, macOS 13, tvOS 15, watchOS 8, *) else {
|
||||||
|
throw XCTSkip("Timer.record(_ duration: Duration) is not available on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
|
||||||
|
let name = "timer-\(UUID().uuidString)"
|
||||||
|
let timer = Timer(label: name)
|
||||||
|
|
||||||
|
let duration = Duration(secondsComponent: 3, attosecondsComponent: 123_000_000_000_000_000)
|
||||||
|
let nanoseconds = duration.components.seconds * 1_000_000_000 + duration.components.attoseconds / 1_000_000_000
|
||||||
|
timer.record(duration: duration)
|
||||||
|
|
||||||
|
// Record a Duration that would overflow,
|
||||||
|
// expect Int64.max to be recorded.
|
||||||
|
timer.record(duration: Duration(secondsComponent: 10_000_000_000, attosecondsComponent: 123))
|
||||||
|
|
||||||
|
let testTimer = try metrics.expectTimer(timer)
|
||||||
|
XCTAssertEqual(testTimer.values.count, 2, "expected number of entries to match")
|
||||||
|
XCTAssertEqual(testTimer.values.first, nanoseconds, "expected value to match")
|
||||||
|
XCTAssertEqual(testTimer.values[1], Int64.max, "expected to record Int64.max if Durataion overflows")
|
||||||
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +139,7 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
let timer = Timer(label: name)
|
let timer = Timer(label: name)
|
||||||
timer.recordNanoseconds(value)
|
timer.recordNanoseconds(value)
|
||||||
|
|
||||||
let testTimer = timer._handler as! TestTimer
|
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(testTimer.values.first, value, "expected value to match")
|
XCTAssertEqual(testTimer.values.first, value, "expected value to match")
|
||||||
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||||
|
|
@ -116,12 +149,12 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
let secondsTimer = Timer(label: secondsName, preferredDisplayUnit: .seconds)
|
let secondsTimer = Timer(label: secondsName, preferredDisplayUnit: .seconds)
|
||||||
secondsTimer.recordSeconds(secondsValue)
|
secondsTimer.recordSeconds(secondsValue)
|
||||||
|
|
||||||
let testSecondsTimer = secondsTimer._handler as! TestTimer
|
let testSecondsTimer = try metrics.expectTimer(secondsTimer)
|
||||||
XCTAssertEqual(testSecondsTimer.values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(testSecondsTimer.values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(metrics.timers.count, 2, "timer should have been stored")
|
XCTAssertEqual(metrics.timers.count, 2, "timer should have been stored")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPreferDisplayUnit() {
|
func testPreferDisplayUnit() throws {
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
|
||||||
|
|
@ -129,35 +162,117 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
let timer = Timer(label: "test", preferredDisplayUnit: .seconds)
|
let timer = Timer(label: "test", preferredDisplayUnit: .seconds)
|
||||||
timer.recordSeconds(value)
|
timer.recordSeconds(value)
|
||||||
|
|
||||||
let testTimer = timer._handler as! TestTimer
|
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"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if compiler(>=6.0)
|
||||||
|
func testTimerMeasure() async throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
// run the test
|
||||||
|
let name = "timer-\(UUID().uuidString)"
|
||||||
|
let delay = Duration.milliseconds(5)
|
||||||
|
let timer = Timer(label: name)
|
||||||
|
try await timer.measure {
|
||||||
|
try await Task.sleep(for: delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
let expectedTimer = try metrics.expectTimer(name)
|
||||||
|
XCTAssertEqual(1, expectedTimer.values.count, "expected number of entries to match")
|
||||||
|
XCTAssertGreaterThan(expectedTimer.values[0], delay.nanosecondsClamped, "expected delay to match")
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func testTimerMeasureFromMainActor() async throws {
|
||||||
|
// bootstrap with our test metrics
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
// run the test
|
||||||
|
let name = "timer-\(UUID().uuidString)"
|
||||||
|
let delay = Duration.milliseconds(5)
|
||||||
|
let timer = Timer(label: name)
|
||||||
|
try await timer.measure {
|
||||||
|
try await Task.sleep(for: delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
let expectedTimer = try metrics.expectTimer(name)
|
||||||
|
XCTAssertEqual(1, expectedTimer.values.count, "expected number of entries to match")
|
||||||
|
XCTAssertGreaterThan(expectedTimer.values[0], delay.nanosecondsClamped, "expected delay to match")
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://bugs.swift.org/browse/SR-6310
|
// https://bugs.swift.org/browse/SR-6310
|
||||||
extension DispatchTimeInterval {
|
extension DispatchTimeInterval {
|
||||||
func nano() -> Int {
|
func nano() -> Int {
|
||||||
switch self {
|
// This wrapping in a optional is a workaround because DispatchTimeInterval
|
||||||
|
// is a non-frozen public enum and Dispatch is built with library evolution
|
||||||
|
// mode turned on.
|
||||||
|
// This means we should have an `@unknown default` case, but this breaks
|
||||||
|
// on non-Darwin platforms.
|
||||||
|
// Switching over an optional means that the `.none` case will map to
|
||||||
|
// `default` (which means we'll always have a valid case to go into
|
||||||
|
// the default case), but in reality this case will never exist as this
|
||||||
|
// optional will never be nil.
|
||||||
|
let interval = Optional(self)
|
||||||
|
switch interval {
|
||||||
case .nanoseconds(let value):
|
case .nanoseconds(let value):
|
||||||
return value
|
return value
|
||||||
case .microseconds(let value):
|
case .microseconds(let value):
|
||||||
|
|
@ -168,6 +283,30 @@ extension DispatchTimeInterval {
|
||||||
return value * 1_000_000_000
|
return value * 1_000_000_000
|
||||||
case .never:
|
case .never:
|
||||||
return 0
|
return 0
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if swift(>=5.7)
|
||||||
|
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||||
|
extension Swift.Duration {
|
||||||
|
fileprivate var nanosecondsClamped: Int64 {
|
||||||
|
let components = self.components
|
||||||
|
|
||||||
|
let secondsComponentNanos = components.seconds.multipliedReportingOverflow(by: 1_000_000_000)
|
||||||
|
let attosCompononentNanos = components.attoseconds / 1_000_000_000
|
||||||
|
let combinedNanos = secondsComponentNanos.partialValue.addingReportingOverflow(attosCompononentNanos)
|
||||||
|
|
||||||
|
guard
|
||||||
|
!secondsComponentNanos.overflow,
|
||||||
|
!combinedNanos.overflow
|
||||||
|
else {
|
||||||
|
return .max
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedNanos.partialValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
@testable import CoreMetrics
|
|
||||||
import Dispatch
|
import Dispatch
|
||||||
import MetricsTestKit
|
import MetricsTestKit
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
@testable import CoreMetrics
|
||||||
|
|
||||||
class SendableTest: XCTestCase {
|
class SendableTest: XCTestCase {
|
||||||
#if compiler(>=5.6)
|
func testSendableMetrics() async throws {
|
||||||
func testSendableMetrics() async {
|
|
||||||
// bootstrap with our test metrics
|
// bootstrap with our test metrics
|
||||||
let metrics = TestMetrics()
|
let metrics = TestMetrics()
|
||||||
MetricsSystem.bootstrapInternal(metrics)
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
|
@ -31,10 +31,10 @@ class SendableTest: XCTestCase {
|
||||||
|
|
||||||
let task = Task.detached { () -> [Int64] in
|
let task = Task.detached { () -> [Int64] in
|
||||||
counter.increment(by: value)
|
counter.increment(by: value)
|
||||||
let handler = counter._handler as! TestCounter
|
let handler = try metrics.expectCounter(counter)
|
||||||
return handler.values
|
return handler.values
|
||||||
}
|
}
|
||||||
let values = await task.value
|
let values = try await task.value
|
||||||
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(values[0], Int64(value), "expected value to match")
|
XCTAssertEqual(values[0], Int64(value), "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
@ -60,25 +60,25 @@ class SendableTest: XCTestCase {
|
||||||
|
|
||||||
let task = Task.detached { () -> [Double] in
|
let task = Task.detached { () -> [Double] in
|
||||||
recorder.record(value)
|
recorder.record(value)
|
||||||
let handler = recorder._handler as! TestRecorder
|
let handler = try metrics.expectRecorder(recorder)
|
||||||
return handler.values
|
return handler.values
|
||||||
}
|
}
|
||||||
let values = await task.value
|
let values = try await task.value
|
||||||
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(values[0], value, "expected value to match")
|
XCTAssertEqual(values[0], value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let name = "meter-\(NSUUID().uuidString)"
|
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 meter = Meter(label: name)
|
||||||
|
|
||||||
let task = Task.detached { () -> [Double] in
|
let task = Task.detached { () -> [Double] in
|
||||||
meter.set(value)
|
meter.set(value)
|
||||||
let handler = meter._handler as! TestMeter
|
let handler = try metrics.expectMeter(meter)
|
||||||
return handler.values
|
return handler.values
|
||||||
}
|
}
|
||||||
let values = await task.value
|
let values = try await task.value
|
||||||
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(values[0], value, "expected value to match")
|
XCTAssertEqual(values[0], value, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
@ -90,13 +90,12 @@ class SendableTest: XCTestCase {
|
||||||
|
|
||||||
let task = Task.detached { () -> [Int64] in
|
let task = Task.detached { () -> [Int64] in
|
||||||
timer.recordNanoseconds(value)
|
timer.recordNanoseconds(value)
|
||||||
let handler = timer._handler as! TestTimer
|
let handler = try metrics.expectTimer(timer)
|
||||||
return handler.values
|
return handler.values
|
||||||
}
|
}
|
||||||
let values = await task.value
|
let values = try await task.value
|
||||||
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
XCTAssertEqual(values.count, 1, "expected number of entries to match")
|
||||||
XCTAssertEqual(values[0], value, "expected value to match")
|
XCTAssertEqual(values[0], value, "expected value to match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,16 +0,0 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
runtime-setup:
|
|
||||||
image: swift-metrics:18.04-5.0
|
|
||||||
build:
|
|
||||||
args:
|
|
||||||
ubuntu_version: "bionic"
|
|
||||||
swift_version: "5.0"
|
|
||||||
|
|
||||||
test:
|
|
||||||
image: swift-metrics:18.04-5.0
|
|
||||||
|
|
||||||
shell:
|
|
||||||
image: swift-metrics:18.04-5.0
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
runtime-setup:
|
|
||||||
image: swift-metrics:18.04-5.1
|
|
||||||
build:
|
|
||||||
args:
|
|
||||||
ubuntu_version: "bionic"
|
|
||||||
swift_version: "5.1"
|
|
||||||
|
|
||||||
test:
|
|
||||||
image: swift-metrics:18.04-5.1
|
|
||||||
environment: []
|
|
||||||
#- SANITIZER_ARG=--sanitize=thread
|
|
||||||
|
|
||||||
shell:
|
|
||||||
image: swift-metrics:18.04-5.1
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
runtime-setup:
|
|
||||||
image: swift-metrics:18.04-5.2
|
|
||||||
build:
|
|
||||||
args:
|
|
||||||
ubuntu_version: "bionic"
|
|
||||||
swift_version: "5.2"
|
|
||||||
|
|
||||||
test:
|
|
||||||
image: swift-metrics:18.04-5.2
|
|
||||||
environment: []
|
|
||||||
#- SANITIZER_ARG=--sanitize=thread
|
|
||||||
|
|
||||||
shell:
|
|
||||||
image: swift-metrics:18.04-5.2
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
runtime-setup:
|
|
||||||
image: swift-metrics:18.04-5.3
|
|
||||||
build:
|
|
||||||
args:
|
|
||||||
ubuntu_version: "bionic"
|
|
||||||
swift_version: "5.3"
|
|
||||||
|
|
||||||
test:
|
|
||||||
image: swift-metrics:18.04-5.3
|
|
||||||
environment: []
|
|
||||||
#- SANITIZER_ARG=--sanitize=thread
|
|
||||||
|
|
||||||
shell:
|
|
||||||
image: swift-metrics:18.04-5.3
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
runtime-setup:
|
|
||||||
image: swift-metrics:20.04-5.4
|
|
||||||
build:
|
|
||||||
args:
|
|
||||||
ubuntu_version: "focal"
|
|
||||||
swift_version: "5.4"
|
|
||||||
|
|
||||||
test:
|
|
||||||
image: swift-metrics:20.04-5.4
|
|
||||||
environment: []
|
|
||||||
#- SANITIZER_ARG=--sanitize=thread
|
|
||||||
|
|
||||||
shell:
|
|
||||||
image: swift-metrics:20.04-5.4
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
runtime-setup:
|
|
||||||
image: swift-metrics:20.04-5.5
|
|
||||||
build:
|
|
||||||
args:
|
|
||||||
ubuntu_version: "focal"
|
|
||||||
swift_version: "5.5"
|
|
||||||
|
|
||||||
test:
|
|
||||||
image: swift-metrics:20.04-5.5
|
|
||||||
environment:
|
|
||||||
- FORCE_TEST_DISCOVERY=--enable-test-discovery
|
|
||||||
#- SANITIZER_ARG=--sanitize=thread
|
|
||||||
|
|
||||||
shell:
|
|
||||||
image: swift-metrics:20.04-5.5
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
runtime-setup:
|
|
||||||
image: swift-metrics:20.04-5.6
|
|
||||||
build:
|
|
||||||
args:
|
|
||||||
ubuntu_version: "focal"
|
|
||||||
swift_version: "5.6"
|
|
||||||
|
|
||||||
test:
|
|
||||||
image: swift-metrics:20.04-5.6
|
|
||||||
environment:
|
|
||||||
- FORCE_TEST_DISCOVERY=--enable-test-discovery
|
|
||||||
#- SANITIZER_ARG=--sanitize=thread
|
|
||||||
|
|
||||||
shell:
|
|
||||||
image: swift-metrics:20.04-5.6
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
runtime-setup:
|
|
||||||
image: swift-metrics:20.04-5.7
|
|
||||||
build:
|
|
||||||
args:
|
|
||||||
ubuntu_version: "focal"
|
|
||||||
swift_version: "5.7"
|
|
||||||
|
|
||||||
test:
|
|
||||||
image: swift-metrics:20.04-5.7
|
|
||||||
environment: []
|
|
||||||
#- SANITIZER_ARG=--sanitize=thread
|
|
||||||
|
|
||||||
shell:
|
|
||||||
image: swift-metrics:20.04-5.7
|
|
||||||
|
|
@ -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.9
|
|
||||||
build:
|
|
||||||
args:
|
|
||||||
base_image: "swiftlang/swift:nightly-5.9-jammy"
|
|
||||||
|
|
||||||
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,43 +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"
|
|
||||||
|
|
||||||
docs:
|
|
||||||
<<: *common
|
|
||||||
environment:
|
|
||||||
- CI
|
|
||||||
command: /bin/bash -xcl "./scripts/generate_docs.sh"
|
|
||||||
|
|
||||||
test:
|
|
||||||
<<: *common
|
|
||||||
command: /bin/bash -xcl "swift test -Xswiftc -warnings-as-errors $${FORCE_TEST_DISCOVERY-} $${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,136 +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
|
|
||||||
|
|
||||||
# repodir
|
|
||||||
function all_modules() {
|
|
||||||
local repodir="$1"
|
|
||||||
(
|
|
||||||
set -eu
|
|
||||||
cd "$repodir"
|
|
||||||
swift package dump-package | jq '.products |
|
|
||||||
map(select(.type | has("library") )) |
|
|
||||||
map(.name) | .[]' | tr -d '"'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
# repodir tag output
|
|
||||||
function build_and_do() {
|
|
||||||
local repodir=$1
|
|
||||||
local tag=$2
|
|
||||||
local output=$3
|
|
||||||
|
|
||||||
(
|
|
||||||
cd "$repodir"
|
|
||||||
git checkout -q "$tag"
|
|
||||||
swift build
|
|
||||||
while read -r module; do
|
|
||||||
swift api-digester -sdk "$sdk" -dump-sdk -module "$module" \
|
|
||||||
-o "$output/$module.json" -I "$repodir/.build/debug"
|
|
||||||
done < <(all_modules "$repodir")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
echo >&2 "Usage: $0 REPO-GITHUB-URL NEW-VERSION OLD-VERSIONS..."
|
|
||||||
echo >&2
|
|
||||||
echo >&2 "This script requires a Swift 5.1+ toolchain."
|
|
||||||
echo >&2
|
|
||||||
echo >&2 "Examples:"
|
|
||||||
echo >&2
|
|
||||||
echo >&2 "Check between master and tag 2.1.1 of swift-nio:"
|
|
||||||
echo >&2 " $0 https://github.com/apple/swift-nio master 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
|
|
||||||
|
|
||||||
sdk=/
|
|
||||||
if [[ "$(uname -s)" == Darwin ]]; then
|
|
||||||
sdk=$(xcrun --show-sdk-path)
|
|
||||||
fi
|
|
||||||
|
|
||||||
hash jq 2> /dev/null || { echo >&2 "ERROR: jq must be installed"; exit 1; }
|
|
||||||
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/*'
|
|
||||||
errors=0
|
|
||||||
|
|
||||||
for old_tag in "$@"; do
|
|
||||||
mkdir "$tmpdir/api-old"
|
|
||||||
mkdir "$tmpdir/api-new"
|
|
||||||
|
|
||||||
echo "Checking public API breakages from $old_tag to $new_tag"
|
|
||||||
|
|
||||||
build_and_do "$repodir" "$new_tag" "$tmpdir/api-new/"
|
|
||||||
build_and_do "$repodir" "$old_tag" "$tmpdir/api-old/"
|
|
||||||
|
|
||||||
for f in "$tmpdir/api-new"/*; do
|
|
||||||
f=$(basename "$f")
|
|
||||||
report="$tmpdir/$f.report"
|
|
||||||
if [[ ! -f "$tmpdir/api-old/$f" ]]; then
|
|
||||||
echo "NOTICE: NEW MODULE $f"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -n "Checking $f... "
|
|
||||||
swift api-digester -sdk "$sdk" -diagnose-sdk \
|
|
||||||
--input-paths "$tmpdir/api-old/$f" -input-paths "$tmpdir/api-new/$f" 2>&1 \
|
|
||||||
> "$report" 2>&1
|
|
||||||
|
|
||||||
if ! shasum "$report" | grep -q cefc4ee5bb7bcdb7cb5a7747efa178dab3c794d5; then
|
|
||||||
echo ERROR
|
|
||||||
echo >&2 "=============================="
|
|
||||||
echo >&2 "ERROR: public API change in $f"
|
|
||||||
echo >&2 "=============================="
|
|
||||||
cat >&2 "$report"
|
|
||||||
errors=$(( errors + 1 ))
|
|
||||||
else
|
|
||||||
echo OK
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
rm -rf "$tmpdir/api-new" "$tmpdir/api-old"
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ "$errors" == 0 ]]; then
|
|
||||||
echo "OK, all seems good"
|
|
||||||
fi
|
|
||||||
echo done
|
|
||||||
exit "$errors"
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
##===----------------------------------------------------------------------===##
|
|
||||||
##
|
|
||||||
## This source file is part of the Swift Metrics API open source project
|
|
||||||
##
|
|
||||||
## Copyright (c) 2018-2019 Apple Inc. and the Swift Metrics API project authors
|
|
||||||
## Licensed under Apache License v2.0
|
|
||||||
##
|
|
||||||
## See LICENSE.txt for license information
|
|
||||||
## See CONTRIBUTORS.txt for the list of Swift Metrics API project authors
|
|
||||||
##
|
|
||||||
## SPDX-License-Identifier: Apache-2.0
|
|
||||||
##
|
|
||||||
##===----------------------------------------------------------------------===##
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
contributors=$( cd "$here"/.. && git shortlog -es | cut -f2 | sed 's/^/- /' )
|
|
||||||
|
|
||||||
cat > "$here/../CONTRIBUTORS.txt" <<- EOF
|
|
||||||
For the purpose of tracking copyright, this is the list of individuals and
|
|
||||||
organizations who have contributed source code to the Swift Metrics API.
|
|
||||||
|
|
||||||
For employees of an organization/company where the copyright of work done
|
|
||||||
by employees of that company is held by the company itself, only the company
|
|
||||||
needs to be listed here.
|
|
||||||
|
|
||||||
## COPYRIGHT HOLDERS
|
|
||||||
|
|
||||||
- Apple Inc. (all contributors with '@apple.com')
|
|
||||||
|
|
||||||
### Contributors
|
|
||||||
|
|
||||||
$contributors
|
|
||||||
|
|
||||||
**Updating this list**
|
|
||||||
|
|
||||||
Please do not edit this file manually. It is generated using \`./scripts/generate_contributors_list.sh\`. If a name is misspelled or appearing multiple times: add an entry in \`./.mailmap\`
|
|
||||||
EOF
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
##===----------------------------------------------------------------------===##
|
|
||||||
##
|
|
||||||
## This source file is part of the Swift Metrics API open source project
|
|
||||||
##
|
|
||||||
## Copyright (c) 2018-2019 Apple Inc. and the Swift Metrics API project authors
|
|
||||||
## Licensed under Apache License v2.0
|
|
||||||
##
|
|
||||||
## See LICENSE.txt for license information
|
|
||||||
## See CONTRIBUTORS.txt for the list of Swift Metrics API project authors
|
|
||||||
##
|
|
||||||
## SPDX-License-Identifier: Apache-2.0
|
|
||||||
##
|
|
||||||
##===----------------------------------------------------------------------===##
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
my_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
root_path="$my_path/.."
|
|
||||||
version=$(git describe --abbrev=0 --tags || echo "0.0.0")
|
|
||||||
modules=(CoreMetrics Metrics)
|
|
||||||
|
|
||||||
if [[ "$(uname -s)" == "Linux" ]]; then
|
|
||||||
# build code if required
|
|
||||||
if [[ ! -d "$root_path/.build/x86_64-unknown-linux" ]]; then
|
|
||||||
swift build
|
|
||||||
fi
|
|
||||||
# setup source-kitten if required
|
|
||||||
mkdir -p "$root_path/.build/sourcekitten"
|
|
||||||
source_kitten_source_path="$root_path/.build/sourcekitten/source"
|
|
||||||
if [[ ! -d "$source_kitten_source_path" ]]; then
|
|
||||||
git clone https://github.com/jpsim/SourceKitten.git "$source_kitten_source_path"
|
|
||||||
fi
|
|
||||||
source_kitten_path="$source_kitten_source_path/.build/debug"
|
|
||||||
if [[ ! -d "$source_kitten_path" ]]; then
|
|
||||||
rm -rf "$source_kitten_source_path/.swift-version"
|
|
||||||
cd "$source_kitten_source_path" && swift build && cd "$root_path"
|
|
||||||
fi
|
|
||||||
# generate
|
|
||||||
for module in "${modules[@]}"; do
|
|
||||||
if [[ ! -f "$root_path/.build/sourcekitten/$module.json" ]]; then
|
|
||||||
"$source_kitten_path/sourcekitten" doc --spm --module-name $module > "$root_path/.build/sourcekitten/$module.json"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
[[ -d docs/$version ]] || mkdir -p docs/$version
|
|
||||||
[[ -d swift-metrics.xcodeproj ]] || swift package generate-xcodeproj
|
|
||||||
|
|
||||||
# run jazzy
|
|
||||||
if ! command -v jazzy > /dev/null; then
|
|
||||||
gem install jazzy --no-ri --no-rdoc
|
|
||||||
fi
|
|
||||||
|
|
||||||
jazzy_dir="$root_path/.build/jazzy"
|
|
||||||
rm -rf "$jazzy_dir"
|
|
||||||
mkdir -p "$jazzy_dir"
|
|
||||||
|
|
||||||
module_switcher="$jazzy_dir/README.md"
|
|
||||||
jazzy_args=(--clean
|
|
||||||
--author 'SwiftMetrics team'
|
|
||||||
--readme "$module_switcher"
|
|
||||||
--author_url https://github.com/apple/swift-metrics
|
|
||||||
--github_url https://github.com/apple/swift-metrics
|
|
||||||
--github-file-prefix https://github.com/apple/swift-metrics/tree/$version
|
|
||||||
--theme fullwidth
|
|
||||||
--xcodebuild-arguments -scheme,swift-metrics-Package)
|
|
||||||
cat > "$module_switcher" <<"EOF"
|
|
||||||
# SwiftMetrics Docs
|
|
||||||
|
|
||||||
SwiftMetrics is a Swift metrics API package.
|
|
||||||
|
|
||||||
To get started with SwiftMetrics, [`import Metrics`](../CoreMetrics/index.html). The most important types are:
|
|
||||||
|
|
||||||
* [`Counter`](https://apple.github.io/swift-metrics/docs/current/CoreMetrics/Classes/Counter.html)
|
|
||||||
* [`Timer`](https://apple.github.io/swift-metrics/docs/current/CoreMetrics/Classes/Timer.html)
|
|
||||||
* [`Recorder`](https://apple.github.io/swift-metrics/docs/current/CoreMetrics/Classes/Recorder.html)
|
|
||||||
* [`Gauge`](https://apple.github.io/swift-metrics/docs/current/CoreMetrics/Classes/Gauge.html)
|
|
||||||
|
|
||||||
SwiftMetrics contains multiple modules:
|
|
||||||
EOF
|
|
||||||
|
|
||||||
for module in "${modules[@]}"; do
|
|
||||||
echo " - [$module](../$module/index.html)" >> "$module_switcher"
|
|
||||||
done
|
|
||||||
|
|
||||||
for module in "${modules[@]}"; do
|
|
||||||
echo "processing $module"
|
|
||||||
args=("${jazzy_args[@]}" --output "$jazzy_dir/docs/$version/$module" --docset-path "$jazzy_dir/docset/$version/$module"
|
|
||||||
--module "$module" --module-version $version
|
|
||||||
--root-url "https://apple.github.io/swift-metrics/docs/$version/$module/")
|
|
||||||
if [[ -f "$root_path/.build/sourcekitten/$module.json" ]]; then
|
|
||||||
args+=(--sourcekitten-sourcefile "$root_path/.build/sourcekitten/$module.json")
|
|
||||||
fi
|
|
||||||
jazzy "${args[@]}"
|
|
||||||
done
|
|
||||||
|
|
||||||
# push to github pages
|
|
||||||
if [[ $PUSH == true ]]; then
|
|
||||||
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
GIT_AUTHOR=$(git --no-pager show -s --format='%an <%ae>' HEAD)
|
|
||||||
git fetch origin +gh-pages:gh-pages
|
|
||||||
git checkout gh-pages
|
|
||||||
rm -rf "docs/$version"
|
|
||||||
rm -rf "docs/current"
|
|
||||||
cp -r "$jazzy_dir/docs/$version" docs/
|
|
||||||
cp -r "docs/$version" docs/current
|
|
||||||
git add --all docs
|
|
||||||
echo '<html><head><meta http-equiv="refresh" content="0; url=docs/current/CoreMetrics/index.html" /></head></html>' > index.html
|
|
||||||
git add index.html
|
|
||||||
touch .nojekyll
|
|
||||||
git add .nojekyll
|
|
||||||
changes=$(git diff-index --name-only HEAD)
|
|
||||||
if [[ -n "$changes" ]]; then
|
|
||||||
echo -e "changes detected\n$changes"
|
|
||||||
git commit --author="$GIT_AUTHOR" -m "publish $version docs"
|
|
||||||
git push origin gh-pages
|
|
||||||
else
|
|
||||||
echo "no changes detected"
|
|
||||||
fi
|
|
||||||
git checkout -f $BRANCH_NAME
|
|
||||||
fi
|
|
||||||
|
|
@ -1,231 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
#
|
|
||||||
# process_test_files.rb
|
|
||||||
#
|
|
||||||
# Copyright 2016 Tony Stone
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# Created by Tony Stone on 5/4/16.
|
|
||||||
#
|
|
||||||
require 'getoptlong'
|
|
||||||
require 'fileutils'
|
|
||||||
require 'pathname'
|
|
||||||
|
|
||||||
include FileUtils
|
|
||||||
|
|
||||||
#
|
|
||||||
# This ruby script will auto generate LinuxMain.swift and the +XCTest.swift extension files for Swift Package Manager on Linux platforms.
|
|
||||||
#
|
|
||||||
# See https://github.com/apple/swift-corelibs-xctest/blob/master/Documentation/Linux.md
|
|
||||||
#
|
|
||||||
def header(fileName)
|
|
||||||
string = <<-eos
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// <FileName>
|
|
||||||
//
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
///
|
|
||||||
/// NOTE: This file was generated by generate_linux_tests.rb
|
|
||||||
///
|
|
||||||
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
|
|
||||||
///
|
|
||||||
eos
|
|
||||||
|
|
||||||
string
|
|
||||||
.sub('<FileName>', File.basename(fileName))
|
|
||||||
.sub('<Date>', Time.now.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
def createExtensionFile(fileName, classes)
|
|
||||||
extensionFile = fileName.sub! '.swift', '+XCTest.swift'
|
|
||||||
print 'Creating file: ' + extensionFile + "\n"
|
|
||||||
|
|
||||||
File.open(extensionFile, 'w') do |file|
|
|
||||||
file.write header(extensionFile)
|
|
||||||
file.write "\n"
|
|
||||||
|
|
||||||
for classArray in classes
|
|
||||||
file.write 'extension ' + classArray[0] + " {\n"
|
|
||||||
file.write ' static var allTests: [(String, (' + classArray[0] + ") -> () throws -> Void)] {\n"
|
|
||||||
file.write " return [\n"
|
|
||||||
|
|
||||||
for funcName in classArray[1]
|
|
||||||
file.write ' ("' + funcName + '", ' + funcName + "),\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
file.write " ]\n"
|
|
||||||
file.write " }\n"
|
|
||||||
file.write "}\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def createLinuxMain(testsDirectory, allTestSubDirectories, files)
|
|
||||||
fileName = testsDirectory + '/LinuxMain.swift'
|
|
||||||
print 'Creating file: ' + fileName + "\n"
|
|
||||||
|
|
||||||
File.open(fileName, 'w') do |file|
|
|
||||||
file.write header(fileName)
|
|
||||||
file.write "\n"
|
|
||||||
|
|
||||||
file.write "#if os(Linux) || os(FreeBSD)\n"
|
|
||||||
for testSubDirectory in allTestSubDirectories.sort { |x, y| x <=> y }
|
|
||||||
file.write '@testable import ' + testSubDirectory + "\n"
|
|
||||||
end
|
|
||||||
file.write "\n"
|
|
||||||
file.write "XCTMain([\n"
|
|
||||||
|
|
||||||
testCases = []
|
|
||||||
for classes in files
|
|
||||||
for classArray in classes
|
|
||||||
testCases << classArray[0]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for testCase in testCases.sort { |x, y| x <=> y }
|
|
||||||
file.write ' testCase(' + testCase + ".allTests),\n"
|
|
||||||
end
|
|
||||||
file.write "])\n"
|
|
||||||
file.write "#endif\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parseSourceFile(fileName)
|
|
||||||
puts 'Parsing file: ' + fileName + "\n"
|
|
||||||
|
|
||||||
classes = []
|
|
||||||
currentClass = nil
|
|
||||||
inIfLinux = false
|
|
||||||
inElse = false
|
|
||||||
ignore = false
|
|
||||||
|
|
||||||
#
|
|
||||||
# Read the file line by line
|
|
||||||
# and parse to find the class
|
|
||||||
# names and func names
|
|
||||||
#
|
|
||||||
File.readlines(fileName).each do |line|
|
|
||||||
if inIfLinux
|
|
||||||
if /\#else/.match(line)
|
|
||||||
inElse = true
|
|
||||||
ignore = true
|
|
||||||
else
|
|
||||||
if /\#end/.match(line)
|
|
||||||
inElse = false
|
|
||||||
inIfLinux = false
|
|
||||||
ignore = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if /\#if[ \t]+os\(Linux\)/.match(line)
|
|
||||||
inIfLinux = true
|
|
||||||
ignore = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
next if ignore
|
|
||||||
# Match class or func
|
|
||||||
match = line[/class[ \t]+[a-zA-Z0-9_]*(?=[ \t]*:[ \t]*XCTestCase)|func[ \t]+test[a-zA-Z0-9_]*(?=[ \t]*\(\))/, 0]
|
|
||||||
if match
|
|
||||||
|
|
||||||
if match[/class/, 0] == 'class'
|
|
||||||
className = match.sub(/^class[ \t]+/, '')
|
|
||||||
#
|
|
||||||
# Create a new class / func structure
|
|
||||||
# and add it to the classes array.
|
|
||||||
#
|
|
||||||
currentClass = [className, []]
|
|
||||||
classes << currentClass
|
|
||||||
else # Must be a func
|
|
||||||
funcName = match.sub(/^func[ \t]+/, '')
|
|
||||||
#
|
|
||||||
# Add each func name the the class / func
|
|
||||||
# structure created above.
|
|
||||||
#
|
|
||||||
currentClass[1] << funcName
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
classes
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Main routine
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
testsDirectory = 'Tests'
|
|
||||||
|
|
||||||
options = GetoptLong.new(['--tests-dir', GetoptLong::OPTIONAL_ARGUMENT])
|
|
||||||
options.quiet = true
|
|
||||||
|
|
||||||
begin
|
|
||||||
options.each do |option, value|
|
|
||||||
case option
|
|
||||||
when '--tests-dir'
|
|
||||||
testsDirectory = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue GetoptLong::InvalidOption
|
|
||||||
end
|
|
||||||
|
|
||||||
allTestSubDirectories = []
|
|
||||||
allFiles = []
|
|
||||||
|
|
||||||
Dir[testsDirectory + '/*'].each do |subDirectory|
|
|
||||||
next unless File.directory?(subDirectory)
|
|
||||||
directoryHasClasses = false
|
|
||||||
Dir[subDirectory + '/*Test{s,}.swift'].each do |fileName|
|
|
||||||
next unless File.file? fileName
|
|
||||||
fileClasses = parseSourceFile(fileName)
|
|
||||||
|
|
||||||
#
|
|
||||||
# If there are classes in the
|
|
||||||
# test source file, create an extension
|
|
||||||
# file for it.
|
|
||||||
#
|
|
||||||
next unless fileClasses.count > 0
|
|
||||||
createExtensionFile(fileName, fileClasses)
|
|
||||||
directoryHasClasses = true
|
|
||||||
allFiles << fileClasses
|
|
||||||
end
|
|
||||||
|
|
||||||
if directoryHasClasses
|
|
||||||
allTestSubDirectories << Pathname.new(subDirectory).split.last.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Last step is the create a LinuxMain.swift file that
|
|
||||||
# references all the classes and funcs in the source files.
|
|
||||||
#
|
|
||||||
if allFiles.count > 0
|
|
||||||
createLinuxMain(testsDirectory, allTestSubDirectories, allFiles)
|
|
||||||
end
|
|
||||||
# eof
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
##===----------------------------------------------------------------------===##
|
|
||||||
##
|
|
||||||
## This source file is part of the Swift Metrics API open source project
|
|
||||||
##
|
|
||||||
## Copyright (c) 2018-2019 Apple Inc. and the Swift Metrics API project authors
|
|
||||||
## Licensed under Apache License v2.0
|
|
||||||
##
|
|
||||||
## See LICENSE.txt for license information
|
|
||||||
## See CONTRIBUTORS.txt for the list of Swift Metrics API project authors
|
|
||||||
##
|
|
||||||
## SPDX-License-Identifier: Apache-2.0
|
|
||||||
##
|
|
||||||
##===----------------------------------------------------------------------===##
|
|
||||||
|
|
||||||
swift package --disable-sandbox preview-documentation --target "$1"
|
|
||||||
|
|
@ -1,169 +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 linux tests... "
|
|
||||||
FIRST_OUT="$(git status --porcelain)"
|
|
||||||
ruby "$here/../scripts/generate_linux_tests.rb" > /dev/null
|
|
||||||
SECOND_OUT="$(git status --porcelain)"
|
|
||||||
if [[ "$FIRST_OUT" != "$SECOND_OUT" ]]; then
|
|
||||||
printf "\033[0;31mmissing changes!\033[0m\n"
|
|
||||||
git --no-pager diff
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
printf "\033[0;32mokay.\033[0m\n"
|
|
||||||
fi
|
|
||||||
|
|
||||||
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... "
|
|
||||||
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