| | |
| | |
| | |
| |
|
| | |
| |
|
| | package syslog |
| |
|
| | import ( |
| | "bufio" |
| | "fmt" |
| | "io" |
| | "net" |
| | "os" |
| | "path/filepath" |
| | "runtime" |
| | "sync" |
| | "testing" |
| | "time" |
| | ) |
| |
|
| | func runPktSyslog(c net.PacketConn, done chan<- string) { |
| | var buf [4096]byte |
| | var rcvd string |
| | ct := 0 |
| | for { |
| | var n int |
| | var err error |
| |
|
| | c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) |
| | n, _, err = c.ReadFrom(buf[:]) |
| | rcvd += string(buf[:n]) |
| | if err != nil { |
| | if oe, ok := err.(*net.OpError); ok { |
| | if ct < 3 && oe.Temporary() { |
| | ct++ |
| | continue |
| | } |
| | } |
| | break |
| | } |
| | } |
| | c.Close() |
| | done <- rcvd |
| | } |
| |
|
| | var crashy = false |
| |
|
| | func testableNetwork(network string) bool { |
| | switch network { |
| | case "unix", "unixgram": |
| | switch runtime.GOOS { |
| | case "ios", "android": |
| | return false |
| | } |
| | } |
| | return true |
| | } |
| |
|
| | func runStreamSyslog(l net.Listener, done chan<- string, wg *sync.WaitGroup) { |
| | for { |
| | var c net.Conn |
| | var err error |
| | if c, err = l.Accept(); err != nil { |
| | return |
| | } |
| | wg.Add(1) |
| | go func(c net.Conn) { |
| | defer wg.Done() |
| | c.SetReadDeadline(time.Now().Add(5 * time.Second)) |
| | b := bufio.NewReader(c) |
| | for ct := 1; !crashy || ct&7 != 0; ct++ { |
| | s, err := b.ReadString('\n') |
| | if err != nil { |
| | break |
| | } |
| | done <- s |
| | } |
| | c.Close() |
| | }(c) |
| | } |
| | } |
| |
|
| | func startServer(t *testing.T, n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) { |
| | if n == "udp" || n == "tcp" { |
| | la = "127.0.0.1:0" |
| | } else { |
| | |
| | if la == "" { |
| | |
| | |
| | |
| | |
| | |
| | dir, err := os.MkdirTemp("", "") |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | t.Cleanup(func() { |
| | if err := os.RemoveAll(dir); err != nil { |
| | t.Errorf("failed to remove socket temp directory: %v", err) |
| | } |
| | }) |
| | la = filepath.Join(dir, "sock") |
| | } |
| | } |
| |
|
| | wg = new(sync.WaitGroup) |
| | if n == "udp" || n == "unixgram" { |
| | l, e := net.ListenPacket(n, la) |
| | if e != nil { |
| | t.Helper() |
| | t.Fatalf("startServer failed: %v", e) |
| | } |
| | addr = l.LocalAddr().String() |
| | sock = l |
| | wg.Add(1) |
| | go func() { |
| | defer wg.Done() |
| | runPktSyslog(l, done) |
| | }() |
| | } else { |
| | l, e := net.Listen(n, la) |
| | if e != nil { |
| | t.Helper() |
| | t.Fatalf("startServer failed: %v", e) |
| | } |
| | addr = l.Addr().String() |
| | sock = l |
| | wg.Add(1) |
| | go func() { |
| | defer wg.Done() |
| | runStreamSyslog(l, done, wg) |
| | }() |
| | } |
| | return |
| | } |
| |
|
| | func TestWithSimulated(t *testing.T) { |
| | t.Parallel() |
| |
|
| | msg := "Test 123" |
| | for _, tr := range []string{"unix", "unixgram", "udp", "tcp"} { |
| | if !testableNetwork(tr) { |
| | continue |
| | } |
| |
|
| | t.Run(tr, func(t *testing.T) { |
| | t.Parallel() |
| |
|
| | done := make(chan string) |
| | addr, sock, srvWG := startServer(t, tr, "", done) |
| | defer srvWG.Wait() |
| | defer sock.Close() |
| | if tr == "unix" || tr == "unixgram" { |
| | defer os.Remove(addr) |
| | } |
| | s, err := Dial(tr, addr, LOG_INFO|LOG_USER, "syslog_test") |
| | if err != nil { |
| | t.Fatalf("Dial() failed: %v", err) |
| | } |
| | err = s.Info(msg) |
| | if err != nil { |
| | t.Fatalf("log failed: %v", err) |
| | } |
| | check(t, msg, <-done, tr) |
| | s.Close() |
| | }) |
| | } |
| | } |
| |
|
| | func TestFlap(t *testing.T) { |
| | net := "unix" |
| | if !testableNetwork(net) { |
| | t.Skipf("skipping on %s/%s; 'unix' is not supported", runtime.GOOS, runtime.GOARCH) |
| | } |
| |
|
| | done := make(chan string) |
| | addr, sock, srvWG := startServer(t, net, "", done) |
| | defer srvWG.Wait() |
| | defer os.Remove(addr) |
| | defer sock.Close() |
| |
|
| | s, err := Dial(net, addr, LOG_INFO|LOG_USER, "syslog_test") |
| | if err != nil { |
| | t.Fatalf("Dial() failed: %v", err) |
| | } |
| | msg := "Moo 2" |
| | err = s.Info(msg) |
| | if err != nil { |
| | t.Fatalf("log failed: %v", err) |
| | } |
| | check(t, msg, <-done, net) |
| |
|
| | |
| | if err := os.Remove(addr); err != nil { |
| | t.Fatal(err) |
| | } |
| | _, sock2, srvWG2 := startServer(t, net, addr, done) |
| | defer srvWG2.Wait() |
| | defer sock2.Close() |
| |
|
| | |
| | msg = "Moo 3" |
| | err = s.Info(msg) |
| | if err != nil { |
| | t.Fatalf("log failed: %v", err) |
| | } |
| | check(t, msg, <-done, net) |
| |
|
| | s.Close() |
| | } |
| |
|
| | func TestNew(t *testing.T) { |
| | if LOG_LOCAL7 != 23<<3 { |
| | t.Fatalf("LOG_LOCAL7 has wrong value") |
| | } |
| | if testing.Short() { |
| | |
| | t.Skip("skipping syslog test during -short") |
| | } |
| |
|
| | s, err := New(LOG_INFO|LOG_USER, "the_tag") |
| | if err != nil { |
| | if err.Error() == "Unix syslog delivery error" { |
| | t.Skip("skipping: syslogd not running") |
| | } |
| | t.Fatalf("New() failed: %s", err) |
| | } |
| | |
| | s.Close() |
| | } |
| |
|
| | func TestNewLogger(t *testing.T) { |
| | if testing.Short() { |
| | t.Skip("skipping syslog test during -short") |
| | } |
| | f, err := NewLogger(LOG_USER|LOG_INFO, 0) |
| | if f == nil { |
| | if err.Error() == "Unix syslog delivery error" { |
| | t.Skip("skipping: syslogd not running") |
| | } |
| | t.Error(err) |
| | } |
| | } |
| |
|
| | func TestDial(t *testing.T) { |
| | if testing.Short() { |
| | t.Skip("skipping syslog test during -short") |
| | } |
| | f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test") |
| | if f != nil { |
| | t.Fatalf("Should have trapped bad priority") |
| | } |
| | f, err = Dial("", "", -1, "syslog_test") |
| | if f != nil { |
| | t.Fatalf("Should have trapped bad priority") |
| | } |
| | l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test") |
| | if err != nil { |
| | if err.Error() == "Unix syslog delivery error" { |
| | t.Skip("skipping: syslogd not running") |
| | } |
| | t.Fatalf("Dial() failed: %s", err) |
| | } |
| | l.Close() |
| | } |
| |
|
| | func check(t *testing.T, in, out, transport string) { |
| | hostname, err := os.Hostname() |
| | if err != nil { |
| | t.Errorf("Error retrieving hostname: %v", err) |
| | return |
| | } |
| |
|
| | if transport == "unixgram" || transport == "unix" { |
| | var month, date, ts string |
| | var pid int |
| | tmpl := fmt.Sprintf("<%d>%%s %%s %%s syslog_test[%%d]: %s\n", LOG_USER+LOG_INFO, in) |
| | n, err := fmt.Sscanf(out, tmpl, &month, &date, &ts, &pid) |
| | if n != 4 || err != nil { |
| | t.Errorf("Got %q, does not match template %q (%d %s)", out, tmpl, n, err) |
| | } |
| | return |
| | } |
| |
|
| | |
| | var parsedHostname, timestamp string |
| | var pid int |
| | tmpl := fmt.Sprintf("<%d>%%s %%s syslog_test[%%d]: %s\n", LOG_USER+LOG_INFO, in) |
| | n, err := fmt.Sscanf(out, tmpl, ×tamp, &parsedHostname, &pid) |
| | if n != 3 || err != nil { |
| | t.Errorf("Got %q, does not match template %q (%d %s)", out, tmpl, n, err) |
| | } |
| | if hostname != parsedHostname { |
| | t.Errorf("Hostname got %q want %q in %q", parsedHostname, hostname, out) |
| | } |
| | } |
| |
|
| | func TestWrite(t *testing.T) { |
| | t.Parallel() |
| |
|
| | tests := []struct { |
| | pri Priority |
| | pre string |
| | msg string |
| | exp string |
| | }{ |
| | {LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"}, |
| | {LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"}, |
| | |
| | {LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"}, |
| | } |
| |
|
| | if hostname, err := os.Hostname(); err != nil { |
| | t.Fatalf("Error retrieving hostname") |
| | } else { |
| | for _, test := range tests { |
| | done := make(chan string) |
| | addr, sock, srvWG := startServer(t, "udp", "", done) |
| | defer srvWG.Wait() |
| | defer sock.Close() |
| | l, err := Dial("udp", addr, test.pri, test.pre) |
| | if err != nil { |
| | t.Fatalf("syslog.Dial() failed: %v", err) |
| | } |
| | defer l.Close() |
| | _, err = io.WriteString(l, test.msg) |
| | if err != nil { |
| | t.Fatalf("WriteString() failed: %v", err) |
| | } |
| | rcvd := <-done |
| | test.exp = fmt.Sprintf("<%d>", test.pri) + test.exp |
| | var parsedHostname, timestamp string |
| | var pid int |
| | if n, err := fmt.Sscanf(rcvd, test.exp, ×tamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname { |
| | t.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, test.exp, n, err) |
| | } |
| | } |
| | } |
| | } |
| |
|
| | func TestConcurrentWrite(t *testing.T) { |
| | addr, sock, srvWG := startServer(t, "udp", "", make(chan string, 1)) |
| | defer srvWG.Wait() |
| | defer sock.Close() |
| | w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?") |
| | if err != nil { |
| | t.Fatalf("syslog.Dial() failed: %v", err) |
| | } |
| | var wg sync.WaitGroup |
| | for i := 0; i < 10; i++ { |
| | wg.Add(1) |
| | go func() { |
| | defer wg.Done() |
| | err := w.Info("test") |
| | if err != nil { |
| | t.Errorf("Info() failed: %v", err) |
| | return |
| | } |
| | }() |
| | } |
| | wg.Wait() |
| | } |
| |
|
| | func TestConcurrentReconnect(t *testing.T) { |
| | crashy = true |
| | defer func() { crashy = false }() |
| |
|
| | const N = 10 |
| | const M = 100 |
| | net := "unix" |
| | if !testableNetwork(net) { |
| | net = "tcp" |
| | if !testableNetwork(net) { |
| | t.Skipf("skipping on %s/%s; neither 'unix' or 'tcp' is supported", runtime.GOOS, runtime.GOARCH) |
| | } |
| | } |
| | done := make(chan string, N*M) |
| | addr, sock, srvWG := startServer(t, net, "", done) |
| | if net == "unix" { |
| | defer os.Remove(addr) |
| | } |
| |
|
| | |
| | count := make(chan int, 1) |
| | go func() { |
| | ct := 0 |
| | for range done { |
| | ct++ |
| | |
| | |
| | |
| | if ct > N*M/2 { |
| | break |
| | } |
| | } |
| | count <- ct |
| | }() |
| |
|
| | var wg sync.WaitGroup |
| | wg.Add(N) |
| | for i := 0; i < N; i++ { |
| | go func() { |
| | defer wg.Done() |
| | w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag") |
| | if err != nil { |
| | t.Errorf("syslog.Dial() failed: %v", err) |
| | return |
| | } |
| | defer w.Close() |
| | for i := 0; i < M; i++ { |
| | err := w.Info("test") |
| | if err != nil { |
| | t.Errorf("Info() failed: %v", err) |
| | return |
| | } |
| | } |
| | }() |
| | } |
| | wg.Wait() |
| | sock.Close() |
| | srvWG.Wait() |
| | close(done) |
| |
|
| | select { |
| | case <-count: |
| | case <-time.After(100 * time.Millisecond): |
| | t.Error("timeout in concurrent reconnect") |
| | } |
| | } |
| |
|