mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
277 lines
8.3 KiB
Swift
277 lines
8.3 KiB
Swift
// RUN: %target-run-simple-swift(%import-libdispatch -parse-as-library)
|
|
|
|
// REQUIRES: concurrency
|
|
// REQUIRES: executable_test
|
|
|
|
// rdar://106849189 move-only types should be supported in freestanding mode
|
|
// UNSUPPORTED: freestanding
|
|
|
|
// UNSUPPORTED: back_deployment_runtime
|
|
// REQUIRES: concurrency_runtime
|
|
|
|
import StdlibUnittest
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
struct TickingClock: Clock {
|
|
struct Duration: DurationProtocol {
|
|
var ticks: Int
|
|
|
|
static func / (_ lhs: Self, _ rhs: Int) -> Self {
|
|
return Duration(ticks: lhs.ticks / rhs)
|
|
}
|
|
static func * (_ lhs: Self, rhs: Int) -> Self {
|
|
return Duration(ticks: lhs.ticks * rhs)
|
|
}
|
|
static func / (_ lhs: Self, _ rhs: Self) -> Double {
|
|
return Double(lhs.ticks) / Double(rhs.ticks)
|
|
}
|
|
static func < (_ lhs: Self, _ rhs: Self) -> Bool {
|
|
return lhs.ticks < rhs.ticks
|
|
}
|
|
static func + (_ lhs: Self, _ rhs: Self) -> Self {
|
|
return Duration(ticks: lhs.ticks + rhs.ticks)
|
|
}
|
|
static func - (_ lhs: Self, _ rhs: Self) -> Self {
|
|
return Duration(ticks: lhs.ticks - rhs.ticks)
|
|
}
|
|
|
|
static var zero: Self {
|
|
return Duration(ticks: 0)
|
|
}
|
|
}
|
|
struct Instant: InstantProtocol {
|
|
typealias Duration = TickingClock.Duration
|
|
var ticksFromStart: Int
|
|
|
|
func advanced(by duration: Duration) -> Self {
|
|
return Instant(ticksFromStart: self.ticksFromStart + duration.ticks)
|
|
}
|
|
func duration(to other: Self) -> Duration {
|
|
return Duration(ticks: other.ticksFromStart - self.ticksFromStart)
|
|
}
|
|
|
|
static func < (_ lhs: Self, _ rhs: Self) -> Bool {
|
|
return lhs.ticksFromStart < rhs.ticksFromStart
|
|
}
|
|
}
|
|
|
|
private var _now: Instant
|
|
var now: Instant { return _now }
|
|
var minimumResolution: Duration { return Duration(ticks: 1) }
|
|
var traits: ClockTraits { [.monotonic] }
|
|
|
|
init() {
|
|
_now = Instant(ticksFromStart: 0)
|
|
}
|
|
|
|
// These are a bit of a lie, since this clock is weird and doesn't
|
|
// actually tell the time; for the purposes of this test, we pretend
|
|
// that the ticks are 20ms.
|
|
func convert(from duration: Duration) -> Swift.Duration? {
|
|
return .seconds(Double(duration.ticks) / 50)
|
|
}
|
|
|
|
func convert(from duration: Swift.Duration) -> Duration? {
|
|
let (seconds, attoseconds) = duration.components
|
|
let extraTicks = attoseconds / 20_000_000_000_000_000
|
|
return Duration(ticks: Int(seconds * 50) + Int(extraTicks))
|
|
}
|
|
|
|
mutating func tick() {
|
|
_now.ticksFromStart += 1
|
|
}
|
|
|
|
func sleep(
|
|
until instant: Instant,
|
|
tolerance: Duration? = nil
|
|
) async throws {
|
|
// Do nothing
|
|
}
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
struct TockingClock: Clock {
|
|
struct Duration: DurationProtocol {
|
|
var tocks: Int
|
|
|
|
static func / (_ lhs: Self, _ rhs: Int) -> Self {
|
|
return Duration(tocks: lhs.tocks / rhs)
|
|
}
|
|
static func * (_ lhs: Self, rhs: Int) -> Self {
|
|
return Duration(tocks: lhs.tocks * rhs)
|
|
}
|
|
static func / (_ lhs: Self, _ rhs: Self) -> Double {
|
|
return Double(lhs.tocks) / Double(rhs.tocks)
|
|
}
|
|
static func < (_ lhs: Self, _ rhs: Self) -> Bool {
|
|
return lhs.tocks < rhs.tocks
|
|
}
|
|
static func + (_ lhs: Self, _ rhs: Self) -> Self {
|
|
return Duration(tocks: lhs.tocks + rhs.tocks)
|
|
}
|
|
static func - (_ lhs: Self, _ rhs: Self) -> Self {
|
|
return Duration(tocks: lhs.tocks - rhs.tocks)
|
|
}
|
|
|
|
static var zero: Self {
|
|
return Duration(tocks: 0)
|
|
}
|
|
}
|
|
struct Instant: InstantProtocol {
|
|
typealias Duration = TockingClock.Duration
|
|
var tocksFromStart: Int
|
|
|
|
func advanced(by duration: Duration) -> Self {
|
|
return Instant(tocksFromStart: self.tocksFromStart + duration.tocks)
|
|
}
|
|
func duration(to other: Self) -> Duration {
|
|
return Duration(tocks: other.tocksFromStart - self.tocksFromStart)
|
|
}
|
|
|
|
static func < (_ lhs: Self, _ rhs: Self) -> Bool {
|
|
return lhs.tocksFromStart < rhs.tocksFromStart
|
|
}
|
|
}
|
|
|
|
private var _now: Instant
|
|
var now: Instant { return _now }
|
|
var minimumResolution: Duration { return Duration(tocks: 1) }
|
|
var traits: ClockTraits { [.monotonic] }
|
|
|
|
init() {
|
|
_now = Instant(tocksFromStart: 1000)
|
|
}
|
|
|
|
// These are a bit of a lie, since this clock is weird and doesn't
|
|
// actually tell the time; for the purposes of this test, we pretend
|
|
// that the tocks are 10ms.
|
|
func convert(from duration: Duration) -> Swift.Duration? {
|
|
return .seconds(Double(duration.tocks) / 100)
|
|
}
|
|
|
|
func convert(from duration: Swift.Duration) -> Duration? {
|
|
let (seconds, attoseconds) = duration.components
|
|
let extraTocks = attoseconds / 10_000_000_000_000_000
|
|
return Duration(tocks: Int(seconds * 100) + Int(extraTocks))
|
|
}
|
|
|
|
mutating func tock() {
|
|
_now.tocksFromStart += 1
|
|
}
|
|
|
|
func sleep(
|
|
until instant: Instant,
|
|
tolerance: Duration? = nil
|
|
) async throws {
|
|
// Do nothing
|
|
}
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
@main struct Main {
|
|
static func main() {
|
|
let tests = TestSuite("clocks")
|
|
|
|
var clockA = TickingClock()
|
|
let clockB = TickingClock()
|
|
|
|
clockA.tick()
|
|
clockA.tick()
|
|
clockA.tick()
|
|
|
|
var clockC = TockingClock()
|
|
|
|
clockC.tock()
|
|
|
|
tests.test("Convert instants from one clock to another") {
|
|
let nowA = clockA.now
|
|
let nowB = clockB.now
|
|
|
|
expectEqual(nowA.ticksFromStart, 3)
|
|
expectEqual(nowB.ticksFromStart, 0)
|
|
|
|
let futureA = nowA.advanced(by: TickingClock.Duration(ticks: 23))
|
|
let futureB = nowB.advanced(by: TickingClock.Duration(ticks: 42))
|
|
|
|
expectEqual(futureA.ticksFromStart, 26)
|
|
expectEqual(futureB.ticksFromStart, 42)
|
|
|
|
let futureAinB = clockB.convert(instant: futureA, from: clockA)!
|
|
let futureBinA = clockA.convert(instant: futureB, from: clockB)!
|
|
|
|
expectEqual(futureAinB.ticksFromStart, 23)
|
|
expectEqual(futureBinA.ticksFromStart, 45)
|
|
|
|
let futureAinBinA = clockA.convert(instant: futureAinB, from: clockB)!
|
|
let futureBinAinB = clockB.convert(instant: futureBinA, from: clockA)!
|
|
|
|
expectEqual(futureAinBinA.ticksFromStart, futureA.ticksFromStart)
|
|
expectEqual(futureBinAinB.ticksFromStart, futureB.ticksFromStart)
|
|
}
|
|
|
|
tests.test("Convert instants between clocks with different representations") {
|
|
let nowA = clockA.now
|
|
let nowC = clockC.now
|
|
|
|
expectEqual(nowA.ticksFromStart, 3)
|
|
expectEqual(nowC.tocksFromStart, 1001)
|
|
|
|
let futureA = nowA.advanced(by: TickingClock.Duration(ticks: 23))
|
|
let futureC = nowC.advanced(by: TockingClock.Duration(tocks: 42))
|
|
|
|
expectEqual(futureA.ticksFromStart, 26)
|
|
expectEqual(futureC.tocksFromStart, 1043)
|
|
|
|
let futureAinC = clockC.convert(instant: futureA, from: clockA)!
|
|
let futureCinA = clockA.convert(instant: futureC, from: clockC)!
|
|
|
|
expectEqual(futureAinC.tocksFromStart, 1047)
|
|
expectEqual(futureCinA.ticksFromStart, 24)
|
|
|
|
let futureAinCinA = clockA.convert(instant: futureAinC, from: clockC)!
|
|
let futureCinAinC = clockC.convert(instant: futureCinA, from: clockA)!
|
|
|
|
expectEqual(futureAinCinA.ticksFromStart, futureA.ticksFromStart)
|
|
expectEqual(futureCinAinC.tocksFromStart, futureC.tocksFromStart)
|
|
}
|
|
|
|
tests.test("Convert instants between continuous and suspending clocks") {
|
|
let continuous = ContinuousClock()
|
|
let suspending = SuspendingClock()
|
|
|
|
let nowC = continuous.now
|
|
let nowS = suspending.now
|
|
|
|
let futureC = nowC.advanced(by: .seconds(5.3))
|
|
let futureS = nowS.advanced(by: .seconds(4.2))
|
|
|
|
let futureCinS = suspending.convert(instant: futureC,
|
|
from: .continuous)!
|
|
let futureSinC = continuous.convert(instant: futureS,
|
|
from: .suspending)!
|
|
|
|
let futureCinSinC = continuous.convert(instant: futureCinS,
|
|
from: .suspending)!
|
|
let futureSinCinS = suspending.convert(instant: futureSinC,
|
|
from: .continuous)!
|
|
|
|
// These clocks may not be exact, so allow differences of up to 50ms
|
|
var delta1 = futureCinSinC - futureC
|
|
var delta2 = futureSinCinS - futureS
|
|
|
|
// Duration is not SignedNumeric, so we have to do things this way
|
|
if delta1 < .zero {
|
|
delta1 = .zero - delta1
|
|
}
|
|
if delta2 < .zero {
|
|
delta2 = .zero - delta2
|
|
}
|
|
|
|
expectLT(delta1, .milliseconds(50))
|
|
expectLT(delta2, .milliseconds(50))
|
|
}
|
|
|
|
runAllTests()
|
|
}
|
|
}
|