Skip to content

Commit 4b384cd

Browse files
committed
Use semaphores in some tests
1 parent b276be6 commit 4b384cd

1 file changed

Lines changed: 50 additions & 74 deletions

File tree

Tests/PowerSync/PowerSync.Common.Tests/Client/PowerSyncDatabaseTests.cs

Lines changed: 50 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -278,18 +278,18 @@ await db.Execute(
278278
[Fact(Timeout = 2000)]
279279
public async Task ReadWhileWriteIsRunningTest()
280280
{
281-
var tcs = new TaskCompletionSource<bool>();
281+
var sem = new TaskCompletionSource<bool>();
282282

283283
// This wont resolve or free until another connection free's it
284284
var writeTask = db.WriteLock(async context =>
285285
{
286-
await tcs.Task; // Wait until read lock signals to proceed
286+
await sem.Task; // Wait until read lock signals to proceed
287287
});
288288

289289
var readTask = db.ReadLock(async context =>
290290
{
291291
// Read logic could execute here while writeLock is still open
292-
tcs.SetResult(true);
292+
sem.SetResult(true);
293293
await Task.CompletedTask;
294294
return 42;
295295
});
@@ -469,15 +469,15 @@ await db.Execute(
469469
public async Task TestConcurrentReadsTest()
470470
{
471471
await db.Execute("INSERT INTO assets(id) VALUES(?)", ["O6-conccurent-1"]);
472-
var tcs = new TaskCompletionSource<bool>();
472+
var sem = new TaskCompletionSource<bool>();
473473

474474
// Start a long-running write transaction
475475
var transactionTask = Task.Run(async () =>
476476
{
477477
await db.WriteTransaction(async tx =>
478478
{
479479
await tx.Execute("INSERT INTO assets(id) VALUES(?)", ["O6-conccurent-2"]);
480-
await tcs.Task;
480+
await sem.Task;
481481
await tx.Commit();
482482
});
483483
});
@@ -487,7 +487,7 @@ await db.WriteTransaction(async tx =>
487487
Assert.Single(result); // The transaction is not commited yet, we should only read 1 asset
488488

489489
// Let the transaction complete
490-
tcs.SetResult(true);
490+
sem.SetResult(true);
491491
await transactionTask;
492492

493493
// Read again after the transaction is committed
@@ -509,7 +509,7 @@ public async Task GetUploadQueueStatsTest()
509509
}
510510

511511
[Fact]
512-
public async Task DynamicQueryTest()
512+
public async Task QueryDynamicTest()
513513
{
514514
string id = Guid.NewGuid().ToString();
515515
string description = "new description";
@@ -526,7 +526,7 @@ await db.Execute(
526526
}
527527

528528
[Fact(Timeout = 2000)]
529-
public async Task DynamicWatchTest()
529+
public async Task WatchDynamicTest()
530530
{
531531
string id = Guid.NewGuid().ToString();
532532
string description = "new description";
@@ -569,40 +569,29 @@ await tx.Execute(
569569
await watched.Task;
570570
}
571571

572-
private class WrappedTCS
573-
{
574-
public TaskCompletionSource<bool> TCS { get; protected set; } = new();
575-
public Task<bool> Task => TCS.Task;
576-
577-
public void Complete() => TCS.TrySetResult(true);
578-
public void Reset() => TCS = new();
579-
}
580-
581572
[Fact(Timeout = 2000)]
582573
public async void WatchDisposableSubscriptionTest()
583574
{
584575
int callCount = 0;
585-
var tcs = new WrappedTCS();
576+
var semaphore = new SemaphoreSlim(0);
586577

587578
var query = await db.Watch("select id from assets", null, new()
588579
{
589580
OnResult = (results) =>
590581
{
591582
callCount++;
592-
tcs.Complete();
583+
semaphore.Release();
593584
},
594585
OnError = (ex) => Assert.Fail(ex.ToString())
595586
});
596-
await tcs.Task;
597-
tcs.Reset();
587+
await semaphore.WaitAsync();
598588
Assert.Equal(1, callCount);
599589

600590
await db.Execute(
601591
"insert into assets(id, description, make) values (?, ?, ?)",
602592
[Guid.NewGuid().ToString(), "some desc", "some make"]
603593
);
604-
await tcs.Task;
605-
tcs.Reset();
594+
await semaphore.WaitAsync();
606595
Assert.Equal(2, callCount);
607596

608597
query.Dispose();
@@ -611,39 +600,38 @@ await db.Execute(
611600
"insert into assets(id, description, make) values (?, ?, ?)",
612601
[Guid.NewGuid().ToString(), "some desc", "some make"]
613602
);
614-
await Task.Delay(100);
615-
Assert.Equal(2, callCount); // Same value
603+
bool receivedResult = await semaphore.WaitAsync(100);
604+
Assert.False(receivedResult, "Received update after disposal");
605+
Assert.Equal(2, callCount);
616606
}
617607

618608
[Fact(Timeout = 2000)]
619609
public async void WatchDisposableCustomTokenTest()
620610
{
621611
var customTokenSource = new CancellationTokenSource();
622612
int callCount = 0;
623-
var tcs = new WrappedTCS();
613+
var sem = new SemaphoreSlim(0);
624614

625615
using var query = await db.Watch("select id, description, make from assets", null, new()
626616
{
627617
OnResult = (results) =>
628618
{
629619
callCount++;
630-
tcs.Complete();
620+
sem.Release();
631621
},
632622
OnError = (ex) => Assert.Fail(ex.ToString())
633623
}, new()
634624
{
635625
Signal = customTokenSource.Token
636626
});
637-
await tcs.Task;
638-
tcs.Reset();
627+
await sem.WaitAsync();
639628
Assert.Equal(1, callCount);
640629

641630
await db.Execute(
642631
"insert into assets(id, description, make) values (?, ?, ?)",
643632
[Guid.NewGuid().ToString(), "some desc", "some make"]
644633
);
645-
await tcs.Task;
646-
tcs.Reset();
634+
await sem.WaitAsync();
647635
Assert.Equal(2, callCount);
648636

649637
customTokenSource.Cancel();
@@ -652,47 +640,40 @@ await db.Execute(
652640
"insert into assets(id, description, make) values (?, ?, ?)",
653641
[Guid.NewGuid().ToString(), "some desc", "some make"]
654642
);
655-
await Task.Delay(100);
656-
Assert.Equal(2, callCount); // Same value
643+
bool receivedResult = await sem.WaitAsync(100);
644+
Assert.False(receivedResult, "Received update after disposal");
645+
646+
Assert.Equal(2, callCount);
657647
}
658648

659649
[Fact(Timeout = 2000)]
660650
public async void WatchSingleCancelledTest()
661651
{
662652
int callCount = 0;
663-
object lockObject = new object();
664653

665-
var watchHandlerFactory = (WrappedTCS tcs) => new WatchHandler<IdResult>
654+
var watchHandlerFactory = (SemaphoreSlim sem) => new WatchHandler<IdResult>
666655
{
667656
OnResult = (result) =>
668657
{
669-
lock (lockObject)
670-
{
671-
callCount++;
672-
}
673-
tcs.Complete();
658+
Interlocked.Increment(ref callCount);
659+
sem.Release();
674660
},
675661
OnError = (ex) => Assert.Fail(ex.ToString()),
676662
};
677663

678-
var tcsAlwaysRunning = new WrappedTCS();
679-
var tcsCancelled = new WrappedTCS();
680-
// Make queryAlwaysRunning slightly more expensive
681-
using var queryAlwaysRunning = await db.Watch("select id from assets order by id", null, watchHandlerFactory(tcsAlwaysRunning));
682-
using var queryCancelled = await db.Watch("select id from assets", null, watchHandlerFactory(tcsCancelled));
664+
var semAlwaysRunning = new SemaphoreSlim(0);
665+
var semCancelled = new SemaphoreSlim(0);
666+
using var queryAlwaysRunning = await db.Watch("select id from assets", null, watchHandlerFactory(semAlwaysRunning));
667+
using var queryCancelled = await db.Watch("select id from assets", null, watchHandlerFactory(semCancelled));
683668

684-
await Task.WhenAll(tcsAlwaysRunning.Task, tcsCancelled.Task);
685-
tcsAlwaysRunning.Reset();
686-
tcsCancelled.Reset();
669+
await Task.WhenAll(semAlwaysRunning.WaitAsync(), semCancelled.WaitAsync());
687670
Assert.Equal(2, callCount);
688671

689672
await db.Execute(
690673
"insert into assets(id, description, make) values (?, ?, ?)",
691674
[Guid.NewGuid().ToString(), "some desc", "some make"]
692675
);
693-
await Task.WhenAll(tcsAlwaysRunning.Task, tcsCancelled.Task);
694-
tcsAlwaysRunning.Reset();
695-
tcsCancelled.Reset();
676+
await Task.WhenAll(semAlwaysRunning.WaitAsync(), semCancelled.WaitAsync());
696677
Assert.Equal(4, callCount);
697678

698679
// Close one query
@@ -702,50 +683,43 @@ await db.Execute(
702683
"insert into assets(id, description, make) values (?, ?, ?)",
703684
[Guid.NewGuid().ToString(), "some desc", "some make"]
704685
);
705-
// Because queryAlwaysRunning is more expensive than queryCancelled, we would expect
706-
// queryCancelled to complete before it if the cancellation failed, hence we can just
707-
// wait for queryAlwaysRunning to complete.
708-
await tcsAlwaysRunning.Task;
709686

687+
// Ensure nothing received from cancelled result
688+
bool receivedResult = await semCancelled.WaitAsync(100);
689+
Assert.False(receivedResult, "Received update after disposal");
690+
691+
await semAlwaysRunning.WaitAsync();
710692
Assert.Equal(5, callCount);
711693
}
712694

713695
[Fact(Timeout = 2000)]
714696
public async void WatchMultipleCancelledTest()
715697
{
716698
int callCount = 0;
717-
object lockObject = new object();
718699

719-
var watchHandlerFactory = (WrappedTCS tcs) => new WatchHandler<IdResult>
700+
var watchHandlerFactory = (SemaphoreSlim sem) => new WatchHandler<IdResult>
720701
{
721702
OnResult = (result) =>
722703
{
723-
lock (lockObject)
724-
{
725-
callCount++;
726-
}
727-
tcs.Complete();
704+
Interlocked.Increment(ref callCount);
705+
sem.Release();
728706
},
729707
OnError = (ex) => Assert.Fail(ex.ToString()),
730708
};
731709

732-
var tcs1 = new WrappedTCS();
733-
var tcs2 = new WrappedTCS();
734-
using var query1 = await db.Watch("select id from assets", null, watchHandlerFactory(tcs1));
735-
using var query2 = await db.Watch("select id from assets", null, watchHandlerFactory(tcs2));
710+
var sem1 = new SemaphoreSlim(0);
711+
var sem2 = new SemaphoreSlim(0);
712+
using var query1 = await db.Watch("select id from assets", null, watchHandlerFactory(sem1));
713+
using var query2 = await db.Watch("select id from assets", null, watchHandlerFactory(sem2));
736714

737-
await Task.WhenAll(tcs1.Task, tcs2.Task);
738-
tcs1.Reset();
739-
tcs2.Reset();
715+
await Task.WhenAll(sem1.WaitAsync(), sem2.WaitAsync());
740716
Assert.Equal(2, callCount);
741717

742718
await db.Execute(
743719
"insert into assets(id, description, make) values (?, ?, ?)",
744720
[Guid.NewGuid().ToString(), "some desc", "some make"]
745721
);
746-
await Task.WhenAll(tcs1.Task, tcs2.Task);
747-
tcs1.Reset();
748-
tcs2.Reset();
722+
await Task.WhenAll(sem1.WaitAsync(), sem2.WaitAsync());
749723
Assert.Equal(4, callCount);
750724

751725
db.UnsubscribeAllQueries();
@@ -754,8 +728,10 @@ await db.Execute(
754728
"insert into assets(id, description, make) values (?, ?, ?)",
755729
[Guid.NewGuid().ToString(), "some desc", "some make"]
756730
);
757-
// Short wait to ensure unsubscription was successful
758-
await Task.Delay(100);
731+
// Ensure no result received from either query
732+
bool receivedResult = await sem1.WaitAsync(100) || await sem2.WaitAsync(100);
733+
Assert.False(receivedResult, "Received update after disposal");
734+
759735
Assert.Equal(4, callCount);
760736
}
761737
}

0 commit comments

Comments
 (0)