From d6550fe96aebc0ed9b9400fa5d0471647e0da3bf Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 6 Oct 2020 13:29:17 +0900 Subject: [PATCH] introducing "core" module --- Package.swift | 52 +- README.md | 209 +---- Sources/Baggage/BaggageContext.swift | 239 ------ Sources/Baggage/BaggageContextCarrier.swift | 54 -- Sources/BaggageBenchmarkTools/ArgParser.swift | 256 ------- .../BenchmarkCategory.swift | 28 - .../BenchmarkTools.swift | 230 ------ .../BaggageBenchmarkTools/DriverUtils.swift | 722 ------------------ Sources/BaggageBenchmarkTools/README_SWIFT.md | 6 - .../BaggagePassingBenchmarks.swift | 189 ----- Sources/BaggageBenchmarks/locks.swift | 253 ------ Sources/BaggageBenchmarks/main.swift | 41 - .../BaggageMetadataLogHandler.swift | 113 --- .../Logger+BaggageContext.swift | 29 - .../LoggingBaggageContextCarrier.swift | 44 -- Sources/CoreBaggage/Baggage.swift | 218 ++++++ Sources/CoreBaggage/BaggageKey.swift | 89 +++ ...ingBaggageContextCarrierTests+XCTest.swift | 34 - .../LoggingBaggageContextCarrierTests.swift | 145 ---- Tests/BaggageLoggingTests/TestLogger.swift | 337 -------- .../BaggageContextCarrierTests+XCTest.swift | 34 - .../BaggageContextCarrierTests.swift | 67 -- .../BaggageTests+XCTest.swift} | 12 +- .../BaggageTests.swift} | 56 +- Tests/LinuxMain.swift | 11 +- scripts/generate_linux_tests.rb | 4 +- scripts/sanity.sh | 4 +- scripts/validate_format.sh | 4 +- scripts/validate_license_headers.sh | 16 +- scripts/validate_naming.sh | 4 +- 30 files changed, 394 insertions(+), 3106 deletions(-) delete mode 100644 Sources/Baggage/BaggageContext.swift delete mode 100644 Sources/Baggage/BaggageContextCarrier.swift delete mode 100644 Sources/BaggageBenchmarkTools/ArgParser.swift delete mode 100644 Sources/BaggageBenchmarkTools/BenchmarkCategory.swift delete mode 100644 Sources/BaggageBenchmarkTools/BenchmarkTools.swift delete mode 100644 Sources/BaggageBenchmarkTools/DriverUtils.swift delete mode 100644 Sources/BaggageBenchmarkTools/README_SWIFT.md delete mode 100644 Sources/BaggageBenchmarks/BaggagePassingBenchmarks.swift delete mode 100644 Sources/BaggageBenchmarks/locks.swift delete mode 100644 Sources/BaggageBenchmarks/main.swift delete mode 100644 Sources/BaggageLogging/BaggageMetadataLogHandler.swift delete mode 100644 Sources/BaggageLogging/Logger+BaggageContext.swift delete mode 100644 Sources/BaggageLogging/LoggingBaggageContextCarrier.swift create mode 100644 Sources/CoreBaggage/Baggage.swift create mode 100644 Sources/CoreBaggage/BaggageKey.swift delete mode 100644 Tests/BaggageLoggingTests/LoggingBaggageContextCarrierTests+XCTest.swift delete mode 100644 Tests/BaggageLoggingTests/LoggingBaggageContextCarrierTests.swift delete mode 100644 Tests/BaggageLoggingTests/TestLogger.swift delete mode 100644 Tests/BaggageTests/BaggageContextCarrierTests+XCTest.swift delete mode 100644 Tests/BaggageTests/BaggageContextCarrierTests.swift rename Tests/{BaggageTests/BaggageContextTests+XCTest.swift => CoreBaggageTests/BaggageTests+XCTest.swift} (75%) rename Tests/{BaggageTests/BaggageContextTests.swift => CoreBaggageTests/BaggageTests.swift} (50%) diff --git a/Package.swift b/Package.swift index f7b8008..79d6508 100644 --- a/Package.swift +++ b/Package.swift @@ -2,70 +2,32 @@ import PackageDescription let package = Package( - name: "swift-baggage-context", + name: "swift-distributed-tracing-baggage-core", products: [ .library( - name: "Baggage", + name: "CoreBaggage", targets: [ - "Baggage", - ] - ), - .library( - name: "BaggageLogging", - targets: [ - "BaggageLogging", + "CoreBaggage", ] ), ], dependencies: [ - .package(url: "https://github.com/apple/swift-log.git", from: "1.3.0"), + // no dependencies ], targets: [ .target( - name: "Baggage", + name: "CoreBaggage", dependencies: [] ), - .target( - name: "BaggageLogging", - dependencies: [ - "Baggage", - .product(name: "Logging", package: "swift-log"), - ] - ), - // ==== -------------------------------------------------------------------------------------------------------- // MARK: Tests .testTarget( - name: "BaggageTests", - dependencies: [ - "Baggage", - ] - ), - - .testTarget( - name: "BaggageLoggingTests", - dependencies: [ - "Baggage", - "BaggageLogging", - ] - ), - - // ==== -------------------------------------------------------------------------------------------------------- - // MARK: Performance / Benchmarks - - .target( - name: "BaggageBenchmarks", + name: "CoreBaggageTests", dependencies: [ - "Baggage", - "BaggageLogging", - "BaggageBenchmarkTools", + "CoreBaggage", ] ), - .target( - name: "BaggageBenchmarkTools", - dependencies: [] - ), ] ) diff --git a/README.md b/README.md index 99464b3..2303759 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,22 @@ -# Baggage Context +# 🧳 Distributed Tracing Baggage Core -[![Swift 5.2](https://img.shields.io/badge/Swift-5.2-ED523F.svg?style=flat)](https://swift.org/download/) -[![Swift 5.1](https://img.shields.io/badge/Swift-5.1-ED523F.svg?style=flat)](https://swift.org/download/) -[![Swift 5.0](https://img.shields.io/badge/Swift-5.0-ED523F.svg?style=flat)](https://swift.org/download/) -[![CI](https://github.com/slashmo/gsoc-swift-baggage-context/workflows/CI/badge.svg)](https://github.com/slashmo/gsoc-swift-baggage-context/actions?query=workflow%3ACI) +`Baggage` is a minimal (zero-dependency) context propagation container, intended to "carry" baggage items +for purposes of cross-cutting tools to be built on top of it. -`BaggageContext` is a minimal (zero-dependency) "context" library meant to "carry" baggage (metadata) for cross-cutting -tools such as tracers. It is purposefully not tied to any specific use-case (in the spirit of the -[Tracing Plane paper](https://cs.brown.edu/~jcmace/papers/mace18universal.pdf)'s BaggageContext). However, it should -enable a vast majority of use cases cross-cutting tools need to support. Unlike mentioned in the paper, our -`BaggageContext` does not implement its own serialization scheme (today). +It is modeled after the concepts explained in [W3C Baggage](https://w3c.github.io/baggage/) and the +in the spirit of [Tracing Plane](https://cs.brown.edu/~jcmace/papers/mace18universal.pdf) baggage context type, + although by itself it does not define a specific serialization format. + +Please refer to [Swift Distributed Tracing Baggage](https://github.com/apple/swift-distributed-tracing-baggage) +and [Swift Distributed Tracing](https://github.com/apple/swift-distributed-tracing) for usage guides of this type. -See https://github.com/slashmo/gsoc-swift-tracing for actual instrument types and implementations which can be used to -deploy various cross-cutting instruments all reusing the same baggage type. More information can be found in the -[SSWG meeting notes](https://gist.github.com/ktoso/4d160232407e4d5835b5ba700c73de37#swift-baggage-context--distributed-tracing). + +## Dependencies + +It should be noted that most libraries and frameworks do NOT need to depend on this package explicitly, +but rather should depend on [Swift Distributed Tracing Baggage](https://github.com/apple/swift-distributed-tracing-baggage) or [Swift Distributed Tracing](https://github.com/apple/swift-distributed-tracing) which will pull the +`CoreBaggage` via their transitive dependencies. This package and the `CoreBaggage` module exist only for +libraries which want to maintain an absolutely minimal dependency footprint, and e.g. do not want to depend on `Logging` modules, which the higher level packages may do. ## Installation @@ -23,178 +26,30 @@ so that's what you'd import in your Swift files. ```swift dependencies: [ .package( - name: "swift-baggage-context", - url: "https://github.com/slashmo/gsoc-swift-baggage-context.git", + name: "swift-baggage-context-core", + url: "https://github.com/apple/swift-distributed-tracing-baggage-core.git", from: "0.3.0" ) ] ``` -## Usage - -`BaggageContext` is intended to be used in conjunction with the instrumentation of distributed systems. To make this -instrumentation work, all parties involved operate on the same `BaggageContext` type. These are the three common -parties, in no specific order, and guidance on how to use `BaggageContext`: - -### End Users - explicit context passing - -You'll likely interact with some API that takes a context. In most cases you already have a context at hand so you -should pass that along. If you're certain you don't have a context at hand, pass along an empty one after thinking about -why that's the case. - -**TODO**: Document the reasoning behind `.background` & `.TODO` once merged ([#26](#26)) - -While this might seem like a burden to take on, this will allow you to immediately add instrumentation (e.g. tracing) -once your application grows. Let's say your profiling some troublesome performance regressions. You won't have the time -to go through the entire system to start passing contexts around. - -> TL;DR: You should always pass around `BaggageContext`, so that you're ready for when you need it. - -Once you are ready to instrument your application, you already have everything in place to get going. Instead of each -instrument operating on its own context type they'll be using the same `BaggageContext` that you're already passing -around to the various instrumentable libraries & frameworks you make use of, so you're free to mix & match any -compatible instrument(s) 🙌 Check out the [swift-tracing](https://github.com/slashmo/gsoc-swift-tracing) repository for -instructions on how to get up & running. - -### Library & Framework Authors - passing context and instrumenting libraries - -Developers creating frameworks/libraries (e.g. NIO, gRPC, AsyncHTTPClient, ...) which benefit from being instrumented -should adopt `BaggageContext` as part of their public API. AsyncHTTPClient for example might accept a context like this: - -```swift -let context = BaggageContext() -client.get(url: "https://swift.org", context: context) +and depend on the module: + +```swift +targets: [ + .target( + name: "MyAwesomeApp", + dependencies: [ + "CoreBaggage", + ] + ), + // ... +] ``` -For more information on where to place this argument and how to name it, take a look at the -[Context-Passing Guidelines](#Context-Passing-Guidelines). - -Generally speaking, frameworks and libraries should treat baggage as an _opaque container_ and simply thread it along -all asynchronous boundaries a call may have to go through. Libraries and frameworks should not attempt to reuse context -as a means of passing values that they need for "normal" operation. - -At cross-cutting boundaries, e.g. right before sending an HTTP -request, they inject the `BaggageContext` into the HTTP headers, allowing context propagation. On the receiving side, an -HTTP server should extract the request headers into a `BaggageContext`. Injecting/extracting is part of the -`swift-tracing` libraries [and documented in its own repository](https://github.com/slashmo/gsoc-swift-tracing). - -### Instrumentation Authors - defining, injecting and extracting baggage - -When implementing instrumentation for cross-cutting tools, `BaggageContext` becomes the way you propagate metadata such -as trace ids. Because each instrument knows what values might be added to the `BaggageContext` they are the ones -creating `BaggageContextKey` types dictating the type of value associated with each key added to the context. To make -accessing values a bit more convenient, we encourage you to add computed properties to `BaggageContextProtocol`: - -```swift -private enum TraceIDKey: BaggageContextKey { - typealias Value = String -} - -extension BaggageContextProtocol { - var traceID: String? { - get { - return self[TraceIDKey.self] - } - set { - self[TraceIDKey.self] = newValue - } - } -} - -var context = BaggageContext() -context.traceID = "4bf92f3577b34da6a3ce929d0e0e4736" -print(context.traceID ?? "new trace id") -``` +## Usage -## Context-Passing Guidelines - -For context-passing to feel consistent and Swifty among all server-side (and not only) libraries and frameworks -aiming to adopt `BaggageContext` (or any of its uses, such as Distributed Tracing), we suggest the following set of -guidelines: - -### Argument naming/positioning - -Propagating baggage context through your system is to be done explicitly, meaning as a parameter in function calls, -following the "flow" of execution. - -When passing baggage context explicitly we strongly suggest sticking to the following style guideline: - -- Assuming the general parameter ordering of Swift function is as follows (except DSL exceptions): - 1. Required non-function parameters (e.g. `(url: String)`), - 2. Defaulted non-function parameters (e.g. `(mode: Mode = .default)`), - 3. Required function parameters, including required trailing closures (e.g. `(onNext elementHandler: (Value) -> ())`), - 4. Defaulted function parameters, including optional trailing closures (e.g. `(onComplete completionHandler: (Reason) -> ()) = { _ in }`). -- Baggage Context should be passed as **the last parameter in the required non-function parameters group in a function declaration**. - -This way when reading the call side, users of these APIs can learn to "ignore" or "skim over" the context parameter and -the method signature remains human-readable and “Swifty”. - -Examples: - -- `func request(_ url: URL,` **`context: BaggageContext`** `)`, which may be called as `httpClient.request(url, context: context)` -- `func handle(_ request: RequestObject,` **`context: BaggageContextCarrier`** `)` - - if a "framework context" exists and _carries_ the baggage context already, it is permitted to pass that context - together with the baggage; - - it is _strongly recommended_ to store the baggage context as `baggage` property of `FrameworkContext` in such cases, - in order to avoid the confusing spelling of `context.context`, and favoring the self-explanatory `context.baggage` - spelling when the baggage is contained in a framework context object. -- `func receiveMessage(_ message: Message, context: FrameworkContext)` -- `func handle(element: Element,` **`context: BaggageContextCarrier`** `, settings: Settings? = nil)` - - before any defaulted non-function parameters -- `func handle(element: Element,` **`context: BaggageContextCarrier`** `, settings: Settings? = nil, onComplete: () -> ())` - - before defaulted parameters, which themselfes are before required function parameters -- `func handle(element: Element,` **`context: BaggageContextCarrier`** `, onError: (Error) -> (), onComplete: (() -> ())? = nil)` - -In case there are _multiple_ "framework-ish" parameters, such as passing a NIO `EventLoop` or similar, we suggest: - -- `func perform(_ work: Work, for user: User,` _`frameworkThing: Thing, eventLoop: NIO.EventLoop,`_ **`context: BaggageContext`** `)` - - pass the baggage as **last** of such non-domain specific parameters as it will be _by far more_ omnipresent than any - specific framework parameter - as it is expected that any framework should be accepting a context if it can do so. - While not all libraries are necessarily going to be implemented using the same frameworks. - -We feel it is important to preserve Swift's human-readable nature of function definitions. In other words, we intend to -keep the read-out-loud phrasing of methods to remain _"request that URL (ignore reading out loud the context parameter)"_ -rather than _"request (ignore this context parameter when reading) that URL"_. - -#### When to use what context type? - -This library defines the following context (carrier) types: - -- `struct BaggageContext` - which is the actual context object, -- `protocol BaggageContextCarrier` - which should be used whenever a library implements an API and does not necessarily -care where it gets a `context` value from - - this pattern enables other frameworks to pass their `FrameworkContext`, like so: - `get(context: MyFrameworkContext())` if they already have such context in scope (e.g. Vapor's `Request` object is a - good example, or Lambda Runtime's `Lambda.Context` -- `protocol LoggingBaggageContextCarrier` - which in addition exposes a logger bound to the passed context - -Finally, some frameworks will have APIs which accept the specific `MyFrameworkContext`, withing frameworks specifically -a lot more frequently than libraries one would hope. It is important when designing APIs to keep in mind -- can this API -work with any context, or is it always going to require _my framework context_, and erring on accepting the most -general type possible. - -#### Existing context argument - -When adapting an existing library/framework to support `BaggageContext` and it already has a "framework context" which -is expected to be passed through "everywhere", we suggest to follow these guidelines for adopting BaggageContext: - -1. Add a `BaggageContext` as a property called `baggage` to your own `context` type, so that the call side for your -users becomes `context.baggage` (rather than the confusing `context.context`) -2. If you cannot or it would not make sense to carry baggage inside your framework's context object, -pass (and accept (!)) the `BaggageContext` in your framework functions like follows: - - if they take no framework context, accept a `context: BaggageContext` which is the same guideline as for all other - cases - - if they already _must_ take a context object and you are out of words (or your API already accepts your framework - context as "context"), pass the baggage as **last** parameter (see above) yet call the parameter `baggage` to - disambiguate your `context` object from the `baggage` context object. - -Examples: - -- `Lamda.Context` may contain `baggage` and this way offer traceIDs and other values - - passing context to a `Lambda.Context` unaware library becomes: `http.request(url: "...", context: context.baggage)`. - - TODO: We are considering a protocol which would simplify this if it is known that Lambda.Context "carries" baggage... -- `ChannelHandlerContext` offers a way to set/get baggage on the underlying channel via `context.baggage = ...` - - WorkInProgress, see: https://github.com/apple/swift-nio/pull/1574 +Please refer to [Swift Distributed Tracing Baggage](https://github.com/apple/swift-distributed-tracing-baggage) for the intended usage. ## Contributing diff --git a/Sources/Baggage/BaggageContext.swift b/Sources/Baggage/BaggageContext.swift deleted file mode 100644 index 9a7fc7a..0000000 --- a/Sources/Baggage/BaggageContext.swift +++ /dev/null @@ -1,239 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -/// A `BaggageContext` is a heterogeneous storage type with value semantics for keyed values in a type-safe -/// fashion. Its values are uniquely identified via `BaggageContextKey`s. These keys also dictate the type of -/// value allowed for a specific key-value pair through their associated type `Value`. -/// -/// ## Subscript access -/// You may access the stored values by subscripting with a key type conforming to `BaggageContextKey`. -/// -/// enum TestIDKey: BaggageContextKey { -/// typealias Value = String -/// } -/// -/// var context = BaggageContext.background -/// // set a new value -/// context[TestIDKey.self] = "abc" -/// // retrieve a stored value -/// context[TestIDKey.self] ?? "default" -/// // remove a stored value -/// context[TestIDKey.self] = nil -/// -/// ## Convenience extensions -/// -/// Libraries may also want to provide an extension, offering the values that users are expected to reach for -/// using the following pattern: -/// -/// extension BaggageContextProtocol { -/// var testID: TestIDKey.Value? { -/// get { -/// self[TestIDKey.self] -/// } set { -/// self[TestIDKey.self] = newValue -/// } -/// } -/// } -public struct BaggageContext: BaggageContextProtocol { - private var _storage = [AnyBaggageContextKey: Any]() - - /// Internal on purpose, please use `TODO` or `.background` to create an "empty" context, - /// which carries more meaning to other developers why an empty context was used. - init() {} - - public subscript(_ key: Key.Type) -> Key.Value? { - get { - guard let value = self._storage[AnyBaggageContextKey(key)] else { return nil } - // safe to force-cast as this subscript is the only way to set a value. - return (value as! Key.Value) - } set { - self._storage[AnyBaggageContextKey(key)] = newValue - } - } - - public func forEach(_ body: (AnyBaggageContextKey, Any) throws -> Void) rethrows { - try self._storage.forEach { key, value in - try body(key, value) - } - } -} - -extension BaggageContext: CustomStringConvertible { - /// A context's description prints only keys of the contained values. - /// This is in order to prevent spilling a lot of detailed information of carried values accidentally. - /// - /// `BaggageContext`s are not intended to be printed "raw" but rather inter-operate with tracing, logging and other systems, - /// which can use the `forEach` function providing access to its underlying values. - public var description: String { - return "\(type(of: self).self)(keys: \(self._storage.map { $0.key.name }))" - } -} - -public protocol BaggageContextProtocol { - /// Provides type-safe access to the baggage's values. - /// - /// Rather than using this subscript directly, users are encouraged to offer a convenience accessor to their values, - /// using the following pattern: - /// - /// extension BaggageContextProtocol { - /// var testID: TestIDKey.Value? { - /// get { - /// self[TestIDKey.self] - /// } set { - /// self[TestIDKey.self] = newValue - /// } - /// } - /// } - subscript(_ key: Key.Type) -> Key.Value? { get set } - - /// Calls the given closure on each key/value pair in the `BaggageContext`. - /// - /// - Parameter body: A closure invoked with the type erased key and value stored for the key in this baggage. - func forEach(_ body: (AnyBaggageContextKey, Any) throws -> Void) rethrows -} - -// ==== ------------------------------------------------------------------------ -// MARK: Baggage keys - -/// `BaggageContextKey`s are used as keys in a `BaggageContext`. Their associated type `Value` guarantees type-safety. -/// To give your `BaggageContextKey` an explicit name you may override the `name` property. -/// -/// In general, `BaggageContextKey`s should be `internal` to the part of a system using it. It is strongly recommended to do -/// convenience extensions on `BaggageContextProtocol`, using the keys directly is considered an anti-pattern. -/// -/// extension BaggageContextProtocol { -/// var testID: TestIDKey.Value? { -/// get { -/// self[TestIDKey.self] -/// } set { -/// self[TestIDKey.self] = newValue -/// } -/// } -/// } -public protocol BaggageContextKey { - /// The type of `Value` uniquely identified by this key. - associatedtype Value - - /// The human-readable name of this key. Defaults to `nil`. - static var name: String? { get } -} - -extension BaggageContextKey { - public static var name: String? { return nil } -} - -/// A type-erased `BaggageContextKey` used when iterating through the `BaggageContext` using its `forEach` method. -public struct AnyBaggageContextKey { - /// The key's type represented erased to an `Any.Type`. - public let keyType: Any.Type - - private let _name: String? - - /// A human-readable String representation of the underlying key. - /// If no explicit name has been set on the wrapped key the type name is used. - public var name: String { - return self._name ?? String(describing: self.keyType.self) - } - - init(_ keyType: Key.Type) where Key: BaggageContextKey { - self.keyType = keyType - self._name = keyType.name - } -} - -extension AnyBaggageContextKey: Hashable { - public static func == (lhs: AnyBaggageContextKey, rhs: AnyBaggageContextKey) -> Bool { - return ObjectIdentifier(lhs.keyType) == ObjectIdentifier(rhs.keyType) - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(ObjectIdentifier(self.keyType)) - } -} - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Background BaggageContext - -extension BaggageContextProtocol { - /// An empty baggage context intended as the "root" or "initial" baggage context background processing tasks, or as the "root" baggage context. - /// - /// It is never canceled, has no values, and has no deadline. - /// It is typically used by the main function, initialization, and tests, and as the top-level Context for incoming requests. - /// - /// ### Usage in frameworks and libraries - /// This function is really only intended to be used frameworks and libraries, at the "top-level" where a request's, - /// message's or task's processing is initiated. For example, a framework handling requests, should create an empty - /// context when handling a request only to immediately populate it with useful trace information extracted from e.g. - /// request headers. - /// - /// ### Usage in applications - /// Application code should never have to create an empty context during the processing lifetime of any request, - /// and only should create contexts if some processing is performed in the background - thus the naming of this property. - /// - /// Usually, a framework such as an HTTP server or similar "request handler" would already provide users - /// with a context to be passed along through subsequent calls. - /// - /// If unsure where to obtain a context from, prefer using `.TODO("Not sure where I should get a context from here?")`, - /// such that other developers are informed that the lack of context was not done on purpose, but rather because either - /// not being sure where to obtain a context from, or other framework limitations -- e.g. the outer framework not being - /// context aware just yet. - public static var background: BaggageContext { - return BaggageContext() - } -} - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: "TO DO" BaggageContext - -extension BaggageContextProtocol { - /// A baggage context intended as a placeholder until a real value can be passed through a function call. - /// - /// It should ONLY be used while prototyping or when the passing of the proper context is not yet possible, - /// e.g. because an external library did not pass it correctly and has to be fixed before the proper context - /// can be obtained where the TO-DO is currently used. - /// - /// ### Crashing on TO-DO context creation - /// You may set the `BAGGAGE_CRASH_TODOS` variable while compiling a project in order to make calls to this function crash - /// with a fatal error, indicating where a to-do baggage context was used. This comes in handy when wanting to ensure that - /// a project never ends up using with code initially was written as "was lazy, did not pass context", yet the - /// project requires context passing to be done correctly throughout the application. Similar checks can be performed - /// at compile time easily using linters (not yet implemented), since it is always valid enough to detect a to-do context - /// being passed as illegal and warn or error when spotted. - /// - /// - Parameters: - /// - reason: Informational reason for developers, why a placeholder context was used instead of a proper one, - /// - Returns: Empty "to-do" baggage context which should be eventually replaced with a carried through one, or `background`. - public static func TODO(_ reason: StaticString? = "", function: String = #function, file: String = #file, line: UInt = #line) -> BaggageContext { - var context = BaggageContext.background - #if BAGGAGE_CRASH_TODOS - fatalError("BAGGAGE_CRASH_TODOS: at \(file):\(line) (function \(function)), reason: \(reason)") - #else - context[TODOKey.self] = .init(file: file, line: line) - return context - #endif - } -} - -internal enum TODOKey: BaggageContextKey { - typealias Value = TODOLocation - static var name: String? { - return "todo" - } -} - -/// Carried automatically by a "to do" baggage context. -/// It can be used to track where a context originated and which "to do" context must be fixed into a real one to avoid this. -public struct TODOLocation { - let file: String - let line: UInt -} diff --git a/Sources/Baggage/BaggageContextCarrier.swift b/Sources/Baggage/BaggageContextCarrier.swift deleted file mode 100644 index 6c075d2..0000000 --- a/Sources/Baggage/BaggageContextCarrier.swift +++ /dev/null @@ -1,54 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Framework Context Protocols - -/// Framework context protocols may conform to this protocol if they are used to carry a baggage object. -/// -/// Notice that the baggage context property is spelled as `baggage`, this is purposefully designed in order to read well -/// with framework context's which often will be passed as `context: FrameworkContext` and used as `context.baggage`. -/// -/// Such carrier protocol also conforms to `BaggageContextProtocol` meaning that it has the same convenient accessors -/// as the actual baggage type. Users should be able to use the `context.myValue` the same way if a raw baggage context, -/// or a framework context was passed around as `context` parameter, allowing for easier migrations between those two when needed. -public protocol BaggageContextCarrier: BaggageContextProtocol { - /// The underlying `BaggageContext`. - var baggage: BaggageContext { get set } -} - -extension BaggageContextCarrier { - public subscript(baggageKey: Key.Type) -> Key.Value? { - get { - return self.baggage[baggageKey] - } set { - self.baggage[baggageKey] = newValue - } - } - - public func forEach(_ body: (AnyBaggageContextKey, Any) throws -> Void) rethrows { - try self.baggage.forEach(body) - } -} - -/// A baggage itself also is a carrier of _itself_. -extension BaggageContext: BaggageContextCarrier { - public var baggage: BaggageContext { - get { - return self - } - set { - self = newValue - } - } -} diff --git a/Sources/BaggageBenchmarkTools/ArgParser.swift b/Sources/BaggageBenchmarkTools/ArgParser.swift deleted file mode 100644 index b20eddb..0000000 --- a/Sources/BaggageBenchmarkTools/ArgParser.swift +++ /dev/null @@ -1,256 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -//===--- ArgParse.swift ---------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import Foundation - -enum ArgumentError: Error { - case missingValue(String) - case invalidType(value: String, type: String, argument: String?) - case unsupportedArgument(String) -} - -extension ArgumentError: CustomStringConvertible { - public var description: String { - switch self { - case .missingValue(let key): - return "missing value for '\(key)'" - case .invalidType(let value, let type, let argument): - return (argument == nil) - ? "'\(value)' is not a valid '\(type)'" - : "'\(value)' is not a valid '\(type)' for '\(argument!)'" - case .unsupportedArgument(let argument): - return "unsupported argument '\(argument)'" - } - } -} - -/// Type-checked parsing of the argument value. -/// -/// - Returns: Typed value of the argument converted using the `parse` function. -/// -/// - Throws: `ArgumentError.invalidType` when the conversion fails. -func checked( - _ parse: (String) throws -> T?, - _ value: String, - argument: String? = nil -) throws -> T { - if let t = try parse(value) { return t } - var type = "\(T.self)" - if type.starts(with: "Optional<") { - let s = type.index(after: type.firstIndex(of: "<")!) - let e = type.index(before: type.endIndex) // ">" - type = String(type[s ..< e]) // strip Optional< > - } - throw ArgumentError.invalidType( - value: value, type: type, argument: argument - ) -} - -/// Parser that converts the program's command line arguments to typed values -/// according to the parser's configuration, storing them in the provided -/// instance of a value-holding type. -class ArgumentParser { - private var result: U - private var validOptions: [String] { - return self.arguments.compactMap { $0.name } - } - - private var arguments: [Argument] = [] - private let programName: String = { - // Strip full path from the program name. - let r = CommandLine.arguments[0].reversed() - let ss = r[r.startIndex ..< (r.firstIndex(of: "/") ?? r.endIndex)] - return String(ss.reversed()) - }() - - private var positionalArgs = [String]() - private var optionalArgsMap = [String: String]() - - /// Argument holds the name of the command line parameter, its help - /// desciption and a rule that's applied to process it. - /// - /// The the rule is typically a value processing closure used to convert it - /// into given type and storing it in the parsing result. - /// - /// See also: addArgument, parseArgument - struct Argument { - let name: String? - let help: String? - let apply: () throws -> Void - } - - /// ArgumentParser is initialized with an instance of a type that holds - /// the results of the parsing of the individual command line arguments. - init(into result: U) { - self.result = result - self.arguments += [ - Argument( - name: "--help", help: "show this help message and exit", - apply: self.printUsage - ), - ] - } - - private func printUsage() { - guard let _ = optionalArgsMap["--help"] else { return } - let space = " " - let maxLength = self.arguments.compactMap { $0.name?.count }.max()! - let padded = { (s: String) in - " \(s)\(String(repeating: space, count: maxLength - s.count)) " - } - let f: (String, String) -> String = { - "\(padded($0))\($1)" - .split(separator: "\n") - .joined(separator: "\n" + padded("")) - } - let positional = f("TEST", "name or number of the benchmark to measure") - let optional = self.arguments.filter { $0.name != nil } - .map { f($0.name!, $0.help ?? "") } - .joined(separator: "\n") - print( - """ - usage: \(self.programName) [--argument=VALUE] [TEST [TEST ...]] - positional arguments: - \(positional) - optional arguments: - \(optional) - """) - exit(0) - } - - /// Parses the command line arguments, returning the result filled with - /// specified argument values or report errors and exit the program if - /// the parsing fails. - public func parse() -> U { - do { - try self.parseArgs() // parse the argument syntax - try self.arguments.forEach { try $0.apply() } // type-check and store values - return self.result - } catch let error as ArgumentError { - fputs("error: \(error)\n", stderr) - exit(1) - } catch { - fflush(stdout) - fatalError("\(error)") - } - } - - /// Using CommandLine.arguments, parses the structure of optional and - /// positional arguments of this program. - /// - /// We assume that optional switch args are of the form: - /// - /// --opt-name[=opt-value] - /// -opt-name[=opt-value] - /// - /// with `opt-name` and `opt-value` not containing any '=' signs. Any - /// other option passed in is assumed to be a positional argument. - /// - /// - Throws: `ArgumentError.unsupportedArgument` on failure to parse - /// the supported argument syntax. - private func parseArgs() throws { - // For each argument we are passed... - for arg in CommandLine.arguments[1 ..< CommandLine.arguments.count] { - // If the argument doesn't match the optional argument pattern. Add - // it to the positional argument list and continue... - if !arg.starts(with: "-") { - self.positionalArgs.append(arg) - continue - } - // Attempt to split it into two components separated by an equals sign. - let components = arg.split(separator: "=") - let optionName = String(components[0]) - guard self.validOptions.contains(optionName) else { - throw ArgumentError.unsupportedArgument(arg) - } - var optionVal: String - switch components.count { - case 1: optionVal = "" - case 2: optionVal = String(components[1]) - default: - // If we do not have two components at this point, we can not have - // an option switch. This is an invalid argument. Bail! - throw ArgumentError.unsupportedArgument(arg) - } - self.optionalArgsMap[optionName] = optionVal - } - } - - /// Add a rule for parsing the specified argument. - /// - /// Stores the type-erased invocation of the `parseArgument` in `Argument`. - /// - /// Parameters: - /// - name: Name of the command line argument. E.g.: `--opt-arg`. - /// `nil` denotes positional arguments. - /// - property: Property on the `result`, to store the value into. - /// - defaultValue: Value used when the command line argument doesn't - /// provide one. - /// - help: Argument's description used when printing usage with `--help`. - /// - parser: Function that converts the argument value to given type `T`. - public func addArgument( - _ name: String?, - _ property: WritableKeyPath, - defaultValue: T? = nil, - help: String? = nil, - parser: @escaping (String) throws -> T? = { _ in nil } - ) { - self.arguments.append( - Argument(name: name, help: help) - { try self.parseArgument(name, property, defaultValue, parser) } - ) - } - - /// Process the specified command line argument. - /// - /// For optional arguments that have a value we attempt to convert it into - /// given type using the supplied parser, performing the type-checking with - /// the `checked` function. - /// If the value is empty the `defaultValue` is used instead. - /// The typed value is finally stored in the `result` into the specified - /// `property`. - /// - /// For the optional positional arguments, the [String] is simply assigned - /// to the specified property without any conversion. - /// - /// See `addArgument` for detailed parameter descriptions. - private func parseArgument( - _ name: String?, - _ property: WritableKeyPath, - _ defaultValue: T?, - _ parse: (String) throws -> T? - ) throws { - if let name = name, let value = optionalArgsMap[name] { - guard !value.isEmpty || defaultValue != nil - else { throw ArgumentError.missingValue(name) } - - self.result[keyPath: property] = value.isEmpty - ? defaultValue! - : try checked(parse, value, argument: name) - } else if name == nil { - self.result[keyPath: property] = self.positionalArgs as! T - } - } -} diff --git a/Sources/BaggageBenchmarkTools/BenchmarkCategory.swift b/Sources/BaggageBenchmarkTools/BenchmarkCategory.swift deleted file mode 100644 index 244d3ca..0000000 --- a/Sources/BaggageBenchmarkTools/BenchmarkCategory.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// -// Based on: https://github.com/apple/swift/tree/cf53143a47278c2a465409a67376642515956777/benchmark/utils -// -//===----------------------------------------------------------------------===// - -public enum BenchmarkCategory: String { - // Most benchmarks are assumed to be "stable" and will be regularly tracked at - // each commit. A handful may be marked unstable if continually tracking them is - // counterproductive. - case unstable - - // Explicit skip marker - case skip -} diff --git a/Sources/BaggageBenchmarkTools/BenchmarkTools.swift b/Sources/BaggageBenchmarkTools/BenchmarkTools.swift deleted file mode 100644 index 79acf4d..0000000 --- a/Sources/BaggageBenchmarkTools/BenchmarkTools.swift +++ /dev/null @@ -1,230 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// -// Based on: https://github.com/apple/swift/tree/cf53143a47278c2a465409a67376642515956777/benchmark/utils -// -//===----------------------------------------------------------------------===// - -#if os(Linux) -import Glibc -#else -import Darwin -#endif - -extension BenchmarkCategory: CustomStringConvertible { - public var description: String { - return self.rawValue - } -} - -extension BenchmarkCategory: Comparable { - public static func < (lhs: BenchmarkCategory, rhs: BenchmarkCategory) -> Bool { - return lhs.rawValue < rhs.rawValue - } -} - -public struct BenchmarkPlatformSet: OptionSet { - public let rawValue: Int - - public init(rawValue: Int) { - self.rawValue = rawValue - } - - public static let darwin = BenchmarkPlatformSet(rawValue: 1 << 0) - public static let linux = BenchmarkPlatformSet(rawValue: 1 << 1) - - public static var currentPlatform: BenchmarkPlatformSet { - #if os(Linux) - return .linux - #else - return .darwin - #endif - } - - public static var allPlatforms: BenchmarkPlatformSet { - return [.darwin, .linux] - } -} - -public struct BenchmarkInfo { - /// The name of the benchmark that should be displayed by the harness. - public var name: String - - /// Shadow static variable for runFunction. - private var _runFunction: (Int) -> Void - - /// A function that invokes the specific benchmark routine. - public var runFunction: ((Int) -> Void)? { - if !self.shouldRun { - return nil - } - return self._runFunction - } - - /// A set of category tags that describe this benchmark. This is used by the - /// harness to allow for easy slicing of the set of benchmarks along tag - /// boundaries, e.x.: run all string benchmarks or ref count benchmarks, etc. - public var tags: Set - - /// The platforms that this benchmark supports. This is an OptionSet. - private var unsupportedPlatforms: BenchmarkPlatformSet - - /// Shadow variable for setUpFunction. - private var _setUpFunction: (() -> Void)? - - /// An optional function that if non-null is run before benchmark samples - /// are timed. - public var setUpFunction: (() -> Void)? { - if !self.shouldRun { - return nil - } - return self._setUpFunction - } - - /// Shadow static variable for computed property tearDownFunction. - private var _tearDownFunction: (() -> Void)? - - /// An optional function that if non-null is run after samples are taken. - public var tearDownFunction: (() -> Void)? { - if !self.shouldRun { - return nil - } - return self._tearDownFunction - } - - public var legacyFactor: Int? - - public init( - name: String, runFunction: @escaping (Int) -> Void, tags: [BenchmarkCategory], - setUpFunction: (() -> Void)? = nil, - tearDownFunction: (() -> Void)? = nil, - unsupportedPlatforms: BenchmarkPlatformSet = [], - legacyFactor: Int? = nil - ) { - self.name = name - self._runFunction = runFunction - self.tags = Set(tags) - self._setUpFunction = setUpFunction - self._tearDownFunction = tearDownFunction - self.unsupportedPlatforms = unsupportedPlatforms - self.legacyFactor = legacyFactor - } - - /// Returns true if this benchmark should be run on the current platform. - var shouldRun: Bool { - return !self.unsupportedPlatforms.contains(.currentPlatform) - } -} - -extension BenchmarkInfo: Comparable { - public static func < (lhs: BenchmarkInfo, rhs: BenchmarkInfo) -> Bool { - return lhs.name < rhs.name - } - - public static func == (lhs: BenchmarkInfo, rhs: BenchmarkInfo) -> Bool { - return lhs.name == rhs.name - } -} - -extension BenchmarkInfo: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(self.name) - } -} - -// Linear function shift register. -// -// This is just to drive benchmarks. I don't make any claim about its -// strength. According to Wikipedia, it has the maximal period for a -// 32-bit register. -struct LFSR { - // Set the register to some seed that I pulled out of a hat. - var lfsr: UInt32 = 0xB789_78E7 - - mutating func shift() { - self.lfsr = (self.lfsr >> 1) ^ (UInt32(bitPattern: -Int32(self.lfsr & 1)) & 0xD000_0001) - } - - mutating func randInt() -> Int64 { - var result: UInt32 = 0 - for _ in 0 ..< 32 { - result = (result << 1) | (self.lfsr & 1) - self.shift() - } - return Int64(bitPattern: UInt64(result)) - } -} - -var lfsrRandomGenerator = LFSR() - -// Start the generator from the beginning -public func SRand() { - lfsrRandomGenerator = LFSR() -} - -public func Random() -> Int64 { - return lfsrRandomGenerator.randInt() -} - -@inlinable // FIXME(inline-always) -@inline(__always) -public func CheckResults( - _ resultsMatch: Bool, - file: StaticString = #file, - function: StaticString = #function, - line: Int = #line -) { - guard _fastPath(resultsMatch) else { - print("Incorrect result in \(function), \(file):\(line)") - abort() - } -} - -public func False() -> Bool { return false } - -/// This is a dummy protocol to test the speed of our protocol dispatch. -public protocol SomeProtocol { func getValue() -> Int } -struct MyStruct: SomeProtocol { - init() {} - func getValue() -> Int { return 1 } -} - -public func someProtocolFactory() -> SomeProtocol { return MyStruct() } - -// Just consume the argument. -// It's important that this function is in another module than the tests -// which are using it. -@inline(never) -public func blackHole(_: T) {} - -// Return the passed argument without letting the optimizer know that. -@inline(never) -public func identity(_ x: T) -> T { - return x -} - -// Return the passed argument without letting the optimizer know that. -// It's important that this function is in another module than the tests -// which are using it. -@inline(never) -public func getInt(_ x: Int) -> Int { return x } - -// The same for String. -@inline(never) -public func getString(_ s: String) -> String { return s } - -// The same for Substring. -@inline(never) -public func getSubstring(_ s: Substring) -> Substring { return s } diff --git a/Sources/BaggageBenchmarkTools/DriverUtils.swift b/Sources/BaggageBenchmarkTools/DriverUtils.swift deleted file mode 100644 index d088bde..0000000 --- a/Sources/BaggageBenchmarkTools/DriverUtils.swift +++ /dev/null @@ -1,722 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -//===--- DriverUtils.swift ------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#if os(Linux) -import Glibc -#else -import Darwin -// import LibProc -#endif - -public struct BenchResults { - typealias T = Int - private let samples: [T] - let maxRSS: Int? - let stats: Stats - - init(_ samples: [T], maxRSS: Int?) { - self.samples = samples.sorted() - self.maxRSS = maxRSS - self.stats = self.samples.reduce(into: Stats(), Stats.collect) - } - - /// Return measured value for given `quantile`. - /// - /// Equivalent to quantile estimate type R-1, SAS-3. See: - /// https://en.wikipedia.org/wiki/Quantile#Estimating_quantiles_from_a_sample - subscript(_ quantile: Double) -> T { - let index = Swift.max( - 0, - Int((Double(self.samples.count) * quantile).rounded(.up)) - 1 - ) - return self.samples[index] - } - - var sampleCount: T { return self.samples.count } - var min: T { return self.samples.first! } - var max: T { return self.samples.last! } - var mean: T { return Int(self.stats.mean.rounded()) } - var sd: T { return Int(self.stats.standardDeviation.rounded()) } - var median: T { return self[0.5] } -} - -public var registeredBenchmarks: [BenchmarkInfo] = [] - -enum TestAction { - case run - case listTests -} - -struct TestConfig { - /// The delimiter to use when printing output. - let delim: String - - /// Duration of the test measurement in seconds. - /// - /// Used to compute the number of iterations, if no fixed amount is specified. - /// This is useful when one wishes for a test to run for a - /// longer amount of time to perform performance analysis on the test in - /// instruments. - let sampleTime: Double - - /// Number of iterations averaged in the sample. - /// When not specified, we'll compute the number of iterations to be averaged - /// in the sample from the actual runtime and the desired `sampleTime`. - let numIters: Int? - - /// The number of samples we should take of each test. - let numSamples: Int? - - /// Quantiles to report in results. - let quantile: Int? - - /// Time unit in which to report results (nanos, micros, millis) (default: nanoseconds) - let timeUnit: TimeUnit - - /// Report quantiles with delta encoding. - let delta: Bool - - /// Is verbose output enabled? - let verbose: Bool - - // Should we log the test's memory usage? - let logMemory: Bool - - /// After we run the tests, should the harness sleep to allow for utilities - /// like leaks that require a PID to run on the test harness. - let afterRunSleep: UInt32? - - /// The list of tests to run. - let tests: [(index: String, info: BenchmarkInfo)] - - let action: TestAction - - init(_ registeredBenchmarks: [BenchmarkInfo]) { - struct PartialTestConfig { - var delim: String? - var tags, skipTags: Set? - var numSamples: UInt? - var numIters: UInt? - var quantile: UInt? - var timeUnit: String? - var delta: Bool? - var afterRunSleep: UInt32? - var sampleTime: Double? - var verbose: Bool? - var logMemory: Bool? - var action: TestAction? - var tests: [String]? - } - - // Custom value type parsers - func tags(tags: String) throws -> Set { - // We support specifying multiple tags by splitting on comma, i.e.: - // --tags=Array,Dictionary - // --skip-tags=Array,Set,unstable,skip - return Set( - try tags.split(separator: ",").map(String.init).map { - try checked({ BenchmarkCategory(rawValue: $0) }, $0) - } - ) - } - func finiteDouble(value: String) -> Double? { - return Double(value).flatMap { $0.isFinite ? $0 : nil } - } - - // Configure the command line argument parser - let p = ArgumentParser(into: PartialTestConfig()) - p.addArgument( - "--num-samples", \.numSamples, - help: "number of samples to take per benchmark;\n" + - "default: 1 or auto-scaled to measure for\n" + - "`sample-time` if num-iters is also specified\n", - parser: { UInt($0) } - ) - p.addArgument( - "--num-iters", \.numIters, - help: "number of iterations averaged in the sample;\n" + - "default: auto-scaled to measure for `sample-time`", - parser: { UInt($0) } - ) - p.addArgument( - "--quantile", \.quantile, - help: "report quantiles instead of normal dist. stats;\n" + - "use 4 to get a five-number summary with quartiles,\n" + - "10 (deciles), 20 (ventiles), 100 (percentiles), etc.", - parser: { UInt($0) } - ) - p.addArgument( - "--time-unit", \.timeUnit, - help: "time unit to be used for reported measurements;\n" + - "supported values: ns, us, ms; default: ns", - parser: { $0 } - ) - p.addArgument( - "--delta", \.delta, defaultValue: true, - help: "report quantiles with delta encoding" - ) - p.addArgument( - "--sample-time", \.sampleTime, - help: "duration of test measurement in seconds\ndefault: 1", - parser: finiteDouble - ) - p.addArgument( - "--verbose", \.verbose, defaultValue: true, - help: "increase output verbosity" - ) - p.addArgument( - "--memory", \.logMemory, defaultValue: true, - help: "log the change in maximum resident set size (MAX_RSS)" - ) - p.addArgument( - "--delim", \.delim, - help: "value delimiter used for log output; default: ,", - parser: { $0 } - ) - p.addArgument( - "--tags", \PartialTestConfig.tags, - help: "run tests matching all the specified categories", - parser: tags - ) - p.addArgument( - "--skip-tags", \PartialTestConfig.skipTags, defaultValue: [], - help: "don't run tests matching any of the specified\n" + - "categories; default: unstable,skip", - parser: tags - ) - p.addArgument( - "--sleep", \.afterRunSleep, - help: "number of seconds to sleep after benchmarking", - parser: { UInt32($0) } - ) - p.addArgument( - "--list", \.action, defaultValue: .listTests, - help: "don't run the tests, just log the list of test \n" + - "numbers, names and tags (respects specified filters)" - ) - p.addArgument(nil, \.tests) // positional arguments - let c = p.parse() - - // Configure from the command line arguments, filling in the defaults. - self.delim = c.delim ?? "," - self.sampleTime = c.sampleTime ?? 1.0 - self.numIters = c.numIters.map { Int($0) } - self.numSamples = c.numSamples.map { Int($0) } - self.quantile = c.quantile.map { Int($0) } - self.timeUnit = c.timeUnit.map { TimeUnit($0) } ?? TimeUnit.nanoseconds - self.delta = c.delta ?? false - self.verbose = c.verbose ?? false - self.logMemory = c.logMemory ?? false - self.afterRunSleep = c.afterRunSleep - self.action = c.action ?? .run - self.tests = TestConfig.filterTests( - registeredBenchmarks, - specifiedTests: Set(c.tests ?? []), - tags: c.tags ?? [], - skipTags: c.skipTags ?? [.unstable, .skip] - ) - - if self.logMemory, self.tests.count > 1 { - print( - """ - warning: The memory usage of a test, reported as the change in MAX_RSS, - is based on measuring the peak memory used by the whole process. - These results are meaningful only when running a single test, - not in the batch mode! - """) - } - - // We always prepare the configuration string and call the print to have - // the same memory usage baseline between verbose and normal mode. - let testList = self.tests.map { $0.1.name }.joined(separator: ", ") - let configuration = """ - --- CONFIG --- - NumSamples: \(numSamples ?? 0) - Verbose: \(verbose) - LogMemory: \(logMemory) - SampleTime: \(sampleTime) - NumIters: \(numIters ?? 0) - Quantile: \(quantile ?? 0) - TimeUnit: \(timeUnit) - Delimiter: \(String(reflecting: delim)) - Tests Filter: \(c.tests ?? []) - Tests to run: \(testList) - --- DATA ---\n - """ - print(self.verbose ? configuration : "", terminator: "") - } - - /// Returns the list of tests to run. - /// - /// - Parameters: - /// - registeredBenchmarks: List of all performance tests to be filtered. - /// - specifiedTests: List of explicitly specified tests to run. These can - /// be specified either by a test name or a test number. - /// - tags: Run tests tagged with all of these categories. - /// - skipTags: Don't run tests tagged with any of these categories. - /// - Returns: An array of test number and benchmark info tuples satisfying - /// specified filtering conditions. - static func filterTests( - _ registeredBenchmarks: [BenchmarkInfo], - specifiedTests: Set, - tags: Set, - skipTags: Set - ) -> [(index: String, info: BenchmarkInfo)] { - let allTests = registeredBenchmarks.sorted() - let indices = Dictionary( - uniqueKeysWithValues: - zip( - allTests.map { $0.name }, - (1...).lazy.map { String($0) } - ) - ) - - func byTags(b: BenchmarkInfo) -> Bool { - return b.tags.isSuperset(of: tags) && - b.tags.isDisjoint(with: skipTags) - } - func byNamesOrIndices(b: BenchmarkInfo) -> Bool { - return specifiedTests.contains(b.name) || - specifiedTests.contains(indices[b.name]!) - } // !! "`allTests` have been assigned an index" - return allTests - .filter(specifiedTests.isEmpty ? byTags : byNamesOrIndices) - .map { (index: indices[$0.name]!, info: $0) } - } -} - -struct Stats { - var n: Int = 0 - var S: Double = 0.0 - var mean: Double = 0.0 - var variance: Double { return self.n < 2 ? 0.0 : self.S / Double(self.n - 1) } - var standardDeviation: Double { return self.variance.squareRoot() } - - static func collect(_ s: inout Stats, _ x: Int) { - Stats.runningMeanVariance(&s, Double(x)) - } - - /// Compute running mean and variance using B. P. Welford's method. - /// - /// See Knuth TAOCP vol 2, 3rd edition, page 232, or - /// https://www.johndcook.com/blog/standard_deviation/ - static func runningMeanVariance(_ s: inout Stats, _ x: Double) { - let n = s.n + 1 - let (k, M_, S_) = (Double(n), s.mean, s.S) - let M = M_ + (x - M_) / k - let S = S_ + (x - M_) * (x - M) - (s.n, s.mean, s.S) = (n, M, S) - } -} - -#if SWIFT_RUNTIME_ENABLE_LEAK_CHECKER - -@_silgen_name("_swift_leaks_startTrackingObjects") -func startTrackingObjects(_: UnsafePointer) -> Void -@_silgen_name("_swift_leaks_stopTrackingObjects") -func stopTrackingObjects(_: UnsafePointer) -> Int - -#endif - -public final class Timer { - #if os(Linux) - public typealias TimeT = timespec - - public init() {} - - public func getTime() -> TimeT { - var ts = timespec(tv_sec: 0, tv_nsec: 0) - clock_gettime(CLOCK_REALTIME, &ts) - return ts - } - - public func getTimeAsInt() -> UInt64 { - return UInt64(getTime().tv_nsec) - } - - public func diffTimeInNanoSeconds(from start: TimeT, to end: TimeT) -> UInt64 { - let oneSecond = 1_000_000_000 // ns - var elapsed = timespec(tv_sec: 0, tv_nsec: 0) - if end.tv_nsec - start.tv_nsec < 0 { - elapsed.tv_sec = end.tv_sec - start.tv_sec - 1 - elapsed.tv_nsec = end.tv_nsec - start.tv_nsec + oneSecond - } else { - elapsed.tv_sec = end.tv_sec - start.tv_sec - elapsed.tv_nsec = end.tv_nsec - start.tv_nsec - } - return UInt64(elapsed.tv_sec) * UInt64(oneSecond) + UInt64(elapsed.tv_nsec) - } - - #else - public typealias TimeT = UInt64 - var info = mach_timebase_info_data_t(numer: 0, denom: 0) - - public init() { - mach_timebase_info(&info) - } - - public func getTime() -> TimeT { - return mach_absolute_time() - } - - public func getTimeAsInt() -> UInt64 { - return UInt64(getTime()) - } - - public func diffTimeInNanoSeconds(from start: TimeT, to end: TimeT) -> UInt64 { - let elapsed = end - start - return elapsed * UInt64(info.numer) / UInt64(info.denom) - } - #endif -} - -extension UInt64 { - public var nanoseconds: Int { return Int(self) } - public var microseconds: Int { return Int(self / 1000) } - public var milliseconds: Int { return Int(self / 1000 / 1000) } - public var seconds: Int { return Int(self / 1000 / 1000 / 1000) } -} - -enum TimeUnit: String { - case nanoseconds = "ns" - case microseconds = "μs" - case milliseconds = "ms" - case seconds = "s" - - init(_ from: String) { - switch from { - case "ns": self = .nanoseconds - case "us", "μs": self = .microseconds - case "ms": self = .milliseconds - case "s": self = .seconds - default: fatalError("Only the following time units are supported: ns, us, ms, s") - } - } - - static var `default` = TimeUnit.nanoseconds -} - -extension TimeUnit: CustomStringConvertible { - public var description: String { - return self.rawValue - } -} - -/// Performance test runner that measures benchmarks and reports the results. -final class TestRunner { - let c: TestConfig - let timer = Timer() - var start, end, lastYield: Timer.TimeT - let baseline = TestRunner.getResourceUtilization() - let schedulerQuantum = UInt64(10_000_000) // nanoseconds (== 10ms, macos) - init(_ config: TestConfig) { - self.c = config - let now = timer.getTime() - (start, end, lastYield) = (now, now, now) - } - - /// Offer to yield CPU to other processes and return current time on resume. - func yield() -> Timer.TimeT { - sched_yield() - return timer.getTime() - } - - #if os(Linux) - private static func getExecutedInstructions() -> UInt64 { - // FIXME: there is a Linux PMC API you can use to get this, but it's - // not quite so straightforward. - return 0 - } - - #else - private static func getExecutedInstructions() -> UInt64 { -// if #available(OSX 10.9, iOS 7.0, *) { -// var u = rusage_info_v4() -// let p = UnsafeMutablePointer(&u) -// p.withMemoryRebound(to: Optional.self, capacity: 1) { up in -// let _ = proc_pid_rusage(getpid(), RUSAGE_INFO_V4, up) -// } -// return u.ri_instructions -// } else { - return 0 -// } - } - #endif - - private static func getResourceUtilization() -> rusage { - #if canImport(Darwin) - let rusageSelf = RUSAGE_SELF - #else - let rusageSelf = RUSAGE_SELF.rawValue - #endif - var u = rusage(); getrusage(rusageSelf, &u); return u - } - - /// Returns maximum resident set size (MAX_RSS) delta in bytes. - /// - /// This method of estimating memory usage is valid only for executing single - /// benchmark. That's why we don't worry about reseting the `baseline` in - /// `resetMeasurements`. - /// - // FIXME: This current implementation doesn't work on Linux. It is disabled - /// permanently to avoid linker errors. Feel free to fix. - func measureMemoryUsage() -> Int? { - #if os(Linux) - return nil - #else - guard c.logMemory else { return nil } - let current = TestRunner.getResourceUtilization() - let maxRSS = current.ru_maxrss - baseline.ru_maxrss - #if canImport(Darwin) - let pageSize = _SC_PAGESIZE - #else - let pageSize = Int32(_SC_PAGESIZE) - #endif - let pages = { maxRSS / sysconf(pageSize) } - func deltaEquation(_ stat: KeyPath) -> String { - let b = baseline[keyPath: stat], c = current[keyPath: stat] - return "\(c) - \(b) = \(c - b)" - } - logVerbose( - """ - MAX_RSS \(deltaEquation(\rusage.ru_maxrss)) (\(pages()) pages) - ICS \(deltaEquation(\rusage.ru_nivcsw)) - VCS \(deltaEquation(\rusage.ru_nvcsw)) - """) - return maxRSS - #endif - } - - private func startMeasurement() { - let spent = timer.diffTimeInNanoSeconds(from: lastYield, to: end) - let nextSampleEstimate = UInt64(Double(lastSampleTime) * 1.5) - - if spent + nextSampleEstimate < schedulerQuantum { - start = timer.getTime() - } else { - logVerbose(" Yielding after ~\(spent.nanoseconds) ns") - let now = yield() - (start, lastYield) = (now, now) - } - } - - private func stopMeasurement() { - end = timer.getTime() - } - - private func resetMeasurements() { - let now = yield() - (start, end, lastYield) = (now, now, now) - } - - /// Time in nanoseconds spent running the last function - var lastSampleTime: UInt64 { - return timer.diffTimeInNanoSeconds(from: start, to: end) - } - - /// Measure the `fn` and return the average sample time per iteration (in c.timeUnit). - func measure(_ name: String, fn: (Int) -> Void, numIters: Int) -> Int { - #if SWIFT_RUNTIME_ENABLE_LEAK_CHECKER - name.withCString { p in startTrackingObjects(p) } - #endif - - startMeasurement() - fn(numIters) - stopMeasurement() - - #if SWIFT_RUNTIME_ENABLE_LEAK_CHECKER - name.withCString { p in stopTrackingObjects(p) } - #endif - - switch c.timeUnit { - case .nanoseconds: return lastSampleTime.nanoseconds / numIters - case .microseconds: return lastSampleTime.microseconds / numIters - case .milliseconds: return lastSampleTime.milliseconds / numIters - case .seconds: return lastSampleTime.seconds / numIters - } - } - - func logVerbose(_ msg: @autoclosure () -> String) { - if c.verbose { print(msg()) } - } - - /// Run the benchmark and return the measured results. - func run(_ test: BenchmarkInfo) -> BenchResults? { - // Before we do anything, check that we actually have a function to - // run. If we don't it is because the benchmark is not supported on - // the platform and we should skip it. - guard let testFn = test.runFunction else { - logVerbose("Skipping unsupported benchmark \(test.name)!") - return nil - } - logVerbose("Running \(test.name)") - - var samples: [Int] = [] - - func addSample(_ time: Int) { - logVerbose(" Sample \(samples.count),\(time)") - samples.append(time) - } - - resetMeasurements() - if let setUp = test.setUpFunction { - setUp() - stopMeasurement() - logVerbose(" SetUp \(lastSampleTime.microseconds)") - resetMeasurements() - } - - // Determine number of iterations for testFn to run for desired time. - func iterationsPerSampleTime() -> (numIters: Int, oneIter: Int) { - let oneIter = measure(test.name, fn: testFn, numIters: 1) - if oneIter > 0 { - let timePerSample = Int(c.sampleTime * 1_000_000.0) // microseconds (μs) - return (max(timePerSample / oneIter, 1), oneIter) - } else { - return (1, oneIter) - } - } - - // Determine the scale of measurements. Re-use the calibration result if - // it is just one measurement. - func calibrateMeasurements() -> Int { - let (numIters, oneIter) = iterationsPerSampleTime() - if numIters == 1 { addSample(oneIter) } - else { resetMeasurements() } // for accurate yielding reports - return numIters - } - - let numIters = min( // Cap to prevent overflow on 32-bit systems when scaled - Int.max / 10000, // by the inner loop multiplier inside the `testFn`. - c.numIters ?? calibrateMeasurements() - ) - - let numSamples = c.numSamples ?? min( - 200, // Cap the number of samples - c.numIters == nil ? 1 : calibrateMeasurements() - ) - - samples.reserveCapacity(numSamples) - logVerbose(" Collecting \(numSamples) samples.") - logVerbose(" Measuring with scale \(numIters).") - for _ in samples.count ..< numSamples { - addSample(measure(test.name, fn: testFn, numIters: numIters)) - } - - test.tearDownFunction?() - if let lf = test.legacyFactor { - logVerbose(" Applying legacy factor: \(lf)") - samples = samples.map { $0 * lf } - } - - return BenchResults(samples, maxRSS: measureMemoryUsage()) - } - - var header: String { - let withUnit = { $0 + "(\(self.c.timeUnit))" } - let withDelta = { "𝚫" + $0 } - func quantiles(q: Int) -> [String] { - // See https://en.wikipedia.org/wiki/Quantile#Specialized_quantiles - let prefix = [ - 2: "MEDIAN", 3: "T", 4: "Q", 5: "QU", 6: "S", 7: "O", 10: "D", - 12: "Dd", 16: "H", 20: "V", 33: "TT", 100: "P", 1000: "Pr", - ][q, default: "\(q)-q"] - let base20 = "0123456789ABCDEFGHIJ".map { String($0) } - let index: (Int) -> String = - { q == 2 ? "" : q <= 20 ? base20[$0] : String($0) } - let tail = (1 ..< q).map { prefix + index($0) } + ["MAX"] - return [withUnit("MIN")] + tail.map(c.delta ? withDelta : withUnit) - } - return ( - ["#", "TEST", "SAMPLES"] + - ( - c.quantile.map(quantiles) - ?? ["MIN", "MAX", "MEAN", "SD", "MEDIAN"].map(withUnit) - ) + - (c.logMemory ? ["MAX_RSS(B)"] : []) - ).joined(separator: c.delim) - } - - /// Execute benchmarks and continuously report the measurement results. - func runBenchmarks() { - var testCount = 0 - - func report(_ index: String, _ t: BenchmarkInfo, results: BenchResults?) { - func values(r: BenchResults) -> [String] { - func quantiles(q: Int) -> [Int] { - let qs = (0 ... q).map { i in r[Double(i) / Double(q)] } - return c.delta ? - qs.reduce(into: (encoded: [], last: 0)) { - $0.encoded.append($1 - $0.last); $0.last = $1 - }.encoded : qs - } - return ( - [r.sampleCount] + - ( - c.quantile.map(quantiles) - ?? [r.min, r.max, r.mean, r.sd, r.median] - ) + - [r.maxRSS].compactMap { $0 } - ).map { (c.delta && $0 == 0) ? "" : String($0) } // drop 0s in deltas - } - let benchmarkStats = ( - [index, t.name] + (results.map(values) ?? ["Unsupported"]) - ).joined(separator: c.delim) - - print(benchmarkStats) - fflush(stdout) - - if results != nil { - testCount += 1 - } - } - - print(header) - - for (index, test) in c.tests { - report(index, test, results: run(test)) - } - - print("\nTotal performance tests executed: \(testCount)") - } -} - -public func main() { - let config = TestConfig(registeredBenchmarks) - switch config.action { - case .listTests: - print("#\(config.delim)Test\(config.delim)[Tags]") - for (index, t) in config.tests { - let testDescription = [index, t.name, t.tags.sorted().description] - .joined(separator: config.delim) - print(testDescription) - } - case .run: - TestRunner(config).runBenchmarks() - if let x = config.afterRunSleep { - sleep(x) - } - } -} diff --git a/Sources/BaggageBenchmarkTools/README_SWIFT.md b/Sources/BaggageBenchmarkTools/README_SWIFT.md deleted file mode 100644 index a7be5c3..0000000 --- a/Sources/BaggageBenchmarkTools/README_SWIFT.md +++ /dev/null @@ -1,6 +0,0 @@ -## Swift Benchmark Utils - -> This benchmarking infrastructure is copied from -https://github.com/apple/swift/tree/cf53143a47278c2a465409a67376642515956777/benchmark/utils -with the intent of producing similar look and feel, as well as because we need some benchmark infra. -> When feasible we will aim to collaborate and contribute improvements back to the mainline Swift project. diff --git a/Sources/BaggageBenchmarks/BaggagePassingBenchmarks.swift b/Sources/BaggageBenchmarks/BaggagePassingBenchmarks.swift deleted file mode 100644 index 4f1a6e8..0000000 --- a/Sources/BaggageBenchmarks/BaggagePassingBenchmarks.swift +++ /dev/null @@ -1,189 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Baggage -import BaggageBenchmarkTools -import Dispatch -import class Foundation.NSLock -public let BaggagePassingBenchmarks: [BenchmarkInfo] = [ - // ==== ---------------------------------------------------------------------------------------------------------------- - // MARK: "Read only" context passing around - BenchmarkInfo( - name: "BaggagePassingBenchmarks.pass_async_empty_100_000 ", - runFunction: { _ in - let context = BaggageContext.background - pass_async(context: context, times: 100_000) - }, - tags: [], - setUpFunction: { setUp() }, - tearDownFunction: tearDown - ), - BenchmarkInfo( - name: "BaggagePassingBenchmarks.pass_async_smol_100_000 ", - runFunction: { _ in - var context = BaggageContext.background - context.k1 = "one" - context.k2 = "two" - context.k3 = "three" - context.k4 = "four" - pass_async(context: context, times: 100_000) - }, - tags: [], - setUpFunction: { setUp() }, - tearDownFunction: tearDown - ), - BenchmarkInfo( - name: "BaggagePassingBenchmarks.pass_async_small_nonconst_100_000", - runFunction: { _ in - var context = BaggageContext.background - context.k1 = "\(Int.random(in: 1 ... Int.max))" - context.k2 = "\(Int.random(in: 1 ... Int.max))" - context.k3 = "\(Int.random(in: 1 ... Int.max))" - context.k4 = "\(Int.random(in: 1 ... Int.max))" - pass_async(context: context, times: 100_000) - }, - tags: [], - setUpFunction: { setUp() }, - tearDownFunction: tearDown - ), - - // ==== ------------------------------------------------------------------------------------------------------------ - // MARK: Passing & Mutating - // Since the context is backed by a dictionary (and nothing else) we rely on its CoW semantics, those writes cause copies - // whilst the previous benchmarks which are read-only do not cause copies of the underlying storage (dictionary). - - BenchmarkInfo( - name: "BaggagePassingBenchmarks.pass_mut_async_small_100_000 ", - runFunction: { _ in - var context = BaggageContext.background - context.k1 = "\(Int.random(in: 1 ... Int.max))" - context.k2 = "\(Int.random(in: 1 ... Int.max))" - context.k3 = "\(Int.random(in: 1 ... Int.max))" - context.k4 = "\(Int.random(in: 1 ... Int.max))" - pass_mut_async(context: context, times: 100_000) - }, - tags: [], - setUpFunction: { setUp() }, - tearDownFunction: tearDown - ), -] - -private func setUp() { - // ... -} - -private func tearDown() { - // ... -} - -@inline(never) -func pass_async(context: BaggageContext, times remaining: Int) { - let latch = CountDownLatch(from: 1) - - func pass_async0(context: BaggageContext, times remaining: Int) { - if remaining == 0 { - latch.countDown() - } - DispatchQueue.global().async { - pass_async0(context: context, times: remaining - 1) - } - } - - pass_async0(context: context, times: remaining - 1) - - latch.wait() -} - -@inline(never) -func pass_mut_async(context: BaggageContext, times remaining: Int) { - var context = context - let latch = CountDownLatch(from: 1) - - func pass_async0(context: BaggageContext, times remaining: Int) { - if remaining == 0 { - latch.countDown() - } - - DispatchQueue.global().async { - // mutate the context - var context = context - context.passCounter = remaining - - pass_async0(context: context, times: remaining - 1) - } - } - - context.passCounter = remaining - pass_async0(context: context, times: remaining - 1) - - latch.wait() -} - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Baggage Keys - -private enum TestPassCounterKey: BaggageContextKey { - typealias Value = Int -} - -private enum TestK1: BaggageContextKey { - typealias Value = String -} - -private enum TestK2: BaggageContextKey { - typealias Value = String -} - -private enum TestK3: BaggageContextKey { - typealias Value = String -} - -private enum TestK4: BaggageContextKey { - typealias Value = String -} - -private enum TestKD1: BaggageContextKey { - typealias Value = [String: String] -} - -extension BaggageContext { - fileprivate var passCounter: TestPassCounterKey.Value { - get { return self[TestPassCounterKey.self] ?? 0 } - set { self[TestPassCounterKey.self] = newValue } - } - - fileprivate var k1: TestK1.Value? { - get { return self[TestK1.self] } - set { self[TestK1.self] = newValue } - } - - fileprivate var k2: TestK2.Value? { - get { return self[TestK2.self] } - set { self[TestK2.self] = newValue } - } - - fileprivate var k3: TestK3.Value? { - get { return self[TestK3.self] } - set { self[TestK3.self] = newValue } - } - - fileprivate var k4: TestK4.Value? { - get { return self[TestK4.self] } - set { self[TestK4.self] = newValue } - } - - fileprivate var kd1: TestKD1.Value? { - get { return self[TestKD1.self] } - set { self[TestKD1.self] = newValue } - } -} diff --git a/Sources/BaggageBenchmarks/locks.swift b/Sources/BaggageBenchmarks/locks.swift deleted file mode 100644 index b014027..0000000 --- a/Sources/BaggageBenchmarks/locks.swift +++ /dev/null @@ -1,253 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) -import Darwin -#else -import Glibc -#endif - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: CountDownLatch - -internal class CountDownLatch { - private var counter: Int - private let condition: _Condition - private let lock: _Mutex - - init(from: Int) { - self.counter = from - self.condition = _Condition() - self.lock = _Mutex() - } - - /// Returns previous value before the decrement was issued. - func countDown() { - self.lock.synchronized { - self.counter -= 1 - - if self.counter == 0 { - self.condition.signalAll() - } - } - } - - var count: Int { - return self.lock.synchronized { - self.counter - } - } - - func wait() { - self.lock.synchronized { - while true { - if self.counter == 0 { - return // done - } - - self.condition.wait(lock) - } - } - } -} - -extension CountDownLatch: CustomStringConvertible { - public var description: String { - return "CountDownLatch(remaining:\(self.count)" - } -} - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Condition - -final class _Condition { - @usableFromInline - var condition: pthread_cond_t = pthread_cond_t() - - public init() { - let error = pthread_cond_init(&self.condition, nil) - - switch error { - case 0: - return - default: - fatalError("Condition could not be created: \(error)") - } - } - - deinit { - pthread_cond_destroy(&condition) - } - - @inlinable - public func wait(_ mutex: _Mutex) { - let error = pthread_cond_wait(&self.condition, &mutex.mutex) - - switch error { - case 0: - return - case EPERM: - fatalError("Wait failed, mutex is not owned by this thread") - case EINVAL: - fatalError("Wait failed, condition is not valid") - default: - fatalError("Wait failed with unspecified error: \(error)") - } - } - -// @inlinable -// public func wait(_ mutex: _Mutex) -> Bool { -// let error = withUnsafePointer(to: time) { p -> Int32 in -// #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) -// return pthread_cond_timedwait_relative_np(&condition, &mutex.mutex, p) -// #else -// return pthread_cond_timedwait(&condition, &mutex.mutex, p) -// #endif -// } -// -// switch error { -// case 0: -// return true -// case ETIMEDOUT: -// return false -// case EPERM: -// fatalError("Wait failed, mutex is not owned by this thread") -// case EINVAL: -// fatalError("Wait failed, condition is not valid") -// default: -// fatalError("Wait failed with unspecified error: \(error)") -// } -// } - - @inlinable - public func signal() { - let error = pthread_cond_signal(&self.condition) - - switch error { - case 0: - return - case EINVAL: - fatalError("Signal failed, condition is not valid") - default: - fatalError("Signal failed with unspecified error: \(error)") - } - } - - @inlinable - public func signalAll() { - let error = pthread_cond_broadcast(&self.condition) - - switch error { - case 0: - return - case EINVAL: - fatalError("Signal failed, condition is not valid") - default: - fatalError("Signal failed with unspecified error: \(error)") - } - } -} - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Mutex - -final class _Mutex { - @usableFromInline - var mutex: pthread_mutex_t = pthread_mutex_t() - - public init() { - var attr: pthread_mutexattr_t = pthread_mutexattr_t() - pthread_mutexattr_init(&attr) - pthread_mutexattr_settype(&attr, Int32(PTHREAD_MUTEX_RECURSIVE)) - - let error = pthread_mutex_init(&self.mutex, &attr) - pthread_mutexattr_destroy(&attr) - - switch error { - case 0: - return - default: - fatalError("Could not create mutex: \(error)") - } - } - - deinit { - pthread_mutex_destroy(&mutex) - } - - @inlinable - public func lock() { - let error = pthread_mutex_lock(&self.mutex) - - switch error { - case 0: - return - case EDEADLK: - fatalError("Mutex could not be acquired because it would have caused a deadlock") - default: - fatalError("Failed with unspecified error: \(error)") - } - } - - @inlinable - public func unlock() { - let error = pthread_mutex_unlock(&self.mutex) - - switch error { - case 0: - return - case EPERM: - fatalError("Mutex could not be unlocked because it is not held by the current thread") - default: - fatalError("Unlock failed with unspecified error: \(error)") - } - } - - @inlinable - public func tryLock() -> Bool { - let error = pthread_mutex_trylock(&self.mutex) - - switch error { - case 0: - return true - case EBUSY: - return false - case EDEADLK: - fatalError("Mutex could not be acquired because it would have caused a deadlock") - default: - fatalError("Failed with unspecified error: \(error)") - } - } - - @inlinable - public func synchronized(_ f: () -> A) -> A { - self.lock() - - defer { - unlock() - } - - return f() - } - - @inlinable - public func synchronized(_ f: () throws -> A) throws -> A { - self.lock() - - defer { - unlock() - } - - return try f() - } -} diff --git a/Sources/BaggageBenchmarks/main.swift b/Sources/BaggageBenchmarks/main.swift deleted file mode 100644 index f1abe60..0000000 --- a/Sources/BaggageBenchmarks/main.swift +++ /dev/null @@ -1,41 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import BaggageBenchmarkTools - -assert({ - print("===========================================================================") - print("= !! YOU ARE RUNNING BENCHMARKS IN DEBUG MODE !! =") - print("= When running on the command line, use: `swift run -c release` =") - print("===========================================================================") - return true -}()) - -@inline(__always) -private func registerBenchmark(_ bench: BenchmarkInfo) { - registeredBenchmarks.append(bench) -} - -@inline(__always) -private func registerBenchmark(_ benches: [BenchmarkInfo]) { - benches.forEach(registerBenchmark) -} - -@inline(__always) -private func registerBenchmark(_ name: String, _ function: @escaping (Int) -> Void, _ tags: [BenchmarkCategory]) { - registerBenchmark(BenchmarkInfo(name: name, runFunction: function, tags: tags)) -} - -registerBenchmark(BaggagePassingBenchmarks) - -main() diff --git a/Sources/BaggageLogging/BaggageMetadataLogHandler.swift b/Sources/BaggageLogging/BaggageMetadataLogHandler.swift deleted file mode 100644 index 0949c98..0000000 --- a/Sources/BaggageLogging/BaggageMetadataLogHandler.swift +++ /dev/null @@ -1,113 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Baggage -import Logging - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: BaggageContext (as additional Logger.Metadata) LogHandler - -/// Proxying log handler which adds `BaggageContext` as metadata when log events are to be emitted. -/// -/// The values stored in the `BaggageContext` are merged with the existing metadata on the logger. If both contain values for the same key, -/// the `BaggageContext` values are preferred. -public struct BaggageMetadataLogHandler: LogHandler { - private var underlying: Logger - private let context: BaggageContext - - public init(logger underlying: Logger, context: BaggageContext) { - self.underlying = underlying - self.context = context - } - - public var logLevel: Logger.Level { - get { - return self.underlying.logLevel - } - set { - self.underlying.logLevel = newValue - } - } - - public func log( - level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt - ) { - guard self.underlying.logLevel <= level else { - return - } - - var effectiveMetadata = self.baggageAsMetadata() - if let metadata = metadata { - effectiveMetadata.merge(metadata, uniquingKeysWith: { _, r in r }) - } - self.underlying.log(level: level, message, metadata: effectiveMetadata, source: source, file: file, function: function, line: line) - } - - public var metadata: Logger.Metadata { - get { - return [:] - } - set { - newValue.forEach { k, v in - self.underlying[metadataKey: k] = v - } - } - } - - /// Note that this does NOT look up inside the baggage. - /// - /// This is because a context lookup either has to use the specific type key, or iterate over all keys to locate one by name, - /// which may be incorrect still, thus rather than making an potentially slightly incorrect lookup, we do not implement peeking - /// into a baggage with String keys through this handler (as that is not a capability `BaggageContext` offers in any case. - public subscript(metadataKey metadataKey: Logger.Metadata.Key) -> Logger.Metadata.Value? { - get { - return self.underlying[metadataKey: metadataKey] - } - set { - self.underlying[metadataKey: metadataKey] = newValue - } - } - - private func baggageAsMetadata() -> Logger.Metadata { - var effectiveMetadata: Logger.Metadata = [:] - self.context.forEach { key, value in - if let convertible = value as? String { - effectiveMetadata[key.name] = .string(convertible) - } else if let convertible = value as? CustomStringConvertible { - effectiveMetadata[key.name] = .stringConvertible(convertible) - } else { - effectiveMetadata[key.name] = .stringConvertible(BaggageValueCustomStringConvertible(value)) - } - } - - return effectiveMetadata - } - - struct BaggageValueCustomStringConvertible: CustomStringConvertible { - let value: Any - - init(_ value: Any) { - self.value = value - } - - var description: String { - return "\(self.value)" - } - } -} diff --git a/Sources/BaggageLogging/Logger+BaggageContext.swift b/Sources/BaggageLogging/Logger+BaggageContext.swift deleted file mode 100644 index ab7cf8f..0000000 --- a/Sources/BaggageLogging/Logger+BaggageContext.swift +++ /dev/null @@ -1,29 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Baggage -import Logging - -extension Logger { - /// Returns a logger that in addition to any explicit metadata passed to log statements, - /// also includes the `BaggageContext` adapted into metadata values. - /// - /// The rendering of baggage values into metadata values is performed on demand, - /// whenever a log statement is effective (i.e. will be logged, according to active `logLevel`). - public func with(context: BaggageContext) -> Logger { - return Logger( - label: self.label, - factory: { _ in BaggageMetadataLogHandler(logger: self, context: context) } - ) - } -} diff --git a/Sources/BaggageLogging/LoggingBaggageContextCarrier.swift b/Sources/BaggageLogging/LoggingBaggageContextCarrier.swift deleted file mode 100644 index 7b1a946..0000000 --- a/Sources/BaggageLogging/LoggingBaggageContextCarrier.swift +++ /dev/null @@ -1,44 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Baggage -import Logging - -/// A `LoggingBaggageContextCarrier` purpose is to be adopted by frameworks which already provide a "FrameworkContext", -/// and to such frameworks to pass their context as `BaggageContextCarrier`. -public protocol LoggingBaggageContextCarrier: BaggageContextCarrier { - /// The logger associated with this context carrier. - /// - /// It should automatically populate the loggers metadata based on the `BaggageContext` associated with this context object. - /// - /// ### Implementation note - /// - /// Libraries and/or frameworks which conform to this protocol with their "Framework Context" types, - /// SHOULD implement this logger by wrapping the "raw" logger associated with this context with the `logger.with(BaggageContext:)` function, - /// which efficiently handles the bridging of baggage to logging metadata values. - /// - /// ### Example implementation - /// - /// Writes to the `logger` metadata SHOULD NOT be reflected in the `baggage`, - /// however writes to the underlying `baggage` SHOULD be reflected in the `logger`. - /// - /// struct MyFrameworkContext: LoggingBaggageContextCarrier { - /// var baggage = BaggageContext() - /// private let _logger: Logger - /// - /// var logger: Logger { - /// return self._logger.with(context: self.baggage) - /// } - /// } - var logger: Logger { get } -} diff --git a/Sources/CoreBaggage/Baggage.swift b/Sources/CoreBaggage/Baggage.swift new file mode 100644 index 0000000..9c1acf4 --- /dev/null +++ b/Sources/CoreBaggage/Baggage.swift @@ -0,0 +1,218 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Distributed Tracing Baggage open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// ==== ---------------------------------------------------------------------------------------------------------------- +// MARK: Baggage + +/// A `Baggage` is a heterogeneous storage type with value semantics for keyed values in a type-safe fashion. +/// +/// Its values are uniquely identified via `Baggage.Key`s (by type identity). These keys also dictate the type of +/// value allowed for a specific key-value pair through their associated type `Value`. +/// +/// ## Defining keys and accessing values +/// Baggage keys are defined as types, most commonly case-less enums (as no actual instances are actually required) +/// which conform to the `Baggage.Key` protocol: +/// +/// private enum TestIDKey: Baggage.Key { +/// typealias Value = String +/// } +/// +/// While defining a key, one should also immediately declare an extension on `Baggage`, +/// to allow convenient and discoverable ways to interact with the baggage item, the extension should take the form of: +/// +/// extension Baggage { +/// var testID: String? { +/// get { +/// self[TestIDKey.self] +/// } set { +/// self[TestIDKey.self] = newValue +/// } +/// } +/// } +/// +/// For consistency, it is recommended to name key types with the `...Key` suffix (e.g. `SomethingKey`) and the property +/// used to access a value identifier by such key the prefix of the key (e.g. `something`). Please also observe the usual +/// Swift naming conventions, e.g. prefer `ID` to `Id` etc. +/// +/// ## Usage +/// Using a baggage container is fairly straight forward, as it boils down to using the prepared computed properties: +/// +/// var baggage = Baggage.topLevel +/// // set a new value +/// baggage.testID = "abc" +/// // retrieve a stored value +/// let testID = baggage.testID ?? "default" +/// // remove a stored value +/// baggage.testIDKey = nil +/// +/// Note that normally a baggage should not be "created" ad-hoc by user code, but rather it should be passed to it from +/// a runtime. For example, when working in an HTTP server framework, it is most likely that the baggage is already passed +/// directly or indirectly (e.g. in a `FrameworkContext`) +/// +/// ### Accessing all values +/// +/// The only way to access "all" values in a baggage context is by using the `forEach` function. +/// The baggage container on purpose does not expose more functions to prevent abuse and treating it as too much of an +/// arbitrary value smuggling container, but only make it convenient for tracing and instrumentation systems which need +/// to access either specific or all items carried inside a baggage. +public struct Baggage { + public typealias Key = BaggageKey + + private var _storage = [AnyBaggageKey: Any]() + + /// Internal on purpose, please use `Baggage.TODO` or `Baggage.topLevel` to create an "empty" context, + /// which carries more meaning to other developers why an empty context was used. + init() {} +} + +extension Baggage { + /// Creates a new empty "top level" baggage, generally used as an "initial" baggage to immediately be populated with + /// some values by a framework or runtime. Another use case is for tasks starting in the "background" (e.g. on a timer), + /// which don't have a "request context" per se that they can pick up, and as such they have to create a "top level" + /// baggage for their work. + /// + /// ## Usage in frameworks and libraries + /// This function is really only intended to be used frameworks and libraries, at the "top-level" where a request's, + /// message's or task's processing is initiated. For example, a framework handling requests, should create an empty + /// context when handling a request only to immediately populate it with useful trace information extracted from e.g. + /// request headers. + /// + /// ## Usage in applications + /// Application code should never have to create an empty context during the processing lifetime of any request, + /// and only should create contexts if some processing is performed in the background - thus the naming of this property. + /// + /// Usually, a framework such as an HTTP server or similar "request handler" would already provide users + /// with a context to be passed along through subsequent calls. + /// + /// If unsure where to obtain a context from, prefer using `.TODO("Not sure where I should get a context from here?")`, + /// in order to inform other developers that the lack of context passing was not done on purpose, but rather because either + /// not being sure where to obtain a context from, or other framework limitations -- e.g. the outer framework not being + /// baggage context aware just yet. + public static var topLevel: Baggage { + return Baggage() + } +} + +extension Baggage { + /// A baggage intended as a placeholder until a real value can be passed through a function call. + /// + /// It should ONLY be used while prototyping or when the passing of the proper context is not yet possible, + /// e.g. because an external library did not pass it correctly and has to be fixed before the proper context + /// can be obtained where the TO-DO is currently used. + /// + /// ## Crashing on TO-DO context creation + /// You may set the `BAGGAGE_CRASH_TODOS` variable while compiling a project in order to make calls to this function crash + /// with a fatal error, indicating where a to-do baggage context was used. This comes in handy when wanting to ensure that + /// a project never ends up using with code initially was written as "was lazy, did not pass context", yet the + /// project requires context passing to be done correctly throughout the application. Similar checks can be performed + /// at compile time easily using linters (not yet implemented), since it is always valid enough to detect a to-do context + /// being passed as illegal and warn or error when spotted. + /// + /// ## Example + /// + /// let baggage = Baggage.TODO("The framework XYZ should be modified to pass us a context here, and we'd pass it along")) + /// + /// - Parameters: + /// - reason: Informational reason for developers, why a placeholder context was used instead of a proper one, + /// - Returns: Empty "to-do" baggage context which should be eventually replaced with a carried through one, or `background`. + public static func TODO(_ reason: StaticString? = "", function: String = #function, file: String = #file, line: UInt = #line) -> Baggage { + var context = Baggage.topLevel + #if BAGGAGE_CRASH_TODOS + fatalError("BAGGAGE_CRASH_TODOS: at \(file):\(line) (function \(function)), reason: \(reason)") + #else + context[TODOKey.self] = .init(file: file, line: line) + return context + #endif + } + + private enum TODOKey: BaggageKey { + typealias Value = TODOLocation + static var nameOverride: String? { + return "todo" + } + } +} + +extension Baggage { + /// Provides type-safe access to the baggage's values. + /// This API should ONLY be used inside of accessor implementations. + /// + /// End users rather than using this subscript should use "accessors" the key's author MUST define, following this pattern: + /// + /// internal enum TestID: Baggage.Key { + /// typealias Value = TestID + /// } + /// + /// extension Baggage { + /// public internal(set) var testID: TestID? { + /// get { + /// self[TestIDKey.self] + /// } + /// set { + /// self[TestIDKey.self] = newValue + /// } + /// } + /// } + /// + /// This is in order to enforce a consistent style across projects and also allow for fine grained control over + /// who may set and who may get such property. Just access control to the Key type itself lacks such fidelity. + /// + /// Note that specific baggage and context types MAY (and usually do), offer also a way to set baggage values, + /// however in the most general case it is not required, as some frameworks may only be able to offer reading. + public subscript(_ key: Key.Type) -> Key.Value? { + get { + guard let value = self._storage[AnyBaggageKey(key)] else { return nil } + // safe to force-cast as this subscript is the only way to set a value. + return (value as! Key.Value) + } + set { + self._storage[AnyBaggageKey(key)] = newValue + } + } + + /// Number of contained baggage items. + public var count: Int { + return self._storage.count + } + + /// Calls the given closure for each item contained in the underlying `Baggage`. + /// + /// Order of those invocations is NOT guaranteed and should not be relied on. + /// + /// - Parameter body: A closure invoked with the type erased key and value stored for the key in this baggage. + public func forEach(_ body: (AnyBaggageKey, Any) throws -> Void) rethrows { + try self._storage.forEach { key, value in + try body(key, value) + } + } +} + +extension Baggage: CustomStringConvertible { + /// A context's description prints only keys of the contained values. + /// This is in order to prevent spilling a lot of detailed information of carried values accidentally. + /// + /// `Baggage`s are not intended to be printed "raw" but rather inter-operate with tracing, logging and other systems, + /// which can use the `forEach` function providing access to its underlying values. + public var description: String { + return "\(type(of: self).self)(keys: \(self._storage.map { $0.key.name }))" + } +} + +/// Carried automatically by a "to do" baggage. +/// It can be used to track where a context originated and which "to do" context must be fixed into a real one to avoid this. +public struct TODOLocation { + /// Source file location where the to-do `Baggage` was created + public let file: String + /// Source line location where the to-do `Baggage` was created + public let line: UInt +} diff --git a/Sources/CoreBaggage/BaggageKey.swift b/Sources/CoreBaggage/BaggageKey.swift new file mode 100644 index 0000000..af3e7c5 --- /dev/null +++ b/Sources/CoreBaggage/BaggageKey.swift @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Distributed Tracing Baggage open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +/// `BaggageKey`s are used as keys in a `Baggage`. Their associated type `Value` guarantees type-safety. +/// To give your `BaggageKey` an explicit name you may override the `name` property. +/// +/// In general, `BaggageKey`s should be `internal` or `private` to the part of a system using it. +/// +/// All access to baggage items should be performed through an accessor computed property defined as shown below: +/// +/// /// The Key type should be internal (or private). +/// enum TestIDKey: Baggage.Key { +/// typealias Value = String +/// static var nameOverride: String? { "test-id" } +/// } +/// +/// extension Baggage { +/// /// This is some useful property documentation. +/// public internal(set) var testID: String? { +/// get { +/// self[TestIDKey.self] +/// } +/// set { +/// self[TestIDKey.self] = newValue +/// } +/// } +/// } +/// +/// This pattern allows library authors fine-grained control over which values may be set, and whic only get by end-users. +public protocol BaggageKey { + /// The type of `Value` uniquely identified by this key. + associatedtype Value + + /// The human-readable name of this key. + /// This name will be used instead of the type name when a value is printed. + /// + /// It MAY also be picked up by an instrument (from Swift Tracing) which serializes baggage items and e.g. used as + /// header name for carried metadata. Though generally speaking header names are NOT required to use the nameOverride, + /// and MAY use their well known names for header names etc, as it depends on the specific transport and instrument used. + /// + /// For example, a baggage key representing the W3C "trace-state" header may want to return "trace-state" here, + /// in order to achieve a consistent look and feel of this baggage item throughout logging and tracing systems. + /// + /// Defaults to `nil`. + static var nameOverride: String? { get } +} + +extension BaggageKey { + public static var nameOverride: String? { return nil } +} + +/// A type-erased `BaggageKey` used when iterating through the `Baggage` using its `forEach` method. +public struct AnyBaggageKey { + /// The key's type represented erased to an `Any.Type`. + public let keyType: Any.Type + + private let _nameOverride: String? + + /// A human-readable String representation of the underlying key. + /// If no explicit name has been set on the wrapped key the type name is used. + public var name: String { + return self._nameOverride ?? String(describing: self.keyType.self) + } + + init(_ keyType: Key.Type) where Key: BaggageKey { + self.keyType = keyType + self._nameOverride = keyType.nameOverride + } +} + +extension AnyBaggageKey: Hashable { + public static func == (lhs: AnyBaggageKey, rhs: AnyBaggageKey) -> Bool { + return ObjectIdentifier(lhs.keyType) == ObjectIdentifier(rhs.keyType) + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self.keyType)) + } +} diff --git a/Tests/BaggageLoggingTests/LoggingBaggageContextCarrierTests+XCTest.swift b/Tests/BaggageLoggingTests/LoggingBaggageContextCarrierTests+XCTest.swift deleted file mode 100644 index a4d09a1..0000000 --- a/Tests/BaggageLoggingTests/LoggingBaggageContextCarrierTests+XCTest.swift +++ /dev/null @@ -1,34 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// -// -// LoggingBaggageContextCarrierTests+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 LoggingBaggageContextCarrierTests { - - @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") - static var allTests : [(String, (LoggingBaggageContextCarrierTests) -> () throws -> Void)] { - return [ - ("test_ContextWithLogger_dumpBaggage", test_ContextWithLogger_dumpBaggage), - ("test_ContextWithLogger_log_withBaggage", test_ContextWithLogger_log_withBaggage), - ("test_ContextWithLogger_log_prefersBaggageContextOverExistingLoggerMetadata", test_ContextWithLogger_log_prefersBaggageContextOverExistingLoggerMetadata), - ] - } -} - diff --git a/Tests/BaggageLoggingTests/LoggingBaggageContextCarrierTests.swift b/Tests/BaggageLoggingTests/LoggingBaggageContextCarrierTests.swift deleted file mode 100644 index b630517..0000000 --- a/Tests/BaggageLoggingTests/LoggingBaggageContextCarrierTests.swift +++ /dev/null @@ -1,145 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Baggage -import BaggageLogging -import Logging -import XCTest - -final class LoggingBaggageContextCarrierTests: XCTestCase { - func test_ContextWithLogger_dumpBaggage() throws { - let baggage = BaggageContext.background - let logger = Logger(label: "TheLogger") - - var context: LoggingBaggageContextCarrier = ExampleFrameworkContext(context: baggage, logger: logger) - context.testID = 42 - - func frameworkFunctionDumpsBaggage(param: String, context: LoggingBaggageContextCarrier) -> String { - var s = "" - context.baggage.forEach { key, item in - s += "\(key.name): \(item)\n" - } - return s - } - - let result = frameworkFunctionDumpsBaggage(param: "x", context: context) - XCTAssertEqual( - result, - """ - TestIDKey: 42 - - """ - ) - } - - func test_ContextWithLogger_log_withBaggage() throws { - let baggage = BaggageContext.background - let logging = TestLogging() - let logger = Logger(label: "TheLogger", factory: { label in logging.make(label: label) }) - - var context: LoggingBaggageContextCarrier = ExampleFrameworkContext(context: baggage, logger: logger) - - context.secondTestID = "value" - context.testID = 42 - context.logger.info("Hello") - - context.testID = nil - context.logger.warning("World") - - logging.history.assertExist(level: .info, message: "Hello", metadata: [ - "TestIDKey": .stringConvertible(42), - "secondIDExplicitlyNamed": "value", - ]) - logging.history.assertExist(level: .warning, message: "World", metadata: [ - "secondIDExplicitlyNamed": "value", - ]) - } - - func test_ContextWithLogger_log_prefersBaggageContextOverExistingLoggerMetadata() { - let baggage = BaggageContext.background - let logging = TestLogging() - var logger = Logger(label: "TheLogger", factory: { label in logging.make(label: label) }) - logger[metadataKey: "secondIDExplicitlyNamed"] = "set on logger" - - var context: LoggingBaggageContextCarrier = ExampleFrameworkContext(context: baggage, logger: logger) - - context.secondTestID = "set on baggage" - - context.logger.info("Hello") - - logging.history.assertExist(level: .info, message: "Hello", metadata: [ - "secondIDExplicitlyNamed": "set on baggage", - ]) - } -} - -struct ExampleFrameworkContext: LoggingBaggageContextCarrier { - var baggage: BaggageContext - - private var _logger: Logger - var logger: Logger { - return self._logger.with(context: self.baggage) - } - - init(context baggage: BaggageContext, logger: Logger) { - self.baggage = baggage - self._logger = logger - } -} - -struct CoolFrameworkContext: LoggingBaggageContextCarrier { - private var _logger: Logger = Logger(label: "some frameworks logger") - var logger: Logger { - return self._logger.with(context: self.baggage) - } - - var baggage: BaggageContext = .background - - // framework context defines other values as well - let frameworkField: String = "" - - // including the popular eventLoop - let eventLoop: FakeEventLoop -} - -struct FakeEventLoop {} - -private extension BaggageContextProtocol { - var testID: Int? { - get { - return self[TestIDKey.self] - } - set { - self[TestIDKey.self] = newValue - } - } - - var secondTestID: String? { - get { - return self[SecondTestIDKey.self] - } - set { - self[SecondTestIDKey.self] = newValue - } - } -} - -private enum TestIDKey: BaggageContextKey { - typealias Value = Int -} - -private enum SecondTestIDKey: BaggageContextKey { - typealias Value = String - - static let name: String? = "secondIDExplicitlyNamed" -} diff --git a/Tests/BaggageLoggingTests/TestLogger.swift b/Tests/BaggageLoggingTests/TestLogger.swift deleted file mode 100644 index 9046e05..0000000 --- a/Tests/BaggageLoggingTests/TestLogger.swift +++ /dev/null @@ -1,337 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Logging API open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the Swift Logging API project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift Logging API project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation -@testable import Logging -import XCTest - -/// Copy of swift-log's TestLogging -internal struct TestLogging { - private let _config = Config() // shared among loggers - private let recorder = Recorder() // shared among loggers - - func make(label: String) -> LogHandler { - return TestLogHandler(label: label, config: self.config, recorder: self.recorder) - } - - var config: Config { return self._config } - var history: History { return self.recorder } -} - -internal struct TestLogHandler: LogHandler { - private let logLevelLock = NSLock() - private let metadataLock = NSLock() - private let recorder: Recorder - private let config: Config - private var logger: Logger // the actual logger - - let label: String - init(label: String, config: Config, recorder: Recorder) { - self.label = label - self.config = config - self.recorder = recorder - self.logger = Logger(label: "test", StreamLogHandler.standardOutput(label: label)) - self.logger.logLevel = .debug - } - - func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, source: String, file: String, function: String, line: UInt) { - let metadata = (self._metadataSet ? self.metadata : MDC.global.metadata).merging(metadata ?? [:], uniquingKeysWith: { _, new in new }) - self.logger.log(level: level, message, metadata: metadata, source: source, file: file, function: function, line: line) - self.recorder.record(level: level, metadata: metadata, message: message, source: source) - } - - private var _logLevel: Logger.Level? - var logLevel: Logger.Level { - get { - // get from config unless set - return self.logLevelLock.withLock { self._logLevel } ?? self.config.get(key: self.label) - } - set { - self.logLevelLock.withLock { self._logLevel = newValue } - } - } - - private var _metadataSet = false - private var _metadata = Logger.Metadata() { - didSet { - self._metadataSet = true - } - } - - public var metadata: Logger.Metadata { - get { - // return self.logger.metadata - return self.metadataLock.withLock { self._metadata } - } - set { - // self.logger.metadata = newValue - self.metadataLock.withLock { self._metadata = newValue } - } - } - - // TODO: would be nice to delegate to local copy of logger but StdoutLogger is a reference type. why? - subscript(metadataKey metadataKey: Logger.Metadata.Key) -> Logger.Metadata.Value? { - get { - // return self.logger[metadataKey: metadataKey] - return self.metadataLock.withLock { self._metadata[metadataKey] } - } - set { - // return logger[metadataKey: metadataKey] = newValue - self.metadataLock.withLock { - self._metadata[metadataKey] = newValue - } - } - } -} - -internal class Config { - private static let ALL = "*" - - private let lock = NSLock() - private var storage = [String: Logger.Level]() - - func get(key: String) -> Logger.Level { - return self.get(key) ?? self.get(Config.ALL) ?? Logger.Level.debug - } - - func get(_ key: String) -> Logger.Level? { - guard let value = (self.lock.withLock { self.storage[key] }) else { - return nil - } - return value - } - - func set(key: String = Config.ALL, value: Logger.Level) { - self.lock.withLock { self.storage[key] = value } - } - - func clear() { - self.lock.withLock { self.storage.removeAll() } - } -} - -internal class Recorder: History { - private let lock = NSLock() - private var _entries = [LogEntry]() - - func record(level: Logger.Level, metadata: Logger.Metadata?, message: Logger.Message, source: String) { - self.lock.withLock { - self._entries.append(LogEntry(level: level, metadata: metadata, message: message.description, source: source)) - } - } - - var entries: [LogEntry] { - return self.lock.withLock { return self._entries } - } -} - -internal protocol History { - var entries: [LogEntry] { get } -} - -internal extension History { - func atLevel(level: Logger.Level) -> [LogEntry] { - return self.entries.filter { entry in - level == entry.level - } - } - - var trace: [LogEntry] { - return self.atLevel(level: .debug) - } - - var debug: [LogEntry] { - return self.atLevel(level: .debug) - } - - var info: [LogEntry] { - return self.atLevel(level: .info) - } - - var warning: [LogEntry] { - return self.atLevel(level: .warning) - } - - var error: [LogEntry] { - return self.atLevel(level: .error) - } -} - -internal struct LogEntry { - let level: Logger.Level - let metadata: Logger.Metadata? - let message: String - let source: String -} - -extension History { - func assertExist(level: Logger.Level, - message: String, - metadata: Logger.Metadata? = nil, - source: String? = nil, - file: StaticString = #file, - line: UInt = #line) { - let source = source ?? Logger.currentModule(filePath: "\(file)") - let entry = self.find(level: level, message: message, metadata: metadata, source: source) - XCTAssertNotNil( - entry, - """ - entry not found: \(level), \(source), \(String(describing: metadata)), \(message) - All entries: - \(self.entries.map { "\($0)" }.joined(separator: "\n")) - """, - file: file, - line: line - ) - } - - func assertNotExist(level: Logger.Level, - message: String, - metadata: Logger.Metadata? = nil, - source: String? = nil, - file: StaticString = #file, - line: UInt = #line) { - let source = source ?? Logger.currentModule(filePath: "\(file)") - let entry = self.find(level: level, message: message, metadata: metadata, source: source) - XCTAssertNil( - entry, - "entry was found: \(level), \(source), \(String(describing: metadata)), \(message)", - file: file, - line: line - ) - } - - func find(level: Logger.Level, message: String, metadata: Logger.Metadata? = nil, source: String) -> LogEntry? { - return self.entries.first { entry in - entry.level == level && - entry.message == message && - entry.metadata ?? [:] == metadata ?? [:] && - entry.source == source - } - } -} - -public class MDC { - private let lock = NSLock() - private var storage = [Int: Logger.Metadata]() - - public static var global = MDC() - - private init() {} - - public subscript(metadataKey: String) -> Logger.Metadata.Value? { - get { - return self.lock.withLock { - return self.storage[self.threadId]?[metadataKey] - } - } - set { - self.lock.withLock { - if self.storage[self.threadId] == nil { - self.storage[self.threadId] = Logger.Metadata() - } - self.storage[self.threadId]![metadataKey] = newValue - } - } - } - - public var metadata: Logger.Metadata { - return self.lock.withLock { - return self.storage[self.threadId] ?? [:] - } - } - - public func clear() { - self.lock.withLock { - _ = self.storage.removeValue(forKey: self.threadId) - } - } - - public func with(metadata: Logger.Metadata, _ body: () throws -> Void) rethrows { - metadata.forEach { self[$0] = $1 } - defer { - metadata.keys.forEach { self[$0] = nil } - } - try body() - } - - public func with(metadata: Logger.Metadata, _ body: () throws -> T) rethrows -> T { - metadata.forEach { self[$0] = $1 } - defer { - metadata.keys.forEach { self[$0] = nil } - } - return try body() - } - - // for testing - internal func flush() { - self.lock.withLock { - self.storage.removeAll() - } - } - - private var threadId: Int { - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - return Int(pthread_mach_thread_np(pthread_self())) - #else - return Int(pthread_self()) - #endif - } -} - -internal extension NSLock { - func withLock(_ body: () -> T) -> T { - self.lock() - defer { - self.unlock() - } - return body() - } -} - -internal struct TestLibrary { - private let logger = Logger(label: "TestLibrary") - private let queue = DispatchQueue(label: "TestLibrary") - - public init() {} - - public func doSomething() { - self.logger.info("TestLibrary::doSomething") - } - - public func doSomethingAsync(completion: @escaping () -> Void) { - // libraries that use global loggers and async, need to make sure they propagate the - // logging metadata when creating a new thread - let metadata = MDC.global.metadata - self.queue.asyncAfter(deadline: .now() + 0.1) { - MDC.global.with(metadata: metadata) { - self.logger.info("TestLibrary::doSomethingAsync") - completion() - } - } - } -} diff --git a/Tests/BaggageTests/BaggageContextCarrierTests+XCTest.swift b/Tests/BaggageTests/BaggageContextCarrierTests+XCTest.swift deleted file mode 100644 index e65e119..0000000 --- a/Tests/BaggageTests/BaggageContextCarrierTests+XCTest.swift +++ /dev/null @@ -1,34 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// -// -// BaggageContextCarrierTests+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 BaggageContextCarrierTests { - - @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") - static var allTests : [(String, (BaggageContextCarrierTests) -> () throws -> Void)] { - return [ - ("testBaggageContextSubscript", testBaggageContextSubscript), - ("testBaggageContextForEach", testBaggageContextForEach), - ("testBaggageContextCarriesItself", testBaggageContextCarriesItself), - ] - } -} - diff --git a/Tests/BaggageTests/BaggageContextCarrierTests.swift b/Tests/BaggageTests/BaggageContextCarrierTests.swift deleted file mode 100644 index a2c61d3..0000000 --- a/Tests/BaggageTests/BaggageContextCarrierTests.swift +++ /dev/null @@ -1,67 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Context Propagation open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@testable import Baggage -import XCTest - -final class BaggageContextCarrierTests: XCTestCase { - func testBaggageContextSubscript() { - var carrier = TestFrameworkContext() - - // mutate baggage context through carrier - carrier[TestKey.self] = 42 - XCTAssertEqual(carrier[TestKey.self], 42) - XCTAssertEqual(carrier.baggage[TestKey.self], 42) - - // mutate baggage context directly - carrier.baggage[OtherKey.self] = "test" - XCTAssertEqual(carrier.baggage[OtherKey.self], "test") - XCTAssertEqual(carrier[OtherKey.self], "test") - } - - func testBaggageContextForEach() { - var contents = [AnyBaggageContextKey: Any]() - var carrier = TestFrameworkContext() - - carrier[TestKey.self] = 42 - carrier[OtherKey.self] = "test" - - carrier.forEach { key, value in - contents[key] = value - } - - XCTAssertNotNil(contents[AnyBaggageContextKey(TestKey.self)]) - XCTAssertEqual(contents[AnyBaggageContextKey(TestKey.self)] as? Int, 42) - XCTAssertNotNil(contents[AnyBaggageContextKey(OtherKey.self)]) - XCTAssertEqual(contents[AnyBaggageContextKey(OtherKey.self)] as? String, "test") - } - - func testBaggageContextCarriesItself() { - var context: BaggageContextCarrier = BaggageContext() - - context.baggage[TestKey.self] = 42 - XCTAssertEqual(context.baggage[TestKey.self], 42) - } -} - -private struct TestFrameworkContext: BaggageContextCarrier { - var baggage = BaggageContext() -} - -private enum TestKey: BaggageContextKey { - typealias Value = Int -} - -private enum OtherKey: BaggageContextKey { - typealias Value = String -} diff --git a/Tests/BaggageTests/BaggageContextTests+XCTest.swift b/Tests/CoreBaggageTests/BaggageTests+XCTest.swift similarity index 75% rename from Tests/BaggageTests/BaggageContextTests+XCTest.swift rename to Tests/CoreBaggageTests/BaggageTests+XCTest.swift index 1c4e44e..0a123e6 100644 --- a/Tests/BaggageTests/BaggageContextTests+XCTest.swift +++ b/Tests/CoreBaggageTests/BaggageTests+XCTest.swift @@ -1,8 +1,8 @@ //===----------------------------------------------------------------------===// // -// This source file is part of the Swift Context Propagation open source project +// This source file is part of the Swift Distributed Tracing Baggage open source project // -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors +// Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -11,7 +11,7 @@ // //===----------------------------------------------------------------------===// // -// BaggageContextTests+XCTest.swift +// BaggageTests+XCTest.swift // import XCTest /// @@ -20,10 +20,10 @@ import XCTest /// Do NOT edit this file directly as it will be regenerated automatically when needed. /// -extension BaggageContextTests { +extension BaggageTests { @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") - static var allTests : [(String, (BaggageContextTests) -> () throws -> Void)] { + static var allTests : [(String, (BaggageTests) -> () throws -> Void)] { return [ ("testSubscriptAccess", testSubscriptAccess), ("testRecommendedConvenienceExtension", testRecommendedConvenienceExtension), @@ -31,7 +31,7 @@ extension BaggageContextTests { ("testSingleKeyBaggageDescription", testSingleKeyBaggageDescription), ("testMultiKeysBaggageDescription", testMultiKeysBaggageDescription), ("test_todo_context", test_todo_context), - ("test_todo_empty", test_todo_empty), + ("test_topLevel", test_topLevel), ] } } diff --git a/Tests/BaggageTests/BaggageContextTests.swift b/Tests/CoreBaggageTests/BaggageTests.swift similarity index 50% rename from Tests/BaggageTests/BaggageContextTests.swift rename to Tests/CoreBaggageTests/BaggageTests.swift index d3e7d6b..0396d0a 100644 --- a/Tests/BaggageTests/BaggageContextTests.swift +++ b/Tests/CoreBaggageTests/BaggageTests.swift @@ -1,8 +1,8 @@ //===----------------------------------------------------------------------===// // -// This source file is part of the Swift Context Propagation open source project +// This source file is part of the Swift Distributed Tracing Baggage open source project // -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors +// Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -11,14 +11,14 @@ // //===----------------------------------------------------------------------===// -import Baggage +import CoreBaggage import XCTest -final class BaggageContextTests: XCTestCase { +final class BaggageTests: XCTestCase { func testSubscriptAccess() { let testID = 42 - var baggage = BaggageContext.background + var baggage = Baggage.topLevel XCTAssertNil(baggage[TestIDKey.self]) baggage[TestIDKey.self] = testID @@ -31,7 +31,7 @@ final class BaggageContextTests: XCTestCase { func testRecommendedConvenienceExtension() { let testID = 42 - var baggage = BaggageContext.background + var baggage = Baggage.topLevel XCTAssertNil(baggage.testID) baggage.testID = testID @@ -42,26 +42,26 @@ final class BaggageContextTests: XCTestCase { } func testEmptyBaggageDescription() { - XCTAssertEqual(String(describing: BaggageContext.background), "BaggageContext(keys: [])") + XCTAssertEqual(String(describing: Baggage.topLevel), "Baggage(keys: [])") } func testSingleKeyBaggageDescription() { - var baggage = BaggageContext.background + var baggage = Baggage.topLevel baggage.testID = 42 - XCTAssertEqual(String(describing: baggage), #"BaggageContext(keys: ["TestIDKey"])"#) + XCTAssertEqual(String(describing: baggage), #"Baggage(keys: ["TestIDKey"])"#) } func testMultiKeysBaggageDescription() { - var baggage = BaggageContext.background + var baggage = Baggage.topLevel baggage.testID = 42 baggage[SecondTestIDKey.self] = "test" let description = String(describing: baggage) - XCTAssert(description.starts(with: "BaggageContext(keys: [")) + XCTAssert(description.starts(with: "Baggage(keys: ["), "Was: \(description)") // use contains instead of `XCTAssertEqual` because the order is non-predictable (Dictionary) - XCTAssert(description.contains("TestIDKey")) - XCTAssert(description.contains("ExplicitKeyName")) + XCTAssert(description.contains("TestIDKey"), "Was: \(description)") + XCTAssert(description.contains("ExplicitKeyName"), "Was: \(description)") } // ==== ------------------------------------------------------------------------------------------------------------ @@ -69,45 +69,33 @@ final class BaggageContextTests: XCTestCase { func test_todo_context() { // the to-do context can be used to record intentions for why a context could not be passed through - let context = BaggageContext.TODO("#1245 Some other library should be adjusted to pass us context") + let context = Baggage.TODO("#1245 Some other library should be adjusted to pass us context") _ = context // avoid "not used" warning - - // TODO: Can't work with protocols; re-consider the entire carrier approach... Context being a Baggage + Logger, and a specific type. -// func take(context: BaggageContextProtocol) { -// _ = context // ignore -// } -// take(context: .TODO("pass from request instead")) } - func test_todo_empty() { - let context = BaggageContext.background + func test_topLevel() { + let context = Baggage.topLevel _ = context // avoid "not used" warning - - // TODO: Can't work with protocols; re-consider the entire carrier approach... Context being a Baggage + Logger, and a specific type. - // static member 'empty' cannot be used on protocol metatype 'BaggageContextProtocol.Protocol' -// func take(context: BaggageContextProtocol) { -// _ = context // ignore -// } -// take(context: .background) } } -private enum TestIDKey: BaggageContextKey { +private enum TestIDKey: Baggage.Key { typealias Value = Int } -private extension BaggageContext { +private extension Baggage { var testID: Int? { get { return self[TestIDKey.self] - } set { + } + set { self[TestIDKey.self] = newValue } } } -private enum SecondTestIDKey: BaggageContextKey { +private enum SecondTestIDKey: Baggage.Key { typealias Value = String - static let name: String? = "ExplicitKeyName" + static let nameOverride: String? = "ExplicitKeyName" } diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 6d7331d..3118537 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1,8 +1,8 @@ //===----------------------------------------------------------------------===// // -// This source file is part of the Swift Context Propagation open source project +// This source file is part of the Swift Distributed Tracing Baggage open source project // -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors +// Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -21,8 +21,7 @@ import XCTest /// #if os(Linux) || os(FreeBSD) - @testable import BaggageLoggingTests - @testable import BaggageTests + @testable import CoreBaggageTests // This protocol is necessary to we can call the 'run' method (on an existential of this protocol) // without the compiler noticing that we're calling a deprecated function. @@ -33,9 +32,7 @@ class LinuxMainRunnerImpl: LinuxMainRunner { @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") func run() { XCTMain([ - testCase(BaggageContextCarrierTests.allTests), - testCase(BaggageContextTests.allTests), - testCase(LoggingBaggageContextCarrierTests.allTests), + testCase(BaggageTests.allTests), ]) } } diff --git a/scripts/generate_linux_tests.rb b/scripts/generate_linux_tests.rb index 9ef8883..b8b6f10 100755 --- a/scripts/generate_linux_tests.rb +++ b/scripts/generate_linux_tests.rb @@ -34,9 +34,9 @@ def header(fileName) string = <<-eos //===----------------------------------------------------------------------===// // -// This source file is part of the Swift Context Propagation open source project +// This source file is part of the Swift Distributed Tracing Baggage open source project // -// Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors +// Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information diff --git a/scripts/sanity.sh b/scripts/sanity.sh index 0b1ffa6..4ad902e 100755 --- a/scripts/sanity.sh +++ b/scripts/sanity.sh @@ -1,9 +1,9 @@ #!/bin/bash ##===----------------------------------------------------------------------===## ## -## This source file is part of the Swift Context Propagation open source project +## This source file is part of the Swift Distributed Tracing Baggage open source project ## -## Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors +## Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors ## Licensed under Apache License v2.0 ## ## See LICENSE.txt for license information diff --git a/scripts/validate_format.sh b/scripts/validate_format.sh index d81282d..20d7f73 100755 --- a/scripts/validate_format.sh +++ b/scripts/validate_format.sh @@ -1,9 +1,9 @@ #!/bin/bash ##===----------------------------------------------------------------------===## ## -## This source file is part of the Swift Context Propagation open source project +## This source file is part of the Swift Distributed Tracing Baggage open source project ## -## Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors +## Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors ## Licensed under Apache License v2.0 ## ## See LICENSE.txt for license information diff --git a/scripts/validate_license_headers.sh b/scripts/validate_license_headers.sh index e237a67..cd1ece9 100755 --- a/scripts/validate_license_headers.sh +++ b/scripts/validate_license_headers.sh @@ -1,9 +1,9 @@ #!/bin/bash ##===----------------------------------------------------------------------===## ## -## This source file is part of the Swift Context Propagation open source project +## This source file is part of the Swift Distributed Tracing Baggage open source project ## -## Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors +## Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors ## Licensed under Apache License v2.0 ## ## See LICENSE.txt for license information @@ -50,9 +50,9 @@ for language in swift-or-c bash dtrace; do cat > "$tmp" <<"EOF" //===----------------------------------------------------------------------===// // -// This source file is part of the Swift Context Propagation open source project +// This source file is part of the Swift Distributed Tracing Baggage open source project // -// Copyright (c) YEARS Apple Inc. and the Swift Baggage Context project authors +// Copyright (c) YEARS Apple Inc. and the Swift Distributed Tracing Baggage project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -68,9 +68,9 @@ EOF #!/bin/bash ##===----------------------------------------------------------------------===## ## -## This source file is part of the Swift Context Propagation open source project +## This source file is part of the Swift Distributed Tracing Baggage open source project ## -## Copyright (c) YEARS Apple Inc. and the Swift Baggage Context project authors +## Copyright (c) YEARS Apple Inc. and the Swift Distributed Tracing Baggage project authors ## Licensed under Apache License v2.0 ## ## See LICENSE.txt for license information @@ -86,9 +86,9 @@ EOF #!/usr/sbin/dtrace -q -s /*===----------------------------------------------------------------------===* * - * This source file is part of the Swift Context Propagation open source project + * This source file is part of the Swift Distributed Tracing Baggage open source project * - * Copyright (c) YEARS Apple Inc. and the Swift Baggage Context project authors + * Copyright (c) YEARS Apple Inc. and the Swift Distributed Tracing Baggage project authors * Licensed under Apache License v2.0 * * See LICENSE.txt for license information diff --git a/scripts/validate_naming.sh b/scripts/validate_naming.sh index 243d78e..19c4c3d 100755 --- a/scripts/validate_naming.sh +++ b/scripts/validate_naming.sh @@ -1,9 +1,9 @@ #!/bin/bash ##===----------------------------------------------------------------------===## ## -## This source file is part of the Swift Context Propagation open source project +## This source file is part of the Swift Distributed Tracing Baggage open source project ## -## Copyright (c) 2020 Apple Inc. and the Swift Baggage Context project authors +## Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing Baggage project authors ## Licensed under Apache License v2.0 ## ## See LICENSE.txt for license information