libgo: update to Go 1.15.4 release

Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/268177
This commit is contained in:
Ian Lance Taylor 2020-11-07 07:25:23 -08:00
parent 0000ea4fb4
commit cf392dbdf1
17 changed files with 267 additions and 29 deletions

View File

@ -1,4 +1,4 @@
ae20684902b82883d3d65f2cde0894c7cb3b995b 893fa057e36ae6c9b2ac5ffdf74634c35b3489c6
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.

View File

@ -1,4 +1,4 @@
1984ee00048b63eacd2155cd6d74a2d13e998272 0e953add9656c32a788e06438cd7b533e968b7f8
The first line of this file holds the git revision number of the The first line of this file holds the git revision number of the
last merge done from the master library sources. last merge done from the master library sources.

View File

@ -1 +1 @@
go1.15.3 go1.15.4

View File

@ -655,11 +655,6 @@ var codeRepoVersionsTests = []struct {
path: "swtch.com/testmod", path: "swtch.com/testmod",
versions: []string{"v1.0.0", "v1.1.1"}, versions: []string{"v1.0.0", "v1.1.1"},
}, },
{
vcs: "git",
path: "gopkg.in/russross/blackfriday.v2",
versions: []string{"v2.0.0", "v2.0.1"},
},
{ {
vcs: "git", vcs: "git",
path: "gopkg.in/natefinch/lumberjack.v2", path: "gopkg.in/natefinch/lumberjack.v2",

View File

@ -11,6 +11,7 @@ import (
"internal/testenv" "internal/testenv"
"io" "io"
"io/ioutil" "io/ioutil"
"math/rand"
"reflect" "reflect"
"runtime/debug" "runtime/debug"
"sync" "sync"
@ -896,6 +897,62 @@ func TestBestSpeedMaxMatchOffset(t *testing.T) {
} }
} }
func TestBestSpeedShiftOffsets(t *testing.T) {
// Test if shiftoffsets properly preserves matches and resets out-of-range matches
// seen in https://github.com/golang/go/issues/4142
enc := newDeflateFast()
// testData may not generate internal matches.
testData := make([]byte, 32)
rng := rand.New(rand.NewSource(0))
for i := range testData {
testData[i] = byte(rng.Uint32())
}
// Encode the testdata with clean state.
// Second part should pick up matches from the first block.
wantFirstTokens := len(enc.encode(nil, testData))
wantSecondTokens := len(enc.encode(nil, testData))
if wantFirstTokens <= wantSecondTokens {
t.Fatalf("test needs matches between inputs to be generated")
}
// Forward the current indicator to before wraparound.
enc.cur = bufferReset - int32(len(testData))
// Part 1 before wrap, should match clean state.
got := len(enc.encode(nil, testData))
if wantFirstTokens != got {
t.Errorf("got %d, want %d tokens", got, wantFirstTokens)
}
// Verify we are about to wrap.
if enc.cur != bufferReset {
t.Errorf("got %d, want e.cur to be at bufferReset (%d)", enc.cur, bufferReset)
}
// Part 2 should match clean state as well even if wrapped.
got = len(enc.encode(nil, testData))
if wantSecondTokens != got {
t.Errorf("got %d, want %d token", got, wantSecondTokens)
}
// Verify that we wrapped.
if enc.cur >= bufferReset {
t.Errorf("want e.cur to be < bufferReset (%d), got %d", bufferReset, enc.cur)
}
// Forward the current buffer, leaving the matches at the bottom.
enc.cur = bufferReset
enc.shiftOffsets()
// Ensure that no matches were picked up.
got = len(enc.encode(nil, testData))
if wantFirstTokens != got {
t.Errorf("got %d, want %d tokens", got, wantFirstTokens)
}
}
func TestMaxStackSize(t *testing.T) { func TestMaxStackSize(t *testing.T) {
// This test must not run in parallel with other tests as debug.SetMaxStack // This test must not run in parallel with other tests as debug.SetMaxStack
// affects all goroutines. // affects all goroutines.

View File

@ -270,6 +270,7 @@ func (e *deflateFast) matchLen(s, t int32, src []byte) int32 {
func (e *deflateFast) reset() { func (e *deflateFast) reset() {
e.prev = e.prev[:0] e.prev = e.prev[:0]
// Bump the offset, so all matches will fail distance check. // Bump the offset, so all matches will fail distance check.
// Nothing should be >= e.cur in the table.
e.cur += maxMatchOffset e.cur += maxMatchOffset
// Protect against e.cur wraparound. // Protect against e.cur wraparound.
@ -288,17 +289,21 @@ func (e *deflateFast) shiftOffsets() {
for i := range e.table[:] { for i := range e.table[:] {
e.table[i] = tableEntry{} e.table[i] = tableEntry{}
} }
e.cur = maxMatchOffset e.cur = maxMatchOffset + 1
return return
} }
// Shift down everything in the table that isn't already too far away. // Shift down everything in the table that isn't already too far away.
for i := range e.table[:] { for i := range e.table[:] {
v := e.table[i].offset - e.cur + maxMatchOffset v := e.table[i].offset - e.cur + maxMatchOffset + 1
if v < 0 { if v < 0 {
// We want to reset e.cur to maxMatchOffset + 1, so we need to shift
// all table entries down by (e.cur - (maxMatchOffset + 1)).
// Because we ignore matches > maxMatchOffset, we can cap
// any negative offsets at 0.
v = 0 v = 0
} }
e.table[i].offset = v e.table[i].offset = v
} }
e.cur = maxMatchOffset e.cur = maxMatchOffset + 1
} }

View File

@ -5265,6 +5265,7 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error {
if len(data) > 0 { if len(data) > 0 {
wrote, err := st.body.Write(data) wrote, err := st.body.Write(data)
if err != nil { if err != nil {
sc.sendWindowUpdate(nil, int(f.Length)-wrote)
return http2streamError(id, http2ErrCodeStreamClosed) return http2streamError(id, http2ErrCodeStreamClosed)
} }
if wrote != len(data) { if wrote != len(data) {
@ -7167,6 +7168,7 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client
cc.inflow.add(http2transportDefaultConnFlow + http2initialWindowSize) cc.inflow.add(http2transportDefaultConnFlow + http2initialWindowSize)
cc.bw.Flush() cc.bw.Flush()
if cc.werr != nil { if cc.werr != nil {
cc.Close()
return nil, cc.werr return nil, cc.werr
} }
@ -7532,6 +7534,15 @@ func (cc *http2ClientConn) roundTrip(req *Request) (res *Response, gotErrAfterRe
bodyWriter := cc.t.getBodyWriterState(cs, body) bodyWriter := cc.t.getBodyWriterState(cs, body)
cs.on100 = bodyWriter.on100 cs.on100 = bodyWriter.on100
defer func() {
cc.wmu.Lock()
werr := cc.werr
cc.wmu.Unlock()
if werr != nil {
cc.Close()
}
}()
cc.wmu.Lock() cc.wmu.Lock()
endStream := !hasBody && !hasTrailers endStream := !hasBody && !hasTrailers
werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs) werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)

View File

@ -382,7 +382,7 @@ func (r *Request) Clone(ctx context.Context) *Request {
if s := r.TransferEncoding; s != nil { if s := r.TransferEncoding; s != nil {
s2 := make([]string, len(s)) s2 := make([]string, len(s))
copy(s2, s) copy(s2, s)
r2.TransferEncoding = s r2.TransferEncoding = s2
} }
r2.Form = cloneURLValues(r.Form) r2.Form = cloneURLValues(r.Form)
r2.PostForm = cloneURLValues(r.PostForm) r2.PostForm = cloneURLValues(r.PostForm)

View File

@ -828,6 +828,27 @@ func TestWithContextDeepCopiesURL(t *testing.T) {
} }
} }
// Ensure that Request.Clone creates a deep copy of TransferEncoding.
// See issue 41907.
func TestRequestCloneTransferEncoding(t *testing.T) {
body := strings.NewReader("body")
req, _ := NewRequest("POST", "https://example.org/", body)
req.TransferEncoding = []string{
"encoding1",
}
clonedReq := req.Clone(context.Background())
// modify original after deep copy
req.TransferEncoding[0] = "encoding2"
if req.TransferEncoding[0] != "encoding2" {
t.Error("expected req.TransferEncoding to be changed")
}
if clonedReq.TransferEncoding[0] != "encoding1" {
t.Error("expected clonedReq.TransferEncoding to be unchanged")
}
}
func TestNoPanicOnRoundTripWithBasicAuth_h1(t *testing.T) { func TestNoPanicOnRoundTripWithBasicAuth_h1(t *testing.T) {
testNoPanicWithBasicAuth(t, h1Mode) testNoPanicWithBasicAuth(t, h1Mode)
} }

View File

@ -37,7 +37,17 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
// and it's safe and valid to get Value's internal pointer. // and it's safe and valid to get Value's internal pointer.
hard := func(v1, v2 Value) bool { hard := func(v1, v2 Value) bool {
switch v1.Kind() { switch v1.Kind() {
case Map, Slice, Ptr, Interface: case Ptr:
if v1.typ.ptrdata == 0 {
// go:notinheap pointers can't be cyclic.
// At least, all of our current uses of go:notinheap have
// that property. The runtime ones aren't cyclic (and we don't use
// DeepEqual on them anyway), and the cgo-generated ones are
// all empty structs.
return false
}
fallthrough
case Map, Slice, Interface:
// Nil pointers cannot be cyclic. Avoid putting them in the visited map. // Nil pointers cannot be cyclic. Avoid putting them in the visited map.
return !v1.IsNil() && !v2.IsNil() return !v1.IsNil() && !v2.IsNil()
} }

View File

@ -91,6 +91,7 @@ func (f flag) ro() flag {
// pointer returns the underlying pointer represented by v. // pointer returns the underlying pointer represented by v.
// v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer // v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer
// if v.Kind() == Ptr, the base type must not be go:notinheap.
func (v Value) pointer() unsafe.Pointer { func (v Value) pointer() unsafe.Pointer {
if v.typ.size != ptrSize || !v.typ.pointers() { if v.typ.size != ptrSize || !v.typ.pointers() {
panic("can't call pointer on a non-pointer Value") panic("can't call pointer on a non-pointer Value")
@ -1263,7 +1264,16 @@ func (v Value) Pointer() uintptr {
// TODO: deprecate // TODO: deprecate
k := v.kind() k := v.kind()
switch k { switch k {
case Chan, Map, Ptr, UnsafePointer: case Ptr:
if v.typ.ptrdata == 0 {
// Handle pointers to go:notinheap types directly,
// so we never materialize such pointers as an
// unsafe.Pointer. (Such pointers are always indirect.)
// See issue 42076.
return *(*uintptr)(v.ptr)
}
fallthrough
case Chan, Map, UnsafePointer:
return uintptr(v.pointer()) return uintptr(v.pointer())
case Func: case Func:
p := v.pointer() p := v.pointer()

View File

@ -82,16 +82,17 @@ type pollDesc struct {
lock mutex // protects the following fields lock mutex // protects the following fields
fd uintptr fd uintptr
closing bool closing bool
everr bool // marks event scanning error happened everr bool // marks event scanning error happened
user uint32 // user settable cookie user uint32 // user settable cookie
rseq uintptr // protects from stale read timers rseq uintptr // protects from stale read timers
rg uintptr // pdReady, pdWait, G waiting for read or nil rg uintptr // pdReady, pdWait, G waiting for read or nil
rt timer // read deadline timer (set if rt.f != nil) rt timer // read deadline timer (set if rt.f != nil)
rd int64 // read deadline rd int64 // read deadline
wseq uintptr // protects from stale write timers wseq uintptr // protects from stale write timers
wg uintptr // pdReady, pdWait, G waiting for write or nil wg uintptr // pdReady, pdWait, G waiting for write or nil
wt timer // write deadline timer wt timer // write deadline timer
wd int64 // write deadline wd int64 // write deadline
self *pollDesc // storage for indirect interface. See (*pollDesc).makeArg.
} }
type pollCache struct { type pollCache struct {
@ -160,6 +161,7 @@ func poll_runtime_pollOpen(fd uintptr) (uintptr, int) {
pd.wseq++ pd.wseq++
pd.wg = 0 pd.wg = 0
pd.wd = 0 pd.wd = 0
pd.self = pd
unlock(&pd.lock) unlock(&pd.lock)
var errno int32 var errno int32
@ -279,14 +281,14 @@ func poll_runtime_pollSetDeadline(ctx uintptr, d int64, mode int) {
// Copy current seq into the timer arg. // Copy current seq into the timer arg.
// Timer func will check the seq against current descriptor seq, // Timer func will check the seq against current descriptor seq,
// if they differ the descriptor was reused or timers were reset. // if they differ the descriptor was reused or timers were reset.
pd.rt.arg = pd pd.rt.arg = pd.makeArg()
pd.rt.seq = pd.rseq pd.rt.seq = pd.rseq
resettimer(&pd.rt, pd.rd) resettimer(&pd.rt, pd.rd)
} }
} else if pd.rd != rd0 || combo != combo0 { } else if pd.rd != rd0 || combo != combo0 {
pd.rseq++ // invalidate current timers pd.rseq++ // invalidate current timers
if pd.rd > 0 { if pd.rd > 0 {
modtimer(&pd.rt, pd.rd, 0, rtf, pd, pd.rseq) modtimer(&pd.rt, pd.rd, 0, rtf, pd.makeArg(), pd.rseq)
} else { } else {
deltimer(&pd.rt) deltimer(&pd.rt)
pd.rt.f = nil pd.rt.f = nil
@ -295,14 +297,14 @@ func poll_runtime_pollSetDeadline(ctx uintptr, d int64, mode int) {
if pd.wt.f == nil { if pd.wt.f == nil {
if pd.wd > 0 && !combo { if pd.wd > 0 && !combo {
pd.wt.f = netpollWriteDeadline pd.wt.f = netpollWriteDeadline
pd.wt.arg = pd pd.wt.arg = pd.makeArg()
pd.wt.seq = pd.wseq pd.wt.seq = pd.wseq
resettimer(&pd.wt, pd.wd) resettimer(&pd.wt, pd.wd)
} }
} else if pd.wd != wd0 || combo != combo0 { } else if pd.wd != wd0 || combo != combo0 {
pd.wseq++ // invalidate current timers pd.wseq++ // invalidate current timers
if pd.wd > 0 && !combo { if pd.wd > 0 && !combo {
modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd, pd.wseq) modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd.makeArg(), pd.wseq)
} else { } else {
deltimer(&pd.wt) deltimer(&pd.wt)
pd.wt.f = nil pd.wt.f = nil
@ -556,3 +558,21 @@ func (c *pollCache) alloc() *pollDesc {
unlock(&c.lock) unlock(&c.lock)
return pd return pd
} }
// makeArg converts pd to an interface{}.
// makeArg does not do any allocation. Normally, such
// a conversion requires an allocation because pointers to
// go:notinheap types (which pollDesc is) must be stored
// in interfaces indirectly. See issue 42076.
func (pd *pollDesc) makeArg() (i interface{}) {
x := (*eface)(unsafe.Pointer(&i))
x._type = pdType
// For gccgo, we still use pd.self here, not &pd.self.
x.data = unsafe.Pointer(pd.self)
return
}
var (
pdEface interface{} = (*pollDesc)(nil)
pdType *_type = efaceOf(&pdEface)._type
)

View File

@ -1258,6 +1258,14 @@ found:
checkdead() checkdead()
unlock(&sched.lock) unlock(&sched.lock)
if GOOS == "darwin" {
// Make sure pendingPreemptSignals is correct when an M exits.
// For #41702.
if atomic.Load(&m.signalPending) != 0 {
atomic.Xadd(&pendingPreemptSignals, -1)
}
}
if osStack { if osStack {
// Return from mstart and let the system thread // Return from mstart and let the system thread
// library free the g0 stack and terminate the thread. // library free the g0 stack and terminate the thread.
@ -3349,11 +3357,24 @@ func syscall_runtime_AfterForkInChild() {
inForkedChild = false inForkedChild = false
} }
// pendingPreemptSignals is the number of preemption signals
// that have been sent but not received. This is only used on Darwin.
// For #41702.
var pendingPreemptSignals uint32
// Called from syscall package before Exec. // Called from syscall package before Exec.
//go:linkname syscall_runtime_BeforeExec syscall.runtime_BeforeExec //go:linkname syscall_runtime_BeforeExec syscall.runtime_BeforeExec
func syscall_runtime_BeforeExec() { func syscall_runtime_BeforeExec() {
// Prevent thread creation during exec. // Prevent thread creation during exec.
execLock.lock() execLock.lock()
// On Darwin, wait for all pending preemption signals to
// be received. See issue #41702.
if GOOS == "darwin" {
for int32(atomic.Load(&pendingPreemptSignals)) > 0 {
osyield()
}
}
} }
// Called from syscall package after Exec. // Called from syscall package after Exec.

View File

@ -347,6 +347,10 @@ func doSigPreempt(gp *g, ctxt *sigctxt, sigpc uintptr) {
// Acknowledge the preemption. // Acknowledge the preemption.
atomic.Xadd(&gp.m.preemptGen, 1) atomic.Xadd(&gp.m.preemptGen, 1)
atomic.Store(&gp.m.signalPending, 0) atomic.Store(&gp.m.signalPending, 0)
if GOOS == "darwin" {
atomic.Xadd(&pendingPreemptSignals, -1)
}
} }
// This is false for gccgo. // This is false for gccgo.
@ -404,6 +408,9 @@ func sigtrampgo(sig uint32, info *_siginfo_t, ctx unsafe.Pointer) {
// no non-Go signal handler for sigPreempt. // no non-Go signal handler for sigPreempt.
// The default behavior for sigPreempt is to ignore // The default behavior for sigPreempt is to ignore
// the signal, so badsignal will be a no-op anyway. // the signal, so badsignal will be a no-op anyway.
if GOOS == "darwin" {
atomic.Xadd(&pendingPreemptSignals, -1)
}
return return
} }
badsignal(uintptr(sig), &c) badsignal(uintptr(sig), &c)

View File

@ -9,12 +9,14 @@ package syscall_test
import ( import (
"internal/testenv" "internal/testenv"
"io" "io"
"math/rand"
"os" "os"
"os/exec" "os/exec"
"os/signal" "os/signal"
"runtime" "runtime"
"syscall" "syscall"
"testing" "testing"
"time"
"unsafe" "unsafe"
) )
@ -245,3 +247,46 @@ func TestInvalidExec(t *testing.T) {
} }
}) })
} }
// TestExec is for issue #41702.
func TestExec(t *testing.T) {
testenv.MustHaveExec(t)
cmd := exec.Command(os.Args[0], "-test.run=TestExecHelper")
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=2")
o, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("%s\n%v", o, err)
}
}
// TestExecHelper is used by TestExec. It does nothing by itself.
// In testing on macOS 10.14, this used to fail with
// "signal: illegal instruction" more than half the time.
func TestExecHelper(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "2" {
return
}
// We don't have to worry about restoring these values.
// We are in a child process that only runs this test,
// and we are going to call syscall.Exec anyhow.
runtime.GOMAXPROCS(50)
os.Setenv("GO_WANT_HELPER_PROCESS", "3")
stop := time.Now().Add(time.Second)
for i := 0; i < 100; i++ {
go func(i int) {
r := rand.New(rand.NewSource(int64(i)))
for time.Now().Before(stop) {
r.Uint64()
}
}(i)
}
time.Sleep(10 * time.Millisecond)
argv := []string{os.Args[0], "-test.run=TestExecHelper"}
syscall.Exec(os.Args[0], argv, os.Environ())
t.Error("syscall.Exec returned")
}

View File

@ -325,10 +325,27 @@ func LoadLocationFromTZData(name string, data []byte) (*Location, error) {
if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) { if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
l.cacheStart = tx[i].when l.cacheStart = tx[i].when
l.cacheEnd = omega l.cacheEnd = omega
zoneIdx := tx[i].index
if i+1 < len(tx) { if i+1 < len(tx) {
l.cacheEnd = tx[i+1].when l.cacheEnd = tx[i+1].when
} else if l.extend != "" {
// If we're at the end of the known zone transitions,
// try the extend string.
if name, _, estart, eend, ok := tzset(l.extend, l.cacheEnd, sec); ok {
l.cacheStart = estart
l.cacheEnd = eend
// Find the zone that is returned by tzset,
// the last transition is not always the correct zone.
for i, z := range l.zone {
if z.name == name {
zoneIdx = uint8(i)
break
}
}
}
} }
l.cacheZone = &l.zone[tx[i].index] l.cacheZone = &l.zone[zoneIdx]
break
} }
} }

View File

@ -189,6 +189,25 @@ func TestMalformedTZData(t *testing.T) {
} }
} }
func TestLoadLocationFromTZDataSlim(t *testing.T) {
// A 2020b slim tzdata for Europe/Berlin
tzData := "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffo\xa2a\xf8\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xffѶ\x96\x00\xff\xff\xff\xff\xd2X\xbe\x80\xff\xff\xff\xffҡO\x10\xff\xff\xff\xff\xd3c\x1b\x90\xff\xff\xff\xff\xd4K#\x90\xff\xff\xff\xff\xd59\xd1 \xff\xff\xff\xff\xd5g\xe7\x90\xff\xff\xff\xffըs\x00\xff\xff\xff\xff\xd6)\xb4\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#<E\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%\x1c'\x10\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'\x05C\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00(\xe5%\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xc5\a\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xa4\xe9\x90\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\x84ː\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000d\xad\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00\f\x88\x00\x00\x00\x00\x1c \x01\x04\x00\x00\x0e\x10\x00\t\x00\x00*0\x01\rLMT\x00CEST\x00CET\x00CEMT\x00\nCET-1CEST,M3.5.0,M10.5.0/3\n"
reference, err := time.LoadLocationFromTZData("Europe/Berlin", []byte(tzData))
if err != nil {
t.Fatal(err)
}
d := time.Date(2020, time.October, 29, 15, 30, 0, 0, reference)
tzName, tzOffset := d.Zone()
if want := "CET"; tzName != want {
t.Errorf("Zone name == %s, want %s", tzName, want)
}
if want := 3600; tzOffset != want {
t.Errorf("Zone offset == %d, want %d", tzOffset, want)
}
}
func TestTzset(t *testing.T) { func TestTzset(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
inStr string inStr string