func TestDataVersioningAvoidsUnnecessaryTruncation(t *testing.T) { expFull := CapnpEncode("(val = 9, duo = 8, ptr1 = (val = 77), ptr2 = (val = 55))", "VerTwoDataTwoPtr") //expEmpty := CapnpEncode("()", "VerEmpty") cv.Convey("Given a struct with 0 ptr fields, and a newer version of the struct with two data and two pointer fields", t, func() { cv.Convey("then old code expecting the smaller struct but reading the newer-bigger struct should not truncate it if it doesn't have to (e.g. not assigning into a composite list), and should preserve all data when re-serializing it.", func() { seg := capn.NewBuffer(nil) scratch := capn.NewBuffer(nil) big := air.NewRootVerTwoDataTwoPtr(seg) one := air.NewVerOneData(scratch) one.SetVal(77) two := air.NewVerOneData(scratch) two.SetVal(55) big.SetVal(9) big.SetDuo(8) big.SetPtr1(one) big.SetPtr2(two) bigVerBytes := ShowSeg("\n\n with our 2x2 new big struct, segment seg is:", seg) cv.So(bigVerBytes, cv.ShouldResemble, expFull) // now pretend to be an old client, reading and writing // expecting an empty struct, but full data should be preserved // and written, because we aren't writing into a cramped/ // fixed-space composite-list space. // Before test, verify that if we force reading into text-form, we get // what we expect. actEmptyCap := string(CapnpDecode(bigVerBytes, "VerEmpty")) cv.So(actEmptyCap, cv.ShouldResemble, "()\n") // okay, now the actual test: weThinkEmptyButActuallyFull := air.ReadRootVerEmpty(seg) freshSeg := capn.NewBuffer(nil) wrapEmpty := air.NewRootWrapEmpty(freshSeg) // here is the critical step, this should not truncate: wrapEmpty.SetMightNotBeReallyEmpty(weThinkEmptyButActuallyFull) // now verify: freshBytes := ShowSeg("\n\n after wrapEmpty.SetMightNotBeReallyEmpty(weThinkEmptyButActuallyFull), segment freshSeg is:", freshSeg) reseg, _, err := capn.ReadFromMemoryZeroCopy(freshBytes) if err != nil { panic(err) } ShowSeg(" after re-reading freshBytes, segment reseg is:", reseg) fmt.Printf("freshBytes decoded by capnp as Wrap2x2: '%s'\n", string(CapnpDecode(freshBytes, "Wrap2x2"))) wrap22 := air.ReadRootWrap2x2plus(reseg) notEmpty := wrap22.MightNotBeReallyEmpty() val := notEmpty.Val() cv.So(val, cv.ShouldEqual, 9) duo := notEmpty.Duo() cv.So(duo, cv.ShouldEqual, 8) ptr1 := notEmpty.Ptr1() ptr2 := notEmpty.Ptr2() cv.So(ptr1.Val(), cv.ShouldEqual, 77) cv.So(ptr2.Val(), cv.ShouldEqual, 55) // Tre should get the default, as it was never set cv.So(notEmpty.Tre(), cv.ShouldEqual, 0) // same for Lst3 cv.So(notEmpty.Lst3().Len(), cv.ShouldEqual, 0) }) }) }
func TestDataVersioningZeroPointersToTwo(t *testing.T) { cv.Convey("Given a struct with 2 ptr fields, and another version of the struct with 0 or 1 pointer fields", t, func() { cv.Convey("then reading serialized bigger-struct-list into the smaller (empty or one data-pointer) list should work, truncating/ignoring the new fields", func() { seg := capn.NewBuffer(nil) scratch := capn.NewBuffer(nil) holder := air.NewRootHoldsVerTwoTwoList(seg) twolist := air.NewVerTwoDataTwoPtrList(scratch, 2) plist := capn.PointerList(twolist) d0 := air.NewVerTwoDataTwoPtr(scratch) d0.SetVal(27) d0.SetDuo(26) v1 := air.NewVerOneData(scratch) v1.SetVal(25) v2 := air.NewVerOneData(scratch) v2.SetVal(23) d0.SetPtr1(v1) d0.SetPtr2(v2) d1 := air.NewVerTwoDataTwoPtr(scratch) d1.SetVal(42) d1.SetDuo(41) w1 := air.NewVerOneData(scratch) w1.SetVal(40) w2 := air.NewVerOneData(scratch) w2.SetVal(38) d1.SetPtr1(w1) d1.SetPtr2(w2) plist.Set(0, capn.Object(d0)) plist.Set(1, capn.Object(d1)) holder.SetMylist(twolist) ShowSeg(" before serializing out, segment scratch is:", scratch) ShowSeg(" before serializing out, segment seg is:", seg) // serialize out buf := bytes.Buffer{} seg.WriteTo(&buf) segbytes := buf.Bytes() // and read-back in using smaller expectations reseg, _, err := capn.ReadFromMemoryZeroCopy(segbytes) if err != nil { panic(err) } ShowSeg(" after re-reading segbytes, segment reseg is:", reseg) fmt.Printf("segbytes decoded by capnp as HoldsVerEmptyList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerEmptyList"))) fmt.Printf("segbytes decoded by capnp as HoldsVerOnePtrList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerOnePtrList"))) fmt.Printf("segbytes decoded by capnp as HoldsVerTwoTwoList: '%s'\n", string(CapnpDecode(segbytes, "HoldsVerTwoTwoList"))) reHolder := air.ReadRootHoldsVerEmptyList(reseg) elist := reHolder.Mylist() lene := elist.Len() cv.So(lene, cv.ShouldEqual, 2) reHolder1 := air.ReadRootHoldsVerOnePtrList(reseg) onelist := reHolder1.Mylist() lenone := onelist.Len() cv.So(lenone, cv.ShouldEqual, 2) for i := 0; i < 2; i++ { ele := onelist.At(i) ptr1 := ele.Ptr() cv.So(ptr1.Val(), cv.ShouldEqual, twolist.At(i).Ptr1().Val()) } reHolder2 := air.ReadRootHoldsVerTwoTwoPlus(reseg) twolist2 := reHolder2.Mylist() lentwo2 := twolist2.Len() cv.So(lentwo2, cv.ShouldEqual, 2) for i := 0; i < 2; i++ { ele := twolist2.At(i) ptr1 := ele.Ptr1() ptr2 := ele.Ptr2() cv.So(ptr1.Val(), cv.ShouldEqual, twolist.At(i).Ptr1().Val()) //cv.So(ptr1.Duo(), cv.ShouldEqual, twolist.At(i).Ptr1().Duo()) cv.So(ptr2.Val(), cv.ShouldEqual, twolist.At(i).Ptr2().Val()) //cv.So(ptr2.Duo(), cv.ShouldEqual, twolist.At(i).Ptr2().Duo()) cv.So(ele.Tre(), cv.ShouldEqual, 0) cv.So(ele.Lst3().Len(), cv.ShouldEqual, 0) } }) }) }