github.com/platonnetwork/platon-go@v0.7.6/console/console_test.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package console 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "io/ioutil" 24 "os" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/PlatONnetwork/PlatON-Go/core" 30 "github.com/PlatONnetwork/PlatON-Go/crypto/bls" 31 32 "github.com/PlatONnetwork/PlatON-Go/p2p/discover" 33 "github.com/PlatONnetwork/PlatON-Go/params" 34 35 "github.com/PlatONnetwork/PlatON-Go/eth" 36 "github.com/PlatONnetwork/PlatON-Go/internal/jsre" 37 "github.com/PlatONnetwork/PlatON-Go/node" 38 _ "github.com/PlatONnetwork/PlatON-Go/x/xcom" 39 ) 40 41 const ( 42 testInstance = "console-tester" 43 ) 44 45 func init() { 46 bls.Init(bls.BLS12_381) 47 } 48 49 // hookedPrompter implements UserPrompter to simulate use input via channels. 50 type hookedPrompter struct { 51 scheduler chan string 52 } 53 54 func (p *hookedPrompter) PromptInput(prompt string) (string, error) { 55 // Send the prompt to the tester 56 select { 57 case p.scheduler <- prompt: 58 case <-time.After(time.Second): 59 return "", errors.New("prompt timeout") 60 } 61 // Retrieve the response and feed to the console 62 select { 63 case input := <-p.scheduler: 64 return input, nil 65 case <-time.After(time.Second): 66 return "", errors.New("input timeout") 67 } 68 } 69 70 func (p *hookedPrompter) PromptPassword(prompt string) (string, error) { 71 return "", errors.New("not implemented") 72 } 73 func (p *hookedPrompter) PromptConfirm(prompt string) (bool, error) { 74 return false, errors.New("not implemented") 75 } 76 func (p *hookedPrompter) SetHistory(history []string) {} 77 func (p *hookedPrompter) AppendHistory(command string) {} 78 func (p *hookedPrompter) ClearHistory() {} 79 func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {} 80 81 // tester is a console test environment for the console tests to operate on. 82 type tester struct { 83 workspace string 84 stack *node.Node 85 ethereum *eth.Ethereum 86 console *Console 87 input *hookedPrompter 88 output *bytes.Buffer 89 } 90 91 // newTester creates a test environment based on which the console can operate. 92 // Please ensure you call Close() on the returned tester to avoid leaks. 93 func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { 94 // Create a temporary storage for the node keys and initialize it 95 workspace, err := ioutil.TempDir("", "console-tester-") 96 if err != nil { 97 t.Fatalf("failed to create temporary keystore: %v", err) 98 } 99 100 // Create a networkless protocol stack and start an Ethereum service within 101 stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance}) 102 if err != nil { 103 t.Fatalf("failed to create node: %v", err) 104 } 105 //ethConf := ð.Config{ 106 // Genesis: core.DeveloperGenesisBlock(15, common.Address{}), 107 //} 108 ethConf := ð.DefaultConfig 109 ethConf.Genesis = core.DefaultGrapeGenesisBlock() 110 n, _ := discover.ParseNode("enode://73f48a69ae73b85c0a578258954936300b305cb063cbd658d680826ebc0d47cedb890f01f15df2f2e510342d16e7bf5aaf3d7be4ba05a3490de0e9663663addc@127.0.0.1:16789") 111 112 var nodes []params.CbftNode 113 var blsKey bls.SecretKey 114 blsKey.SetByCSPRNG() 115 nodes = append(nodes, params.CbftNode{Node: *n, BlsPubKey: *blsKey.GetPublicKey()}) 116 ethConf.Genesis.Config.Cbft = ¶ms.CbftConfig{ 117 InitialNodes: nodes, 118 } 119 if confOverride != nil { 120 confOverride(ethConf) 121 } 122 if err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { 123 124 return eth.New(ctx, ethConf) 125 }); err != nil { 126 t.Fatalf("failed to register Ethereum protocol: %v", err) 127 } 128 // Start the node and assemble the JavaScript console around it 129 if err = stack.Start(); err != nil { 130 t.Fatalf("failed to start test stack: %v", err) 131 } 132 client, err := stack.Attach() 133 if err != nil { 134 t.Fatalf("failed to attach to node: %v", err) 135 } 136 prompter := &hookedPrompter{scheduler: make(chan string)} 137 printer := new(bytes.Buffer) 138 139 console, err := New(Config{ 140 DataDir: stack.DataDir(), 141 DocRoot: "testdata", 142 Client: client, 143 Prompter: prompter, 144 Printer: printer, 145 Preload: []string{"preload.js"}, 146 }) 147 if err != nil { 148 t.Fatalf("failed to create JavaScript console: %v", err) 149 } 150 // Create the final tester and return 151 var ethereum *eth.Ethereum 152 stack.Service(ðereum) 153 154 return &tester{ 155 workspace: workspace, 156 stack: stack, 157 ethereum: ethereum, 158 console: console, 159 input: prompter, 160 output: printer, 161 } 162 } 163 164 // Close cleans up any temporary data folders and held resources. 165 func (env *tester) Close(t *testing.T) { 166 if err := env.console.Stop(false); err != nil { 167 t.Errorf("failed to stop embedded console: %v", err) 168 } 169 if err := env.stack.Stop(); err != nil { 170 t.Errorf("failed to stop embedded node: %v", err) 171 } 172 os.RemoveAll(env.workspace) 173 } 174 175 // Tests that the node lists the correct welcome message, notably that it contains 176 // the instance name, coinbase account, block number, data directory and supported 177 // console modules. 178 func TestWelcome(t *testing.T) { 179 tester := newTester(t, nil) 180 defer tester.Close(t) 181 182 tester.console.Welcome() 183 184 output := tester.output.String() 185 if want := "Welcome"; !strings.Contains(output, want) { 186 t.Fatalf("console output missing welcome message: have\n%s\nwant also %s", output, want) 187 } 188 if want := fmt.Sprintf("instance: %s", testInstance); !strings.Contains(output, want) { 189 t.Fatalf("console output missing instance: have\n%s\nwant also %s", output, want) 190 } 191 if want := "at block: 0"; !strings.Contains(output, want) { 192 t.Fatalf("console output missing sync status: have\n%s\nwant also %s", output, want) 193 } 194 if want := fmt.Sprintf("datadir: %s", tester.workspace); !strings.Contains(output, want) { 195 t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want) 196 } 197 } 198 199 func TestApi(t *testing.T) { 200 tester := newTester(t, nil) 201 defer tester.Close(t) 202 fmt.Fprintf(tester.console.printer, "Welcome to the PlatON JavaScript console!\n\n") 203 _, err := tester.console.jsre.Run(` 204 console.log("instance: " + web3.version.node); 205 console.log("at block: " + platon.blockNumber + " (" + new Date(1000 * platon.getBlock(platon.blockNumber).timestamp) + ")"); 206 console.log(" datadir: " + admin.datadir); 207 console.log(" protocolVersion: " + platon.protocolVersion); 208 console.log(" sync: " + platon.syncing); 209 console.log("",platon.protocolVersion) 210 console.log("syncing",platon.syncing) 211 console.log("gasPrice",platon.gasPrice) 212 console.log("accounts",platon.accounts) 213 console.log("blockNumber",platon.blockNumber) 214 console.log("getBalance",platon.getBalance("0x8605cdbbdb6d264aa742e77020dcbc58fcdce182")) 215 console.log("getStorageAt",platon.getStorageAt("0x8605cdbbdb6d264aa742e77020dcbc58fcdce182")) 216 console.log("getTransactionCount",platon.getTransactionCount("0x8605cdbbdb6d264aa742e77020dcbc58fcdce182")) 217 console.log("getBlockTransactionCountByHash or ByNumber",platon.getBlockTransactionCount("1234")) 218 //console.log("getCode",platon.getCode("0x8605cdbbdb6d264aa742e77020dcbc58fcdce182")) 219 //adr = personal.newAccount("123456") 220 //personal.unlockAccount(adr,"123456",30) 221 //console.log("sign",platon.sign(adr, "0xdeadbeaf")) 222 //console.log("sendTransaction",platon.sendTransaction({from:adr,to:adr,value:0,gas:0,gasPrice:0})) 223 //console.log("sendRawTransaction",platon.sendRawTransaction({from:platon.accounts[0],to:platon.accounts[1],value:10,gas:88888,gasPrice:3333})) 224 //console.log("call",platon.call({from:platon.accounts[0],to:platon.accounts[1],value:10,gas:88888,gasPrice:3333})) 225 //console.log("estimateGas",platon.estimateGas({from:platon.accounts[0],to:platon.accounts[1],value:10,gas:88888,gasPrice:3333})) 226 //console.log("getBlockByHash or number",platon.getBlock("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")) 227 //console.log("getTransactionByHash",platon.getTransaction("0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331")) 228 //console.log("getTransactionByBlockHashAndIndex",platon.getTransactionFromBlock(["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "1"])) 229 //console.log("getTransactionReceipt",platon.getTransactionReceipt()) 230 //console.log("newFilter",platon.newFilter()) 231 //console.log("newBlockFilter",platon.newBlockFilter()) 232 //console.log("newPendingTransactionFilter",platon.newPendingTransactionFilter()) 233 //console.log("uninstallFilter",platon.uninstallFilter()) 234 //console.log("getFilterChanges",platon.getFilterChanges()) 235 //console.log("getFilterLogs",platon.getFilterLogs()) 236 //console.log("getLogs",platon.getLogs({"topics":["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"]})) 237 //console.log("signTransaction",platon.signTransaction()) 238 //console.log("test personal",personal.openWallet("adad")) 239 `) 240 if err != nil { 241 t.Error(err) 242 } 243 t.Log(tester.output.String()) 244 } 245 246 // Tests that JavaScript statement evaluation works as intended. 247 func TestEvaluate(t *testing.T) { 248 tester := newTester(t, nil) 249 defer tester.Close(t) 250 251 tester.console.Evaluate("2 + 2") 252 if output := tester.output.String(); !strings.Contains(output, "4") { 253 t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") 254 } 255 } 256 257 // Tests that the console can be used in interactive mode. 258 func TestInteractive(t *testing.T) { 259 // Create a tester and run an interactive console in the background 260 tester := newTester(t, nil) 261 defer tester.Close(t) 262 263 go tester.console.Interactive() 264 265 // Wait for a prompt and send a statement back 266 select { 267 case <-tester.input.scheduler: 268 case <-time.After(time.Second): 269 t.Fatalf("initial prompt timeout") 270 } 271 select { 272 case tester.input.scheduler <- "2+2": 273 case <-time.After(time.Second): 274 t.Fatalf("input feedback timeout") 275 } 276 // Wait for the second prompt and ensure first statement was evaluated 277 select { 278 case <-tester.input.scheduler: 279 case <-time.After(time.Second): 280 t.Fatalf("secondary prompt timeout") 281 } 282 if output := tester.output.String(); !strings.Contains(output, "4") { 283 t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") 284 } 285 } 286 287 // Tests that preloaded JavaScript files have been executed before user is given 288 // input. 289 func TestPreload(t *testing.T) { 290 tester := newTester(t, nil) 291 defer tester.Close(t) 292 293 tester.console.Evaluate("preloaded") 294 if output := tester.output.String(); !strings.Contains(output, "some-preloaded-string") { 295 t.Fatalf("preloaded variable missing: have %s, want %s", output, "some-preloaded-string") 296 } 297 } 298 299 // Tests that JavaScript scripts can be executes from the configured asset path. 300 func TestExecute(t *testing.T) { 301 tester := newTester(t, nil) 302 defer tester.Close(t) 303 304 tester.console.Execute("exec.js") 305 306 tester.console.Evaluate("execed") 307 if output := tester.output.String(); !strings.Contains(output, "some-executed-string") { 308 t.Fatalf("execed variable missing: have %s, want %s", output, "some-executed-string") 309 } 310 } 311 312 // Tests that the JavaScript objects returned by statement executions are properly 313 // pretty printed instead of just displaying "[object]". 314 func TestPrettyPrint(t *testing.T) { 315 tester := newTester(t, nil) 316 defer tester.Close(t) 317 318 tester.console.Evaluate("obj = {int: 1, string: 'two', list: [3, 3, 3], obj: {null: null, func: function(){}}}") 319 320 // Define some specially formatted fields 321 var ( 322 one = jsre.NumberColor("1") 323 two = jsre.StringColor("\"two\"") 324 three = jsre.NumberColor("3") 325 null = jsre.SpecialColor("null") 326 fun = jsre.FunctionColor("function()") 327 ) 328 // Assemble the actual output we're after and verify 329 want := `{ 330 int: ` + one + `, 331 list: [` + three + `, ` + three + `, ` + three + `], 332 obj: { 333 null: ` + null + `, 334 func: ` + fun + ` 335 }, 336 string: ` + two + ` 337 } 338 ` 339 if output := tester.output.String(); output != want { 340 t.Fatalf("pretty print mismatch: have %s, want %s", output, want) 341 } 342 } 343 344 // Tests that the JavaScript exceptions are properly formatted and colored. 345 func TestPrettyError(t *testing.T) { 346 tester := newTester(t, nil) 347 defer tester.Close(t) 348 tester.console.Evaluate("throw 'hello'") 349 350 want := jsre.ErrorColor("hello") + "\n" 351 if output := tester.output.String(); output != want { 352 t.Fatalf("pretty error mismatch: have %s, want %s", output, want) 353 } 354 } 355 356 // Tests that tests if the number of indents for JS input is calculated correct. 357 func TestIndenting(t *testing.T) { 358 testCases := []struct { 359 input string 360 expectedIndentCount int 361 }{ 362 {`var a = 1;`, 0}, 363 {`"some string"`, 0}, 364 {`"some string with (parenthesis`, 0}, 365 {`"some string with newline 366 ("`, 0}, 367 {`function v(a,b) {}`, 0}, 368 {`function f(a,b) { var str = "asd("; };`, 0}, 369 {`function f(a) {`, 1}, 370 {`function f(a, function(b) {`, 2}, 371 {`function f(a, function(b) { 372 var str = "a)}"; 373 });`, 0}, 374 {`function f(a,b) { 375 var str = "a{b(" + a, ", " + b; 376 }`, 0}, 377 {`var str = "\"{"`, 0}, 378 {`var str = "'("`, 0}, 379 {`var str = "\\{"`, 0}, 380 {`var str = "\\\\{"`, 0}, 381 {`var str = 'a"{`, 0}, 382 {`var obj = {`, 1}, 383 {`var obj = { {a:1`, 2}, 384 {`var obj = { {a:1}`, 1}, 385 {`var obj = { {a:1}, b:2}`, 0}, 386 {`var obj = {}`, 0}, 387 {`var obj = { 388 a: 1, b: 2 389 }`, 0}, 390 {`var test = }`, -1}, 391 {`var str = "a\""; var obj = {`, 1}, 392 } 393 394 for i, tt := range testCases { 395 counted := countIndents(tt.input) 396 if counted != tt.expectedIndentCount { 397 t.Errorf("test %d: invalid indenting: have %d, want %d", i, counted, tt.expectedIndentCount) 398 } 399 } 400 }