github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/internal/epoll/poller_test.go (about)

     1  package epoll
     2  
     3  import (
     4  	"errors"
     5  	"math"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/cilium/ebpf/internal/unix"
    11  )
    12  
    13  func TestPoller(t *testing.T) {
    14  	t.Parallel()
    15  
    16  	event, poller := mustNewPoller(t)
    17  
    18  	done := make(chan struct{}, 1)
    19  	read := func() {
    20  		defer func() {
    21  			done <- struct{}{}
    22  		}()
    23  
    24  		events := make([]unix.EpollEvent, 1)
    25  
    26  		n, err := poller.Wait(events, time.Time{})
    27  		if errors.Is(err, os.ErrClosed) {
    28  			return
    29  		}
    30  
    31  		if err != nil {
    32  			t.Error("Error from wait:", err)
    33  			return
    34  		}
    35  
    36  		if n != 1 {
    37  			t.Errorf("Got %d instead of 1 events", n)
    38  		}
    39  
    40  		if e := events[0]; e.Pad != 42 {
    41  			t.Errorf("Incorrect value in EpollEvent.Pad: %d != 42", e.Pad)
    42  		}
    43  	}
    44  
    45  	if err := event.add(1); err != nil {
    46  		t.Fatal(err)
    47  	}
    48  
    49  	go read()
    50  	select {
    51  	case <-done:
    52  	case <-time.After(time.Second):
    53  		t.Fatal("Timed out")
    54  	}
    55  
    56  	if _, err := event.read(); err != nil {
    57  		t.Fatal(err)
    58  	}
    59  
    60  	go read()
    61  	select {
    62  	case <-done:
    63  		t.Fatal("Wait doesn't block")
    64  	case <-time.After(time.Second):
    65  	}
    66  
    67  	if err := poller.Close(); err != nil {
    68  		t.Fatal("Close returns an error:", err)
    69  	}
    70  
    71  	select {
    72  	case <-done:
    73  	case <-time.After(time.Second):
    74  		t.Fatal("Close doesn't unblock Wait")
    75  	}
    76  
    77  	if err := poller.Close(); !errors.Is(err, os.ErrClosed) {
    78  		t.Fatal("Closing a second time doesn't return ErrClosed:", err)
    79  	}
    80  }
    81  
    82  func TestPollerDeadline(t *testing.T) {
    83  	t.Parallel()
    84  
    85  	_, poller := mustNewPoller(t)
    86  	events := make([]unix.EpollEvent, 1)
    87  
    88  	_, err := poller.Wait(events, time.Now().Add(-time.Second))
    89  	if !errors.Is(err, os.ErrDeadlineExceeded) {
    90  		t.Fatal("Expected os.ErrDeadlineExceeded on deadline in the past, got", err)
    91  	}
    92  
    93  	done := make(chan struct{})
    94  	go func() {
    95  		defer close(done)
    96  
    97  		_, err := poller.Wait(events, time.Now().Add(math.MaxInt64))
    98  		if !errors.Is(err, os.ErrClosed) {
    99  			t.Error("Expected os.ErrClosed when interrupting deadline, got", err)
   100  		}
   101  	}()
   102  
   103  	// Wait for the goroutine to enter the syscall.
   104  	time.Sleep(time.Second)
   105  
   106  	poller.Close()
   107  	<-done
   108  }
   109  
   110  func mustNewPoller(t *testing.T) (*eventFd, *Poller) {
   111  	t.Helper()
   112  
   113  	event, err := newEventFd()
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  	t.Cleanup(func() { event.close() })
   118  
   119  	poller, err := New()
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  	t.Cleanup(func() { poller.Close() })
   124  
   125  	if err := poller.Add(event.raw, 42); err != nil {
   126  		t.Fatal("Can't add fd:", err)
   127  	}
   128  
   129  	return event, poller
   130  }