Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9f49f51
Add backpressure counter for monitoring token queue load
chall37 Jan 20, 2026
8c6388c
Fix backpressure counter initialization and document high-priority by…
chall37 Jan 20, 2026
6168547
Add backpressure release handler and queue cleanup accounting
chall37 Jan 27, 2026
72180a1
Add FairnessScheduler with feature flag integration
chall37 Jan 27, 2026
4572c3d
Add feature flag gating tests and fix sessionId=0 bug
chall37 Jan 27, 2026
5e4bb75
Remove accidentally added utility script
chall37 Jan 27, 2026
386d8dd
Added thread access annotations.
chall37 Jan 28, 2026
d4e4e73
Add queue access annotations to new TokenExecutor vars.
chall37 Jan 28, 2026
4a26a13
Add queue annotations and preconditions to executeTurn/cleanupForUnre…
chall37 Jan 28, 2026
73f0397
Add dispatchPreconditions to FairnessScheduler internal methods.
chall37 Jan 28, 2026
66cae4c
Extract signalAndRelease() helper in TokenArray.
chall37 Jan 28, 2026
12b9af0
Move FairnessSchedulerExecutor conformance to extension.
chall37 Jan 29, 2026
8130b46
Cache useFairnessScheduler flag at TokenExecutorImpl init time.
chall37 Jan 29, 2026
dfcc58a
Replace executionScheduled flag with IdempotentOperationJoiner
chall37 Jan 29, 2026
a71a047
Rewrite executeTurn() to align with execute() patterns
chall37 Jan 29, 2026
3d40055
Add TokenExecutorDeferCompletionOrderingTests for high-priority task …
chall37 Jan 29, 2026
2660c64
Preserve tokens on unregister and move registration to setTerminalEna…
chall37 Jan 31, 2026
e37cb24
Fix deadlock in FairnessScheduler by using private Mutex instead of m…
chall37 Feb 1, 2026
89a1a2b
Add session restoration tests and fix cleanup tests for token preserv…
chall37 Feb 1, 2026
f39a058
Improve fairness scheduler test quality and reliability
chall37 Feb 1, 2026
5661fb8
Remove dead discardAllAndReturnCount() methods from TwoTierTokenQueue
chall37 Feb 1, 2026
e6e6c2f
Restore async dispatch for FairnessScheduler hot path to fix performa…
chall37 Feb 1, 2026
ddcb761
Increase FairnessScheduler token budget from 500 to 1000
chall37 Feb 1, 2026
dd960d1
Optimize FairnessScheduler callback path and skip legacy session trac…
chall37 Feb 3, 2026
6becd94
Add threading documentation to FairnessSchedulerExecutor protocol met…
chall37 Feb 3, 2026
0a6c41e
Set isRegistered flag on FairnessScheduler registration
chall37 Feb 3, 2026
7a6fac7
Add thread access documentation to TokenExecutorImpl member variables
chall37 Feb 3, 2026
6d0f5d2
Add missing VT100ScreenDelegate methods to FakeSession
chall37 Feb 3, 2026
688c0d1
Change dispatchPrecondition gate from DEBUG to ITERM_DEBUG
chall37 Feb 3, 2026
6a7ee3e
Merge upstream/master: resolve VT100ScreenTests conflict with correct…
chall37 Feb 3, 2026
883be29
Address PR #568 review feedback
chall37 Feb 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,541 changes: 1,541 additions & 0 deletions ModernTests/FairnessScheduler/FairnessSchedulerTests.swift

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// MockFairnessSchedulerExecutor.swift
// ModernTests
//
// Mock executor for testing FairnessScheduler in isolation.
// This simulates the TokenExecutor interface that FairnessScheduler will use.
//

import Foundation
@testable import iTerm2SharedARC

// TurnResult and FairnessSchedulerExecutor are defined in FairnessScheduler.swift

/// Mock implementation for testing FairnessScheduler without real TokenExecutor.
final class MockFairnessSchedulerExecutor: FairnessSchedulerExecutor {

// MARK: - Configuration

/// The result to return from executeTurn. Set this before each test scenario.
var turnResult: TurnResult = .completed

/// If set, executeTurn calls this instead of using turnResult.
/// Useful for dynamic behavior or async testing.
var executeTurnHandler: ((Int, @escaping (TurnResult) -> Void) -> Void)?

/// Delay before calling completion (simulates execution time)
var executionDelay: TimeInterval = 0

// MARK: - Call Tracking

struct ExecuteTurnCall: Equatable {
let tokenBudget: Int
let timestamp: Date

static func == (lhs: ExecuteTurnCall, rhs: ExecuteTurnCall) -> Bool {
return lhs.tokenBudget == rhs.tokenBudget
}
}

private(set) var executeTurnCalls: [ExecuteTurnCall] = []
private(set) var totalTokenBudgetConsumed: Int = 0

// MARK: - FairnessSchedulerExecutor

func executeTurn(tokenBudget: Int, completion: @escaping (TurnResult) -> Void) {
executeTurnCalls.append(ExecuteTurnCall(tokenBudget: tokenBudget, timestamp: Date()))
totalTokenBudgetConsumed += tokenBudget

if let handler = executeTurnHandler {
handler(tokenBudget, completion)
return
}

if executionDelay > 0 {
DispatchQueue.global().asyncAfter(deadline: .now() + executionDelay) { [turnResult] in
completion(turnResult)
}
} else {
completion(turnResult)
}
}

// MARK: - Test Helpers

func reset() {
turnResult = .completed
executeTurnHandler = nil
executionDelay = 0
executeTurnCalls = []
totalTokenBudgetConsumed = 0
}

/// Returns the number of times executeTurn was called
var executeTurnCallCount: Int {
return executeTurnCalls.count
}
}
25 changes: 25 additions & 0 deletions ModernTests/FairnessScheduler/Mocks/MockSideEffectPerformer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// MockSideEffectPerformer.swift
// ModernTests
//
// Mock implementation of VT100ScreenSideEffectPerforming for testing.
// Used to create VT100ScreenMutableState instances without a real PTYSession.
//

import Foundation
@testable import iTerm2SharedARC

/// Mock implementation of VT100ScreenSideEffectPerforming for testing VT100ScreenMutableState.
/// Returns nil for delegates which is acceptable for test scenarios (init/deinit don't use them).
@objc final class MockSideEffectPerformer: NSObject, VT100ScreenSideEffectPerforming {

// MARK: - VT100ScreenSideEffectPerforming

@objc func sideEffectPerformingScreenDelegate() -> (any VT100ScreenDelegate)! {
return nil
}

@objc func sideEffectPerformingIntervalTreeObserver() -> (any iTermIntervalTreeObserver)! {
return nil
}
}
170 changes: 170 additions & 0 deletions ModernTests/FairnessScheduler/Mocks/MockTokenExecutorDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//
// MockTokenExecutorDelegate.swift
// ModernTests
//
// Mock implementation of TokenExecutorDelegate for testing.
// Provides configurable behavior and call tracking for token execution tests.
//

import Foundation
@testable import iTerm2SharedARC

/// Mock implementation of TokenExecutorDelegate for testing.
final class MockTokenExecutorDelegate: NSObject, TokenExecutorDelegate {

// MARK: - Configuration

/// When true, tokenExecutorShouldQueueTokens() returns true (simulates paused/blocked state)
var shouldQueueTokens = false

/// When true, tokenExecutorShouldDiscard() returns true
var shouldDiscardTokens = false

/// Callback invoked when tokenExecutorWillExecuteTokens is called.
/// Use this to fulfill expectations in tests.
var onWillExecute: (() -> Void)?

// MARK: - Call Tracking

private let lock = NSLock()
private var _executedLengths: [(total: Int, excluding: Int, throughput: Int)] = []
private var _syncCount = 0
private var _willExecuteCount = 0
private var _handledFlags: [Int64] = []

var executedLengths: [(total: Int, excluding: Int, throughput: Int)] {
lock.lock()
defer { lock.unlock() }
return _executedLengths
}

var syncCount: Int {
lock.lock()
defer { lock.unlock() }
return _syncCount
}

var willExecuteCount: Int {
lock.lock()
defer { lock.unlock() }
return _willExecuteCount
}

var handledFlags: [Int64] {
lock.lock()
defer { lock.unlock() }
return _handledFlags
}

// MARK: - TokenExecutorDelegate

func tokenExecutorShouldQueueTokens() -> Bool {
return shouldQueueTokens
}

func tokenExecutorShouldDiscard(token: VT100Token, highPriority: Bool) -> Bool {
return shouldDiscardTokens
}

func tokenExecutorDidExecute(lengthTotal: Int, lengthExcludingInBandSignaling: Int, throughput: Int) {
lock.lock()
_executedLengths.append((lengthTotal, lengthExcludingInBandSignaling, throughput))
lock.unlock()
}

func tokenExecutorCursorCoordString() -> NSString {
return "(0,0)" as NSString
}

func tokenExecutorSync() {
lock.lock()
_syncCount += 1
lock.unlock()
}

func tokenExecutorHandleSideEffectFlags(_ flags: Int64) {
lock.lock()
_handledFlags.append(flags)
lock.unlock()
}

func tokenExecutorWillExecuteTokens() {
lock.lock()
_willExecuteCount += 1
lock.unlock()
onWillExecute?()
}

// MARK: - Test Helpers

func reset() {
lock.lock()
shouldQueueTokens = false
shouldDiscardTokens = false
_executedLengths = []
_syncCount = 0
_willExecuteCount = 0
_handledFlags = []
onWillExecute = nil
lock.unlock()
}
}

/// TokenExecutorDelegate that tracks execution order for ordering tests.
/// Provides callbacks when tokens are executed to verify priority ordering.
final class OrderTrackingTokenExecutorDelegate: NSObject, TokenExecutorDelegate {

private let lock = NSLock()
private var _willExecuteCount = 0
private var _totalExecutedLength = 0

/// Callback invoked with lengthTotal when tokenExecutorDidExecute is called
var onExecute: ((Int) -> Void)?

var willExecuteCount: Int {
lock.lock()
defer { lock.unlock() }
return _willExecuteCount
}

var totalExecutedLength: Int {
lock.lock()
defer { lock.unlock() }
return _totalExecutedLength
}

// MARK: - TokenExecutorDelegate

func tokenExecutorShouldQueueTokens() -> Bool {
return false // Allow execution
}

func tokenExecutorShouldDiscard(token: VT100Token, highPriority: Bool) -> Bool {
return false // Don't discard
}

func tokenExecutorDidExecute(lengthTotal: Int, lengthExcludingInBandSignaling: Int, throughput: Int) {
lock.lock()
_totalExecutedLength += lengthTotal
lock.unlock()
onExecute?(lengthTotal)
}

func tokenExecutorCursorCoordString() -> NSString {
return "(0,0)" as NSString
}

func tokenExecutorSync() {
// Not used in ordering tests
}

func tokenExecutorHandleSideEffectFlags(_ flags: Int64) {
// Not used in ordering tests
}

func tokenExecutorWillExecuteTokens() {
lock.lock()
_willExecuteCount += 1
lock.unlock()
}
}
Loading