golang.org/x/build@v0.0.0-20240506185731-218518f32b70/maintner/netsource_test.go (about)

     1  // Copyright 2017 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 maintner
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"reflect"
    11  	"testing"
    12  )
    13  
    14  func TestSumSegSize(t *testing.T) {
    15  	tests := []struct {
    16  		in   []fileSeg
    17  		want int64
    18  	}{
    19  		{
    20  			in:   []fileSeg{fileSeg{size: 1}},
    21  			want: 1,
    22  		},
    23  		{
    24  			in:   []fileSeg{fileSeg{size: 1}, fileSeg{size: 100}},
    25  			want: 101,
    26  		},
    27  		{
    28  			in:   nil,
    29  			want: 0,
    30  		},
    31  	}
    32  	for i, tt := range tests {
    33  		got := sumSegSize(tt.in)
    34  		if got != tt.want {
    35  			t.Errorf("%d. sumSegSize = %v; want %v", i, got, tt.want)
    36  		}
    37  	}
    38  }
    39  
    40  func TestSumCommonPrefixSize(t *testing.T) {
    41  	tests := []struct {
    42  		a, b   []fileSeg
    43  		summer func(file string, n int64) string
    44  		want   int64
    45  	}{
    46  		{
    47  			a:    []fileSeg{fileSeg{size: 1, sha224: "abab"}},
    48  			b:    []fileSeg{fileSeg{size: 1, sha224: "abab"}},
    49  			want: 1,
    50  		},
    51  		{
    52  			a:    []fileSeg{fileSeg{size: 1, sha224: "abab"}},
    53  			b:    []fileSeg{fileSeg{size: 1, sha224: "eeee"}},
    54  			want: 0,
    55  		},
    56  		{
    57  			a: []fileSeg{
    58  				fileSeg{size: 100, sha224: "abab"},
    59  				fileSeg{size: 100, sha224: "abab", file: "a.mutlog"},
    60  			},
    61  			b: []fileSeg{
    62  				fileSeg{size: 100, sha224: "abab"},
    63  				fileSeg{size: 50, sha224: "cccc"},
    64  			},
    65  			summer: func(file string, n int64) string {
    66  				if file == "a.mutlog" && n == 50 {
    67  					return "cccc"
    68  				}
    69  				return "xxx"
    70  			},
    71  			want: 150,
    72  		},
    73  		{
    74  			a: []fileSeg{
    75  				fileSeg{size: 100, sha224: "abab"},
    76  				fileSeg{size: 50, sha224: "cccc"},
    77  			},
    78  			b: []fileSeg{
    79  				fileSeg{size: 100, sha224: "abab"},
    80  				fileSeg{size: 100, sha224: "abab", file: "b.mutlog"},
    81  			},
    82  			summer: func(file string, n int64) string {
    83  				if file == "b.mutlog" && n == 50 {
    84  					return "cccc"
    85  				}
    86  				return "xxx"
    87  			},
    88  			want: 150,
    89  		},
    90  	}
    91  	for i, tt := range tests {
    92  		summer := tt.summer
    93  		if summer == nil {
    94  			summer = func(file string, n int64) string {
    95  				t.Errorf("%d. unexpected call to prefix summer for file=%q, n=%v", i, file, n)
    96  				return ""
    97  			}
    98  		}
    99  		ns := &netMutSource{
   100  			testHookFilePrefixSum224: summer,
   101  		}
   102  		got := ns.sumCommonPrefixSize(tt.a, tt.b)
   103  		if got != tt.want {
   104  			t.Errorf("%d. sumCommonPrefixSize = %v; want %v", i, got, tt.want)
   105  		}
   106  	}
   107  }
   108  
   109  func TestTrimLeadingSegBytes(t *testing.T) {
   110  	tests := []struct {
   111  		in   []fileSeg
   112  		trim int64
   113  		want []fileSeg
   114  	}{
   115  		{
   116  			in:   []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
   117  			trim: 0,
   118  			want: []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
   119  		},
   120  		{
   121  			in:   []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
   122  			trim: 150,
   123  			want: nil,
   124  		},
   125  		{
   126  			in:   []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
   127  			trim: 100,
   128  			want: []fileSeg{fileSeg{size: 50}},
   129  		},
   130  		{
   131  			in:   []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
   132  			trim: 25,
   133  			want: []fileSeg{fileSeg{size: 100, skip: 25}, fileSeg{size: 50}},
   134  		},
   135  	}
   136  	for i, tt := range tests {
   137  		copyIn := append([]fileSeg(nil), tt.in...)
   138  		got := trimLeadingSegBytes(tt.in, tt.trim)
   139  		if !reflect.DeepEqual(tt.in, copyIn) {
   140  			t.Fatalf("%d. trimLeadingSegBytes modified its input", i)
   141  		}
   142  		if !reflect.DeepEqual(got, tt.want) {
   143  			t.Fatalf("%d. trim = %+v; want %+v", i, got, tt.want)
   144  		}
   145  	}
   146  }
   147  
   148  func TestGetNewSegments(t *testing.T) {
   149  	type testCase struct {
   150  		name       string
   151  		lastSegs   []fileSeg
   152  		serverSegs [][]LogSegmentJSON
   153  
   154  		// prefixSum is the prefix sum to use if called.
   155  		// If empty, prefixSum calls are errors.
   156  		prefixSum string
   157  
   158  		want          []fileSeg
   159  		wantSplit     bool
   160  		wantSumCommon int64
   161  		wantUnchanged bool
   162  	}
   163  	tests := []testCase{
   164  		{
   165  			name: "first_download",
   166  			serverSegs: [][]LogSegmentJSON{
   167  				[]LogSegmentJSON{
   168  					{Number: 1, Size: 100, SHA224: "abc"},
   169  					{Number: 2, Size: 200, SHA224: "def"},
   170  				},
   171  			},
   172  			want: []fileSeg{
   173  				{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
   174  				{seg: 2, size: 200, sha224: "def", file: "/fake/0002.mutlog"},
   175  			},
   176  		},
   177  		{
   178  			name: "incremental_download_growseg", // from first_download, segment 2 grows a bit
   179  			lastSegs: []fileSeg{
   180  				{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
   181  				{seg: 2, size: 200, sha224: "def", file: "/fake/0002.mutlog"},
   182  			},
   183  			prefixSum: "def",
   184  			serverSegs: [][]LogSegmentJSON{
   185  				[]LogSegmentJSON{
   186  					{Number: 1, Size: 100, SHA224: "abc"},
   187  					{Number: 2, Size: 205, SHA224: "defdef"},
   188  				},
   189  			},
   190  			want: []fileSeg{
   191  				{seg: 2, size: 205, sha224: "defdef", skip: 200, file: "/fake/0002.mutlog"},
   192  			},
   193  		},
   194  		{
   195  			name: "incremental_download_growseg_and_newseg", // from first_download, segment 2 grows, and segment 3 appears.
   196  			lastSegs: []fileSeg{
   197  				{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
   198  				{seg: 2, size: 200, sha224: "def", file: "/fake/0002.mutlog"},
   199  			},
   200  			prefixSum: "def",
   201  			serverSegs: [][]LogSegmentJSON{
   202  				[]LogSegmentJSON{
   203  					{Number: 1, Size: 100, SHA224: "abc"},
   204  					{Number: 2, Size: 250, SHA224: "defdef"},
   205  					{Number: 3, Size: 300, SHA224: "fff"},
   206  				},
   207  			},
   208  			want: []fileSeg{
   209  				{seg: 2, size: 250, sha224: "defdef", skip: 200, file: "/fake/0002.mutlog"},
   210  				{seg: 3, size: 300, sha224: "fff", skip: 0, file: "/fake/0003.mutlog"},
   211  			},
   212  		},
   213  		{
   214  			name: "incremental_download_newseg", // from first_download, segment 3 appears.
   215  			lastSegs: []fileSeg{
   216  				{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
   217  				{seg: 2, size: 200, sha224: "def", file: "/fake/0002.mutlog"},
   218  			},
   219  			serverSegs: [][]LogSegmentJSON{
   220  				[]LogSegmentJSON{
   221  					{Number: 1, Size: 100, SHA224: "abc"},
   222  					{Number: 2, Size: 200, SHA224: "def"},
   223  					{Number: 3, Size: 300, SHA224: "fff"},
   224  				},
   225  			},
   226  			want: []fileSeg{
   227  				{seg: 3, size: 300, sha224: "fff", skip: 0, file: "/fake/0003.mutlog"},
   228  			},
   229  		},
   230  		{
   231  			name: "faulty_server_returns_no_new_data",
   232  			lastSegs: []fileSeg{
   233  				{seg: 1, size: 101, sha224: "abc", file: "/fake/0001.mutlog"},
   234  			},
   235  			serverSegs: [][]LogSegmentJSON{
   236  				[]LogSegmentJSON{
   237  					{Number: 1, Size: 101, SHA224: "abc"}, // Same as lastSegs, results in unchanged error.
   238  				},
   239  				[]LogSegmentJSON{
   240  					{Number: 1, Size: 101, SHA224: "abc"},
   241  					{Number: 2, Size: 102, SHA224: "def"},
   242  				},
   243  			},
   244  			wantUnchanged: true,
   245  		},
   246  		{
   247  			name: "split_error_diff_first_seg_same_size",
   248  			lastSegs: []fileSeg{
   249  				{seg: 1, size: 101, sha224: "abc", file: "/fake/0001.mutlog"},
   250  			},
   251  			serverSegs: [][]LogSegmentJSON{
   252  				[]LogSegmentJSON{
   253  					{Number: 1, Size: 101, SHA224: "def"},
   254  				},
   255  			},
   256  			wantSplit: true,
   257  		},
   258  		{
   259  			name: "split_error_diff_first_seg_and_longer",
   260  			lastSegs: []fileSeg{
   261  				{seg: 1, size: 101, sha224: "abc", file: "/fake/0001.mutlog"},
   262  			},
   263  			serverSegs: [][]LogSegmentJSON{
   264  				[]LogSegmentJSON{
   265  					{Number: 1, Size: 102, SHA224: "def"},
   266  				},
   267  			},
   268  			prefixSum: "ffffffffff", // no match
   269  			wantSplit: true,
   270  		},
   271  		{
   272  			name: "split_error_diff_first_seg_and_shorter",
   273  			lastSegs: []fileSeg{
   274  				{seg: 1, size: 101, sha224: "abc", file: "/fake/0001.mutlog"},
   275  			},
   276  			serverSegs: [][]LogSegmentJSON{
   277  				[]LogSegmentJSON{
   278  					{Number: 1, Size: 50, SHA224: "def"},
   279  				},
   280  			},
   281  			prefixSum: "ffffffffff", // no match
   282  			wantSplit: true,
   283  		},
   284  		{
   285  			name: "split_error_same_first_seg_but_shorter",
   286  			lastSegs: []fileSeg{
   287  				{seg: 1, size: 101, sha224: "abc", file: "/fake/0001.mutlog"},
   288  			},
   289  			serverSegs: [][]LogSegmentJSON{
   290  				[]LogSegmentJSON{
   291  					{Number: 1, Size: 50, SHA224: "def"},
   292  				},
   293  			},
   294  			prefixSum:     "def", // match
   295  			wantSplit:     true,
   296  			wantSumCommon: 50,
   297  		},
   298  		{
   299  			name: "split_error_diff_final_seg",
   300  			lastSegs: []fileSeg{
   301  				{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
   302  				{seg: 2, size: 2, sha224: "def", file: "/fake/0002.mutlog"},
   303  			},
   304  			serverSegs: [][]LogSegmentJSON{
   305  				[]LogSegmentJSON{
   306  					{Number: 1, Size: 100, SHA224: "abc"},
   307  					{Number: 2, Size: 4, SHA224: "fff"},
   308  				},
   309  			},
   310  			prefixSum:     "not_def",
   311  			wantSplit:     true,
   312  			wantSumCommon: 100,
   313  		},
   314  	}
   315  	for _, tt := range tests {
   316  		t.Run(tt.name, func(t *testing.T) {
   317  			serverSegCalls := 0
   318  			syncSegCalls := 0
   319  			ns := &netMutSource{
   320  				last: tt.lastSegs,
   321  				testHookGetServerSegments: func(_ context.Context, waitSizeNot int64) (segs []LogSegmentJSON, err error) {
   322  					serverSegCalls++
   323  					if serverSegCalls%2 == 1 {
   324  						return nil, fetchError{PossiblyRetryable: true, Err: fmt.Errorf("fake error to simulate the internet saying 'not this time' every now and then")}
   325  					}
   326  					if len(tt.serverSegs) == 0 {
   327  						return nil, nil
   328  					}
   329  					segs = tt.serverSegs[0]
   330  					if len(tt.serverSegs) > 1 {
   331  						tt.serverSegs = tt.serverSegs[1:]
   332  					}
   333  					return segs, nil
   334  				},
   335  				testHookSyncSeg: func(_ context.Context, seg LogSegmentJSON) (fileSeg, []byte, error) {
   336  					syncSegCalls++
   337  					if syncSegCalls%3 == 1 {
   338  						return fileSeg{}, nil, fetchError{PossiblyRetryable: true, Err: fmt.Errorf("fake error to simulate the internet saying 'not this time' every now and then")}
   339  					}
   340  					return fileSeg{
   341  						seg:    seg.Number,
   342  						size:   seg.Size,
   343  						sha224: seg.SHA224,
   344  						file:   fmt.Sprintf("/fake/%04d.mutlog", seg.Number),
   345  					}, nil, nil
   346  				},
   347  				testHookOnSplit: func(sumCommon int64) {
   348  					if got, want := sumCommon, tt.wantSumCommon; got != want {
   349  						t.Errorf("sumCommon = %v; want %v", got, want)
   350  					}
   351  				},
   352  				testHookFilePrefixSum224: func(file string, n int64) string {
   353  					if tt.prefixSum != "" {
   354  						return tt.prefixSum
   355  					}
   356  					t.Errorf("unexpected call to filePrefixSum224(%q, %d)", file, n)
   357  					return "XXXX"
   358  				},
   359  			}
   360  			got, err := ns.getNewSegments(context.Background())
   361  			if tt.wantSplit {
   362  				if err != ErrSplit {
   363  					t.Fatalf("wanted ErrSplit; got %+v, %v", got, err)
   364  				}
   365  				// Success.
   366  				return
   367  			}
   368  			if tt.wantUnchanged {
   369  				if err == nil || err.Error() != "maintner.netsource: maintnerd server returned unchanged log segments" {
   370  					t.Fatalf("wanted unchanged; got %+v, %v", got, err)
   371  				}
   372  				// Success.
   373  				return
   374  			}
   375  			if err != nil {
   376  				t.Fatal(err)
   377  			}
   378  			if !reflect.DeepEqual(got, tt.want) {
   379  				t.Errorf("mismatch\n got: %+v\nwant: %+v\n", got, tt.want)
   380  			}
   381  		})
   382  	}
   383  }