diff --git a/executor.go b/executor.go index 2ed4463beb..048b21c204 100644 --- a/executor.go +++ b/executor.go @@ -162,6 +162,22 @@ func (o *entrypointOption) ApplyToExecutor(e *Executor) { e.Entrypoint = o.entrypoint } +// WithUserWorkingDir sets the directory that Task was originally invoked from +// by the user. By default, this is set to the user's current working +// directory. This is used to resolve relative paths in variables such as +// USER_WORKING_DIR. +func WithUserWorkingDir(dir string) ExecutorOption { + return &userWorkingDirOption{dir} +} + +type userWorkingDirOption struct { + dir string +} + +func (o *userWorkingDirOption) ApplyToExecutor(e *Executor) { + e.UserWorkingDir = o.dir +} + // WithTempDir sets the temporary directory that will be used by [Executor] for // storing temporary files like checksums and cached remote files. By default, // the temporary directory is set to the user's temporary directory. diff --git a/executor_test.go b/executor_test.go index e0ce4e2786..034de84197 100644 --- a/executor_test.go +++ b/executor_test.go @@ -3,15 +3,28 @@ package task_test import ( "bytes" "cmp" + "context" "fmt" + "io/fs" + rand "math/rand/v2" + "net/http" + "net/http/httptest" + "net/url" "os" "path/filepath" + "regexp" + "runtime" + "strings" "testing" + "time" + "github.com/Masterminds/semver/v3" "github.com/sebdah/goldie/v2" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/go-task/task/v3" + "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/experiments" "github.com/go-task/task/v3/internal/filepathext" "github.com/go-task/task/v3/taskfile/ast" @@ -31,12 +44,25 @@ type ( ExecutorTest struct { TaskTest task string + tasks []string vars map[string]any input string executorOpts []task.ExecutorOption wantSetupError bool wantRunError bool wantStatusError bool + noRun bool + assertFns []func(t *testing.T, r *ExecutorTestResult) + } + // An ExecutorTestResult carries the outcome of an [ExecutorTest] run. It is + // passed to any assertion functions registered with [WithAssert], for + // checks that a golden fixture can't express, such as an error's concrete + // type, timing bounds, or the [task.Executor]'s internal state. + ExecutorTestResult struct { + Executor *task.Executor + Output string + Err error + Duration time.Duration } ) @@ -72,47 +98,6 @@ func NewExecutorTest(t *testing.T, opts ...ExecutorTestOption) { tt.run(t) } -// Functional options - -// WithInput tells the test to create a reader with the given input. This can be -// used to simulate user input when a task requires it. -func WithInput(input string) ExecutorTestOption { - return &inputTestOption{input} -} - -type inputTestOption struct { - input string -} - -func (opt *inputTestOption) applyToExecutorTest(t *ExecutorTest) { - t.input = opt.input -} - -// WithRunError tells the test to expect an error during the run phase of the -// task execution. A fixture will be created with the output of any errors. -func WithRunError() ExecutorTestOption { - return &runErrorTestOption{} -} - -type runErrorTestOption struct{} - -func (opt *runErrorTestOption) applyToExecutorTest(t *ExecutorTest) { - t.wantRunError = true -} - -// WithStatusError tells the test to make an additional call to -// [task.Executor.Status] after the task has been run. A fixture will be created -// with the output of any errors. -func WithStatusError() ExecutorTestOption { - return &statusErrorTestOption{} -} - -type statusErrorTestOption struct{} - -func (opt *statusErrorTestOption) applyToExecutorTest(t *ExecutorTest) { - t.wantStatusError = true -} - // Helpers // writeFixtureErrRun is a wrapper for writing the output of an error during the @@ -168,9 +153,19 @@ func (tt *ExecutorTest) run(t *testing.T) { goldie.WithEqualFn(NormalizedEqual), ) + // runAsserts runs any functions registered with WithAssert against the + // current outcome of the test. + runAsserts := func(result *ExecutorTestResult) { + t.Helper() + for _, fn := range tt.assertFns { + fn(t, result) + } + } + // Call setup and check for errors if err := e.Setup(); tt.wantSetupError { require.Error(t, err) + runAsserts(&ExecutorTestResult{Executor: e, Err: err}) tt.writeFixtureErrSetup(t, g, err) tt.writeFixtureBuffer(t, g, buffer.buf) return @@ -178,30 +173,46 @@ func (tt *ExecutorTest) run(t *testing.T) { require.NoError(t, err) } - // Create the task call + // If the test doesn't want to run a task, stop here. There's no + // output, so no fixture is written. + if tt.noRun { + runAsserts(&ExecutorTestResult{Executor: e}) + return + } + + // Create the task call(s) vars := ast.NewVars() for key, value := range tt.vars { vars.Set(key, ast.Var{Value: value}) } - call := &task.Call{ - Task: tt.task, - Vars: vars, + taskNames := tt.tasks + if len(taskNames) == 0 { + taskNames = []string{tt.task} + } + calls := make([]*task.Call, 0, len(taskNames)) + for _, name := range taskNames { + calls = append(calls, &task.Call{Task: name, Vars: vars}) } // Run the task and check for errors ctx := t.Context() - if err := e.Run(ctx, call); tt.wantRunError { + start := time.Now() + err := e.Run(ctx, calls...) + duration := time.Since(start) + if tt.wantRunError { require.Error(t, err) + runAsserts(&ExecutorTestResult{Executor: e, Output: buffer.buf.String(), Err: err, Duration: duration}) tt.writeFixtureErrRun(t, g, err) tt.writeFixtureBuffer(t, g, buffer.buf) return } else { require.NoError(t, err) } + runAsserts(&ExecutorTestResult{Executor: e, Output: buffer.buf.String(), Duration: duration}) // If the status flag is set, run the status check if tt.wantStatusError { - if err := e.Status(ctx, call); err != nil { + if err := e.Status(ctx, calls[0]); err != nil { tt.writeFixtureStatus(t, g, err.Error()) } } @@ -1048,7 +1059,6 @@ func TestReference(t *testing.T) { } func TestVarInheritance(t *testing.T) { - enableExperimentForTest(t, &experiments.EnvPrecedence, 1) tests := []struct { name string call string @@ -1111,6 +1121,7 @@ func TestVarInheritance(t *testing.T) { task.WithForce(true), ), WithTask(cmp.Or(test.call, "default")), + WithExperiment(&experiments.EnvPrecedence, 1), ) } } @@ -1165,6 +1176,149 @@ func TestIncludeChecksum(t *testing.T) { ) } +// writeFile writes content to a file, creating any intermediate directories. +func writeFile(t *testing.T, dir, name, content string) { + t.Helper() + require.NoError(t, os.WriteFile(filepathext.SmartJoin(dir, name), []byte(content), 0o644)) +} + +// gitignoreStep writes a set of files then runs the task once, capturing its +// output as a golden fixture named run. +type gitignoreStep struct { + write map[string]string + run string +} + +// gitignoreSeq drives a checksum task through a sequence of runs against a +// fixture dir. create seeds runtime files (removed on cleanup); restore resets +// tracked files to their committed content on cleanup; artifacts are +// task-produced files to delete on cleanup. +type gitignoreSeq struct { + dir string + task string + create map[string]string + restore map[string]string + artifacts []string + steps []gitignoreStep +} + +func (s gitignoreSeq) run(t *testing.T) { + t.Helper() + cleanup := func() { + // The fixture manages its own .git marker so that gitignore filtering + // resolves a repo root regardless of the build source: an in-tree + // checkout would otherwise inherit the go-task .git, but a GitHub + // source tarball has none, which would silently disable filtering and + // break the golden fixtures. + _ = os.RemoveAll(filepathext.SmartJoin(s.dir, ".git")) + _ = os.RemoveAll(filepathext.SmartJoin(s.dir, ".task")) + for name := range s.create { + _ = os.Remove(filepathext.SmartJoin(s.dir, name)) + } + for _, name := range s.artifacts { + _ = os.Remove(filepathext.SmartJoin(s.dir, name)) + } + for name, content := range s.restore { + writeFile(t, s.dir, name, content) + } + } + cleanup() + t.Cleanup(cleanup) + require.NoError(t, os.MkdirAll(filepathext.SmartJoin(s.dir, ".git"), 0o755)) + for name, content := range s.create { + writeFile(t, s.dir, name, content) + } + for _, step := range s.steps { + for name, content := range step.write { + writeFile(t, s.dir, name, content) + } + NewExecutorTest(t, + WithName(step.run), + WithExecutorOptions(task.WithDir(s.dir)), + WithTask(s.task), + ) + } +} + +func TestGitignoreChecksum(t *testing.T) { //nolint:paralleltest // shares testdata/gitignore and mutates fixture files + gitignoreSeq{ + dir: "testdata/gitignore", + task: "build", + create: map[string]string{"ignored.txt": "ignored\n"}, + restore: map[string]string{"source.txt": "source content\n"}, + artifacts: []string{"generated.txt"}, + steps: []gitignoreStep{ + {run: "first run"}, + {run: "up to date"}, + {run: "ignored file modified", write: map[string]string{"ignored.txt": "ignored modified\n"}}, + {run: "source file modified", write: map[string]string{"source.txt": "source modified\n"}}, + }, + }.run(t) +} + +// TestGitignoreNegation checks that a `!pattern` in a nested .gitignore +// re-includes a file excluded by a parent .gitignore. +func TestGitignoreNegation(t *testing.T) { //nolint:paralleltest // mutates fixture files + gitignoreSeq{ + dir: "testdata/gitignore_negation", + task: "build", + create: map[string]string{"sub/debug.log": "debug\n", "sub/other.log": "other\n"}, + steps: []gitignoreStep{ + {run: "first run"}, + {run: "up to date"}, + {run: "ignored file modified", write: map[string]string{"sub/other.log": "other modified\n"}}, + {run: "reincluded file modified", write: map[string]string{"sub/debug.log": "debug modified\n"}}, + }, + }.run(t) +} + +// TestGitignoreNested checks that a .gitignore in a subdirectory below the task +// dir is honored when its files are reached by a deep glob. +func TestGitignoreNested(t *testing.T) { //nolint:paralleltest // mutates fixture files + gitignoreSeq{ + dir: "testdata/gitignore_nested", + task: "build", + create: map[string]string{"sub/secret.dat": "secret\n"}, + restore: map[string]string{"sub/keep.txt": "keep\n"}, + steps: []gitignoreStep{ + {run: "first run"}, + {run: "up to date"}, + {run: "ignored file modified", write: map[string]string{"sub/secret.dat": "secret modified\n"}}, + {run: "source file modified", write: map[string]string{"sub/keep.txt": "keep modified\n"}}, + }, + }.run(t) +} + +// TestGitignoreIncluded checks that a top-level use_gitignore in an included +// Taskfile is propagated onto its tasks during merge. +func TestGitignoreIncluded(t *testing.T) { //nolint:paralleltest // mutates fixture files + gitignoreSeq{ + dir: "testdata/gitignore_included", + task: "included:build", + create: map[string]string{"ignored.txt": "ignored\n"}, + steps: []gitignoreStep{ + {run: "first run"}, + {run: "up to date"}, + {run: "ignored file modified", write: map[string]string{"ignored.txt": "ignored modified\n"}}, + }, + }.run(t) +} + +// TestGitignoreIncludedOverride checks that an explicit use_gitignore: false in +// an included Taskfile is preserved even when the root Taskfile sets it to true. +func TestGitignoreIncludedOverride(t *testing.T) { //nolint:paralleltest // mutates fixture files + gitignoreSeq{ + dir: "testdata/gitignore_included_override", + task: "included:build", + create: map[string]string{"ignored.txt": "ignored\n"}, + steps: []gitignoreStep{ + {run: "first run"}, + {run: "up to date"}, + {run: "ignored file modified", write: map[string]string{"ignored.txt": "ignored modified\n"}}, + }, + }.run(t) +} + func TestIncludeSilent(t *testing.T) { t.Parallel() @@ -1289,3 +1443,1871 @@ func TestIf(t *testing.T) { NewExecutorTest(t, opts...) } } + +func TestIncludes(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes")), + ) +} + +func TestIncludesMultiLevel(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes_multi_level")), + ) +} + +func TestIncludesEmptyMain(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes_empty")), + WithTask("included:default"), + ) +} + +func TestIncludesDependencies(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes_deps")), + ) +} + +func TestIncludesCallingRoot(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes_call_root_task")), + WithTask("included:call-root"), + ) +} + +func TestIncludesOptional(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes_optional")), + ) +} + +func TestIncludesFromCustomTaskfile(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/includes_yaml"), + task.WithEntrypoint("testdata/includes_yaml/Custom.ext"), + ), + ) +} + +func TestIncludesShadowedDefault(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes_shadowed_default")), + WithTask("included"), + ) +} + +func TestIncludesUnshadowedDefault(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes_unshadowed_default")), + WithTask("included"), + ) +} + +func TestIncludesRemote(t *testing.T) { + dir := "testdata/includes_remote" + os.RemoveAll(filepath.Join(dir, ".task", "remote")) + + srv := httptest.NewServer(http.FileServer(http.Dir(dir))) + defer srv.Close() + + tcs := []struct { + firstRemote string + secondRemote string + }{ + { + firstRemote: srv.URL + "/first/Taskfile.yml", + secondRemote: srv.URL + "/first/second/Taskfile.yml", + }, + { + firstRemote: srv.URL + "/first/Taskfile.yml", + secondRemote: "./second/Taskfile.yml", + }, + { + firstRemote: srv.URL + "/first/", + secondRemote: srv.URL + "/first/second/", + }, + } + + taskCalls := []*task.Call{ + {Task: "first:write-file"}, + {Task: "first:second:write-file"}, + } + + for i, tc := range tcs { + t.Run(fmt.Sprint(i), func(t *testing.T) { + t.Setenv("FIRST_REMOTE_URL", tc.firstRemote) + t.Setenv("SECOND_REMOTE_URL", tc.secondRemote) + + var buff SyncBuffer + + // Extract host from server URL for trust testing + parsedURL, err := url.Parse(srv.URL) + require.NoError(t, err) + trustedHost := parsedURL.Host + + executors := []struct { + name string + executor *task.Executor + }{ + { + name: "online, always download", + executor: task.NewExecutor( + task.WithDir(dir), + task.WithStdout(&buff), + task.WithStderr(&buff), + task.WithTimeout(time.Minute), + task.WithInsecure(true), + task.WithStdout(&buff), + task.WithStderr(&buff), + task.WithVerbose(true), + + // Without caching + task.WithAssumeYes(true), + task.WithDownload(true), + ), + }, + { + name: "offline, use cache", + executor: task.NewExecutor( + task.WithDir(dir), + task.WithStdout(&buff), + task.WithStderr(&buff), + task.WithTimeout(time.Minute), + task.WithInsecure(true), + task.WithStdout(&buff), + task.WithStderr(&buff), + task.WithVerbose(true), + + // With caching + task.WithAssumeYes(false), + task.WithDownload(false), + task.WithOffline(true), + ), + }, + { + name: "with trusted hosts, no prompts", + executor: task.NewExecutor( + task.WithDir(dir), + task.WithStdout(&buff), + task.WithStderr(&buff), + task.WithTimeout(time.Minute), + task.WithInsecure(true), + task.WithStdout(&buff), + task.WithStderr(&buff), + task.WithVerbose(true), + + // With trusted hosts + task.WithTrustedHosts([]string{trustedHost}), + task.WithDownload(true), + ), + }, + } + + for _, e := range executors { + t.Run(e.name, func(t *testing.T) { + require.NoError(t, e.executor.Setup()) + + for k, taskCall := range taskCalls { + t.Run(taskCall.Task, func(t *testing.T) { + expectedContent := fmt.Sprint(rand.Int64()) //nolint:gosec + t.Setenv("CONTENT", expectedContent) + + outputFile := fmt.Sprintf("%d.%d.txt", i, k) + t.Setenv("OUTPUT_FILE", outputFile) + + path := filepath.Join(dir, outputFile) + require.NoError(t, os.RemoveAll(path)) + + require.NoError(t, e.executor.Run(t.Context(), taskCall)) + + actualContent, err := os.ReadFile(path) + require.NoError(t, err) + assert.Equal(t, expectedContent, strings.TrimSpace(string(actualContent))) + }) + } + }) + } + + t.Log("\noutput:\n", buff.buf.String()) + }) + } +} + +func TestIncludesHttp(t *testing.T) { //nolint:paralleltest // sets INCLUDE_ROOT per iteration + dir, err := filepath.Abs("testdata/includes_http") + require.NoError(t, err) + + srv := httptest.NewServer(http.FileServer(http.Dir(dir))) + defer srv.Close() + + t.Cleanup(func() { + // This test fills the .task/remote directory with cache entries because the include URL + // is different on every test due to the dynamic nature of the TCP port in srv.URL + if err := os.RemoveAll(filepath.Join(dir, ".task")); err != nil { + t.Logf("error cleaning up: %s", err) + } + }) + + taskfiles, err := fs.Glob(os.DirFS(dir), "root-taskfile-*.yml") + require.NoError(t, err) + + remotes := []struct { + name string + root string + }{ + { + name: "local", + root: ".", + }, + { + name: "http-remote", + root: srv.URL, + }, + } + + tcs := []struct { + name, dir string + }{ + { + name: "second-with-dir-1:third-with-dir-1:default", + dir: filepath.Join(dir, "dir-1"), + }, + { + name: "second-with-dir-1:third-with-dir-2:default", + dir: filepath.Join(dir, "dir-2"), + }, + } + + for _, taskfile := range taskfiles { + for _, remote := range remotes { //nolint:paralleltest // sets INCLUDE_ROOT per iteration + t.Setenv("INCLUDE_ROOT", remote.root) + + NewExecutorTest(t, + WithName(fmt.Sprintf("%s/%s", taskfile, remote.name)), + WithExecutorOptions( + task.WithEntrypoint(filepath.Join(dir, taskfile)), + task.WithDir(dir), + task.WithInsecure(true), + task.WithDownload(true), + task.WithAssumeYes(true), + task.WithVerbose(true), + task.WithTimeout(time.Minute), + ), + WithNoRun(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + for _, tc := range tcs { + compiled, err := r.Executor.CompiledTask(&task.Call{Task: tc.name}) + require.NoError(t, err) + assert.Equal(t, tc.dir, compiled.Dir) + } + }), + ) + } + } +} + +func TestSupportedFileNames(t *testing.T) { + t.Parallel() + + fileNames := []string{ + "Taskfile.yml", + "Taskfile.yaml", + "Taskfile.dist.yml", + "Taskfile.dist.yaml", + } + for _, fileName := range fileNames { + t.Run(fileName, func(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir(fmt.Sprintf("testdata/file_names/%s", fileName))), + ) + }) + } +} + +func TestDynamicVariablesShouldRunOnTheTaskDir(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dir/dynamic_var")), + ) +} + +func TestDotenvShouldIncludeAllEnvFiles(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv/default")), + ) +} + +func TestDotenvShouldAllowMissingEnv(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv/missing_env")), + ) +} + +func TestDotenvHasLocalEnvInPath(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv/local_env_in_path")), + ) +} + +func TestDotenvHasLocalVarInPath(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv/local_var_in_path")), + ) +} + +func TestDotenvHasEnvVarInPath(t *testing.T) { // nolint:paralleltest // cannot run in parallel + t.Setenv("ENV_VAR", "testing") + + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv/env_var_in_path")), + ) +} + +func TestTaskDotenv(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv_task/default")), + WithTask("dotenv"), + ) +} + +func TestTaskDotenvFail(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv_task/default")), + WithTask("no-dotenv"), + ) +} + +func TestTaskDotenvOverriddenByEnv(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv_task/default")), + WithTask("dotenv-overridden-by-env"), + ) +} + +func TestTaskDotenvWithVarName(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv_task/default")), + WithTask("dotenv-with-var-name"), + ) +} + +func TestRunOnlyRunsJobsHashOnce(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/run")), + WithTask("generate-hash"), + ) +} + +func TestRunOnlyRunsJobsHashOnceWithWildcard(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/run")), + WithTask("deploy"), + ) +} + +func TestSingleCmdDep(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/single_cmd_dep")), + WithTask("foo"), + ) +} + +func TestShortTaskNotation(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/short_task_notation"), + task.WithSilent(true), + ), + ) +} + +func TestExitCodeZero(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/exit_code")), + WithTask("exit-zero"), + ) +} + +func TestExitCodeOne(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/exit_code")), + WithTask("exit-one"), + WithRunError(), + ) +} + +func TestOutputGroup(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/output_group")), + WithTask("bye"), + ) +} + +func TestOutputGroupErrorOnlySwallowsOutputOnSuccess(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/output_group_error_only")), + WithTask("passing"), + ) +} + +func TestOutputGroupErrorOnlyShowsOutputOnFailure(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/output_group_error_only")), + WithTask("failing"), + WithRunError(), + ) +} + +func TestIncludedVars(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/include_with_vars")), + WithTask("task1"), + ) +} + +func TestIncludedVarsMultiLevel(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/include_with_vars_multi_level")), + ) +} + +func TestTaskfileWalk(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + dir string + }{ + {name: "walk from root directory", dir: "testdata/taskfile_walk"}, + {name: "walk from sub directory", dir: "testdata/taskfile_walk/foo"}, + {name: "walk from sub sub directory", dir: "testdata/taskfile_walk/foo/bar"}, + } + for _, test := range tests { + NewExecutorTest(t, + WithName(test.name), + WithExecutorOptions(task.WithDir(test.dir)), + ) + } +} + +func TestPOSIXShellOptsGlobalLevel(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/shopts/global_level")), + WithTask("pipefail"), + ) +} + +func TestPOSIXShellOptsTaskLevel(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/shopts/task_level")), + WithTask("pipefail"), + ) +} + +func TestPOSIXShellOptsCommandLevel(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/shopts/command_level")), + WithTask("pipefail"), + ) +} + +func TestBashShellOptsGlobalLevel(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/shopts/global_level")), + WithTask("globstar"), + ) +} + +func TestBashShellOptsTaskLevel(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/shopts/task_level")), + WithTask("globstar"), + ) +} + +func TestBashShellOptsCommandLevel(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/shopts/command_level")), + WithTask("globstar"), + ) +} + +func TestSplitArgs(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/split_args"), + task.WithSilent(true), + ), + WithVar("CLI_ARGS", "foo bar 'foo bar baz'"), + ) +} + +func TestWildcard(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + call string + wantErr bool + }{ + {name: "basic wildcard", call: "wildcard-foo"}, + {name: "double wildcard", call: "foo-wildcard-bar"}, + {name: "store wildcard", call: "start-foo"}, + {name: "alias", call: "s-foo"}, + {name: "matches exactly", call: "matches-exactly-*"}, + {name: "no matches", call: "no-match", wantErr: true}, + {name: "multiple matches", call: "wildcard-foo-bar"}, + } + + for _, test := range tests { + opts := []ExecutorTestOption{ + WithName(test.call), + WithExecutorOptions( + task.WithDir("testdata/wildcards"), + task.WithSilent(true), + task.WithForce(true), + ), + WithTask(test.call), + } + if test.wantErr { + opts = append(opts, WithRunError()) + } + NewExecutorTest(t, opts...) + } +} + +func TestIgnoreNilElements(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + dir string + }{ + {"nil cmd", "testdata/ignore_nil_elements/cmds"}, + {"nil dep", "testdata/ignore_nil_elements/deps"}, + {"nil include", "testdata/ignore_nil_elements/includes"}, + {"nil precondition", "testdata/ignore_nil_elements/preconditions"}, + } + + for _, test := range tests { + NewExecutorTest(t, + WithName(test.name), + WithExecutorOptions( + task.WithDir(test.dir), + task.WithSilent(true), + ), + ) + } +} + +func TestRunWhenChanged(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/run_when_changed"), + task.WithForceAll(true), + task.WithSilent(true), + ), + WithTask("start"), + ) +} + +func TestRunOnceSharedFailurePropagates(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/run_once_failure")), + WithRunError(), + ) +} + +func TestForce(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + force bool + forceAll bool + }{ + {name: "force", force: true}, + {name: "force-all", forceAll: true}, + {name: "force with gentle force experiment", force: true}, + {name: "force-all with gentle force experiment", forceAll: true}, + } + for _, tt := range tests { + NewExecutorTest(t, + WithName(tt.name), + WithExecutorOptions( + task.WithDir("testdata/force"), + task.WithForce(tt.force), + task.WithForceAll(tt.forceAll), + ), + WithTask("task-with-dep"), + ) + } +} + +func TestIncludesInternal(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + task string + expectedErr bool + }{ + {"included internal task via task", "task-1", false}, + {"included internal task via dep", "task-2", false}, + {"included internal direct", "included:task-3", true}, + } + + for _, test := range tests { + opts := []ExecutorTestOption{ + WithName(test.name), + WithExecutorOptions( + task.WithDir("testdata/internal_task"), + task.WithSilent(true), + ), + WithTask(test.task), + } + if test.expectedErr { + opts = append(opts, WithRunError()) + } + NewExecutorTest(t, opts...) + } +} + +func TestInternalTask(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + task string + expectedErr bool + }{ + {"internal task via task", "task-1", false}, + {"internal task via dep", "task-2", false}, + {"internal direct", "task-3", true}, + } + + for _, test := range tests { + opts := []ExecutorTestOption{ + WithName(test.name), + WithExecutorOptions( + task.WithDir("testdata/internal_task"), + task.WithSilent(true), + ), + WithTask(test.task), + } + if test.expectedErr { + opts = append(opts, WithRunError()) + } + NewExecutorTest(t, opts...) + } +} + +func TestIncludesInterpolation(t *testing.T) { // nolint:paralleltest // cannot run in parallel + const dir = "testdata/includes_interpolation" + tests := []struct { + name string + task string + }{ + {"include", "include"}, + {"include_with_env_variable", "include-with-env-variable"}, + {"include_with_dir", "include-with-dir"}, + } + t.Setenv("MODULE", "included") + + for _, test := range tests { // nolint:paralleltest // cannot run in parallel + NewExecutorTest(t, + WithName(test.name), + WithExecutorOptions( + task.WithDir(filepath.Join(dir, test.name)), + task.WithSilent(true), + ), + WithTask(test.task), + ) + } +} + +func TestIncludesFlatten(t *testing.T) { + t.Parallel() + + const dir = "testdata/includes_flatten" + tests := []struct { + name string + taskfile string + task string + expectedErr bool + }{ + {name: "included flatten", taskfile: "Taskfile.yml", task: "gen"}, + {name: "included flatten with default", taskfile: "Taskfile.yml", task: "default"}, + {name: "included flatten can call entrypoint tasks", taskfile: "Taskfile.yml", task: "from_entrypoint"}, + {name: "included flatten with deps", taskfile: "Taskfile.yml", task: "with_deps"}, + {name: "included flatten nested", taskfile: "Taskfile.yml", task: "from_nested"}, + {name: "included flatten multiple same task", taskfile: "Taskfile.multiple.yml", task: "gen", expectedErr: true}, + } + + for _, test := range tests { + opts := []ExecutorTestOption{ + WithName(test.name), + WithExecutorOptions( + task.WithDir(dir), + task.WithEntrypoint(dir+"/"+test.taskfile), + task.WithSilent(true), + ), + WithTask(test.task), + } + if test.expectedErr { + opts = append(opts, WithSetupError()) + } + NewExecutorTest(t, opts...) + } +} + +func TestTaskIgnoreErrors(t *testing.T) { + t.Parallel() + + NewExecutorTest(t, + WithName("task-should-pass"), + WithExecutorOptions(task.WithDir("testdata/ignore_errors")), + WithTask("task-should-pass"), + ) + NewExecutorTest(t, + WithName("task-should-fail"), + WithExecutorOptions(task.WithDir("testdata/ignore_errors")), + WithTask("task-should-fail"), + WithRunError(), + ) + NewExecutorTest(t, + WithName("cmd-should-pass"), + WithExecutorOptions(task.WithDir("testdata/ignore_errors")), + WithTask("cmd-should-pass"), + ) + NewExecutorTest(t, + WithName("cmd-should-fail"), + WithExecutorOptions(task.WithDir("testdata/ignore_errors")), + WithTask("cmd-should-fail"), + WithRunError(), + ) +} + +func TestDeferredCmds(t *testing.T) { + t.Parallel() + + NewExecutorTest(t, + WithName("task-2"), + WithExecutorOptions(task.WithDir("testdata/deferred")), + WithTask("task-2"), + WithRunError(), + ) + NewExecutorTest(t, + WithName("parent"), + WithExecutorOptions(task.WithDir("testdata/deferred")), + WithTask("parent"), + ) +} + +func TestIncludeCycle(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/includes_cycle"), + task.WithSilent(true), + ), + WithSetupError(), + WithFixtureTemplating(), + ) +} + +func TestIncludesIncorrect(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/includes_incorrect"), + task.WithSilent(true), + ), + WithSetupError(), + ) +} + +func TestIncludesMissingTaskfile(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/includes_missing_taskfile"), + task.WithSilent(true), + ), + WithSetupError(), + WithFixtureTemplating(), + ) +} + +func TestIncludesOptionalImplicitFalse(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes_optional_implicit_false")), + WithSetupError(), + WithFixtureTemplating(), + ) +} + +func TestIncludesOptionalExplicitFalse(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/includes_optional_explicit_false")), + WithSetupError(), + WithFixtureTemplating(), + ) +} + +func TestDotenvShouldErrorWhenIncludingDependantDotenvs(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/dotenv/error_included_envs"), + task.WithSummary(true), + ), + WithSetupError(), + ) +} + +func TestTaskDotenvParseErrorMessage(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dotenv/parse_error")), + WithSetupError(), + WithFixtureTemplating(), + ) +} + +func TestDisplaysErrorOnVersion1Schema(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/version/v1"), + task.WithVersionCheck(true), + ), + WithSetupError(), + WithFixtureTemplating(), + ) +} + +func TestDisplaysErrorOnVersion2Schema(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/version/v2"), + task.WithVersionCheck(true), + ), + WithSetupError(), + WithFixtureTemplating(), + ) +} + +func TestExpand(t *testing.T) { + t.Parallel() + + home, err := os.UserHomeDir() + require.NoError(t, err) + + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/expand")), + WithTask("pwd"), + WithFixtureTemplateData("HOME", filepath.ToSlash(home)), + ) +} + +func TestUserWorkingDirectory(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/user_working_dir")), + WithFixtureTemplating(), + ) +} + +func TestAbsPath(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/abs_path"), + task.WithSilent(true), + ), + WithFixtureTemplating(), + ) +} + +func TestPlatforms(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/platforms")), + WithTask("build-"+runtime.GOOS), + WithFixtureTemplateData("GOOS", runtime.GOOS), + ) +} + +func TestIncludedTaskfileVarMerging(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + task string + }{ + {"foo", "foo:pwd"}, + {"bar", "bar:pwd"}, + } + for _, test := range tests { + NewExecutorTest(t, + WithName(test.name), + WithExecutorOptions( + task.WithDir("testdata/included_taskfile_var_merging"), + task.WithSilent(true), + ), + WithTask(test.task), + WithFixtureTemplating(), + ) + } +} + +func TestIncludesRelativePath(t *testing.T) { + t.Parallel() + + NewExecutorTest(t, + WithName("common:pwd"), + WithExecutorOptions(task.WithDir("testdata/includes_rel_path")), + WithTask("common:pwd"), + WithFixtureTemplating(), + ) + NewExecutorTest(t, + WithName("included:common:pwd"), + WithExecutorOptions(task.WithDir("testdata/includes_rel_path")), + WithTask("included:common:pwd"), + WithFixtureTemplating(), + ) +} + +func TestWhenNoDirAttributeItRunsInSameDirAsTaskfile(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dir")), + WithTask("whereami"), + WithFixtureTemplating(), + ) +} + +func TestWhenDirAttributeAndDirExistsItRunsInThatDir(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dir/explicit_exists")), + WithTask("whereami"), + WithFixtureTemplating(), + ) +} + +func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) { + t.Parallel() + + const toBeCreated = "testdata/dir/explicit_doesnt_exist/createme" + + // Ensure that the directory to be created doesn't actually exist. + _ = os.RemoveAll(toBeCreated) + if _, err := os.Stat(toBeCreated); err == nil { + t.Errorf("Directory should not exist: %v", err) + } + t.Cleanup(func() { _ = os.RemoveAll(toBeCreated) }) + + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dir/explicit_doesnt_exist/")), + WithTask("whereami"), + WithFixtureTemplating(), + ) +} + +func TestDynamicVariablesRunOnTheNewCreatedDir(t *testing.T) { + t.Parallel() + + const toBeCreated = "testdata/dir/dynamic_var_on_created_dir/created" + + // Ensure that the directory to be created doesn't actually exist. + _ = os.RemoveAll(toBeCreated) + if _, err := os.Stat(toBeCreated); err == nil { + t.Errorf("Directory should not exist: %v", err) + } + t.Cleanup(func() { _ = os.RemoveAll(toBeCreated) }) + + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/dir/dynamic_var_on_created_dir")), + WithFixtureTemplating(), + // Take only the first line, as Windows may output additional debug info. + WithPostProcessFn(PPFirstLine), + ) +} + +func TestEvaluateSymlinksInPaths(t *testing.T) { // nolint:paralleltest // cannot run in parallel + const dir = "testdata/evaluate_symlinks_in_paths" + t.Cleanup(func() { + _ = os.RemoveAll(dir + "/.task") + }) + + steps := []struct { + name string + task string + }{ + {"default (1)", "default"}, + {"test-sym (1)", "test-sym"}, + {"default (2)", "default"}, + {"default (3)", "default"}, + {"reset", "reset"}, + } + for _, step := range steps { // nolint:paralleltest // cannot run in parallel + NewExecutorTest(t, + WithName(step.name), + WithExecutorOptions(task.WithDir(dir)), + WithTask(step.task), + ) + } +} + +func TestIgnoreErrorsOnTimeout(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + task string + expectError bool + }{ + {name: "ignored at task level", task: "task-timeout-should-pass"}, + {name: "ignored at command level", task: "cmd-timeout-should-pass"}, + {name: "not ignored", task: "cmd-timeout-should-fail", expectError: true}, + } + + for _, test := range tests { + opts := []ExecutorTestOption{ + WithName(test.name), + WithExecutorOptions(task.WithDir("testdata/ignore_errors")), + WithTask(test.task), + } + if test.expectError { + opts = append(opts, WithRunError()) + } + NewExecutorTest(t, opts...) + } +} + +func TestExitImmediately(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/exit_immediately"), + task.WithSilent(true), + ), + WithRunError(), + ) +} + +func TestRunOnceSharedDeps(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/run_once_shared_deps"), + task.WithForceAll(true), + ), + WithTask("build"), + // service-a:build and service-b:build run concurrently, so their + // output can interleave in either order, and whichever of them wins + // the race is credited with the shared "run: once" library:build dep. + WithPostProcessFn(func(t *testing.T, b []byte) []byte { + t.Helper() + re := regexp.MustCompile(`service-[ab]:library:build`) + return re.ReplaceAll(b, []byte("service-x:library:build")) + }), + WithPostProcessFn(PPSortedLines), + ) +} + +func TestCommandTimeout(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + task string + expectError bool + }{ + {name: "timeout exceeded", task: "timeout-exceeded", expectError: true}, + {name: "timeout not exceeded", task: "timeout-not-exceeded"}, + {name: "no timeout", task: "no-timeout"}, + {name: "multiple commands with timeout", task: "multiple-cmds-timeout", expectError: true}, + } + + for _, test := range tests { + opts := []ExecutorTestOption{ + WithName(test.name), + WithExecutorOptions(task.WithDir("testdata/timeout")), + WithTask(test.task), + } + if test.expectError { + opts = append(opts, WithRunError()) + } + NewExecutorTest(t, opts...) + } +} + +func TestIncludesWithExclude(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + task string + expectError bool + }{ + {name: "included:bar", task: "included:bar"}, + {name: "included:foo", task: "included:foo", expectError: true}, + {name: "included:foo:child", task: "included:foo:child"}, + {name: "included:namespace", task: "included:namespace"}, + {name: "included:namespace:one", task: "included:namespace:one", expectError: true}, + {name: "included:namespace-other:one", task: "included:namespace-other:one"}, + {name: "bar", task: "bar", expectError: true}, + {name: "foo", task: "foo"}, + {name: "namespace", task: "namespace"}, + {name: "namespace:two", task: "namespace:two", expectError: true}, + {name: "namespace-other:one", task: "namespace-other:one"}, + } + + for _, test := range tests { + opts := []ExecutorTestOption{ + WithName(test.name), + WithExecutorOptions( + task.WithDir("testdata/includes_with_excludes"), + task.WithSilent(true), + ), + WithTask(test.task), + } + if test.expectError { + opts = append(opts, WithRunError()) + } + NewExecutorTest(t, opts...) + } +} + +func TestCyclicDep(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/cyclic")), + WithTask("task-1"), + WithRunError(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + var taskCalledTooManyTimesError *errors.TaskCalledTooManyTimesError + assert.ErrorAs(t, r.Err, &taskCalledTooManyTimesError) + }), + ) +} + +func TestTaskVersion(t *testing.T) { + t.Parallel() + + NewExecutorTest(t, + WithName("v1"), + WithExecutorOptions( + task.WithDir("testdata/version/v1"), + task.WithVersionCheck(true), + ), + WithSetupError(), + WithFixtureTemplating(), + ) + NewExecutorTest(t, + WithName("v2"), + WithExecutorOptions( + task.WithDir("testdata/version/v2"), + task.WithVersionCheck(true), + ), + WithSetupError(), + WithFixtureTemplating(), + ) + NewExecutorTest(t, + WithName("v3"), + WithExecutorOptions( + task.WithDir("testdata/version/v3"), + task.WithVersionCheck(true), + ), + WithNoRun(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + assert.Equal(t, semver.MustParse("3"), r.Executor.Taskfile.Version) + assert.Equal(t, 2, r.Executor.Taskfile.Tasks.Len()) + }), + ) +} + +func TestDry(t *testing.T) { + t.Parallel() + + _ = os.Remove(filepathext.SmartJoin("testdata/dry", "file.txt")) + + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/dry"), + task.WithDry(true), + ), + WithTask("build"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + _, err := os.Stat(filepathext.SmartJoin(r.Executor.Dir, "file.txt")) + assert.Error(t, err, "file.txt should not exist in dry mode") + }), + ) +} + +func TestDryChecksum(t *testing.T) { + t.Parallel() + + const dir = "testdata/dry_checksum" + checksumFile := filepathext.SmartJoin(dir, ".task/checksum/default") + _ = os.Remove(checksumFile) + + NewExecutorTest(t, + WithName("dry"), + WithExecutorOptions( + task.WithDir(dir), + task.WithDry(true), + ), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + _, err := os.Stat(checksumFile) + require.Error(t, err, "checksum file should not exist") + }), + ) + NewExecutorTest(t, + WithName("not dry"), + WithExecutorOptions(task.WithDir(dir)), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + _, err := os.Stat(checksumFile) + require.NoError(t, err, "checksum file should exist") + }), + ) +} + +// fixedSourceModTime pins a source file's modification time so that +// timestamp-fingerprinting output (which reads that mtime) is deterministic +// across machines and test runs, and can be golden-fixture compared. +var fixedSourceModTime = time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) + +func TestStatusVariables(t *testing.T) { + t.Parallel() + + const dir = "testdata/status_vars" + _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task")) + _ = os.Remove(filepathext.SmartJoin(dir, "generated.txt")) + + NewExecutorTest(t, + WithName("build-checksum"), + WithExecutorOptions( + task.WithDir(dir), + task.WithVerbose(true), + ), + WithTask("build-checksum"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + assert.Contains(t, r.Output, "3e464c4b03f4b65d740e1e130d4d108a") + }), + ) + + sourceFile := filepathext.SmartJoin(dir, "source.txt") + require.NoError(t, os.Chtimes(sourceFile, fixedSourceModTime, fixedSourceModTime)) + NewExecutorTest(t, + WithName("build-ts"), + WithExecutorOptions( + task.WithDir(dir), + task.WithVerbose(true), + ), + WithTask("build-ts"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + inf, err := os.Stat(sourceFile) + require.NoError(t, err) + assert.Contains(t, r.Output, fmt.Sprintf("%d", inf.ModTime().Unix())) + assert.Contains(t, r.Output, inf.ModTime().String()) + }), + ) +} + +func TestCmdsVariables(t *testing.T) { + t.Parallel() + + const dir = "testdata/cmds_vars" + _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task")) + + NewExecutorTest(t, + WithName("build-checksum"), + WithExecutorOptions( + task.WithDir(dir), + task.WithVerbose(true), + ), + WithTask("build-checksum"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + assert.Contains(t, r.Output, "3e464c4b03f4b65d740e1e130d4d108a") + }), + ) + + sourceFile := filepathext.SmartJoin(dir, "source.txt") + require.NoError(t, os.Chtimes(sourceFile, fixedSourceModTime, fixedSourceModTime)) + NewExecutorTest(t, + WithName("build-ts"), + WithExecutorOptions( + task.WithDir(dir), + task.WithVerbose(true), + ), + WithTask("build-ts"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + inf, err := os.Stat(sourceFile) + require.NoError(t, err) + assert.Contains(t, r.Output, fmt.Sprintf("%d", inf.ModTime().Unix())) + assert.Contains(t, r.Output, inf.ModTime().String()) + }), + ) +} + +func TestFingerprintVarMethod(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + dir string + executorOpts []task.ExecutorOption + wantErr bool + pinSourceModTime bool + assertOutput func(t *testing.T, output string) + }{ + { + name: "TIMESTAMP is injected when the method is inherited from the Taskfile", + dir: "testdata/method_taskfile_timestamp", + // The output embeds the source file's modification time; pin it + // so the value is deterministic across machines and test runs. + pinSourceModTime: true, + assertOutput: func(t *testing.T, output string) { + t.Helper() + // An unresolved variable renders as an empty string, so this + // has to match an actual timestamp, not just the prefix. + assert.Regexp(t, `ts=\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}`, output) + }, + }, + { + name: "no variable is injected when the effective method is none", + dir: "testdata/method_taskfile_none", + assertOutput: func(t *testing.T, output string) { + t.Helper() + assert.Contains(t, output, "cs=\n") + }, + }, + { + name: "an invalid method doesn't fail a run that skips fingerprinting", + dir: "testdata/method_invalid", + executorOpts: []task.ExecutorOption{task.WithForce(true)}, + assertOutput: func(t *testing.T, output string) { + t.Helper() + assert.Contains(t, output, "cs=[]\n") + }, + }, + { + name: "an invalid method is still reported by the up-to-date check", + dir: "testdata/method_invalid", + wantErr: true, + }, + } + for _, tt := range tests { + _ = os.RemoveAll(filepathext.SmartJoin(tt.dir, ".task")) + if tt.pinSourceModTime { + sourceFile := filepathext.SmartJoin(tt.dir, "source.txt") + require.NoError(t, os.Chtimes(sourceFile, fixedSourceModTime, fixedSourceModTime)) + } + + opts := []ExecutorTestOption{ + WithName(tt.name), + WithExecutorOptions(append([]task.ExecutorOption{task.WithDir(tt.dir)}, tt.executorOpts...)...), + WithTask("build"), + } + if tt.wantErr { + opts = append(opts, WithRunError()) + } else { + opts = append(opts, WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + tt.assertOutput(t, r.Output) + })) + } + NewExecutorTest(t, opts...) + } +} + +func TestErrorCode(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + task string + expected int + }{ + {name: "direct task", task: "direct", expected: 42}, + {name: "indirect task", task: "indirect", expected: 42}, + } + + for _, test := range tests { + NewExecutorTest(t, + WithName(test.name), + WithExecutorOptions( + task.WithDir("testdata/error_code"), + task.WithSilent(true), + ), + WithTask(test.task), + WithRunError(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + var taskRunErr *errors.TaskRunError + require.ErrorAs(t, r.Err, &taskRunErr) + assert.Equal(t, test.expected, taskRunErr.TaskExitCode(), "unexpected exit code from task") + }), + ) + } +} + +func TestRunOnceJoinerHonorsItsOwnTimeout(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/run_once_timeout")), + WithRunError(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + // The joiner used to wait on the shared execution alone, ignoring + // its own timeout for as long as that execution took. + assert.Less(t, r.Duration, 5*time.Second) + + var timeoutErr *errors.TaskTimeoutError + require.ErrorAs(t, r.Err, &timeoutErr) + assert.Equal(t, "joiner", timeoutErr.TaskName) + assert.NotContains(t, r.Output, "should not be reached") + }), + ) +} + +func TestDeferredTaskTimeout(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/deferred"), + task.WithVerbose(true), + ), + WithTask("parent-with-timeout"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + assert.Less(t, r.Duration, 500*time.Millisecond) + assert.Contains(t, r.Output, "parent completed") + assert.NotContains(t, r.Output, "\ncleanup completed\n") + assert.Contains(t, r.Output, "ignored error in deferred cmd") + }), + ) +} + +func TestExitCodeTimeout(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/exit_code")), + WithTask("exit-timeout"), + WithRunError(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + var runErr *errors.TaskRunError + require.ErrorAs(t, r.Err, &runErr) + assert.Equal(t, errors.TimeoutExitCode, runErr.TaskExitCode()) + }), + ) +} + +func TestDepTimeout(t *testing.T) { + t.Parallel() + + NewExecutorTest(t, + WithName("timeout exceeded"), + WithExecutorOptions(task.WithDir("testdata/dep_timeout")), + WithTask("timeout-exceeded"), + WithRunError(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + assert.Less(t, r.Duration, 5*time.Second) + + var timeoutErr *errors.TaskTimeoutError + require.ErrorAs(t, r.Err, &timeoutErr) + assert.Equal(t, "slow", timeoutErr.TaskName) + assert.NotContains(t, r.Output, "should not be reached") + }), + ) + NewExecutorTest(t, + WithName("timeout not exceeded"), + WithExecutorOptions(task.WithDir("testdata/dep_timeout")), + WithTask("timeout-not-exceeded"), + ) +} + +func TestCommandTimeoutBoundsIfCondition(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/timeout")), + WithTask("slow-if-condition"), + WithRunError(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + assert.Less(t, r.Duration, 5*time.Second) + + var timeoutErr *errors.TaskTimeoutError + require.ErrorAs(t, r.Err, &timeoutErr) + // A condition that times out fails the command, it does not skip it. + assert.NotContains(t, r.Output, "condition was met") + }), + ) +} + +func TestCommandTimeoutAttribution(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + task string + notContains string + }{ + { + name: "a command declaring no timeout is not blamed for one", + task: "inherited-timeout", + notContains: "(0s)", + }, + { + name: "a command is not blamed for a timeout it never reached", + task: "larger-child-timeout", + notContains: "10m", + }, + } + + for _, test := range tests { + NewExecutorTest(t, + WithName(test.name), + WithExecutorOptions(task.WithDir("testdata/timeout")), + WithTask(test.task), + WithRunError(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + assert.Contains(t, r.Err.Error(), "command timeout exceeded (500ms)") + assert.NotContains(t, r.Err.Error(), test.notContains) + + var timeoutErr *errors.TaskTimeoutError + require.ErrorAs(t, r.Err, &timeoutErr) + assert.Equal(t, test.task, timeoutErr.TaskName) + + // --watch swallows context errors; a timeout must not look like one. + assert.False(t, errors.Is(r.Err, context.DeadlineExceeded)) + }), + ) + } +} + +func TestUserWorkingDirectoryWithIncluded(t *testing.T) { + t.Parallel() + + wd, err := os.Getwd() + require.NoError(t, err) + wd = filepath.ToSlash(filepathext.SmartJoin(wd, "testdata/user_working_dir_with_includes/somedir")) + + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/user_working_dir_with_includes"), + task.WithUserWorkingDir(wd), + ), + WithTask("included:echo"), + WithFixtureTemplating(), + ) +} + +func TestIncludeWithVarsInInclude(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/include_with_vars_inside_include")), + WithNoRun(), + ) +} + +func TestGitignoreTaskListFallback(t *testing.T) { //nolint:paralleltest // shares testdata/gitignore with TestGitignoreChecksum + NewExecutorTest(t, + WithExecutorOptions(task.WithDir("testdata/gitignore")), + WithNoRun(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + listed, err := r.Executor.CompiledTaskForTaskList(&task.Call{Task: "build"}) + require.NoError(t, err) + assert.True(t, listed.ShouldUseGitignore(), + "task list should reflect the global use_gitignore fallback") + + listedOff, err := r.Executor.CompiledTaskForTaskList(&task.Call{Task: "build-no-use_gitignore"}) + require.NoError(t, err) + assert.False(t, listedOff.ShouldUseGitignore(), + "explicit use_gitignore: false must be preserved in the list path") + }), + ) +} + +func TestSummary(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/summary"), + task.WithSummary(true), + task.WithSilent(true), + ), + WithTasks("task-with-summary", "other-task-with-summary"), + ) +} + +func TestSilence(t *testing.T) { + t.Parallel() + + tests := []string{ + "silent", + "chatty", + "task-test-silent-calls-chatty-non-silenced", + "task-test-silent-calls-chatty-silenced", + "task-test-chatty-calls-chatty-non-silenced", + "task-test-chatty-calls-chatty-silenced", + "task-test-no-cmds-calls-chatty-silenced", + "task-test-chatty-calls-silenced-cmd", + "task-test-is-silent-depends-on-chatty-non-silenced", + "task-test-is-silent-depends-on-chatty-silenced", + "task-test-is-chatty-depends-on-chatty-silenced", + } + + for i, taskName := range tests { + opts := []ExecutorTestOption{ + WithName(taskName), + WithExecutorOptions(task.WithDir("testdata/silent")), + WithTask(taskName), + } + if i == 0 { + // Verify that the silent flag is in place before running anything. + opts = append(opts, WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + fetchedTask, err := r.Executor.GetTask(&task.Call{Task: "task-test-silent-calls-chatty-silenced"}) + require.NoError(t, err, "Unable to look up task task-test-silent-calls-chatty-silenced") + require.True(t, fetchedTask.Cmds[0].Silent, "The task task-test-silent-calls-chatty-silenced should have a silent call to chatty") + })) + } + NewExecutorTest(t, opts...) + } +} + +func TestGenerates(t *testing.T) { + t.Parallel() + + const dir = "testdata/generates" + const srcTask = "sub/src.txt" + srcFile := filepathext.SmartJoin(dir, srcTask) + + destTasks := []string{"rel.txt", "abs.txt", "my text file.txt"} + for _, f := range append([]string{srcTask}, destTasks...) { + _ = os.Remove(filepathext.SmartJoin(dir, f)) + } + + for _, destTask := range destTasks { + destFile := filepathext.SmartJoin(dir, destTask) + NewExecutorTest(t, + WithName(destTask+" (first run)"), + WithExecutorOptions(task.WithDir(dir)), + WithTask(destTask), + WithFixtureTemplating(), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + _, err := os.Stat(srcFile) + assert.NoError(t, err, "File should exist") + _, err = os.Stat(destFile) + assert.NoError(t, err, "File should exist") + }), + ) + NewExecutorTest(t, + WithName(destTask+" (up to date)"), + WithExecutorOptions(task.WithDir(dir)), + WithTask(destTask), + ) + } +} + +func TestStatusChecksum(t *testing.T) { // nolint:paralleltest // cannot run in parallel + const dir = "testdata/checksum" + + tests := []struct { + files []string + task string + }{ + {[]string{"generated.txt", ".task/checksum/build"}, "build"}, + {[]string{"generated-wildcard.txt", ".task/checksum/build-wildcard"}, "build-wildcard"}, + {[]string{"generated.txt", ".task/checksum/build-with-status"}, "build-with-status"}, + } + + for _, test := range tests { // nolint:paralleltest // cannot run in parallel + for _, f := range test.files { + _ = os.Remove(filepathext.SmartJoin(dir, f)) + } + checksumFile := filepathext.SmartJoin(dir, test.files[1]) + + var capturedTime time.Time + NewExecutorTest(t, + WithName(test.task+" (first run)"), + WithExecutorOptions(task.WithDir(dir)), + WithTask(test.task), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + for _, f := range test.files { + _, err := os.Stat(filepathext.SmartJoin(dir, f)) + require.NoError(t, err) + } + // Capture the modification time, so we can ensure the + // checksum file is not regenerated when the hash hasn't + // changed. + s, err := os.Stat(checksumFile) + require.NoError(t, err) + capturedTime = s.ModTime() + }), + ) + NewExecutorTest(t, + WithName(test.task+" (up to date)"), + WithExecutorOptions(task.WithDir(dir)), + WithTask(test.task), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + s, err := os.Stat(checksumFile) + require.NoError(t, err) + assert.Equal(t, capturedTime, s.ModTime()) + }), + ) + } +} + +// TestStatusTimestamp is a regression test for https://github.com/go-task/task/issues/1230. +// When using method: timestamp, deleting a generated file should cause the task to re-run, +// not be skipped because the timestamp file is still present. +func TestStatusTimestamp(t *testing.T) { // nolint:paralleltest // cannot run in parallel + const dir = "testdata/timestamp" + generatedFile := filepathext.SmartJoin(dir, "generated.txt") + + _ = os.Remove(generatedFile) + _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task")) + + NewExecutorTest(t, + WithName("first run"), + WithExecutorOptions(task.WithDir(dir)), + WithTask("build"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + _, err := os.Stat(generatedFile) + require.NoError(t, err, "generated.txt should exist after first run") + }), + ) + NewExecutorTest(t, + WithName("up to date"), + WithExecutorOptions(task.WithDir(dir)), + WithTask("build"), + ) + + // Delete the generated file (simulate a clean), but leave the timestamp file. + require.NoError(t, os.Remove(generatedFile)) + + NewExecutorTest(t, + WithName("re-run after generated file removed"), + WithExecutorOptions(task.WithDir(dir)), + WithTask("build"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + // This is the regression: previously the task was incorrectly + // skipped because the timestamp file was still present. + assert.NotContains(t, r.Output, "is up to date", "task should re-run when generated file is missing") + _, err := os.Stat(generatedFile) + require.NoError(t, err, "generated.txt should be recreated after third run") + }), + ) +} + +// TestStatusChecksumMissingGenerated is a regression test for https://github.com/go-task/task/issues/1230. +// When using method: checksum, deleting a generated file should cause the task to re-run, +// not be skipped because the checksum file still matches. +func TestStatusChecksumMissingGenerated(t *testing.T) { // nolint:paralleltest // cannot run in parallel + const dir = "testdata/checksum" + generatedFile := filepathext.SmartJoin(dir, "generated.txt") + + _ = os.Remove(generatedFile) + _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task")) + + NewExecutorTest(t, + WithName("first run"), + WithExecutorOptions(task.WithDir(dir)), + WithTask("build"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + _, err := os.Stat(generatedFile) + require.NoError(t, err, "generated.txt should exist after first run") + }), + ) + NewExecutorTest(t, + WithName("up to date"), + WithExecutorOptions(task.WithDir(dir)), + WithTask("build"), + ) + + // Delete the generated file (simulate a clean), but leave the checksum file. + require.NoError(t, os.Remove(generatedFile)) + + NewExecutorTest(t, + WithName("re-run after generated file removed"), + WithExecutorOptions(task.WithDir(dir)), + WithTask("build"), + WithAssert(func(t *testing.T, r *ExecutorTestResult) { + t.Helper() + // This is the regression: previously the task was incorrectly + // skipped because the checksum file still matched. + assert.NotContains(t, r.Output, "is up to date", "task should re-run when generated file is missing") + _, err := os.Stat(generatedFile) + require.NoError(t, err, "generated.txt should be recreated after third run") + }), + ) +} diff --git a/formatter_test.go b/formatter_test.go index b92c8d5579..a55007356d 100644 --- a/formatter_test.go +++ b/formatter_test.go @@ -67,33 +67,6 @@ func NewFormatterTest(t *testing.T, opts ...FormatterTestOption) { tt.run(t) } -// Functional options - -// WithListOptions sets the list options for the formatter. -func WithListOptions(opts task.ListOptions) FormatterTestOption { - return &listOptionsTestOption{opts} -} - -type listOptionsTestOption struct { - listOptions task.ListOptions -} - -func (opt *listOptionsTestOption) applyToFormatterTest(t *FormatterTest) { - t.listOptions = opt.listOptions -} - -// WithListError tells the test to expect an error when running the formatter. -// A fixture will be created with the output of any errors. -func WithListError() FormatterTestOption { - return &listErrorTestOption{} -} - -type listErrorTestOption struct{} - -func (opt *listErrorTestOption) applyToFormatterTest(t *FormatterTest) { - t.wantListError = true -} - // Helpers // writeFixtureErrList is a wrapper for writing the output of an error when diff --git a/task_test.go b/task_test.go index 540e7ba8de..59f609955e 100644 --- a/task_test.go +++ b/task_test.go @@ -2,36 +2,21 @@ package task_test import ( "bytes" - "context" - "fmt" - "io" - "io/fs" "maps" - rand "math/rand/v2" - "net/http" - "net/http/httptest" - "net/url" "os" "path/filepath" - "regexp" - "runtime" "slices" "sort" "strings" "sync" "testing" - "time" - "github.com/Masterminds/semver/v3" "github.com/sebdah/goldie/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/go-task/task/v3" - "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/experiments" - "github.com/go-task/task/v3/internal/filepathext" - "github.com/go-task/task/v3/taskfile/ast" ) func init() { @@ -39,10 +24,6 @@ func init() { } type ( - TestOption interface { - ExecutorTestOption - FormatterTestOption - } TaskTest struct { name string experiments map[*experiments.Experiment]int @@ -52,6 +33,21 @@ type ( } ) +// SyncBuffer is a threadsafe buffer for testing. +// Some times replace stdout/stderr with a buffer to capture output. +// stdout and stderr are threadsafe, but a regular bytes.Buffer is not. +// Using this instead helps prevents race conditions with output. +type SyncBuffer struct { + buf bytes.Buffer + mu sync.Mutex +} + +func (sb *SyncBuffer) Write(p []byte) (n int, err error) { + sb.mu.Lock() + defer sb.mu.Unlock() + return sb.buf.Write(p) +} + // goldenFileName makes the file path for fixture files safe for all well-known // operating systems. Windows in particular has a lot of restrictions the // characters that can be used in file paths. @@ -124,11 +120,13 @@ func (tt *TaskTest) writeFixtureErrSetup( tt.writeFixture(t, g, "err-setup", []byte(err.Error())) } +// // Functional options +// // WithName gives the test fixture output a name. This should be used when // running multiple tests in a single test function. -func WithName(name string) TestOption { +func WithName(name string) *nameTestOption { return &nameTestOption{name: name} } @@ -146,7 +144,7 @@ func (opt *nameTestOption) applyToFormatterTest(t *FormatterTest) { // WithTask sets the name of the task to run. This should be used when the task // to run is not the default task. -func WithTask(task string) TestOption { +func WithTask(task string) *taskTestOption { return &taskTestOption{task: task} } @@ -164,7 +162,7 @@ func (opt *taskTestOption) applyToFormatterTest(t *FormatterTest) { // WithVar sets a variable to be passed to the task. This can be called multiple // times to set more than one variable. -func WithVar(key string, value any) TestOption { +func WithVar(key string, value any) *varTestOption { return &varTestOption{key: key, value: value} } @@ -183,7 +181,7 @@ func (opt *varTestOption) applyToFormatterTest(t *FormatterTest) { // WithExecutorOptions sets the [task.ExecutorOption]s to be used when creating // a [task.Executor]. -func WithExecutorOptions(executorOpts ...task.ExecutorOption) TestOption { +func WithExecutorOptions(executorOpts ...task.ExecutorOption) *executorOptionsTestOption { return &executorOptionsTestOption{executorOpts: executorOpts} } @@ -201,7 +199,7 @@ func (opt *executorOptionsTestOption) applyToFormatterTest(t *FormatterTest) { // WithExperiment sets an experiment to be enabled for the test. This can be // called multiple times to enable more than one experiment. -func WithExperiment(experiment *experiments.Experiment, value int) TestOption { +func WithExperiment(experiment *experiments.Experiment, value int) *experimentTestOption { return &experimentTestOption{experiment: experiment, value: value} } @@ -222,7 +220,7 @@ func (opt *experimentTestOption) applyToFormatterTest(t *FormatterTest) { // functions are run on the output of the task before a fixture is created. This // can be used to remove absolute paths, sort lines, etc. This can be called // multiple times to add more than one post-process function. -func WithPostProcessFn(fn PostProcessFn) TestOption { +func WithPostProcessFn(fn PostProcessFn) *postProcessFnTestOption { return &postProcessFnTestOption{fn: fn} } @@ -240,7 +238,7 @@ func (opt *postProcessFnTestOption) applyToFormatterTest(t *FormatterTest) { // WithSetupError sets the test to expect an error during the setup phase of the // task execution. A fixture will be created with the output of any errors. -func WithSetupError() TestOption { +func WithSetupError() *setupErrorTestOption { return &setupErrorTestOption{} } @@ -258,7 +256,7 @@ func (opt *setupErrorTestOption) applyToFormatterTest(t *FormatterTest) { // the default set of data. This is useful if the golden file is dynamic in some // way (e.g. contains user-specific directories). To add more data, see // WithFixtureTemplateData. -func WithFixtureTemplating() TestOption { +func WithFixtureTemplating() *fixtureTemplatingTestOption { return &fixtureTemplatingTestOption{} } @@ -275,7 +273,7 @@ func (opt *fixtureTemplatingTestOption) applyToFormatterTest(t *FormatterTest) { // WithFixtureTemplateData adds data to the golden fixture file templates. Keys // given here will override any existing values. This option will also enable // global templating, so you do not need to call WithFixtureTemplating as well. -func WithFixtureTemplateData(key string, value any) TestOption { +func WithFixtureTemplateData(key string, value any) *fixtureTemplateDataTestOption { return &fixtureTemplateDataTestOption{key, value} } @@ -294,7 +292,122 @@ func (opt *fixtureTemplateDataTestOption) applyToFormatterTest(t *FormatterTest) t.fixtureTemplateData[opt.k] = opt.v } -// Post-processing +// WithInput tells the test to create a reader with the given input. This can be +// used to simulate user input when a task requires it. +func WithInput(input string) *inputTestOption { + return &inputTestOption{input} +} + +type inputTestOption struct { + input string +} + +func (opt *inputTestOption) applyToExecutorTest(t *ExecutorTest) { + t.input = opt.input +} + +// WithRunError tells the test to expect an error during the run phase of the +// task execution. A fixture will be created with the output of any errors. +func WithRunError() *runErrorTestOption { + return &runErrorTestOption{} +} + +type runErrorTestOption struct{} + +func (opt *runErrorTestOption) applyToExecutorTest(t *ExecutorTest) { + t.wantRunError = true +} + +// WithStatusError tells the test to make an additional call to +// [task.Executor.Status] after the task has been run. A fixture will be created +// with the output of any errors. +func WithStatusError() *statusErrorTestOption { + return &statusErrorTestOption{} +} + +type statusErrorTestOption struct{} + +func (opt *statusErrorTestOption) applyToExecutorTest(t *ExecutorTest) { + t.wantStatusError = true +} + +// WithTasks sets the names of multiple tasks to run in a single call to +// [task.Executor.Run]. Use this instead of [WithTask] when the test needs to +// call more than one task at once (e.g. to test summaries spanning several +// tasks). +func WithTasks(tasks ...string) *tasksTestOption { + return &tasksTestOption{tasks: tasks} +} + +type tasksTestOption struct { + tasks []string +} + +func (opt *tasksTestOption) applyToExecutorTest(t *ExecutorTest) { + t.tasks = opt.tasks +} + +// WithNoRun tells the test to stop after a successful setup, without calling +// [task.Executor.Run]. This is useful for tests that only care about the +// state of the [task.Executor] (or its parsed Taskfile) after setup, and for +// tests that construct an [task.Executor] but never actually call a task. No +// output fixture is written, since no task is run. +func WithNoRun() *noRunTestOption { + return &noRunTestOption{} +} + +type noRunTestOption struct{} + +func (opt *noRunTestOption) applyToExecutorTest(t *ExecutorTest) { + t.noRun = true +} + +// WithAssert registers a function to run custom assertions against the +// [ExecutorTestResult] of the test, in addition to the usual error and golden +// fixture checks. This is useful for assertions that a golden fixture can't +// express, such as an error's concrete type, timing bounds, or the +// [task.Executor]'s internal state. This can be called multiple times to add +// more than one assertion function. +func WithAssert(fn func(t *testing.T, r *ExecutorTestResult)) *assertTestOption { + return &assertTestOption{fn: fn} +} + +type assertTestOption struct { + fn func(t *testing.T, r *ExecutorTestResult) +} + +func (opt *assertTestOption) applyToExecutorTest(t *ExecutorTest) { + t.assertFns = append(t.assertFns, opt.fn) +} + +// WithListOptions sets the list options for the formatter. +func WithListOptions(opts task.ListOptions) *listOptionsTestOption { + return &listOptionsTestOption{opts} +} + +type listOptionsTestOption struct { + listOptions task.ListOptions +} + +func (opt *listOptionsTestOption) applyToFormatterTest(t *FormatterTest) { + t.listOptions = opt.listOptions +} + +// WithListError tells the test to expect an error when running the formatter. +// A fixture will be created with the output of any errors. +func WithListError() *listErrorTestOption { + return &listErrorTestOption{} +} + +type listErrorTestOption struct{} + +func (opt *listErrorTestOption) applyToFormatterTest(t *FormatterTest) { + t.wantListError = true +} + +// +// Post-processing functions +// // A PostProcessFn is a function that can be applied to the output of a test // fixture before the file is written. @@ -310,6 +423,15 @@ func PPSortedLines(t *testing.T, b []byte) []byte { return []byte(strings.Join(lines, "\n") + "\n") } +// PPFirstLine keeps only the first line of the output of the task. This is +// useful when a platform (e.g. Windows) may print additional, non-deterministic +// debug info after the line we actually care about. +func PPFirstLine(t *testing.T, b []byte) []byte { + t.Helper() + line, _, _ := bytes.Cut(b, []byte("\n")) + return append(line, '\n') +} + // normalizeOutput normalizes cross-platform differences for byte slice comparison: // - Converts CRLF and CR to LF (line endings) // - Converts backslashes to forward slashes (Windows paths) @@ -376,3084 +498,3 @@ func TestNormalizePathSeparators(t *testing.T) { }) } } - -// SyncBuffer is a threadsafe buffer for testing. -// Some times replace stdout/stderr with a buffer to capture output. -// stdout and stderr are threadsafe, but a regular bytes.Buffer is not. -// Using this instead helps prevents race conditions with output. -type SyncBuffer struct { - buf bytes.Buffer - mu sync.Mutex -} - -func (sb *SyncBuffer) Write(p []byte) (n int, err error) { - sb.mu.Lock() - defer sb.mu.Unlock() - return sb.buf.Write(p) -} - -// fileContentTest provides a basic reusable test-case for running a Taskfile -// and inspect generated files. -type fileContentTest struct { - Dir string - Entrypoint string - Target string - TrimSpace bool - Files map[string]string -} - -func (fct fileContentTest) name(file string) string { - return fmt.Sprintf("target=%q,file=%q", fct.Target, file) -} - -func (fct fileContentTest) Run(t *testing.T) { - t.Helper() - - for f := range fct.Files { - _ = os.Remove(filepathext.SmartJoin(fct.Dir, f)) - } - - e := task.NewExecutor( - task.WithDir(fct.Dir), - task.WithTempDir(task.TempDir{ - Remote: filepathext.SmartJoin(fct.Dir, ".task"), - Fingerprint: filepathext.SmartJoin(fct.Dir, ".task"), - }), - task.WithEntrypoint(fct.Entrypoint), - task.WithStdout(io.Discard), - task.WithStderr(io.Discard), - ) - - require.NoError(t, e.Setup(), "e.Setup()") - require.NoError(t, e.Run(t.Context(), &task.Call{Task: fct.Target}), "e.Run(target)") - for name, expectContent := range fct.Files { - t.Run(fct.name(name), func(t *testing.T) { - path := filepathext.SmartJoin(e.Dir, name) - b, err := os.ReadFile(path) - require.NoError(t, err, "Error reading file") - s := string(b) - if fct.TrimSpace { - s = strings.TrimSpace(s) - } - assert.Equal(t, expectContent, s, "unexpected file content in %s", path) - }) - } -} - -func TestGenerates(t *testing.T) { - t.Parallel() - - const dir = "testdata/generates" - - const ( - srcTask = "sub/src.txt" - relTask = "rel.txt" - absTask = "abs.txt" - fileWithSpaces = "my text file.txt" - ) - - srcFile := filepathext.SmartJoin(dir, srcTask) - - for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} { - path := filepathext.SmartJoin(dir, task) - _ = os.Remove(path) - if _, err := os.Stat(path); err == nil { - t.Errorf("File should not exist: %v", err) - } - } - - buff := bytes.NewBuffer(nil) - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(buff), - task.WithStderr(buff), - ) - require.NoError(t, e.Setup()) - - for _, theTask := range []string{relTask, absTask, fileWithSpaces} { - destFile := filepathext.SmartJoin(dir, theTask) - upToDate := fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) + - fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask) - - // Run task for the first time. - require.NoError(t, e.Run(t.Context(), &task.Call{Task: theTask})) - - if _, err := os.Stat(srcFile); err != nil { - t.Errorf("File should exist: %v", err) - } - if _, err := os.Stat(destFile); err != nil { - t.Errorf("File should exist: %v", err) - } - // Ensure task was not incorrectly found to be up-to-date on first run. - if buff.String() == upToDate { - t.Errorf("Wrong output message: %s", buff.String()) - } - buff.Reset() - - // Re-run task to ensure it's now found to be up-to-date. - require.NoError(t, e.Run(t.Context(), &task.Call{Task: theTask})) - if buff.String() != upToDate { - t.Errorf("Wrong output message: %s", buff.String()) - } - buff.Reset() - } -} - -func TestStatusChecksum(t *testing.T) { // nolint:paralleltest // cannot run in parallel - const dir = "testdata/checksum" - - tests := []struct { - files []string - task string - }{ - {[]string{"generated.txt", ".task/checksum/build"}, "build"}, - {[]string{"generated-wildcard.txt", ".task/checksum/build-wildcard"}, "build-wildcard"}, - {[]string{"generated.txt", ".task/checksum/build-with-status"}, "build-with-status"}, - } - - for _, test := range tests { // nolint:paralleltest // cannot run in parallel - t.Run(test.task, func(t *testing.T) { - for _, f := range test.files { - _ = os.Remove(filepathext.SmartJoin(dir, f)) - - _, err := os.Stat(filepathext.SmartJoin(dir, f)) - require.Error(t, err) - } - - var buff bytes.Buffer - tempDir := task.TempDir{ - Remote: filepathext.SmartJoin(dir, ".task"), - Fingerprint: filepathext.SmartJoin(dir, ".task"), - } - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithTempDir(tempDir), - ) - require.NoError(t, e.Setup()) - - require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.task})) - for _, f := range test.files { - _, err := os.Stat(filepathext.SmartJoin(dir, f)) - require.NoError(t, err) - } - - // Capture the modification time, so we can ensure the checksum file - // is not regenerated when the hash hasn't changed. - s, err := os.Stat(filepathext.SmartJoin(tempDir.Fingerprint, "checksum/"+test.task)) - require.NoError(t, err) - time := s.ModTime() - - buff.Reset() - require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.task})) - assert.Equal(t, `task: Task "`+test.task+`" is up to date`+"\n", buff.String()) - - s, err = os.Stat(filepathext.SmartJoin(tempDir.Fingerprint, "checksum/"+test.task)) - require.NoError(t, err) - assert.Equal(t, time, s.ModTime()) - }) - } -} - -// TestStatusTimestamp is a regression test for https://github.com/go-task/task/issues/1230. -// When using method: timestamp, deleting a generated file should cause the task to re-run, -// not be skipped because the timestamp file is still present. -func TestStatusTimestamp(t *testing.T) { // nolint:paralleltest // cannot run in parallel - const dir = "testdata/timestamp" - - generatedFile := filepathext.SmartJoin(dir, "generated.txt") - tempDir := task.TempDir{ - Remote: filepathext.SmartJoin(dir, ".task"), - Fingerprint: filepathext.SmartJoin(dir, ".task"), - } - - // Clean up any state from previous runs. - _ = os.Remove(generatedFile) - _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task")) - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithTempDir(tempDir), - ) - require.NoError(t, e.Setup()) - - // First run: task should execute and create generated.txt. - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"})) - _, err := os.Stat(generatedFile) - require.NoError(t, err, "generated.txt should exist after first run") - buff.Reset() - - // Second run: task should be up to date. - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"})) - assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String()) - buff.Reset() - - // Delete the generated file (simulate a clean), but leave the timestamp file. - require.NoError(t, os.Remove(generatedFile)) - _, err = os.Stat(generatedFile) - require.Error(t, err, "generated.txt should be gone") - - // Third run: task MUST re-run because generated.txt is missing. - // This is the regression: previously the task was incorrectly skipped. - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"})) - assert.NotContains(t, buff.String(), "is up to date", "task should re-run when generated file is missing") - _, err = os.Stat(generatedFile) - require.NoError(t, err, "generated.txt should be recreated after third run") -} - -// TestStatusChecksumMissingGenerated is a regression test for https://github.com/go-task/task/issues/1230. -// When using method: checksum, deleting a generated file should cause the task to re-run, -// not be skipped because the checksum file still matches. -func TestStatusChecksumMissingGenerated(t *testing.T) { // nolint:paralleltest // cannot run in parallel - const dir = "testdata/checksum" - - generatedFile := filepathext.SmartJoin(dir, "generated.txt") - tempDir := task.TempDir{ - Remote: filepathext.SmartJoin(dir, ".task"), - Fingerprint: filepathext.SmartJoin(dir, ".task"), - } - - // Clean up any state from previous runs. - _ = os.Remove(generatedFile) - _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task")) - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithTempDir(tempDir), - ) - require.NoError(t, e.Setup()) - - // First run: task should execute and create generated.txt. - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"})) - _, err := os.Stat(generatedFile) - require.NoError(t, err, "generated.txt should exist after first run") - buff.Reset() - - // Second run: task should be up to date. - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"})) - assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String()) - buff.Reset() - - // Delete the generated file (simulate a clean), but leave the checksum file. - require.NoError(t, os.Remove(generatedFile)) - _, err = os.Stat(generatedFile) - require.Error(t, err, "generated.txt should be gone") - - // Third run: task MUST re-run because generated.txt is missing. - // This is the regression: previously the task was incorrectly skipped. - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"})) - assert.NotContains(t, buff.String(), "is up to date", "task should re-run when generated file is missing") - _, err = os.Stat(generatedFile) - require.NoError(t, err, "generated.txt should be recreated after third run") -} - -// The injected fingerprint variable follows the method the up-to-date check -// uses, including when that method comes from the Taskfile level. -func TestFingerprintVarMethod(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - dir string - executorOpts []task.ExecutorOption - wantErr string - assertOutput func(t *testing.T, output string) - }{ - { - name: "TIMESTAMP is injected when the method is inherited from the Taskfile", - dir: "testdata/method_taskfile_timestamp", - assertOutput: func(t *testing.T, output string) { - t.Helper() - // An unresolved variable renders as an empty string, so this - // has to match an actual timestamp, not just the prefix. - assert.Regexp(t, `ts=\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}`, output) - }, - }, - { - name: "no variable is injected when the effective method is none", - dir: "testdata/method_taskfile_none", - assertOutput: func(t *testing.T, output string) { - t.Helper() - assert.Contains(t, output, "cs=\n") - }, - }, - { - name: "an invalid method doesn't fail a run that skips fingerprinting", - dir: "testdata/method_invalid", - executorOpts: []task.ExecutorOption{task.WithForce(true)}, - assertOutput: func(t *testing.T, output string) { - t.Helper() - assert.Contains(t, output, "cs=[]\n") - }, - }, - { - name: "an invalid method is still reported by the up-to-date check", - dir: "testdata/method_invalid", - wantErr: `task: invalid method "checksums"`, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - _ = os.RemoveAll(filepathext.SmartJoin(tt.dir, ".task")) - - var buff bytes.Buffer - opts := append([]task.ExecutorOption{ - task.WithDir(tt.dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithTempDir(task.TempDir{ - Remote: filepathext.SmartJoin(tt.dir, ".task"), - Fingerprint: filepathext.SmartJoin(tt.dir, ".task"), - }), - }, tt.executorOpts...) - e := task.NewExecutor(opts...) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "build"}) - if tt.wantErr != "" { - require.ErrorContains(t, err, tt.wantErr) - return - } - require.NoError(t, err) - tt.assertOutput(t, buff.String()) - }) - } -} - -func writeFile(t *testing.T, dir, name, content string) { - t.Helper() - require.NoError(t, os.WriteFile(filepathext.SmartJoin(dir, name), []byte(content), 0o644)) -} - -// gitignoreStep writes a set of files then runs the task once, capturing its -// output as a golden fixture named run. -type gitignoreStep struct { - write map[string]string - run string -} - -// gitignoreSeq drives a checksum task through a sequence of runs against a -// fixture dir. create seeds runtime files (removed on cleanup); restore resets -// tracked files to their committed content on cleanup; artifacts are -// task-produced files to delete on cleanup. -type gitignoreSeq struct { - dir string - task string - create map[string]string - restore map[string]string - artifacts []string - steps []gitignoreStep -} - -func (s gitignoreSeq) run(t *testing.T) { - t.Helper() - cleanup := func() { - // The fixture manages its own .git marker so that gitignore filtering - // resolves a repo root regardless of the build source: an in-tree - // checkout would otherwise inherit the go-task .git, but a GitHub - // source tarball has none, which would silently disable filtering and - // break the golden fixtures. - _ = os.RemoveAll(filepathext.SmartJoin(s.dir, ".git")) - _ = os.RemoveAll(filepathext.SmartJoin(s.dir, ".task")) - for name := range s.create { - _ = os.Remove(filepathext.SmartJoin(s.dir, name)) - } - for _, name := range s.artifacts { - _ = os.Remove(filepathext.SmartJoin(s.dir, name)) - } - for name, content := range s.restore { - writeFile(t, s.dir, name, content) - } - } - cleanup() - t.Cleanup(cleanup) - require.NoError(t, os.MkdirAll(filepathext.SmartJoin(s.dir, ".git"), 0o755)) - for name, content := range s.create { - writeFile(t, s.dir, name, content) - } - for _, step := range s.steps { - for name, content := range step.write { - writeFile(t, s.dir, name, content) - } - NewExecutorTest(t, - WithName(step.run), - WithExecutorOptions(task.WithDir(s.dir)), - WithTask(s.task), - ) - } -} - -func TestGitignoreChecksum(t *testing.T) { //nolint:paralleltest // shares testdata/gitignore and mutates fixture files - gitignoreSeq{ - dir: "testdata/gitignore", - task: "build", - create: map[string]string{"ignored.txt": "ignored\n"}, - restore: map[string]string{"source.txt": "source content\n"}, - artifacts: []string{"generated.txt"}, - steps: []gitignoreStep{ - {run: "first run"}, - {run: "up to date"}, - {run: "ignored file modified", write: map[string]string{"ignored.txt": "ignored modified\n"}}, - {run: "source file modified", write: map[string]string{"source.txt": "source modified\n"}}, - }, - }.run(t) -} - -// TestGitignoreNegation checks that a `!pattern` in a nested .gitignore -// re-includes a file excluded by a parent .gitignore. -func TestGitignoreNegation(t *testing.T) { //nolint:paralleltest // mutates fixture files - gitignoreSeq{ - dir: "testdata/gitignore_negation", - task: "build", - create: map[string]string{"sub/debug.log": "debug\n", "sub/other.log": "other\n"}, - steps: []gitignoreStep{ - {run: "first run"}, - {run: "up to date"}, - {run: "ignored file modified", write: map[string]string{"sub/other.log": "other modified\n"}}, - {run: "reincluded file modified", write: map[string]string{"sub/debug.log": "debug modified\n"}}, - }, - }.run(t) -} - -// TestGitignoreNested checks that a .gitignore in a subdirectory below the task -// dir is honored when its files are reached by a deep glob. -func TestGitignoreNested(t *testing.T) { //nolint:paralleltest // mutates fixture files - gitignoreSeq{ - dir: "testdata/gitignore_nested", - task: "build", - create: map[string]string{"sub/secret.dat": "secret\n"}, - restore: map[string]string{"sub/keep.txt": "keep\n"}, - steps: []gitignoreStep{ - {run: "first run"}, - {run: "up to date"}, - {run: "ignored file modified", write: map[string]string{"sub/secret.dat": "secret modified\n"}}, - {run: "source file modified", write: map[string]string{"sub/keep.txt": "keep modified\n"}}, - }, - }.run(t) -} - -// TestGitignoreIncluded checks that a top-level use_gitignore in an included -// Taskfile is propagated onto its tasks during merge. -func TestGitignoreIncluded(t *testing.T) { //nolint:paralleltest // mutates fixture files - gitignoreSeq{ - dir: "testdata/gitignore_included", - task: "included:build", - create: map[string]string{"ignored.txt": "ignored\n"}, - steps: []gitignoreStep{ - {run: "first run"}, - {run: "up to date"}, - {run: "ignored file modified", write: map[string]string{"ignored.txt": "ignored modified\n"}}, - }, - }.run(t) -} - -// TestGitignoreIncludedOverride checks that an explicit use_gitignore: false in -// an included Taskfile is preserved even when the root Taskfile sets it to true. -func TestGitignoreIncludedOverride(t *testing.T) { //nolint:paralleltest // mutates fixture files - gitignoreSeq{ - dir: "testdata/gitignore_included_override", - task: "included:build", - create: map[string]string{"ignored.txt": "ignored\n"}, - steps: []gitignoreStep{ - {run: "first run"}, - {run: "up to date"}, - {run: "ignored file modified", write: map[string]string{"ignored.txt": "ignored modified\n"}}, - }, - }.run(t) -} - -func TestGitignoreTaskListFallback(t *testing.T) { //nolint:paralleltest // shares testdata/gitignore with TestGitignoreChecksum - const dir = "testdata/gitignore" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - listed, err := e.CompiledTaskForTaskList(&task.Call{Task: "build"}) - require.NoError(t, err) - assert.True(t, listed.ShouldUseGitignore(), - "task list should reflect the global use_gitignore fallback") - - // "build-no-use_gitignore" explicitly disables it. - listedOff, err := e.CompiledTaskForTaskList(&task.Call{Task: "build-no-use_gitignore"}) - require.NoError(t, err) - assert.False(t, listedOff.ShouldUseGitignore(), - "explicit use_gitignore: false must be preserved in the list path") -} - -func TestStatusVariables(t *testing.T) { - t.Parallel() - - const dir = "testdata/status_vars" - - _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task")) - _ = os.Remove(filepathext.SmartJoin(dir, "generated.txt")) - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithTempDir(task.TempDir{ - Remote: filepathext.SmartJoin(dir, ".task"), - Fingerprint: filepathext.SmartJoin(dir, ".task"), - }), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(false), - task.WithVerbose(true), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-checksum"})) - - assert.Contains(t, buff.String(), "3e464c4b03f4b65d740e1e130d4d108a") - - buff.Reset() - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-ts"})) - - inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt")) - require.NoError(t, err) - ts := fmt.Sprintf("%d", inf.ModTime().Unix()) - tf := inf.ModTime().String() - - assert.Contains(t, buff.String(), ts) - assert.Contains(t, buff.String(), tf) -} - -func TestCmdsVariables(t *testing.T) { - t.Parallel() - - const dir = "testdata/cmds_vars" - - _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task")) - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithTempDir(task.TempDir{ - Remote: filepathext.SmartJoin(dir, ".task"), - Fingerprint: filepathext.SmartJoin(dir, ".task"), - }), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(false), - task.WithVerbose(true), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-checksum"})) - - assert.Contains(t, buff.String(), "3e464c4b03f4b65d740e1e130d4d108a") - - buff.Reset() - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-ts"})) - inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt")) - require.NoError(t, err) - ts := fmt.Sprintf("%d", inf.ModTime().Unix()) - tf := inf.ModTime().String() - - assert.Contains(t, buff.String(), ts) - assert.Contains(t, buff.String(), tf) -} - -func TestCyclicDep(t *testing.T) { - t.Parallel() - - const dir = "testdata/cyclic" - - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(io.Discard), - task.WithStderr(io.Discard), - ) - require.NoError(t, e.Setup()) - err := e.Run(t.Context(), &task.Call{Task: "task-1"}) - var taskCalledTooManyTimesError *errors.TaskCalledTooManyTimesError - assert.ErrorAs(t, err, &taskCalledTooManyTimesError) -} - -func TestTaskVersion(t *testing.T) { - t.Parallel() - - tests := []struct { - Dir string - Version *semver.Version - wantErr bool - }{ - {"testdata/version/v1", semver.MustParse("1"), true}, - {"testdata/version/v2", semver.MustParse("2"), true}, - {"testdata/version/v3", semver.MustParse("3"), false}, - } - - for _, test := range tests { - t.Run(test.Dir, func(t *testing.T) { - t.Parallel() - - e := task.NewExecutor( - task.WithDir(test.Dir), - task.WithStdout(io.Discard), - task.WithStderr(io.Discard), - task.WithVersionCheck(true), - ) - err := e.Setup() - if test.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - assert.Equal(t, test.Version, e.Taskfile.Version) - assert.Equal(t, 2, e.Taskfile.Tasks.Len()) - }) - } -} - -func TestTaskIgnoreErrors(t *testing.T) { - t.Parallel() - - const dir = "testdata/ignore_errors" - - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(io.Discard), - task.WithStderr(io.Discard), - ) - require.NoError(t, e.Setup()) - - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task-should-pass"})) - require.Error(t, e.Run(t.Context(), &task.Call{Task: "task-should-fail"})) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "cmd-should-pass"})) - require.Error(t, e.Run(t.Context(), &task.Call{Task: "cmd-should-fail"})) -} - -func TestIgnoreErrorsOnTimeout(t *testing.T) { - t.Parallel() - - const dir = "testdata/ignore_errors" - tests := []struct { - name string - task string - expectError bool - }{ - {name: "ignored at task level", task: "task-timeout-should-pass"}, - {name: "ignored at command level", task: "cmd-timeout-should-pass"}, - {name: "not ignored", task: "cmd-timeout-should-fail", expectError: true}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: test.task}) - if test.expectError { - require.Error(t, err) - assert.NotContains(t, buff.String(), "reached the end") - return - } - require.NoError(t, err) - assert.Contains(t, buff.String(), "reached the end") - }) - } -} - -func TestExpand(t *testing.T) { - t.Parallel() - - const dir = "testdata/expand" - - home, err := os.UserHomeDir() - if err != nil { - t.Errorf("Couldn't get $HOME: %v", err) - } - var buff bytes.Buffer - - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "pwd"})) - assert.Equal(t, home, strings.TrimSpace(buff.String())) -} - -func TestDry(t *testing.T) { - t.Parallel() - - const dir = "testdata/dry" - - file := filepathext.SmartJoin(dir, "file.txt") - _ = os.Remove(file) - - var buff bytes.Buffer - - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithDry(true), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"})) - - assert.Equal(t, "task: [build] touch file.txt", strings.TrimSpace(buff.String())) - if _, err := os.Stat(file); err == nil { - t.Errorf("File should not exist %s", file) - } -} - -// TestDryChecksum tests if the checksum file is not being written to disk -// if the dry mode is enabled. -func TestDryChecksum(t *testing.T) { - t.Parallel() - - const dir = "testdata/dry_checksum" - - checksumFile := filepathext.SmartJoin(dir, ".task/checksum/default") - _ = os.Remove(checksumFile) - - e := task.NewExecutor( - task.WithDir(dir), - task.WithTempDir(task.TempDir{ - Remote: filepathext.SmartJoin(dir, ".task"), - Fingerprint: filepathext.SmartJoin(dir, ".task"), - }), - task.WithStdout(io.Discard), - task.WithStderr(io.Discard), - task.WithDry(true), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"})) - - _, err := os.Stat(checksumFile) - require.Error(t, err, "checksum file should not exist") - - e.Dry = false - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"})) - _, err = os.Stat(checksumFile) - require.NoError(t, err, "checksum file should exist") -} - -func TestIncludes(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/includes", - Target: "default", - TrimSpace: true, - Files: map[string]string{ - "main.txt": "main", - "included_directory.txt": "included_directory", - "included_directory_without_dir.txt": "included_directory_without_dir", - "included_taskfile_without_dir.txt": "included_taskfile_without_dir", - "./module2/included_directory_with_dir.txt": "included_directory_with_dir", - "./module2/included_taskfile_with_dir.txt": "included_taskfile_with_dir", - "os_include.txt": "os", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestIncludesMultiLevel(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/includes_multi_level", - Target: "default", - TrimSpace: true, - Files: map[string]string{ - "called_one.txt": "one", - "called_two.txt": "two", - "called_three.txt": "three", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestIncludesRemote(t *testing.T) { - dir := "testdata/includes_remote" - os.RemoveAll(filepath.Join(dir, ".task", "remote")) - - srv := httptest.NewServer(http.FileServer(http.Dir(dir))) - defer srv.Close() - - tcs := []struct { - firstRemote string - secondRemote string - }{ - { - firstRemote: srv.URL + "/first/Taskfile.yml", - secondRemote: srv.URL + "/first/second/Taskfile.yml", - }, - { - firstRemote: srv.URL + "/first/Taskfile.yml", - secondRemote: "./second/Taskfile.yml", - }, - { - firstRemote: srv.URL + "/first/", - secondRemote: srv.URL + "/first/second/", - }, - } - - taskCalls := []*task.Call{ - {Task: "first:write-file"}, - {Task: "first:second:write-file"}, - } - - for i, tc := range tcs { - t.Run(fmt.Sprint(i), func(t *testing.T) { - t.Setenv("FIRST_REMOTE_URL", tc.firstRemote) - t.Setenv("SECOND_REMOTE_URL", tc.secondRemote) - - var buff SyncBuffer - - // Extract host from server URL for trust testing - parsedURL, err := url.Parse(srv.URL) - require.NoError(t, err) - trustedHost := parsedURL.Host - - executors := []struct { - name string - executor *task.Executor - }{ - { - name: "online, always download", - executor: task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithTimeout(time.Minute), - task.WithInsecure(true), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithVerbose(true), - - // Without caching - task.WithAssumeYes(true), - task.WithDownload(true), - ), - }, - { - name: "offline, use cache", - executor: task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithTimeout(time.Minute), - task.WithInsecure(true), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithVerbose(true), - - // With caching - task.WithAssumeYes(false), - task.WithDownload(false), - task.WithOffline(true), - ), - }, - { - name: "with trusted hosts, no prompts", - executor: task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithTimeout(time.Minute), - task.WithInsecure(true), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithVerbose(true), - - // With trusted hosts - task.WithTrustedHosts([]string{trustedHost}), - task.WithDownload(true), - ), - }, - } - - for _, e := range executors { - t.Run(e.name, func(t *testing.T) { - require.NoError(t, e.executor.Setup()) - - for k, taskCall := range taskCalls { - t.Run(taskCall.Task, func(t *testing.T) { - expectedContent := fmt.Sprint(rand.Int64()) //nolint:gosec - t.Setenv("CONTENT", expectedContent) - - outputFile := fmt.Sprintf("%d.%d.txt", i, k) - t.Setenv("OUTPUT_FILE", outputFile) - - path := filepath.Join(dir, outputFile) - require.NoError(t, os.RemoveAll(path)) - - require.NoError(t, e.executor.Run(t.Context(), taskCall)) - - actualContent, err := os.ReadFile(path) - require.NoError(t, err) - assert.Equal(t, expectedContent, strings.TrimSpace(string(actualContent))) - }) - } - }) - } - - t.Log("\noutput:\n", buff.buf.String()) - }) - } -} - -func TestIncludeCycle(t *testing.T) { - t.Parallel() - - const dir = "testdata/includes_cycle" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - - err := e.Setup() - require.Error(t, err) - assert.Contains(t, err.Error(), "task: include cycle detected between") -} - -func TestIncludesIncorrect(t *testing.T) { - t.Parallel() - - const dir = "testdata/includes_incorrect" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - - err := e.Setup() - require.Error(t, err) - assert.Contains(t, err.Error(), "Failed to parse testdata/includes_incorrect/incomplete.yml:", err.Error()) -} - -func TestIncludesMissingTaskfile(t *testing.T) { - t.Parallel() - - const dir = "testdata/includes_missing_taskfile" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - - err := e.Setup() - require.Error(t, err) - assert.Contains(t, err.Error(), "include must specify taskfile or dir") - assert.NotContains(t, err.Error(), "include cycle detected") -} - -func TestIncludesEmptyMain(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/includes_empty", - Target: "included:default", - TrimSpace: true, - Files: map[string]string{ - "file.txt": "default", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestIncludesHttp(t *testing.T) { - dir, err := filepath.Abs("testdata/includes_http") - require.NoError(t, err) - - srv := httptest.NewServer(http.FileServer(http.Dir(dir))) - defer srv.Close() - - t.Cleanup(func() { - // This test fills the .task/remote directory with cache entries because the include URL - // is different on every test due to the dynamic nature of the TCP port in srv.URL - if err := os.RemoveAll(filepath.Join(dir, ".task")); err != nil { - t.Logf("error cleaning up: %s", err) - } - }) - - taskfiles, err := fs.Glob(os.DirFS(dir), "root-taskfile-*.yml") - require.NoError(t, err) - - remotes := []struct { - name string - root string - }{ - { - name: "local", - root: ".", - }, - { - name: "http-remote", - root: srv.URL, - }, - } - - for _, taskfile := range taskfiles { - t.Run(taskfile, func(t *testing.T) { - for _, remote := range remotes { - t.Run(remote.name, func(t *testing.T) { - t.Setenv("INCLUDE_ROOT", remote.root) - entrypoint := filepath.Join(dir, taskfile) - - var buff SyncBuffer - e := task.NewExecutor( - task.WithEntrypoint(entrypoint), - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithInsecure(true), - task.WithDownload(true), - task.WithAssumeYes(true), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithVerbose(true), - task.WithTimeout(time.Minute), - ) - require.NoError(t, e.Setup()) - defer func() { t.Log("output:", buff.buf.String()) }() - - tcs := []struct { - name, dir string - }{ - { - name: "second-with-dir-1:third-with-dir-1:default", - dir: filepath.Join(dir, "dir-1"), - }, - { - name: "second-with-dir-1:third-with-dir-2:default", - dir: filepath.Join(dir, "dir-2"), - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - task, err := e.CompiledTask(&task.Call{Task: tc.name}) - require.NoError(t, err) - assert.Equal(t, tc.dir, task.Dir) - }) - } - }) - } - }) - } -} - -func TestIncludesDependencies(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/includes_deps", - Target: "default", - TrimSpace: true, - Files: map[string]string{ - "default.txt": "default", - "called_dep.txt": "called_dep", - "called_task.txt": "called_task", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestIncludesCallingRoot(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/includes_call_root_task", - Target: "included:call-root", - TrimSpace: true, - Files: map[string]string{ - "root_task.txt": "root task", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestIncludesOptional(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/includes_optional", - Target: "default", - TrimSpace: true, - Files: map[string]string{ - "called_dep.txt": "called_dep", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestIncludesOptionalImplicitFalse(t *testing.T) { - t.Parallel() - - const dir = "testdata/includes_optional_implicit_false" - wd, _ := os.Getwd() - - message := "task: No Taskfile found at \"%s/%s/TaskfileOptional.yml\"" - expected := fmt.Sprintf(message, filepath.ToSlash(wd), dir) - - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(io.Discard), - task.WithStderr(io.Discard), - ) - - err := e.Setup() - require.Error(t, err) - assert.Equal(t, expected, err.Error()) -} - -func TestIncludesOptionalExplicitFalse(t *testing.T) { - t.Parallel() - - const dir = "testdata/includes_optional_explicit_false" - wd, _ := os.Getwd() - - message := "task: No Taskfile found at \"%s/%s/TaskfileOptional.yml\"" - expected := fmt.Sprintf(message, filepath.ToSlash(wd), dir) - - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(io.Discard), - task.WithStderr(io.Discard), - ) - - err := e.Setup() - require.Error(t, err) - assert.Equal(t, expected, err.Error()) -} - -func TestIncludesFromCustomTaskfile(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Entrypoint: "testdata/includes_yaml/Custom.ext", - Dir: "testdata/includes_yaml", - Target: "default", - TrimSpace: true, - Files: map[string]string{ - "main.txt": "main", - "included_with_yaml_extension.txt": "included_with_yaml_extension", - "included_with_custom_file.txt": "included_with_custom_file", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestIncludesRelativePath(t *testing.T) { - t.Parallel() - - const dir = "testdata/includes_rel_path" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - - require.NoError(t, e.Setup()) - - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "common:pwd"})) - assert.Contains(t, filepath.ToSlash(buff.String()), "testdata/includes_rel_path/common") - - buff.Reset() - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "included:common:pwd"})) - assert.Contains(t, filepath.ToSlash(buff.String()), "testdata/includes_rel_path/common") -} - -func TestIncludesInternal(t *testing.T) { - t.Parallel() - - const dir = "testdata/internal_task" - tests := []struct { - name string - task string - expectedErr bool - expectedOutput string - }{ - {"included internal task via task", "task-1", false, "Hello, World!\n"}, - {"included internal task via dep", "task-2", false, "Hello, World!\n"}, - {"included internal direct", "included:task-3", true, "task: No tasks with description available. Try --list-all to list all tasks\n"}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: test.task}) - if test.expectedErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - assert.Equal(t, test.expectedOutput, buff.String()) - }) - } -} - -func TestIncludesFlatten(t *testing.T) { - t.Parallel() - - const dir = "testdata/includes_flatten" - tests := []struct { - name string - taskfile string - task string - expectedErr bool - expectedOutput string - }{ - {name: "included flatten", taskfile: "Taskfile.yml", task: "gen", expectedOutput: "gen from included\n"}, - {name: "included flatten with default", taskfile: "Taskfile.yml", task: "default", expectedOutput: "default from included flatten\n"}, - {name: "included flatten can call entrypoint tasks", taskfile: "Taskfile.yml", task: "from_entrypoint", expectedOutput: "from entrypoint\n"}, - {name: "included flatten with deps", taskfile: "Taskfile.yml", task: "with_deps", expectedOutput: "gen from included\nwith_deps from included\n"}, - {name: "included flatten nested", taskfile: "Taskfile.yml", task: "from_nested", expectedOutput: "from nested\n"}, - {name: "included flatten multiple same task", taskfile: "Taskfile.multiple.yml", task: "gen", expectedErr: true, expectedOutput: "task: Found multiple tasks (gen) included by \"included\"\""}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithEntrypoint(dir+"/"+test.taskfile), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - err := e.Setup() - if test.expectedErr { - assert.EqualError(t, err, test.expectedOutput) - } else { - require.NoError(t, err) - _ = e.Run(t.Context(), &task.Call{Task: test.task}) - assert.Equal(t, test.expectedOutput, buff.String()) - } - }) - } -} - -func TestIncludesInterpolation(t *testing.T) { // nolint:paralleltest // cannot run in parallel - const dir = "testdata/includes_interpolation" - tests := []struct { - name string - task string - expectedErr bool - expectedOutput string - }{ - {"include", "include", false, "include\n"}, - {"include_with_env_variable", "include-with-env-variable", false, "include_with_env_variable\n"}, - {"include_with_dir", "include-with-dir", false, "included\n"}, - } - t.Setenv("MODULE", "included") - - for _, test := range tests { // nolint:paralleltest // cannot run in parallel - t.Run(test.name, func(t *testing.T) { - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(filepath.Join(dir, test.name)), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: test.task}) - if test.expectedErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - assert.Equal(t, test.expectedOutput, buff.String()) - }) - } -} - -func TestIncludesWithExclude(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/includes_with_excludes"), - task.WithSilent(true), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "included:bar"}) - require.NoError(t, err) - assert.Equal(t, "bar\n", buff.String()) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "included:foo"}) - require.Error(t, err) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "included:foo:child"}) - require.NoError(t, err) - assert.Equal(t, "foo:child\n", buff.String()) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "included:namespace"}) - require.NoError(t, err) - assert.Equal(t, "namespace\n", buff.String()) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "included:namespace:one"}) - require.Error(t, err) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "included:namespace-other:one"}) - require.NoError(t, err) - assert.Equal(t, "namespace-other:one\n", buff.String()) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "bar"}) - require.Error(t, err) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "foo"}) - require.NoError(t, err) - assert.Equal(t, "foo\n", buff.String()) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "namespace"}) - require.NoError(t, err) - assert.Equal(t, "namespace\n", buff.String()) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "namespace:two"}) - require.Error(t, err) - buff.Reset() - - err = e.Run(t.Context(), &task.Call{Task: "namespace-other:one"}) - require.NoError(t, err) - assert.Equal(t, "namespace-other:one\n", buff.String()) -} - -func TestIncludedTaskfileVarMerging(t *testing.T) { - t.Parallel() - - const dir = "testdata/included_taskfile_var_merging" - tests := []struct { - name string - task string - expectedOutput string - }{ - {"foo", "foo:pwd", "included_taskfile_var_merging/foo\n"}, - {"bar", "bar:pwd", "included_taskfile_var_merging/bar\n"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: test.task}) - require.NoError(t, err) - assert.Contains(t, filepath.ToSlash(buff.String()), test.expectedOutput) - }) - } -} - -func TestInternalTask(t *testing.T) { - t.Parallel() - - const dir = "testdata/internal_task" - tests := []struct { - name string - task string - expectedErr bool - expectedOutput string - }{ - {"internal task via task", "task-1", false, "Hello, World!\n"}, - {"internal task via dep", "task-2", false, "Hello, World!\n"}, - {"internal direct", "task-3", true, ""}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: test.task}) - if test.expectedErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - assert.Equal(t, test.expectedOutput, buff.String()) - }) - } -} - -func TestIncludesShadowedDefault(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/includes_shadowed_default", - Target: "included", - TrimSpace: true, - Files: map[string]string{ - "file.txt": "shadowed", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestIncludesUnshadowedDefault(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/includes_unshadowed_default", - Target: "included", - TrimSpace: true, - Files: map[string]string{ - "file.txt": "included", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestSupportedFileNames(t *testing.T) { - t.Parallel() - - fileNames := []string{ - "Taskfile.yml", - "Taskfile.yaml", - "Taskfile.dist.yml", - "Taskfile.dist.yaml", - } - for _, fileName := range fileNames { - t.Run(fileName, func(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: fmt.Sprintf("testdata/file_names/%s", fileName), - Target: "default", - TrimSpace: true, - Files: map[string]string{ - "output.txt": "hello", - }, - } - tt.Run(t) - }) - } -} - -func TestSummary(t *testing.T) { - t.Parallel() - - const dir = "testdata/summary" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSummary(true), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task-with-summary"}, &task.Call{Task: "other-task-with-summary"})) - - data, err := os.ReadFile(filepathext.SmartJoin(dir, "task-with-summary.txt")) - require.NoError(t, err) - - expectedOutput := string(data) - if runtime.GOOS == "windows" { - expectedOutput = strings.ReplaceAll(expectedOutput, "\r\n", "\n") - } - - assert.Equal(t, expectedOutput, buff.String()) -} - -func TestWhenNoDirAttributeItRunsInSameDirAsTaskfile(t *testing.T) { - t.Parallel() - - const expected = "dir" - const dir = "testdata/" + expected - var out bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&out), - task.WithStderr(&out), - ) - - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "whereami"})) - - // got should be the "dir" part of "testdata/dir" - // Normalize path separators for cross-platform compatibility (Windows uses backslashes) - normalized := normalizePathSeparators(out.String()) - got := strings.TrimSuffix(filepath.Base(normalized), "\n") - assert.Equal(t, expected, got, "Mismatch in the working directory") -} - -func TestWhenDirAttributeAndDirExistsItRunsInThatDir(t *testing.T) { - t.Parallel() - - const expected = "exists" - const dir = "testdata/dir/explicit_exists" - var out bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&out), - task.WithStderr(&out), - ) - - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "whereami"})) - - // Normalize path separators for cross-platform compatibility (Windows uses backslashes) - normalized := normalizePathSeparators(out.String()) - got := strings.TrimSuffix(filepath.Base(normalized), "\n") - assert.Equal(t, expected, got, "Mismatch in the working directory") -} - -func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) { - t.Parallel() - - const expected = "createme" - const dir = "testdata/dir/explicit_doesnt_exist/" - const toBeCreated = dir + expected - const target = "whereami" - var out bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&out), - task.WithStderr(&out), - ) - - // Ensure that the directory to be created doesn't actually exist. - _ = os.RemoveAll(toBeCreated) - if _, err := os.Stat(toBeCreated); err == nil { - t.Errorf("Directory should not exist: %v", err) - } - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: target})) - - // Normalize path separators for cross-platform compatibility (Windows uses backslashes) - normalized := normalizePathSeparators(out.String()) - got := strings.TrimSuffix(filepath.Base(normalized), "\n") - assert.Equal(t, expected, got, "Mismatch in the working directory") - - // Clean-up after ourselves only if no error. - _ = os.RemoveAll(toBeCreated) -} - -func TestDynamicVariablesRunOnTheNewCreatedDir(t *testing.T) { - t.Parallel() - - const expected = "created" - const dir = "testdata/dir/dynamic_var_on_created_dir/" - const toBeCreated = dir + expected - const target = "default" - var out bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&out), - task.WithStderr(&out), - ) - - // Ensure that the directory to be created doesn't actually exist. - _ = os.RemoveAll(toBeCreated) - if _, err := os.Stat(toBeCreated); err == nil { - t.Errorf("Directory should not exist: %v", err) - } - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: target})) - - // Normalize path separators for cross-platform compatibility (Windows uses backslashes) - // Take only the first line as Windows may output additional debug info - normalized := normalizePathSeparators(out.String()) - firstLine, _, _ := strings.Cut(normalized, "\n") - got := filepath.Base(firstLine) - assert.Equal(t, expected, got, "Mismatch in the working directory") - - // Clean-up after ourselves only if no error. - _ = os.RemoveAll(toBeCreated) -} - -func TestDynamicVariablesShouldRunOnTheTaskDir(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/dir/dynamic_var", - Target: "default", - TrimSpace: false, - Files: map[string]string{ - "subdirectory/from_root_taskfile.txt": "subdirectory\n", - "subdirectory/from_included_taskfile.txt": "subdirectory\n", - "subdirectory/from_included_taskfile_task.txt": "subdirectory\n", - "subdirectory/from_interpolated_dir.txt": "subdirectory\n", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestDisplaysErrorOnVersion1Schema(t *testing.T) { - t.Parallel() - - e := task.NewExecutor( - task.WithDir("testdata/version/v1"), - task.WithStdout(io.Discard), - task.WithStderr(io.Discard), - task.WithVersionCheck(true), - ) - err := e.Setup() - require.Error(t, err) - assert.Regexp(t, regexp.MustCompile(`task: Invalid schema version in Taskfile \".*testdata\/version\/v1\/Taskfile\.yml\":\nSchema version \(1\.0\.0\) no longer supported\. Please use v3 or above`), err.Error()) -} - -func TestDisplaysErrorOnVersion2Schema(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/version/v2"), - task.WithStdout(io.Discard), - task.WithStderr(&buff), - task.WithVersionCheck(true), - ) - err := e.Setup() - require.Error(t, err) - assert.Regexp(t, regexp.MustCompile(`task: Invalid schema version in Taskfile \".*testdata\/version\/v2\/Taskfile\.yml\":\nSchema version \(2\.0\.0\) no longer supported\. Please use v3 or above`), err.Error()) -} - -func TestShortTaskNotation(t *testing.T) { - t.Parallel() - - const dir = "testdata/short_task_notation" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"})) - assert.Equal(t, "string-slice-1\nstring-slice-2\nstring\n", buff.String()) -} - -func TestDotenvShouldIncludeAllEnvFiles(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/dotenv/default", - Target: "default", - TrimSpace: false, - Files: map[string]string{ - "include.txt": "INCLUDE1='from_include1' INCLUDE2='from_include2'\n", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestDotenvShouldErrorWhenIncludingDependantDotenvs(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/dotenv/error_included_envs"), - task.WithSummary(true), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - - err := e.Setup() - require.Error(t, err) - assert.Contains(t, err.Error(), "move the dotenv") -} - -func TestDotenvShouldAllowMissingEnv(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/dotenv/missing_env", - Target: "default", - TrimSpace: false, - Files: map[string]string{ - "include.txt": "INCLUDE1='' INCLUDE2=''\n", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestDotenvHasLocalEnvInPath(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/dotenv/local_env_in_path", - Target: "default", - TrimSpace: false, - Files: map[string]string{ - "var.txt": "VAR='var_in_dot_env_1'\n", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestDotenvHasLocalVarInPath(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/dotenv/local_var_in_path", - Target: "default", - TrimSpace: false, - Files: map[string]string{ - "var.txt": "VAR='var_in_dot_env_3'\n", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestDotenvHasEnvVarInPath(t *testing.T) { // nolint:paralleltest // cannot run in parallel - t.Setenv("ENV_VAR", "testing") - - tt := fileContentTest{ - Dir: "testdata/dotenv/env_var_in_path", - Target: "default", - TrimSpace: false, - Files: map[string]string{ - "var.txt": "VAR='var_in_dot_env_2'\n", - }, - } - tt.Run(t) -} - -func TestTaskDotenvParseErrorMessage(t *testing.T) { - t.Parallel() - - e := task.NewExecutor( - task.WithDir("testdata/dotenv/parse_error"), - ) - - path, _ := filepath.Abs(filepath.Join(e.Dir, ".env-with-error")) - expected := fmt.Sprintf("error reading env file %s:", path) - - err := e.Setup() - require.ErrorContains(t, err, expected) -} - -func TestTaskDotenv(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/dotenv_task/default", - Target: "dotenv", - TrimSpace: true, - Files: map[string]string{ - "dotenv.txt": "foo", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestTaskDotenvFail(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/dotenv_task/default", - Target: "no-dotenv", - TrimSpace: true, - Files: map[string]string{ - "no-dotenv.txt": "global", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestTaskDotenvOverriddenByEnv(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/dotenv_task/default", - Target: "dotenv-overridden-by-env", - TrimSpace: true, - Files: map[string]string{ - "dotenv-overridden-by-env.txt": "overridden", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestTaskDotenvWithVarName(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/dotenv_task/default", - Target: "dotenv-with-var-name", - TrimSpace: true, - Files: map[string]string{ - "dotenv-with-var-name.txt": "foo", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestExitImmediately(t *testing.T) { - t.Parallel() - - const dir = "testdata/exit_immediately" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - - require.Error(t, e.Run(t.Context(), &task.Call{Task: "default"})) - assert.Contains(t, buff.String(), `"this_should_fail": executable file not found in $PATH`) -} - -func TestRunOnlyRunsJobsHashOnce(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/run", - Target: "generate-hash", - Files: map[string]string{ - "hash.txt": "starting 1\n1\n2\n", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestRunOnlyRunsJobsHashOnceWithWildcard(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/run", - Target: "deploy", - Files: map[string]string{ - "wildcard.txt": "Deploy infra\nDeploy js\nDeploy go\n", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestRunOnceSharedDeps(t *testing.T) { - t.Parallel() - - const dir = "testdata/run_once_shared_deps" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithForceAll(true), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"})) - - rx := regexp.MustCompile(`task: \[service-[a,b]:library:build\] echo "build library"`) - matches := rx.FindAllStringSubmatch(buff.String(), -1) - assert.Len(t, matches, 1) - assert.Contains(t, buff.String(), `task: [service-a:build] echo "build a"`) - assert.Contains(t, buff.String(), `task: [service-b:build] echo "build b"`) -} - -func TestRunOnceSharedFailurePropagates(t *testing.T) { - t.Parallel() - - const dir = "testdata/run_once_failure" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "default"}) - require.Error(t, err) - assert.Contains(t, err.Error(), `Failed to run task "shared"`) - assert.NotContains(t, buff.String(), "should not be reached") - // The shared task still ran only once, which is the point of run: once. - assert.Equal(t, 1, strings.Count(buff.String(), "shared ran")) -} - -func TestRunOnceJoinerHonorsItsOwnTimeout(t *testing.T) { - t.Parallel() - - const dir = "testdata/run_once_timeout" - - // The two deps run concurrently, so they need a buffer they can share. - var buff SyncBuffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - start := time.Now() - err := e.Run(t.Context(), &task.Call{Task: "default"}) - require.Error(t, err) - // The joiner used to wait on the shared execution alone, ignoring its own - // timeout for as long as that execution took. - assert.Less(t, time.Since(start), 5*time.Second) - - var timeoutErr *errors.TaskTimeoutError - require.ErrorAs(t, err, &timeoutErr) - assert.Equal(t, "joiner", timeoutErr.TaskName) - assert.NotContains(t, buff.buf.String(), "should not be reached") -} - -func TestRunWhenChanged(t *testing.T) { - t.Parallel() - - const dir = "testdata/run_when_changed" - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithForceAll(true), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "start"})) - expectedOutputOrder := strings.TrimSpace(` -login server=fubar user=fubar -login server=foo user=foo -login server=bar user=bar -`) - assert.Contains(t, buff.String(), expectedOutputOrder) -} - -func TestDeferredCmds(t *testing.T) { - t.Parallel() - - const dir = "testdata/deferred" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - expectedOutputOrder := strings.TrimSpace(` -task: [task-2] echo 'cmd ran' -cmd ran -task: [task-2] exit 1 -task: [task-2] echo 'failing' && exit 2 -failing -echo ran -task-1 ran successfully -task: [task-1] echo 'task-1 ran successfully' -task-1 ran successfully -`) - require.Error(t, e.Run(t.Context(), &task.Call{Task: "task-2"})) - assert.Contains(t, buff.String(), expectedOutputOrder) - buff.Reset() - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "parent"})) - assert.Contains(t, buff.String(), "child task deferred value-from-parent") -} - -func TestDeferredTaskTimeout(t *testing.T) { - t.Parallel() - - const dir = "testdata/deferred" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithVerbose(true), - ) - require.NoError(t, e.Setup()) - - start := time.Now() - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "parent-with-timeout"})) - assert.Less(t, time.Since(start), 500*time.Millisecond) - assert.Contains(t, buff.String(), "parent completed") - assert.NotContains(t, buff.String(), "\ncleanup completed\n") - assert.Contains(t, buff.String(), "ignored error in deferred cmd") -} - -func TestExitCodeZero(t *testing.T) { - t.Parallel() - - const dir = "testdata/exit_code" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "exit-zero"})) - assert.Equal(t, "FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE=", strings.TrimSpace(buff.String())) -} - -func TestExitCodeOne(t *testing.T) { - t.Parallel() - - const dir = "testdata/exit_code" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - require.Error(t, e.Run(t.Context(), &task.Call{Task: "exit-one"})) - assert.Equal(t, "FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE=1", strings.TrimSpace(buff.String())) -} - -func TestExitCodeTimeout(t *testing.T) { - t.Parallel() - - const dir = "testdata/exit_code" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "exit-timeout"}) - require.Error(t, err) - assert.Equal(t, "EXIT_CODE=124", strings.TrimSpace(buff.String())) - - var runErr *errors.TaskRunError - require.ErrorAs(t, err, &runErr) - assert.Equal(t, errors.TimeoutExitCode, runErr.TaskExitCode()) -} - -func TestIgnoreNilElements(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - dir string - }{ - {"nil cmd", "testdata/ignore_nil_elements/cmds"}, - {"nil dep", "testdata/ignore_nil_elements/deps"}, - {"nil include", "testdata/ignore_nil_elements/includes"}, - {"nil precondition", "testdata/ignore_nil_elements/preconditions"}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(test.dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"})) - assert.Equal(t, "string-slice-1\n", buff.String()) - }) - } -} - -func TestOutputGroup(t *testing.T) { - t.Parallel() - - const dir = "testdata/output_group" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - expectedOutputOrder := strings.TrimSpace(` -task: [hello] echo 'Hello!' -::group::hello -Hello! -::endgroup:: -task: [bye] echo 'Bye!' -::group::bye -Bye! -::endgroup:: -`) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "bye"})) - t.Log(buff.String()) - assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder) -} - -func TestOutputGroupErrorOnlySwallowsOutputOnSuccess(t *testing.T) { - t.Parallel() - - const dir = "testdata/output_group_error_only" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "passing"})) - t.Log(buff.String()) - assert.Empty(t, buff.String()) -} - -func TestOutputGroupErrorOnlyShowsOutputOnFailure(t *testing.T) { - t.Parallel() - - const dir = "testdata/output_group_error_only" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - require.Error(t, e.Run(t.Context(), &task.Call{Task: "failing"})) - t.Log(buff.String()) - assert.Contains(t, "failing-output", strings.TrimSpace(buff.String())) - assert.NotContains(t, "passing", strings.TrimSpace(buff.String())) -} - -func TestIncludedVars(t *testing.T) { - t.Parallel() - - const dir = "testdata/include_with_vars" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - expectedOutputOrder := strings.TrimSpace(` -task: [included1:task1] echo "VAR_1 is included1-var1" -VAR_1 is included1-var1 -task: [included1:task1] echo "VAR_2 is included-default-var2" -VAR_2 is included-default-var2 -task: [included2:task1] echo "VAR_1 is included2-var1" -VAR_1 is included2-var1 -task: [included2:task1] echo "VAR_2 is included-default-var2" -VAR_2 is included-default-var2 -task: [included3:task1] echo "VAR_1 is included-default-var1" -VAR_1 is included-default-var1 -task: [included3:task1] echo "VAR_2 is included-default-var2" -VAR_2 is included-default-var2 -`) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task1"})) - t.Log(buff.String()) - assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder) -} - -func TestIncludeWithVarsInInclude(t *testing.T) { - t.Parallel() - - const dir = "testdata/include_with_vars_inside_include" - var buff bytes.Buffer - e := task.Executor{ - Dir: dir, - Stdout: &buff, - Stderr: &buff, - } - require.NoError(t, e.Setup()) -} - -func TestIncludedVarsMultiLevel(t *testing.T) { - t.Parallel() - - const dir = "testdata/include_with_vars_multi_level" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - expectedOutputOrder := strings.TrimSpace(` -task: [lib:greet] echo 'Hello world' -Hello world -task: [foo:lib:greet] echo 'Hello foo' -Hello foo -task: [bar:lib:greet] echo 'Hello bar' -Hello bar -`) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"})) - t.Log(buff.String()) - assert.Equal(t, expectedOutputOrder, strings.TrimSpace(buff.String())) -} - -func TestErrorCode(t *testing.T) { - t.Parallel() - - const dir = "testdata/error_code" - tests := []struct { - name string - task string - expected int - }{ - { - name: "direct task", - task: "direct", - expected: 42, - }, { - name: "indirect task", - task: "indirect", - expected: 42, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: test.task}) - require.Error(t, err) - taskRunErr, ok := err.(*errors.TaskRunError) - assert.True(t, ok, "cannot cast returned error to *task.TaskRunError") - assert.Equal(t, test.expected, taskRunErr.TaskExitCode(), "unexpected exit code from task") - }) - } -} - -func TestCommandTimeout(t *testing.T) { - t.Parallel() - - const dir = "testdata/timeout" - tests := []struct { - name string - task string - expectError bool - errorContains string - }{ - { - name: "timeout exceeded", - task: "timeout-exceeded", - expectError: true, - errorContains: "timeout exceeded", - }, - { - name: "timeout not exceeded", - task: "timeout-not-exceeded", - expectError: false, - }, - { - name: "no timeout", - task: "no-timeout", - expectError: false, - }, - { - name: "multiple commands with timeout", - task: "multiple-cmds-timeout", - expectError: true, - errorContains: "timeout exceeded", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: test.task}) - if test.expectError { - require.Error(t, err) - assert.Contains(t, err.Error(), test.errorContains) - } else { - require.NoError(t, err) - } - }) - } -} - -func TestDepTimeout(t *testing.T) { - t.Parallel() - - const dir = "testdata/dep_timeout" - - t.Run("timeout exceeded", func(t *testing.T) { - t.Parallel() - - var buff SyncBuffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - start := time.Now() - err := e.Run(t.Context(), &task.Call{Task: "timeout-exceeded"}) - require.Error(t, err) - assert.Less(t, time.Since(start), 5*time.Second) - - var timeoutErr *errors.TaskTimeoutError - require.ErrorAs(t, err, &timeoutErr) - assert.Equal(t, "slow", timeoutErr.TaskName) - assert.NotContains(t, buff.buf.String(), "should not be reached") - }) - - t.Run("timeout not exceeded", func(t *testing.T) { - t.Parallel() - - var buff SyncBuffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "timeout-not-exceeded"})) - assert.Contains(t, buff.buf.String(), "reached the end") - }) -} - -func TestCommandTimeoutBoundsIfCondition(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/timeout"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - start := time.Now() - err := e.Run(t.Context(), &task.Call{Task: "slow-if-condition"}) - require.Error(t, err) - assert.Less(t, time.Since(start), 5*time.Second) - - var timeoutErr *errors.TaskTimeoutError - require.ErrorAs(t, err, &timeoutErr) - // A condition that times out fails the command, it does not skip it. - assert.NotContains(t, buff.String(), "condition was met") -} - -func TestCommandTimeoutAttribution(t *testing.T) { - t.Parallel() - - const dir = "testdata/timeout" - tests := []struct { - name string - task string - notContains string - }{ - { - name: "a command declaring no timeout is not blamed for one", - task: "inherited-timeout", - notContains: "(0s)", - }, - { - name: "a command is not blamed for a timeout it never reached", - task: "larger-child-timeout", - notContains: "10m", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(io.Discard), - task.WithStderr(io.Discard), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: test.task}) - require.Error(t, err) - assert.Contains(t, err.Error(), "command timeout exceeded (500ms)") - assert.NotContains(t, err.Error(), test.notContains) - - var timeoutErr *errors.TaskTimeoutError - require.ErrorAs(t, err, &timeoutErr) - assert.Equal(t, test.task, timeoutErr.TaskName) - - // --watch swallows context errors; a timeout must not look like one. - assert.False(t, errors.Is(err, context.DeadlineExceeded)) - }) - } -} - -func TestEvaluateSymlinksInPaths(t *testing.T) { // nolint:paralleltest // cannot run in parallel - const dir = "testdata/evaluate_symlinks_in_paths" - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(false), - ) - tests := []struct { - name string - task string - expected string - }{ - { - name: "default (1)", - task: "default", - expected: "task: [default] echo \"some job\"\nsome job", - }, - { - name: "test-sym (1)", - task: "test-sym", - expected: "task: [test-sym] echo \"shared file source changed\" > src/shared/b", - }, - { - name: "default (2)", - task: "default", - expected: "task: [default] echo \"some job\"\nsome job", - }, - { - name: "default (3)", - task: "default", - expected: `task: Task "default" is up to date`, - }, - { - name: "reset", - task: "reset", - expected: "task: [reset] echo \"shared file source\" > src/shared/b\ntask: [reset] echo \"file source\" > src/a", - }, - } - for _, test := range tests { // nolint:paralleltest // cannot run in parallel - t.Run(test.name, func(t *testing.T) { - require.NoError(t, e.Setup()) - err := e.Run(t.Context(), &task.Call{Task: test.task}) - require.NoError(t, err) - assert.Equal(t, test.expected, strings.TrimSpace(buff.String())) - buff.Reset() - }) - } - err := os.RemoveAll(dir + "/.task") - require.NoError(t, err) -} - -func TestTaskfileWalk(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - dir string - expected string - }{ - { - name: "walk from root directory", - dir: "testdata/taskfile_walk", - expected: "foo\n", - }, { - name: "walk from sub directory", - dir: "testdata/taskfile_walk/foo", - expected: "foo\n", - }, { - name: "walk from sub sub directory", - dir: "testdata/taskfile_walk/foo/bar", - expected: "foo\n", - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir(test.dir), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"})) - assert.Equal(t, test.expected, buff.String()) - }) - } -} - -func TestUserWorkingDirectory(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/user_working_dir"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - wd, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"})) - // Use filepath.ToSlash because USER_WORKING_DIR uses forward slashes on all platforms - assert.Equal(t, fmt.Sprintf("%s\n", filepath.ToSlash(wd)), buff.String()) -} - -func TestUserWorkingDirectoryWithIncluded(t *testing.T) { - t.Parallel() - - wd, err := os.Getwd() - require.NoError(t, err) - - wd = filepath.ToSlash(filepathext.SmartJoin(wd, "testdata/user_working_dir_with_includes/somedir")) - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/user_working_dir_with_includes"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - e.UserWorkingDir = wd - - require.NoError(t, err) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "included:echo"})) - // Normalize path separators for cross-platform compatibility (Windows uses backslashes) - assert.Equal(t, fmt.Sprintf("%s\n", wd), normalizePathSeparators(buff.String())) -} - -func TestPlatforms(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/platforms"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-" + runtime.GOOS})) - assert.Equal(t, fmt.Sprintf("task: [build-%s] echo 'Running task on %s'\nRunning task on %s\n", runtime.GOOS, runtime.GOOS, runtime.GOOS), buff.String()) -} - -func TestPOSIXShellOptsGlobalLevel(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/shopts/global_level"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "pipefail"}) - require.NoError(t, err) - assert.Equal(t, "pipefail\ton\n", buff.String()) -} - -func TestPOSIXShellOptsTaskLevel(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/shopts/task_level"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "pipefail"}) - require.NoError(t, err) - assert.Equal(t, "pipefail\ton\n", buff.String()) -} - -func TestPOSIXShellOptsCommandLevel(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/shopts/command_level"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "pipefail"}) - require.NoError(t, err) - assert.Equal(t, "pipefail\ton\n", buff.String()) -} - -func TestBashShellOptsGlobalLevel(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/shopts/global_level"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "globstar"}) - require.NoError(t, err) - assert.Equal(t, "globstar\ton\n", buff.String()) -} - -func TestBashShellOptsTaskLevel(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/shopts/task_level"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "globstar"}) - require.NoError(t, err) - assert.Equal(t, "globstar\ton\n", buff.String()) -} - -func TestBashShellOptsCommandLevel(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/shopts/command_level"), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "globstar"}) - require.NoError(t, err) - assert.Equal(t, "globstar\ton\n", buff.String()) -} - -func TestSplitArgs(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/split_args"), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - - vars := ast.NewVars() - vars.Set("CLI_ARGS", ast.Var{Value: "foo bar 'foo bar baz'"}) - - err := e.Run(t.Context(), &task.Call{Task: "default", Vars: vars}) - require.NoError(t, err) - assert.Equal(t, "3\n", buff.String()) -} - -func TestAbsPath(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/abs_path"), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - ) - require.NoError(t, e.Setup()) - - err := e.Run(t.Context(), &task.Call{Task: "default"}) - require.NoError(t, err) - - cwd, err := os.Getwd() - require.NoError(t, err) - expected := filepath.Join(cwd, "bar") + "\n" - assert.Equal(t, expected, buff.String()) -} - -func TestSingleCmdDep(t *testing.T) { - t.Parallel() - - tt := fileContentTest{ - Dir: "testdata/single_cmd_dep", - Target: "foo", - Files: map[string]string{ - "foo.txt": "foo\n", - "bar.txt": "bar\n", - }, - } - t.Run("", func(t *testing.T) { - t.Parallel() - tt.Run(t) - }) -} - -func TestSilence(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/silent"), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(false), - ) - require.NoError(t, e.Setup()) - - // First verify that the silent flag is in place. - fetchedTask, err := e.GetTask(&task.Call{Task: "task-test-silent-calls-chatty-silenced"}) - require.NoError(t, err, "Unable to look up task task-test-silent-calls-chatty-silenced") - require.True(t, fetchedTask.Cmds[0].Silent, "The task task-test-silent-calls-chatty-silenced should have a silent call to chatty") - - // Then test the two basic cases where the task is silent or not. - // A silenced task. - err = e.Run(t.Context(), &task.Call{Task: "silent"}) - require.NoError(t, err) - require.Empty(t, buff.String(), "siWhile running lent: Expected not see output, because the task is silent") - - buff.Reset() - - // A chatty (not silent) task. - err = e.Run(t.Context(), &task.Call{Task: "chatty"}) - require.NoError(t, err) - require.NotEmpty(t, buff.String(), "chWhile running atty: Expected to see output, because the task is not silent") - - buff.Reset() - - // Then test invoking the two task from other tasks. - // A silenced task that calls a chatty task. - err = e.Run(t.Context(), &task.Call{Task: "task-test-silent-calls-chatty-non-silenced"}) - require.NoError(t, err) - require.NotEmpty(t, buff.String(), "While running task-test-silent-calls-chatty-non-silenced: Expected to see output. The task is silenced, but the called task is not. Silence does not propagate to called tasks.") - - buff.Reset() - - // A silent task that does a silent call to a chatty task. - err = e.Run(t.Context(), &task.Call{Task: "task-test-silent-calls-chatty-silenced"}) - require.NoError(t, err) - require.Empty(t, buff.String(), "While running task-test-silent-calls-chatty-silenced: Expected not to see output. The task calls chatty task, but the call is silenced.") - - buff.Reset() - - // A chatty task that does a call to a chatty task. - err = e.Run(t.Context(), &task.Call{Task: "task-test-chatty-calls-chatty-non-silenced"}) - require.NoError(t, err) - require.NotEmpty(t, buff.String(), "While running task-test-chatty-calls-chatty-non-silenced: Expected to see output. Both caller and callee are chatty and not silenced.") - - buff.Reset() - - // A chatty task that does a silenced call to a chatty task. - err = e.Run(t.Context(), &task.Call{Task: "task-test-chatty-calls-chatty-silenced"}) - require.NoError(t, err) - require.NotEmpty(t, buff.String(), "While running task-test-chatty-calls-chatty-silenced: Expected to see output. Call to a chatty task is silenced, but the parent task is not.") - - buff.Reset() - - // A chatty task with no cmd's of its own that does a silenced call to a chatty task. - err = e.Run(t.Context(), &task.Call{Task: "task-test-no-cmds-calls-chatty-silenced"}) - require.NoError(t, err) - require.Empty(t, buff.String(), "While running task-test-no-cmds-calls-chatty-silenced: Expected not to see output. While the task itself is not silenced, it does not have any cmds and only does an invocation of a silenced task.") - - buff.Reset() - - // A chatty task that does a silenced invocation of a task. - err = e.Run(t.Context(), &task.Call{Task: "task-test-chatty-calls-silenced-cmd"}) - require.NoError(t, err) - require.Empty(t, buff.String(), "While running task-test-chatty-calls-silenced-cmd: Expected not to see output. While the task itself is not silenced, its call to the chatty task is silent.") - - buff.Reset() - - // Then test calls via dependencies. - // A silent task that depends on a chatty task. - err = e.Run(t.Context(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-non-silenced"}) - require.NoError(t, err) - require.NotEmpty(t, buff.String(), "While running task-test-is-silent-depends-on-chatty-non-silenced: Expected to see output. The task is silent and depends on a chatty task. Dependencies does not inherit silence.") - - buff.Reset() - - // A silent task that depends on a silenced chatty task. - err = e.Run(t.Context(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-silenced"}) - require.NoError(t, err) - require.Empty(t, buff.String(), "While running task-test-is-silent-depends-on-chatty-silenced: Expected not to see output. The task is silent and has a silenced dependency on a chatty task.") - - buff.Reset() - - // A chatty task that, depends on a silenced chatty task. - err = e.Run(t.Context(), &task.Call{Task: "task-test-is-chatty-depends-on-chatty-silenced"}) - require.NoError(t, err) - require.Empty(t, buff.String(), "While running task-test-is-chatty-depends-on-chatty-silenced: Expected not to see output. The task is chatty but does not have commands and has a silenced dependency on a chatty task.") - - buff.Reset() -} - -func TestForce(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - env map[string]string - force bool - forceAll bool - }{ - { - name: "force", - force: true, - }, - { - name: "force-all", - forceAll: true, - }, - { - name: "force with gentle force experiment", - force: true, - env: map[string]string{ - "TASK_X_GENTLE_FORCE": "1", - }, - }, - { - name: "force-all with gentle force experiment", - forceAll: true, - env: map[string]string{ - "TASK_X_GENTLE_FORCE": "1", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/force"), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithForce(tt.force), - task.WithForceAll(tt.forceAll), - ) - require.NoError(t, e.Setup()) - require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task-with-dep"})) - }) - } -} - -func TestWildcard(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - call string - expectedOutput string - wantErr bool - }{ - { - name: "basic wildcard", - call: "wildcard-foo", - expectedOutput: "Hello foo\n", - }, - { - name: "double wildcard", - call: "foo-wildcard-bar", - expectedOutput: "Hello foo bar\n", - }, - { - name: "store wildcard", - call: "start-foo", - expectedOutput: "Starting foo\n", - }, - { - name: "alias", - call: "s-foo", - expectedOutput: "Starting foo\n", - }, - { - name: "matches exactly", - call: "matches-exactly-*", - expectedOutput: "I don't consume matches: []\n", - }, - { - name: "no matches", - call: "no-match", - wantErr: true, - }, - { - name: "multiple matches", - call: "wildcard-foo-bar", - expectedOutput: "Hello foo-bar\n", - }, - } - - for _, test := range tests { - t.Run(test.call, func(t *testing.T) { - t.Parallel() - - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/wildcards"), - task.WithStdout(&buff), - task.WithStderr(&buff), - task.WithSilent(true), - task.WithForce(true), - ) - require.NoError(t, e.Setup()) - if test.wantErr { - require.Error(t, e.Run(t.Context(), &task.Call{Task: test.call})) - return - } - require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.call})) - assert.Equal(t, test.expectedOutput, buff.String()) - }) - } -} - -// enableExperimentForTest enables the experiment behind pointer e for the duration of test t and sub-tests, -// with the experiment being restored to its previous state when tests complete. -// -// Typically experiments are controlled via TASK_X_ env vars, but we cannot use those in tests -// because the experiment settings are parsed during experiments.init(), before any tests run. -func enableExperimentForTest(t *testing.T, e *experiments.Experiment, val int) { - t.Helper() - prev := *e - *e = experiments.Experiment{ - Name: prev.Name, - AllowedValues: []int{val}, - Value: val, - } - t.Cleanup(func() { *e = prev }) -} diff --git a/testdata/abs_path/testdata/TestAbsPath.golden b/testdata/abs_path/testdata/TestAbsPath.golden new file mode 100644 index 0000000000..e80fee0a59 --- /dev/null +++ b/testdata/abs_path/testdata/TestAbsPath.golden @@ -0,0 +1 @@ +{{.TEST_DIR}}/bar diff --git a/testdata/checksum/testdata/TestStatusChecksum-build-wildcard_(first_run).golden b/testdata/checksum/testdata/TestStatusChecksum-build-wildcard_(first_run).golden new file mode 100644 index 0000000000..389d4dd5c2 --- /dev/null +++ b/testdata/checksum/testdata/TestStatusChecksum-build-wildcard_(first_run).golden @@ -0,0 +1 @@ +task: [build-wildcard] cp ./source.txt ./generated-wildcard.txt diff --git a/testdata/checksum/testdata/TestStatusChecksum-build-wildcard_(up_to_date).golden b/testdata/checksum/testdata/TestStatusChecksum-build-wildcard_(up_to_date).golden new file mode 100644 index 0000000000..4aaeaa96d2 --- /dev/null +++ b/testdata/checksum/testdata/TestStatusChecksum-build-wildcard_(up_to_date).golden @@ -0,0 +1 @@ +task: Task "build-wildcard" is up to date diff --git a/testdata/checksum/testdata/TestStatusChecksum-build-with-status_(first_run).golden b/testdata/checksum/testdata/TestStatusChecksum-build-with-status_(first_run).golden new file mode 100644 index 0000000000..52382320be --- /dev/null +++ b/testdata/checksum/testdata/TestStatusChecksum-build-with-status_(first_run).golden @@ -0,0 +1 @@ +task: [build-with-status] cp ./source.txt ./generated.txt diff --git a/testdata/checksum/testdata/TestStatusChecksum-build-with-status_(up_to_date).golden b/testdata/checksum/testdata/TestStatusChecksum-build-with-status_(up_to_date).golden new file mode 100644 index 0000000000..d42cf82562 --- /dev/null +++ b/testdata/checksum/testdata/TestStatusChecksum-build-with-status_(up_to_date).golden @@ -0,0 +1 @@ +task: Task "build-with-status" is up to date diff --git a/testdata/checksum/testdata/TestStatusChecksum-build_(first_run).golden b/testdata/checksum/testdata/TestStatusChecksum-build_(first_run).golden new file mode 100644 index 0000000000..b1ce73317f --- /dev/null +++ b/testdata/checksum/testdata/TestStatusChecksum-build_(first_run).golden @@ -0,0 +1 @@ +task: [build] cp ./source.txt ./generated.txt diff --git a/testdata/checksum/testdata/TestStatusChecksum-build_(up_to_date).golden b/testdata/checksum/testdata/TestStatusChecksum-build_(up_to_date).golden new file mode 100644 index 0000000000..6bcd855798 --- /dev/null +++ b/testdata/checksum/testdata/TestStatusChecksum-build_(up_to_date).golden @@ -0,0 +1 @@ +task: Task "build" is up to date diff --git a/testdata/checksum/testdata/TestStatusChecksumMissingGenerated-first_run.golden b/testdata/checksum/testdata/TestStatusChecksumMissingGenerated-first_run.golden new file mode 100644 index 0000000000..b1ce73317f --- /dev/null +++ b/testdata/checksum/testdata/TestStatusChecksumMissingGenerated-first_run.golden @@ -0,0 +1 @@ +task: [build] cp ./source.txt ./generated.txt diff --git a/testdata/checksum/testdata/TestStatusChecksumMissingGenerated-re-run_after_generated_file_removed.golden b/testdata/checksum/testdata/TestStatusChecksumMissingGenerated-re-run_after_generated_file_removed.golden new file mode 100644 index 0000000000..b1ce73317f --- /dev/null +++ b/testdata/checksum/testdata/TestStatusChecksumMissingGenerated-re-run_after_generated_file_removed.golden @@ -0,0 +1 @@ +task: [build] cp ./source.txt ./generated.txt diff --git a/testdata/checksum/testdata/TestStatusChecksumMissingGenerated-up_to_date.golden b/testdata/checksum/testdata/TestStatusChecksumMissingGenerated-up_to_date.golden new file mode 100644 index 0000000000..6bcd855798 --- /dev/null +++ b/testdata/checksum/testdata/TestStatusChecksumMissingGenerated-up_to_date.golden @@ -0,0 +1 @@ +task: Task "build" is up to date diff --git a/testdata/cmds_vars/testdata/TestCmdsVariables-build-checksum.golden b/testdata/cmds_vars/testdata/TestCmdsVariables-build-checksum.golden new file mode 100644 index 0000000000..6f31ea8237 --- /dev/null +++ b/testdata/cmds_vars/testdata/TestCmdsVariables-build-checksum.golden @@ -0,0 +1,4 @@ +task: "build-checksum" started +task: [build-checksum] echo "3e464c4b03f4b65d740e1e130d4d108a" +3e464c4b03f4b65d740e1e130d4d108a +task: "build-checksum" finished diff --git a/testdata/cmds_vars/testdata/TestCmdsVariables-build-ts.golden b/testdata/cmds_vars/testdata/TestCmdsVariables-build-ts.golden new file mode 100644 index 0000000000..475ea3def7 --- /dev/null +++ b/testdata/cmds_vars/testdata/TestCmdsVariables-build-ts.golden @@ -0,0 +1,6 @@ +task: "build-ts" started +task: [build-ts] echo '1704067200' +1704067200 +task: [build-ts] echo '2024-01-01 00:00:00 +0000 UTC' +2024-01-01 00:00:00 +0000 UTC +task: "build-ts" finished diff --git a/testdata/cyclic/testdata/TestCyclicDep-err-run.golden b/testdata/cyclic/testdata/TestCyclicDep-err-run.golden new file mode 100644 index 0000000000..da011a16b3 --- /dev/null +++ b/testdata/cyclic/testdata/TestCyclicDep-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Failed to run task "task-1": task: Failed to run task "task-2": task: Maximum task call exceeded (1000) for task "task-1": probably an cyclic dep or infinite loop \ No newline at end of file diff --git a/testdata/cyclic/testdata/TestCyclicDep.golden b/testdata/cyclic/testdata/TestCyclicDep.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/deferred/testdata/TestDeferredCmds-parent.golden b/testdata/deferred/testdata/TestDeferredCmds-parent.golden new file mode 100644 index 0000000000..a6c15bd940 --- /dev/null +++ b/testdata/deferred/testdata/TestDeferredCmds-parent.golden @@ -0,0 +1,4 @@ +task: [child] echo "child task immediate value-from-parent" +child task immediate value-from-parent +task: [child] echo "child task deferred value-from-parent" +child task deferred value-from-parent diff --git a/testdata/deferred/testdata/TestDeferredCmds-task-2-err-run.golden b/testdata/deferred/testdata/TestDeferredCmds-task-2-err-run.golden new file mode 100644 index 0000000000..f52580093d --- /dev/null +++ b/testdata/deferred/testdata/TestDeferredCmds-task-2-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "task-2": exit status 1 \ No newline at end of file diff --git a/testdata/deferred/testdata/TestDeferredCmds-task-2.golden b/testdata/deferred/testdata/TestDeferredCmds-task-2.golden new file mode 100644 index 0000000000..27a0e27c42 --- /dev/null +++ b/testdata/deferred/testdata/TestDeferredCmds-task-2.golden @@ -0,0 +1,9 @@ +task: [task-2] echo 'cmd ran' +cmd ran +task: [task-2] exit 1 +task: [task-2] echo 'failing' && exit 2 +failing +echo ran +task-1 ran successfully +task: [task-1] echo 'task-1 ran successfully' +task-1 ran successfully diff --git a/testdata/deferred/testdata/TestDeferredTaskTimeout.golden b/testdata/deferred/testdata/TestDeferredTaskTimeout.golden new file mode 100644 index 0000000000..c9663b6b27 --- /dev/null +++ b/testdata/deferred/testdata/TestDeferredTaskTimeout.golden @@ -0,0 +1,8 @@ +task: "parent-with-timeout" started +task: [parent-with-timeout] echo 'parent completed' +parent completed +task: "parent-with-timeout" finished +task: "slow-cleanup" started +task: [slow-cleanup] sleep 1 && echo 'cleanup completed' +task: "slow-cleanup" failed: context deadline exceeded +task: ignored error in deferred cmd: task: [parent-with-timeout] command timeout exceeded (100ms) diff --git a/testdata/dep_timeout/testdata/TestDepTimeout-timeout_exceeded-err-run.golden b/testdata/dep_timeout/testdata/TestDepTimeout-timeout_exceeded-err-run.golden new file mode 100644 index 0000000000..be3ff23fa5 --- /dev/null +++ b/testdata/dep_timeout/testdata/TestDepTimeout-timeout_exceeded-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "timeout-exceeded": task: [slow] command timeout exceeded (300ms) \ No newline at end of file diff --git a/testdata/dep_timeout/testdata/TestDepTimeout-timeout_exceeded.golden b/testdata/dep_timeout/testdata/TestDepTimeout-timeout_exceeded.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/dep_timeout/testdata/TestDepTimeout-timeout_not_exceeded.golden b/testdata/dep_timeout/testdata/TestDepTimeout-timeout_not_exceeded.golden new file mode 100644 index 0000000000..eb580278ce --- /dev/null +++ b/testdata/dep_timeout/testdata/TestDepTimeout-timeout_not_exceeded.golden @@ -0,0 +1,2 @@ +quick +reached the end diff --git a/testdata/dir/dynamic_var/Taskfile.yml b/testdata/dir/dynamic_var/Taskfile.yml index b92f0f8749..961b0c3064 100644 --- a/testdata/dir/dynamic_var/Taskfile.yml +++ b/testdata/dir/dynamic_var/Taskfile.yml @@ -17,16 +17,15 @@ tasks: from-root-taskfile: cmds: - - echo '{{.TASK_DIR}}' > from_root_taskfile.txt + - echo '{{.TASK_DIR}}' dir: subdirectory vars: TASK_DIR: sh: basename "$(pwd)" - silent: true from-interpolated-dir: cmds: - - echo '{{.INTERPOLATED_DIR}}' > from_interpolated_dir.txt + - echo '{{.INTERPOLATED_DIR}}' dir: '{{.DIRECTORY}}' vars: INTERPOLATED_DIR: diff --git a/testdata/dir/dynamic_var/subdirectory/Taskfile.yml b/testdata/dir/dynamic_var/subdirectory/Taskfile.yml index b5865abd9f..b8915ae3fe 100644 --- a/testdata/dir/dynamic_var/subdirectory/Taskfile.yml +++ b/testdata/dir/dynamic_var/subdirectory/Taskfile.yml @@ -7,13 +7,11 @@ vars: tasks: from-included-taskfile: cmds: - - echo '{{.TASKFILE_DIR}}' > from_included_taskfile.txt - silent: true + - echo '{{.TASKFILE_DIR}}' from-included-taskfile-task: cmds: - - echo '{{.TASKFILE_TASK_DIR}}' > from_included_taskfile_task.txt - silent: true + - echo '{{.TASKFILE_TASK_DIR}}' vars: TASKFILE_TASK_DIR: sh: basename "$(pwd)" diff --git a/testdata/dir/dynamic_var/testdata/TestDynamicVariablesShouldRunOnTheTaskDir.golden b/testdata/dir/dynamic_var/testdata/TestDynamicVariablesShouldRunOnTheTaskDir.golden new file mode 100644 index 0000000000..4f2d9797f4 --- /dev/null +++ b/testdata/dir/dynamic_var/testdata/TestDynamicVariablesShouldRunOnTheTaskDir.golden @@ -0,0 +1,8 @@ +task: [from-root-taskfile] echo 'subdirectory' +subdirectory +task: [sub:from-included-taskfile] echo 'subdirectory' +subdirectory +task: [sub:from-included-taskfile-task] echo 'subdirectory' +subdirectory +task: [from-interpolated-dir] echo 'subdirectory' +subdirectory diff --git a/testdata/dir/dynamic_var_on_created_dir/testdata/TestDynamicVariablesRunOnTheNewCreatedDir.golden b/testdata/dir/dynamic_var_on_created_dir/testdata/TestDynamicVariablesRunOnTheNewCreatedDir.golden new file mode 100644 index 0000000000..680da0d15a --- /dev/null +++ b/testdata/dir/dynamic_var_on_created_dir/testdata/TestDynamicVariablesRunOnTheNewCreatedDir.golden @@ -0,0 +1 @@ +task: [default] echo {{.TEST_DIR}}/testdata/dir/dynamic_var_on_created_dir/created diff --git a/testdata/dir/explicit_doesnt_exist/testdata/TestWhenDirAttributeItCreatesMissingAndRunsInThatDir.golden b/testdata/dir/explicit_doesnt_exist/testdata/TestWhenDirAttributeItCreatesMissingAndRunsInThatDir.golden new file mode 100644 index 0000000000..f6e891260e --- /dev/null +++ b/testdata/dir/explicit_doesnt_exist/testdata/TestWhenDirAttributeItCreatesMissingAndRunsInThatDir.golden @@ -0,0 +1 @@ +{{.TEST_DIR}}/testdata/dir/explicit_doesnt_exist/createme diff --git a/testdata/dir/explicit_exists/testdata/TestWhenDirAttributeAndDirExistsItRunsInThatDir.golden b/testdata/dir/explicit_exists/testdata/TestWhenDirAttributeAndDirExistsItRunsInThatDir.golden new file mode 100644 index 0000000000..0c85100f2b --- /dev/null +++ b/testdata/dir/explicit_exists/testdata/TestWhenDirAttributeAndDirExistsItRunsInThatDir.golden @@ -0,0 +1 @@ +{{.TEST_DIR}}/testdata/dir/explicit_exists/exists diff --git a/testdata/dir/testdata/TestWhenNoDirAttributeItRunsInSameDirAsTaskfile.golden b/testdata/dir/testdata/TestWhenNoDirAttributeItRunsInSameDirAsTaskfile.golden new file mode 100644 index 0000000000..9b77498f69 --- /dev/null +++ b/testdata/dir/testdata/TestWhenNoDirAttributeItRunsInSameDirAsTaskfile.golden @@ -0,0 +1 @@ +{{.TEST_DIR}}/testdata/dir diff --git a/testdata/dotenv/default/Taskfile.yml b/testdata/dotenv/default/Taskfile.yml index f33195d1cb..085e7f7810 100644 --- a/testdata/dotenv/default/Taskfile.yml +++ b/testdata/dotenv/default/Taskfile.yml @@ -5,4 +5,4 @@ dotenv: ['../include1/.env', '../include1/envs/.env'] tasks: default: cmds: - - echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" > include.txt + - echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" diff --git a/testdata/dotenv/default/testdata/TestDotenvShouldIncludeAllEnvFiles.golden b/testdata/dotenv/default/testdata/TestDotenvShouldIncludeAllEnvFiles.golden new file mode 100644 index 0000000000..c8b1159df6 --- /dev/null +++ b/testdata/dotenv/default/testdata/TestDotenvShouldIncludeAllEnvFiles.golden @@ -0,0 +1,2 @@ +task: [default] echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" +INCLUDE1='from_include1' INCLUDE2='from_include2' diff --git a/testdata/dotenv/env_var_in_path/Taskfile.yml b/testdata/dotenv/env_var_in_path/Taskfile.yml index 84c3a38eb3..5b3eb7e594 100644 --- a/testdata/dotenv/env_var_in_path/Taskfile.yml +++ b/testdata/dotenv/env_var_in_path/Taskfile.yml @@ -5,4 +5,4 @@ dotenv: [".env.{{.ENV_VAR}}"] tasks: default: cmds: - - echo "VAR='$VAR_IN_DOTENV'" > var.txt + - echo "VAR='$VAR_IN_DOTENV'" diff --git a/testdata/dotenv/env_var_in_path/testdata/TestDotenvHasEnvVarInPath.golden b/testdata/dotenv/env_var_in_path/testdata/TestDotenvHasEnvVarInPath.golden new file mode 100644 index 0000000000..12d96e1830 --- /dev/null +++ b/testdata/dotenv/env_var_in_path/testdata/TestDotenvHasEnvVarInPath.golden @@ -0,0 +1,2 @@ +task: [default] echo "VAR='$VAR_IN_DOTENV'" +VAR='var_in_dot_env_2' diff --git a/testdata/dotenv/error_included_envs/testdata/TestDotenvShouldErrorWhenIncludingDependantDotenvs-err-setup.golden b/testdata/dotenv/error_included_envs/testdata/TestDotenvShouldErrorWhenIncludingDependantDotenvs-err-setup.golden new file mode 100644 index 0000000000..8e2763ffc1 --- /dev/null +++ b/testdata/dotenv/error_included_envs/testdata/TestDotenvShouldErrorWhenIncludingDependantDotenvs-err-setup.golden @@ -0,0 +1 @@ +task: Included Taskfiles can't have dotenv declarations. Please, move the dotenv declaration to the main Taskfile \ No newline at end of file diff --git a/testdata/dotenv/error_included_envs/testdata/TestDotenvShouldErrorWhenIncludingDependantDotenvs.golden b/testdata/dotenv/error_included_envs/testdata/TestDotenvShouldErrorWhenIncludingDependantDotenvs.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/dotenv/local_env_in_path/Taskfile.yml b/testdata/dotenv/local_env_in_path/Taskfile.yml index 27306777b6..809b95a3b7 100644 --- a/testdata/dotenv/local_env_in_path/Taskfile.yml +++ b/testdata/dotenv/local_env_in_path/Taskfile.yml @@ -8,4 +8,4 @@ dotenv: [".env.{{.LOCAL_ENV}}"] tasks: default: cmds: - - echo "VAR='$VAR_IN_DOTENV'" > var.txt + - echo "VAR='$VAR_IN_DOTENV'" diff --git a/testdata/dotenv/local_env_in_path/testdata/TestDotenvHasLocalEnvInPath.golden b/testdata/dotenv/local_env_in_path/testdata/TestDotenvHasLocalEnvInPath.golden new file mode 100644 index 0000000000..b8b76e94d4 --- /dev/null +++ b/testdata/dotenv/local_env_in_path/testdata/TestDotenvHasLocalEnvInPath.golden @@ -0,0 +1,2 @@ +task: [default] echo "VAR='$VAR_IN_DOTENV'" +VAR='var_in_dot_env_1' diff --git a/testdata/dotenv/local_var_in_path/Taskfile.yml b/testdata/dotenv/local_var_in_path/Taskfile.yml index c667d606f2..282fa5f9d9 100644 --- a/testdata/dotenv/local_var_in_path/Taskfile.yml +++ b/testdata/dotenv/local_var_in_path/Taskfile.yml @@ -10,4 +10,4 @@ dotenv: [".env.{{.LOCAL_VAR}}"] tasks: default: cmds: - - echo "VAR='$VAR_IN_DOTENV'" > var.txt + - echo "VAR='$VAR_IN_DOTENV'" diff --git a/testdata/dotenv/local_var_in_path/testdata/TestDotenvHasLocalVarInPath.golden b/testdata/dotenv/local_var_in_path/testdata/TestDotenvHasLocalVarInPath.golden new file mode 100644 index 0000000000..5f3ae7c7d8 --- /dev/null +++ b/testdata/dotenv/local_var_in_path/testdata/TestDotenvHasLocalVarInPath.golden @@ -0,0 +1,2 @@ +task: [default] echo "VAR='$VAR_IN_DOTENV'" +VAR='var_in_dot_env_3' diff --git a/testdata/dotenv/missing_env/Taskfile.yml b/testdata/dotenv/missing_env/Taskfile.yml index 865ab6ddd6..083ed20aca 100644 --- a/testdata/dotenv/missing_env/Taskfile.yml +++ b/testdata/dotenv/missing_env/Taskfile.yml @@ -5,4 +5,4 @@ dotenv: ['.env'] tasks: default: cmds: - - echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" > include.txt + - echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" diff --git a/testdata/dotenv/missing_env/testdata/TestDotenvShouldAllowMissingEnv.golden b/testdata/dotenv/missing_env/testdata/TestDotenvShouldAllowMissingEnv.golden new file mode 100644 index 0000000000..933344956b --- /dev/null +++ b/testdata/dotenv/missing_env/testdata/TestDotenvShouldAllowMissingEnv.golden @@ -0,0 +1,2 @@ +task: [default] echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" +INCLUDE1='' INCLUDE2='' diff --git a/testdata/dotenv/parse_error/testdata/TestTaskDotenvParseErrorMessage-err-setup.golden b/testdata/dotenv/parse_error/testdata/TestTaskDotenvParseErrorMessage-err-setup.golden new file mode 100644 index 0000000000..fa18a4f54a --- /dev/null +++ b/testdata/dotenv/parse_error/testdata/TestTaskDotenvParseErrorMessage-err-setup.golden @@ -0,0 +1 @@ +error reading env file {{.TEST_DIR}}/testdata/dotenv/parse_error/.env-with-error: unexpected character "/n" in variable name near "SOME_VAR/n" \ No newline at end of file diff --git a/testdata/dotenv/parse_error/testdata/TestTaskDotenvParseErrorMessage.golden b/testdata/dotenv/parse_error/testdata/TestTaskDotenvParseErrorMessage.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/dotenv_task/default/Taskfile.yml b/testdata/dotenv_task/default/Taskfile.yml index 3adbc5be59..54462703ec 100644 --- a/testdata/dotenv_task/default/Taskfile.yml +++ b/testdata/dotenv_task/default/Taskfile.yml @@ -7,22 +7,22 @@ tasks: dotenv: dotenv: ['.env'] cmds: - - echo "$FOO" > dotenv.txt + - echo "$FOO" dotenv-overridden-by-env: dotenv: ['.env'] env: FOO: overridden cmds: - - echo "$FOO" > dotenv-overridden-by-env.txt + - echo "$FOO" dotenv-with-var-name: vars: DOTENV: .env dotenv: ['{{.DOTENV}}'] cmds: - - echo "$FOO" > dotenv-with-var-name.txt + - echo "$FOO" no-dotenv: cmds: - - echo "$FOO" > no-dotenv.txt + - echo "$FOO" diff --git a/testdata/dotenv_task/default/testdata/TestTaskDotenv.golden b/testdata/dotenv_task/default/testdata/TestTaskDotenv.golden new file mode 100644 index 0000000000..3f73b8dff8 --- /dev/null +++ b/testdata/dotenv_task/default/testdata/TestTaskDotenv.golden @@ -0,0 +1,2 @@ +task: [dotenv] echo "$FOO" +foo diff --git a/testdata/dotenv_task/default/testdata/TestTaskDotenvFail.golden b/testdata/dotenv_task/default/testdata/TestTaskDotenvFail.golden new file mode 100644 index 0000000000..36b432e31b --- /dev/null +++ b/testdata/dotenv_task/default/testdata/TestTaskDotenvFail.golden @@ -0,0 +1,2 @@ +task: [no-dotenv] echo "$FOO" +global diff --git a/testdata/dotenv_task/default/testdata/TestTaskDotenvOverriddenByEnv.golden b/testdata/dotenv_task/default/testdata/TestTaskDotenvOverriddenByEnv.golden new file mode 100644 index 0000000000..752ab8b3b7 --- /dev/null +++ b/testdata/dotenv_task/default/testdata/TestTaskDotenvOverriddenByEnv.golden @@ -0,0 +1,2 @@ +task: [dotenv-overridden-by-env] echo "$FOO" +overridden diff --git a/testdata/dotenv_task/default/testdata/TestTaskDotenvWithVarName.golden b/testdata/dotenv_task/default/testdata/TestTaskDotenvWithVarName.golden new file mode 100644 index 0000000000..087444b003 --- /dev/null +++ b/testdata/dotenv_task/default/testdata/TestTaskDotenvWithVarName.golden @@ -0,0 +1,2 @@ +task: [dotenv-with-var-name] echo "$FOO" +foo diff --git a/testdata/dry/testdata/TestDry.golden b/testdata/dry/testdata/TestDry.golden new file mode 100644 index 0000000000..ecd86bf0fe --- /dev/null +++ b/testdata/dry/testdata/TestDry.golden @@ -0,0 +1 @@ +task: [build] touch file.txt diff --git a/testdata/dry_checksum/testdata/TestDryChecksum-dry.golden b/testdata/dry_checksum/testdata/TestDryChecksum-dry.golden new file mode 100644 index 0000000000..71eaf98a79 --- /dev/null +++ b/testdata/dry_checksum/testdata/TestDryChecksum-dry.golden @@ -0,0 +1 @@ +task: [default] echo "Working..." diff --git a/testdata/dry_checksum/testdata/TestDryChecksum-not_dry.golden b/testdata/dry_checksum/testdata/TestDryChecksum-not_dry.golden new file mode 100644 index 0000000000..74a903b6d3 --- /dev/null +++ b/testdata/dry_checksum/testdata/TestDryChecksum-not_dry.golden @@ -0,0 +1,2 @@ +task: [default] echo "Working..." +Working... diff --git a/testdata/error_code/testdata/TestErrorCode-direct_task-err-run.golden b/testdata/error_code/testdata/TestErrorCode-direct_task-err-run.golden new file mode 100644 index 0000000000..6eaca593c6 --- /dev/null +++ b/testdata/error_code/testdata/TestErrorCode-direct_task-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "direct": exit status 42 \ No newline at end of file diff --git a/testdata/error_code/testdata/TestErrorCode-direct_task.golden b/testdata/error_code/testdata/TestErrorCode-direct_task.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/error_code/testdata/TestErrorCode-indirect_task-err-run.golden b/testdata/error_code/testdata/TestErrorCode-indirect_task-err-run.golden new file mode 100644 index 0000000000..7367df5369 --- /dev/null +++ b/testdata/error_code/testdata/TestErrorCode-indirect_task-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "indirect": task: Failed to run task "direct": exit status 42 \ No newline at end of file diff --git a/testdata/error_code/testdata/TestErrorCode-indirect_task.golden b/testdata/error_code/testdata/TestErrorCode-indirect_task.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-default_(1).golden b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-default_(1).golden new file mode 100644 index 0000000000..de7526ace2 --- /dev/null +++ b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-default_(1).golden @@ -0,0 +1,2 @@ +task: [default] echo "some job" +some job diff --git a/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-default_(2).golden b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-default_(2).golden new file mode 100644 index 0000000000..de7526ace2 --- /dev/null +++ b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-default_(2).golden @@ -0,0 +1,2 @@ +task: [default] echo "some job" +some job diff --git a/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-default_(3).golden b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-default_(3).golden new file mode 100644 index 0000000000..b34902ede9 --- /dev/null +++ b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-default_(3).golden @@ -0,0 +1 @@ +task: Task "default" is up to date diff --git a/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-reset.golden b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-reset.golden new file mode 100644 index 0000000000..d9b134cfeb --- /dev/null +++ b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-reset.golden @@ -0,0 +1,2 @@ +task: [reset] echo "shared file source" > src/shared/b +task: [reset] echo "file source" > src/a diff --git a/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-test-sym_(1).golden b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-test-sym_(1).golden new file mode 100644 index 0000000000..98936f382c --- /dev/null +++ b/testdata/evaluate_symlinks_in_paths/testdata/TestEvaluateSymlinksInPaths-test-sym_(1).golden @@ -0,0 +1 @@ +task: [test-sym] echo "shared file source changed" > src/shared/b diff --git a/testdata/exit_code/testdata/TestExitCodeOne-err-run.golden b/testdata/exit_code/testdata/TestExitCodeOne-err-run.golden new file mode 100644 index 0000000000..e46f4a4bb6 --- /dev/null +++ b/testdata/exit_code/testdata/TestExitCodeOne-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "exit-one": exit status 1 \ No newline at end of file diff --git a/testdata/exit_code/testdata/TestExitCodeOne.golden b/testdata/exit_code/testdata/TestExitCodeOne.golden new file mode 100644 index 0000000000..0e8502ed5c --- /dev/null +++ b/testdata/exit_code/testdata/TestExitCodeOne.golden @@ -0,0 +1 @@ +FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE=1 diff --git a/testdata/exit_code/testdata/TestExitCodeTimeout-err-run.golden b/testdata/exit_code/testdata/TestExitCodeTimeout-err-run.golden new file mode 100644 index 0000000000..2b7fbcec85 --- /dev/null +++ b/testdata/exit_code/testdata/TestExitCodeTimeout-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "exit-timeout": task: [exit-timeout] command timeout exceeded (200ms) \ No newline at end of file diff --git a/testdata/exit_code/testdata/TestExitCodeTimeout.golden b/testdata/exit_code/testdata/TestExitCodeTimeout.golden new file mode 100644 index 0000000000..0b075cd04c --- /dev/null +++ b/testdata/exit_code/testdata/TestExitCodeTimeout.golden @@ -0,0 +1 @@ +EXIT_CODE=124 diff --git a/testdata/exit_code/testdata/TestExitCodeZero.golden b/testdata/exit_code/testdata/TestExitCodeZero.golden new file mode 100644 index 0000000000..b3fd696a64 --- /dev/null +++ b/testdata/exit_code/testdata/TestExitCodeZero.golden @@ -0,0 +1 @@ +FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE= diff --git a/testdata/exit_immediately/testdata/TestExitImmediately-err-run.golden b/testdata/exit_immediately/testdata/TestExitImmediately-err-run.golden new file mode 100644 index 0000000000..a25b9e6de1 --- /dev/null +++ b/testdata/exit_immediately/testdata/TestExitImmediately-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "default": exit status 127 \ No newline at end of file diff --git a/testdata/exit_immediately/testdata/TestExitImmediately.golden b/testdata/exit_immediately/testdata/TestExitImmediately.golden new file mode 100644 index 0000000000..732961a69f --- /dev/null +++ b/testdata/exit_immediately/testdata/TestExitImmediately.golden @@ -0,0 +1 @@ +"this_should_fail": executable file not found in $PATH diff --git a/testdata/expand/testdata/TestExpand.golden b/testdata/expand/testdata/TestExpand.golden new file mode 100644 index 0000000000..226855837f --- /dev/null +++ b/testdata/expand/testdata/TestExpand.golden @@ -0,0 +1 @@ +{{.HOME}} diff --git a/testdata/file_names/Taskfile.dist.yaml/Taskfile.dist.yaml b/testdata/file_names/Taskfile.dist.yaml/Taskfile.dist.yaml index 6c62d05b90..e2de43632e 100644 --- a/testdata/file_names/Taskfile.dist.yaml/Taskfile.dist.yaml +++ b/testdata/file_names/Taskfile.dist.yaml/Taskfile.dist.yaml @@ -1,4 +1,4 @@ version: '3' tasks: - default: echo "hello" > output.txt + default: echo "hello" diff --git a/testdata/file_names/Taskfile.dist.yaml/testdata/TestSupportedFileNames-Taskfile.dist.yaml.golden b/testdata/file_names/Taskfile.dist.yaml/testdata/TestSupportedFileNames-Taskfile.dist.yaml.golden new file mode 100644 index 0000000000..a3b6b7be10 --- /dev/null +++ b/testdata/file_names/Taskfile.dist.yaml/testdata/TestSupportedFileNames-Taskfile.dist.yaml.golden @@ -0,0 +1,2 @@ +task: [default] echo "hello" +hello diff --git a/testdata/file_names/Taskfile.dist.yml/Taskfile.dist.yml b/testdata/file_names/Taskfile.dist.yml/Taskfile.dist.yml index 6c62d05b90..e2de43632e 100644 --- a/testdata/file_names/Taskfile.dist.yml/Taskfile.dist.yml +++ b/testdata/file_names/Taskfile.dist.yml/Taskfile.dist.yml @@ -1,4 +1,4 @@ version: '3' tasks: - default: echo "hello" > output.txt + default: echo "hello" diff --git a/testdata/file_names/Taskfile.dist.yml/testdata/TestSupportedFileNames-Taskfile.dist.yml.golden b/testdata/file_names/Taskfile.dist.yml/testdata/TestSupportedFileNames-Taskfile.dist.yml.golden new file mode 100644 index 0000000000..a3b6b7be10 --- /dev/null +++ b/testdata/file_names/Taskfile.dist.yml/testdata/TestSupportedFileNames-Taskfile.dist.yml.golden @@ -0,0 +1,2 @@ +task: [default] echo "hello" +hello diff --git a/testdata/file_names/Taskfile.yaml/Taskfile.yaml b/testdata/file_names/Taskfile.yaml/Taskfile.yaml index 6c62d05b90..e2de43632e 100644 --- a/testdata/file_names/Taskfile.yaml/Taskfile.yaml +++ b/testdata/file_names/Taskfile.yaml/Taskfile.yaml @@ -1,4 +1,4 @@ version: '3' tasks: - default: echo "hello" > output.txt + default: echo "hello" diff --git a/testdata/file_names/Taskfile.yaml/testdata/TestSupportedFileNames-Taskfile.yaml.golden b/testdata/file_names/Taskfile.yaml/testdata/TestSupportedFileNames-Taskfile.yaml.golden new file mode 100644 index 0000000000..a3b6b7be10 --- /dev/null +++ b/testdata/file_names/Taskfile.yaml/testdata/TestSupportedFileNames-Taskfile.yaml.golden @@ -0,0 +1,2 @@ +task: [default] echo "hello" +hello diff --git a/testdata/file_names/Taskfile.yml/Taskfile.yml b/testdata/file_names/Taskfile.yml/Taskfile.yml index 6c62d05b90..e2de43632e 100644 --- a/testdata/file_names/Taskfile.yml/Taskfile.yml +++ b/testdata/file_names/Taskfile.yml/Taskfile.yml @@ -1,4 +1,4 @@ version: '3' tasks: - default: echo "hello" > output.txt + default: echo "hello" diff --git a/testdata/file_names/Taskfile.yml/testdata/TestSupportedFileNames-Taskfile.yml.golden b/testdata/file_names/Taskfile.yml/testdata/TestSupportedFileNames-Taskfile.yml.golden new file mode 100644 index 0000000000..a3b6b7be10 --- /dev/null +++ b/testdata/file_names/Taskfile.yml/testdata/TestSupportedFileNames-Taskfile.yml.golden @@ -0,0 +1,2 @@ +task: [default] echo "hello" +hello diff --git a/testdata/force/testdata/TestForce-force-all.golden b/testdata/force/testdata/TestForce-force-all.golden new file mode 100644 index 0000000000..71c1728791 --- /dev/null +++ b/testdata/force/testdata/TestForce-force-all.golden @@ -0,0 +1,4 @@ +task: [indirect] echo "indirect" +indirect +task: [task-with-dep] echo "direct" +direct diff --git a/testdata/force/testdata/TestForce-force-all_with_gentle_force_experiment.golden b/testdata/force/testdata/TestForce-force-all_with_gentle_force_experiment.golden new file mode 100644 index 0000000000..71c1728791 --- /dev/null +++ b/testdata/force/testdata/TestForce-force-all_with_gentle_force_experiment.golden @@ -0,0 +1,4 @@ +task: [indirect] echo "indirect" +indirect +task: [task-with-dep] echo "direct" +direct diff --git a/testdata/force/testdata/TestForce-force.golden b/testdata/force/testdata/TestForce-force.golden new file mode 100644 index 0000000000..98780babc6 --- /dev/null +++ b/testdata/force/testdata/TestForce-force.golden @@ -0,0 +1,3 @@ +task: Task "indirect" is up to date +task: [task-with-dep] echo "direct" +direct diff --git a/testdata/force/testdata/TestForce-force_with_gentle_force_experiment.golden b/testdata/force/testdata/TestForce-force_with_gentle_force_experiment.golden new file mode 100644 index 0000000000..98780babc6 --- /dev/null +++ b/testdata/force/testdata/TestForce-force_with_gentle_force_experiment.golden @@ -0,0 +1,3 @@ +task: Task "indirect" is up to date +task: [task-with-dep] echo "direct" +direct diff --git a/testdata/generates/testdata/TestGenerates-abs.txt_(first_run).golden b/testdata/generates/testdata/TestGenerates-abs.txt_(first_run).golden new file mode 100644 index 0000000000..a958c34c4d --- /dev/null +++ b/testdata/generates/testdata/TestGenerates-abs.txt_(first_run).golden @@ -0,0 +1,2 @@ +task: Task "sub/src.txt" is up to date +task: [abs.txt] cat src.txt > '{{.TEST_DIR}}/testdata/generates/abs.txt' diff --git a/testdata/generates/testdata/TestGenerates-abs.txt_(up_to_date).golden b/testdata/generates/testdata/TestGenerates-abs.txt_(up_to_date).golden new file mode 100644 index 0000000000..78b02b789a --- /dev/null +++ b/testdata/generates/testdata/TestGenerates-abs.txt_(up_to_date).golden @@ -0,0 +1,2 @@ +task: Task "sub/src.txt" is up to date +task: Task "abs.txt" is up to date diff --git a/testdata/generates/testdata/TestGenerates-my_text_file.txt_(first_run).golden b/testdata/generates/testdata/TestGenerates-my_text_file.txt_(first_run).golden new file mode 100644 index 0000000000..695334d937 --- /dev/null +++ b/testdata/generates/testdata/TestGenerates-my_text_file.txt_(first_run).golden @@ -0,0 +1,2 @@ +task: Task "sub/src.txt" is up to date +task: [my text file.txt] cat sub/src.txt > 'my text file.txt' diff --git a/testdata/generates/testdata/TestGenerates-my_text_file.txt_(up_to_date).golden b/testdata/generates/testdata/TestGenerates-my_text_file.txt_(up_to_date).golden new file mode 100644 index 0000000000..752d8a7ee6 --- /dev/null +++ b/testdata/generates/testdata/TestGenerates-my_text_file.txt_(up_to_date).golden @@ -0,0 +1,2 @@ +task: Task "sub/src.txt" is up to date +task: Task "my text file.txt" is up to date diff --git a/testdata/generates/testdata/TestGenerates-rel.txt_(first_run).golden b/testdata/generates/testdata/TestGenerates-rel.txt_(first_run).golden new file mode 100644 index 0000000000..f4db316305 --- /dev/null +++ b/testdata/generates/testdata/TestGenerates-rel.txt_(first_run).golden @@ -0,0 +1,3 @@ +task: [sub/src.txt] mkdir -p sub +task: [sub/src.txt] echo "hello world" > sub/src.txt +task: [rel.txt] cat src.txt > '../rel.txt' diff --git a/testdata/generates/testdata/TestGenerates-rel.txt_(up_to_date).golden b/testdata/generates/testdata/TestGenerates-rel.txt_(up_to_date).golden new file mode 100644 index 0000000000..f4b7cd374e --- /dev/null +++ b/testdata/generates/testdata/TestGenerates-rel.txt_(up_to_date).golden @@ -0,0 +1,2 @@ +task: Task "sub/src.txt" is up to date +task: Task "rel.txt" is up to date diff --git a/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-ignored_at_command_level.golden b/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-ignored_at_command_level.golden new file mode 100644 index 0000000000..582e0daf3e --- /dev/null +++ b/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-ignored_at_command_level.golden @@ -0,0 +1,3 @@ +task: [cmd-timeout-should-pass] sleep 10 +task: [cmd-timeout-should-pass] echo "reached the end" +reached the end diff --git a/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-ignored_at_task_level.golden b/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-ignored_at_task_level.golden new file mode 100644 index 0000000000..03169513ef --- /dev/null +++ b/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-ignored_at_task_level.golden @@ -0,0 +1,3 @@ +task: [task-timeout-should-pass] sleep 10 +task: [task-timeout-should-pass] echo "reached the end" +reached the end diff --git a/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-not_ignored-err-run.golden b/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-not_ignored-err-run.golden new file mode 100644 index 0000000000..c97b047fc8 --- /dev/null +++ b/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-not_ignored-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "cmd-timeout-should-fail": task: [cmd-timeout-should-fail] command timeout exceeded (200ms) \ No newline at end of file diff --git a/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-not_ignored.golden b/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-not_ignored.golden new file mode 100644 index 0000000000..51103b1373 --- /dev/null +++ b/testdata/ignore_errors/testdata/TestIgnoreErrorsOnTimeout-not_ignored.golden @@ -0,0 +1 @@ +task: [cmd-timeout-should-fail] sleep 10 diff --git a/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-cmd-should-fail-err-run.golden b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-cmd-should-fail-err-run.golden new file mode 100644 index 0000000000..c58721afd0 --- /dev/null +++ b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-cmd-should-fail-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "cmd-should-fail": exit status 1 \ No newline at end of file diff --git a/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-cmd-should-fail.golden b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-cmd-should-fail.golden new file mode 100644 index 0000000000..3fac99d21a --- /dev/null +++ b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-cmd-should-fail.golden @@ -0,0 +1 @@ +task: [cmd-should-fail] exit 1 diff --git a/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-cmd-should-pass.golden b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-cmd-should-pass.golden new file mode 100644 index 0000000000..dbbe76b32f --- /dev/null +++ b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-cmd-should-pass.golden @@ -0,0 +1 @@ +task: [cmd-should-pass] exit 1 diff --git a/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-task-should-fail-err-run.golden b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-task-should-fail-err-run.golden new file mode 100644 index 0000000000..6aa02fc8dd --- /dev/null +++ b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-task-should-fail-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "task-should-fail": exit status 1 \ No newline at end of file diff --git a/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-task-should-fail.golden b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-task-should-fail.golden new file mode 100644 index 0000000000..1b41081981 --- /dev/null +++ b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-task-should-fail.golden @@ -0,0 +1 @@ +task: [task-should-fail] exit 1 diff --git a/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-task-should-pass.golden b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-task-should-pass.golden new file mode 100644 index 0000000000..8115790663 --- /dev/null +++ b/testdata/ignore_errors/testdata/TestTaskIgnoreErrors-task-should-pass.golden @@ -0,0 +1 @@ +task: [task-should-pass] exit 1 diff --git a/testdata/ignore_nil_elements/cmds/testdata/TestIgnoreNilElements-nil_cmd.golden b/testdata/ignore_nil_elements/cmds/testdata/TestIgnoreNilElements-nil_cmd.golden new file mode 100644 index 0000000000..636fe9d21e --- /dev/null +++ b/testdata/ignore_nil_elements/cmds/testdata/TestIgnoreNilElements-nil_cmd.golden @@ -0,0 +1 @@ +string-slice-1 diff --git a/testdata/ignore_nil_elements/deps/testdata/TestIgnoreNilElements-nil_dep.golden b/testdata/ignore_nil_elements/deps/testdata/TestIgnoreNilElements-nil_dep.golden new file mode 100644 index 0000000000..636fe9d21e --- /dev/null +++ b/testdata/ignore_nil_elements/deps/testdata/TestIgnoreNilElements-nil_dep.golden @@ -0,0 +1 @@ +string-slice-1 diff --git a/testdata/ignore_nil_elements/includes/testdata/TestIgnoreNilElements-nil_include.golden b/testdata/ignore_nil_elements/includes/testdata/TestIgnoreNilElements-nil_include.golden new file mode 100644 index 0000000000..636fe9d21e --- /dev/null +++ b/testdata/ignore_nil_elements/includes/testdata/TestIgnoreNilElements-nil_include.golden @@ -0,0 +1 @@ +string-slice-1 diff --git a/testdata/ignore_nil_elements/preconditions/testdata/TestIgnoreNilElements-nil_precondition.golden b/testdata/ignore_nil_elements/preconditions/testdata/TestIgnoreNilElements-nil_precondition.golden new file mode 100644 index 0000000000..636fe9d21e --- /dev/null +++ b/testdata/ignore_nil_elements/preconditions/testdata/TestIgnoreNilElements-nil_precondition.golden @@ -0,0 +1 @@ +string-slice-1 diff --git a/testdata/include_with_vars/testdata/TestIncludedVars.golden b/testdata/include_with_vars/testdata/TestIncludedVars.golden new file mode 100644 index 0000000000..0b4eafb428 --- /dev/null +++ b/testdata/include_with_vars/testdata/TestIncludedVars.golden @@ -0,0 +1,12 @@ +task: [included1:task1] echo "VAR_1 is included1-var1" +VAR_1 is included1-var1 +task: [included1:task1] echo "VAR_2 is included-default-var2" +VAR_2 is included-default-var2 +task: [included2:task1] echo "VAR_1 is included2-var1" +VAR_1 is included2-var1 +task: [included2:task1] echo "VAR_2 is included-default-var2" +VAR_2 is included-default-var2 +task: [included3:task1] echo "VAR_1 is included-default-var1" +VAR_1 is included-default-var1 +task: [included3:task1] echo "VAR_2 is included-default-var2" +VAR_2 is included-default-var2 diff --git a/testdata/include_with_vars_multi_level/testdata/TestIncludedVarsMultiLevel.golden b/testdata/include_with_vars_multi_level/testdata/TestIncludedVarsMultiLevel.golden new file mode 100644 index 0000000000..f7a8ef5c1c --- /dev/null +++ b/testdata/include_with_vars_multi_level/testdata/TestIncludedVarsMultiLevel.golden @@ -0,0 +1,6 @@ +task: [lib:greet] echo 'Hello world' +Hello world +task: [foo:lib:greet] echo 'Hello foo' +Hello foo +task: [bar:lib:greet] echo 'Hello bar' +Hello bar diff --git a/testdata/included_taskfile_var_merging/testdata/TestIncludedTaskfileVarMerging-bar.golden b/testdata/included_taskfile_var_merging/testdata/TestIncludedTaskfileVarMerging-bar.golden new file mode 100644 index 0000000000..3dcbb895e6 --- /dev/null +++ b/testdata/included_taskfile_var_merging/testdata/TestIncludedTaskfileVarMerging-bar.golden @@ -0,0 +1,2 @@ +bar +{{.TEST_DIR}}/testdata/included_taskfile_var_merging/bar diff --git a/testdata/included_taskfile_var_merging/testdata/TestIncludedTaskfileVarMerging-foo.golden b/testdata/included_taskfile_var_merging/testdata/TestIncludedTaskfileVarMerging-foo.golden new file mode 100644 index 0000000000..268e1f2807 --- /dev/null +++ b/testdata/included_taskfile_var_merging/testdata/TestIncludedTaskfileVarMerging-foo.golden @@ -0,0 +1,2 @@ +foo +{{.TEST_DIR}}/testdata/included_taskfile_var_merging/foo diff --git a/testdata/includes/Taskfile.yml b/testdata/includes/Taskfile.yml index 8ed9e416de..42b0e8d064 100644 --- a/testdata/includes/Taskfile.yml +++ b/testdata/includes/Taskfile.yml @@ -29,4 +29,4 @@ tasks: gen: cmds: - - echo main > main.txt + - echo main diff --git a/testdata/includes/Taskfile2.yml b/testdata/includes/Taskfile2.yml index 858fb38bc7..4f9e7dc8d6 100644 --- a/testdata/includes/Taskfile2.yml +++ b/testdata/includes/Taskfile2.yml @@ -3,4 +3,4 @@ version: '3' tasks: gen: cmds: - - echo included_taskfile > included_taskfile.txt + - echo included_taskfile diff --git a/testdata/includes/Taskfile_darwin.yml b/testdata/includes/Taskfile_darwin.yml index 731c7d0cca..97d11e6690 100644 --- a/testdata/includes/Taskfile_darwin.yml +++ b/testdata/includes/Taskfile_darwin.yml @@ -1,4 +1,4 @@ version: '3' tasks: - gen: echo 'os' > os_include.txt + gen: echo 'os' diff --git a/testdata/includes/Taskfile_linux.yml b/testdata/includes/Taskfile_linux.yml index 731c7d0cca..97d11e6690 100644 --- a/testdata/includes/Taskfile_linux.yml +++ b/testdata/includes/Taskfile_linux.yml @@ -1,4 +1,4 @@ version: '3' tasks: - gen: echo 'os' > os_include.txt + gen: echo 'os' diff --git a/testdata/includes/Taskfile_windows.yml b/testdata/includes/Taskfile_windows.yml index 731c7d0cca..97d11e6690 100644 --- a/testdata/includes/Taskfile_windows.yml +++ b/testdata/includes/Taskfile_windows.yml @@ -1,4 +1,4 @@ version: '3' tasks: - gen: echo 'os' > os_include.txt + gen: echo 'os' diff --git a/testdata/includes/included/Taskfile.yml b/testdata/includes/included/Taskfile.yml index 93b82347a3..402b5b0b42 100644 --- a/testdata/includes/included/Taskfile.yml +++ b/testdata/includes/included/Taskfile.yml @@ -3,4 +3,4 @@ version: '3' tasks: gen: cmds: - - echo included_directory > included_directory.txt + - echo included_directory diff --git a/testdata/includes/module1/Taskfile.yml b/testdata/includes/module1/Taskfile.yml index 3659073e62..82574b85dd 100644 --- a/testdata/includes/module1/Taskfile.yml +++ b/testdata/includes/module1/Taskfile.yml @@ -3,8 +3,8 @@ version: '3' tasks: gen_dir: cmds: - - echo included_directory_without_dir > included_directory_without_dir.txt + - echo included_directory_without_dir gen_file: cmds: - - echo included_taskfile_without_dir > included_taskfile_without_dir.txt + - echo included_taskfile_without_dir diff --git a/testdata/includes/module2/Taskfile.yml b/testdata/includes/module2/Taskfile.yml index 09bbdb60bc..4e979d9647 100644 --- a/testdata/includes/module2/Taskfile.yml +++ b/testdata/includes/module2/Taskfile.yml @@ -1,10 +1,14 @@ version: '3' +vars: + DIR: + sh: basename "$(pwd)" + tasks: gen_dir: cmds: - - echo included_directory_with_dir > included_directory_with_dir.txt + - 'echo "{{.DIR}}: included_directory_with_dir"' gen_file: cmds: - - echo included_taskfile_with_dir > included_taskfile_with_dir.txt + - 'echo "{{.DIR}}: included_taskfile_with_dir"' diff --git a/testdata/includes/testdata/TestIncludes.golden b/testdata/includes/testdata/TestIncludes.golden new file mode 100644 index 0000000000..7da75b0f19 --- /dev/null +++ b/testdata/includes/testdata/TestIncludes.golden @@ -0,0 +1,16 @@ +task: [gen] echo main +main +task: [included:gen] echo included_directory +included_directory +task: [included_taskfile:gen] echo included_taskfile +included_taskfile +task: [included_without_dir:gen_file] echo included_taskfile_without_dir +included_taskfile_without_dir +task: [included_taskfile_without_dir:gen_dir] echo included_directory_without_dir +included_directory_without_dir +task: [included_with_dir:gen_file] echo "module2: included_taskfile_with_dir" +module2: included_taskfile_with_dir +task: [included_taskfile_with_dir:gen_dir] echo "module2: included_directory_with_dir" +module2: included_directory_with_dir +task: [included_os:gen] echo 'os' +os diff --git a/testdata/includes_call_root_task/Taskfile.yml b/testdata/includes_call_root_task/Taskfile.yml index 2637337820..ae7ba92dfb 100644 --- a/testdata/includes_call_root_task/Taskfile.yml +++ b/testdata/includes_call_root_task/Taskfile.yml @@ -6,4 +6,4 @@ includes: tasks: root-task: cmds: - - echo "root task" > root_task.txt + - echo "root task" diff --git a/testdata/includes_call_root_task/testdata/TestIncludesCallingRoot.golden b/testdata/includes_call_root_task/testdata/TestIncludesCallingRoot.golden new file mode 100644 index 0000000000..afb215c062 --- /dev/null +++ b/testdata/includes_call_root_task/testdata/TestIncludesCallingRoot.golden @@ -0,0 +1,2 @@ +task: [root-task] echo "root task" +root task diff --git a/testdata/includes_cycle/testdata/TestIncludeCycle-err-setup.golden b/testdata/includes_cycle/testdata/TestIncludeCycle-err-setup.golden new file mode 100644 index 0000000000..3e23692072 --- /dev/null +++ b/testdata/includes_cycle/testdata/TestIncludeCycle-err-setup.golden @@ -0,0 +1 @@ +task: include cycle detected between {{.TEST_DIR}}/testdata/includes_cycle/Taskfile.yml <--> {{.TEST_DIR}}/testdata/includes_cycle/one/Taskfile.yml \ No newline at end of file diff --git a/testdata/includes_cycle/testdata/TestIncludeCycle.golden b/testdata/includes_cycle/testdata/TestIncludeCycle.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/includes_deps/Taskfile2.yml b/testdata/includes_deps/Taskfile2.yml index c5e704a7f7..e0b18d5ae5 100644 --- a/testdata/includes_deps/Taskfile2.yml +++ b/testdata/includes_deps/Taskfile2.yml @@ -4,13 +4,13 @@ tasks: default: deps: [called_dep] cmds: - - echo "default" > default.txt + - echo "default" - task: called_task called_dep: cmds: - - echo "called_dep" > called_dep.txt + - echo "called_dep" called_task: cmds: - - echo "called_task" > called_task.txt + - echo "called_task" diff --git a/testdata/includes_deps/testdata/TestIncludesDependencies.golden b/testdata/includes_deps/testdata/TestIncludesDependencies.golden new file mode 100644 index 0000000000..9baa8821d0 --- /dev/null +++ b/testdata/includes_deps/testdata/TestIncludesDependencies.golden @@ -0,0 +1,6 @@ +task: [included:called_dep] echo "called_dep" +called_dep +task: [included:default] echo "default" +default +task: [included:called_task] echo "called_task" +called_task diff --git a/testdata/includes_empty/Taskfile2.yml b/testdata/includes_empty/Taskfile2.yml index 7e984ef771..bd7aa9052c 100644 --- a/testdata/includes_empty/Taskfile2.yml +++ b/testdata/includes_empty/Taskfile2.yml @@ -1,10 +1,9 @@ version: '3' vars: - FILE: file.txt CONTENT: default tasks: default: cmds: - - echo "{{.CONTENT}}" > {{.FILE}} + - echo "{{.CONTENT}}" diff --git a/testdata/includes_empty/testdata/TestIncludesEmptyMain.golden b/testdata/includes_empty/testdata/TestIncludesEmptyMain.golden new file mode 100644 index 0000000000..9200ec62a2 --- /dev/null +++ b/testdata/includes_empty/testdata/TestIncludesEmptyMain.golden @@ -0,0 +1,2 @@ +task: [included:default] echo "default" +default diff --git a/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten.golden b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten.golden new file mode 100644 index 0000000000..9c02a387f7 --- /dev/null +++ b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten.golden @@ -0,0 +1 @@ +gen from included diff --git a/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_can_call_entrypoint_tasks.golden b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_can_call_entrypoint_tasks.golden new file mode 100644 index 0000000000..a19cf7a66a --- /dev/null +++ b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_can_call_entrypoint_tasks.golden @@ -0,0 +1 @@ +from entrypoint diff --git a/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_multiple_same_task-err-setup.golden b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_multiple_same_task-err-setup.golden new file mode 100644 index 0000000000..566144d32e --- /dev/null +++ b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_multiple_same_task-err-setup.golden @@ -0,0 +1 @@ +task: Found multiple tasks (gen) included by "included"" \ No newline at end of file diff --git a/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_multiple_same_task.golden b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_multiple_same_task.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_nested.golden b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_nested.golden new file mode 100644 index 0000000000..1cbabdc085 --- /dev/null +++ b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_nested.golden @@ -0,0 +1 @@ +from nested diff --git a/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_with_default.golden b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_with_default.golden new file mode 100644 index 0000000000..83892e5cc6 --- /dev/null +++ b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_with_default.golden @@ -0,0 +1 @@ +default from included flatten diff --git a/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_with_deps.golden b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_with_deps.golden new file mode 100644 index 0000000000..1e1d9fae85 --- /dev/null +++ b/testdata/includes_flatten/testdata/TestIncludesFlatten-included_flatten_with_deps.golden @@ -0,0 +1,2 @@ +gen from included +with_deps from included diff --git a/testdata/includes_incorrect/testdata/TestIncludesIncorrect-err-setup.golden b/testdata/includes_incorrect/testdata/TestIncludesIncorrect-err-setup.golden new file mode 100644 index 0000000000..53ce8b837b --- /dev/null +++ b/testdata/includes_incorrect/testdata/TestIncludesIncorrect-err-setup.golden @@ -0,0 +1,2 @@ +task: Failed to parse testdata/includes_incorrect/incomplete.yml: +yaml: line 4: found unexpected end of stream \ No newline at end of file diff --git a/testdata/includes_incorrect/testdata/TestIncludesIncorrect.golden b/testdata/includes_incorrect/testdata/TestIncludesIncorrect.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/includes_interpolation/include/testdata/TestIncludesInterpolation-include.golden b/testdata/includes_interpolation/include/testdata/TestIncludesInterpolation-include.golden new file mode 100644 index 0000000000..a1a869c8c8 --- /dev/null +++ b/testdata/includes_interpolation/include/testdata/TestIncludesInterpolation-include.golden @@ -0,0 +1 @@ +include diff --git a/testdata/includes_unshadowed_default/file.txt b/testdata/includes_interpolation/include_with_dir/testdata/TestIncludesInterpolation-include_with_dir.golden similarity index 100% rename from testdata/includes_unshadowed_default/file.txt rename to testdata/includes_interpolation/include_with_dir/testdata/TestIncludesInterpolation-include_with_dir.golden diff --git a/testdata/includes_interpolation/include_with_env_variable/testdata/TestIncludesInterpolation-include_with_env_variable.golden b/testdata/includes_interpolation/include_with_env_variable/testdata/TestIncludesInterpolation-include_with_env_variable.golden new file mode 100644 index 0000000000..00fd8d541f --- /dev/null +++ b/testdata/includes_interpolation/include_with_env_variable/testdata/TestIncludesInterpolation-include_with_env_variable.golden @@ -0,0 +1 @@ +include_with_env_variable diff --git a/testdata/includes_missing_taskfile/testdata/TestIncludesMissingTaskfile-err-setup.golden b/testdata/includes_missing_taskfile/testdata/TestIncludesMissingTaskfile-err-setup.golden new file mode 100644 index 0000000000..5604c49b12 --- /dev/null +++ b/testdata/includes_missing_taskfile/testdata/TestIncludesMissingTaskfile-err-setup.golden @@ -0,0 +1,6 @@ +err: include must specify taskfile or dir +file: {{.TEST_DIR}}/testdata/includes_missing_taskfile/Taskfile.yml:5:5 + 3 | includes: + 4 |  GOBIN: +> 5 |  sh: echo $(go env GOPATH)/bin + | ^ \ No newline at end of file diff --git a/testdata/includes_missing_taskfile/testdata/TestIncludesMissingTaskfile.golden b/testdata/includes_missing_taskfile/testdata/TestIncludesMissingTaskfile.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/includes_multi_level/called_one.txt b/testdata/includes_multi_level/called_one.txt deleted file mode 100644 index 5626abf0f7..0000000000 --- a/testdata/includes_multi_level/called_one.txt +++ /dev/null @@ -1 +0,0 @@ -one diff --git a/testdata/includes_multi_level/called_three.txt b/testdata/includes_multi_level/called_three.txt deleted file mode 100644 index 2bdf67abb1..0000000000 --- a/testdata/includes_multi_level/called_three.txt +++ /dev/null @@ -1 +0,0 @@ -three diff --git a/testdata/includes_multi_level/called_two.txt b/testdata/includes_multi_level/called_two.txt deleted file mode 100644 index f719efd430..0000000000 --- a/testdata/includes_multi_level/called_two.txt +++ /dev/null @@ -1 +0,0 @@ -two diff --git a/testdata/includes_multi_level/one/Taskfile.yml b/testdata/includes_multi_level/one/Taskfile.yml index 80e1bfa6b9..13727de859 100644 --- a/testdata/includes_multi_level/one/Taskfile.yml +++ b/testdata/includes_multi_level/one/Taskfile.yml @@ -4,4 +4,4 @@ includes: 'two': ./two/ tasks: - default: echo one > called_one.txt + default: echo one diff --git a/testdata/includes_multi_level/one/two/Taskfile.yml b/testdata/includes_multi_level/one/two/Taskfile.yml index ed3930641f..a7dad804a6 100644 --- a/testdata/includes_multi_level/one/two/Taskfile.yml +++ b/testdata/includes_multi_level/one/two/Taskfile.yml @@ -4,4 +4,4 @@ includes: 'three': ./three/Taskfile.yml tasks: - default: echo two > called_two.txt + default: echo two diff --git a/testdata/includes_multi_level/one/two/three/Taskfile.yml b/testdata/includes_multi_level/one/two/three/Taskfile.yml index 8c49bc702b..f781783875 100644 --- a/testdata/includes_multi_level/one/two/three/Taskfile.yml +++ b/testdata/includes_multi_level/one/two/three/Taskfile.yml @@ -1,4 +1,4 @@ version: '3' tasks: - default: echo three > called_three.txt + default: echo three diff --git a/testdata/includes_multi_level/testdata/TestIncludesMultiLevel.golden b/testdata/includes_multi_level/testdata/TestIncludesMultiLevel.golden new file mode 100644 index 0000000000..2f5e8ad22a --- /dev/null +++ b/testdata/includes_multi_level/testdata/TestIncludesMultiLevel.golden @@ -0,0 +1,6 @@ +task: [one:default] echo one +one +task: [one:two:default] echo two +two +task: [one:two:three:default] echo three +three diff --git a/testdata/includes_optional/Taskfile.yml b/testdata/includes_optional/Taskfile.yml index 0791bdfe41..2ace537db3 100644 --- a/testdata/includes_optional/Taskfile.yml +++ b/testdata/includes_optional/Taskfile.yml @@ -8,4 +8,4 @@ includes: tasks: default: cmds: - - echo "called_dep" > called_dep.txt + - echo "called_dep" diff --git a/testdata/includes_optional/testdata/TestIncludesOptional.golden b/testdata/includes_optional/testdata/TestIncludesOptional.golden new file mode 100644 index 0000000000..50b38b7878 --- /dev/null +++ b/testdata/includes_optional/testdata/TestIncludesOptional.golden @@ -0,0 +1,2 @@ +task: [default] echo "called_dep" +called_dep diff --git a/testdata/includes_optional_explicit_false/testdata/TestIncludesOptionalExplicitFalse-err-setup.golden b/testdata/includes_optional_explicit_false/testdata/TestIncludesOptionalExplicitFalse-err-setup.golden new file mode 100644 index 0000000000..e4e867bdcc --- /dev/null +++ b/testdata/includes_optional_explicit_false/testdata/TestIncludesOptionalExplicitFalse-err-setup.golden @@ -0,0 +1 @@ +task: No Taskfile found at "{{.TEST_DIR}}/testdata/includes_optional_explicit_false/TaskfileOptional.yml" \ No newline at end of file diff --git a/testdata/includes_optional_explicit_false/testdata/TestIncludesOptionalExplicitFalse.golden b/testdata/includes_optional_explicit_false/testdata/TestIncludesOptionalExplicitFalse.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/includes_optional_implicit_false/testdata/TestIncludesOptionalImplicitFalse-err-setup.golden b/testdata/includes_optional_implicit_false/testdata/TestIncludesOptionalImplicitFalse-err-setup.golden new file mode 100644 index 0000000000..3287a45d10 --- /dev/null +++ b/testdata/includes_optional_implicit_false/testdata/TestIncludesOptionalImplicitFalse-err-setup.golden @@ -0,0 +1 @@ +task: No Taskfile found at "{{.TEST_DIR}}/testdata/includes_optional_implicit_false/TaskfileOptional.yml" \ No newline at end of file diff --git a/testdata/includes_optional_implicit_false/testdata/TestIncludesOptionalImplicitFalse.golden b/testdata/includes_optional_implicit_false/testdata/TestIncludesOptionalImplicitFalse.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/includes_rel_path/testdata/TestIncludesRelativePath-common-pwd.golden b/testdata/includes_rel_path/testdata/TestIncludesRelativePath-common-pwd.golden new file mode 100644 index 0000000000..fe61610a32 --- /dev/null +++ b/testdata/includes_rel_path/testdata/TestIncludesRelativePath-common-pwd.golden @@ -0,0 +1,2 @@ +task: [common:pwd] pwd +{{.TEST_DIR}}/testdata/includes_rel_path/common diff --git a/testdata/includes_rel_path/testdata/TestIncludesRelativePath-included-common-pwd.golden b/testdata/includes_rel_path/testdata/TestIncludesRelativePath-included-common-pwd.golden new file mode 100644 index 0000000000..e4ed565aaa --- /dev/null +++ b/testdata/includes_rel_path/testdata/TestIncludesRelativePath-included-common-pwd.golden @@ -0,0 +1,2 @@ +task: [included:common:pwd] pwd +{{.TEST_DIR}}/testdata/includes_rel_path/common diff --git a/testdata/includes_shadowed_default/Taskfile.yml b/testdata/includes_shadowed_default/Taskfile.yml index 5588c96212..82fffc6027 100644 --- a/testdata/includes_shadowed_default/Taskfile.yml +++ b/testdata/includes_shadowed_default/Taskfile.yml @@ -7,4 +7,4 @@ includes: tasks: included: cmds: - - echo "shadowed" > file.txt + - echo "shadowed" diff --git a/testdata/includes_shadowed_default/Taskfile2.yml b/testdata/includes_shadowed_default/Taskfile2.yml index e944799237..76dc19626b 100644 --- a/testdata/includes_shadowed_default/Taskfile2.yml +++ b/testdata/includes_shadowed_default/Taskfile2.yml @@ -3,4 +3,4 @@ version: '3' tasks: default: cmds: - - echo "included" > file.txt + - echo "included" diff --git a/testdata/includes_shadowed_default/file.txt b/testdata/includes_shadowed_default/file.txt deleted file mode 100644 index 92a8299c52..0000000000 --- a/testdata/includes_shadowed_default/file.txt +++ /dev/null @@ -1 +0,0 @@ -shadowed diff --git a/testdata/includes_shadowed_default/testdata/TestIncludesShadowedDefault.golden b/testdata/includes_shadowed_default/testdata/TestIncludesShadowedDefault.golden new file mode 100644 index 0000000000..fac47663de --- /dev/null +++ b/testdata/includes_shadowed_default/testdata/TestIncludesShadowedDefault.golden @@ -0,0 +1,2 @@ +task: [included] echo "shadowed" +shadowed diff --git a/testdata/includes_unshadowed_default/Taskfile2.yml b/testdata/includes_unshadowed_default/Taskfile2.yml index e944799237..76dc19626b 100644 --- a/testdata/includes_unshadowed_default/Taskfile2.yml +++ b/testdata/includes_unshadowed_default/Taskfile2.yml @@ -3,4 +3,4 @@ version: '3' tasks: default: cmds: - - echo "included" > file.txt + - echo "included" diff --git a/testdata/includes_unshadowed_default/testdata/TestIncludesUnshadowedDefault.golden b/testdata/includes_unshadowed_default/testdata/TestIncludesUnshadowedDefault.golden new file mode 100644 index 0000000000..4453181763 --- /dev/null +++ b/testdata/includes_unshadowed_default/testdata/TestIncludesUnshadowedDefault.golden @@ -0,0 +1,2 @@ +task: [included:default] echo "included" +included diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-bar-err-run.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-bar-err-run.golden new file mode 100644 index 0000000000..f1fc961988 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-bar-err-run.golden @@ -0,0 +1 @@ +task: Task "bar" does not exist \ No newline at end of file diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-bar.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-bar.golden new file mode 100644 index 0000000000..56e8128e82 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-bar.golden @@ -0,0 +1 @@ +task: No tasks with description available. Try --list-all to list all tasks diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-foo.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-foo.golden new file mode 100644 index 0000000000..257cc5642c --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-foo.golden @@ -0,0 +1 @@ +foo diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-bar.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-bar.golden new file mode 100644 index 0000000000..5716ca5987 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-bar.golden @@ -0,0 +1 @@ +bar diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-foo-child.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-foo-child.golden new file mode 100644 index 0000000000..6e0bc347d7 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-foo-child.golden @@ -0,0 +1 @@ +foo:child diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-foo-err-run.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-foo-err-run.golden new file mode 100644 index 0000000000..5d8c0b6b5d --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-foo-err-run.golden @@ -0,0 +1 @@ +task: Task "included:foo" does not exist \ No newline at end of file diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-foo.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-foo.golden new file mode 100644 index 0000000000..56e8128e82 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-foo.golden @@ -0,0 +1 @@ +task: No tasks with description available. Try --list-all to list all tasks diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace-one-err-run.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace-one-err-run.golden new file mode 100644 index 0000000000..7501caad0d --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace-one-err-run.golden @@ -0,0 +1 @@ +task: Task "included:namespace:one" does not exist \ No newline at end of file diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace-one.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace-one.golden new file mode 100644 index 0000000000..56e8128e82 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace-one.golden @@ -0,0 +1 @@ +task: No tasks with description available. Try --list-all to list all tasks diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace-other-one.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace-other-one.golden new file mode 100644 index 0000000000..97a5185ef6 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace-other-one.golden @@ -0,0 +1 @@ +namespace-other:one diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace.golden new file mode 100644 index 0000000000..f6907897f6 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-included-namespace.golden @@ -0,0 +1 @@ +namespace diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace-other-one.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace-other-one.golden new file mode 100644 index 0000000000..97a5185ef6 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace-other-one.golden @@ -0,0 +1 @@ +namespace-other:one diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace-two-err-run.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace-two-err-run.golden new file mode 100644 index 0000000000..1bb42e7a5c --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace-two-err-run.golden @@ -0,0 +1 @@ +task: Task "namespace:two" does not exist \ No newline at end of file diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace-two.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace-two.golden new file mode 100644 index 0000000000..56e8128e82 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace-two.golden @@ -0,0 +1 @@ +task: No tasks with description available. Try --list-all to list all tasks diff --git a/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace.golden b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace.golden new file mode 100644 index 0000000000..f6907897f6 --- /dev/null +++ b/testdata/includes_with_excludes/testdata/TestIncludesWithExclude-namespace.golden @@ -0,0 +1 @@ +namespace diff --git a/testdata/includes_yaml/Custom.ext b/testdata/includes_yaml/Custom.ext index 5264ca1ea7..a689e9afca 100644 --- a/testdata/includes_yaml/Custom.ext +++ b/testdata/includes_yaml/Custom.ext @@ -13,4 +13,4 @@ tasks: gen: cmds: - - echo main > main.txt + - echo main diff --git a/testdata/includes_yaml/included/Taskfile.yaml b/testdata/includes_yaml/included/Taskfile.yaml index 6e3f496237..5c58bda211 100644 --- a/testdata/includes_yaml/included/Taskfile.yaml +++ b/testdata/includes_yaml/included/Taskfile.yaml @@ -3,4 +3,4 @@ version: '3' tasks: gen: cmds: - - echo included_with_yaml_extension > included_with_yaml_extension.txt + - echo included_with_yaml_extension diff --git a/testdata/includes_yaml/included/custom.yaml b/testdata/includes_yaml/included/custom.yaml index f72e199f80..6c6582e136 100644 --- a/testdata/includes_yaml/included/custom.yaml +++ b/testdata/includes_yaml/included/custom.yaml @@ -3,4 +3,4 @@ version: '3' tasks: gen: cmds: - - echo included_with_custom_file > included_with_custom_file.txt + - echo included_with_custom_file diff --git a/testdata/includes_yaml/testdata/TestIncludesFromCustomTaskfile.golden b/testdata/includes_yaml/testdata/TestIncludesFromCustomTaskfile.golden new file mode 100644 index 0000000000..9609c8c13d --- /dev/null +++ b/testdata/includes_yaml/testdata/TestIncludesFromCustomTaskfile.golden @@ -0,0 +1,6 @@ +task: [gen] echo main +main +task: [included:gen] echo included_with_yaml_extension +included_with_yaml_extension +task: [custom:gen] echo included_with_custom_file +included_with_custom_file diff --git a/testdata/internal_task/testdata/TestIncludesInternal-included_internal_direct-err-run.golden b/testdata/internal_task/testdata/TestIncludesInternal-included_internal_direct-err-run.golden new file mode 100644 index 0000000000..c3d40dc766 --- /dev/null +++ b/testdata/internal_task/testdata/TestIncludesInternal-included_internal_direct-err-run.golden @@ -0,0 +1 @@ +task: Task "included:task-3" does not exist \ No newline at end of file diff --git a/testdata/internal_task/testdata/TestIncludesInternal-included_internal_direct.golden b/testdata/internal_task/testdata/TestIncludesInternal-included_internal_direct.golden new file mode 100644 index 0000000000..56e8128e82 --- /dev/null +++ b/testdata/internal_task/testdata/TestIncludesInternal-included_internal_direct.golden @@ -0,0 +1 @@ +task: No tasks with description available. Try --list-all to list all tasks diff --git a/testdata/internal_task/testdata/TestIncludesInternal-included_internal_task_via_dep.golden b/testdata/internal_task/testdata/TestIncludesInternal-included_internal_task_via_dep.golden new file mode 100644 index 0000000000..8ab686eafe --- /dev/null +++ b/testdata/internal_task/testdata/TestIncludesInternal-included_internal_task_via_dep.golden @@ -0,0 +1 @@ +Hello, World! diff --git a/testdata/internal_task/testdata/TestIncludesInternal-included_internal_task_via_task.golden b/testdata/internal_task/testdata/TestIncludesInternal-included_internal_task_via_task.golden new file mode 100644 index 0000000000..8ab686eafe --- /dev/null +++ b/testdata/internal_task/testdata/TestIncludesInternal-included_internal_task_via_task.golden @@ -0,0 +1 @@ +Hello, World! diff --git a/testdata/internal_task/testdata/TestInternalTask-internal_direct-err-run.golden b/testdata/internal_task/testdata/TestInternalTask-internal_direct-err-run.golden new file mode 100644 index 0000000000..7bdf8521df --- /dev/null +++ b/testdata/internal_task/testdata/TestInternalTask-internal_direct-err-run.golden @@ -0,0 +1 @@ +task: Task "task-3" is internal \ No newline at end of file diff --git a/testdata/internal_task/testdata/TestInternalTask-internal_direct.golden b/testdata/internal_task/testdata/TestInternalTask-internal_direct.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/internal_task/testdata/TestInternalTask-internal_task_via_dep.golden b/testdata/internal_task/testdata/TestInternalTask-internal_task_via_dep.golden new file mode 100644 index 0000000000..8ab686eafe --- /dev/null +++ b/testdata/internal_task/testdata/TestInternalTask-internal_task_via_dep.golden @@ -0,0 +1 @@ +Hello, World! diff --git a/testdata/internal_task/testdata/TestInternalTask-internal_task_via_task.golden b/testdata/internal_task/testdata/TestInternalTask-internal_task_via_task.golden new file mode 100644 index 0000000000..8ab686eafe --- /dev/null +++ b/testdata/internal_task/testdata/TestInternalTask-internal_task_via_task.golden @@ -0,0 +1 @@ +Hello, World! diff --git a/testdata/method_invalid/testdata/TestFingerprintVarMethod-an_invalid_method_doesn't_fail_a_run_that_skips_fingerprinting.golden b/testdata/method_invalid/testdata/TestFingerprintVarMethod-an_invalid_method_doesn't_fail_a_run_that_skips_fingerprinting.golden new file mode 100644 index 0000000000..c6df3f3d85 --- /dev/null +++ b/testdata/method_invalid/testdata/TestFingerprintVarMethod-an_invalid_method_doesn't_fail_a_run_that_skips_fingerprinting.golden @@ -0,0 +1,2 @@ +task: [build] echo "cs=[]" +cs=[] diff --git a/testdata/method_invalid/testdata/TestFingerprintVarMethod-an_invalid_method_is_still_reported_by_the_up-to-date_check-err-run.golden b/testdata/method_invalid/testdata/TestFingerprintVarMethod-an_invalid_method_is_still_reported_by_the_up-to-date_check-err-run.golden new file mode 100644 index 0000000000..63c1524b62 --- /dev/null +++ b/testdata/method_invalid/testdata/TestFingerprintVarMethod-an_invalid_method_is_still_reported_by_the_up-to-date_check-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "build": task: invalid method "checksums" \ No newline at end of file diff --git a/testdata/method_invalid/testdata/TestFingerprintVarMethod-an_invalid_method_is_still_reported_by_the_up-to-date_check.golden b/testdata/method_invalid/testdata/TestFingerprintVarMethod-an_invalid_method_is_still_reported_by_the_up-to-date_check.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/method_taskfile_none/testdata/TestFingerprintVarMethod-no_variable_is_injected_when_the_effective_method_is_none.golden b/testdata/method_taskfile_none/testdata/TestFingerprintVarMethod-no_variable_is_injected_when_the_effective_method_is_none.golden new file mode 100644 index 0000000000..9791272833 --- /dev/null +++ b/testdata/method_taskfile_none/testdata/TestFingerprintVarMethod-no_variable_is_injected_when_the_effective_method_is_none.golden @@ -0,0 +1,2 @@ +task: [build] echo "cs=" +cs= diff --git a/testdata/method_taskfile_timestamp/testdata/TestFingerprintVarMethod-TIMESTAMP_is_injected_when_the_method_is_inherited_from_the_Taskfile.golden b/testdata/method_taskfile_timestamp/testdata/TestFingerprintVarMethod-TIMESTAMP_is_injected_when_the_method_is_inherited_from_the_Taskfile.golden new file mode 100644 index 0000000000..082063280b --- /dev/null +++ b/testdata/method_taskfile_timestamp/testdata/TestFingerprintVarMethod-TIMESTAMP_is_injected_when_the_method_is_inherited_from_the_Taskfile.golden @@ -0,0 +1,2 @@ +task: [build] echo "ts=2024-01-01 00:00:00 +0000 UTC" +ts=2024-01-01 00:00:00 +0000 UTC diff --git a/testdata/output_group/testdata/TestOutputGroup.golden b/testdata/output_group/testdata/TestOutputGroup.golden new file mode 100644 index 0000000000..b2d39bbf85 --- /dev/null +++ b/testdata/output_group/testdata/TestOutputGroup.golden @@ -0,0 +1,8 @@ +task: [hello] echo 'Hello!' +::group::hello +Hello! +::endgroup:: +task: [bye] echo 'Bye!' +::group::bye +Bye! +::endgroup:: diff --git a/testdata/output_group_error_only/testdata/TestOutputGroupErrorOnlyShowsOutputOnFailure-err-run.golden b/testdata/output_group_error_only/testdata/TestOutputGroupErrorOnlyShowsOutputOnFailure-err-run.golden new file mode 100644 index 0000000000..2b5b8372ed --- /dev/null +++ b/testdata/output_group_error_only/testdata/TestOutputGroupErrorOnlyShowsOutputOnFailure-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "failing": exit status 1 \ No newline at end of file diff --git a/testdata/output_group_error_only/testdata/TestOutputGroupErrorOnlyShowsOutputOnFailure.golden b/testdata/output_group_error_only/testdata/TestOutputGroupErrorOnlyShowsOutputOnFailure.golden new file mode 100644 index 0000000000..b34fac2d26 --- /dev/null +++ b/testdata/output_group_error_only/testdata/TestOutputGroupErrorOnlyShowsOutputOnFailure.golden @@ -0,0 +1 @@ +failing-output diff --git a/testdata/output_group_error_only/testdata/TestOutputGroupErrorOnlySwallowsOutputOnSuccess.golden b/testdata/output_group_error_only/testdata/TestOutputGroupErrorOnlySwallowsOutputOnSuccess.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/platforms/testdata/TestPlatforms.golden b/testdata/platforms/testdata/TestPlatforms.golden new file mode 100644 index 0000000000..2fa20cc338 --- /dev/null +++ b/testdata/platforms/testdata/TestPlatforms.golden @@ -0,0 +1,2 @@ +task: [build-{{.GOOS}}] echo 'Running task on {{.GOOS}}' +Running task on {{.GOOS}} diff --git a/testdata/run/Taskfile.yml b/testdata/run/Taskfile.yml index c9591c5967..2f4fb39cfe 100644 --- a/testdata/run/Taskfile.yml +++ b/testdata/run/Taskfile.yml @@ -3,7 +3,6 @@ run: when_changed tasks: generate-hash: - - rm -f hash.txt - task: input-content vars: { CONTENT: '1' } - task: input-content @@ -16,20 +15,19 @@ tasks: - task: create-output vars: { CONTENT: '1' } cmds: - - echo {{.CONTENT}} >> hash.txt + - echo {{.CONTENT}} create-output: run: once cmds: - - echo starting {{.CONTENT}} >> hash.txt + - echo starting {{.CONTENT}} deploy: cmds: - - rm -rf wildcard.txt - task: deploy:infra - task: deploy:js - task: deploy:go deploy:*: run: once - cmd: echo "Deploy {{index .MATCH 0}}" >> wildcard.txt + cmd: echo "Deploy {{index .MATCH 0}}" diff --git a/testdata/run/testdata/TestRunOnlyRunsJobsHashOnce.golden b/testdata/run/testdata/TestRunOnlyRunsJobsHashOnce.golden new file mode 100644 index 0000000000..0510731a2c --- /dev/null +++ b/testdata/run/testdata/TestRunOnlyRunsJobsHashOnce.golden @@ -0,0 +1,6 @@ +task: [create-output] echo starting 1 +starting 1 +task: [input-content] echo 1 +1 +task: [input-content] echo 2 +2 diff --git a/testdata/run/testdata/TestRunOnlyRunsJobsHashOnceWithWildcard.golden b/testdata/run/testdata/TestRunOnlyRunsJobsHashOnceWithWildcard.golden new file mode 100644 index 0000000000..4158d0743f --- /dev/null +++ b/testdata/run/testdata/TestRunOnlyRunsJobsHashOnceWithWildcard.golden @@ -0,0 +1,6 @@ +task: [deploy:infra] echo "Deploy infra" +Deploy infra +task: [deploy:js] echo "Deploy js" +Deploy js +task: [deploy:go] echo "Deploy go" +Deploy go diff --git a/testdata/run_once_failure/testdata/TestRunOnceSharedFailurePropagates-err-run.golden b/testdata/run_once_failure/testdata/TestRunOnceSharedFailurePropagates-err-run.golden new file mode 100644 index 0000000000..fde3973b23 --- /dev/null +++ b/testdata/run_once_failure/testdata/TestRunOnceSharedFailurePropagates-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "default": task: Failed to run task "shared": exit status 1 \ No newline at end of file diff --git a/testdata/run_once_failure/testdata/TestRunOnceSharedFailurePropagates.golden b/testdata/run_once_failure/testdata/TestRunOnceSharedFailurePropagates.golden new file mode 100644 index 0000000000..1c33a71de4 --- /dev/null +++ b/testdata/run_once_failure/testdata/TestRunOnceSharedFailurePropagates.golden @@ -0,0 +1 @@ +shared ran diff --git a/testdata/run_once_shared_deps/testdata/TestRunOnceSharedDeps.golden b/testdata/run_once_shared_deps/testdata/TestRunOnceSharedDeps.golden new file mode 100644 index 0000000000..7030e35240 --- /dev/null +++ b/testdata/run_once_shared_deps/testdata/TestRunOnceSharedDeps.golden @@ -0,0 +1,6 @@ +build a +build b +build library +task: [service-a:build] echo "build a" +task: [service-b:build] echo "build b" +task: [service-x:library:build] echo "build library" diff --git a/testdata/run_once_timeout/testdata/TestRunOnceJoinerHonorsItsOwnTimeout-err-run.golden b/testdata/run_once_timeout/testdata/TestRunOnceJoinerHonorsItsOwnTimeout-err-run.golden new file mode 100644 index 0000000000..ec5d398b11 --- /dev/null +++ b/testdata/run_once_timeout/testdata/TestRunOnceJoinerHonorsItsOwnTimeout-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "default": task: Failed to run task "joiner": task: [joiner] command timeout exceeded (500ms) \ No newline at end of file diff --git a/testdata/run_once_timeout/testdata/TestRunOnceJoinerHonorsItsOwnTimeout.golden b/testdata/run_once_timeout/testdata/TestRunOnceJoinerHonorsItsOwnTimeout.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/run_when_changed/testdata/TestRunWhenChanged.golden b/testdata/run_when_changed/testdata/TestRunWhenChanged.golden new file mode 100644 index 0000000000..274f869ea7 --- /dev/null +++ b/testdata/run_when_changed/testdata/TestRunWhenChanged.golden @@ -0,0 +1,3 @@ +login server=fubar user=fubar +login server=foo user=foo +login server=bar user=bar diff --git a/testdata/shopts/command_level/testdata/TestBashShellOptsCommandLevel.golden b/testdata/shopts/command_level/testdata/TestBashShellOptsCommandLevel.golden new file mode 100644 index 0000000000..b3371bee76 --- /dev/null +++ b/testdata/shopts/command_level/testdata/TestBashShellOptsCommandLevel.golden @@ -0,0 +1 @@ +globstar on diff --git a/testdata/shopts/command_level/testdata/TestPOSIXShellOptsCommandLevel.golden b/testdata/shopts/command_level/testdata/TestPOSIXShellOptsCommandLevel.golden new file mode 100644 index 0000000000..3d81db8387 --- /dev/null +++ b/testdata/shopts/command_level/testdata/TestPOSIXShellOptsCommandLevel.golden @@ -0,0 +1 @@ +pipefail on diff --git a/testdata/shopts/global_level/testdata/TestBashShellOptsGlobalLevel.golden b/testdata/shopts/global_level/testdata/TestBashShellOptsGlobalLevel.golden new file mode 100644 index 0000000000..b3371bee76 --- /dev/null +++ b/testdata/shopts/global_level/testdata/TestBashShellOptsGlobalLevel.golden @@ -0,0 +1 @@ +globstar on diff --git a/testdata/shopts/global_level/testdata/TestPOSIXShellOptsGlobalLevel.golden b/testdata/shopts/global_level/testdata/TestPOSIXShellOptsGlobalLevel.golden new file mode 100644 index 0000000000..3d81db8387 --- /dev/null +++ b/testdata/shopts/global_level/testdata/TestPOSIXShellOptsGlobalLevel.golden @@ -0,0 +1 @@ +pipefail on diff --git a/testdata/shopts/task_level/testdata/TestBashShellOptsTaskLevel.golden b/testdata/shopts/task_level/testdata/TestBashShellOptsTaskLevel.golden new file mode 100644 index 0000000000..b3371bee76 --- /dev/null +++ b/testdata/shopts/task_level/testdata/TestBashShellOptsTaskLevel.golden @@ -0,0 +1 @@ +globstar on diff --git a/testdata/shopts/task_level/testdata/TestPOSIXShellOptsTaskLevel.golden b/testdata/shopts/task_level/testdata/TestPOSIXShellOptsTaskLevel.golden new file mode 100644 index 0000000000..3d81db8387 --- /dev/null +++ b/testdata/shopts/task_level/testdata/TestPOSIXShellOptsTaskLevel.golden @@ -0,0 +1 @@ +pipefail on diff --git a/testdata/short_task_notation/testdata/TestShortTaskNotation.golden b/testdata/short_task_notation/testdata/TestShortTaskNotation.golden new file mode 100644 index 0000000000..4a6487275d --- /dev/null +++ b/testdata/short_task_notation/testdata/TestShortTaskNotation.golden @@ -0,0 +1,3 @@ +string-slice-1 +string-slice-2 +string diff --git a/testdata/silent/testdata/TestSilence-chatty.golden b/testdata/silent/testdata/TestSilence-chatty.golden new file mode 100644 index 0000000000..01dd71494b --- /dev/null +++ b/testdata/silent/testdata/TestSilence-chatty.golden @@ -0,0 +1 @@ +task: [chatty] exit 0 diff --git a/testdata/silent/testdata/TestSilence-silent.golden b/testdata/silent/testdata/TestSilence-silent.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/silent/testdata/TestSilence-task-test-chatty-calls-chatty-non-silenced.golden b/testdata/silent/testdata/TestSilence-task-test-chatty-calls-chatty-non-silenced.golden new file mode 100644 index 0000000000..a58fe6448f --- /dev/null +++ b/testdata/silent/testdata/TestSilence-task-test-chatty-calls-chatty-non-silenced.golden @@ -0,0 +1,2 @@ +task: [task-test-chatty-calls-chatty-non-silenced] exit 0 +task: [chatty] exit 0 diff --git a/testdata/silent/testdata/TestSilence-task-test-chatty-calls-chatty-silenced.golden b/testdata/silent/testdata/TestSilence-task-test-chatty-calls-chatty-silenced.golden new file mode 100644 index 0000000000..875b29da6e --- /dev/null +++ b/testdata/silent/testdata/TestSilence-task-test-chatty-calls-chatty-silenced.golden @@ -0,0 +1 @@ +task: [task-test-chatty-calls-chatty-silenced] exit 0 diff --git a/testdata/silent/testdata/TestSilence-task-test-chatty-calls-silenced-cmd.golden b/testdata/silent/testdata/TestSilence-task-test-chatty-calls-silenced-cmd.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/silent/testdata/TestSilence-task-test-is-chatty-depends-on-chatty-silenced.golden b/testdata/silent/testdata/TestSilence-task-test-is-chatty-depends-on-chatty-silenced.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/silent/testdata/TestSilence-task-test-is-silent-depends-on-chatty-non-silenced.golden b/testdata/silent/testdata/TestSilence-task-test-is-silent-depends-on-chatty-non-silenced.golden new file mode 100644 index 0000000000..01dd71494b --- /dev/null +++ b/testdata/silent/testdata/TestSilence-task-test-is-silent-depends-on-chatty-non-silenced.golden @@ -0,0 +1 @@ +task: [chatty] exit 0 diff --git a/testdata/silent/testdata/TestSilence-task-test-is-silent-depends-on-chatty-silenced.golden b/testdata/silent/testdata/TestSilence-task-test-is-silent-depends-on-chatty-silenced.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/silent/testdata/TestSilence-task-test-no-cmds-calls-chatty-silenced.golden b/testdata/silent/testdata/TestSilence-task-test-no-cmds-calls-chatty-silenced.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/silent/testdata/TestSilence-task-test-silent-calls-chatty-non-silenced.golden b/testdata/silent/testdata/TestSilence-task-test-silent-calls-chatty-non-silenced.golden new file mode 100644 index 0000000000..01dd71494b --- /dev/null +++ b/testdata/silent/testdata/TestSilence-task-test-silent-calls-chatty-non-silenced.golden @@ -0,0 +1 @@ +task: [chatty] exit 0 diff --git a/testdata/silent/testdata/TestSilence-task-test-silent-calls-chatty-silenced.golden b/testdata/silent/testdata/TestSilence-task-test-silent-calls-chatty-silenced.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/single_cmd_dep/Taskfile.yml b/testdata/single_cmd_dep/Taskfile.yml index 3023d2b1b5..de7a1f40e7 100644 --- a/testdata/single_cmd_dep/Taskfile.yml +++ b/testdata/single_cmd_dep/Taskfile.yml @@ -3,6 +3,6 @@ version: "3" tasks: foo: deps: [bar] - cmd: echo foo > foo.txt + cmd: echo foo - bar: echo bar > bar.txt + bar: echo bar diff --git a/testdata/single_cmd_dep/testdata/TestSingleCmdDep.golden b/testdata/single_cmd_dep/testdata/TestSingleCmdDep.golden new file mode 100644 index 0000000000..e35b11e1b0 --- /dev/null +++ b/testdata/single_cmd_dep/testdata/TestSingleCmdDep.golden @@ -0,0 +1,4 @@ +task: [bar] echo bar +bar +task: [foo] echo foo +foo diff --git a/testdata/split_args/testdata/TestSplitArgs.golden b/testdata/split_args/testdata/TestSplitArgs.golden new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/testdata/split_args/testdata/TestSplitArgs.golden @@ -0,0 +1 @@ +3 diff --git a/testdata/status_vars/testdata/TestStatusVariables-build-checksum.golden b/testdata/status_vars/testdata/TestStatusVariables-build-checksum.golden new file mode 100644 index 0000000000..083c03d93d --- /dev/null +++ b/testdata/status_vars/testdata/TestStatusVariables-build-checksum.golden @@ -0,0 +1,3 @@ +task: "build-checksum" started +task: status command echo "3e464c4b03f4b65d740e1e130d4d108a" exited zero +task: "build-checksum" finished diff --git a/testdata/status_vars/testdata/TestStatusVariables-build-ts.golden b/testdata/status_vars/testdata/TestStatusVariables-build-ts.golden new file mode 100644 index 0000000000..b687ecae83 --- /dev/null +++ b/testdata/status_vars/testdata/TestStatusVariables-build-ts.golden @@ -0,0 +1,4 @@ +task: "build-ts" started +task: status command echo '1704067200' exited zero +task: status command echo '2024-01-01 00:00:00 +0000 UTC' exited zero +task: "build-ts" finished diff --git a/testdata/summary/task-with-summary.txt b/testdata/summary/testdata/TestSummary.golden similarity index 100% rename from testdata/summary/task-with-summary.txt rename to testdata/summary/testdata/TestSummary.golden diff --git a/testdata/taskfile_walk/foo/bar/testdata/TestTaskfileWalk-walk_from_sub_sub_directory.golden b/testdata/taskfile_walk/foo/bar/testdata/TestTaskfileWalk-walk_from_sub_sub_directory.golden new file mode 100644 index 0000000000..257cc5642c --- /dev/null +++ b/testdata/taskfile_walk/foo/bar/testdata/TestTaskfileWalk-walk_from_sub_sub_directory.golden @@ -0,0 +1 @@ +foo diff --git a/testdata/taskfile_walk/foo/testdata/TestTaskfileWalk-walk_from_sub_directory.golden b/testdata/taskfile_walk/foo/testdata/TestTaskfileWalk-walk_from_sub_directory.golden new file mode 100644 index 0000000000..257cc5642c --- /dev/null +++ b/testdata/taskfile_walk/foo/testdata/TestTaskfileWalk-walk_from_sub_directory.golden @@ -0,0 +1 @@ +foo diff --git a/testdata/taskfile_walk/testdata/TestTaskfileWalk-walk_from_root_directory.golden b/testdata/taskfile_walk/testdata/TestTaskfileWalk-walk_from_root_directory.golden new file mode 100644 index 0000000000..257cc5642c --- /dev/null +++ b/testdata/taskfile_walk/testdata/TestTaskfileWalk-walk_from_root_directory.golden @@ -0,0 +1 @@ +foo diff --git a/testdata/timeout/testdata/TestCommandTimeout-multiple_commands_with_timeout-err-run.golden b/testdata/timeout/testdata/TestCommandTimeout-multiple_commands_with_timeout-err-run.golden new file mode 100644 index 0000000000..3ba42a9591 --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeout-multiple_commands_with_timeout-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "multiple-cmds-timeout": task: [multiple-cmds-timeout] command timeout exceeded (1s) \ No newline at end of file diff --git a/testdata/timeout/testdata/TestCommandTimeout-multiple_commands_with_timeout.golden b/testdata/timeout/testdata/TestCommandTimeout-multiple_commands_with_timeout.golden new file mode 100644 index 0000000000..96d000eee5 --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeout-multiple_commands_with_timeout.golden @@ -0,0 +1,3 @@ +task: [multiple-cmds-timeout] echo "first" +first +task: [multiple-cmds-timeout] sleep 10 diff --git a/testdata/timeout/testdata/TestCommandTimeout-no_timeout.golden b/testdata/timeout/testdata/TestCommandTimeout-no_timeout.golden new file mode 100644 index 0000000000..de208dc464 --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeout-no_timeout.golden @@ -0,0 +1,2 @@ +task: [no-timeout] echo "no timeout" +no timeout diff --git a/testdata/timeout/testdata/TestCommandTimeout-timeout_exceeded-err-run.golden b/testdata/timeout/testdata/TestCommandTimeout-timeout_exceeded-err-run.golden new file mode 100644 index 0000000000..bc97752947 --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeout-timeout_exceeded-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "timeout-exceeded": task: [timeout-exceeded] command timeout exceeded (1s) \ No newline at end of file diff --git a/testdata/timeout/testdata/TestCommandTimeout-timeout_exceeded.golden b/testdata/timeout/testdata/TestCommandTimeout-timeout_exceeded.golden new file mode 100644 index 0000000000..07d461b529 --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeout-timeout_exceeded.golden @@ -0,0 +1 @@ +task: [timeout-exceeded] sleep 10 diff --git a/testdata/timeout/testdata/TestCommandTimeout-timeout_not_exceeded.golden b/testdata/timeout/testdata/TestCommandTimeout-timeout_not_exceeded.golden new file mode 100644 index 0000000000..393bc7f5cc --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeout-timeout_not_exceeded.golden @@ -0,0 +1,2 @@ +task: [timeout-not-exceeded] echo "quick command" +quick command diff --git a/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_declaring_no_timeout_is_not_blamed_for_one-err-run.golden b/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_declaring_no_timeout_is_not_blamed_for_one-err-run.golden new file mode 100644 index 0000000000..360326e99a --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_declaring_no_timeout_is_not_blamed_for_one-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "inherited-timeout": task: [inherited-timeout] command timeout exceeded (500ms) \ No newline at end of file diff --git a/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_declaring_no_timeout_is_not_blamed_for_one.golden b/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_declaring_no_timeout_is_not_blamed_for_one.golden new file mode 100644 index 0000000000..476a25aa2f --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_declaring_no_timeout_is_not_blamed_for_one.golden @@ -0,0 +1 @@ +task: [slow-without-timeout] sleep 10 diff --git a/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_is_not_blamed_for_a_timeout_it_never_reached-err-run.golden b/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_is_not_blamed_for_a_timeout_it_never_reached-err-run.golden new file mode 100644 index 0000000000..86b6d5240c --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_is_not_blamed_for_a_timeout_it_never_reached-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "larger-child-timeout": task: [larger-child-timeout] command timeout exceeded (500ms) \ No newline at end of file diff --git a/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_is_not_blamed_for_a_timeout_it_never_reached.golden b/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_is_not_blamed_for_a_timeout_it_never_reached.golden new file mode 100644 index 0000000000..6f0ecca7b8 --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeoutAttribution-a_command_is_not_blamed_for_a_timeout_it_never_reached.golden @@ -0,0 +1 @@ +task: [slow-with-larger-timeout] sleep 10 diff --git a/testdata/timeout/testdata/TestCommandTimeoutBoundsIfCondition-err-run.golden b/testdata/timeout/testdata/TestCommandTimeoutBoundsIfCondition-err-run.golden new file mode 100644 index 0000000000..5905e63032 --- /dev/null +++ b/testdata/timeout/testdata/TestCommandTimeoutBoundsIfCondition-err-run.golden @@ -0,0 +1 @@ +task: Failed to run task "slow-if-condition": task: [slow-if-condition] command timeout exceeded (500ms) \ No newline at end of file diff --git a/testdata/timeout/testdata/TestCommandTimeoutBoundsIfCondition.golden b/testdata/timeout/testdata/TestCommandTimeoutBoundsIfCondition.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/timestamp/testdata/TestStatusTimestamp-first_run.golden b/testdata/timestamp/testdata/TestStatusTimestamp-first_run.golden new file mode 100644 index 0000000000..b1ce73317f --- /dev/null +++ b/testdata/timestamp/testdata/TestStatusTimestamp-first_run.golden @@ -0,0 +1 @@ +task: [build] cp ./source.txt ./generated.txt diff --git a/testdata/timestamp/testdata/TestStatusTimestamp-re-run_after_generated_file_removed.golden b/testdata/timestamp/testdata/TestStatusTimestamp-re-run_after_generated_file_removed.golden new file mode 100644 index 0000000000..b1ce73317f --- /dev/null +++ b/testdata/timestamp/testdata/TestStatusTimestamp-re-run_after_generated_file_removed.golden @@ -0,0 +1 @@ +task: [build] cp ./source.txt ./generated.txt diff --git a/testdata/timestamp/testdata/TestStatusTimestamp-up_to_date.golden b/testdata/timestamp/testdata/TestStatusTimestamp-up_to_date.golden new file mode 100644 index 0000000000..6bcd855798 --- /dev/null +++ b/testdata/timestamp/testdata/TestStatusTimestamp-up_to_date.golden @@ -0,0 +1 @@ +task: Task "build" is up to date diff --git a/testdata/user_working_dir/testdata/TestUserWorkingDirectory.golden b/testdata/user_working_dir/testdata/TestUserWorkingDirectory.golden new file mode 100644 index 0000000000..c92d64119d --- /dev/null +++ b/testdata/user_working_dir/testdata/TestUserWorkingDirectory.golden @@ -0,0 +1 @@ +{{.TEST_DIR}} diff --git a/testdata/user_working_dir_with_includes/testdata/TestUserWorkingDirectoryWithIncluded.golden b/testdata/user_working_dir_with_includes/testdata/TestUserWorkingDirectoryWithIncluded.golden new file mode 100644 index 0000000000..ac1c873061 --- /dev/null +++ b/testdata/user_working_dir_with_includes/testdata/TestUserWorkingDirectoryWithIncluded.golden @@ -0,0 +1 @@ +{{.TEST_DIR}}/testdata/user_working_dir_with_includes/somedir diff --git a/testdata/version/v1/testdata/TestDisplaysErrorOnVersion1Schema-err-setup.golden b/testdata/version/v1/testdata/TestDisplaysErrorOnVersion1Schema-err-setup.golden new file mode 100644 index 0000000000..a2ce457c65 --- /dev/null +++ b/testdata/version/v1/testdata/TestDisplaysErrorOnVersion1Schema-err-setup.golden @@ -0,0 +1,2 @@ +task: Invalid schema version in Taskfile "{{.TEST_DIR}}/testdata/version/v1/Taskfile.yml": +Schema version (1.0.0) no longer supported. Please use v3 or above \ No newline at end of file diff --git a/testdata/version/v1/testdata/TestDisplaysErrorOnVersion1Schema.golden b/testdata/version/v1/testdata/TestDisplaysErrorOnVersion1Schema.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/version/v1/testdata/TestTaskVersion-v1-err-setup.golden b/testdata/version/v1/testdata/TestTaskVersion-v1-err-setup.golden new file mode 100644 index 0000000000..a2ce457c65 --- /dev/null +++ b/testdata/version/v1/testdata/TestTaskVersion-v1-err-setup.golden @@ -0,0 +1,2 @@ +task: Invalid schema version in Taskfile "{{.TEST_DIR}}/testdata/version/v1/Taskfile.yml": +Schema version (1.0.0) no longer supported. Please use v3 or above \ No newline at end of file diff --git a/testdata/version/v1/testdata/TestTaskVersion-v1.golden b/testdata/version/v1/testdata/TestTaskVersion-v1.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/version/v2/testdata/TestDisplaysErrorOnVersion2Schema-err-setup.golden b/testdata/version/v2/testdata/TestDisplaysErrorOnVersion2Schema-err-setup.golden new file mode 100644 index 0000000000..b007b84b30 --- /dev/null +++ b/testdata/version/v2/testdata/TestDisplaysErrorOnVersion2Schema-err-setup.golden @@ -0,0 +1,2 @@ +task: Invalid schema version in Taskfile "{{.TEST_DIR}}/testdata/version/v2/Taskfile.yml": +Schema version (2.0.0) no longer supported. Please use v3 or above \ No newline at end of file diff --git a/testdata/version/v2/testdata/TestDisplaysErrorOnVersion2Schema.golden b/testdata/version/v2/testdata/TestDisplaysErrorOnVersion2Schema.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/version/v2/testdata/TestTaskVersion-v2-err-setup.golden b/testdata/version/v2/testdata/TestTaskVersion-v2-err-setup.golden new file mode 100644 index 0000000000..b007b84b30 --- /dev/null +++ b/testdata/version/v2/testdata/TestTaskVersion-v2-err-setup.golden @@ -0,0 +1,2 @@ +task: Invalid schema version in Taskfile "{{.TEST_DIR}}/testdata/version/v2/Taskfile.yml": +Schema version (2.0.0) no longer supported. Please use v3 or above \ No newline at end of file diff --git a/testdata/version/v2/testdata/TestTaskVersion-v2.golden b/testdata/version/v2/testdata/TestTaskVersion-v2.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/wildcards/testdata/TestWildcard-foo-wildcard-bar.golden b/testdata/wildcards/testdata/TestWildcard-foo-wildcard-bar.golden new file mode 100644 index 0000000000..e2781e68bd --- /dev/null +++ b/testdata/wildcards/testdata/TestWildcard-foo-wildcard-bar.golden @@ -0,0 +1 @@ +Hello foo bar diff --git a/testdata/wildcards/testdata/TestWildcard-matches-exactly--.golden b/testdata/wildcards/testdata/TestWildcard-matches-exactly--.golden new file mode 100644 index 0000000000..37963f7996 --- /dev/null +++ b/testdata/wildcards/testdata/TestWildcard-matches-exactly--.golden @@ -0,0 +1 @@ +I don't consume matches: [] diff --git a/testdata/wildcards/testdata/TestWildcard-no-match-err-run.golden b/testdata/wildcards/testdata/TestWildcard-no-match-err-run.golden new file mode 100644 index 0000000000..87b1f384a8 --- /dev/null +++ b/testdata/wildcards/testdata/TestWildcard-no-match-err-run.golden @@ -0,0 +1 @@ +task: Task "no-match" does not exist \ No newline at end of file diff --git a/testdata/wildcards/testdata/TestWildcard-no-match.golden b/testdata/wildcards/testdata/TestWildcard-no-match.golden new file mode 100644 index 0000000000..56e8128e82 --- /dev/null +++ b/testdata/wildcards/testdata/TestWildcard-no-match.golden @@ -0,0 +1 @@ +task: No tasks with description available. Try --list-all to list all tasks diff --git a/testdata/wildcards/testdata/TestWildcard-s-foo.golden b/testdata/wildcards/testdata/TestWildcard-s-foo.golden new file mode 100644 index 0000000000..571f4d0afb --- /dev/null +++ b/testdata/wildcards/testdata/TestWildcard-s-foo.golden @@ -0,0 +1 @@ +Starting foo diff --git a/testdata/wildcards/testdata/TestWildcard-start-foo.golden b/testdata/wildcards/testdata/TestWildcard-start-foo.golden new file mode 100644 index 0000000000..571f4d0afb --- /dev/null +++ b/testdata/wildcards/testdata/TestWildcard-start-foo.golden @@ -0,0 +1 @@ +Starting foo diff --git a/testdata/wildcards/testdata/TestWildcard-wildcard-foo-bar.golden b/testdata/wildcards/testdata/TestWildcard-wildcard-foo-bar.golden new file mode 100644 index 0000000000..08332b29f0 --- /dev/null +++ b/testdata/wildcards/testdata/TestWildcard-wildcard-foo-bar.golden @@ -0,0 +1 @@ +Hello foo-bar diff --git a/testdata/wildcards/testdata/TestWildcard-wildcard-foo.golden b/testdata/wildcards/testdata/TestWildcard-wildcard-foo.golden new file mode 100644 index 0000000000..dc1b2474ca --- /dev/null +++ b/testdata/wildcards/testdata/TestWildcard-wildcard-foo.golden @@ -0,0 +1 @@ +Hello foo