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 := &eth.Config{
   106  	//	Genesis:   core.DeveloperGenesisBlock(15, common.Address{}),
   107  	//}
   108  	ethConf := &eth.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 = &params.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(&ethereum)
   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  }