| 433 | } |
| 434 | |
| 435 | func runsh(ctx context.Context, path, testDir, tempDir string, zt *ZTest, extraEnv ...string) error { |
| 436 | var stdin io.Reader |
| 437 | for _, f := range zt.Inputs { |
| 438 | b, _, err := f.load(testDir) |
| 439 | if err != nil { |
| 440 | return err |
| 441 | } |
| 442 | if f.Name == "stdin" { |
| 443 | stdin = bytes.NewReader(b) |
| 444 | continue |
| 445 | } |
| 446 | if err := os.WriteFile(filepath.Join(tempDir, f.Name), b, 0644); err != nil { |
| 447 | return err |
| 448 | } |
| 449 | } |
| 450 | stdout, stderr, err := RunShell(ctx, tempDir, path, zt.Script, stdin, zt.Env, extraEnv) |
| 451 | if err != nil { |
| 452 | return fmt.Errorf("script failed: %w\n=== stdout ===\n%s=== stderr ===\n%s", |
| 453 | err, stdout, stderr) |
| 454 | } |
| 455 | for _, f := range zt.Outputs { |
| 456 | var actual string |
| 457 | switch f.Name { |
| 458 | case "stdout": |
| 459 | actual = stdout |
| 460 | case "stderr": |
| 461 | actual = stderr |
| 462 | default: |
| 463 | b, err := os.ReadFile(filepath.Join(tempDir, f.Name)) |
| 464 | if err != nil { |
| 465 | return fmt.Errorf("%s: %w", f.Name, err) |
| 466 | } |
| 467 | actual = string(b) |
| 468 | } |
| 469 | expected, expectedRE, err := f.load(testDir) |
| 470 | if err != nil { |
| 471 | return err |
| 472 | } |
| 473 | if expected != nil && string(expected) != actual { |
| 474 | return diffErr(f.Name, string(expected), actual) |
| 475 | } |
| 476 | if expectedRE != nil && !expectedRE.MatchString(actual) { |
| 477 | return fmt.Errorf("%s: regexp %q does not match %q", f.Name, expectedRE, actual) |
| 478 | } |
| 479 | } |
| 480 | return nil |
| 481 | } |
| 482 | |
| 483 | // runInternal runs query over input and returns the output. input |
| 484 | // may be in any format recognized by "super -i auto" and may be gzip-compressed. |