Skip to content

Commit 875b271

Browse files
pblazejclaude
andcommitted
test(publish): add failing republish-after-unpublish regression test
Reproduces #958: publish simulcast video (720x1280, 3 layers) + audio, unpublish all, then republish — only audio reaches the remote side, video never appears (trackPublications.count → 1 instead of 2). The test uses the same parameterized V0/V1 pattern as the existing signaling tests and is expected to fail until the underlying transceiver reuse issue is fixed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7bf838d commit 875b271

1 file changed

Lines changed: 57 additions & 0 deletions

File tree

Tests/LiveKitCoreTests/PeerConnectionSignalingTests.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,63 @@ struct PeerConnectionSignalingTests {
372372
}
373373
}
374374

375+
@Test(.bug("https://github.com/livekit/client-sdk-swift/issues/958"),
376+
arguments: SignalingMode.allCases)
377+
func republishAfterUnpublishAll(mode: SignalingMode) async throws {
378+
try await TestEnvironment.withRooms([
379+
roomTestingOptions(mode: mode, canPublish: true),
380+
roomTestingOptions(mode: mode, canSubscribe: true),
381+
]) { rooms in
382+
let room1 = rooms[0]
383+
let room2 = rooms[1]
384+
385+
let publisherIdentity = try #require(room1.localParticipant.identity)
386+
let remoteParticipant = try #require(room2.remoteParticipants[publisherIdentity])
387+
388+
// Phase 1: Publish audio + simulcast video (720x1280 triggers 3 layers)
389+
let audioTrack1 = TestAudioTrack(name: "audio-1")
390+
let videoTrack1 = LocalVideoTrack.createBufferTrack(name: "video-1")
391+
if let capturer = videoTrack1.capturer as? BufferCapturer {
392+
var pb: CVPixelBuffer?
393+
CVPixelBufferCreate(kCFAllocatorDefault, 720, 1280, kCVPixelFormatType_32BGRA, nil, &pb)
394+
if let pb { capturer.capture(pb) }
395+
}
396+
397+
try await room1.localParticipant.publish(audioTrack: audioTrack1)
398+
try await room1.localParticipant.publish(videoTrack: videoTrack1)
399+
400+
try await waitForPublish(on: remoteParticipant) {
401+
$0.trackPublications.count >= 2
402+
}
403+
404+
// Phase 2: Unpublish all
405+
await room1.localParticipant.unpublishAll()
406+
407+
try await waitForPublish(on: remoteParticipant) {
408+
$0.trackPublications.isEmpty
409+
}
410+
411+
try await Task.sleep(nanoseconds: 2_000_000_000)
412+
413+
// Phase 3: Republish new tracks
414+
let audioTrack2 = TestAudioTrack(name: "audio-2")
415+
let videoTrack2 = LocalVideoTrack.createBufferTrack(name: "video-2")
416+
if let capturer = videoTrack2.capturer as? BufferCapturer {
417+
var pb: CVPixelBuffer?
418+
CVPixelBufferCreate(kCFAllocatorDefault, 720, 1280, kCVPixelFormatType_32BGRA, nil, &pb)
419+
if let pb { capturer.capture(pb) }
420+
}
421+
422+
try await room1.localParticipant.publish(audioTrack: audioTrack2)
423+
try await room1.localParticipant.publish(videoTrack: videoTrack2)
424+
425+
try await waitForPublish(on: remoteParticipant) {
426+
$0.trackPublications.count >= 2
427+
}
428+
#expect(remoteParticipant.trackPublications.count >= 2, "Remote should see republished tracks")
429+
}
430+
}
431+
375432
@Test(arguments: SignalingMode.allCases)
376433
func doubleReconnect(mode: SignalingMode) async throws {
377434
let reconnectWatcher = ReconnectWatcher()

0 commit comments

Comments
 (0)