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 }