github.com/muyo/sno@v1.2.1/partition_test.go (about)

     1  package sno
     2  
     3  import (
     4  	"sync/atomic"
     5  	"testing"
     6  )
     7  
     8  func TestPartition_Public_Conversions(t *testing.T) {
     9  	t.Run("AsUint16", func(t *testing.T) {
    10  		src := Partition{255, 255}
    11  		expected := uint16(MaxPartition)
    12  		actual := src.AsUint16()
    13  
    14  		if actual != expected {
    15  			t.Errorf("expected [%d], got [%d]", expected, actual)
    16  		}
    17  	})
    18  
    19  	t.Run("PutUint16", func(t *testing.T) {
    20  		expected := Partition{255, 255}
    21  		actual := Partition{}
    22  		actual.PutUint16(MaxPartition)
    23  
    24  		if actual != expected {
    25  			t.Errorf("expected [%s], got [%s]", expected, actual)
    26  		}
    27  	})
    28  }
    29  
    30  func TestPartition_Internal_Conversions(t *testing.T) {
    31  	public := Partition{255, 255}
    32  	internal := uint32(MaxPartition) << 16
    33  
    34  	t.Run("to-internal", func(t *testing.T) {
    35  		expected := internal
    36  		actual := partitionToInternalRepr(public)
    37  
    38  		if actual != expected {
    39  			t.Errorf("expected [%d], got [%d]", expected, actual)
    40  		}
    41  	})
    42  
    43  	t.Run("to-public", func(t *testing.T) {
    44  		expected := public
    45  		actual := partitionToPublicRepr(internal)
    46  
    47  		if actual != expected {
    48  			t.Errorf("expected [%d], got [%d]", expected, actual)
    49  		}
    50  	})
    51  }
    52  
    53  func TestPartition_Internal_Generation(t *testing.T) {
    54  	t.Run("monotonic-increments", func(t *testing.T) {
    55  		// Reset global count (leaving seed as is).
    56  		atomic.StoreUint32(&partitions, 0)
    57  
    58  		var prevPartition = uint32(seed) << 16
    59  
    60  		for i := 0; i < 100; i++ {
    61  			p, err := genPartition()
    62  			if err != nil {
    63  				t.Fatal(err)
    64  			}
    65  
    66  			// Note: genPartition() shifts to make space for the sequence,
    67  			// so we can't simply check for an increment of 1 within the resulting
    68  			// uint32. The below is a tiny bit faster than converting back
    69  			// to an uint16.
    70  			if p-prevPartition != 1<<16 {
    71  				t.Errorf("expected [%d], got [%d]", prevPartition+1<<16, p)
    72  				break
    73  			}
    74  
    75  			prevPartition = p
    76  		}
    77  	})
    78  
    79  	t.Run("pool-exhaustion", func(t *testing.T) {
    80  		// Reset global count (leaving seed as is).
    81  		atomic.StoreUint32(&partitions, 0)
    82  
    83  		for i := 0; i < 2*MaxPartition; i++ {
    84  			_, err := genPartition()
    85  
    86  			if err != nil {
    87  				verr, ok := err.(*PartitionPoolExhaustedError)
    88  				if !ok {
    89  					t.Fatalf("expected error type [%T], got [%T]", &PartitionPoolExhaustedError{}, err)
    90  					return
    91  				}
    92  
    93  				if i < MaxPartition {
    94  					t.Fatalf("expected errors no sooner than after [%d] iterations, got to [%d]", MaxPartition, i)
    95  					return
    96  				}
    97  
    98  				errMsgActual := verr.Error()
    99  				errMsgExpected := errPartitionPoolExhaustedMsg
   100  
   101  				if errMsgActual != errMsgExpected {
   102  					t.Fatalf("expected error msg [%s], got [%s]", errMsgExpected, errMsgActual)
   103  				}
   104  			}
   105  
   106  			if i >= MaxPartition {
   107  				if err == nil {
   108  					t.Fatalf("expected constant errors after [%d] iterations, got no error at [%d]", MaxPartition, i)
   109  					return
   110  				}
   111  			}
   112  		}
   113  	})
   114  
   115  	// Clean up.
   116  	atomic.StoreUint32(&partitions, 0)
   117  }