diff --git a/cmd/codeaf/hosted_takeback_test.go b/cmd/codeaf/hosted_takeback_test.go index 83d73feffb..993a2cef0b 100644 --- a/cmd/codeaf/hosted_takeback_test.go +++ b/cmd/codeaf/hosted_takeback_test.go @@ -15,6 +15,7 @@ import ( "os" "os/exec" "path/filepath" + "slices" "strings" "sync" "sync/atomic" @@ -49,6 +50,7 @@ type hostedMoveRig struct { mu sync.Mutex requests []string acquires []time.Time + resumes []time.Time changes chan struct{} } @@ -87,6 +89,9 @@ func newHostedMoveRig(t *testing.T, wrap ...func(http.Handler) http.Handler) *ho if strings.HasSuffix(r.URL.Path, "/acquire") { h.acquires = append(h.acquires, time.Now()) } + if strings.HasSuffix(r.URL.Path, "/heartbeat") { + h.resumes = append(h.resumes, time.Now()) + } h.mu.Unlock() service.ServeHTTP(w, r) })) @@ -321,7 +326,7 @@ func TestHostedTakeBackReachesAcquireWithoutSharedUploadsOrReleaseDelay(t *testi h.wait("take-back card", func() bool { return strings.Contains(screen.String(), "Continue this chat here?") }) h.mu.Lock() h.requests = nil - h.acquires = nil + h.acquires, h.resumes = nil, nil h.mu.Unlock() started = time.Now() if _, err := io.WriteString(typed, "1\r"); err != nil { @@ -330,8 +335,18 @@ func TestHostedTakeBackReachesAcquireWithoutSharedUploadsOrReleaseDelay(t *testi h.wait("hosted take-back arrival", func() bool { return strings.Contains(screen.String(), "Moved from other computer") }) h.mu.Lock() defer h.mu.Unlock() - if len(h.acquires) < 2 { - t.Fatalf("take and reopened hosted writer made %d acquires; want both", len(h.acquires)) + if len(h.acquires) == 0 { + t.Fatal("take-back made no acquire") + } + // The reopened writer resumes the hold the take acquired (F1, #2009): it + // renews that fence rather than acquiring another. + resumed := slices.IndexFunc(h.resumes, func(at time.Time) bool { return !at.Before(h.acquires[0]) }) + if resumed < 0 { + t.Fatal("the reopened hosted writer never resumed the taken hold") + } + t.Logf("relay resume after first acquire: %s", h.resumes[resumed].Sub(h.acquires[0])) + if gap := h.resumes[resumed].Sub(h.acquires[0]); gap >= 5*time.Second { + t.Errorf("resume gap=%s", gap) } t.Logf("relay first acquire after key: %s", h.acquires[0].Sub(started)) if elapsed := h.acquires[0].Sub(started); elapsed > time.Second { diff --git a/docs/changes/unreleased/2183-reopen-fence-test.md b/docs/changes/unreleased/2183-reopen-fence-test.md new file mode 100644 index 0000000000..94fd0ba996 --- /dev/null +++ b/docs/changes/unreleased/2183-reopen-fence-test.md @@ -0,0 +1,6 @@ +--- +kind: internal +title: F1 reopen resumes the same hold in the stale-consent and hosted take-back tests +pr: 2183 +surface: [chat] +--- diff --git a/internal/syncsetup/continue_reopened_writer_test.go b/internal/syncsetup/continue_reopened_writer_test.go index b504bb0a68..d85db69540 100644 --- a/internal/syncsetup/continue_reopened_writer_test.go +++ b/internal/syncsetup/continue_reopened_writer_test.go @@ -22,6 +22,10 @@ func TestAReturnAfterTheWriterReopensIsNotALostRace(t *testing.T) { moved, err := h.continuerB().Take(ctx, h.cell.ID) mustNil(t, err) stale := &directory.LeaseExpectation{Device: h.b.Device.ID(), Fence: moved.Taken.Driving.Fence} + // Opening an installed copy resumes the same hold (F1, #2009); a clean + // quit releases it, so the writer's return is a new ownership. + first := h.openOn(h.b, h.engB, moved.Taken.Cell, workspaceOf(moved.Taken.Cell.Root), nameB) + mustNil(t, first.drive.Close(ctx)) h.openOn(h.b, h.engB, moved.Taken.Cell, workspaceOf(moved.Taken.Cell.Root), nameB) current, err := h.a.Dir.Cell(ctx, h.cell.ID) mustNil(t, err)