github.com/ericwq/aprilsh@v0.0.0-20240517091432-958bc568daa0/statesync/user_test.go (about) 1 // Copyright 2022 wangqi. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 package statesync 6 7 import ( 8 "reflect" 9 "strings" 10 "testing" 11 12 "github.com/ericwq/aprilsh/terminal" 13 "github.com/rivo/uniseg" 14 ) 15 16 func TestUserStreamSubtract(t *testing.T) { 17 sizes := []struct { 18 width, height int 19 }{ 20 {80, 40}, {132, 60}, {140, 70}, 21 } 22 23 tc := []struct { 24 name string 25 sizeB bool // add sizes data 26 keystroke string 27 prefix string 28 remains string 29 }{ 30 {"subtract english keystroke from prefix", true, "Hello world", "Hello ", "world"}, 31 {"subtract chinese keystroke from prefix", false, "你好!中国", "你好!", "中国"}, 32 {"subtract equal keystroke from prefix", false, "equal prefix", "equal prefix", ""}, 33 } 34 35 for _, v := range tc { 36 37 u1 := UserStream{} 38 39 // add user keystroke 40 chs := []rune(v.keystroke) 41 for i := range chs { 42 u1.PushBack([]rune{chs[i]}) 43 // fmt.Printf("#test Subtract() PushBack %q into u1\n", chs[i]) 44 } 45 // fmt.Printf("#test Subtract() base %s\n", &u1) 46 47 // add size data 48 if v.sizeB { 49 for _, v := range sizes { 50 u1.PushBackResize(v.width, v.height) 51 } 52 // fmt.Printf("#test DiffFrom() base+size %s\n", &u1) 53 } 54 55 u2 := UserStream{} 56 57 // add prefix user keystroke 58 prefix := []rune(v.prefix) 59 for i := range prefix { 60 u2.PushBack([]rune{prefix[i]}) 61 // fmt.Printf("#test Subtract() PushBack %q into u2\n", prefix[i]) 62 } 63 // fmt.Printf("#test Subtract() prefix %s\n", &u2) 64 65 // subtract the prefix from u1 66 u1.Subtract(&u2) 67 68 // only collect the UserByteType part 69 var output strings.Builder 70 for _, v := range u1.actions { 71 switch v.theType { 72 case UserByteType: 73 output.WriteString(string(v.userByte.Chs)) 74 } 75 } 76 // fmt.Printf("#test Subtract() result %s\n", &u1) 77 78 // validate the result 79 got := output.String() 80 if got != v.remains { 81 t.Errorf("%q expect %q, got %q\n", v.name, v.remains, got) 82 } 83 } 84 } 85 86 func TestUserStreamUserEvent(t *testing.T) { 87 e1 := NewUserEvent(terminal.UserByte{Chs: []rune("🇧🇷")}) 88 e2 := NewUserEvent(terminal.UserByte{Chs: []rune("🇧🇷")}) 89 90 if !reflect.DeepEqual(e1, e2) { 91 t.Errorf("#test UserEvent equal should return true, %v, %v\n", e1, e2) 92 } 93 94 e1 = NewUserEventResize(terminal.Resize{Width: 80, Height: 40}) 95 e2 = NewUserEventResize(terminal.Resize{Width: 80, Height: 40}) 96 97 if !reflect.DeepEqual(e1, e2) { 98 t.Errorf("#test UserEvent equal should return true, %v, %v\n", e1, e2) 99 } 100 } 101 102 func TestUserStreamApplyString(t *testing.T) { 103 baseSize := []struct { 104 width, height int 105 }{ 106 {80, 40}, {132, 60}, {140, 70}, 107 } 108 109 deltaSize := []struct { 110 width, height int 111 }{ 112 {80, 40}, {132, 60}, {140, 70}, 113 } 114 115 tc := []struct { 116 name string 117 keystroke string 118 prefix string 119 }{ 120 {"diff & apply english keystroke from prefix", "Hello world", "Hello "}, 121 {"diff & apply chinese keystroke from prefix", "你好!中国", "你好!"}, 122 {"diff & apply equal prefix", "equal prefix", "equal prefix"}, 123 {"diff & apply flag", "Chin\u0308\u0308a 🏖 i国旗🇳🇱Fun 🌈with F🇧🇷lg", ""}, 124 } 125 126 for _, v := range tc { 127 128 u1 := UserStream{} 129 // add user keystroke 130 graphemes := uniseg.NewGraphemes(v.keystroke) 131 for graphemes.Next() { 132 chs := graphemes.Runes() 133 u1.PushBack(chs) 134 // if v.prefix == "" { 135 // fmt.Printf("#test ApplyString() %c %q %x\n", chs, chs, chs) 136 // } 137 } 138 // add base size data 139 for _, v := range baseSize { 140 // u1.pushBackResize(terminal.Resize{Width: v.width, Height: v.height}) 141 u1.PushBackResize(v.width, v.height) 142 } 143 // fmt.Printf("#test ApplyString() base+size %s len=%d\n", &u1, len(u1.actions)) 144 145 u2 := UserStream{} 146 // add prefix user keystroke 147 graphemes = uniseg.NewGraphemes(v.prefix) 148 for graphemes.Next() { 149 chs := graphemes.Runes() 150 u2.PushBack(chs) 151 } 152 153 // add delta size data 154 for _, v := range deltaSize { 155 // u2.pushBackResize(terminal.Resize{Width: v.width, Height: v.height}) 156 u2.PushBackResize(v.width, v.height) 157 } 158 // fmt.Printf("#test ApplyString() prefix %s len=%d\n", &u2, len(u2.actions)) 159 160 diff := u1.DiffFrom(&u2) 161 u1.Subtract(&u2) // after DiffFrom(), u1 is not affected. Call subtract to modify it. 162 // fmt.Printf("#test ApplyString() u1=%s diff len=%d\n", &u1, len(diff)) 163 164 u3 := UserStream{} 165 u3.ApplyString(diff) 166 // fmt.Printf("#test ApplyString() u3=%s\n\n", &u3) 167 168 if !u1.Equal(&u3) { 169 t.Errorf("%q expect \n%s, got \n%s\n", v.name, &u1, &u3) 170 } 171 } 172 } 173 174 func TestUserStreamInitDiff(t *testing.T) { 175 u3 := UserStream{} 176 got := u3.InitDiff() 177 expect := "" 178 if expect != got { 179 t.Errorf("#test InitDiff() expect %q, got %q\n", expect, got) 180 } 181 } 182 183 func TestUserStreamApplyStringFail(t *testing.T) { 184 diff := "malformed diff" 185 u3 := &UserStream{} 186 if err := u3.ApplyString(diff); err == nil { 187 t.Error("#test ApplyString() expect error, got nil") 188 } 189 } 190 191 func TestUserStreamString(t *testing.T) { 192 tc := []struct { 193 title string 194 keystroke string 195 size bool 196 expect string 197 }{ 198 {"no size", "has keystroke, no size data", false, "Keystroke:\"has keystroke, no size data\", Resize:, size=27"}, 199 {"no keystroke", "", true, "Keystroke:\"\", Resize:(80,40),(132,60),(140,70),, size=3"}, 200 { 201 "both keystroke and size", "has both keystroke and data", true, 202 "Keystroke:\"has both keystroke and data\", Resize:(80,40),(132,60),(140,70),, size=30", 203 }, 204 {"empty", "", false, "Keystroke:\"\", Resize:, size=0"}, 205 } 206 207 sizes := []struct { 208 width, height int 209 }{ 210 {80, 40}, {132, 60}, {140, 70}, 211 } 212 for _, v := range tc { 213 214 u1 := UserStream{} 215 216 // add user keystroke 217 chs := []rune(v.keystroke) 218 for i := range chs { 219 u1.PushBack([]rune{chs[i]}) 220 } 221 222 // add size data 223 if v.size { 224 for _, v := range sizes { 225 u1.PushBackResize(v.width, v.height) 226 } 227 } 228 229 got := u1.String() 230 if v.expect != got { 231 t.Errorf("%q expect [%s], got [%s]\n", v.title, v.expect, got) 232 } 233 } 234 } 235 236 func TestUserStreamGetAction(t *testing.T) { 237 tc := []struct { 238 title string 239 keystrokeStr string 240 addSizeItem bool 241 expectSize int 242 idx01 int 243 item1 terminal.UserByte 244 idx02 int 245 item2 terminal.Resize 246 idx03 int 247 item3 terminal.ActOn 248 }{ 249 { 250 "english keystroke and size", "has both keystroke and data", true, 30, 251 6, 252 terminal.UserByte{Chs: []rune{'t'}}, 253 28, 254 terminal.Resize{Width: 132, Height: 60}, 255 31, nil, 256 }, 257 { 258 "chinese keystroke and size", "包含用户输入和窗口大小调整数据", true, 18, 259 6, 260 terminal.UserByte{Chs: []rune("和")}, 261 15, 262 terminal.Resize{Width: 80, Height: 40}, 263 18, nil, 264 }, 265 } 266 267 sizes := []struct { 268 width, height int 269 }{ 270 {80, 40}, {132, 60}, {140, 70}, 271 } 272 for _, v := range tc { 273 274 us := UserStream{} 275 276 // add user keystroke 277 chs := []rune(v.keystrokeStr) 278 for i := range chs { 279 us.PushBack([]rune{chs[i]}) 280 } 281 282 // add size data 283 if v.addSizeItem { 284 for _, v := range sizes { 285 us.PushBackResize(v.width, v.height) 286 } 287 } 288 289 // validate size 290 if v.expectSize != us.Size() { 291 t.Errorf("%q expect size %d, got %d\n", v.title, v.expectSize, us.Size()) 292 } 293 294 // validate user byte item 295 if !reflect.DeepEqual(v.item1, us.GetAction(v.idx01)) { 296 t.Errorf("%q expect index %d contains %q, got %q\n", v.title, v.idx01, v.item1, us.GetAction(v.idx01)) 297 } 298 299 // validate size item 300 if !reflect.DeepEqual(v.item2, us.GetAction(v.idx02)) { 301 t.Errorf("%q expect index %d contains %q, got %q\n", v.title, v.idx02, v.item2, us.GetAction(v.idx02)) 302 } 303 304 // validate out-of-range item 305 if us.GetAction(v.idx03) != v.item3 { 306 t.Errorf("%q getAction() expect %q, got %q\n", v.title, v.item3, us.GetAction(v.idx03)) 307 } 308 } 309 } 310 311 func TestUserStreamClone(t *testing.T) { 312 us := &UserStream{} 313 314 // prepare user input data 315 keystrokeStr := "data for clone" 316 chs := []rune(keystrokeStr) 317 for i := range chs { 318 us.PushBack([]rune{chs[i]}) 319 } 320 321 // prepare resize data 322 sizes := []struct { 323 width, height int 324 }{ 325 {80, 40}, {132, 60}, {140, 70}, 326 } 327 for _, v := range sizes { 328 us.PushBackResize(v.width, v.height) 329 } 330 331 clone := us.Clone() 332 333 if !reflect.DeepEqual(us, clone) { 334 t.Errorf("#test expect %v, got %v\n", us, clone) 335 } 336 337 clone.ResetInput() // just for coverage 338 } 339 340 func TestUserStreamEmpty(t *testing.T) { 341 us := &UserStream{} 342 343 // prepare user input data 344 keystrokeStr := "data for clone" 345 chs := []rune(keystrokeStr) 346 for i := range chs { 347 us.PushBack([]rune{chs[i]}) 348 } 349 350 if us.Empty() { 351 t.Errorf("#test expect false, got %t\n", us.Empty()) 352 } 353 }