github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/flyweight/flightweight_test.go (about) 1 /* 2 Copyright 2016 Stanislav Liberman 3 Copyright 2022 Steven Stern 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18 package flyweight 19 20 import ( 21 "github.com/lirm/aeron-go/aeron/atomic" 22 "github.com/lirm/aeron-go/aeron/util" 23 "github.com/stretchr/testify/suite" 24 "testing" 25 ) 26 27 const ( 28 str1 = "Hello, World!" 29 str2 = "I hope the alignment is right" 30 str3 = "Goodbye!" 31 ) 32 33 type FlyweightAlignmentTestSuite struct { 34 suite.Suite 35 36 shouldAlign bool 37 stringBuf *atomic.Buffer 38 } 39 40 type StringFly struct { 41 FWBase 42 43 s StringField 44 s2 StringField 45 s3 StringField 46 47 shouldWrap bool 48 } 49 50 func (m *StringFly) Wrap(buf *atomic.Buffer, offset int) Flyweight { 51 pos := offset 52 pos += m.s.Wrap(buf, pos, m, m.shouldWrap) 53 pos += m.s2.Wrap(buf, pos, m, m.shouldWrap) 54 pos += m.s3.Wrap(buf, pos, m, m.shouldWrap) 55 m.SetSize(pos - offset) 56 return m 57 } 58 59 // Only works for small strings 60 func (f *FlyweightAlignmentTestSuite) BuildStringFly(str string) []byte { 61 bytes := []byte{byte(len(str)), 0, 0, 0} 62 bytes = append(bytes, []byte(str)...) 63 if f.shouldAlign { 64 for len(bytes)%4 != 0 { 65 bytes = append(bytes, 0) 66 } 67 } 68 return bytes 69 } 70 71 func (f *FlyweightAlignmentTestSuite) SetupTest() { 72 // This checks for a regression bug in StringField.Wrap. The original bug was that if a StringField starts with an 73 // unaligned offset, Wrap() returned a bad offset. To check for regressions, we need a somewhat awkward 74 // construction: 75 // 76 // str1: ends with an unaligned offset, setting up the bug 77 // str2: triggers the bug, returning a bad offset 78 // str3: uses the bad offset and corrupts the buffer (does not have to be a string) 79 testBuffer := f.BuildStringFly(str1) 80 testBuffer = append(testBuffer, f.BuildStringFly(str2)...) 81 testBuffer = append(testBuffer, f.BuildStringFly(str3)...) 82 f.stringBuf = atomic.MakeBuffer(testBuffer) 83 84 sf := StringFly{shouldWrap: f.shouldAlign} 85 sf.Wrap(f.stringBuf, 0) 86 } 87 88 // Return the possibly-aligned length of `str1`, plus 4 to account for the length field. 89 func (f *FlyweightAlignmentTestSuite) len(str string) int { 90 if f.shouldAlign { 91 return 4 + int(util.AlignInt32(int32(len(str)), 4)) 92 } else { 93 return 4 + len(str) 94 } 95 } 96 97 func (f *FlyweightAlignmentTestSuite) TestStringFlyweight() { 98 sf := StringFly{shouldWrap: f.shouldAlign} 99 sf.Wrap(f.stringBuf, 0) 100 f.Assert().Equal(f.len(str1)+f.len(str2)+f.len(str3), sf.Size()) 101 f.Assert().Equal(sf.s.Get(), str1) 102 f.Assert().Equal(sf.s2.Get(), str2) 103 f.Assert().Equal(sf.s3.Get(), str3) 104 } 105 106 type FlyweightTestSuite struct { 107 suite.Suite 108 } 109 110 type PaddedFly struct { 111 FWBase 112 113 l1 Int64Field 114 i1 Int32Field 115 pad Padding 116 i2 Int32Field 117 pad2 Padding 118 } 119 120 func (m *PaddedFly) Wrap(buf *atomic.Buffer, offset int) Flyweight { 121 pos := offset 122 pos += m.l1.Wrap(buf, pos) 123 pos += m.i1.Wrap(buf, pos) 124 pos += m.pad.Wrap(buf, pos, 64, 64) 125 pos += m.i2.Wrap(buf, pos) 126 pos += m.pad2.Wrap(buf, pos, 128, 64) 127 128 m.SetSize(pos - offset) 129 return m 130 } 131 132 func (f *FlyweightTestSuite) TestPaddingWrap() { 133 buf := atomic.MakeBuffer(make([]byte, 256), 256) 134 135 var fw PaddedFly 136 fw.Wrap(buf, 0) 137 138 f.Assert().Equal(fw.Size(), 192) 139 } 140 141 // Another regression test: Copy of PaddedFly, plus a tiny extra padding that should not be negative. 142 type MorePaddedFly struct { 143 FWBase 144 145 l1 Int64Field 146 i1 Int32Field 147 pad Padding 148 i2 Int32Field 149 pad2 Padding 150 i3 Int32Field 151 pad3 Padding 152 } 153 154 func (m *MorePaddedFly) Wrap(buf *atomic.Buffer, offset int) Flyweight { 155 pos := offset 156 pos += m.l1.Wrap(buf, pos) 157 pos += m.i1.Wrap(buf, pos) 158 pos += m.pad.Wrap(buf, pos, 64, 64) 159 pos += m.i2.Wrap(buf, pos) 160 pos += m.pad2.Wrap(buf, pos, 128, 64) 161 pos += m.i3.Wrap(buf, pos) 162 pos += m.pad3.Wrap(buf, pos, 1, 64) 163 164 m.SetSize(pos - offset) 165 return m 166 } 167 168 func (f *FlyweightTestSuite) TestMinimumPaddingWrap() { 169 buf := atomic.MakeBuffer(make([]byte, 512), 512) 170 171 var fw MorePaddedFly 172 fw.Wrap(buf, 0) 173 174 f.Assert().Equal(fw.Size(), 256) 175 } 176 177 func (f *FlyweightTestSuite) TestLengthAndRawDataFieldCopyString() { 178 testString := "Talos loves Aeron" 179 buf := atomic.MakeBuffer(make([]byte, 128), 128) 180 field := LengthAndRawDataField{} 181 field.Wrap(buf, 0) 182 field.CopyString(testString) 183 f.Require().Equal(field.GetAsASCII(), testString) 184 f.Require().Equal(field.GetAsBuffer().GetBytesArray(0, int32(len(testString))), []byte(testString)) 185 f.Require().EqualValues(field.Length(), len(testString)) 186 } 187 188 func (f *FlyweightTestSuite) TestLengthAndRawDataFieldCopyBytes() { 189 testString := "Talos loves Aeron" 190 buf := atomic.MakeBuffer(make([]byte, 128), 128) 191 field := LengthAndRawDataField{} 192 field.Wrap(buf, 0) 193 field.CopyBuffer(atomic.MakeBuffer([]byte(testString)), 0, int32(len(testString))) 194 f.Require().Equal(field.GetAsASCII(), testString) 195 f.Require().Equal(field.GetAsBuffer().GetBytesArray(0, int32(len(testString))), []byte(testString)) 196 f.Require().EqualValues(field.Length(), len(testString)) 197 } 198 199 func (f *FlyweightTestSuite) TestLengthAndRawDataFieldGetFields() { 200 testString := "Talos loves Aeron" 201 bufferBytes := append([]byte{byte(len(testString)), 0, 0, 0}, 202 []byte(testString)...) 203 buf := atomic.MakeBuffer(bufferBytes) 204 field := LengthAndRawDataField{} 205 f.Require().Equal(field.Wrap(buf, 0), len(testString)+4) 206 f.Require().Equal(field.GetAsASCII(), testString) 207 f.Require().Equal(field.GetAsBuffer().GetBytesArray(0, int32(len(testString))), []byte(testString)) 208 } 209 210 func TestFlyweight(t *testing.T) { 211 suite.Run(t, &FlyweightAlignmentTestSuite{shouldAlign: true}) 212 suite.Run(t, &FlyweightAlignmentTestSuite{shouldAlign: false}) 213 suite.Run(t, new(FlyweightTestSuite)) 214 }