Skip to content

Commit 3abed3c

Browse files
authored
TestGen generating performance comparative tests (#93)
* feat: generate perf cmp tests in testgen * chore: add generated perf tests
1 parent 102d5a7 commit 3abed3c

12 files changed

+20211
-45
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ build: clean
3131

3232
testgen:
3333
@echo "Generating tests"
34-
cd testgen/ && rm -f generated_*.go && go run *.go && mv generated_endtoend_*tests.go ../tests/endtoend/ && mv generated_validation_*_test.go ../internal/codegenerator/ && mv generated_function_code_*_test.go ../internal/codegenerator/
34+
cd testgen/ && rm -f generated_*.go && go run *.go && mv generated_endtoend_*tests.go ../tests/endtoend/ && mv generated_validation_*_test.go ../internal/codegenerator/ && mv generated_function_code_*_test.go ../internal/codegenerator/ && mv generated_cmp_perf_*.go ../tests/cmpbenchtests/generated_tests/
3535

3636
endtoendtests: build
3737
@echo "Running endtoend tests"
@@ -41,7 +41,7 @@ endtoendtests: build
4141

4242
cmpbenchtests: build
4343
@echo "Running cmp bench tests"
44-
rm -f tests/cmpbenchtests/generated_tests/*
44+
rm -f tests/cmpbenchtests/generated_tests/valid*.go && rm -f tests/cmpbenchtests/generated_tests/types.go
4545
cd tests/cmpbenchtests; go run .
4646
$(VALIDGEN_BIN) tests/cmpbenchtests/generated_tests
4747
go clean -testcache
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Code generated by TestGen. DO NOT EDIT.
2+
3+
package benchtests
4+
5+
import (
6+
"testing"
7+
8+
"github.com/go-playground/validator/v10"
9+
)
10+
11+
{{range .Tests}}
12+
type ValidGen{{.TestName}}Struct struct {
13+
Field {{.FieldType}} `valid:"{{.ValidGenTag}}"`
14+
}
15+
16+
type Validator{{.TestName}}Struct struct {
17+
Field {{.FieldType}} `validate:"{{.ValidatorTag}}"`
18+
}
19+
{{end}}
20+
21+
{{range .Tests}}
22+
func BenchmarkValidGen{{.TestName}}(b *testing.B) {
23+
data := &ValidGen{{.TestName}}Struct{
24+
Field: {{.ValidInput}},
25+
}
26+
27+
for b.Loop() {
28+
if err := ValidGen{{.TestName}}StructValidate(data); len(err) > 0 {
29+
b.FailNow()
30+
}
31+
}
32+
}
33+
34+
func BenchmarkValidator{{.TestName}}(b *testing.B) {
35+
var validate *validator.Validate
36+
37+
validate = validator.New(validator.WithRequiredStructEnabled())
38+
data := &Validator{{.TestName}}Struct{
39+
Field: {{.ValidInput}},
40+
}
41+
42+
for b.Loop() {
43+
if err := validate.Struct(data); err != nil {
44+
b.FailNow()
45+
}
46+
}
47+
}
48+
{{end}}

testgen/cmp_perf_pointer_tests.tpl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Code generated by TestGen. DO NOT EDIT.
2+
3+
package benchtests
4+
5+
import (
6+
"testing"
7+
8+
"github.com/go-playground/validator/v10"
9+
)
10+
11+
{{range .Tests}}
12+
type ValidGen{{.TestName}}Struct struct {
13+
Field {{.FieldType}} `valid:"{{.ValidGenTag}}"`
14+
}
15+
16+
type Validator{{.TestName}}Struct struct {
17+
Field {{.FieldType}} `validate:"{{.ValidatorTag}}"`
18+
}
19+
{{end}}
20+
21+
{{range .Tests}}
22+
func BenchmarkValidGen{{.TestName}}(b *testing.B) {
23+
var validInput {{.BasicType}} = {{.ValidInput}}
24+
data := &ValidGen{{.TestName}}Struct{
25+
Field: &validInput,
26+
}
27+
28+
for b.Loop() {
29+
if err := ValidGen{{.TestName}}StructValidate(data); len(err) > 0 {
30+
b.FailNow()
31+
}
32+
}
33+
}
34+
35+
func BenchmarkValidator{{.TestName}}(b *testing.B) {
36+
var validate *validator.Validate
37+
38+
validate = validator.New(validator.WithRequiredStructEnabled())
39+
40+
var validInput {{.BasicType}} = {{.ValidInput}}
41+
42+
data := &Validator{{.TestName}}Struct{
43+
Field: &validInput,
44+
}
45+
46+
for b.Loop() {
47+
if err := validate.Struct(data); err != nil {
48+
b.FailNow()
49+
}
50+
}
51+
}
52+
{{end}}

testgen/generate_cmp_perf_tests.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"go/format"
7+
"log"
8+
"os"
9+
"strings"
10+
"text/template"
11+
12+
"github.com/opencodeco/validgen/internal/common"
13+
"golang.org/x/text/cases"
14+
"golang.org/x/text/language"
15+
)
16+
17+
type CmpBenchTests struct {
18+
Tests []CmpBenchTest
19+
}
20+
21+
type CmpBenchTest struct {
22+
TestName string
23+
FieldType string
24+
BasicType string
25+
ValidGenTag string
26+
ValidatorTag string
27+
ValidInput string
28+
}
29+
30+
func generateComparativePerformanceTests() {
31+
generateComparativePerformanceTest("cmp_perf_no_pointer_tests.tpl", "generated_cmp_perf_no_pointer_test.go", false)
32+
generateComparativePerformanceTest("cmp_perf_pointer_tests.tpl", "generated_cmp_perf_pointer_test.go", true)
33+
}
34+
35+
func generateComparativePerformanceTest(tpl, dest string, pointer bool) {
36+
log.Printf("Generating comparative performance tests file: tpl[%s] dest[%s] pointer[%v]\n", tpl, dest, pointer)
37+
38+
benchTests := CmpBenchTests{}
39+
40+
for _, typeVal := range typesValidation {
41+
if typeVal.validatorTag == "" {
42+
log.Printf("Skipping tag %s: go-validator tag not defined\n", typeVal.tag)
43+
continue
44+
}
45+
46+
for _, testCase := range typeVal.testCases {
47+
if testCase.excludeIf&cmpBenchTests != 0 {
48+
log.Printf("Skipping test: tag %s type %s\n", typeVal.tag, testCase.typeClass)
49+
continue
50+
}
51+
if testCase.excludeIf&noPointer != 0 && !pointer {
52+
log.Printf("Skipping no pointer: tag %s type %s\n", typeVal.tag, testCase.typeClass)
53+
continue
54+
}
55+
56+
normalizedType := testCase.typeClass
57+
if pointer {
58+
normalizedType = "*" + normalizedType
59+
}
60+
61+
fTypes := common.HelperFromNormalizedToBasicTypes(normalizedType)
62+
sNames := common.HelperFromNormalizedToStringNames(normalizedType)
63+
64+
for i := range fTypes {
65+
validGenTag := typeVal.tag
66+
if typeVal.argsCount != common.ZeroValue {
67+
validGenTag += "=" + testCase.validation
68+
}
69+
goValidatorTag := typeVal.validatorTag
70+
if typeVal.argsCount != common.ZeroValue {
71+
goValidatorTag += "=" + testCase.validation
72+
}
73+
testName := cases.Title(language.Und).String(typeVal.tag) + sNames[i]
74+
75+
basicType, _ := strings.CutPrefix(fTypes[i], "*")
76+
77+
benchTests.Tests = append(benchTests.Tests, CmpBenchTest{
78+
TestName: testName,
79+
FieldType: fTypes[i],
80+
BasicType: basicType,
81+
ValidGenTag: validGenTag,
82+
ValidatorTag: goValidatorTag,
83+
ValidInput: strings.ReplaceAll(testCase.validCase, "{{.BasicType}}", basicType),
84+
})
85+
}
86+
}
87+
}
88+
89+
log.Printf("%d test cases were generated\n", len(benchTests.Tests))
90+
91+
if err := benchTests.GenerateFile(tpl, dest); err != nil {
92+
log.Fatalf("error generating comparative performance tests file %s", err)
93+
}
94+
95+
log.Println("Generating done")
96+
}
97+
98+
func (cbt *CmpBenchTests) GenerateFile(tplFile, output string) error {
99+
tpl, err := os.ReadFile(tplFile)
100+
if err != nil {
101+
return fmt.Errorf("error reading %s: %s", tplFile, err)
102+
}
103+
104+
tmpl, err := template.New("BenchTest").Parse(string(tpl))
105+
if err != nil {
106+
return fmt.Errorf("error parsing template %s: %s", tplFile, err)
107+
}
108+
109+
code := new(bytes.Buffer)
110+
if err := tmpl.Execute(code, cbt); err != nil {
111+
return err
112+
}
113+
114+
formattedCode, err := format.Source(code.Bytes())
115+
if err != nil {
116+
return err
117+
}
118+
119+
if err := os.WriteFile(output, formattedCode, 0644); err != nil {
120+
return err
121+
}
122+
123+
return nil
124+
}

testgen/generate_function_code_tests.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,9 @@ func generateFunctionCodeTestsFile(tpl, dest string, pointer bool) {
6565
}
6666

6767
for _, toGenerate := range typeValidation.testCases {
68-
// Default ("") gen no pointer and pointer test.
69-
if toGenerate.generateFor != "" {
70-
if toGenerate.generateFor == "pointer" && !pointer {
71-
continue
72-
}
73-
if toGenerate.generateFor == "nopointer" && pointer {
74-
continue
75-
}
68+
if toGenerate.excludeIf&noPointer != 0 && !pointer {
69+
log.Printf("Skipping no pointer: tag %s type %s\n", typeValidation.tag, toGenerate.typeClass)
70+
continue
7671
}
7772

7873
normalizedType := toGenerate.typeClass

testgen/generate_tests.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ func main() {
1010
generateValidationTypesEndToEndTests()
1111
generateValidationCodeUnitTests()
1212
generateFunctionCodeUnitTests()
13+
generateComparativePerformanceTests()
1314

1415
fmt.Println("Generating done")
1516
}

testgen/generate_validation_code_tests.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,9 @@ func generateValidationCodeTestsFile(tpl, dest string, pointer bool) {
4747

4848
for _, typeValidation := range typesValidation {
4949
for _, toGenerate := range typeValidation.testCases {
50-
// Default ("") gen no pointer and pointer test.
51-
if toGenerate.generateFor != "" {
52-
if toGenerate.generateFor == "pointer" && !pointer {
53-
continue
54-
}
55-
if toGenerate.generateFor == "nopointer" && pointer {
56-
continue
57-
}
50+
if toGenerate.excludeIf&noPointer != 0 && !pointer {
51+
log.Printf("Skipping no pointer: tag %s type %s\n", typeValidation.tag, toGenerate.typeClass)
52+
continue
5853
}
5954

6055
normalizedType := toGenerate.typeClass

testgen/generate_validation_types_tests.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,11 @@ func generateValidationTypesTestsFile(tpl, dest string, pointer bool) {
5252
StructName: structName,
5353
})
5454
for _, toGenerate := range testCase.testCases {
55-
// Default ("") gen no pointer and pointer test.
56-
if toGenerate.generateFor != "" {
57-
if toGenerate.generateFor == "pointer" && !pointer {
58-
continue
59-
}
60-
if toGenerate.generateFor == "nopointer" && pointer {
61-
continue
62-
}
55+
if toGenerate.excludeIf&noPointer != 0 && !pointer {
56+
log.Printf("Skipping no pointer: tag %s type %s\n", testCase.tag, toGenerate.typeClass)
57+
continue
6358
}
59+
6460
normalizedType := toGenerate.typeClass
6561
if pointer {
6662
normalizedType = "*" + normalizedType

0 commit comments

Comments
 (0)