diff --git a/Sources/SkipFoundation/Measurement.swift b/Sources/SkipFoundation/Measurement.swift new file mode 100644 index 0000000..b88efca --- /dev/null +++ b/Sources/SkipFoundation/Measurement.swift @@ -0,0 +1,137 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public typealias NSMeasurement = Measurement + +public struct Measurement : Hashable, Comparable, CustomStringConvertible { + public var value: Double + public var unit: UnitType + + public init(value: Double, unit: UnitType) { + self.value = value + self.unit = unit + } + + // MARK: Conversion + + public func converted(to otherUnit: UnitType) -> Measurement { + if unit === otherUnit || unit == otherUnit { + return Measurement(value: value, unit: otherUnit) + } + guard let fromDim = unit as? Dimension, + let toDim = otherUnit as? Dimension else { + return Measurement(value: value, unit: otherUnit) + } + let baseValue = fromDim.converter.baseUnitValue(fromValue: value) + let result = toDim.converter.value(fromBaseUnitValue: baseValue) + return Measurement(value: result, unit: otherUnit) + } + + // MARK: Equatable (exact comparison, matching Apple Foundation) + + public static func ==(lhs: Measurement, rhs: Measurement) -> Bool { + if lhs.unit == rhs.unit { return lhs.value == rhs.value } + guard let lhsDim = lhs.unit as? Dimension, + let rhsDim = rhs.unit as? Dimension else { + return false + } + let lhsBase = lhsDim.converter.baseUnitValue(fromValue: lhs.value) + let rhsBase = rhsDim.converter.baseUnitValue(fromValue: rhs.value) + return lhsBase == rhsBase + } + + // MARK: Comparable + + public static func <(lhs: Measurement, rhs: Measurement) -> Bool { + guard let lhsDim = lhs.unit as? Dimension, + let rhsDim = rhs.unit as? Dimension else { + return lhs.value < rhs.value + } + return lhsDim.converter.baseUnitValue(fromValue: lhs.value) < + rhsDim.converter.baseUnitValue(fromValue: rhs.value) + } + + // MARK: Hashable + + public func hash(into hasher: inout Hasher) { + if let dim = unit as? Dimension { + hasher.combine(dim.converter.baseUnitValue(fromValue: value)) + } else { + hasher.combine(value) + hasher.combine(unit) + } + } + + // MARK: CustomStringConvertible + + public var description: String { "\(value) \(unit.symbol)" } + + // MARK: Arithmetic (named methods — Skip does not support custom operators) + + public func adding(_ other: Measurement) -> Measurement { + if unit == other.unit { + return Measurement(value: value + other.value, unit: unit) + } + let otherConverted = other.converted(to: unit) + return Measurement(value: value + otherConverted.value, unit: unit) + } + + public func subtracting(_ other: Measurement) -> Measurement { + if unit == other.unit { + return Measurement(value: value - other.value, unit: unit) + } + let otherConverted = other.converted(to: unit) + return Measurement(value: value - otherConverted.value, unit: unit) + } + + public mutating func negate() { + value = -value + } + + public func multiplied(by scalar: Double) -> Measurement { + return Measurement(value: value * scalar, unit: unit) + } + + public func divided(by scalar: Double) -> Measurement { + return Measurement(value: value / scalar, unit: unit) + } + + // NOTE: Codable is not conformable here — Kotlin type erasure prevents the + // companion object from referencing the generic UnitType. All Codable + // encode/decode happens on the native Swift side (Foundation / + // swift-corelibs-foundation). +} + +#else +import Foundation + +// Provide the same named methods on native Foundation.Measurement +// so that cross-platform code can use either operators or named methods. +extension Measurement where UnitType: Dimension { + public func adding(_ other: Measurement) -> Measurement { + if unit == other.unit { + return Measurement(value: value + other.value, unit: unit) + } + let otherConverted = other.converted(to: unit) + return Measurement(value: value + otherConverted.value, unit: unit) + } + + public func subtracting(_ other: Measurement) -> Measurement { + if unit == other.unit { + return Measurement(value: value - other.value, unit: unit) + } + let otherConverted = other.converted(to: unit) + return Measurement(value: value - otherConverted.value, unit: unit) + } + + public func multiplied(by scalar: Double) -> Measurement { + return Measurement(value: value * scalar, unit: unit) + } + + public func divided(by scalar: Double) -> Measurement { + return Measurement(value: value / scalar, unit: unit) + } +} + +#endif diff --git a/Sources/SkipFoundation/Units/Unit.swift b/Sources/SkipFoundation/Units/Unit.swift new file mode 100644 index 0000000..2524d8f --- /dev/null +++ b/Sources/SkipFoundation/Units/Unit.swift @@ -0,0 +1,144 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +// "Unit" is a reserved name in Kotlin (kotlin.Unit). On iOS, Foundation.Unit +// is used directly. In Skip/Kotlin, FoundationUnit is the base class; consumers +// use Dimension subclasses (UnitMass, UnitLength, etc.) — not Unit directly. +public typealias NSUnit = FoundationUnit +public typealias NSDimension = Dimension + +// MARK: - UnitConverter + +public class UnitConverter { + public init() {} + + public func baseUnitValue(fromValue value: Double) -> Double { + return value + } + + public func value(fromBaseUnitValue baseUnitValue: Double) -> Double { + return baseUnitValue + } +} + +// MARK: - UnitConverterLinear + +public final class UnitConverterLinear : UnitConverter, Hashable { + public let coefficient: Double + public let constant: Double + + public init(coefficient: Double, constant: Double = 0) { + self.coefficient = coefficient + self.constant = constant + super.init() + } + + public override func baseUnitValue(fromValue value: Double) -> Double { + return value * coefficient + constant + } + + public override func value(fromBaseUnitValue baseUnitValue: Double) -> Double { + return (baseUnitValue - constant) / coefficient + } + + public static func ==(lhs: UnitConverterLinear, rhs: UnitConverterLinear) -> Bool { + return lhs.coefficient == rhs.coefficient && lhs.constant == rhs.constant + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(coefficient) + hasher.combine(constant) + } +} + +// MARK: - UnitConverterReciprocal + +public final class UnitConverterReciprocal : UnitConverter, Hashable { + public let reciprocal: Double + + public init(reciprocal: Double) { + self.reciprocal = reciprocal + super.init() + } + + public override func baseUnitValue(fromValue value: Double) -> Double { + return reciprocal / value + } + + public override func value(fromBaseUnitValue baseUnitValue: Double) -> Double { + return reciprocal / baseUnitValue + } + + public static func ==(lhs: UnitConverterReciprocal, rhs: UnitConverterReciprocal) -> Bool { + return lhs.reciprocal == rhs.reciprocal + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(reciprocal) + } +} + +// MARK: - FoundationUnit (named to avoid Kotlin's kotlin.Unit conflict) + +public class FoundationUnit : Hashable, CustomStringConvertible { + public let symbol: String + + public init(symbol: String) { + self.symbol = symbol + } + + public var description: String { symbol } + + public static func ==(lhs: FoundationUnit, rhs: FoundationUnit) -> Bool { + if lhs === rhs { return true } + guard type(of: lhs) == type(of: rhs) else { return false } + return lhs.symbol == rhs.symbol + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(symbol) + } +} + +// MARK: - Dimension + +public class Dimension : FoundationUnit { + public let converter: UnitConverter + + public init(symbol: String, converter: UnitConverter) { + self.converter = converter + super.init(symbol: symbol) + } + + public class func baseUnit() -> Dimension { + fatalError("Subclass must override baseUnit()") + } + + public static func ==(lhs: Dimension, rhs: Dimension) -> Bool { + if lhs === rhs { return true } + guard type(of: lhs) == type(of: rhs) else { return false } + guard lhs.symbol == rhs.symbol else { return false } + // Compare converters by type and value + if let lc = lhs.converter as? UnitConverterLinear, + let rc = rhs.converter as? UnitConverterLinear { + return lc == rc + } + if let lr = lhs.converter as? UnitConverterReciprocal, + let rr = rhs.converter as? UnitConverterReciprocal { + return lr == rr + } + return false + } + + public override func hash(into hasher: inout Hasher) { + hasher.combine(symbol) + if let lc = converter as? UnitConverterLinear { + hasher.combine(lc) + } else if let lr = converter as? UnitConverterReciprocal { + hasher.combine(lr) + } + } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitAcceleration.swift b/Sources/SkipFoundation/Units/UnitAcceleration.swift new file mode 100644 index 0000000..0160f79 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitAcceleration.swift @@ -0,0 +1,12 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitAcceleration : Dimension { + public static let metersPerSecondSquared = UnitAcceleration(symbol: "m/s²", converter: UnitConverterLinear(coefficient: 1.0)) + public static let gravity = UnitAcceleration(symbol: "g", converter: UnitConverterLinear(coefficient: 9.81)) + + public override class func baseUnit() -> Dimension { metersPerSecondSquared } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitAngle.swift b/Sources/SkipFoundation/Units/UnitAngle.swift new file mode 100644 index 0000000..17bac2e --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitAngle.swift @@ -0,0 +1,16 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitAngle : Dimension { + public static let degrees = UnitAngle(symbol: "°", converter: UnitConverterLinear(coefficient: 1.0)) + public static let arcMinutes = UnitAngle(symbol: "ʹ", converter: UnitConverterLinear(coefficient: 1.0 / 60.0)) + public static let arcSeconds = UnitAngle(symbol: "ʺ", converter: UnitConverterLinear(coefficient: 1.0 / 3600.0)) + public static let radians = UnitAngle(symbol: "rad", converter: UnitConverterLinear(coefficient: 180.0 / Double.pi)) + public static let gradians = UnitAngle(symbol: "grad", converter: UnitConverterLinear(coefficient: 0.9)) + public static let revolutions = UnitAngle(symbol: "rev", converter: UnitConverterLinear(coefficient: 360.0)) + + public override class func baseUnit() -> Dimension { degrees } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitArea.swift b/Sources/SkipFoundation/Units/UnitArea.swift new file mode 100644 index 0000000..f0d1dee --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitArea.swift @@ -0,0 +1,24 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitArea : Dimension { + public static let squareMegameters = UnitArea(symbol: "Mm²", converter: UnitConverterLinear(coefficient: 1e12)) + public static let squareKilometers = UnitArea(symbol: "km²", converter: UnitConverterLinear(coefficient: 1e6)) + public static let squareMeters = UnitArea(symbol: "m²", converter: UnitConverterLinear(coefficient: 1.0)) + public static let squareCentimeters = UnitArea(symbol: "cm²", converter: UnitConverterLinear(coefficient: 0.0001)) + public static let squareMillimeters = UnitArea(symbol: "mm²", converter: UnitConverterLinear(coefficient: 0.000001)) + public static let squareMicrometers = UnitArea(symbol: "µm²", converter: UnitConverterLinear(coefficient: 1e-12)) + public static let squareNanometers = UnitArea(symbol: "nm²", converter: UnitConverterLinear(coefficient: 1e-18)) + public static let squareInches = UnitArea(symbol: "in²", converter: UnitConverterLinear(coefficient: 0.00064516)) + public static let squareFeet = UnitArea(symbol: "ft²", converter: UnitConverterLinear(coefficient: 0.092903)) + public static let squareYards = UnitArea(symbol: "yd²", converter: UnitConverterLinear(coefficient: 0.836127)) + public static let squareMiles = UnitArea(symbol: "mi²", converter: UnitConverterLinear(coefficient: 2.59e6)) + public static let acres = UnitArea(symbol: "ac", converter: UnitConverterLinear(coefficient: 4046.86)) + public static let ares = UnitArea(symbol: "a", converter: UnitConverterLinear(coefficient: 100.0)) + public static let hectares = UnitArea(symbol: "ha", converter: UnitConverterLinear(coefficient: 10000.0)) + + public override class func baseUnit() -> Dimension { squareMeters } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitConcentrationMass.swift b/Sources/SkipFoundation/Units/UnitConcentrationMass.swift new file mode 100644 index 0000000..14ee452 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitConcentrationMass.swift @@ -0,0 +1,16 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitConcentrationMass : Dimension { + public static let gramsPerLiter = UnitConcentrationMass(symbol: "g/L", converter: UnitConverterLinear(coefficient: 1.0)) + public static let milligramsPerDeciliter = UnitConcentrationMass(symbol: "mg/dL", converter: UnitConverterLinear(coefficient: 0.01)) + + public static func millimolesPerLiter(withGramsPerMole gramsPerMole: Double) -> UnitConcentrationMass { + return UnitConcentrationMass(symbol: "mmol/L", converter: UnitConverterLinear(coefficient: gramsPerMole / 1000.0)) + } + + public override class func baseUnit() -> Dimension { gramsPerLiter } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitDispersion.swift b/Sources/SkipFoundation/Units/UnitDispersion.swift new file mode 100644 index 0000000..d47b9ed --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitDispersion.swift @@ -0,0 +1,11 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitDispersion : Dimension { + public static let partsPerMillion = UnitDispersion(symbol: "ppm", converter: UnitConverterLinear(coefficient: 1.0)) + + public override class func baseUnit() -> Dimension { partsPerMillion } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitDuration.swift b/Sources/SkipFoundation/Units/UnitDuration.swift new file mode 100644 index 0000000..e575fa9 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitDuration.swift @@ -0,0 +1,17 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitDuration : Dimension { + public static let hours = UnitDuration(symbol: "hr", converter: UnitConverterLinear(coefficient: 3600.0)) + public static let minutes = UnitDuration(symbol: "min", converter: UnitConverterLinear(coefficient: 60.0)) + public static let seconds = UnitDuration(symbol: "s", converter: UnitConverterLinear(coefficient: 1.0)) + public static let milliseconds = UnitDuration(symbol: "ms", converter: UnitConverterLinear(coefficient: 0.001)) + public static let microseconds = UnitDuration(symbol: "µs", converter: UnitConverterLinear(coefficient: 0.000001)) + public static let nanoseconds = UnitDuration(symbol: "ns", converter: UnitConverterLinear(coefficient: 1e-9)) + public static let picoseconds = UnitDuration(symbol: "ps", converter: UnitConverterLinear(coefficient: 1e-12)) + + public override class func baseUnit() -> Dimension { seconds } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitElectricCharge.swift b/Sources/SkipFoundation/Units/UnitElectricCharge.swift new file mode 100644 index 0000000..a7c0003 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitElectricCharge.swift @@ -0,0 +1,16 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitElectricCharge : Dimension { + public static let coulombs = UnitElectricCharge(symbol: "C", converter: UnitConverterLinear(coefficient: 1.0)) + public static let megaampereHours = UnitElectricCharge(symbol: "MAh", converter: UnitConverterLinear(coefficient: 3.6e9)) + public static let kiloampereHours = UnitElectricCharge(symbol: "kAh", converter: UnitConverterLinear(coefficient: 3.6e6)) + public static let ampereHours = UnitElectricCharge(symbol: "Ah", converter: UnitConverterLinear(coefficient: 3600.0)) + public static let milliampereHours = UnitElectricCharge(symbol: "mAh", converter: UnitConverterLinear(coefficient: 3.6)) + public static let microampereHours = UnitElectricCharge(symbol: "µAh", converter: UnitConverterLinear(coefficient: 0.0036)) + + public override class func baseUnit() -> Dimension { coulombs } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitElectricCurrent.swift b/Sources/SkipFoundation/Units/UnitElectricCurrent.swift new file mode 100644 index 0000000..a8ef1f9 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitElectricCurrent.swift @@ -0,0 +1,15 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitElectricCurrent : Dimension { + public static let megaamperes = UnitElectricCurrent(symbol: "MA", converter: UnitConverterLinear(coefficient: 1e6)) + public static let kiloamperes = UnitElectricCurrent(symbol: "kA", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let amperes = UnitElectricCurrent(symbol: "A", converter: UnitConverterLinear(coefficient: 1.0)) + public static let milliamperes = UnitElectricCurrent(symbol: "mA", converter: UnitConverterLinear(coefficient: 0.001)) + public static let microamperes = UnitElectricCurrent(symbol: "µA", converter: UnitConverterLinear(coefficient: 0.000001)) + + public override class func baseUnit() -> Dimension { amperes } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitElectricPotentialDifference.swift b/Sources/SkipFoundation/Units/UnitElectricPotentialDifference.swift new file mode 100644 index 0000000..0357903 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitElectricPotentialDifference.swift @@ -0,0 +1,15 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitElectricPotentialDifference : Dimension { + public static let megavolts = UnitElectricPotentialDifference(symbol: "MV", converter: UnitConverterLinear(coefficient: 1e6)) + public static let kilovolts = UnitElectricPotentialDifference(symbol: "kV", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let volts = UnitElectricPotentialDifference(symbol: "V", converter: UnitConverterLinear(coefficient: 1.0)) + public static let millivolts = UnitElectricPotentialDifference(symbol: "mV", converter: UnitConverterLinear(coefficient: 0.001)) + public static let microvolts = UnitElectricPotentialDifference(symbol: "µV", converter: UnitConverterLinear(coefficient: 0.000001)) + + public override class func baseUnit() -> Dimension { volts } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitElectricResistance.swift b/Sources/SkipFoundation/Units/UnitElectricResistance.swift new file mode 100644 index 0000000..3d52ad7 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitElectricResistance.swift @@ -0,0 +1,15 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitElectricResistance : Dimension { + public static let megaohms = UnitElectricResistance(symbol: "MΩ", converter: UnitConverterLinear(coefficient: 1e6)) + public static let kiloohms = UnitElectricResistance(symbol: "kΩ", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let ohms = UnitElectricResistance(symbol: "Ω", converter: UnitConverterLinear(coefficient: 1.0)) + public static let milliohms = UnitElectricResistance(symbol: "mΩ", converter: UnitConverterLinear(coefficient: 0.001)) + public static let microohms = UnitElectricResistance(symbol: "µΩ", converter: UnitConverterLinear(coefficient: 0.000001)) + + public override class func baseUnit() -> Dimension { ohms } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitEnergy.swift b/Sources/SkipFoundation/Units/UnitEnergy.swift new file mode 100644 index 0000000..0afc121 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitEnergy.swift @@ -0,0 +1,15 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitEnergy : Dimension { + public static let kilojoules = UnitEnergy(symbol: "kJ", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let joules = UnitEnergy(symbol: "J", converter: UnitConverterLinear(coefficient: 1.0)) + public static let kilocalories = UnitEnergy(symbol: "kCal", converter: UnitConverterLinear(coefficient: 4184.0)) + public static let calories = UnitEnergy(symbol: "cal", converter: UnitConverterLinear(coefficient: 4.184)) + public static let kilowattHours = UnitEnergy(symbol: "kWh", converter: UnitConverterLinear(coefficient: 3600000.0)) + + public override class func baseUnit() -> Dimension { joules } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitFrequency.swift b/Sources/SkipFoundation/Units/UnitFrequency.swift new file mode 100644 index 0000000..db3872e --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitFrequency.swift @@ -0,0 +1,19 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitFrequency : Dimension { + public static let terahertz = UnitFrequency(symbol: "THz", converter: UnitConverterLinear(coefficient: 1e12)) + public static let gigahertz = UnitFrequency(symbol: "GHz", converter: UnitConverterLinear(coefficient: 1e9)) + public static let megahertz = UnitFrequency(symbol: "MHz", converter: UnitConverterLinear(coefficient: 1e6)) + public static let kilohertz = UnitFrequency(symbol: "kHz", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let hertz = UnitFrequency(symbol: "Hz", converter: UnitConverterLinear(coefficient: 1.0)) + public static let millihertz = UnitFrequency(symbol: "mHz", converter: UnitConverterLinear(coefficient: 0.001)) + public static let microhertz = UnitFrequency(symbol: "µHz", converter: UnitConverterLinear(coefficient: 0.000001)) + public static let nanohertz = UnitFrequency(symbol: "nHz", converter: UnitConverterLinear(coefficient: 1e-9)) + public static let framesPerSecond = UnitFrequency(symbol: "fps", converter: UnitConverterLinear(coefficient: 1.0)) + + public override class func baseUnit() -> Dimension { hertz } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitFuelEfficiency.swift b/Sources/SkipFoundation/Units/UnitFuelEfficiency.swift new file mode 100644 index 0000000..90a05e4 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitFuelEfficiency.swift @@ -0,0 +1,13 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitFuelEfficiency : Dimension { + public static let litersPer100Kilometers = UnitFuelEfficiency(symbol: "L/100km", converter: UnitConverterLinear(coefficient: 1.0)) + public static let milesPerImperialGallon = UnitFuelEfficiency(symbol: "mpg", converter: UnitConverterReciprocal(reciprocal: 282.481)) + public static let milesPerGallon = UnitFuelEfficiency(symbol: "mpg", converter: UnitConverterReciprocal(reciprocal: 235.215)) + + public override class func baseUnit() -> Dimension { litersPer100Kilometers } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitIlluminance.swift b/Sources/SkipFoundation/Units/UnitIlluminance.swift new file mode 100644 index 0000000..e13d30f --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitIlluminance.swift @@ -0,0 +1,11 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitIlluminance : Dimension { + public static let lux = UnitIlluminance(symbol: "lx", converter: UnitConverterLinear(coefficient: 1.0)) + + public override class func baseUnit() -> Dimension { lux } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitInformationStorage.swift b/Sources/SkipFoundation/Units/UnitInformationStorage.swift new file mode 100644 index 0000000..b4564a4 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitInformationStorage.swift @@ -0,0 +1,56 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitInformationStorage : Dimension { + // Base unit + public static let bytes = UnitInformationStorage(symbol: "B", converter: UnitConverterLinear(coefficient: 1.0)) + + // Sub-byte + public static let bits = UnitInformationStorage(symbol: "bit", converter: UnitConverterLinear(coefficient: 0.125)) + public static let nibbles = UnitInformationStorage(symbol: "nibble", converter: UnitConverterLinear(coefficient: 0.5)) + + // Decimal bytes + public static let yottabytes = UnitInformationStorage(symbol: "YB", converter: UnitConverterLinear(coefficient: 1e24)) + public static let zettabytes = UnitInformationStorage(symbol: "ZB", converter: UnitConverterLinear(coefficient: 1e21)) + public static let exabytes = UnitInformationStorage(symbol: "EB", converter: UnitConverterLinear(coefficient: 1e18)) + public static let petabytes = UnitInformationStorage(symbol: "PB", converter: UnitConverterLinear(coefficient: 1e15)) + public static let terabytes = UnitInformationStorage(symbol: "TB", converter: UnitConverterLinear(coefficient: 1e12)) + public static let gigabytes = UnitInformationStorage(symbol: "GB", converter: UnitConverterLinear(coefficient: 1e9)) + public static let megabytes = UnitInformationStorage(symbol: "MB", converter: UnitConverterLinear(coefficient: 1e6)) + public static let kilobytes = UnitInformationStorage(symbol: "kB", converter: UnitConverterLinear(coefficient: 1000.0)) + + // Decimal bits + public static let yottabits = UnitInformationStorage(symbol: "Yb", converter: UnitConverterLinear(coefficient: 1.25e23)) + public static let zettabits = UnitInformationStorage(symbol: "Zb", converter: UnitConverterLinear(coefficient: 1.25e20)) + public static let exabits = UnitInformationStorage(symbol: "Eb", converter: UnitConverterLinear(coefficient: 1.25e17)) + public static let petabits = UnitInformationStorage(symbol: "Pb", converter: UnitConverterLinear(coefficient: 1.25e14)) + public static let terabits = UnitInformationStorage(symbol: "Tb", converter: UnitConverterLinear(coefficient: 1.25e11)) + public static let gigabits = UnitInformationStorage(symbol: "Gb", converter: UnitConverterLinear(coefficient: 1.25e8)) + public static let megabits = UnitInformationStorage(symbol: "Mb", converter: UnitConverterLinear(coefficient: 125000.0)) + public static let kilobits = UnitInformationStorage(symbol: "kb", converter: UnitConverterLinear(coefficient: 125.0)) + + // Binary bytes (1024-based) + public static let yobibytes = UnitInformationStorage(symbol: "YiB", converter: UnitConverterLinear(coefficient: 1208925819614629174706176.0)) + public static let zebibytes = UnitInformationStorage(symbol: "ZiB", converter: UnitConverterLinear(coefficient: 1180591620717411303424.0)) + public static let exbibytes = UnitInformationStorage(symbol: "EiB", converter: UnitConverterLinear(coefficient: 1152921504606846976.0)) + public static let pebibytes = UnitInformationStorage(symbol: "PiB", converter: UnitConverterLinear(coefficient: 1125899906842624.0)) + public static let tebibytes = UnitInformationStorage(symbol: "TiB", converter: UnitConverterLinear(coefficient: 1099511627776.0)) + public static let gibibytes = UnitInformationStorage(symbol: "GiB", converter: UnitConverterLinear(coefficient: 1073741824.0)) + public static let mebibytes = UnitInformationStorage(symbol: "MiB", converter: UnitConverterLinear(coefficient: 1048576.0)) + public static let kibibytes = UnitInformationStorage(symbol: "KiB", converter: UnitConverterLinear(coefficient: 1024.0)) + + // Binary bits (1024-based) + public static let yobibits = UnitInformationStorage(symbol: "Yib", converter: UnitConverterLinear(coefficient: 151115727451828646838272.0)) + public static let zebibits = UnitInformationStorage(symbol: "Zib", converter: UnitConverterLinear(coefficient: 147573952589676412928.0)) + public static let exbibits = UnitInformationStorage(symbol: "Eib", converter: UnitConverterLinear(coefficient: 144115188075855872.0)) + public static let pebibits = UnitInformationStorage(symbol: "Pib", converter: UnitConverterLinear(coefficient: 140737488355328.0)) + public static let tebibits = UnitInformationStorage(symbol: "Tib", converter: UnitConverterLinear(coefficient: 137438953472.0)) + public static let gibibits = UnitInformationStorage(symbol: "Gib", converter: UnitConverterLinear(coefficient: 134217728.0)) + public static let mebibits = UnitInformationStorage(symbol: "Mib", converter: UnitConverterLinear(coefficient: 131072.0)) + public static let kibibits = UnitInformationStorage(symbol: "Kib", converter: UnitConverterLinear(coefficient: 128.0)) + + public override class func baseUnit() -> Dimension { bytes } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitLength.swift b/Sources/SkipFoundation/Units/UnitLength.swift new file mode 100644 index 0000000..b38fcef --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitLength.swift @@ -0,0 +1,32 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitLength : Dimension { + public static let megameters = UnitLength(symbol: "Mm", converter: UnitConverterLinear(coefficient: 1000000.0)) + public static let kilometers = UnitLength(symbol: "km", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let hectometers = UnitLength(symbol: "hm", converter: UnitConverterLinear(coefficient: 100.0)) + public static let decameters = UnitLength(symbol: "dam", converter: UnitConverterLinear(coefficient: 10.0)) + public static let meters = UnitLength(symbol: "m", converter: UnitConverterLinear(coefficient: 1.0)) + public static let decimeters = UnitLength(symbol: "dm", converter: UnitConverterLinear(coefficient: 0.1)) + public static let centimeters = UnitLength(symbol: "cm", converter: UnitConverterLinear(coefficient: 0.01)) + public static let millimeters = UnitLength(symbol: "mm", converter: UnitConverterLinear(coefficient: 0.001)) + public static let micrometers = UnitLength(symbol: "µm", converter: UnitConverterLinear(coefficient: 0.000001)) + public static let nanometers = UnitLength(symbol: "nm", converter: UnitConverterLinear(coefficient: 1e-9)) + public static let picometers = UnitLength(symbol: "pm", converter: UnitConverterLinear(coefficient: 1e-12)) + public static let inches = UnitLength(symbol: "in", converter: UnitConverterLinear(coefficient: 0.0254)) + public static let feet = UnitLength(symbol: "ft", converter: UnitConverterLinear(coefficient: 0.3048)) + public static let yards = UnitLength(symbol: "yd", converter: UnitConverterLinear(coefficient: 0.9144)) + public static let miles = UnitLength(symbol: "mi", converter: UnitConverterLinear(coefficient: 1609.344)) + public static let scandinavianMiles = UnitLength(symbol: "smi", converter: UnitConverterLinear(coefficient: 10000.0)) + public static let lightyears = UnitLength(symbol: "ly", converter: UnitConverterLinear(coefficient: 9.4607304725808e15)) + public static let nauticalMiles = UnitLength(symbol: "NM", converter: UnitConverterLinear(coefficient: 1852.0)) + public static let fathoms = UnitLength(symbol: "ftm", converter: UnitConverterLinear(coefficient: 1.8288)) + public static let furlongs = UnitLength(symbol: "fur", converter: UnitConverterLinear(coefficient: 201.168)) + public static let astronomicalUnits = UnitLength(symbol: "ua", converter: UnitConverterLinear(coefficient: 1.495978707e11)) + public static let parsecs = UnitLength(symbol: "pc", converter: UnitConverterLinear(coefficient: 3.0856775814913673e16)) + + public override class func baseUnit() -> Dimension { meters } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitMass.swift b/Sources/SkipFoundation/Units/UnitMass.swift new file mode 100644 index 0000000..0cb04fd --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitMass.swift @@ -0,0 +1,26 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitMass : Dimension { + public static let kilograms = UnitMass(symbol: "kg", converter: UnitConverterLinear(coefficient: 1.0)) + public static let grams = UnitMass(symbol: "g", converter: UnitConverterLinear(coefficient: 0.001)) + public static let decigrams = UnitMass(symbol: "dg", converter: UnitConverterLinear(coefficient: 0.0001)) + public static let centigrams = UnitMass(symbol: "cg", converter: UnitConverterLinear(coefficient: 0.00001)) + public static let milligrams = UnitMass(symbol: "mg", converter: UnitConverterLinear(coefficient: 0.000001)) + public static let micrograms = UnitMass(symbol: "µg", converter: UnitConverterLinear(coefficient: 1e-9)) + public static let nanograms = UnitMass(symbol: "ng", converter: UnitConverterLinear(coefficient: 1e-12)) + public static let picograms = UnitMass(symbol: "pg", converter: UnitConverterLinear(coefficient: 1e-15)) + public static let ounces = UnitMass(symbol: "oz", converter: UnitConverterLinear(coefficient: 0.0283495)) + public static let pounds = UnitMass(symbol: "lb", converter: UnitConverterLinear(coefficient: 0.453592)) + public static let stones = UnitMass(symbol: "st", converter: UnitConverterLinear(coefficient: 6.35029)) + public static let metricTons = UnitMass(symbol: "t", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let shortTons = UnitMass(symbol: "ton", converter: UnitConverterLinear(coefficient: 907.185)) + public static let carats = UnitMass(symbol: "ct", converter: UnitConverterLinear(coefficient: 0.0002)) + public static let ouncesTroy = UnitMass(symbol: "oz t", converter: UnitConverterLinear(coefficient: 0.0311035)) + public static let slugs = UnitMass(symbol: "slug", converter: UnitConverterLinear(coefficient: 14.5939)) + + public override class func baseUnit() -> Dimension { kilograms } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitPower.swift b/Sources/SkipFoundation/Units/UnitPower.swift new file mode 100644 index 0000000..2076ea6 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitPower.swift @@ -0,0 +1,21 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitPower : Dimension { + public static let terawatts = UnitPower(symbol: "TW", converter: UnitConverterLinear(coefficient: 1e12)) + public static let gigawatts = UnitPower(symbol: "GW", converter: UnitConverterLinear(coefficient: 1e9)) + public static let megawatts = UnitPower(symbol: "MW", converter: UnitConverterLinear(coefficient: 1e6)) + public static let kilowatts = UnitPower(symbol: "kW", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let watts = UnitPower(symbol: "W", converter: UnitConverterLinear(coefficient: 1.0)) + public static let milliwatts = UnitPower(symbol: "mW", converter: UnitConverterLinear(coefficient: 0.001)) + public static let microwatts = UnitPower(symbol: "µW", converter: UnitConverterLinear(coefficient: 0.000001)) + public static let nanowatts = UnitPower(symbol: "nW", converter: UnitConverterLinear(coefficient: 1e-9)) + public static let picowatts = UnitPower(symbol: "pW", converter: UnitConverterLinear(coefficient: 1e-12)) + public static let femtowatts = UnitPower(symbol: "fW", converter: UnitConverterLinear(coefficient: 1e-15)) + public static let horsepower = UnitPower(symbol: "hp", converter: UnitConverterLinear(coefficient: 745.7)) + + public override class func baseUnit() -> Dimension { watts } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitPressure.swift b/Sources/SkipFoundation/Units/UnitPressure.swift new file mode 100644 index 0000000..5753e1a --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitPressure.swift @@ -0,0 +1,20 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitPressure : Dimension { + public static let newtonsPerMetersSquared = UnitPressure(symbol: "N/m²", converter: UnitConverterLinear(coefficient: 1.0)) + public static let gigapascals = UnitPressure(symbol: "GPa", converter: UnitConverterLinear(coefficient: 1e9)) + public static let megapascals = UnitPressure(symbol: "MPa", converter: UnitConverterLinear(coefficient: 1e6)) + public static let kilopascals = UnitPressure(symbol: "kPa", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let hectopascals = UnitPressure(symbol: "hPa", converter: UnitConverterLinear(coefficient: 100.0)) + public static let inchesOfMercury = UnitPressure(symbol: "inHg", converter: UnitConverterLinear(coefficient: 3386.39)) + public static let bars = UnitPressure(symbol: "bar", converter: UnitConverterLinear(coefficient: 100000.0)) + public static let millibars = UnitPressure(symbol: "mbar", converter: UnitConverterLinear(coefficient: 100.0)) + public static let millimetersOfMercury = UnitPressure(symbol: "mmHg", converter: UnitConverterLinear(coefficient: 133.322)) + public static let poundsForcePerSquareInch = UnitPressure(symbol: "psi", converter: UnitConverterLinear(coefficient: 6894.76)) + + public override class func baseUnit() -> Dimension { newtonsPerMetersSquared } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitSpeed.swift b/Sources/SkipFoundation/Units/UnitSpeed.swift new file mode 100644 index 0000000..0b95323 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitSpeed.swift @@ -0,0 +1,14 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitSpeed : Dimension { + public static let metersPerSecond = UnitSpeed(symbol: "m/s", converter: UnitConverterLinear(coefficient: 1.0)) + public static let kilometersPerHour = UnitSpeed(symbol: "km/h", converter: UnitConverterLinear(coefficient: 0.277778)) + public static let milesPerHour = UnitSpeed(symbol: "mph", converter: UnitConverterLinear(coefficient: 0.44704)) + public static let knots = UnitSpeed(symbol: "kn", converter: UnitConverterLinear(coefficient: 0.514444)) + + public override class func baseUnit() -> Dimension { metersPerSecond } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitTemperature.swift b/Sources/SkipFoundation/Units/UnitTemperature.swift new file mode 100644 index 0000000..0f8c594 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitTemperature.swift @@ -0,0 +1,13 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitTemperature : Dimension { + public static let kelvin = UnitTemperature(symbol: "K", converter: UnitConverterLinear(coefficient: 1.0, constant: 0.0)) + public static let celsius = UnitTemperature(symbol: "°C", converter: UnitConverterLinear(coefficient: 1.0, constant: 273.15)) + public static let fahrenheit = UnitTemperature(symbol: "°F", converter: UnitConverterLinear(coefficient: 5.0 / 9.0, constant: 255.37222222222428)) + + public override class func baseUnit() -> Dimension { kelvin } +} + +#endif diff --git a/Sources/SkipFoundation/Units/UnitVolume.swift b/Sources/SkipFoundation/Units/UnitVolume.swift new file mode 100644 index 0000000..ba993d6 --- /dev/null +++ b/Sources/SkipFoundation/Units/UnitVolume.swift @@ -0,0 +1,50 @@ +// Copyright 2023–2025 Skip +// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception +#if SKIP + +public class UnitVolume : Dimension { + // Metric + public static let megaliters = UnitVolume(symbol: "ML", converter: UnitConverterLinear(coefficient: 1e6)) + public static let kiloliters = UnitVolume(symbol: "kL", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let liters = UnitVolume(symbol: "L", converter: UnitConverterLinear(coefficient: 1.0)) + public static let deciliters = UnitVolume(symbol: "dL", converter: UnitConverterLinear(coefficient: 0.1)) + public static let centiliters = UnitVolume(symbol: "cL", converter: UnitConverterLinear(coefficient: 0.01)) + public static let milliliters = UnitVolume(symbol: "mL", converter: UnitConverterLinear(coefficient: 0.001)) + + // Cubic metric + public static let cubicKilometers = UnitVolume(symbol: "km³", converter: UnitConverterLinear(coefficient: 1e12)) + public static let cubicMeters = UnitVolume(symbol: "m³", converter: UnitConverterLinear(coefficient: 1000.0)) + public static let cubicDecimeters = UnitVolume(symbol: "dm³", converter: UnitConverterLinear(coefficient: 1.0)) + public static let cubicCentimeters = UnitVolume(symbol: "cm³", converter: UnitConverterLinear(coefficient: 0.001)) + public static let cubicMillimeters = UnitVolume(symbol: "mm³", converter: UnitConverterLinear(coefficient: 0.000001)) + + // Cubic imperial + public static let cubicInches = UnitVolume(symbol: "in³", converter: UnitConverterLinear(coefficient: 0.0163871)) + public static let cubicFeet = UnitVolume(symbol: "ft³", converter: UnitConverterLinear(coefficient: 28.3168)) + public static let cubicYards = UnitVolume(symbol: "yd³", converter: UnitConverterLinear(coefficient: 764.555)) + public static let cubicMiles = UnitVolume(symbol: "mi³", converter: UnitConverterLinear(coefficient: 4.168e12)) + + // US customary + public static let acreFeet = UnitVolume(symbol: "af", converter: UnitConverterLinear(coefficient: 1.233e6)) + public static let bushels = UnitVolume(symbol: "bsh", converter: UnitConverterLinear(coefficient: 35.2391)) + public static let teaspoons = UnitVolume(symbol: "tsp", converter: UnitConverterLinear(coefficient: 0.00492892)) + public static let tablespoons = UnitVolume(symbol: "tbsp", converter: UnitConverterLinear(coefficient: 0.0147868)) + public static let fluidOunces = UnitVolume(symbol: "fl oz", converter: UnitConverterLinear(coefficient: 0.0295735)) + public static let cups = UnitVolume(symbol: "cup", converter: UnitConverterLinear(coefficient: 0.24)) + public static let pints = UnitVolume(symbol: "pt", converter: UnitConverterLinear(coefficient: 0.473176)) + public static let quarts = UnitVolume(symbol: "qt", converter: UnitConverterLinear(coefficient: 0.946353)) + public static let gallons = UnitVolume(symbol: "gal", converter: UnitConverterLinear(coefficient: 3.78541)) + + // Imperial + public static let imperialTeaspoons = UnitVolume(symbol: "tsp", converter: UnitConverterLinear(coefficient: 0.00591939)) + public static let imperialTablespoons = UnitVolume(symbol: "tbsp", converter: UnitConverterLinear(coefficient: 0.0177582)) + public static let imperialFluidOunces = UnitVolume(symbol: "fl oz", converter: UnitConverterLinear(coefficient: 0.0284131)) + public static let imperialPints = UnitVolume(symbol: "pt", converter: UnitConverterLinear(coefficient: 0.568261)) + public static let imperialQuarts = UnitVolume(symbol: "qt", converter: UnitConverterLinear(coefficient: 1.13652)) + public static let imperialGallons = UnitVolume(symbol: "gal", converter: UnitConverterLinear(coefficient: 4.54609)) + public static let metricCups = UnitVolume(symbol: "metric cup", converter: UnitConverterLinear(coefficient: 0.25)) + + public override class func baseUnit() -> Dimension { liters } +} + +#endif diff --git a/Tests/SkipFoundationTests/Units/TestDimension.swift b/Tests/SkipFoundationTests/Units/TestDimension.swift index abd2f7b..8aac579 100644 --- a/Tests/SkipFoundationTests/Units/TestDimension.swift +++ b/Tests/SkipFoundationTests/Units/TestDimension.swift @@ -18,11 +18,7 @@ import XCTest class TestDimension: XCTestCase { func test_encodeDecode() { - #if SKIP - throw XCTSkip("TODO") - #else let original = Dimension(symbol: "symbol", converter: UnitConverterLinear(coefficient: 1.0)) - #endif // !SKIP } } diff --git a/Tests/SkipFoundationTests/Units/TestMeasurement.swift b/Tests/SkipFoundationTests/Units/TestMeasurement.swift index 2f08e72..61118ac 100644 --- a/Tests/SkipFoundationTests/Units/TestMeasurement.swift +++ b/Tests/SkipFoundationTests/Units/TestMeasurement.swift @@ -15,26 +15,9 @@ import XCTest // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if false && !DARWIN_COMPATIBILITY_TESTS // https://bugs.swift.org/browse/SR-10904 -class CustomUnit: Unit { - override required init(symbol: String) { - super.init(symbol: symbol) - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - public static let bugs = CustomUnit(symbol: "bug") - public static let features = CustomUnit(symbol: "feature") -} -#endif - class TestMeasurement: XCTestCase { + #if !SKIP func testHashing() { - #if SKIP - throw XCTSkip("TODO") - #else let lengths: [[Measurement]] = [ [ Measurement(value: 5, unit: UnitLength.kilometers), @@ -79,37 +62,56 @@ class TestMeasurement: XCTestCase { ] checkHashable(custom, equalityOracle: { $0 == $1 }) #endif - #endif // !SKIP } - #if !SKIP let fixtures = [ Fixtures.zeroMeasurement, Fixtures.lengthMeasurement, Fixtures.frequencyMeasurement, Fixtures.angleMeasurement, ] - #endif func testCodingRoundtrip() throws { - #if SKIP - throw XCTSkip("TODO") - #else for fixture in fixtures { try fixture.assertValueRoundtripsInCoder() } - #endif // !SKIP } - + func testLoadedValuesMatch() throws { - #if SKIP - throw XCTSkip("TODO") - #else for fixture in fixtures { // try fixture.assertLoadedValuesMatch() } - #endif // !SKIP } -} + #endif + + func testMeasurementCreation() { + let m = Measurement(value: 42.0, unit: UnitLength.meters) + XCTAssertEqual(m.value, 42.0) + XCTAssertEqual(m.unit.symbol, "m") + } + func testMeasurementConversion() { + let km = Measurement(value: 1.0, unit: UnitLength.kilometers) + let m = km.converted(to: UnitLength.meters) + XCTAssertEqual(m.value, 1000.0, accuracy: 0.001) + } + func testMeasurementEquality() { + let a = Measurement(value: 1000.0, unit: UnitLength.meters) + let b = Measurement(value: 1.0, unit: UnitLength.kilometers) + XCTAssertEqual(a, b) + } + + func testMeasurementComparison() { + let a = Measurement(value: 1.0, unit: UnitLength.kilometers) + let b = Measurement(value: 500.0, unit: UnitLength.meters) + XCTAssertTrue(a > b) + } + + func testMeasurementArithmetic() { + let a = Measurement(value: 2.0, unit: UnitMass.kilograms) + let b = Measurement(value: 500.0, unit: UnitMass.grams) + let sum = a.adding(b) + XCTAssertEqual(sum.converted(to: UnitMass.kilograms).value, 2.5, accuracy: 0.001) + } +} diff --git a/Tests/SkipFoundationTests/Units/TestUnit.swift b/Tests/SkipFoundationTests/Units/TestUnit.swift index 11567ad..519ef38 100644 --- a/Tests/SkipFoundationTests/Units/TestUnit.swift +++ b/Tests/SkipFoundationTests/Units/TestUnit.swift @@ -17,11 +17,11 @@ import XCTest class TestUnit: XCTestCase { + // Unit(symbol:) conflicts with kotlin.Unit, and this test uses generics + // that don't transpile cleanly. Run on native only. + #if !SKIP @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) func test_equality() { - #if SKIP - throw XCTSkip("TODO") - #else let s1 = "a" let s2 = "ab" @@ -97,9 +97,7 @@ class TestUnit: XCTestCase { testEquality(ofDimensionSubclass: UnitSpeed.self) testEquality(ofDimensionSubclass: UnitTemperature.self) testEquality(ofDimensionSubclass: UnitVolume.self) - #endif // !SKIP } + #endif } - - diff --git a/Tests/SkipFoundationTests/Units/TestUnitConverter.swift b/Tests/SkipFoundationTests/Units/TestUnitConverter.swift index 36e6c0c..f57269e 100644 --- a/Tests/SkipFoundationTests/Units/TestUnitConverter.swift +++ b/Tests/SkipFoundationTests/Units/TestUnitConverter.swift @@ -19,9 +19,6 @@ class TestUnitConverter: XCTestCase { func test_baseUnit() { - #if SKIP - throw XCTSkip("TODO") - #else XCTAssertEqual(UnitAcceleration.baseUnit().symbol, UnitAcceleration.metersPerSecondSquared.symbol) XCTAssertEqual(UnitAngle.baseUnit().symbol, @@ -64,25 +61,17 @@ class TestUnitConverter: XCTestCase { UnitTemperature.kelvin.symbol) XCTAssertEqual(UnitVolume.baseUnit().symbol, UnitVolume.liters.symbol) - #endif // !SKIP } func test_linearity() { - #if SKIP - throw XCTSkip("TODO") - #else let coefficient = 7.0 let baseUnitConverter = UnitConverterLinear(coefficient: coefficient) XCTAssertEqual(baseUnitConverter.value(fromBaseUnitValue: coefficient), 1.0) XCTAssertEqual(baseUnitConverter.baseUnitValue(fromValue: 1), coefficient) - #endif // !SKIP } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) func test_bijectivity() { - #if SKIP - throw XCTSkip("TODO") - #else let delta = 1e-9 let testIdentity: (Dimension) -> Double = { dimension in let converter = dimension.converter @@ -305,13 +294,9 @@ class TestUnitConverter: XCTestCase { XCTAssertEqual(testIdentity(UnitVolume.imperialQuarts), 1, accuracy: delta) XCTAssertEqual(testIdentity(UnitVolume.imperialGallons), 1, accuracy: delta) XCTAssertEqual(testIdentity(UnitVolume.metricCups), 1, accuracy: delta) - #endif // !SKIP } func test_equality() { - #if SKIP - throw XCTSkip("TODO") - #else let u1 = UnitConverterLinear(coefficient: 1, constant: 2) let u2 = UnitConverterLinear(coefficient: 1, constant: 2) XCTAssertEqual(u1, u2) @@ -326,7 +311,6 @@ class TestUnitConverter: XCTestCase { XCTAssertNotEqual(u4, u1) // Cannot test NSUnitConverterReciprocal due to no support for @testable import. - #endif // !SKIP } } diff --git a/Tests/SkipFoundationTests/Units/TestUnitInformationStorage.swift b/Tests/SkipFoundationTests/Units/TestUnitInformationStorage.swift index 8ecbe26..38fb48a 100644 --- a/Tests/SkipFoundationTests/Units/TestUnitInformationStorage.swift +++ b/Tests/SkipFoundationTests/Units/TestUnitInformationStorage.swift @@ -18,34 +18,28 @@ import XCTest class TestUnitInformationStorage: XCTestCase { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) func testUnitInformationStorage() { - #if SKIP - throw XCTSkip("TODO") - #else let bits = Measurement(value: 8, unit: UnitInformationStorage.bits) XCTAssertEqual( - bits.converted(to: .bytes).value, + bits.converted(to: UnitInformationStorage.bytes).value, 1, "Conversion from bits to bytes" ) XCTAssertEqual( - bits.converted(to: .nibbles).value, + bits.converted(to: UnitInformationStorage.nibbles).value, 2, "Conversion from bits to nibbles" ) XCTAssertEqual( - bits.converted(to: .yottabits).value, + bits.converted(to: UnitInformationStorage.yottabits).value, 8.0e-24, accuracy: 1.0e-27, "Conversion from bits to yottabits" ) XCTAssertEqual( - bits.converted(to: .gibibits).value, + bits.converted(to: UnitInformationStorage.gibibits).value, 7.450581e-09, accuracy: 1.0e-12, "Conversion from bits to gibibits" ) - #endif // !SKIP } } - - diff --git a/Tests/SkipFoundationTests/Units/TestUnitVolume.swift b/Tests/SkipFoundationTests/Units/TestUnitVolume.swift index 8864f0d..220c50d 100644 --- a/Tests/SkipFoundationTests/Units/TestUnitVolume.swift +++ b/Tests/SkipFoundationTests/Units/TestUnitVolume.swift @@ -17,9 +17,6 @@ import XCTest class TestUnitVolume: XCTestCase { func testMetricVolumeConversions() { - #if SKIP - throw XCTSkip("TODO") - #else let cubicKilometers = Measurement(value: 4, unit: UnitVolume.cubicKilometers) XCTAssertEqual(cubicKilometers, Measurement(value: 4e9, unit: UnitVolume.cubicMeters), "Conversion from cubicKilometers to cubicMeters") @@ -40,53 +37,42 @@ class TestUnitVolume: XCTestCase { XCTAssertEqual(liters, Measurement(value: 5000, unit: UnitVolume.milliliters), "Conversion from liters to milliliters") XCTAssertEqual(liters, Measurement(value: 5000, unit: UnitVolume.cubicCentimeters), "Conversion from liters to cubicCentimeters") XCTAssertEqual(liters, Measurement(value: 5e6, unit: UnitVolume.cubicMillimeters), "Conversion from liters to cubicMillimeters") - #endif // !SKIP } func testMetricToImperialVolumeConversion() { - #if SKIP - throw XCTSkip("TODO") - #else let liters = Measurement(value: 10, unit: UnitVolume.liters) - XCTAssertEqual(liters.converted(to: .cubicInches).value, 610.236, accuracy: 0.001, "Conversion from liters to cubicInches") - #endif // !SKIP + XCTAssertEqual(liters.converted(to: UnitVolume.cubicInches).value, 610.236, accuracy: 0.001, "Conversion from liters to cubicInches") } func testImperialVolumeConversions() { - #if SKIP - throw XCTSkip("TODO") - #else let cubicMiles = Measurement(value: 1, unit: UnitVolume.cubicMiles) - XCTAssertEqual(cubicMiles.converted(to: .cubicYards).value, 1760 * 1760 * 1760, accuracy: 1_000_000, "Conversion from cubicMiles to cubicYards") + XCTAssertEqual(cubicMiles.converted(to: UnitVolume.cubicYards).value, 1760.0 * 1760.0 * 1760.0, accuracy: 1_000_000, "Conversion from cubicMiles to cubicYards") let cubicYards = Measurement(value: 1, unit: UnitVolume.cubicYards) - XCTAssertEqual(cubicYards.converted(to: .cubicFeet).value, 27, accuracy: 0.001, "Conversion from cubicYards to cubicFeet") + XCTAssertEqual(cubicYards.converted(to: UnitVolume.cubicFeet).value, 27, accuracy: 0.001, "Conversion from cubicYards to cubicFeet") let cubicFeet = Measurement(value: 1, unit: UnitVolume.cubicFeet) - XCTAssertEqual(cubicFeet.converted(to: .cubicInches).value, 1728, accuracy: 0.01, "Conversion from cubicFeet to cubicInches") + XCTAssertEqual(cubicFeet.converted(to: UnitVolume.cubicInches).value, 1728, accuracy: 0.01, "Conversion from cubicFeet to cubicInches") let gallons = Measurement(value: 1, unit: UnitVolume.gallons) - XCTAssertEqual(gallons.converted(to: .quarts).value, 4, accuracy: 0.001, "Conversion from gallons to quarts") + XCTAssertEqual(gallons.converted(to: UnitVolume.quarts).value, 4, accuracy: 0.001, "Conversion from gallons to quarts") let quarts = Measurement(value: 1, unit: UnitVolume.quarts) - XCTAssertEqual(quarts.converted(to: .pints).value, 2, accuracy: 0.001, "Conversion from quarts to pints") + XCTAssertEqual(quarts.converted(to: UnitVolume.pints).value, 2, accuracy: 0.001, "Conversion from quarts to pints") let pints = Measurement(value: 1, unit: UnitVolume.pints) - XCTAssertEqual(pints.converted(to: .cups).value, 2, accuracy: 0.05, "Conversion from pints to cups") + XCTAssertEqual(pints.converted(to: UnitVolume.cups).value, 2, accuracy: 0.05, "Conversion from pints to cups") let cups = Measurement(value: 1, unit: UnitVolume.cups) - XCTAssertEqual(cups.converted(to: .fluidOunces).value, 8.12, accuracy: 0.01, "Conversion from cups to fluidOunces") + XCTAssertEqual(cups.converted(to: UnitVolume.fluidOunces).value, 8.12, accuracy: 0.01, "Conversion from cups to fluidOunces") let fluidOunces = Measurement(value: 1, unit: UnitVolume.fluidOunces) - XCTAssertEqual(fluidOunces.converted(to: .tablespoons).value, 2, accuracy: 0.001, "Conversion from fluidOunces to tablespoons") + XCTAssertEqual(fluidOunces.converted(to: UnitVolume.tablespoons).value, 2, accuracy: 0.001, "Conversion from fluidOunces to tablespoons") let tablespoons = Measurement(value: 1, unit: UnitVolume.tablespoons) - XCTAssertEqual(tablespoons.converted(to: .teaspoons).value, 3, accuracy: 0.001, "Conversion from tablespoons to teaspoons") + XCTAssertEqual(tablespoons.converted(to: UnitVolume.teaspoons).value, 3, accuracy: 0.001, "Conversion from tablespoons to teaspoons") let teaspoons = Measurement(value: 1, unit: UnitVolume.teaspoons) - XCTAssertEqual(teaspoons.converted(to: .cubicInches).value, 0.3, accuracy: 0.001, "Conversion from teaspoons to cubicInches") - #endif // !SKIP + XCTAssertEqual(teaspoons.converted(to: UnitVolume.cubicInches).value, 0.3, accuracy: 0.001, "Conversion from teaspoons to cubicInches") } } - -