diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bb460e7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+.DS_Store
+/.build
+/Packages
+/*.xcodeproj
+xcuserdata/
+DerivedData/
+.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/Beton.iml b/.idea/Beton.iml
new file mode 100644
index 0000000..97d3aaa
--- /dev/null
+++ b/.idea/Beton.iml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..edc9591
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/markdown.xml b/.idea/markdown.xml
new file mode 100644
index 0000000..ec0b30f
--- /dev/null
+++ b/.idea/markdown.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..7026b53
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..ce42929
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fdfc178
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 21Gram Consulting
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..58b39a7
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,51 @@
+// swift-tools-version:5.5
+
+import PackageDescription
+
+let package = Package(
+ name: "Beton",
+ defaultLocalization: LanguageTag("en_US"),
+ platforms: [
+ .macOS(.v12)
+ ],
+ products: [
+ .library(name: "Beton", targets: ["Beton"]),
+ ],
+ targets: [
+ .target(name: "Beton"),
+ .target(
+ name: "XCTBeton",
+ dependencies: [
+ .byName(name: "Beton")
+ ]
+ ),
+ .testTarget(type: .unit),
+ .testTarget(type: .component),
+ .testTarget(type: .integration),
+ .testTarget(type: .system),
+ .testTarget(type: .performance),
+ ]
+)
+
+/// MARK: Convenience units
+
+extension PackageDescription.Target {
+ enum TestType: String {
+ case unit = "Unit"
+ case component = "Component"
+ case integration = "Integration"
+ case system = "System"
+ case performance = "Performance"
+ }
+
+ static func testTarget(type: TestType) -> PackageDescription.Target {
+ .testTarget(
+ name: "\(type.rawValue)Tests",
+ dependencies: [
+ .byName(name: "Beton"),
+ .byName(name: "XCTBeton")
+ ],
+ resources: [.process("Resources")]
+ )
+ }
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cea07b4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+# Beton
+
+A description of this package.
+
+```bash
+
+docc convert --transform-for-static-hosting \
+--additional-symbol-graph-dir .build
+
+```
\ No newline at end of file
diff --git a/Sources/Beton/Beton.swift b/Sources/Beton/Beton.swift
new file mode 100644
index 0000000..ff2b4e2
--- /dev/null
+++ b/Sources/Beton/Beton.swift
@@ -0,0 +1 @@
+@_exported import Foundation
diff --git a/Sources/Beton/Bundle/Bundle+localizationBundles.swift b/Sources/Beton/Bundle/Bundle+localizationBundles.swift
new file mode 100644
index 0000000..38e564b
--- /dev/null
+++ b/Sources/Beton/Bundle/Bundle+localizationBundles.swift
@@ -0,0 +1,17 @@
+import Foundation
+
+public extension Bundle {
+ var localizationBundles: [Locale: Bundle] {
+ localizations
+ .compactMap { [self] in
+ guard let path = path(forResource: $0, ofType: "lproj") else { return nil }
+ guard let bundle = Bundle(path: path) else { return nil }
+ return (key: Locale(identifier: $0), value: bundle)
+ }
+ .reduce([:]) { (bundles: [Locale: Bundle], entry: (key: Locale, value: Bundle)) in
+ var bundles = bundles
+ bundles[entry.key] = entry.value
+ return bundles
+ }
+ }
+}
diff --git a/Sources/Beton/Bundle/Bundle+localizedString.swift b/Sources/Beton/Bundle/Bundle+localizedString.swift
new file mode 100644
index 0000000..2aa1d34
--- /dev/null
+++ b/Sources/Beton/Bundle/Bundle+localizedString.swift
@@ -0,0 +1,15 @@
+import Foundation
+
+public extension Bundle {
+ func localizedString(_ key: String) -> String {
+ self.localizedString(forKey: key, value: nil, table: nil)
+ }
+
+ func localizedString(_ key: String, from table: String) -> String {
+ self.localizedString(forKey: key, value: nil, table: table)
+ }
+
+ func localizedString(_ key: String, fallback: String) -> String {
+ self.localizedString(forKey: key, value: fallback, table: nil)
+ }
+}
\ No newline at end of file
diff --git a/Sources/Beton/Math/pow.swift b/Sources/Beton/Math/pow.swift
new file mode 100644
index 0000000..b1632c9
--- /dev/null
+++ b/Sources/Beton/Math/pow.swift
@@ -0,0 +1,8 @@
+import Foundation
+
+public func pow(_ x: Measurement, _ y: Double) -> Measurement {
+ Measurement(
+ value: pow(x.value, y),
+ unit: x.unit
+ )
+}
\ No newline at end of file
diff --git a/Sources/Beton/Math/sqrt.swift b/Sources/Beton/Math/sqrt.swift
new file mode 100644
index 0000000..6003c97
--- /dev/null
+++ b/Sources/Beton/Math/sqrt.swift
@@ -0,0 +1,8 @@
+import Foundation
+
+public func sqrt(_ x: Measurement) -> Measurement {
+ Measurement(
+ value: sqrt(x.value),
+ unit: x.unit
+ )
+}
\ No newline at end of file
diff --git a/Sources/Beton/Measurement/Measurement+AdditiveArithmetic.swift b/Sources/Beton/Measurement/Measurement+AdditiveArithmetic.swift
new file mode 100644
index 0000000..cccf148
--- /dev/null
+++ b/Sources/Beton/Measurement/Measurement+AdditiveArithmetic.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension Measurement: AdditiveArithmetic where UnitType: Unit, UnitType.U == UnitType {
+ public static var zero: Self { Self.init(value: 0.0, unit: .default) }
+}
diff --git a/Sources/Beton/Measurement/Unit/Unit.swift b/Sources/Beton/Measurement/Unit/Unit.swift
new file mode 100644
index 0000000..89e4efd
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/Unit.swift
@@ -0,0 +1,6 @@
+import Foundation
+
+public protocol Unit: Foundation.Unit {
+ associatedtype U = Self
+ static var `default`: U { get }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitAcceleration+default.swift b/Sources/Beton/Measurement/Unit/UnitAcceleration+default.swift
new file mode 100644
index 0000000..c14f447
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitAcceleration+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitAcceleration: Unit {
+ public static var `default`: UnitAcceleration { .metersPerSecondSquared }
+}
\ No newline at end of file
diff --git a/Sources/Beton/Measurement/Unit/UnitAngle+default.swift b/Sources/Beton/Measurement/Unit/UnitAngle+default.swift
new file mode 100644
index 0000000..6d09204
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitAngle+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitAngle: Unit {
+ public static var `default`: UnitAngle { .degrees }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitArea+default.swift b/Sources/Beton/Measurement/Unit/UnitArea+default.swift
new file mode 100644
index 0000000..6fb3972
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitArea+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitArea: Unit {
+ public static var `default`: UnitArea { .squareMeters }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitConcentrationMass+default.swift b/Sources/Beton/Measurement/Unit/UnitConcentrationMass+default.swift
new file mode 100644
index 0000000..26d3a77
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitConcentrationMass+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitConcentrationMass: Unit {
+ public static var `default`: UnitConcentrationMass { .gramsPerLiter }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitDispersion+default.swift b/Sources/Beton/Measurement/Unit/UnitDispersion+default.swift
new file mode 100644
index 0000000..e8132e7
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitDispersion+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitDispersion: Unit {
+ public static var `default`: UnitDispersion { .partsPerMillion }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitDuration+default.swift b/Sources/Beton/Measurement/Unit/UnitDuration+default.swift
new file mode 100644
index 0000000..4519711
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitDuration+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitDuration: Unit {
+ public static var `default`: UnitDuration { .seconds }
+}
\ No newline at end of file
diff --git a/Sources/Beton/Measurement/Unit/UnitElectricCharge+default.swift b/Sources/Beton/Measurement/Unit/UnitElectricCharge+default.swift
new file mode 100644
index 0000000..b1d16e7
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitElectricCharge+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitElectricCharge: Unit {
+ public static var `default`: UnitElectricCharge { .ampereHours }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitElectricCurrent+default.swift b/Sources/Beton/Measurement/Unit/UnitElectricCurrent+default.swift
new file mode 100644
index 0000000..eaf852a
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitElectricCurrent+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitElectricCurrent: Unit {
+ public static var `default`: UnitElectricCurrent { .amperes }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitElectricPotentialDifference+default.swift b/Sources/Beton/Measurement/Unit/UnitElectricPotentialDifference+default.swift
new file mode 100644
index 0000000..af5d0bc
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitElectricPotentialDifference+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitElectricPotentialDifference: Unit {
+ public static var `default`: UnitElectricPotentialDifference { .volts }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitElectricResistance+default.swift b/Sources/Beton/Measurement/Unit/UnitElectricResistance+default.swift
new file mode 100644
index 0000000..f9dbe71
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitElectricResistance+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitElectricResistance: Unit {
+ public static var `default`: UnitElectricResistance { .ohms }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitEnergy+default.swift b/Sources/Beton/Measurement/Unit/UnitEnergy+default.swift
new file mode 100644
index 0000000..e8ff819
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitEnergy+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitEnergy: Unit {
+ public static var `default`: UnitEnergy { .joules }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitFrequency+default.swift b/Sources/Beton/Measurement/Unit/UnitFrequency+default.swift
new file mode 100644
index 0000000..ffd2df5
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitFrequency+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitFrequency: Unit {
+ public static var `default`: UnitFrequency { .hertz }
+}
\ No newline at end of file
diff --git a/Sources/Beton/Measurement/Unit/UnitFuelEfficiency+default.swift b/Sources/Beton/Measurement/Unit/UnitFuelEfficiency+default.swift
new file mode 100644
index 0000000..d10f464
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitFuelEfficiency+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitFuelEfficiency: Unit {
+ public static var `default`: UnitFuelEfficiency { .litersPer100Kilometers }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitIlluminance+default.swift b/Sources/Beton/Measurement/Unit/UnitIlluminance+default.swift
new file mode 100644
index 0000000..0325432
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitIlluminance+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitIlluminance: Unit {
+ public static var `default`: UnitIlluminance { .lux }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitInformationStorage+default.swift b/Sources/Beton/Measurement/Unit/UnitInformationStorage+default.swift
new file mode 100644
index 0000000..102ac59
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitInformationStorage+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitInformationStorage: Unit {
+ public static var `default`: UnitInformationStorage { .bits }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitLength+default.swift b/Sources/Beton/Measurement/Unit/UnitLength+default.swift
new file mode 100644
index 0000000..96a8831
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitLength+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitLength: Unit {
+ public static var `default`: UnitLength { .meters }
+}
\ No newline at end of file
diff --git a/Sources/Beton/Measurement/Unit/UnitMass+default.swift b/Sources/Beton/Measurement/Unit/UnitMass+default.swift
new file mode 100644
index 0000000..02ed877
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitMass+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitMass: Unit {
+ public static var `default`: UnitMass { .grams }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitPower+default.swift b/Sources/Beton/Measurement/Unit/UnitPower+default.swift
new file mode 100644
index 0000000..cc9d8a6
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitPower+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitPower: Unit {
+ public static var `default`: UnitPower { .watts }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitPressure+default.swift b/Sources/Beton/Measurement/Unit/UnitPressure+default.swift
new file mode 100644
index 0000000..35a1e3f
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitPressure+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitPressure: Unit {
+ public static var `default`: UnitPressure { .bars }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitSpeed+default.swift b/Sources/Beton/Measurement/Unit/UnitSpeed+default.swift
new file mode 100644
index 0000000..e3fcdde
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitSpeed+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitSpeed: Unit {
+ public static var `default`: UnitSpeed { .kilometersPerHour }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitTemperature+default.swift b/Sources/Beton/Measurement/Unit/UnitTemperature+default.swift
new file mode 100644
index 0000000..7421bd5
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitTemperature+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitTemperature: Unit {
+ public static var `default`: UnitTemperature { .celsius }
+}
diff --git a/Sources/Beton/Measurement/Unit/UnitVolume+default.swift b/Sources/Beton/Measurement/Unit/UnitVolume+default.swift
new file mode 100644
index 0000000..7b046b1
--- /dev/null
+++ b/Sources/Beton/Measurement/Unit/UnitVolume+default.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension UnitVolume: Unit {
+ public static var `default`: UnitVolume { .liters }
+}
diff --git a/Sources/Beton/Sequence/Sequence+sum.swift b/Sources/Beton/Sequence/Sequence+sum.swift
new file mode 100644
index 0000000..fc3444a
--- /dev/null
+++ b/Sources/Beton/Sequence/Sequence+sum.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+public extension Sequence where Self.Element: AdditiveArithmetic {
+ func sum() -> Element { reduce(Element.zero, +) }
+}
diff --git a/Sources/XCTBeton/Performance/XCTMeasureOptions.swift b/Sources/XCTBeton/Performance/XCTMeasureOptions.swift
new file mode 100644
index 0000000..3c3816f
--- /dev/null
+++ b/Sources/XCTBeton/Performance/XCTMeasureOptions.swift
@@ -0,0 +1,4 @@
+import Beton
+import class XCTest.XCTMeasureOptions
+
+open class XCTMeasureOptions: XCTest.XCTMeasureOptions {}
diff --git a/Sources/XCTBeton/Performance/XCTMetric/XCTCPUMetric.swift b/Sources/XCTBeton/Performance/XCTMetric/XCTCPUMetric.swift
new file mode 100644
index 0000000..3069f02
--- /dev/null
+++ b/Sources/XCTBeton/Performance/XCTMetric/XCTCPUMetric.swift
@@ -0,0 +1,15 @@
+import Beton
+import class XCTest.XCTCPUMetric
+
+public class XCTCPUMetric: XCTest.XCTCPUMetric, XCTMetric {
+ deinit { forgetSelf() }
+
+ public override func reportMeasurements(from startTime: XCTPerformanceMeasurementTimestamp, to endTime: XCTPerformanceMeasurementTimestamp) throws -> [XCTPerformanceMeasurement] {
+ storing(measurements: try super.reportMeasurements(from: startTime, to: endTime))
+ }
+
+ override public func copy(with zone: NSZone? = nil) -> Any {
+ remembering(copy: super.copy(with: zone))
+ }
+
+}
\ No newline at end of file
diff --git a/Sources/XCTBeton/Performance/XCTMetric/XCTClockMetric.swift b/Sources/XCTBeton/Performance/XCTMetric/XCTClockMetric.swift
new file mode 100644
index 0000000..5286e93
--- /dev/null
+++ b/Sources/XCTBeton/Performance/XCTMetric/XCTClockMetric.swift
@@ -0,0 +1,15 @@
+import Beton
+import class XCTest.XCTClockMetric
+
+public class XCTClockMetric: XCTest.XCTClockMetric, XCTMetric {
+ deinit { forgetSelf() }
+
+ public override func reportMeasurements(from startTime: XCTPerformanceMeasurementTimestamp, to endTime: XCTPerformanceMeasurementTimestamp) throws -> [XCTPerformanceMeasurement] {
+ storing(measurements: try super.reportMeasurements(from: startTime, to: endTime))
+ }
+
+ override public func copy(with zone: NSZone? = nil) -> Any {
+ remembering(copy: super.copy(with: zone))
+ }
+
+}
\ No newline at end of file
diff --git a/Sources/XCTBeton/Performance/XCTMetric/XCTMemoryMetric.swift b/Sources/XCTBeton/Performance/XCTMetric/XCTMemoryMetric.swift
new file mode 100644
index 0000000..9f6204c
--- /dev/null
+++ b/Sources/XCTBeton/Performance/XCTMetric/XCTMemoryMetric.swift
@@ -0,0 +1,15 @@
+import Beton
+import class XCTest.XCTMemoryMetric
+
+public class XCTMemoryMetric: XCTest.XCTMemoryMetric, XCTMetric {
+ deinit { forgetSelf() }
+
+ public override func reportMeasurements(from startTime: XCTPerformanceMeasurementTimestamp, to endTime: XCTPerformanceMeasurementTimestamp) throws -> [XCTPerformanceMeasurement] {
+ storing(measurements: try super.reportMeasurements(from: startTime, to: endTime))
+ }
+
+ override public func copy(with zone: NSZone? = nil) -> Any {
+ remembering(copy: super.copy(with: zone))
+ }
+
+}
\ No newline at end of file
diff --git a/Sources/XCTBeton/Performance/XCTMetric/XCTMetric+measurementStorage.swift b/Sources/XCTBeton/Performance/XCTMetric/XCTMetric+measurementStorage.swift
new file mode 100644
index 0000000..267d977
--- /dev/null
+++ b/Sources/XCTBeton/Performance/XCTMetric/XCTMetric+measurementStorage.swift
@@ -0,0 +1,37 @@
+import Beton
+
+fileprivate var results: [ObjectIdentifier: [XCTPerformanceMeasurement]] = [:]
+fileprivate var copyMap: [ObjectIdentifier: ObjectIdentifier] = [:]
+
+public extension XCTMetric {
+ var measurements: [XCTPerformanceMeasurement] { results[id] ?? [] }
+}
+
+internal extension XCTMetric {
+ func storing(measurements: [XCTPerformanceMeasurement]) -> [XCTPerformanceMeasurement] {
+ if results[originId] == nil { results[originId] = [] }
+ results[originId]?.append(contentsOf: measurements)
+ return measurements
+ }
+
+ func remembering(copy: Copy) -> Copy {
+ if let copy = copy as? Self { copyMap[copy.id] = id }
+ return copy
+ }
+
+ func forgetSelf() {
+ results[id] = nil
+ copyMap[id] = nil
+ }
+}
+
+fileprivate extension XCTMetric {
+ var id: ObjectIdentifier { ObjectIdentifier(self) }
+
+ var originId: ObjectIdentifier {
+ var originId = id
+ while copyMap[originId] != nil { originId = copyMap[originId]! }
+ return originId
+ }
+
+}
\ No newline at end of file
diff --git a/Sources/XCTBeton/Performance/XCTMetric/XCTMetric.swift b/Sources/XCTBeton/Performance/XCTMetric/XCTMetric.swift
new file mode 100644
index 0000000..1c4f768
--- /dev/null
+++ b/Sources/XCTBeton/Performance/XCTMetric/XCTMetric.swift
@@ -0,0 +1,7 @@
+import Beton
+import protocol XCTest.XCTMetric
+import class XCTest.XCTPerformanceMeasurement
+
+public protocol XCTMetric: XCTest.XCTMetric {
+ var measurements: [XCTPerformanceMeasurement] { get }
+}
diff --git a/Sources/XCTBeton/Performance/XCTMetric/XCTStorageMetric.swift b/Sources/XCTBeton/Performance/XCTMetric/XCTStorageMetric.swift
new file mode 100644
index 0000000..8969d93
--- /dev/null
+++ b/Sources/XCTBeton/Performance/XCTMetric/XCTStorageMetric.swift
@@ -0,0 +1,16 @@
+import Beton
+import class XCTest.XCTStorageMetric
+
+public class XCTStorageMetric: XCTest.XCTStorageMetric, XCTMetric {
+
+ deinit { forgetSelf() }
+
+ public override func reportMeasurements(from startTime: XCTPerformanceMeasurementTimestamp, to endTime: XCTPerformanceMeasurementTimestamp) throws -> [XCTPerformanceMeasurement] {
+ storing(measurements: try super.reportMeasurements(from: startTime, to: endTime))
+ }
+
+ override public func copy(with zone: NSZone? = nil) -> Any {
+ remembering(copy: super.copy(with: zone))
+ }
+
+}
\ No newline at end of file
diff --git a/Sources/XCTBeton/XCTBeton.swift b/Sources/XCTBeton/XCTBeton.swift
new file mode 100644
index 0000000..9c5629a
--- /dev/null
+++ b/Sources/XCTBeton/XCTBeton.swift
@@ -0,0 +1,97 @@
+@_exported import func XCTest.XCTAssertEqual
+@_exported import var XCTest.XCT_UI_TESTING_AVAILABLE
+@_exported import let XCTest.XCTestErrorDomain
+@_exported import struct XCTest.XCTestError
+@_exported import class XCTest._XCTestCaseInterruptionException
+@_exported import func XCTest._XCTPreformattedFailureHandler
+@_exported import func XCTest.XCTAssertThrowsError
+@_exported import class XCTest.XCUIElement
+@_exported import func XCTest.XCTAssertNil
+@_exported import class XCTest.XCTestObserver
+@_exported import protocol XCTest.XCTestObservation
+@_exported import class XCTest.XCTestObservationCenter
+@_exported import class XCTest.XCTIssueReference
+@_exported import enum XCTest.XCUIProtectedResource
+@_exported import protocol XCTest.XCUIScreenshotProviding
+@_exported import func XCTest.XCTAssertNotEqualWithAccuracy
+@_exported import class XCTest.XCTExpectedFailure
+@_exported import func XCTest.XCTAssertEqualWithAccuracy
+@_exported import class XCTest.XCTKVOExpectation
+@_exported import func XCTest.XCTAssertTrue
+@_exported import class XCTest.XCTMutableIssue
+@_exported import class XCTest.XCTDarwinNotificationExpectation
+@_exported import class XCTest.XCTestLog
+@_exported import protocol XCTest.XCUIElementAttributes
+@_exported import class XCTest.XCTSourceCodeSymbolInfo
+@_exported import func XCTest.XCTAssertNotIdentical
+@_exported import typealias XCTest.XCWaitCompletionHandler
+@_exported import let XCTest.XCTestScopeNone
+@_exported import let XCTest.XCUIIdentifierCloseWindow
+@_exported import struct XCTest.XCUIKeyboardKey
+@_exported import class XCTest.XCUIScreenshot
+@_exported import let XCTest.XCTestToolKey
+@_exported import func XCTest.XCTSkipUnless
+@_exported import class XCTest.XCTApplicationLaunchMetric
+@_exported import struct XCTest.XCTSkip
+@_exported import class XCTest.XCTPerformanceMeasurement
+@_exported import let XCTest.XCUIIdentifierZoomWindow
+@_exported import class XCTest.XCTNSNotificationExpectation
+@_exported import func XCTest.XCTAssertIdentical
+@_exported import let XCTest.XCTestScopeAll
+@_exported import class XCTest.XCTSourceCodeLocation
+@_exported import class XCTest.XCUIElementQuery
+@_exported import func XCTest.XCTAssert
+@_exported import func XCTest.XCTAssertNoThrow
+@_exported import func XCTest.XCTAssertLessThan
+@_exported import let XCTest.XCTestObserverClassKey
+@_exported import func XCTest._XCTDescriptionForValue
+@_exported import func XCTest.XCTAssertNotEqual
+@_exported import protocol XCTest.XCUIElementTypeQueryProvider
+@_exported import class XCTest.XCTPerformanceMeasurementTimestamp
+@_exported import let XCTest.XCTestedUnitPath
+@_exported import class XCTest.XCTestSuite
+@_exported import func XCTest.XCTAssertNotNil
+@_exported import class XCTest.XCUIApplication
+@_exported import class XCTest.XCTNSPredicateExpectation
+@_exported import class XCTest.XCTAttachment
+@_exported import class XCTest.XCUICoordinate
+@_exported import class XCTest.XCTOSSignpostMetric
+@_exported import enum XCTest.XCUIRemoteButton
+@_exported import let XCTest.XCUIIdentifierMinimizeWindow
+@_exported import class XCTest.XCTestRun
+@_exported import protocol XCTest.XCUIElementSnapshotProviding
+@_exported import protocol XCTest.XCUIElementSnapshot
+@_exported import func XCTest.XCTFail
+@_exported import struct XCTest.XCTPerformanceMetric
+@_exported import let XCTest.XCTestScopeKey
+@_exported import class XCTest.XCTestProbe
+@_exported import func XCTest.XCTAssertGreaterThan
+@_exported import class XCTest.XCTestExpectation
+@_exported import func XCTest.XCTExpectFailure
+@_exported import struct XCTest.XCTIssue
+@_exported import enum XCTest._XCTAssertionType
+@_exported import func XCTest._XCTFailureFormat
+@_exported import func XCTest.XCTAssertFalse
+@_exported import func XCTest.XCTAssertGreaterThanOrEqual
+@_exported import class XCTest.XCTSourceCodeContext
+@_exported import class XCTest.XCTSourceCodeFrame
+@_exported import class XCTest.XCTestSuiteRun
+@_exported import class XCTest.XCTWaiter
+@_exported import func XCTest.XCTSelfTestMain
+@_exported import let XCTest.XCUIIdentifierFullScreenWindow
+@_exported import protocol XCTest.XCTActivity
+@_exported import func XCTest.XCTAssertLessThanOrEqual
+@_exported import func XCTest.XCTSkipIf
+@_exported import struct XCTest.XCUIGestureVelocity
+@_exported import func XCTest.XCTUnwrap
+@_exported import protocol XCTest.XCTWaiterDelegate
+@_exported import class XCTest._XCTSkipFailureException
+@_exported import let XCTest.XCTestScopeSelf
+@_exported import class XCTest.XCTContext
+@_exported import class XCTest.XCUIScreen
+@_exported import class XCTest.XCTestCaseRun
+@_exported import func XCTest.XCTAssertEqual
+
+import class XCTest.XCTest
+
+public typealias XCTBeton = XCTest
\ No newline at end of file
diff --git a/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertClockIdentifier.swift b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertClockIdentifier.swift
new file mode 100644
index 0000000..a4a8efc
--- /dev/null
+++ b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertClockIdentifier.swift
@@ -0,0 +1,15 @@
+import Beton
+
+public enum XCTAssertClockIdentifier {
+ case timeMonotonic
+}
+
+extension XCTAssertClockIdentifier: XCTAssertMetricIdentifier {
+ public typealias RelatedMetric = XCTClockMetric
+
+ public var identifier: String {
+ switch self {
+ case .timeMonotonic: return "com.apple.dt.XCTMetric_Clock.time.monotonic"
+ }
+ }
+}
diff --git a/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertCpuIdentifier.swift b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertCpuIdentifier.swift
new file mode 100644
index 0000000..4fea8ed
--- /dev/null
+++ b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertCpuIdentifier.swift
@@ -0,0 +1,19 @@
+import Beton
+
+public enum XCTAssertCpuIdentifier {
+ case time
+ case cycles
+ case instructionsRetired
+}
+
+extension XCTAssertCpuIdentifier: XCTAssertMetricIdentifier {
+ public typealias RelatedMetric = XCTCPUMetric
+
+ public var identifier: String {
+ switch self {
+ case .time: return "com.apple.dt.XCTMetric_CPU.time"
+ case .cycles: return "com.apple.dt.XCTMetric_CPU.cycles"
+ case .instructionsRetired: return "com.apple.dt.XCTMetric_CPU.instructions_retired"
+ }
+ }
+}
diff --git a/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertDiskIdentifier.swift b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertDiskIdentifier.swift
new file mode 100644
index 0000000..ff46274
--- /dev/null
+++ b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertDiskIdentifier.swift
@@ -0,0 +1,15 @@
+import Beton
+
+public enum XCTAssertDiskIdentifier {
+ case logicalWrites
+}
+
+extension XCTAssertDiskIdentifier: XCTAssertMetricIdentifier {
+ public typealias RelatedMetric = XCTStorageMetric
+
+ public var identifier: String {
+ switch self {
+ case .logicalWrites: return "com.apple.dt.XCTMetric_Disk.logical_writes"
+ }
+ }
+}
diff --git a/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertMemoryIdentifier.swift b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertMemoryIdentifier.swift
new file mode 100644
index 0000000..29a19d0
--- /dev/null
+++ b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertMemoryIdentifier.swift
@@ -0,0 +1,17 @@
+import Beton
+
+public enum XCTAssertMemoryIdentifier {
+ case physical
+ case physicalPeak
+}
+
+extension XCTAssertMemoryIdentifier: XCTAssertMetricIdentifier {
+ public typealias RelatedMetric = XCTMemoryMetric
+
+ public var identifier: String {
+ switch self {
+ case .physical: return "com.apple.dt.XCTMetric_Memory.physical"
+ case .physicalPeak: return "com.apple.dt.XCTMetric_Memory.physical_peak"
+ }
+ }
+}
diff --git a/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertMetric.swift b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertMetric.swift
new file mode 100644
index 0000000..ad292d6
--- /dev/null
+++ b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTAssertMetric.swift
@@ -0,0 +1,25 @@
+import Beton
+
+public struct XCTAssertMetric where Identifier: XCTAssertMetricIdentifier {}
+public extension XCTAssertMetric {
+ static var cpu: XCTAssertMetric { .init() }
+ static var memory: XCTAssertMetric { .init() }
+ static var disk: XCTAssertMetric { .init() }
+ static var clock: XCTAssertMetric { .init() }
+}
+
+public extension XCTAssertMetric {
+ enum Aspect {
+ case average(maximum: Double)
+ case relativeStandardDeviation(maximum: Double)
+ }
+}
+
+internal extension XCTAssertMetric.Aspect {
+ var maximum: Double {
+ switch self {
+ case let .average(maximum): return maximum
+ case let .relativeStandardDeviation(maximum): return maximum
+ }
+ }
+}
\ No newline at end of file
diff --git a/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTestCase+XCTAssertMetric.swift b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTestCase+XCTAssertMetric.swift
new file mode 100644
index 0000000..429b5ff
--- /dev/null
+++ b/Sources/XCTBeton/XCTestCase/XCTAssertMetric/XCTestCase+XCTAssertMetric.swift
@@ -0,0 +1,67 @@
+import Beton
+import XCTest
+
+extension XCTestCase {
+ public final func XCTAssertMetric(
+ _ metric: XCTAssertMetric,
+ _ identifier: Identifier,
+ _ aspect: XCTAssertMetric.Aspect,
+ _ message: @autoclosure () -> String = "",
+ file: StaticString = #filePath,
+ line: UInt = #line
+ ) {
+ let measurements: [Double] = fetchMeasurement(for: identifier).map(\.value).map(\.value)
+ XCTAssertLessThanOrEqual(
+ measurements[keyPath: aspect.measurementKeyPath],
+ aspect.maximum,
+ errorMessage(for: identifier, message: message()),
+ file: file,
+ line: line
+ )
+ XCTAssertFalse(
+ measurements.isEmpty,
+ errorMessage(
+ for: identifier,
+ message: "No measurement results found."
+ ),
+ file: file,
+ line: line
+ )
+ }
+
+ private func errorMessage(for identifier: Identifier, message: String) -> String {
+ message.isEmpty
+ ? #"XCTAssertMetric<\#(Identifier.self).\#(identifier)>"#
+ : #"XCTAssertMetric<\#(Identifier.self).\#(identifier)> - \#(message)"#
+ }
+
+ internal func fetchMeasurement(for identifier: Identifier) -> [XCTPerformanceMeasurement] where Identifier: XCTAssertMetricIdentifier {
+ lastRunMetrics
+ .compactMap { $0 as? Identifier.RelatedMetric }
+ .map(\.measurements)
+ .reduce([], +)
+ .filter { $0.identifier == identifier.identifier } // TODO: this naming is fucking dumb
+ }
+}
+
+fileprivate extension Array where Element == Double {
+ var average: Element { sum() / Double(count) }
+
+ var standardDeviation: Element {
+ let squaredDifferences = map { pow($0 - average, 2.0) }
+ let variance = squaredDifferences.reduce(.zero, +) / Double(count - 1)
+ return sqrt(variance)
+ }
+
+ var relativeStandardDeviation: Double { standardDeviation / average }
+
+}
+
+fileprivate extension XCTAssertMetric.Aspect {
+ var measurementKeyPath: KeyPath<[Double], Double> {
+ switch self {
+ case .average: return \.average
+ case .relativeStandardDeviation: return \.relativeStandardDeviation
+ }
+ }
+}
\ No newline at end of file
diff --git a/Sources/XCTBeton/XCTestCase/XCTAssertMetricIdentifier.swift b/Sources/XCTBeton/XCTestCase/XCTAssertMetricIdentifier.swift
new file mode 100644
index 0000000..5280e19
--- /dev/null
+++ b/Sources/XCTBeton/XCTestCase/XCTAssertMetricIdentifier.swift
@@ -0,0 +1,6 @@
+import Beton
+
+public protocol XCTAssertMetricIdentifier {
+ associatedtype RelatedMetric: XCTMetric
+ var identifier: String { get }
+}
\ No newline at end of file
diff --git a/Sources/XCTBeton/XCTestCase/XCTestCase+measure.swift b/Sources/XCTBeton/XCTestCase/XCTestCase+measure.swift
new file mode 100644
index 0000000..2cf42b9
--- /dev/null
+++ b/Sources/XCTBeton/XCTestCase/XCTestCase+measure.swift
@@ -0,0 +1,17 @@
+import Beton
+
+extension XCTestCase {
+ @nonobjc open func measure(metrics: [XCTMetric], block: () -> Void) {
+ self.measure(metrics: metrics, options: Self.defaultMeasureOptions, block: block)
+ }
+
+ @nonobjc open func measure(options: XCTMeasureOptions, block: () -> Void) {
+ self.measure(metrics: Self.defaultMetrics, options: options, block: block)
+ }
+
+ @nonobjc open func measure(metrics: [XCTMetric], options: XCTMeasureOptions, block: () -> Void) {
+ super.measure(metrics: metrics, options: options, block: block)
+ precondition(lastRunMetrics.isEmpty, "Must not have performed measurements yet!")
+ lastRunMetrics = metrics
+ }
+}
\ No newline at end of file
diff --git a/Sources/XCTBeton/XCTestCase/XCTestCase.swift b/Sources/XCTBeton/XCTestCase/XCTestCase.swift
new file mode 100644
index 0000000..390c2f9
--- /dev/null
+++ b/Sources/XCTBeton/XCTestCase/XCTestCase.swift
@@ -0,0 +1,11 @@
+import Beton
+import class XCTest.XCTestCase
+
+open class XCTestCase: XCTest.XCTestCase {
+ internal var lastRunMetrics: [XCTMetric] = []
+}
+
+extension XCTest.XCTestCase {
+ open class var defaultMetrics: [XCTMetric] { [XCTClockMetric()] }
+ open class var defaultMeasureOptions: XCTMeasureOptions { XCTMeasureOptions() }
+}
\ No newline at end of file
diff --git a/Tests/ComponentTests/Resources/en_US.lproj/Test.strings b/Tests/ComponentTests/Resources/en_US.lproj/Test.strings
new file mode 100644
index 0000000..85c3d86
--- /dev/null
+++ b/Tests/ComponentTests/Resources/en_US.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Test";
\ No newline at end of file
diff --git a/Tests/ComponentTests/Resources/hu_HU.lproj/Test.strings b/Tests/ComponentTests/Resources/hu_HU.lproj/Test.strings
new file mode 100644
index 0000000..eb55259
--- /dev/null
+++ b/Tests/ComponentTests/Resources/hu_HU.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Teszt";
\ No newline at end of file
diff --git a/Tests/IntegrationTests/Resources/en_US.lproj/Test.strings b/Tests/IntegrationTests/Resources/en_US.lproj/Test.strings
new file mode 100644
index 0000000..85c3d86
--- /dev/null
+++ b/Tests/IntegrationTests/Resources/en_US.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Test";
\ No newline at end of file
diff --git a/Tests/IntegrationTests/Resources/hu_HU.lproj/Test.strings b/Tests/IntegrationTests/Resources/hu_HU.lproj/Test.strings
new file mode 100644
index 0000000..eb55259
--- /dev/null
+++ b/Tests/IntegrationTests/Resources/hu_HU.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Teszt";
\ No newline at end of file
diff --git a/Tests/PerformanceTests/Beton/Bundle/BundleTest.swift b/Tests/PerformanceTests/Beton/Bundle/BundleTest.swift
new file mode 100644
index 0000000..e13fe20
--- /dev/null
+++ b/Tests/PerformanceTests/Beton/Bundle/BundleTest.swift
@@ -0,0 +1,51 @@
+import Beton
+import XCTBeton
+
+class BundleTest: XCTestCase {
+ override func tearDown() {
+ super.tearDown()
+ XCTAssertMetric(.cpu, .cycles, .average(maximum: 5000))
+ XCTAssertMetric(.cpu, .instructionsRetired, .average(maximum: 4500))
+ XCTAssertMetric(.cpu, .time, .average(maximum: 0.002))
+ XCTAssertMetric(.memory, .physical, .average(maximum: 60))
+ XCTAssertMetric(.memory, .physicalPeak, .average(maximum: 0))
+ XCTAssertMetric(.disk, .logicalWrites, .average(maximum: 0))
+ XCTAssertMetric(.clock, .timeMonotonic, .average(maximum: 0.005))
+ }
+
+ func testLocalizationBundles() {
+ measure(metrics: .defaults) {
+ Bundle.module.localizationBundles
+ }
+ }
+
+ func testLocalizedString_keyOnly() {
+ measure(metrics: .defaults) {
+ Bundle.module.localizedString("Test")
+ }
+ }
+
+ func testLocalizedString_keyAndTableOnly() {
+ measure(metrics: .defaults) {
+ Bundle.module.localizedString("Test", from: "Test")
+ }
+ }
+
+ func testLocalizedString_keyAndValueOnly() {
+ measure(metrics: .defaults) {
+ Bundle.module.localizedString("Test", fallback: "Test")
+ }
+ }
+
+}
+
+extension Array where Self.Element == XCTMetric {
+ static var defaults: [Element] {
+ [
+ XCTCPUMetric(),
+ XCTMemoryMetric(),
+ XCTStorageMetric(),
+ XCTClockMetric(),
+ ]
+ }
+}
diff --git a/Tests/PerformanceTests/Resources/en_US.lproj/Test.strings b/Tests/PerformanceTests/Resources/en_US.lproj/Test.strings
new file mode 100644
index 0000000..85c3d86
--- /dev/null
+++ b/Tests/PerformanceTests/Resources/en_US.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Test";
\ No newline at end of file
diff --git a/Tests/PerformanceTests/Resources/hu_HU.lproj/Test.strings b/Tests/PerformanceTests/Resources/hu_HU.lproj/Test.strings
new file mode 100644
index 0000000..eb55259
--- /dev/null
+++ b/Tests/PerformanceTests/Resources/hu_HU.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Teszt";
\ No newline at end of file
diff --git a/Tests/SystemTests/Resources/en_US.lproj/Test.strings b/Tests/SystemTests/Resources/en_US.lproj/Test.strings
new file mode 100644
index 0000000..85c3d86
--- /dev/null
+++ b/Tests/SystemTests/Resources/en_US.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Test";
\ No newline at end of file
diff --git a/Tests/SystemTests/Resources/hu_HU.lproj/Test.strings b/Tests/SystemTests/Resources/hu_HU.lproj/Test.strings
new file mode 100644
index 0000000..eb55259
--- /dev/null
+++ b/Tests/SystemTests/Resources/hu_HU.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Teszt";
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Bundle/BundleTest+localizedString.swift b/Tests/UnitTests/Beton/Bundle/BundleTest+localizedString.swift
new file mode 100644
index 0000000..d1f3320
--- /dev/null
+++ b/Tests/UnitTests/Beton/Bundle/BundleTest+localizedString.swift
@@ -0,0 +1,26 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+extension BundleTest {
+ func testLocalizedString_keyOnly() {
+ XCTAssertEqual(
+ Bundle.module.localizedString("Test"),
+ Bundle.module.localizedString(forKey: "Test", value: nil, table: nil)
+ )
+ }
+
+ func testLocalizedString_keyAndTableOnly() {
+ XCTAssertEqual(
+ Bundle.module.localizedString("Test", from: "Test"),
+ Bundle.module.localizedString(forKey: "Test", value: nil, table: "Test")
+ )
+ }
+
+ func testLocalizedString_keyAndValueOnly() {
+ XCTAssertEqual(
+ Bundle.module.localizedString("Test", fallback: "Test"),
+ Bundle.module.localizedString(forKey: "Test", value: "Test", table: nil)
+ )
+ }
+}
diff --git a/Tests/UnitTests/Beton/Bundle/BundleTest+localiztationBundles.swift b/Tests/UnitTests/Beton/Bundle/BundleTest+localiztationBundles.swift
new file mode 100644
index 0000000..95c30ed
--- /dev/null
+++ b/Tests/UnitTests/Beton/Bundle/BundleTest+localiztationBundles.swift
@@ -0,0 +1,11 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+extension BundleTest {
+ func testLocalizationBundles() {
+ XCTAssertTrue(Bundle.module.localizationBundles.keys.contains(Locale(identifier: "en_US")))
+ XCTAssertTrue(Bundle.module.localizationBundles.keys.contains(Locale(identifier: "hu_HU")))
+ XCTAssertFalse(Bundle.module.localizationBundles.keys.contains(Locale(identifier: "de_DE")))
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Bundle/BundleTest.swift b/Tests/UnitTests/Beton/Bundle/BundleTest.swift
new file mode 100644
index 0000000..f499940
--- /dev/null
+++ b/Tests/UnitTests/Beton/Bundle/BundleTest.swift
@@ -0,0 +1,4 @@
+import Foundation
+import XCTest
+
+class BundleTest: XCTestCase {}
diff --git a/Tests/UnitTests/Beton/MathTest.swift b/Tests/UnitTests/Beton/MathTest.swift
new file mode 100644
index 0000000..399cf6c
--- /dev/null
+++ b/Tests/UnitTests/Beton/MathTest.swift
@@ -0,0 +1,20 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class MathTest: XCTestCase {
+
+ func testPow() {
+ XCTAssertEqual(
+ pow(Measurement(value: 2, unit: .bits), 3),
+ Measurement(value: 1, unit: .bytes)
+ )
+ }
+
+ func testSqrt() {
+ XCTAssertEqual(
+ sqrt(Measurement(value: 8, unit: .bits)),
+ Measurement(value: sqrt(8), unit: .bits)
+ )
+ }
+}
diff --git a/Tests/UnitTests/Beton/Measurement/MeasurementTest+AdditiveArithmetic.swift b/Tests/UnitTests/Beton/Measurement/MeasurementTest+AdditiveArithmetic.swift
new file mode 100644
index 0000000..fd91d73
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/MeasurementTest+AdditiveArithmetic.swift
@@ -0,0 +1,30 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+extension MeasurementTest {
+ func testZero() {
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitAcceleration.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitAngle.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitArea.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitConcentrationMass.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitDispersion.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitDuration.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitElectricCharge.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitElectricCurrent.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitElectricPotentialDifference.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitElectricResistance.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitEnergy.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitFrequency.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitFuelEfficiency.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitIlluminance.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitInformationStorage.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitLength.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitMass.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitPower.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitPressure.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitSpeed.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitTemperature.default))
+ XCTAssertEqual(Measurement.zero, Measurement(value: 0, unit: UnitVolume.default))
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/MeasurementTest.swift b/Tests/UnitTests/Beton/Measurement/MeasurementTest.swift
new file mode 100644
index 0000000..d54a7b4
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/MeasurementTest.swift
@@ -0,0 +1,6 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class MeasurementTest: XCTestCase {
+}
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitAccelerationTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitAccelerationTest.swift
new file mode 100644
index 0000000..2e3f7d5
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitAccelerationTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitAccelerationTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitAcceleration.default, .metersPerSecondSquared)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitAngleTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitAngleTest.swift
new file mode 100644
index 0000000..031e3e0
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitAngleTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitAngleTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitAngle.default, .degrees)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitAreaTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitAreaTest.swift
new file mode 100644
index 0000000..4cae16e
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitAreaTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitAreaTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitArea.default, .squareMeters)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitConcentrationMassTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitConcentrationMassTest.swift
new file mode 100644
index 0000000..df4deb7
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitConcentrationMassTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitConcentrationMassTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitConcentrationMass.default, .gramsPerLiter)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitDispersionTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitDispersionTest.swift
new file mode 100644
index 0000000..f233bbc
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitDispersionTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitDispersionTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitDispersion.default, .partsPerMillion)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitDurationTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitDurationTest.swift
new file mode 100644
index 0000000..3e1e760
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitDurationTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitDurationTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitDuration.default, .seconds )
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricChargeTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricChargeTest.swift
new file mode 100644
index 0000000..028d93e
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricChargeTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitElectricChargeTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitElectricCharge.default, .ampereHours)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricCurrentTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricCurrentTest.swift
new file mode 100644
index 0000000..bb8e9c6
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricCurrentTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitElectricCurrentTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitElectricCurrent.default, .default)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricPotentialDifferenceTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricPotentialDifferenceTest.swift
new file mode 100644
index 0000000..58ee257
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricPotentialDifferenceTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitElectricPotentialDifferenceTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitElectricPotentialDifference.default, .volts)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricResistanceTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricResistanceTest.swift
new file mode 100644
index 0000000..0a30a6d
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitElectricResistanceTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitElectricResistanceTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitElectricResistance.default, .ohms)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitEnergyTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitEnergyTest.swift
new file mode 100644
index 0000000..9c684d0
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitEnergyTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitEnergyTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitEnergy.default, .joules)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitFrequencyTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitFrequencyTest.swift
new file mode 100644
index 0000000..379ab06
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitFrequencyTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitFrequencyTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitFrequency.default, .hertz)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitFuelEfficiencyTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitFuelEfficiencyTest.swift
new file mode 100644
index 0000000..91f9734
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitFuelEfficiencyTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitFuelEfficiencyTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitFuelEfficiency.default, .litersPer100Kilometers)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitIlluminanceTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitIlluminanceTest.swift
new file mode 100644
index 0000000..6bc9394
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitIlluminanceTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitIlluminanceTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitIlluminance.default, .lux)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitInformationStorageTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitInformationStorageTest.swift
new file mode 100644
index 0000000..0a8cd8e
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitInformationStorageTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitInformationStorageTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitInformationStorage.default, .bits)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitLengthTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitLengthTest.swift
new file mode 100644
index 0000000..0dda942
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitLengthTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitLengthTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitLength.default, .meters)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitMassTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitMassTest.swift
new file mode 100644
index 0000000..81b8527
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitMassTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitMassTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitMass.default, .grams)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitPowerTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitPowerTest.swift
new file mode 100644
index 0000000..d7c28bf
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitPowerTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitPowerTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitPower.default, .watts)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitPressureTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitPressureTest.swift
new file mode 100644
index 0000000..ac97355
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitPressureTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitPressureTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitPressure.default, .bars)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitSpeedTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitSpeedTest.swift
new file mode 100644
index 0000000..63f4f94
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitSpeedTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitSpeedTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitSpeed.default, .kilometersPerHour)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitTemperatureTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitTemperatureTest.swift
new file mode 100644
index 0000000..a6a135a
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitTemperatureTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitTemperatureTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitTemperature.default, .celsius)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Measurement/Unit/UnitVolumeTest.swift b/Tests/UnitTests/Beton/Measurement/Unit/UnitVolumeTest.swift
new file mode 100644
index 0000000..4101051
--- /dev/null
+++ b/Tests/UnitTests/Beton/Measurement/Unit/UnitVolumeTest.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class UnitVolumeTest: XCTestCase {
+ func testDefault() {
+ XCTAssertEqual(UnitVolume.default, .liters)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/Beton/Sequence/SequenceTest+testSum.swift b/Tests/UnitTests/Beton/Sequence/SequenceTest+testSum.swift
new file mode 100644
index 0000000..d9584ab
--- /dev/null
+++ b/Tests/UnitTests/Beton/Sequence/SequenceTest+testSum.swift
@@ -0,0 +1,9 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+extension SequenceTest {
+ func testSum() {
+ XCTAssertEqual([1, 2, 3, 4, 5, 6].sum(), 21)
+ }
+}
diff --git a/Tests/UnitTests/Beton/Sequence/SequenceTest.swift b/Tests/UnitTests/Beton/Sequence/SequenceTest.swift
new file mode 100644
index 0000000..ab64307
--- /dev/null
+++ b/Tests/UnitTests/Beton/Sequence/SequenceTest.swift
@@ -0,0 +1,6 @@
+import Foundation
+import XCTest
+@testable import Beton
+
+class SequenceTest: XCTestCase {
+}
diff --git a/Tests/UnitTests/Resources/en_US.lproj/Test.strings b/Tests/UnitTests/Resources/en_US.lproj/Test.strings
new file mode 100644
index 0000000..85c3d86
--- /dev/null
+++ b/Tests/UnitTests/Resources/en_US.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Test";
\ No newline at end of file
diff --git a/Tests/UnitTests/Resources/hu_HU.lproj/Test.strings b/Tests/UnitTests/Resources/hu_HU.lproj/Test.strings
new file mode 100644
index 0000000..eb55259
--- /dev/null
+++ b/Tests/UnitTests/Resources/hu_HU.lproj/Test.strings
@@ -0,0 +1 @@
+"Test" = "Teszt";
\ No newline at end of file
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertClockIdentifierTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertClockIdentifierTest.swift
new file mode 100644
index 0000000..6298bbe
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertClockIdentifierTest.swift
@@ -0,0 +1,11 @@
+import Beton
+@testable import XCTBeton
+
+class XCTAssertClockIdentifierTest: XCTestCase {
+ func testIdentifier() {
+ XCTAssertEqual(
+ XCTAssertClockIdentifier.timeMonotonic.identifier,
+ "com.apple.dt.XCTMetric_Clock.time.monotonic"
+ )
+ }
+}
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertCpuIdentifierTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertCpuIdentifierTest.swift
new file mode 100644
index 0000000..c0581f4
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertCpuIdentifierTest.swift
@@ -0,0 +1,10 @@
+import Beton
+@testable import XCTBeton
+
+class XCTAssertCpuIdentifierTest: XCTestCase {
+ func testIdentifier() {
+ XCTAssertEqual(XCTAssertCpuIdentifier.time.identifier, "com.apple.dt.XCTMetric_CPU.time")
+ XCTAssertEqual(XCTAssertCpuIdentifier.cycles.identifier, "com.apple.dt.XCTMetric_CPU.cycles")
+ XCTAssertEqual(XCTAssertCpuIdentifier.instructionsRetired.identifier, "com.apple.dt.XCTMetric_CPU.instructions_retired")
+ }
+}
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertDiskIdentifierTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertDiskIdentifierTest.swift
new file mode 100644
index 0000000..b6de2ba
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertDiskIdentifierTest.swift
@@ -0,0 +1,8 @@
+import Beton
+@testable import XCTBeton
+
+class XCTAssertDiskIdentifierTest: XCTestCase {
+ func testIdentifier() {
+ XCTAssertEqual(XCTAssertDiskIdentifier.logicalWrites.identifier, "com.apple.dt.XCTMetric_Disk.logical_writes")
+ }
+}
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertMemoryIdentifierTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertMemoryIdentifierTest.swift
new file mode 100644
index 0000000..42a790b
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertMemoryIdentifierTest.swift
@@ -0,0 +1,9 @@
+import Beton
+@testable import XCTBeton
+
+class XCTAssertMemoryIdentifierTest: XCTestCase {
+ func testIdentifier() {
+ XCTAssertEqual(XCTAssertMemoryIdentifier.physical.identifier, "com.apple.dt.XCTMetric_Memory.physical")
+ XCTAssertEqual(XCTAssertMemoryIdentifier.physicalPeak.identifier, "com.apple.dt.XCTMetric_Memory.physical_peak")
+ }
+}
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertMetric.AspectTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertMetric.AspectTest.swift
new file mode 100644
index 0000000..cfd2354
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTAssertMetric/XCTAssertMetric.AspectTest.swift
@@ -0,0 +1,23 @@
+import Beton
+@testable import XCTBeton
+
+class XCTAssertMetric_AspectTest: XCTestCase {
+ private func AssertMaximum(
+ of: XCTAssertMetric,
+ aspect: XCTAssertMetric.Aspect,
+ equals: Double
+ ) {
+ XCTAssertEqual(aspect.maximum, equals)
+ }
+
+ func testMaximum() {
+ AssertMaximum(of: .clock, aspect: .relativeStandardDeviation(maximum: 123), equals: 123)
+ AssertMaximum(of: .clock, aspect: .average(maximum: 123), equals: 123)
+ AssertMaximum(of: .disk, aspect: .relativeStandardDeviation(maximum: 123), equals: 123)
+ AssertMaximum(of: .disk, aspect: .average(maximum: 123), equals: 123)
+ AssertMaximum(of: .memory, aspect: .relativeStandardDeviation(maximum: 123), equals: 123)
+ AssertMaximum(of: .memory, aspect: .average(maximum: 123), equals: 123)
+ AssertMaximum(of: .cpu, aspect: .relativeStandardDeviation(maximum: 123), equals: 123)
+ AssertMaximum(of: .cpu, aspect: .average(maximum: 123), equals: 123)
+ }
+}
\ No newline at end of file
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTCPUMetricTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTCPUMetricTest.swift
new file mode 100644
index 0000000..5ef1e7e
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTCPUMetricTest.swift
@@ -0,0 +1,6 @@
+import Beton
+import XCTBeton
+
+class XCTCPUMetricTest: XCTMetricTest {
+ override func setUp() { unit = .init() }
+}
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTClockMetricTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTClockMetricTest.swift
new file mode 100644
index 0000000..9e977da
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTClockMetricTest.swift
@@ -0,0 +1,6 @@
+import Beton
+import XCTBeton
+
+class XCTClockMetricTest: XCTMetricTest {
+ override func setUp() { unit = .init() }
+}
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTMemoryMetricTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTMemoryMetricTest.swift
new file mode 100644
index 0000000..563f9dc
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTMemoryMetricTest.swift
@@ -0,0 +1,6 @@
+import Beton
+import XCTBeton
+
+class XCTMemoryMetricTest: XCTMetricTest {
+ override func setUp() { unit = .init() }
+}
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTMetricTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTMetricTest.swift
new file mode 100644
index 0000000..e57bd54
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTMetricTest.swift
@@ -0,0 +1,11 @@
+import Beton
+import XCTBeton
+
+class XCTMetricTest: XCTestCase where Unit: XCTMetric {
+ var unit: Unit! = nil
+
+ func testMeasurement_emptyByDefault() {
+ XCTAssertTrue(unit.measurements.isEmpty, "Measurements should be empty by default.")
+ }
+
+}
diff --git a/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTStorageMetricTest.swift b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTStorageMetricTest.swift
new file mode 100644
index 0000000..e5a0e73
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/Performance/XCTMetric/XCTStorageMetricTest.swift
@@ -0,0 +1,6 @@
+import Beton
+import XCTBeton
+
+class XCTStorageMetricTest: XCTMetricTest {
+ override func setUp() { unit = .init() }
+}
diff --git a/Tests/UnitTests/XCTBeton/XCTestCaseTest.swift b/Tests/UnitTests/XCTBeton/XCTestCaseTest.swift
new file mode 100644
index 0000000..aa7e213
--- /dev/null
+++ b/Tests/UnitTests/XCTBeton/XCTestCaseTest.swift
@@ -0,0 +1,15 @@
+import Beton
+@testable import XCTBeton
+
+class XCTestCaseTest: XCTestCase {
+ func testLastRunMetrics() {
+ let testCase = XCTestCase()
+ XCTAssertTrue(testCase.lastRunMetrics.isEmpty, "Should initially be empty.")
+ }
+
+ func testDefaultMetrics() {
+ XCTAssertEqual(XCTestCase.defaultMetrics.count, 1, "Should have only one default metric.")
+ XCTAssertTrue(XCTestCase.defaultMetrics[0] is XCTClockMetric, "First item should be an XCTClockMetric.")
+ }
+
+}