gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/streambufferlru_test.go (about)

     1  package renter
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/opentracing/opentracing-go"
     8  	"gitlab.com/NebulousLabs/fastrand"
     9  	"gitlab.com/NebulousLabs/threadgroup"
    10  	"gitlab.com/SkynetLabs/skyd/skymodules"
    11  	"go.sia.tech/siad/types"
    12  )
    13  
    14  // TestStreamLRU checks that all of the code that forms the LRU for a
    15  // stream
    16  //
    17  // This test has 100% coverage of the streambufferlru.go file.
    18  func TestStreamLRU(t *testing.T) {
    19  	if testing.Short() {
    20  		t.SkipNow()
    21  	}
    22  
    23  	// create a ctx with test span
    24  	ctx := opentracing.ContextWithSpan(context.Background(), testSpan())
    25  
    26  	// Create a usable stream, this creates the stream buffer that the LRU talks
    27  	// to and gives a good opporutnity to probe the LRU.
    28  	var tg threadgroup.ThreadGroup
    29  	data := fastrand.Bytes(15999) // 1 byte short of 1000 data sections.
    30  	dataSource := newMockDataSource(data, 16)
    31  	dt := skymodules.NewDistributionTrackerStandard()
    32  	sbs := newStreamBufferSet(dt, &tg, newNoOpLRU())
    33  	stream := sbs.callNewStream(ctx, dataSource, 0, 0, types.ZeroCurrency, 0, false)
    34  
    35  	// Extract the LRU from the stream to test it directly.
    36  	lru := stream.lru
    37  	// Empty the LRU so that we are working with a clean slate.
    38  	lru.callEvictAll()
    39  	// Set the size of the LRU to 4.
    40  	lru.staticSize = 4
    41  
    42  	// Check that the LRU is empty / a clean slate.
    43  	if lru.head != nil {
    44  		t.Fatal("lru is not empty")
    45  	}
    46  	if lru.tail != nil {
    47  		t.Fatal("lru is not empty")
    48  	}
    49  	if len(lru.nodes) != 0 {
    50  		t.Fatal("lru is not empty")
    51  	}
    52  
    53  	// Check that the stream buffer is empty.
    54  	sb := lru.staticStreamBuffer
    55  	if len(sb.dataSections) != 0 {
    56  		t.Fatal("stream buffer is not empty")
    57  	}
    58  
    59  	// Add the first node.
    60  	lru.callUpdate(0)
    61  	// Check that the lru has one node.
    62  	if lru.head == nil {
    63  		t.Fatal("bad")
    64  	}
    65  	if lru.head != lru.tail {
    66  		t.Fatal("bad")
    67  	}
    68  	if len(lru.nodes) != 1 {
    69  		t.Fatal("bad")
    70  	}
    71  	if len(sb.dataSections) != 1 {
    72  		t.Fatal("bad")
    73  	}
    74  	_, exists := sb.dataSections[0]
    75  	if !exists {
    76  		t.Fatal("bad")
    77  	}
    78  
    79  	// Add nodes 1, 2, 3, then perform an integrity check.
    80  	lru.callUpdate(1)
    81  	lru.callUpdate(2)
    82  	lru.callUpdate(3)
    83  	if len(lru.nodes) != 4 {
    84  		t.Fatal("bad")
    85  	}
    86  	if len(sb.dataSections) != 4 {
    87  		t.Fatal("bad")
    88  	}
    89  	if lru.head.index != 3 {
    90  		t.Fatal("bad")
    91  	}
    92  	if lru.head.next.index != 2 {
    93  		t.Fatal("bad")
    94  	}
    95  	if lru.head.next.next.index != 1 {
    96  		t.Fatal("bad")
    97  	}
    98  	if lru.tail.index != 0 {
    99  		t.Fatal("bad")
   100  	}
   101  	if lru.tail.prev.index != 1 {
   102  		t.Fatal("bad")
   103  	}
   104  
   105  	// Call update with 4, this should cause an eviction.
   106  	lru.callUpdate(4)
   107  	if len(lru.nodes) != 4 {
   108  		t.Fatal("bad", len(lru.nodes))
   109  	}
   110  	if len(sb.dataSections) != 4 {
   111  		t.Fatal("bad")
   112  	}
   113  	if lru.head.index != 4 {
   114  		t.Fatal("bad")
   115  	}
   116  	if lru.head.next.index != 3 {
   117  		t.Fatal("bad")
   118  	}
   119  	if lru.head.next.next.index != 2 {
   120  		t.Fatal("bad")
   121  	}
   122  	if lru.tail.index != 1 {
   123  		t.Fatal("bad")
   124  	}
   125  	if lru.tail.prev.index != 2 {
   126  		t.Fatal("bad")
   127  	}
   128  	_, exists = lru.nodes[0]
   129  	if exists {
   130  		t.Fatal("bad")
   131  	}
   132  	_, exists = sb.dataSections[0]
   133  	if exists {
   134  		t.Fatal("bad")
   135  	}
   136  
   137  	// Call update with 1, this should move 1 to the head of the LRU.
   138  	lru.callUpdate(1)
   139  	if len(lru.nodes) != 4 {
   140  		t.Fatal("bad", len(lru.nodes))
   141  	}
   142  	if len(sb.dataSections) != 4 {
   143  		t.Fatal("bad")
   144  	}
   145  	if lru.head.index != 1 {
   146  		t.Fatal("bad")
   147  	}
   148  	if lru.head.next.index != 4 {
   149  		t.Fatal("bad")
   150  	}
   151  	if lru.head.next.next.index != 3 {
   152  		t.Fatal("bad")
   153  	}
   154  	if lru.tail.index != 2 {
   155  		t.Fatal("bad", lru.tail.index)
   156  	}
   157  	if lru.tail.prev.index != 3 {
   158  		t.Fatal("bad")
   159  	}
   160  
   161  	// Call update with 3, this should move 3 to the head of the LRU. Unlike the
   162  	// previous check, which updated the tail, this check updates a center node.
   163  	lru.callUpdate(3)
   164  	if len(lru.nodes) != 4 {
   165  		t.Fatal("bad", len(lru.nodes))
   166  	}
   167  	if len(sb.dataSections) != 4 {
   168  		t.Fatal("bad")
   169  	}
   170  	if lru.head.index != 3 {
   171  		t.Fatal("bad")
   172  	}
   173  	if lru.head.next.index != 1 {
   174  		t.Fatal("bad")
   175  	}
   176  	if lru.head.next.next.index != 4 {
   177  		t.Fatal("bad")
   178  	}
   179  	if lru.head.next.next.next.index != 2 {
   180  		t.Fatal("bad")
   181  	}
   182  	if lru.tail.index != 2 {
   183  		t.Fatal("bad", lru.tail.index)
   184  	}
   185  	if lru.tail.prev.index != 4 {
   186  		t.Fatal("bad")
   187  	}
   188  	if lru.tail.prev.prev.index != 1 {
   189  		t.Fatal("bad")
   190  	}
   191  	if lru.tail.prev.prev.prev.index != 3 {
   192  		t.Fatal("bad")
   193  	}
   194  
   195  	// Call update with 3 again, nothing should change.
   196  	lru.callUpdate(3)
   197  	if len(lru.nodes) != 4 {
   198  		t.Fatal("bad", len(lru.nodes))
   199  	}
   200  	if len(sb.dataSections) != 4 {
   201  		t.Fatal("bad")
   202  	}
   203  	if lru.head.index != 3 {
   204  		t.Fatal("bad")
   205  	}
   206  	if lru.head.next.index != 1 {
   207  		t.Fatal("bad")
   208  	}
   209  	if lru.head.next.next.index != 4 {
   210  		t.Fatal("bad")
   211  	}
   212  	if lru.head.next.next.next.index != 2 {
   213  		t.Fatal("bad")
   214  	}
   215  	if lru.tail.index != 2 {
   216  		t.Fatal("bad", lru.tail.index)
   217  	}
   218  	if lru.tail.prev.index != 4 {
   219  		t.Fatal("bad")
   220  	}
   221  	if lru.tail.prev.prev.index != 1 {
   222  		t.Fatal("bad")
   223  	}
   224  	if lru.tail.prev.prev.prev.index != 3 {
   225  		t.Fatal("bad")
   226  	}
   227  
   228  	// streamBuffer should have data pieces 1,2,3,4.
   229  	_, exists = sb.dataSections[1]
   230  	if !exists {
   231  		t.Fatal("bad")
   232  	}
   233  	_, exists = sb.dataSections[2]
   234  	if !exists {
   235  		t.Fatal("bad")
   236  	}
   237  	_, exists = sb.dataSections[3]
   238  	if !exists {
   239  		t.Fatal("bad")
   240  	}
   241  	_, exists = sb.dataSections[4]
   242  	if !exists {
   243  		t.Fatal("bad")
   244  	}
   245  
   246  	// Try inserting another new node, this should evict '2'.
   247  	lru.callUpdate(10)
   248  	if len(lru.nodes) != 4 {
   249  		t.Fatal("bad", len(lru.nodes))
   250  	}
   251  	if len(sb.dataSections) != 4 {
   252  		t.Fatal("bad")
   253  	}
   254  	if lru.head.index != 10 {
   255  		t.Fatal("bad")
   256  	}
   257  	if lru.head.next.index != 3 {
   258  		t.Fatal("bad")
   259  	}
   260  	if lru.head.next.next.index != 1 {
   261  		t.Fatal("bad")
   262  	}
   263  	if lru.head.next.next.next.index != 4 {
   264  		t.Fatal("bad")
   265  	}
   266  	if lru.tail.index != 4 {
   267  		t.Fatal("bad", lru.tail.index)
   268  	}
   269  	if lru.tail.prev.index != 1 {
   270  		t.Fatal("bad")
   271  	}
   272  	if lru.tail.prev.prev.index != 3 {
   273  		t.Fatal("bad")
   274  	}
   275  	if lru.tail.prev.prev.prev.index != 10 {
   276  		t.Fatal("bad")
   277  	}
   278  
   279  	// streamBuffer should have data pieces 1,3,4,10.
   280  	_, exists = sb.dataSections[1]
   281  	if !exists {
   282  		t.Fatal("bad")
   283  	}
   284  	_, exists = sb.dataSections[10]
   285  	if !exists {
   286  		t.Fatal("bad")
   287  	}
   288  	_, exists = sb.dataSections[3]
   289  	if !exists {
   290  		t.Fatal("bad")
   291  	}
   292  	_, exists = sb.dataSections[4]
   293  	if !exists {
   294  		t.Fatal("bad")
   295  	}
   296  
   297  	// Add another node and attempt to evict the tail node.
   298  	lru.callUpdate(2)
   299  	lru.managedEvict()
   300  	if len(lru.nodes) != 4 {
   301  		t.Fatal("bad", len(lru.nodes))
   302  	}
   303  	if len(sb.dataSections) != 4 {
   304  		t.Fatal("bad", len(sb.dataSections))
   305  	}
   306  	if lru.head.index != 2 {
   307  		t.Fatal("bad", lru.head.index)
   308  	}
   309  	if lru.head.next.index != 10 {
   310  		t.Fatal("bad", lru.head.next.index)
   311  	}
   312  	if lru.tail.index != 1 {
   313  		t.Fatal("bad", lru.tail.index)
   314  	}
   315  	if lru.tail.prev.index != 3 {
   316  		t.Fatal("bad", lru.tail.prev.index)
   317  	}
   318  
   319  	// streamBuffer should have data pieces 1,2,3,10.
   320  	_, exists = sb.dataSections[1]
   321  	if !exists {
   322  		t.Fatal("bad")
   323  	}
   324  	_, exists = sb.dataSections[2]
   325  	if !exists {
   326  		t.Fatal("bad")
   327  	}
   328  	_, exists = sb.dataSections[3]
   329  	if !exists {
   330  		t.Fatal("bad")
   331  	}
   332  	_, exists = sb.dataSections[10]
   333  	if !exists {
   334  		t.Fatal("bad")
   335  	}
   336  
   337  	// Evict again. Should be a no-op now.
   338  	lru.managedEvict()
   339  	if len(lru.nodes) != 4 {
   340  		t.Fatal("bad", len(lru.nodes))
   341  	}
   342  
   343  	// streamBuffer should have data pieces 1,3.
   344  	_, exists = sb.dataSections[1]
   345  	if !exists {
   346  		t.Fatal("bad")
   347  	}
   348  	_, exists = sb.dataSections[3]
   349  	if !exists {
   350  		t.Fatal("bad")
   351  	}
   352  
   353  	// Evict everything again.
   354  	lru.callEvictAll()
   355  	if len(lru.nodes) != 0 {
   356  		t.Fatal("bad", len(lru.nodes))
   357  	}
   358  	if len(sb.dataSections) != 0 {
   359  		t.Fatal("bad")
   360  	}
   361  	if lru.head != nil {
   362  		t.Fatal("lru is not empty")
   363  	}
   364  	if lru.tail != nil {
   365  		t.Fatal("lru is not empty")
   366  	}
   367  }