package main import ( "testing" "unsafe" "golang.org/x/sys/unix" ) func putCmsg(buf []byte, off int, level, typ int32, dataLen int) int { h := (*unix.Cmsghdr)(unsafe.Pointer(&buf[off])) h.SetLen(unix.CmsgLen(dataLen)) h.Level = level h.Type = typ return off + unix.CmsgSpace(dataLen) } func putStamp(buf []byte, off int, raw unix.Timespec) int { next := putCmsg(buf, off, unix.SOL_SOCKET, unix.SCM_TIMESTAMPING, scmTimestampingLen) ts := (*[3]unix.Timespec)(unsafe.Pointer(&buf[off+cmsgDataOff])) ts[2] = raw return next } func TestHwTimestampReadsRawHardwareClock(t *testing.T) { buf := make([]byte, cmsgLen) n := putStamp(buf, 0, unix.Timespec{Sec: 5, Nsec: 7}) got, ok := hwTimestamp(buf[:n]) if !ok || got != 5e9+7 { t.Errorf("hwTimestamp = %d, %v; want 5000000007, true", got, ok) } } func TestHwTimestampSkipsOtherMessages(t *testing.T) { buf := make([]byte, cmsgLen) off := putCmsg(buf, 0, unix.SOL_SOCKET, unix.SCM_RIGHTS, 4) n := putStamp(buf, off, unix.Timespec{Sec: 1, Nsec: 2}) got, ok := hwTimestamp(buf[:n]) if !ok || got != 1e9+2 { t.Errorf("hwTimestamp = %d, %v; want 1000000002, true", got, ok) } } func TestHwTimestampRejectsZeroStamp(t *testing.T) { buf := make([]byte, cmsgLen) n := putStamp(buf, 0, unix.Timespec{}) if got, ok := hwTimestamp(buf[:n]); ok { t.Errorf("hwTimestamp = %d, %v; want a zero stamp refused", got, ok) } } func TestHwTimestampWithoutAStamp(t *testing.T) { buf := make([]byte, cmsgLen) n := putCmsg(buf, 0, unix.SOL_SOCKET, unix.SCM_RIGHTS, 4) if _, ok := hwTimestamp(buf[:n]); ok { t.Error("a buffer carrying no timestamp yielded one") } if _, ok := hwTimestamp(nil); ok { t.Error("an empty buffer yielded a timestamp") } } func TestHwTimestampRefusesBadLengths(t *testing.T) { buf := make([]byte, cmsgLen) n := putStamp(buf, 0, unix.Timespec{Sec: 1}) if _, ok := hwTimestamp(buf[:n-1]); ok { t.Error("a message running past the buffer yielded a timestamp") } short := make([]byte, cmsgLen) putCmsg(short, 0, unix.SOL_SOCKET, unix.SCM_TIMESTAMPING, scmTimestampingLen) (*unix.Cmsghdr)(unsafe.Pointer(&short[0])).SetLen(unix.SizeofCmsghdr - 1) if _, ok := hwTimestamp(short); ok { t.Error("a message shorter than its own header yielded a timestamp") } trunc := make([]byte, cmsgLen) tn := putCmsg(trunc, 0, unix.SOL_SOCKET, unix.SCM_TIMESTAMPING, scmTimestampingLen/3) if _, ok := hwTimestamp(trunc[:tn]); ok { t.Error("a truncated stamp yielded a timestamp") } } func BenchmarkHwTimestamp(b *testing.B) { buf := make([]byte, cmsgLen) n := putStamp(buf, 0, unix.Timespec{Sec: 5, Nsec: 7}) oob := buf[:n] b.ReportAllocs() for b.Loop() { if _, ok := hwTimestamp(oob); !ok { b.Fatal("no timestamp") } } }