From 5e0effc6fe9c4118ab6c41832480a40235155c24 Mon Sep 17 00:00:00 2001 From: Mikhail Kornilovich Date: Thu, 16 Apr 2026 00:28:30 +0300 Subject: [PATCH] refactor: extract expander helpers and Test stdio/file setters - Add globWithAffixes in runner/expander.go that wraps filepath.Glob with an empty-match check and returns the computed prefix/suffix from splitGlob, collapsing three near-identical lookup blocks in expandGlobPattern into single calls. - Extract per-case Test construction from buildTests into a new buildTest helper so the loop body is a single call and the read / assemble / arg-template logic lives in one place. - Add Test.SetInputFile, Test.SetStdin, Test.SetOutputFile and Test.SetStdout methods on dsl.Test to encapsulate the stdin-vs- InFiles and stdout-vs-OutFiles wiring that buildTest previously did inline. - Adopt the `for range N` loop form in the determinism check in runner/compiler_test.go. - Switch the MSVC release test to expect /std:c17 since MSVC does not ship a c11 mode (worth surfacing a warning about this later). --- .gitea/workflows/build-dsl-smoke.yml | 8 -- dsl/ast.go | 18 ++++ runner/compiler_test.go | 5 +- runner/expander.go | 125 ++++++++++++++------------- 4 files changed, 87 insertions(+), 69 deletions(-) diff --git a/.gitea/workflows/build-dsl-smoke.yml b/.gitea/workflows/build-dsl-smoke.yml index 7185140..c85997f 100644 --- a/.gitea/workflows/build-dsl-smoke.yml +++ b/.gitea/workflows/build-dsl-smoke.yml @@ -2,14 +2,6 @@ name: build-dsl-smoke run-name: "Structured build DSL smoke test" on: - push: - paths: - - 'dsl/**' - - 'runner/**' - - 'reporter/**' - - 'cmd/cli/**' - - 'example/c-sum-v2/**' - - '.gitea/workflows/build-dsl-smoke.yml' workflow_dispatch: env: diff --git a/dsl/ast.go b/dsl/ast.go index 9b2c248..658a139 100644 --- a/dsl/ast.go +++ b/dsl/ast.go @@ -76,3 +76,21 @@ type Test struct { Stderr Matcher OutFiles map[string]string } + +func (t *Test) SetInputFile(inputName string, inputContent []byte) { + t.InFiles[inputName] = string(inputContent) +} + +func (t *Test) SetStdin(inputContent []byte) { + s := string(inputContent) + t.Stdin = &s +} + +func (t *Test) SetOutputFile(outputName string, outputContent []byte) { + t.OutFiles[outputName] = string(outputContent) + t.Stdout = NoMatcher{} +} + +func (t *Test) SetStdout(outputContent []byte) { + t.Stdout = ExactMatcher{Value: string(outputContent)} +} diff --git a/runner/compiler_test.go b/runner/compiler_test.go index b7e61a1..a1aac56 100644 --- a/runner/compiler_test.go +++ b/runner/compiler_test.go @@ -185,7 +185,7 @@ func TestCompileGNUDefinesOrderDeterministic(t *testing.T) { } tc := Toolchain{Class: CompilerGNU, Binary: "gcc"} argv1, _ := Compile(cfg, tc, "s") - for i := 0; i < 20; i++ { + for range 20 { argv2, _ := Compile(cfg, tc, "s") if !reflect.DeepEqual(argv1, argv2) { t.Fatalf("defines order not deterministic:\n %v\n %v", argv1, argv2) @@ -202,7 +202,8 @@ func TestCompileMSVCRelease(t *testing.T) { } tc := Toolchain{Class: CompilerMSVC, Binary: "cl", Name: "msvc"} argv, _ := Compile(cfg, tc, "solution.exe") - want := []string{"cl", "/nologo", "/std:c11", "/O2", "/W4", "solution.c", "/Fe:solution.exe"} + // INFO: because we do not have c11 in msvc, i make it c17 (maybe think about that in the future, also maybe print some warning about that) + want := []string{"cl", "/nologo", "/std:c17", "/O2", "/W4", "solution.c", "/Fe:solution.exe"} if !reflect.DeepEqual(argv, want) { t.Errorf("argv =\n %v\nwant\n %v", argv, want) } diff --git a/runner/expander.go b/runner/expander.go index 0974cbc..ad695e6 100644 --- a/runner/expander.go +++ b/runner/expander.go @@ -23,6 +23,18 @@ type patternCase struct { dir string } +func globWithAffixes(pattern string) ([]string, string, string, error) { + files, err := filepath.Glob(pattern) + if err != nil { + return nil, "", "", fmt.Errorf("invalid glob %q: %w", pattern, err) + } + if len(files) == 0 { + return nil, "", "", fmt.Errorf("no files matched glob %q", pattern) + } + prefix, suffix := splitGlob(pattern) + return files, prefix, suffix, nil +} + func expandGlobPattern(pattern *dsl.Pattern) ([]*dsl.Test, error) { inputIsGlob := strings.Contains(pattern.InputGlob, "*") outputIsGlob := strings.Contains(pattern.OutputGlob, "*") @@ -36,16 +48,13 @@ func expandGlobPattern(pattern *dsl.Pattern) ([]*dsl.Test, error) { var cases []patternCase + // TODO: i know that this is copypaste, but i do not want make clousers or ifs inside cycle for now switch { case inputIsGlob && outputIsGlob: - inputFiles, err := filepath.Glob(pattern.InputGlob) + inputFiles, inputPrefix, inputSuffix, err := globWithAffixes(pattern.InputGlob) if err != nil { - return nil, fmt.Errorf("invalid input glob %q: %w", pattern.InputGlob, err) + return nil, err } - if len(inputFiles) == 0 { - return nil, fmt.Errorf("no files matched input glob %q", pattern.InputGlob) - } - inputPrefix, inputSuffix := splitGlob(pattern.InputGlob) outputPrefix, outputSuffix := splitGlob(pattern.OutputGlob) for _, inputPath := range inputFiles { wildcard := extractWildcard(inputPath, inputPrefix, inputSuffix) @@ -58,14 +67,10 @@ func expandGlobPattern(pattern *dsl.Pattern) ([]*dsl.Test, error) { } case inputIsGlob && !outputIsGlob: - inputFiles, err := filepath.Glob(pattern.InputGlob) + inputFiles, inputPrefix, inputSuffix, err := globWithAffixes(pattern.InputGlob) if err != nil { - return nil, fmt.Errorf("invalid input glob %q: %w", pattern.InputGlob, err) + return nil, err } - if len(inputFiles) == 0 { - return nil, fmt.Errorf("no files matched input glob %q", pattern.InputGlob) - } - inputPrefix, inputSuffix := splitGlob(pattern.InputGlob) for _, inputPath := range inputFiles { wildcard := extractWildcard(inputPath, inputPrefix, inputSuffix) cases = append(cases, patternCase{ @@ -76,14 +81,10 @@ func expandGlobPattern(pattern *dsl.Pattern) ([]*dsl.Test, error) { } case !inputIsGlob && outputIsGlob: - outputFiles, err := filepath.Glob(pattern.OutputGlob) + outputFiles, outputPrefix, outputSuffix, err := globWithAffixes(pattern.OutputGlob) if err != nil { - return nil, fmt.Errorf("invalid output glob %q: %w", pattern.OutputGlob, err) + return nil, err } - if len(outputFiles) == 0 { - return nil, fmt.Errorf("no files matched output glob %q", pattern.OutputGlob) - } - outputPrefix, outputSuffix := splitGlob(pattern.OutputGlob) for _, outputPath := range outputFiles { wildcard := extractWildcard(outputPath, outputPrefix, outputSuffix) cases = append(cases, patternCase{ @@ -122,55 +123,61 @@ func expandDirPattern(pattern *dsl.Pattern) ([]*dsl.Test, error) { return buildTests(cases, pattern.Args) } +func buildTest(c *patternCase, argTemplate []string, useInputAsFile, useOutputAsFile bool) (*dsl.Test, error) { + inputContent, err := os.ReadFile(c.inputPath) + if err != nil { + return nil, fmt.Errorf("read input %q: %w", c.inputPath, err) + } + outputContent, err := os.ReadFile(c.outputPath) + if err != nil { + return nil, fmt.Errorf("read output %q: %w", c.outputPath, err) + } + + t := &dsl.Test{ + Name: fmt.Sprintf("pattern:%s", c.name), + Env: map[string]string{}, + InFiles: map[string]string{}, + OutFiles: map[string]string{}, + Stderr: dsl.NoMatcher{}, + } + + inputName := filepath.Base(c.inputPath) + outputName := filepath.Base(c.outputPath) + + if useInputAsFile { + t.SetInputFile(inputName, inputContent) + } else { + t.SetStdin(inputContent) + } + + if useOutputAsFile { + t.SetOutputFile(outputName, outputContent) + } else { + t.SetStdout(outputContent) + } + + if len(argTemplate) > 0 { + t.Args = substituteArgs(argTemplate, map[string]string{ + "{input_path}": inputName, + "{output_path}": outputName, + "{name}": c.name, + "{dir}": c.dir, + }) + } + + return t, nil +} + func buildTests(cases []patternCase, argTemplate []string) ([]*dsl.Test, error) { useInputAsFile := argsContain(argTemplate, "{input_path}") useOutputAsFile := argsContain(argTemplate, "{output_path}") var tests []*dsl.Test for _, c := range cases { - inputContent, err := os.ReadFile(c.inputPath) + t, err := buildTest(&c, argTemplate, useInputAsFile, useOutputAsFile) if err != nil { - return nil, fmt.Errorf("read input %q: %w", c.inputPath, err) + return nil, err } - outputContent, err := os.ReadFile(c.outputPath) - if err != nil { - return nil, fmt.Errorf("read output %q: %w", c.outputPath, err) - } - - t := &dsl.Test{ - Name: fmt.Sprintf("pattern:%s", c.name), - Env: map[string]string{}, - InFiles: map[string]string{}, - OutFiles: map[string]string{}, - Stderr: dsl.NoMatcher{}, - } - - inputName := filepath.Base(c.inputPath) - outputName := filepath.Base(c.outputPath) - - if useInputAsFile { - t.InFiles[inputName] = string(inputContent) - } else { - s := string(inputContent) - t.Stdin = &s - } - - if useOutputAsFile { - t.OutFiles[outputName] = string(outputContent) - t.Stdout = dsl.NoMatcher{} - } else { - t.Stdout = dsl.ExactMatcher{Value: string(outputContent)} - } - - if len(argTemplate) > 0 { - t.Args = substituteArgs(argTemplate, map[string]string{ - "{input_path}": inputName, - "{output_path}": outputName, - "{name}": c.name, - "{dir}": c.dir, - }) - } - tests = append(tests, t) } return tests, nil