mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-11-03 14:17:19 +01:00
Ghostgram for iOS, rebuilt from scratch on Telegram iOS 13.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
412 lines
15 KiB
Swift
412 lines
15 KiB
Swift
import XCTest
|
|
import Foundation
|
|
#if SSK_LEGACY
|
|
@testable import SwiftSignalKitLegacy
|
|
fileprivate typealias CoreSignalKitTimer = SwiftSignalKitLegacy.Timer
|
|
#else
|
|
@testable import SwiftSignalKit2
|
|
fileprivate typealias CoreSignalKitTimer = SwiftSignalKit2.Timer
|
|
#endif
|
|
|
|
final class CoreThreadPoolTests: XCTestCase {
|
|
private static let pool = ThreadPool(threadCount: 4, threadPriority: 0.5)
|
|
|
|
func testTasksOnOneThreadPoolQueueExecuteSeriallyInOrder() {
|
|
let queue = CoreThreadPoolTests.pool.nextQueue()
|
|
let log = CoreEventLog()
|
|
let inFlight = CoreCounter()
|
|
let overlaps = CoreCounter()
|
|
let done = self.expectation(description: "all tasks")
|
|
let count = 60
|
|
for i in 0 ..< count {
|
|
queue.addTask(ThreadPoolTask { _ in
|
|
if inFlight.increment() > 1 {
|
|
overlaps.increment()
|
|
}
|
|
if i % 10 == 0 {
|
|
usleep(1000)
|
|
}
|
|
log.append("\(i)")
|
|
inFlight.decrement()
|
|
if i == count - 1 {
|
|
done.fulfill()
|
|
}
|
|
})
|
|
}
|
|
self.wait(for: [done], timeout: 5.0)
|
|
XCTAssertEqual(log.events, (0 ..< count).map { "\($0)" })
|
|
XCTAssertEqual(overlaps.value, 0)
|
|
}
|
|
|
|
func testTasksOnDifferentQueuesCanRunConcurrently() {
|
|
let first = CoreThreadPoolTests.pool.nextQueue()
|
|
let second = CoreThreadPoolTests.pool.nextQueue()
|
|
let firstStarted = DispatchSemaphore(value: 0)
|
|
let secondStarted = DispatchSemaphore(value: 0)
|
|
let done = self.expectation(description: "both")
|
|
done.expectedFulfillmentCount = 2
|
|
let observed = CoreEventLog()
|
|
first.addTask(ThreadPoolTask { _ in
|
|
firstStarted.signal()
|
|
let result = secondStarted.wait(timeout: .now() + 3.0)
|
|
observed.append("first saw second: \(result == .success)")
|
|
done.fulfill()
|
|
})
|
|
second.addTask(ThreadPoolTask { _ in
|
|
secondStarted.signal()
|
|
let result = firstStarted.wait(timeout: .now() + 3.0)
|
|
observed.append("second saw first: \(result == .success)")
|
|
done.fulfill()
|
|
})
|
|
self.wait(for: [done], timeout: 5.0)
|
|
XCTAssertEqual(Set(observed.events), Set(["first saw second: true", "second saw first: true"]))
|
|
}
|
|
|
|
func testCancelledTaskIsSkippedWhenCancelledBeforeExecution() {
|
|
let queue = CoreThreadPoolTests.pool.nextQueue()
|
|
let log = CoreEventLog()
|
|
let gate = DispatchSemaphore(value: 0)
|
|
let done = self.expectation(description: "third")
|
|
queue.addTask(ThreadPoolTask { _ in
|
|
_ = gate.wait(timeout: .now() + 5.0)
|
|
log.append("first")
|
|
})
|
|
let cancelled = ThreadPoolTask { _ in
|
|
log.append("cancelled")
|
|
}
|
|
queue.addTask(cancelled)
|
|
cancelled.cancel()
|
|
queue.addTask(ThreadPoolTask { _ in
|
|
log.append("third")
|
|
done.fulfill()
|
|
})
|
|
gate.signal()
|
|
self.wait(for: [done], timeout: 5.0)
|
|
XCTAssertEqual(log.events, ["first", "third"])
|
|
}
|
|
|
|
func testCancelDuringExecutionIsVisibleThroughState() {
|
|
let started = DispatchSemaphore(value: 0)
|
|
let cancelledSignal = DispatchSemaphore(value: 0)
|
|
let done = self.expectation(description: "task")
|
|
let observed = CoreEventLog()
|
|
let task = ThreadPoolTask { state in
|
|
observed.append("before \(state.cancelled.with { $0 })")
|
|
started.signal()
|
|
_ = cancelledSignal.wait(timeout: .now() + 5.0)
|
|
observed.append("after \(state.cancelled.with { $0 })")
|
|
done.fulfill()
|
|
}
|
|
CoreThreadPoolTests.pool.addTask(task)
|
|
XCTAssertEqual(started.wait(timeout: .now() + 5.0), .success)
|
|
task.cancel()
|
|
cancelledSignal.signal()
|
|
self.wait(for: [done], timeout: 5.0)
|
|
XCTAssertEqual(observed.events, ["before false", "after true"])
|
|
}
|
|
|
|
func testIsCurrentThreadInPool() {
|
|
let pool = CoreThreadPoolTests.pool
|
|
XCTAssertFalse(pool.isCurrentThreadInPool())
|
|
let done = self.expectation(description: "task")
|
|
var inside: Bool?
|
|
pool.addTask(ThreadPoolTask { _ in
|
|
inside = pool.isCurrentThreadInPool()
|
|
done.fulfill()
|
|
})
|
|
self.wait(for: [done], timeout: 5.0)
|
|
XCTAssertEqual(inside, true)
|
|
let otherDone = self.expectation(description: "dispatch")
|
|
var onDispatch: Bool?
|
|
DispatchQueue.global().async {
|
|
onDispatch = pool.isCurrentThreadInPool()
|
|
otherDone.fulfill()
|
|
}
|
|
self.wait(for: [otherDone], timeout: 5.0)
|
|
XCTAssertEqual(onDispatch, false)
|
|
}
|
|
|
|
func testThreadPoolTaskExecuteRunsSynchronouslyUnlessCancelled() {
|
|
let log = CoreEventLog()
|
|
let task = ThreadPoolTask { state in
|
|
log.append("run cancelled=\(state.cancelled.with { $0 })")
|
|
}
|
|
task.execute()
|
|
task.execute()
|
|
task.cancel()
|
|
task.execute()
|
|
XCTAssertEqual(log.events, ["run cancelled=false", "run cancelled=false"])
|
|
}
|
|
|
|
func testThreadPoolTaskCancelFromInsideActionIsObserved() {
|
|
let log = CoreEventLog()
|
|
var taskRef: ThreadPoolTask?
|
|
taskRef = ThreadPoolTask { state in
|
|
log.append("before \(state.cancelled.with { $0 })")
|
|
taskRef?.cancel()
|
|
log.append("after \(state.cancelled.with { $0 })")
|
|
}
|
|
taskRef?.execute()
|
|
taskRef?.execute()
|
|
XCTAssertEqual(log.events, ["before false", "after true"])
|
|
taskRef = nil
|
|
}
|
|
|
|
func testThreadPoolQueueEqualityIsIdentity() {
|
|
let a = CoreThreadPoolTests.pool.nextQueue()
|
|
let b = CoreThreadPoolTests.pool.nextQueue()
|
|
XCTAssertTrue(a == a)
|
|
XCTAssertFalse(a == b)
|
|
XCTAssertTrue(ThreadPoolQueue(threadPool: CoreThreadPoolTests.pool) != a)
|
|
}
|
|
|
|
func testThreadPoolAddTaskRunsOnPoolThread() {
|
|
let done = self.expectation(description: "task")
|
|
done.expectedFulfillmentCount = 10
|
|
let inPool = CoreCounter()
|
|
for _ in 0 ..< 10 {
|
|
CoreThreadPoolTests.pool.addTask(ThreadPoolTask { _ in
|
|
if CoreThreadPoolTests.pool.isCurrentThreadInPool() {
|
|
inPool.increment()
|
|
}
|
|
done.fulfill()
|
|
})
|
|
}
|
|
self.wait(for: [done], timeout: 5.0)
|
|
XCTAssertEqual(inPool.value, 10)
|
|
}
|
|
|
|
func testTaskAddedFromInsideRunningTaskOnSameQueueRunsAfterIt() {
|
|
let queue = CoreThreadPoolTests.pool.nextQueue()
|
|
let log = CoreEventLog()
|
|
let done = self.expectation(description: "nested")
|
|
queue.addTask(ThreadPoolTask { _ in
|
|
queue.addTask(ThreadPoolTask { _ in
|
|
log.append("nested")
|
|
done.fulfill()
|
|
})
|
|
usleep(2000)
|
|
log.append("outer end")
|
|
})
|
|
self.wait(for: [done], timeout: 5.0)
|
|
XCTAssertEqual(log.events, ["outer end", "nested"])
|
|
}
|
|
}
|
|
|
|
final class CoreTimerTests: XCTestCase {
|
|
func testOneShotTimerFiresOnceOnItsQueue() {
|
|
let queue = Queue(name: "CoreTimerTests.oneShot")
|
|
let fired = CoreCounter()
|
|
let onQueue = CoreFlag()
|
|
let firstFire = self.expectation(description: "fired")
|
|
let timer = CoreSignalKitTimer(timeout: 0.02, repeat: false, completion: {
|
|
onQueue.set(queue.isCurrent())
|
|
if fired.increment() == 1 {
|
|
firstFire.fulfill()
|
|
}
|
|
}, queue: queue)
|
|
timer.start()
|
|
self.wait(for: [firstFire], timeout: 5.0)
|
|
let settle = self.expectation(description: "settle")
|
|
queue.after(0.1) {
|
|
settle.fulfill()
|
|
}
|
|
self.wait(for: [settle], timeout: 5.0)
|
|
XCTAssertEqual(fired.value, 1)
|
|
XCTAssertEqual(onQueue.value, true)
|
|
withExtendedLifetime(timer) {}
|
|
}
|
|
|
|
func testOneShotTimerDoesNotFireBeforeTimeout() {
|
|
let queue = Queue(name: "CoreTimerTests.delay")
|
|
let start = DispatchTime.now()
|
|
var elapsed: Double = 0.0
|
|
let fired = self.expectation(description: "fired")
|
|
let timer = CoreSignalKitTimer(timeout: 0.05, repeat: false, completion: {
|
|
elapsed = Double(DispatchTime.now().uptimeNanoseconds - start.uptimeNanoseconds) / 1_000_000_000.0
|
|
fired.fulfill()
|
|
}, queue: queue)
|
|
timer.start()
|
|
self.wait(for: [fired], timeout: 5.0)
|
|
XCTAssertGreaterThanOrEqual(elapsed, 0.049)
|
|
withExtendedLifetime(timer) {}
|
|
}
|
|
|
|
func testRepeatingTimerFiresUntilInvalidated() {
|
|
let queue = Queue(name: "CoreTimerTests.repeat")
|
|
let fired = CoreCounter()
|
|
let threeFires = self.expectation(description: "three fires")
|
|
threeFires.expectedFulfillmentCount = 3
|
|
let timer = CoreSignalKitTimer(timeout: 0.01, repeat: true, completion: { timer in
|
|
let count = fired.increment()
|
|
if count <= 3 {
|
|
threeFires.fulfill()
|
|
}
|
|
if count == 3 {
|
|
timer.invalidate()
|
|
}
|
|
}, queue: queue)
|
|
timer.start()
|
|
self.wait(for: [threeFires], timeout: 5.0)
|
|
let settle = self.expectation(description: "settle")
|
|
queue.after(0.08) {
|
|
settle.fulfill()
|
|
}
|
|
self.wait(for: [settle], timeout: 5.0)
|
|
XCTAssertEqual(fired.value, 3)
|
|
withExtendedLifetime(timer) {}
|
|
}
|
|
|
|
func testInvalidateBeforeFirePreventsFiring() {
|
|
let queue = Queue(name: "CoreTimerTests.invalidate")
|
|
let fired = CoreCounter()
|
|
let timer = CoreSignalKitTimer(timeout: 0.03, repeat: false, completion: {
|
|
fired.increment()
|
|
}, queue: queue)
|
|
timer.start()
|
|
timer.invalidate()
|
|
timer.invalidate()
|
|
let settle = self.expectation(description: "settle")
|
|
queue.after(0.12) {
|
|
settle.fulfill()
|
|
}
|
|
self.wait(for: [settle], timeout: 5.0)
|
|
XCTAssertEqual(fired.value, 0)
|
|
withExtendedLifetime(timer) {}
|
|
}
|
|
|
|
func testInvalidateRepeatingTimerFromOutsideStopsIt() {
|
|
let queue = Queue(name: "CoreTimerTests.invalidateRepeat")
|
|
let fired = CoreCounter()
|
|
let firstFire = self.expectation(description: "first")
|
|
let timer = CoreSignalKitTimer(timeout: 0.01, repeat: true, completion: {
|
|
if fired.increment() == 1 {
|
|
firstFire.fulfill()
|
|
}
|
|
}, queue: queue)
|
|
timer.start()
|
|
self.wait(for: [firstFire], timeout: 5.0)
|
|
queue.sync {
|
|
timer.invalidate()
|
|
}
|
|
let countAtInvalidate = fired.value
|
|
let settle = self.expectation(description: "settle")
|
|
queue.after(0.08) {
|
|
settle.fulfill()
|
|
}
|
|
self.wait(for: [settle], timeout: 5.0)
|
|
XCTAssertEqual(fired.value, countAtInvalidate)
|
|
withExtendedLifetime(timer) {}
|
|
}
|
|
|
|
func testInvalidateWithoutStartIsNoop() {
|
|
let timer = CoreSignalKitTimer(timeout: 0.01, repeat: false, completion: {
|
|
}, queue: Queue.mainQueue())
|
|
timer.invalidate()
|
|
}
|
|
|
|
func testDeinitInvalidatesTimer() {
|
|
let queue = Queue(name: "CoreTimerTests.deinit")
|
|
let fired = CoreCounter()
|
|
weak var weakTimer: CoreSignalKitTimer?
|
|
do {
|
|
let timer = CoreSignalKitTimer(timeout: 0.03, repeat: true, completion: {
|
|
fired.increment()
|
|
}, queue: queue)
|
|
weakTimer = timer
|
|
timer.start()
|
|
}
|
|
XCTAssertNil(weakTimer)
|
|
let settle = self.expectation(description: "settle")
|
|
queue.after(0.12) {
|
|
settle.fulfill()
|
|
}
|
|
self.wait(for: [settle], timeout: 5.0)
|
|
XCTAssertEqual(fired.value, 0)
|
|
}
|
|
|
|
func testRunningTimerDoesNotRetainItself() {
|
|
let queue = Queue(name: "CoreTimerTests.retain")
|
|
weak var weakTimer: CoreSignalKitTimer?
|
|
var timer: CoreSignalKitTimer? = CoreSignalKitTimer(timeout: 10.0, repeat: false, completion: {
|
|
}, queue: queue)
|
|
weakTimer = timer
|
|
timer?.start()
|
|
XCTAssertNotNil(weakTimer)
|
|
timer = nil
|
|
XCTAssertNil(weakTimer)
|
|
}
|
|
|
|
func testCompletionReceivesTheTimer() {
|
|
let queue = Queue(name: "CoreTimerTests.argument")
|
|
let fired = self.expectation(description: "fired")
|
|
var received: CoreSignalKitTimer?
|
|
let timer = CoreSignalKitTimer(timeout: 0.01, repeat: false, completion: { timer in
|
|
received = timer
|
|
fired.fulfill()
|
|
}, queue: queue)
|
|
timer.start()
|
|
self.wait(for: [fired], timeout: 5.0)
|
|
XCTAssertTrue(received === timer)
|
|
received = nil
|
|
}
|
|
|
|
func testTimerOnMainQueueFiresOnMainThread() {
|
|
let fired = self.expectation(description: "fired")
|
|
var onMain: Bool?
|
|
let timer = CoreSignalKitTimer(timeout: 0.01, repeat: false, completion: {
|
|
onMain = Thread.isMainThread
|
|
fired.fulfill()
|
|
}, queue: Queue.mainQueue())
|
|
timer.start()
|
|
self.wait(for: [fired], timeout: 5.0)
|
|
XCTAssertEqual(onMain, true)
|
|
withExtendedLifetime(timer) {}
|
|
}
|
|
|
|
func testTimerCanBeRestartedAfterFiring() {
|
|
let queue = Queue(name: "CoreTimerTests.restart")
|
|
let fired = CoreCounter()
|
|
let first = self.expectation(description: "first")
|
|
let second = self.expectation(description: "second")
|
|
let timer = CoreSignalKitTimer(timeout: 0.01, repeat: false, completion: {
|
|
let count = fired.increment()
|
|
if count == 1 {
|
|
first.fulfill()
|
|
} else if count == 2 {
|
|
second.fulfill()
|
|
}
|
|
}, queue: queue)
|
|
timer.start()
|
|
self.wait(for: [first], timeout: 5.0)
|
|
queue.sync {
|
|
}
|
|
timer.start()
|
|
self.wait(for: [second], timeout: 5.0)
|
|
XCTAssertEqual(fired.value, 2)
|
|
withExtendedLifetime(timer) {}
|
|
}
|
|
|
|
func testTimerCanBeRestartedAfterInvalidate() {
|
|
let queue = Queue(name: "CoreTimerTests.restartInvalidated")
|
|
let fired = CoreCounter()
|
|
let firedOnce = self.expectation(description: "fired")
|
|
let timer = CoreSignalKitTimer(timeout: 0.02, repeat: false, completion: {
|
|
if fired.increment() == 1 {
|
|
firedOnce.fulfill()
|
|
}
|
|
}, queue: queue)
|
|
timer.start()
|
|
timer.invalidate()
|
|
timer.start()
|
|
self.wait(for: [firedOnce], timeout: 5.0)
|
|
let settle = self.expectation(description: "settle")
|
|
queue.after(0.08) {
|
|
settle.fulfill()
|
|
}
|
|
self.wait(for: [settle], timeout: 5.0)
|
|
XCTAssertEqual(fired.value, 1)
|
|
withExtendedLifetime(timer) {}
|
|
}
|
|
}
|