github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/settings/settings_test.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package settings_test
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  	"unicode"
    20  
    21  	"github.com/cockroachdb/cockroach/pkg/settings"
    22  	"github.com/cockroachdb/cockroach/pkg/testutils"
    23  	"github.com/cockroachdb/cockroach/pkg/util/protoutil"
    24  	"github.com/cockroachdb/errors"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  type dummy struct {
    29  	msg1       string
    30  	growsbyone string
    31  }
    32  
    33  func (d *dummy) Unmarshal(data []byte) error {
    34  	s := string(data)
    35  	parts := strings.Split(s, ".")
    36  	if len(parts) != 2 {
    37  		return errors.Errorf("expected two parts, not %v", parts)
    38  	}
    39  	*d = dummy{
    40  		msg1: parts[0], growsbyone: parts[1],
    41  	}
    42  	return nil
    43  }
    44  
    45  func (d *dummy) Marshal() ([]byte, error) {
    46  	if c := d.msg1 + d.growsbyone; strings.Contains(c, ".") {
    47  		return nil, errors.Newf("must not contain dots: %s", c)
    48  	}
    49  	return []byte(d.msg1 + "." + d.growsbyone), nil
    50  }
    51  
    52  func (d *dummy) MarshalTo(data []byte) (int, error) {
    53  	encoded, err := d.Marshal()
    54  	if err != nil {
    55  		return 0, err
    56  	}
    57  	return copy(data, encoded), nil
    58  }
    59  
    60  func (d *dummy) Size() int {
    61  	encoded, _ := d.Marshal()
    62  	return len(encoded)
    63  }
    64  
    65  // implement the protoutil.Message interface
    66  func (d *dummy) ProtoMessage() {}
    67  func (d *dummy) Reset()        { *d = dummy{} }
    68  func (d *dummy) String() string {
    69  	return fmt.Sprintf("&{%s %s}", d.msg1, d.growsbyone)
    70  }
    71  
    72  type dummyTransformer struct{}
    73  
    74  var _ settings.StateMachineSettingImpl = &dummyTransformer{}
    75  
    76  func (d *dummyTransformer) Decode(val []byte) (interface{}, error) {
    77  	var oldD dummy
    78  	if err := protoutil.Unmarshal(val, &oldD); err != nil {
    79  		return nil, err
    80  	}
    81  	return oldD, nil
    82  }
    83  
    84  func (d *dummyTransformer) DecodeToString(val []byte) (string, error) {
    85  	dum, err := d.Decode(val)
    86  	if err != nil {
    87  		return "", err
    88  	}
    89  	return fmt.Sprintf("%v", dum), nil
    90  }
    91  
    92  func (d *dummyTransformer) ValidateLogical(
    93  	ctx context.Context, sv *settings.Values, old []byte, newV string,
    94  ) ([]byte, error) {
    95  	var oldD dummy
    96  	if err := protoutil.Unmarshal(old, &oldD); err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	// We have a new proposed update to the value, validate it.
   101  	if len(newV) != len(oldD.growsbyone)+1 {
   102  		return nil, errors.New("dashes component must grow by exactly one")
   103  	}
   104  	newD := oldD
   105  	newD.growsbyone = newV
   106  	b, err := newD.Marshal()
   107  	return b, err
   108  }
   109  
   110  func (d *dummyTransformer) ValidateGossipUpdate(
   111  	ctx context.Context, sv *settings.Values, val []byte,
   112  ) error {
   113  	var updateVal dummy
   114  	return protoutil.Unmarshal(val, &updateVal)
   115  }
   116  
   117  func (d *dummyTransformer) SettingsListDefault() string {
   118  	panic("unimplemented")
   119  }
   120  
   121  // BeforeChange is part of the StateMachineSettingImpl interface.
   122  func (d *dummyTransformer) BeforeChange(
   123  	ctx context.Context, encodedVal []byte, sv *settings.Values,
   124  ) {
   125  	// noop
   126  }
   127  
   128  const mb = int64(1024 * 1024)
   129  
   130  var changes = struct {
   131  	boolTA   int
   132  	strFooA  int
   133  	i1A      int
   134  	fA       int
   135  	dA       int
   136  	eA       int
   137  	byteSize int
   138  	mA       int
   139  }{}
   140  
   141  var boolTA = settings.RegisterBoolSetting("bool.t", "desc", true)
   142  var boolFA = settings.RegisterBoolSetting("bool.f", "desc", false)
   143  var strFooA = settings.RegisterStringSetting("str.foo", "desc", "")
   144  var strBarA = settings.RegisterStringSetting("str.bar", "desc", "bar")
   145  var i1A = settings.RegisterIntSetting("i.1", "desc", 0)
   146  var i2A = settings.RegisterIntSetting("i.2", "desc", 5)
   147  var fA = settings.RegisterFloatSetting("f", "desc", 5.4)
   148  var dA = settings.RegisterDurationSetting("d", "desc", time.Second)
   149  var _ = settings.RegisterPublicNonNegativeDurationSettingWithMaximum("d_with_maximum", "desc", time.Second, time.Hour)
   150  var eA = settings.RegisterEnumSetting("e", "desc", "foo", map[int64]string{1: "foo", 2: "bar", 3: "baz"})
   151  var byteSize = settings.RegisterByteSizeSetting("zzz", "desc", mb)
   152  var mA = settings.RegisterStateMachineSettingImpl("statemachine", "foo", &dummyTransformer{})
   153  
   154  func init() {
   155  	settings.RegisterBoolSetting("sekretz", "desc", false).SetReportable(false)
   156  	settings.RegisterBoolSetting("rezervedz", "desc", false).SetVisibility(settings.Reserved)
   157  }
   158  
   159  var strVal = settings.RegisterValidatedStringSetting(
   160  	"str.val", "desc", "", func(sv *settings.Values, v string) error {
   161  		for _, c := range v {
   162  			if !unicode.IsLetter(c) {
   163  				return errors.Errorf("not all runes of %s are letters: %c", v, c)
   164  			}
   165  		}
   166  		return nil
   167  	})
   168  var dVal = settings.RegisterNonNegativeDurationSetting("dVal", "desc", time.Second)
   169  var fVal = settings.RegisterNonNegativeFloatSetting("fVal", "desc", 5.4)
   170  var byteSizeVal = settings.RegisterValidatedByteSizeSetting(
   171  	"byteSize.Val", "desc", mb, func(v int64) error {
   172  		if v < 0 {
   173  			return errors.Errorf("bytesize cannot be negative")
   174  		}
   175  		return nil
   176  	})
   177  var iVal = settings.RegisterValidatedIntSetting(
   178  	"i.Val", "desc", 0, func(v int64) error {
   179  		if v < 0 {
   180  			return errors.Errorf("int cannot be negative")
   181  		}
   182  		return nil
   183  	})
   184  
   185  func TestValidation(t *testing.T) {
   186  	sv := &settings.Values{}
   187  	sv.Init(settings.TestOpaque)
   188  
   189  	u := settings.NewUpdater(sv)
   190  	t.Run("d_with_maximum", func(t *testing.T) {
   191  		err := u.Set("d_with_maximum", "1h", "d")
   192  		require.NoError(t, err)
   193  		err = u.Set("d_with_maximum", "0h", "d")
   194  		require.NoError(t, err)
   195  		err = u.Set("d_with_maximum", "30m", "d")
   196  		require.NoError(t, err)
   197  
   198  		err = u.Set("d_with_maximum", "-1m", "d")
   199  		require.Error(t, err)
   200  		err = u.Set("d_with_maximum", "1h1s", "d")
   201  		require.Error(t, err)
   202  	})
   203  }
   204  
   205  func TestCache(t *testing.T) {
   206  	ctx := context.Background()
   207  	sv := &settings.Values{}
   208  	sv.Init(settings.TestOpaque)
   209  
   210  	boolTA.SetOnChange(sv, func() { changes.boolTA++ })
   211  	strFooA.SetOnChange(sv, func() { changes.strFooA++ })
   212  	i1A.SetOnChange(sv, func() { changes.i1A++ })
   213  	fA.SetOnChange(sv, func() { changes.fA++ })
   214  	dA.SetOnChange(sv, func() { changes.dA++ })
   215  	eA.SetOnChange(sv, func() { changes.eA++ })
   216  	byteSize.SetOnChange(sv, func() { changes.byteSize++ })
   217  	mA.SetOnChange(sv, func() { changes.mA++ })
   218  
   219  	t.Run("StateMachineSetting", func(t *testing.T) {
   220  		u := settings.NewUpdater(sv)
   221  		mB := settings.RegisterStateMachineSettingImpl("local.m", "foo", &dummyTransformer{})
   222  		// State-machine settings don't have defaults, so we need to start by
   223  		// setting it to something.
   224  		if err := u.Set("local.m", "default.X", "m"); err != nil {
   225  			t.Fatal(err)
   226  		}
   227  
   228  		if exp, act := "{default X}", mB.String(sv); exp != act {
   229  			t.Fatalf("wanted %q, got %q", exp, act)
   230  		}
   231  
   232  		growsTooFast := "grows too fast"
   233  		curVal := []byte(mB.Get(sv))
   234  		if _, err := mB.Validate(ctx, sv, curVal, growsTooFast); !testutils.IsError(err,
   235  			"must grow by exactly one") {
   236  			t.Fatal(err)
   237  		}
   238  
   239  		hasDots := "a."
   240  		if _, err := mB.Validate(ctx, sv, curVal, hasDots); !testutils.IsError(err,
   241  			"must not contain dots") {
   242  			t.Fatal(err)
   243  		}
   244  
   245  		ab := "ab"
   246  		if _, err := mB.Validate(ctx, sv, curVal, ab); err != nil {
   247  			t.Fatal(err)
   248  		}
   249  
   250  		if _, err := mB.Validate(ctx, sv, []byte("takes.precedence"), ab); !testutils.IsError(err,
   251  			"must grow by exactly one") {
   252  			t.Fatal(err)
   253  		}
   254  		precedenceX := "precedencex"
   255  		if _, err := mB.Validate(ctx, sv, []byte("takes.precedence"), precedenceX); err != nil {
   256  			t.Fatal(err)
   257  		}
   258  		if err := u.Set("local.m", "default.XX", "m"); err != nil {
   259  			t.Fatal(err)
   260  		}
   261  		u.ResetRemaining()
   262  		if exp, act := "{default XX}", mB.String(sv); exp != act {
   263  			t.Fatalf("wanted %q, got %q", exp, act)
   264  		}
   265  	})
   266  
   267  	t.Run("defaults", func(t *testing.T) {
   268  		if expected, actual := false, boolFA.Get(sv); expected != actual {
   269  			t.Fatalf("expected %v, got %v", expected, actual)
   270  		}
   271  		if expected, actual := true, boolTA.Get(sv); expected != actual {
   272  			t.Fatalf("expected %v, got %v", expected, actual)
   273  		}
   274  		if expected, actual := "", strFooA.Get(sv); expected != actual {
   275  			t.Fatalf("expected %v, got %v", expected, actual)
   276  		}
   277  		if expected, actual := "bar", strBarA.Get(sv); expected != actual {
   278  			t.Fatalf("expected %v, got %v", expected, actual)
   279  		}
   280  		if expected, actual := "", strVal.Get(sv); expected != actual {
   281  			t.Fatalf("expected %v, got %v", expected, actual)
   282  		}
   283  		if expected, actual := int64(0), i1A.Get(sv); expected != actual {
   284  			t.Fatalf("expected %v, got %v", expected, actual)
   285  		}
   286  		if expected, actual := int64(5), i2A.Get(sv); expected != actual {
   287  			t.Fatalf("expected %v, got %v", expected, actual)
   288  		}
   289  		if expected, actual := int64(0), iVal.Get(sv); expected != actual {
   290  			t.Fatalf("expected %v, got %v", expected, actual)
   291  		}
   292  		if expected, actual := 5.4, fA.Get(sv); expected != actual {
   293  			t.Fatalf("expected %v, got %v", expected, actual)
   294  		}
   295  		if expected, actual := 5.4, fVal.Get(sv); expected != actual {
   296  			t.Fatalf("expected %v, got %v", expected, actual)
   297  		}
   298  		if expected, actual := time.Second, dA.Get(sv); expected != actual {
   299  			t.Fatalf("expected %v, got %v", expected, actual)
   300  		}
   301  		if expected, actual := time.Second, dVal.Get(sv); expected != actual {
   302  			t.Fatalf("expected %v, got %v", expected, actual)
   303  		}
   304  		if expected, actual := mb, byteSize.Get(sv); expected != actual {
   305  			t.Fatalf("expected %v, got %v", expected, actual)
   306  		}
   307  		if expected, actual := mb, byteSizeVal.Get(sv); expected != actual {
   308  			t.Fatalf("expected %v, got %v", expected, actual)
   309  		}
   310  		if expected, actual := int64(1), eA.Get(sv); expected != actual {
   311  			t.Fatalf("expected %v, got %v", expected, actual)
   312  		}
   313  		// Note that we don't test the state-machine setting for a default, since it
   314  		// doesn't have one and it would crash.
   315  	})
   316  
   317  	t.Run("lookup", func(t *testing.T) {
   318  		if actual, ok := settings.Lookup("i.1", settings.LookupForLocalAccess); !ok || i1A != actual {
   319  			t.Fatalf("expected %v, got %v (exists: %v)", i1A, actual, ok)
   320  		}
   321  		if actual, ok := settings.Lookup("i.Val", settings.LookupForLocalAccess); !ok || iVal != actual {
   322  			t.Fatalf("expected %v, got %v (exists: %v)", iVal, actual, ok)
   323  		}
   324  		if actual, ok := settings.Lookup("f", settings.LookupForLocalAccess); !ok || fA != actual {
   325  			t.Fatalf("expected %v, got %v (exists: %v)", fA, actual, ok)
   326  		}
   327  		if actual, ok := settings.Lookup("fVal", settings.LookupForLocalAccess); !ok || fVal != actual {
   328  			t.Fatalf("expected %v, got %v (exists: %v)", fVal, actual, ok)
   329  		}
   330  		if actual, ok := settings.Lookup("d", settings.LookupForLocalAccess); !ok || dA != actual {
   331  			t.Fatalf("expected %v, got %v (exists: %v)", dA, actual, ok)
   332  		}
   333  		if actual, ok := settings.Lookup("dVal", settings.LookupForLocalAccess); !ok || dVal != actual {
   334  			t.Fatalf("expected %v, got %v (exists: %v)", dVal, actual, ok)
   335  		}
   336  		if actual, ok := settings.Lookup("e", settings.LookupForLocalAccess); !ok || eA != actual {
   337  			t.Fatalf("expected %v, got %v (exists: %v)", eA, actual, ok)
   338  		}
   339  		if actual, ok := settings.Lookup("statemachine", settings.LookupForLocalAccess); !ok || mA != actual {
   340  			t.Fatalf("expected %v, got %v (exists: %v)", mA, actual, ok)
   341  		}
   342  		if actual, ok := settings.Lookup("dne", settings.LookupForLocalAccess); ok {
   343  			t.Fatalf("expected nothing, got %v", actual)
   344  		}
   345  	})
   346  
   347  	t.Run("read and write each type", func(t *testing.T) {
   348  		u := settings.NewUpdater(sv)
   349  		if expected, actual := 0, changes.boolTA; expected != actual {
   350  			t.Fatalf("expected %d, got %d", expected, actual)
   351  		}
   352  		if err := u.Set("bool.t", settings.EncodeBool(false), "b"); err != nil {
   353  			t.Fatal(err)
   354  		}
   355  		if expected, actual := 1, changes.boolTA; expected != actual {
   356  			t.Fatalf("expected %d, got %d", expected, actual)
   357  		}
   358  		if err := u.Set("bool.f", settings.EncodeBool(true), "b"); err != nil {
   359  			t.Fatal(err)
   360  		}
   361  		if expected, actual := 0, changes.strFooA; expected != actual {
   362  			t.Fatalf("expected %d, got %d", expected, actual)
   363  		}
   364  		if err := u.Set("str.foo", "baz", "s"); err != nil {
   365  			t.Fatal(err)
   366  		}
   367  		if expected, actual := 1, changes.strFooA; expected != actual {
   368  			t.Fatalf("expected %d, got %d", expected, actual)
   369  		}
   370  		if err := u.Set("str.val", "valid", "s"); err != nil {
   371  			t.Fatal(err)
   372  		}
   373  		if err := u.Set("i.2", settings.EncodeInt(3), "i"); err != nil {
   374  			t.Fatal(err)
   375  		}
   376  		if expected, actual := 0, changes.fA; expected != actual {
   377  			t.Fatalf("expected %d, got %d", expected, actual)
   378  		}
   379  		if err := u.Set("f", settings.EncodeFloat(3.1), "f"); err != nil {
   380  			t.Fatal(err)
   381  		}
   382  		if expected, actual := 1, changes.fA; expected != actual {
   383  			t.Fatalf("expected %d, got %d", expected, actual)
   384  		}
   385  		if err := u.Set("fVal", settings.EncodeFloat(3.1), "f"); err != nil {
   386  			t.Fatal(err)
   387  		}
   388  		if expected, actual := 0, changes.dA; expected != actual {
   389  			t.Fatalf("expected %d, got %d", expected, actual)
   390  		}
   391  		if err := u.Set("d", settings.EncodeDuration(2*time.Hour), "d"); err != nil {
   392  			t.Fatal(err)
   393  		}
   394  		if expected, actual := 1, changes.dA; expected != actual {
   395  			t.Fatalf("expected %d, got %d", expected, actual)
   396  		}
   397  		if err := u.Set("dVal", settings.EncodeDuration(2*time.Hour), "d"); err != nil {
   398  			t.Fatal(err)
   399  		}
   400  		if expected, actual := 0, changes.byteSize; expected != actual {
   401  			t.Fatalf("expected %d, got %d", expected, actual)
   402  		}
   403  		if err := u.Set("zzz", settings.EncodeInt(mb*5), "z"); err != nil {
   404  			t.Fatal(err)
   405  		}
   406  		if expected, actual := 1, changes.byteSize; expected != actual {
   407  			t.Fatalf("expected %d, got %d", expected, actual)
   408  		}
   409  		if err := u.Set("byteSize.Val", settings.EncodeInt(mb*5), "z"); err != nil {
   410  			t.Fatal(err)
   411  		}
   412  		if expected, actual := 0, changes.mA; expected != actual {
   413  			t.Fatalf("expected %d, got %d", expected, actual)
   414  		}
   415  		if err := u.Set("statemachine", "default.AB", "m"); err != nil {
   416  			t.Fatal(err)
   417  		}
   418  		if expected, actual := 1, changes.mA; expected != actual {
   419  			t.Fatalf("expected %d, got %d", expected, actual)
   420  		}
   421  		if expected, actual := 0, changes.eA; expected != actual {
   422  			t.Fatalf("expected %d, got %d", expected, actual)
   423  		}
   424  		if err := u.Set("e", settings.EncodeInt(2), "e"); err != nil {
   425  			t.Fatal(err)
   426  		}
   427  		if expected, actual := 1, changes.eA; expected != actual {
   428  			t.Fatalf("expected %d, got %d", expected, actual)
   429  		}
   430  		if expected, err := "strconv.Atoi: parsing \"notAValidValue\": invalid syntax",
   431  			u.Set("e", "notAValidValue", "e"); !testutils.IsError(err, expected) {
   432  			t.Fatalf("expected '%s' != actual error '%s'", expected, err)
   433  		}
   434  		u.ResetRemaining()
   435  
   436  		if expected, actual := false, boolTA.Get(sv); expected != actual {
   437  			t.Fatalf("expected %v, got %v", expected, actual)
   438  		}
   439  		if expected, actual := true, boolFA.Get(sv); expected != actual {
   440  			t.Fatalf("expected %v, got %v", expected, actual)
   441  		}
   442  		if expected, actual := "baz", strFooA.Get(sv); expected != actual {
   443  			t.Fatalf("expected %v, got %v", expected, actual)
   444  		}
   445  		if expected, actual := "valid", strVal.Get(sv); expected != actual {
   446  			t.Fatalf("expected %v, got %v", expected, actual)
   447  		}
   448  		if expected, actual := int64(3), i2A.Get(sv); expected != actual {
   449  			t.Fatalf("expected %v, got %v", expected, actual)
   450  		}
   451  		if expected, actual := 3.1, fA.Get(sv); expected != actual {
   452  			t.Fatalf("expected %v, got %v", expected, actual)
   453  		}
   454  		if expected, actual := 3.1, fVal.Get(sv); expected != actual {
   455  			t.Fatalf("expected %v, got %v", expected, actual)
   456  		}
   457  		if expected, actual := 2*time.Hour, dA.Get(sv); expected != actual {
   458  			t.Fatalf("expected %v, got %v", expected, actual)
   459  		}
   460  		if expected, actual := 2*time.Hour, dVal.Get(sv); expected != actual {
   461  			t.Fatalf("expected %v, got %v", expected, actual)
   462  		}
   463  		if expected, actual := int64(2), eA.Get(sv); expected != actual {
   464  			t.Fatalf("expected %v, got %v", expected, actual)
   465  		}
   466  		if expected, actual := mb*5, byteSize.Get(sv); expected != actual {
   467  			t.Fatalf("expected %v, got %v", expected, actual)
   468  		}
   469  		if expected, actual := mb*5, byteSizeVal.Get(sv); expected != actual {
   470  			t.Fatalf("expected %v, got %v", expected, actual)
   471  		}
   472  		if expected, actual := "default.AB", mA.Get(sv); expected != actual {
   473  			t.Fatalf("expected %v, got %v", expected, actual)
   474  		}
   475  
   476  		// We didn't change this one, so should still see the default.
   477  		if expected, actual := "bar", strBarA.Get(sv); expected != actual {
   478  			t.Fatalf("expected %v, got %v", expected, actual)
   479  		}
   480  	})
   481  
   482  	t.Run("any setting not included in an Updater reverts to default", func(t *testing.T) {
   483  		{
   484  			u := settings.NewUpdater(sv)
   485  			if err := u.Set("bool.f", settings.EncodeBool(true), "b"); err != nil {
   486  				t.Fatal(err)
   487  			}
   488  			if expected, actual := 0, changes.i1A; expected != actual {
   489  				t.Fatalf("expected %d, got %d", expected, actual)
   490  			}
   491  			if err := u.Set("i.1", settings.EncodeInt(1), "i"); err != nil {
   492  				t.Fatal(err)
   493  			}
   494  			if expected, actual := 1, changes.i1A; expected != actual {
   495  				t.Fatalf("expected %d, got %d", expected, actual)
   496  			}
   497  			if err := u.Set("i.2", settings.EncodeInt(7), "i"); err != nil {
   498  				t.Fatal(err)
   499  			}
   500  			if err := u.Set("i.Val", settings.EncodeInt(1), "i"); err != nil {
   501  				t.Fatal(err)
   502  			}
   503  			u.ResetRemaining()
   504  		}
   505  
   506  		if expected, actual := true, boolFA.Get(sv); expected != actual {
   507  			t.Fatalf("expected %v, got %v", expected, actual)
   508  		}
   509  		// If the updater doesn't have a key, e.g. if the setting has been deleted,
   510  		// Doneing it from the cache.
   511  		settings.NewUpdater(sv).ResetRemaining()
   512  
   513  		if expected, actual := 2, changes.boolTA; expected != actual {
   514  			t.Fatalf("expected %d, got %d", expected, actual)
   515  		}
   516  
   517  		if expected, actual := 2, changes.i1A; expected != actual {
   518  			t.Fatalf("expected %d, got %d", expected, actual)
   519  		}
   520  
   521  		if expected, actual := false, boolFA.Get(sv); expected != actual {
   522  			t.Fatalf("expected %v, got %v", expected, actual)
   523  		}
   524  
   525  		if expected, actual := false, boolFA.Get(sv); expected != actual {
   526  			t.Fatalf("expected %v, got %v", expected, actual)
   527  		}
   528  	})
   529  
   530  	t.Run("an invalid update to a given setting preserves its previously set value", func(t *testing.T) {
   531  		{
   532  			u := settings.NewUpdater(sv)
   533  			if err := u.Set("i.2", settings.EncodeInt(9), "i"); err != nil {
   534  				t.Fatal(err)
   535  			}
   536  			u.ResetRemaining()
   537  		}
   538  		before := i2A.Get(sv)
   539  
   540  		// Doneing after attempting to set with wrong type preserves the current
   541  		// value.
   542  		{
   543  			u := settings.NewUpdater(sv)
   544  			if err := u.Set("i.2", settings.EncodeBool(false), "b"); !testutils.IsError(err,
   545  				"setting 'i.2' defined as type i, not b",
   546  			) {
   547  				t.Fatal(err)
   548  			}
   549  			u.ResetRemaining()
   550  		}
   551  
   552  		if expected, actual := before, i2A.Get(sv); expected != actual {
   553  			t.Fatalf("expected %v, got %v", expected, actual)
   554  		}
   555  
   556  		// Doneing after attempting to set with the wrong type preserves the
   557  		// current value.
   558  		{
   559  			u := settings.NewUpdater(sv)
   560  			if err := u.Set("i.2", settings.EncodeBool(false), "i"); !testutils.IsError(err,
   561  				"strconv.Atoi: parsing \"false\": invalid syntax",
   562  			) {
   563  				t.Fatal(err)
   564  			}
   565  			u.ResetRemaining()
   566  		}
   567  
   568  		if expected, actual := before, i2A.Get(sv); expected != actual {
   569  			t.Fatalf("expected %v, got %v", expected, actual)
   570  		}
   571  
   572  		// Doneing after attempting to set with invalid value preserves the
   573  		// current value.
   574  		beforestrVal := strVal.Get(sv)
   575  		{
   576  			u := settings.NewUpdater(sv)
   577  			if err := u.Set("str.val", "abc2def", "s"); !testutils.IsError(err,
   578  				"not all runes of abc2def are letters: 2",
   579  			) {
   580  				t.Fatal(err)
   581  			}
   582  			u.ResetRemaining()
   583  		}
   584  		if expected, actual := beforestrVal, strVal.Get(sv); expected != actual {
   585  			t.Fatalf("expected %v, got %v", expected, actual)
   586  		}
   587  
   588  		beforeDVal := dVal.Get(sv)
   589  		{
   590  			u := settings.NewUpdater(sv)
   591  			if err := u.Set("dVal", settings.EncodeDuration(-time.Hour), "d"); !testutils.IsError(err,
   592  				"cannot set dVal to a negative duration: -1h0m0s",
   593  			) {
   594  				t.Fatal(err)
   595  			}
   596  			u.ResetRemaining()
   597  		}
   598  		if expected, actual := beforeDVal, dVal.Get(sv); expected != actual {
   599  			t.Fatalf("expected %v, got %v", expected, actual)
   600  		}
   601  
   602  		beforeByteSizeVal := byteSizeVal.Get(sv)
   603  		{
   604  			u := settings.NewUpdater(sv)
   605  			if err := u.Set("byteSize.Val", settings.EncodeInt(-mb), "z"); !testutils.IsError(err,
   606  				"bytesize cannot be negative",
   607  			) {
   608  				t.Fatal(err)
   609  			}
   610  			u.ResetRemaining()
   611  		}
   612  		if expected, actual := beforeByteSizeVal, byteSizeVal.Get(sv); expected != actual {
   613  			t.Fatalf("expected %v, got %v", expected, actual)
   614  		}
   615  
   616  		beforeFVal := fVal.Get(sv)
   617  		{
   618  			u := settings.NewUpdater(sv)
   619  			if err := u.Set("fVal", settings.EncodeFloat(-1.1), "f"); !testutils.IsError(err,
   620  				"cannot set fVal to a negative value: -1.1",
   621  			) {
   622  				t.Fatal(err)
   623  			}
   624  			u.ResetRemaining()
   625  		}
   626  		if expected, actual := beforeFVal, fVal.Get(sv); expected != actual {
   627  			t.Fatalf("expected %v, got %v", expected, actual)
   628  		}
   629  
   630  		beforeIVal := iVal.Get(sv)
   631  		{
   632  			u := settings.NewUpdater(sv)
   633  			if err := u.Set("i.Val", settings.EncodeInt(-1), "i"); !testutils.IsError(err,
   634  				"int cannot be negative",
   635  			) {
   636  				t.Fatal(err)
   637  			}
   638  			u.ResetRemaining()
   639  		}
   640  		if expected, actual := beforeIVal, iVal.Get(sv); expected != actual {
   641  			t.Fatalf("expected %v, got %v", expected, actual)
   642  		}
   643  
   644  		beforeMarsh := mA.Get(sv)
   645  		{
   646  			u := settings.NewUpdater(sv)
   647  			if err := u.Set("statemachine", "too.many.dots", "m"); !testutils.IsError(err,
   648  				"expected two parts",
   649  			) {
   650  				t.Fatal(err)
   651  			}
   652  			u.ResetRemaining()
   653  		}
   654  		if expected, actual := beforeMarsh, mA.Get(sv); expected != actual {
   655  			t.Fatalf("expected %v, got %v", expected, actual)
   656  		}
   657  	})
   658  
   659  }
   660  
   661  func TestIsReportable(t *testing.T) {
   662  	if v, ok := settings.Lookup("bool.t", settings.LookupForLocalAccess); !ok || !settings.TestingIsReportable(v) {
   663  		t.Errorf("expected 'bool.t' to be marked as isReportable() = true")
   664  	}
   665  	if v, ok := settings.Lookup("sekretz", settings.LookupForLocalAccess); !ok || settings.TestingIsReportable(v) {
   666  		t.Errorf("expected 'sekretz' to be marked as isReportable() = false")
   667  	}
   668  }
   669  
   670  func TestOnChangeWithMaxSettings(t *testing.T) {
   671  	// Register MaxSettings settings to ensure that no errors occur.
   672  	maxName, err := batchRegisterSettings(t, t.Name(), settings.MaxSettings-settings.NumRegisteredSettings())
   673  	if err != nil {
   674  		t.Fatalf("expected no error to register %d settings, but get error: %v", settings.MaxSettings, err)
   675  	}
   676  
   677  	// Change the max slotIdx setting to ensure that no errors occur.
   678  	sv := &settings.Values{}
   679  	sv.Init(settings.TestOpaque)
   680  	var changes int
   681  	s, ok := settings.Lookup(maxName, settings.LookupForLocalAccess)
   682  	if !ok {
   683  		t.Fatalf("expected lookup of %s to succeed", maxName)
   684  	}
   685  	intSetting, ok := s.(*settings.IntSetting)
   686  	if !ok {
   687  		t.Fatalf("expected int setting, got %T", s)
   688  	}
   689  	intSetting.SetOnChange(sv, func() { changes++ })
   690  
   691  	u := settings.NewUpdater(sv)
   692  	if err := u.Set(maxName, settings.EncodeInt(9), "i"); err != nil {
   693  		t.Fatal(err)
   694  	}
   695  
   696  	if changes != 1 {
   697  		t.Errorf("expected the max slot setting changed")
   698  	}
   699  }
   700  
   701  func TestMaxSettingsPanics(t *testing.T) {
   702  	defer settings.TestingSaveRegistry()()
   703  
   704  	// Register too many settings which will cause a panic which is caught and converted to an error.
   705  	_, err := batchRegisterSettings(t, t.Name(),
   706  		settings.MaxSettings-settings.NumRegisteredSettings()+1)
   707  	expectedErr := "too many settings; increase MaxSettings"
   708  	if !testutils.IsError(err, expectedErr) {
   709  		t.Errorf("expected error %v, but got %v", expectedErr, err)
   710  	}
   711  
   712  }
   713  
   714  func batchRegisterSettings(t *testing.T, keyPrefix string, count int) (name string, err error) {
   715  	defer func() {
   716  		// Catch panic and convert it to an error.
   717  		if r := recover(); r != nil {
   718  			if panicErr, ok := r.(error); ok {
   719  				err = errors.WithStackDepth(panicErr, 1)
   720  			} else {
   721  				err = errors.NewWithDepthf(1, "panic: %v", r)
   722  			}
   723  		}
   724  	}()
   725  	for i := 0; i < count; i++ {
   726  		name = fmt.Sprintf("%s_%3d", keyPrefix, i)
   727  		settings.RegisterValidatedIntSetting(name, "desc", 0, nil)
   728  	}
   729  	return name, err
   730  }
   731  
   732  var overrideBool = settings.RegisterBoolSetting("override.bool", "desc", true)
   733  var overrideInt = settings.RegisterIntSetting("override.int", "desc", 0)
   734  var overrideDuration = settings.RegisterDurationSetting("override.duration", "desc", time.Second)
   735  var overrideFloat = settings.RegisterFloatSetting("override.float", "desc", 1.0)
   736  
   737  func TestOverride(t *testing.T) {
   738  	sv := &settings.Values{}
   739  	sv.Init(settings.TestOpaque)
   740  
   741  	// Test override for bool setting.
   742  	require.Equal(t, true, overrideBool.Get(sv))
   743  	overrideBool.Override(sv, false)
   744  	require.Equal(t, false, overrideBool.Get(sv))
   745  	u := settings.NewUpdater(sv)
   746  	u.ResetRemaining()
   747  	require.Equal(t, false, overrideBool.Get(sv))
   748  
   749  	// Test override for int setting.
   750  	require.Equal(t, int64(0), overrideInt.Get(sv))
   751  	overrideInt.Override(sv, 42)
   752  	require.Equal(t, int64(42), overrideInt.Get(sv))
   753  	u.ResetRemaining()
   754  	require.Equal(t, int64(42), overrideInt.Get(sv))
   755  
   756  	// Test override for duration setting.
   757  	require.Equal(t, time.Second, overrideDuration.Get(sv))
   758  	overrideDuration.Override(sv, 42*time.Second)
   759  	require.Equal(t, 42*time.Second, overrideDuration.Get(sv))
   760  	u.ResetRemaining()
   761  	require.Equal(t, 42*time.Second, overrideDuration.Get(sv))
   762  
   763  	// Test override for float setting.
   764  	require.Equal(t, 1.0, overrideFloat.Get(sv))
   765  	overrideFloat.Override(sv, 42.0)
   766  	require.Equal(t, 42.0, overrideFloat.Get(sv))
   767  	u.ResetRemaining()
   768  	require.Equal(t, 42.0, overrideFloat.Get(sv))
   769  }