github.com/ericwq/aprilsh@v0.0.0-20240517091432-958bc568daa0/statesync/complete_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  	"io"
     9  	"log/slog"
    10  	"math"
    11  	"reflect"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/ericwq/aprilsh/terminal"
    17  	"github.com/ericwq/aprilsh/util"
    18  )
    19  
    20  func TestCompleteSubtract(t *testing.T) {
    21  	c, _ := NewComplete(8, 4, 4)
    22  	c.Subtract(c) // do nothing, just for coverage
    23  	c.GetEmulator()
    24  }
    25  
    26  func TestCompleteInitDiff(t *testing.T) {
    27  	c, _ := NewComplete(8, 4, 4)
    28  	got := c.InitDiff()
    29  
    30  	expect := ""
    31  	if expect != got {
    32  		t.Errorf("#test InitDiff() expect %q, got %q\n", expect, got)
    33  	}
    34  }
    35  
    36  func TestCompleteApplyString(t *testing.T) {
    37  	tc := []struct {
    38  		label         string
    39  		seq           string
    40  		width, height int
    41  		echoAck       uint64
    42  	}{
    43  		{"fill one row with string", "\x1B[4;4HErase to the end of line\x1B[0K.", 0, 0, 0},
    44  		{"fill one row and resize", "\x1B[6;67HLAST", 70, 30, 0},
    45  		{"fill one row and set ack", "\x1B[7;7H左边\x1B[7;77H中文", 0, 0, 3},
    46  	}
    47  
    48  	util.Logger.CreateLogger(io.Discard, true, slog.LevelDebug)
    49  	// util.Logger.CreateLogger(os.Stdout, true, slog.LevelDebug)
    50  
    51  	for _, v := range tc {
    52  		c0, _ := NewComplete(80, 40, 40)
    53  		c1, _ := NewComplete(80, 40, 40)
    54  
    55  		// resize new state if necessary
    56  		if v.height != 0 && v.width != 0 {
    57  			r := terminal.Resize{Width: v.width, Height: v.height}
    58  			emu := c1.terminal
    59  			r.Handle(emu)
    60  		}
    61  
    62  		// print some data on screen
    63  		c1.terminal.HandleStream(v.seq)
    64  
    65  		// validate the equal is false
    66  		if c1.Equal(c0) {
    67  			t.Errorf("%q expect false equal(), got true", v.label)
    68  		}
    69  
    70  		// set echoAck for new state
    71  		if v.echoAck != 0 {
    72  			c1.echoAck = v.echoAck
    73  		}
    74  
    75  		// new state calculate difference with old state as parameter
    76  		diff := c1.DiffFrom(c0)
    77  
    78  		// apply to the old state
    79  		c0.ApplyString(diff)
    80  
    81  		// validate the result
    82  		if got := c0.DiffFrom(c1); got != "" {
    83  			// if !c0.Equal(c1) {
    84  			// got := c0.DiffFrom(c1)
    85  			t.Errorf("%q expect empty result after ApplyString(), got %q\n", v.label, got)
    86  		}
    87  	}
    88  }
    89  
    90  func TestApplyString_Fail(t *testing.T) {
    91  	diff := "mislead\n\x04:\x02@\x03\n2\x120\"."
    92  
    93  	c, _ := NewComplete(80, 40, 40)
    94  	if err := c.ApplyString(diff); err == nil {
    95  		t.Error("#test feed ApplyString with wrong parameter, expect error.")
    96  	}
    97  }
    98  
    99  func TestCompleteSetEchoAck(t *testing.T) {
   100  	tc := []struct {
   101  		label  string
   102  		data   []pair
   103  		expect bool
   104  	}{
   105  		{"find two states", []pair{{1, 49}, {2, 43}, {3, 52}}, true},
   106  		{"too quick to find the latest state", []pair{{1, 9}, {2, 13}, {3, 12}}, false},
   107  	}
   108  
   109  	c, _ := NewComplete(8, 4, 4)
   110  	now := time.Now().UnixMilli()
   111  
   112  	for _, v := range tc {
   113  		// reset history
   114  		c.inputHistory = make([]pair, 0)
   115  		c.echoAck = 0
   116  
   117  		// register the frame number and time
   118  		var ts int64 = 0
   119  		for _, p := range v.data {
   120  
   121  			ts += p.timestamp
   122  			// note: the timestamp is delta value in ms.
   123  			c.RegisterInputFrame(p.frameNum, now+ts)
   124  			// fmt.Printf("#test setEchoAck timestamp=%d, ts=%d\n", p.timestamp, ts)
   125  		}
   126  
   127  		// fmt.Printf("#test setEchoAck inputHistory = %v\n", c.inputHistory)
   128  
   129  		got := c.SetEchoAck(now + ts)
   130  		// fmt.Printf("#test setEchoAck inputHistory = %v\n", c.inputHistory)
   131  		if v.expect != got {
   132  			t.Errorf("%q expect %t, got %t\n", v.label, v.expect, got)
   133  		}
   134  	}
   135  }
   136  
   137  func TestCompleteWaitTime(t *testing.T) {
   138  	tc := []struct {
   139  		label  string
   140  		data   []pair
   141  		time   int64
   142  		expect int
   143  	}{
   144  		{"history size <2", []pair{{1, 49}}, 0, math.MaxInt},
   145  		{"now < last +50 ", []pair{{1, 49}, {2, 43}}, 9, 50 - 9},
   146  		{"last +50 <= now", []pair{{1, 49}, {2, 43}}, 50, 0},
   147  	}
   148  
   149  	c, _ := NewComplete(8, 4, 4)
   150  	now := time.Now().UnixMilli()
   151  
   152  	for _, v := range tc {
   153  		// reset history
   154  		c.inputHistory = make([]pair, 0)
   155  		c.echoAck = 0
   156  
   157  		// register the frame number and time
   158  		var ts int64 = 0
   159  		for _, p := range v.data {
   160  
   161  			ts += p.timestamp
   162  			// note: the timestamp is delta value in ms.
   163  			c.RegisterInputFrame(p.frameNum, now+ts)
   164  			// fmt.Printf("#test setEchoAck timestamp=%d, ts=%d\n", p.timestamp, ts)
   165  		}
   166  
   167  		got := c.WaitTime(now + ts + v.time)
   168  		if v.expect != got {
   169  			t.Errorf("%q expect %d, got %d\n", v.label, v.expect, got)
   170  		}
   171  	}
   172  }
   173  
   174  func TestCompleteResetInput(t *testing.T) {
   175  	c, _ := NewComplete(8, 4, 4)
   176  
   177  	c.ResetInput()
   178  	if c.terminal.GetCursorCol() != 0 || c.terminal.GetCursorRow() != 0 {
   179  		t.Errorf("#test after resetInput() the cursor should be in (0,0), got (%d,%d)\n",
   180  			c.terminal.GetCursorRow(), c.terminal.GetCursorCol())
   181  	}
   182  }
   183  
   184  func TestCompleteClone(t *testing.T) {
   185  	c, _ := NewComplete(8, 4, 4)
   186  	clone := c.Clone()
   187  
   188  	if !c.Equal(clone) {
   189  		t.Errorf("#test clone expect %v, got %v\n", c, clone)
   190  	}
   191  }
   192  
   193  func TestDiffFrom(t *testing.T) {
   194  	tc := []struct {
   195  		label        string
   196  		nCols, nRows int      // screen size
   197  		seq1         []string // sequence before action
   198  		seq2         []string // sequence after action
   199  		resp         string
   200  	}{
   201  		{"simple case", 80, 40, []string{},
   202  			[]string{
   203  				"nvide:0.8.9\r\n",
   204  				"\r\nLua, C/C++ and Golang Integrated Development Environment.\r\nPowered by neovim, luals, gopls and clangd.\r\n", "\x1b]0;aprilsh\a",
   205  				"ide@openrc-nvide:~/develop $ \x1b[6n"}, "\x1b[5;30R"},
   206  		{"vi and quit", 80, 40,
   207  			[]string{
   208  				"\x1b]0;aprilsh\a", // set title to avoid title stack warning
   209  				/*vi start*/ "\x1b[?1049h\x1b[22;0;0t\x1b[?1h\x1b=\x1b[H\x1b[2J\x1b]11;?\a\x1b[?2004h\x1b[?u\x1b[c\x1b[?25h",
   210  				// "\x1b]11;rgb:0000/0000/0000\x1b\\\x1b[?64;1;9;15;21;22c"
   211  				/*clear screen*/ "\x1b[?25l\x1b(B\x1b[m\x1b[H\x1b[2J\x1b[>4;2m\x1b]112\a\x1b[2 q\x1b[?1002h\x1b[?1006h\x1b[38;2;233;233;244m\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[J\x1b[H",
   212  				/*vi file*/ "\x1b(B\x1b[m\x1b[38;2;98;100;131m  \x1b(B\x1b[m\x1b[38;2;248;248;242m1 \x1b(B\x1b[m\x1b[38;2;233;233;244m\x1b[48;2;45;48;62mgo 1.19                                                                                                                                                         \r\n\x1b(B\x1b[m\x1b[38;2;98;100;131m  \x1b(B\x1b[m\x1b[38;2;94;95;105m2 \x1b(B\x1b[m\x1b[38;2;233;233;244m\x1b[K\r\n\x1b(B\x1b[m\x1b[38;2;98;100;131m  \x1b(B\x1b[m\x1b[38;2;94;95;105m3 \x1b(B\x1b[m\x1b[38;2;233;233;244muse (\x1b[K\r\n\x1b(B\x1b[m\x1b[38;2;98;100;131m  \x1b(B\x1b[m\x1b[38;2;94;95;105m4 \x1b(B\x1b[m\x1b[38;2;233;233;244m   ./aprilsh\x1b[K\r\n\x1b(B\x1b[m\x1b[38;2;98;100;131m  \x1b(B\x1b[m\x1b[38;2;94;95;105m5 \x1b(B\x1b[m\x1b[38;2;233;233;244m   ./terminfo\x1b[K\r\n\x1b(B\x1b[m\x1b[38;2;98;100;131m  \x1b(B\x1b[m\x1b[38;2;94;95;105m6 \x1b(B\x1b[m\x1b[38;2;233;233;244m)\x1b[K\x1b(B\x1b[m\x1b[38;2;98;100;131m\r\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b[K\n\x1b(B\x1b[0;1m\x1b[38;2;40;42;54m\x1b[48;2;139;155;205m \ue7c5\x1b[39;3H NORMAL\x1b(B\x1b[m\x1b[38;2;139;155;205m\x1b[48;2;94;95;105m\ue0bc\x1b[39;11H \x1b(B\x1b[m\x1b[38;2;94;95;105m\x1b[48;2;65;67;79m\ue0bc\x1b[39;13H \x1b(B\x1b[m\x1b[38;2;248;248;242m\x1b[48;2;65;67;79m go.work \x1b(B\x1b[m\x1b[38;2;65;67;79m\ue0bc\x1b[39;24H                                                                                                                         \x1b(B\x1b[m\x1b[38;2;255;112;112m\ue0b6\x1b[39;146H\x1b(B\x1b[m\x1b[38;2;55;56;68m\x1b[48;2;255;112;112m\U000f024b\x1b[39;147H \x1b(B\x1b[m\x1b[38;2;248;248;242m\x1b[48;2;65;67;79m develop \x1b(B\x1b[m\x1b[38;2;80;250;123m\x1b[48;2;65;67;79m\ue0b6\x1b[39;158H\x1b(B\x1b[m\x1b[38;2;40;42;54m\x1b[48;2;80;250;123m\ue612\x1b[39;159H \x1b(B\x1b[m\x1b[38;2;80;250;123m\x1b[48;2;65;67;79m Top \x1b(B\x1b[m\x1b[38;2;233;233;244m\r\n\x1b[J\x1b]112\a\x1b[2 q\x1b[1;5H\x1b[?25h",
   213  				/*screen border*/ "\x1b[?25l\n\n\n\x1b(B\x1b[m\x1b[38;2;60;61;73m│\x1b[4;6H  \x1b[5;5H│\x1b[5;6H  \x1b[1;5H\x1b[?25h",
   214  				/*loading*/ "\x1b[?25l\x1b[39;52H\x1b(B\x1b[m\x1b[38;2;80;250;123m \U000f0aa2\x1b[39;54H Setting up workspace Loading packages... (0%)                             \x1b(B\x1b[m\x1b[38;2;139;155;205m \uf085\x1b[39;131H  LSP ~ gopls \x1b[1;5H\x1b[?25h",
   215  				/*loading*/ "\x1b[?25l\x1b[39;49H\x1b(B\x1b[m\x1b[38;2;80;250;123m \U000f0aa2\x1b[39;51H Setting up workspace Finished loading packages. (0%)\x1b[1;5H\x1b[?25h",
   216  				/*loading*/ "\x1b[?25l\x1b[39;49H\x1b(B\x1b[m\x1b[38;2;65;67;79m                                                                                \x1b[1;5H\x1b[?25h",
   217  			},
   218  			[]string{
   219  				/*1st sequence after :q*/ "\x1b[?25l\r\x1b[40;1H\x1b[?25h",
   220  				/*2nd sequence after :q*/ "\x1b[?25l\x1b]112\a\x1b[2 q\x1b[?25h",
   221  				/*3rd sequence after :q*/ "\x1b[?25l\x1b]112\a\x1b[2 q\x1b[?1002l\x1b[?1006l\x1b(B\x1b[m\x1b[?25h\x1b[?1l\x1b>\x1b[>4;0m\x1b[?1049l\x1b[23;0;0t\x1b[?2004l\x1b[?1004l\x1b[?25h",
   222  				/*4th sequence after :q*/ "ide@openrc-nvide:~/develop $ \x1b[6n",
   223  			}, "\x1b[1;30R"},
   224  		{"screen with content then vi utf-8 file", 80, 40,
   225  			[]string{
   226  				"\x1b]0;aprilsh\a", // set title to avoid title stack warning
   227  				"ide@openrc-nvide:~/develop $ \x1b[6n"},
   228  			[]string{
   229  				"\x1b[?1049h\x1b[22;0;0t\x1b[?1h\x1b=\x1b[H\x1b[2J\x1b]11;?\a\x1b[?2004h\x1b[?u\x1b[c\x1b[?25h",
   230  			}, "\x1b]11;rgb:0000/0000/0000\x1b\\\x1b[?64;1;9;15;21;22c"},
   231  		{"cat file larger than screen rows", 80, 40,
   232  			[]string{
   233  				"nvide:0.8.9\r\n\r\nLua, C/C++ and Golang Integrated Development Environment.\r\nPowered by neovim, luals, gopls and clangd.\r\n",
   234  				"\x1b]0;aprilsh\a",
   235  				"ide@openrc-nvide:~ $ cd develop\r\n",
   236  				"ide@openrc-nvide:~/develop $ ls\r\n",
   237  				"\x1b[1;34mNvChad\x1b[m                     \x1b[0;0mgit.md\x1b[m                     \x1b[1;34mgolangIDE\x1b[m                  \x1b[1;34mneovim-lua\x1b[m                 \x1b[1;34ms6\x1b[m\r\n\x1b[1;34maprilsh\x1b[m                    \x1b[0;0mgo.work\x1b[m                    \x1b[1;34mmosh\x1b[m                       \x1b[1;34mnvide\x1b[m                      \x1b[1;34mtelescope.nvim\x1b[m\r\n\x1b[1;34mdotfiles\x1b[m                   \x1b[0;0mgo.work.sum\x1b[m                \x1b[0;0mmosh-1.3.2.tar.gz\x1b[m          \x1b[0;0mpersonal-access-token.txt\x1b[m  \x1b[1;34mterminfo\x1b[m\r\n",
   238  				"ide@openrc-nvide:~/develop $ cat tokens.txt\r\n",
   239  			},
   240  			[]string{"111\r\naaa\r\nbbb\r\n\r\nccc\r\nddd\r\n\r\neeee\r\nffff\r\n\r\nggg\r\nhhh#\r\n\r\napp\r\niii\r\n\r\njjj\r\nkkk#\r\n\r\nlll\r\nmmm\r\n\r\n#nnn \r\n\r\nooo.\r\nppp\r\n\r\nqqq\r\nrrr\r\nsss\r\nttt\r\n\r\n隐uuu\r\n方式vvv\r\niwww\r\nxxx\r\n\r\nyyy\r\nzzz\r\n\r\n专用aaa\r\nbbb\r\nccc\r\nddd\r\n\r\neee\r\nfff\r\nggg\r\n"}, // total 48 \n
   241  			""},
   242  		{"return on last row", 101, 24,
   243  			[]string{"nvide:0.8.9\r\n",
   244  				"\r\nLua, C/C++ and Golang Integrated Development Environment.\r\nPowered by neovim, luals, gopls and clangd.\r\n",
   245  				"\x1b]0;aprilsh\a",
   246  				"ide@openrc-nvide:~ $ ls\r\n",
   247  				"\x1b[1;34mdevelop\x1b[m  \x1b[1;34mproj\x1b[m\r\n",
   248  				"ide@openrc-nvide:~ $ cd develop\r\n",
   249  				"ide@openrc-nvide:~/develop $ ls -al\r\n",
   250  				"total 1696\r\ndrwxr-xr-x   22 ide      develop        704 Oct 26 20:28 \x1b[1;34m.\x1b[m\r\ndrwxr-sr-x    1 ide      develop       4096 Oct 26 21:08 \x1b[1;34m..\x1b[m\r\n-rw-r--r--    1 ide      develop       8196 Sep 15 22:10 \x1b[0;0m.DS_Store\x1b[m\r\ndrwxr-xr-x   19 ide      develop        608 Oct 27 19:13 \x1b[1;34maprilsh\x1b[m\r\ndrwxr-xr-x   14 ide      develop        448 May 26  2022 \x1b[1;34mdocTerminal\x1b[m\r\ndrwxr-xr-x   29 ide      develop        928 Aug  7 22:02 \x1b[1;34mexamples\x1b[m\r\n-rw-r--r--    1 ide      develop         50 May 21 14:35 \x1b[0;0mgo.work\x1b[m\r\n-rw-r--r--    1 ide      develop        694 Aug  5 22:36 \x1b[0;0mgo.work.sum\x1b[m\r\ndrwxr-xr-x   18 ide      develop        576 Jun 24 22:36 \x1b[1;34mgoutmp\x1b[m\r\ndrwxr-xr-x    9 ide      develop        288 Dec 17  2020 \x1b[1;34mgskills\x1b[m\r\n-rwxr-xr-x    1 ide      develop    1484717 Nov 15  2021 \x1b[1;32mgskills.key\x1b[m\r\ndrwxr-xr-x    6 ide      develop        192 Nov 15  2021 \x1b[1;34mimages\x1b[m\r\ndrwxr-sr-x   32 ide      develop       1024 May  9  2021 \x1b[1;34mldd3\x1b[m\r\ndrwxr-xr-x   16 ide      develop        512 Jun 15 21:48 \x1b[1;34mlibutempter\x1b[m\r\ndrwxr-xr-x   51 ide      develop       1632 Apr 30  2022 \x1b[1;34mncurses-6.3\x1b[m\r\ndrwxr-xr-x   14 ide      develop        448 Sep 27 22:01 \x1b[1;34mnvide\x1b[m\r\n-rw-rw-rw-    1 ide      develop        782 Oct 26 20:27 \x1b[0;0mpersonal-access-token.txt\x1b[m\r\ndrwxr-xr-x   10 ide      develop        320 May 30 18:59 \x1b[1;34ms6\x1b[m\r\ndrwxr-xr-x   33 ide      develop       1056 Jun 28 06:17 \x1b[1;34mterminfo\x1b[m\r\ndrwxr-xr-x   82 ide      develop       2624 May 11  2022 \x1b[1;34mvttest-20220215\x1b[m\r\n-rw-r--r--    1 ide      develop     216949 May 11  2022 \x1b[0;0mvttest.tar.gz\x1b[m\r\ndrwxr-xr-x    5 ide      develop        160 May  2  2022 \x1b[1;34mworkspace\x1b[m\r\nide@openrc-nvide:~/develop $ ",
   251  			},
   252  			[]string{"\r\n"},
   253  			""},
   254  		{"write on ring buffer", 8, 4,
   255  			[]string{"8888888\r\na\r\nb\r\nc\r\n",
   256  				"a1\r\na2\r\na3\r\na4\r\n",
   257  				"b2\r\nb2\r\nb3\r\nb4\r\n",
   258  				"c2\r\nc2\r\nc3\r\nc4\r\n",
   259  			},
   260  			[]string{"x1\r\nx2\r\nx3\r\nx4\r\n"}, ""},
   261  	}
   262  
   263  	util.Logger.CreateLogger(io.Discard, true, slog.LevelDebug)
   264  	// util.Logger.CreateLogger(os.Stderr, true, slog.LevelDebug)
   265  
   266  	for _, v := range tc {
   267  		t.Run(v.label, func(t *testing.T) {
   268  			savedLines := v.nRows * 3
   269  
   270  			a, _ := NewComplete(v.nCols, v.nRows, savedLines)
   271  			c, _ := NewComplete(v.nCols, v.nRows, savedLines)
   272  
   273  			// fmt.Printf("#TestDiffFrom point=%d\n", 333)
   274  			// assumed state prepare
   275  			var t2 strings.Builder
   276  			var ss strings.Builder
   277  			for i := range v.seq1 {
   278  				ss.WriteString(v.seq1[i])
   279  			}
   280  			t2.WriteString(a.Act(ss.String()))
   281  			// fmt.Printf("#TestDiffFrom point=%d\n", 444)
   282  			c.Act(ss.String())
   283  
   284  			if !c.EqualTrace(a) {
   285  				t.Errorf("%s: prepare stage error\n", v.label)
   286  			}
   287  			c.Reset()
   288  			a.Reset()
   289  
   290  			// current state changed after :q command
   291  			t2.Reset()
   292  			ss.Reset()
   293  			for i := range v.seq2 {
   294  				ss.WriteString(v.seq2[i])
   295  			}
   296  
   297  			t2.WriteString(c.Act(ss.String()))
   298  			if v.resp != t2.String() {
   299  				t.Errorf("%s: terminal response expect %q, got %q\n", v.label, v.resp, t2.String())
   300  			}
   301  
   302  			// util.Log.Debug("TestDiffFrom","point", 601)
   303  			diff := c.DiffFrom(a)
   304  			// util.Log.Debug("TestDiffFrom","point", 602)
   305  
   306  			n := a.Clone()
   307  			n.ApplyString(diff)
   308  			if !c.EqualTrace(n) {
   309  				t.Errorf("%s: round-trip Instruction verification failed!", v.label)
   310  				t.Logf("%s: diff=%q", v.label, diff)
   311  			}
   312  
   313  			// util.Log.Debug("TestDiffFrom","point", 603)
   314  			cd := c.InitDiff()
   315  			// util.Log.Debug("TestDiffFrom","point", 604)
   316  			nd := n.InitDiff()
   317  			// util.Log.Debug("TestDiffFrom","point", 605)
   318  			if cd != nd {
   319  				t.Errorf("%s: target state Instruction verification failed!", v.label)
   320  				// t.Logf("current state diff=%q", cd)
   321  				// t.Logf("new     state diff=%q", nd)
   322  			}
   323  		})
   324  	}
   325  }
   326  
   327  func TestEqual(t *testing.T) {
   328  	tc := []struct {
   329  		label string
   330  		seq0  string
   331  		seq1  string
   332  	}{
   333  		{"custom equal", "\x1B[6;67HLAST\x1B[1;7H", "\x1B[6;67HLAST\x1B[1;7H"},
   334  		// {"fill one row and set ack", "\x1B[7;7H左边\x1B[7;77H中文", 0, 0, 3},
   335  	}
   336  	v := tc[0]
   337  	c0, _ := NewComplete(80, 40, 40)
   338  	c1, _ := NewComplete(80, 40, 40)
   339  
   340  	c0.terminal.HandleStream(v.seq0)
   341  	c1.terminal.HandleStream(v.seq1)
   342  
   343  	if !c0.Equal(c1) {
   344  		t.Errorf("%q expect not equal object\n", v.label)
   345  	}
   346  }
   347  
   348  func (c *Complete) equalDiffFrom(x *Complete) bool {
   349  	// use DiffFrom to compare the state
   350  	if diff := c.DiffFrom(x); diff != "" {
   351  		return false
   352  	}
   353  	return true
   354  	// return reflect.DeepEqual(c.terminal, x.terminal) && c.echoAck == x.echoAck
   355  }
   356  
   357  func (c *Complete) deepEqual(x *Complete) bool {
   358  	return reflect.DeepEqual(c.terminal, x.terminal) && c.echoAck == x.echoAck
   359  }
   360  
   361  // check Equal mthod
   362  // func (c *Complete) customEqual(x *Complete) bool {
   363  // 	if c.echoAck != x.echoAck {
   364  // 		return false
   365  // 	}
   366  //
   367  // 	return c.terminal.Equal(x.terminal)
   368  // }
   369  
   370  // https://blog.logrocket.com/benchmarking-golang-improve-function-performance/
   371  // https://coder.today/tech/2018-11-10_profiling-your-golang-app-in-3-steps/
   372  // https://www.speedscope.app/
   373  func BenchmarkDiffFromEqual(b *testing.B) {
   374  	tc := []struct {
   375  		label string
   376  		seq0  string
   377  		seq1  string
   378  	}{
   379  		{"fill one row with string", "\x1B[6;67HLAST", "\x1B[6;67HLAST"},
   380  		// {"fill one row and set ack", "\x1B[7;7H左边\x1B[7;77H中文", 0, 0, 3},
   381  	}
   382  	v := tc[0]
   383  	c0, _ := NewComplete(80, 40, 40)
   384  	c1, _ := NewComplete(80, 40, 40)
   385  
   386  	c0.terminal.HandleStream(v.seq0)
   387  	c1.terminal.HandleStream(v.seq1)
   388  
   389  	for i := 0; i < b.N; i++ {
   390  		c0.equalDiffFrom(c1)
   391  	}
   392  }
   393  
   394  func BenchmarkDeepEqual(b *testing.B) {
   395  	tc := []struct {
   396  		label string
   397  		seq0  string
   398  		seq1  string
   399  	}{
   400  		{"fill one row with string", "\x1B[6;67HLAST", "\x1B[6;67HLAST"},
   401  		// {"fill one row and set ack", "\x1B[7;7H左边\x1B[7;77H中文", 0, 0, 3},
   402  	}
   403  	v := tc[0]
   404  	c0, _ := NewComplete(80, 40, 40)
   405  	c1, _ := NewComplete(80, 40, 40)
   406  
   407  	c0.terminal.HandleStream(v.seq0)
   408  	c1.terminal.HandleStream(v.seq1)
   409  
   410  	for i := 0; i < b.N; i++ {
   411  		c0.deepEqual(c1)
   412  	}
   413  }
   414  
   415  func BenchmarkEqual(b *testing.B) {
   416  	tc := []struct {
   417  		label string
   418  		seq0  string
   419  		seq1  string
   420  	}{
   421  		{"fill one row with string", "\x1B[6;67HLAST", "\x1B[6;67HLAST"},
   422  		// {"fill one row and set ack", "\x1B[7;7H左边\x1B[7;77H中文", 0, 0, 3},
   423  	}
   424  	v := tc[0]
   425  	c0, _ := NewComplete(80, 40, 40)
   426  	c1, _ := NewComplete(80, 40, 40)
   427  
   428  	c0.terminal.HandleStream(v.seq0)
   429  	c1.terminal.HandleStream(v.seq1)
   430  
   431  	for i := 0; i < b.N; i++ {
   432  		c0.Equal(c1)
   433  	}
   434  }
   435  
   436  func BenchmarkDiffFrom(b *testing.B) {
   437  	tc := []struct {
   438  		label string
   439  		seq0  string
   440  		seq1  string
   441  	}{
   442  		{"fill one row with string", "\x1B[4;4HErase to the end of line\x1B[0K.", "\x1B[6;67HLAST"},
   443  		// {"fill one row and set ack", "\x1B[7;7H左边\x1B[7;77H中文", 0, 0, 3},
   444  	}
   445  	v := tc[0]
   446  	c0, _ := NewComplete(80, 40, 40)
   447  	c1, _ := NewComplete(80, 40, 40)
   448  
   449  	c0.terminal.HandleStream(v.seq0)
   450  	c1.terminal.HandleStream(v.seq1)
   451  
   452  	for i := 0; i < b.N; i++ {
   453  		c0.DiffFrom(c1)
   454  	}
   455  }
   456  
   457  func BenchmarkFramebuffer_Equal(b *testing.B) {
   458  	tc := []struct {
   459  		label string
   460  		seq0  string
   461  		seq1  string
   462  	}{
   463  		{"fill one row with string", "\x1B[4;4HErase to the end of line\x1B[0K.", "\x1B[6;67HLAST"},
   464  		// {"fill one row and set ack", "\x1B[7;7H左边\x1B[7;77H中文", 0, 0, 3},
   465  	}
   466  	v := tc[0]
   467  	c0, _ := NewComplete(80, 40, 40)
   468  	c1, _ := NewComplete(80, 40, 40)
   469  
   470  	c0.terminal.HandleStream(v.seq0)
   471  	c1.terminal.HandleStream(v.seq1)
   472  
   473  	for i := 0; i < b.N; i++ {
   474  		c0.getFramebuffer().Equal(c1.getFramebuffer())
   475  	}
   476  }
   477  
   478  func BenchmarkNewFrame(b *testing.B) {
   479  	tc := []struct {
   480  		label string
   481  		seq0  string
   482  		seq1  string
   483  	}{
   484  		{"fill one row with string", "\x1B[4;4HErase to the end of line\x1B[0K.", "\x1B[6;67HLAST"},
   485  		// {"fill one row and set ack", "\x1B[7;7H左边\x1B[7;77H中文", 0, 0, 3},
   486  	}
   487  	v := tc[0]
   488  	c0, _ := NewComplete(80, 40, 40)
   489  	c1, _ := NewComplete(80, 40, 40)
   490  
   491  	c0.terminal.HandleStream(v.seq0)
   492  	c1.terminal.HandleStream(v.seq1)
   493  
   494  	for i := 0; i < b.N; i++ {
   495  		c0.display.NewFrame(true, c0.terminal, c1.terminal)
   496  	}
   497  }