Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
611b33b
chore: remove useless code
alexgarzao Oct 26, 2025
e324603
feat: testgen building end-to-end tests
alexgarzao Oct 26, 2025
2cdca99
chore: turn public GenValidations api
alexgarzao Oct 26, 2025
c2142d2
feat: testgen bulding validation code tests
alexgarzao Oct 26, 2025
f6bdb38
test: add generated validation code test files
alexgarzao Oct 26, 2025
581fa9a
chore: makefile rule to build generated tests and move to the correct
alexgarzao Oct 26, 2025
98b323a
chore: add DO NOT EDIT to all templates
alexgarzao Oct 26, 2025
5591942
chore: fix DO NOT EDIT header
alexgarzao Oct 26, 2025
47d14f6
chore: better func name
alexgarzao Oct 26, 2025
64050fa
doc: initial testgen readme
alexgarzao Oct 27, 2025
2ee054c
chore: remove useless code
alexgarzao Oct 27, 2025
abc9fe5
feat: testgen generating tests to check function validate output
alexgarzao Oct 27, 2025
f06c1cb
test: commit tests generated by testgen
alexgarzao Oct 27, 2025
7bb3ad6
Merge branch 'main' into 93-testgen-function-code-tests
alexgarzao Oct 28, 2025
e0325e6
chore: remove use slice[n] to avoid panic
alexgarzao Oct 28, 2025
4f9fa12
chore: better way to chain calls in the makefile
alexgarzao Oct 28, 2025
427754d
chore: use bitfield to exclude tests to be generated
alexgarzao Oct 29, 2025
b205a3a
chore: add govalidator tag to generated cmp perf tests
alexgarzao Oct 29, 2025
8a8c8f8
feat: generate perf cmp tests in testgen
alexgarzao Oct 29, 2025
c09649e
chore: add generated perf tests
alexgarzao Oct 29, 2025
2f9c8ed
chore: use fmt instead of log
alexgarzao Oct 29, 2025
fd656b3
refact: one func to exec template and format code
alexgarzao Oct 29, 2025
dc1fdf3
Merge branch 'main' into 93-refactor-testgen
alexgarzao Oct 29, 2025
8b5e2b3
Merge branch 'main' into 93-testgen-generating-cmp-perf-tests
alexgarzao Oct 29, 2025
4638353
Merge branch '93-testgen-generating-cmp-perf-tests' into 93-refactor-…
alexgarzao Oct 29, 2025
8d09241
Merge branch 'main' into 93-refactor-testgen
alexgarzao Nov 1, 2025
1771b6b
chore: add missed \n in error messages
alexgarzao Nov 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions testgen/execute_template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"bytes"
"fmt"
"go/format"
"os"
"text/template"
)

func ExecTemplate(tplName, tplFile, output string, data any) error {
tpl, err := os.ReadFile(tplFile)
if err != nil {
return fmt.Errorf("error reading %s: %s", tplFile, err)
}

tmpl, err := template.New(tplName).Parse(string(tpl))
if err != nil {
return fmt.Errorf("error parsing template %s: %s", tplFile, err)
}

code := new(bytes.Buffer)
if err := tmpl.Execute(code, data); err != nil {
return err
}

formattedCode, err := format.Source(code.Bytes())
if err != nil {
return err
}

if err := os.WriteFile(output, formattedCode, 0644); err != nil {
return err
}

return nil
}
62 changes: 19 additions & 43 deletions testgen/generate_cmp_perf_tests.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package main

import (
"bytes"
"fmt"
"go/format"
"log"
"os"
"strings"
"text/template"

"github.com/opencodeco/validgen/internal/common"
"golang.org/x/text/cases"
Expand All @@ -27,29 +22,36 @@ type CmpBenchTest struct {
ValidInput string
}

func generateComparativePerformanceTests() {
generateComparativePerformanceTest("cmp_perf_no_pointer_tests.tpl", "generated_cmp_perf_no_pointer_test.go", false)
generateComparativePerformanceTest("cmp_perf_pointer_tests.tpl", "generated_cmp_perf_pointer_test.go", true)
func generateComparativePerformanceTests() error {
if err := generateComparativePerformanceTest("cmp_perf_no_pointer_tests.tpl", "generated_cmp_perf_no_pointer_test.go", false); err != nil {
return err
}

if err := generateComparativePerformanceTest("cmp_perf_pointer_tests.tpl", "generated_cmp_perf_pointer_test.go", true); err != nil {
return err
}

return nil
}

func generateComparativePerformanceTest(tpl, dest string, pointer bool) {
log.Printf("Generating comparative performance tests file: tpl[%s] dest[%s] pointer[%v]\n", tpl, dest, pointer)
func generateComparativePerformanceTest(tplFile, outputFile string, pointer bool) error {
fmt.Printf("Generating comparative performance tests file: tplFile[%s] outputFile[%s] pointer[%v]\n", tplFile, outputFile, pointer)

benchTests := CmpBenchTests{}

for _, typeVal := range typesValidation {
if typeVal.validatorTag == "" {
log.Printf("Skipping tag %s: go-validator tag not defined\n", typeVal.tag)
fmt.Printf("Skipping tag %s: go-validator tag not defined\n", typeVal.tag)
continue
}

for _, testCase := range typeVal.testCases {
if testCase.excludeIf&cmpBenchTests != 0 {
log.Printf("Skipping test: tag %s type %s\n", typeVal.tag, testCase.typeClass)
fmt.Printf("Skipping test: tag %s type %s\n", typeVal.tag, testCase.typeClass)
continue
}
if testCase.excludeIf&noPointer != 0 && !pointer {
log.Printf("Skipping no pointer: tag %s type %s\n", typeVal.tag, testCase.typeClass)
fmt.Printf("Skipping no pointer: tag %s type %s\n", typeVal.tag, testCase.typeClass)
continue
}

Expand Down Expand Up @@ -86,39 +88,13 @@ func generateComparativePerformanceTest(tpl, dest string, pointer bool) {
}
}

log.Printf("%d test cases were generated\n", len(benchTests.Tests))

if err := benchTests.GenerateFile(tpl, dest); err != nil {
log.Fatalf("error generating comparative performance tests file %s", err)
}

log.Println("Generating done")
}
fmt.Printf("%d test cases were generated\n", len(benchTests.Tests))

func (cbt *CmpBenchTests) GenerateFile(tplFile, output string) error {
tpl, err := os.ReadFile(tplFile)
if err != nil {
return fmt.Errorf("error reading %s: %s", tplFile, err)
if err := ExecTemplate("BenchTest", tplFile, outputFile, benchTests); err != nil {
return fmt.Errorf("error generating comparative performance tests file %s", err)
}

tmpl, err := template.New("BenchTest").Parse(string(tpl))
if err != nil {
return fmt.Errorf("error parsing template %s: %s", tplFile, err)
}

code := new(bytes.Buffer)
if err := tmpl.Execute(code, cbt); err != nil {
return err
}

formattedCode, err := format.Source(code.Bytes())
if err != nil {
return err
}

if err := os.WriteFile(output, formattedCode, 0644); err != nil {
return err
}
fmt.Printf("Generating %s done\n", outputFile)

return nil
}
60 changes: 18 additions & 42 deletions testgen/generate_function_code_tests.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
package main

import (
"bytes"
"fmt"
"go/format"
"log"
"os"
"text/template"

"github.com/opencodeco/validgen/internal/analyzer"
"github.com/opencodeco/validgen/internal/codegenerator"
Expand Down Expand Up @@ -34,13 +29,20 @@ type FunctionCodeTestField struct {
Tag string
}

func generateFunctionCodeUnitTests() {
generateFunctionCodeTestsFile("function_code_test.tpl", "generated_function_code_no_pointer_test.go", false)
generateFunctionCodeTestsFile("function_code_test.tpl", "generated_function_code_pointer_test.go", true)
func generateFunctionCodeUnitTests() error {
if err := generateFunctionCodeUnitTest("function_code_test.tpl", "generated_function_code_no_pointer_test.go", false); err != nil {
return err
}

if err := generateFunctionCodeUnitTest("function_code_test.tpl", "generated_function_code_pointer_test.go", true); err != nil {
return err
}

return nil
}

func generateFunctionCodeTestsFile(tpl, dest string, pointer bool) {
log.Printf("Generating function code test file: tpl[%s] dest[%s] pointer[%v]\n", tpl, dest, pointer)
func generateFunctionCodeUnitTest(tplFile, outputFile string, pointer bool) error {
fmt.Printf("Generating function code test file: tplFile[%s] outputFile[%s] pointer[%v]\n", tplFile, outputFile, pointer)

funcName := "TestBuildFunctionCode"
if pointer {
Expand All @@ -66,7 +68,7 @@ func generateFunctionCodeTestsFile(tpl, dest string, pointer bool) {

for _, toGenerate := range typeValidation.testCases {
if toGenerate.excludeIf&noPointer != 0 && !pointer {
log.Printf("Skipping no pointer: tag %s type %s\n", typeValidation.tag, toGenerate.typeClass)
fmt.Printf("Skipping no pointer: tag %s type %s\n", typeValidation.tag, toGenerate.typeClass)
continue
}

Expand All @@ -84,7 +86,7 @@ func generateFunctionCodeTestsFile(tpl, dest string, pointer bool) {
fieldName := "Field" + cases.Title(language.Und).String(typeValidation.tag) + fieldType.ToStringName()
parsedValidation, err := analyzer.ParserValidation(validation)
if err != nil {
log.Fatalf("failed to parse validation %q: %v", validation, err)
return fmt.Errorf("failed to parse validation %q: %v", validation, err)
}

newTest.Fields = append(newTest.Fields, FunctionCodeTestField{
Expand All @@ -110,45 +112,19 @@ func generateFunctionCodeTestsFile(tpl, dest string, pointer bool) {

expectedCode, err := gv.BuildFuncValidatorCode()
if err != nil {
log.Fatalf("failed to build function validator code for struct %q: %v", newTest.StructName, err)
return fmt.Errorf("failed to build function validator code for struct %q: %v", newTest.StructName, err)
}

newTest.ExpectedCode = expectedCode

testCases.Tests = append(testCases.Tests, newTest)
}

if err := testCases.GenerateFile(tpl, dest); err != nil {
log.Fatalf("error generating function code tests file %s", err)
}

log.Printf("Generating %s done\n", dest)
}

func (tc *FunctionCodeTestCases) GenerateFile(tplFile, output string) error {
tpl, err := os.ReadFile(tplFile)
if err != nil {
return fmt.Errorf("error reading %s: %s", tplFile, err)
}

tmpl, err := template.New("ValidationCodeTests").Parse(string(tpl))
if err != nil {
return err
}

code := new(bytes.Buffer)
if err := tmpl.Execute(code, tc); err != nil {
return err
if err := ExecTemplate("FunctionCodeTests", tplFile, outputFile, testCases); err != nil {
return fmt.Errorf("generating function code tests file %s", err)
}

formattedCode, err := format.Source(code.Bytes())
if err != nil {
return err
}

if err := os.WriteFile(output, formattedCode, 0644); err != nil {
return err
}
fmt.Printf("Generating %s done\n", outputFile)

return nil
}
24 changes: 20 additions & 4 deletions testgen/generate_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,31 @@ package main

import (
"fmt"
"os"
)

func main() {
fmt.Println("Generating tests files")

generateValidationTypesEndToEndTests()
generateValidationCodeUnitTests()
generateFunctionCodeUnitTests()
generateComparativePerformanceTests()
if err := generateValidationTypesEndToEndTests(); err != nil {
fmt.Printf("error generating validation types end-to-end tests: %s\n", err)
os.Exit(1)
}

if err := generateValidationCodeUnitTests(); err != nil {
fmt.Printf("error generating validation code unit tests: %s\n", err)
os.Exit(1)
}

if err := generateFunctionCodeUnitTests(); err != nil {
fmt.Printf("error generating function code unit tests: %s\n", err)
os.Exit(1)
}

if err := generateComparativePerformanceTests(); err != nil {
fmt.Printf("error generating comparative performance tests: %s\n", err)
os.Exit(1)
}

fmt.Println("Generating done")
}
60 changes: 18 additions & 42 deletions testgen/generate_validation_code_tests.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
package main

import (
"bytes"
"fmt"
"go/format"
"log"
"os"
"text/template"

"github.com/opencodeco/validgen/internal/analyzer"
"github.com/opencodeco/validgen/internal/codegenerator"
Expand All @@ -28,13 +23,20 @@ type ValidationCodeTestCase struct {
ExpectedCode string
}

func generateValidationCodeUnitTests() {
generateValidationCodeTestsFile("build_validation_code_test.tpl", "generated_validation_code_no_pointer_test.go", false)
generateValidationCodeTestsFile("build_validation_code_test.tpl", "generated_validation_code_pointer_test.go", true)
func generateValidationCodeUnitTests() error {
if err := generateValidationCodeUnitTest("build_validation_code_test.tpl", "generated_validation_code_no_pointer_test.go", false); err != nil {
return err
}

if err := generateValidationCodeUnitTest("build_validation_code_test.tpl", "generated_validation_code_pointer_test.go", true); err != nil {
return err
}

return nil
}

func generateValidationCodeTestsFile(tpl, dest string, pointer bool) {
log.Printf("Generating validation code test file: tpl[%s] dest[%s] pointer[%v]\n", tpl, dest, pointer)
func generateValidationCodeUnitTest(tplFile, outputFile string, pointer bool) error {
fmt.Printf("Generating validation code test file: tplFile[%s] outputFile[%s] pointer[%v]\n", tplFile, outputFile, pointer)

funcName := "TestBuildValidationCode"
if pointer {
Expand All @@ -48,7 +50,7 @@ func generateValidationCodeTestsFile(tpl, dest string, pointer bool) {
for _, typeValidation := range typesValidation {
for _, toGenerate := range typeValidation.testCases {
if toGenerate.excludeIf&noPointer != 0 && !pointer {
log.Printf("Skipping no pointer: tag %s type %s\n", typeValidation.tag, toGenerate.typeClass)
fmt.Printf("Skipping no pointer: tag %s type %s\n", typeValidation.tag, toGenerate.typeClass)
continue
}

Expand All @@ -68,11 +70,11 @@ func generateValidationCodeTestsFile(tpl, dest string, pointer bool) {
gv := codegenerator.GenValidations{}
parsedValidation, err := analyzer.ParserValidation(validation)
if err != nil {
log.Fatalf("failed to parse validation %q: %v", validation, err)
return fmt.Errorf("failed to parse validation %q: %v", validation, err)
}
expectedValidationCode, err := gv.BuildValidationCode(fieldName, fieldType, []*analyzer.Validation{parsedValidation})
if err != nil {
log.Fatalf("failed to build validation code for %q: %v", fieldName, err)
return fmt.Errorf("failed to build validation code for %q: %v", fieldName, err)
}

testCases.Tests = append(testCases.Tests, ValidationCodeTestCase{
Expand All @@ -86,37 +88,11 @@ func generateValidationCodeTestsFile(tpl, dest string, pointer bool) {
}
}

if err := testCases.GenerateFile(tpl, dest); err != nil {
log.Fatalf("error generation validation code tests file %s", err)
}

log.Printf("Generating %s done\n", dest)
}

func (at *ValidationCodeTestCases) GenerateFile(tplFile, output string) error {
tpl, err := os.ReadFile(tplFile)
if err != nil {
return fmt.Errorf("error reading %s: %s", tplFile, err)
}

tmpl, err := template.New("ValidationCodeTests").Parse(string(tpl))
if err != nil {
return err
}

code := new(bytes.Buffer)
if err := tmpl.Execute(code, at); err != nil {
return err
if err := ExecTemplate("ValidationCodeTests", tplFile, outputFile, testCases); err != nil {
return fmt.Errorf("error generating validation code tests file %s", err)
}

formattedCode, err := format.Source(code.Bytes())
if err != nil {
return err
}

if err := os.WriteFile(output, formattedCode, 0644); err != nil {
return err
}
fmt.Printf("Generating %s done\n", outputFile)

return nil
}
Loading
Loading