1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package time_test
6
7import (
8	"reflect"
9	"testing"
10	"time"
11	_ "time/tzdata"
12)
13
14var zones = []string{
15	"Asia/Jerusalem",
16	"America/Los_Angeles",
17}
18
19func TestEmbeddedTZData(t *testing.T) {
20	undo := time.DisablePlatformSources()
21	defer undo()
22
23	for _, zone := range zones {
24		ref, err := time.LoadLocation(zone)
25		if err != nil {
26			t.Errorf("LoadLocation(%q): %v", zone, err)
27			continue
28		}
29
30		embedded, err := time.LoadFromEmbeddedTZData(zone)
31		if err != nil {
32			t.Errorf("LoadFromEmbeddedTZData(%q): %v", zone, err)
33			continue
34		}
35		sample, err := time.LoadLocationFromTZData(zone, []byte(embedded))
36		if err != nil {
37			t.Errorf("LoadLocationFromTZData failed for %q: %v", zone, err)
38			continue
39		}
40
41		// Compare the name and zone fields of ref and sample.
42		// The tx field changes faster as tzdata is updated.
43		// The cache fields are expected to differ.
44		v1 := reflect.ValueOf(ref).Elem()
45		v2 := reflect.ValueOf(sample).Elem()
46		typ := v1.Type()
47		nf := typ.NumField()
48		found := 0
49		for i := 0; i < nf; i++ {
50			ft := typ.Field(i)
51			if ft.Name != "name" && ft.Name != "zone" {
52				continue
53			}
54			found++
55			if !equal(t, v1.Field(i), v2.Field(i)) {
56				t.Errorf("zone %s: system and embedded tzdata field %s differs", zone, ft.Name)
57			}
58		}
59		if found != 2 {
60			t.Errorf("test must be updated for change to time.Location struct")
61		}
62	}
63}
64
65// equal is a small version of reflect.DeepEqual that we use to
66// compare the values of zoneinfo unexported fields.
67func equal(t *testing.T, f1, f2 reflect.Value) bool {
68	switch f1.Type().Kind() {
69	case reflect.Slice:
70		if f1.Len() != f2.Len() {
71			return false
72		}
73		for i := 0; i < f1.Len(); i++ {
74			if !equal(t, f1.Index(i), f2.Index(i)) {
75				return false
76			}
77		}
78		return true
79	case reflect.Struct:
80		nf := f1.Type().NumField()
81		for i := 0; i < nf; i++ {
82			if !equal(t, f1.Field(i), f2.Field(i)) {
83				return false
84			}
85		}
86		return true
87	case reflect.String:
88		return f1.String() == f2.String()
89	case reflect.Bool:
90		return f1.Bool() == f2.Bool()
91	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
92		return f1.Int() == f2.Int()
93	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
94		return f1.Uint() == f2.Uint()
95	default:
96		t.Errorf("test internal error: unsupported kind %v", f1.Type().Kind())
97		return true
98	}
99}
100