// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package runtime_test import ( "bytes" "encoding/binary" "errors" "internal/testenv" "os/exec" "reflect" "runtime" "testing" "time" ) func TestFakeTime(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("faketime not supported on windows") } // Faketime is advanced in checkdead. External linking brings in cgo, // causing checkdead not working. testenv.MustInternalLink(t, false) t.Parallel() exe, err := buildTestProg(t, "testfaketime", "-tags=faketime") if err != nil { t.Fatal(err) } var stdout, stderr bytes.Buffer cmd := exec.Command(exe) cmd.Stdout = &stdout cmd.Stderr = &stderr err = testenv.CleanCmdEnv(cmd).Run() if err != nil { t.Fatalf("exit status: %v\n%s", err, stderr.String()) } t.Logf("raw stdout: %q", stdout.String()) t.Logf("raw stderr: %q", stderr.String()) f1, err1 := parseFakeTime(stdout.Bytes()) if err1 != nil { t.Fatal(err1) } f2, err2 := parseFakeTime(stderr.Bytes()) if err2 != nil { t.Fatal(err2) } const time0 = 1257894000000000000 got := [][]fakeTimeFrame{f1, f2} var want = [][]fakeTimeFrame{{ {time0 + 1, "line 2\n"}, {time0 + 1, "line 3\n"}, {time0 + 1e9, "line 5\n"}, {time0 + 1e9, "2009-11-10T23:00:01Z"}, }, { {time0, "line 1\n"}, {time0 + 2, "line 4\n"}, }} if !reflect.DeepEqual(want, got) { t.Fatalf("want %v, got %v", want, got) } } type fakeTimeFrame struct { time uint64 data string } func parseFakeTime(x []byte) ([]fakeTimeFrame, error) { var frames []fakeTimeFrame for len(x) != 0 { if len(x) < 4+8+4 { return nil, errors.New("truncated header") } const magic = "\x00\x00PB" if string(x[:len(magic)]) != magic { return nil, errors.New("bad magic") } x = x[len(magic):] time := binary.BigEndian.Uint64(x) x = x[8:] dlen := binary.BigEndian.Uint32(x) x = x[4:] data := string(x[:dlen]) x = x[dlen:] frames = append(frames, fakeTimeFrame{time, data}) } return frames, nil } func TestTimeTimerType(t *testing.T) { // runtime.timeTimer (exported for testing as TimeTimer) // must have time.Timer and time.Ticker as a prefix // (meaning those two must have the same layout). runtimeTimeTimer := reflect.TypeOf(runtime.TimeTimer{}) check := func(name string, typ reflect.Type) { n1 := runtimeTimeTimer.NumField() n2 := typ.NumField() if n1 != n2+1 { t.Errorf("runtime.TimeTimer has %d fields, want %d (%s has %d fields)", n1, n2+1, name, n2) return } for i := 0; i < n2; i++ { f1 := runtimeTimeTimer.Field(i) f2 := typ.Field(i) t1 := f1.Type t2 := f2.Type if t1 != t2 && !(t1.Kind() == reflect.UnsafePointer && t2.Kind() == reflect.Chan) { t.Errorf("runtime.Timer field %s %v incompatible with %s field %s %v", f1.Name, t1, name, f2.Name, t2) } if f1.Offset != f2.Offset { t.Errorf("runtime.Timer field %s offset %d incompatible with %s field %s offset %d", f1.Name, f1.Offset, name, f2.Name, f2.Offset) } } } check("time.Timer", reflect.TypeOf(time.Timer{})) check("time.Ticker", reflect.TypeOf(time.Ticker{})) }