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).
This commit is contained in:
@@ -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:
|
||||
|
||||
18
dsl/ast.go
18
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)}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user