Source file src/unique/handle_test.go

     1  // Copyright 2024 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  
     5  package unique
     6  
     7  import (
     8  	"fmt"
     9  	"internal/abi"
    10  	"reflect"
    11  	"runtime"
    12  	"testing"
    13  )
    14  
    15  // Set up special types. Because the internal maps are sharded by type,
    16  // this will ensure that we're not overlapping with other tests.
    17  type testString string
    18  type testIntArray [4]int
    19  type testEface any
    20  type testStringArray [3]string
    21  type testStringStruct struct {
    22  	a string
    23  }
    24  type testStringStructArrayStruct struct {
    25  	s [2]testStringStruct
    26  }
    27  type testStruct struct {
    28  	z float64
    29  	b string
    30  }
    31  
    32  func TestHandle(t *testing.T) {
    33  	testHandle[testString](t, "foo")
    34  	testHandle[testString](t, "bar")
    35  	testHandle[testString](t, "")
    36  	testHandle[testIntArray](t, [4]int{7, 77, 777, 7777})
    37  	testHandle[testEface](t, nil)
    38  	testHandle[testStringArray](t, [3]string{"a", "b", "c"})
    39  	testHandle[testStringStruct](t, testStringStruct{"x"})
    40  	testHandle[testStringStructArrayStruct](t, testStringStructArrayStruct{
    41  		s: [2]testStringStruct{testStringStruct{"y"}, testStringStruct{"z"}},
    42  	})
    43  	testHandle[testStruct](t, testStruct{0.5, "184"})
    44  }
    45  
    46  func testHandle[T comparable](t *testing.T, value T) {
    47  	name := reflect.TypeFor[T]().Name()
    48  	t.Run(fmt.Sprintf("%s/%#v", name, value), func(t *testing.T) {
    49  		t.Parallel()
    50  
    51  		v0 := Make(value)
    52  		v1 := Make(value)
    53  
    54  		if v0.Value() != v1.Value() {
    55  			t.Error("v0.Value != v1.Value")
    56  		}
    57  		if v0.Value() != value {
    58  			t.Errorf("v0.Value not %#v", value)
    59  		}
    60  		if v0 != v1 {
    61  			t.Error("v0 != v1")
    62  		}
    63  
    64  		drainMaps(t)
    65  		checkMapsFor(t, value)
    66  	})
    67  }
    68  
    69  // drainMaps ensures that the internal maps are drained.
    70  func drainMaps(t *testing.T) {
    71  	t.Helper()
    72  
    73  	wait := make(chan struct{}, 1)
    74  
    75  	// Set up a one-time notification for the next time the cleanup runs.
    76  	// Note: this will only run if there's no other active cleanup, so
    77  	// we can be sure that the next time cleanup runs, it'll see the new
    78  	// notification.
    79  	cleanupMu.Lock()
    80  	cleanupNotify = append(cleanupNotify, func() {
    81  		select {
    82  		case wait <- struct{}{}:
    83  		default:
    84  		}
    85  	})
    86  
    87  	runtime.GC()
    88  	cleanupMu.Unlock()
    89  
    90  	// Wait until cleanup runs.
    91  	<-wait
    92  }
    93  
    94  func checkMapsFor[T comparable](t *testing.T, value T) {
    95  	// Manually load the value out of the map.
    96  	typ := abi.TypeOf(value)
    97  	a, ok := uniqueMaps.Load(typ)
    98  	if !ok {
    99  		return
   100  	}
   101  	m := a.(*uniqueMap[T])
   102  	wp, ok := m.Load(value)
   103  	if !ok {
   104  		return
   105  	}
   106  	if wp.Strong() != nil {
   107  		t.Errorf("value %v still referenced a handle (or tiny block?) ", value)
   108  		return
   109  	}
   110  	t.Errorf("failed to drain internal maps of %v", value)
   111  }
   112  

View as plain text