package vals import ( "reflect" "testing" ) // Tester is a helper for testing properties of a value. type Tester struct { t *testing.T v any } // TestValue returns a ValueTester. func TestValue(t *testing.T, v any) Tester { // Hack to get test coverage on the marker method IsStructMap, which is // never invoked. if m, ok := v.(StructMap); ok { m.IsStructMap() } return Tester{t, v} } // Kind tests the Kind of the value. func (vt Tester) Kind(wantKind string) Tester { vt.t.Helper() kind := Kind(vt.v) if kind != wantKind { vt.t.Errorf("Kind(v) = %s, want %s", kind, wantKind) } return vt } // Bool tests the Boool of the value. func (vt Tester) Bool(wantBool bool) Tester { vt.t.Helper() b := Bool(vt.v) if b != wantBool { vt.t.Errorf("Bool(v) = %v, want %v", b, wantBool) } return vt } // Hash tests the Hash of the value. func (vt Tester) Hash(wantHash uint32) Tester { vt.t.Helper() hash := Hash(vt.v) if hash != wantHash { vt.t.Errorf("Hash(v) = %v, want %v", hash, wantHash) } return vt } // Len tests the Len of the value. func (vt Tester) Len(wantLen int) Tester { vt.t.Helper() kind := Len(vt.v) if kind != wantLen { vt.t.Errorf("Len(v) = %v, want %v", kind, wantLen) } return vt } // Repr tests the Repr of the value. func (vt Tester) Repr(wantRepr string) Tester { vt.t.Helper() kind := ReprPlain(vt.v) if kind != wantRepr { vt.t.Errorf("Repr(v) = %s, want %s", kind, wantRepr) } return vt } // Equal tests that the value is Equal to every of the given values. func (vt Tester) Equal(others ...any) Tester { vt.t.Helper() for _, other := range others { eq := Equal(vt.v, other) if !eq { vt.t.Errorf("Equal(v, %v) = false, want true", other) } } return vt } // NotEqual tests that the value is not Equal to any of the given values. func (vt Tester) NotEqual(others ...any) Tester { vt.t.Helper() for _, other := range others { eq := Equal(vt.v, other) if eq { vt.t.Errorf("Equal(v, %v) = true, want false", other) } } return vt } // HasKey tests that the value has each of the given keys. func (vt Tester) HasKey(keys ...any) Tester { vt.t.Helper() for _, key := range keys { has := HasKey(vt.v, key) if !has { vt.t.Errorf("HasKey(v, %v) = false, want true", key) } } return vt } // HasNoKey tests that the value does not have any of the given keys. func (vt Tester) HasNoKey(keys ...any) Tester { vt.t.Helper() for _, key := range keys { has := HasKey(vt.v, key) if has { vt.t.Errorf("HasKey(v, %v) = true, want false", key) } } return vt } // AllKeys tests that the given keys match what the result of IterateKeys on the // value. // // NOTE: This now checks equality using reflect.DeepEqual, since all the builtin // types have string keys. This can be changed in future to use Equal is the // need arises. func (vt Tester) AllKeys(wantKeys ...any) Tester { vt.t.Helper() keys, err := collectKeys(vt.v) if err != nil { vt.t.Errorf("IterateKeys(v, f) -> err %v, want nil", err) } if !reflect.DeepEqual(keys, wantKeys) { vt.t.Errorf("IterateKeys(v, f) calls f with %v, want %v", keys, wantKeys) } return vt } func collectKeys(v any) ([]any, error) { var keys []any err := IterateKeys(v, func(k any) bool { keys = append(keys, k) return true }) return keys, err } // Index tests that Index'ing the value with the given key returns the wanted value // and no error. func (vt Tester) Index(key, wantVal any) Tester { vt.t.Helper() got, err := Index(vt.v, key) if err != nil { vt.t.Errorf("Index(v, %v) -> err %v, want nil", key, err) } if !Equal(got, wantVal) { vt.t.Errorf("Index(v, %v) -> %v, want %v", key, got, wantVal) } return vt } // IndexError tests that Index'ing the value with the given key returns the given // error. func (vt Tester) IndexError(key any, wantErr error) Tester { vt.t.Helper() _, err := Index(vt.v, key) if !reflect.DeepEqual(err, wantErr) { vt.t.Errorf("Index(v, %v) -> err %v, want %v", key, err, wantErr) } return vt } // Assoc tests that Assoc'ing the value with the given key-value pair returns // the wanted new value and no error. func (vt Tester) Assoc(key, val, wantNew any) Tester { vt.t.Helper() got, err := Assoc(vt.v, key, val) if err != nil { vt.t.Errorf("Assoc(v, %v) -> err %v, want nil", key, err) } if !Equal(got, wantNew) { vt.t.Errorf("Assoc(v, %v) -> %v, want %v", key, got, wantNew) } return vt } // AssocError tests that Assoc'ing the value with the given key-value pair // returns the given error. func (vt Tester) AssocError(key, val any, wantErr error) Tester { vt.t.Helper() _, err := Assoc(vt.v, key, val) if !reflect.DeepEqual(err, wantErr) { vt.t.Errorf("Assoc(v, %v) -> err %v, want %v", key, err, wantErr) } return vt }