Skip to content

Split SendspinKit control and data planes - #35

Merged
teancom merged 1 commit into
mainfrom
control-data-plane-split
Jun 14, 2026
Merged

Split SendspinKit control and data planes#35
teancom merged 1 commit into
mainfrom
control-data-plane-split

Conversation

@teancom

@teancom teancom commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Split the protocol client into a thin @mainactor facade and an off-MainActor connection actor, so all heavy audio and control work runs off the main actor while @observable state still updates on the main actor for SwiftUI.

Architecture (one-way dependency: facade -> connection -> engine):

  • SendspinClient (@mainactor @observable): thin facade holding public API, observable state, and the public events stream. Owns nothing audio-related.
  • SendspinConnection (actor): owns the transport, ordered message loop, protocol-intent gates, clock sync, and the AudioEngine. Holds no facade reference and imports nothing @MainActor-isolated (proven at compile time). Single writer of session state.
  • AudioEngine (actor): owns decode/schedule/output/sync-telemetry and the seamless-format state machine. No @mainactor anywhere.

Control plane: connection emits ConnectionEvent on a control AsyncStream; the facade drains it on MainActor, applies @observable state, re-emits public ClientEvent (single public emission point).

Data plane: audio/artwork/visualizer bytes bypass the facade and are yielded directly to the public continuation off-main via SessionValidityToken, an atomic check-and-yield guard for stale binary events from retired connections.

Lifetime is managed by owned objects, not generation counters: a supervisor task, run-once teardown, an identity guard, and SessionValidityToken. Reconnect builds a new connection+engine+token; shutdown() invalidates the old token so its in-flight binary events are silently dropped.

Transport hardening:

  • NWWebSocketTransport gains outbound client-initiated dial (ws:// / wss://) in addition to inbound-accepted connections.
  • Per-send deadline via nonisolated timedSend racing NWConnection.send against a timeout; first contender wins, losers are no-ops. sendTimeout is init-injectable (5s default) for testability.
  • New SendCompletion type coordinates the race under OSAllocatedUnfairLock, resuming the continuation outside the critical section (reentrancy-safe).
  • New TransportError.sendTimedOut distinguishes a wedged connection.
  • FrameInbox for ordered inbound frame handling, made lifecycle-finished.
  • WatermarkedSink onWarning now captured under lock and invoked after release (reentrancy-safe).

Models:

  • Splits the monolithic SendspinMessage into per-domain files (Hello, ClientState, ServerState, Stream, Time, Command, Goodbye).
  • Adds static typeString to every concrete message with let type derivation, preserving synthesized Codable.

Connection-owned audio state:

  • Tightens audio state ownership to the connection/engine; seamless-format classification is synchronous via announcedPlayerFormat. EngineReport carries full target operational state (bidirectional into/out of .error/.synchronized).

Test infrastructure and coverage:

  • New TestBox (generic isolation-safe single-value box) and AsyncTestDeadline helpers (runUnstructuredWithDeadline, observeTask, waitUntil) replacing ad-hoc timeout scaffolding.
  • New SendCompletionTests, FrameInboxTests, DataPlaneSinkTests, SessionRetireTests, ControlEventSinkTests.
  • Expanded integration coverage for stream ordering, transport failure paths, connection teardown/retirement, data-plane sequencing, optimistic controller state reconciliation, and hardware-volume startup safety.
  • LoopbackWebSocketServer bounded-start helper; MockTransport and ServerMessageHelpers conveniences.

Split the protocol client into a thin @mainactor facade and an off-MainActor
connection actor, so all heavy audio and control work runs off the main actor
while @observable state still updates on the main actor for SwiftUI.

Architecture (one-way dependency: facade -> connection -> engine):
- SendspinClient (@mainactor @observable): thin facade holding public API,
  observable state, and the public events stream. Owns nothing audio-related.
- SendspinConnection (actor): owns the transport, ordered message loop,
  protocol-intent gates, clock sync, and the AudioEngine. Holds no facade
  reference and imports nothing @MainActor-isolated (proven at compile time).
  Single writer of session state.
- AudioEngine (actor): owns decode/schedule/output/sync-telemetry and the
  seamless-format state machine. No @mainactor anywhere.

Control plane: connection emits ConnectionEvent on a control AsyncStream;
the facade drains it on MainActor, applies @observable state, re-emits
public ClientEvent (single public emission point).

Data plane: audio/artwork/visualizer bytes bypass the facade and are yielded
directly to the public continuation off-main via SessionValidityToken, an
atomic check-and-yield guard for stale binary events from retired connections.

Lifetime is managed by owned objects, not generation counters: a supervisor
task, run-once teardown, an identity guard, and SessionValidityToken.
Reconnect builds a new connection+engine+token; shutdown() invalidates the
old token so its in-flight binary events are silently dropped.

Transport hardening:
- NWWebSocketTransport gains outbound client-initiated dial (ws:// / wss://)
  in addition to inbound-accepted connections.
- Per-send deadline via nonisolated timedSend racing NWConnection.send
  against a timeout; first contender wins, losers are no-ops. sendTimeout
  is init-injectable (5s default) for testability.
- New SendCompletion type coordinates the race under OSAllocatedUnfairLock,
  resuming the continuation outside the critical section (reentrancy-safe).
- New TransportError.sendTimedOut distinguishes a wedged connection.
- FrameInbox for ordered inbound frame handling, made lifecycle-finished.
- WatermarkedSink onWarning now captured under lock and invoked after
  release (reentrancy-safe).

Models:
- Splits the monolithic SendspinMessage into per-domain files
  (Hello, ClientState, ServerState, Stream, Time, Command, Goodbye).
- Adds static typeString to every concrete message with let type derivation,
  preserving synthesized Codable.

Connection-owned audio state:
- Tightens audio state ownership to the connection/engine; seamless-format
  classification is synchronous via announcedPlayerFormat. EngineReport
  carries full target operational state (bidirectional into/out of
  .error/.synchronized).

Test infrastructure and coverage:
- New TestBox (generic isolation-safe single-value box) and AsyncTestDeadline
  helpers (runUnstructuredWithDeadline, observeTask, waitUntil) replacing
  ad-hoc timeout scaffolding.
- New SendCompletionTests, FrameInboxTests, DataPlaneSinkTests,
  SessionRetireTests, ControlEventSinkTests.
- Expanded integration coverage for stream ordering, transport failure paths,
  connection teardown/retirement, data-plane sequencing, optimistic
  controller state reconciliation, and hardware-volume startup safety.
- LoopbackWebSocketServer bounded-start helper; MockTransport and
  ServerMessageHelpers conveniences.
@teancom
teancom force-pushed the control-data-plane-split branch from 37ac35b to b4e7e0d Compare June 14, 2026 17:24
@github-actions

Copy link
Copy Markdown

Test Coverage Report

Line Coverage: 91.01% · Function Coverage: 88.37%

Detailed Coverage Report
Filename                                                                         Regions    Missed Regions     Cover   Functions  Missed Functions  Executed       Lines      Missed Lines     Cover    Branches   Missed Branches     Cover
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.build/arm64-apple-macosx/debug/SendspinKitPackageTests.derived/runner.swift           9                 1    88.89%           2                 0   100.00%          17                 3    82.35%           0                 0         -
.build/checkouts/swift-opus/Sources/Opus/AVAudioFormat+Extensions.swift               33                21    36.36%           3                 1    66.67%          48                27    43.75%           0                 0         -
.build/checkouts/swift-opus/Sources/Opus/Opus.Application.swift                        3                 1    66.67%           3                 1    66.67%           9                 3    66.67%           0                 0         -
.build/checkouts/swift-opus/Sources/Opus/Opus.Decoder.swift                           36                15    58.33%           8                 2    75.00%          90                28    68.89%           0                 0         -
.build/checkouts/swift-opus/Sources/Opus/Opus.Encoder.swift                           36                36     0.00%          11                11     0.00%          86                86     0.00%           0                 0         -
.build/checkouts/swift-opus/Sources/Opus/Opus.Error.swift                              3                 1    66.67%           3                 1    66.67%           9                 3    66.67%           0                 0         -
Sources/SendspinKit/Audio/AudioDecoder.swift                                         110                66    40.00%          26                 9    65.38%         390               182    53.33%           0                 0         -
Sources/SendspinKit/Audio/AudioEngine.swift                                          120                13    89.17%          44                 5    88.64%         382                46    87.96%           0                 0         -
Sources/SendspinKit/Audio/AudioPlayer.swift                                          142                49    65.49%          57                 6    89.47%         615               120    80.49%           0                 0         -
Sources/SendspinKit/Audio/AudioScheduler.swift                                        51                 1    98.04%          22                 0   100.00%         173                 3    98.27%           0                 0         -
Sources/SendspinKit/Audio/DataPlaneCommand.swift                                       9                 0   100.00%           1                 0   100.00%          16                 0   100.00%           0                 0         -
Sources/SendspinKit/Audio/DataPlaneSink.swift                                          5                 0   100.00%           5                 0   100.00%          18                 0   100.00%           0                 0         -
Sources/SendspinKit/Audio/NoOpAudioOutput.swift                                       13                10    23.08%          13                10    23.08%          28                14    50.00%           0                 0         -
Sources/SendspinKit/Audio/PCMRingBuffer.swift                                         27                 4    85.19%          14                 1    92.86%          81                 1    98.77%           0                 0         -
Sources/SendspinKit/Audio/SyncCorrection.swift                                        26                 4    84.62%          12                 3    75.00%          65                 4    93.85%           0                 0         -
Sources/SendspinKit/Audio/VolumeController.swift                                      52                32    38.46%          15                 8    46.67%         129                69    46.51%           0                 0         -
Sources/SendspinKit/Client/ArtworkConfiguration.swift                                  5                 2    60.00%           1                 0   100.00%           5                 0   100.00%           0                 0         -
Sources/SendspinKit/Client/ClientTypes.swift                                          18                11    38.89%           4                 2    50.00%          37                18    51.35%           0                 0         -
Sources/SendspinKit/Client/ClockSyncStats.swift                                        9                 9     0.00%           2                 2     0.00%          12                12     0.00%           0                 0         -
Sources/SendspinKit/Client/ConnectionState.swift                                      12                12     0.00%           2                 2     0.00%          20                20     0.00%           0                 0         -
Sources/SendspinKit/Client/ControlEventSink.swift                                      4                 1    75.00%           4                 1    75.00%          15                 1    93.33%           0                 0         -
Sources/SendspinKit/Client/PlayerConfiguration.swift                                  11                 3    72.73%           1                 0   100.00%          18                 1    94.44%           0                 0         -
Sources/SendspinKit/Client/SendspinClient+ClockSync.swift                              7                 1    85.71%           3                 0   100.00%          14                 1    92.86%           0                 0         -
Sources/SendspinKit/Client/SendspinClient+Commands.swift                              72                30    58.33%          21                10    52.38%         133                37    72.18%           0                 0         -
Sources/SendspinKit/Client/SendspinClient+Discovery.swift                             22                15    31.82%           6                 5    16.67%          84                71    15.48%           0                 0         -
Sources/SendspinKit/Client/SendspinClient+Handshake.swift                              7                 1    85.71%           1                 0   100.00%          26                 0   100.00%           0                 0         -
Sources/SendspinKit/Client/SendspinClient+MultiServer.swift                           43                 4    90.70%           9                 2    77.78%         156                 2    98.72%           0                 0         -
Sources/SendspinKit/Client/SendspinClient.swift                                      153                15    90.20%          50                 1    98.00%         484                15    96.90%           0                 0         -
Sources/SendspinKit/Client/SendspinConnection+ClientStateDelta.swift                  29                 0   100.00%           7                 0   100.00%          33                 0   100.00%           0                 0         -
Sources/SendspinKit/Client/SendspinConnection+Lifecycle.swift                         50                 4    92.00%          11                 1    90.91%         176                 4    97.73%           0                 0         -
Sources/SendspinKit/Client/SendspinConnection+MessageHandling.swift                  157                29    81.53%          52                17    67.31%         395                40    89.87%           0                 0         -
Sources/SendspinKit/Client/SendspinConnection+Outbound.swift                          25                 4    84.00%           5                 1    80.00%          47                 1    97.87%           0                 0         -
Sources/SendspinKit/Client/SendspinConnection+RequestFormat.swift                      8                 0   100.00%           2                 0   100.00%          12                 0   100.00%           0                 0         -
Sources/SendspinKit/Client/SendspinConnection.swift                                   72                 8    88.89%          34                 2    94.12%         177                 8    95.48%           0                 0         -
Sources/SendspinKit/Client/SessionValidityToken.swift                                 14                 0   100.00%          10                 0   100.00%          38                 0   100.00%           0                 0         -
Sources/SendspinKit/Client/UnderrunMonitor.swift                                      16                 0   100.00%           7                 0   100.00%          31                 0   100.00%           0                 0         -
Sources/SendspinKit/ConfigurationError.swift                                          20                12    40.00%           1                 0   100.00%          38                12    68.42%           0                 0         -
Sources/SendspinKit/Discovery/ClientAdvertiser.swift                                  54                36    33.33%          27                16    40.74%         170                93    45.29%           0                 0         -
Sources/SendspinKit/Discovery/DiscoveredServer.swift                                   2                 2     0.00%           2                 2     0.00%          10                10     0.00%           0                 0         -
Sources/SendspinKit/Discovery/ServerDiscovery.swift                                  113                93    17.70%          42                30    28.57%         313               267    14.70%           0                 0         -
Sources/SendspinKit/Models/ArtworkModels.swift                                        27                 1    96.30%           5                 1    80.00%          46                 1    97.83%           0                 0         -
Sources/SendspinKit/Models/AudioFormatSpec.swift                                      25                 1    96.00%           7                 1    85.71%          42                 1    97.62%           0                 0         -
Sources/SendspinKit/Models/BinaryMessage.swift                                        14                 0   100.00%           4                 0   100.00%          42                 0   100.00%           0                 0         -
Sources/SendspinKit/Models/ClientRole.swift                                           27                 5    81.48%          16                 5    68.75%          56                 5    91.07%           0                 0         -
Sources/SendspinKit/Models/ClientStateMessages.swift                                  31                 4    87.10%           7                 1    85.71%          57                 7    87.72%           0                 0         -
Sources/SendspinKit/Models/CommandMessages.swift                                       5                 0   100.00%           5                 0   100.00%          16                 0   100.00%           0                 0         -
Sources/SendspinKit/Models/GoodbyeMessage.swift                                        1                 0   100.00%           1                 0   100.00%           1                 0   100.00%           0                 0         -
Sources/SendspinKit/Models/HelloMessages.swift                                         6                 3    50.00%           6                 3    50.00%          12                 3    75.00%           0                 0         -
Sources/SendspinKit/Models/ServerStateMessages.swift                                  88                14    84.09%          11                 1    90.91%          93                15    83.87%           0                 0         -
Sources/SendspinKit/Models/StreamMessages.swift                                       35                 1    97.14%          17                 0   100.00%          58                 0   100.00%           0                 0         -
Sources/SendspinKit/Models/TimeMessages.swift                                          2                 1    50.00%           2                 1    50.00%           2                 1    50.00%           0                 0         -
Sources/SendspinKit/SendspinKit.swift                                                  2                 0   100.00%           2                 0   100.00%           6                 0   100.00%           0                 0         -
Sources/SendspinKit/Synchronization/ClockSynchronizer.swift                           23                 4    82.61%          13                 0   100.00%          84                 8    90.48%           0                 0         -
Sources/SendspinKit/Synchronization/MonotonicClock.swift                               6                 1    83.33%           6                 1    83.33%          21                 1    95.24%           0                 0         -
Sources/SendspinKit/Synchronization/SendspinTimeFilter.swift                          39                 1    97.44%          20                 1    95.00%         142                 1    99.30%           0                 0         -
Sources/SendspinKit/Synchronization/TimeFilterSnapshot.swift                           6                 0   100.00%           2                 0   100.00%          13                 0   100.00%           0                 0         -
Sources/SendspinKit/Transport/FrameInbox.swift                                        28                 1    96.43%          13                 1    92.31%         115                 1    99.13%           0                 0         -
Sources/SendspinKit/Transport/NWWebSocketTransport.swift                              98                23    76.53%          43                 3    93.02%         371                38    89.76%           0                 0         -
Sources/SendspinKit/Transport/SendspinEncoding.swift                                   3                 0   100.00%           2                 0   100.00%           8                 0   100.00%           0                 0         -
Sources/SendspinKit/Transport/TransportError.swift                                     8                 8     0.00%           2                 2     0.00%          11                11     0.00%           0                 0         -
Sources/SendspinKit/WatermarkedSink.swift                                             27                 2    92.59%          15                 0   100.00%         114                 3    97.37%           0                 0         -
Tests/SendspinKitTests/Audio/AudioEngineTests.swift                                  142                20    85.92%          65                 7    89.23%         641                37    94.23%           0                 0         -
Tests/SendspinKitTests/Audio/AudioPlayerTests.swift                                   21                 0   100.00%           8                 0   100.00%          92                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Audio/AudioProcessCallbackTests.swift                          70                 3    95.71%          34                 0   100.00%         252                 3    98.81%           0                 0         -
Tests/SendspinKitTests/Audio/DataPlaneSinkTests.swift                                 88                 1    98.86%          35                 0   100.00%         343                 2    99.42%           0                 0         -
Tests/SendspinKitTests/Audio/FLACDecoderTests.swift                                    6                 0   100.00%           3                 0   100.00%          34                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Audio/OpusDecoderTests.swift                                    9                 0   100.00%           3                 0   100.00%          33                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Audio/PCMRingBufferTests.swift                                 23                 0   100.00%          21                 0   100.00%         187                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Audio/SyncCorrectionTests.swift                                13                 0   100.00%          13                 0   100.00%          77                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Audio/VolumeControlTests.swift                                 49                 5    89.80%          23                 0   100.00%         151                 0   100.00%           0                 0         -
Tests/SendspinKitTests/AudioSchedulerTests.swift                                      19                 5    73.68%          17                 5    70.59%         203                27    86.70%           0                 0         -
Tests/SendspinKitTests/Client/ControlEventSinkTests.swift                              5                 0   100.00%           3                 0   100.00%          29                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Client/MultiServerArbitrationTests.swift                        9                 0   100.00%           9                 0   100.00%          58                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Client/SendspinClientTests.swift                               33                 4    87.88%          10                 1    90.00%         126                 3    97.62%           0                 0         -
Tests/SendspinKitTests/Client/SendspinConnectionTests.swift                          337                25    92.58%         116                 8    93.10%        1446                22    98.48%           0                 0         -
Tests/SendspinKitTests/Client/SessionRetireTests.swift                                 9                 0   100.00%           2                 0   100.00%          40                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Client/UnderrunMonitorTests.swift                              10                 0   100.00%           8                 0   100.00%          65                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Discovery/ClientAdvertiserTests.swift                          22                 0   100.00%          11                 0   100.00%         121                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Discovery/ServerDiscoveryTests.swift                           17                 1    94.12%          13                 0   100.00%          79                 1    98.73%           0                 0         -
Tests/SendspinKitTests/Helpers/AsyncTestDeadline.swift                                28                 6    78.57%          12                 4    66.67%          91                10    89.01%           0                 0         -
Tests/SendspinKitTests/Helpers/EventCollection.swift                                  16                 1    93.75%           7                 1    85.71%          54                 1    98.15%           0                 0         -
Tests/SendspinKitTests/Helpers/LoopbackWebSocketServer.swift                          35                 4    88.57%          16                 0   100.00%         131                 6    95.42%           0                 0         -
Tests/SendspinKitTests/Helpers/MockTransport.swift                                    34                 4    88.24%          22                 1    95.45%          69                 6    91.30%           0                 0         -
Tests/SendspinKitTests/Helpers/ServerMessageHelpers.swift                              9                 3    66.67%           6                 3    50.00%          32                 3    90.62%           0                 0         -
Tests/SendspinKitTests/Helpers/TestBox.swift                                           3                 0   100.00%           3                 0   100.00%           9                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Integration/ArtworkObserverValidityTests.swift                 15                 0   100.00%           8                 0   100.00%          59                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Integration/AudioPipelineIntegrationTests.swift                29                 0   100.00%          11                 0   100.00%         167                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Integration/BinaryMessageIntegrationTests.swift                38                 0   100.00%          17                 0   100.00%         228                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Integration/ClientIntegrationTests.swift                      343                25    92.71%         112                 4    96.43%        1189                25    97.90%           0                 0         -
Tests/SendspinKitTests/Integration/ClientStateDeltaTests.swift                        38                 2    94.74%          10                 0   100.00%         128                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Integration/ClockSyncIntegrationTests.swift                    13                 0   100.00%           4                 0   100.00%         131                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Integration/ConnectionLostTeardownTests.swift                  70                 2    97.14%          35                 0   100.00%         258                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Integration/FrameOrderingTests.swift                          197                 8    95.94%          50                 0   100.00%         718                10    98.61%           0                 0         -
Tests/SendspinKitTests/Integration/MessageRoundTripTests.swift                        15                 0   100.00%           3                 0   100.00%         122                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Integration/SessionStateResetTests.swift                       88                 0   100.00%          53                 0   100.00%         315                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Integration/StreamRequestFormatTests.swift                    188                 2    98.94%          80                 0   100.00%         564                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Models/ArtworkModelTests.swift                                 94                 0   100.00%          30                 0   100.00%         456                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Models/AudioFormatSpecTests.swift                              52                 0   100.00%          22                 0   100.00%         181                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Models/BinaryMessageTests.swift                                28                 0   100.00%          20                 0   100.00%         145                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Models/MessageEncodingTests.swift                              60                 0   100.00%          18                 0   100.00%         260                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Models/StreamMessageTests.swift                                 3                 0   100.00%           1                 0   100.00%          28                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Models/VersionedRoleTests.swift                                31                 0   100.00%          21                 0   100.00%         134                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Synchronization/ClockSynchronizerTests.swift                   10                 0   100.00%           5                 0   100.00%         132                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Synchronization/MonotonicClockTests.swift                       7                 0   100.00%           6                 0   100.00%          46                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Synchronization/SendspinTimeFilterTests.swift                  53                 0   100.00%          23                 0   100.00%         340                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Synchronization/TimeFilterSnapshotTests.swift                  27                 0   100.00%          13                 0   100.00%         159                 0   100.00%           0                 0         -
Tests/SendspinKitTests/Transport/FrameInboxTests.swift                                27                 2    92.59%          18                 0   100.00%         140                 2    98.57%           0                 0         -
Tests/SendspinKitTests/Transport/NWWebSocketTransportOutboundTests.swift              49                13    73.47%          17                 3    82.35%         196                15    92.35%           0                 0         -
Tests/SendspinKitTests/Transport/NWWebSocketTransportTests.swift                      76                18    76.32%          29                 2    93.10%         262                39    85.11%           0                 0         -
Tests/SendspinKitTests/Transport/NextFrameTests.swift                                 25                 3    88.00%          11                 1    90.91%         166                 5    96.99%           0                 0         -
Tests/SendspinKitTests/Transport/SendCompletionTests.swift                            28                 3    89.29%          13                 0   100.00%         106                 5    95.28%           0                 0         -
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL                                                                               4710               776    83.52%        1832               213    88.37%       16903              1520    91.01%           0                 0         -

@teancom
teancom merged commit 79e01bd into main Jun 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant