package pfile import ( "errors" "reflect" "testing" ) func (al allocList) Assert(t *testing.T, state map[[2]uint64][]uint64) { t.Helper() if len(al) != len(state) { t.Fatalf("Expected %d items, but found %d.", len(state), len(al)) } for key, expected := range state { val, ok := al[key] if !ok { t.Fatalf("Expected to find key %v.", key) } if !reflect.DeepEqual(val, expected) { t.Fatalf("For %v, expected %v but got %v.", key, expected, val) } } } func (al *allocList) With(collectionID, itemID uint64, pages ...uint64) *allocList { al.Store(collectionID, itemID, pages) return al } func (al *allocList) Equals(rhs *allocList) bool { if len(*rhs) != len(*al) { return false } for key, val := range *rhs { actual := (*al)[key] if !reflect.DeepEqual(val, actual) { return false } } return true } func TestAllocList(t *testing.T) { const ( CREATE = "CREATE" PUSH = "PUSH" STORE = "STORE" REMOVE = "REMOVE" ) type TestCase struct { Name string Action string Key [2]uint64 Page uint64 Pages []uint64 // For STORE command. Expected *allocList ExpectedLen int } testCases := []TestCase{{ Name: "Create something", Action: CREATE, Key: [2]uint64{1, 1}, Page: 1, Expected: newAllocList().With(1, 1, 1), ExpectedLen: 1, }, { Name: "Push onto something", Action: PUSH, Key: [2]uint64{1, 1}, Page: 2, Expected: newAllocList().With(1, 1, 1, 2), ExpectedLen: 1, }, { Name: "Push onto something again", Action: PUSH, Key: [2]uint64{1, 1}, Page: 3, Expected: newAllocList().With(1, 1, 1, 2, 3), ExpectedLen: 1, }, { Name: "Store something", Action: STORE, Key: [2]uint64{2, 2}, Pages: []uint64{4, 5, 6}, Expected: newAllocList().With(1, 1, 1, 2, 3).With(2, 2, 4, 5, 6), ExpectedLen: 2, }, { Name: "Remove something", Action: REMOVE, Key: [2]uint64{1, 1}, Expected: newAllocList().With(2, 2, 4, 5, 6), ExpectedLen: 1, }} al := newAllocList() for _, tc := range testCases { switch tc.Action { case CREATE: al.Create(tc.Key[0], tc.Key[1], tc.Page) case PUSH: al.Push(tc.Key[0], tc.Key[1], tc.Page) case STORE: al.Store(tc.Key[0], tc.Key[1], tc.Pages) case REMOVE: al.Remove(tc.Key[0], tc.Key[1]) default: t.Fatalf("Unknown action: %s", tc.Action) } if !al.Equals(tc.Expected) { t.Fatal(tc.Name, al, tc.Expected) } if al.Len() != tc.ExpectedLen { t.Fatal(tc.Name, al.Len(), tc.ExpectedLen) } } } func TestAllocListIterate_eachError(t *testing.T) { al := newAllocList().With(1, 1, 2, 3, 4, 5) myErr := errors.New("xxx") err := al.Iterate(func(collectionID, itemID uint64, pageIDs []uint64) error { return myErr }) if err != myErr { t.Fatal(err) } } func TestAllocListIterate(t *testing.T) { al := newAllocList().With(1, 1, 2, 3, 4, 5).With(2, 2, 6, 7) expected := map[uint64][]uint64{ 1: {2, 3, 4, 5}, 2: {6, 7}, } err := al.Iterate(func(collectionID, itemID uint64, pageIDs []uint64) error { e, ok := expected[collectionID] if !ok { t.Fatalf("Not found: %d", collectionID) } if !reflect.DeepEqual(e, pageIDs) { t.Fatalf("%v != %v", pageIDs, e) } return nil }) if err != nil { t.Fatal(err) } } func TestAllocListPushNoHead(t *testing.T) { al := newAllocList().With(1, 1, 2, 3, 4, 5).With(2, 2, 6, 7) if !al.Push(1, 1, 8) { t.Fatal("Failed to push onto head page") } if al.Push(1, 2, 9) { t.Fatal("Pushed with no head.") } }