1
0

tun_file_linux_test.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. //go:build linux && !android && !e2e_testing
  2. // +build linux,!android,!e2e_testing
  3. package overlay
  4. import (
  5. "errors"
  6. "os"
  7. "sync"
  8. "testing"
  9. "time"
  10. "golang.org/x/sys/unix"
  11. )
  12. // newReadPipe returns a read fd. The matching write fd is registered for cleanup.
  13. // The caller takes ownership of the read fd (pass it to newTunFd / newFriend).
  14. func newReadPipe(t *testing.T) int {
  15. t.Helper()
  16. var fds [2]int
  17. if err := unix.Pipe2(fds[:], unix.O_CLOEXEC); err != nil {
  18. t.Fatalf("pipe2: %v", err)
  19. }
  20. t.Cleanup(func() { _ = unix.Close(fds[1]) })
  21. return fds[0]
  22. }
  23. func TestTunFile_WakeForShutdown_UnblocksRead(t *testing.T) {
  24. tf, err := newTunFd(newReadPipe(t))
  25. if err != nil {
  26. t.Fatalf("newTunFd: %v", err)
  27. }
  28. t.Cleanup(func() { _ = tf.Close() })
  29. done := make(chan error, 1)
  30. go func() {
  31. _, err := tf.Read(make([]byte, 64))
  32. done <- err
  33. }()
  34. // Verify Read is actually blocked in poll.
  35. select {
  36. case err := <-done:
  37. t.Fatalf("Read returned before shutdown signal: %v", err)
  38. case <-time.After(50 * time.Millisecond):
  39. }
  40. if err := tf.wakeForShutdown(); err != nil {
  41. t.Fatalf("wakeForShutdown: %v", err)
  42. }
  43. select {
  44. case err := <-done:
  45. if !errors.Is(err, os.ErrClosed) {
  46. t.Fatalf("expected os.ErrClosed, got %v", err)
  47. }
  48. case <-time.After(2 * time.Second):
  49. t.Fatal("Read did not wake on shutdown")
  50. }
  51. }
  52. func TestTunFile_WakeForShutdown_WakesFriends(t *testing.T) {
  53. parent, err := newTunFd(newReadPipe(t))
  54. if err != nil {
  55. t.Fatalf("newTunFd: %v", err)
  56. }
  57. friend, err := parent.newFriend(newReadPipe(t))
  58. if err != nil {
  59. _ = parent.Close()
  60. t.Fatalf("newFriend: %v", err)
  61. }
  62. t.Cleanup(func() {
  63. _ = friend.Close()
  64. _ = parent.Close()
  65. })
  66. readers := []*tunFile{parent, friend}
  67. errs := make([]error, len(readers))
  68. var wg sync.WaitGroup
  69. for i, r := range readers {
  70. wg.Add(1)
  71. go func(i int, r *tunFile) {
  72. defer wg.Done()
  73. _, errs[i] = r.Read(make([]byte, 64))
  74. }(i, r)
  75. }
  76. time.Sleep(50 * time.Millisecond)
  77. if err := parent.wakeForShutdown(); err != nil {
  78. t.Fatalf("wakeForShutdown: %v", err)
  79. }
  80. done := make(chan struct{})
  81. go func() { wg.Wait(); close(done) }()
  82. select {
  83. case <-done:
  84. case <-time.After(2 * time.Second):
  85. t.Fatal("readers did not wake")
  86. }
  87. for i, err := range errs {
  88. if !errors.Is(err, os.ErrClosed) {
  89. t.Errorf("reader %d: expected os.ErrClosed, got %v", i, err)
  90. }
  91. }
  92. }
  93. func TestTunFile_Close_Idempotent(t *testing.T) {
  94. tf, err := newTunFd(newReadPipe(t))
  95. if err != nil {
  96. t.Fatalf("newTunFd: %v", err)
  97. }
  98. if err := tf.Close(); err != nil {
  99. t.Fatalf("first Close: %v", err)
  100. }
  101. if err := tf.Close(); err != nil {
  102. t.Fatalf("second Close should be a no-op, got %v", err)
  103. }
  104. }