Files
nezha_domains/service/singleton/sqlite_hold_tracker_agentcompat_linux_test.go
2026-07-20 04:37:59 +00:00

290 lines
7.6 KiB
Go

//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)
}
}