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  }