github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/utilities/console/console_test.go (about) 1 package console 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/neatlab/neatio/internal/jsre" 14 neatptc "github.com/neatlab/neatio/neatptc" 15 "github.com/neatlab/neatio/network/node" 16 ) 17 18 const ( 19 testInstance = "console-tester" 20 testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" 21 ) 22 23 type hookedPrompter struct { 24 scheduler chan string 25 } 26 27 func (p *hookedPrompter) PromptInput(prompt string) (string, error) { 28 29 select { 30 case p.scheduler <- prompt: 31 case <-time.After(time.Second): 32 return "", errors.New("prompt timeout") 33 } 34 35 select { 36 case input := <-p.scheduler: 37 return input, nil 38 case <-time.After(time.Second): 39 return "", errors.New("input timeout") 40 } 41 } 42 43 func (p *hookedPrompter) PromptPassword(prompt string) (string, error) { 44 return "", errors.New("not implemented") 45 } 46 func (p *hookedPrompter) PromptConfirm(prompt string) (bool, error) { 47 return false, errors.New("not implemented") 48 } 49 func (p *hookedPrompter) SetHistory(history []string) {} 50 func (p *hookedPrompter) AppendHistory(command string) {} 51 func (p *hookedPrompter) ClearHistory() {} 52 func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {} 53 54 type tester struct { 55 workspace string 56 stack *node.Node 57 ethereum *neatptc.NeatIO 58 console *Console 59 input *hookedPrompter 60 output *bytes.Buffer 61 } 62 63 func newTester(t *testing.T, confOverride func(*neatptc.Config)) *tester { 64 65 workspace, err := ioutil.TempDir("", "console-tester-") 66 if err != nil { 67 t.Fatalf("failed to create temporary keystore: %v", err) 68 } 69 70 stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance}) 71 if err != nil { 72 t.Fatalf("failed to create node: %v", err) 73 } 74 75 if err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return neatptc.New(ctx, ethConf) }); err != nil { 76 t.Fatalf("failed to register Ethereum protocol: %v", err) 77 } 78 79 if err = stack.Start(); err != nil { 80 t.Fatalf("failed to start test stack: %v", err) 81 } 82 client, err := stack.Attach() 83 if err != nil { 84 t.Fatalf("failed to attach to node: %v", err) 85 } 86 prompter := &hookedPrompter{scheduler: make(chan string)} 87 printer := new(bytes.Buffer) 88 89 console, err := New(Config{ 90 DataDir: stack.DataDir(), 91 DocRoot: "testdata", 92 Client: client, 93 Prompter: prompter, 94 Printer: printer, 95 Preload: []string{"preload.js"}, 96 }) 97 if err != nil { 98 t.Fatalf("failed to create JavaScript console: %v", err) 99 } 100 101 var neatio *neatptc.NeatIO 102 stack.Service(&neatio) 103 104 return &tester{ 105 workspace: workspace, 106 stack: stack, 107 ethereum: ethereum, 108 console: console, 109 input: prompter, 110 output: printer, 111 } 112 } 113 114 func (env *tester) Close(t *testing.T) { 115 if err := env.console.Stop(false); err != nil { 116 t.Errorf("failed to stop embedded console: %v", err) 117 } 118 if err := env.stack.Stop(); err != nil { 119 t.Errorf("failed to stop embedded node: %v", err) 120 } 121 os.RemoveAll(env.workspace) 122 } 123 124 func TestWelcome(t *testing.T) { 125 tester := newTester(t, nil) 126 defer tester.Close(t) 127 128 tester.console.Welcome() 129 130 output := tester.output.String() 131 if want := "Welcome"; !strings.Contains(output, want) { 132 t.Fatalf("console output missing welcome message: have\n%s\nwant also %s", output, want) 133 } 134 if want := fmt.Sprintf("instance: %s", testInstance); !strings.Contains(output, want) { 135 t.Fatalf("console output missing instance: have\n%s\nwant also %s", output, want) 136 } 137 if want := fmt.Sprintf("coinbase: %s", testAddress); !strings.Contains(output, want) { 138 t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want) 139 } 140 if want := "at block: 0"; !strings.Contains(output, want) { 141 t.Fatalf("console output missing sync status: have\n%s\nwant also %s", output, want) 142 } 143 if want := fmt.Sprintf("datadir: %s", tester.workspace); !strings.Contains(output, want) { 144 t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want) 145 } 146 } 147 148 func TestEvaluate(t *testing.T) { 149 tester := newTester(t, nil) 150 defer tester.Close(t) 151 152 tester.console.Evaluate("2 + 2") 153 if output := tester.output.String(); !strings.Contains(output, "4") { 154 t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") 155 } 156 } 157 158 func TestInteractive(t *testing.T) { 159 160 tester := newTester(t, nil) 161 defer tester.Close(t) 162 163 go tester.console.Interactive() 164 165 select { 166 case <-tester.input.scheduler: 167 case <-time.After(time.Second): 168 t.Fatalf("initial prompt timeout") 169 } 170 select { 171 case tester.input.scheduler <- "2+2": 172 case <-time.After(time.Second): 173 t.Fatalf("input feedback timeout") 174 } 175 176 select { 177 case <-tester.input.scheduler: 178 case <-time.After(time.Second): 179 t.Fatalf("secondary prompt timeout") 180 } 181 if output := tester.output.String(); !strings.Contains(output, "4") { 182 t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") 183 } 184 } 185 186 func TestPreload(t *testing.T) { 187 tester := newTester(t, nil) 188 defer tester.Close(t) 189 190 tester.console.Evaluate("preloaded") 191 if output := tester.output.String(); !strings.Contains(output, "some-preloaded-string") { 192 t.Fatalf("preloaded variable missing: have %s, want %s", output, "some-preloaded-string") 193 } 194 } 195 196 func TestExecute(t *testing.T) { 197 tester := newTester(t, nil) 198 defer tester.Close(t) 199 200 tester.console.Execute("exec.js") 201 202 tester.console.Evaluate("execed") 203 if output := tester.output.String(); !strings.Contains(output, "some-executed-string") { 204 t.Fatalf("execed variable missing: have %s, want %s", output, "some-executed-string") 205 } 206 } 207 208 func TestPrettyPrint(t *testing.T) { 209 tester := newTester(t, nil) 210 defer tester.Close(t) 211 212 tester.console.Evaluate("obj = {int: 1, string: 'two', list: [3, 3, 3], obj: {null: null, func: function(){}}}") 213 214 var ( 215 one = jsre.NumberColor("1") 216 two = jsre.StringColor("\"two\"") 217 three = jsre.NumberColor("3") 218 null = jsre.SpecialColor("null") 219 fun = jsre.FunctionColor("function()") 220 ) 221 222 want := `{ 223 int: ` + one + `, 224 list: [` + three + `, ` + three + `, ` + three + `], 225 obj: { 226 null: ` + null + `, 227 func: ` + fun + ` 228 }, 229 string: ` + two + ` 230 } 231 ` 232 if output := tester.output.String(); output != want { 233 t.Fatalf("pretty print mismatch: have %s, want %s", output, want) 234 } 235 } 236 237 func TestPrettyError(t *testing.T) { 238 tester := newTester(t, nil) 239 defer tester.Close(t) 240 tester.console.Evaluate("throw 'hello'") 241 242 want := jsre.ErrorColor("hello") + "\n" 243 if output := tester.output.String(); output != want { 244 t.Fatalf("pretty error mismatch: have %s, want %s", output, want) 245 } 246 } 247 248 func TestIndenting(t *testing.T) { 249 testCases := []struct { 250 input string 251 expectedIndentCount int 252 }{ 253 {`var a = 1;`, 0}, 254 {`"some string"`, 0}, 255 {`"some string with (parentesis`, 0}, 256 {`"some string with newline 257 ("`, 0}, 258 {`function v(a,b) {}`, 0}, 259 {`function f(a,b) { var str = "asd("; };`, 0}, 260 {`function f(a) {`, 1}, 261 {`function f(a, function(b) {`, 2}, 262 {`function f(a, function(b) { 263 var str = "a)}"; 264 });`, 0}, 265 {`function f(a,b) { 266 var str = "a{b(" + a, ", " + b; 267 }`, 0}, 268 {`var str = "\"{"`, 0}, 269 {`var str = "'("`, 0}, 270 {`var str = "\\{"`, 0}, 271 {`var str = "\\\\{"`, 0}, 272 {`var str = 'a"{`, 0}, 273 {`var obj = {`, 1}, 274 {`var obj = { {a:1`, 2}, 275 {`var obj = { {a:1}`, 1}, 276 {`var obj = { {a:1}, b:2}`, 0}, 277 {`var obj = {}`, 0}, 278 {`var obj = { 279 a: 1, b: 2 280 }`, 0}, 281 {`var test = }`, -1}, 282 {`var str = "a\""; var obj = {`, 1}, 283 } 284 285 for i, tt := range testCases { 286 counted := countIndents(tt.input) 287 if counted != tt.expectedIndentCount { 288 t.Errorf("test %d: invalid indenting: have %d, want %d", i, counted, tt.expectedIndentCount) 289 } 290 } 291 }