├── .gitignore
├── LICENSE.txt
├── Package.swift
├── README.md
├── Sources
├── CwlFrameAddress
│ ├── CwlFrameAddress.c
│ └── include
│ │ └── CwlFrameAddress.h
├── CwlUtils
│ ├── CwlAddressInfo.swift
│ ├── CwlCaseNameCodable.swift
│ ├── CwlCollection.swift
│ ├── CwlCustomExecutionContext.swift
│ ├── CwlDebugContext.swift
│ ├── CwlDeferredWork.swift
│ ├── CwlDeque.swift
│ ├── CwlDispatch.swift
│ ├── CwlExec.swift
│ ├── CwlExecutionType.swift
│ ├── CwlFew.swift
│ ├── CwlKeyValueObserver.swift
│ ├── CwlLifetime.swift
│ ├── CwlMutex.swift
│ ├── CwlOnDelete.swift
│ ├── CwlRandom.swift
│ ├── CwlResult.swift
│ ├── CwlScalarScanner.swift
│ ├── CwlSerializingContext.swift
│ ├── CwlStackFrame.swift
│ ├── CwlSysctl.swift
│ ├── CwlUnanticipatedError.swift
│ ├── CwlUtils.h
│ ├── CwlWrappers.swift
│ └── Info.plist
└── ReferenceRandomGenerators
│ ├── include
│ └── ReferenceRandomGenerators.h
│ ├── mt19937-64.c
│ └── xoshiro256starstar.c
└── Tests
├── CwlUtilsPerformanceTests
├── CwlDequePerformanceTests.swift
├── CwlMutexPerformanceTests.swift
└── CwlRandomPerformanceTests.swift
├── CwlUtilsTests
├── CwlAddressInfoTests.swift
├── CwlCaseNameCodableTests.swift
├── CwlCollectionTests.swift
├── CwlDebugContextTests.swift
├── CwlDeferredWorkTests.swift
├── CwlDequeTests.swift
├── CwlDispatchTests.swift
├── CwlExecTests.swift
├── CwlKeyValueObserverTests.swift
├── CwlMutexTests.swift
├── CwlOnDeleteTests.swift
├── CwlRandomTests.swift
├── CwlScalarScannerTests.swift
├── CwlStackFrameTests.swift
├── CwlSysctlTests.swift
├── CwlUnanticipatedErrorTests.swift
├── CwlUtilsTests-BridgingHeader.h
├── CwlWrapperTests.swift
└── Info.plist
├── CwlUtils_iOSTestApp
├── AppDelegate.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
└── ViewController.swift
├── CwlUtils_iOSTestAppUITests
└── CwlUtils_iOSTestAppUITests.swift
├── CwlUtils_macOSTestApp
├── AppDelegate.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ └── Main.storyboard
├── Info.plist
└── ViewController.swift
└── CwlUtils_macOSTestAppUITests
└── CwlUtils_macOSTestAppUITests.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | project.xcworkspace/
3 | *.xcscmblueprint
4 | xcuserdata/
5 | .build
6 | Package.pins
7 | Package.resolved
8 | Packages/
9 | .swiftpm
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright © 2017 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12 | SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 |
17 |
18 |
19 | Testing targets (not the release build of CwlUtils) include code under the following two licenses:
20 |
21 | /*
22 | A C-program for MT19937-64 (2004/9/29 version).
23 | Coded by Takuji Nishimura and Makoto Matsumoto.
24 |
25 | This is a 64-bit version of Mersenne Twister pseudorandom number
26 | generator.
27 |
28 | Before using, initialize the state by using init_genrand64(seed)
29 | or init_by_array64(init_key, key_length).
30 |
31 | Copyright (C) 2004, Makoto Matsumoto and Takuji Nishimura,
32 | All rights reserved.
33 |
34 | Redistribution and use in source and binary forms, with or without
35 | modification, are permitted provided that the following conditions
36 | are met:
37 |
38 | 1. Redistributions of source code must retain the above copyright
39 | notice, this list of conditions and the following disclaimer.
40 |
41 | 2. Redistributions in binary form must reproduce the above copyright
42 | notice, this list of conditions and the following disclaimer in the
43 | documentation and/or other materials provided with the distribution.
44 |
45 | 3. The names of its contributors may not be used to endorse or promote
46 | products derived from this software without specific prior written
47 | permission.
48 |
49 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
50 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
51 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
52 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
53 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
54 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
55 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
56 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
57 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
58 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
59 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60 |
61 | References:
62 | T. Nishimura, ``Tables of 64-bit Mersenne Twisters''
63 | ACM Transactions on Modeling and
64 | Computer Simulation 10. (2000) 348--357.
65 | M. Matsumoto and T. Nishimura,
66 | ``Mersenne Twister: a 623-dimensionally equidistributed
67 | uniform pseudorandom number generator''
68 | ACM Transactions on Modeling and
69 | Computer Simulation 8. (Jan. 1998) 3--30.
70 |
71 | Any feedback is very welcome.
72 | http://www.math.hiroshima-u.ac.jp/~m-mat/MT/emt.html
73 | email: m-mat @ math.sci.hiro
74 |
75 |
76 |
77 | /* Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org)
78 |
79 | To the extent possible under law, the author has dedicated all copyright
80 | and related and neighboring rights to this software to the public domain
81 | worldwide. This software is distributed without any warranty.
82 |
83 | See . */
84 | shima-u.ac.jp (remove spaces)
85 | */
86 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "CwlUtils",
6 | products: [
7 | .library(name: "CwlUtils", targets: ["CwlUtils"]),
8 | ],
9 | dependencies: [
10 | .package(url: "https://github.com/mattgallagher/CwlPreconditionTesting.git", from: "2.0.0"),
11 | ],
12 | targets: [
13 | .target(
14 | name: "CwlUtils",
15 | dependencies: [
16 | .target(name: "CwlFrameAddress"),
17 | .target(name: "ReferenceRandomGenerators")
18 | ]
19 | ),
20 | .target(name: "CwlFrameAddress"),
21 | .target(name: "ReferenceRandomGenerators"),
22 | .testTarget(
23 | name: "CwlUtilsTests",
24 | dependencies: [
25 | .target(name: "CwlUtils"),
26 | .product(name: "CwlPreconditionTesting")
27 | ]
28 | ),
29 | ]
30 | )
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CwlUtils
2 |
3 | A collection of utilities written as part of articles on [Cocoa with Love](https://www.cocoawithlove.com)
4 |
5 | ## Included functionality
6 |
7 | The following features are included in the library.
8 |
9 | * [CwlStackFrame.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlStackFrame.swift) from [Better stack traces in Swift](https://cocoawithlove.com/blog/2016/02/28/stack-traces-in-swift.html)
10 | * [CwlSysctl.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlSysctl.swift) from [Gathering system information in Swift with sysctl](https://www.cocoawithlove.com/blog/2016/03/08/swift-wrapper-for-sysctl.html)
11 | * [CwlUnanticipatedError.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlUnanticipatedError.swift) from [Presenting unanticipated errors to users](https://www.cocoawithlove.com/blog/2016/04/14/error-recovery-attempter.html)
12 | * [CwlScalarScanner.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlScalarScanner.swift) from [Swift name demangling: C++ vs Swift for parsing](https://www.cocoawithlove.com/blog/2016/05/01/swift-name-demangling.html)
13 | * [CwlRandom.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlRandom.swift) from [Random number generators in Swift](https://www.cocoawithlove.com/blog/2016/05/19/random-numbers.html)
14 | * [CwlMutex.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlMutex.swift) from [Mutexes and closure capture in Swift](https://www.cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html)
15 | * [CwlDispatch.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlDispatch.swift) from [Design patterns for safe timer usage](https://www.cocoawithlove.com/blog/2016/07/30/timer-problems.html)
16 | * [CwlResult.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlResult.swift) from [Values and errors, part 1: 'Result' in Swift](https://www.cocoawithlove.com/blog/2016/08/21/result-types-part-one.html)
17 | * [CwlDeque.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlDeque.swift) from [Optimizing a copy-on-write double-ended queue in Swift](https://www.cocoawithlove.com/blog/2016/09/22/deque.html)
18 | * [CwlExec.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlExec.swift) from [Specifying function execution contexts](https://www.cocoawithlove.com/blog/specifying-execution-contexts.html)
19 | * [CwlDebugContext.swift](https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlDebugContext.swift) from [Testing actions over time](https://www.cocoawithlove.com/blog/testing-actions-over-time.html)
20 |
21 | ## Adding to your project
22 |
23 | The CwlUtils library requires the [Swift Package Manager](#swift-package-manager). Minimum requirements are iOS 8 or macOS 10.10 and Swift 5.0.
24 |
25 | Add the following to the `dependencies` array in your "Package.swift" file:
26 |
27 | .package(url: "https://github.com/mattgallagher/CwlUtils.git", from: Version(3, 0, 0)),
28 |
29 | > NOTE: even though this git repository includes its dependencies in the Dependencies folder, building via the Swift Package manager fetches and builds these dependencies independently.
30 |
31 | ## CocoaPods and Carthage
32 |
33 | Up to version 2.2.1, this library supported CocoaPods and Carthage. If you wish to use these package managers, you can check out the [CwlUtils 2.2.1 tag](https://github.com/mattgallagher/CwlUtils/releases/tag/2.2.1)
34 |
--------------------------------------------------------------------------------
/Sources/CwlFrameAddress/CwlFrameAddress.c:
--------------------------------------------------------------------------------
1 | //
2 | // CwlFrameAddress.c
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/26.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | #include "CwlFrameAddress.h"
22 |
23 | uintptr_t frame_address()
24 | {
25 | return (uintptr_t)__builtin_frame_address(1);
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/CwlFrameAddress/include/CwlFrameAddress.h:
--------------------------------------------------------------------------------
1 | //
2 | // CwlFrameAddress.h
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/26.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | #include
22 |
23 | #ifndef CwlFrameAddress_h
24 | #define CwlFrameAddress_h
25 |
26 | uintptr_t frame_address(void);
27 |
28 | #endif /* CwlFrameAddress_h */
29 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlAddressInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlAddressInfo.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/26.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | /// A wrapper around dl_info, used for symbolicating instruction addresses.
24 | public struct AddressInfo {
25 | private let info: dl_info
26 |
27 | /// Address for which this struct was constructed
28 | public let address: UInt
29 |
30 | /// Construct for an address
31 | public init(address: UInt) {
32 | self.address = address
33 |
34 | var i = dl_info()
35 | dladdr(UnsafeRawPointer(bitPattern: address), &i)
36 | self.info = i
37 | }
38 |
39 | /// -returns: the "image" (shared object pathname) for the instruction
40 | public var image: String {
41 | if let dli_fname = info.dli_fname, let fname = String(validatingUTF8: dli_fname), let _ = fname.range(of: "/", options: .backwards, range: nil, locale: nil) {
42 | return (fname as NSString).lastPathComponent
43 | } else {
44 | return "???"
45 | }
46 | }
47 |
48 | /// - returns: the symbol nearest the address
49 | public var symbol: String {
50 | if let dli_sname = info.dli_sname, let sname = String(validatingUTF8: dli_sname) {
51 | return sname
52 | } else if let dli_fname = info.dli_fname, let _ = String(validatingUTF8: dli_fname) {
53 | return self.image
54 | } else {
55 | return String(format: "0x%1x", UInt(bitPattern: info.dli_saddr))
56 | }
57 | }
58 |
59 | /// - returns: the address' offset relative to the nearest symbol
60 | public var offset: Int {
61 | if let dli_sname = info.dli_sname, let _ = String(validatingUTF8: dli_sname) {
62 | return Int(address - UInt(bitPattern: info.dli_saddr))
63 | } else if let dli_fname = info.dli_fname, let _ = String(validatingUTF8: dli_fname) {
64 | return Int(address - UInt(bitPattern: info.dli_fbase))
65 | } else {
66 | return Int(address - UInt(bitPattern: info.dli_saddr))
67 | }
68 | }
69 |
70 | /// - parameter index: the stack frame index
71 | /// - returns: a formatted string matching that used by NSThread.callStackSymbols
72 | public func formattedDescription(index: Int) -> String {
73 | return self.image.utf8CString.withUnsafeBufferPointer { (imageBuffer: UnsafeBufferPointer) -> String in
74 | #if arch(x86_64) || arch(arm64)
75 | return String(format: "%-4ld%-35s 0x%016llx %@ + %ld", index, UInt(bitPattern: imageBuffer.baseAddress), self.address, self.symbol, self.offset)
76 | #else
77 | return String(format: "%-4d%-35s 0x%08lx %@ + %d", index, UInt(bitPattern: imageBuffer.baseAddress), self.address, self.symbol, self.offset)
78 | #endif
79 | }
80 | }
81 | }
82 |
83 | /// When applied to the output of callStackReturnAddresses, produces identical output to the execinfo function "backtrace_symbols" or NSThread.callStackSymbols
84 | /// - parameter addresses: an array of memory addresses, generally as produced by `callStackReturnAddresses`
85 | /// - returns: an array of formatted, symbolicated stack frame descriptions.
86 | public func symbolsForCallStack(addresses: [UInt]) -> [String] {
87 | return Array(addresses.enumerated().map { tuple -> String in
88 | return AddressInfo(address: tuple.element).formattedDescription(index: tuple.offset)
89 | })
90 | }
91 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlCaseNameCodable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlCaseNameCodable.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 9/3/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | private struct EmptyKeyedDecodingContainer: KeyedDecodingContainerProtocol {
12 | typealias Key = K
13 |
14 | var codingPath: [CodingKey] { return [] }
15 | var allKeys: [K] { return [] }
16 |
17 | func contains(_ key: K) -> Bool {
18 | return false
19 | }
20 |
21 | private func keyNotFoundError(_ key: K) -> Error {
22 | return DecodingError.keyNotFound(key, DecodingError.Context.init(codingPath: [], debugDescription: "EmptyKeyedDecodingContainer contains no values"))
23 | }
24 |
25 | func decodeNil(forKey key: K) throws -> Bool { throw keyNotFoundError(key) }
26 | func decode(_ type: Bool.Type, forKey key: K) throws -> Bool { throw keyNotFoundError(key) }
27 | func decode(_ type: String.Type, forKey key: K) throws -> String { throw keyNotFoundError(key) }
28 | func decode(_ type: Double.Type, forKey key: K) throws -> Double { throw keyNotFoundError(key) }
29 | func decode(_ type: Float.Type, forKey key: K) throws -> Float { throw keyNotFoundError(key) }
30 | func decode(_ type: Int.Type, forKey key: K) throws -> Int { throw keyNotFoundError(key) }
31 | func decode(_ type: Int8.Type, forKey key: K) throws -> Int8 { throw keyNotFoundError(key) }
32 | func decode(_ type: Int16.Type, forKey key: K) throws -> Int16 { throw keyNotFoundError(key) }
33 | func decode(_ type: Int32.Type, forKey key: K) throws -> Int32 { throw keyNotFoundError(key) }
34 | func decode(_ type: Int64.Type, forKey key: K) throws -> Int64 { throw keyNotFoundError(key) }
35 | func decode(_ type: UInt.Type, forKey key: K) throws -> UInt { throw keyNotFoundError(key) }
36 | func decode(_ type: UInt8.Type, forKey key: K) throws -> UInt8 { throw keyNotFoundError(key) }
37 | func decode(_ type: UInt16.Type, forKey key: K) throws -> UInt16 { throw keyNotFoundError(key) }
38 | func decode(_ type: UInt32.Type, forKey key: K) throws -> UInt32 { throw keyNotFoundError(key) }
39 | func decode(_ type: UInt64.Type, forKey key: K) throws -> UInt64 { throw keyNotFoundError(key) }
40 | func decode(_ type: T.Type, forKey key: K) throws -> T where T : Decodable { throw keyNotFoundError(key) }
41 | func nestedContainer(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer where NestedKey : CodingKey { throw keyNotFoundError(key) }
42 | func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer { throw keyNotFoundError(key) }
43 | func superDecoder() throws -> Decoder { fatalError() }
44 | func superDecoder(forKey key: K) throws -> Decoder { throw keyNotFoundError(key) }
45 | }
46 |
47 | public extension KeyedDecodingContainer {
48 | static func empty() -> KeyedDecodingContainer {
49 | return KeyedDecodingContainer(EmptyKeyedDecodingContainer())
50 | }
51 | }
52 |
53 | /// A protocol that enums with associated values can adopt to help in implementing Codable conformance.
54 | /// The downside is that there are some runtime enforced requirements:
55 | /// 1. the `CaseName` associated type must be raw constructible from each of the case names in `CaseNameCodable`
56 | /// 2. the `decode(from:)` method must validly construct each `CaseNameCodable`
57 | /// NOTE: the encoded value will be `nil` if the associated value has no contents or there is no associated value. In these cases, you shouldn't attempt to read the value.
58 | public protocol CaseNameCodable: Codable {
59 | associatedtype CaseName: CaseNameDecoder where CaseName.AssociatedEnum == Self
60 | }
61 |
62 | public protocol CaseNameDecoder: Codable, CodingKey, RawRepresentable where RawValue == String {
63 | associatedtype AssociatedEnum
64 | func decode(from container: KeyedDecodingContainer) throws -> AssociatedEnum
65 | }
66 |
67 | public extension CaseNameCodable {
68 | private func caseName(from mirror: Mirror) -> CaseName {
69 | let label = mirror.children.first?.label ?? String(describing: self)
70 | guard let key = CaseName(rawValue: label) else {
71 | fatalError("Unable to find a CaseName for \(self) matching \(label)")
72 | }
73 | return key
74 | }
75 |
76 | var caseName: CaseName {
77 | return caseName(from: Mirror(reflecting: self))
78 | }
79 |
80 | func encode(to encoder: Encoder) throws {
81 | let mirror = Mirror(reflecting: self)
82 | let key = caseName(from: mirror)
83 | var container = encoder.container(keyedBy: CaseName.self)
84 | if let value = mirror.children.first?.value as? Encodable {
85 | try container.encode(EncodableWrapper(value: value), forKey: key)
86 | } else {
87 | try container.encodeNil(forKey: key)
88 | }
89 | }
90 |
91 | init(from decoder: Decoder) throws {
92 | let container = try decoder.container(keyedBy: CaseName.self)
93 | guard let key = container.allKeys.first else {
94 | throw DecodingError.dataCorrupted(DecodingError.Context.init(codingPath: decoder.codingPath, debugDescription: "Missing enum key"))
95 | }
96 | self = try key.decode(from: container)
97 | }
98 | }
99 |
100 | private struct EncodableWrapper: Encodable {
101 | let value: Encodable
102 |
103 | func encode(to encoder: Encoder) throws {
104 | try value.encode(to: encoder)
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlCollection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlCollection.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | extension Optional {
24 | /// Reurns true if the current optional is nil. Useful for testing for nil at a specific point during chained unwrapping of nested optionals.
25 | public var isNil: Bool {
26 | return self == nil
27 | }
28 | }
29 |
30 | extension Collection {
31 | /// Returns the element at the specified index iff it is within bounds, otherwise nil.
32 | public func at(_ i: Index) -> Iterator.Element? {
33 | return (i >= startIndex && i < endIndex) ? self[i] : nil
34 | }
35 | }
36 |
37 | extension Collection {
38 | /// Constrains the range to the indices of self and returns a SubSequence.
39 | public func at(_ range: Range) -> SubSequence {
40 | let start = (range.lowerBound >= startIndex) ? (range.lowerBound < endIndex ? range.lowerBound : endIndex) : startIndex
41 | let end = (range.upperBound < endIndex) ? (range.upperBound > startIndex ? range.upperBound : startIndex) : endIndex
42 | return self[start..) -> SubSequence {
49 | let start = (range.lowerBound >= startIndex) ? (range.lowerBound < endIndex ? range.lowerBound : endIndex) : startIndex
50 | let end = (range.upperBound < endIndex) ? (range.upperBound > startIndex ? range.upperBound : startIndex) : endIndex
51 | return self[start.. Self {
62 | var result = self
63 | result.append(newElement)
64 | return result
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlDeferredWork.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlDeferredWork.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 5/10/2015.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | // This type is designed for guarding against mutex re-entrancy by following two simple rules:
24 | //
25 | // 1. No user "work" (functions or closures) should be invoked inside a private mutex
26 | // 2. No user supplied data should be released inside a private mutex
27 | //
28 | // To facilitate these requirements, any user "work" or data ownership should be handled inside `DeferredWork` blocks. These blocks allow this user code to be queued in the desired order but since the `runWork` function should only be called outside the mutex, these blocks run safely outside the mutex.
29 | //
30 | // This pattern has two associated risks:
31 | // 1. If the deferred work calls back into the mutex, it must be able to ensure that it is still relevant (hasn't been superceded by an action that may have occurred between the end of the mutex and the performing of the `DeferredWork`. This may involve a token (inside the mutex, only the most recent token is accepted) or the mutex queueing further requests until the most recent `DeferredWork` completes.
32 | // 2. The `runWork` must be manually invoked. Automtic invocation (e.g in the `deinit` of a lifetime managed `class` instance) would add heap allocation overhead and would also be easy to accidentally release at the wrong point (inside the mutex) causing erratic problems. Instead, the `runWork` is guarded with a `DEBUG`-only `OnDelete` check that ensures that the `runWork` has been correctly invoked by the time the `DeferredWork` falls out of scope.
33 | public struct DeferredWork {
34 | typealias PossibleWork = Few<() -> Void>
35 |
36 | var work: PossibleWork
37 |
38 | #if DEBUG
39 | let invokeCheck: OnDelete = { () -> OnDelete in
40 | var sourceStack = Thread.callStackReturnAddresses
41 | return OnDelete {
42 | let symbols = symbolsForCallStack(addresses: sourceStack.map { $0.uintValue })
43 | preconditionFailure("Failed to perform work deferred at location:\n" + symbols.joined(separator: "\n"))
44 | }
45 | }()
46 | #endif
47 |
48 | public init() {
49 | work = .none
50 | }
51 |
52 | public init(initial: @escaping () -> Void) {
53 | work = .single(initial)
54 | }
55 |
56 | public mutating func append(_ other: DeferredWork) {
57 | #if DEBUG
58 | precondition(invokeCheck.isValid && other.invokeCheck.isValid, "Work appended to an already cancelled/invoked DeferredWork")
59 | other.invokeCheck.invalidate()
60 | #endif
61 |
62 | switch other.work {
63 | case .none: break
64 | case .single(let otherWork): self.append(otherWork)
65 | case .array(let otherWork):
66 | switch work {
67 | case .none: work = .array(otherWork)
68 | case .single(let existing):
69 | var newWork: Array<() -> Void> = [existing]
70 | newWork.append(contentsOf: otherWork)
71 | work = .array(newWork)
72 | case .array(var existing):
73 | work = .none
74 | existing.append(contentsOf: otherWork)
75 | work = .array(existing)
76 | }
77 | }
78 | }
79 |
80 | public mutating func append(_ additionalWork: @escaping () -> Void) {
81 | #if DEBUG
82 | precondition(invokeCheck.isValid, "Work appended to an already cancelled/invoked DeferredWork")
83 | #endif
84 |
85 | switch work {
86 | case .none: work = .single(additionalWork)
87 | case .single(let existing): work = .array([existing, additionalWork])
88 | case .array(var existing):
89 | work = .none
90 | existing.append(additionalWork)
91 | work = .array(existing)
92 | }
93 | }
94 |
95 | public mutating func runWork() {
96 | #if DEBUG
97 | precondition(invokeCheck.isValid, "Work run multiple times")
98 | invokeCheck.invalidate()
99 | #endif
100 |
101 | switch work {
102 | case .none: break
103 | case .single(let w): w()
104 | case .array(let ws):
105 | for w in ws {
106 | w()
107 | }
108 | }
109 | work = .none
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlDispatch.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlDispatch.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/07/29.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | public extension DispatchSource {
24 | // An overload of timer that immediately sets the handler and schedules the timer
25 | class func singleTimer(interval: DispatchTimeInterval, leeway: DispatchTimeInterval = .nanoseconds(0), queue: DispatchQueue, handler: @escaping () -> Void) -> DispatchSourceTimer {
26 | let result = DispatchSource.makeTimerSource(queue: queue)
27 | result.setEventHandler(handler: handler)
28 | #if swift(>=4)
29 | result.schedule(deadline: DispatchTime.now() + interval, leeway: leeway)
30 | #else
31 | result.scheduleOneshot(deadline: DispatchTime.now() + interval, leeway: leeway)
32 | #endif
33 | result.resume()
34 | return result
35 | }
36 |
37 | // An overload of timer that always uses the default global queue (because it is intended to enter the appropriate mutex as a separate step) and passes a user-supplied Int to the handler function to allow ignoring callbacks if cancelled or rescheduled before mutex acquisition.
38 | class func singleTimer(parameter: T, interval: DispatchTimeInterval, leeway: DispatchTimeInterval = .nanoseconds(0), queue: DispatchQueue = DispatchQueue.global(), handler: @escaping (T) -> Void) -> DispatchSourceTimer {
39 | let result = DispatchSource.makeTimerSource(queue: queue)
40 | result.scheduleOneshot(parameter: parameter, interval: interval, leeway: leeway, handler: handler)
41 | result.resume()
42 | return result
43 | }
44 |
45 | // An overload of timer that immediately sets the handler and schedules the timer
46 | class func repeatingTimer(interval: DispatchTimeInterval, leeway: DispatchTimeInterval = .nanoseconds(0), queue: DispatchQueue = DispatchQueue.global(), handler: @escaping () -> Void) -> DispatchSourceTimer {
47 | let result = DispatchSource.makeTimerSource(queue: queue)
48 | result.setEventHandler(handler: handler)
49 | #if swift(>=4)
50 | result.schedule(deadline: DispatchTime.now() + interval, repeating: interval, leeway: leeway)
51 | #else
52 | result.scheduleRepeating(deadline: DispatchTime.now() + interval, interval: interval, leeway: leeway)
53 | #endif
54 | result.resume()
55 | return result
56 | }
57 |
58 | // An overload of timer that always uses the default global queue (because it is intended to enter the appropriate mutex as a separate step) and passes a user-supplied Int to the handler function to allow ignoring callbacks if cancelled or rescheduled before mutex acquisition.
59 | class func repeatingTimer(parameter: T, interval: DispatchTimeInterval, leeway: DispatchTimeInterval = .nanoseconds(0), queue: DispatchQueue = DispatchQueue.global(), handler: @escaping (T) -> Void) -> DispatchSourceTimer {
60 | let result = DispatchSource.makeTimerSource(queue: queue)
61 | result.scheduleRepeating(parameter: parameter, interval: interval, leeway: leeway, handler: handler)
62 | result.resume()
63 | return result
64 | }
65 | }
66 |
67 | public extension DispatchSourceTimer {
68 | // An overload of scheduleOneshot that updates the handler function with a new user-supplied parameter when it changes the expiry deadline
69 | func scheduleOneshot(parameter: T, interval: DispatchTimeInterval, leeway: DispatchTimeInterval = .nanoseconds(0), handler: @escaping (T) -> Void) {
70 | suspend()
71 | setEventHandler { handler(parameter) }
72 | #if swift(>=4)
73 | schedule(deadline: DispatchTime.now() + interval, leeway: leeway)
74 | #else
75 | scheduleOneshot(deadline: DispatchTime.now() + interval, leeway: leeway)
76 | #endif
77 | resume()
78 | }
79 |
80 | // An overload of scheduleOneshot that updates the handler function with a new user-supplied parameter when it changes the expiry deadline
81 | func scheduleRepeating(parameter: T, interval: DispatchTimeInterval, leeway: DispatchTimeInterval = .nanoseconds(0), handler: @escaping (T) -> Void) {
82 | suspend()
83 | setEventHandler { handler(parameter) }
84 | #if swift(>=4)
85 | schedule(deadline: DispatchTime.now() + interval, repeating: interval, leeway: leeway)
86 | #else
87 | scheduleRepeating(deadline: DispatchTime.now() + interval, interval: interval, leeway: leeway)
88 | #endif
89 | resume()
90 | }
91 | }
92 |
93 | public extension DispatchTime {
94 | func since(_ previous: DispatchTime) -> DispatchTimeInterval {
95 | return .nanoseconds(Int(uptimeNanoseconds - previous.uptimeNanoseconds))
96 | }
97 | }
98 |
99 | public extension DispatchTimeInterval {
100 | static func interval(_ seconds: TimeInterval) -> DispatchTimeInterval {
101 | if MemoryLayout.size < 8 {
102 | return .milliseconds(Int(seconds * Double(NSEC_PER_SEC / NSEC_PER_MSEC)))
103 | } else {
104 | return .nanoseconds(Int(seconds * Double(NSEC_PER_SEC)))
105 | }
106 | }
107 |
108 | var seconds: Double {
109 | #if swift (>=3.2)
110 | switch self {
111 | case .seconds(let t): return Double(t)
112 | case .milliseconds(let t): return (Double(NSEC_PER_MSEC) / Double(NSEC_PER_SEC)) * Double(t)
113 | case .microseconds(let t): return (Double(NSEC_PER_USEC) / Double(NSEC_PER_SEC)) * Double(t)
114 | case .nanoseconds(let t): return (1.0 / Double(NSEC_PER_SEC)) * Double(t)
115 | case .never: return Double.infinity
116 | #if swift (>=5)
117 | default: fatalError("Unknown case")
118 | #endif
119 | }
120 | #else
121 | switch self {
122 | case .seconds(let t): return Double(t)
123 | case .milliseconds(let t): return (Double(NSEC_PER_MSEC) / Double(NSEC_PER_SEC)) * Double(t)
124 | case .microseconds(let t): return (Double(NSEC_PER_USEC) / Double(NSEC_PER_SEC)) * Double(t)
125 | case .nanoseconds(let t): return (1.0 / Double(NSEC_PER_SEC)) * Double(t)
126 | }
127 | #endif
128 | }
129 |
130 | var nanoseconds: Int64 {
131 | #if swift (>=3.2)
132 | switch self {
133 | case .seconds(let t): return Int64(NSEC_PER_SEC) * Int64(t)
134 | case .milliseconds(let t): return Int64(NSEC_PER_MSEC) * Int64(t)
135 | case .microseconds(let t): return Int64(NSEC_PER_USEC) * Int64(t)
136 | case .nanoseconds(let t): return Int64(t)
137 | case .never: return Int64.max
138 | #if swift (>=5)
139 | default: fatalError("Unknown case")
140 | #endif
141 | }
142 | #else
143 | switch self {
144 | case .seconds(let t): return Int64(NSEC_PER_SEC) * Int64(t)
145 | case .milliseconds(let t): return Int64(NSEC_PER_MSEC) * Int64(t)
146 | case .microseconds(let t): return Int64(NSEC_PER_USEC) * Int64(t)
147 | case .nanoseconds(let t): return Int64(t)
148 | }
149 | #endif
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlExecutionType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlExecutionType.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 30/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | /// Describes 7 key execution context types. The six most common exist as a pair between a sychronous and asynchronous version:
21 | /// * immediate and concurrentAsync
22 | /// * mutex and serialAsync
23 | /// * thread and threadAsync
24 | /// With the final context being an additional variation on mutex:
25 | /// * recursiveMutex
26 | ///
27 | /// This list of of execution context types should *not* be considered exhaustive so in general, it is better to interrogate the boolean properties in which you're interested.
28 | /// These properties are currently:
29 | /// * isImmediate[InCurrentContext|Always]
30 | /// * isReentrant
31 | /// * isConcurrent
32 | public enum ExecutionType {
33 | /// This execution type models a simple function invocation.
34 | /// * completes before `invoke` returns (immediate)
35 | /// * applies no mutex so nested calls to `invoke` will succeed (reentrant)
36 | /// * will let parallel calls run at the same time (concurrent)
37 | /// * invocation always inherits the caller's context (nest always)
38 | /// e.g. directly calling
39 | case immediate
40 |
41 | /// This execution type models a global concurrent work pool.
42 | /// * runs outside the current context and might not complete before `invoke` returns (asynchronous)
43 | /// * involves no mutex so nested calls to `invokeSync` are permitted (reentrant)
44 | /// * will let parallel calls run at the same time (concurrent)
45 | /// * normally async but `invokeSync` is invoked from the calling context (sync nests)
46 | /// e.g. DispatchQueue.global().async
47 | case concurrentAsync
48 |
49 | /// This execution type models a scoped non-recursive mutex.
50 | /// * completes before `invoke` returns (immediate)
51 | /// * applies a non-reentrant mutex so nested calls to `invoke` will deadlock (non-reentrant)
52 | /// * will serialize parallel calls to run one at a time (serial)
53 | /// * invocation always inherits the caller's context (nest always)
54 | /// e.g. dispatchQueue.sync
55 | case mutex
56 |
57 | /// This execution type models a scoped recursive mutex. The associated test function returns `true` if the `invoke` or `invokeSync` function can be elided (replaced by direct invocation, since the current context is known to be inside the mutex).
58 | /// * completes before `invoke` returns (immediate)
59 | /// * applies a mutex but a nested `invoke` will safely re-enter the mutex (reentrant)
60 | /// * will serialize parallel calls to run one at a time (serial)
61 | /// * invocation always inherits the caller's context (nest always)
62 | /// e.g. NSRecursiveLock.lock(before:)
63 | case recursiveMutex(() -> Bool)
64 |
65 | /// This execution type models a thread. The associated test function returns `true` if the `invoke` or `invokeSync` function can be elided (replaced by direct invocation, since the current context is known to be inside the thread).
66 | /// * if test function returns true, then `invoke` is immediate in the current context, otherwise asychronous (immediate/asynchronous)
67 | /// * nested calls to `invoke` are permitted since they will simply be run immediately (reentrant)
68 | /// * will serialize parallel calls to run one at a time (serial)
69 | /// * invocation only inherits the caller's context if test function returns true in current context (nest thread)
70 | /// e.g. `if Thread.isMainThread { /* do work */ } else { DispatchQueue.main.async { /* do work */ }`
71 | case thread(() -> Bool)
72 |
73 | /// This execution type models a thread on which work is typically performed asynchronously. The associated test function returns `true` if the `invoke` or `invokeSync` function can be elided (replaced by direct invocation, since the current context is known to be inside the thread).
74 | /// * `invoke` is always asynchronous (asynchronous)
75 | /// * detects when it is already on the current thread so nested calls to `invokeSync` will not deadlock (reentrant)
76 | /// * will serialize parallel calls to run one at a time (serial)
77 | /// * normally async but `invokeSync` nests if already on its thread (nest syncThread)
78 | /// e.g. DispatchQueue.main.async
79 | case threadAsync(() -> Bool)
80 |
81 | /// This execution type models an asynchronous resource that lacks any synchronous access.
82 | /// * runs outside the current context and might not complete before `invoke` returns (asynchronous)
83 | /// * applies a non-reentrant mutex so nested calls to `invokeSync` will deadlock (non-reentrant)
84 | /// * will serialize parallel calls to run one at a time (serial)
85 | /// * invocation never inherits the caller's context (nest no)
86 | /// e.g. a serial resource that offers a `performAsync(_:() -> Void)` but doesn't offer a `performSync(_:() -> Void)`
87 | case serialAsync
88 | }
89 |
90 | public extension ExecutionType {
91 | /// Returns true if a block executed with `invoke` is guaranteed to complete before `invoke` returns in the current context.
92 | /// The inverse of this value is "isAsyncInCurrentContext".
93 | ///
94 | /// NOTE: this property runs a function for case `.thread` to see if the current thread is the target thread. Any change queue/thread may break the guarantee (the name "thread" is representative-only and might not refer to a literal thread).
95 | var isImmediateInCurrentContext: Bool {
96 | switch self {
97 | case .immediate, .mutex, .recursiveMutex: return true
98 | case .thread(let isCurrent): return isCurrent()
99 | case .serialAsync, .concurrentAsync, .threadAsync: return false
100 | }
101 | }
102 |
103 | /// Inverse of `isImmediateInCurrentContext`
104 | var isAsyncInCurrentContext: Bool { return !isImmediateInCurrentContext }
105 |
106 | /// Returns true if a block executed with `invoke` is always guaranteed to complete before `invoke` returns.
107 | /// The inverse of this value is "isPotentiallyAsync"
108 | var isImmediateAlways: Bool {
109 | switch self {
110 | case .immediate, .mutex, .recursiveMutex: return true
111 | case .thread, .serialAsync, .concurrentAsync, .threadAsync: return false
112 | }
113 | }
114 |
115 | /// Inverse of `isImmediateAlways`
116 | var isPotentiallyAsync: Bool { return !isImmediateInCurrentContext }
117 |
118 | /// Returns true if calling `invoke` or `invokeSync` within an executed block will succeed (not deadlock).
119 | /// The inverse of this value is "non-reentrant"
120 | var isReentrant: Bool {
121 | switch self {
122 | case .immediate, .recursiveMutex, .thread, .threadAsync, .concurrentAsync: return true
123 | case .mutex, .serialAsync: return false
124 | }
125 | }
126 |
127 | /// Inverse of `isReentrant`
128 | var isNonReentrant: Bool { return !isReentrant }
129 |
130 | /// Returns true if calling `invoke` simultaneously on separate threads may result in simultaneous execution.
131 | /// The inverse of this value is "serial"
132 | var isConcurrent: Bool {
133 | switch self {
134 | case .immediate, .concurrentAsync: return true
135 | case .mutex, .recursiveMutex, .serialAsync, .thread, .threadAsync: return false
136 | }
137 | }
138 |
139 | /// Inverse of `isConcurrent`
140 | var isSerial: Bool { return !isConcurrent }
141 | }
142 |
143 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlFew.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlFew.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 29/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import Foundation
21 |
22 | public enum Few {
23 | case none
24 | case single(T)
25 | case array(Array)
26 | }
27 |
28 | extension Few: Collection {
29 | public func index(after i: Int) -> Int {
30 | return i + 1
31 | }
32 |
33 | public var count: Int {
34 | switch self {
35 | case .none: return 0
36 | case .single: return 1
37 | case .array(let a): return a.count
38 | }
39 | }
40 |
41 | public var startIndex: Int {
42 | return 0
43 | }
44 |
45 | public var endIndex: Int {
46 | switch self {
47 | case .none: return 0
48 | case .single: return 1
49 | case .array(let a): return a.endIndex
50 | }
51 | }
52 |
53 | public subscript(key: Int) -> T {
54 | switch self {
55 | case .none: fatalError()
56 | case .single(let value): return value
57 | case .array(let a): return a[key]
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlLifetime.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlLifetime.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2017/04/18.
6 | // Copyright © 2017 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | /// This protocol exists to keep alive and terminate-at-will asynchronous and ongoing tasks. It is an
24 | /// implementation of the "Disposable" pattern.
25 | ///
26 | /// While conformance to this protocol requires just one function, conforming to this protocol also signals three important traits:
27 | /// 1. instances manage an underlying resource
28 | /// 2. the resource will last until one the first of the following end-conditions occurs:
29 | /// a. The resource terminates on its own
30 | /// b. All references to the Lifetime instance are released
31 | /// c. The `cancel()` function is invoked
32 | ///
33 | /// ideally, as well:
34 | ///
35 | /// 3. no further effects or actions of any kind will occur after the first end-condition is registered in the
36 | /// resource's context, no further messages or notifications sent or received, no resurrection possible
37 | /// 4. any subsequent end conditions after the first are safe and have no effect
38 | /// 5. if Self is a reference type, `cancel` should be explicitly invoked on deinit
39 | /// 6. `cancel` should invoke `cancel` on any owned child Lifetime instances
40 | ///
41 | /// Examples of violations of the last 4 points exist be should be kept rare.
42 | public protocol Lifetime {
43 | /// Immediately set the resource managed by this instance to an "end-of-life" state.
44 | /// This a mutating method and should be called only in executation contexts where changing `self` is threadsafe.
45 | mutating func cancel()
46 | }
47 |
48 | public typealias Cancellable = Lifetime
49 |
50 | /// An array of Lifetime that conforms to Lifetime. Note that a conditional conformance on Array can't properly conform
51 | // to Lifetime since it would permit adding new lifetimes after the aggregate was cancelled.
52 | public class AggregateLifetime: Lifetime {
53 | private var lifetimes: [Lifetime]?
54 | public init(lifetimes: [Lifetime] = []) {
55 | self.lifetimes = lifetimes
56 | }
57 | public func cancel() {
58 | if var ls = lifetimes {
59 | for i in ls.indices {
60 | ls[i].cancel()
61 | }
62 | lifetimes = nil
63 | }
64 | }
65 | public static func +=(left: AggregateLifetime, right: Lifetime) {
66 | left.lifetimes?.append(right)
67 | }
68 | deinit {
69 | cancel()
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlMutex.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlMutex.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | #if os(Linux)
22 | import Glibc
23 | #else
24 | import Darwin
25 | #endif
26 |
27 | /// A basic mutex protocol that requires nothing more than "performing work inside the mutex".
28 | public protocol ScopedMutex {
29 | /// Perform work inside the mutex
30 | func sync(execute work: () throws -> R) rethrows -> R
31 |
32 | /// Perform work inside the mutex, returning immediately if the mutex is in-use
33 | func trySync(execute work: () throws -> R) rethrows -> R?
34 | }
35 |
36 | /// A more specific kind of mutex that assume an underlying primitive and unbalanced lock/trylock/unlock operators
37 | public protocol RawMutex: ScopedMutex {
38 | associatedtype MutexPrimitive
39 |
40 | var underlyingMutex: MutexPrimitive { get set }
41 |
42 | func unbalancedLock()
43 | func unbalancedTryLock() -> Bool
44 | func unbalancedUnlock()
45 | }
46 |
47 | extension RawMutex {
48 | public func sync(execute work: () throws -> R) rethrows -> R {
49 | unbalancedLock()
50 | defer { unbalancedUnlock() }
51 | return try work()
52 | }
53 | public func trySync(execute work: () throws -> R) rethrows -> R? {
54 | guard unbalancedTryLock() else { return nil }
55 | defer { unbalancedUnlock() }
56 | return try work()
57 | }
58 | }
59 |
60 | /// A basic wrapper around the "NORMAL" and "RECURSIVE" `pthread_mutex_t` (a general purpose mutex). This type is a "class" type to take advantage of the "deinit" method and prevent accidental copying of the `pthread_mutex_t`.
61 | public final class PThreadMutex: RawMutex {
62 | public typealias MutexPrimitive = pthread_mutex_t
63 |
64 | // Non-recursive "PTHREAD_MUTEX_NORMAL" and recursive "PTHREAD_MUTEX_RECURSIVE" mutex types.
65 | public enum PThreadMutexType {
66 | case normal
67 | case recursive
68 | }
69 |
70 | public var underlyingMutex = pthread_mutex_t()
71 |
72 | /// Default constructs as ".Normal" or ".Recursive" on request.
73 | public init(type: PThreadMutexType = .normal) {
74 | var attr = pthread_mutexattr_t()
75 | guard pthread_mutexattr_init(&attr) == 0 else {
76 | preconditionFailure()
77 | }
78 | switch type {
79 | case .normal:
80 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL)
81 | case .recursive:
82 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)
83 | }
84 | guard pthread_mutex_init(&underlyingMutex, &attr) == 0 else {
85 | preconditionFailure()
86 | }
87 | pthread_mutexattr_destroy(&attr)
88 | }
89 |
90 | deinit {
91 | pthread_mutex_destroy(&underlyingMutex)
92 | }
93 |
94 | public func unbalancedLock() {
95 | pthread_mutex_lock(&underlyingMutex)
96 | }
97 |
98 | public func unbalancedTryLock() -> Bool {
99 | return pthread_mutex_trylock(&underlyingMutex) == 0
100 | }
101 |
102 | public func unbalancedUnlock() {
103 | pthread_mutex_unlock(&underlyingMutex)
104 | }
105 | }
106 |
107 | /// A basic wrapper around `os_unfair_lock` (a non-FIFO, high performance lock that offers safety against priority inversion). This type is a "class" type to prevent accidental copying of the `os_unfair_lock`.
108 | @available(OSX 10.12, iOS 10, tvOS 10, watchOS 3, *)
109 | public final class UnfairLock: RawMutex {
110 | public typealias MutexPrimitive = os_unfair_lock
111 |
112 | public init() {
113 | }
114 |
115 | /// Exposed as an "unsafe" public property so non-scoped patterns can be implemented, if required.
116 | public var underlyingMutex = os_unfair_lock()
117 |
118 | public func unbalancedLock() {
119 | os_unfair_lock_lock(&underlyingMutex)
120 | }
121 |
122 | public func unbalancedTryLock() -> Bool {
123 | return os_unfair_lock_trylock(&underlyingMutex)
124 | }
125 |
126 | public func unbalancedUnlock() {
127 | os_unfair_lock_unlock(&underlyingMutex)
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlOnDelete.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlOnDelete.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Swift
22 |
23 | public final class OnDelete: Lifetime {
24 | var block: (() -> Void)?
25 |
26 | public init(_ b: @escaping () -> Void) {
27 | block = b
28 | }
29 |
30 | public func invalidate() {
31 | block = nil
32 | }
33 |
34 | public func cancel() {
35 | block?()
36 | block = nil
37 | }
38 |
39 | public var isValid: Bool {
40 | return block != nil
41 | }
42 |
43 | deinit {
44 | cancel()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlRandom.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlRandom.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/05/17.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | #if !swift(>=4.2)
24 | public protocol RandomNumberGenerator {
25 | mutating func next() -> UInt64
26 | }
27 | public struct SystemRandomNumberGenerator: RandomNumberGenerator {
28 | public init() {}
29 | public mutating func next() -> UInt64 {
30 | var value: UInt64 = 0
31 | arc4random_buf(&value, MemoryLayout.size)
32 | return value
33 | }
34 | }
35 | #endif
36 |
37 | public protocol RandomGenerator: RandomNumberGenerator {
38 | mutating func randomize(buffer: UnsafeMutableRawBufferPointer)
39 | }
40 |
41 | extension RandomGenerator {
42 | public mutating func randomize(value: inout Value) {
43 | withUnsafeMutablePointer(to: &value) { ptr in
44 | self.randomize(buffer: UnsafeMutableRawBufferPointer(start: ptr, count: MemoryLayout.size))
45 | }
46 | }
47 | }
48 |
49 | public struct DevRandom: RandomGenerator {
50 | class FileDescriptor {
51 | let value: CInt
52 | init() {
53 | value = open("/dev/urandom", O_RDONLY)
54 | precondition(value >= 0)
55 | }
56 | deinit {
57 | close(value)
58 | }
59 | }
60 |
61 | let fd: FileDescriptor
62 | public init() {
63 | fd = FileDescriptor()
64 | }
65 |
66 | public mutating func randomize(buffer: UnsafeMutableRawBufferPointer) {
67 | let result = read(fd.value, buffer.baseAddress, buffer.count)
68 | precondition(result == buffer.count)
69 | }
70 |
71 | public mutating func next() -> UInt64 {
72 | var bits: UInt64 = 0
73 | withUnsafeMutablePointer(to: &bits) { ptr in
74 | self.randomize(buffer: UnsafeMutableRawBufferPointer(start: ptr, count: MemoryLayout.size))
75 | }
76 | return bits
77 | }
78 | }
79 |
80 | public struct Xoshiro: RandomNumberGenerator {
81 | public typealias StateType = (UInt64, UInt64, UInt64, UInt64)
82 |
83 | private var state: StateType = (0, 0, 0, 0)
84 |
85 | public init() {
86 | var dr = DevRandom()
87 | dr.randomize(value: &state)
88 | }
89 |
90 | public init(seed: StateType) {
91 | self.state = seed
92 | }
93 |
94 | public mutating func next() -> UInt64 {
95 | // Derived from public domain implementation of xoshiro256** here:
96 | // http://xoshiro.di.unimi.it
97 | // by David Blackman and Sebastiano Vigna
98 | let x = state.1 &* 5
99 | let result = ((x &<< 7) | (x &>> 57)) &* 9
100 | let t = state.1 &<< 17
101 | state.2 ^= state.0
102 | state.3 ^= state.1
103 | state.1 ^= state.2
104 | state.0 ^= state.3
105 | state.2 ^= t
106 | state.3 = (state.3 &<< 45) | (state.3 &>> 19)
107 | return result
108 | }
109 | }
110 |
111 | public struct MersenneTwister: RandomNumberGenerator {
112 | // 312 words of storage is 13 x 6 x 4
113 | private typealias StateType = (
114 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
115 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
116 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
117 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
118 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
119 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
120 |
121 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
122 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
123 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
124 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
125 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
126 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
127 |
128 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
129 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
130 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
131 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
132 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
133 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
134 |
135 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
136 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
137 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
138 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
139 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64,
140 | UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64
141 | )
142 |
143 | private var state_internal: StateType = (
144 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
146 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
147 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
148 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
149 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150 |
151 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
152 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
154 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
155 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 |
158 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
162 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
163 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
164 |
165 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
166 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
170 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
171 | )
172 | private var index: Int
173 | private static let stateCount: Int = 312
174 |
175 | public init() {
176 | var dr = DevRandom()
177 | dr.randomize(value: &state_internal)
178 | index = MersenneTwister.stateCount
179 | }
180 |
181 | public init(seed: UInt64) {
182 | index = MersenneTwister.stateCount
183 | withUnsafeMutablePointer(to: &state_internal) { $0.withMemoryRebound(to: UInt64.self, capacity: MersenneTwister.stateCount) { state in
184 | state[0] = seed
185 | for i in 1..> 62)) &+ UInt64(i)
187 | }
188 | } }
189 | }
190 |
191 | public mutating func next() -> UInt64 {
192 | if index == MersenneTwister.stateCount {
193 | withUnsafeMutablePointer(to: &state_internal) { $0.withMemoryRebound(to: UInt64.self, capacity: MersenneTwister.stateCount) { state in
194 | let n = MersenneTwister.stateCount
195 | let m = n / 2
196 | let a: UInt64 = 0xB5026F5AA96619E9
197 | let lowerMask: UInt64 = (1 << 31) - 1
198 | let upperMask: UInt64 = ~lowerMask
199 | var (i, j, stateM) = (0, m, state[m])
200 | repeat {
201 | let x1 = (state[i] & upperMask) | (state[i &+ 1] & lowerMask)
202 | state[i] = state[i &+ m] ^ (x1 >> 1) ^ ((state[i &+ 1] & 1) &* a)
203 | let x2 = (state[j] & upperMask) | (state[j &+ 1] & lowerMask)
204 | state[j] = state[j &- m] ^ (x2 >> 1) ^ ((state[j &+ 1] & 1) &* a)
205 | (i, j) = (i &+ 1, j &+ 1)
206 | } while i != m &- 1
207 |
208 | let x3 = (state[m &- 1] & upperMask) | (stateM & lowerMask)
209 | state[m &- 1] = state[n &- 1] ^ (x3 >> 1) ^ ((stateM & 1) &* a)
210 | let x4 = (state[n &- 1] & upperMask) | (state[0] & lowerMask)
211 | state[n &- 1] = state[m &- 1] ^ (x4 >> 1) ^ ((state[0] & 1) &* a)
212 | } }
213 |
214 | index = 0
215 | }
216 |
217 | var result = withUnsafePointer(to: &state_internal) { $0.withMemoryRebound(to: UInt64.self, capacity: MersenneTwister.stateCount) { ptr in
218 | return ptr[index]
219 | } }
220 | index = index &+ 1
221 |
222 | result ^= (result >> 29) & 0x5555555555555555
223 | result ^= (result << 17) & 0x71D67FFFEDA60000
224 | result ^= (result << 37) & 0xFFF7EEE000000000
225 | result ^= result >> 43
226 |
227 | return result
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlResult.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlResult.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | /// Either a Success value or an Failure error
24 | public extension Result {
25 | /// Convenience tester/getter for the value
26 | var value: Success? {
27 | switch self {
28 | case .success(let s): return s
29 | case .failure: return nil
30 | }
31 | }
32 |
33 | /// Convenience tester/getter for the error
34 | var error: Failure? {
35 | switch self {
36 | case .success: return nil
37 | case .failure(let f): return f
38 | }
39 | }
40 |
41 | /// Test whether the result is an error.
42 | var isSuccess: Bool {
43 | return !isFailure
44 | }
45 |
46 | /// Test whether the result is an error.
47 | var isFailure: Bool {
48 | switch self {
49 | case .success: return false
50 | case .failure: return true
51 | }
52 | }
53 | }
54 |
55 | public extension Result where Failure == Swift.Error {
56 | /// Chains another Result to this one. In the event that this Result is a .Success, the provided transformer closure is used to transform the value into another value (of a potentially new type) and a new Result is made from that value. In the event that this Result is a .Failure, the next Result will have the same error as this one.
57 | func mapThrows(_ transform: (Success) throws -> U) -> Result {
58 | switch self {
59 | case .success(let val): return Result { try transform(val) }
60 | case .failure(let e): return .failure(e)
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlSerializingContext.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlSerializingContext.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 19/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import Foundation
21 |
22 | /// An `ExecutionContext` wraps a mutex around calls invoked by an underlying execution context. The effect is to serialize concurrent contexts (immediate or concurrent).
23 | public struct SerializingContext: CustomExecutionContext {
24 | public let underlying: Exec
25 | public let mutex = PThreadMutex(type: .recursive)
26 |
27 | public init(concurrentContext: Exec) {
28 | underlying = concurrentContext
29 | }
30 |
31 | public var type: ExecutionType {
32 | switch underlying.type {
33 | case .immediate: return .mutex
34 | case .concurrentAsync: return .serialAsync
35 | case .mutex, .recursiveMutex, .thread, .threadAsync, .serialAsync: return underlying.type
36 | }
37 | }
38 |
39 | public func invoke(_ execute: @escaping () -> Void) {
40 | if case .direct = underlying {
41 | mutex.sync(execute: execute)
42 | } else {
43 | underlying.invoke { [mutex] in mutex.sync(execute: execute) }
44 | }
45 | }
46 |
47 | public func invokeAsync(_ execute: @escaping () -> Void) {
48 | underlying.invokeAsync { [mutex] in mutex.sync(execute: execute) }
49 | }
50 |
51 | @available(*, deprecated, message: "Use invokeSync instead")
52 | public func invokeAndWait(_ execute: @escaping () -> Void) {
53 | invokeSync(execute)
54 | }
55 |
56 | public func invokeSync(_ execute: () throws -> Return) rethrows -> Return {
57 | if case .direct = underlying {
58 | return try mutex.sync(execute: execute)
59 | } else {
60 | return try underlying.invokeSync { [mutex] () throws -> Return in try mutex.sync(execute: execute) }
61 | }
62 | }
63 |
64 | /// Run `execute` on the execution context after `interval` (plus `leeway`) unless the returned `Lifetime` is cancelled or released before running occurs.
65 | public func singleTimer(interval: DispatchTimeInterval, leeway: DispatchTimeInterval, handler: @escaping () -> Void) -> Lifetime {
66 | return mutex.sync { () -> Lifetime in
67 | let wrapper = MutexWrappedLifetime(mutex: mutex)
68 | let lifetime = underlying.singleTimer(interval: interval, leeway: leeway) { [weak wrapper] in
69 | if let w = wrapper {
70 | w.mutex.sync {
71 | // Need to perform this double check since the timer may have been cancelled/changed before we managed to enter the mutex
72 | if w.lifetime != nil {
73 | handler()
74 | }
75 | }
76 | }
77 | }
78 | wrapper.lifetime = lifetime
79 | return wrapper
80 | }
81 | }
82 |
83 | /// Run `execute` on the execution context after `interval` (plus `leeway`), passing the `parameter` value as an argument, unless the returned `Lifetime` is cancelled or released before running occurs.
84 | public func singleTimer(parameter: T, interval: DispatchTimeInterval, leeway: DispatchTimeInterval, handler: @escaping (T) -> Void) -> Lifetime {
85 | return mutex.sync { () -> Lifetime in
86 | let wrapper = MutexWrappedLifetime(mutex: mutex)
87 | let lifetime = underlying.singleTimer(parameter: parameter, interval: interval, leeway: leeway) { [weak wrapper] p in
88 | if let w = wrapper {
89 | w.mutex.sync {
90 | // Need to perform this double check since the timer may have been cancelled/changed before we managed to enter the mutex
91 | if w.lifetime != nil {
92 | handler(p)
93 | }
94 | }
95 | }
96 | }
97 | wrapper.lifetime = lifetime
98 | return wrapper
99 | }
100 | }
101 |
102 | /// Run `execute` on the execution context after `interval` (plus `leeway`), and again every `interval` (within a `leeway` margin of error) unless the returned `Lifetime` is cancelled or released before running occurs.
103 | public func periodicTimer(interval: DispatchTimeInterval, leeway: DispatchTimeInterval, handler: @escaping () -> Void) -> Lifetime {
104 | return mutex.sync { () -> Lifetime in
105 | let wrapper = MutexWrappedLifetime(mutex: mutex)
106 | let lifetime = underlying.periodicTimer(interval: interval, leeway: leeway) { [weak wrapper] in
107 | if let w = wrapper {
108 | w.mutex.sync {
109 | // Need to perform this double check since the timer may have been cancelled/changed before we managed to enter the mutex
110 | if w.lifetime != nil {
111 | handler()
112 | }
113 | }
114 | }
115 | }
116 | wrapper.lifetime = lifetime
117 | return wrapper
118 | }
119 | }
120 |
121 | /// Run `execute` on the execution context after `interval` (plus `leeway`), passing the `parameter` value as an argument, and again every `interval` (within a `leeway` margin of error) unless the returned `Lifetime` is cancelled or released before running occurs.
122 | public func periodicTimer(parameter: T, interval: DispatchTimeInterval, leeway: DispatchTimeInterval, handler: @escaping (T) -> Void) -> Lifetime {
123 | return mutex.sync { () -> Lifetime in
124 | let wrapper = MutexWrappedLifetime(mutex: mutex)
125 | let lifetime = underlying.periodicTimer(parameter: parameter, interval: interval, leeway: leeway) { [weak wrapper] p in
126 | if let w = wrapper {
127 | w.mutex.sync {
128 | if w.lifetime != nil {
129 | handler(p)
130 | }
131 | }
132 | }
133 | }
134 | wrapper.lifetime = lifetime
135 | return wrapper
136 | }
137 | }
138 |
139 | /// Gets a timestamp representing the host uptime the in the current context
140 | public func timestamp() -> DispatchTime {
141 | return underlying.timestamp()
142 | }
143 | }
144 |
145 | /// A wrapper around Lifetime that applies a mutex on the cancel operation.
146 | /// This is a class so that `SerializingContext` can pass it weakly to the timer closure, avoiding having the timer keep itself alive.
147 | private class MutexWrappedLifetime: Lifetime {
148 | var lifetime: Lifetime? = nil
149 | let mutex: PThreadMutex
150 |
151 | init(mutex: PThreadMutex) {
152 | self.mutex = mutex
153 | }
154 |
155 | func cancel() {
156 | mutex.sync {
157 | lifetime?.cancel()
158 | lifetime = nil
159 | }
160 | }
161 |
162 | deinit {
163 | cancel()
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlStackFrame.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlStackFrame.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/26.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Darwin
22 |
23 | #if SWIFT_PACKAGE
24 | import CwlFrameAddress
25 | #endif
26 |
27 | /// A utility class for walking through stack frames.
28 | public struct StackFrame {
29 | /// The underlying data of the struct is a basic UInt. A value of 0 represents an invalid frame.
30 | public let address: UInt
31 |
32 | /// The return address is pushed onto the stack immediately ahead of the previous frame pointer. If `self.address` is `0` this will return `0`
33 | /// - returns: the return address for the stack frame identified by `address`
34 | public var returnAddress: UInt { get {
35 | guard address != 0 else { return 0 }
36 | return UnsafeMutablePointer(bitPattern: address)!.advanced(by: FP_LINK_OFFSET).pointee
37 | } }
38 |
39 | /// Preferred constructor gives the "current" StackFrame (where "current" refers to the frame that invokes this function). Also returns the `stackBounds` for use in subsequent calls to `next`.
40 | /// - returns: a `StackFrame` representing the caller's stack frame and `stackBounds` which should be passed into any future calls to `next`.
41 | @inline(never)
42 | public static func current() -> (frame: StackFrame, stackBounds: ClosedRange) {
43 | let stackBounds = currentStackBounds()
44 | let frame = StackFrame(address: frame_address())
45 |
46 | if !stackBounds.contains(frame.address) || !isAligned(frame.address) {
47 | return (StackFrame(address: 0), stackBounds: stackBounds);
48 | }
49 |
50 | return (frame: frame.next(inBounds: stackBounds), stackBounds: stackBounds)
51 | }
52 |
53 | /// Follow the frame link pointer and return the result as another StackFrame.
54 | /// - returns: a `StackFrame` representing the stack frame after self, if it exists and is valid.
55 | public func next(inBounds stackBounds: ClosedRange) -> StackFrame {
56 | guard address != 0 else { return self }
57 | let nextFrameAddress = UnsafeMutablePointer(bitPattern: address)?.pointee
58 | if !stackBounds.contains(nextFrameAddress!) || !isAligned(nextFrameAddress!) || (nextFrameAddress ?? 0) <= address {
59 | return StackFrame(address: 0)
60 | }
61 |
62 | return StackFrame(address: nextFrameAddress!)
63 | }
64 | }
65 |
66 | /// Traverses the frames on current stack and gathers the return addresses for traversed stack frames as an array of UInt.
67 | /// - parameter skip: number of stack frames to skip over before copying return addresses to the result array.
68 | /// - parameter maximumAddresses: limit on the number of return addresses to return (default is `Int.max`)
69 | /// - returns: The array of return addresses on the current stack within the skip/maximumAddresses bounds.
70 | @inline(never)
71 | public func callStackReturnAddresses(skip: UInt = 0, maximumAddresses: Int = Int.max) -> [UInt] {
72 | guard maximumAddresses > 0 else { return [] }
73 |
74 | var result = [UInt]()
75 | var skipsRemaining = skip
76 | var addressesRemaining = maximumAddresses
77 |
78 | let maximumReserve = 32
79 | result.reserveCapacity(maximumAddresses < maximumReserve ? maximumAddresses : maximumReserve)
80 |
81 | var (frame, bounds) = StackFrame.current()
82 | var returnAddress = frame.returnAddress
83 |
84 | while returnAddress != 0 && addressesRemaining > 0 {
85 | if skipsRemaining > 0 {
86 | skipsRemaining -= 1
87 | } else {
88 | result.append(returnAddress)
89 | addressesRemaining -= 1
90 | }
91 | frame = frame.next(inBounds: bounds)
92 | returnAddress = frame.returnAddress
93 | }
94 |
95 | return result
96 | }
97 |
98 | // These values come from:
99 | // http://www.opensource.apple.com/source/Libc/Libc-997.90.3/gen/thread_stack_pcs.c
100 | #if arch(x86_64)
101 | let ISALIGNED_MASK: UInt = 0xf
102 | let ISALIGNED_RESULT: UInt = 0
103 | let FP_LINK_OFFSET = 1
104 | #elseif arch(i386)
105 | let ISALIGNED_MASK: UInt = 0xf
106 | let ISALIGNED_RESULT: UInt = 8
107 | let FP_LINK_OFFSET = 1
108 | #else
109 | let ISALIGNED_MASK: UInt = 0x1
110 | let ISALIGNED_RESULT: UInt = 0
111 | let FP_LINK_OFFSET = 1
112 | #endif
113 |
114 | /// Use the pthread functions to get the bounds of the current stack as a closed interval.
115 | /// - returns: a closed interval containing the memory address range for the current stack
116 | private func currentStackBounds() -> ClosedRange {
117 | let currentThread = pthread_self()
118 | let t = UInt(bitPattern: pthread_get_stackaddr_np(currentThread))
119 | return ((t - UInt(bitPattern: pthread_get_stacksize_np(currentThread))) ... t)
120 | }
121 |
122 | /// We traverse the stack using "downstack links". To avoid problems with these links, we ensure that frame pointers are "aligned" (valid stack frames are 16 byte aligned on x86 and 2 byte aligned on ARM).
123 | /// - parameter address: the address to analyze
124 | /// - returns: true if `address` is aligned according to stack rules for the current architecture
125 | private func isAligned(_ address: UInt) -> Bool {
126 | return (address & ISALIGNED_MASK) == ISALIGNED_RESULT
127 | }
128 |
129 | /// Get the calling function's address and look it up, attempting to find the symbol.
130 | /// NOTE: This is mostly useful in debug environements. Outside this, non-public functions and images without symbols will return incomplete information.
131 | /// - parameter skipCount: the number of stack frames to skip over before analyzing
132 | /// - returns: the `dladdr` identifier for the specified frame, if one exists
133 | @inline(never)
134 | public func callingFunctionIdentifier(skipCount: UInt = 0) -> String {
135 | let address = callStackReturnAddresses(skip: skipCount + 1, maximumAddresses: 1).first ?? 0
136 | return AddressInfo(address: address).symbol
137 | }
138 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlSysctl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlSysctl.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/03.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | /// A "static"-only namespace around a series of functions that operate on buffers returned from the `Darwin.sysctl` function
24 | public struct Sysctl {
25 | /// Possible errors.
26 | public enum Error: Swift.Error {
27 | case unknown
28 | case malformedUTF8
29 | case invalidSize
30 | case posixError(POSIXErrorCode)
31 | }
32 |
33 | /// Access the raw data for an array of sysctl identifiers.
34 | public static func data(for keys: [Int32]) throws -> [Int8] {
35 | return try keys.withUnsafeBufferPointer() { keysPointer throws -> [Int8] in
36 | // Preflight the request to get the required data size
37 | var requiredSize = 0
38 | let preFlightResult = Darwin.sysctl(UnsafeMutablePointer(mutating: keysPointer.baseAddress), UInt32(keys.count), nil, &requiredSize, nil, 0)
39 | if preFlightResult != 0 {
40 | throw POSIXErrorCode(rawValue: errno).map {
41 | print($0.rawValue)
42 | return Error.posixError($0)
43 | } ?? Error.unknown
44 | }
45 |
46 | // Run the actual request with an appropriately sized array buffer
47 | let data = Array(repeating: 0, count: requiredSize)
48 | let result = data.withUnsafeBufferPointer() { dataBuffer -> Int32 in
49 | return Darwin.sysctl(UnsafeMutablePointer(mutating: keysPointer.baseAddress), UInt32(keys.count), UnsafeMutableRawPointer(mutating: dataBuffer.baseAddress), &requiredSize, nil, 0)
50 | }
51 | if result != 0 {
52 | throw POSIXErrorCode(rawValue: errno).map { Error.posixError($0) } ?? Error.unknown
53 | }
54 |
55 | return data
56 | }
57 | }
58 |
59 | /// Convert a sysctl name string like "hw.memsize" to the array of `sysctl` identifiers (e.g. [CTL_HW, HW_MEMSIZE])
60 | public static func keys(for name: String) throws -> [Int32] {
61 | var keysBufferSize = Int(CTL_MAXNAME)
62 | var keysBuffer = Array(repeating: 0, count: keysBufferSize)
63 | try keysBuffer.withUnsafeMutableBufferPointer { (lbp: inout UnsafeMutableBufferPointer) throws in
64 | try name.withCString { (nbp: UnsafePointer) throws in
65 | guard sysctlnametomib(nbp, lbp.baseAddress, &keysBufferSize) == 0 else {
66 | throw POSIXErrorCode(rawValue: errno).map { Error.posixError($0) } ?? Error.unknown
67 | }
68 | }
69 | }
70 | if keysBuffer.count > keysBufferSize {
71 | keysBuffer.removeSubrange(keysBufferSize..(ofType: T.Type, forKeys keys: [Int32]) throws -> T {
78 | let buffer = try data(for: keys)
79 | if buffer.count != MemoryLayout.size {
80 | throw Error.invalidSize
81 | }
82 | return try buffer.withUnsafeBufferPointer() { bufferPtr throws -> T in
83 | guard let baseAddress = bufferPtr.baseAddress else { throw Error.unknown }
84 | return baseAddress.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
85 | }
86 | }
87 |
88 | /// Invoke `sysctl` with an array of identifers, interpreting the returned buffer as the specified type. This function will throw `Error.invalidSize` if the size of buffer returned from `sysctl` fails to match the size of `T`.
89 | public static func value(ofType type: T.Type, forKeys keys: Int32...) throws -> T {
90 | return try value(ofType: type, forKeys: keys)
91 | }
92 |
93 | /// Invoke `sysctl` with the specified name, interpreting the returned buffer as the specified type. This function will throw `Error.invalidSize` if the size of buffer returned from `sysctl` fails to match the size of `T`.
94 | public static func value(ofType type: T.Type, forName name: String) throws -> T {
95 | return try value(ofType: type, forKeys: keys(for: name))
96 | }
97 |
98 | /// Invoke `sysctl` with an array of identifers, interpreting the returned buffer as a `String`. This function will throw `Error.malformedUTF8` if the buffer returned from `sysctl` cannot be interpreted as a UTF8 buffer.
99 | public static func string(for keys: [Int32]) throws -> String {
100 | let optionalString = try data(for: keys).withUnsafeBufferPointer() { dataPointer -> String? in
101 | dataPointer.baseAddress.flatMap { String(validatingUTF8: $0) }
102 | }
103 | guard let s = optionalString else {
104 | throw Error.malformedUTF8
105 | }
106 | return s
107 | }
108 |
109 | /// Invoke `sysctl` with an array of identifers, interpreting the returned buffer as a `String`. This function will throw `Error.malformedUTF8` if the buffer returned from `sysctl` cannot be interpreted as a UTF8 buffer.
110 | public static func string(for keys: Int32...) throws -> String {
111 | return try string(for: keys)
112 | }
113 |
114 | /// Invoke `sysctl` with the specified name, interpreting the returned buffer as a `String`. This function will throw `Error.malformedUTF8` if the buffer returned from `sysctl` cannot be interpreted as a UTF8 buffer.
115 | public static func string(for name: String) throws -> String {
116 | return try string(for: keys(for: name))
117 | }
118 |
119 | /// e.g. "MyComputer.local" (from System Preferences -> Sharing -> Computer Name) or
120 | /// "My-Name-iPhone" (from Settings -> General -> About -> Name)
121 | public static var hostName: String { return try! Sysctl.string(for: [CTL_KERN, KERN_HOSTNAME]) }
122 |
123 | /// e.g. "x86_64" or "N71mAP"
124 | /// NOTE: this is *corrected* on iOS devices to fetch hw.model
125 | public static var machine: String {
126 | #if os(iOS) && !arch(x86_64) && !arch(i386)
127 | return try! Sysctl.string(for: [CTL_HW, HW_MODEL])
128 | #else
129 | return try! Sysctl.string(for: [CTL_HW, HW_MACHINE])
130 | #endif
131 | }
132 |
133 | /// e.g. "MacPro4,1" or "iPhone8,1"
134 | /// NOTE: this is *corrected* on iOS devices to fetch hw.machine
135 | public static var model: String {
136 | #if os(iOS) && !arch(x86_64) && !arch(i386)
137 | return try! Sysctl.string(for: [CTL_HW, HW_MACHINE])
138 | #else
139 | return try! Sysctl.string(for: [CTL_HW, HW_MODEL])
140 | #endif
141 | }
142 |
143 | /// e.g. "8" or "2"
144 | public static var activeCPUs: Int32 { return try! Sysctl.value(ofType: Int32.self, forKeys: [CTL_HW, HW_AVAILCPU]) }
145 |
146 | /// e.g. "15.3.0" or "15.0.0"
147 | public static var osRelease: String { return try! Sysctl.string(for: [CTL_KERN, KERN_OSRELEASE]) }
148 |
149 | /// e.g. "Darwin" or "Darwin"
150 | public static var osType: String { return try! Sysctl.string(for: [CTL_KERN, KERN_OSTYPE]) }
151 |
152 | /// e.g. "15D21" or "13D20"
153 | public static var osVersion: String { return try! Sysctl.string(for: [CTL_KERN, KERN_OSVERSION]) }
154 |
155 | /// e.g. "Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64" or
156 | /// "Darwin Kernel Version 15.0.0: Wed Dec 9 22:19:38 PST 2015; root:xnu-3248.31.3~2/RELEASE_ARM64_S8000"
157 | public static var version: String { return try! Sysctl.string(for: [CTL_KERN, KERN_VERSION]) }
158 |
159 | #if os(macOS)
160 | /// e.g. 199506 (not available on iOS)
161 | public static var osRev: Int32 { return try! Sysctl.value(ofType: Int32.self, forKeys: [CTL_KERN, KERN_OSREV]) }
162 |
163 | /// e.g. 2659000000 (not available on iOS)
164 | public static var cpuFreq: Int64 { return try! Sysctl.value(ofType: Int64.self, forName: "hw.cpufrequency") }
165 |
166 | /// e.g. 25769803776 (not available on iOS)
167 | public static var memSize: UInt64 { return try! Sysctl.value(ofType: UInt64.self, forKeys: [CTL_HW, HW_MEMSIZE]) }
168 | #endif
169 | }
170 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlUnanticipatedError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlUnanticipatedError.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/03/05.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | #if os(macOS)
22 | import Cocoa
23 | #elseif os(iOS)
24 | import UIKit
25 | import MobileCoreServices
26 | #endif
27 |
28 | public extension Error {
29 | /// Return an NSError with the same properties as this error but with an `UnanticipatedErrorRecoveryAttempter` attached.
30 | func withUnanticipatedErrorRecoveryAttempter(file: String = #file, line: Int = #line) -> NSError {
31 | let e = self as NSError
32 | var userInfo: [String: Any] = e.userInfo
33 |
34 | // Move any existing NSLocalizedRecoverySuggestionErrorKey to a new key (we want to replace it but don't want to lose potentially useful information)
35 | if let previousSuggestion = userInfo[NSLocalizedRecoverySuggestionErrorKey] {
36 | userInfo[UnanticipatedErrorRecoveryAttempter.previousRecoverySuggestionKey] = previousSuggestion
37 | }
38 |
39 | // Attach a new NSLocalizedRecoverySuggestionErrorKey and our recovery attempter and options
40 | let directory = ((file as NSString).deletingLastPathComponent as NSString).lastPathComponent
41 | let filename = (file as NSString).lastPathComponent
42 | let suggestion = String(format: NSLocalizedString("The error occurred at line %ld of the %@/%@ file in the program's code.", comment: ""), line, directory, filename)
43 | userInfo[NSLocalizedDescriptionKey] = self.localizedDescription
44 | userInfo[NSLocalizedRecoverySuggestionErrorKey] = suggestion
45 | userInfo[NSLocalizedRecoveryOptionsErrorKey] = UnanticipatedErrorRecoveryAttempter.localizedRecoveryOptions()
46 | userInfo[NSRecoveryAttempterErrorKey] = UnanticipatedErrorRecoveryAttempter()
47 |
48 | // Attach the call stack
49 | userInfo[UnanticipatedErrorRecoveryAttempter.callStackSymbols] = Thread.callStackSymbols
50 |
51 | return NSError(domain: e.domain, code: e.code, userInfo: userInfo)
52 | }
53 | }
54 |
55 | // A function that returns an `Error` of a non-public type, that already has `withUnanticipatedErrorRecoveryAttempter`
56 | public func undeclaredError(file: String = #file, line: Int = #line) -> Error {
57 | struct UndeclaredError: LocalizedError {
58 | var errorDescription: String? { return NSLocalizedString("An unspecified error occurred.", comment: "") }
59 | }
60 | return UndeclaredError().withUnanticipatedErrorRecoveryAttempter(file: file, line: line )
61 | }
62 |
63 | /// A convenience wrapper that applies `withUnanticipatedErrorRecoveryAttempter` to any error thrown by the wrapped function
64 | public func rethrowUnanticipated(file: String = #file, line: Int = #line, execute: () throws -> T) throws -> T {
65 | do {
66 | return try execute()
67 | } catch {
68 | throw error.withUnanticipatedErrorRecoveryAttempter(file: file, line: line)
69 | }
70 | }
71 |
72 | /// Class usable as the NSRecoveryAttempterErrorKey object in an NSError that presents the 'Unexpected' error and gives the option of copying the full error to the pasteboard.
73 | public class UnanticipatedErrorRecoveryAttempter: NSObject {
74 | /// Key used in NSError.userInfo dictionaries to store call stack addresses
75 | public static let callStackSymbols = "CwlUtils.CallStackReturnAddresses"
76 |
77 | /// Key used in NSError.userInfo dictionaries to store an OnDelete object that raises a fatal error if not cancelled
78 | public static let previousRecoverySuggestionKey = "CwlUtils.PreviousRecoverySuggestion"
79 |
80 | /// Present two buttons: "Copy details" and "OK"
81 | fileprivate class func localizedRecoveryOptions() -> [String] {
82 | return [NSLocalizedString("OK", comment:""), NSLocalizedString("Copy details", comment:"")]
83 | }
84 |
85 | /// There are two possible `attemptRecoveryFromError` methods. This one just feeds into the other.
86 | public override func attemptRecovery(fromError error: Error, optionIndex: Int, delegate: Any?, didRecoverSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) -> Void {
87 | _ = self.attemptRecovery(fromError: error, optionIndex: optionIndex)
88 | }
89 |
90 | /// Generate the "detailed" information for the pasteboard (the error dialog itself will show the brief details)
91 | private func extendedErrorInformation(_ error: NSError) -> String {
92 | var userInfo = error.userInfo
93 |
94 | // Fetch and format diagnostic information for display
95 | let callStackSymbols = (userInfo[UnanticipatedErrorRecoveryAttempter.callStackSymbols] as? [String]).map { $0.joined(separator: "\n") } ?? NSLocalizedString("(Call stack unavailable)", comment: "")
96 | let localizedDescription = error.localizedDescription
97 | let localizedRecoverySuggestion = error.localizedRecoverySuggestion ?? ""
98 | let applicationName = (Bundle.main.infoDictionary?[kCFBundleNameKey as String] as? String) ?? ProcessInfo.processInfo.processName
99 | let applicationVersion = (Bundle.main.infoDictionary?[kCFBundleVersionKey as String] as? String) ?? NSLocalizedString("(App version unavailable)", comment: "")
100 | let locales = Locale.preferredLanguages.joined(separator: ", ")
101 | let machineInfo = "\(Sysctl.machine)/\(Sysctl.model), \(ProcessInfo.processInfo.operatingSystemVersionString)"
102 |
103 | // Remove already handled keys from the userInfo. Anything not yet handled will be output as part of the diagnostic information.
104 | userInfo.removeValue(forKey: NSLocalizedRecoverySuggestionErrorKey)
105 | userInfo.removeValue(forKey: NSLocalizedRecoveryOptionsErrorKey)
106 | userInfo.removeValue(forKey: NSRecoveryAttempterErrorKey)
107 | userInfo.removeValue(forKey: UnanticipatedErrorRecoveryAttempter.previousRecoverySuggestionKey)
108 | userInfo.removeValue(forKey: UnanticipatedErrorRecoveryAttempter.callStackSymbols)
109 |
110 | return "\(applicationName)/\(applicationVersion), \(machineInfo), \(locales)\n\n\(localizedDescription)\n\(localizedRecoverySuggestion)\n\n\(error.domain): \(error.code). \(userInfo)\n\n\(callStackSymbols)"
111 | }
112 |
113 | /// When a button is tapped, either close the dialog or copy the error details as appropriate.
114 | public override func attemptRecovery(fromError error: Error, optionIndex: Int) -> Bool {
115 | // The "Copy details" button is index 1 in the buttons array.
116 | let copyDetailsButtonIndex = 1
117 |
118 | switch optionIndex {
119 | case copyDetailsButtonIndex:
120 | #if os(macOS)
121 | #if swift(>=4)
122 | NSPasteboard.general.clearContents()
123 | NSPasteboard.general.setString(extendedErrorInformation(error as NSError), forType:NSPasteboard.PasteboardType.string)
124 | #else
125 | NSPasteboard.general().clearContents()
126 | NSPasteboard.general().setString(extendedErrorInformation(error as NSError), forType:NSPasteboardTypeString)
127 | #endif
128 | #elseif os(iOS)
129 | UIPasteboard.general.string = extendedErrorInformation(error as NSError)
130 | #endif
131 | return true
132 | default:
133 | return false;
134 | }
135 | }
136 | }
137 |
138 | #if os(iOS)
139 |
140 | /// A protocol to provide functionality similar to NSResponder.presentError on Mac OS X.
141 | public protocol ErrorPresenter {
142 | func presentError(_ error: NSError, _ completion: (() -> Void)?)
143 | }
144 |
145 | // Implement the ErrorPresent on UIViewController rather than UIResponder since presenting a `UIAlertController` requires a parent `UIViewController`
146 | extension UIViewController: ErrorPresenter {
147 | /// An adapter function that allows the UnanticipatedErrorRecoveryAttempter to be used on iOS to present errors over a UIViewController.
148 | public func presentError(_ error: NSError, _ completion: (() -> Void)? = nil) {
149 | #if swift(>=4.2)
150 | let alert = UIAlertController(title: error.localizedDescription, message: error.localizedRecoverySuggestion ?? error.localizedFailureReason, preferredStyle: UIAlertController.Style.alert)
151 |
152 | if let ro = error.localizedRecoveryOptions, let ra = error.recoveryAttempter as? UnanticipatedErrorRecoveryAttempter {
153 | for (index, option) in ro.enumerated() {
154 | alert.addAction(UIAlertAction(title: option, style: UIAlertAction.Style.default, handler: { (action: UIAlertAction?) -> Void in
155 | _ = ra.attemptRecovery(fromError: error, optionIndex: index)
156 | }))
157 | }
158 | }
159 | #else
160 | let alert = UIAlertController(title: error.localizedDescription, message: error.localizedRecoverySuggestion ?? error.localizedFailureReason, preferredStyle: UIAlertControllerStyle.alert)
161 |
162 | if let ro = error.localizedRecoveryOptions, let ra = error.recoveryAttempter as? UnanticipatedErrorRecoveryAttempter {
163 | for (index, option) in ro.enumerated() {
164 | alert.addAction(UIAlertAction(title: option, style: UIAlertActionStyle.default, handler: { (action: UIAlertAction?) -> Void in
165 | _ = ra.attemptRecovery(fromError: error, optionIndex: index)
166 | }))
167 | }
168 | }
169 | #endif
170 | self.present(alert, animated: true, completion: completion)
171 | }
172 | }
173 |
174 | #endif
175 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlUtils.h:
--------------------------------------------------------------------------------
1 | //
2 | // CwlUtils.h
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/26.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | #import
22 |
23 | //! Project version number for CwlUtils.
24 | FOUNDATION_EXPORT double CwlUtilsVersionNumber;
25 |
26 | //! Project version string for CwlUtils.
27 | FOUNDATION_EXPORT const unsigned char CwlUtilsVersionString[];
28 |
29 | #import "CwlFrameAddress.h"
30 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/CwlWrappers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlWrappers.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | /// A class wrapper around a type (usually a value type) so it can be moved without copying.
24 | public class Box {
25 | public fileprivate(set) var value: T
26 | public init(_ t: T) {
27 | value = t
28 | }
29 | }
30 |
31 | //// A class wrapper around a type (usually a value type) so changes to it can be shared (usually as an ad hoc communication channel). NOTE: this version is *not* threadsafe, use AtomicBox for that.
32 | public final class MutableBox: Box {
33 | public override var value: T { get { return super.value } set { super.value = newValue } }
34 | public override init(_ t: T) {
35 | super.init(t)
36 | }
37 | }
38 |
39 | // A class wrapper around a type (usually a value type) so changes to it can be shared in a thread-safe manner (usually as an ad hoc communication channel).
40 | /// "Atomic" in this sense refers to the semantics, not the implementation. This uses a pthread mutex, not CAS-style atomic operations.
41 | public final class AtomicBox {
42 | private var mutex = PThreadMutex()
43 | private var internalValue: T
44 |
45 | public init(_ t: T) {
46 | internalValue = t
47 | }
48 |
49 | public var value: T {
50 | get {
51 | mutex.unbalancedLock()
52 | defer { mutex.unbalancedUnlock() }
53 | return internalValue
54 | }
55 | }
56 |
57 | @discardableResult
58 | public func mutate(_ f: (inout T) throws -> Void) rethrows -> T {
59 | mutex.unbalancedLock()
60 | defer { mutex.unbalancedUnlock() }
61 | try f(&internalValue)
62 | return internalValue
63 | }
64 | }
65 |
66 | /// A struct wrapper around an optional and a construction function that presents the optional through the `value()` function as though it's a lazy var. Unlike a true lazy var, you can query if the value has been initialized.
67 | public struct Lazy {
68 | var valueIfInitialized: T?
69 | let valueConstructor: () -> T
70 |
71 | public init(valueConstructor: @escaping () -> T) {
72 | self.valueConstructor = valueConstructor
73 | }
74 | public var isInitialized: Bool { return valueIfInitialized != nil }
75 | public mutating func value() -> T {
76 | if let v = valueIfInitialized {
77 | return v
78 | }
79 | let v = valueConstructor()
80 | valueIfInitialized = v
81 | return v
82 | }
83 | }
84 |
85 | /// A wrapper around a type (usually a class type) so it can be weakly referenced from an Array or other strong container.
86 | public struct Weak {
87 | public weak var value: T?
88 |
89 | public init(_ value: T?) {
90 | self.value = value
91 | }
92 |
93 | public func contains(_ other: T) -> Bool {
94 | if let v = value {
95 | return v === other
96 | } else {
97 | return false
98 | }
99 | }
100 | }
101 |
102 | /// A wrapper around a type (usually a class type) so it can be referenced unowned from an Array or other strong container.
103 | public struct Unowned {
104 | public unowned let value: T
105 | public init(_ value: T) {
106 | self.value = value
107 | }
108 | }
109 |
110 | /// A enum wrapper around a type (usually a class type) so its ownership can be set at runtime.
111 | public enum PossiblyWeak {
112 | case strong(T)
113 | case weak(Weak)
114 |
115 | public init(strong value: T) {
116 | self = PossiblyWeak.strong(value)
117 | }
118 |
119 | public init(weak value: T) {
120 | self = PossiblyWeak.weak(Weak(value))
121 | }
122 |
123 | public var value: T? {
124 | switch self {
125 | case .strong(let t): return t
126 | case .weak(let weakT): return weakT.value
127 | }
128 | }
129 |
130 | public func contains(_ other: T) -> Bool {
131 | switch self {
132 | case .strong(let t): return t === other
133 | case .weak(let weakT):
134 | if let wt = weakT.value {
135 | return wt === other
136 | }
137 | return false
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/Sources/CwlUtils/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 | NSHumanReadableCopyright
26 | Copyright © 2017 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Sources/ReferenceRandomGenerators/include/ReferenceRandomGenerators.h:
--------------------------------------------------------------------------------
1 | //
2 | // ReferenceRandomGenerators.h
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #ifndef ReferenceRandomGenerators_h
21 | #define ReferenceRandomGenerators_h
22 |
23 | #import
24 |
25 | typedef struct {
26 | uint64_t s[4];
27 | } xoshiro_state;
28 |
29 | uint64_t xoshiro_next(xoshiro_state *s);
30 |
31 | struct mt19937_64 {
32 | unsigned long long mt[312];
33 | int mti;
34 | };
35 |
36 | void init_genrand64(struct mt19937_64* context, unsigned long long seed);
37 | unsigned long long genrand64_int64(struct mt19937_64* context);
38 | double genrand64_real1(struct mt19937_64* context);
39 | double genrand64_real2(struct mt19937_64* context);
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/Sources/ReferenceRandomGenerators/mt19937-64.c:
--------------------------------------------------------------------------------
1 | /*
2 | A C-program for MT19937-64 (2004/9/29 version).
3 | Coded by Takuji Nishimura and Makoto Matsumoto.
4 |
5 | This is a 64-bit version of Mersenne Twister pseudorandom number
6 | generator.
7 |
8 | Before using, initialize the state by using init_genrand64(seed)
9 | or init_by_array64(init_key, key_length).
10 |
11 | Copyright (C) 2004, Makoto Matsumoto and Takuji Nishimura,
12 | All rights reserved.
13 |
14 | Redistribution and use in source and binary forms, with or without
15 | modification, are permitted provided that the following conditions
16 | are met:
17 |
18 | 1. Redistributions of source code must retain the above copyright
19 | notice, this list of conditions and the following disclaimer.
20 |
21 | 2. Redistributions in binary form must reproduce the above copyright
22 | notice, this list of conditions and the following disclaimer in the
23 | documentation and/or other materials provided with the distribution.
24 |
25 | 3. The names of its contributors may not be used to endorse or promote
26 | products derived from this software without specific prior written
27 | permission.
28 |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
33 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 |
41 | References:
42 | T. Nishimura, ``Tables of 64-bit Mersenne Twisters''
43 | ACM Transactions on Modeling and
44 | Computer Simulation 10. (2000) 348--357.
45 | M. Matsumoto and T. Nishimura,
46 | ``Mersenne Twister: a 623-dimensionally equidistributed
47 | uniform pseudorandom number generator''
48 | ACM Transactions on Modeling and
49 | Computer Simulation 8. (Jan. 1998) 3--30.
50 |
51 | Any feedback is very welcome.
52 | http://www.math.hiroshima-u.ac.jp/~m-mat/MT/emt.html
53 | email: m-mat @ math.sci.hiroshima-u.ac.jp (remove spaces)
54 | */
55 |
56 |
57 | #include
58 | #include
59 | #include
60 |
61 | #define NN 312
62 | #define MM 156
63 | #define MATRIX_A 0xB5026F5AA96619E9ULL
64 | #define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */
65 | #define LM 0x7FFFFFFFULL /* Least significant 31 bits */
66 |
67 | struct mt19937_64 {
68 | unsigned long long mt[NN];
69 | size_t mti;
70 | };
71 |
72 | /* initializes mt[NN] with a seed */
73 | void init_genrand64(struct mt19937_64* context, unsigned long long seed)
74 | {
75 | context->mt[0] = seed;
76 | for (context->mti=1; context->mtimti++)
77 | context->mt[context->mti] = (6364136223846793005ULL * (context->mt[context->mti-1] ^ (context->mt[context->mti-1] >> 62)) + context->mti);
78 | }
79 |
80 | struct mt19937_64 init_genrand64_fromtime()
81 | {
82 | struct mt19937_64 context;
83 | init_genrand64(&context, mach_absolute_time());
84 | return context;
85 | }
86 |
87 | /* initialize by an array with array-length */
88 | /* init_key is the array for initializing keys */
89 | /* key_length is its length */
90 | void init_by_array64(struct mt19937_64* context, unsigned long long init_key[],
91 | unsigned long long key_length)
92 | {
93 | unsigned long long i, j, k;
94 | init_genrand64(context, 19650218ULL);
95 | i=1; j=0;
96 | k = (NN>key_length ? NN : key_length);
97 | for (; k; k--) {
98 | context->mt[i] = (context->mt[i] ^ ((context->mt[i-1] ^ (context->mt[i-1] >> 62)) * 3935559000370003845ULL))
99 | + init_key[j] + j; /* non linear */
100 | i++; j++;
101 | if (i>=NN) { context->mt[0] = context->mt[NN-1]; i=1; }
102 | if (j>=key_length) j=0;
103 | }
104 | for (k=NN-1; k; k--) {
105 | context->mt[i] = (context->mt[i] ^ ((context->mt[i-1] ^ (context->mt[i-1] >> 62)) * 2862933555777941757ULL))
106 | - i; /* non linear */
107 | i++;
108 | if (i>=NN) { context->mt[0] = context->mt[NN-1]; i=1; }
109 | }
110 |
111 | context->mt[0] = 1ULL << 63; /* MSB is 1; assuring non-zero initial array */
112 | }
113 |
114 | /* generates a random number on [0, 2^64-1]-interval */
115 | unsigned long long genrand64_int64(struct mt19937_64* context)
116 | {
117 | #if 0
118 | /* This is the original implementation. It is replaced by the alternate implementation, below. */
119 | int i;
120 | unsigned long long x;
121 | static unsigned long long mag01[2]={0ULL, MATRIX_A};
122 |
123 | if (mti >= NN) { /* generate NN words at one time */
124 |
125 | /* if init_genrand64() has not been called, */
126 | /* a default initial seed is used */
127 | if (mti == NN+1)
128 | init_genrand64(5489ULL);
129 |
130 | for (i=0;i>1) ^ mag01[(int)(x&1ULL)];
133 | }
134 | for (;i>1) ^ mag01[(int)(x&1ULL)];
137 | }
138 | x = (mt[NN-1]&UM)|(mt[0]&LM);
139 | mt[NN-1] = mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
140 |
141 | mti = 0;
142 | }
143 |
144 | x = mt[mti++];
145 |
146 | x ^= (x >> 29) & 0x5555555555555555ULL;
147 | x ^= (x << 17) & 0x71D67FFFEDA60000ULL;
148 | x ^= (x << 37) & 0xFFF7EEE000000000ULL;
149 | x ^= (x >> 43);
150 |
151 | return x;
152 | #else
153 | /* This is the altered Cocoa with Love implementation. */
154 | size_t i;
155 | size_t j;
156 | unsigned long long result;
157 |
158 | if (context->mti >= NN) {/* generate NN words at one time */
159 | size_t mid = NN / 2;
160 | unsigned long long stateMid = context->mt[mid];
161 | unsigned long long x;
162 | unsigned long long y;
163 |
164 | /* NOTE: this "untwist" code is modified from the original to improve
165 | * performance, as described here:
166 | * http://www.cocoawithlove.com/blog/2016/05/19/random-numbers.html
167 | * These modifications are offered for use under the original icense at
168 | * the top of this file.
169 | */
170 | for (i = 0, j = mid; i != mid - 1; i++, j++) {
171 | x = (context->mt[i] & UM) | (context->mt[i + 1] & LM);
172 | context->mt[i] = context->mt[i + mid] ^ (x >> 1) ^ ((context->mt[i + 1] & 1) * MATRIX_A);
173 | y = (context->mt[j] & UM) | (context->mt[j + 1] & LM);
174 | context->mt[j] = context->mt[j - mid] ^ (y >> 1) ^ ((context->mt[j + 1] & 1) * MATRIX_A);
175 | }
176 | x = (context->mt[mid - 1] & UM) | (stateMid & LM);
177 | context->mt[mid - 1] = context->mt[NN - 1] ^ (x >> 1) ^ ((stateMid & 1) * MATRIX_A);
178 | y = (context->mt[NN - 1] & UM) | (context->mt[0] & LM);
179 | context->mt[NN - 1] = context->mt[mid - 1] ^ (y >> 1) ^ ((context->mt[0] & 1) * MATRIX_A);
180 |
181 | context->mti = 0;
182 | }
183 |
184 | result = context->mt[context->mti];
185 | context->mti = context->mti + 1;
186 |
187 | result ^= (result >> 29) & 0x5555555555555555ULL;
188 | result ^= (result << 17) & 0x71D67FFFEDA60000ULL;
189 | result ^= (result << 37) & 0xFFF7EEE000000000ULL;
190 | result ^= (result >> 43);
191 |
192 | return result;
193 | #endif
194 | }
195 |
196 |
197 | /* generates a random number on [0, 2^63-1]-interval */
198 | long long genrand64_int63(struct mt19937_64* context)
199 | {
200 | return (long long)(genrand64_int64(context) >> 1);
201 | }
202 |
203 | /* generates a random number on [0,1]-real-interval */
204 | double genrand64_real1(struct mt19937_64* context)
205 | {
206 | return (genrand64_int64(context) >> 11) * (1.0/9007199254740991.0);
207 | }
208 |
209 | /* generates a random number on [0,1)-real-interval */
210 | double genrand64_real2(struct mt19937_64* context)
211 | {
212 | return (genrand64_int64(context) >> 11) * (1.0/9007199254740992.0);
213 | }
214 |
215 | /* generates a random number on (0,1)-real-interval */
216 | double genrand64_real3(struct mt19937_64* context)
217 | {
218 | return ((genrand64_int64(context) >> 12) + 0.5) * (1.0/4503599627370496.0);
219 | }
220 |
--------------------------------------------------------------------------------
/Sources/ReferenceRandomGenerators/xoshiro256starstar.c:
--------------------------------------------------------------------------------
1 | /* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
2 |
3 | To the extent possible under law, the author has dedicated all copyright
4 | and related and neighboring rights to this software to the public domain
5 | worldwide. This software is distributed without any warranty.
6 |
7 | See . */
8 |
9 | #include
10 |
11 | /* This is xoshiro256** 1.0, our all-purpose, rock-solid generator. It has
12 | excellent (sub-ns) speed, a state (256 bits) that is large enough for
13 | any parallel application, and it passes all tests we are aware of.
14 |
15 | For generating just floating-point numbers, xoshiro256+ is even faster.
16 |
17 | The state must be seeded so that it is not everywhere zero. If you have
18 | a 64-bit seed, we suggest to seed a splitmix64 generator and use its
19 | output to fill s. */
20 |
21 | static inline uint64_t rotl(const uint64_t x, int k) {
22 | return (x << k) | (x >> (64 - k));
23 | }
24 |
25 | typedef struct {
26 | uint64_t s[4];
27 | } xoshiro_state;
28 |
29 | uint64_t xoshiro_next(xoshiro_state *state) {
30 | const uint64_t result_starstar = rotl(state->s[1] * 5, 7) * 9;
31 |
32 | const uint64_t t = state->s[1] << 17;
33 |
34 | state->s[2] ^= state->s[0];
35 | state->s[3] ^= state->s[1];
36 | state->s[1] ^= state->s[2];
37 | state->s[0] ^= state->s[3];
38 |
39 | state->s[2] ^= t;
40 |
41 | state->s[3] = rotl(state->s[3], 45);
42 |
43 | return result_starstar;
44 | }
45 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsPerformanceTests/CwlDequePerformanceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlDequePerformanceTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/09/13.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | #if DEBUG
26 | let outerCount = 100
27 | #else
28 | let outerCount = 100_000
29 | #endif
30 |
31 | let innerCount = 20
32 |
33 | class DequePerformanceTests: XCTestCase {
34 | func testFIFOPerformance() {
35 | measure { () -> Void in
36 | var accumulator = 0
37 | for _ in 1...outerCount {
38 | var deque = Deque()
39 | for i in 1...innerCount {
40 | deque.append(i)
41 | accumulator ^= (deque.last ?? 0)
42 | }
43 | for _ in 1...innerCount {
44 | accumulator ^= (deque.first ?? 0)
45 | deque.remove(at: 0)
46 | }
47 | }
48 | XCTAssert(accumulator == 0)
49 | }
50 | }
51 |
52 | func testReferenceArrayPerformance() {
53 | measure { () -> Void in
54 | var accumulator = 0
55 | for _ in 1...outerCount {
56 | var deque = Array()
57 | for i in 1...innerCount {
58 | deque.append(i)
59 | accumulator ^= (deque.last ?? 0)
60 | }
61 | for _ in 1...innerCount {
62 | accumulator ^= (deque.first ?? 0)
63 | deque.remove(at: 0)
64 | }
65 | }
66 | XCTAssert(accumulator == 0)
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsPerformanceTests/CwlMutexPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlMutexAdditionalComparisons.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/06/16.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import Foundation
21 | import CwlUtils
22 | import XCTest
23 |
24 | private extension PThreadMutex {
25 | func sync_same_file(f: () throws -> R) rethrows -> R {
26 | pthread_mutex_lock(&underlyingMutex)
27 | defer { pthread_mutex_unlock(&underlyingMutex) }
28 | return try f()
29 | }
30 | }
31 |
32 | public struct DispatchSemaphoreWrapper {
33 | let s = DispatchSemaphore(value: 1)
34 | init() {}
35 | func sync(f: () throws -> R) rethrows -> R {
36 | _ = s.wait(timeout: DispatchTime.distantFuture)
37 | defer { s.signal() }
38 | return try f()
39 | }
40 | }
41 |
42 | let iterations = 10_000_000
43 |
44 | class TestClass {
45 | var testVariable: Int = 0
46 | init() {
47 | }
48 | func increment() {
49 | testVariable += 1
50 | }
51 | }
52 |
53 | class MutexPerformanceTests: XCTestCase {
54 |
55 | func testPThreadSyncCapturingClosurePerformance() {
56 | let mutex = PThreadMutex()
57 | measure { () -> Void in
58 | var total = 0
59 | for _ in 0.. Void in
71 | var total = 0
72 | for _ in 0.. Void in
84 | var total = 0
85 | for _ in 0.. Void in
97 | var total = 0
98 | for _ in 0.. Void in
110 | var total = 0
111 | for _ in 0.. Void in
123 | var total = 0
124 | for _ in 0.. Void in
136 | var total = 0
137 | for _ in 0.. Void in
149 | var total = 0
150 | for _ in 0.. Void in
163 | var total = 0
164 | for _ in 0.. Void in
32 | var sum: UInt64 = 0
33 | for _ in 0..<(PerformanceIterations / 100) {
34 | sum = sum &+ generator.next()
35 | }
36 | XCTAssert(sum != 0)
37 | }
38 | }
39 |
40 | func testArc4RandomDiv10() {
41 | var generator = SystemRandomNumberGenerator()
42 |
43 | measure { () -> Void in
44 | var sum: UInt64 = 0
45 | for _ in 0..<(PerformanceIterations / 10) {
46 | sum = sum &+ generator.next()
47 | }
48 | XCTAssert(sum != 0)
49 | }
50 | }
51 |
52 | func testLfsr258() {
53 | var generator = Lfsr258()
54 |
55 | measure { () -> Void in
56 | var sum: UInt64 = 0
57 | for _ in 0.. Void in
68 | var sum: UInt64 = 0
69 | for _ in 0.. Void in
80 | var sum: UInt64 = 0
81 | for _ in 0.. Void in
92 | var sum: UInt64 = 0
93 | for _ in 0.. Void in
104 | var sum: UInt64 = 0
105 | for _ in 0.. Void in
116 | var sum: UInt64 = 0
117 | for _ in 0.. Void in
128 | var sum: UInt64 = 0
129 | for _ in 0.. UInt64 {
156 | return genrand64_int64(&state)
157 | }
158 | }
159 |
160 | private struct Xoshiro256starstar: RandomNumberGenerator {
161 | var state = { () -> xoshiro_state in var dr = DevRandom(); return xoshiro_state(s: (dr.next(), dr.next(), dr.next(), dr.next())) }()
162 |
163 | init() {
164 | }
165 |
166 | init(seed: (UInt64, UInt64, UInt64, UInt64)) {
167 | self.state.s = seed
168 | }
169 |
170 | mutating func next() -> UInt64 {
171 | return xoshiro_next(&state)
172 | }
173 | }
174 |
175 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlAddressInfoTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlAddressInfoTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/26.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class AddressInfoTests: XCTestCase {
26 | func testCallingFunctionIdentifier() {
27 | let name = callingFunctionIdentifier()
28 | #if SWIFT_PACKAGE
29 | XCTAssert(name.hasSuffix("11AddressInfoC0C29testCallingFunctionIdentifieryyF"))
30 | #else
31 | XCTAssert(name.hasSuffix("16AddressInfoTestsC29testCallingFunctionIdentifieryyF"))
32 | #endif
33 | }
34 |
35 | func testSymbolsForCallStackAddresses() {
36 | var b = Thread.callStackSymbols
37 | b.remove(at: 0)
38 | var a = symbolsForCallStack(addresses: callStackReturnAddresses())
39 | a.remove(at: 0)
40 | XCTAssert(a == b)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlCaseNameCodableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlCaseNameCodableTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 9/3/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XCTest
11 | import CwlUtils
12 |
13 | struct A: Codable {
14 | var value: Int = 123
15 | }
16 |
17 | struct B: Codable {
18 | }
19 |
20 | enum Sum: CaseNameCodable {
21 | enum CaseName: String, CaseNameDecoder {
22 | case a
23 | case b
24 | case c
25 |
26 | func decode(from container: KeyedDecodingContainer) throws -> Sum {
27 | switch self {
28 | case .a: return .a(try container.decode(A.self, forKey: self))
29 | case .b: return .b(B())
30 | case .c: return .c
31 | }
32 | }
33 | }
34 |
35 | case a(A)
36 | case b(B)
37 | case c
38 | }
39 |
40 | class CaseNameCodableTests: XCTestCase {
41 | func testCaseNameCodableAssociatedValue() {
42 | let x = Sum.a(A())
43 | let data = try! JSONEncoder().encode(x)
44 | let string = String(data: data, encoding: .utf8)!
45 | XCTAssertEqual(string, #"{"a":{"value":123}}"#)
46 |
47 | let y = try! JSONDecoder().decode(Sum.self, from: data)
48 | if case .a(let a) = y, a.value == 123 {
49 | } else {
50 | XCTFail("Failed to decode as expected")
51 | }
52 | }
53 |
54 | func testCaseNameCodableEmptyAssociatedValue() {
55 | let x = Sum.b(B())
56 | let data = try! JSONEncoder().encode(x)
57 | let string = String(data: data, encoding: .utf8)!
58 | XCTAssertEqual(string, #"{"b":{}}"#)
59 |
60 | let y = try! JSONDecoder().decode(Sum.self, from: data)
61 | if case .b = y {
62 | } else {
63 | XCTFail("Failed to decode as expected")
64 | }
65 | }
66 |
67 | func testCaseNameCodableNoAssociatedValue() {
68 | let x = Sum.c
69 | let data = try! JSONEncoder().encode(x)
70 | let string = String(data: data, encoding: .utf8)!
71 | XCTAssertEqual(string, #"{"c":null}"#)
72 |
73 | let y = try! JSONDecoder().decode(Sum.self, from: data)
74 | if case .c = y {
75 | } else {
76 | XCTFail("Failed to decode as expected")
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlCollectionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlCollectionTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class CollectionTests: XCTestCase {
26 | func testIsNil() {
27 | let a = Optional>.some(nil)
28 | XCTAssert(a.isNil == false)
29 | XCTAssert(a?.isNil == true)
30 | }
31 | func testAtCollection() {
32 | let a = 7..<9
33 | let at_0 = a.at(7)
34 | let at_2 = a.at(9)
35 | let at_1 = a.at(-1)
36 | XCTAssert(at_0! == 7 && at_1 == nil && at_2 == nil)
37 | }
38 | func testCollectionAppend() {
39 | var a = [0, 1, 2]
40 | a += 3
41 | a += Optional.some(4)
42 | a += nil
43 | XCTAssertEqual(a, [0, 1, 2, 3, 4])
44 |
45 | let b = [0, 1]
46 | let c = b.appending(2)
47 | XCTAssertEqual(c, [0, 1, 2])
48 | }
49 | func testRangeAtCollection() {
50 | let a = 7..<29
51 | let at_1 = a.at(5..<6 as Range)
52 | let at_2 = a.at(5..<7 as Range)
53 | let at_3 = a.at(5..<8 as Range)
54 | let at_4 = a.at(12..<15 as Range)
55 | let at_5 = a.at(5..<30 as Range)
56 | let at_6 = a.at(28..<30 as Range)
57 | let at_7 = a.at(29..<30 as Range)
58 | let at_8 = a.at(30..<30 as Range)
59 | XCTAssert(at_1 == 7..<7)
60 | XCTAssert(at_2 == 7..<7)
61 | XCTAssert(at_3 == 7..<8)
62 | XCTAssert(at_4 == 12..<15)
63 | XCTAssert(at_5 == 7..<29)
64 | XCTAssert(at_6 == 28..<29)
65 | XCTAssert(at_7 == 29..<29)
66 | XCTAssert(at_8 == 29..<29)
67 | }
68 | func testCountableRangeAtCollection() {
69 | let a = 7..<29
70 | let at_1 = a.at(5..<6 as CountableRange)
71 | let at_2 = a.at(5..<7 as CountableRange)
72 | let at_3 = a.at(5..<8 as CountableRange)
73 | let at_4 = a.at(12..<15 as CountableRange)
74 | let at_5 = a.at(5..<30 as CountableRange)
75 | let at_6 = a.at(28..<30 as CountableRange)
76 | let at_7 = a.at(29..<30 as CountableRange)
77 | let at_8 = a.at(30..<30 as CountableRange)
78 | XCTAssert(at_1 == 7..<7)
79 | XCTAssert(at_2 == 7..<7)
80 | XCTAssert(at_3 == 7..<8)
81 | XCTAssert(at_4 == 12..<15)
82 | XCTAssert(at_5 == 7..<29)
83 | XCTAssert(at_6 == 28..<29)
84 | XCTAssert(at_7 == 29..<29)
85 | XCTAssert(at_8 == 29..<29)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlDeferredWorkTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlDeferredWorkTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 11/6/16.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 | import CwlPreconditionTesting
25 |
26 | #if SWIFT_PACKAGE
27 | import CwlMachBadInstructionHandler
28 | #endif
29 |
30 | class DeferredWorkTests: XCTestCase {
31 |
32 | #if os(iOS) || os(macOS)
33 | override func setUp() {
34 | // Suppress Swift runtime's direct triggering of the debugger.
35 | _swift_reportFatalErrorsToDebugger = false
36 | }
37 |
38 | override func tearDown() {
39 | // Undo our debugger change.
40 | _swift_reportFatalErrorsToDebugger = true
41 | }
42 | #endif
43 |
44 | func testDeferredWork() {
45 | var dw = DeferredWork()
46 | dw.runWork()
47 | }
48 |
49 | func testDeferredWorkDoubleRun() {
50 | var dw = DeferredWork()
51 | dw.runWork()
52 |
53 | let e = catchBadInstruction {
54 | dw.runWork()
55 | }
56 | XCTAssert(e != nil)
57 | }
58 |
59 | func testDeferredWorkAppendRun() {
60 | var a = false
61 | var b = false
62 | var c = false
63 | var dw = DeferredWork { a = true }
64 | let dw2 = DeferredWork { b = true }
65 |
66 | dw.append(dw2)
67 | dw.runWork()
68 |
69 | XCTAssert(a == true && b == true)
70 |
71 | let dw3 = DeferredWork { c = true }
72 |
73 | let e1 = catchBadInstruction {
74 | dw.append(dw3)
75 | }
76 | XCTAssert(e1 != nil)
77 | XCTAssert(c == false)
78 |
79 | let e2 = catchBadInstruction {
80 | var x = false
81 | dw.append {
82 | XCTFail()
83 | x = true
84 | withExtendedLifetime(x) {}
85 | }
86 | }
87 | XCTAssert(e2 != nil)
88 | }
89 |
90 | func testDeferredWorkClosureOnConstruction() {
91 | var reachedPoint1 = false
92 | var dw = DeferredWork { reachedPoint1 = true }
93 | XCTAssert(!reachedPoint1)
94 | dw.runWork()
95 | XCTAssert(reachedPoint1)
96 | }
97 |
98 | func testDeferredWorkAppend() {
99 | var reachedPoint1 = false
100 | var reachedPoint2 = false
101 | var reachedPoint3 = false
102 | var dw = DeferredWork {
103 | XCTAssert(!reachedPoint2)
104 | XCTAssert(!reachedPoint3)
105 | reachedPoint1 = true
106 | }
107 | dw.append {
108 | XCTAssert(reachedPoint1)
109 | XCTAssert(!reachedPoint3)
110 | reachedPoint2 = true
111 | }
112 | dw.append {
113 | XCTAssert(reachedPoint1)
114 | XCTAssert(reachedPoint2)
115 | reachedPoint3 = true
116 | }
117 | XCTAssert(!reachedPoint1)
118 | XCTAssert(!reachedPoint2)
119 | XCTAssert(!reachedPoint3)
120 | dw.runWork()
121 | XCTAssert(reachedPoint1)
122 | XCTAssert(reachedPoint2)
123 | XCTAssert(reachedPoint3)
124 |
125 | var reachedPoint4 = false
126 | var dw2 = DeferredWork()
127 | dw2.append { reachedPoint4 = true }
128 | XCTAssert(!reachedPoint4)
129 | dw2.runWork()
130 | XCTAssert(reachedPoint4)
131 | }
132 |
133 | func testDeferredWorkAppendOther() {
134 | do {
135 | // None plus none
136 | var dw1 = DeferredWork()
137 | let dw2 = DeferredWork()
138 | dw1.append(dw2)
139 | dw1.runWork()
140 | }
141 |
142 | do {
143 | // None plus single
144 | var reachedPoint2 = false
145 | var dw1 = DeferredWork()
146 | let dw2 = DeferredWork { reachedPoint2 = true }
147 | dw1.append(dw2)
148 | XCTAssert(!reachedPoint2)
149 | dw1.runWork()
150 | XCTAssert(reachedPoint2)
151 | }
152 |
153 | do {
154 | // None plus multiple
155 | var reachedPoint1 = false
156 | var reachedPoint2 = false
157 | var dw1 = DeferredWork()
158 | var dw2 = DeferredWork { reachedPoint2 = true }
159 | dw2.append { reachedPoint1 = true }
160 | dw1.append(dw2)
161 | XCTAssert(!reachedPoint1)
162 | XCTAssert(!reachedPoint2)
163 | dw1.runWork()
164 | XCTAssert(reachedPoint1)
165 | XCTAssert(reachedPoint2)
166 | }
167 |
168 | do {
169 | // Single plus none
170 | var reachedPoint1 = false
171 | var dw1 = DeferredWork { reachedPoint1 = true }
172 | let dw2 = DeferredWork()
173 | dw1.append(dw2)
174 | XCTAssert(!reachedPoint1)
175 | dw1.runWork()
176 | XCTAssert(reachedPoint1)
177 | }
178 |
179 | do {
180 | // Single plus single
181 | var reachedPoint1 = false
182 | var reachedPoint2 = false
183 | var dw1 = DeferredWork { reachedPoint1 = true }
184 | let dw2 = DeferredWork { reachedPoint2 = true }
185 | dw1.append(dw2)
186 | XCTAssert(!reachedPoint1)
187 | XCTAssert(!reachedPoint2)
188 | dw1.runWork()
189 | XCTAssert(reachedPoint1)
190 | XCTAssert(reachedPoint2)
191 | }
192 |
193 | do {
194 | // Single plus multiple
195 | var reachedPoint1 = false
196 | var reachedPoint2 = false
197 | var reachedPoint3 = false
198 | var dw1 = DeferredWork { reachedPoint1 = true }
199 | var dw2 = DeferredWork { reachedPoint2 = true }
200 | dw2.append { reachedPoint3 = true }
201 | dw1.append(dw2)
202 | XCTAssert(!reachedPoint1)
203 | XCTAssert(!reachedPoint2)
204 | XCTAssert(!reachedPoint3)
205 | dw1.runWork()
206 | XCTAssert(reachedPoint1)
207 | XCTAssert(reachedPoint2)
208 | XCTAssert(reachedPoint3)
209 | }
210 |
211 | do {
212 | // Multiple plus none
213 | var reachedPoint1 = false
214 | var reachedPoint2 = false
215 | var dw1 = DeferredWork { reachedPoint1 = true }
216 | let dw2 = DeferredWork()
217 | dw1.append { reachedPoint2 = true }
218 | dw1.append(dw2)
219 | XCTAssert(!reachedPoint1)
220 | XCTAssert(!reachedPoint2)
221 | dw1.runWork()
222 | XCTAssert(reachedPoint1)
223 | XCTAssert(reachedPoint2)
224 | }
225 |
226 | do {
227 | // Multiple plus single
228 | var reachedPoint1 = false
229 | var reachedPoint2 = false
230 | var reachedPoint3 = false
231 | var dw1 = DeferredWork { reachedPoint1 = true }
232 | let dw2 = DeferredWork { reachedPoint3 = true }
233 | dw1.append { reachedPoint2 = true }
234 | dw1.append(dw2)
235 | XCTAssert(!reachedPoint1)
236 | XCTAssert(!reachedPoint2)
237 | XCTAssert(!reachedPoint3)
238 | dw1.runWork()
239 | XCTAssert(reachedPoint1)
240 | XCTAssert(reachedPoint2)
241 | XCTAssert(reachedPoint3)
242 | }
243 |
244 | do {
245 | // Multiple plus multiple
246 | var reachedPoint1 = false
247 | var reachedPoint2 = false
248 | var reachedPoint3 = false
249 | var reachedPoint4 = false
250 | var dw1 = DeferredWork { reachedPoint1 = true }
251 | var dw2 = DeferredWork { reachedPoint3 = true }
252 | dw1.append { reachedPoint2 = true }
253 | dw2.append { reachedPoint4 = true }
254 | dw1.append(dw2)
255 | XCTAssert(!reachedPoint1)
256 | XCTAssert(!reachedPoint2)
257 | XCTAssert(!reachedPoint3)
258 | XCTAssert(!reachedPoint4)
259 | dw1.runWork()
260 | XCTAssert(reachedPoint1)
261 | XCTAssert(reachedPoint2)
262 | XCTAssert(reachedPoint3)
263 | XCTAssert(reachedPoint4)
264 | }
265 | }
266 |
267 | func testDeferredWorkWithoutRunning() {
268 | #if DEBUG
269 | let e = catchBadInstruction {
270 | _ = DeferredWork()
271 | }
272 | XCTAssert(e != nil)
273 | #endif
274 | }
275 | }
276 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlDequeTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlDequeTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/09/13.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class DequeTests: XCTestCase {
26 | func testAppend() {
27 | var deque = Deque>()
28 | for i in 1...100 {
29 | deque.append(.success(i))
30 | }
31 | XCTAssert(deque.count == 100)
32 |
33 | for i in 1...2_000 {
34 | deque.remove(at: 0)
35 | deque.append(.success(i))
36 | }
37 | for i in 1...2_000 {
38 | deque.remove(at: deque.count - 1)
39 | deque.insert(.success(i), at: 0)
40 | }
41 |
42 | var i = 0
43 | while deque.count > 0 {
44 | deque.remove(at: 0)
45 | i += 1
46 | }
47 | XCTAssert(i == 100)
48 | }
49 |
50 | func testRemoveFirst() {
51 | var deque = Deque>()
52 | deque.append(.success(1))
53 | deque.append(.success(3))
54 | deque.append(.success(5))
55 | deque.append(.success(7))
56 | deque.append(.success(11))
57 |
58 | XCTAssert(deque.removeFirst().value == 1)
59 | XCTAssert(deque.removeFirst().value == 3)
60 |
61 | deque.append(.success(13))
62 | deque.append(.success(17))
63 | deque.append(.success(19))
64 | deque.append(.success(23))
65 |
66 | XCTAssert(deque[4].value == 17)
67 |
68 | XCTAssert(deque.removeFirst().value == 5)
69 |
70 | deque.append(.success(29))
71 |
72 | XCTAssert(deque.removeFirst().value == 7)
73 | XCTAssert(deque.removeFirst().value == 11)
74 | XCTAssert(deque.removeFirst().value == 13)
75 |
76 | deque.append(.success(31))
77 | deque.append(.success(37))
78 | deque.append(.success(41))
79 | deque.append(.success(43))
80 | XCTAssert(deque.removeFirst().value == 17)
81 |
82 | XCTAssert(deque.count == 7)
83 | }
84 |
85 | func testAdvancingRemoveInsert() {
86 | var deque: Deque = [0, 1, 2]
87 | deque.remove(at: 0)
88 | XCTAssert(Array(deque) == [1, 2])
89 | deque.insert(3, at: 2)
90 | XCTAssert(Array(deque) == [1, 2, 3])
91 | deque.remove(at: 0)
92 | XCTAssert(Array(deque) == [2, 3])
93 | deque.insert(4, at: 2)
94 | XCTAssert(Array(deque) == [2, 3, 4])
95 | deque.remove(at: 0)
96 | XCTAssert(Array(deque) == [3, 4])
97 | deque.insert(5, at: 2)
98 | XCTAssert(Array(deque) == [3, 4, 5])
99 |
100 | XCTAssert(deque[0] == 3)
101 | XCTAssert(deque[1] == 4)
102 | XCTAssert(deque[2] == 5)
103 | }
104 |
105 | func testRegressingRemoveInsert() {
106 | var deque: Deque = [0, 1, 2]
107 | deque.remove(at: 2)
108 | XCTAssert(Array(deque) == [0, 1])
109 | deque.insert(-1, at: 0)
110 | XCTAssert(Array(deque) == [-1, 0, 1])
111 | deque.remove(at: 2)
112 | XCTAssert(Array(deque) == [-1, 0])
113 | deque.insert(-2, at: 0)
114 | XCTAssert(Array(deque) == [-2, -1, 0])
115 | deque.remove(at: 2)
116 | XCTAssert(Array(deque) == [-2, -1])
117 | deque.insert(-3, at: 0)
118 | XCTAssert(Array(deque) == [-3, -2, -1])
119 |
120 | XCTAssert(deque[0] == -3)
121 | XCTAssert(deque[1] == -2)
122 | XCTAssert(deque[2] == -1)
123 | }
124 |
125 | func testRangeReplacing() {
126 | var deque: Deque = [0, 1, 2]
127 | deque.replaceSubrange(1..<1, with: [3, 4, 5, 6, 7, 8])
128 | XCTAssert(Array(deque) == [0, 3, 4, 5, 6, 7, 8, 1, 2])
129 |
130 | deque.replaceSubrange(4..<6, with: [9, 10, 11, 12, 13])
131 | XCTAssert(Array(deque) == [0, 3, 4, 5, 9, 10, 11, 12, 13, 8, 1, 2])
132 |
133 | deque.replaceSubrange(9..<12, with: [14])
134 | XCTAssert(Array(deque) == [0, 3, 4, 5, 9, 10, 11, 12, 13, 14])
135 |
136 | deque.replaceSubrange(0..<8, with: [15, 16])
137 | XCTAssert(Array(deque) == [15, 16, 13, 14])
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlDispatchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlDispatchTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/07/30.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class DispatchTests: XCTestCase {
26 | let queue = DispatchQueue(label: "")
27 | let value = 3
28 | let key = DispatchSpecificKey()
29 |
30 | override func setUp() {
31 | queue.setSpecific(key: key, value: value)
32 | }
33 |
34 | func testSynchronousSingleTimer() {
35 | do {
36 | // Ensure falling out of scope cancels the timer
37 | _ = DispatchSource.singleTimer(interval: .milliseconds(10), queue: queue) {
38 | XCTFail()
39 | }
40 | }
41 |
42 | let ex = expectation(description: "")
43 | var timer: DispatchSourceTimer? = nil
44 | queue.sync {
45 | timer = DispatchSource.singleTimer(interval: .milliseconds(50), queue: queue) {
46 | // Ensure test occurs on the appropriate queue
47 | XCTAssert(DispatchQueue.getSpecific(key: self.key) != nil)
48 | ex.fulfill()
49 | }
50 | }
51 | waitForExpectations(timeout: 1e2, handler: nil)
52 | withExtendedLifetime(timer) {}
53 | }
54 |
55 | func testSynchronousPeriodicTimer() {
56 | do {
57 | // Ensure falling out of scope cancels the timer
58 | _ = DispatchSource.repeatingTimer(interval: .milliseconds(10), queue: queue) {
59 | XCTFail()
60 | }
61 | }
62 |
63 | let ex = expectation(description: "")
64 | var timer: DispatchSourceTimer? = nil
65 | var fireCount = 0
66 | queue.sync {
67 | timer = DispatchSource.repeatingTimer(interval: .milliseconds(50), queue: queue) {
68 | // Ensure test occurs on the appropriate queue
69 | XCTAssert(DispatchQueue.getSpecific(key: self.key) == self.value)
70 |
71 | fireCount += 1
72 | if fireCount == 3 {
73 | ex.fulfill()
74 | }
75 | }
76 | }
77 | waitForExpectations(timeout: 1e2, handler: nil)
78 | withExtendedLifetime(timer) {}
79 | }
80 |
81 | func testParametricSingleTimer() {
82 | do {
83 | // Ensure falling out of scope cancels the timer
84 | _ = DispatchSource.singleTimer(parameter: 0, interval: .milliseconds(10)) { p in
85 | XCTFail()
86 | }
87 | }
88 |
89 | let ex1 = expectation(description: "")
90 | let ex2 = expectation(description: "")
91 | var timer: DispatchSourceTimer? = nil
92 | var outerMutexComplete = false
93 |
94 | // Test rescheduling a timer during the callback for an earlier scheduling
95 | queue.sync {
96 | timer = DispatchSource.singleTimer(parameter: 1, interval: .milliseconds(1)) { p in
97 | XCTAssert(!outerMutexComplete)
98 | self.queue.sync {
99 | XCTAssert(outerMutexComplete)
100 | XCTAssert(p == 1)
101 | ex1.fulfill()
102 | }
103 | }
104 | Thread.sleep(forTimeInterval: 0.1)
105 | timer?.scheduleOneshot(parameter: 2, interval: .milliseconds(1)) { p in
106 | XCTAssert(outerMutexComplete)
107 | self.queue.sync {
108 | XCTAssert(outerMutexComplete)
109 | XCTAssert(p == 2)
110 | ex2.fulfill()
111 | }
112 | }
113 | Thread.sleep(forTimeInterval: 0.1)
114 | outerMutexComplete = true
115 | }
116 | waitForExpectations(timeout: 1e2, handler: nil)
117 | withExtendedLifetime(timer) {}
118 | }
119 |
120 | func testParametricPeriodicTimer() {
121 | do {
122 | // Ensure falling out of scope cancels the timer
123 | _ = DispatchSource.repeatingTimer(parameter: 0, interval: .milliseconds(10)) { p in
124 | XCTFail()
125 | }
126 | }
127 |
128 | let ex1 = expectation(description: "")
129 | let ex2 = expectation(description: "")
130 | var timer: DispatchSourceTimer? = nil
131 | var outerMutexComplete = false
132 | var fireCount = 0
133 |
134 | // Test rescheduling a timer during the callback for an earlier scheduling
135 | queue.sync {
136 | timer = DispatchSource.repeatingTimer(parameter: 1, interval: .milliseconds(1)) { p in
137 | XCTAssert(!outerMutexComplete)
138 | self.queue.sync {
139 | XCTAssert(outerMutexComplete)
140 | XCTAssert(p == 1)
141 | ex1.fulfill()
142 | }
143 | }
144 | Thread.sleep(forTimeInterval: 0.1)
145 | timer?.scheduleRepeating(parameter: 2, interval: .milliseconds(1)) { p in
146 | XCTAssert(outerMutexComplete)
147 | self.queue.sync {
148 | XCTAssert(outerMutexComplete)
149 | XCTAssert(p == 2)
150 |
151 | fireCount += 1
152 | if fireCount == 3 {
153 | ex2.fulfill()
154 | }
155 | }
156 | }
157 | Thread.sleep(forTimeInterval: 0.1)
158 | outerMutexComplete = true
159 | }
160 | waitForExpectations(timeout: 1e2, handler: nil)
161 | withExtendedLifetime(timer) {}
162 | }
163 |
164 | func testFromTimeInterval() {
165 | let d = DispatchTimeInterval.interval(1.125)
166 | XCTAssert(d.seconds == 1.125)
167 | }
168 |
169 | func testToSeconds() {
170 | let s = DispatchTimeInterval.seconds(1).seconds
171 | XCTAssert(s > 0.99999999 && s < 1.00000001)
172 |
173 | let m = DispatchTimeInterval.milliseconds(1).seconds
174 | XCTAssert(m > 0.99999999e-3 && m < 1.00000001e-3)
175 |
176 | let u = DispatchTimeInterval.microseconds(1).seconds
177 | XCTAssert(u > 0.99999999e-6 && u < 1.00000001e-6)
178 |
179 | let n = DispatchTimeInterval.nanoseconds(1).seconds
180 | XCTAssert(n > 0.99999999e-9 && n < 1.00000001e-9)
181 | }
182 |
183 | func testToNanoseconds() {
184 | let s = DispatchTimeInterval.seconds(1).nanoseconds
185 | XCTAssert(s == Int64(1e9))
186 |
187 | let m = DispatchTimeInterval.milliseconds(1).nanoseconds
188 | XCTAssert(m == Int64(1e6))
189 |
190 | let u = DispatchTimeInterval.microseconds(1).nanoseconds
191 | XCTAssert(u == Int64(1e3))
192 |
193 | let n = DispatchTimeInterval.nanoseconds(1).nanoseconds
194 | XCTAssert(n == Int64(1))
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlExecTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlExecTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class ExecTests: XCTestCase {
26 | func testDirect() {
27 | XCTAssert(Exec.direct.type.isConcurrent == true)
28 | XCTAssert(Exec.direct.type.isImmediateInCurrentContext == true)
29 |
30 | var x = false
31 | Exec.direct.invoke { x = true }
32 | XCTAssert(x, "Block ran")
33 |
34 | var y = false
35 | Exec.direct.invokeSync { y = true }
36 | XCTAssert(y, "Block ran")
37 |
38 | let e2 = expectation(description: "Block 2 not invoked")
39 | Exec.direct.invokeAsync { e2.fulfill() }
40 |
41 | waitForExpectations(timeout: 1e1, handler: nil)
42 |
43 | let serialized = Exec.direct.serialized()
44 | if case .custom = serialized {
45 | } else {
46 | XCTFail()
47 | }
48 | }
49 |
50 | func testMain() {
51 | XCTAssert(Thread.current == Thread.main)
52 | XCTAssert(Exec.main.type.isConcurrent == false)
53 | XCTAssert(Exec.main.type.isImmediateInCurrentContext == true)
54 |
55 | let e3 = expectation(description: "Block not invoked")
56 | Exec.global.invoke {
57 | XCTAssert(Exec.main.type.isImmediateInCurrentContext == false)
58 |
59 | let lock = DispatchQueue(label: "")
60 | var x = false
61 | lock.sync {
62 | Exec.main.invoke {
63 | lock.sync { x = true }
64 | }
65 | XCTAssert(!x, "Block should not yet run")
66 | }
67 |
68 | var y = false
69 | Exec.main.invokeSync { y = true }
70 | XCTAssert(y, "Block ran")
71 |
72 | e3.fulfill()
73 | }
74 |
75 | var x = false
76 | Exec.main.invoke { x = true }
77 | XCTAssert(x, "Block ran")
78 |
79 | var y = false
80 | Exec.main.invokeSync { y = true }
81 | XCTAssert(y, "Block ran")
82 |
83 | let e2 = expectation(description: "Block not invoked")
84 | Exec.main.invokeAsync { e2.fulfill() }
85 |
86 | waitForExpectations(timeout: 1e1, handler: nil)
87 |
88 | let serialized = Exec.main.serialized()
89 | if case .main = serialized {
90 | } else {
91 | XCTFail()
92 | }
93 | }
94 |
95 | func testMainAsync() {
96 | XCTAssert(Exec.mainAsync.type.isConcurrent == false)
97 | XCTAssert(Exec.mainAsync.type.isImmediateInCurrentContext == false)
98 |
99 | let e1 = expectation(description: "Block not invoked")
100 | var run1 = false
101 | Exec.mainAsync.invoke {
102 | run1 = true
103 | e1.fulfill()
104 | }
105 | XCTAssert(run1 == false)
106 |
107 | var run2 = false
108 | Exec.mainAsync.invokeSync { run2 = true }
109 | XCTAssert(run2 == true)
110 |
111 | let e5 = expectation(description: "Block not invoked")
112 | Exec.global.invoke {
113 | let lock = DispatchQueue(label: "")
114 | var x = false
115 | lock.sync {
116 | Exec.mainAsync.invoke {
117 | lock.sync { x = true }
118 | }
119 | }
120 | XCTAssert(!x, "Block should not yet run")
121 |
122 | var y = false
123 | Exec.mainAsync.invokeSync { y = true }
124 | XCTAssert(y, "Block ran")
125 |
126 | Exec.mainAsync.invokeAsync {
127 | e5.fulfill()
128 | }
129 | }
130 |
131 | let e4 = expectation(description: "Block not invoked")
132 | var run3 = false
133 | Exec.mainAsync.invokeAsync {
134 | run3 = true
135 | e4.fulfill()
136 | }
137 | XCTAssert(run3 == false)
138 |
139 | waitForExpectations(timeout: 1e1, handler: nil)
140 |
141 | let serialized = Exec.mainAsync.serialized()
142 | if case .queue(let q, let t) = serialized, case .threadAsync = t {
143 | XCTAssert(q == DispatchQueue.main)
144 | } else {
145 | XCTFail()
146 | }
147 | }
148 |
149 | func testQueue() {
150 | let ec1 = Exec.syncQueue()
151 | guard case .queue(let q1, _) = ec1 else {
152 | fatalError()
153 | }
154 | let sk1 = DispatchSpecificKey()
155 | q1.setSpecific(key: sk1, value: ())
156 |
157 | XCTAssert(ec1.type.isConcurrent == false)
158 | XCTAssert(ec1.type.isImmediateInCurrentContext == true)
159 |
160 | let ec2 = Exec.asyncQueue()
161 | guard case .queue(let q2, _) = ec2 else {
162 | fatalError()
163 | }
164 | let sk2 = DispatchSpecificKey()
165 | q2.setSpecific(key: sk2, value: ())
166 | XCTAssert(ec2.type.isConcurrent == false)
167 | XCTAssert(ec2.type.isImmediateInCurrentContext == false)
168 |
169 | var a = false
170 | ec1.invoke() {
171 | XCTAssert(DispatchQueue.getSpecific(key: sk1) != nil)
172 | a = true
173 | }
174 | XCTAssert(a)
175 |
176 | let x1 = expectation(description: "Block not invoked")
177 | ec2.invoke() {
178 | XCTAssert(DispatchQueue.getSpecific(key: sk2) != nil)
179 | x1.fulfill()
180 | }
181 |
182 | let x2 = expectation(description: "Block 2 not invoked")
183 | ec1.invokeAsync() {
184 | XCTAssert(DispatchQueue.getSpecific(key: sk1) != nil)
185 | x2.fulfill()
186 | }
187 |
188 | let x3 = expectation(description: "Block 3 not invoked")
189 | ec2.invokeAsync() {
190 | XCTAssert(DispatchQueue.getSpecific(key: sk2) != nil)
191 | x3.fulfill()
192 | }
193 |
194 | var y1 = false
195 | ec1.invokeSync() {
196 | XCTAssert(DispatchQueue.getSpecific(key: sk1) != nil)
197 | y1 = true
198 | }
199 | XCTAssert(y1)
200 |
201 | var y2 = false
202 | ec2.invokeSync() {
203 | XCTAssert(DispatchQueue.getSpecific(key: sk2) != nil)
204 | y2 = true
205 | }
206 | XCTAssert(y2)
207 |
208 | waitForExpectations(timeout: 1e1, handler: nil)
209 |
210 | let serialized = ec1.serialized()
211 | if case .queue(_, let t) = serialized, case .mutex = t {
212 | } else {
213 | XCTFail()
214 | }
215 | }
216 |
217 | func testGlobal() {
218 | let variants: [Exec] = [.interactive, .user, .global, .utility, .background]
219 | let expectations1: [XCTestExpectation] = variants.map({ v in self.expectation(description: "Block \(v), 1 not invoked") })
220 | let expectations2: [XCTestExpectation] = variants.map({ v in self.expectation(description: "Block \(v), 2 not invoked") })
221 | for (i, variant) in variants.enumerated() {
222 | variant.invoke { expectations1[i].fulfill() }
223 | variant.invokeAsync { expectations2[i].fulfill() }
224 | XCTAssert(variant.type.isConcurrent == true)
225 | XCTAssert(variant.type.isImmediateInCurrentContext == false)
226 |
227 | var x = false
228 | variant.invokeSync { x = true }
229 | XCTAssert(x)
230 |
231 | let serialized = variant.serialized()
232 | if case .custom = serialized {
233 | } else {
234 | XCTFail()
235 | }
236 | }
237 | waitForExpectations(timeout: 1e1, handler: nil)
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlKeyValueObserverTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlKeyValueObserverTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | #if RUN_DEPRECATED_TESTS
26 |
27 | class TestObservable: NSObject {
28 | @objc dynamic var someProperty: String
29 | @objc dynamic var unrelatedProperty: NSNumber
30 | @objc dynamic weak var weakProperty: TestObservable?
31 | @objc dynamic var chainedObservable: TestObservable?
32 |
33 | init(value: String) {
34 | someProperty = value
35 | unrelatedProperty = false
36 | super.init()
37 | }
38 | }
39 |
40 | class KeyValueObserverTests: XCTestCase {
41 | func testBasicProperty() {
42 | var results: [(String?, KeyValueObserver.CallbackReason)] = Array()
43 | var kvo2Count = 0
44 | var testObservable1: TestObservable? = TestObservable(value: "empty")
45 | var kvo: KeyValueObserver?
46 | var kvo2: KeyValueObserver?
47 |
48 | if let to1 = testObservable1 {
49 | kvo = KeyValueObserver(source: to1, keyPath: #keyPath(TestObservable.someProperty), options: NSKeyValueObservingOptions.new) { (change: [NSKeyValueChangeKey: Any], reason: KeyValueObserver.CallbackReason) -> Void in
50 | results.append((change[NSKeyValueChangeKey.newKey] as? String, reason))
51 | }
52 | }
53 |
54 | if let to1 = testObservable1 {
55 | kvo2 = KeyValueObserver(source: to1, keyPath: #keyPath(TestObservable.someProperty), options: NSKeyValueObservingOptions()) { (change: [NSKeyValueChangeKey: Any], reason: KeyValueObserver.CallbackReason) -> Void in
56 | kvo2Count += 1
57 | }
58 | }
59 |
60 | // Test basic observer notifications
61 | testObservable1?.someProperty = "filled"
62 |
63 | // Test source deleted notification
64 | testObservable1 = nil
65 |
66 | XCTAssert(results[0].0 == "filled" && results[0].1 == .valueChanged)
67 | XCTAssert(results[1].0 == nil && results[1].1 == .sourceDeleted)
68 | XCTAssert(results.count == 2)
69 |
70 | XCTAssert(kvo2Count == 2)
71 |
72 | withExtendedLifetime(kvo) {}
73 | withExtendedLifetime(kvo2) {}
74 | }
75 |
76 | func testCancel() {
77 | var results: [(String?, KeyValueObserver.CallbackReason)] = Array()
78 | let testObservable = TestObservable(value: "empty")
79 | let kvo = KeyValueObserver(source: testObservable, keyPath: #keyPath(TestObservable.someProperty)) { (change: [NSKeyValueChangeKey: Any], reason: KeyValueObserver.CallbackReason) -> Void in
80 | results.append((change[NSKeyValueChangeKey.newKey] as? String, reason))
81 | }
82 |
83 | testObservable.someProperty = "one"
84 | kvo.cancel()
85 | testObservable.someProperty = "two"
86 |
87 | XCTAssert(results.count == 2)
88 | XCTAssert(results.at(0)?.0 == "empty" && results.at(0)?.1 == .valueChanged)
89 | XCTAssert(results.at(1)?.0 == "one" && results.at(1)?.1 == .valueChanged)
90 | }
91 |
92 | func testChainedProperty() {
93 | var results: [(String?, KeyValueObserver.CallbackReason)] = Array()
94 | var testObservable1: TestObservable? = TestObservable(value: "one")
95 | let testObservable2: TestObservable? = TestObservable(value: "two")
96 | let testObservable3: TestObservable? = TestObservable(value: "three")
97 | let testObservable4: TestObservable? = TestObservable(value: "four")
98 | var kvo: KeyValueObserver?
99 |
100 | if let to1 = testObservable1, let to2 = testObservable2, let to3 = testObservable3 {
101 | to2.chainedObservable = to3
102 | to1.chainedObservable = to2
103 | kvo = KeyValueObserver(source: to1, keyPath: "chainedObservable.chainedObservable.someProperty", options: NSKeyValueObservingOptions.new) { (change: [NSKeyValueChangeKey: Any], reason: KeyValueObserver.CallbackReason) -> Void in
104 | results.append((change[NSKeyValueChangeKey.newKey] as? String, reason))
105 | }
106 | }
107 |
108 | // Test key path observer notifications
109 | testObservable3?.someProperty = "filled"
110 | XCTAssert(results[0].0 == "filled" && results[0].1 == .valueChanged)
111 |
112 | // Test various combinations of path changes
113 | testObservable2?.chainedObservable = testObservable4
114 | XCTAssert(results[1].0 == "four" && results[1].1 == .pathChanged)
115 |
116 | testObservable4?.someProperty = "cleared"
117 | XCTAssert(results[2].0 == "cleared" && results[2].1 == .valueChanged)
118 |
119 | testObservable2?.chainedObservable = nil
120 | XCTAssert(results[3].0 == nil && results[3].1 == .pathChanged)
121 |
122 | testObservable2?.chainedObservable = testObservable3
123 | XCTAssert(results[4].0 == "filled" && results[4].1 == .pathChanged)
124 |
125 | testObservable2?.chainedObservable = nil
126 | XCTAssert(results[5].0 == nil && results[5].1 == .pathChanged)
127 |
128 | testObservable1 = nil
129 | XCTAssert(results[6].0 == nil && results[6].1 == .sourceDeleted)
130 | XCTAssert(results.count == 7)
131 |
132 | withExtendedLifetime(kvo) { () -> Void in }
133 | }
134 |
135 | func testWeakPropertyNotifications() {
136 | var results: [(String?, KeyValueObserver.CallbackReason)] = Array()
137 | let testObservable1: TestObservable? = TestObservable(value: "empty")
138 | var kvo: KeyValueObserver?
139 |
140 | if let to1 = testObservable1 {
141 | kvo = KeyValueObserver(source: to1, keyPath: "weakProperty", options: NSKeyValueObservingOptions.new) { (change: [NSKeyValueChangeKey: Any], reason: KeyValueObserver.CallbackReason) -> Void in
142 | results.append(((change[NSKeyValueChangeKey.newKey] as? TestObservable)?.someProperty, reason))
143 | }
144 | }
145 |
146 | autoreleasepool() {
147 | var to2: TestObservable? = TestObservable(value: "monkey")
148 | testObservable1?.weakProperty = to2
149 | XCTAssert(results[0].0 == "monkey" && results[0].1 == .valueChanged)
150 | to2 = nil
151 | }
152 |
153 | XCTAssert(results[1].0 == nil && results[1].1 == .valueChanged)
154 | XCTAssert(results.count == 2)
155 |
156 | withExtendedLifetime(kvo!) { () -> Void in }
157 | }
158 |
159 | func testChangeOptions() {
160 | var results: [[AnyHashable: Any]] = Array()
161 | let options: [NSKeyValueObservingOptions] = [NSKeyValueObservingOptions(), NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old, [NSKeyValueObservingOptions.initial, NSKeyValueObservingOptions.new], [NSKeyValueObservingOptions.prior, NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old]]
162 |
163 | for o in options {
164 | let testObservable1 = TestObservable(value: "empty")
165 | let testObservable2 = TestObservable(value: "empty")
166 | let testObservable3 = TestObservable(value: "empty")
167 | testObservable1.chainedObservable = testObservable2
168 | testObservable2.chainedObservable = testObservable3
169 | let kvo = KeyValueObserver(source: testObservable1, keyPath: "chainedObservable.chainedObservable.someProperty", options: o) { (change: [NSKeyValueChangeKey: Any], reason: KeyValueObserver.CallbackReason) -> Void in
170 | results.append(change)
171 | }
172 |
173 | // Test basic observer notifications
174 | testObservable3.someProperty = "filled"
175 |
176 | // Cancel to avoid TargetDeleted notifications
177 | kvo.cancel()
178 | }
179 |
180 | XCTAssert(results.count == 7)
181 |
182 | // allZeros
183 | XCTAssert(Set(results[0].keys) == [NSKeyValueChangeKey.kindKey])
184 |
185 | // New
186 | XCTAssert(Set(results[1].keys) == [NSKeyValueChangeKey.kindKey, NSKeyValueChangeKey.newKey])
187 |
188 | // Old
189 | XCTAssert(Set(results[2].keys) == [NSKeyValueChangeKey.kindKey, NSKeyValueChangeKey.oldKey])
190 |
191 | // Initial
192 | XCTAssert(Set(results[3].keys) == [NSKeyValueChangeKey.kindKey, NSKeyValueChangeKey.newKey])
193 |
194 | // Initial, Setting
195 | XCTAssert(Set(results[4].keys) == [NSKeyValueChangeKey.kindKey, NSKeyValueChangeKey.newKey])
196 |
197 | // Prior (prior)
198 | XCTAssert(Set(results[5].keys) == [NSKeyValueChangeKey.kindKey, NSKeyValueChangeKey.notificationIsPriorKey, NSKeyValueChangeKey.oldKey])
199 |
200 | // Prior (post)
201 | XCTAssert(Set(results[6].keys) == [NSKeyValueChangeKey.kindKey, NSKeyValueChangeKey.newKey, NSKeyValueChangeKey.oldKey])
202 | }
203 | }
204 |
205 | #endif
206 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlMutexTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlMutexTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class PthreadTests: XCTestCase {
26 | func testPthreadMutex() {
27 | let mutex1 = PThreadMutex()
28 |
29 | let e1 = expectation(description: "Block1 not invoked")
30 | mutex1.sync {
31 | e1.fulfill()
32 | let reenter: Void? = mutex1.trySync() {
33 | XCTFail()
34 | }
35 | XCTAssert(reenter == nil)
36 | }
37 |
38 | let mutex2 = PThreadMutex(type: .recursive)
39 |
40 | let e2 = expectation(description: "Block2 not invoked")
41 | let e3 = expectation(description: "Block3 not invoked")
42 | mutex2.sync {
43 | e2.fulfill()
44 | let reenter: Void? = mutex2.trySync() {
45 | e3.fulfill()
46 | }
47 | XCTAssert(reenter != nil)
48 | }
49 |
50 | let e4 = expectation(description: "Block4 not invoked")
51 | let r = mutex1.sync { () -> Int in
52 | e4.fulfill()
53 | let reenter: Void? = mutex1.trySync() {
54 | XCTFail()
55 | }
56 | XCTAssert(reenter == nil)
57 | return 13
58 | }
59 | XCTAssert(r == 13)
60 |
61 | waitForExpectations(timeout: 0, handler: nil)
62 | }
63 | }
64 |
65 | extension PThreadMutex {
66 | public func sync_generic_param(_ param: inout T, f: (inout T) throws -> R) rethrows -> R {
67 | pthread_mutex_lock(&underlyingMutex)
68 | defer { pthread_mutex_unlock(&underlyingMutex) }
69 | return try f(¶m)
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlOnDeleteTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlOnDeleteTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class OnDeleteTests: XCTestCase {
26 | func testOnDelete() {
27 | var x = false
28 | do {
29 | let o = OnDelete { x = true }
30 | XCTAssert(x == false)
31 | withExtendedLifetime(o) { () in }
32 | }
33 | XCTAssert(x == true)
34 |
35 | var y = false
36 | do {
37 | let o = OnDelete { y = true }
38 | XCTAssert(y == false)
39 |
40 | XCTAssert(o.isValid == true)
41 |
42 | o.invalidate()
43 |
44 | XCTAssert(o.isValid == false)
45 | }
46 | XCTAssert(y == false)
47 |
48 | var z = false
49 | do {
50 | let o = OnDelete { z = true }
51 | XCTAssert(z == false)
52 | o.cancel()
53 | }
54 | XCTAssert(z == true)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlRandomTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlRandomTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/05/17.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | #if SWIFT_PACKAGE
26 | import ReferenceRandomGenerators
27 | #endif
28 |
29 | let VerificationIterations = 1000
30 |
31 | class RandomTests: XCTestCase {
32 | func genericTest(generator: inout Generator, testUnique: Bool = true) {
33 | let a = generator.next()
34 | let b = generator.next()
35 | let c = generator.next()
36 | let d = generator.next()
37 |
38 | if testUnique {
39 | XCTAssert(a != b && a != c && a != d && b != c && b != d && c != d, "Technically, we *could* get a collision...")
40 | }
41 | }
42 |
43 | func testDevRandom() {
44 | var generator = DevRandom()
45 | genericTest(generator: &generator)
46 | }
47 |
48 | func testArc4Random() {
49 | var generator = SystemRandomNumberGenerator()
50 | genericTest(generator: &generator)
51 | }
52 |
53 | func testLfsr258() {
54 | var generator = Lfsr258()
55 | genericTest(generator: &generator)
56 | }
57 |
58 | func testLfsr176() {
59 | var generator = Lfsr176()
60 | genericTest(generator: &generator)
61 | }
62 |
63 | func testConstantNonRandom() {
64 | var generator = ConstantNonRandom()
65 | genericTest(generator: &generator, testUnique: false)
66 | }
67 |
68 | func testXoshiro() {
69 | var generator = Xoshiro()
70 | genericTest(generator: &generator)
71 |
72 | // Test Xoshiro against the reference implementation to verify output
73 | var g1 = Xoshiro(seed: (12345678, 87654321, 10293847, 29384756))
74 | var g2 = Xoshiro256starstar(seed: (12345678, 87654321, 10293847, 29384756))
75 | for _ in 0.. UInt64 {
117 | return state
118 | }
119 | }
120 |
121 | public struct Lfsr258: RandomNumberGenerator {
122 | public typealias StateType = (UInt64, UInt64, UInt64, UInt64, UInt64)
123 |
124 | static let k: (UInt64, UInt64, UInt64, UInt64, UInt64) = (1, 9, 12, 17, 23)
125 | static let q: (UInt64, UInt64, UInt64, UInt64, UInt64) = (1, 24, 3, 5, 3)
126 | static let s: (UInt64, UInt64, UInt64, UInt64, UInt64) = (10, 5, 29, 23, 8)
127 |
128 | var state: StateType = (0, 0, 0, 0, 0)
129 |
130 | public init() {
131 | var r = DevRandom()
132 | repeat {
133 | r.randomize(value: &state.0)
134 | } while state.0 < Lfsr258.k.0
135 | repeat {
136 | r.randomize(value: &state.1)
137 | } while state.1 < Lfsr258.k.1
138 | repeat {
139 | r.randomize(value: &state.2)
140 | } while state.2 < Lfsr258.k.2
141 | repeat {
142 | r.randomize(value: &state.3)
143 | } while state.3 < Lfsr258.k.3
144 | repeat {
145 | r.randomize(value: &state.4)
146 | } while state.4 < Lfsr258.k.4
147 | }
148 |
149 | public init(seed: StateType) {
150 | self.state = seed
151 | }
152 |
153 | public mutating func next() -> UInt64 {
154 | // Constants from "Tables of Maximally-Equidistributed Combined LFSR Generators" by Pierre L'Ecuyer:
155 | // http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme2.ps
156 | let l: UInt64 = 64
157 | let x0 = (((state.0 << Lfsr258.q.0) ^ state.0) >> (l - Lfsr258.k.0 - Lfsr258.s.0))
158 | state.0 = ((state.0 & (UInt64.max << Lfsr258.k.0)) << Lfsr258.s.0) | x0
159 | let x1 = (((state.1 << Lfsr258.q.1) ^ state.1) >> (l - Lfsr258.k.1 - Lfsr258.s.1))
160 | state.1 = ((state.1 & (UInt64.max << Lfsr258.k.1)) << Lfsr258.s.1) | x1
161 | let x2 = (((state.2 << Lfsr258.q.2) ^ state.2) >> (l - Lfsr258.k.2 - Lfsr258.s.2))
162 | state.2 = ((state.2 & (UInt64.max << Lfsr258.k.2)) << Lfsr258.s.2) | x2
163 | let x3 = (((state.3 << Lfsr258.q.3) ^ state.3) >> (l - Lfsr258.k.3 - Lfsr258.s.3))
164 | state.3 = ((state.3 & (UInt64.max << Lfsr258.k.3)) << Lfsr258.s.3) | x3
165 | let x4 = (((state.4 << Lfsr258.q.4) ^ state.4) >> (l - Lfsr258.k.4 - Lfsr258.s.4))
166 | state.4 = ((state.4 & (UInt64.max << Lfsr258.k.4)) << Lfsr258.s.4) | x4
167 | return (state.0 ^ state.1 ^ state.2 ^ state.3 ^ state.4)
168 | }
169 | }
170 |
171 | public struct Lfsr176: RandomNumberGenerator {
172 | public typealias StateType = (UInt64, UInt64, UInt64)
173 |
174 | static let k: (UInt64, UInt64, UInt64) = (1, 6, 9)
175 | static let q: (UInt64, UInt64, UInt64) = (5, 19, 24)
176 | static let s: (UInt64, UInt64, UInt64) = (24, 13, 17)
177 |
178 | var state: StateType = (0, 0, 0)
179 |
180 | public init() {
181 | var r = DevRandom()
182 | repeat {
183 | r.randomize(value: &state.0)
184 | } while state.0 < Lfsr176.k.0
185 | repeat {
186 | r.randomize(value: &state.1)
187 | } while state.1 < Lfsr176.k.1
188 | repeat {
189 | r.randomize(value: &state.2)
190 | } while state.2 < Lfsr176.k.2
191 | }
192 |
193 | public init(seed: StateType) {
194 | self.state = seed
195 | }
196 |
197 | public mutating func next() -> UInt64 {
198 | // Constants from "Tables of Maximally-Equidistributed Combined LFSR Generators" by Pierre L'Ecuyer:
199 | // http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme2.ps
200 | let l: UInt64 = 64
201 | let x0 = (((state.0 << Lfsr176.q.0) ^ state.0) >> (l - Lfsr176.k.0 - Lfsr176.s.0))
202 | state.0 = ((state.0 & (UInt64.max << Lfsr176.k.0)) << Lfsr176.s.0) | x0
203 | let x1 = (((state.1 << Lfsr176.q.1) ^ state.1) >> (l - Lfsr176.k.1 - Lfsr176.s.1))
204 | state.1 = ((state.1 & (UInt64.max << Lfsr176.k.1)) << Lfsr176.s.1) | x1
205 | let x2 = (((state.2 << Lfsr176.q.2) ^ state.2) >> (l - Lfsr176.k.2 - Lfsr176.s.2))
206 | state.2 = ((state.2 & (UInt64.max << Lfsr176.k.2)) << Lfsr176.s.2) | x2
207 | return (state.0 ^ state.1 ^ state.2)
208 | }
209 | }
210 |
211 |
212 | //
213 | // NOTE: the following two implementations are duplicated in CwlRandomPerformanceTests.swift and in CwlRandomTests.swift file.
214 | // The reason for this duplication is that CwlRandomPerformanceTests.swift is normally excluded from the build, so CwlRandomTests.swift can't rely on it. But CwlRandomPerformanceTests.swift needs these structs included locally because they need to be inlined (to make the performance comparison fair) and whole module optimization is disabled for this testing bundle (to allow testing across module boundaries where desired).
215 | //
216 |
217 | private struct MT19937_64: RandomNumberGenerator {
218 | typealias WordType = UInt64
219 | var state = mt19937_64()
220 |
221 | init() {
222 | var dr = DevRandom()
223 | init_genrand64(&state, dr.next())
224 | }
225 |
226 | init(seed: UInt64) {
227 | init_genrand64(&state, seed)
228 | }
229 |
230 | mutating func next() -> UInt64 {
231 | return genrand64_int64(&state)
232 | }
233 | }
234 |
235 | private struct Xoshiro256starstar: RandomNumberGenerator {
236 | var state = { () -> xoshiro_state in var dr = DevRandom(); return xoshiro_state(s: (dr.next(), dr.next(), dr.next(), dr.next())) }()
237 |
238 | init() {
239 | }
240 |
241 | init(seed: (UInt64, UInt64, UInt64, UInt64)) {
242 | self.state.s = seed
243 | }
244 |
245 | mutating func next() -> UInt64 {
246 | return xoshiro_next(&state)
247 | }
248 | }
249 |
250 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlStackFrameTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlStackFrameTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/26.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class StackFrameTests: XCTestCase {
26 | func testCallStackReturnAddresses() {
27 | var a = callStackReturnAddresses()
28 | a.remove(at: 0)
29 | var b = Thread.callStackReturnAddresses.map { $0.uintValue }
30 | b.remove(at: 0)
31 | XCTAssert(a == b)
32 |
33 | a = callStackReturnAddresses(skip: 2)
34 | b.remove(at: 0)
35 | XCTAssert(a == b)
36 |
37 | a = callStackReturnAddresses(skip: 2, maximumAddresses: 10)
38 | XCTAssert(a == Array(b[0..<10]))
39 | }
40 |
41 | func testNSThreadCallStackReturnAddressesPerformance() {
42 | measure {
43 | var traces = [[NSNumber]]()
44 | for _ in 0..<1000 {
45 | traces.append(Thread.callStackReturnAddresses)
46 | }
47 | }
48 | }
49 |
50 | func testCallStackReturnAddressesPerformance() {
51 | measure {
52 | var traces = [[UInt]]()
53 | for _ in 0..<1000 {
54 | traces.append(callStackReturnAddresses())
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlSysctlTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlSysctlTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/03.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class SysctlTests: XCTestCase {
26 | func testSysctl() {
27 | let hostName = Sysctl.hostName
28 | XCTAssert(hostName != "")
29 |
30 | let machine = Sysctl.machine
31 |
32 | #if arch(x86_64)
33 | XCTAssert(machine == "x86_64")
34 | #else
35 | XCTAssert(machine != "")
36 | #endif
37 |
38 | let model = Sysctl.model
39 | XCTAssert(model != "")
40 |
41 | let osRelease = Sysctl.osRelease
42 | XCTAssert(osRelease != "")
43 |
44 | let osType = Sysctl.osType
45 | XCTAssert(osType == "Darwin")
46 |
47 | let osVersion = Sysctl.osVersion
48 | XCTAssert(osVersion != "")
49 |
50 | let version = Sysctl.version
51 | XCTAssert(version.hasPrefix("Darwin Kernel Version"))
52 |
53 | let activeCPUs = Sysctl.activeCPUs
54 | XCTAssert(activeCPUs > 0)
55 |
56 | #if os(macOS)
57 | let osRev = Sysctl.osRev
58 | XCTAssert(osRev != 0)
59 |
60 | let cpuFreq = Sysctl.cpuFreq
61 | XCTAssert(cpuFreq > 1_000_000_000)
62 |
63 | let memSize = Sysctl.memSize
64 | XCTAssert(memSize > 1_000_000_000)
65 | #endif
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlUnanticipatedErrorTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlUnanticipatedErrorTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/03/05.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | enum TestCode: Int, Error {
26 | case zeroValue = 0
27 | case oneValue = 1
28 | case testValue = 2
29 | }
30 |
31 | #if os(iOS) || os(macOS)
32 |
33 | class UnanticipatedErrorTests: XCTestCase {
34 | func testUnanticipatedError() {
35 | let e = TestCode.testValue.withUnanticipatedErrorRecoveryAttempter()
36 |
37 | // NOTE: error codes equal raw values only if the raw values are zero based with no gaps
38 | XCTAssert(e.code == TestCode.testValue.rawValue)
39 |
40 | let userInfo = e.userInfo
41 | if let callStackSymbols = userInfo[UnanticipatedErrorRecoveryAttempter.callStackSymbols] as? [String] {
42 | XCTAssert(callStackSymbols.count > 1, "No call stack symbols present")
43 | } else {
44 | XCTFail("Call stack symbols not present")
45 | }
46 |
47 | XCTAssertNotNil(userInfo[NSLocalizedRecoverySuggestionErrorKey])
48 | XCTAssertNotNil(userInfo[NSLocalizedRecoveryOptionsErrorKey])
49 | XCTAssertNotNil(userInfo[NSRecoveryAttempterErrorKey])
50 |
51 | XCTAssert((userInfo[NSLocalizedRecoveryOptionsErrorKey] as? [Any])?.count == 2, "Wrong number of options")
52 |
53 | let attempter = userInfo[NSRecoveryAttempterErrorKey] as? NSObject
54 | let backup = pasteboardBackup()
55 | attempter?.attemptRecovery(fromError: e as NSError, optionIndex: 1)
56 | let clipboardString = pasteboardString()
57 |
58 | // The following (ugly) compile-time conditional is a best effort at testing for the simulator (no actual simulator macro is provided)
59 | XCTAssert(clipboardString?.range(of: e.localizedRecoverySuggestion!) != nil, "Simulator pasteboard will fail prior to iOS 10/Xcode 8")
60 |
61 | restorePasteboard(backup)
62 | }
63 | }
64 |
65 | #endif
66 |
67 | #if os(iOS)
68 |
69 | func pasteboardBackup() -> [Dictionary] {
70 | return ((UIPasteboard.general.items as NSArray).copy() as? [Dictionary]) ?? Array>()
71 | }
72 |
73 | func restorePasteboard(_ items: [Dictionary]) {
74 | UIPasteboard.general.items = items
75 | }
76 |
77 | func pasteboardString() -> String? {
78 | return UIPasteboard.general.string
79 | }
80 |
81 | #elseif os(macOS)
82 |
83 | func pasteboardBackup() -> [NSPasteboardItem] {
84 | #if swift(>=4)
85 | let general = NSPasteboard.general
86 | #else
87 | let general = NSPasteboard.general()
88 | #endif
89 | return general.pasteboardItems?.map { item in
90 | let backupItem = NSPasteboardItem()
91 | for type in item.types {
92 | if let data = (item.data(forType: type) as NSData?)?.copy() as? Data {
93 | backupItem.setData(data, forType: type)
94 | }
95 | }
96 | return backupItem
97 | } ?? []
98 | }
99 |
100 | func restorePasteboard(_ items: [NSPasteboardItem]) {
101 | #if swift(>=4)
102 | let general = NSPasteboard.general
103 | #else
104 | let general = NSPasteboard.general()
105 | #endif
106 | general.clearContents()
107 | general.writeObjects(items)
108 | }
109 |
110 | func pasteboardString() -> String? {
111 | #if swift(>=4)
112 | return NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.string)
113 | #else
114 | return NSPasteboard.general().string(forType: NSPasteboardTypeString)
115 | #endif
116 | }
117 |
118 | #endif
119 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlUtilsTests-BridgingHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // CwlUtilsBridgingHeader.h
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #ifndef CwlUtilsTests_BridgingHeader_h
21 | #define CwlUtilsTests_BridgingHeader_h
22 |
23 | #import
24 |
25 | #import "ReferenceRandomGenerators.h"
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/CwlWrapperTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlWrapperTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class WrapperTests: XCTestCase {
26 | func testBox() {
27 | let b = Box(5)
28 | XCTAssert(b.value == 5)
29 | }
30 |
31 | func testAtomicBox() {
32 | let a = AtomicBox(5)
33 | let b = a
34 | a.mutate { $0 = 3 }
35 | XCTAssert(a.value == 3 && b.value == 3)
36 | }
37 |
38 | func testLazy() {
39 | var x = Lazy(valueConstructor: { 3 })
40 | XCTAssert(!x.isInitialized)
41 | XCTAssert(x.value() == 3)
42 | XCTAssert(x.isInitialized)
43 | }
44 |
45 | func testWeak() {
46 | let q = NSObject()
47 | let w = { () -> Weak in
48 | let o = NSObject()
49 | let p = NSObject()
50 | let innerW = Weak(o)
51 | XCTAssert(innerW.value != nil)
52 | XCTAssert(innerW.contains(o))
53 | XCTAssert(!innerW.contains(p))
54 | return innerW
55 | }()
56 | XCTAssert(w.value == nil)
57 | XCTAssert(!w.contains(q))
58 | }
59 |
60 | func testUnownedWrapper() {
61 | var uow: Unowned? = nil
62 | let w = { () -> Weak in
63 | let o = NSObject()
64 | let innerW = Weak(o)
65 | uow = Unowned(o)
66 | XCTAssert(innerW.value != nil)
67 | XCTAssert(uow != nil)
68 | XCTAssert(uow!.value == o)
69 | return innerW
70 | }()
71 | XCTAssert(w.value == nil)
72 | XCTAssert(uow != nil)
73 | }
74 |
75 | func testPossiblyWeak() {
76 | let q = NSObject()
77 | let w = { () -> PossiblyWeak in
78 | let o = NSObject()
79 | let p = NSObject()
80 | let innerW = PossiblyWeak.strong(o)
81 | XCTAssert(innerW.value != nil)
82 | XCTAssert(innerW.contains(o))
83 | XCTAssert(!innerW.contains(p))
84 | return innerW
85 | }()
86 | XCTAssert(w.value != nil)
87 | XCTAssert(!w.contains(q))
88 |
89 | let x = { () -> PossiblyWeak in
90 | let o = NSObject()
91 | let p = NSObject()
92 | let innerW = PossiblyWeak.weak(Weak(o))
93 | XCTAssert(innerW.value != nil)
94 | XCTAssert(innerW.contains(o))
95 | XCTAssert(!innerW.contains(p))
96 | return innerW
97 | }()
98 | XCTAssert(x.value == nil)
99 | XCTAssert(!x.contains(q))
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Tests/CwlUtilsTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFBundleDevelopmentRegion
5 | en
6 | CFBundleExecutable
7 | $(EXECUTABLE_NAME)
8 | CFBundleIdentifier
9 | $(PRODUCT_BUNDLE_IDENTIFIER)
10 | CFBundleInfoDictionaryVersion
11 | 6.0
12 | CFBundleName
13 | $(PRODUCT_NAME)
14 | CFBundlePackageType
15 | BNDL
16 | CFBundleShortVersionString
17 | 1.0
18 | CFBundleSignature
19 | ????
20 | CFBundleVersion
21 | $(CURRENT_PROJECT_VERSION)
22 | NSPrincipalClass
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_iOSTestApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CwlUtils_iOSTestApp
4 | //
5 | // Created by Matt Gallagher on 2016/18/04.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 | //
21 |
22 | import UIKit
23 |
24 | @UIApplicationMain
25 | class AppDelegate: UIResponder, UIApplicationDelegate {
26 | var window: UIWindow?
27 |
28 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
29 | // Override point for customization after application launch.
30 | return true
31 | }
32 |
33 | func applicationWillResignActive(_ application: UIApplication) {
34 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
35 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
36 | }
37 |
38 | func applicationDidEnterBackground(_ application: UIApplication) {
39 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
41 | }
42 |
43 | func applicationWillEnterForeground(_ application: UIApplication) {
44 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
45 | }
46 |
47 | func applicationDidBecomeActive(_ application: UIApplication) {
48 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
49 | }
50 |
51 | func applicationWillTerminate(_ application: UIApplication) {
52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
53 | }
54 |
55 |
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_iOSTestApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Tests/CwlUtils_iOSTestApp/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_iOSTestApp/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
33 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_iOSTestApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_iOSTestApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CwlUtils_iOSTestApp
4 | //
5 | // Created by Matt Gallagher on 2016/18/04.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import UIKit
22 | import CwlUtils
23 |
24 | func showAlert(error: NSError) {
25 | let alert = UIAlertController(title: error.localizedDescription, message: error.localizedFailureReason, preferredStyle: UIAlertController.Style.alert)
26 | alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: UIAlertAction.Style.default, handler: nil))
27 | UIApplication.shared.windows[0].rootViewController!.present(alert, animated: true, completion: nil)
28 | }
29 |
30 | class ViewController: UIViewController {
31 | override func viewDidLoad() {
32 | super.viewDidLoad()
33 | // Do any additional setup after loading the view, typically from a nib.
34 | }
35 |
36 | override func didReceiveMemoryWarning() {
37 | super.didReceiveMemoryWarning()
38 | // Dispose of any resources that can be recreated.
39 | }
40 |
41 | func process(data: NSData) {
42 | }
43 |
44 | func someProcessingTask1(path: String) {
45 | do {
46 | let data = try NSData(contentsOfFile: path, options: .mappedIfSafe)
47 | process(data: data)
48 | } catch {
49 | showAlert(error: error as NSError)
50 | }
51 | }
52 |
53 | @IBAction func someUserAction1(_ sender: AnyObject) {
54 | someProcessingTask1(path: "/invalid/path")
55 | }
56 |
57 | func someProcessingTask2(path: String) throws {
58 | try rethrowUnanticipated {
59 | let data = try NSData(contentsOfFile: path, options: .mappedIfSafe)
60 | process(data: data)
61 | }
62 | }
63 |
64 | @IBAction func someUserAction2(_ sender: AnyObject) {
65 | do {
66 | try someProcessingTask2(path: "/invalid/path")
67 | } catch {
68 | presentError(error as NSError)
69 | }
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_iOSTestAppUITests/CwlUtils_iOSTestAppUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlUtils_iOSTestAppUITests.swift
3 | // CwlUtils_iOSTestAppUITests
4 | //
5 | // Created by Matt Gallagher on 2016/18/04.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import XCTest
22 |
23 | class CwlUtils_iOSTestAppUITests: XCTestCase {
24 | override func setUp() {
25 | super.setUp()
26 |
27 | // Put setup code here. This method is called before the invocation of each test method in the class.
28 |
29 | // In UI tests it is usually best to stop immediately when a failure occurs.
30 | continueAfterFailure = false
31 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
32 | XCUIApplication().launch()
33 |
34 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
35 | }
36 |
37 | override func tearDown() {
38 | // Put teardown code here. This method is called after the invocation of each test method in the class.
39 | super.tearDown()
40 | }
41 |
42 | func testAlerts() {
43 | let app = XCUIApplication()
44 | app.buttons["Trigger site handled error"].tap()
45 |
46 | let theFilePathCouldnTBeOpenedBecauseThereIsNoSuchFileAlert = app.alerts["The file “path” couldn’t be opened because there is no such file."]
47 | let okButton = theFilePathCouldnTBeOpenedBecauseThereIsNoSuchFileAlert.buttons["OK"]
48 | okButton.tap()
49 |
50 | let triggerUnanticipatedErrorButton = app.buttons["Trigger unanticipated error"]
51 | triggerUnanticipatedErrorButton.tap()
52 | okButton.tap()
53 |
54 | triggerUnanticipatedErrorButton.tap()
55 | let copyDetailsButton = theFilePathCouldnTBeOpenedBecauseThereIsNoSuchFileAlert.buttons["Copy details"]
56 | copyDetailsButton.tap()
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_macOSTestApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CwlUtils_macOSTestApp
4 | //
5 | // Created by Matt Gallagher on 2017/01/31.
6 | // Copyright © 2017 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Cocoa
22 |
23 | @NSApplicationMain
24 | class AppDelegate: NSObject, NSApplicationDelegate {
25 |
26 |
27 |
28 | func applicationDidFinishLaunching(_ aNotification: Notification) {
29 | // Insert code here to initialize your application
30 | }
31 |
32 | func applicationWillTerminate(_ aNotification: Notification) {
33 | // Insert code here to tear down your application
34 | }
35 |
36 |
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_macOSTestApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Tests/CwlUtils_macOSTestApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSMainStoryboardFile
26 | Main
27 | NSPrincipalClass
28 | NSApplication
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_macOSTestApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CwlUtils_macOSTestApp
4 | //
5 | // Created by Matt Gallagher on 2017/01/31.
6 | // Copyright © 2017 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Cocoa
22 | import CwlUtils
23 |
24 | func showAlert(error: Error) {
25 | NSAlert(error: error).runModal()
26 | }
27 |
28 | class ViewController: NSViewController {
29 |
30 | let b = NSButton()
31 |
32 | override func viewDidLoad() {
33 | super.viewDidLoad()
34 |
35 | // Do any additional setup after loading the view.
36 | }
37 |
38 | override var representedObject: Any? {
39 | didSet {
40 | // Update the view, if already loaded.
41 | }
42 | }
43 |
44 | func process(data: NSData) {
45 | }
46 |
47 | func someProcessingTask1(path: String) {
48 | do {
49 | let data = try NSData(contentsOfFile: path, options: .mappedIfSafe)
50 | process(data: data)
51 | } catch {
52 | showAlert(error: error)
53 | }
54 | }
55 |
56 | @IBAction func someUserAction1(_ sender: AnyObject) {
57 | someProcessingTask1(path: "/invalid/path")
58 | }
59 |
60 | func someProcessingTask2(path: String) throws {
61 | try rethrowUnanticipated {
62 | let data = try NSData(contentsOfFile: path, options: .mappedIfSafe)
63 | process(data: data)
64 | }
65 | }
66 |
67 | @IBAction func someUserAction2(_ sender: AnyObject) {
68 | do {
69 | try someProcessingTask2(path: "/invalid/path")
70 | } catch {
71 | presentError(error)
72 | }
73 | }
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/Tests/CwlUtils_macOSTestAppUITests/CwlUtils_macOSTestAppUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlUtils_macOSTestAppUITests.swift
3 | // CwlUtils_macOSTestAppUITests
4 | //
5 | // Created by Matt Gallagher on 2016/18/04.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import XCTest
22 |
23 | class CwlUtils_macOSTestAppUITests: XCTestCase {
24 | override func setUp() {
25 | super.setUp()
26 |
27 | // Put setup code here. This method is called before the invocation of each test method in the class.
28 |
29 | // In UI tests it is usually best to stop immediately when a failure occurs.
30 | continueAfterFailure = false
31 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
32 | XCUIApplication().launch()
33 |
34 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
35 | }
36 |
37 | override func tearDown() {
38 | // Put teardown code here. This method is called after the invocation of each test method in the class.
39 | super.tearDown()
40 | }
41 |
42 | func testAlerts() {
43 | let app = XCUIApplication()
44 | let cwlutilsOsxharnessWindow = app.windows["Window"]
45 | let alertContents = app.dialogs["alert"]
46 |
47 | cwlutilsOsxharnessWindow.buttons["Trigger site handled error"].click()
48 | alertContents.buttons["OK"].click()
49 |
50 | let unanticipatedButton = cwlutilsOsxharnessWindow.buttons["Trigger unanticipated error"]
51 |
52 | unanticipatedButton.click()
53 | alertContents.buttons["OK"].click()
54 |
55 | unanticipatedButton.click()
56 | alertContents.buttons["Copy details"].click()
57 | }
58 | }
59 |
--------------------------------------------------------------------------------