mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
test(agentcompat): cover SQLite hold arbitration
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
@@ -0,0 +1,289 @@
|
|||||||
|
//go:build agentcompat && linux
|
||||||
|
|
||||||
|
package singleton
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sqliteHoldTestJournal = SQLiteJournalIdentity{
|
||||||
|
MountID: 2, DeviceMajor: 8, DeviceMinor: 1, Inode: 13, BirthSeconds: 10, BirthNanoseconds: 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
func sqliteHoldTestTransaction(id SQLiteTransactionIdentity) SQLiteTransaction {
|
||||||
|
return SQLiteTransaction{Connection: SQLiteConnectionIdentity(3), Identity: id}
|
||||||
|
}
|
||||||
|
|
||||||
|
func recordSQLiteHoldTestUpdate(t *testing.T, tracker *SQLiteHoldTracker, transaction SQLiteTransaction) {
|
||||||
|
t.Helper()
|
||||||
|
if err := tracker.RecordSQLiteExecution(transaction, SQLiteExecutionOrigin{
|
||||||
|
Operation: SQLiteOperationUpdate, Table: "settings", StackHash: 21, FirstNezhaFrame: "singleton.test",
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tracker.RecordSQLiteUpdate(transaction, SQLiteUpdateObservation{
|
||||||
|
Operation: SQLiteOperationUpdate, Table: "settings", Journal: sqliteHoldTestJournal,
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSQLiteHoldTrackerSelectsFutureCandidateAfterZeroCandidateArm(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
tracker := NewSQLiteHoldTracker()
|
||||||
|
session, err := tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
transaction := sqliteHoldTestTransaction(1)
|
||||||
|
if err := tracker.BeginSQLiteTransaction(transaction); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, transaction)
|
||||||
|
finalization, err := tracker.BeginSQLiteFinalization(transaction)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tracker.ReleaseSQLiteHold(session); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := finalization.Wait(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSQLiteHoldTrackerSelectsSingleActiveCandidate(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
tracker := NewSQLiteHoldTracker()
|
||||||
|
transaction := sqliteHoldTestTransaction(2)
|
||||||
|
if err := tracker.BeginSQLiteTransaction(transaction); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, transaction)
|
||||||
|
|
||||||
|
// When
|
||||||
|
session, err := tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
finalization, finalizationErr := tracker.BeginSQLiteFinalization(transaction)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if err != nil || finalizationErr != nil {
|
||||||
|
t.Fatalf("arm=%v finalization=%v", err, finalizationErr)
|
||||||
|
}
|
||||||
|
if err := tracker.ReleaseSQLiteHold(session); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := finalization.Wait(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSQLiteHoldTrackerAbortsDuplicateCandidates(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
tracker := NewSQLiteHoldTracker()
|
||||||
|
first := sqliteHoldTestTransaction(3)
|
||||||
|
second := sqliteHoldTestTransaction(4)
|
||||||
|
for _, transaction := range []SQLiteTransaction{first, second} {
|
||||||
|
if err := tracker.BeginSQLiteTransaction(transaction); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When
|
||||||
|
_, err := tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if !errors.Is(err, ErrSQLiteHoldAmbiguousCandidate) {
|
||||||
|
t.Fatalf("expected ambiguous candidate error, got %v", err)
|
||||||
|
}
|
||||||
|
if _, err := tracker.BeginSQLiteFinalization(first); !errors.Is(err, ErrSQLiteHoldNotSelected) {
|
||||||
|
t.Fatalf("expected no selected transaction, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSQLiteHoldTrackerStaleReleaseCannotReleaseNewSession(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
tracker := NewSQLiteHoldTracker()
|
||||||
|
first := sqliteHoldTestTransaction(5)
|
||||||
|
if err := tracker.BeginSQLiteTransaction(first); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, first)
|
||||||
|
staleSession, err := tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tracker.AbortSQLiteHold(staleSession); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tracker.FinishSQLiteTransaction(first); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
second := sqliteHoldTestTransaction(6)
|
||||||
|
if err := tracker.BeginSQLiteTransaction(second); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, second)
|
||||||
|
session, err := tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
finalization, err := tracker.BeginSQLiteFinalization(second)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When
|
||||||
|
err = tracker.ReleaseSQLiteHold(staleSession)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if !errors.Is(err, ErrSQLiteHoldStaleSession) {
|
||||||
|
t.Fatalf("expected stale session error, got %v", err)
|
||||||
|
}
|
||||||
|
if finalization.Released() {
|
||||||
|
t.Fatal("stale release released the new session")
|
||||||
|
}
|
||||||
|
if err := tracker.ReleaseSQLiteHold(session); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := finalization.Wait(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSQLiteHoldTrackerLinearizesArmBeforeFinalization(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
tracker := NewSQLiteHoldTracker()
|
||||||
|
transaction := sqliteHoldTestTransaction(7)
|
||||||
|
if err := tracker.BeginSQLiteTransaction(transaction); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, transaction)
|
||||||
|
session, err := tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When
|
||||||
|
finalization, err := tracker.BeginSQLiteFinalization(transaction)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if finalization.Released() {
|
||||||
|
t.Fatal("finalization released before its session release")
|
||||||
|
}
|
||||||
|
if err := tracker.ReleaseSQLiteHold(session); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := finalization.Wait(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSQLiteHoldTrackerRollbackNeverWaits(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
tracker := NewSQLiteHoldTracker()
|
||||||
|
transaction := sqliteHoldTestTransaction(8)
|
||||||
|
if err := tracker.BeginSQLiteTransaction(transaction); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, transaction)
|
||||||
|
session, err := tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
finalization, err := tracker.BeginSQLiteFinalization(transaction)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When
|
||||||
|
err = tracker.FinishSQLiteTransaction(transaction)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := finalization.Wait(); !errors.Is(err, ErrSQLiteHoldAborted) {
|
||||||
|
t.Fatalf("expected aborted finalization, got %v", err)
|
||||||
|
}
|
||||||
|
if err := tracker.ReleaseSQLiteHold(session); !errors.Is(err, ErrSQLiteHoldStaleSession) {
|
||||||
|
t.Fatalf("expected stale release, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSQLiteHoldTrackerAbortUnblocksSelectedFinalization(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
tracker := NewSQLiteHoldTracker()
|
||||||
|
transaction := sqliteHoldTestTransaction(9)
|
||||||
|
if err := tracker.BeginSQLiteTransaction(transaction); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, transaction)
|
||||||
|
session, err := tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
finalization, err := tracker.BeginSQLiteFinalization(transaction)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When
|
||||||
|
err = tracker.AbortSQLiteHold(session)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := finalization.Wait(); !errors.Is(err, ErrSQLiteHoldAborted) {
|
||||||
|
t.Fatalf("expected aborted finalization, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSQLiteHoldTrackerCleansUpAfterReleasedFinalization(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
tracker := NewSQLiteHoldTracker()
|
||||||
|
first := sqliteHoldTestTransaction(10)
|
||||||
|
if err := tracker.BeginSQLiteTransaction(first); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, first)
|
||||||
|
session, err := tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
finalization, err := tracker.BeginSQLiteFinalization(first)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tracker.ReleaseSQLiteHold(session); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := finalization.Wait(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tracker.FinishSQLiteTransaction(first); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
second := sqliteHoldTestTransaction(11)
|
||||||
|
if err := tracker.BeginSQLiteTransaction(second); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recordSQLiteHoldTestUpdate(t, tracker, second)
|
||||||
|
|
||||||
|
// When
|
||||||
|
_, err = tracker.ArmSQLiteHold(sqliteHoldTestJournal)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user