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 }