Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ validgen/
│ ├── parser_validation.go # Validation tag parsing
│ └── test_elements.go # Test condition building
├── types/ # Common types and error handling
└── tests/ # Integration, unit, and benchmark tests
└── tests/ # Integration and end-to-end tests
```

## Code Style & Conventions
Expand Down Expand Up @@ -97,7 +97,7 @@ func UserValidate(obj *User) []error {
- **Unit tests**: Test individual validation parsing and code generation
- **Integration tests**: Test complete struct validation generation
- **End-to-end tests**: Test full CLI workflow with real Go files
- **Benchmark tests**: Performance comparison with reflection-based validators
- **Benchmark tests**: Performance comparison lives in opencodeco/validgen-benchmarks

### File Processing Workflow
1. `FindFiles()` - Recursively discover `.go` files
Expand Down Expand Up @@ -167,13 +167,13 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/
# Typical development workflow
make build # Build the binary
make endtoendtests # Run full test suite
make benchtests # Run performance benchmarks
# Benchmarks live in opencodeco/validgen-benchmarks
```

## When Working on ValidGen

1. **Always regenerate test validators** after code changes using `make endtoendtests`
2. **Run benchmarks** to ensure performance improvements with `make benchtests`
2. **Run benchmarks** from opencodeco/validgen-benchmarks when a change can affect generated validation speed
3. **Test multiple packages** - structs in different packages should work correctly
4. **Validate generated code** - ensure it compiles and passes tests
5. **Maintain compatibility** - keep validator tag syntax compatible where possible
Expand Down
23 changes: 6 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
.PHONY: clean unittests benchtests build endtoendtests cmpbenchtests testgen setup lint
.PHONY: clean unittests build endtoendtests testgen setup lint

BIN_DIR=bin
BIN_NAME=validgen
VALIDGEN_BIN=$(BIN_DIR)/$(BIN_NAME)
BENCH_TIME=5s
ifdef VALIDGEN_BENCHMARKS_DIR
export VALIDGEN_BENCHMARKS_DIR := $(abspath $(VALIDGEN_BENCHMARKS_DIR))
endif
GOLANGCILINT_PATH=$(HOME)/bin
GOLANGCILINT_BIN=$(GOLANGCILINT_PATH)/golangci-lint

all: clean unittests build endtoendtests benchtests cmpbenchtests
all: clean unittests build endtoendtests

clean:
@echo "Cleaning"
Expand All @@ -18,20 +20,13 @@ unittests:
go clean -testcache
go test -v ./internal/... ./types/... ./testgen/

benchtests: build
@echo "Running bench tests"
find tests/bench/ -name '*_validator.go' -exec rm \{} \;
$(VALIDGEN_BIN) tests/bench
go clean -testcache
go test -bench=. -v -benchmem -benchtime=$(BENCH_TIME) ./tests/bench

build: clean
@echo "Building"
go build -o $(VALIDGEN_BIN) .

testgen:
@echo "Generating tests"
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/
cd testgen/ && rm -f generated_*.go && go run . && mv generated_endtoend_*tests.go ../tests/endtoend/ && mv generated_validation_*_test.go ../internal/codegenerator/ && mv generated_function_code_*_test.go ../internal/codegenerator/

endtoendtests: build
@echo "Running endtoend tests"
Expand All @@ -43,12 +38,6 @@ endtoendtests: build
$(VALIDGEN_BIN) -unmarshal-json tests/jsonunmarshal
cd tests/jsonunmarshal; go run .

cmpbenchtests: build
@echo "Running cmp bench tests"
$(VALIDGEN_BIN) tests/cmpbenchtests/
go clean -testcache
go test -bench=. -v -benchmem -benchtime=$(BENCH_TIME) ./tests/cmpbenchtests/

setup:
@echo "Setting up"
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(GOLANGCILINT_PATH) v2.5.0
Expand Down
35 changes: 11 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,18 +183,19 @@ cd validgen
make unittests
```

## Steps to run the benchmark tests
## Benchmarks

The steps to run the benchmark tests are:
Comparisons with [go-playground/validator](https://github.com/go-playground/validator) live in [validgen-benchmarks](https://github.com/opencodeco/validgen-benchmarks). That module holds the small three-way benchmark, the generated comparison suite, and the color helper check.

```bash
# Enter in the project root folder
cd validgen

# Run the benchmark tests
make benchtests
git clone git@github.com:opencodeco/validgen-benchmarks.git
cd validgen-benchmarks
make bench
make color
```

`make cmp` runs the generated suite. The default bench time is 5 seconds per benchmark. `make cmp BENCH_TIME=100ms` is a shorter pass.

## Steps to run the end-to-end tests

The steps to run the end-to-end tests are:
Expand All @@ -221,28 +222,14 @@ Build the CLI from this repository with `make build`. The binary is `bin/validge

Pass `-unmarshal-json` to also generate `UnmarshalJSON` methods. The samples repository shows that flag on `signup`.

## Steps to run the benchmark tests comparing ValidGen and Validator

The steps to run the benchmark tests are:

```bash
# Enter in the project root folder
cd validgen

# Run the benchmark tests
make cmpbenchtests
```

The command `make cmpbenchtests` invoke the following command:

`go test -bench=. -v -benchmem -benchtime=5s ./tests/cmpbenchtests/generated_tests`
## Recorded comparison results

The setup used was:
These numbers are from an Apple M4 Pro, 12 cores, running `go test -bench=. -benchmem -benchtime=5s` on the generated suite:

```bash
goos: darwin
goarch: arm64
pkg: github.com/opencodeco/validgen/tests/cmpbenchtests/generated_tests
pkg: github.com/opencodeco/validgen-benchmarks/cmp
cpu: Apple M4 Pro (12 Cores used)
```

Expand Down
4 changes: 2 additions & 2 deletions docs/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,12 @@ The same path is rewritten on every run. Structs in one package and directory sh

TestGen is `package main` in `testgen/`. `make testgen` runs it and moves the files it writes.

`generate_tests.go` calls four generators. Each one executes a template under `testgen/`, formats the result with `go/format`, and writes a pair of pointer and non-pointer files.
`generate_tests.go` calls four generators. Three of them execute a template under `testgen/`. The comparative benchmark generator reads its templates from `$VALIDGEN_BENCHMARKS_DIR/cmp` when that variable is set. Each generator formats the result with `go/format` and writes a pair of pointer and non-pointer files.

- `generated_endtoend_no_pointer_tests.go` and `generated_endtoend_pointer_tests.go` move to `tests/endtoend/`.
- `generated_validation_code_no_pointer_test.go` and `generated_validation_code_pointer_test.go` move to `internal/codegenerator/`.
- `generated_function_code_no_pointer_test.go` and `generated_function_code_pointer_test.go` move to `internal/codegenerator/`.
- `generated_cmp_perf_no_pointer_test.go` and `generated_cmp_perf_pointer_test.go` move to `tests/cmpbenchtests/`.
- `generated_cmp_perf_no_pointer_test.go` and `generated_cmp_perf_pointer_test.go` are written to `$VALIDGEN_BENCHMARKS_DIR/cmp/` when that variable is set. The templates are in [validgen-benchmarks](https://github.com/opencodeco/validgen-benchmarks). TestGen skips those files when the variable is unset.

The case list is `typesValidation` in `testgen/validations.go`. [testgen/README.md](../testgen/README.md) records which suites that list drives and which suites stay hand-written.

Expand Down
16 changes: 2 additions & 14 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,8 @@ module github.com/opencodeco/validgen
go 1.24.3

require (
github.com/go-playground/validator/v10 v10.28.0
github.com/sergi/go-diff v1.4.0
github.com/stretchr/testify v1.11.1
golang.org/x/text v0.29.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require github.com/stretchr/testify v1.11.1 // indirect
19 changes: 0 additions & 19 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
Expand All @@ -26,14 +12,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
Expand Down
8 changes: 6 additions & 2 deletions testgen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ However, these generators lacked a common configuration, didn't implement all te

## What TestGen generates

`typesValidation` in `validations.go` is the case list. `make testgen` walks that list and writes four suites. Each suite is a pointer file and a non-pointer file. The list contains non-field operations only.
`typesValidation` in `validations.go` is the case list. `make testgen` walks that list and writes the end-to-end suite and the two unit-test suites. It writes the comparative benchmark suite when `VALIDGEN_BENCHMARKS_DIR` is set. Each suite is a pointer file and a non-pointer file. The list contains non-field operations only.

- Benchmark tests between ValidGen and GoValidator, in `tests/cmpbenchtests/generated_cmp_perf_*`.
- Benchmark tests between ValidGen and GoValidator, written to `opencodeco/validgen-benchmarks` when `VALIDGEN_BENCHMARKS_DIR` is set. The templates are `cmp/cmp_perf_*_tests.tpl` in that repository.
- End-to-end tests for each validation, type class, and valid or invalid input, in `tests/endtoend/generated_endtoend_*`.
- Unit tests for `BuildValidationCode`, in `internal/codegenerator/generated_validation_code_*`.
- Unit tests for the generated validator function, in `internal/codegenerator/generated_function_code_*`.
Expand Down Expand Up @@ -114,4 +114,8 @@ cd validgen

# Run testgen
make testgen

# Also regenerate the comparative benchmarks
export VALIDGEN_BENCHMARKS_DIR=/absolute/path/to/validgen-benchmarks
make testgen
```
48 changes: 0 additions & 48 deletions testgen/cmp_perf_no_pointer_tests.tpl

This file was deleted.

52 changes: 0 additions & 52 deletions testgen/cmp_perf_pointer_tests.tpl

This file was deleted.

21 changes: 19 additions & 2 deletions testgen/generate_cmp_perf_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package main

import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/opencodeco/validgen/internal/common"
Expand All @@ -23,11 +25,26 @@ type CmpBenchTest struct {
}

func generateComparativePerformanceTests() error {
if err := generateComparativePerformanceTest("cmp_perf_no_pointer_tests.tpl", "generated_cmp_perf_no_pointer_test.go", false); err != nil {
dir := os.Getenv("VALIDGEN_BENCHMARKS_DIR")
if dir == "" {
fmt.Println("Skipping comparative performance tests: VALIDGEN_BENCHMARKS_DIR is not set")
return nil
}

cmpDir := filepath.Join(dir, "cmp")
if err := generateComparativePerformanceTest(
filepath.Join(cmpDir, "cmp_perf_no_pointer_tests.tpl"),
filepath.Join(cmpDir, "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 {
if err := generateComparativePerformanceTest(
filepath.Join(cmpDir, "cmp_perf_pointer_tests.tpl"),
filepath.Join(cmpDir, "generated_cmp_perf_pointer_test.go"),
true,
); err != nil {
return err
}

Expand Down
Loading
Loading