Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Fuzzer and Fuzzer CLI #3769

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
15b188a
feat: add fuzz and fuzz cli
rlaau Feb 5, 2025
f2e44d6
feat: add fuzz and fuzz cli
rlaau Feb 17, 2025
b02cd42
feat: add fuzz and fuzz cli
rlaau Feb 17, 2025
415eecc
fix: fixed some annotation
rlaau Feb 18, 2025
cb32d86
fixed: traslated korean annotations to english
rlaau Feb 19, 2025
d827623
fixed: traslated korean annotations to english
rlaau Feb 19, 2025
0842243
change lexnumber to use lookup table
rlaau Feb 19, 2025
2819d90
changed lexNumber to use init lookup table
rlaau Feb 19, 2025
0a3e3e9
using strBuilder in for loops
rlaau Feb 19, 2025
fd2eb6f
Sort ParserMark only once using the init() function
rlaau Feb 19, 2025
f2bfebc
Add an explanation about the factorize boundary 500
rlaau Feb 19, 2025
12e4fea
Merge branch 'master' into master
rlaau Feb 19, 2025
492456f
sync upstream
rlaau Feb 19, 2025
75b6d5a
changed naming to go style ver 1
rlaau Feb 19, 2025
7e4ed15
changed method name getXXX to XXX
rlaau Feb 19, 2025
32a72dd
refactoring
rlaau Feb 20, 2025
128b6a7
refactored mutator
rlaau Feb 20, 2025
6e6a765
refactored cache
rlaau Feb 21, 2025
3f70a08
mutator refactoring 2
rlaau Feb 21, 2025
5012a46
mutator refactoring end
rlaau Feb 21, 2025
562f1a1
Update gnovm/stdlibs/testing/fuzzing/fuzz_manager.gno
rlaau Feb 21, 2025
95f4bf9
Merge branch 'master' into master
rlaau Feb 21, 2025
f716fd4
resolved conflict with debugger
rlaau Feb 21, 2025
c15d1f7
resolved conflict with debugger
rlaau Feb 21, 2025
a001b82
fixed pcg annotation, panic message, and randomFloat func
rlaau Feb 23, 2025
546bac0
Refactor logging logic for better clarity and maintainability
rlaau Feb 26, 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
Binary file added gnovm/cmd/gno/gno
Binary file not shown.
70 changes: 49 additions & 21 deletions gnovm/cmd/gno/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ type testCfg struct {
updateGoldenTests bool
printRuntimeMetrics bool
printEvents bool
debug bool
debugAddr string

fuzzName string
fuzzIters int
debug bool
debugAddr string
}

func newTestCmd(io commands.IO) *commands.Command {
Expand Down Expand Up @@ -62,27 +65,27 @@ specific directives that can be added using code comments.

These single-line directives can set "input parameters" for the machine used
to perform the test:
- "PKGPATH:" is a single line directive that can be used to define the
package used to interact with the tested package. If not specified, "main" is
used.
- "MAXALLOC:" is a single line directive that can be used to define a limit
to the VM allocator. If this limit is exceeded, the VM will panic. Default to
0, no limit.
- "SEND:" is a single line directive that can be used to send an amount of
token along with the transaction. The format is for example "1000000ugnot".
Default is empty.
- "PKGPATH:" is a single line directive that can be used to define the
package used to interact with the tested package. If not specified, "main" is
used.
- "MAXALLOC:" is a single line directive that can be used to define a limit
to the VM allocator. If this limit is exceeded, the VM will panic. Default to
0, no limit.
- "SEND:" is a single line directive that can be used to send an amount of
token along with the transaction. The format is for example "1000000ugnot".
Default is empty.

These directives, instead, match the comment that follows with the result
of the GnoVM, acting as a "golden test":
- "Output:" tests the following comment with the standard output of the
filetest.
- "Error:" tests the following comment with any panic, or other kind of
error that the filetest generates (like a parsing or preprocessing error).
- "Realm:" tests the following comment against the store log, which can show
what realm information is stored.
- "Stacktrace:" can be used to verify the following lines against the
stacktrace of the error.
- "Events:" can be used to verify the emitted events against a JSON.
- "Output:" tests the following comment with the standard output of the
filetest.
- "Error:" tests the following comment with any panic, or other kind of
error that the filetest generates (like a parsing or preprocessing error).
- "Realm:" tests the following comment against the store log, which can show
what realm information is stored.
- "Stacktrace:" can be used to verify the following lines against the
stacktrace of the error.
- "Events:" can be used to verify the emitted events against a JSON.

To speed up execution, imports of pure packages are processed separately from
the execution of the tests. This makes testing faster, but means that the
Expand Down Expand Up @@ -146,6 +149,18 @@ func (c *testCfg) RegisterFlags(fs *flag.FlagSet) {
"print emitted events",
)

fs.StringVar(
&c.fuzzName,
"fuzz",
"",
"specify fuzz target name (e.g. FuzzXXX) or 'Fuzz' for all fuzz tests",
)
fs.IntVar(
&c.fuzzIters,
"i",
50000,
"number of fuzz iterations to run")

fs.BoolVar(
&c.debug,
"debug",
Expand Down Expand Up @@ -198,13 +213,20 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error {
stdout := goio.Discard
if cfg.verbose {
stdout = io.Out()

}
//Set up options to run tests.

opts := test.NewTestOptions(cfg.rootDir, io.In(), stdout, io.Err())
opts.RunFlag = cfg.run
opts.Sync = cfg.updateGoldenTests
opts.Verbose = cfg.verbose
opts.Metrics = cfg.printRuntimeMetrics
opts.Events = cfg.printEvents

opts.FuzzName = cfg.fuzzName
opts.FuzzIters = cfg.fuzzIters

opts.Debug = cfg.debug

buildErrCount := 0
Expand Down Expand Up @@ -235,9 +257,15 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error {
err = test.Test(memPkg, pkg.Dir, opts)
})

if cfg.fuzzName != "" {
if testErrCount > 0 || buildErrCount > 0 {
return fmt.Errorf(" --- %d build errors, %d test errors", buildErrCount, testErrCount)
} else {
return nil
}
}
duration := time.Since(startedAt)
dstr := fmtDuration(duration)

if hasError || err != nil {
if err != nil {
io.ErrPrintfln("%s: test pkg: %v", pkg.Dir, err)
Expand Down
1 change: 1 addition & 0 deletions gnovm/pkg/gnolang/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func init() {
// runtime debugging flag.

var enabled bool = true
var Enabled *bool = &enabled

func (debugging) Println(args ...interface{}) {
if debug {
Expand Down
46 changes: 32 additions & 14 deletions gnovm/pkg/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ type TestOptions struct {
Metrics bool
// Uses Error to print the events emitted.
Events bool
// Fuzz target function's name
FuzzName string
// Fuzz iters
FuzzIters int

filetestBuffer bytes.Buffer
outWriter proxyWriter
Expand Down Expand Up @@ -282,8 +286,13 @@ func (opts *TestOptions) runTestFiles(
)
}
}()

tests := loadTestFuncs(memPkg.Name, files)
var testOrFuzz string
if opts.FuzzName != "" {
testOrFuzz = "Fuzz"
} else {
testOrFuzz = "Test"
}
tests := loadTestOrFuzz(memPkg.Name, files, testOrFuzz)

var alloc *gno.Allocator
if opts.Metrics {
Expand Down Expand Up @@ -321,6 +330,23 @@ func (opts *TestOptions) runTestFiles(
testingtv := gno.TypedValue{T: &gno.PackageType{}, V: testingpv}
testingcx := &gno.ConstExpr{TypedValue: testingtv}

calledFunction := gno.Sel(testingcx, fmt.Sprintf("Run%s", testOrFuzz)) // Call testing.RunTest or testing.RunFuzz
var params []interface{}
if testOrFuzz == "Fuzz" {
params = append(params, gno.Str(opts.FuzzName)) // fuzz name
params = append(params, gno.Num(strconv.FormatInt(int64(opts.FuzzIters), 10))) // fuzz iters
params = append(params, gno.Nx(strconv.FormatBool(opts.Verbose))) // is verbose?
} else {
params = append(params, gno.Str(opts.RunFlag)) // run flag
params = append(params, gno.Nx(strconv.FormatBool(opts.Verbose))) // is verbose?
}
params = append(params, &gno.CompositeLitExpr{ // Last param, the testing.InternalTest or testing.InternalFuzz
Type: gno.Sel(testingcx, fmt.Sprintf("Internal%s", testOrFuzz)),
Elts: gno.KeyValueExprs{
{Key: gno.X("Name"), Value: gno.Str(tf.Name)},
{Key: gno.X("F"), Value: gno.Nx(tf.Name)}},
})

if opts.Debug {
fileContent := func(ppath, name string) string {
p := filepath.Join(opts.RootDir, ppath, name)
Expand All @@ -339,16 +365,8 @@ func (opts *TestOptions) runTestFiles(
}

eval := m.Eval(gno.Call(
gno.Sel(testingcx, "RunTest"), // Call testing.RunTest
gno.Str(opts.RunFlag), // run flag
gno.Nx(strconv.FormatBool(opts.Verbose)), // is verbose?
&gno.CompositeLitExpr{ // Third param, the testing.InternalTest
Type: gno.Sel(testingcx, "InternalTest"),
Elts: gno.KeyValueExprs{
{Key: gno.X("Name"), Value: gno.Str(tf.Name)},
{Key: gno.X("F"), Value: gno.Nx(tf.Name)},
},
},
calledFunction,
params...,
))

if opts.Events {
Expand Down Expand Up @@ -416,12 +434,12 @@ type testFunc struct {
Name string
}

func loadTestFuncs(pkgName string, tfiles *gno.FileSet) (rt []testFunc) {
func loadTestOrFuzz(pkgName string, tfiles *gno.FileSet, testOrFuzz string) (rt []testFunc) {
for _, tf := range tfiles.Files {
for _, d := range tf.Decls {
if fd, ok := d.(*gno.FuncDecl); ok {
fname := string(fd.Name)
if strings.HasPrefix(fname, "Test") {
if strings.HasPrefix(fname, testOrFuzz) {
tf := testFunc{
Package: pkgName,
Name: fname,
Expand Down
72 changes: 62 additions & 10 deletions gnovm/stdlibs/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading