| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package typeutil_test |
| |
| // TODO(adonovan): |
| // - test use of explicit hasher across two maps. |
| // - test hashcodes are consistent with equals for a range of types |
| // (e.g. all types generated by type-checking some body of real code). |
| |
| import ( |
| "go/types" |
| "testing" |
| |
| "golang.org/x/tools/go/types/typeutil" |
| ) |
| |
| var ( |
| tStr = types.Typ[types.String] // string |
| tPStr1 = types.NewPointer(tStr) // *string |
| tPStr2 = types.NewPointer(tStr) // *string, again |
| tInt = types.Typ[types.Int] // int |
| tChanInt1 = types.NewChan(types.RecvOnly, tInt) // <-chan int |
| tChanInt2 = types.NewChan(types.RecvOnly, tInt) // <-chan int, again |
| ) |
| |
| func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) { |
| if !types.Identical(x, y) { |
| t.Errorf("%s: not equal: %s, %s", comment, x, y) |
| } |
| if x == y { |
| t.Errorf("%s: identical: %v, %v", comment, x, y) |
| } |
| } |
| |
| func TestAxioms(t *testing.T) { |
| checkEqualButNotIdentical(t, tPStr1, tPStr2, "tPstr{1,2}") |
| checkEqualButNotIdentical(t, tChanInt1, tChanInt2, "tChanInt{1,2}") |
| } |
| |
| func TestMap(t *testing.T) { |
| var tmap *typeutil.Map |
| |
| // All methods but Set are safe on on (*T)(nil). |
| tmap.Len() |
| tmap.At(tPStr1) |
| tmap.Delete(tPStr1) |
| tmap.KeysString() |
| _ = tmap.String() |
| |
| tmap = new(typeutil.Map) |
| |
| // Length of empty map. |
| if l := tmap.Len(); l != 0 { |
| t.Errorf("Len() on empty Map: got %d, want 0", l) |
| } |
| // At of missing key. |
| if v := tmap.At(tPStr1); v != nil { |
| t.Errorf("At() on empty Map: got %v, want nil", v) |
| } |
| // Deletion of missing key. |
| if tmap.Delete(tPStr1) { |
| t.Errorf("Delete() on empty Map: got true, want false") |
| } |
| // Set of new key. |
| if prev := tmap.Set(tPStr1, "*string"); prev != nil { |
| t.Errorf("Set() on empty Map returned non-nil previous value %s", prev) |
| } |
| |
| // Now: {*string: "*string"} |
| |
| // Length of non-empty map. |
| if l := tmap.Len(); l != 1 { |
| t.Errorf("Len(): got %d, want 1", l) |
| } |
| // At via insertion key. |
| if v := tmap.At(tPStr1); v != "*string" { |
| t.Errorf("At(): got %q, want \"*string\"", v) |
| } |
| // At via equal key. |
| if v := tmap.At(tPStr2); v != "*string" { |
| t.Errorf("At(): got %q, want \"*string\"", v) |
| } |
| // Iteration over sole entry. |
| tmap.Iterate(func(key types.Type, value interface{}) { |
| if key != tPStr1 { |
| t.Errorf("Iterate: key: got %s, want %s", key, tPStr1) |
| } |
| if want := "*string"; value != want { |
| t.Errorf("Iterate: value: got %s, want %s", value, want) |
| } |
| }) |
| |
| // Setion with key equal to present one. |
| if prev := tmap.Set(tPStr2, "*string again"); prev != "*string" { |
| t.Errorf("Set() previous value: got %s, want \"*string\"", prev) |
| } |
| |
| // Setion of another association. |
| if prev := tmap.Set(tChanInt1, "<-chan int"); prev != nil { |
| t.Errorf("Set() previous value: got %s, want nil", prev) |
| } |
| |
| // Now: {*string: "*string again", <-chan int: "<-chan int"} |
| |
| want1 := "{*string: \"*string again\", <-chan int: \"<-chan int\"}" |
| want2 := "{<-chan int: \"<-chan int\", *string: \"*string again\"}" |
| if s := tmap.String(); s != want1 && s != want2 { |
| t.Errorf("String(): got %s, want %s", s, want1) |
| } |
| |
| want1 = "{*string, <-chan int}" |
| want2 = "{<-chan int, *string}" |
| if s := tmap.KeysString(); s != want1 && s != want2 { |
| t.Errorf("KeysString(): got %s, want %s", s, want1) |
| } |
| |
| // Keys(). |
| I := types.Identical |
| switch k := tmap.Keys(); { |
| case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok |
| case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok |
| default: |
| t.Errorf("Keys(): got %v, want %s", k, want2) |
| } |
| |
| if l := tmap.Len(); l != 2 { |
| t.Errorf("Len(): got %d, want 1", l) |
| } |
| // At via original key. |
| if v := tmap.At(tPStr1); v != "*string again" { |
| t.Errorf("At(): got %q, want \"*string again\"", v) |
| } |
| hamming := 1 |
| tmap.Iterate(func(key types.Type, value interface{}) { |
| switch { |
| case I(key, tChanInt1): |
| hamming *= 2 // ok |
| case I(key, tPStr1): |
| hamming *= 3 // ok |
| } |
| }) |
| if hamming != 6 { |
| t.Errorf("Iterate: hamming: got %d, want %d", hamming, 6) |
| } |
| |
| if v := tmap.At(tChanInt2); v != "<-chan int" { |
| t.Errorf("At(): got %q, want \"<-chan int\"", v) |
| } |
| // Deletion with key equal to present one. |
| if !tmap.Delete(tChanInt2) { |
| t.Errorf("Delete() of existing key: got false, want true") |
| } |
| |
| // Now: {*string: "*string again"} |
| |
| if l := tmap.Len(); l != 1 { |
| t.Errorf("Len(): got %d, want 1", l) |
| } |
| // Deletion again. |
| if !tmap.Delete(tPStr2) { |
| t.Errorf("Delete() of existing key: got false, want true") |
| } |
| |
| // Now: {} |
| |
| if l := tmap.Len(); l != 0 { |
| t.Errorf("Len(): got %d, want %d", l, 0) |
| } |
| if s := tmap.String(); s != "{}" { |
| t.Errorf("Len(): got %q, want %q", s, "") |
| } |
| } |