github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/expressions/parse_statement_test.go (about)

     1  package expressions
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/lmorg/murex/config/defaults"
     7  	"github.com/lmorg/murex/lang"
     8  	"github.com/lmorg/murex/test"
     9  	"github.com/lmorg/murex/test/count"
    10  	"github.com/lmorg/murex/utils/consts"
    11  	"github.com/lmorg/murex/utils/home"
    12  	"github.com/lmorg/murex/utils/json"
    13  )
    14  
    15  type testParseStatementT struct {
    16  	Statement string
    17  	Args      []string
    18  	Pipes     []string
    19  	Cast      string
    20  	Exec      bool
    21  	Error     bool
    22  }
    23  
    24  func testParseStatement(t *testing.T, tests []testParseStatementT) {
    25  	t.Helper()
    26  	count.Tests(t, len(tests))
    27  
    28  	for i, test := range tests {
    29  		lang.InitEnv()
    30  		defaults.Config(lang.ShellProcess.Config, false)
    31  		p := lang.NewTestProcess()
    32  		p.Name.Set("TestParseStatement")
    33  		p.Config.Set("proc", "strict-vars", false, nil)
    34  		p.Config.Set("proc", "strict-arrays", false, nil)
    35  		tree := NewParser(p, []rune(test.Statement), 0)
    36  		err := tree.ParseStatement(test.Exec)
    37  		if err == nil {
    38  			err = tree.statement.validate()
    39  		}
    40  
    41  		actual := make([]string, len(tree.statement.parameters)+1)
    42  		actual[0] = string(tree.statement.command)
    43  		for j := range tree.statement.parameters {
    44  			actual[j+1] = string(tree.statement.parameters[j])
    45  		}
    46  
    47  		if (err != nil) != test.Error ||
    48  			json.LazyLogging(test.Args) != json.LazyLogging(actual) ||
    49  			json.LazyLogging(test.Pipes) != json.LazyLogging(tree.statement.namedPipes) ||
    50  			test.Cast != string(tree.statement.cast) {
    51  			t.Errorf("Parser error in test %d", i)
    52  			t.Logf("  Statement: %s", test.Statement)
    53  			t.Logf("  Exec:      %v", test.Exec)
    54  			t.Logf("  Expected:  %s", json.LazyLoggingPretty(test.Args))
    55  			t.Logf("  Actual:    %s", json.LazyLoggingPretty(actual))
    56  			t.Logf("  length:    %d", len(tree.statement.parameters))
    57  			t.Logf("  pipe exp:  %s", json.LazyLogging(test.Pipes))
    58  			t.Logf("  pipe act:  %s", json.LazyLogging(tree.statement.namedPipes))
    59  			t.Logf("  pipe len:  %d", len(tree.statement.namedPipes))
    60  			t.Logf("  exp cast:  %s", test.Cast)
    61  			t.Logf("  act cast:  %s", string(tree.statement.cast))
    62  			t.Logf("  exp err:   %v", test.Error)
    63  			t.Logf("  act err:   %v", err)
    64  		}
    65  	}
    66  }
    67  
    68  func TestParseStatement(t *testing.T) {
    69  	tests := []testParseStatementT{
    70  		{
    71  			Statement: `echo hello world`,
    72  			Args: []string{
    73  				"echo", "hello", "world",
    74  			},
    75  			Exec: false,
    76  		},
    77  		{
    78  			Statement: `echo hello world`,
    79  			Args: []string{
    80  				"echo", "hello", "world",
    81  			},
    82  			Exec: true,
    83  		},
    84  		{
    85  			Statement: `echo 'hello world'`,
    86  			Args: []string{
    87  				"echo", "'hello world'",
    88  			},
    89  			Exec: false,
    90  		},
    91  		{
    92  			Statement: `echo 'hello world'`,
    93  			Args: []string{
    94  				"echo", "hello world",
    95  			},
    96  			Exec: true,
    97  		},
    98  		{
    99  			Statement: `echo "hello world"`,
   100  			Args: []string{
   101  				"echo", `"hello world"`,
   102  			},
   103  			Exec: false,
   104  		},
   105  		{
   106  			Statement: `echo "hello world"`,
   107  			Args: []string{
   108  				"echo", `hello world`,
   109  			},
   110  			Exec: true,
   111  		},
   112  		{
   113  			Statement: `echo (hello world)`,
   114  			Args: []string{
   115  				"echo", "(hello world)",
   116  			},
   117  			Exec: false,
   118  		},
   119  		{
   120  			Statement: `echo (hello world)`,
   121  			Args: []string{
   122  				"echo", "hello world",
   123  			},
   124  			Exec: true,
   125  		},
   126  		{
   127  			Statement: `echo h(ello worl)d`,
   128  			Args: []string{
   129  				"echo", "h(ello worl)", "d",
   130  			},
   131  			Exec: false,
   132  		},
   133  		{
   134  			Statement: `echo echo(hello worl)d`,
   135  			Args: []string{
   136  				"echo", "hello worl", "d",
   137  			},
   138  			Exec: true,
   139  		},
   140  		{
   141  			Statement: `echo %(hello world)`,
   142  			Args: []string{
   143  				"echo", "%(hello world)",
   144  			},
   145  			Exec: false,
   146  		},
   147  		{
   148  			Statement: `echo %(hello world)`,
   149  			Args: []string{
   150  				"echo", "hello world",
   151  			},
   152  			Exec: true,
   153  		},
   154  		{
   155  			Statement: `echo {hello world}`,
   156  			Args: []string{
   157  				"echo", "{hello world}",
   158  			},
   159  			Exec: true,
   160  		},
   161  		{
   162  			Statement: `echo {hello world}`,
   163  			Args: []string{
   164  				"echo", "{hello world}",
   165  			},
   166  			Exec: true,
   167  		},
   168  		/////
   169  		{
   170  			Statement: `echo ${out bob}`,
   171  			Args: []string{
   172  				"echo", "${out bob}",
   173  			},
   174  			Exec: false,
   175  		},
   176  		{
   177  			Statement: `echo ${out bob}`,
   178  			Args: []string{
   179  				"echo", "bob",
   180  			},
   181  			Exec: true,
   182  		},
   183  		{
   184  			Statement: `echo "${out bob}"`,
   185  			Args: []string{
   186  				"echo", `"${out bob}"`,
   187  			},
   188  			Exec: false,
   189  		},
   190  		{
   191  			Statement: `echo "${out bob}"`,
   192  			Args: []string{
   193  				"echo", "bob",
   194  			},
   195  			Exec: true,
   196  		},
   197  		{
   198  			Statement: `echo -${out bob}-`,
   199  			Args: []string{
   200  				"echo", "-bob-",
   201  			},
   202  			Exec: true,
   203  		},
   204  		/////
   205  		{
   206  			Statement: `echo @{ja: [1..3]}`,
   207  			Args: []string{
   208  				"echo", "@{ja: [1..3]}",
   209  			},
   210  			Exec: false,
   211  		},
   212  		{
   213  			Statement: `echo @{ja: [1..3]}`,
   214  			Args: []string{
   215  				"echo", "1", "2", "3",
   216  			},
   217  			Exec: true,
   218  		},
   219  		{
   220  			Statement: `echo "@{ja: [1..3]}"`,
   221  			Args: []string{
   222  				"echo", `"@{ja: [1..3]}"`,
   223  			},
   224  			Exec: false,
   225  		},
   226  		{
   227  			Statement: `echo "@{ja: [1..3]}"`,
   228  			Args: []string{
   229  				"echo", `@{ja: [1..3]}`,
   230  			},
   231  			Exec: true,
   232  		},
   233  		{
   234  			Statement: `echo -@{ja: [1..3]}-`,
   235  			Args: []string{
   236  				"echo", `-@{ja: [1..3]}-`,
   237  			},
   238  			Exec: true,
   239  		},
   240  		{
   241  			Statement: `echo - @{ja: [1..3]}-`,
   242  			Args: []string{
   243  				"echo", `-`, `1`, `2`, `3`, `-`,
   244  			},
   245  			Exec: true,
   246  		},
   247  		/////
   248  		{
   249  			Statement: `echo $bob`,
   250  			Args: []string{
   251  				"echo", "$bob",
   252  			},
   253  			Exec: false,
   254  		},
   255  		{
   256  			Statement: `echo $bob`,
   257  			Args: []string{
   258  				"echo", "",
   259  			},
   260  			Exec: true,
   261  		},
   262  		{
   263  			Statement: `echo "$bob"`,
   264  			Args: []string{
   265  				"echo", `"$bob"`,
   266  			},
   267  			Exec: false,
   268  		},
   269  		{
   270  			Statement: `echo "$bob"`,
   271  			Args: []string{
   272  				"echo", "",
   273  			},
   274  			Exec: true,
   275  		},
   276  		{
   277  			Statement: `echo '$bob'`,
   278  			Args: []string{
   279  				"echo", "'$bob'",
   280  			},
   281  			Exec: false,
   282  		},
   283  		{
   284  			Statement: `echo '$bob'`,
   285  			Args: []string{
   286  				"echo", "$bob",
   287  			},
   288  			Exec: true,
   289  		},
   290  		{
   291  			Statement: `echo -$bob-`,
   292  			Args: []string{
   293  				"echo", "--",
   294  			},
   295  			Exec: true,
   296  		},
   297  		/////
   298  		{
   299  			Statement: `echo @bob`,
   300  			Args: []string{
   301  				"echo", "@bob",
   302  			},
   303  			Exec: false,
   304  		},
   305  		{
   306  			Statement: `echo @bob`,
   307  			Args: []string{
   308  				"echo",
   309  			},
   310  			Exec: true,
   311  		},
   312  		{
   313  			Statement: `echo "@bob"`,
   314  			Args: []string{
   315  				"echo", `"@bob"`,
   316  			},
   317  			Exec: false,
   318  		},
   319  		{
   320  			Statement: `echo "@bob"`,
   321  			Args: []string{
   322  				"echo", `@bob`,
   323  			},
   324  			Exec: true,
   325  		},
   326  		{
   327  			Statement: `echo -@bob-`,
   328  			Args: []string{
   329  				"echo", `-@bob-`,
   330  			},
   331  			Exec: true,
   332  		},
   333  		{
   334  			Statement: `echo - @bob-`,
   335  			Args: []string{
   336  				"echo", `-`, `-`,
   337  			},
   338  			Exec: true,
   339  		},
   340  		/////
   341  		{
   342  			Statement: `echo { "bob" }`,
   343  			Args: []string{
   344  				"echo", `{ "bob" }`,
   345  			},
   346  			Exec: false,
   347  		},
   348  		{
   349  			Statement: `echo { 'bob' }`,
   350  			Args: []string{
   351  				"echo", `{ 'bob' }`,
   352  			},
   353  			Exec: false,
   354  		},
   355  	}
   356  
   357  	testParseStatement(t, tests)
   358  }
   359  
   360  func TestParseStatementNamedPipe(t *testing.T) {
   361  	tests := []testParseStatementT{
   362  		{
   363  			Statement: `echo <123> hello world`,
   364  			Args: []string{
   365  				"echo", "hello", "world",
   366  			},
   367  			Pipes: []string{
   368  				"123",
   369  			},
   370  			Exec: false,
   371  		},
   372  		{
   373  			Statement: `echo <123> hello world`,
   374  			Args: []string{
   375  				"echo", "hello", "world",
   376  			},
   377  			Pipes: []string{
   378  				"123",
   379  			},
   380  			Exec: true,
   381  		},
   382  		{
   383  			Statement: `echo <1<23> hello world`,
   384  			Args: []string{
   385  				"echo", "<1<23>", "hello", "world",
   386  			},
   387  			Exec: false,
   388  		},
   389  		{
   390  			Statement: `echo <1<23> hello world`,
   391  			Args: []string{
   392  				"echo", "<1<23>", "hello", "world",
   393  			},
   394  			Exec: true,
   395  		},
   396  		{
   397  			Statement: `echo "<123>" hello world`,
   398  			Args: []string{
   399  				"echo", `"<123>"`, "hello", "world",
   400  			},
   401  			Exec: false,
   402  		},
   403  		{
   404  			Statement: `echo "<123>" hello world`,
   405  			Args: []string{
   406  				"echo", "<123>", "hello", "world",
   407  			},
   408  			Exec: true,
   409  		},
   410  	}
   411  
   412  	testParseStatement(t, tests)
   413  }
   414  
   415  func TestParseStatementExistingCode(t *testing.T) {
   416  	tests := []testParseStatementT{
   417  		{
   418  			Statement: `
   419  				test unit private autocomplete.variables {
   420  					"PreBlock": ({ global MUREX_UNIT_TEST=foobar }),
   421  					"PostBlock": ({ !global MUREX_UNIT_TEST }),
   422  					"StdoutRegex": (^([_a-zA-Z0-9]+\n)+),
   423  					"StdoutType":  "str",
   424  					"StdoutBlock": ({
   425  						-> len -> set len;
   426  						if { = len>0 } then {
   427  							out "Len greater than 0"
   428  						} else {
   429  							err "No elements returned"
   430  						}
   431  					}),
   432  					"StdoutIsArray": true
   433  				}`,
   434  			Args: []string{
   435  				"test", "unit", "private", "autocomplete.variables", `{
   436  					"PreBlock": ({ global MUREX_UNIT_TEST=foobar }),
   437  					"PostBlock": ({ !global MUREX_UNIT_TEST }),
   438  					"StdoutRegex": (^([_a-zA-Z0-9]+\n)+),
   439  					"StdoutType":  "str",
   440  					"StdoutBlock": ({
   441  						-> len -> set len;
   442  						if { = len>0 } then {
   443  							out "Len greater than 0"
   444  						} else {
   445  							err "No elements returned"
   446  						}
   447  					}),
   448  					"StdoutIsArray": true
   449  				}`,
   450  			},
   451  			Exec: false,
   452  		},
   453  
   454  		/////
   455  
   456  		{
   457  			Statement: `
   458  				test unit private autocomplete.variables {
   459  					"PreBlock": ({ global MUREX_UNIT_TEST=foobar }),
   460  					"PostBlock": ({ !global MUREX_UNIT_TEST }),
   461  					"StdoutRegex": (^([_a-zA-Z0-9]+\n)+),
   462  					"StdoutType":  "str",
   463  					"StdoutBlock": ({
   464  						-> len -> set len;
   465  						if { = len>0 } then {
   466  							out "Len greater than 0"
   467  						} else {
   468  							err "No elements returned"
   469  						}
   470  					}),
   471  					"StdoutIsArray": true
   472  				}`,
   473  			Args: []string{
   474  				"test", "unit", "private", "autocomplete.variables", `{
   475  					"PreBlock": ({ global MUREX_UNIT_TEST=foobar }),
   476  					"PostBlock": ({ !global MUREX_UNIT_TEST }),
   477  					"StdoutRegex": (^([_a-zA-Z0-9]+\n)+),
   478  					"StdoutType":  "str",
   479  					"StdoutBlock": ({
   480  						-> len -> set len;
   481  						if { = len>0 } then {
   482  							out "Len greater than 0"
   483  						} else {
   484  							err "No elements returned"
   485  						}
   486  					}),
   487  					"StdoutIsArray": true
   488  				}`,
   489  			},
   490  			Exec: true,
   491  		},
   492  
   493  		/////
   494  	}
   495  
   496  	testParseStatement(t, tests)
   497  }
   498  
   499  func TestParseStatementStrings(t *testing.T) {
   500  	tests := []testParseStatementT{
   501  		{
   502  			Statement: `echo 'hello world'`,
   503  			Args: []string{
   504  				"echo", `'hello world'`,
   505  			},
   506  			Exec: false,
   507  		},
   508  		{
   509  			Statement: `echo 'hello world'`,
   510  			Args: []string{
   511  				"echo", `hello world`,
   512  			},
   513  			Exec: true,
   514  		},
   515  		/////
   516  		{
   517  			Statement: `echo "hello world"`,
   518  			Args: []string{
   519  				"echo", `"hello world"`,
   520  			},
   521  			Exec: false,
   522  		},
   523  		{
   524  			Statement: `echo "hello world"`,
   525  			Args: []string{
   526  				"echo", `hello world`,
   527  			},
   528  			Exec: true,
   529  		},
   530  	}
   531  
   532  	testParseStatement(t, tests)
   533  }
   534  
   535  func TestParseStatementObjCreators(t *testing.T) {
   536  	tests := []testParseStatementT{
   537  		{
   538  			Statement: `echo %(hello world)`,
   539  			Args: []string{
   540  				"echo", `%(hello world)`,
   541  			},
   542  			Exec: false,
   543  		},
   544  		{
   545  			Statement: `echo %(hello world)`,
   546  			Args: []string{
   547  				"echo", `hello world`,
   548  			},
   549  			Exec: true,
   550  		},
   551  		/////
   552  		{
   553  			Statement: `echo %[hello world]`,
   554  			Args: []string{
   555  				"echo", "%[hello world]",
   556  			},
   557  			Exec: false,
   558  		},
   559  		{
   560  			Statement: `echo %[hello world]`,
   561  			Args: []string{
   562  				"echo", `["hello","world"]`,
   563  			},
   564  			Exec: true,
   565  		},
   566  		/////
   567  		{
   568  			Statement: `echo %{hello: world}`,
   569  			Args: []string{
   570  				"echo", "%{hello: world}",
   571  			},
   572  			Exec: false,
   573  		},
   574  		{
   575  			Statement: `echo %{hello: world}`,
   576  			Args: []string{
   577  				"echo", `{"hello":"world"}`,
   578  			},
   579  			Exec: true,
   580  		},
   581  	}
   582  
   583  	testParseStatement(t, tests)
   584  }
   585  
   586  func TestParseStatementEscCrLf(t *testing.T) {
   587  	tests := []testParseStatementT{
   588  		{
   589  			Statement: "echo 1\\\n2\\\n3\n",
   590  			Args: []string{
   591  				"echo", "1", "2", "3",
   592  			},
   593  			Exec: false,
   594  		},
   595  		{
   596  			Statement: "echo 1\\\n2\\\n3\n",
   597  			Args: []string{
   598  				"echo", "1", "2", "3",
   599  			},
   600  			Exec: true,
   601  		},
   602  	}
   603  
   604  	testParseStatement(t, tests)
   605  }
   606  
   607  func TestParseStatementEscape(t *testing.T) {
   608  	tests := []testParseStatementT{
   609  		{
   610  			Statement: `echo hello\sworld`,
   611  			Args: []string{
   612  				"echo", "hello\\sworld",
   613  			},
   614  			Exec: false,
   615  		},
   616  		{
   617  			Statement: `echo hello\sworld`,
   618  			Args: []string{
   619  				"echo", "hello world",
   620  			},
   621  			Exec: true,
   622  		},
   623  		/////
   624  		{
   625  			Statement: `echo hello\tworld`,
   626  			Args: []string{
   627  				"echo", "hello\\tworld",
   628  			},
   629  			Exec: false,
   630  		},
   631  		{
   632  			Statement: `echo hello\tworld`,
   633  			Args: []string{
   634  				"echo", "hello\tworld",
   635  			},
   636  			Exec: true,
   637  		},
   638  		/////
   639  		{
   640  			Statement: `echo hello\rworld`,
   641  			Args: []string{
   642  				"echo", "hello\\rworld",
   643  			},
   644  			Exec: false,
   645  		},
   646  		{
   647  			Statement: `echo hello\rworld`,
   648  			Args: []string{
   649  				"echo", "hello\rworld",
   650  			},
   651  			Exec: true,
   652  		},
   653  		/////
   654  		{
   655  			Statement: `echo hello\nworld`,
   656  			Args: []string{
   657  				"echo", "hello\\nworld",
   658  			},
   659  			Exec: false,
   660  		},
   661  		{
   662  			Statement: `echo hello\nworld`,
   663  			Args: []string{
   664  				"echo", "hello\nworld",
   665  			},
   666  			Exec: true,
   667  		},
   668  	}
   669  
   670  	testParseStatement(t, tests)
   671  }
   672  
   673  func TestParseStatementStdin(t *testing.T) {
   674  	tests := []testParseStatementT{
   675  		{
   676  			Statement: "<stdin>",
   677  			Args: []string{
   678  				consts.NamedPipeProcName, "stdin",
   679  			},
   680  			Exec: false,
   681  		},
   682  		{
   683  			Statement: "<stdin>",
   684  			Args: []string{
   685  				consts.NamedPipeProcName, "stdin",
   686  			},
   687  			Exec: true,
   688  		},
   689  	}
   690  
   691  	testParseStatement(t, tests)
   692  }
   693  
   694  func TestParseStatementTilde(t *testing.T) {
   695  	tests := []test.MurexTest{
   696  		{
   697  			Block:  "echo ~",
   698  			Stdout: home.MyDir + "\n",
   699  		},
   700  	}
   701  
   702  	test.RunMurexTests(tests, t)
   703  }
   704  
   705  func TestParseStatementParenthesesQuote(t *testing.T) {
   706  	tests := []test.MurexTest{
   707  		{
   708  			Block:  "(echo foobar)",
   709  			Stdout: "echo foobar",
   710  		},
   711  	}
   712  
   713  	test.RunMurexTests(tests, t)
   714  }
   715  
   716  func TestParseStatementCast(t *testing.T) {
   717  	tests := []testParseStatementT{
   718  		{
   719  			Statement: ":str cat",
   720  			Args: []string{
   721  				"cat",
   722  			},
   723  			Cast: "str",
   724  			Exec: false,
   725  		},
   726  		{
   727  			Statement: ":str cat",
   728  			Args: []string{
   729  				"cat",
   730  			},
   731  			Cast: "str",
   732  			Exec: true,
   733  		},
   734  		/////
   735  		{
   736  			Statement: ":str cat: bob",
   737  			Args: []string{
   738  				"cat:", "bob",
   739  			},
   740  			Cast: "str",
   741  			Exec: false,
   742  		},
   743  		{
   744  			Statement: ":str cat: bob",
   745  			Args: []string{
   746  				"cat", "bob",
   747  			},
   748  			Cast: "str",
   749  			Exec: true,
   750  		},
   751  	}
   752  
   753  	testParseStatement(t, tests)
   754  }