go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/prjmanager/copyonwrite/cow_test.go (about)

     1  // Copyright 2021 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package copyonwrite
    16  
    17  import (
    18  	"testing"
    19  
    20  	. "github.com/smartystreets/goconvey/convey"
    21  	. "go.chromium.org/luci/common/testing/assertions"
    22  )
    23  
    24  type el struct {
    25  	id, val int
    26  }
    27  
    28  // evenCubedOddDeleted deletes `el`s with odd IDs and sets val to id^3 for even
    29  // ones.
    30  func evenCubedOddDeleted(v any) any {
    31  	in := v.(*el)
    32  	if in.id&1 == 1 {
    33  		return Deletion
    34  	}
    35  	c := in.id * in.id * in.id
    36  	if in.val == c {
    37  		return in
    38  	}
    39  	return &el{in.id, c}
    40  }
    41  
    42  func TestUpdate(t *testing.T) {
    43  	t.Parallel()
    44  
    45  	mustNoop := func(in Slice, m Modifier, add Slice) {
    46  		out, u := Update(in, m, add)
    47  		So(u, ShouldBeFalse)
    48  		So(out, ShouldResemble, in)
    49  	}
    50  
    51  	Convey("Update noops", t, func() {
    52  		Convey("empty", func() {
    53  			mustNoop(nil, nil, nil)
    54  			mustNoop(elSlice{}, nil, nil)
    55  			mustNoop(nil, evenCubedOddDeleted, nil)
    56  			mustNoop(nil, evenCubedOddDeleted, elSlice{})
    57  		})
    58  		Convey("no changes", func() {
    59  			mustNoop(elSlice{{0, 0}, {2, 8}, {4, 64}}, nil, nil)
    60  			mustNoop(elSlice{{0, 0}, {2, 8}, {4, 64}}, evenCubedOddDeleted, nil)
    61  			mustNoop(elSlice{{0, 0}, {2, 8}, {4, 64}}, evenCubedOddDeleted, elSlice{})
    62  		})
    63  	})
    64  
    65  	Convey("Update works on Slice", t, func() {
    66  		Convey("deletes", func() {
    67  			res, u := Update(elSlice{{1, 0}, {4, 64}, {3, 0}, {0, 0}}, evenCubedOddDeleted, nil)
    68  			So(u, ShouldBeTrue)
    69  			So(res, ShouldResemble, elSlice{{4, 64}, {0, 0}})
    70  		})
    71  		Convey("modifies", func() {
    72  			res, u := Update(elSlice{{2, 8}, {4, 0}}, evenCubedOddDeleted, nil)
    73  			So(u, ShouldBeTrue)
    74  			So(res, ShouldResemble, elSlice{{2, 8}, {4, 64}})
    75  		})
    76  		Convey("modifies and deletes", func() {
    77  			res, u := Update(elSlice{{1, 0}, {2, 8}, {3, 0}, {4, 0}}, evenCubedOddDeleted, nil)
    78  			So(u, ShouldBeTrue)
    79  			So(res, ShouldResemble, elSlice{{2, 8}, {4, 64}})
    80  		})
    81  		Convey("creates on empty", func() {
    82  			res, u := Update(nil, nil, elSlice{{6, 0}, {5, 0}})
    83  			So(u, ShouldBeTrue)
    84  			So(res, ShouldResemble, elSlice{{6, 0}, {5, 0}})
    85  		})
    86  		Convey("creates", func() {
    87  			res, u := Update(elSlice{{1, 0}}, nil, elSlice{{6, 0}, {5, 0}})
    88  			So(u, ShouldBeTrue)
    89  			So(res, ShouldResemble, elSlice{{6, 0}, {5, 0}, {1, 0}})
    90  		})
    91  		Convey("creates, modifies and deletes", func() {
    92  			res, u := Update(elSlice{{1, 0}, {2, 8}, {3, 0}, {4, 0}}, evenCubedOddDeleted, elSlice{{5, 25}, {0, 0}})
    93  			So(u, ShouldBeTrue)
    94  			So(res, ShouldResemble, elSlice{{5, 25}, {0, 0}, {2, 8}, {4, 64}})
    95  		})
    96  	})
    97  
    98  	Convey("Update works on SortedSlice", t, func() {
    99  		Convey("panics if toAdd is not sorted", func() {
   100  			So(func() { Update(elSortedSlice{}, nil, elSlice{{3, 8}}) },
   101  				ShouldPanicLike, "Different types for in and toAdd slices")
   102  		})
   103  		Convey("creates sorted", func() {
   104  			res, u := Update(elSortedSlice{}, nil, elSortedSlice{{3, 8}, {1, 8}, {4, 1}, {2, 0}})
   105  			So(u, ShouldBeTrue)
   106  			So(res, ShouldResemble, elSortedSlice{{1, 8}, {2, 0}, {3, 8}, {4, 1}})
   107  		})
   108  		Convey("modifies and deletes", func() {
   109  			res, u := Update(elSortedSlice{{1, 0}, {2, 8}, {3, 0}, {4, 0}}, evenCubedOddDeleted, nil)
   110  			So(u, ShouldBeTrue)
   111  			So(res, ShouldResemble, elSortedSlice{{2, 8}, {4, 64}})
   112  		})
   113  		Convey("deletes everything", func() {
   114  			res, u := Update(elSortedSlice{{1, 0}, {3, 0}}, evenCubedOddDeleted, nil)
   115  			So(u, ShouldBeTrue)
   116  			So(res, ShouldResemble, elSortedSlice{})
   117  		})
   118  		Convey("creates, modifies and deletes", func() {
   119  			in := elSortedSlice{{1, 0}, {2, 8}, {3, 0}, {4, 0}, {6, 1}, {7, 3}}
   120  			res, u := Update(in, evenCubedOddDeleted, elSortedSlice{{10, 100}, {0, 0}, {5, 25}})
   121  			So(u, ShouldBeTrue)
   122  			So(res, ShouldResemble, elSortedSlice{{0, 0}, {2, 8}, {4, 64}, {5, 25}, {6, 216}, {10, 100}})
   123  
   124  			res, u = Update(in, evenCubedOddDeleted, elSortedSlice{{3, 3}})
   125  			So(u, ShouldBeTrue)
   126  			So(res, ShouldResemble, elSortedSlice{{2, 8}, {3, 3}, {4, 64}, {6, 216}})
   127  		})
   128  	})
   129  }
   130  
   131  type elSlice []*el
   132  
   133  var _ Slice = elSlice(nil)
   134  
   135  func (e elSlice) Len() int {
   136  	return len(e)
   137  }
   138  
   139  func (e elSlice) At(index int) any {
   140  	return e[index]
   141  }
   142  
   143  func (e elSlice) Append(v any) Slice {
   144  	return append(e, v.(*el))
   145  }
   146  
   147  func (e elSlice) CloneShallow(length int, capacity int) Slice {
   148  	r := make(elSlice, length, capacity)
   149  	copy(r, e[:length])
   150  	return r
   151  }
   152  
   153  type elSortedSlice []*el
   154  
   155  var _ SortedSlice = elSortedSlice(nil)
   156  
   157  func (e elSortedSlice) Len() int {
   158  	return len(e)
   159  }
   160  
   161  func (e elSortedSlice) At(index int) any {
   162  	return e[index]
   163  }
   164  
   165  func (e elSortedSlice) Append(v any) Slice {
   166  	return append(e, v.(*el))
   167  }
   168  
   169  func (e elSortedSlice) CloneShallow(length int, capacity int) Slice {
   170  	r := make(elSortedSlice, length, capacity)
   171  	copy(r, e[:length])
   172  	return r
   173  }
   174  
   175  func (e elSortedSlice) Less(i int, j int) bool {
   176  	return e[i].id < e[j].id
   177  }
   178  
   179  func (e elSortedSlice) Swap(i int, j int) {
   180  	e[i], e[j] = e[j], e[i]
   181  }
   182  
   183  func (_ elSortedSlice) LessElements(a any, b any) bool {
   184  	return a.(*el).id < b.(*el).id
   185  }