| 1 | package extension |
| 2 | |
| 3 | import ( |
| 4 | "errors" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | // countCloser records Close invocations and the order they happened in. |
| 9 | type countCloser struct { |
| 10 | closes *int |
| 11 | name string |
| 12 | order *[]string |
| 13 | err error |
| 14 | closedN int |
| 15 | } |
| 16 | |
| 17 | func (c *countCloser) Close() error { |
| 18 | *c.closes++ |
| 19 | c.closedN++ |
| 20 | *c.order = append(*c.order, c.name) |
| 21 | return c.err |
| 22 | } |
| 23 | |
| 24 | // TestRuntimeSetCloseOnce: Close is idempotent — most closers (processes, |
| 25 | // sockets) are not safe to close twice, and cleanup paths overlap. |
| 26 | func TestRuntimeSetCloseOnce(t *testing.T) { |
| 27 | closes := 0 |
| 28 | var order []string |
| 29 | set := NewRuntimeSet(1) |
| 30 | set.Add(&countCloser{closes: &closes, name: "first", order: &order}) |
| 31 | set.Add(&countCloser{closes: &closes, name: "second", order: &order}) |
| 32 | |
| 33 | if err := set.Close(); err != nil { |
| 34 | t.Fatalf("first Close: %v", err) |
| 35 | } |
| 36 | if closes != 2 { |
| 37 | t.Fatalf("closes = %d, want 2", closes) |
| 38 | } |
| 39 | // Reverse registration order: later resources may depend on earlier ones. |
| 40 | if len(order) != 2 || order[0] != "second" || order[1] != "first" { |
| 41 | t.Fatalf("close order = %v, want [second first]", order) |
| 42 | } |
| 43 | if err := set.Close(); err != nil { |
| 44 | t.Fatalf("second Close: %v", err) |
| 45 | } |
| 46 | if closes != 2 { |
| 47 | t.Fatalf("closes after double Close = %d, want still 2", closes) |
| 48 | } |
| 49 | if !set.Closed() { |
| 50 | t.Fatal("Closed() = false after Close") |
| 51 | } |
| 52 | if set.Len() != 0 { |
| 53 | t.Fatalf("Len() = %d after Close, want 0", set.Len()) |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | // TestRuntimeSetCloseIfGeneration: a stale cleanup path must never close |
| 58 | // resources that now belong to a newer runtime. |
| 59 | func TestRuntimeSetCloseIfGeneration(t *testing.T) { |
| 60 | closes := 0 |
| 61 | var order []string |
| 62 | set := NewRuntimeSet(5) |
| 63 | set.Add(&countCloser{closes: &closes, name: "c", order: &order}) |
| 64 | |
| 65 | if set.CloseIfGeneration(4) { |
| 66 | t.Fatal("CloseIfGeneration(4) closed a generation-5 set") |
| 67 | } |
| 68 | if closes != 0 { |
| 69 | t.Fatal("wrong generation closed resources") |
| 70 | } |
| 71 | if !set.CloseIfGeneration(5) { |
| 72 | t.Fatal("CloseIfGeneration(5) did not close the generation-5 set") |
| 73 | } |
| 74 | if closes != 1 { |
| 75 | t.Fatalf("closes = %d, want 1", closes) |
| 76 | } |
| 77 | // After closing, a repeat guard call is a no-op report, not a re-close. |
| 78 | set.CloseIfGeneration(5) |
| 79 | if closes != 1 { |
| 80 | t.Fatalf("closes after repeat = %d, want still 1", closes) |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | // TestRuntimeSetAddAfterClose: registering a resource on a dead set closes it |
| 85 | // immediately — leaking it would strand sidecar processes. |
| 86 | func TestRuntimeSetAddAfterClose(t *testing.T) { |
| 87 | closes := 0 |
| 88 | var order []string |
| 89 | set := NewRuntimeSet(1) |
| 90 | if err := set.Close(); err != nil { |
| 91 | t.Fatalf("Close: %v", err) |
| 92 | } |
| 93 | set.Add(&countCloser{closes: &closes, name: "late", order: &order}) |
| 94 | if closes != 1 { |
| 95 | t.Fatalf("closes = %d, want the late closer closed immediately", closes) |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | // TestRuntimeSetCloseErrors: close failures are joined, not dropped, and the |
| 100 | // remaining closers still run. |
| 101 | func TestRuntimeSetCloseErrors(t *testing.T) { |
| 102 | closes := 0 |
| 103 | var order []string |
| 104 | boom := errors.New("boom") |
| 105 | set := NewRuntimeSet(1) |
| 106 | set.Add(&countCloser{closes: &closes, name: "ok", order: &order}) |
| 107 | set.Add(&countCloser{closes: &closes, name: "bad", order: &order, err: boom}) |
| 108 | err := set.Close() |
| 109 | if !errors.Is(err, boom) { |
| 110 | t.Fatalf("Close error = %v, want boom", err) |
| 111 | } |
| 112 | if closes != 2 { |
| 113 | t.Fatalf("closes = %d, want both closers attempted", closes) |
| 114 | } |
| 115 | } |
| 116 |